From 4d512228d01bde364e853bcf77f5015d974cda7c Mon Sep 17 00:00:00 2001 From: Petr Levtonov Date: Sun, 1 Mar 2020 20:51:30 +0100 Subject: [PATCH 1/2] #12: Comply with phpstan max level --- .gitattributes | 1 + .phan/config.php | 7 +- CHANGELOG.md | 4 + composer.json | 8 +- phpstan.neon.dist | 4 +- src/Batch/BatchJob.php | 74 ++++++++++---- src/Batch/BatchRunner.php | 39 +++++--- src/Batch/Strategy/AbstractStrategy.php | 2 +- src/Batch/Strategy/CallbackStrategy.php | 13 +-- src/Batch/Strategy/ChunkStrategy.php | 11 ++- src/Batch/Strategy/StrategyInterface.php | 8 +- src/Batch/Strategy/ThrottleStrategy.php | 17 +++- src/Deferred/Deferred.php | 77 ++++++++------- src/Deferred/DeferredAggregate.php | 31 ++++-- src/Deferred/DeferredInterface.php | 21 ++-- src/Deferred/PromiseInterface.php | 24 ++--- .../WrappedEventDispatcher.php | 39 ++++---- src/Exception/ForkException.php | 19 +++- src/Exception/ProcessControlException.php | 4 +- src/Exception/UnexpectedTypeException.php | 8 +- src/Factory.php | 8 +- src/Fork.php | 99 +++++++++++++------ src/ProcessManager.php | 47 ++++++--- src/SharedMemory.php | 25 +++-- src/Util/Error.php | 85 ++++++++-------- src/Util/ExitMessage.php | 37 +++++-- src/Util/ThrottleIterator.php | 60 +++++++---- tests/Batch/Strategy/ChunkStrategyTest.php | 11 ++- tests/Deferred/DeferredAggregateTest.php | 16 +-- tests/Deferred/DeferredTest.php | 53 ++++------ .../SignalEventDispatcherTest.php | 9 +- .../SignalEventDispatcherTestTrait.php | 13 ++- .../EventDispatcher/SignalEventSubscriber.php | 7 +- .../WrappedEventDispatcherTest.php | 10 +- tests/ProcessManagerTest.php | 28 +++--- tests/Signal/SignalHandlerWrapperTest.php | 2 +- tests/Util/ThrottleIteratorStub.php | 13 ++- tests/Util/ThrottleIteratorTest.php | 3 +- 38 files changed, 596 insertions(+), 341 deletions(-) diff --git a/.gitattributes b/.gitattributes index 6cafeb8..17e341a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -7,3 +7,4 @@ /phpstan.neon.dist export-ignore /phpunit.xml.dist export-ignore /tests export-ignore +/phan export-ignore diff --git a/.phan/config.php b/.phan/config.php index a4e9bf0..edc0ae3 100644 --- a/.phan/config.php +++ b/.phan/config.php @@ -49,7 +49,7 @@ // (See `backward_compatibility_checks` for additional options) // Automatically inferred from composer.json requirement for "php" of // "^7.2.0" - 'target_php_version' => '7.4', + 'target_php_version' => '7.2', // If enabled, missing properties will be created when // they are first seen. If false, we'll report an @@ -292,7 +292,10 @@ // Add any issue types (such as `'PhanUndeclaredMethod'`) // to this black-list to inhibit them from being reported. - 'suppress_issue_types' => [], + 'suppress_issue_types' => [ + 'PhanUnreferencedPublicMethod', + 'PhanUnreferencedClass', + ], // A regular expression to match files to be excluded // from parsing and analysis and will not be read at all. diff --git a/CHANGELOG.md b/CHANGELOG.md index 78a3222..8977885 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- #12: Fix all phpstan errors and general typing improvements. + ## [4.0.0] - 2020-02-09 ### Changed diff --git a/composer.json b/composer.json index 03e5835..d453016 100644 --- a/composer.json +++ b/composer.json @@ -43,8 +43,8 @@ "ext-posix": "*", "ext-shmop": "*", "friendsofphp/php-cs-fixer": "^2.16", - "phan/phan": "^2.4", - "phpstan/phpstan": "^0.12.4", + "phan/phan": "^2.6", + "phpstan/phpstan": "^0.12.18", "phpunit/phpunit": "^8.5", "squizlabs/php_codesniffer": "^3.5", "symfony/var-dumper": "^5.0" @@ -74,7 +74,8 @@ "/php_cs.dist", "/phpstan.neon.dist", "/phpunit.xml.dist", - "/tests" + "/tests", + "/phan" ] }, "config": { @@ -91,6 +92,7 @@ "cs": "vendor/bin/phpcs --standard=PSR12 src/ tests/", "csf": "vendor/bin/php-cs-fixer fix", "static": "vendor/bin/phpstan analyse", + "phan": "vendor/bin/phan --no-progress-bar", "test": "vendor/bin/phpunit", "coverage": "vendor/bin/phpunit --coverage-html coverage" } diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 514c28f..4cc0828 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,6 +1,6 @@ parameters: - level: 5 + level: max paths: - src - - tests inferPrivatePropertyTypeFromConstructor: true + treatPhpDocTypesAsCertain: false diff --git a/src/Batch/BatchJob.php b/src/Batch/BatchJob.php index e3b9835..dec05dc 100644 --- a/src/Batch/BatchJob.php +++ b/src/Batch/BatchJob.php @@ -15,87 +15,121 @@ use TheLevti\phpfork\Batch\Strategy\ChunkStrategy; use TheLevti\phpfork\Batch\Strategy\StrategyInterface; -use TheLevti\phpfork\Exception\UnexpectedTypeException; +use TheLevti\phpfork\Fork; use TheLevti\phpfork\ProcessManager; class BatchJob { - private $manager; + /** + * @var \TheLevti\phpfork\ProcessManager $processManager + */ + protected $processManager; + + /** + * @var mixed $data + */ private $data; + + /** + * @var \TheLevti\phpfork\Batch\Strategy\StrategyInterface $strategy + */ private $strategy; + + /** + * @var string $name + */ private $name; + + /** + * @var callable $callback + */ private $callback; - public function __construct(ProcessManager $manager, $data = null, StrategyInterface $strategy = null) + /** + * @param \TheLevti\phpfork\ProcessManager $processManager + * @param mixed|null $data + * @param \TheLevti\phpfork\Batch\Strategy\StrategyInterface|null $strategy + * @return void + */ + public function __construct(ProcessManager $processManager, $data = null, StrategyInterface $strategy = null) { - $this->manager = $manager; + $this->processManager = $processManager; $this->data = $data; $this->strategy = $strategy ?: new ChunkStrategy(); $this->name = ''; } - public function setName($name) + public function setName(string $name): self { $this->name = $name; return $this; } - public function setStrategy(StrategyInterface $strategy) + public function setStrategy(StrategyInterface $strategy): self { $this->strategy = $strategy; return $this; } - public function setData($data) + /** + * @param mixed $data + * @return \TheLevti\phpfork\Batch\BatchJob + */ + public function setData($data): self { $this->data = $data; return $this; } - public function setCallback($callback) + public function setCallback(callable $callback): self { - if (!is_callable($callback)) { - throw new UnexpectedTypeException($callback, 'callable'); - } - $this->callback = $callback; return $this; } - public function execute($callback = null) + public function execute(?callable $callback = null): Fork { if (null !== $callback) { $this->setCallback($callback); } - return $this->manager->fork($this)->setName($this->name . ' batch'); + return $this->processManager->fork($this)->setName($this->name . ' batch'); } /** * Runs in a child process. * - * @see execute() + * @see \TheLevti\phpfork\Batch\BatchJob::execute() + * + * @return array Result from the batch job. */ - public function __invoke() + public function __invoke(): array { + /** @var array $forks */ $forks = []; foreach ($this->strategy->createBatches($this->data) as $index => $batch) { - $forks[] = $this->manager + $forks[] = $this->processManager ->fork($this->strategy->createRunner($batch, $this->callback)) ->setName(sprintf('%s batch #%d', $this->name, $index)) ; } - // block until all forks have exited - $this->manager->wait(); + $this->processManager->wait(); + /** @var array $results */ $results = []; foreach ($forks as $fork) { - $results = array_merge($results, $fork->getResult()); + $batchResult = $fork->getResult(); + + if (is_array($batchResult)) { + $results = array_merge($results, $batchResult); + } else { + $results[] = $batchResult; + } } return $results; diff --git a/src/Batch/BatchRunner.php b/src/Batch/BatchRunner.php index 731e09f..bc5b4e3 100644 --- a/src/Batch/BatchRunner.php +++ b/src/Batch/BatchRunner.php @@ -13,12 +13,19 @@ namespace TheLevti\phpfork\Batch; -use TheLevti\phpfork\Exception\UnexpectedTypeException; use TheLevti\phpfork\SharedMemory; +use UnexpectedValueException; class BatchRunner { + /** + * @var array|callable $batch + */ private $batch; + + /** + * @var callable $callback + */ private $callback; /** @@ -28,28 +35,36 @@ class BatchRunner * * function($item, $index, $batch, $sharedMem) * - * @param mixed $batch The batch - * @param callable $callback The callback + * @param array|callable $batch + * @param callable $callback */ - public function __construct($batch, $callback) + public function __construct($batch, callable $callback) { - if (!is_callable($callback)) { - throw new UnexpectedTypeException($callback, 'callable'); - } - $this->batch = $batch; $this->callback = $callback; } - public function __invoke(SharedMemory $shm) + /** + * @param \TheLevti\phpfork\SharedMemory $shm + * @throws \UnexpectedValueException + * @return array + */ + public function __invoke(SharedMemory $shm): array { // lazy batch... - if ($this->batch instanceof \Closure) { - $this->batch = call_user_func($this->batch); + if (is_callable($this->batch)) { + /** @var array $batchArray */ + $batchArray = call_user_func($this->batch); + } elseif (is_array($this->batch)) { + /** @var array $batchArray */ + $batchArray = $this->batch; + } else { + throw new UnexpectedValueException('Batch is not an array nor a callable.'); } + /** @var array $results */ $results = []; - foreach ($this->batch as $index => $item) { + foreach ($batchArray as $index => $item) { $results[$index] = call_user_func($this->callback, $item, $index, $this->batch, $shm); } diff --git a/src/Batch/Strategy/AbstractStrategy.php b/src/Batch/Strategy/AbstractStrategy.php index e41b90e..9b36f58 100644 --- a/src/Batch/Strategy/AbstractStrategy.php +++ b/src/Batch/Strategy/AbstractStrategy.php @@ -17,7 +17,7 @@ abstract class AbstractStrategy implements StrategyInterface { - public function createRunner($batch, $callback) + public function createRunner($batch, $callback): callable { return new BatchRunner($batch, $callback); } diff --git a/src/Batch/Strategy/CallbackStrategy.php b/src/Batch/Strategy/CallbackStrategy.php index c9c598a..5653d6c 100644 --- a/src/Batch/Strategy/CallbackStrategy.php +++ b/src/Batch/Strategy/CallbackStrategy.php @@ -13,22 +13,19 @@ namespace TheLevti\phpfork\Batch\Strategy; -use TheLevti\phpfork\Exception\UnexpectedTypeException; - class CallbackStrategy extends AbstractStrategy { + /** + * @var callable $callback + */ private $callback; - public function __construct($callback) + public function __construct(callable $callback) { - if (!is_callable($callback)) { - throw new UnexpectedTypeException($callback, 'callable'); - } - $this->callback = $callback; } - public function createBatches($data) + public function createBatches($data): iterable { return call_user_func($this->callback, $data); } diff --git a/src/Batch/Strategy/ChunkStrategy.php b/src/Batch/Strategy/ChunkStrategy.php index c61d3c0..b68ae6b 100644 --- a/src/Batch/Strategy/ChunkStrategy.php +++ b/src/Batch/Strategy/ChunkStrategy.php @@ -20,16 +20,23 @@ */ class ChunkStrategy extends AbstractStrategy { + /** + * @var int $forks + */ private $forks; + + /** + * @var bool $preserveKeys + */ private $preserveKeys; - public function __construct($forks = 3, $preserveKeys = false) + public function __construct(int $forks = 3, bool $preserveKeys = false) { $this->forks = $forks; $this->preserveKeys = $preserveKeys; } - public function createBatches($data) + public function createBatches($data): iterable { if (!is_array($data) && !$data instanceof \Traversable) { throw new UnexpectedTypeException($data, 'array or Traversable'); diff --git a/src/Batch/Strategy/StrategyInterface.php b/src/Batch/Strategy/StrategyInterface.php index 34a7562..14c7580 100644 --- a/src/Batch/Strategy/StrategyInterface.php +++ b/src/Batch/Strategy/StrategyInterface.php @@ -23,9 +23,9 @@ interface StrategyInterface * * @param mixed $data The raw batch data * - * @return array|\Traversable An iterator of batches + * @return iterable An iterator of batches */ - public function createBatches($data); + public function createBatches($data): iterable; /** * Creates a batch runner for the supplied list. @@ -33,10 +33,10 @@ public function createBatches($data); * A batch runner is a callable that is passed to ProcessManager::fork() * that should run each item in the supplied batch through a callable. * - * @param mixed $batch A batch of items + * @param array|callable $batch A batch of items * @param callable $callback The batch callback * * @return callable A callable for the child process */ - public function createRunner($batch, $callback); + public function createRunner($batch, $callback): callable; } diff --git a/src/Batch/Strategy/ThrottleStrategy.php b/src/Batch/Strategy/ThrottleStrategy.php index 72036d7..eaf5d8b 100644 --- a/src/Batch/Strategy/ThrottleStrategy.php +++ b/src/Batch/Strategy/ThrottleStrategy.php @@ -17,28 +17,35 @@ class ThrottleStrategy implements StrategyInterface { + /** + * @var \TheLevti\phpfork\Batch\Strategy\StrategyInterface $delegate + */ private $delegate; + + /** + * @var int $threshold + */ private $threshold; - public function __construct(StrategyInterface $delegate, $threshold = 3) + public function __construct(StrategyInterface $delegate, int $threshold = 3) { $this->delegate = $delegate; $this->threshold = $threshold; } - public function createBatches($data) + public function createBatches($data): iterable { $batches = $this->delegate->createBatches($data); // wrap each batch in the throttle iterator - foreach ($batches as $i => $batch) { - $batches[$i] = new ThrottleIterator($batch, $this->threshold); + foreach ($batches as &$batch) { + $batch = new ThrottleIterator($batch, $this->threshold); } return $batches; } - public function createRunner($batch, $callback) + public function createRunner($batch, $callback): callable { return $this->delegate->createRunner($batch, $callback); } diff --git a/src/Deferred/Deferred.php b/src/Deferred/Deferred.php index ba85fa4..29b5291 100644 --- a/src/Deferred/Deferred.php +++ b/src/Deferred/Deferred.php @@ -13,20 +13,38 @@ namespace TheLevti\phpfork\Deferred; -use TheLevti\phpfork\Exception\UnexpectedTypeException; +use LogicException; class Deferred implements DeferredInterface { - /** @var string $state */ + /** + * @var string $state + */ private $state; - /** @var array $progressCallbacks */ + + /** + * @var array $progressCallbacks + */ private $progressCallbacks; - /** @var array $alwaysCallbacks */ + + /** + * @var array $alwaysCallbacks + */ private $alwaysCallbacks; - /** @var array $doneCallbacks */ + + /** + * @var array $doneCallbacks + */ private $doneCallbacks; - /** @var array $failCallbacks */ + + /** + * @var array $failCallbacks + */ private $failCallbacks; + + /** + * @var array $callbackArgs + */ private $callbackArgs; public function __construct() @@ -39,28 +57,20 @@ public function __construct() $this->failCallbacks = []; } - public function getState() + public function getState(): string { return $this->state; } - public function progress($progress) + public function progress(callable $progress): PromiseInterface { - if (!is_callable($progress)) { - throw new UnexpectedTypeException($progress, 'callable'); - } - $this->progressCallbacks[] = $progress; return $this; } - public function always($always) + public function always(callable $always): PromiseInterface { - if (!is_callable($always)) { - throw new UnexpectedTypeException($always, 'callable'); - } - switch ($this->state) { case DeferredInterface::STATE_PENDING: $this->alwaysCallbacks[] = $always; @@ -73,12 +83,8 @@ public function always($always) return $this; } - public function done($done) + public function done(callable $done): PromiseInterface { - if (!is_callable($done)) { - throw new UnexpectedTypeException($done, 'callable'); - } - switch ($this->state) { case DeferredInterface::STATE_PENDING: $this->doneCallbacks[] = $done; @@ -90,12 +96,8 @@ public function done($done) return $this; } - public function fail($fail) + public function fail(callable $fail): PromiseInterface { - if (!is_callable($fail)) { - throw new UnexpectedTypeException($fail, 'callable'); - } - switch ($this->state) { case DeferredInterface::STATE_PENDING: $this->failCallbacks[] = $fail; @@ -108,24 +110,23 @@ public function fail($fail) return $this; } - public function then($done, $fail = null) + public function then(callable $done, callable $fail = null): PromiseInterface { $this->done($done); - if ($fail) { + if (is_callable($fail)) { $this->fail($fail); } return $this; } - public function notify() + public function notify(...$args): DeferredInterface { if (DeferredInterface::STATE_PENDING !== $this->state) { - throw new \LogicException('Cannot notify a deferred object that is no longer pending'); + throw new LogicException('Cannot notify a deferred object that is no longer pending'); } - $args = func_get_args(); foreach ($this->progressCallbacks as $func) { call_user_func_array($func, $args); } @@ -133,10 +134,10 @@ public function notify() return $this; } - public function resolve() + public function resolve(...$args): DeferredInterface { if (DeferredInterface::STATE_REJECTED === $this->state) { - throw new \LogicException('Cannot resolve a deferred object that has already been rejected'); + throw new LogicException('Cannot resolve a deferred object that has already been rejected'); } if (DeferredInterface::STATE_RESOLVED === $this->state) { @@ -144,7 +145,7 @@ public function resolve() } $this->state = DeferredInterface::STATE_RESOLVED; - $this->callbackArgs = func_get_args(); + $this->callbackArgs = $args; while ($func = array_shift($this->alwaysCallbacks)) { call_user_func_array($func, $this->callbackArgs); @@ -157,10 +158,10 @@ public function resolve() return $this; } - public function reject() + public function reject(...$args): DeferredInterface { if (DeferredInterface::STATE_RESOLVED === $this->state) { - throw new \LogicException('Cannot reject a deferred object that has already been resolved'); + throw new LogicException('Cannot reject a deferred object that has already been resolved'); } if (DeferredInterface::STATE_REJECTED === $this->state) { @@ -168,7 +169,7 @@ public function reject() } $this->state = DeferredInterface::STATE_REJECTED; - $this->callbackArgs = func_get_args(); + $this->callbackArgs = $args; while ($func = array_shift($this->alwaysCallbacks)) { call_user_func_array($func, $this->callbackArgs); diff --git a/src/Deferred/DeferredAggregate.php b/src/Deferred/DeferredAggregate.php index e779efc..b9ee1d2 100644 --- a/src/Deferred/DeferredAggregate.php +++ b/src/Deferred/DeferredAggregate.php @@ -17,12 +17,22 @@ class DeferredAggregate implements PromiseInterface { + /** + * @var array $children + */ private $children; + + /** + * @var \TheLevti\phpfork\Deferred\Deferred $delegate + */ private $delegate; + /** + * @param array $children + * @throws UnexpectedTypeException + */ public function __construct(array $children) { - // validate children foreach ($children as $child) { if (!$child instanceof PromiseInterface) { throw new UnexpectedTypeException($child, 'TheLevti\phpfork\Deferred\PromiseInterface'); @@ -41,52 +51,55 @@ public function __construct(array $children) $this->tick(); } - public function getState() + public function getState(): string { return $this->delegate->getState(); } - public function getChildren() + /** + * @return array + */ + public function getChildren(): array { return $this->children; } - public function progress($progress) + public function progress(callable $progress): PromiseInterface { $this->delegate->progress($progress); return $this; } - public function always($always) + public function always(callable $always): PromiseInterface { $this->delegate->always($always); return $this; } - public function done($done) + public function done(callable $done): PromiseInterface { $this->delegate->done($done); return $this; } - public function fail($fail) + public function fail(callable $fail): PromiseInterface { $this->delegate->fail($fail); return $this; } - public function then($done, $fail = null) + public function then(callable $done, callable $fail = null): PromiseInterface { $this->delegate->then($done, $fail); return $this; } - public function tick() + public function tick(): void { $pending = count($this->children); diff --git a/src/Deferred/DeferredInterface.php b/src/Deferred/DeferredInterface.php index 224c16f..3f795c3 100644 --- a/src/Deferred/DeferredInterface.php +++ b/src/Deferred/DeferredInterface.php @@ -19,10 +19,11 @@ interface DeferredInterface extends PromiseInterface * Notifies the promise of progress. Any arguments will be passed along to * the callbacks. * - * @throws \LogicException If the promise is not pending - * @return DeferredInterface The current promise + * @param mixed ...$args Arguments that are passed to the callbacks. + * @throws \LogicException If the promise is not pending + * @return \TheLevti\phpfork\Deferred\DeferredInterface The current promise */ - public function notify(); + public function notify(...$args): self; /** * Marks the current promise as successful. @@ -30,10 +31,11 @@ public function notify(); * Calls "always" callbacks first, followed by "done" callbacks. Any * arguments will be passed along to the callbacks * - * @throws \LogicException If the promise was previously rejected - * @return DeferredInterface The current promise + * @param mixed ...$args Arguments that are passed to the callbacks. + * @throws \LogicException If the promise was previously rejected + * @return \TheLevti\phpfork\Deferred\DeferredInterface The current promise */ - public function resolve(); + public function resolve(...$args): self; /** * Marks the current promise as failed. @@ -41,8 +43,9 @@ public function resolve(); * Calls "always" callbacks first, followed by "fail" callbacks. Any * arguments will be passed along to the callbacks. * - * @throws \LogicException If the promise was previously resolved - * @return DeferredInterface The current promise + * @param mixed ...$args Arguments that are passed to the callbacks. + * @throws \LogicException If the promise was previously resolved + * @return \TheLevti\phpfork\Deferred\DeferredInterface The current promise */ - public function reject(); + public function reject(...$args): self; } diff --git a/src/Deferred/PromiseInterface.php b/src/Deferred/PromiseInterface.php index e41ea85..b127a70 100644 --- a/src/Deferred/PromiseInterface.php +++ b/src/Deferred/PromiseInterface.php @@ -28,16 +28,16 @@ interface PromiseInterface * * @return string A promise state constant */ - public function getState(); + public function getState(): string; /** * Adds a callback to be called upon progress. * * @param callable $progress The callback * - * @return PromiseInterface The current promise + * @return \TheLevti\phpfork\Deferred\PromiseInterface The current promise */ - public function progress($progress); + public function progress(callable $progress): self; /** * Adds a callback to be called whether the promise is resolved or rejected. @@ -47,9 +47,9 @@ public function progress($progress); * * @param callable $always The callback * - * @return PromiseInterface The current promise + * @return \TheLevti\phpfork\Deferred\PromiseInterface The current promise */ - public function always($always); + public function always(callable $always): self; /** * Adds a callback to be called when the promise completes successfully. @@ -58,9 +58,9 @@ public function always($always); * * @param callable $done The callback * - * @return PromiseInterface The current promise + * @return \TheLevti\phpfork\Deferred\PromiseInterface The current promise */ - public function done($done); + public function done(callable $done): self; /** * Adds a callback to be called when the promise fails. @@ -69,17 +69,17 @@ public function done($done); * * @param callable $fail The callback * - * @return PromiseInterface The current promise + * @return \TheLevti\phpfork\Deferred\PromiseInterface The current promise */ - public function fail($fail); + public function fail(callable $fail): self; /** * Adds done and fail callbacks. * * @param callable $done The done callback - * @param callable $fail The fail callback + * @param callable|null $fail The fail callback * - * @return PromiseInterface The current promise + * @return \TheLevti\phpfork\Deferred\PromiseInterface The current promise */ - public function then($done, $fail = null); + public function then(callable $done, callable $fail = null): self; } diff --git a/src/EventDispatcher/WrappedEventDispatcher.php b/src/EventDispatcher/WrappedEventDispatcher.php index 77d12d0..60324d6 100644 --- a/src/EventDispatcher/WrappedEventDispatcher.php +++ b/src/EventDispatcher/WrappedEventDispatcher.php @@ -16,41 +16,30 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -/** - * Wraps another event dispatcher, adding signal handling capabilities to it. - */ class WrappedEventDispatcher implements SignalEventDispatcherInterface { use SignalEventDispatcherTrait; /** - * The wrapped event dispatcher. - * * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $delegate */ private $delegate; - /** - * Constructs a new instance of the WrappedEventDispatcher class. - * - * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $delegate - * The wrapped event dispatcher. - */ public function __construct(EventDispatcherInterface $delegate) { $this->delegate = $delegate; } - /** - * {@inheritDoc} - */ public function dispatch($event, string $eventName = null): object { return call_user_func([$this->delegate, 'dispatch'], $event, $eventName); } /** - * {@inheritDoc} + * @param string $eventName + * @param callable $listener + * @param int $priority + * @return mixed */ public function addListener($eventName, $listener, $priority = 0) { @@ -58,7 +47,8 @@ public function addListener($eventName, $listener, $priority = 0) } /** - * {@inheritDoc} + * @param EventSubscriberInterface $subscriber + * @return mixed */ public function addSubscriber(EventSubscriberInterface $subscriber) { @@ -66,7 +56,9 @@ public function addSubscriber(EventSubscriberInterface $subscriber) } /** - * {@inheritDoc} + * @param string $eventName + * @param callable $listener + * @return mixed */ public function removeListener($eventName, $listener) { @@ -74,7 +66,8 @@ public function removeListener($eventName, $listener) } /** - * {@inheritDoc} + * @param EventSubscriberInterface $subscriber + * @return mixed */ public function removeSubscriber(EventSubscriberInterface $subscriber) { @@ -82,7 +75,8 @@ public function removeSubscriber(EventSubscriberInterface $subscriber) } /** - * {@inheritDoc} + * @param string|null $eventName + * @return array */ public function getListeners($eventName = null) { @@ -90,7 +84,9 @@ public function getListeners($eventName = null) } /** - * {@inheritDoc} + * @param string $eventName + * @param callable $listener + * @return int|null */ public function getListenerPriority($eventName, $listener) { @@ -98,7 +94,8 @@ public function getListenerPriority($eventName, $listener) } /** - * {@inheritDoc} + * @param string|null $eventName + * @return bool */ public function hasListeners($eventName = null) { diff --git a/src/Exception/ForkException.php b/src/Exception/ForkException.php index 84b2f3b..bbabbee 100644 --- a/src/Exception/ForkException.php +++ b/src/Exception/ForkException.php @@ -13,18 +13,24 @@ namespace TheLevti\phpfork\Exception; +use RuntimeException; use TheLevti\phpfork\Util\Error; /** * Turns an error passed through shared memory into an exception. */ -class ForkException extends \RuntimeException +class ForkException extends RuntimeException { + /** @var string $name */ private $name; + + /** @var int $pid */ private $pid; + + /** @var \TheLevti\phpfork\Util\Error|null $error */ private $error; - public function __construct($name, $pid, Error $error = null) + public function __construct(string $name, int $pid, ?Error $error = null) { $this->name = $name; $this->pid = $pid; @@ -50,12 +56,17 @@ public function __construct($name, $pid, Error $error = null) } } - public function getPid() + public function getName(): string + { + return $this->name; + } + + public function getPid(): int { return $this->pid; } - public function getError() + public function getError(): ?Error { return $this->error; } diff --git a/src/Exception/ProcessControlException.php b/src/Exception/ProcessControlException.php index 3c8bbd0..3fcba31 100644 --- a/src/Exception/ProcessControlException.php +++ b/src/Exception/ProcessControlException.php @@ -13,6 +13,8 @@ namespace TheLevti\phpfork\Exception; -class ProcessControlException extends \RuntimeException +use RuntimeException; + +class ProcessControlException extends RuntimeException { } diff --git a/src/Exception/UnexpectedTypeException.php b/src/Exception/UnexpectedTypeException.php index b1ee9e5..5dafb80 100644 --- a/src/Exception/UnexpectedTypeException.php +++ b/src/Exception/UnexpectedTypeException.php @@ -13,8 +13,14 @@ namespace TheLevti\phpfork\Exception; -class UnexpectedTypeException extends \LogicException +use LogicException; + +class UnexpectedTypeException extends LogicException { + /** + * @param mixed $value + * @param mixed $expectedType + */ public function __construct($value, $expectedType) { parent::__construct(sprintf( diff --git a/src/Factory.php b/src/Factory.php index fe37da9..4543acf 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -21,11 +21,11 @@ class Factory /** * Creates a new batch job instance. * - * @param ProcessManager $manager The process manager - * @param null $data Data for the batch job - * @param StrategyInterface $strategy The strategy + * @param \TheLevti\phpfork\ProcessManager $manager The process manager. + * @param mixed|null $data Data for the batch job. + * @param \TheLevti\phpfork\Batch\Strategy\StrategyInterface|null $strategy The strategy. * - * @return BatchJob A new batch job instance + * @return \TheLevti\phpfork\Batch\BatchJob A new batch job instance */ public function createBatchJob(ProcessManager $manager, $data = null, StrategyInterface $strategy = null) { diff --git a/src/Fork.php b/src/Fork.php index 54bab64..b08f8bf 100644 --- a/src/Fork.php +++ b/src/Fork.php @@ -15,49 +15,62 @@ use TheLevti\phpfork\Deferred\Deferred; use TheLevti\phpfork\Deferred\DeferredInterface; +use TheLevti\phpfork\Deferred\PromiseInterface; use TheLevti\phpfork\Exception\ForkException; use TheLevti\phpfork\Exception\ProcessControlException; use TheLevti\phpfork\Util\ExitMessage; class Fork implements DeferredInterface { + /** @var \TheLevti\phpfork\Deferred\DeferredInterface $defer */ private $defer; + + /** @var int $pid */ private $pid; + + /** @var \TheLevti\phpfork\SharedMemory $shm */ private $shm; + + /** @var bool $debug */ private $debug; + + /** @var string $name */ private $name; + + /** @var int|null $status */ private $status; + + /** @var \TheLevti\phpfork\Util\ExitMessage|null $message */ private $message; /** @var array $messages */ private $messages; - public function __construct($pid, SharedMemory $shm, $debug = false) + public function __construct(int $pid, SharedMemory $shm, bool $debug = false) { $this->defer = new Deferred(); $this->pid = $pid; $this->shm = $shm; $this->debug = $debug; $this->name = ''; + $this->status = null; + $this->message = null; $this->messages = []; } - /** - * Assign a name to the current fork (useful for debugging). - */ - public function setName($name) + public function setName(string $name): self { $this->name = $name; return $this; } - public function getPid() + public function getPid(): int { return $this->pid; } - public function wait($hang = true) + public function wait(bool $hang = true): self { if ($this->isExited()) { return $this; @@ -77,7 +90,7 @@ public function wait($hang = true) /** * Processes a status value retrieved while waiting for this fork to exit. */ - public function processWaitStatus($status) + public function processWaitStatus(int $status): void { if ($this->isExited()) { throw new \LogicException('Cannot set status on an exited fork'); @@ -98,6 +111,9 @@ public function processWaitStatus($status) } } + /** + * @return array + */ public function receive(): array { foreach ($this->shm->receive() as $message) { @@ -111,7 +127,7 @@ public function receive(): array return $this->messages; } - public function kill($signal = SIGINT) + public function kill(int $signal = SIGINT): self { if (false === $this->shm->signal($signal)) { throw new ProcessControlException('Unable to send signal'); @@ -120,139 +136,160 @@ public function kill($signal = SIGINT) return $this; } + /** + * @return mixed|null + */ public function getResult() { if ($this->message) { return $this->message->getResult(); } + + return null; } + /** + * @return mixed|null + */ public function getOutput() { if ($this->message) { return $this->message->getOutput(); } + + return null; } + /** + * @return mixed|null + */ public function getError() { if ($this->message) { return $this->message->getError(); } + + return null; } + /** + * @return array + */ public function getMessages(): array { return $this->messages; } - public function isSuccessful() + public function isSuccessful(): bool { return 0 === $this->getExitStatus(); } - public function isExited() + public function isExited(): bool { return null !== $this->status && pcntl_wifexited($this->status); } - public function isStopped() + public function isStopped(): bool { return null !== $this->status && pcntl_wifstopped($this->status); } - public function isSignaled() + public function isSignaled(): bool { return null !== $this->status && pcntl_wifsignaled($this->status); } - public function getExitStatus() + public function getExitStatus(): ?int { if (null !== $this->status) { return pcntl_wexitstatus($this->status); } + + return null; } - public function getTermSignal() + public function getTermSignal(): ?int { if (null !== $this->status) { return pcntl_wtermsig($this->status); } + + return null; } - public function getStopSignal() + public function getStopSignal(): ?int { if (null !== $this->status) { return pcntl_wstopsig($this->status); } + + return null; } - public function getState() + public function getState(): string { return $this->defer->getState(); } - public function progress($progress): Fork + public function progress(callable $progress): PromiseInterface { $this->defer->progress($progress); return $this; } - public function always($always): Fork + public function always(callable $always): PromiseInterface { $this->defer->always($always); return $this; } - public function done($done): Fork + public function done(callable $done): PromiseInterface { $this->defer->done($done); return $this; } - public function fail($fail): Fork + public function fail(callable $fail): PromiseInterface { $this->defer->fail($fail); return $this; } - public function then($done, $fail = null): Fork + public function then(callable $done, callable $fail = null): PromiseInterface { $this->defer->then($done, $fail); return $this; } - public function notify(): Fork + public function notify(...$args): DeferredInterface { - $args = func_get_args(); array_unshift($args, $this); - call_user_func_array([$this->defer, 'notify'], array_values($args)); + call_user_func_array([$this->defer, 'notify'], $args); return $this; } - public function resolve(): Fork + public function resolve(...$args): DeferredInterface { - $args = func_get_args(); array_unshift($args, $this); - call_user_func_array([$this->defer, 'resolve'], array_values($args)); + call_user_func_array([$this->defer, 'resolve'], $args); return $this; } - public function reject(): Fork + public function reject(...$args): DeferredInterface { - $args = func_get_args(); array_unshift($args, $this); - call_user_func_array([$this->defer, 'reject'], array_values($args)); + call_user_func_array([$this->defer, 'reject'], $args); return $this; } diff --git a/src/ProcessManager.php b/src/ProcessManager.php index 6ee892b..13f8664 100644 --- a/src/ProcessManager.php +++ b/src/ProcessManager.php @@ -16,6 +16,7 @@ use Exception; use InvalidArgumentException; use Symfony\Contracts\EventDispatcher\Event; +use TheLevti\phpfork\Batch\BatchJob; use TheLevti\phpfork\Batch\Strategy\StrategyInterface; use TheLevti\phpfork\EventDispatcher\Events; use TheLevti\phpfork\EventDispatcher\SignalEventDispatcher; @@ -26,11 +27,19 @@ class ProcessManager { + /** @var \TheLevti\phpfork\EventDispatcher\SignalEventDispatcherInterface $dispatcher */ private $dispatcher; + + /** @var \TheLevti\phpfork\Factory $factory */ private $factory; + + /** @var bool $debug */ private $debug; + /** @var bool $zombieOkay */ private $zombieOkay; + + /** @var int|null $signal */ private $signal; /** @var array $forks */ @@ -39,12 +48,13 @@ class ProcessManager public function __construct( SignalEventDispatcherInterface $dispatcher = null, Factory $factory = null, - $debug = false + bool $debug = false ) { $this->dispatcher = $dispatcher ?: new SignalEventDispatcher(); $this->factory = $factory ?: new Factory(); $this->debug = $debug; $this->zombieOkay = false; + $this->signal = null; $this->forks = []; } @@ -60,27 +70,38 @@ public function __destruct() } } - public function getEventDispatcher() + public function getEventDispatcher(): SignalEventDispatcherInterface { return $this->dispatcher; } - public function setDebug($debug) + public function setDebug(bool $debug): void { $this->debug = $debug; } - public function zombieOkay($zombieOkay = true) + public function zombieOkay(bool $zombieOkay = true): void { $this->zombieOkay = $zombieOkay; } - public function createBatchJob($data = null, StrategyInterface $strategy = null) + /** + * @param mixed|null $data + * @param \TheLevti\phpfork\Batch\Strategy\StrategyInterface|null $strategy + * @return \TheLevti\phpfork\Batch\BatchJob + */ + public function createBatchJob($data = null, ?StrategyInterface $strategy = null): BatchJob { return $this->factory->createBatchJob($this, $data, $strategy); } - public function process($data, $callable, StrategyInterface $strategy = null) + /** + * @param mixed|null $data + * @param callable|null $callable + * @param \TheLevti\phpfork\Batch\Strategy\StrategyInterface|null $strategy + * @return \TheLevti\phpfork\Fork + */ + public function process($data = null, ?callable $callable = null, ?StrategyInterface $strategy = null): Fork { return $this->createBatchJob($data, $strategy)->execute($callable); } @@ -150,13 +171,13 @@ public function fork(callable $callable): Fork ); } - public function monitor($signal = SIGUSR1) + public function monitor(int $signal = SIGUSR1): void { $this->signal = $signal; $this->dispatcher->addSignalListener($signal, [$this, 'check']); } - public function check() + public function check(): void { foreach ($this->forks as $fork) { foreach ($fork->receive() as $message) { @@ -165,14 +186,14 @@ public function check() } } - public function wait($hang = true) + public function wait(bool $hang = true): void { foreach ($this->forks as $fork) { $fork->wait($hang); } } - public function waitForNext($hang = true) + public function waitForNext(bool $hang = true): ?Fork { if (-1 === $pid = pcntl_wait($status, ($hang ? WNOHANG : 0) | WUNTRACED)) { throw new ProcessControlException('Error while waiting for next fork to exit'); @@ -183,9 +204,11 @@ public function waitForNext($hang = true) return $this->forks[$pid]; } + + return null; } - public function waitFor($pid, $hang = true) + public function waitFor(int $pid, bool $hang = true): Fork { if (!isset($this->forks[$pid])) { throw new InvalidArgumentException('There is no fork with PID ' . $pid); @@ -197,7 +220,7 @@ public function waitFor($pid, $hang = true) /** * Sends a signal to all forks. */ - public function killAll($signal = SIGINT) + public function killAll(int $signal = SIGINT): void { foreach ($this->forks as $fork) { $fork->kill($signal); diff --git a/src/SharedMemory.php b/src/SharedMemory.php index f79faf0..d809b60 100644 --- a/src/SharedMemory.php +++ b/src/SharedMemory.php @@ -22,17 +22,20 @@ class SharedMemory { /** @var int $pid */ private $pid; - /** @var int|null $pid */ + + /** @var int|null $ppid */ private $ppid; + + /** @var int|null $signal */ private $signal; /** * Constructor. * - * @param integer $pid The child process id or null if this is the child - * @param integer $signal The signal to send after writing to shared memory + * @param int|null $pid The child process id or null if this is the child + * @param int|null $signal The signal to send after writing to shared memory */ - public function __construct($pid = null, $signal = null) + public function __construct(?int $pid = null, ?int $signal = null) { if (null === $pid) { // child @@ -105,7 +108,7 @@ public function receive() * @param int $pause The number of microseconds to pause after * signalling. */ - public function send($message, $signal = null, $pause = 500) + public function send($message, $signal = null, int $pause = 500): void { $messages = $this->receive(); if (!is_array($message)) { @@ -141,7 +144,15 @@ public function send($message, $signal = null, $pause = 500) return; } - $this->signal(null === $signal ? $this->signal : $signal); + if (null === $signal) { + $signal = $this->signal; + + if (null == $signal) { + return; + } + } + + $this->signal($signal); usleep($pause); } @@ -149,7 +160,7 @@ public function send($message, $signal = null, $pause = 500) /** * Sends a signal to the other process. */ - public function signal($signal) + public function signal(int $signal): bool { $pid = null === $this->ppid ? $this->pid : $this->ppid; diff --git a/src/Util/Error.php b/src/Util/Error.php index 74f0657..b71bd5a 100644 --- a/src/Util/Error.php +++ b/src/Util/Error.php @@ -13,79 +13,82 @@ namespace TheLevti\phpfork\Util; +use Exception; use Serializable; class Error implements Serializable { + /** @var string $class */ private $class; + + /** @var string $message */ private $message; + + /** @var string $file */ private $file; + + /** @var int $line */ private $line; - private $code; - public static function fromException(\Exception $e) - { - $flat = new self(); - $flat->setClass(get_class($e)); - $flat->setMessage($e->getMessage()); - $flat->setFile($e->getFile()); - $flat->setLine($e->getLine()); - $flat->setCode($e->getCode()); - - return $flat; - } + /** @var mixed $code */ + private $code; - public function getClass() + public static function fromException(Exception $exception): self { - return $this->class; + return new self( + get_class($exception), + $exception->getMessage(), + $exception->getFile(), + $exception->getLine(), + $exception->getCode() + ); } - public function setClass($class) + /** + * @param string $class + * @param string $message + * @param string $file + * @param int $line + * @param mixed $code + */ + public function __construct(string $class, string $message, string $file, int $line, $code) { $this->class = $class; + $this->message = $message; + $this->file = $file; + $this->line = $line; + $this->code = $code; } - public function getMessage() + public function getClass(): string { - return $this->message; + return $this->class; } - public function setMessage($message) + public function getMessage(): string { - $this->message = $message; + return $this->message; } - public function getFile() + public function getFile(): string { return $this->file; } - public function setFile($file) - { - $this->file = $file; - } - - public function getLine() + public function getLine(): int { return $this->line; } - public function setLine($line) - { - $this->line = $line; - } - + /** + * @return mixed + */ public function getCode() { return $this->code; } - public function setCode($code) - { - $this->code = $code; - } - - public function serialize() + public function serialize(): string { return serialize([ $this->class, @@ -96,7 +99,11 @@ public function serialize() ]); } - public function unserialize($str) + /** + * @param string $serialized + * @return void + */ + public function unserialize($serialized) { list( $this->class, @@ -104,6 +111,6 @@ public function unserialize($str) $this->file, $this->line, $this->code - ) = unserialize($str); + ) = unserialize($serialized); } } diff --git a/src/Util/ExitMessage.php b/src/Util/ExitMessage.php index dd57d40..95538da 100644 --- a/src/Util/ExitMessage.php +++ b/src/Util/ExitMessage.php @@ -15,41 +15,60 @@ class ExitMessage implements \Serializable { + /** @var mixed $result */ private $result; + + /** @var mixed $output */ private $output; + + /** @var \TheLevti\phpfork\Util\Error|null $error */ private $error; + /** + * @return mixed + */ public function getResult() { return $this->result; } - public function setResult($result) + /** + * @param mixed $result + * @return void + */ + public function setResult($result): void { $this->result = $result; } + /** + * @return mixed + */ public function getOutput() { return $this->output; } - public function setOutput($output) + /** + * @param mixed $output + * @return void + */ + public function setOutput($output): void { $this->output = $output; } - public function getError() + public function getError(): ?Error { return $this->error; } - public function setError(Error $error) + public function setError(Error $error): void { $this->error = $error; } - public function serialize() + public function serialize(): string { return serialize([ $this->result, @@ -58,12 +77,16 @@ public function serialize() ]); } - public function unserialize($str) + /** + * @param string $serialized + * @return void + */ + public function unserialize($serialized) { list( $this->result, $this->output, $this->error - ) = unserialize($str); + ) = unserialize($serialized); } } diff --git a/src/Util/ThrottleIterator.php b/src/Util/ThrottleIterator.php index 35b327f..388574a 100644 --- a/src/Util/ThrottleIterator.php +++ b/src/Util/ThrottleIterator.php @@ -13,20 +13,34 @@ namespace TheLevti\phpfork\Util; +use ArrayIterator; +use Iterator; +use IteratorAggregate; +use OuterIterator; use TheLevti\phpfork\Exception\UnexpectedTypeException; /** * Throttles iteration based on a system load threshold. */ -class ThrottleIterator implements \OuterIterator +class ThrottleIterator implements OuterIterator { + /** @var callable|iterable $inner */ private $inner; + + /** @var float $threshold */ private $threshold; + + /** @var int|null $lastThrottle */ private $lastThrottle; - public function __construct($inner, $threshold) + /** + * @param callable|iterable $inner + * @param float $threshold + * @throws UnexpectedTypeException + */ + public function __construct($inner, float $threshold) { - if (!is_callable($inner) && !is_array($inner) && !$inner instanceof \Traversable) { + if (!is_callable($inner) && !is_iterable($inner)) { throw new UnexpectedTypeException($inner, 'callable, array, or Traversable'); } @@ -36,31 +50,33 @@ public function __construct($inner, $threshold) /** * Attempts to lazily resolve the supplied inner to an instance of Iterator. + * + * @return Iterator */ - public function getInnerIterator() + public function getInnerIterator(): Iterator { if (is_callable($this->inner)) { - // callable $this->inner = call_user_func($this->inner); } if (is_array($this->inner)) { - // array - $this->inner = new \ArrayIterator($this->inner); - } elseif ($this->inner instanceof \IteratorAggregate) { - // IteratorAggregate - while ($this->inner instanceof \IteratorAggregate) { + $this->inner = new ArrayIterator($this->inner); + } elseif ($this->inner instanceof IteratorAggregate) { + while ($this->inner instanceof IteratorAggregate) { $this->inner = $this->inner->getIterator(); } } - if (!$this->inner instanceof \Iterator) { + if (!$this->inner instanceof Iterator) { throw new UnexpectedTypeException($this->inner, 'Iterator'); } return $this->inner; } + /** + * @return mixed + */ public function current() { // only throttle every 5s @@ -71,39 +87,49 @@ public function current() return $this->getInnerIterator()->current(); } + /** + * + * @return scalar + */ public function key() { return $this->getInnerIterator()->key(); } + /** + * @return void + */ public function next() { - return $this->getInnerIterator()->next(); + $this->getInnerIterator()->next(); } + /** + * @return void + */ public function rewind() { - return $this->getInnerIterator()->rewind(); + $this->getInnerIterator()->rewind(); } - public function valid() + public function valid(): bool { return $this->getInnerIterator()->valid(); } - protected function getLoad() + protected function getLoad(): float { list($load) = sys_getloadavg(); return $load; } - protected function sleep($period) + protected function sleep(int $period): void { sleep($period); } - private function throttle($period = 1) + private function throttle(int $period = 1): void { $this->lastThrottle = time(); diff --git a/tests/Batch/Strategy/ChunkStrategyTest.php b/tests/Batch/Strategy/ChunkStrategyTest.php index 30e61d5..4ef0e3d 100644 --- a/tests/Batch/Strategy/ChunkStrategyTest.php +++ b/tests/Batch/Strategy/ChunkStrategyTest.php @@ -19,8 +19,12 @@ class ChunkStrategyTest extends TestCase { /** * @dataProvider provideNumber + * + * @param int $number + * @param array $expectedCounts + * @return void */ - public function testChunkArray($number, $expectedCounts) + public function testChunkArray(int $number, array $expectedCounts): void { $strategy = new ChunkStrategy($number); $batches = $strategy->createBatches(range(1, 100)); @@ -34,7 +38,10 @@ public function testChunkArray($number, $expectedCounts) $this->assertEquals(count($expectedCounts), $batchesCount); } - public function provideNumber() + /** + * @return array>> + */ + public function provideNumber(): array { return [ [1, [100]], diff --git a/tests/Deferred/DeferredAggregateTest.php b/tests/Deferred/DeferredAggregateTest.php index 581f1cb..23b1321 100644 --- a/tests/Deferred/DeferredAggregateTest.php +++ b/tests/Deferred/DeferredAggregateTest.php @@ -18,15 +18,15 @@ class DeferredAggregateTest extends TestCase { - public function testInvalidChild() + public function testInvalidChild(): void { $this->expectException(UnexpectedTypeException::class); $this->expectDeprecationMessage('PromiseInterface'); - $defer = new DeferredAggregate(['asdf']); + new DeferredAggregate(['asdf']); } - public function testNoChildren() + public function testNoChildren(): void { $defer = new DeferredAggregate([]); @@ -38,7 +38,7 @@ public function testNoChildren() $this->assertEquals(['done'], $log); } - public function testResolvedChildren() + public function testResolvedChildren(): void { $child = new Deferred(); $child->resolve(); @@ -53,7 +53,7 @@ public function testResolvedChildren() $this->assertEquals(['done'], $log); } - public function testResolution() + public function testResolution(): void { $child1 = new Deferred(); $child2 = new Deferred(); @@ -74,7 +74,7 @@ public function testResolution() $this->assertEquals(['done'], $log); } - public function testRejection() + public function testRejection(): void { $child1 = new Deferred(); $child2 = new Deferred(); @@ -101,7 +101,7 @@ public function testRejection() $this->assertEquals(['fail'], $log); } - public function testNested() + public function testNested(): void { $child1a = new Deferred(); $child1b = new Deferred(); @@ -117,7 +117,7 @@ public function testNested() $this->assertEquals('resolved', $defer->getState()); } - public function testFail() + public function testFail(): void { $child = new Deferred(); $defer = new DeferredAggregate([$child]); diff --git a/tests/Deferred/DeferredTest.php b/tests/Deferred/DeferredTest.php index eba1494..6dfba32 100644 --- a/tests/Deferred/DeferredTest.php +++ b/tests/Deferred/DeferredTest.php @@ -15,10 +15,10 @@ use LogicException; use PHPUnit\Framework\TestCase; -use TheLevti\phpfork\Exception\UnexpectedTypeException; class DeferredTest extends TestCase { + /** @var \TheLevti\phpfork\Deferred\DeferredInterface $defer */ private $defer; protected function setUp(): void @@ -34,7 +34,7 @@ protected function tearDown(): void /** * @dataProvider getMethodAndKey */ - public function testCallbackOrder($method, $expected) + public function testCallbackOrder(string $method, string $expected): void { $log = []; @@ -62,7 +62,7 @@ public function testCallbackOrder($method, $expected) /** * @dataProvider getMethodAndKey */ - public function testThen($method, $expected) + public function testThen(string $method, string $expected): void { $log = []; @@ -80,7 +80,7 @@ public function testThen($method, $expected) /** * @dataProvider getMethod */ - public function testMultipleResolve($method) + public function testMultipleResolve(string $method): void { $log = []; @@ -97,7 +97,7 @@ public function testMultipleResolve($method) /** * @dataProvider getMethodAndInvalid */ - public function testInvalidResolve($method, $invalid) + public function testInvalidResolve(string $method, string $invalid): void { $this->expectException(LogicException::class); $this->expectExceptionMessage('that has already been'); @@ -109,7 +109,7 @@ public function testInvalidResolve($method, $invalid) /** * @dataProvider getMethodAndQueue */ - public function testAlreadyResolved($resolve, $queue, $expect = true) + public function testAlreadyResolved(string $resolve, string $queue, bool $expect = true): void { // resolve the object $this->defer->$resolve(); @@ -123,19 +123,9 @@ public function testAlreadyResolved($resolve, $queue, $expect = true) } /** - * @dataProvider getMethodAndInvalidCallback + * @return array> */ - public function testInvalidCallback($method, $invalid) - { - $this->expectException(UnexpectedTypeException::class); - $this->expectExceptionMessage('callable'); - - $this->defer->$method($invalid); - } - - // providers - - public function getMethodAndKey() + public function getMethodAndKey(): array { return [ ['resolve', 'done'], @@ -143,7 +133,10 @@ public function getMethodAndKey() ]; } - public function getMethodAndInvalid() + /** + * @return array> + */ + public function getMethodAndInvalid(): array { return [ ['resolve', 'reject'], @@ -151,7 +144,10 @@ public function getMethodAndInvalid() ]; } - public function getMethodAndQueue() + /** + * @return array> + */ + public function getMethodAndQueue(): array { return [ ['resolve', 'always'], @@ -163,19 +159,10 @@ public function getMethodAndQueue() ]; } - public function getMethodAndInvalidCallback() - { - return [ - ['always', 'foo!'], - ['always', ['foo!']], - ['done', 'foo!'], - ['done', ['foo!']], - ['fail', 'foo!'], - ['fail', ['foo!']], - ]; - } - - public function getMethod() + /** + * @return array> + */ + public function getMethod(): array { return [ ['resolve'], diff --git a/tests/EventDispatcher/SignalEventDispatcherTest.php b/tests/EventDispatcher/SignalEventDispatcherTest.php index 03b6024..8636bb5 100644 --- a/tests/EventDispatcher/SignalEventDispatcherTest.php +++ b/tests/EventDispatcher/SignalEventDispatcherTest.php @@ -53,7 +53,14 @@ protected function setUp(): void protected function tearDown(): void { - $this->processManager->getEventDispatcher()->removeSignalHandlerWrappers(); + $eventDispatcher = $this->processManager->getEventDispatcher(); + + if ( + $eventDispatcher instanceof SignalEventDispatcher || + $eventDispatcher instanceof WrappedEventDispatcher + ) { + $eventDispatcher->removeSignalHandlerWrappers(); + } pcntl_async_signals($this->async); $this->errorReporting = error_reporting($this->errorReporting); diff --git a/tests/EventDispatcher/SignalEventDispatcherTestTrait.php b/tests/EventDispatcher/SignalEventDispatcherTestTrait.php index a163732..903cdd4 100644 --- a/tests/EventDispatcher/SignalEventDispatcherTestTrait.php +++ b/tests/EventDispatcher/SignalEventDispatcherTestTrait.php @@ -23,7 +23,7 @@ */ trait SignalEventDispatcherTestTrait { - public function testSingleListenerOneSignal() + public function testSingleListenerOneSignal(): void { $signaled = false; @@ -185,10 +185,13 @@ function (SharedMemory $sharedMem) use (&$testSig) { $this->assertEquals(5, $sigOrig); $this->assertEquals(3, $sigNew); - $this->processManager - ->getEventDispatcher() - ->removeSignalHandlerWrappers() - ; + $eventDispatcher = $this->processManager->getEventDispatcher(); + if ( + $eventDispatcher instanceof SignalEventDispatcher || + $eventDispatcher instanceof WrappedEventDispatcher + ) { + $eventDispatcher->removeSignalHandlerWrappers(); + } $this->assertEquals( $origSigHandler, diff --git a/tests/EventDispatcher/SignalEventSubscriber.php b/tests/EventDispatcher/SignalEventSubscriber.php index 6369358..ba88488 100644 --- a/tests/EventDispatcher/SignalEventSubscriber.php +++ b/tests/EventDispatcher/SignalEventSubscriber.php @@ -17,14 +17,17 @@ class SignalEventSubscriber implements EventSubscriberInterface { - public static function getSubscribedEvents() + /** + * @return array> + */ + public static function getSubscribedEvents(): array { return [ SignalEvent::getEventName(SIGUSR1) => ['onSigusr1', -128], ]; } - public function onSigusr1() + public function onSigusr1(): void { // Do nothing. } diff --git a/tests/EventDispatcher/WrappedEventDispatcherTest.php b/tests/EventDispatcher/WrappedEventDispatcherTest.php index d1da603..da432bf 100644 --- a/tests/EventDispatcher/WrappedEventDispatcherTest.php +++ b/tests/EventDispatcher/WrappedEventDispatcherTest.php @@ -57,7 +57,13 @@ protected function setUp(): void protected function tearDown(): void { - $this->processManager->getEventDispatcher()->removeSignalHandlerWrappers(); + $eventDispatcher = $this->processManager->getEventDispatcher(); + if ( + $eventDispatcher instanceof SignalEventDispatcher || + $eventDispatcher instanceof WrappedEventDispatcher + ) { + $eventDispatcher->removeSignalHandlerWrappers(); + } pcntl_async_signals($this->async); $this->errorReporting = error_reporting($this->errorReporting); @@ -65,7 +71,7 @@ protected function tearDown(): void parent::tearDown(); } - public function testDelegate() + public function testDelegate(): void { $eventDispatcher = $this->processManager->getEventDispatcher(); $sigEventSubscriber = new SignalEventSubscriber(); diff --git a/tests/ProcessManagerTest.php b/tests/ProcessManagerTest.php index fd10c17..a4eaca5 100644 --- a/tests/ProcessManagerTest.php +++ b/tests/ProcessManagerTest.php @@ -22,7 +22,7 @@ class ProcessManagerTest extends TestCase /** * Process Manager object * - * @var ProcessManager + * @var \TheLevti\phpfork\ProcessManager $manager */ private $manager; @@ -36,7 +36,7 @@ protected function tearDown(): void unset($this->manager); } - public function testDoneCallbacks() + public function testDoneCallbacks(): void { $success = null; @@ -44,7 +44,9 @@ public function testDoneCallbacks() echo 'output'; return 'result'; - })->done(function () use (&$success) { + }); + + $fork->done(function () use (&$success) { $success = true; })->fail(function () use (&$success) { $success = false; @@ -57,13 +59,15 @@ public function testDoneCallbacks() $this->assertEquals('result', $fork->getResult()); } - public function testFailCallbacks() + public function testFailCallbacks(): void { $success = null; $fork = $this->manager->fork(function () { throw new \Exception('child error'); - })->done(function () use (&$success) { + }); + + $fork->done(function () use (&$success) { $success = true; })->fail(function () use (&$success) { $success = false; @@ -75,7 +79,7 @@ public function testFailCallbacks() $this->assertNotEmpty($fork->getError()); } - public function testObjectReturn() + public function testObjectReturn(): void { $mock = $this->getMockBuilder(stdClass::class)->setMethods(['__sleep'])->getMock(); $mock->method('__sleep')->willThrowException(new Exception("Hey, don\'t serialize me!")); @@ -90,7 +94,7 @@ public function testObjectReturn() $this->assertFalse($fork->isSuccessful()); } - public function testBatchProcessing() + public function testBatchProcessing(): void { $expected = range(100, 109); @@ -106,7 +110,7 @@ public function testBatchProcessing() /** * Test batch processing with return values containing a newline character */ - public function testBatchProcessingWithNewlineReturnValues() + public function testBatchProcessingWithNewlineReturnValues(): void { $range = range(100, 109); $expected = [ @@ -135,9 +139,9 @@ public function testBatchProcessingWithNewlineReturnValues() /** * Data provider for `testLargeBatchProcessing()` * - * @return array + * @return array> */ - public function batchProvider() + public function batchProvider(): array { return [ [10], @@ -155,11 +159,11 @@ public function batchProvider() * * @dataProvider batchProvider */ - public function testLargeBatchProcessing($rangeEnd) + public function testLargeBatchProcessing(int $rangeEnd): void { $expected = array_fill(0, $rangeEnd, null); - /** @var Fork $fork */ + /** @var \TheLevti\phpfork\Fork $fork */ $fork = $this->manager->process($expected, function ($item) { return $item; }); diff --git a/tests/Signal/SignalHandlerWrapperTest.php b/tests/Signal/SignalHandlerWrapperTest.php index d4985e1..a03fe69 100644 --- a/tests/Signal/SignalHandlerWrapperTest.php +++ b/tests/Signal/SignalHandlerWrapperTest.php @@ -17,7 +17,7 @@ class SignalHandlerWrapperTest extends TestCase { - public function testSignalHandlerWrapper() + public function testSignalHandlerWrapper(): void { $prevCalls = 0; $currCalls = 0; diff --git a/tests/Util/ThrottleIteratorStub.php b/tests/Util/ThrottleIteratorStub.php index f749715..fd0c694 100644 --- a/tests/Util/ThrottleIteratorStub.php +++ b/tests/Util/ThrottleIteratorStub.php @@ -15,15 +15,22 @@ class ThrottleIteratorStub extends ThrottleIterator { + /** + * @var array $loads + */ public $loads = []; + + /** + * @var array $sleeps + */ public $sleeps = []; - protected function getLoad() + protected function getLoad(): float { - return (int) array_shift($this->loads); + return (float)array_shift($this->loads); } - protected function sleep($period) + protected function sleep(int $period): void { $this->sleeps[] = $period; } diff --git a/tests/Util/ThrottleIteratorTest.php b/tests/Util/ThrottleIteratorTest.php index cf3d324..e88c4a3 100644 --- a/tests/Util/ThrottleIteratorTest.php +++ b/tests/Util/ThrottleIteratorTest.php @@ -17,6 +17,7 @@ class ThrottleIteratorTest extends TestCase { + /** @var \TheLevti\phpfork\Util\ThrottleIteratorStub $iterator */ private $iterator; protected function setUp(): void @@ -30,7 +31,7 @@ protected function tearDown(): void unset($this->iterator); } - public function testIteration() + public function testIteration(): void { iterator_to_array($this->iterator); $this->assertEquals([1, 2, 4], $this->iterator->sleeps); From 1bb98888aebf56d2eb0d8400209dd84aa247ea80 Mon Sep 17 00:00:00 2001 From: Petr Levtonov Date: Sat, 28 Mar 2020 20:20:29 +0100 Subject: [PATCH 2/2] Update CHANGELOG.md --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8977885..f7cde5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [5.0.0] - 2020-03-28 + ### Changed - #12: Fix all phpstan errors and general typing improvements. @@ -86,7 +88,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added progress callbacks to Deferred. - Added serializable objects for exit and error messages. -[Unreleased]: https://github.com/TheLevti/phpfork/compare/4.0.0...HEAD +[Unreleased]: https://github.com/TheLevti/phpfork/compare/5.0.0...HEAD +[5.0.0]: https://github.com/TheLevti/phpfork/releases/5.0.0 [4.0.0]: https://github.com/TheLevti/phpfork/releases/4.0.0 [3.0.0]: https://github.com/TheLevti/phpfork/releases/3.0.0 [2.0.2]: https://github.com/TheLevti/phpfork/releases/2.0.2