diff --git a/.gitignore b/.gitignore
index 3534a65..e232598 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,7 @@
/build/
+/cache/
/composer.lock
+/docs/api/
/.env
/.phpcheck/
/.php_cs.cache
diff --git a/Makefile b/Makefile
index 7401ef4..e031422 100644
--- a/Makefile
+++ b/Makefile
@@ -6,6 +6,12 @@ watch:
watch-playground:
while inotifywait -e close_write -r composer.* ./src ./checks ./functions ./playground ./tests; do php playground/test.php; done
+docs-build:
+ php sami.phar update sami.config.php
+
+docs-clean:
+ rm -rf build/ cache/
+
phpcheck:
@phpcheck
diff --git a/checks/GeneratorCheck.php b/checks/GeneratorCheck.php
index c2980a2..447c029 100644
--- a/checks/GeneratorCheck.php
+++ b/checks/GeneratorCheck.php
@@ -201,7 +201,7 @@ public function checkFakerWithArgs(int $int): bool
}
/**
- * @param int $int {@gen variant(123, choose(0, 1000000))}
+ * @param int $int {@gen variant("123", choose(0, 1000000))}
*/
public function checkVariant(int $int): bool
{
diff --git a/composer.json b/composer.json
index 0244277..bff02ae 100644
--- a/composer.json
+++ b/composer.json
@@ -39,6 +39,7 @@
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.14",
"phpmd/phpmd": "^2.6",
+ "phpstan/phpstan": "^0.11.5",
"phpunit/phpunit": "^8.1"
},
"config": {
diff --git a/sami.config.php b/sami.config.php
new file mode 100644
index 0000000..ce988df
--- /dev/null
+++ b/sami.config.php
@@ -0,0 +1,24 @@
+files()
+ ->name('*.php')
+ ->in($dir = __DIR__.'/src');
+
+return new Sami(
+ $iterator,
+ [
+ 'default_opened_level' => 2,
+ 'remote_repository' => new GitHubRemoteRepository('datashaman/phpcheck', dirname($dir)),
+ 'sort_class_constants' => true,
+ 'sort_class_interfaces' => true,
+ 'sort_class_methods' => true,
+ 'sort_class_properties' => true,
+ 'sort_class_traits' => true,
+ 'title' => 'PHPCheck API',
+ ]
+);
diff --git a/sami.phar b/sami.phar
new file mode 100644
index 0000000..6a0a951
Binary files /dev/null and b/sami.phar differ
diff --git a/src/Coverage/Coverage.php b/src/Coverage/Coverage.php
index 4f6c4f3..e051034 100644
--- a/src/Coverage/Coverage.php
+++ b/src/Coverage/Coverage.php
@@ -9,6 +9,9 @@
*/
namespace Datashaman\PHPCheck\Coverage;
+/**
+ * Abstract base class for coverage classes.
+ */
abstract class Coverage
{
public function __destruct()
diff --git a/src/Coverage/HtmlCoverage.php b/src/Coverage/HtmlCoverage.php
index 370e03a..367579c 100644
--- a/src/Coverage/HtmlCoverage.php
+++ b/src/Coverage/HtmlCoverage.php
@@ -11,8 +11,22 @@
use SebastianBergmann\CodeCoverage\Report\Html\Facade as HtmlFacade;
+/**
+ * This class produces an HTML coverage report to a specified folder.
+ */
class HtmlCoverage extends Coverage
{
+ private $folder;
+
+ public function __construct(string $folder)
+ {
+ $this->folder = $folder;
+ }
+
+ /**
+ * Processing is done in the __destruct method to ensure maximum coverage
+ * results.
+ */
public function __destruct()
{
global $coverage;
@@ -20,6 +34,6 @@ public function __destruct()
parent::__destruct();
$writer = new HtmlFacade();
- $writer->process($coverage, $this->input->getOption('coverage-html'));
+ $writer->process($coverage, $this->folder);
}
}
diff --git a/src/Coverage/TextCoverage.php b/src/Coverage/TextCoverage.php
index dc9d5b6..8b4df82 100644
--- a/src/Coverage/TextCoverage.php
+++ b/src/Coverage/TextCoverage.php
@@ -9,10 +9,35 @@
*/
namespace Datashaman\PHPCheck\Coverage;
+use function Datashaman\PHPCheck\{
+ app,
+};
use SebastianBergmann\CodeCoverage\Report\Text;
+/**
+ * This class produces a text coverage report to standard output or a specified file.
+ */
class TextCoverage extends Coverage
{
+ private $_output;
+ private $_noAnsi;
+
+ /**
+ * @param null|string $output
+ * @param null|bool $noAnsi
+ */
+ public function __construct(
+ string $output = null,
+ bool $noAnsi = null
+ ) {
+ $this->_output = $output;
+ $this->_noAnsi = $noAnsi;
+ }
+
+ /**
+ * Processing is done in the __destruct method to ensure maximum coverage
+ * results.
+ */
public function __destruct()
{
global $coverage;
@@ -21,16 +46,16 @@ public function __destruct()
$writer = new Text();
- if ($this->input->getOption('coverage-text')) {
+ if ($this->_output) {
$output = $writer->process($coverage, false);
- \file_put_contents($this->input->getOption('coverage-text'), $output);
+ \file_put_contents($this->_output, $output);
return;
}
$color = true;
- if ($this->input->getOption('no-ansi') !== false) {
+ if ($this->_noAnsi !== false) {
$color = false;
}
diff --git a/src/CheckError.php b/src/Exceptions/CheckError.php
similarity index 93%
rename from src/CheckError.php
rename to src/Exceptions/CheckError.php
index e7f2d2b..8592b72 100644
--- a/src/CheckError.php
+++ b/src/Exceptions/CheckError.php
@@ -7,7 +7,7 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
-namespace Datashaman\PHPCheck;
+namespace Datashaman\PHPCheck\Exceptions;
use Exception;
diff --git a/src/Example.php b/src/Exceptions/Example.php
similarity index 91%
rename from src/Example.php
rename to src/Exceptions/Example.php
index 4703eb6..a69abcb 100644
--- a/src/Example.php
+++ b/src/Exceptions/Example.php
@@ -7,7 +7,7 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
-namespace Datashaman\PHPCheck;
+namespace Datashaman\PHPCheck\Exceptions;
use Exception;
diff --git a/src/ExecutionError.php b/src/Exceptions/ExecutionError.php
similarity index 95%
rename from src/ExecutionError.php
rename to src/Exceptions/ExecutionError.php
index f1c8a2d..2da1787 100644
--- a/src/ExecutionError.php
+++ b/src/Exceptions/ExecutionError.php
@@ -7,7 +7,7 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
-namespace Datashaman\PHPCheck;
+namespace Datashaman\PHPCheck\Exceptions;
use Exception;
use Throwable;
diff --git a/src/ExecutionFailure.php b/src/Exceptions/ExecutionFailure.php
similarity index 94%
rename from src/ExecutionFailure.php
rename to src/Exceptions/ExecutionFailure.php
index ef9e50f..8048b45 100644
--- a/src/ExecutionFailure.php
+++ b/src/Exceptions/ExecutionFailure.php
@@ -7,7 +7,7 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
-namespace Datashaman\PHPCheck;
+namespace Datashaman\PHPCheck\Exceptions;
use Exception;
diff --git a/src/Reflection.php b/src/Reflection.php
index 3671e85..2b769c7 100644
--- a/src/Reflection.php
+++ b/src/Reflection.php
@@ -24,11 +24,6 @@ public function getClass($class)
return new ReflectionClass($class);
}
- public function getMethod($class, $method)
- {
- return new ReflectionMethod($class, $method);
- }
-
public function getFunctionSignature(ReflectionFunction $function): string
{
return $function->getName();
@@ -39,34 +34,6 @@ public function getMethodSignature(ReflectionMethod $method): string
return $method->getDeclaringClass()->getName() . '::' . $method->getName();
}
- public function getParamAnnotations($reflectionCallable): array
- {
- $factory = DocBlockFactory::createInstance();
- $docComment = $reflectionCallable->getDocComment();
-
- if ($docComment === false) {
- return [];
- }
-
- $docBlock = $factory->create($docComment);
-
- return $docBlock->getTagsByName('param');
- }
-
- public function getParamAnnotation(ReflectionParameter $param): ?Param
- {
- $method = $param->getDeclaringFunction();
- $annotations = $this->getParamAnnotations($method);
-
- foreach ($annotations as $annotation) {
- if ($annotation->getVariableName() === $param->getName()) {
- return $annotation;
- }
- }
-
- return null;
- }
-
public function getParamTags(ReflectionParameter $param): array
{
$annotation = $this->getParamAnnotation($param);
@@ -95,4 +62,32 @@ public function reflect($subject)
return $subject;
}
+
+ private function getParamAnnotations($reflectionCallable): array
+ {
+ $factory = DocBlockFactory::createInstance();
+ $docComment = $reflectionCallable->getDocComment();
+
+ if ($docComment === false) {
+ return [];
+ }
+
+ $docBlock = $factory->create($docComment);
+
+ return $docBlock->getTagsByName('param');
+ }
+
+ private function getParamAnnotation(ReflectionParameter $param): ?Param
+ {
+ $method = $param->getDeclaringFunction();
+ $annotations = $this->getParamAnnotations($method);
+
+ foreach ($annotations as $annotation) {
+ if ($annotation->getVariableName() === $param->getName()) {
+ return $annotation;
+ }
+ }
+
+ return null;
+ }
}
diff --git a/src/Results/Failure.php b/src/Results/Failure.php
index d44d91e..abe2309 100644
--- a/src/Results/Failure.php
+++ b/src/Results/Failure.php
@@ -9,6 +9,8 @@
*/
namespace Datashaman\PHPCheck\Results;
+use Exception;
+
class Failure
{
/**
diff --git a/src/RunState.php b/src/RunState.php
index 4f96477..4eb0fe0 100644
--- a/src/RunState.php
+++ b/src/RunState.php
@@ -28,7 +28,7 @@ class TEXT NOT NULL,
)
EOT;
- protected const INSERT_RESULT_SQL = <<<'EOT'
+ private const INSERT_RESULT_SQL = <<<'EOT'
INSERT OR REPLACE INTO results (
class,
method,
@@ -44,17 +44,17 @@ class,
)
EOT;
- protected const SELECT_DEFECT_SQL = <<<'EOT'
+ private const SELECT_DEFECT_SQL = <<<'EOT'
SELECT args FROM results WHERE class = :class AND method = :method AND status IN ('ERROR', 'FAILURE')
EOT;
- protected $errors = [];
+ private $errors = [];
- protected $failures = [];
+ private $failures = [];
- protected $startTime;
+ private $startTime;
- protected $successes = [];
+ private $successes = [];
public static function getSubscribedEvents(): array
{
@@ -90,22 +90,22 @@ public function getDefectArgs(ReflectionMethod $method): ?array
return null;
}
- public function getErrors()
+ public function getErrors(): array
{
return $this->errors;
}
- public function getFailures()
+ public function getFailures(): array
{
return $this->failures;
}
- public function getSuccesses()
+ public function getSuccesses(): array
{
return $this->successes;
}
- public function getStartTime()
+ public function getStartTime(): float
{
return $this->startTime;
}
@@ -136,7 +136,7 @@ public function onSuccess(Events\SuccessEvent $event): void
$this->saveResult($event);
}
- protected function saveResult(Events\ResultEvent $event): void
+ private function saveResult(Events\ResultEvent $event): void
{
$args = $event->args ? (\json_encode($event->args) ?: '') : '';
@@ -151,7 +151,7 @@ protected function saveResult(Events\ResultEvent $event): void
$statement->execute();
}
- protected function formatMicrotime(float $microtime): string
+ private function formatMicrotime(float $microtime): string
{
$decimal = \preg_match('/^[0-9]*\\.([0-9]+)$/', (string) $microtime, $reg)
? \mb_substr(\str_pad($reg[1], 6, '0'), 0, 6)
diff --git a/src/Runner.php b/src/Runner.php
index 773c0fd..c636f5e 100644
--- a/src/Runner.php
+++ b/src/Runner.php
@@ -11,8 +11,8 @@
*/
namespace Datashaman\PHPCheck;
+use DateTime;
use Exception;
-use InvalidArgumentException;
use phpDocumentor\Reflection\DocBlockFactory;
use ReflectionFunctionAbstract;
use ReflectionMethod;
@@ -38,6 +38,8 @@ class Runner implements EventSubscriberInterface
private $input;
+ private $maxSuccess;
+
private $output;
private $totalIterations = 0;
@@ -96,11 +98,14 @@ public function execute(InputInterface $input, OutputInterface $output): void
$output->writeln('You must specify a directory for coverage-html');
exit(1);
}
- $coverage = new Coverage\HtmlCoverage($this);
+ $coverage = new Coverage\HtmlCoverage($input->getOption('coverage-html'));
}
if ($input->getOption('coverage-text') !== false) {
- $coverage = new Coverage\TextCoverage($this);
+ $coverage = new Coverage\TextCoverage(
+ $input->getOption('coverage-text'),
+ $input->getOption('no-ansi')
+ );
}
$config = $this->getConfig();
@@ -114,7 +119,7 @@ public function execute(InputInterface $input, OutputInterface $output): void
$output->writeln('You must specify a filename for log-junit');
exit(1);
}
- $reporter = new Subscribers\JUnitReporter($this);
+ $reporter = new Subscribers\JUnitReporter();
$dispatcher->addSubscriber($reporter);
}
@@ -123,7 +128,7 @@ public function execute(InputInterface $input, OutputInterface $output): void
$output->writeln('You must specify a filename for log-text');
exit(1);
}
- $reporter = new Subscribers\TextReporter($this);
+ $reporter = new Subscribers\TextReporter();
$dispatcher->addSubscriber($reporter);
}
@@ -156,10 +161,7 @@ public function execute(InputInterface $input, OutputInterface $output): void
$classes = \array_diff(
\get_declared_classes(),
- $classes,
- [
- Check::class,
- ]
+ $classes
);
$classes = \array_filter(
@@ -207,10 +209,8 @@ function ($class) {
if (!$noDefects && $defectArgs) {
try {
\call_user_func($closure, ...$defectArgs);
- } catch (InvalidArgumentException $exception) {
- throw new ExecutionFailure($defectArgs, $exception);
} catch (Throwable $throwable) {
- throw new ExecutionError($defectArgs, $throwable);
+ throw new Exceptions\ExecutionError($defectArgs, $throwable);
}
}
@@ -221,14 +221,14 @@ function ($class) {
);
if (!$result['passed']) {
- throw new ExecutionFailure($result['input'], $result['error']);
+ throw new Exceptions\ExecutionFailure($result['input']);
}
}
$event = new Events\SuccessEvent($method, $tags);
$dispatcher->dispatch(CheckEvents::SUCCESS, $event);
$status = 'SUCCESS';
- } catch (ExecutionFailure $failure) {
+ } catch (Exceptions\ExecutionFailure $failure) {
$event = new Events\FailureEvent(
$method,
$tags,
@@ -236,7 +236,7 @@ function ($class) {
);
$dispatcher->dispatch(CheckEvents::FAILURE, $event);
$status = 'FAILURE';
- } catch (ExecutionError $error) {
+ } catch (Exceptions\ExecutionError $error) {
$event = new Events\ErrorEvent(
$method,
$tags,
@@ -256,7 +256,7 @@ function ($class) {
$dispatcher->dispatch(CheckEvents::END_ALL, $event);
}
- public function checks(
+ private function checks(
int $size,
callable $subject,
callable $check = null,
@@ -289,7 +289,7 @@ public function checks(
while (!$passed) {
try {
$input = $this->shrink($input);
- } catch (Example $e) {
+ } catch (Exceptions\Example $e) {
$input = $e->args;
$exhausted = true;
}
@@ -315,7 +315,7 @@ public function checks(
return $result;
}
- protected function getConfig(string $filename = null): ?SimpleXMLElement
+ private function getConfig(string $filename = null): ?SimpleXMLElement
{
$filename = self::CONFIG_FILE;
@@ -333,7 +333,7 @@ protected function getConfig(string $filename = null): ?SimpleXMLElement
return null;
}
- protected function gatherPaths(OutputInterface $output, string $pathArgument): array
+ private function gatherPaths(OutputInterface $output, string $pathArgument): array
{
$path = \realpath($pathArgument);
@@ -365,7 +365,7 @@ protected function gatherPaths(OutputInterface $output, string $pathArgument): a
return $paths;
}
- protected function getMethodTags(ReflectionMethod $method)
+ private function getMethodTags(ReflectionMethod $method)
{
$factory = DocBlockFactory::createInstance();
$docComment = $method->getDocComment();
@@ -412,7 +412,7 @@ protected function getMethodTags(ReflectionMethod $method)
return $result;
}
- protected function getFilter(InputInterface $input)
+ private function getFilter(InputInterface $input)
{
$filter = $input->getOption('filter');
@@ -431,7 +431,7 @@ protected function getFilter(InputInterface $input)
return $parts;
}
- protected function shrink($args)
+ private function shrink($args)
{
foreach ($args as &$arg) {
if (\is_string($arg)) {
@@ -476,10 +476,10 @@ protected function shrink($args)
}
}
- throw new Example($args);
+ throw new Exceptions\Example($args);
}
- protected function passed(
+ private function passed(
ReflectionFunctionAbstract $subject,
ReflectionFunctionAbstract $check,
array $input
@@ -491,7 +491,7 @@ protected function passed(
try {
\set_error_handler(
function ($code, $message, $file, $line): void {
- throw new CheckError(
+ throw new Exceptions\CheckError(
$message,
$code,
$file,
@@ -506,7 +506,7 @@ function ($code, $message, $file, $line): void {
$output = $subject->invoke(...$input);
$passed = $check->invoke($input, $output);
}
- } catch (CheckError $error) {
+ } catch (Exceptions\CheckError $error) {
$passed = false;
}
diff --git a/src/Subscribers/ConsoleReporter.php b/src/Subscribers/ConsoleReporter.php
index 89d21e9..6b33dc1 100644
--- a/src/Subscribers/ConsoleReporter.php
+++ b/src/Subscribers/ConsoleReporter.php
@@ -12,18 +12,17 @@
namespace Datashaman\PHPCheck\Subscribers;
use function Datashaman\PHPCheck\app;
-
use Datashaman\PHPCheck\CheckCommand;
use Datashaman\PHPCheck\CheckEvents;
+
use Datashaman\PHPCheck\Events;
-use Datashaman\PHPCheck\Traits\LogTrait;
+use function Datashaman\PHPCheck\reflection;
+use function Datashaman\PHPCheck\repr;
use NunoMaduro\Collision\Writer;
use Whoops\Exception\Inspector;
-class ConsoleReporter extends Reporter
+class ConsoleReporter extends Subscriber
{
- use LogTrait;
-
protected const HEADER = 'PHPCheck %s by Marlin Forbes and contributors.';
protected const STATUS_CHARACTERS = [
@@ -38,9 +37,9 @@ class ConsoleReporter extends Reporter
'SUCCESS' => 'info',
];
- protected $output;
+ private $output;
- protected $writer;
+ private $writer;
public static function getSubscribedEvents(): array
{
@@ -62,7 +61,6 @@ public function __construct()
'#' . $baseDir . '/src/*#',
'#' . $baseDir . '/bin/*#',
'#vendor/symfony/console.*#',
- '#vendor/webmozart/assert.*#',
]
);
}
@@ -80,7 +78,7 @@ public function onStartAll(Events\StartAllEvent $event): void
public function onStart(Events\StartEvent $event): void
{
if ($this->output->isDebug()) {
- $signature = $this->getMethodSignature($event->method);
+ $signature = reflection()->getMethodSignature($event->method);
$this->output->writeln("Check '$signature' started");
}
}
@@ -88,7 +86,7 @@ public function onStart(Events\StartEvent $event): void
public function onEnd(Events\EndEvent $event): void
{
if ($this->output->isDebug()) {
- $signature = $this->getMethodSignature($event->method);
+ $signature = reflection()->getMethodSignature($event->method);
$this->output->writeln("Check '$signature' ended");
return;
@@ -145,11 +143,11 @@ public function onEndAll(Events\EndAllEvent $event): void
foreach ($failures as $index => $failure) {
$number = $index + 1;
- $signature = $this->getMethodSignature($failure->method);
+ $signature = reflection()->getMethodSignature($failure->method);
$this->output->writeln("$number) $signature");
$this->output->writeln('');
- $this->output->writeln('ARGS: ' . $this->repr($failure->args));
+ $this->output->writeln('ARGS: ' . repr($failure->args));
$this->output->writeln('');
// $inspector = new Inspector($failure->cause);
@@ -169,7 +167,7 @@ public function onEndAll(Events\EndAllEvent $event): void
foreach ($errors as $index => $error) {
$number = $index + 1;
- $signature = $this->getMethodSignature($error->method);
+ $signature = reflection()->getMethodSignature($error->method);
$this->output->writeln("$number) $signature");
$inspector = new Inspector($error->cause);
@@ -189,4 +187,16 @@ public function onEndAll(Events\EndAllEvent $event): void
: "OK $stats"
);
}
+
+ private function convertBytes(int $bytes): string
+ {
+ if ($bytes == 0) {
+ return '0.00 B';
+ }
+
+ $suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
+ $exponent = (int) \floor(\log($bytes, 1024));
+
+ return \sprintf('%.2f %s', \round($bytes / 1024 ** $exponent, 2), $suffixes[$exponent]);
+ }
}
diff --git a/src/Subscribers/JUnitReporter.php b/src/Subscribers/JUnitReporter.php
index 6cbb522..d83729d 100644
--- a/src/Subscribers/JUnitReporter.php
+++ b/src/Subscribers/JUnitReporter.php
@@ -13,13 +13,16 @@
use Datashaman\PHPCheck\CheckEvents;
use Datashaman\PHPCheck\Events;
+use function Datashaman\PHPCheck\{
+ app
+};
use SimpleXMLElement;
-class JUnitReporter extends Reporter
+class JUnitReporter extends Subscriber
{
- protected $testsuite;
+ private $testsuite;
- protected $testcase;
+ private $testcase;
public static function getSubscribedEvents(): array
{
@@ -69,6 +72,6 @@ public function onFailure(Events\FailureEvent $event): void
public function onEndAll(): void
{
- $this->testsuite->asXML($this->input->getOption('log-junit'));
+ $this->testsuite->asXML(app('runner')->getInput()->getOption('log-junit'));
}
}
diff --git a/src/Subscribers/Reporter.php b/src/Subscribers/Reporter.php
deleted file mode 100644
index 7c39673..0000000
--- a/src/Subscribers/Reporter.php
+++ /dev/null
@@ -1,16 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-namespace Datashaman\PHPCheck\Subscribers;
-
-abstract class Reporter extends Subscriber
-{
-}
diff --git a/src/Subscribers/Subscriber.php b/src/Subscribers/Subscriber.php
index 830f791..2358a2b 100644
--- a/src/Subscribers/Subscriber.php
+++ b/src/Subscribers/Subscriber.php
@@ -9,27 +9,8 @@
*/
namespace Datashaman\PHPCheck\Subscribers;
-use function Datashaman\PHPCheck\reflection;
-
-use ReflectionMethod;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
abstract class Subscriber implements EventSubscriberInterface
{
- protected function getMethodSignature(ReflectionMethod $method): string
- {
- return reflection()->getMethodSignature($method);
- }
-
- protected function convertBytes(int $bytes): string
- {
- if ($bytes == 0) {
- return '0.00 B';
- }
-
- $suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
- $exponent = (int) \floor(\log($bytes, 1024));
-
- return \sprintf('%.2f %s', \round($bytes / 1024 ** $exponent, 2), $suffixes[$exponent]);
- }
}
diff --git a/src/Subscribers/Tabulator.php b/src/Subscribers/Tabulator.php
index 12f1cea..44fa4b0 100644
--- a/src/Subscribers/Tabulator.php
+++ b/src/Subscribers/Tabulator.php
@@ -13,22 +13,21 @@
use function Datashaman\PHPCheck\app;
use Datashaman\PHPCheck\CheckEvents;
-
use function Datashaman\PHPCheck\evalWithArgs;
use Datashaman\PHPCheck\Events;
-use Datashaman\PHPCheck\Traits\LogTrait;
+
+use function Datashaman\PHPCheck\reflection;
+use function Datashaman\PHPCheck\repr;
use Ds\Map;
use Exception;
class Tabulator extends Subscriber
{
- use LogTrait;
-
- protected $names;
+ private $names;
- protected $tables;
+ private $tables;
- protected $stats;
+ private $stats;
public static function getSubscribedEvents(): array
{
@@ -122,7 +121,7 @@ public function onEndAll(Events\EndAllEvent $event): void
$output->writeln('');
foreach ($this->tables->keys() as $index => $method) {
- $output->writeln($index + 1 . ') ' . $this->getMethodSignature($method));
+ $output->writeln($index + 1 . ') ' . reflection()->getMethodSignature($method));
$output->writeln('');
['tables' => $tables, 'tags' => $tags] = $this->tables->get($method);
@@ -164,13 +163,13 @@ function ($value, $count) use ($total) {
\preg_replace(
'/(^\[|\]$)/',
'',
- $this->repr($value)
+ repr($value)
)
)
);
if ($cover->hasKey($value)) {
- $expected = $cover[$value];
+ $expected = $cover->get($value);
if ($percentage < $expected) {
$warnings[] = [$value, $expected, $percentage];
@@ -189,7 +188,7 @@ function ($value, $count) use ($total) {
"Table '%s' had only %.1f%% %s, but expected %.1f%%",
$label,
$percentage,
- $this->repr($value),
+ repr($value),
$expected
)
);
diff --git a/src/Subscribers/TextReporter.php b/src/Subscribers/TextReporter.php
index 1208446..bbfaeb6 100644
--- a/src/Subscribers/TextReporter.php
+++ b/src/Subscribers/TextReporter.php
@@ -9,10 +9,12 @@
*/
namespace Datashaman\PHPCheck\Subscribers;
+use function Datashaman\PHPCheck\app;
use Datashaman\PHPCheck\CheckEvents;
use Datashaman\PHPCheck\Events;
+use function Datashaman\PHPCheck\reflection;
-class TextReporter extends Reporter
+class TextReporter extends Subscriber
{
protected $file;
@@ -35,7 +37,7 @@ public function __call(string $name, array $args): void
public function onStartAll(Events\StartAllEvent $event): void
{
- $this->file = \fopen($this->input->getOption('log-text'), 'a');
+ $this->file = \fopen(app('runner')->getInput()->getOption('log-text'), 'a');
$this->report('onStartAll', $event);
}
diff --git a/src/Traits/LogTrait.php b/src/Traits/LogTrait.php
deleted file mode 100644
index 7847e33..0000000
--- a/src/Traits/LogTrait.php
+++ /dev/null
@@ -1,96 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-namespace Datashaman\PHPCheck\Traits;
-
-use Ds\Map;
-
-trait LogTrait
-{
- public function repr($value)
- {
- if ($value instanceof Map) {
- return \get_class($value) . ' {#' . \spl_object_id($value) . '}';
- }
-
- if (\is_string($value)) {
- return '"' . $value . '"';
- }
-
- if (\is_numeric($value)) {
- return $value;
- }
-
- if (\is_array($value)) {
- if (\count($value)) {
- $keys = \array_keys($value);
-
- if (\is_int($keys[0])) {
- return '[' . \implode(', ', \array_map(
- function ($item) use ($value) {
- return $this->repr($item);
- },
- $value
- )) . ']';
- } else {
- return '[' . \implode(', ', \array_map(
- function ($key) use ($value) {
- return "$key=" . $this->repr($value[$key]);
- },
- $keys
- )) . ']';
- }
- } else {
- return '[]';
- }
- }
-
- return \json_encode($value);
- }
-
- public function logExecution($subject, $method, $values = null): void
- {
- static $counter = 0;
- static $lastArgs;
-
- if (\getenv('APP_ENV') !== 'dev') {
- return;
- }
-
- $parts = [];
-
- if (\is_array($values)) {
- foreach ($values as $key => $value) {
- if (\is_int($key)) {
- $parts[] = $this->repr($value);
- } else {
- $parts[] = $key . '=' . $this->repr($value);
- }
- }
- } else {
- $parts = null === $values
- ? []
- : [$this->repr($values)];
- }
-
- if (isset($lastArgs)) {
- if (\func_get_args() == $lastArgs) {
- $counter++;
-
- return;
- }
- $counter++;
- $times = $counter > 1 ? "$counter times" : '';
- \file_put_contents(LOG, $subject . ' ' . $method . '(' . ($parts ? \implode(', ', $parts) : '') . ") $times\n", \FILE_APPEND);
- $counter = 0;
- }
-
- $lastArgs = \func_get_args();
- }
-}
diff --git a/src/bootstrap.php b/src/bootstrap.php
index 769156b..57bce15 100644
--- a/src/bootstrap.php
+++ b/src/bootstrap.php
@@ -50,7 +50,7 @@
$c['dispatcher']->addSubscriber($c['state']);
$c['dispatcher']->addSubscriber($c['tabulator']);
- $c['dispatcher']->addSubscriber(new ConsoleReporter($runner));
+ $c['dispatcher']->addSubscriber(new ConsoleReporter());
return $runner;
});
diff --git a/src/generators.php b/src/generators.php
index bc9bf5c..f660319 100644
--- a/src/generators.php
+++ b/src/generators.php
@@ -15,6 +15,7 @@
use DateTime;
use DateTimeZone;
use Ds\Map;
+use Exception;
use Generator;
use Webmozart\Assert\Assert;
@@ -369,7 +370,7 @@ function () use ($attr, $args, $faker) {
);
}
-function floats(float $min, float $max): Generator
+function floats(float $min = \PHP_FLOAT_MIN, float $max = \PHP_FLOAT_MAX): Generator
{
logExecution('mkGen', 'floats', [$min, $max]);
@@ -401,7 +402,35 @@ function frequency(array $frequencies): Generator
$map->put($gen, $weighted);
}
- return pick($map);
+ return makeGen(
+ function (Random $r) use ($map) {
+ $count = $map->count();
+
+ if ($count <= 1) {
+ return $map->keys()->first();
+ }
+
+ $sum = $map->sum();
+
+ if ($sum < 1) {
+ throw new Exception('Negative or all-zero weights not allowed');
+ }
+
+ $targetWeight = $r->random(1, $sum);
+
+ foreach ($map as $key => $weight) {
+ if ($weight < 0) {
+ throw new Exception('Negative weights not allowed');
+ }
+
+ $targetWeight -= $weight;
+
+ if ($targetWeight <= 0) {
+ return $key;
+ }
+ }
+ }
+ );
}
function growingElements(array $array): Generator
@@ -537,17 +566,6 @@ function (Random $r) use ($gens) {
);
}
-function pick(Map $weighted): Generator
-{
- logExecution('mkGen', 'pick', $weighted);
-
- return makeGen(
- function (Random $r) use ($weighted) {
- return $r->arrayWeightRand($weighted);
- }
- );
-}
-
function resize(int $n, Generator $gen): Generator
{
logExecution('mkGen', 'resize', [$n, $gen]);
@@ -667,7 +685,7 @@ function suchThatMaybe(Generator $gen, callable $f): Generator
};
return makeGen(
- function (Random $r, $n) use ($gen, $try) {
+ function (Random $r, $n) use ($try) {
return $try($n, $n * 2, $r);
}
);
@@ -689,7 +707,7 @@ function (Random $r) use ($positions, $zones) {
);
}
-function variant(int $seed, Generator $gen): Generator
+function variant(string $seed, Generator $gen): Generator
{
logExecution('mkGen', 'variant', [$seed, $gen]);
diff --git a/src/helpers.php b/src/helpers.php
index a2053eb..0a1906f 100644
--- a/src/helpers.php
+++ b/src/helpers.php
@@ -9,6 +9,8 @@
*/
namespace Datashaman\PHPCheck;
+use Ds\Map;
+
\define('LOG', 'phpcheck.log');
function app($name, callable $f = null)
@@ -65,7 +67,7 @@ function repr($value)
if (\is_int($keys[0])) {
return '[' . \implode(', ', \array_map(
- function ($item) use ($value) {
+ function ($item) {
return repr($item);
},
$value