diff --git a/composer.json b/composer.json index e4268c8..8ebceae 100644 --- a/composer.json +++ b/composer.json @@ -38,10 +38,10 @@ "symplify/easy-coding-standard": "^6.0" }, "scripts": { - "check-cs": "ecs check src", - "fix-cs": "ecs check src --fix", + "check-cs": "ecs check src examples", + "fix-cs": "ecs check src examples --fix", "lint": "parallel-lint --colors --exclude vendor .", - "phpstan": "phpstan analyze src --level max -c phpstan.neon --error-format=fileoutput", + "phpstan": "phpstan analyze src examples --level max -c phpstan.neon --error-format=fileoutput", "test": [ "@lint", "@check-cs", diff --git a/ecs.yml b/ecs.yml index 6122870..eb02797 100644 --- a/ecs.yml +++ b/ecs.yml @@ -16,4 +16,6 @@ parameters: SlevomatCodingStandard\Sniffs\Namespaces\ReferenceUsedNamesOnlySniff: ~ Symplify\CodingStandard\Sniffs\DependencyInjection\NoClassInstantiationSniff: ~ Symplify\CodingStandard\Sniffs\CleanCode\ForbiddenStaticFunctionSniff: ~ - SlevomatCodingStandard\Sniffs\TypeHints\TypeHintDeclarationSniff.MissingTraversableReturnTypeHintSpecification: ~ #DELETE + PhpCsFixer\Fixer\Import\OrderedImportsFixer: ~ + Symplify\CodingStandard\Fixer\Commenting\BlockPropertyCommentFixer: ~ + Symplify\CodingStandard\Sniffs\ControlStructure\SprintfOverContactSniff: ~ diff --git a/examples/badInput.php b/examples/badInput.php index 18999dc..dd0a133 100644 --- a/examples/badInput.php +++ b/examples/badInput.php @@ -1,8 +1,9 @@ 8, 8 => 2, 3 => 2, - 4 => 2 + 4 => 2, ]; $lineGraph->addMarkers($lineA, [AsciiColorizer::GREEN, AsciiColorizer::BOLD], [AsciiColorizer::RED]); -echo $lineGraph->chart(); +$lineGraph->chart()->print(); diff --git a/examples/example.php b/examples/example.php index f752962..b5b4c3f 100644 --- a/examples/example.php +++ b/examples/example.php @@ -1,9 +1,10 @@ setFormat( //control how y axis labels will be printed out function ($x, Settings $settings) { $padding = $settings->getPadding(); - $paddingLength = \strlen($padding); + $paddingLength = strlen($padding); return substr($padding . round($x, 2), -$paddingLength); } @@ -57,7 +58,6 @@ function ($x, Settings $settings) { Linechart::CROSS //Point can be made more visible with crosslines. Default is Linegraph::POINT ); - $graph = $lineGraph->chart(); //Graph is an object with all data drawn. It can be simply echoed or we can leverage its methods for output control $graph->clearScreen(); //clears already outputed graphs diff --git a/examples/floats.php b/examples/floats.php index 5159d48..6dfd036 100644 --- a/examples/floats.php +++ b/examples/floats.php @@ -1,9 +1,10 @@ chart()->clearScreen()->print()->wait(); $lineGraph->clearAllMarkers(); } -} catch (Exception $e) { +} catch (\Throwable $e) { echo $e->getMessage(); } diff --git a/examples/html.php b/examples/html.php index 0c5fd14..1e696b3 100644 --- a/examples/html.php +++ b/examples/html.php @@ -1,9 +1,10 @@ addMarkers($lineA, ['color: green'], ['color: red']); $lineGraph->setSettings($settings); - -echo $lineGraph->chart(); +$lineGraph->chart()->print(); diff --git a/examples/image.php b/examples/image.php index dc4e469..7db9813 100644 --- a/examples/image.php +++ b/examples/image.php @@ -1,10 +1,11 @@ outputChart(); +$text = (string) $graph; // Calculate the required width to hold this text $enclosingBox = imagettfbbox($fontSize, 0, $font, $text); -$width = ($enclosingBox[2] - $enclosingBox[0]) - 10; +$width = $enclosingBox[2] - $enclosingBox[0] - 10; $height = $fontSize * $graph->getSettings()->getHeight() * 2; diff --git a/examples/largeKeys.php b/examples/largeKeys.php index bfb9242..70dbcf1 100644 --- a/examples/largeKeys.php +++ b/examples/largeKeys.php @@ -1,8 +1,9 @@ addMarkers($lineA, [AsciiColorizer::GREEN, AsciiColorizer::BOLD]); -echo $lineGraph->chart(); +$lineGraph->chart()->print(); diff --git a/examples/line.php b/examples/line.php index 2d38d8c..503efd6 100644 --- a/examples/line.php +++ b/examples/line.php @@ -2,13 +2,12 @@ declare(strict_types=1); -use noximo\PHPColoredAsciiLinechart\Linechart; use noximo\PHPColoredAsciiLinechart\Settings; +use noximo\PHPColoredAsciiLinechart\Linechart; require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php'; $settings = new Settings(); $settings->setFPS(40); -$settings->setHeight(null); $lineGraph = new Linechart(); $lineGraph->setSettings($settings); @@ -16,6 +15,6 @@ try { $linechart = new Linechart(); $linechart->addMarkers([1, 1, 1, 1, 1, 1])->chart()->print(); -} catch (Exception $e) { +} catch (\Throwable $e) { echo $e->getMessage(); } diff --git a/examples/randomValues.php b/examples/randomValues.php index 2d025a6..d4e4aa9 100644 --- a/examples/randomValues.php +++ b/examples/randomValues.php @@ -1,9 +1,10 @@ setSettings($settings); try { + $lineA = []; + $lineB = []; + $lineC = []; + $lineD = []; for ($i = 0; $i < 120; $i++) { - $lineA[$i] = ($lineA[$i - 1] ?? 1) + random_int(-2, 2); - $lineB[$i] = ($lineB[$i - 1] ?? 1) + random_int(-2, 2); - $lineC[$i] = ($lineC[$i - 1] ?? 1) + random_int(-2, 2); - $lineD[$i] = ($lineD[$i - 1] ?? 1) + random_int(-2, 2); + $lineA[$i] = $lineA[$i - 1] ?? 1 + random_int(-2, 2); + $lineB[$i] = $lineB[$i - 1] ?? 1 + random_int(-2, 2); + $lineC[$i] = $lineC[$i - 1] ?? 1 + random_int(-2, 2); + $lineD[$i] = $lineD[$i - 1] ?? 1 + random_int(-2, 2); } for ($y = 0; $y < 1500; $y++) { array_shift($lineA); @@ -39,6 +44,6 @@ $lineGraph->chart()->clearScreen()->print()->wait(); $lineGraph->clearAllMarkers(); } -} catch (Exception $e) { - echo $e->getMessage(); +} catch (Throwable $throwable) { + echo $throwable->getMessage(); } diff --git a/examples/realData.php b/examples/realData.php index 3197d83..722688f 100644 --- a/examples/realData.php +++ b/examples/realData.php @@ -1,24 +1,24 @@ setFPS(40); -$settings->setHeight(null); $lineGraph = new Linechart(); $lineGraph->setSettings($settings); try { - $line = [0.0208, 0.020858, 0.021, 0.021, 0.0211, 0.0211, 0.0211, 0.0211, 0.0211, 0.021056, 0.0211, 0.0211, 0.0211, 0.0211, 0.0211, 0.0211, 0.0211, 0.0211, 0.021124, 0.0214, 0.0215, 0.021436, 0.02149, 0.021488, 0.02149, 0.02145, 0.02145, 0.021406, 0.02145, 0.02145, 0.02145, 0.0214, 0.02145, 0.021487, 0.02149, 0.021482, 0.02148, 0.02148, 0.0215, 0.0215, 0.0215, 0.0215, 0.021499, 0.021473, 0.021454, 0.021497, 0.021489, 0.021454, 0.021705, 0.02151, 0.021513,]; + $line = [0.0208, 0.020858, 0.021, 0.021, 0.0211, 0.0211, 0.0211, 0.0211, 0.0211, 0.021056, 0.0211, 0.0211, 0.0211, 0.0211, 0.0211, 0.0211, 0.0211, 0.0211, 0.021124, 0.0214, 0.0215, 0.021436, 0.02149, 0.021488, 0.02149, 0.02145, 0.02145, 0.021406, 0.02145, 0.02145, 0.02145, 0.0214, 0.02145, 0.021487, 0.02149, 0.021482, 0.02148, 0.02148, 0.0215, 0.0215, 0.0215, 0.0215, 0.021499, 0.021473, 0.021454, 0.021497, 0.021489, 0.021454, 0.021705, 0.02151, 0.021513]; $lineGraph->addMarkers($line, [AsciiColorizer::GREEN], [AsciiColorizer::RED]); $lineGraph->chart()->clearScreen()->print()->wait(); $lineGraph->clearAllMarkers(); -} catch (Exception $e) { +} catch (\Throwable $e) { echo $e->getMessage(); } diff --git a/examples/sinus.php b/examples/sinus.php index 60b167f..8a096d2 100644 --- a/examples/sinus.php +++ b/examples/sinus.php @@ -1,9 +1,10 @@ setFPS(40); -$settings->setHeight(null); $lineGraph = new Linechart(); $lineGraph->setSettings($settings); @@ -23,7 +23,6 @@ $lineGraph->chart()->clearScreen()->print()->wait(); $lineGraph->clearAllMarkers(); - -} catch (Exception $e) { +} catch (\Throwable $e) { echo $e->getMessage(); } diff --git a/log/phpstan.html b/log/phpstan.html new file mode 100644 index 0000000..21ca6ae --- /dev/null +++ b/log/phpstan.html @@ -0,0 +1,217 @@ + + + + PHPStan analysis result + +

PHPStan analysis result

+
2019-08-20 22:11:06
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Files with errors: 2
C:\wamp64\www\PHPColoredAsciiLinechart\examples\image.php (9×)
+ 49 + + Parameter #1 $im of function imagedestroy expects resource, resource|false given.
+ - '#Parameter \#1 \$im of function imagedestroy expects resource, resource\|false given#' +
+ 48 + + Parameter #1 $im of function imagepng expects resource, resource|false given.
+ - '#Parameter \#1 \$im of function imagepng expects resource, resource\|false given#' +
+ 45 + + Parameter #1 $im of function imagettftext expects resource, resource|false given.
+ - '#Parameter \#1 \$im of function imagettftext expects resource, resource\|false given#' +
+ 43 + + Parameter #1 $im of function imagefilledrectangle expects resource, resource|false given.
+ - '#Parameter \#1 \$im of function imagefilledrectangle expects resource, resource\|false given#' +
+ 43 + + Parameter #4 $x2 of function imagefilledrectangle expects int, float|int given.
+ - '#Parameter \#4 \$x2 of function imagefilledrectangle expects int, float\|int given#' +
+ 41 + + Parameter #1 $im of function imagecolorallocate expects resource, resource|false given.
+ - '#Parameter \#1 \$im of function imagecolorallocate expects resource, resource\|false given#' +
+ 40 + + Parameter #1 $im of function imagecolorallocate expects resource, resource|false given.
+ - '#Parameter \#1 \$im of function imagecolorallocate expects resource, resource\|false given#' +
+ 39 + + Parameter #1 $x_size of function imagecreatetruecolor expects int, float|int given.
+ - '#Parameter \#1 \$x_size of function imagecreatetruecolor expects int, float\|int given#' +
+ 36 + + Only numeric types are allowed in *, int|null given on the right side.
+ - '#Only numeric types are allowed in \*, int\|null given on the right side#' +
C:\wamp64\www\PHPColoredAsciiLinechart\examples\randomValues.php (4×)
+ 35 + + Only numeric types are allowed in +, int|false given on the left side.
+ - '#Only numeric types are allowed in \+, int\|false given on the left side#' +
+ 33 + + Only numeric types are allowed in +, int|false given on the left side.
+ - '#Only numeric types are allowed in \+, int\|false given on the left side#' +
+ 31 + + Only numeric types are allowed in +, int|false given on the left side.
+ - '#Only numeric types are allowed in \+, int\|false given on the left side#' +
+ 29 + + Only numeric types are allowed in +, int|false given on the left side.
+ - '#Only numeric types are allowed in \+, int\|false given on the left side#' +
+
+ This file was made thanks to + PHPStan and was outputted by + PHPStan FileOutput +
+
+ diff --git a/phpstan.neon b/phpstan.neon index a740de3..b55ddea 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -4,19 +4,10 @@ parameters: excludes_analyse: - %rootDir%/vendor ignoreErrors: - - '#Access to an undefined property ccxt\\Exchange::\$secret.#' - - '#Access to an undefined property ccxt\\Exchange::\$apiKey.#' - - '#Access to an undefined property ccxt\\Exchange::\$has.#' - - '#Constant ROOT_DIR not found.#' - - '#Constant BITTREX_API_KEY not found.#' - - '#Constant BITTREX_API_SECRET not found.#' includes: - - vendor/phpstan/phpstan-nette/extension.neon - - vendor/phpstan/phpstan-nette/rules.neon - vendor/phpstan/phpstan-deprecation-rules/rules.neon - vendor/phpstan/phpstan-strict-rules/rules.neon - - vendor/grifart/phpstan-oneline/config.neon services: errorFormatter.fileoutput: - class: noximo\FileOutput(./log/phpstan.html, @errorFormatter.compact) + class: noximo\FileOutput(./log/phpstan.html, @errorFormatter.raw) diff --git a/src/Chart.php b/src/Chart.php index a6634c8..f72944a 100644 --- a/src/Chart.php +++ b/src/Chart.php @@ -4,6 +4,10 @@ namespace noximo\PHPColoredAsciiLinechart; +use function chr; +use function strlen; +use function is_resource; + /** * Class Graph * @package noximo\PHPColoredConsoleLinegraph @@ -59,74 +63,74 @@ public function __toString() { return $this->prepareChart() . $this->prepareText(); } - + public function getSettings(): Settings { return $this->settings; } - + public function setSettings(Settings $settings): self { $this->settings = $settings; return $this; } - + public function getResults(): array { return $this->results; } - + public function getMin(): float { return $this->min; } - + public function setMin(float $min): self { $this->min = $min; return $this; } - + public function getMax(): float { return $this->max; } - + public function setMax(float $max): self { $this->max = $max; return $this; } - + public function setWidth(int $width): self { $this->width = $width; return $this; } - + public function addResult(array $result): void { $this->results[] = $result; } - + public function printAndwait(): self { $this->print()->wait(); return $this; } - + public function wait(): self { usleep((int) round(1000000 / $this->settings->getFps())); return $this; } - + public function print(): self { $this->output($this->prepareChart()); @@ -134,7 +138,7 @@ public function print(): self return $this; } - + public function toArray(): array { $return = []; @@ -146,11 +150,13 @@ public function toArray(): array return $return; } - - public function clearScreen(?bool $useAlternativeMethod = null): self + + public function clearScreen(bool $useAlternativeMethod = false): self { if ($useAlternativeMethod) { - $output = \chr(27) . \chr(91) . 'H' . \chr(27) . \chr(91) . 'J'; + $chr27 = chr(27); + $chr91 = chr(91); + $output = sprintf('%s%sH%s%sJ', $chr27, $chr91, $chr27, $chr91); } else { $output = "\033[0;0f"; } @@ -159,7 +165,7 @@ public function clearScreen(?bool $useAlternativeMethod = null): self return $this; } - + public function setAlltimeMaxHeight(int $allTimeMaxHeight): self { $this->allTimeMaxHeight = $allTimeMaxHeight; @@ -175,7 +181,7 @@ public function printText(): self return $this; } - + private function prepareChart(): string { $return = ''; @@ -206,41 +212,54 @@ private function prepareText(): string return $return; } - + private function output(string $output): void { $fopen = fopen('php://stdout', 'wb'); - if (\is_resource($fopen)) { + if (is_resource($fopen)) { fwrite($fopen, $output); } } - + private function merge(): array { - $x = 0; $merged = []; foreach ($this->results as $result) { foreach ($result as $x => $row) { - foreach ($row as $y => $cell) { - if ($this->shouldBeMerged($merged, (string) $x, (string) $y, (string) $cell)) { - $merged[$x][$y] = (string) $cell; - } - } + $merged = $this->mergeRow($merged, $row, $x); } } + return $this->adjustAllTimeMaxHeight($merged, $x ?? 0); + } + + private function mergeRow(array $merged, array $row, int $x): array + { + foreach ($row as $y => $cell) { + $cell = (string) $cell; + if ($this->shouldBeMerged($merged, $x, $y, $cell)) { + $merged[$x][$y] = $cell; + } + } + + return $merged; + } + + private function adjustAllTimeMaxHeight(array $merged, int $x): array + { if ($x < $this->allTimeMaxHeight) { - $width = $this->width + \strlen($this->settings->getPadding()); + $width = $this->width + strlen($this->settings->getPadding()); + $filledArray = array_fill(0, $width, ' '); for ($i = 0, $iMax = $this->allTimeMaxHeight - $x; $i < $iMax; $i++) { - $merged[] = array_fill(0, $width, ' '); + $merged[] = $filledArray; } } return $merged; } - - private function shouldBeMerged(array $merged, string $x, string $y, string $cell): bool + + private function shouldBeMerged(array $merged, int $x, int $y, string $cell): bool { - return !isset($merged[$x][$y]) || ($cell !== ' ' && strpos($merged[$x][$y], 'm') === false); + return !isset($merged[$x][$y]) || ($cell !== ' '); } } diff --git a/src/Colorizers/AsciiColorizer.php b/src/Colorizers/AsciiColorizer.php index 42c5ebf..4771012 100644 --- a/src/Colorizers/AsciiColorizer.php +++ b/src/Colorizers/AsciiColorizer.php @@ -5,100 +5,145 @@ namespace noximo\PHPColoredAsciiLinechart\Colorizers; use ReflectionClass; +use ReflectionException; +use function chr; +use function in_array; /** * Class Colorize * @package noximo\PHPColoredConsoleLinegraph */ -final class AsciiColorizer implements IColorizer +final class AsciiColorizer implements ColorizerInterface { + /** @var int */ public const BOLD = 1; + /** @var int */ public const DARK = 2; + /** @var int */ public const ITALIC = 3; + /** @var int */ public const UNDERLINE = 4; + /** @var int */ public const BLINK = 5; + /** @var int */ public const REVERSE = 7; + /** @var int */ public const CONCEALED = 8; + /** @var int */ public const BLACK = 30; + /** @var int */ public const RED = 31; + /** @var int */ public const GREEN = 32; + /** @var int */ public const YELLOW = 33; + /** @var int */ public const BLUE = 34; + /** @var int */ public const MAGENTA = 35; + /** @var int */ public const CYAN = 36; + /** @var int */ public const LIGHT_GRAY = 37; + /** @var int */ public const DARK_GRAY = 90; + /** @var int */ public const LIGHT_RED = 91; + /** @var int */ public const LIGHT_GREEN = 92; + /** @var int */ public const LIGHT_YELLOW = 93; + /** @var int */ public const LIGHT_BLUE = 94; + /** @var int */ public const LIGHT_MAGENTA = 95; + /** @var int */ public const LIGHT_CYAN = 96; + /** @var int */ public const WHITE = 97; + /** @var int */ public const BACKGROUND_DEFAULT = 49; + /** @var int */ public const BACKGROUND_BLACK = 40; + /** @var int */ public const BACKGROUND_RED = 41; + /** @var int */ public const BACKGROUND_GREEN = 42; + /** @var int */ public const BACKGROUND_YELLOW = 43; + /** @var int */ public const BACKGROUND_BLUE = 44; + /** @var int */ public const BACKGROUND_MAGENTA = 45; + /** @var int */ public const BACKGROUND_CYAN = 46; + /** @var int */ public const BACKGROUND_LIGHT_GRAY = 47; + /** @var int */ public const BACKGROUND_DARK_GRAY = 100; + /** @var int */ public const BACKGROUND_LIGHT_RED = 101; + /** @var int */ public const BACKGROUND_LIGHT_GREEN = 102; + /** @var int */ public const BACKGROUND_LIGHT_YELLOW = 103; + /** @var int */ public const BACKGROUND_LIGHT_BLUE = 104; + /** @var int */ public const BACKGROUND_LIGHT_MAGENTA = 105; + /** @var int */ public const BACKGROUND_LIGHT_CYAN = 106; + /** @var int */ public const BACKGROUND_WHITE = 107; + /** @var array */ private static $constants = []; /** * @param array|null $colors * + * @return string * @throws ColorException - * @throws \ReflectionException + * @throws ReflectionException */ public function colorize(string $text, ?array $colors = null): string { @@ -107,32 +152,34 @@ public function colorize(string $text, ?array $colors = null): string } foreach ($colors as $color) { - if (!$this->colorExists($color)) { + if (!$this->colorExists((int) $color)) { throw new ColorException('Unknown color ' . $color . ', use constans of class Color'); } } - return \chr(27) . '[0m' . \chr(27) . '[' . implode(';', $colors) . 'm' . $text . \chr(27) . '[0m'; + $chr27 = chr(27); + + return sprintf('%s[0m%s[%sm%s%s[0m', $chr27, $chr27, implode(';', $colors), $text, $chr27); } /** - * @throws \ReflectionException + * @throws ReflectionException */ public function colorExists(int $color): bool { - if (empty(self::$constants)) { + if (count(self::$constants) === 0) { $oClass = new ReflectionClass(self::class); self::$constants = $oClass->getConstants(); } - return \in_array($color, self::$constants, true); + return in_array($color, self::$constants, true); } - + public function getEOL(): string { return PHP_EOL; } - + public function processFinalText(string $text): string { return $text; diff --git a/src/Colorizers/IColorizer.php b/src/Colorizers/ColorizerInterface.php similarity index 73% rename from src/Colorizers/IColorizer.php rename to src/Colorizers/ColorizerInterface.php index 643f08d..95d934f 100644 --- a/src/Colorizers/IColorizer.php +++ b/src/Colorizers/ColorizerInterface.php @@ -4,18 +4,14 @@ namespace noximo\PHPColoredAsciiLinechart\Colorizers; -/** - * Interface IColor - * @package noximo\PHPColoredConsoleLinegraph\Colorizers - */ -interface IColorizer +interface ColorizerInterface { /** * @param array|null $colors */ public function colorize(string $text, ?array $colors = null): string; - + public function getEOL(): string; - + public function processFinalText(string $text): string; } diff --git a/src/Colorizers/HTMLColorizer.php b/src/Colorizers/HTMLColorizer.php index 6067e5e..07b69d1 100644 --- a/src/Colorizers/HTMLColorizer.php +++ b/src/Colorizers/HTMLColorizer.php @@ -8,7 +8,7 @@ * Class HTMLColorizer * @package noximo\PHPColoredAsciiLinechart\Colorizers */ -final class HTMLColorizer implements IColorizer +final class HTMLColorizer implements ColorizerInterface { /** * @param array|null $styles @@ -21,14 +21,14 @@ public function colorize(string $text, ?array $styles = null): string $cssStyles = implode(';', str_replace(' ', '', $styles)); - return "" . $text . ''; + return sprintf("%s", $cssStyles, $text); } - + public function getEOL(): string { return '
'; } - + public function processFinalText(string $text): string { $div = "
"; diff --git a/src/Colorizers/ImageColorizer.php b/src/Colorizers/ImageColorizer.php index 8cd05f8..2fe9271 100644 --- a/src/Colorizers/ImageColorizer.php +++ b/src/Colorizers/ImageColorizer.php @@ -8,7 +8,7 @@ * Class HTMLColorizer * @package noximo\PHPColoredAsciiLinechart\Colorizers */ -final class ImageColorizer implements IColorizer +final class ImageColorizer implements ColorizerInterface { /** * @param array|null $styles @@ -17,12 +17,12 @@ public function colorize(string $text, ?array $styles = null): string { return $text; } - + public function getEOL(): string { return PHP_EOL; } - + public function processFinalText(string $text): string { return $text; diff --git a/src/Linechart.php b/src/Linechart.php index bd54de7..5b73ee6 100644 --- a/src/Linechart.php +++ b/src/Linechart.php @@ -4,9 +4,9 @@ namespace noximo\PHPColoredAsciiLinechart; -use noximo\PHPColoredAsciiLinechart\Colorizers\IColorizer; -use function in_array; +use noximo\PHPColoredAsciiLinechart\Colorizers\ColorizerInterface; use function strlen; +use function in_array; /** * Class LineGraph @@ -14,30 +14,39 @@ */ final class Linechart { + /** @var string */ public const CROSS = 'cross'; + /** @var string */ public const POINT = 'point'; + /** @var string */ public const DASHED_LINE = 'dashedLine'; + /** @var string */ public const POINT_X = 'x'; + /** @var string */ public const POINT_Y = 'y'; + /** @var string */ public const VALUE = 'value'; + /** @var string */ public const COLORS = 'colors'; + /** @var string */ public const FULL_LINE = 'fullLIne'; + /** @var string */ public const MARKERS = 'markers'; + /** @var string */ public const COLORS_DOWN = 'colorsDown'; + /** @var string */ public const SPREADS = 'spreads'; - private $width; - /** * @var array = [ * [['markers' => [1,2,3.45], SELF::COLORS => [1,2,3]]]; @@ -45,40 +54,43 @@ final class Linechart */ private $allmarkers = []; + /** @var int */ + private $width; + /** * @var array|null */ private $currentColors; /** - * @var float|null + * @var float */ - private $range; + private $range = 0.0; /** - * @var float|null + * @var float */ - private $ratio; + private $ratio = 0.0; /** - * @var float|null + * @var float */ - private $min2; + private $min2 = 0.0; /** - * @var float|null + * @var float */ - private $max2; + private $max2 = 0.0; /** - * @var int|null + * @var int */ - private $rows; + private $rows = 1; /** - * @var int|null + * @var int */ - private $offset; + private $offset = 0; /** * @var float|null @@ -96,7 +108,7 @@ final class Linechart private $settings; /** - * @var IColorizer|null + * @var ColorizerInterface */ private $colorizer; @@ -109,6 +121,7 @@ final class Linechart */ public function addPoint(int $x, float $y, ?array $colors = null, ?string $appearance = null): self { + $markers = []; $markers[0] = $y; $markers[$x] = $y; if (!in_array($appearance, [self::CROSS, self::POINT], true)) { @@ -119,43 +132,6 @@ public function addPoint(int $x, float $y, ?array $colors = null, ?string $appea return $this; } - /** - * @param array $markers - * @param array|null $colors - * @param array|null $colorsDown - * @param string|null $point - * @return Linechart - */ - private function addMarkerData(array $markers, ?array $colors = null, ?array $colorsDown = null, ?string $point = null): self - { - $markersData = [ - self::MARKERS => $this->normalizeData($markers), - self::COLORS => $colors ?? [], - self::COLORS_DOWN => $colorsDown ?? $colors ?? [], - self::POINT => $point, - ]; - - $this->allmarkers[] = $markersData; - - return $this; - } - - private function normalizeData(array $markers): array - { - $markers = array_filter($markers, '\is_int', ARRAY_FILTER_USE_KEY); - ksort($markers); - - reset($markers); - $firstKey = key($markers); - - $keys = []; - foreach (array_keys($markers) as $key) { - $keys[] = $key - $firstKey; - } - - return array_combine($keys, $markers); - } - /** * @param array $markers * @param array|null $colors @@ -176,25 +152,7 @@ public function chart(): Chart $this->prepareData(); foreach ($this->allmarkers as $markersData) { - $markersData[self::MARKERS] = $this->adjustMarkerValues($markersData[self::MARKERS]); - $this->currentColors = $this->currentColors ?? $markersData[self::COLORS]; - $result = $this->prepareResult(); - - $result = $this->processBorder($result, $markersData); - $isPoint = in_array($markersData[self::POINT], [self::CROSS, self::POINT], true); - $isLine = in_array($markersData[self::POINT], [self::DASHED_LINE, self::FULL_LINE], true); - - foreach ($markersData[self::MARKERS] as $x => $value) { - $y0 = (int)(round($value * $this->ratio) - $this->min2); - - if ($this->isPresent($markersData[self::MARKERS], $x + 1)) { - $result = $this->processLinearGraph($result, $markersData, $x, $y0); - } else if ($x !== 0 && $isPoint) { - $result = $this->processPoint($result, $markersData, $y0, $x); - } else if ($x === 0 && $isLine) { - $result = $this->processLine($result, $y0, $markersData[self::POINT]); - } - } + $result = $this->getResultFromMarkersData($markersData); $this->currentColors = null; $graph->addResult($result); @@ -221,9 +179,90 @@ public function setSettings(Settings $settings): self return $this; } + public function clearAllMarkers(): self + { + $this->allmarkers = []; + + return $this; + } + + /** + * @param string $value + * @param string[] $color + */ + public function addText(string $value, array $color): void + { + $this->text[] = [ + self::VALUE => $value, + self::COLORS => $color, + ]; + } + + /** + * @param array $values + * @param float $mainValue + * @param array $colors + */ + public function addSpread(array $values, float $mainValue, array $colors): void + { + foreach ($values as $value) { + $colors = $colors ?? []; + $appearance = $value === 1 ? self::FULL_LINE : self::DASHED_LINE; + $this->addLine($value * $mainValue, $colors ?? [], $appearance); + } + } + + /** + * @param float $value alias y coordinate + * @param array|null $colors + * @param string|null $appearance + * @return Linechart + */ + public function addLine(float $value, ?array $colors = null, ?string $appearance = null): self + { + $markers = []; + $markers[0] = $value; + if (!in_array($appearance, [self::DASHED_LINE, self::FULL_LINE], true)) { + $appearance = self::DASHED_LINE; + } + $this->addMarkerData($markers, $colors, null, $appearance); + + return $this; + } + + public function getText(): array + { + return $this->text; + } + + public function clearText(): void + { + $this->text = []; + } + + /** + * @param array $markers + * @param array|null $colors + * @param array|null $colorsDown + * @param string|null $point + * @return Linechart + */ + private function addMarkerData(array $markers, ?array $colors = null, ?array $colorsDown = null, ?string $point = null): self + { + $markersData = [ + self::MARKERS => $this->normalizeData($markers), + self::COLORS => $colors ?? [], + self::COLORS_DOWN => $colorsDown ?? $colors ?? [], + self::POINT => $point, + ]; + + $this->allmarkers[] = $markersData; + + return $this; + } + private function prepareData(): void { - $this->colorizer = $this->getSettings()->getColorizer(); [$min, $max, $width] = $this->findMinMax($this->allmarkers); $this->adjuster = $this->findAdjuster($min, $max); @@ -232,19 +271,66 @@ private function prepareData(): void $this->range = max(1, abs($max - $min)); - $height = (int)($this->getSettings()->getHeight() ?? $this->range); + $height = (int) ($this->getSettings()->getHeight() ?? $this->range); $this->ratio = $height / $this->range; $this->min2 = $min * $this->ratio; $this->max2 = $max * $this->ratio; - $this->rows = (int)max(0, abs(round($this->max2 - $this->min2))); + $this->rows = (int) max(0, abs(round($this->max2 - $this->min2))); $this->offset = $this->getSettings()->getOffset(); $this->width = $width + $this->offset; } + private function getResultFromMarkersData(array $markersData): array + { + $markersData[self::MARKERS] = $this->adjustMarkerValues($markersData[self::MARKERS]); + $this->currentColors = $this->currentColors ?? $markersData[self::COLORS]; + + $result = $this->processBorder($this->prepareResult(), $markersData); + + $isPoint = in_array($markersData[self::POINT], [self::CROSS, self::POINT], true); + $isLine = in_array($markersData[self::POINT], [self::DASHED_LINE, self::FULL_LINE], true); + + foreach ($markersData[self::MARKERS] as $x => $value) { + $y0 = (int) (round($value * $this->ratio) - $this->min2); + + if ($this->isPresent($markersData[self::MARKERS], $x + 1)) { + $result = $this->processLinearGraph($result, $markersData, $x, $y0); + } elseif ($x !== 0 && $isPoint) { + $result = $this->processPoint($result, $markersData, $y0, $x); + } elseif ($x === 0 && $isLine) { + $result = $this->processLine($result, $y0, $markersData[self::POINT]); + } + } + + return $result; + } + + private function normalizeData(array $markers): array + { + $markers = array_filter($markers, '\is_int', ARRAY_FILTER_USE_KEY); + ksort($markers); + + reset($markers); + $firstKey = key($markers); + + $keys = []; + foreach (array_keys($markers) as $key) { + $keys[] = $key - $firstKey; + } + + $combined = array_combine($keys, $markers); + + if ($combined === false) { + return []; + } + + return $combined; + } + private function findMinMax(array $allmarkers): array { $width = 0; @@ -252,7 +338,7 @@ private function findMinMax(array $allmarkers): array $max = -PHP_INT_MAX; foreach ($allmarkers as $markers) { end($markers[self::MARKERS]); - $width = (int)max($width, key($markers[self::MARKERS])); + $width = (int) max($width, key($markers[self::MARKERS])); /** @var int[][] $markers */ foreach ($markers[self::MARKERS] as $value) { @@ -294,28 +380,16 @@ private function adjustMarkerValues(array $markers): array } return array_map(function ($value) { - return $value * $this->adjuster; + return $this->adjuster !== null ? $value * $this->adjuster : $value; }, $markers); } - private function prepareResult(): array - { - $result = []; - - /** @noinspection ForeachInvariantsInspection */ - for ($i = 0; $i <= $this->rows; $i++) { - $result[$i] = array_fill(0, $this->width, ' '); - } - - return $result; - } - private function processBorder(array $result, array $markersData): array { $format = $this->getSettings()->getFormat(); - $y0 = (int)(round($markersData[self::MARKERS][0] * $this->ratio) - $this->min2); - $y = (int)floor($this->min2); - $yMax = (int)ceil($this->max2); + $y0 = (int) (round($markersData[self::MARKERS][0] * $this->ratio) - $this->min2); + $y = (int) floor($this->min2); + $yMax = (int) ceil($this->max2); for (; $y <= $yMax; ++$y) { $rows = $this->rows === 0 ? 1 : $this->rows; @@ -324,9 +398,9 @@ private function processBorder(array $result, array $markersData): array $label = $format($rawLabel, $this->getSettings()); $border = '┤'; - if ($y - $this->min2 === $rows - $y0) { - $label = $this->colorizer->colorize($label, $this->currentColors); - $border = $this->colorizer->colorize('┼', $this->currentColors); + if ($y - $this->min2 === (float) ($rows - $y0)) { + $label = $this->colorize($label, $this->currentColors); + $border = $this->colorize('┼', $this->currentColors); } $result[$y - $this->min2][max($this->offset - strlen($label), 0)] = $label; @@ -336,13 +410,16 @@ private function processBorder(array $result, array $markersData): array return $result; } - private function deadjust(float $number): float + private function prepareResult(): array { - if ($this->adjuster !== null) { - $number /= $this->adjuster; + $result = []; + + /** @noinspection ForeachInvariantsInspection */ + for ($i = 0; $i <= $this->rows; $i++) { + $result[$i] = array_fill(0, $this->width, ' '); } - return $number; + return $result; } private function isPresent(array $markers, int $x): bool @@ -352,9 +429,9 @@ private function isPresent(array $markers, int $x): bool private function processLinearGraph(array $result, array $markersData, int $x, int $y): array { - $y1 = (int)(round($markersData[self::MARKERS][$x + 1] * $this->ratio) - $this->min2); + $y1 = (int) (round($markersData[self::MARKERS][$x + 1] * $this->ratio) - $this->min2); if ($y === $y1) { - $result[$this->rows - $y][$x + $this->offset] = $this->colorizer->colorize('─', $this->currentColors); + $result[$this->rows - $y][$x + $this->offset] = $this->colorize('─', $this->currentColors); } else { if ($y > $y1) { $connectA = '╰'; @@ -367,13 +444,13 @@ private function processLinearGraph(array $result, array $markersData, int $x, i $this->currentColors = $markersData[self::COLORS]; } - $result[$this->rows - $y1][$x + $this->offset] = $this->colorizer->colorize($connectA, $this->currentColors); - $result[$this->rows - $y][$x + $this->offset] = $this->colorizer->colorize($connectB, $this->currentColors); + $result[$this->rows - $y1][$x + $this->offset] = $this->colorize($connectA, $this->currentColors); + $result[$this->rows - $y][$x + $this->offset] = $this->colorize($connectB, $this->currentColors); $from = min($y, $y1); $to = max($y, $y1); for ($i = $from + 1; $i < $to; $i++) { - $result[$this->rows - $i][$x + $this->offset] = $this->colorizer->colorize('│', $this->currentColors); + $result[$this->rows - $i][$x + $this->offset] = $this->colorize('│', $this->currentColors); } } @@ -384,14 +461,14 @@ private function processPoint(array $result, array $markersData, int $y, int $x) { if ($markersData[self::POINT] === self::CROSS) { for ($i = 0; $i <= $this->width - $this->offset - 2; $i++) { - $result[$this->rows - $y][$i + $this->offset] = $this->colorizer->colorize('╌', $this->currentColors); + $result[$this->rows - $y][$i + $this->offset] = $this->colorize('╌', $this->currentColors); } for ($i = 0; $i <= $this->rows; $i++) { - $result[$this->rows - $i][$x + $this->offset] = $this->colorizer->colorize('╎', $this->currentColors); + $result[$this->rows - $i][$x + $this->offset] = $this->colorize('╎', $this->currentColors); } } - $result[$this->rows - $y][$x + $this->offset] = $this->colorizer->colorize('o', $this->currentColors); + $result[$this->rows - $y][$x + $this->offset] = $this->colorize('o', $this->currentColors); return $result; } @@ -404,69 +481,27 @@ private function processLine(array $result, int $y, string $lineStyle): array } for ($i = 0; $i <= $this->width - $this->offset - 2; $i++) { - $result[$this->rows - $y][$i + $this->offset] = $this->colorizer->colorize($line, $this->currentColors); + $result[$this->rows - $y][$i + $this->offset] = $this->colorize($line, $this->currentColors); } return $result; } - public function clearAllMarkers(): self - { - $this->allmarkers = []; - - return $this; - } - - /** - * @param string $value - * @param string[] $color - */ - public function addText(string $value, array $color): void - { - $this->text[] = [ - self::VALUE => $value, - self::COLORS => $color, - ]; - } - - /** - * @param array $values - * @param float $mainValue - * @param array $colors - */ - public function addSpread(array $values, float $mainValue, array $colors): void - { - foreach ($values as $value) { - $colors = $colors ?? []; - $appearance = $value === 1 ? self::FULL_LINE : self::DASHED_LINE; - $this->addLine($value * $mainValue, $colors ?? [], $appearance); - } - } - - /** - * @param float $value alias y coordinate - * @param array|null $colors - * @param string|null $appearance - * @return Linechart - */ - public function addLine(float $value, ?array $colors = null, ?string $appearance = null): self + private function deadjust(float $number): float { - $markers[0] = $value; - if (!in_array($appearance, [self::DASHED_LINE, self::FULL_LINE], true)) { - $appearance = self::DASHED_LINE; + if ($this->adjuster !== null) { + $number /= $this->adjuster; } - $this->addMarkerData($markers, $colors, null, $appearance); - return $this; + return $number; } - public function getText(): array + private function colorize(string $label, ?array $currentColors = null): string { - return $this->text; - } + if ($this->colorizer === null) { + $this->colorizer = $this->getSettings()->getColorizer(); + } - public function clearText(): void - { - $this->text = []; + return $this->colorizer->colorize($label, $currentColors); } } diff --git a/src/Settings.php b/src/Settings.php index 7b9ee91..fec5392 100644 --- a/src/Settings.php +++ b/src/Settings.php @@ -5,7 +5,7 @@ namespace noximo\PHPColoredAsciiLinechart; use noximo\PHPColoredAsciiLinechart\Colorizers\AsciiColorizer; -use noximo\PHPColoredAsciiLinechart\Colorizers\IColorizer; +use noximo\PHPColoredAsciiLinechart\Colorizers\ColorizerInterface; /** * Class Config @@ -39,7 +39,7 @@ final class Settings private $fps = 12; /** - * @var IColorizer + * @var ColorizerInterface */ private $colorizer; @@ -78,7 +78,7 @@ public function getHeight(): ?int return $this->height; } - public function setHeight(?int $height): self + public function setHeight(int $height): self { $this->height = $height; @@ -129,12 +129,12 @@ public function setFPS(int $fps): self return $this; } - public function getColorizer(): IColorizer + public function getColorizer(): ColorizerInterface { return $this->colorizer ?? new AsciiColorizer(); } - public function setColorizer(IColorizer $colorizer): self + public function setColorizer(ColorizerInterface $colorizer): self { $this->colorizer = $colorizer;