diff --git a/.env b/.env
new file mode 100644
index 00000000..949df516
--- /dev/null
+++ b/.env
@@ -0,0 +1 @@
+COMPOSE_FILE=compose.yaml:compose.dev.yaml
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..68c86bf2
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,90 @@
+name: CI Pipeline
+
+on:
+ push:
+ branches: [ "master" ]
+ pull_request:
+ branches: [ "master" ]
+ workflow_dispatch:
+ inputs:
+ run_long_tests:
+ description: 'Run long tests'
+ required: false
+ default: false
+ type: boolean
+
+permissions:
+ contents: read
+
+jobs:
+ setup:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
+ - name: Validate composer.json and composer.lock
+ run: composer validate --strict
+
+ - name: Cache Composer packages
+ id: composer-cache
+ uses: actions/cache@v3
+ with:
+ path: vendor
+ key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-php-
+
+ - name: Build with cache
+ run: |
+ docker buildx create --use
+ docker buildx build \
+ --cache-from type=gha \
+ --cache-to type=gha,mode=max \
+ --load \
+ --tag recruiter-php \
+ .
+
+ test-fast:
+ needs: setup
+ runs-on: ubuntu-latest
+ env:
+ COMPOSE_FILE: compose.yaml
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+ - name: Restore cache and build
+ run: |
+ docker buildx create --use
+ docker buildx build \
+ --cache-from type=gha \
+ --load \
+ --tag recruiter-php \
+ .
+ - name: Run fast tests
+ run: make test
+
+ test-long:
+ needs: setup
+ runs-on: ubuntu-latest
+ timeout-minutes: 90
+ if: github.event.inputs.run_long_tests == 'true'
+ env:
+ COMPOSE_FILE: compose.yaml
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+ - name: Restore cache and build
+ run: |
+ docker buildx create --use
+ docker buildx build \
+ --cache-from type=gha \
+ --load \
+ --tag recruiter-php \
+ .
+ - name: Run long tests
+ run: make test-long
diff --git a/.gitignore b/.gitignore
index ff2779ce..01beab6a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
-.*
+.*.cache
+phpunit.xml
composer.phar
composer.lock
vendor/
diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
new file mode 100644
index 00000000..d1055d88
--- /dev/null
+++ b/.php-cs-fixer.dist.php
@@ -0,0 +1,25 @@
+setRiskyAllowed(true)
+ ->setRules([
+ '@PSR12' => true,
+ '@Symfony' => true,
+ 'array_indentation' => true,
+ 'array_syntax' => ['syntax' => 'short'],
+ 'concat_space' => ['spacing' => 'one'],
+ /* 'declare_strict_types' => true, */
+ 'string_implicit_backslashes' => true,
+ 'list_syntax' => ['syntax' => 'short'],
+ 'multiline_whitespace_before_semicolons' => ['strategy' => 'new_line_for_chained_calls'],
+ 'ordered_imports' => true,
+ 'phpdoc_to_comment' => false,
+ 'trailing_comma_in_multiline' => ['elements' => ['arrays', 'arguments', 'parameters']],
+ 'visibility_required' => ['elements' => ['property', 'method', 'const']],
+ ])
+ ->setFinder(
+ PhpCsFixer\Finder::create()
+ ->in(__DIR__ . '/src')
+ ->in(__DIR__ . '/spec')
+ )
+;
diff --git a/Dockerfile b/Dockerfile
index 7214a211..2f486df5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,18 +1,41 @@
-FROM php:7.2-cli
+FROM php:8.4-cli AS base
-RUN apt-get update \
- && apt-get install -y mongodb git
+# Install system dependencies
+RUN apt-get update && apt-get install -y \
+ git \
+ unzip \
+ libssl-dev \
+ libcurl4-openssl-dev \
+ pkg-config \
+ && rm -rf /var/lib/apt/lists/*
+# Install MongoDB extension
RUN pecl install mongodb \
- && docker-php-ext-install bcmath pdo_mysql mbstring opcache pcntl \
- && docker-php-ext-enable mongodb
+ && docker-php-ext-enable mongodb \
+ && docker-php-ext-install -j$(nproc) \
+ bcmath \
+ pdo_mysql \
+ opcache \
+ pcntl
-RUN curl -sS https://getcomposer.org/installer | php && mv composer.phar /usr/local/bin/composer && composer global require hirak/prestissimo --no-plugins --no-scripts
+# Copy Composer from official image
+COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
+# Set working directory
WORKDIR /app
-COPY . /app
+FROM base AS code
-RUN composer install
+# Set environment variable for Composer
+ENV COMPOSER_ALLOW_SUPERUSER=1
-ENTRYPOINT /etc/init.d/mongodb start && vendor/bin/phpunit
+# Copy composer files
+COPY composer.json composer.lock* ./
+
+# Install dependencies including dev dependencies for testing
+RUN composer install --optimize-autoloader
+
+# Copy application code
+COPY . .
+
+CMD ["tail", "-f", "/dev/null"]
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..df96bd4d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,51 @@
+.PHONY: build up down test test-long phpstan rector fix-cs install update shell logs clean
+
+# Build the Docker image
+build:
+ docker compose build
+
+# Start the services
+up:
+ docker compose up -d
+
+# Stop the services
+down:
+ docker compose down
+
+# Install dependencies
+install:
+ docker compose run --rm php composer install
+
+# Update dependencies
+update:
+ docker compose run --rm php composer update
+
+# Run all tests except the long ones
+test: up
+ docker compose exec php vendor/bin/phpunit --exclude-group=long
+
+# Run long tests specifically
+test-long: up
+ docker compose exec php vendor/bin/phpunit --group=long
+
+phpstan: up
+ docker compose exec php vendor/bin/phpstan --memory-limit=2G
+
+rector: up
+ docker compose exec php vendor/bin/rector
+
+fix-cs: up
+ docker compose exec php vendor/bin/php-cs-fixer fix -v
+
+# Open a shell in the PHP container
+shell:
+ docker compose exec php bash
+
+# View logs
+logs:
+ docker compose logs -f php
+
+# Clean up containers and volumes
+clean:
+ docker compose down -v
+ docker compose rm -f
diff --git a/compose.dev.yaml b/compose.dev.yaml
new file mode 100644
index 00000000..e985c6b9
--- /dev/null
+++ b/compose.dev.yaml
@@ -0,0 +1,4 @@
+services:
+ php:
+ volumes:
+ - .:/app
diff --git a/compose.yaml b/compose.yaml
new file mode 100644
index 00000000..e0724f21
--- /dev/null
+++ b/compose.yaml
@@ -0,0 +1,19 @@
+services:
+ php:
+ build: .
+ working_dir: /app
+ environment:
+ - COMPOSER_ALLOW_SUPERUSER=1
+ - MONGODB_URI=mongodb://mongodb:27017/recruiter
+ depends_on:
+ - mongodb
+
+ mongodb:
+ image: mongo:8
+ container_name: recruiter_mongodb
+ restart: unless-stopped
+ volumes:
+ - mongodb_data:/data/db
+
+volumes:
+ mongodb_data:
diff --git a/composer.json b/composer.json
index 95fb02e3..3953b885 100644
--- a/composer.json
+++ b/composer.json
@@ -1,6 +1,7 @@
{
"name": "recruiterphp/recruiter",
"description": "Job Queue Manager: high performance, high volume, persistent, fault tolerant. 100% PHP/MongoDB, 100% Awesome",
+ "license": "MIT",
"type": "project",
"keywords": [
"job",
@@ -13,8 +14,6 @@
"manager",
"mongodb"
],
- "homepage": "https://github.com/recruiterphp/recruiter",
- "license": "MIT",
"authors": [
{
"name": "gabriele.lana",
@@ -25,44 +24,57 @@
"homepage": "https://github.com/recruiterphp/recruiter/graphs/contributors"
}
],
+ "homepage": "https://github.com/recruiterphp/recruiter",
"require": {
- "php": "~7.2",
+ "php": "^8.4",
+ "ext-bcmath": "*",
"ext-mongodb": ">=1.1",
- "alcaeus/mongo-php-adapter": "^1.1",
- "recruiterphp/geezer": "^5",
- "gabrielelana/byte-units": "~0.1",
- "monolog/monolog": ">=1",
- "recruiterphp/concurrency": "^3.0",
- "psr/log": "^1.0",
- "symfony/console": "^4.2",
- "symfony/event-dispatcher": "^3.4|^4.0",
- "ulrichsg/getopt-php": "~2.1",
- "mongodb/mongodb": "^1.4",
- "mtdowling/cron-expression": "^1.2"
- },
- "suggest": {
- "symfony/console": "In order to use Recruiter\\Command\\RecruiterJobCommand."
+ "ext-posix": "*",
+ "dragonmantank/cron-expression": "^3.4",
+ "gabrielelana/byte-units": "^0.5",
+ "mongodb/mongodb": "^2.1",
+ "monolog/monolog": "^3.9",
+ "psr/log": "^3.0",
+ "recruiterphp/concurrency": "^5.0",
+ "recruiterphp/geezer": "^7.0",
+ "symfony/console": "^7.3",
+ "symfony/event-dispatcher": "^7.3",
+ "ulrichsg/getopt-php": "^4.0"
},
"require-dev": {
- "phpunit/phpunit": "^8",
+ "ext-pcntl": "*",
+ "dms/phpunit-arraysubset-asserts": "^0.5",
+ "ergebnis/composer-normalize": "^2.47",
+ "friendsofphp/php-cs-fixer": "^3.85",
+ "giorgiosironi/eris": "^1.0",
"phpstan/phpstan": "*",
- "giorgiosironi/eris": "dev-master",
- "dms/phpunit-arraysubset-asserts": "^0.1.0"
+ "phpunit/phpunit": "^10.0",
+ "rector/rector": "^2.1",
+ "symfony/var-dumper": "^7.3"
+ },
+ "suggest": {
+ "symfony/console": "In order to use Recruiter\\Command\\RecruiterJobCommand."
},
"minimum-stability": "dev",
"prefer-stable": true,
- "bin": [
- "bin/recruiter"
- ],
"autoload": {
"psr-4": {
"Recruiter\\": "src/Recruiter",
- "Timeless\\": "src/Timeless",
- "Sink\\": "src/Sink"
+ "Sink\\": "src/Sink",
+ "Timeless\\": "src/Timeless"
},
"files": [
"src/Timeless/functions.php",
"src/Recruiter/functions.php"
]
+ },
+ "bin": [
+ "bin/recruiter"
+ ],
+ "config": {
+ "allow-plugins": {
+ "ergebnis/composer-normalize": true
+ },
+ "sort-packages": true
}
}
diff --git a/examples/bootstrap.php b/examples/bootstrap.php
index 5e278a1e..1911e47f 100644
--- a/examples/bootstrap.php
+++ b/examples/bootstrap.php
@@ -1,6 +1,12 @@
getEventDispatcher()->addListener('job.failure.last', function($event) {
- error_log("Job definitively failed: " . var_export($event->export(), true));
+
+assert(isset($recruiter));
+assert($recruiter instanceof Recruiter);
+
+$recruiter->getEventDispatcher()->addListener('job.failure.last', function ($event): void {
+ error_log('Job definitively failed: ' . var_export($event->export(), true));
});
diff --git a/examples/jobAndWorkerTagged.php b/examples/jobAndWorkerTagged.php
index 632e2238..d8795837 100755
--- a/examples/jobAndWorkerTagged.php
+++ b/examples/jobAndWorkerTagged.php
@@ -3,17 +3,16 @@
require __DIR__ . '/../vendor/autoload.php';
-use Recruiter\Recruiter;
use Recruiter\Factory;
+use Recruiter\Infrastructure\Memory\MemoryLimit;
+use Recruiter\Infrastructure\Persistence\Mongodb\URI as MongoURI;
+use Recruiter\Recruiter;
use Recruiter\Workable\LazyBones;
-use Recruiter\Worker;
-use Recruiter\Option\MemoryLimit;
$factory = new Factory();
$db = $factory->getMongoDb(
- $hosts = 'localhost:27017',
+ MongoURI::fromEnvironment(),
$options = [],
- $dbName = 'recruiter'
);
$db->drop();
@@ -23,9 +22,10 @@
->asJobOf($recruiter)
->inGroup('mail')
->inBackground()
- ->execute();
+ ->execute()
+;
-$memoryLimit = new MemoryLimit('memory-limit', '64MB');
+$memoryLimit = new MemoryLimit('64MB');
$worker = $recruiter->hire($memoryLimit);
$worker->workOnJobsGroupedAs('mail');
$assignments = $recruiter->assignJobsToWorkers();
diff --git a/examples/jobFailedBecauseOfNonRetriableException.php b/examples/jobFailedBecauseOfNonRetriableException.php
index a1e452c3..18e088bf 100755
--- a/examples/jobFailedBecauseOfNonRetriableException.php
+++ b/examples/jobFailedBecauseOfNonRetriableException.php
@@ -3,37 +3,37 @@
require __DIR__ . '/../vendor/autoload.php';
-use Timeless as T;
-
-use Recruiter\Recruiter;
use Recruiter\Factory;
+use Recruiter\Infrastructure\Memory\MemoryLimit;
+use Recruiter\Infrastructure\Persistence\Mongodb\URI as MongoURI;
+use Recruiter\Recruiter;
use Recruiter\Workable\AlwaysFail;
-use Recruiter\RetryPolicy;
-use Recruiter\Worker;
-use Recruiter\Option\MemoryLimit;
+use Timeless as T;
$factory = new Factory();
$db = $factory->getMongoDb(
- $hosts = 'localhost:27017',
+ MongoURI::fromEnvironment(),
$options = [],
- $dbName = 'recruiter'
);
$db->drop();
$recruiter = new Recruiter($db);
-(new AlwaysFail())
+new AlwaysFail()
->asJobOf($recruiter)
- ->retryManyTimes(5, T\seconds(1), 'DomainException')
+ ->retryManyTimes(5, T\seconds(1), DomainException::class)
->inBackground()
- ->execute();
+ ->execute()
+;
-$memoryLimit = new MemoryLimit('memory-limit', '64MB');
+$memoryLimit = new MemoryLimit('64MB');
$worker = $recruiter->hire($memoryLimit);
while (true) {
printf("Try to do my work\n");
$assignments = $recruiter->assignJobsToWorkers();
- if ($assignments === 0) break;
+ if (0 === $assignments) {
+ break;
+ }
$worker->work();
usleep(1200 * 1000);
}
diff --git a/examples/jobRetriedManyTimesUntilArchived.php b/examples/jobRetriedManyTimesUntilArchived.php
index e713e515..8972afcc 100755
--- a/examples/jobRetriedManyTimesUntilArchived.php
+++ b/examples/jobRetriedManyTimesUntilArchived.php
@@ -3,37 +3,37 @@
require __DIR__ . '/../vendor/autoload.php';
-use Timeless as T;
-
-use Recruiter\Recruiter;
use Recruiter\Factory;
+use Recruiter\Infrastructure\Memory\MemoryLimit;
+use Recruiter\Infrastructure\Persistence\Mongodb\URI as MongoURI;
+use Recruiter\Recruiter;
use Recruiter\Workable\AlwaysFail;
-use Recruiter\RetryPolicy;
-use Recruiter\Worker;
-use Recruiter\Option\MemoryLimit;
+use Timeless as T;
$factory = new Factory();
$db = $factory->getMongoDb(
- $hosts = 'localhost:27017',
+ MongoURI::fromEnvironment(),
$options = [],
- $dbName = 'recruiter'
);
$db->drop();
$recruiter = new Recruiter($db);
-(new AlwaysFail())
+new AlwaysFail()
->asJobOf($recruiter)
->retryManyTimes(5, T\second(1))
->inBackground()
- ->execute();
+ ->execute()
+;
-$memoryLimit = new MemoryLimit('memory-limit', '64MB');
+$memoryLimit = new MemoryLimit('64MB');
$worker = $recruiter->hire($memoryLimit);
while (true) {
printf("Try to do my work\n");
$assignments = $recruiter->assignJobsToWorkers();
- if ($assignments === 0) break;
+ if (0 === count($assignments)) {
+ break;
+ }
$worker->work();
usleep(1200 * 1000);
}
diff --git a/examples/oneTimeJob.php b/examples/oneTimeJob.php
index 16561ff9..54a15ecb 100755
--- a/examples/oneTimeJob.php
+++ b/examples/oneTimeJob.php
@@ -3,17 +3,16 @@
require __DIR__ . '/../vendor/autoload.php';
-use Recruiter\Recruiter;
use Recruiter\Factory;
+use Recruiter\Infrastructure\Memory\MemoryLimit;
+use Recruiter\Infrastructure\Persistence\Mongodb\URI as MongoURI;
+use Recruiter\Recruiter;
use Recruiter\Workable\LazyBones;
-use Recruiter\Worker;
-use Recruiter\Option\MemoryLimit;
$factory = new Factory();
$db = $factory->getMongoDb(
- $hosts = 'localhost:27017',
+ MongoURI::fromEnvironment(),
$options = [],
- $dbName = 'recruiter'
);
$db->drop();
@@ -22,9 +21,10 @@
LazyBones::waitForMs(200, 100)
->asJobOf($recruiter)
->inBackground()
- ->execute();
+ ->execute()
+;
-$memoryLimit = new MemoryLimit('memory-limit', '64MB');
+$memoryLimit = new MemoryLimit('64MB');
$worker = $recruiter->hire($memoryLimit);
$assignments = $recruiter->assignJobsToWorkers();
$worker->work();
diff --git a/phpstan.neon b/phpstan.neon
index 4e803c3c..2db8cff2 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -1,18 +1,13 @@
parameters:
- level: max
+ level: 2
paths:
- src
- excludes_analyse:
- - %currentWorkingDirectory%/vendor/
- autoload_directories:
- - %currentWorkingDirectory%/spec
+ - spec
+ scanDirectories:
+ - %currentWorkingDirectory%/vendor/giorgiosironi/eris/src/
ignoreErrors:
- - '#Instantiated class Recruiter\\Workable\\ThisClassDoesnNotExists not found.#'
- - '#Constructor of class Recruiter\\Workable\\FailsInConstructor has an unused parameter \$parameters.#'
- - '#Static call to instance method stdClass::import()#'
- - '#Call to function is_callable\(\) with .recruiter_stept_back. will always evaluate to false.#'
- - '#Call to function is_callable\(\) with .recruiter_becameā¦. will always evaluate to false.#'
- '#Function recruiter_became_master not found.#'
- '#Function recruiter_stept_back not found.#'
- - '#Call to an undefined method Traversable::toArray().#'
+ - '#Unsafe usage of new static\(\).#'
+ - '#to an undefined .* Sink\\BlackHole#'
inferPrivatePropertyTypeFromConstructor: true
diff --git a/phpunit.xml b/phpunit.dist.xml
similarity index 100%
rename from phpunit.xml
rename to phpunit.dist.xml
diff --git a/rector.php b/rector.php
new file mode 100644
index 00000000..bfef9bcc
--- /dev/null
+++ b/rector.php
@@ -0,0 +1,18 @@
+withPaths([
+ __DIR__ . '/examples',
+ __DIR__ . '/spec',
+ __DIR__ . '/src',
+ ])
+ // uncomment to reach your current PHP version
+ ->withPhpSets()
+ ->withTypeCoverageLevel(4)
+ ->withDeadCodeLevel(0)
+ ->withCodeQualityLevel(1)
+;
diff --git a/spec/Recruiter/Acceptance/AssignmentTest.php b/spec/Recruiter/Acceptance/AssignmentTest.php
index c7f08a3a..4c764bb7 100644
--- a/spec/Recruiter/Acceptance/AssignmentTest.php
+++ b/spec/Recruiter/Acceptance/AssignmentTest.php
@@ -1,21 +1,23 @@
asJobOf($this->recruiter)
->inBackground()
- ->execute();
+ ->execute()
+ ;
$worker = $this->recruiter->hire($memoryLimit);
- list ($assignments, $totalNumber) = $this->recruiter->assignJobsToWorkers();
+ [$assignments, $totalNumber] = $this->recruiter->assignJobsToWorkers();
$this->assertEquals(1, count($assignments));
$this->assertEquals(1, $totalNumber);
$this->assertTrue((bool) $worker->work());
diff --git a/spec/Recruiter/Acceptance/BaseAcceptanceTest.php b/spec/Recruiter/Acceptance/BaseAcceptanceTestCase.php
similarity index 69%
rename from spec/Recruiter/Acceptance/BaseAcceptanceTest.php
rename to spec/Recruiter/Acceptance/BaseAcceptanceTestCase.php
index 347be835..244b10ac 100644
--- a/spec/Recruiter/Acceptance/BaseAcceptanceTest.php
+++ b/spec/Recruiter/Acceptance/BaseAcceptanceTestCase.php
@@ -1,30 +1,37 @@
recruiterDb = $factory->getMongoDb(MongoURI::from('mongodb://localhost:27017/recruiter'), []);
+ $this->recruiterDb = $factory->getMongoDb(URI::fromEnvironment(), []);
$this->cleanDb();
$this->files = ['/tmp/recruiter.log', '/tmp/worker.log'];
$this->cleanLogs();
@@ -39,17 +46,17 @@ public function setUp(): void
$this->processWorkers = [];
}
- public function tearDown(): void
+ protected function tearDown(): void
{
$this->terminateProcesses(SIGKILL);
}
- protected function cleanDb()
+ protected function cleanDb(): void
{
$this->recruiterDb->drop();
}
- protected function clean()
+ protected function clean(): void
{
$this->terminateProcesses(SIGKILL);
$this->cleanLogs();
@@ -57,7 +64,7 @@ protected function clean()
$this->jobs = 0;
}
- public function cleanLogs()
+ public function cleanLogs(): void
{
foreach ($this->files as $file) {
if (file_exists($file)) {
@@ -66,21 +73,23 @@ public function cleanLogs()
}
}
- protected function numberOfWorkers()
+ protected function numberOfWorkers(): int
{
- return $this->roster->count();
+ return $this->roster->countDocuments();
}
- protected function waitForNumberOfWorkersToBe($expectedNumber, $howManySeconds = 1)
+ protected function waitForNumberOfWorkersToBe($expectedNumber, $howManySeconds = 1): void
{
Timeout::inSeconds($howManySeconds, "workers to be $expectedNumber")
->until(function () use ($expectedNumber) {
- $this->recruiter->retireDeadWorkers(new DateTimeImmutable(), T\seconds(0));
+ $this->recruiter->retireDeadWorkers(new \DateTimeImmutable(), T\seconds(0));
+
return $this->numberOfWorkers() == $expectedNumber;
- });
+ })
+ ;
}
- protected function startRecruiter()
+ protected function startRecruiter(): array
{
$descriptors = [
0 => ['pipe', 'r'],
@@ -92,17 +101,20 @@ protected function startRecruiter()
$process = proc_open('exec php bin/recruiter start:recruiter --backoff-to 5000ms --lease-time 10s --considered-dead-after 20s >> /tmp/recruiter.log 2>&1', $descriptors, $pipes, $cwd);
- Timeout::inSeconds(1, "recruiter to be up")
+ Timeout::inSeconds(1, 'recruiter to be up')
->until(function () use ($process) {
$status = proc_get_status($process);
+
return $status['running'];
- });
+ })
+ ;
$this->processRecruiter = [$process, $pipes, 'recruiter'];
+
return $this->processRecruiter;
}
- protected function startCleaner()
+ protected function startCleaner(): array
{
$descriptors = [
0 => ['pipe', 'r'],
@@ -111,11 +123,13 @@ protected function startCleaner()
];
$cwd = __DIR__ . '/../../../';
$process = proc_open('exec php bin/recruiter start:cleaner --wait-at-least=5s --wait-at-most=1m --lease-time 20s >> /tmp/cleaner.log 2>&1', $descriptors, $pipes, $cwd);
- Timeout::inSeconds(1, "cleaner to be up")
+ Timeout::inSeconds(1, 'cleaner to be up')
->until(function () use ($process) {
$status = proc_get_status($process);
+
return $status['running'];
- });
+ })
+ ;
$this->processCleaner = [$process, $pipes, 'cleaner'];
return $this->processCleaner;
@@ -138,67 +152,71 @@ protected function startWorker(array $additionalOptions = [])
$cwd = __DIR__ . '/../../../';
$process = proc_open("exec php bin/recruiter start:worker $options >> /tmp/worker.log 2>&1", $descriptors, $pipes, $cwd);
- Timeout::inSeconds(1, "worker to be up")
+ Timeout::inSeconds(1, 'worker to be up')
->until(function () use ($process) {
$status = proc_get_status($process);
+
return $status['running'];
- });
+ })
+ ;
// proc_get_status($process);
$this->processWorkers[] = [$process, $pipes, 'worker'];
+
return end($this->processWorkers);
}
- protected function stopProcessWithSignal(array $processAndPipes, $signal)
+ protected function stopProcessWithSignal(array $processAndPipes, int $signal): void
{
- list($process, $pipes, $name) = $processAndPipes;
+ [$process, $pipes, $name] = $processAndPipes;
proc_terminate($process, $signal);
$this->lastStatus = proc_get_status($process);
- Timeout
- ::inSeconds(30, function () use ($signal) {
- return 'termination of process: ' . var_export($this->lastStatus, true) . " after sending the `$signal` signal to it";
- })
+ Timeout::inSeconds(30, fn () => 'termination of process: ' . var_export($this->lastStatus, true) . " after sending the `$signal` signal to it")
->until(function () use ($process) {
$this->lastStatus = proc_get_status($process);
- return $this->lastStatus['running'] == false;
- });
+
+ return false == $this->lastStatus['running'];
+ })
+ ;
}
/**
- * @param integer $duration milliseconds
+ * @param int $duration milliseconds
*/
- protected function enqueueJob($duration = 10, $tag = 'generic')
+ protected function enqueueJob(int $duration = 10, $tag = 'generic'): void
{
- $workable = ShellCommand::fromCommandLine("sleep " . ($duration / 1000));
+ $workable = ShellCommand::fromCommandLine('sleep ' . ($duration / 1000));
$workable
->asJobOf($this->recruiter)
->inGroup($tag)
->inBackground()
- ->execute();
- $this->jobs++;
+ ->execute()
+ ;
+ ++$this->jobs;
}
- protected function enqueueJobWithRetryPolicy($duration = 10, RetryPolicy $retryPolicy)
+ protected function enqueueJobWithRetryPolicy(int $duration, RetryPolicy $retryPolicy): void
{
- $workable = ShellCommand::fromCommandLine("sleep " . ($duration / 1000));
+ $workable = ShellCommand::fromCommandLine('sleep ' . ($duration / 1000));
$workable
->asJobOf($this->recruiter)
->retryWithPolicy($retryPolicy)
->inBackground()
- ->execute();
- $this->jobs++;
+ ->execute()
+ ;
+ ++$this->jobs;
}
- protected function start($workers)
+ protected function start(int $workers): void
{
$this->startRecruiter();
$this->startCleaner();
- for ($i = 0; $i < $workers; $i++) {
+ for ($i = 0; $i < $workers; ++$i) {
$this->startWorker();
}
}
- private function terminateProcesses($signal)
+ private function terminateProcesses(int $signal): void
{
if ($this->processRecruiter) {
$this->stopProcessWithSignal($this->processRecruiter, $signal);
@@ -214,51 +232,52 @@ private function terminateProcesses($signal)
$this->processWorkers = [];
}
- protected function restartWorkerGracefully($workerIndex)
+ protected function restartWorkerGracefully($workerIndex): void
{
$this->stopProcessWithSignal($this->processWorkers[$workerIndex], SIGTERM);
$this->processWorkers[$workerIndex] = $this->startWorker();
}
- protected function restartWorkerByKilling($workerIndex)
+ protected function restartWorkerByKilling($workerIndex): void
{
$this->stopProcessWithSignal($this->processWorkers[$workerIndex], SIGKILL);
$this->processWorkers[$workerIndex] = $this->startWorker();
}
- protected function restartRecruiterGracefully()
+ protected function restartRecruiterGracefully(): void
{
$this->stopProcessWithSignal($this->processRecruiter, SIGTERM);
$this->startRecruiter();
}
- protected function restartRecruiterByKilling()
+ protected function restartRecruiterByKilling(): void
{
$this->stopProcessWithSignal($this->processRecruiter, SIGKILL);
$this->startRecruiter();
}
- protected function files()
+ protected function files(): string
{
$logs = '';
if (getenv('TEST_DUMP')) {
foreach ($this->files as $file) {
- $logs .= $file. ":". PHP_EOL;
+ $logs .= $file . ':' . PHP_EOL;
$logs .= file_get_contents($file);
}
} else {
$logs .= var_export($this->files, true);
}
+
return $logs;
}
- private function optionsToString(array $options = [])
+ private function optionsToString(array $options = []): string
{
$optionsString = '';
foreach ($options as $option => $value) {
$optionsString .= " --$option=$value";
- };
+ }
return $optionsString;
}
diff --git a/spec/Recruiter/Acceptance/EnduranceTest.php b/spec/Recruiter/Acceptance/EnduranceTest.php
index 3ef3a43c..650bb6ee 100644
--- a/spec/Recruiter/Acceptance/EnduranceTest.php
+++ b/spec/Recruiter/Acceptance/EnduranceTest.php
@@ -1,22 +1,26 @@
jobRepository = new Repository($this->recruiterDb);
@@ -24,57 +28,50 @@ public function setUp(): void
$this->files[] = $this->actionLog;
}
- public function testNotWithstandingCrashesJobsAreEventuallyPerformed()
+ public function testNotWithstandingCrashesJobsAreEventuallyPerformed(): void
{
$this
->limitTo(100)
->forAll(
Generator\bind(
Generator\choose(1, 4),
- function ($workers) {
- return Generator\tuple(
- Generator\constant($workers),
- Generator\seq(Generator\oneOf(
- Generator\map(
- function ($durationAndTag) {
- list($duration, $tag) = $durationAndTag;
- return ['enqueueJob', $duration, $tag];
- },
- Generator\tuple(
- Generator\nat(),
- Generator\elements(['generic', 'fast-lane'])
- )
- ),
- Generator\map(
- function ($workerIndex) {
- return ['restartWorkerGracefully', $workerIndex];
- },
- Generator\choose(0, $workers - 1)
- ),
- Generator\map(
- function ($workerIndex) {
- return ['restartWorkerByKilling', $workerIndex];
- },
- Generator\choose(0, $workers - 1)
+ fn ($workers) => Generator\tuple(
+ Generator\constant($workers),
+ Generator\seq(Generator\oneOf(
+ Generator\map(
+ function ($durationAndTag) {
+ [$duration, $tag] = $durationAndTag;
+
+ return ['enqueueJob', $duration, $tag];
+ },
+ Generator\tuple(
+ Generator\nat(),
+ Generator\elements(['generic', 'fast-lane']),
),
- Generator\constant('restartRecruiterGracefully'),
- Generator\constant('restartRecruiterByKilling'),
- Generator\map(
- function ($milliseconds) {
- return ['sleep', $milliseconds];
- },
- Generator\choose(1, 1000)
- )
- ))
- );
- }
- )
+ ),
+ Generator\map(
+ fn ($workerIndex) => ['restartWorkerGracefully', $workerIndex],
+ Generator\choose(0, $workers - 1),
+ ),
+ Generator\map(
+ fn ($workerIndex) => ['restartWorkerByKilling', $workerIndex],
+ Generator\choose(0, $workers - 1),
+ ),
+ Generator\constant('restartRecruiterGracefully'),
+ Generator\constant('restartRecruiterByKilling'),
+ Generator\map(
+ fn ($milliseconds) => ['sleep', $milliseconds],
+ Generator\choose(1, 1000),
+ ),
+ )),
+ ),
+ ),
)
->hook(Listener\log('/tmp/recruiter-test-iterations.log'))
->hook(Listener\collectFrequencies())
->disableShrinking()
- ->then(function ($tuple) {
- list ($workers, $actions) = $tuple;
+ ->then(function ($tuple): void {
+ [$workers, $actions] = $tuple;
$this->clean();
$this->start($workers);
foreach ($actions as $action) {
@@ -84,7 +81,7 @@ function ($milliseconds) {
$method = array_shift($arguments);
call_user_func_array(
[$this, $method],
- $arguments
+ $arguments,
);
} else {
$this->$action();
@@ -94,13 +91,10 @@ function ($milliseconds) {
$estimatedTime = max(count($actions) * 4, 60);
Timeout::inSeconds(
$estimatedTime,
- function () {
- return "all $this->jobs jobs to be performed. Now is " . date('c') . " Logs: " . $this->files();
- }
+ fn () => "all $this->jobs jobs to be performed. Now is " . date('c') . ' Logs: ' . $this->files(),
)
- ->until(function () {
- return $this->jobRepository->countArchived() === $this->jobs;
- });
+ ->until(fn () => $this->jobRepository->countArchived() === $this->jobs)
+ ;
$at = T\now();
$statistics = $this->recruiter->statistics($tag = null, $at);
@@ -115,7 +109,8 @@ function () {
}
// TODO: add tolerance
$this->assertEquals($statistics['throughput']['value'], $cumulativeThroughput);
- });
+ })
+ ;
}
private function logAction($action)
@@ -123,11 +118,11 @@ private function logAction($action)
file_put_contents(
$this->actionLog,
sprintf(
- "[ACTIONS][PHPUNIT][%s] %s" . PHP_EOL,
+ '[ACTIONS][PHPUNIT][%s] %s' . PHP_EOL,
date('c'),
- json_encode($action)
+ json_encode($action),
),
- FILE_APPEND
+ FILE_APPEND,
);
}
diff --git a/spec/Recruiter/Acceptance/FaultToleranceTest.php b/spec/Recruiter/Acceptance/FaultToleranceTest.php
index d71afafe..0faeb0b8 100644
--- a/spec/Recruiter/Acceptance/FaultToleranceTest.php
+++ b/spec/Recruiter/Acceptance/FaultToleranceTest.php
@@ -1,72 +1,74 @@
enqueueJob();
$worker = $this->recruiter->hire($memoryLimit);
$this->recruiter->bookJobsForWorkers();
$this->recruiter->rollbackLockedJobs();
- list ($assignments, $totalNumber) = $this->recruiter->assignJobsToWorkers();
- $this->assertEquals(1, count($assignments));
+ [$assignments, $totalNumber] = $this->recruiter->assignJobsToWorkers();
+ $this->assertCount(1, $assignments);
$this->assertEquals(1, $totalNumber);
}
- public function testRetryPolicyMustBeAppliedEvenWhenWorkerDiesInConstructor()
+ public function testRetryPolicyMustBeAppliedEvenWhenWorkerDiesInConstructor(): void
{
- (new FailsInConstructor([], false))
+ new FailsInConstructor([], false)
->asJobOf($this->recruiter)
->inBackground()
->retryWithPolicy(RetryManyTimes::forTimes(1, 0))
- ->execute();
+ ->execute()
+ ;
$worker = $this->startWorker();
- $this->waitForNumberOfWorkersToBe(1);
+ $this->waitForNumberOfWorkersToBe(1, 5);
- list ($assignments, $_) = $this->recruiter->assignJobsToWorkers();
- $this->assertEquals(1, count($assignments));
+ [$assignments, $_] = $this->recruiter->assignJobsToWorkers();
+ $this->assertCount(1, $assignments);
sleep(2);
$jobDocument = current($this->scheduled->find()->toArray());
$this->assertEquals(1, $jobDocument['attempts']);
- $this->assertEquals('Recruiter\\Workable\\FailsInConstructor', $jobDocument['workable']['class']);
+ $this->assertEquals(FailsInConstructor::class, $jobDocument['workable']['class']);
$this->assertStringContainsString('This job failed while instantiating a workable', $jobDocument['last_execution']['message']);
$this->assertStringContainsString('I am supposed to fail in constructor code for testing purpose', $jobDocument['last_execution']['message']);
- list ($assignments, $_) = $this->recruiter->assignJobsToWorkers();
- $this->assertEquals(1, count($assignments));
+ [$assignments, $_] = $this->recruiter->assignJobsToWorkers();
+ $this->assertCount(1, $assignments);
sleep(2);
$jobDocument = current($this->archived->find()->toArray());
$this->assertEquals(2, $jobDocument['attempts']);
- $this->assertEquals('Recruiter\\Workable\\FailsInConstructor', $jobDocument['workable']['class']);
+ $this->assertEquals(FailsInConstructor::class, $jobDocument['workable']['class']);
$this->assertStringContainsString('This job failed while instantiating a workable', $jobDocument['last_execution']['message']);
$this->assertStringContainsString('I am supposed to fail in constructor code for testing purpose', $jobDocument['last_execution']['message']);
- list ($assignments, $_) = $this->recruiter->assignJobsToWorkers();
- $this->assertEquals(0, count($assignments));
+ [$assignments, $_] = $this->recruiter->assignJobsToWorkers();
+ $this->assertCount(0, $assignments);
}
- public function testRetryPolicyMustBeAppliedEvenWhenWorkerDies()
+ public function testRetryPolicyMustBeAppliedEvenWhenWorkerDies(): void
{
// This job will fail with a fatal error and we want it to be
// retried at most 1 time, this means at most 2
// executions. The problem is that the retry policy is
// evaluated after the execution but fatal errors are not
// catchable and so the job will stay scheduled forever
- (new ThrowsFatalError())
+ new ExitsAbruptly()
->asJobOf($this->recruiter)
->inBackground()
->retryWithPolicy(RetryManyTimes::forTimes(1, 0))
- ->execute();
+ ->execute()
+ ;
// Right now we recover for dead jobs when we
// Recruiter::retireDeadWorkers and when we
@@ -78,8 +80,8 @@ public function testRetryPolicyMustBeAppliedEvenWhenWorkerDies()
// First execution of the job
$worker = $this->startWorker();
$this->waitForNumberOfWorkersToBe(1);
- list ($assignments, $_) = $this->recruiter->assignJobsToWorkers();
- $this->assertEquals(1, count($assignments));
+ [$assignments, $_] = $this->recruiter->assignJobsToWorkers();
+ $this->assertCount(1, $assignments);
sleep(2);
// The worker is dead and the job is not properly scheduled
$this->recruiter->retireDeadWorkers(new \DateTimeImmutable(), T\seconds(0));
@@ -89,12 +91,12 @@ public function testRetryPolicyMustBeAppliedEvenWhenWorkerDies()
$worker = $this->startWorker();
$this->waitForNumberOfWorkersToBe(1);
// Here the job is assigned and rescheduled by the retry policy because found crashed
- list ($assignments, $_) = $this->recruiter->assignJobsToWorkers();
- $this->assertEquals(1, count($assignments));
+ [$assignments, $_] = $this->recruiter->assignJobsToWorkers();
+ $this->assertCount(1, $assignments);
sleep(2);
// The worker is dead and the job is not properly scheduled
$this->recruiter->retireDeadWorkers(new \DateTimeImmutable(), T\seconds(0));
- $this->waitForNumberOfWorkersToBe(0);
+ $this->waitForNumberOfWorkersToBe(0, 2);
$this->assertJobIsMarkedAsCrashed();
// Third execution of the job
@@ -103,17 +105,17 @@ public function testRetryPolicyMustBeAppliedEvenWhenWorkerDies()
// Here the job is assigned and archived by the retry policy
// because found crashed and because it has been already
// executed 2 times
- list ($assignments, $_) = $this->recruiter->assignJobsToWorkers();
- $this->assertEquals(1, count($assignments));
+ [$assignments, $_] = $this->recruiter->assignJobsToWorkers();
+ $this->assertCount(1, $assignments);
sleep(1);
// The worker is not dead and the job is not scheduled anymore
$this->assertEquals(0, $this->recruiter->queued());
}
- private function assertJobIsMarkedAsCrashed()
+ private function assertJobIsMarkedAsCrashed(): void
{
$jobs = iterator_to_array($this->recruiterDb->selectCollection('scheduled')->find());
- $this->assertEquals(1, count($jobs));
+ $this->assertCount(1, $jobs);
foreach ($jobs as $job) {
$this->assertArrayHasKey('last_execution', $job);
$this->assertArrayHasKey('crashed', $job['last_execution']);
diff --git a/spec/Recruiter/Acceptance/HooksTest.php b/spec/Recruiter/Acceptance/HooksTest.php
index 05266dcf..f52bb3ce 100644
--- a/spec/Recruiter/Acceptance/HooksTest.php
+++ b/spec/Recruiter/Acceptance/HooksTest.php
@@ -1,70 +1,79 @@
memoryLimit = new MemoryLimit('64MB');
parent::setUp();
}
- public function testAfterFailureWithoutRetryEventIsFired()
+ public function testAfterFailureWithoutRetryEventIsFired(): void
{
$this->events = [];
$this->recruiter
->getEventDispatcher()
->addListener(
'job.failure.last',
- function (Event $event) {
+ function (Event $event): void {
$this->events[] = $event;
- }
- );
+ },
+ )
+ ;
- $job = (new AlwaysFail())
+ $job = new AlwaysFail()
->asJobOf($this->recruiter)
->inBackground()
- ->execute();
+ ->execute()
+ ;
$worker = $this->recruiter->hire($this->memoryLimit);
$this->recruiter->assignJobsToWorkers();
$worker->work();
$this->assertEquals(1, count($this->events));
- $this->assertInstanceOf('Recruiter\Job\Event', $this->events[0]);
+ $this->assertInstanceOf(Event::class, $this->events[0]);
$this->assertEquals('not-scheduled-by-retry-policy', $this->events[0]->export()['why']);
}
- public function testAfterLastFailureEventIsFired()
+ public function testAfterLastFailureEventIsFired(): void
{
$this->events = [];
$this->recruiter
->getEventDispatcher()
->addListener(
'job.failure.last',
- function (Event $event) {
+ function (Event $event): void {
$this->events[] = $event;
- }
- );
+ },
+ )
+ ;
- $job = (new AlwaysFail())
+ $job = new AlwaysFail()
->asJobOf($this->recruiter)
->retryWithPolicy(RetryManyTimes::forTimes(1, 0))
->inBackground()
- ->execute();
+ ->execute()
+ ;
- $runAJob = function ($howManyTimes, $worker) {
+ $runAJob = function ($howManyTimes, $worker): void {
for ($i = 0; $i < $howManyTimes;) {
- list($_, $assigned) = $this->recruiter->assignJobsToWorkers();
+ [$_, $assigned] = $this->recruiter->assignJobsToWorkers();
$worker->work();
if ($assigned > 0) {
- $i++;
+ ++$i;
}
}
};
@@ -73,56 +82,61 @@ function (Event $event) {
$runAJob(2, $worker);
$this->assertEquals(1, count($this->events));
- $this->assertInstanceOf('Recruiter\Job\Event', $this->events[0]);
+ $this->assertInstanceOf(Event::class, $this->events[0]);
$this->assertEquals('tried-too-many-times', $this->events[0]->export()['why']);
}
- public function testJobStartedIsFired()
+ public function testJobStartedIsFired(): void
{
$this->events = [];
$this->recruiter
->getEventDispatcher()
->addListener(
'job.started',
- function (Event $event) {
+ function (Event $event): void {
$this->events[] = $event;
- }
- );
+ },
+ )
+ ;
- $job = (new AlwaysSucceed())
+ $job = new AlwaysSucceed()
->asJobOf($this->recruiter)
->inBackground()
- ->execute();
+ ->execute()
+ ;
$worker = $this->recruiter->hire($this->memoryLimit);
$this->recruiter->assignJobsToWorkers();
$worker->work();
$this->assertEquals(1, count($this->events));
- $this->assertInstanceOf('Recruiter\Job\Event', $this->events[0]);
+ $this->assertInstanceOf(Event::class, $this->events[0]);
}
- public function testJobEndedIsFired()
+ public function testJobEndedIsFired(): void
{
$this->events = [];
$this->recruiter
->getEventDispatcher()
->addListener(
'job.ended',
- function (Event $event) {
+ function (Event $event): void {
$this->events[] = $event;
- }
- );
+ },
+ )
+ ;
- (new AlwaysSucceed())
+ new AlwaysSucceed()
->asJobOf($this->recruiter)
->inBackground()
- ->execute();
+ ->execute()
+ ;
- (new AlwaysFail())
+ new AlwaysFail()
->asJobOf($this->recruiter)
->inBackground()
- ->execute();
+ ->execute()
+ ;
$worker = $this->recruiter->hire($this->memoryLimit);
$this->recruiter->assignJobsToWorkers();
@@ -131,7 +145,7 @@ function (Event $event) {
$worker->work();
$this->assertEquals(2, count($this->events));
- $this->assertInstanceOf('Recruiter\Job\Event', $this->events[0]);
- $this->assertInstanceOf('Recruiter\Job\Event', $this->events[1]);
+ $this->assertInstanceOf(Event::class, $this->events[0]);
+ $this->assertInstanceOf(Event::class, $this->events[1]);
}
}
diff --git a/spec/Recruiter/Acceptance/RepeatableJobsAreScheduledTest.php b/spec/Recruiter/Acceptance/RepeatableJobsAreScheduledTest.php
index 7d1a29b4..f5d692cc 100644
--- a/spec/Recruiter/Acceptance/RepeatableJobsAreScheduledTest.php
+++ b/spec/Recruiter/Acceptance/RepeatableJobsAreScheduledTest.php
@@ -1,22 +1,21 @@
0,
'group' => 'generic',
'workable' => [
- 'class' => 'Recruiter\\Workable\\SampleRepeatableCommand',
+ 'class' => SampleRepeatableCommand::class,
'parameters' => [],
'method' => 'execute',
],
@@ -48,16 +47,16 @@ public function testARepeatableJobIsScheduledAtExpectedScheduledTime()
'executions' => 1,
],
'retry_policy' => [
- 'class' => 'Recruiter\\RetryPolicy\\ExponentialBackoff',
+ 'class' => ExponentialBackoff::class,
'parameters' => [
'retry_how_many_times' => 2,
'seconds_to_initially_wait_before_retry' => 5,
],
- ]
+ ],
], $jobData);
}
- public function testOnlyASingleJobAreScheduledForTheSameSchedulingTime()
+ public function testOnlyASingleJobAreScheduledForTheSameSchedulingTime(): void
{
$expectedScheduleDate = strtotime('2019-05-16T14:00:00');
$schedulePolicy = new FixedSchedulePolicy($expectedScheduleDate);
@@ -72,11 +71,11 @@ public function testOnlyASingleJobAreScheduledForTheSameSchedulingTime()
$this->assertEquals(
T\MongoDate::from(Moment::fromTimestamp($expectedScheduleDate)),
- $jobs[0]->export()['scheduled_at']
+ $jobs[0]->export()['scheduled_at'],
);
}
- public function testAJobIsScheduledForEverySchedulingTime()
+ public function testAJobIsScheduledForEverySchedulingTime(): void
{
$expectedScheduleDates = [
strtotime('2019-05-16T14:00:00'),
@@ -94,7 +93,7 @@ public function testAJobIsScheduledForEverySchedulingTime()
$this->assertEquals(1, $jobs[1]->export()['scheduled']['executions']);
}
- public function testANewJobIsNotScheduledIfItShouldBeUniqueAndTheOldOneIsStillRunning()
+ public function testANewJobIsNotScheduledIfItShouldBeUniqueAndTheOldOneIsStillRunning(): void
{
$schedulePolicy = new FixedSchedulePolicy([
strtotime('2019-05-16T14:00:00'),
@@ -109,7 +108,7 @@ public function testANewJobIsNotScheduledIfItShouldBeUniqueAndTheOldOneIsStillRu
$this->assertEquals(1, count($jobs));
}
- public function testSchedulersAreUniqueOnUrn()
+ public function testSchedulersAreUniqueOnUrn(): void
{
$aSchedulerAlreadyHaveSomeAttempts = 3;
$this->IHaveAScheduleWithALongStory('unique-urn', $aSchedulerAlreadyHaveSomeAttempts);
@@ -134,8 +133,8 @@ public function testSchedulersAreUniqueOnUrn()
private function IHaveAScheduleWithALongStory(string $urn, $attempts)
{
$scheduleTimes = [];
- for ($i = 1; $i <= $attempts; $i++) {
- $scheduleTimes[] = strtotime("2018-05-" . $i . "T15:00:00");
+ for ($i = 1; $i <= $attempts; ++$i) {
+ $scheduleTimes[] = strtotime('2018-05-' . $i . 'T15:00:00');
}
$schedulePolicy = new FixedSchedulePolicy($scheduleTimes);
@@ -144,13 +143,13 @@ private function IHaveAScheduleWithALongStory(string $urn, $attempts)
$this->recruiterScheduleJobsNTimes($attempts);
}
- private function scheduleAJob(string $urn, SchedulePolicy $schedulePolicy = null, bool $unique = false)
+ private function scheduleAJob(string $urn, ?SchedulePolicy $schedulePolicy = null, bool $unique = false)
{
if (is_null($schedulePolicy)) {
$schedulePolicy = new FixedSchedulePolicy(strtotime('2023-02-18T17:00:00'));
}
- $scheduler = (new SampleRepeatableCommand())
+ $scheduler = new SampleRepeatableCommand()
->asRepeatableJobOf($this->recruiter)
->repeatWithPolicy($schedulePolicy)
->retryWithPolicy(ExponentialBackoff::forTimes(2, 5))
@@ -160,7 +159,7 @@ private function scheduleAJob(string $urn, SchedulePolicy $schedulePolicy = null
return $scheduler->create();
}
- private function recruiterScheduleJobsNTimes(int $nth = 1)
+ private function recruiterScheduleJobsNTimes(int $nth = 1): void
{
$i = 0;
while ($i++ < $nth) {
@@ -171,29 +170,29 @@ private function recruiterScheduleJobsNTimes(int $nth = 1)
private function fetchScheduledJobs()
{
$jobsRepository = new JobsRepository($this->recruiterDb);
+
return $jobsRepository->all();
}
private function fetchSchedulers()
{
$schedulersRepository = new SchedulersRepository($this->recruiterDb);
+
return $schedulersRepository->all();
}
}
class FixedSchedulePolicy implements SchedulePolicy
{
- private $timestamp;
- private $index;
+ private array $timestamps;
- public function __construct($timestamps, $index = 0)
+ public function __construct($timestamps, private int $index = 0)
{
if (!is_array($timestamps)) {
$timestamps = [$timestamps];
}
$this->timestamps = $timestamps;
- $this->index = $index;
}
public function next(): Moment
diff --git a/spec/Recruiter/Acceptance/SyncronousExecutionTest.php b/spec/Recruiter/Acceptance/SyncronousExecutionTest.php
index e1898b5b..382c0524 100644
--- a/spec/Recruiter/Acceptance/SyncronousExecutionTest.php
+++ b/spec/Recruiter/Acceptance/SyncronousExecutionTest.php
@@ -1,13 +1,14 @@
enqueueAnAnswerJob(43, T\now()->after(T\seconds(30)));
@@ -21,12 +22,13 @@ public function testJobsAreExecutedInOrderOfScheduling()
$this->assertEquals(43, end($results)->result());
}
- public function testAReportIsReturnedInOrderToSortOutIfAnErrorOccured()
+ public function testAReportIsReturnedInOrderToSortOutIfAnErrorOccured(): void
{
- (new AlwaysFail())
+ new AlwaysFail()
->asJobOf($this->recruiter)
->inBackground()
- ->execute();
+ ->execute()
+ ;
$report = $this->recruiter->flushJobsSynchronously();
@@ -40,7 +42,8 @@ private function enqueueAnAnswerJob($answer, $scheduledAt)
->asJobOf($this->recruiter)
->scheduleAt($scheduledAt)
->inBackground()
- ->execute();
+ ->execute()
+ ;
}
}
diff --git a/spec/Recruiter/Acceptance/WorkerGuaranteedToExitWhenAMemoryLeakOccurs.php b/spec/Recruiter/Acceptance/WorkerGuaranteedToExitWhenAMemoryLeakOccurs.php
index f6121ab2..301363c5 100644
--- a/spec/Recruiter/Acceptance/WorkerGuaranteedToExitWhenAMemoryLeakOccurs.php
+++ b/spec/Recruiter/Acceptance/WorkerGuaranteedToExitWhenAMemoryLeakOccurs.php
@@ -2,25 +2,27 @@
namespace Recruiter\Acceptance;
-Recruiter\Concurrency\Timeout;
+use Recruiter\Concurrency\Timeout;
use Recruiter\Workable\ConsumingMemoryCommand;
use Timeless as T;
-class WorkerGuaranteedToExitWhenAMemoryLeakOccurs extends BaseAcceptanceTest
+class WorkerGuaranteedToExitWhenAMemoryLeakOccurs extends BaseAcceptanceTestCase
{
/**
* @group acceptance
+ *
* @dataProvider provideMemoryConsumptions
*/
- public function testWorkerKillItselfAfterAMemoryLeakButNotAfterABigMemoryConsumptionWithoutLeak($withMemoryLeak, $howManyItems, $memoryLimit, $expectedWorkerAlive)
+ public function testWorkerKillItselfAfterAMemoryLeakButNotAfterABigMemoryConsumptionWithoutLeak($withMemoryLeak, $howManyItems, $memoryLimit, $expectedWorkerAlive): void
{
- (new ConsumingMemoryCommand([
+ new ConsumingMemoryCommand([
'withMemoryLeak' => $withMemoryLeak,
'howManyItems' => $howManyItems,
- ]))
+ ])
->asJobOf($this->recruiter)
->inBackground()
- ->execute();
+ ->execute()
+ ;
$this->startRecruiter();
@@ -30,13 +32,15 @@ public function testWorkerKillItselfAfterAMemoryLeakButNotAfterABigMemoryConsump
]);
$this->waitForNumberOfWorkersToBe($numberOfWorkersBefore + 1, 5);
- Timeout::inSeconds(5, function () {
+ Timeout::inSeconds(5, function (): void {
})
->until(function () {
$at = T\now();
$statistics = $this->recruiter->statistics($tag = null, $at);
- return $statistics['jobs']['queued'] == 0;
- });
+
+ return 0 == $statistics['jobs']['queued'];
+ })
+ ;
$numberOfWorkersCurrently = $this->numberOfWorkers();
@@ -49,14 +53,14 @@ public function testWorkerKillItselfAfterAMemoryLeakButNotAfterABigMemoryConsump
$this->assertEquals(
$numberOfExpectedWorkers,
$numberOfWorkersCurrently,
- "The number of workers before was $numberOfWorkersBefore and now after starting 1 and execute a job we have $numberOfWorkersCurrently"
+ "The number of workers before was $numberOfWorkersBefore and now after starting 1 and execute a job we have $numberOfWorkersCurrently",
);
}
public static function provideMemoryConsumptions()
{
return [
- //legend: [$withMemoryLeak, $howManyItems, $memoryLimit, $expectedWorkerAlive],
+ // legend: [$withMemoryLeak, $howManyItems, $memoryLimit, $expectedWorkerAlive],
[false, 2000000, '20MB', true],
[true, 2000000, '20MB', false],
[true, 2000000, '128MB', true],
diff --git a/spec/Recruiter/Acceptance/WorkerGuaranteedToExitWithFailureCodeInCaseOfExceptionTest.php b/spec/Recruiter/Acceptance/WorkerGuaranteedToExitWithFailureCodeInCaseOfExceptionTest.php
index e066a577..e683558b 100644
--- a/spec/Recruiter/Acceptance/WorkerGuaranteedToExitWithFailureCodeInCaseOfExceptionTest.php
+++ b/spec/Recruiter/Acceptance/WorkerGuaranteedToExitWithFailureCodeInCaseOfExceptionTest.php
@@ -2,17 +2,16 @@
namespace Recruiter\Acceptance;
-use Recruiter\Workable\FactoryMethodCommand;
-use Recruiter\Workable\ThrowsFatalError;
+use Recruiter\Workable\ExitsAbruptly;
-class WorkerGuaranteedToExitWithFailureCodeInCaseOfExceptionTest extends BaseAcceptanceTest
+class WorkerGuaranteedToExitWithFailureCodeInCaseOfExceptionTest extends BaseAcceptanceTestCase
{
/**
* @group acceptance
*/
- public function testInCaseOfExceptionTheExitCodeOfWorkerProcessIsNotZero()
+ public function testInCaseOfExceptionTheExitCodeOfWorkerProcessIsNotZero(): void
{
- (new ThrowsFatalError())
+ new ExitsAbruptly()
->asJobOf($this->recruiter)
->inBackground()
->execute()
@@ -21,7 +20,7 @@ public function testInCaseOfExceptionTheExitCodeOfWorkerProcessIsNotZero()
$worker = $this->startWorker();
$workerProcess = $worker[0];
$this->waitForNumberOfWorkersToBe(1);
- list ($assignments, $_) = $this->recruiter->assignJobsToWorkers();
+ [$assignments, $_] = $this->recruiter->assignJobsToWorkers();
$this->assertEquals(1, count($assignments));
$this->waitForNumberOfWorkersToBe(0, $seconds = 10);
diff --git a/spec/Recruiter/Acceptance/WorkerGuaranteedToRetireAfterDeathTest.php b/spec/Recruiter/Acceptance/WorkerGuaranteedToRetireAfterDeathTest.php
index 95ca3c9f..8e6c93fe 100644
--- a/spec/Recruiter/Acceptance/WorkerGuaranteedToRetireAfterDeathTest.php
+++ b/spec/Recruiter/Acceptance/WorkerGuaranteedToRetireAfterDeathTest.php
@@ -2,12 +2,12 @@
namespace Recruiter\Acceptance;
-class WorkerGuaranteedToRetireAfterDeathTest extends BaseAcceptanceTest
+class WorkerGuaranteedToRetireAfterDeathTest extends BaseAcceptanceTestCase
{
/**
* @group acceptance
*/
- public function testRetireAfterAskedToStop()
+ public function testRetireAfterAskedToStop(): void
{
$numberOfWorkersBefore = $this->numberOfWorkers();
$processAndPipes = $this->startWorker();
@@ -17,7 +17,7 @@ public function testRetireAfterAskedToStop()
$this->assertEquals(
$numberOfWorkersBefore,
$numberOfWorkersCurrently,
- "The number of workers before was $numberOfWorkersBefore and now after starting and stopping 1 we have $numberOfWorkersCurrently"
+ "The number of workers before was $numberOfWorkersBefore and now after starting and stopping 1 we have $numberOfWorkersCurrently",
);
}
}
diff --git a/spec/Recruiter/Acceptance/WorkerRepositoryTest.php b/spec/Recruiter/Acceptance/WorkerRepositoryTest.php
index 3364f238..0c27dd63 100644
--- a/spec/Recruiter/Acceptance/WorkerRepositoryTest.php
+++ b/spec/Recruiter/Acceptance/WorkerRepositoryTest.php
@@ -3,23 +3,25 @@
namespace Recruiter\Acceptance;
use Recruiter\Worker\Repository;
-use Recruiter\Recruiter;
-class WorkerRepositoryTest extends BaseAcceptanceTest
+class WorkerRepositoryTest extends BaseAcceptanceTestCase
{
- public function setUp(): void
+ private Repository $repository;
+
+ #[\Override]
+ protected function setUp(): void
{
parent::setUp();
$this->repository = new Repository(
$this->recruiterDb,
- $this->recruiter
+ $this->recruiter,
);
}
/**
* @group acceptance
*/
- public function testRetireWorkerWithPid()
+ public function testRetireWorkerWithPid(): void
{
$this->givenWorkerWithPid(10);
$this->assertEquals(1, $this->numberOfWorkers());
diff --git a/spec/Recruiter/CleanerTest.php b/spec/Recruiter/CleanerTest.php
index a1a70f64..8ae984a4 100644
--- a/spec/Recruiter/CleanerTest.php
+++ b/spec/Recruiter/CleanerTest.php
@@ -2,38 +2,48 @@
namespace Recruiter;
+use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
-use Timeless\Interval;
+use Recruiter\Job\Repository;
use Timeless as T;
+use Timeless\Interval;
+use Timeless\Moment;
class CleanerTest extends TestCase
{
- public function setUp(): void
+ private T\ClockInterface $clock;
+ private Moment $now;
+ private MockObject $jobRepository;
+ private Cleaner $cleaner;
+ private Interval $interval;
+
+ protected function setUp(): void
{
$this->clock = T\clock()->stop();
$this->now = $this->clock->now();
$this->jobRepository = $this
- ->getMockBuilder('Recruiter\Job\Repository')
+ ->getMockBuilder(Repository::class)
->disableOriginalConstructor()
- ->getMock();
+ ->getMock()
+ ;
$this->cleaner = new Cleaner($this->jobRepository);
$this->interval = Interval::parse('10s');
}
- public function tearDown(): void
+ protected function tearDown(): void
{
T\clock()->start();
}
- public function testShouldCreateCleaner()
+ public function testShouldCreateCleaner(): void
{
- $this->assertInstanceOf('Recruiter\Cleaner', $this->cleaner);
+ $this->assertInstanceOf(Cleaner::class, $this->cleaner);
}
- public function testDelegatesTheCleanupOfArchivedJobsToTheJobsRepository()
+ public function testDelegatesTheCleanupOfArchivedJobsToTheJobsRepository(): void
{
$expectedUpperLimit = $this->now->before($this->interval);
@@ -41,11 +51,12 @@ public function testDelegatesTheCleanupOfArchivedJobsToTheJobsRepository()
->expects($this->once())
->method('cleanArchived')
->with($expectedUpperLimit)
- ->will($this->returnValue($jobsCleaned = 10));
+ ->will($this->returnValue($jobsCleaned = 10))
+ ;
$this->assertEquals(
$jobsCleaned,
- $this->cleaner->cleanArchived($this->interval)
+ $this->cleaner->cleanArchived($this->interval),
);
}
}
diff --git a/spec/Recruiter/ExampleTest.php b/spec/Recruiter/ExampleTest.php
index 9b9b279f..9b4eecf5 100644
--- a/spec/Recruiter/ExampleTest.php
+++ b/spec/Recruiter/ExampleTest.php
@@ -6,7 +6,7 @@
class ExampleTest extends TestCase
{
- public function testMustPass()
+ public function testMustPass(): void
{
$this->assertTrue(true);
}
diff --git a/spec/Recruiter/FactoryTest.php b/spec/Recruiter/FactoryTest.php
index 293f1623..9f7fa61d 100644
--- a/spec/Recruiter/FactoryTest.php
+++ b/spec/Recruiter/FactoryTest.php
@@ -2,69 +2,56 @@
namespace Recruiter;
-use MongoDB;
+use MongoDB\Database;
use PHPUnit\Framework\TestCase;
use Recruiter\Infrastructure\Persistence\Mongodb\URI as MongoURI;
class FactoryTest extends TestCase
{
- /**
- * @var Factory
- */
- private $factory;
-
- /**
- * @var string
- */
- private $dbHost;
-
- /**
- * @var string
- */
- private $dbName;
+ private Factory $factory;
+ private MongoURI $mongoURI;
protected function setUp(): void
{
$this->factory = new Factory();
- $this->dbHost = 'localhost:27017';
- $this->dbName = 'recruiter';
+ $this->mongoURI = MongoURI::fromEnvironment();
}
- public function testShouldCreateAMongoDatabaseConnection()
+ public function testShouldCreateAMongoDatabaseConnection(): void
{
$this->assertInstanceOf(
- 'MongoDB\Database',
- $this->creationOfDefaultMongoDb()
+ Database::class,
+ $this->creationOfDefaultMongoDb(),
);
}
- public function testWriteConcernIsMajorityByDefault()
+ public function testWriteConcernIsMajorityByDefault(): void
{
$mongoDb = $this->creationOfDefaultMongoDb();
$this->assertEquals('majority', $mongoDb->getWriteConcern()->getW());
}
- public function testShouldOverwriteTheWriteConcernPassedInTheOptions()
+ public function testShouldOverwriteTheWriteConcernPassedInTheOptions(): void
{
$mongoDb = $this->factory->getMongoDb(
- MongoURI::from('mongodb://localhost:27017/recruiter'),
+ $this->mongoURI,
[
'connectTimeoutMS' => 1000,
'w' => '0',
- ]
+ ],
);
$this->assertEquals('majority', $mongoDb->getWriteConcern()->getW());
}
- private function creationOfDefaultMongoDb()
+ private function creationOfDefaultMongoDb(): Database
{
return $this->factory->getMongoDb(
- MongoURI::from(sprintf('mongodb://%s/%s', $this->dbHost, $this->dbName)),
+ $this->mongoURI,
[
'connectTimeoutMS' => 1000,
'w' => '0',
- ]
+ ],
);
}
}
diff --git a/spec/Recruiter/FinalizerMethodsAreCalledWhenWorkableImplementsFinalizerInterfaceTest.php b/spec/Recruiter/FinalizerMethodsAreCalledWhenWorkableImplementsFinalizerInterfaceTest.php
index 3a60e5fc..a5891c64 100644
--- a/spec/Recruiter/FinalizerMethodsAreCalledWhenWorkableImplementsFinalizerInterfaceTest.php
+++ b/spec/Recruiter/FinalizerMethodsAreCalledWhenWorkableImplementsFinalizerInterfaceTest.php
@@ -2,59 +2,77 @@
namespace Recruiter;
-use Exception;
+use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
+use Recruiter\Job\Repository;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class FinalizerMethodsAreCalledWhenWorkableImplementsFinalizerInterfaceTest extends TestCase
{
- public function setUp(): void
+ private MockObject&Repository $repository;
+ private MockObject&EventDispatcherInterface $dispatcher;
+ private ListenerSpy $listener;
+
+ /**
+ * @throws \PHPUnit\Framework\MockObject\Exception
+ */
+ protected function setUp(): void
{
$this->repository = $this
- ->getMockBuilder('Recruiter\Job\Repository')
+ ->getMockBuilder(Repository::class)
->disableOriginalConstructor()
- ->getMock();
+ ->getMock()
+ ;
- $this->dispatcher = $this->createMock(
- 'Symfony\Component\EventDispatcher\EventDispatcherInterface'
- );
+ $this->dispatcher = $this->createMock(EventDispatcherInterface::class);
+ $this->listener = new ListenerSpy();
}
- public function testFinalizableFailureMethodsAreCalledWhenJobFails()
+ public function testFinalizableFailureMethodsAreCalledWhenJobFails(): void
{
$exception = new \Exception('job was failed');
- $listener = $this->createPartialMock('StdClass', ['methodWasCalled']);
- $listener
- ->expects($this->exactly(3))
- ->method('methodWasCalled')
- ->withConsecutive(
- [$this->equalTo('afterFailure'), $exception],
- [$this->equalTo('afterLastFailure'), $exception],
- [$this->equalTo('finalize'), $exception]
- );
- $workable = new FinalizableWorkable(function () use ($exception) {
+
+ $workable = new FinalizableWorkable(function () use ($exception): void {
throw $exception;
- }, $listener);
+ }, $this->listener);
$job = Job::around($workable, $this->repository);
$job->execute($this->dispatcher);
+
+ $calls = $this->listener->calls;
+ $this->assertCount(3, $calls);
+
+ $this->assertSame('afterFailure', $calls[0][0]);
+ $this->assertSame($exception, $calls[0][1]);
+
+ $this->assertSame('afterLastFailure', $calls[1][0]);
+ $this->assertSame($exception, $calls[1][1]);
+
+ $this->assertSame('finalize', $calls[2][0]);
+ $this->assertSame($exception, $calls[2][1]);
}
- public function testFinalizableSuccessfullMethodsAreCalledWhenJobIsDone()
+ public function testFinalizableSuccessfullMethodsAreCalledWhenJobIsDone(): void
{
- $listener = $this->createPartialMock('StdClass', ['methodWasCalled']);
- $listener
- ->expects($this->exactly(2))
- ->method('methodWasCalled')
- ->withConsecutive(
- [$this->equalTo('afterSuccess')],
- [$this->equalTo('finalize')]
- );
- $workable = new FinalizableWorkable(function () {
- return true;
- }, $listener);
+ $workable = new FinalizableWorkable(fn () => true, $this->listener);
$job = Job::around($workable, $this->repository);
$job->execute($this->dispatcher);
+
+ $calls = $this->listener->calls;
+ $this->assertCount(2, $calls);
+ $this->assertSame('afterSuccess', $calls[0][0]);
+ $this->assertSame('finalize', $calls[1][0]);
+ }
+}
+
+class ListenerSpy
+{
+ public array $calls = [];
+
+ public function methodWasCalled(string $name, ?\Throwable $exception = null): void
+ {
+ $this->calls[] = [$name, $exception];
}
}
@@ -65,36 +83,35 @@ class FinalizableWorkable implements Workable, Finalizable
private $whatToDo;
- private $listener;
-
- public function __construct(callable $whatToDo, $listener)
+ public function __construct(callable $whatToDo, private $listener)
{
- $this->listener = $listener;
+ $this->parameters = [];
$this->whatToDo = $whatToDo;
}
- public function execute()
+ public function execute(): mixed
{
$whatToDo = $this->whatToDo;
+
return $whatToDo();
}
- public function afterSuccess()
+ public function afterSuccess(): void
{
$this->listener->methodWasCalled(__FUNCTION__);
}
- public function afterFailure(Exception $e)
+ public function afterFailure(\Exception $e): void
{
$this->listener->methodWasCalled(__FUNCTION__, $e);
}
- public function afterLastFailure(Exception $e)
+ public function afterLastFailure(\Exception $e): void
{
$this->listener->methodWasCalled(__FUNCTION__, $e);
}
- public function finalize(?Exception $e = null)
+ public function finalize(?\Exception $e = null): void
{
$this->listener->methodWasCalled(__FUNCTION__, $e);
}
diff --git a/spec/Recruiter/Infrastructure/Memory/MemoryLimitTest.php b/spec/Recruiter/Infrastructure/Memory/MemoryLimitTest.php
index d552d154..52012ec7 100644
--- a/spec/Recruiter/Infrastructure/Memory/MemoryLimitTest.php
+++ b/spec/Recruiter/Infrastructure/Memory/MemoryLimitTest.php
@@ -1,4 +1,5 @@
expectException(MemoryLimitExceededException::class);
$memoryLimit = new MemoryLimit(1);
diff --git a/spec/Recruiter/Job/EventTest.php b/spec/Recruiter/Job/EventTest.php
index bf81513a..33e5cb62 100644
--- a/spec/Recruiter/Job/EventTest.php
+++ b/spec/Recruiter/Job/EventTest.php
@@ -1,15 +1,16 @@
'generic',
- 'tags' =>[
+ 'tags' => [
1 => 'billing-notification',
],
]);
@@ -17,11 +18,11 @@ public function testHasTagReturnsTrueWhenTheExportedJobContainsTheTag()
$this->assertTrue($event->hasTag('billing-notification'));
}
- public function testHasTagReturnsFalseWhenTheExportedJobDoesNotContainTheTag()
+ public function testHasTagReturnsFalseWhenTheExportedJobDoesNotContainTheTag(): void
{
$event = new Event([
'group' => 'generic',
- 'tags' =>[
+ 'tags' => [
1 => 'billing-notification',
],
]);
@@ -29,7 +30,7 @@ public function testHasTagReturnsFalseWhenTheExportedJobDoesNotContainTheTag()
$this->assertFalse($event->hasTag('inexistant-tag'));
}
- public function testHasTagReturnsFalseWhenTheExportedJobDoesNotContainTags()
+ public function testHasTagReturnsFalseWhenTheExportedJobDoesNotContainTags(): void
{
$event = new Event([
]);
diff --git a/spec/Recruiter/Job/RepositoryTest.php b/spec/Recruiter/Job/RepositoryTest.php
index 39153943..547d5212 100644
--- a/spec/Recruiter/Job/RepositoryTest.php
+++ b/spec/Recruiter/Job/RepositoryTest.php
@@ -1,38 +1,50 @@
recruiterDb = $factory->getMongoDb(MongoURI::from('mongodb://localhost:27017/recruiter'), []);
+ $this->recruiterDb = $factory->getMongoDb(MongoURI::fromEnvironment(), []);
$this->recruiterDb->drop();
$this->repository = new Repository($this->recruiterDb);
$this->clock = T\clock()->stop();
- $this->eventDispatcher = $this->createMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
+ $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
}
- public function tearDown(): void
+ protected function tearDown(): void
{
T\clock()->start();
}
- public function testCountsQueuedJobsAsOfNow()
+ public function testCountsQueuedJobsAsOfNow(): void
{
$this->aJobToSchedule()->inGroup('generic')->inBackground()->execute();
$this->aJobToSchedule()->inGroup('generic')->inBackground()->execute();
@@ -42,7 +54,7 @@ public function testCountsQueuedJobsAsOfNow()
$this->assertEquals(1, $this->repository->queued('fast-lane'));
}
- public function testCountsQueuedJobsWithCornerCaseTagging()
+ public function testCountsQueuedJobsWithCornerCaseTagging(): void
{
$this->aJobToSchedule()->inBackground()->execute();
$this->aJobToSchedule()->inGroup([])->inBackground()->execute();
@@ -52,7 +64,7 @@ public function testCountsQueuedJobsWithCornerCaseTagging()
$this->assertEquals(4, $this->repository->queued('generic'));
}
- public function testCountsQueudJobsWithScheduledAtGreatherThanASpecificDate()
+ public function testCountsQueudJobsWithScheduledAtGreatherThanASpecificDate(): void
{
$this->aJobToSchedule()->inBackground()->execute();
$time1 = $this->clock->now();
@@ -63,19 +75,19 @@ public function testCountsQueudJobsWithScheduledAtGreatherThanASpecificDate()
$this->repository->queued(
'generic',
T\now(),
- T\now()->before(T\hour(24))
- )
+ T\now()->before(T\hour(24)),
+ ),
);
}
- public function testCountsPostponedJobs()
+ public function testCountsPostponedJobs(): void
{
$this->aJobToSchedule()->inBackground()->execute();
$this->aJobToSchedule()->scheduleIn(T\hour(24))->execute();
$this->assertEquals(1, $this->repository->postponed('generic'));
}
- public function testRecentHistory()
+ public function testRecentHistory(): void
{
$ed = $this->eventDispatcher;
$this->repository->archive($this->aJob()->beforeExecution($ed)->afterExecution(42, $ed));
@@ -85,7 +97,7 @@ public function testRecentHistory()
[
'throughput' => [
'value' => 3.0,
- 'value_per_second' => 3/60.0,
+ 'value_per_second' => 3 / 60.0,
],
'latency' => [
'average' => 5.0,
@@ -94,11 +106,11 @@ public function testRecentHistory()
'average' => 0.0,
],
],
- $this->repository->recentHistory()
+ $this->repository->recentHistory(),
);
}
- public function testCountQueuedJobsGroupingByASpecificKeyword()
+ public function testCountQueuedJobsGroupingByASpecificKeyword(): void
{
$workable1 = $this->workableMock();
$workable2 = $this->workableMock();
@@ -106,12 +118,14 @@ public function testCountQueuedJobsGroupingByASpecificKeyword()
$workable1
->expects($this->any())
->method('export')
- ->will($this->returnValue(['seller' => 'seller1']));
+ ->will($this->returnValue(['seller' => 'seller1']))
+ ;
$workable2
->expects($this->any())
->method('export')
- ->will($this->returnValue(['seller' => 'seller2']));
+ ->will($this->returnValue(['seller' => 'seller2']))
+ ;
$job1 = $this->aJob($workable1);
$job2 = $this->aJob($workable2);
@@ -125,65 +139,65 @@ public function testCountQueuedJobsGroupingByASpecificKeyword()
'seller1' => '1',
'seller2' => '2',
],
- $this->repository->queuedGroupedBy('workable.parameters.seller', [])
+ $this->repository->queuedGroupedBy('workable.parameters.seller', []),
);
}
- public function testGetDelayedScheduledJobs()
+ public function testGetDelayedScheduledJobs(): void
{
$workable1 = $this->workableMockWithCustomParameters([
- 'job1' => 'delayed_and_unpicked'
+ 'job1' => 'delayed_and_unpicked',
]);
$workable2 = $this->workableMockWithCustomParameters([
- 'job2' => 'delayed_and_unpicked'
+ 'job2' => 'delayed_and_unpicked',
]);
$workable3 = $this->workableMockWithCustomParameters([
- 'job3' => 'in_schedulation'
+ 'job3' => 'in_schedulation',
]);
$this->aJobToSchedule($this->aJob($workable1))->inBackground()->execute();
$this->aJobToSchedule($this->aJob($workable2))->inBackground()->execute();
$lowerLimit = $this->clock->now();
- $fiveHoursInSeconds = 5*60*60;
+ $fiveHoursInSeconds = 5 * 60 * 60;
$this->clock->driftForwardBySeconds($fiveHoursInSeconds);
$this->aJobToSchedule($this->aJob($workable3))->inBackground()->execute();
$jobs = $this->repository->delayedScheduledJobs($lowerLimit);
$jobsFounds = 0;
foreach ($jobs as $job) {
$this->assertEquals('delayed_and_unpicked', reset($job->export()['workable']['parameters']));
- $jobsFounds++;
+ ++$jobsFounds;
}
$this->assertEquals(2, $jobsFounds);
}
- public function testCountDelayedScheduledJobs()
+ public function testCountDelayedScheduledJobs(): void
{
$this->aJobToSchedule($this->aJob())->inBackground()->execute();
$this->aJobToSchedule($this->aJob())->inBackground()->execute();
$lowerLimit = $this->clock->now();
- $twoHoursInSeconds = 2*60*60;
+ $twoHoursInSeconds = 2 * 60 * 60;
$this->clock->driftForwardBySeconds($twoHoursInSeconds);
$this->aJobToSchedule($this->aJob())->inBackground()->execute();
$this->assertEquals(2, $this->repository->countDelayedScheduledJobs($lowerLimit));
}
- public function testCountRecentJobsWithManyAttempts()
+ public function testCountRecentJobsWithManyAttempts(): void
{
$ed = $this->eventDispatcher;
$this->repository->archive($this->aJob()->beforeExecution($ed)->beforeExecution($ed)->afterExecution(42, $ed));
$this->clock->now();
- $threeHoursInSeconds = 3*60*60;
+ $threeHoursInSeconds = 3 * 60 * 60;
$this->clock->driftForwardBySeconds($threeHoursInSeconds);
$lowerLimit = $this->clock->now();
$this->repository->archive($this->aJob()->beforeExecution($ed)->beforeExecution($ed)->afterExecution(42, $ed));
$this->repository->archive($this->aJob()->beforeExecution($ed)->beforeExecution($ed)->afterExecution(42, $ed));
- $oneHourInSeconds = 60*60;
+ $oneHourInSeconds = 60 * 60;
$this->clock->driftForwardBySeconds($oneHourInSeconds);
$createdAt = $endedAt = $this->clock->now();
$this->repository->save($this->jobMockWithAttemptsAndCustomParameters($createdAt, $endedAt));
$this->repository->save($this->jobMockWithAttemptsAndCustomParameters($createdAt, $endedAt));
$this->aJobToSchedule($this->aJob())->inBackground()->execute();
$upperLimit = $this->clock->now();
- $oneHourInSeconds = 60*60;
+ $oneHourInSeconds = 60 * 60;
$this->clock->driftForwardBySeconds($oneHourInSeconds);
$createdAt = $endedAt = $this->clock->now();
$this->repository->archive($this->aJob()->beforeExecution($ed)->beforeExecution($ed)->afterExecution(42, $ed));
@@ -191,35 +205,35 @@ public function testCountRecentJobsWithManyAttempts()
$this->assertEquals(4, $this->repository->countRecentJobsWithManyAttempts($lowerLimit, $upperLimit));
}
- public function testGetRecentJobsWithManyAttempts()
+ public function testGetRecentJobsWithManyAttempts(): void
{
$ed = $this->eventDispatcher;
$workable1 = $this->workableMockWithCustomParameters([
- 'job1' => 'many_attempts_and_archived_but_too_old'
+ 'job1' => 'many_attempts_and_archived_but_too_old',
]);
$workable2 = $this->workableMockWithCustomParameters([
- 'job2' => 'many_attempts_and_archived'
+ 'job2' => 'many_attempts_and_archived',
]);
$workable3 = $this->workableMockWithCustomParameters([
- 'job3' => 'many_attempts_and_archived'
+ 'job3' => 'many_attempts_and_archived',
]);
- $workable4 = [
- 'job4' => 'many_attempts_and_scheduled'
+ $workable4 = [
+ 'job4' => 'many_attempts_and_scheduled',
];
- $workable5 = [
- 'job5' => 'many_attempts_and_scheduled'
+ $workable5 = [
+ 'job5' => 'many_attempts_and_scheduled',
];
$workable6 = $this->workableMockWithCustomParameters([
- 'job6' => 'one_attempt_and_scheduled'
+ 'job6' => 'one_attempt_and_scheduled',
]);
$this->repository->archive($this->aJob($workable1)->beforeExecution($ed)->beforeExecution($ed)->afterExecution(42, $ed));
$this->clock->now();
- $threeHoursInSeconds = 3*60*60;
+ $threeHoursInSeconds = 3 * 60 * 60;
$this->clock->driftForwardBySeconds($threeHoursInSeconds);
$lowerLimit = $this->clock->now();
$this->repository->archive($this->aJob($workable2)->beforeExecution($ed)->beforeExecution($ed)->afterExecution(42, $ed));
$this->repository->archive($this->aJob($workable3)->beforeExecution($ed)->beforeExecution($ed)->afterExecution(42, $ed));
- $oneHourInSeconds = 60*60;
+ $oneHourInSeconds = 60 * 60;
$this->clock->driftForwardBySeconds($oneHourInSeconds);
$createdAt = $endedAt = $this->clock->now();
$this->repository->save($this->jobMockWithAttemptsAndCustomParameters($createdAt, $endedAt, $workable4));
@@ -229,16 +243,16 @@ public function testGetRecentJobsWithManyAttempts()
$jobs = $this->repository->recentJobsWithManyAttempts($lowerLimit, $upperLimit);
$jobsFounds = 0;
foreach ($jobs as $job) {
- $this->assertRegExp(
+ $this->assertMatchesRegularExpression(
'/many_attempts_and_archived|many_attempts_and_scheduled/',
- reset($job->export()['workable']['parameters'])
+ reset($job->export()['workable']['parameters']),
);
- $jobsFounds++;
+ ++$jobsFounds;
}
$this->assertEquals(4, $jobsFounds);
}
- public function testCountSlowRecentJobs()
+ public function testCountSlowRecentJobs(): void
{
$ed = $this->eventDispatcher;
$elapseTimeInSecondsBeforeJobsExecutionEnd = 6;
@@ -246,20 +260,20 @@ public function testCountSlowRecentJobs()
$this->repository->save(
$this->jobMockWithAttemptsAndCustomParameters(
$createdAt,
- $endedAt->after(Interval::parse($elapseTimeInSecondsBeforeJobsExecutionEnd . ' s'))
- )
+ $endedAt->after(Interval::parse($elapseTimeInSecondsBeforeJobsExecutionEnd . ' s')),
+ ),
);
$archivedJobSlowExpired = $this->aJob()->beforeExecution($ed);
$this->clock->driftForwardBySeconds($elapseTimeInSecondsBeforeJobsExecutionEnd);
$archivedJobSlowExpired->afterExecution(42, $ed);
- $threeHoursInSeconds = 3*60*60;
+ $threeHoursInSeconds = 3 * 60 * 60;
$this->clock->driftForwardBySeconds($threeHoursInSeconds);
$lowerLimit = $createdAt = $endedAt = $this->clock->now();
$this->repository->save(
$this->jobMockWithAttemptsAndCustomParameters(
$createdAt,
- $endedAt->after(Interval::parse($elapseTimeInSecondsBeforeJobsExecutionEnd . ' s'))
- )
+ $endedAt->after(Interval::parse($elapseTimeInSecondsBeforeJobsExecutionEnd . ' s')),
+ ),
);
$archivedJobSlow1 = $this->aJob()->beforeExecution($ed);
$this->clock->driftForwardBySeconds($elapseTimeInSecondsBeforeJobsExecutionEnd);
@@ -269,7 +283,7 @@ public function testCountSlowRecentJobs()
$this->clock->driftForwardBySeconds($elapseTimeInSecondsBeforeJobsExecutionEnd);
$archivedJobSlow2->afterExecution(42, $ed);
$this->repository->archive($archivedJobSlow2);
- $oneHourInSeconds = 60*60;
+ $oneHourInSeconds = 60 * 60;
$this->clock->driftForwardBySeconds($oneHourInSeconds);
$createdAt = $endedAt = $this->clock->now();
$archivedJobNotSlow = $this->aJob()->beforeExecution($ed)->afterExecution(42, $ed);
@@ -277,31 +291,31 @@ public function testCountSlowRecentJobs()
$this->repository->save(
$this->jobMockWithAttemptsAndCustomParameters(
$createdAt,
- $endedAt->after(Interval::parse($elapseTimeInSecondsBeforeJobsExecutionEnd . ' s'))
- )
+ $endedAt->after(Interval::parse($elapseTimeInSecondsBeforeJobsExecutionEnd . ' s')),
+ ),
);
- $oneHourInSeconds = 60*60;
+ $oneHourInSeconds = 60 * 60;
$this->clock->driftForwardBySeconds($oneHourInSeconds);
$upperLimit = $createdAt = $endedAt = $this->clock->now();
$this->repository->save(
$this->jobMockWithAttemptsAndCustomParameters(
$createdAt,
- $endedAt
- )
+ $endedAt,
+ ),
);
- $oneHourInSeconds = 60*60;
+ $oneHourInSeconds = 60 * 60;
$this->clock->driftForwardBySeconds($oneHourInSeconds);
$createdAt = $endedAt = $this->clock->now();
$this->repository->save(
$this->jobMockWithAttemptsAndCustomParameters(
$createdAt,
- $endedAt->after(Interval::parse($elapseTimeInSecondsBeforeJobsExecutionEnd . ' s'))
- )
+ $endedAt->after(Interval::parse($elapseTimeInSecondsBeforeJobsExecutionEnd . ' s')),
+ ),
);
$this->assertEquals(4, $this->repository->countSlowRecentJobs($lowerLimit, $upperLimit));
}
- public function testGetSlowRecentJobs()
+ public function testGetSlowRecentJobs(): void
{
$ed = $this->eventDispatcher;
$elapseTimeInSecondsBeforeJobsExecutionEnd = 6;
@@ -310,81 +324,81 @@ public function testGetSlowRecentJobs()
$this->jobMockWithAttemptsAndCustomParameters(
$createdAt,
$endedAt->after(Interval::parse($elapseTimeInSecondsBeforeJobsExecutionEnd . ' s')),
- ['job_scheduled_old' => 'slow_jobs_scheduled_but_too_old']
- )
+ ['job_scheduled_old' => 'slow_jobs_scheduled_but_too_old'],
+ ),
);
$archivedJobSlowExpired = $this->aJob($this->workableMockWithCustomParameters([
- 'job_archived_old' => 'slow_job_archived_but_too_old'
- ]))->beforeExecution($ed);
+ 'job_archived_old' => 'slow_job_archived_but_too_old',
+ ]))->beforeExecution($ed);
$this->clock->driftForwardBySeconds($elapseTimeInSecondsBeforeJobsExecutionEnd);
$archivedJobSlowExpired->afterExecution(42, $ed);
- $threeHoursInSeconds = 3*60*60;
+ $threeHoursInSeconds = 3 * 60 * 60;
$this->clock->driftForwardBySeconds($threeHoursInSeconds);
$lowerLimit = $createdAt = $endedAt = $this->clock->now();
$this->repository->save(
$this->jobMockWithAttemptsAndCustomParameters(
$createdAt,
$endedAt->after(Interval::parse($elapseTimeInSecondsBeforeJobsExecutionEnd . ' s')),
- ['job1_scheduled' => 'slow_job_recent_scheduled']
- )
+ ['job1_scheduled' => 'slow_job_recent_scheduled'],
+ ),
);
$archivedJobSlow1 = $this->aJob($this->workableMockWithCustomParameters([
- 'job1_archived' => 'slow_job_recent_archived'
- ]))->beforeExecution($ed);
+ 'job1_archived' => 'slow_job_recent_archived',
+ ]))->beforeExecution($ed);
$archivedJobSlow2 = $this->aJob($this->workableMockWithCustomParameters([
- 'job2_archived' => 'slow_job_recent_archived'
- ]))->beforeExecution($ed);
+ 'job2_archived' => 'slow_job_recent_archived',
+ ]))->beforeExecution($ed);
$this->clock->driftForwardBySeconds($elapseTimeInSecondsBeforeJobsExecutionEnd);
$archivedJobSlow1->afterExecution(41, $ed);
$this->repository->archive($archivedJobSlow1);
$archivedJobSlow2->afterExecution(42, $ed);
$this->repository->archive($archivedJobSlow2);
- $oneHourInSeconds = 60*60;
+ $oneHourInSeconds = 60 * 60;
$this->clock->driftForwardBySeconds($oneHourInSeconds);
$createdAt = $endedAt = $this->clock->now();
$archivedJobNotSlow = $this->aJob($this->workableMockWithCustomParameters([
- 'job_archived' => 'job_archived_not_slow'
- ]))->beforeExecution($ed)->afterExecution(42, $ed);
+ 'job_archived' => 'job_archived_not_slow',
+ ]))->beforeExecution($ed)->afterExecution(42, $ed);
$this->repository->save(
$this->jobMockWithAttemptsAndCustomParameters(
$createdAt,
$endedAt->after(Interval::parse($elapseTimeInSecondsBeforeJobsExecutionEnd . ' s')),
- ['job2_scheduled' => 'slow_job_recent_scheduled']
- )
+ ['job2_scheduled' => 'slow_job_recent_scheduled'],
+ ),
);
- $oneHourInSeconds = 60*60;
+ $oneHourInSeconds = 60 * 60;
$this->clock->driftForwardBySeconds($oneHourInSeconds);
$upperLimit = $createdAt = $endedAt = $this->clock->now();
$this->repository->save(
$this->jobMockWithAttemptsAndCustomParameters(
$createdAt,
$endedAt,
- ['job_scheduled' => 'job_recent_scheduled_slow']
- )
+ ['job_scheduled' => 'job_recent_scheduled_slow'],
+ ),
);
- $oneHourInSeconds = 60*60;
+ $oneHourInSeconds = 60 * 60;
$this->clock->driftForwardBySeconds($oneHourInSeconds);
$createdAt = $endedAt = $this->clock->now();
$this->repository->save(
$this->jobMockWithAttemptsAndCustomParameters(
$createdAt,
$endedAt->after(Interval::parse($elapseTimeInSecondsBeforeJobsExecutionEnd . ' s')),
- ['job3_scheduled' => 'slow_job_recent_scheduled']
- )
+ ['job3_scheduled' => 'slow_job_recent_scheduled'],
+ ),
);
$jobs = $this->repository->slowRecentJobs($lowerLimit, $upperLimit);
$jobsFounds = 0;
foreach ($jobs as $job) {
- $this->assertRegExp(
+ $this->assertMatchesRegularExpression(
'/slow_job_recent_archived|slow_job_recent_scheduled/',
- reset($job->export()['workable']['parameters'])
+ reset($job->export()['workable']['parameters']),
);
- $jobsFounds++;
+ ++$jobsFounds;
}
$this->assertEquals(4, $jobsFounds);
}
- public function testCleanOldArchived()
+ public function testCleanOldArchived(): void
{
$ed = $this->eventDispatcher;
$this->repository->archive($this->aJob()->beforeExecution($ed)->afterExecution(42, $ed));
@@ -394,7 +408,7 @@ public function testCleanOldArchived()
$this->assertEquals(0, $this->repository->countArchived());
}
- public function testCleaningOfOldArchivedCanBeLimitedByTime()
+ public function testCleaningOfOldArchivedCanBeLimitedByTime(): void
{
$ed = $this->eventDispatcher;
$this->repository->archive($this->aJob()->beforeExecution($ed)->afterExecution(42, $ed));
@@ -413,7 +427,8 @@ private function aJob($workable = null)
}
return Job::around($workable, $this->repository)
- ->scheduleAt(T\now()->before(T\seconds(5)));
+ ->scheduleAt(T\now()->before(T\seconds(5)))
+ ;
}
private function aJobToSchedule($job = null)
@@ -425,55 +440,60 @@ private function aJobToSchedule($job = null)
return new JobToSchedule($job);
}
- private function workableMock()
+ private function workableMock(): MockObject&Workable
{
return $this
- ->getMockBuilder('Recruiter\Workable')
- ->getMock();
+ ->getMockBuilder(Workable::class)
+ ->getMock()
+ ;
}
- private function workableMockWithCustomParameters($parameters)
+ private function workableMockWithCustomParameters(array $parameters): MockObject&Workable
{
$workable = $this->workableMock();
$workable
->expects($this->any())
->method('export')
- ->will($this->returnValue($parameters));
+ ->willReturn($parameters)
+ ;
+
return $workable;
}
- private function jobExecutionMock($executionParameters)
+ private function jobExecutionMock(array $executionParameters): MockObject&JobExecution
{
$jobExecutionMock = $this
- ->getMockBuilder('Recruiter\JobExecution')
- ->getMock();
+ ->getMockBuilder(JobExecution::class)
+ ->getMock()
+ ;
$jobExecutionMock->expects($this->once())
->method('export')
- ->will($this->returnValue($executionParameters));
+ ->will($this->returnValue($executionParameters))
+ ;
return $jobExecutionMock;
}
private function jobMockWithAttemptsAndCustomParameters(
- Moment $createdAt = null,
- Moment $endedAt = null,
- array $workableParameters = null
- ) {
+ ?Moment $createdAt = null,
+ ?Moment $endedAt = null,
+ ?array $workableParameters = null,
+ ): Job&MockObject {
$parameters = [
'_id' => new ObjectId(),
'created_at' => T\MongoDate::from($createdAt),
- "done" => false,
- "attempts" => 10,
- "group" => "generic",
- "scheduled_at" => T\MongoDate::from($createdAt),
- "last_execution" => [
- "started_at" => T\MongoDate::from($createdAt),
- "ended_at" => T\MongoDate::from($endedAt)
+ 'done' => false,
+ 'attempts' => 10,
+ 'group' => 'generic',
+ 'scheduled_at' => T\MongoDate::from($createdAt),
+ 'last_execution' => [
+ 'started_at' => T\MongoDate::from($createdAt),
+ 'ended_at' => T\MongoDate::from($endedAt),
+ ],
+ 'retry_policy' => [
+ 'class' => DoNotDoItAgain::class,
+ 'parameters' => [],
],
- "retry_policy" => [
- "class" => "Recruiter\\RetryPolicy\\DoNotDoItAgain",
- "parameters" => []
- ]
];
if (!empty($workableParameters)) {
@@ -482,12 +502,15 @@ private function jobMockWithAttemptsAndCustomParameters(
$parameters['workable']['parameters'] = $workableParameters;
}
$job = $this
- ->getMockBuilder('Recruiter\Job')
+ ->getMockBuilder(Job::class)
->disableOriginalConstructor()
- ->getMock();
+ ->getMock()
+ ;
$job->expects($this->once())
->method('export')
- ->will($this->returnValue($parameters));
+ ->willReturn($parameters)
+ ;
+
return $job;
}
}
diff --git a/spec/Recruiter/JobCallCustomMethodOnWorkableTest.php b/spec/Recruiter/JobCallCustomMethodOnWorkableTest.php
index 1c3cb65e..6c8970f8 100644
--- a/spec/Recruiter/JobCallCustomMethodOnWorkableTest.php
+++ b/spec/Recruiter/JobCallCustomMethodOnWorkableTest.php
@@ -2,40 +2,48 @@
namespace Recruiter;
-use Exception;
+use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
+use Recruiter\Job\Repository;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class JobCallCustomMethodOnWorkableTest extends TestCase
{
- public function setUp(): void
+ private MockObject&Workable $workable;
+ private MockObject&Repository $repository;
+ private Job $job;
+
+ protected function setUp(): void
{
$this->workable = $this
- ->getMockBuilder('Recruiter\Workable')
- ->setMethods(['export', 'import', 'asJobOf', 'send'])
- ->getMock();
+ ->getMockBuilder(DummyWorkableWithSendCustomMethod::class)
+ ->onlyMethods(['export', 'import', 'asJobOf', 'send'])
+ ->getMock()
+ ;
$this->repository = $this
- ->getMockBuilder('Recruiter\Job\Repository')
+ ->getMockBuilder(Repository::class)
->disableOriginalConstructor()
- ->getMock();
+ ->getMock()
+ ;
$this->job = Job::around($this->workable, $this->repository);
}
- public function testConfigureMethodToCallOnWorkable()
+ public function testConfigureMethodToCallOnWorkable(): void
{
$this->workable->expects($this->once())->method('send');
$this->job->methodToCallOnWorkable('send');
- $this->job->execute($this->createMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'));
+ $this->job->execute($this->createMock(EventDispatcherInterface::class));
}
- public function testRaiseExceptionWhenConfigureMethodToCallOnWorkableThatDoNotExists()
+ public function testRaiseExceptionWhenConfigureMethodToCallOnWorkableThatDoNotExists(): void
{
- $this->expectException(Exception::class);
+ $this->expectException(\Exception::class);
$this->job->methodToCallOnWorkable('methodThatDoNotExists');
}
- public function testCustomMethodIsSaved()
+ public function testCustomMethodIsSaved(): void
{
$this->job->methodToCallOnWorkable('send');
$jobExportedToDocument = $this->job->export();
@@ -44,7 +52,7 @@ public function testCustomMethodIsSaved()
$this->assertEquals('send', $jobExportedToDocument['workable']['method']);
}
- public function testCustomMethodIsConservedAfterImport()
+ public function testCustomMethodIsConservedAfterImport(): void
{
$workable = new DummyWorkableWithSendCustomMethod();
$job = Job::around($workable, $this->repository);
diff --git a/spec/Recruiter/JobSendEventsToWorkableTest.php b/spec/Recruiter/JobSendEventsToWorkableTest.php
index ab72e71e..e2bc698b 100644
--- a/spec/Recruiter/JobSendEventsToWorkableTest.php
+++ b/spec/Recruiter/JobSendEventsToWorkableTest.php
@@ -2,39 +2,42 @@
namespace Recruiter;
+use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Recruiter\Job\Event;
use Recruiter\Job\EventListener;
+use Recruiter\Job\Repository;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class JobSendEventsToWorkableTest extends TestCase
{
- public function setUp(): void
+ private MockObject&Repository $repository;
+ private MockObject&EventDispatcherInterface $dispatcher;
+
+ protected function setUp(): void
{
$this->repository = $this
- ->getMockBuilder('Recruiter\Job\Repository')
+ ->getMockBuilder(Repository::class)
->disableOriginalConstructor()
- ->getMock();
+ ->getMock()
+ ;
- $this->dispatcher = $this->createMock(
- 'Symfony\Component\EventDispatcher\EventDispatcherInterface'
- );
+ $this->dispatcher = $this->createMock(EventDispatcherInterface::class);
}
- public function testTakeRetryPolicyFromRetriableInstance()
+ public function testTakeRetryPolicyFromRetriableInstance(): void
{
- $listener = $this->createPartialMock('StdClass', ['onEvent']);
- $listener
- ->expects($this->exactly(3))
- ->method('onEvent')
- ->withConsecutive(
- [$this->equalTo('job.started'), $this->anything()],
- [$this->equalTo('job.ended'), $this->anything()],
- [$this->equalTo('job.failure.last'), $this->anything()]
- );
+ $listener = new EventListenerSpy();
$workable = new WorkableThatIsAlsoAnEventListener($listener);
$job = Job::around($workable, $this->repository);
$job->execute($this->dispatcher);
+
+ $events = $listener->events;
+ $this->assertCount(3, $events);
+ $this->assertSame('job.started', $events[0][0]);
+ $this->assertSame('job.ended', $events[1][0]);
+ $this->assertSame('job.failure.last', $events[2][0]);
}
}
@@ -42,18 +45,28 @@ class WorkableThatIsAlsoAnEventListener implements Workable, EventListener
{
use WorkableBehaviour;
- public function __construct($listener)
+ public function __construct(private readonly EventListener $listener)
{
- $this->listener = $listener;
+ $this->parameters = [];
}
- public function onEvent($channel, Event $e)
+ public function onEvent($channel, Event $ev): void
{
- return $this->listener->onEvent($channel, $e);
+ $this->listener->onEvent($channel, $ev);
}
- public function execute()
+ public function execute(): never
{
throw new \Exception();
}
}
+
+class EventListenerSpy implements EventListener
+{
+ public array $events = [];
+
+ public function onEvent($channel, Event $ev): void
+ {
+ $this->events[] = [$channel, $ev];
+ }
+}
diff --git a/spec/Recruiter/JobTakeRetryPolicyFromRetriableWorkableTest.php b/spec/Recruiter/JobTakeRetryPolicyFromRetriableWorkableTest.php
index 4bafeda3..cd056f79 100644
--- a/spec/Recruiter/JobTakeRetryPolicyFromRetriableWorkableTest.php
+++ b/spec/Recruiter/JobTakeRetryPolicyFromRetriableWorkableTest.php
@@ -2,26 +2,35 @@
namespace Recruiter;
+use PHPUnit\Framework\MockObject\Exception;
+use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
-use Timeless as T;
-use Recruiter\RetryPolicy;
+use Recruiter\Job\Repository;
+use Recruiter\RetryPolicy\BaseRetryPolicy;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class JobTakeRetryPolicyFromRetriableWorkableTest extends TestCase
{
- public function setUp(): void
+ private MockObject&Repository $repository;
+ private MockObject&EventDispatcherInterface $eventDispatcher;
+
+ protected function setUp(): void
{
$this->repository = $this
- ->getMockBuilder('Recruiter\Job\Repository')
+ ->getMockBuilder(Repository::class)
->disableOriginalConstructor()
- ->getMock();
+ ->getMock()
+ ;
- $this->eventDispatcher = $this
- ->createMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
+ $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
}
- public function testTakeRetryPolicyFromRetriableInstance()
+ /**
+ * @throws Exception
+ */
+ public function testTakeRetryPolicyFromRetriableInstance(): void
{
- $retryPolicy = $this->createMock('Recruiter\RetryPolicy\BaseRetryPolicy');
+ $retryPolicy = $this->createMock(BaseRetryPolicy::class);
$retryPolicy->expects($this->once())->method('schedule');
$workable = new WorkableThatIsAlsoRetriable($retryPolicy);
@@ -35,9 +44,9 @@ class WorkableThatIsAlsoRetriable implements Workable, Retriable
{
use WorkableBehaviour;
- public function __construct(RetryPolicy $retryWithPolicy)
+ public function __construct(private readonly RetryPolicy $retryWithPolicy)
{
- $this->retryWithPolicy = $retryWithPolicy;
+ $this->parameters = [];
}
public function retryWithPolicy(): RetryPolicy
@@ -45,7 +54,7 @@ public function retryWithPolicy(): RetryPolicy
return $this->retryWithPolicy;
}
- public function execute()
+ public function execute(): never
{
throw new \Exception();
}
diff --git a/spec/Recruiter/JobTest.php b/spec/Recruiter/JobTest.php
index 6df774bf..09f3cc8d 100644
--- a/spec/Recruiter/JobTest.php
+++ b/spec/Recruiter/JobTest.php
@@ -1,24 +1,30 @@
repository = $this
- ->getMockBuilder('Recruiter\Job\Repository')
+ ->getMockBuilder(Repository::class)
->disableOriginalConstructor()
- ->getMock();
+ ->getMock()
+ ;
}
- public function testRetryStatisticsOnFirstExecution()
+ public function testRetryStatisticsOnFirstExecution(): void
{
- $job = Job::around(new AlwaysFail, $this->repository);
+ $job = Job::around(new AlwaysFail(), $this->repository);
$retryStatistics = $job->retryStatistics();
$this->assertIsArray($retryStatistics);
$this->assertArrayHasKey('job_id', $retryStatistics);
@@ -32,11 +38,11 @@ public function testRetryStatisticsOnFirstExecution()
/**
* @depends testRetryStatisticsOnFirstExecution
*/
- public function testRetryStatisticsOnSubsequentExecutions()
+ public function testRetryStatisticsOnSubsequentExecutions(): void
{
- $job = Job::around(new AlwaysFail, $this->repository);
+ $job = Job::around(new AlwaysFail(), $this->repository);
// maybe make the argument optional
- $job->execute($this->createMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'));
+ $job->execute($this->createMock(EventDispatcherInterface::class));
$job = Job::import($job->export(), $this->repository);
$retryStatistics = $job->retryStatistics();
$this->assertEquals(1, $retryStatistics['retry_number']);
@@ -49,14 +55,14 @@ public function testRetryStatisticsOnSubsequentExecutions()
$this->assertArrayHasKey('message', $lastExecution);
$this->assertArrayHasKey('trace', $lastExecution);
$this->assertEquals("Sorry, I'm good for nothing", $lastExecution['message']);
- $this->assertRegexp("/.*AlwaysFail->execute.*/", $lastExecution['trace']);
+ $this->assertMatchesRegularExpression('/.*AlwaysFail->execute.*/', $lastExecution['trace']);
}
- public function testArrayAsGroupIsNotAllowed()
+ public function testArrayAsGroupIsNotAllowed(): void
{
- $this->expectException(RuntimeException::class);
+ $this->expectException(\RuntimeException::class);
$memoryLimit = new MemoryLimit(1);
- $job = Job::around(new AlwaysFail, $this->repository);
+ $job = Job::around(new AlwaysFail(), $this->repository);
$job->inGroup(['test']);
}
}
diff --git a/spec/Recruiter/JobToBePassedRetryStatisticsTest.php b/spec/Recruiter/JobToBePassedRetryStatisticsTest.php
index cce51d76..ec72fd4f 100644
--- a/spec/Recruiter/JobToBePassedRetryStatisticsTest.php
+++ b/spec/Recruiter/JobToBePassedRetryStatisticsTest.php
@@ -2,27 +2,36 @@
namespace Recruiter;
+use PHPUnit\Framework\MockObject\Exception;
+use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
-use Timeless as T;
+use Recruiter\Job\Repository;
use Recruiter\RetryPolicy\DoNotDoItAgain;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class JobToBePassedRetryStatisticsTest extends TestCase
{
- public function setUp(): void
+ private MockObject&Repository $repository;
+
+ protected function setUp(): void
{
$this->repository = $this
- ->getMockBuilder('Recruiter\Job\Repository')
+ ->getMockBuilder(Repository::class)
->disableOriginalConstructor()
- ->getMock();
+ ->getMock()
+ ;
}
- public function testTakeRetryPolicyFromRetriableInstance()
+ /**
+ * @throws Exception
+ */
+ public function testTakeRetryPolicyFromRetriableInstance(): void
{
$workable = new WorkableThatUsesRetryStatistics();
$job = Job::around($workable, $this->repository);
- $job->execute($this->createMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'));
- $this->assertTrue($job->done(), "Job requiring retry statistics was not executed correctly: " . var_export($job->export(), true));
+ $job->execute($this->createMock(EventDispatcherInterface::class));
+ $this->assertTrue($job->done(), 'Job requiring retry statistics was not executed correctly: ' . var_export($job->export(), true));
}
}
diff --git a/spec/Recruiter/JobToScheduleTest.php b/spec/Recruiter/JobToScheduleTest.php
index 5de7f8f7..be23900f 100644
--- a/spec/Recruiter/JobToScheduleTest.php
+++ b/spec/Recruiter/JobToScheduleTest.php
@@ -2,138 +2,152 @@
namespace Recruiter;
+use PHPUnit\Framework\MockObject\Exception;
+use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Timeless as T;
-use Recruiter\RetryPolicy;
class JobToScheduleTest extends TestCase
{
- public function setUp(): void
+ private T\ClockInterface $clock;
+ private MockObject&Job $job;
+
+ /**
+ * @throws Exception
+ */
+ protected function setUp(): void
{
$this->clock = T\clock()->stop();
- $this->job = $this
- ->getMockBuilder('Recruiter\Job')
- ->disableOriginalConstructor()
- ->getMock();
+ $this->job = $this->createMock(Job::class);
}
- public function tearDown(): void
+ protected function tearDown(): void
{
$this->clock->start();
}
- public function testInBackgroundShouldScheduleJobNow()
+ public function testInBackgroundShouldScheduleJobNow(): void
{
$this->job
->expects($this->once())
->method('scheduleAt')
->with(
- $this->equalTo($this->clock->now())
- );
+ $this->equalTo($this->clock->now()),
+ )
+ ;
- (new JobToSchedule($this->job))
+ new JobToSchedule($this->job)
->inBackground()
- ->execute();
+ ->execute()
+ ;
}
- public function testScheduledInShouldScheduleInCertainAmountOfTime()
+ public function testScheduledInShouldScheduleInCertainAmountOfTime(): void
{
$amountOfTime = T\minutes(10);
$this->job
->expects($this->once())
->method('scheduleAt')
->with(
- $this->equalTo($amountOfTime->fromNow())
- );
+ $this->equalTo($amountOfTime->fromNow()),
+ )
+ ;
- (new JobToSchedule($this->job))
+ new JobToSchedule($this->job)
->scheduleIn($amountOfTime)
- ->execute();
+ ->execute()
+ ;
}
- public function testConfigureRetryPolicy()
+ public function testConfigureRetryPolicy(): void
{
$doNotDoItAgain = new RetryPolicy\DoNotDoItAgain();
$this->job
->expects($this->once())
->method('retryWithPolicy')
- ->with($doNotDoItAgain);
+ ->with($doNotDoItAgain)
+ ;
- (new JobToSchedule($this->job))
+ new JobToSchedule($this->job)
->inBackground()
->retryWithPolicy($doNotDoItAgain)
- ->execute();
+ ->execute()
+ ;
}
- public function tesShortcutToConfigureJobToNotBeRetried()
+ public function tesShortcutToConfigureJobToNotBeRetried(): void
{
$this->job
->expects($this->once())
->method('retryWithPolicy')
- ->with($this->isInstanceOf('Recruiter\RetryPolicy\DoNotDoItAgain'));
+ ->with($this->isInstanceOf(RetryPolicy\DoNotDoItAgain::class))
+ ;
- (new JobToSchedule($this->job))
+ new JobToSchedule($this->job)
->inBackground()
->doNotRetry()
- ->execute();
+ ->execute()
+ ;
}
- public function testShouldNotExecuteJobWhenScheduled()
+ public function testShouldNotExecuteJobWhenScheduled(): void
{
$this->job
->expects($this->once())
- ->method('save');
+ ->method('save')
+ ;
$this->job
->expects($this->never())
- ->method('execute');
+ ->method('execute')
+ ;
- (new JobToSchedule($this->job))
+ new JobToSchedule($this->job)
->inBackground()
- ->execute();
+ ->execute()
+ ;
}
- public function testShouldExecuteJobWhenNotScheduled()
+ public function testShouldExecuteJobWhenNotScheduled(): void
{
$this->job
->expects($this->never())
- ->method('scheduleAt');
+ ->method('scheduleAt')
+ ;
$this->job
->expects($this->once())
- ->method('execute');
+ ->method('execute')
+ ;
- (new JobToSchedule($this->job))
- ->execute(
- $this->createMock('Symfony\Component\EventDispatcher\EventDispatcherInterface')
- );
+ new JobToSchedule($this->job)->execute();
}
- public function testConfigureMethodToCallOnWorkableInJob()
+ public function testConfigureMethodToCallOnWorkableInJob(): void
{
$this->job
->expects($this->once())
->method('methodToCallOnWorkable')
- ->with('send');
+ ->with('send')
+ ;
- (new JobToSchedule($this->job))
- ->send();
+ new JobToSchedule($this->job)
+ ->send()
+ ;
}
- public function testReturnsJobId()
+ public function testReturnsJobId(): void
{
$this->job
->expects($this->any())
->method('id')
- ->will($this->returnValue('42'));
+ ->will($this->returnValue('42'))
+ ;
$this->assertEquals(
'42',
- (new JobToSchedule($this->job))
- ->execute(
- $this->createMock('Symfony\Component\EventDispatcher\EventDispatcherInterface')
- )
+ new JobToSchedule($this->job)->execute(),
);
}
}
diff --git a/spec/Recruiter/PickAvailableWorkersTest.php b/spec/Recruiter/PickAvailableWorkersTest.php
index 5ca538a4..2170aa23 100644
--- a/spec/Recruiter/PickAvailableWorkersTest.php
+++ b/spec/Recruiter/PickAvailableWorkersTest.php
@@ -2,23 +2,30 @@
namespace Recruiter;
-use ArrayIterator;
+use MongoDB\BSON\Int64;
use MongoDB\BSON\ObjectId;
+use MongoDB\Collection;
+use MongoDB\Driver\CursorInterface;
+use MongoDB\Driver\Server;
+use PHPUnit\Framework\MockObject\Exception;
+use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
class PickAvailableWorkersTest extends TestCase
{
- public function setUp(): void
- {
- $this->repository = $this
- ->getMockBuilder('MongoDB\Collection')
- ->disableOriginalConstructor()
- ->getMock();
+ private MockObject&Collection $repository;
+ private int $workersPerUnit;
+ /**
+ * @throws Exception
+ */
+ protected function setUp(): void
+ {
+ $this->repository = $this->createMock(Collection::class);
$this->workersPerUnit = 42;
}
- public function testNoWorkersAreFound()
+ public function testNoWorkersAreFound(): void
{
$this->withNoAvailableWorkers();
@@ -27,31 +34,31 @@ public function testNoWorkersAreFound()
$this->assertEquals([], $picked);
}
- public function testFewWorkersWithNoSpecifiSkill()
+ public function testFewWorkersWithNoSpecificSkill(): void
{
$callbackHasBeenCalled = false;
$this->withAvailableWorkers(['*' => 3]);
$picked = Worker::pickAvailableWorkers($this->repository, $this->workersPerUnit);
- list ($worksOn, $workers) = $picked[0];
+ [$worksOn, $workers] = $picked[0];
$this->assertEquals('*', $worksOn);
- $this->assertEquals(3, count($workers));
+ $this->assertCount(3, $workers);
}
- public function testFewWorkersWithSameSkill()
+ public function testFewWorkersWithSameSkill(): void
{
$callbackHasBeenCalled = false;
$this->withAvailableWorkers(['send-emails' => 3]);
$picked = Worker::pickAvailableWorkers($this->repository, $this->workersPerUnit);
- list ($worksOn, $workers) = $picked[0];
+ [$worksOn, $workers] = $picked[0];
$this->assertEquals('send-emails', $worksOn);
$this->assertEquals(3, count($workers));
}
- public function testFewWorkersWithSomeDifferentSkills()
+ public function testFewWorkersWithSomeDifferentSkills(): void
{
$this->withAvailableWorkers(['send-emails' => 3, 'count-transactions' => 3]);
$picked = Worker::pickAvailableWorkers($this->repository, $this->workersPerUnit);
@@ -59,7 +66,7 @@ public function testFewWorkersWithSomeDifferentSkills()
$allSkillsGiven = [];
$totalWorkersGiven = 0;
foreach ($picked as $pickedRow) {
- list ($worksOn, $workers) = $pickedRow;
+ [$worksOn, $workers] = $pickedRow;
$allSkillsGiven[] = $worksOn;
$totalWorkersGiven += count($workers);
}
@@ -67,7 +74,7 @@ public function testFewWorkersWithSomeDifferentSkills()
$this->assertEquals(6, $totalWorkersGiven);
}
- public function testMoreWorkersThanAllowedPerUnit()
+ public function testMoreWorkersThanAllowedPerUnit(): void
{
$this->withAvailableWorkers(['send-emails' => $this->workersPerUnit + 10]);
@@ -75,21 +82,24 @@ public function testMoreWorkersThanAllowedPerUnit()
$totalWorkersGiven = 0;
foreach ($picked as $pickedRow) {
- list ($worksOn, $workers) = $pickedRow;
+ [$worksOn, $workers] = $pickedRow;
$totalWorkersGiven += count($workers);
}
$this->assertEquals($this->workersPerUnit, $totalWorkersGiven);
}
- private function withAvailableWorkers($workers)
+ /**
+ * @throws Exception
+ */
+ private function withAvailableWorkers($workers): void
{
$workersThatShouldBeFound = [];
foreach ($workers as $skill => $quantity) {
- for ($counter = 0; $counter < $quantity; $counter++) {
+ for ($counter = 0; $counter < $quantity; ++$counter) {
$workerId = new ObjectId();
- $workersThatShouldBeFound[(string)$workerId] = [
+ $workersThatShouldBeFound[(string) $workerId] = [
'_id' => $workerId,
- 'work_on' => $skill
+ 'work_on' => $skill,
];
}
}
@@ -97,7 +107,8 @@ private function withAvailableWorkers($workers)
$this->repository
->expects($this->any())
->method('find')
- ->will($this->returnValue(new ArrayIterator($workersThatShouldBeFound)));
+ ->willReturn(new FakeCursor($workersThatShouldBeFound))
+ ;
}
private function withNoAvailableWorkers()
@@ -105,7 +116,8 @@ private function withNoAvailableWorkers()
$this->repository
->expects($this->any())
->method('find')
- ->will($this->returnValue(new ArrayIterator([])));
+ ->willReturn(new FakeCursor())
+ ;
}
private function assertArrayAreEquals($expected, $given)
@@ -115,3 +127,63 @@ private function assertArrayAreEquals($expected, $given)
$this->assertEquals($expected, $given);
}
}
+
+class FakeCursor implements CursorInterface, \Iterator
+{
+ private array $data;
+
+ public function __construct(array $data = [])
+ {
+ $this->data = array_values($data);
+ }
+
+ public function getId(): Int64
+ {
+ return new Int64(42);
+ }
+
+ public function getServer(): Server
+ {
+ throw new \LogicException('Not implemented');
+ }
+
+ public function isDead(): bool
+ {
+ throw new \LogicException('Not implemented');
+ }
+
+ public function setTypeMap(array $typemap): void
+ {
+ throw new \LogicException('Not implemented');
+ }
+
+ public function toArray(): array
+ {
+ throw new \LogicException('Not implemented');
+ }
+
+ public function current(): object|array|null
+ {
+ return current($this->data);
+ }
+
+ public function next(): void
+ {
+ next($this->data);
+ }
+
+ public function key(): ?int
+ {
+ return key($this->data);
+ }
+
+ public function valid(): bool
+ {
+ return null !== key($this->data);
+ }
+
+ public function rewind(): void
+ {
+ reset($this->data);
+ }
+}
diff --git a/spec/Recruiter/RetryPolicy/ExponentialBackoffTest.php b/spec/Recruiter/RetryPolicy/ExponentialBackoffTest.php
index fa451aad..0106c203 100644
--- a/spec/Recruiter/RetryPolicy/ExponentialBackoffTest.php
+++ b/spec/Recruiter/RetryPolicy/ExponentialBackoffTest.php
@@ -1,58 +1,66 @@
jobExecutedFor(1);
$retryPolicy = new ExponentialBackoff(100, T\seconds(5));
$job->expects($this->once())
->method('scheduleIn')
- ->with(T\seconds(5));
+ ->with(T\seconds(5))
+ ;
$retryPolicy->schedule($job);
}
- public function testAfterEachFailureDoublesTheAmountOfTimeToWaitBetweenRetries()
+ public function testAfterEachFailureDoublesTheAmountOfTimeToWaitBetweenRetries(): void
{
$job = $this->jobExecutedFor(2);
$retryPolicy = new ExponentialBackoff(100, T\seconds(5));
$job->expects($this->once())
->method('scheduleIn')
- ->with(T\seconds(10));
+ ->with(T\seconds(10))
+ ;
$retryPolicy->schedule($job);
}
- public function testAfterTooManyFailuresGivesUp()
+ public function testAfterTooManyFailuresGivesUp(): void
{
$job = $this->jobExecutedFor(101);
$retryPolicy = new ExponentialBackoff(100, T\seconds(5));
$job->expects($this->once())
->method('archive')
- ->with('tried-too-many-times');
+ ->with('tried-too-many-times')
+ ;
$retryPolicy->schedule($job);
}
- public function testCanBeCreatedByTargetingAMaximumInterval()
+ public function testCanBeCreatedByTargetingAMaximumInterval(): void
{
$this->assertEquals(
ExponentialBackoff::forAnInterval(1025, T\seconds(1)),
- new ExponentialBackoff(10, 1)
+ new ExponentialBackoff(10, 1),
);
}
- private function jobExecutedFor($times)
+ private function jobExecutedFor(int $times): MockObject&JobAfterFailure
{
- $job = $this->getMockBuilder('Recruiter\JobAfterFailure')->disableOriginalConstructor()->getMock();
+ $job = $this->getMockBuilder(JobAfterFailure::class)->disableOriginalConstructor()->getMock();
$job->expects($this->any())
->method('numberOfAttempts')
- ->will($this->returnValue($times));
+ ->willReturn($times)
+ ;
+
return $job;
}
}
diff --git a/spec/Recruiter/RetryPolicy/RetriableExceptionFilterTest.php b/spec/Recruiter/RetryPolicy/RetriableExceptionFilterTest.php
index 3d2354b6..f0767844 100644
--- a/spec/Recruiter/RetryPolicy/RetriableExceptionFilterTest.php
+++ b/spec/Recruiter/RetryPolicy/RetriableExceptionFilterTest.php
@@ -2,132 +2,149 @@
namespace Recruiter\RetryPolicy;
-use Exception;
-use InvalidArgumentException;
+use PHPUnit\Framework\MockObject\Exception;
+use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
+use Recruiter\JobAfterFailure;
+use Recruiter\RetryPolicy;
class RetriableExceptionFilterTest extends TestCase
{
- public function setUp(): void
+ private MockObject&RetryPolicy $filteredRetryPolicy;
+
+ /**
+ * @throws Exception
+ */
+ protected function setUp(): void
{
- $this->filteredRetryPolicy = $this->createMock('Recruiter\RetryPolicy');
+ $this->filteredRetryPolicy = $this->createMock(RetryPolicy::class);
}
- public function testCallScheduleOnRetriableException()
+ /**
+ * @throws Exception
+ */
+ public function testCallScheduleOnRetriableException(): void
{
- $exception = $this->createMock('Exception');
- $classOfException = get_class($exception);
+ $exception = $this->createMock(\Exception::class);
+ $classOfException = $exception::class;
$filter = new RetriableExceptionFilter($this->filteredRetryPolicy, [$classOfException]);
$this->filteredRetryPolicy
->expects($this->once())
- ->method('schedule');
+ ->method('schedule')
+ ;
$filter->schedule($this->jobFailedWithException($exception));
}
- public function testDoNotCallScheduleOnNonRetriableException()
+ public function testDoNotCallScheduleOnNonRetriableException(): void
{
- $exception = $this->createMock('Exception');
- $classOfException = get_class($exception);
+ $exception = $this->createMock(\Exception::class);
+ $classOfException = $exception::class;
$filter = new RetriableExceptionFilter($this->filteredRetryPolicy, [$classOfException]);
$this->filteredRetryPolicy
->expects($this->never())
- ->method('schedule');
+ ->method('schedule')
+ ;
- $filter->schedule($this->jobFailedWithException(new Exception('Test')));
+ $filter->schedule($this->jobFailedWithException(new \Exception('Test')));
}
- public function testWhenExceptionIsNotRetriableThenArchiveTheJob()
+ public function testWhenExceptionIsNotRetriableThenArchiveTheJob(): void
{
- $exception = $this->createMock('Exception');
- $classOfException = get_class($exception);
+ $exception = $this->createMock(\Exception::class);
+ $classOfException = $exception::class;
$filter = new RetriableExceptionFilter($this->filteredRetryPolicy, [$classOfException]);
- $job = $this->jobFailedWithException(new Exception('Test'));
+ $job = $this->jobFailedWithException(new \Exception('Test'));
$job->expects($this->once())
->method('archive')
- ->with('non-retriable-exception');
+ ->with('non-retriable-exception')
+ ;
$filter->schedule($job);
}
- public function testAllExceptionsAreRetriableByDefault()
+ public function testAllExceptionsAreRetriableByDefault(): void
{
$this->filteredRetryPolicy
->expects($this->once())
- ->method('schedule');
+ ->method('schedule')
+ ;
$filter = new RetriableExceptionFilter($this->filteredRetryPolicy);
- $filter->schedule($this->jobFailedWithException(new Exception('Test')));
+ $filter->schedule($this->jobFailedWithException(new \Exception('Test')));
}
- public function testJobFailedWithSomethingThatIsNotAnException()
+ public function testJobFailedWithSomethingThatIsNotAnException(): void
{
$jobAfterFailure = $this->jobFailedWithException(null);
$jobAfterFailure
->expects($this->once())
- ->method('archive');
+ ->method('archive')
+ ;
$filter = new RetriableExceptionFilter($this->filteredRetryPolicy);
$filter->schedule($jobAfterFailure);
}
- public function testExportFilteredRetryPolicy()
+ public function testExportFilteredRetryPolicy(): void
{
$this->filteredRetryPolicy
->expects($this->once())
->method('export')
- ->will($this->returnValue(['key' => 'value']));
+ ->will($this->returnValue(['key' => 'value']))
+ ;
$filter = new RetriableExceptionFilter($this->filteredRetryPolicy);
$this->assertEquals(
[
'retriable_exceptions' => ['Exception'],
- 'filtered_retry_policy' => [
- 'class' => get_class($this->filteredRetryPolicy),
- 'parameters' => ['key' => 'value']
- ]
+ 'filtered_retry_policy' => [
+ 'class' => $this->filteredRetryPolicy::class,
+ 'parameters' => ['key' => 'value'],
+ ],
],
- $filter->export()
+ $filter->export(),
);
}
- public function testImportRetryPolicy()
+ public function testImportRetryPolicy(): void
{
$filteredRetryPolicy = new DoNotDoItAgain();
$filter = new RetriableExceptionFilter($filteredRetryPolicy);
$exported = $filter->export();
$filter = RetriableExceptionFilter::import($exported);
- $filter->schedule($this->jobFailedWithException(new Exception('Test')));
+ $filter->schedule($this->jobFailedWithException(new \Exception('Test')));
$this->assertEquals($exported, $filter->export());
}
- public function testRetriableExceptionsThatAreNotExceptions()
+ public function testRetriableExceptionsThatAreNotExceptions(): void
{
- $this->expectException(InvalidArgumentException::class);
+ $this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage("Only subclasses of Exception can be retriable exceptions, 'StdClass' is not");
$retryPolicy = new DoNotDoItAgain();
$notAnExceptionClass = 'StdClass';
new RetriableExceptionFilter($retryPolicy, [$notAnExceptionClass]);
}
-
- private function jobFailedWithException($exception)
+ private function jobFailedWithException(mixed $exception): MockObject&JobAfterFailure
{
$jobAfterFailure = $this
- ->getMockBuilder('Recruiter\JobAfterFailure')
+ ->getMockBuilder(JobAfterFailure::class)
->disableOriginalConstructor()
- ->getMock();
+ ->getMock()
+ ;
$jobAfterFailure
->expects($this->any())
->method('causeOfFailure')
- ->will($this->returnValue($exception));
+ ->willReturn($exception)
+ ;
return $jobAfterFailure;
}
diff --git a/spec/Recruiter/RetryPolicy/SelectByExceptionTest.php b/spec/Recruiter/RetryPolicy/SelectByExceptionTest.php
index 06e272bf..26ef5e9d 100644
--- a/spec/Recruiter/RetryPolicy/SelectByExceptionTest.php
+++ b/spec/Recruiter/RetryPolicy/SelectByExceptionTest.php
@@ -1,31 +1,32 @@
when(InvalidArgumentException::class)->then(new DoNotDoItAgain())
- ->when(LogicException::class)->then(new DoNotDoItAgain())
- ->build();
+ SelectByException::create()
+ ->when(\InvalidArgumentException::class)->then(new DoNotDoItAgain())
+ ->when(\LogicException::class)->then(new DoNotDoItAgain())
+ ->build()
+ ;
- $this->assertInstanceOf(RetryPolicy::class, $retryPolicy);
+ $this->expectNotToPerformAssertions();
}
- public function testCanBeExportedAndImported()
+ public function testCanBeExportedAndImported(): void
{
$retryPolicy = SelectByException::create()
- ->when(InvalidArgumentException::class)->then(new DoNotDoItAgain())
- ->when(LogicException::class)->then(new DoNotDoItAgain())
- ->build();
+ ->when(\InvalidArgumentException::class)->then(new DoNotDoItAgain())
+ ->when(\LogicException::class)->then(new DoNotDoItAgain())
+ ->build()
+ ;
$retryPolicyExported = $retryPolicy->export();
$retryPolicyImported = SelectByException::import($retryPolicyExported);
@@ -34,26 +35,30 @@ public function testCanBeExportedAndImported()
$this->assertEquals($retryPolicyExported, $retryPolicyImported->export());
}
- public function testSelectByException()
+ public function testSelectByException(): void
{
- $exception = new InvalidArgumentException('something');
+ $exception = new \InvalidArgumentException('something');
$retryPolicy = new SelectByException([
- new RetriableException(get_class($exception), RetryForever::afterSeconds(10))
+ new RetriableException($exception::class, RetryForever::afterSeconds(10)),
]);
$job = $this->jobFailedWith($exception);
$job->expects($this->once())
->method('scheduleIn')
- ->with(T\seconds(10));
+ ->with(T\seconds(10))
+ ;
$retryPolicy->schedule($job);
}
- public function testDefaultDoNotSchedule()
+ /**
+ * @throws \Exception
+ */
+ public function testDefaultDoNotSchedule(): void
{
- $exception = new Exception('something');
+ $exception = new \Exception('something');
$retryPolicy = new SelectByException([
- new RetriableException(InvalidArgumentException::class, RetryForever::afterSeconds(10))
+ new RetriableException(\InvalidArgumentException::class, RetryForever::afterSeconds(10)),
]);
$job = $this->jobFailedWith($exception);
@@ -63,12 +68,14 @@ public function testDefaultDoNotSchedule()
$retryPolicy->schedule($job);
}
- private function jobFailedWith(Exception $exception)
+ private function jobFailedWith(\Throwable $exception): MockObject&JobAfterFailure
{
- $job = $this->getMockBuilder('Recruiter\JobAfterFailure')->disableOriginalConstructor()->getMock();
+ $job = $this->getMockBuilder(JobAfterFailure::class)->disableOriginalConstructor()->getMock();
$job->expects($this->any())
->method('causeOfFailure')
- ->will($this->returnValue($exception));
+ ->willReturn($exception)
+ ;
+
return $job;
}
}
diff --git a/spec/Recruiter/RetryPolicy/TimeTableTest.php b/spec/Recruiter/RetryPolicy/TimeTableTest.php
index d6c3fbf1..d2a8499d 100644
--- a/spec/Recruiter/RetryPolicy/TimeTableTest.php
+++ b/spec/Recruiter/RetryPolicy/TimeTableTest.php
@@ -2,15 +2,17 @@
namespace Recruiter\RetryPolicy;
-use Exception;
+use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
+use Recruiter\Job;
+use Recruiter\JobAfterFailure;
use Timeless as T;
class TimeTableTest extends TestCase
{
private $scheduler;
- public function setUp(): void
+ protected function setUp(): void
{
$this->scheduler = new TimeTable([
'5 minutes ago' => '1 minute',
@@ -19,52 +21,56 @@ public function setUp(): void
]);
}
- public function testShouldRescheduleInOneMinuteWhenWasCreatedLessThanFiveMinutesAgo()
+ public function testShouldRescheduleInOneMinuteWhenWasCreatedLessThanFiveMinutesAgo(): void
{
$expectedToBeScheduledAt = T\minute(1)->fromNow()->toSecondPrecision();
$wasCreatedAt = T\seconds(10)->ago();
$job = $this->givenJobThat($wasCreatedAt);
$job->expects($this->once())
->method('scheduleAt')
- ->with($this->equalTo($expectedToBeScheduledAt));
+ ->with($this->equalTo($expectedToBeScheduledAt))
+ ;
$this->scheduler->schedule($job);
}
- public function testShouldRescheduleInFiveMinutesWhenWasCreatedLessThanOneHourAgo()
+ public function testShouldRescheduleInFiveMinutesWhenWasCreatedLessThanOneHourAgo(): void
{
$expectedToBeScheduledAt = T\minutes(5)->fromNow()->toSecondPrecision();
$wasCreatedAt = T\minutes(30)->ago();
$job = $this->givenJobThat($wasCreatedAt);
$job->expects($this->once())
->method('scheduleAt')
- ->with($this->equalTo($expectedToBeScheduledAt));
+ ->with($this->equalTo($expectedToBeScheduledAt))
+ ;
$this->scheduler->schedule($job);
}
- public function testShouldRescheduleInFiveMinutesWhenWasCreatedLessThan24HoursAgo()
+ public function testShouldRescheduleInFiveMinutesWhenWasCreatedLessThan24HoursAgo(): void
{
$expectedToBeScheduledAt = T\hour(1)->fromNow()->toSecondPrecision();
$wasCreatedAt = T\hours(3)->ago();
$job = $this->givenJobThat($wasCreatedAt);
$job->expects($this->once())
->method('scheduleAt')
- ->with($this->equalTo($expectedToBeScheduledAt));
+ ->with($this->equalTo($expectedToBeScheduledAt))
+ ;
$this->scheduler->schedule($job);
}
- public function testShouldNotBeRescheduledWhenWasCreatedMoreThan24HoursAgo()
+ public function testShouldNotBeRescheduledWhenWasCreatedMoreThan24HoursAgo(): void
{
$job = $this->jobThatWasCreated('2 days ago');
$job->expects($this->never())->method('scheduleAt');
$this->scheduler->schedule($job);
}
- public function testIsLastRetryReturnTrueIfJobWasCreatedMoreThanLastTimeSpen()
+ public function testIsLastRetryReturnTrueIfJobWasCreatedMoreThanLastTimeSpen(): void
{
- $job = $this->createMock('Recruiter\Job');
+ $job = $this->createMock(Job::class);
$job->expects($this->any())
->method('createdAt')
- ->will($this->returnValue(T\hours(3)->ago()));
+ ->will($this->returnValue(T\hours(3)->ago()))
+ ;
$tt = new TimeTable([
'1 minute ago' => '1 minute',
@@ -73,60 +79,67 @@ public function testIsLastRetryReturnTrueIfJobWasCreatedMoreThanLastTimeSpen()
$this->assertTrue($tt->isLastRetry($job));
}
- public function testIsLastRetryReturnFalseIfJobWasCreatedLessThanLastTimeSpen()
+ public function testIsLastRetryReturnFalseIfJobWasCreatedLessThanLastTimeSpen(): void
{
- $job = $this->createMock('Recruiter\Job');
+ $job = $this->createMock(Job::class);
$job->expects($this->any())
->method('createdAt')
- ->will($this->returnValue(T\hours(3)->ago()));
+ ->will($this->returnValue(T\hours(3)->ago()))
+ ;
$tt = new TimeTable([
'1 hour ago' => '1 minute',
- '24 hours ago' => '1 minute'
+ '24 hours ago' => '1 minute',
]);
$this->assertFalse($tt->isLastRetry($job));
}
- public function testInvalidTimeTableBecauseTimeWindow()
+ public function testInvalidTimeTableBecauseTimeWindow(): void
{
- $this->expectException(Exception::class);
+ $this->expectException(\Exception::class);
$tt = new TimeTable(['1 minute' => '1 second']);
}
- public function testInvalidTimeTableBecauseRescheduleTime()
+ public function testInvalidTimeTableBecauseRescheduleTime(): void
{
- $this->expectException(Exception::class);
+ $this->expectException(\Exception::class);
$tt = new TimeTable(['1 minute ago' => '1 second ago']);
}
- public function testInvalidTimeTableBecauseRescheduleTimeIsGreaterThanTimeWindow()
+ public function testInvalidTimeTableBecauseRescheduleTimeIsGreaterThanTimeWindow(): void
{
- $this->expectException(Exception::class);
+ $this->expectException(\Exception::class);
$tt = new TimeTable(['1 minute ago' => '2 minutes']);
}
- private function givenJobThat(T\Moment $wasCreatedAt)
+ private function givenJobThat(T\Moment $wasCreatedAt): MockObject&JobAfterFailure
{
- $job = $this->getMockBuilder('Recruiter\JobAfterFailure')
+ $job = $this->getMockBuilder(JobAfterFailure::class)
->disableOriginalConstructor()
- ->setMethods(['createdAt', 'scheduleAt'])
- ->getMock();
+ ->onlyMethods(['createdAt', 'scheduleAt'])
+ ->getMock()
+ ;
$job->expects($this->any())
->method('createdAt')
- ->will($this->returnValue($wasCreatedAt));
+ ->willReturn($wasCreatedAt)
+ ;
+
return $job;
}
- private function jobThatWasCreated($relativeTime)
+ private function jobThatWasCreated(string $relativeTime): MockObject&JobAfterFailure
{
- $wasCreatedAt = T\Moment::fromTimestamp(strtotime($relativeTime), T\now()->seconds());
- $job = $this->getMockBuilder('Recruiter\JobAfterFailure')
+ $wasCreatedAt = T\Moment::fromTimestamp(strtotime($relativeTime));
+ $job = $this->getMockBuilder(JobAfterFailure::class)
->disableOriginalConstructor()
- ->setMethods(['createdAt', 'scheduleAt'])
- ->getMock();
+ ->onlyMethods(['createdAt', 'scheduleAt'])
+ ->getMock()
+ ;
$job->expects($this->any())
->method('createdAt')
- ->will($this->returnValue($wasCreatedAt));
+ ->willReturn($wasCreatedAt)
+ ;
+
return $job;
}
}
diff --git a/spec/Recruiter/SchedulePolicy/CronTest.php b/spec/Recruiter/SchedulePolicy/CronTest.php
index 51dece3b..3d6804c6 100644
--- a/spec/Recruiter/SchedulePolicy/CronTest.php
+++ b/spec/Recruiter/SchedulePolicy/CronTest.php
@@ -2,7 +2,6 @@
namespace Recruiter\SchedulePolicy;
-use DateTime;
use PHPUnit\Framework\TestCase;
use Timeless\Moment;
@@ -11,15 +10,15 @@ class CronTest extends TestCase
/**
* @dataProvider cronExpressions
*/
- public function testCronCanBeExportedAndImportedWithoutDataLoss(string $cronExpression, string $expectedDate)
+ public function testCronCanBeExportedAndImportedWithoutDataLoss(string $cronExpression, string $expectedDate): void
{
- $cron = new Cron($cronExpression, DateTime::createFromFormat('Y-m-d H:i:s', '2019-01-15 15:00:00'));
+ $cron = new Cron($cronExpression, \DateTime::createFromFormat('Y-m-d H:i:s', '2019-01-15 15:00:00'));
$cron = Cron::import($cron->export());
$this->assertEquals(
- Moment::fromDateTime(new DateTime($expectedDate)),
+ Moment::fromDateTime(new \DateTime($expectedDate)),
$cron->next(),
- 'calculated schedule time is: ' . $cron->next()->format()
+ 'calculated schedule time is: ' . $cron->next()->format(),
);
}
diff --git a/spec/Recruiter/TaggableWorkableTest.php b/spec/Recruiter/TaggableWorkableTest.php
index 5479a37a..65e96aa4 100644
--- a/spec/Recruiter/TaggableWorkableTest.php
+++ b/spec/Recruiter/TaggableWorkableTest.php
@@ -2,21 +2,24 @@
namespace Recruiter;
+use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
-use Timeless as T;
-use Recruiter\Taggable;
+use Recruiter\Job\Repository;
class TaggableWorkableTest extends TestCase
{
- public function setUp(): void
+ private MockObject&Repository $repository;
+
+ protected function setUp(): void
{
$this->repository = $this
- ->getMockBuilder('Recruiter\Job\Repository')
+ ->getMockBuilder(Repository::class)
->disableOriginalConstructor()
- ->getMock();
+ ->getMock()
+ ;
}
- public function testWorkableExportsTags()
+ public function testWorkableExportsTags(): void
{
$workable = new WorkableTaggable(['a', 'b']);
$job = Job::around($workable, $this->repository);
@@ -26,7 +29,7 @@ public function testWorkableExportsTags()
$this->assertEquals(['a', 'b'], $exported['tags']);
}
- public function testCanSetTagsOnJobs()
+ public function testCanSetTagsOnJobs(): void
{
$workable = new WorkableTaggable([]);
$job = Job::around($workable, $this->repository);
@@ -37,7 +40,7 @@ public function testCanSetTagsOnJobs()
$this->assertEquals(['c'], $exported['tags']);
}
- public function testTagsAreMergedTogether()
+ public function testTagsAreMergedTogether(): void
{
$workable = new WorkableTaggable(['a', 'b']);
$job = Job::around($workable, $this->repository);
@@ -48,7 +51,7 @@ public function testTagsAreMergedTogether()
$this->assertEquals(['a', 'b', 'c'], $exported['tags']);
}
- public function testTagsAreUnique()
+ public function testTagsAreUnique(): void
{
$workable = new WorkableTaggable(['c']);
$job = Job::around($workable, $this->repository);
@@ -59,7 +62,7 @@ public function testTagsAreUnique()
$this->assertEquals(['c'], $exported['tags']);
}
- public function testEmptyTagsAreNotExported()
+ public function testEmptyTagsAreNotExported(): void
{
$workable = new WorkableTaggable([]);
$job = Job::around($workable, $this->repository);
@@ -68,7 +71,7 @@ public function testEmptyTagsAreNotExported()
$this->assertArrayNotHasKey('tags', $exported);
}
- public function testTagsAreImported()
+ public function testTagsAreImported(): void
{
$workable = new WorkableTaggable(['a', 'b']);
$job = Job::around($workable, $this->repository);
@@ -93,24 +96,21 @@ class WorkableTaggable implements Workable, Taggable
{
use WorkableBehaviour;
- private $tags;
-
- public function __construct(array $tags)
+ public function __construct(private array $tags)
{
- $this->tags = $tags;
}
- public function taggedAs()
+ public function taggedAs(): array
{
return $this->tags;
}
- public function export()
+ public function export(): array
{
return ['tags' => $this->tags];
}
- public static function import($parameters)
+ public static function import(array $parameters): static
{
return new self($parameters['tags']);
}
diff --git a/spec/Recruiter/WaitStrategyTest.php b/spec/Recruiter/WaitStrategyTest.php
index b721e1dd..62b3578f 100644
--- a/spec/Recruiter/WaitStrategyTest.php
+++ b/spec/Recruiter/WaitStrategyTest.php
@@ -7,33 +7,38 @@
class WaitStrategyTest extends TestCase
{
- public function setUp(): void
+ private T\Interval $waited;
+ private \Closure $howToWait;
+ private T\Interval $timeToWaitAtLeast;
+ private T\Interval $timeToWaitAtMost;
+
+ protected function setUp(): void
{
- $this->waited = 0;
- $this->howToWait = function($microseconds) {
- $this->waited = T\milliseconds($microseconds/1000);
+ $this->waited = T\milliseconds(0);
+ $this->howToWait = function ($microseconds): void {
+ $this->waited = T\milliseconds($microseconds / 1000);
};
$this->timeToWaitAtLeast = T\milliseconds(250);
$this->timeToWaitAtMost = T\seconds(30);
}
- public function testStartsToWaitTheMinimumAmountOfTime()
+ public function testStartsToWaitTheMinimumAmountOfTime(): void
{
$ws = new WaitStrategy(
$this->timeToWaitAtLeast,
$this->timeToWaitAtMost,
- $this->howToWait
+ $this->howToWait,
);
$ws->wait();
$this->assertEquals($this->timeToWaitAtLeast, $this->waited);
}
- public function testBackingOffIncreasesTheIntervalExponentially()
+ public function testBackingOffIncreasesTheIntervalExponentially(): void
{
$ws = new WaitStrategy(
$this->timeToWaitAtLeast,
$this->timeToWaitAtMost,
- $this->howToWait
+ $this->howToWait,
);
$ws->wait();
$this->assertEquals($this->timeToWaitAtLeast, $this->waited);
@@ -43,7 +48,7 @@ public function testBackingOffIncreasesTheIntervalExponentially()
$this->assertEquals($this->timeToWaitAtLeast->multiplyBy(4), $this->waited);
}
- public function testBackingOffCannotIncreaseTheIntervalOverAMaximum()
+ public function testBackingOffCannotIncreaseTheIntervalOverAMaximum(): void
{
$ws = new WaitStrategy(T\seconds(1), T\seconds(2), $this->howToWait);
$ws->backOff();
@@ -54,12 +59,12 @@ public function testBackingOffCannotIncreaseTheIntervalOverAMaximum()
$this->assertEquals(T\seconds(2), $this->waited);
}
- public function testGoingForwardLowersTheSleepingPeriod()
+ public function testGoingForwardLowersTheSleepingPeriod(): void
{
$ws = new WaitStrategy(
$this->timeToWaitAtLeast,
$this->timeToWaitAtMost,
- $this->howToWait
+ $this->howToWait,
);
$ws->backOff();
$ws->goForward();
@@ -67,12 +72,12 @@ public function testGoingForwardLowersTheSleepingPeriod()
$this->assertEquals($this->timeToWaitAtLeast, $this->waited);
}
- public function testTheSleepingPeriodCanBeResetToTheMinimum()
+ public function testTheSleepingPeriodCanBeResetToTheMinimum(): void
{
$ws = new WaitStrategy(
$this->timeToWaitAtLeast,
$this->timeToWaitAtMost,
- $this->howToWait
+ $this->howToWait,
);
$ws->backOff();
$ws->backOff();
@@ -83,12 +88,12 @@ public function testTheSleepingPeriodCanBeResetToTheMinimum()
$this->assertEquals($this->timeToWaitAtLeast, $this->waited);
}
- public function testGoingForwardCannotLowerTheIntervalBelowMinimum()
+ public function testGoingForwardCannotLowerTheIntervalBelowMinimum(): void
{
$ws = new WaitStrategy(
$this->timeToWaitAtLeast,
$this->timeToWaitAtMost,
- $this->howToWait
+ $this->howToWait,
);
$ws->goForward();
$ws->goForward();
diff --git a/spec/Recruiter/Workable/FactoryMethodCommandTest.php b/spec/Recruiter/Workable/FactoryMethodCommandTest.php
index 508e3991..05950dd4 100644
--- a/spec/Recruiter/Workable/FactoryMethodCommandTest.php
+++ b/spec/Recruiter/Workable/FactoryMethodCommandTest.php
@@ -1,34 +1,38 @@
myObject()
- ->myMethod('answer', 42);
+ ->myMethod('answer', 42)
+ ;
$this->assertEquals('42', $workable->execute());
}
- public function testCanBeImportedAndExported()
+ public function testCanBeImportedAndExported(): void
{
$workable = FactoryMethodCommand::from('Recruiter\Workable\DummyFactory::create')
->myObject()
- ->myMethod('answer', 42);
+ ->myMethod('answer', 42)
+ ;
$this->assertEquals(
$workable,
- FactoryMethodCommand::import($workable->export())
+ FactoryMethodCommand::import($workable->export()),
);
}
- public function testPassesRetryStatisticsAsAnAdditionalArgumentToTheLastMethodToCall()
+ public function testPassesRetryStatisticsAsAnAdditionalArgumentToTheLastMethodToCall(): void
{
$workable = FactoryMethodCommand::from('Recruiter\Workable\DummyFactory::create')
->myObject()
- ->myNeedyMethod();
+ ->myNeedyMethod()
+ ;
$this->assertEquals(2, $workable->execute(['retry_number' => 2]));
}
}
diff --git a/spec/Recruiter/Workable/ShellCommandTest.php b/spec/Recruiter/Workable/ShellCommandTest.php
index c2ad1bc8..a5cbc362 100644
--- a/spec/Recruiter/Workable/ShellCommandTest.php
+++ b/spec/Recruiter/Workable/ShellCommandTest.php
@@ -1,22 +1,23 @@
assertEquals('42', $workable->execute());
}
- public function testCanBeImportedAndExported()
+ public function testCanBeImportedAndExported(): void
{
$workable = ShellCommand::fromCommandLine('echo 42');
$this->assertEquals(
$workable,
- ShellCommand::import($workable->export())
+ ShellCommand::import($workable->export()),
);
}
}
diff --git a/spec/Recruiter/WorkablePersistenceTest.php b/spec/Recruiter/WorkablePersistenceTest.php
index 95fba471..283b528f 100644
--- a/spec/Recruiter/WorkablePersistenceTest.php
+++ b/spec/Recruiter/WorkablePersistenceTest.php
@@ -6,12 +6,12 @@
class WorkablePersistenceTest extends TestCase
{
- public function testCanBeExportedAndImported()
+ public function testCanBeExportedAndImported(): void
{
$job = new SomethingWorkable(['key' => 'value']);
$this->assertEquals(
$job,
- SomethingWorkable::import($job->export())
+ SomethingWorkable::import($job->export()),
);
}
}
diff --git a/spec/Recruiter/WorkerProcessTest.php b/spec/Recruiter/WorkerProcessTest.php
index 8ef203fa..209ea21b 100644
--- a/spec/Recruiter/WorkerProcessTest.php
+++ b/spec/Recruiter/WorkerProcessTest.php
@@ -2,74 +2,85 @@
namespace Recruiter;
+use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
+use Recruiter\Worker\Process;
+use Recruiter\Worker\Repository;
+use Sink\BlackHole;
class WorkerProcessTest extends TestCase
{
- public function setUp(): void
+ private int $pid;
+ private MockObject&Repository $repository;
+
+ protected function setUp(): void
{
$this->pid = 4242;
- $this->repository = $this->getMockBuilder('Recruiter\Worker\Repository')
+ $this->repository = $this->getMockBuilder(Repository::class)
->disableOriginalConstructor()
- ->getMock();
+ ->getMock()
+ ;
}
- public function testIfNotAliveWhenIsNotAliveReturnsItself()
+ public function testIfNotAliveWhenIsNotAliveReturnsItself(): void
{
$process = $this->givenWorkerProcessDead();
- $this->assertInstanceOf('Recruiter\Worker\Process', $process->ifDead());
+ $this->assertInstanceOf(Process::class, $process->ifDead());
}
- public function testIfNotAliveWhenIsAliveReturnsBlackHole()
+ public function testIfNotAliveWhenIsAliveReturnsBlackHole(): void
{
$process = $this->givenWorkerProcessAlive();
- $this->assertInstanceOf('Sink\BlackHole', $process->ifDead());
+ $this->assertInstanceOf(BlackHole::class, $process->ifDead());
}
- public function testRetireWorkerIfNotAlive()
+ public function testRetireWorkerIfNotAlive(): void
{
$this->repository
->expects($this->once())
->method('retireWorkerWithPid')
- ->with($this->pid);
+ ->with($this->pid)
+ ;
$process = $this->givenWorkerProcessDead();
$process->cleanUp($this->repository);
}
- public function testDoNotRetireWorkerIfAlive()
+ public function testDoNotRetireWorkerIfAlive(): void
{
$this->repository
->expects($this->never())
->method('retireWorkerWithPid')
- ->with($this->pid);
+ ->with($this->pid)
+ ;
$process = $this->givenWorkerProcessAlive();
$process->cleanUp($this->repository);
}
-
- private function givenWorkerProcessAlive()
+ private function givenWorkerProcessAlive(): MockObject&Process
{
return $this->givenWorkerProcess(true);
}
- private function givenWorkerProcessDead()
+ private function givenWorkerProcessDead(): MockObject&Process
{
return $this->givenWorkerProcess(false);
}
- private function givenWorkerProcess($alive)
+ private function givenWorkerProcess(bool $alive): MockObject&Process
{
- $process = $this->getMockBuilder('Recruiter\Worker\Process')
- ->setMethods(['isAlive'])
+ $process = $this->getMockBuilder(Process::class)
+ ->onlyMethods(['isAlive'])
->setConstructorArgs([$this->pid])
- ->getMock();
+ ->getMock()
+ ;
$process->expects($this->any())
->method('isAlive')
- ->will($this->returnValue($alive));
+ ->willReturn($alive)
+ ;
return $process;
}
diff --git a/spec/Sink/BlackHoleTest.php b/spec/Sink/BlackHoleTest.php
index 82867707..2fa25654 100644
--- a/spec/Sink/BlackHoleTest.php
+++ b/spec/Sink/BlackHoleTest.php
@@ -6,61 +6,61 @@
class BlackHoleTest extends TestCase
{
- public function testMethodCall()
+ public function testMethodCall(): void
{
$instance = new BlackHole();
- $this->assertInstanceOf('Sink\BlackHole', $instance->whateverMethod());
+ $this->assertInstanceOf(BlackHole::class, $instance->whateverMethod());
}
- public function testGetter()
+ public function testGetter(): void
{
$instance = new BlackHole();
- $this->assertInstanceOf('Sink\BlackHole', $instance->whateverProperty);
+ $this->assertInstanceOf(BlackHole::class, $instance->whateverProperty);
}
- public function testSetterReturnsTheValue()
+ public function testSetterReturnsTheValue(): void
{
$instance = new BlackHole();
$this->assertEquals(42, $instance->whateverProperty = 42);
}
- public function testNothingIsSet()
+ public function testNothingIsSet(): void
{
$instance = new BlackHole();
$instance->whateverProperty = 42;
$this->assertFalse(isset($instance->whateverProperty));
}
- public function testToString()
+ public function testToString(): void
{
$instance = new BlackHole();
$this->assertEquals('', (string) $instance);
}
- public function testInvoke()
+ public function testInvoke(): void
{
$instance = new BlackHole();
- $this->assertInstanceOf('Sink\BlackHole', $instance());
+ $this->assertInstanceOf(BlackHole::class, $instance());
}
- public function testCallStatic()
+ public function testCallStatic(): void
{
$instance = BlackHole::whateverStaticMethod();
- $this->assertInstanceOf('Sink\BlackHole', $instance);
+ $this->assertInstanceOf(BlackHole::class, $instance);
}
- public function testIsIterableButItIsAlwaysEmpty()
+ public function testIsIterableButItIsAlwaysEmpty(): void
{
$instance = new BlackHole();
$this->assertEmpty(iterator_to_array($instance));
}
- public function testIsAccessibleAsAnArrayAlwaysGetItself()
+ public function testIsAccessibleAsAnArrayAlwaysGetItself(): void
{
$instance = new BlackHole();
- $this->assertInstanceOf('Sink\BlackHole', $instance[42]);
- $this->assertInstanceOf('Sink\BlackHole', $instance['aString']);
- $this->assertInstanceOf('Sink\BlackHole', $instance[[1,2,3]]);
+ $this->assertInstanceOf(BlackHole::class, $instance[42]);
+ $this->assertInstanceOf(BlackHole::class, $instance['aString']);
+ $this->assertInstanceOf(BlackHole::class, $instance[[1, 2, 3]]);
}
/* public function testIsAccessibleAsAnArrayExists() */
diff --git a/spec/Timeless/IntervalFormatTest.php b/spec/Timeless/IntervalFormatTest.php
index 5a697c11..208368bf 100644
--- a/spec/Timeless/IntervalFormatTest.php
+++ b/spec/Timeless/IntervalFormatTest.php
@@ -6,7 +6,7 @@
class IntervalFormatTest extends TestCase
{
- public function testFormatExtended()
+ public function testFormatExtended(): void
{
$this->assertEquals('4 milliseconds', milliseconds(4)->format('milliseconds'));
$this->assertEquals('1 second', milliseconds(1000)->format('seconds'));
@@ -19,7 +19,7 @@ public function testFormatExtended()
$this->assertEquals('1 year', months(12)->format('years'));
}
- public function testFormatShort()
+ public function testFormatShort(): void
{
$this->assertEquals('4ms', milliseconds(4)->format('ms'));
$this->assertEquals('1s', milliseconds(1000)->format('s'));
diff --git a/spec/Timeless/IntervalParseTest.php b/spec/Timeless/IntervalParseTest.php
index c3f8f210..bbd276d1 100644
--- a/spec/Timeless/IntervalParseTest.php
+++ b/spec/Timeless/IntervalParseTest.php
@@ -2,12 +2,11 @@
namespace Timeless;
-use DateInterval;
use PHPUnit\Framework\TestCase;
class IntervalParseTest extends TestCase
{
- public function testParseExtendedFormat()
+ public function testParseExtendedFormat(): void
{
$this->assertEquals(milliseconds(4), Interval::parse('4 milliseconds'));
$this->assertEquals(milliseconds(4), Interval::parse('4milliseconds'));
@@ -66,7 +65,7 @@ public function testParseExtendedFormat()
$this->assertEquals(years(1), Interval::parse('1 year'));
}
- public function testParseShortFormat()
+ public function testParseShortFormat(): void
{
$this->assertEquals(milliseconds(4), Interval::parse('4 ms'));
$this->assertEquals(milliseconds(4), Interval::parse('4ms'));
@@ -86,21 +85,21 @@ public function testParseShortFormat()
$this->assertEquals(years(4), Interval::parse('4y'));
}
- public function testFromDateInterval()
+ public function testFromDateInterval(): void
{
- $this->assertEquals(days(2), Interval::fromDateInterval(new DateInterval('P2D')));
- $this->assertEquals(minutes(10), Interval::fromDateInterval(new DateInterval('PT10M')));
- $this->assertEquals(days(2)->add(minutes(10)), Interval::fromDateInterval(new DateInterval('P2DT10M')));
+ $this->assertEquals(days(2), Interval::fromDateInterval(new \DateInterval('P2D')));
+ $this->assertEquals(minutes(10), Interval::fromDateInterval(new \DateInterval('PT10M')));
+ $this->assertEquals(days(2)->add(minutes(10)), Interval::fromDateInterval(new \DateInterval('P2DT10M')));
}
- public function testNumberAsIntervalFormat()
+ public function testNumberAsIntervalFormat(): void
{
$this->expectException(InvalidIntervalFormat::class);
$this->expectExceptionMessage("Maybe you mean '5 seconds' or something like that?");
Interval::parse(5);
}
- public function testBadString()
+ public function testBadString(): void
{
$this->expectException(InvalidIntervalFormat::class);
Interval::parse('whatever');
diff --git a/spec/Timeless/MongoDateTest.php b/spec/Timeless/MongoDateTest.php
index 093556e1..ad7bb26e 100644
--- a/spec/Timeless/MongoDateTest.php
+++ b/spec/Timeless/MongoDateTest.php
@@ -1,4 +1,5 @@
forAll(
- Generator\choose(0, 1500 * 1000 * 1000)
+ Generator\choose(0, 1500 * 1000 * 1000),
)
- ->then(function ($milliseconds) {
+ ->then(function ($milliseconds): void {
$moment = new Moment($milliseconds);
$this->assertEquals(
$moment,
- MongoDate::toMoment(MongoDate::from($moment))
+ MongoDate::toMoment(MongoDate::from($moment)),
);
- });
+ })
+ ;
}
}
diff --git a/src/Recruiter/AlreadyRunningException.php b/src/Recruiter/AlreadyRunningException.php
index a534409a..172e4bd2 100644
--- a/src/Recruiter/AlreadyRunningException.php
+++ b/src/Recruiter/AlreadyRunningException.php
@@ -2,8 +2,6 @@
namespace Recruiter;
-use Exception;
-
-class AlreadyRunningException extends Exception
+class AlreadyRunningException extends \Exception
{
}
diff --git a/src/Recruiter/CannotRetireWorkerAtWorkException.php b/src/Recruiter/CannotRetireWorkerAtWorkException.php
index 452d5359..5e7864cd 100644
--- a/src/Recruiter/CannotRetireWorkerAtWorkException.php
+++ b/src/Recruiter/CannotRetireWorkerAtWorkException.php
@@ -1,8 +1,7 @@
repository = $repository;
}
public function cleanArchived(Interval $gracePeriod)
@@ -25,7 +19,7 @@ public function cleanArchived(Interval $gracePeriod)
return $this->repository->cleanArchived($upperLimit);
}
- public function cleanScheduled(Interval $gracePeriod = null)
+ public function cleanScheduled(?Interval $gracePeriod = null)
{
$upperLimit = T\now();
if (!is_null($gracePeriod)) {
diff --git a/src/Recruiter/Command/RecruiterJobCommand.php b/src/Recruiter/Command/RecruiterJobCommand.php
index d5f12374..59854c62 100644
--- a/src/Recruiter/Command/RecruiterJobCommand.php
+++ b/src/Recruiter/Command/RecruiterJobCommand.php
@@ -1,25 +1,22 @@
recruiter = $recruiter;
}
- protected function configure()
+ protected function configure(): void
{
$this
->setName('recruiter:command')
@@ -27,16 +24,19 @@ protected function configure()
->addArgument(
'shell_command',
InputArgument::REQUIRED,
- 'The command to run'
+ 'The command to run',
)
;
}
- protected function execute(InputInterface $input, OutputInterface $output)
+ protected function execute(InputInterface $input, OutputInterface $output): int
{
ShellCommand::fromCommandLine($input->getArgument('shell_command'))
->asJobOf($this->recruiter)
->inBackground()
- ->execute();
+ ->execute()
+ ;
+
+ return self::SUCCESS;
}
}
diff --git a/src/Recruiter/Factory.php b/src/Recruiter/Factory.php
index ddf73c86..f863c0c5 100644
--- a/src/Recruiter/Factory.php
+++ b/src/Recruiter/Factory.php
@@ -3,13 +3,13 @@
namespace Recruiter;
use MongoDB\Client;
+use MongoDB\Database;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use Recruiter\Infrastructure\Persistence\Mongodb\URI;
-use UnexpectedValueException;
class Factory
{
- public function getMongoDb(URI $uri, array $options = [])
+ public function getMongoDb(URI $uri, array $options = []): Database
{
try {
$optionsWithMajorityConcern = ['w' => 'majority'];
@@ -22,19 +22,13 @@ public function getMongoDb(URI $uri, array $options = [])
'document' => 'array',
'root' => 'array',
],
- ], $options)
+ ], $options),
);
$client->listDatabases(); // in order to avoid lazy connections and catch eventually connection exceptions here
+
return $client->selectDatabase($uri->database());
} catch (DriverRuntimeException $e) {
- throw new UnexpectedValueException(
- sprintf(
- "'No MongoDB running at '%s'",
- $uri->__toString()
- ),
- $e->getCode(),
- $e
- );
+ throw new \UnexpectedValueException(sprintf("'No MongoDB running at '%s'", $uri->__toString()), $e->getCode(), $e);
}
}
}
diff --git a/src/Recruiter/Finalizable.php b/src/Recruiter/Finalizable.php
index ccdec82d..c7433dcc 100644
--- a/src/Recruiter/Finalizable.php
+++ b/src/Recruiter/Finalizable.php
@@ -1,17 +1,16 @@
factory = $factory;
- $this->logger = $logger;
}
- protected function configure()
+ protected function configure(): void
{
$this
->setName('bko:analytics')
@@ -55,18 +35,18 @@ protected function configure()
't',
InputOption::VALUE_REQUIRED,
'HOSTNAME[:PORT][/DB] MongoDB coordinates',
- 'mongodb://localhost:27017/recruiter'
+ (string) MongoURI::fromEnvironment(),
)
->addOption(
'group',
'g',
InputOption::VALUE_REQUIRED,
- 'limit analytics to a specific job group'
+ 'limit analytics to a specific job group',
)
;
}
- protected function execute(InputInterface $input, OutputInterface $output)
+ protected function execute(InputInterface $input, OutputInterface $output): int
{
/** @var string */
$target = $input->getOption('target');
@@ -88,7 +68,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
->setRows([array_values($analytic)])
;
- for ($i = 0; $i < count($analytic); $i++) {
+ for ($i = 0; $i < count($analytic); ++$i) {
$table->setColumnStyle($i, $rightAligned);
$table->setColumnWidth($i, $columnsWidth);
}
@@ -96,6 +76,8 @@ protected function execute(InputInterface $input, OutputInterface $output)
$table->render();
echo PHP_EOL;
}
+
+ return self::SUCCESS;
}
private function calculateColumnsWidth(array $analytics): int
@@ -105,8 +87,8 @@ private function calculateColumnsWidth(array $analytics): int
$maxColumns = max($maxColumns, count($analytic));
}
- // casual constants, found by try and error
- $terminalWidth = (new Terminal())->getWidth() - (($maxColumns + 2) * 2);
+ // casual constants, found by trial and error
+ $terminalWidth = new Terminal()->getWidth() - (($maxColumns + 2) * 2);
return intval(floor($terminalWidth / $maxColumns));
}
diff --git a/src/Recruiter/Infrastructure/Command/Bko/JobRecoverCommand.php b/src/Recruiter/Infrastructure/Command/Bko/JobRecoverCommand.php
index c484c085..960ba347 100644
--- a/src/Recruiter/Infrastructure/Command/Bko/JobRecoverCommand.php
+++ b/src/Recruiter/Infrastructure/Command/Bko/JobRecoverCommand.php
@@ -1,9 +1,9 @@
factory = $factory;
- $this->logger = $logger;
}
protected function configure()
@@ -64,23 +39,23 @@ protected function configure()
't',
InputOption::VALUE_REQUIRED,
'HOSTNAME[:PORT][/DB] MongoDB coordinates',
- 'mongodb://localhost:27017/recruiter'
+ (string) MongoURI::fromEnvironment(),
)
->addOption(
'scheduleAt',
's',
InputOption::VALUE_REQUIRED,
- 're-scheduling the job at specific datetime'
+ 're-scheduling the job at specific datetime',
)
->addArgument(
'jobId',
InputArgument::REQUIRED,
- 'the id of the job in archived collection to be recovered'
+ 'the id of the job in archived collection to be recovered',
)
;
}
- protected function execute(InputInterface $input, OutputInterface $output)
+ protected function execute(InputInterface $input, OutputInterface $output): int
{
/** @var string */
$target = $input->getOption('target');
@@ -99,16 +74,19 @@ protected function execute(InputInterface $input, OutputInterface $output)
if ($input->getOption('scheduleAt')) {
/** @var string */
$scheduleAt = $input->getOption('scheduleAt');
- $job->scheduleAt(Moment::fromDateTime(new DateTime($scheduleAt)));
+ $job->scheduleAt(Moment::fromDateTime(new \DateTime($scheduleAt)));
} else {
$job->scheduleAt(T\now());
}
$job
->scheduledBy('recovering-archived-job', $archivedJobId, -1)
- ->save();
+ ->save()
+ ;
$output->writeln("Job recovered, new job id is `{$job->id()}`");
+
+ return self::SUCCESS;
}
private function createJobFromAnArchivedJob(Job $archivedJob, JobRepository $repository): Job
diff --git a/src/Recruiter/Infrastructure/Command/Bko/RemoveSchedulerCommand.php b/src/Recruiter/Infrastructure/Command/Bko/RemoveSchedulerCommand.php
index f7798df2..df3aa187 100644
--- a/src/Recruiter/Infrastructure/Command/Bko/RemoveSchedulerCommand.php
+++ b/src/Recruiter/Infrastructure/Command/Bko/RemoveSchedulerCommand.php
@@ -1,4 +1,5 @@
factory = $factory;
- $this->logger = $logger;
}
protected function configure()
@@ -59,7 +40,7 @@ protected function configure()
't',
InputOption::VALUE_REQUIRED,
'HOSTNAME[:PORT][/DB] MongoDB coordinates',
- 'mongodb://localhost:27017/recruiter'
+ (string) MongoURI::fromEnvironment(),
)
;
}
@@ -72,12 +53,13 @@ protected function initialize(InputInterface $input, OutputInterface $output)
$this->schedulerRepository = new SchedulerRepository($db);
}
- protected function execute(InputInterface $input, OutputInterface $output)
+ protected function execute(InputInterface $input, OutputInterface $output): int
{
$outputData = $this->buildOutputData();
if (!$outputData) {
$output->writeln('There are no schedulers yet.');
- return null;
+
+ return self::SUCCESS;
}
$this->printTable($outputData, $output);
@@ -89,15 +71,18 @@ protected function execute(InputInterface $input, OutputInterface $output)
$this->schedulerRepository->deleteByUrn($selectedUrn);
$this->logger->info("[Recruiter] the scheduler with urn `$selectedUrn` was deleted!");
}
+
+ return self::SUCCESS;
}
private function selectUrnToDelete(array $urns, InputInterface $input, OutputInterface $output)
{
+ /** @var QuestionHelper $helper */
$helper = $this->getHelper('question');
$question = new ChoiceQuestion(
'Please select the scheduler which you want delete',
$urns,
- null
+ null,
);
$question->setErrorMessage('scheduler %s is invalid.');
@@ -111,7 +96,7 @@ private function selectUrnToDelete(array $urns, InputInterface $input, OutputInt
return $selectedUrn;
}
- private function printTable(array $data, OutputInterface $output)
+ private function printTable(array $data, OutputInterface $output): void
{
$rows = [];
foreach ($data as $row) {
@@ -129,13 +114,13 @@ private function printTable(array $data, OutputInterface $output)
echo PHP_EOL;
}
- protected function buildOutputData()
+ protected function buildOutputData(): ?array
{
$outputData = [];
$i = 0;
$schedulers = $this->schedulerRepository->all();
- if (! $schedulers) {
+ if (!$schedulers) {
return null;
}
@@ -144,7 +129,7 @@ protected function buildOutputData()
$info = [
'createdAt' => $data['created_at']->toDateTime()->format('c'),
- 'lastScheduling' => ($data['last_scheduling']['scheduled_at'])->toDateTime()->format('c'),
+ 'lastScheduling' => $data['last_scheduling']['scheduled_at']->toDateTime()->format('c'),
'workable' => $data['job']['workable']['class'],
'policy' => $scheduler->schedulePolicy()->export(),
];
@@ -164,7 +149,7 @@ protected function buildOutputData()
}
$outputData[] = [
- '' => "" . $i++ . "",
+ '' => '' . $i++ . '',
'urn' => $data['urn'],
'info' => $infoString,
];
diff --git a/src/Recruiter/Infrastructure/Command/CleanerCommand.php b/src/Recruiter/Infrastructure/Command/CleanerCommand.php
index 6a83f108..ee0ff3d3 100644
--- a/src/Recruiter/Infrastructure/Command/CleanerCommand.php
+++ b/src/Recruiter/Infrastructure/Command/CleanerCommand.php
@@ -1,74 +1,39 @@
factory = $factory;
- $this->logger = $logger;
}
public static function toRobustCommand(Factory $factory, LoggerInterface $logger): RobustCommandRunner
@@ -84,7 +49,7 @@ public function execute(): bool
$this->log(sprintf(
'[%s] cleaned up %d old jobs from the archive' . PHP_EOL,
$memoryUsage->format(),
- $numberOfJobsCleaned
+ $numberOfJobsCleaned,
), LogLevel::INFO);
$this->log(sprintf('going to sleep for %sms', $this->waitStrategy->current()), LogLevel::DEBUG);
@@ -94,9 +59,10 @@ public function execute(): bool
return $numberOfJobsCleaned > 0;
}
- public function shutdown(?Throwable $e = null): bool
+ public function shutdown(?\Throwable $e = null): bool
{
$this->log('ok, see you space cowboy...', LogLevel::INFO);
+
return true;
}
@@ -127,8 +93,10 @@ public function description(): string
public function definition(): InputDefinition
{
+ $defaultMongoUri = (string) MongoURI::fromEnvironment();
+
return new InputDefinition([
- new InputOption('target', 't', InputOption::VALUE_REQUIRED, 'HOSTNAME[:PORT][/DB] MongoDB coordinates', 'mongodb://localhost:27017/recruiter'),
+ new InputOption('target', 't', InputOption::VALUE_REQUIRED, 'HOSTNAME[:PORT][/DB] MongoDB coordinates', $defaultMongoUri),
new InputOption('clean-after', 'c', InputOption::VALUE_REQUIRED, 'delete jobs after :period', '5days'),
new InputOption('wait-at-least', null, InputOption::VALUE_REQUIRED, 'Time to wait at least before to search for jobs to clear', '1m'),
new InputOption('wait-at-most', null, InputOption::VALUE_REQUIRED, 'Upper limit of time to wait before next polling', '3m'),
@@ -146,7 +114,7 @@ public function init(InputInterface $input): void
$this->waitStrategy = new ExponentialBackoffStrategy(
Interval::parse($input->getOption('wait-at-least'))->ms(),
- Interval::parse($input->getOption('wait-at-most'))->ms()
+ Interval::parse($input->getOption('wait-at-most'))->ms(),
);
$this->memoryLimit = new MemoryLimit($input->getOption('memory-limit'));
$this->gracePeriod = Interval::parse($input->getOption('clean-after'));
@@ -168,7 +136,7 @@ private function log(string $message, string $level = LogLevel::DEBUG): void
'program' => $this->name(),
'datetime' => date('c'),
'pid' => posix_getpid(),
- ]
+ ],
);
}
}
diff --git a/src/Recruiter/Infrastructure/Command/RecruiterCommand.php b/src/Recruiter/Infrastructure/Command/RecruiterCommand.php
index 8d06743c..287ab84c 100644
--- a/src/Recruiter/Infrastructure/Command/RecruiterCommand.php
+++ b/src/Recruiter/Infrastructure/Command/RecruiterCommand.php
@@ -1,11 +1,14 @@
factory = $factory;
- $this->logger = $logger;
+ private Recruiter $recruiter;
+ private Interval $consideredDeadAfter;
+ private LeadershipStrategy $leadershipStrategy;
+ private WaitStrategy $waitStrategy;
+ private MemoryLimit $memoryLimit;
+
+ public function __construct(private readonly Factory $factory, private readonly LoggerInterface $logger)
+ {
}
public static function toRobustCommand(Factory $factory, LoggerInterface $logger): RobustCommandRunner
@@ -89,7 +52,7 @@ public function execute(): bool
return count($assignment) > 0;
}
- private function rollbackLockedJobs()
+ private function rollbackLockedJobs(): void
{
$rollbackStartAt = microtime(true);
$rolledBack = $this->recruiter->rollbackLockedJobs();
@@ -102,7 +65,7 @@ private function rollbackLockedJobs()
private function assignJobsToWorkers(): array
{
$pickStartAt = microtime(true);
- list($assignment, $actualNumber) = $this->recruiter->assignJobsToWorkers();
+ [$assignment, $actualNumber] = $this->recruiter->assignJobsToWorkers();
$pickEndAt = microtime(true);
foreach ($assignment as $worker => $job) {
$this->log(sprintf(' tried to assign job `%s` to worker `%s`', $job, $worker), LogLevel::INFO);
@@ -114,7 +77,7 @@ private function assignJobsToWorkers(): array
$memoryUsage->format(),
count($assignment),
($pickEndAt - $pickStartAt) * 1000,
- $actualNumber
+ $actualNumber,
), LogLevel::DEBUG);
$this->memoryLimit->ensure($memoryUsage);
@@ -128,27 +91,27 @@ private function scheduleRepeatableJobs(): void
$this->recruiter->scheduleRepeatableJobs();
$creationEndAt = microtime(true);
- //FIXME:! log every job created?
+ // FIXME:! log every job created?
/* foreach ($assignment as $worker => $job) { */
/* $this->log(sprintf(' tried to assign job `%s` to worker `%s`', $job, $worker)); */
/* } */
$this->log(sprintf(
'creation of jobs from crontab in %fms',
- ($creationEndAt - $creationStartAt) * 1000
+ ($creationEndAt - $creationStartAt) * 1000,
));
}
- private function retireDeadWorkers()
+ private function retireDeadWorkers(): void
{
$unlockedJobs = $this->recruiter->retireDeadWorkers(
- new DateTimeImmutable(),
- $this->consideredDeadAfter
+ new \DateTimeImmutable(),
+ $this->consideredDeadAfter,
);
$this->log(sprintf('unlocked %d jobs due to dead workers', $unlockedJobs), LogLevel::DEBUG);
}
- public function shutdown(?Throwable $e = null): bool
+ public function shutdown(?\Throwable $e = null): bool
{
$this->recruiter->bye();
$this->log('ok, see you space cowboy...', LogLevel::INFO);
@@ -183,8 +146,10 @@ public function description(): string
public function definition(): InputDefinition
{
+ $defaultMongoUri = (string) MongoURI::fromEnvironment();
+
return new InputDefinition([
- new InputOption('target', 't', InputOption::VALUE_REQUIRED, 'HOSTNAME[:PORT][/DB] MongoDB coordinates', 'mongodb://localhost:27017/recruiter'),
+ new InputOption('target', 't', InputOption::VALUE_REQUIRED, 'HOSTNAME[:PORT][/DB] MongoDB coordinates', $defaultMongoUri),
new InputOption('backoff-to', 'b', InputOption::VALUE_REQUIRED, 'Upper limit of time to wait before next polling (milliseconds)', '1600ms'),
new InputOption('backoff-from', 'f', InputOption::VALUE_REQUIRED, 'Time to wait at least before to search for new jobs (milliseconds)', '200ms'),
new InputOption('lease-time', 'l', InputOption::VALUE_REQUIRED, 'Maximum time to hold a lock before a refresh', '60s'),
@@ -206,7 +171,7 @@ public function init(InputInterface $input): void
$this->waitStrategy = new ExponentialBackoffStrategy(
Interval::parse($input->getOption('backoff-from'))->ms(),
- Interval::parse($input->getOption('backoff-to'))->ms()
+ Interval::parse($input->getOption('backoff-to'))->ms(),
);
$this->consideredDeadAfter = Interval::parse($input->getOption('considered-dead-after'));
@@ -233,7 +198,7 @@ private function log(string $message, string $level = LogLevel::DEBUG): void
'program' => $this->name(),
'datetime' => date('c'),
'pid' => posix_getpid(),
- ]
+ ],
);
}
diff --git a/src/Recruiter/Infrastructure/Command/WorkerCommand.php b/src/Recruiter/Infrastructure/Command/WorkerCommand.php
index 45de89ad..81927a4a 100644
--- a/src/Recruiter/Infrastructure/Command/WorkerCommand.php
+++ b/src/Recruiter/Infrastructure/Command/WorkerCommand.php
@@ -1,21 +1,20 @@
factory = $factory;
- $this->logger = $logger;
}
public static function toRobustCommand(Factory $factory, LoggerInterface $logger): RobustCommandRunner
@@ -81,7 +67,7 @@ public function execute(): bool
return (bool) $doneSomeWork;
}
- public function shutdown(?Throwable $e = null): bool
+ public function shutdown(?\Throwable $e = null): bool
{
if ($this->worker->retireIfNotAssigned()) {
$this->log(sprintf('worker `%s` retired', $this->worker->id()), LogLevel::INFO);
@@ -119,8 +105,10 @@ public function description(): string
public function definition(): InputDefinition
{
+ $defaultMongoUri = (string) MongoURI::fromEnvironment();
+
return new InputDefinition([
- new InputOption('target', 't', InputOption::VALUE_REQUIRED, 'HOSTNAME[:PORT][/DB] MongoDB coordinates', 'mongodb://localhost:27017/recruiter'),
+ new InputOption('target', 't', InputOption::VALUE_REQUIRED, 'HOSTNAME[:PORT][/DB] MongoDB coordinates', $defaultMongoUri),
new InputOption('backoff-to', 'b', InputOption::VALUE_REQUIRED, 'Upper limit of time to wait before next polling', '6400ms'),
new InputOption('backoff-from', null, InputOption::VALUE_REQUIRED, 'Time to wait at least before to search for new jobs', '200ms'),
new InputOption('memory-limit', 'm', InputOption::VALUE_REQUIRED, 'Maximum amount of memory allocable', '64MB'),
@@ -138,7 +126,7 @@ public function init(InputInterface $input): void
$this->waitStrategy = new ExponentialBackoffStrategy(
Interval::parse($input->getOption('backoff-from'))->ms(),
- Interval::parse($input->getOption('backoff-to'))->ms()
+ Interval::parse($input->getOption('backoff-to'))->ms(),
);
$memoryLimit = new MemoryLimit($input->getOption('memory-limit'));
@@ -170,7 +158,7 @@ private function log(string $message, string $level = LogLevel::DEBUG): void
'datetime' => date('c'),
'pid' => posix_getpid(),
'workerId' => (string) $this->worker->id(),
- ]
+ ],
);
}
diff --git a/src/Recruiter/Infrastructure/Filesystem/BootstrapFile.php b/src/Recruiter/Infrastructure/Filesystem/BootstrapFile.php
index 9f12e88b..860d6fb1 100644
--- a/src/Recruiter/Infrastructure/Filesystem/BootstrapFile.php
+++ b/src/Recruiter/Infrastructure/Filesystem/BootstrapFile.php
@@ -1,29 +1,24 @@
filePath = $this->validate($filePath);
+ $this->validate($filePath);
}
- public static function fromFilePath(string $filePath): Self
+ public static function fromFilePath(string $filePath): self
{
- return new Static($filePath);
+ return new static($filePath);
}
public function load(Recruiter $recruiter)
@@ -31,27 +26,19 @@ public function load(Recruiter $recruiter)
return require $this->filePath;
}
- private function validate($filePath): string
+ private function validate(string $filePath): void
{
if (!file_exists($filePath)) {
$this->throwBecauseFile($filePath, "doesn't exists");
}
if (!is_readable($filePath)) {
- $this->throwBecauseFile($filePath, "is not readable");
+ $this->throwBecauseFile($filePath, 'is not readable');
}
-
- return $filePath;
}
- private function throwBecauseFile($filePath, $reason)
+ private function throwBecauseFile(string $filePath, string $reason): never
{
- throw new UnexpectedValueException(
- sprintf(
- "Bootstrap file has an invalid value: file '%s' %s",
- $filePath,
- $reason
- )
- );
+ throw new \UnexpectedValueException(sprintf("Bootstrap file has an invalid value: file '%s' %s", $filePath, $reason));
}
}
diff --git a/src/Recruiter/Infrastructure/Memory/MemoryLimit.php b/src/Recruiter/Infrastructure/Memory/MemoryLimit.php
index e59fd636..e04e05ab 100644
--- a/src/Recruiter/Infrastructure/Memory/MemoryLimit.php
+++ b/src/Recruiter/Infrastructure/Memory/MemoryLimit.php
@@ -1,13 +1,13 @@
limit = ByteUnits\parse($limit);
} catch (ByteUnits\ParseException $e) {
- throw new UnexpectedValueException(
- sprintf("Memory limit '%s' is an invalid value: %s", $limit, $e->getMessage())
- );
+ throw new \UnexpectedValueException(sprintf("Memory limit '%s' is an invalid value: %s", $limit, $e->getMessage()));
}
}
@@ -28,11 +26,7 @@ public function ensure($used)
{
$used = ByteUnits\box($used);
if ($used->isGreaterThan($this->limit)) {
- throw new MemoryLimitExceededException(sprintf(
- 'Memory limit reached, %s is more than the force limit of %s',
- $used->format(),
- $this->limit->format()
- ));
+ throw new MemoryLimitExceededException(sprintf('Memory limit reached, %s is more than the force limit of %s', $used->format(), $this->limit->format()));
}
}
}
diff --git a/src/Recruiter/Infrastructure/Memory/MemoryLimitExceededException.php b/src/Recruiter/Infrastructure/Memory/MemoryLimitExceededException.php
index 8188be2c..4516250a 100644
--- a/src/Recruiter/Infrastructure/Memory/MemoryLimitExceededException.php
+++ b/src/Recruiter/Infrastructure/Memory/MemoryLimitExceededException.php
@@ -1,9 +1,9 @@
uri = $uri;
+ return self::from(getenv('MONGODB_URI'));
}
- public static function from(?string $uri): self
+ public static function from(string|self|null $uri): self
{
+ if ($uri instanceof self) {
+ return $uri;
+ }
+
if (!$uri) {
$uri = self::DEFAULT_URI;
}
@@ -41,7 +40,7 @@ public function database(): string
return substr($parsed['path'], 1);
}
- public function __toString()
+ public function __toString(): string
{
return $this->uri;
}
diff --git a/src/Recruiter/Job.php b/src/Recruiter/Job.php
index ba95b6a7..ef8af24f 100644
--- a/src/Recruiter/Job.php
+++ b/src/Recruiter/Job.php
@@ -1,32 +1,20 @@
retryWithPolicy() : new RetryPolicy\DoNotDoItAgain(),
new JobExecution(),
- $repository
+ $repository,
);
}
- public static function import($document, Repository $repository)
+ public static function import($document, Repository $repository): self
{
return new self(
$document,
WorkableInJob::import($document),
RetryPolicyInJob::import($document),
JobExecution::import($document),
- $repository
+ $repository,
);
}
- public function __construct($status, Workable $workable, RetryPolicy $retryPolicy, JobExecution $lastJobExecution, Repository $repository)
- {
- $this->status = $status;
- $this->workable = $workable;
- $this->retryPolicy = $retryPolicy;
- $this->lastJobExecution = $lastJobExecution;
- $this->repository = $repository;
+ public function __construct(
+ private array $status,
+ private readonly Workable $workable,
+ private RetryPolicy $retryPolicy,
+ private JobExecution $lastJobExecution,
+ private readonly Repository $repository,
+ ) {
}
public function id()
@@ -63,7 +51,7 @@ public function id()
return $this->status['_id'];
}
- public function createdAt()
+ public function createdAt(): Moment
{
return T\MongoDate::toMoment($this->status['created_at']);
}
@@ -76,10 +64,14 @@ public function numberOfAttempts()
public function retryWithPolicy(RetryPolicy $retryPolicy)
{
$this->retryPolicy = $retryPolicy;
+
return $this;
}
- public function taggedAs(array $tags)
+ /**
+ * @return $this
+ */
+ public function taggedAs(array $tags): static
{
if (!empty($tags)) {
$this->status['tags'] = $tags;
@@ -88,34 +80,47 @@ public function taggedAs(array $tags)
return $this;
}
- public function inGroup($group)
+ public function inGroup(array|string $group): static
{
if (is_array($group)) {
- throw new RuntimeException(
+ throw new \RuntimeException(
"Group can be only single string, for other uses use `taggedAs` method.
Received group: `" . var_export($group, true) . "`"
);
}
+
if (!empty($group)) {
$this->status['group'] = $group;
}
+
return $this;
}
- public function scheduleAt(Moment $at)
+ /**
+ * @return $this
+ */
+ public function scheduleAt(Moment $at): static
{
$this->status['locked'] = false;
$this->status['scheduled_at'] = T\MongoDate::from($at);
+
return $this;
}
- public function withUrn(string $urn)
+ /**
+ * @return $this
+ */
+ public function withUrn(string $urn): static
{
$this->status['urn'] = $urn;
+
return $this;
}
- public function scheduledBy(string $namespace, string $id, int $executions)
+ /**
+ * @return $this
+ */
+ public function scheduledBy(string $namespace, string $id, int $executions): static
{
$this->status['scheduled'] = [
'by' => [
@@ -128,15 +133,15 @@ public function scheduledBy(string $namespace, string $id, int $executions)
return $this;
}
- public function methodToCallOnWorkable($method)
+ public function methodToCallOnWorkable($method): void
{
if (!method_exists($this->workable, $method)) {
- throw new Exception("Unknown method '$method' on workable instance");
+ throw new \Exception("Unknown method '$method' on workable instance");
}
$this->status['workable']['method'] = $method;
}
- public function execute(EventDispatcherInterface $eventDispatcher)
+ public function execute(EventDispatcherInterface $eventDispatcher): JobExecution
{
$methodToCall = $this->status['workable']['method'];
try {
@@ -145,14 +150,14 @@ public function execute(EventDispatcherInterface $eventDispatcher)
$result = $this->workable->$methodToCall($this->retryStatistics());
$this->afterExecution($result, $eventDispatcher);
}
- } catch (Throwable $exception) {
+ } catch (\Throwable $exception) {
$this->afterFailure($exception, $eventDispatcher);
}
return $this->lastJobExecution;
}
- public function retryStatistics()
+ public function retryStatistics(): array
{
return [
'job_id' => (string) $this->id(),
@@ -184,19 +189,20 @@ public function export()
$this->lastJobExecution->export(),
$this->tagsToUseFor($this->workable),
WorkableInJob::export($this->workable, $this->status['workable']['method']),
- RetryPolicyInJob::export($this->retryPolicy)
+ RetryPolicyInJob::export($this->retryPolicy),
);
}
public function beforeExecution(EventDispatcherInterface $eventDispatcher)
{
- $this->status['attempts'] += 1;
+ ++$this->status['attempts'];
$this->lastJobExecution = new JobExecution();
$this->lastJobExecution->started($this->scheduledAt());
$this->emit('job.started', $eventDispatcher);
if ($this->hasBeenScheduled()) {
$this->save();
}
+
return $this;
}
@@ -209,6 +215,7 @@ public function afterExecution($result, EventDispatcherInterface $eventDispatche
if ($this->hasBeenScheduled()) {
$this->archive('done');
}
+
return $this;
}
@@ -222,6 +229,7 @@ private function recoverFromCrash(EventDispatcherInterface $eventDispatcher)
if ($this->lastJobExecution->isCrashed()) {
return !$archived = $this->afterFailure(new WorkerDiedInTheLineOfDutyException(), $eventDispatcher);
}
+
return true;
}
@@ -238,19 +246,20 @@ private function afterFailure($exception, $eventDispatcher)
$this->emit('job.failure.last', $eventDispatcher);
$this->triggerOnWorkable('afterLastFailure', $exception);
}
+
return $archived;
}
- private function emit($eventType, $eventDispatcher)
+ private function emit($eventType, EventDispatcherInterface $eventDispatcher): void
{
$event = new Event($this->export());
- $eventDispatcher->dispatch($eventType, $event);
+ $eventDispatcher->dispatch($event, $eventType);
if ($this->workable instanceof EventListener) {
$this->workable->onEvent($eventType, $event);
}
}
- private function triggerOnWorkable($method, ?Throwable $e = null)
+ private function triggerOnWorkable($method, ?\Throwable $e = null)
{
if ($this->workable instanceof Finalizable) {
$this->workable->$method($e);
@@ -285,6 +294,7 @@ private function tagsToUseFor(Workable $workable)
if (!empty($tagsToUse)) {
return ['tags' => array_values(array_unique($tagsToUse))];
}
+
return [];
}
@@ -300,7 +310,7 @@ private static function initialize()
'group' => 'generic',
],
WorkableInJob::initialize(),
- RetryPolicyInJob::initialize()
+ RetryPolicyInJob::initialize(),
);
}
@@ -310,24 +320,22 @@ public static function pickReadyJobsForWorkers(MongoCollection $collection, $wor
iterator_to_array(
$collection
->find(
- (
- Worker::canWorkOnAnyJobs($worksOn) ?
- [ 'scheduled_at' => ['$lt' => T\MongoDate::now()],
- 'locked' => false,
- ] :
- [ 'scheduled_at' => ['$lt' => T\MongoDate::now()],
- 'locked' => false,
- 'group' => $worksOn,
- ]
- ),
+ Worker::canWorkOnAnyJobs($worksOn) ?
+ ['scheduled_at' => ['$lt' => T\MongoDate::now()],
+ 'locked' => false,
+ ] :
+ ['scheduled_at' => ['$lt' => T\MongoDate::now()],
+ 'locked' => false,
+ 'group' => $worksOn,
+ ],
[
'projection' => ['_id' => 1],
'sort' => ['scheduled_at' => 1],
'limit' => count($workers),
- ]
- )
+ ],
+ ),
),
- '_id'
+ '_id',
);
if (count($jobs) > 0) {
@@ -347,13 +355,13 @@ public static function rollbackLockedNotIn(MongoCollection $collection, array $e
'$set' => [
'locked' => false,
'last_execution.crashed' => true,
- ]
- ]
+ ],
+ ],
);
return $result->getModifiedCount();
} catch (BulkWriteException $e) {
- throw new InvalidArgumentException("Not valid excluded jobs filter: " . var_export($excluded, true), -1, $e);
+ throw new \InvalidArgumentException('Not valid excluded jobs filter: ' . var_export($excluded, true), -1, $e);
}
}
@@ -361,7 +369,7 @@ public static function lockAll(MongoCollection $collection, $jobs)
{
$collection->updateMany(
['_id' => ['$in' => array_values($jobs)]],
- ['$set' => ['locked' => true]]
+ ['$set' => ['locked' => true]],
);
}
}
diff --git a/src/Recruiter/Job/Event.php b/src/Recruiter/Job/Event.php
index 71738ebe..946d9489 100644
--- a/src/Recruiter/Job/Event.php
+++ b/src/Recruiter/Job/Event.php
@@ -1,25 +1,24 @@
jobExport = $jobExport;
}
- public function export()
+ public function export(): array
{
return $this->jobExport;
}
- public function hasTag($wantedTag)
+ public function hasTag(string $wantedTag): bool
{
$tags = array_key_exists('tags', $this->jobExport) ? $this->jobExport['tags'] : [];
+
return in_array($wantedTag, $tags);
}
}
diff --git a/src/Recruiter/Job/EventListener.php b/src/Recruiter/Job/EventListener.php
index cc2dc6d4..f82db1d1 100644
--- a/src/Recruiter/Job/EventListener.php
+++ b/src/Recruiter/Job/EventListener.php
@@ -4,5 +4,5 @@
interface EventListener
{
- public function onEvent($channel, Event $ev);
+ public function onEvent($channel, Event $ev): void;
}
diff --git a/src/Recruiter/Job/Repository.php b/src/Recruiter/Job/Repository.php
index 9ea4e305..d99fbe41 100644
--- a/src/Recruiter/Job/Repository.php
+++ b/src/Recruiter/Job/Repository.php
@@ -1,18 +1,18 @@
archived = $db->selectCollection('archived');
}
- public function all()
+ public function all(): array
{
return $this->map(
$this->scheduled->find([], [
'sort' => ['scheduled_at' => -1],
- ])
+ ]),
);
}
- public function archiveAll()
+ public function archiveAll(): void
{
foreach ($this->all() as $job) {
$this->archive($job);
}
}
- public function scheduled($id)
+ public function scheduled(string|ObjectId $id): Job
{
if (is_string($id)) {
$id = new ObjectId($id);
@@ -44,14 +44,14 @@ public function scheduled($id)
$found = $this->map($this->scheduled->find(['_id' => $id]));
- if (count($found) === 0) {
- throw new Exception("Unable to find scheduled job with ObjectId('{$id}')");
+ if (0 === count($found)) {
+ throw new \Exception("Unable to find scheduled job with ObjectId('{$id}')");
}
return $found[0];
}
- public function archived($id)
+ public function archived(string|ObjectId $id): Job
{
if (is_string($id)) {
$id = new ObjectId($id);
@@ -59,35 +59,35 @@ public function archived($id)
$found = $this->map($this->archived->find(['_id' => $id]));
- if (count($found) === 0) {
- throw new Exception("Unable to find archived job with ObjectId('{$id}')");
+ if (0 === count($found)) {
+ throw new \Exception("Unable to find archived job with ObjectId('{$id}')");
}
return $found[0];
}
- public function save(Job $job)
+ public function save(Job $job): void
{
$document = $job->export();
$this->scheduled->replaceOne(
['_id' => $document['_id']],
$document,
- ['upsert' => true]
+ ['upsert' => true],
);
}
- public function archive(Job $job)
+ public function archive(Job $job): void
{
$document = $job->export();
$this->scheduled->deleteOne(['_id' => $document['_id']]);
$this->archived->replaceOne(['_id' => $document['_id']], $document, ['upsert' => true]);
}
- public function releaseAll($jobIds)
+ public function releaseAll($jobIds): int
{
$result = $this->scheduled->updateMany(
['_id' => ['$in' => $jobIds]],
- ['$set' => ['locked' => false, 'last_execution.crashed' => true]]
+ ['$set' => ['locked' => false, 'last_execution.crashed' => true]],
);
return $result->getModifiedCount();
@@ -95,35 +95,35 @@ public function releaseAll($jobIds)
public function countArchived(): int
{
- return $this->archived->count();
+ return $this->archived->countDocuments();
}
- public function cleanArchived(T\Moment $upperLimit)
+ public function cleanArchived(T\Moment $upperLimit): int
{
$documents = $this->archived->find(
[
'last_execution.ended_at' => [
'$lte' => T\MongoDate::from($upperLimit),
- ]
+ ],
],
- ['projection' => ['_id' => 1]]
+ ['projection' => ['_id' => 1]],
);
$deleted = 0;
foreach ($documents as $document) {
$this->archived->deleteOne(['_id' => $document['_id']]);
- $deleted++;
+ ++$deleted;
}
return $deleted;
}
- public function cleanScheduled(T\Moment $upperLimit)
+ public function cleanScheduled(T\Moment $upperLimit): int
{
$result = $this->scheduled->deleteMany([
'created_at' => [
'$lte' => T\MongoDate::from($upperLimit),
- ]
+ ],
]);
return $result->getDeletedCount();
@@ -131,55 +131,55 @@ public function cleanScheduled(T\Moment $upperLimit)
public function queued(
$group = null,
- T\Moment $at = null,
- T\Moment $from = null,
- array $query = []
- ) {
- if ($at === null) {
+ ?T\Moment $at = null,
+ ?T\Moment $from = null,
+ array $query = [],
+ ): int {
+ if (null === $at) {
$at = T\now();
}
$query['scheduled_at']['$lte'] = T\MongoDate::from($at);
- if ($from !== null) {
+ if (null !== $from) {
$query['scheduled_at']['$gt'] = T\MongoDate::from($from);
}
- if ($group !== null) {
+ if (null !== $group) {
$query['group'] = $group;
}
return $this->scheduled->count($query);
}
- public function postponed($group = null, T\Moment $at = null, array $query = [])
+ public function postponed($group = null, ?T\Moment $at = null, array $query = []): int
{
- if ($at === null) {
+ if (null === $at) {
$at = T\now();
}
$query['scheduled_at']['$gt'] = T\MongoDate::from($at);
- if ($group !== null) {
+ if (null !== $group) {
$query['group'] = $group;
}
- return $this->scheduled->count($query);
+ return $this->scheduled->countDocuments($query);
}
- public function scheduledCount($group = null, array $query = [])
+ public function scheduledCount($group = null, array $query = []): int
{
- if ($group !== null) {
+ if (null !== $group) {
$query['group'] = $group;
}
- return $this->scheduled->count($query);
+ return $this->scheduled->countDocuments($query);
}
- public function queuedGroupedBy($field, array $query = [], $group = null)
+ public function queuedGroupedBy($field, array $query = [], $group = null): array
{
$query['scheduled_at']['$lte'] = T\MongoDate::from(T\now());
- if ($group !== null) {
+ if (null !== $group) {
$query['group'] = $group;
}
@@ -199,9 +199,9 @@ public function queuedGroupedBy($field, array $query = [], $group = null)
return $distinctAndCount;
}
- public function recentHistory($group = null, T\Moment $at = null, array $query = [])
+ public function recentHistory($group = null, ?T\Moment $at = null, array $query = []): array
{
- if ($at === null) {
+ if (null === $at) {
$at = T\now();
}
$lastMinute = array_merge(
@@ -209,11 +209,11 @@ public function recentHistory($group = null, T\Moment $at = null, array $query =
[
'last_execution.ended_at' => [
'$gt' => T\MongoDate::from($at->before(T\minute(1))),
- '$lte' => T\MongoDate::from($at)
+ '$lte' => T\MongoDate::from($at),
],
- ]
+ ],
);
- if ($group !== null) {
+ if (null !== $group) {
$lastMinute['group'] = $group;
}
$cursor = $this->archived->aggregate($pipeline = [
@@ -237,22 +237,22 @@ public function recentHistory($group = null, T\Moment $at = null, array $query =
]);
$documents = $cursor->toArray();
- if (count($documents) === 0) {
+ if (0 === count($documents)) {
$throughputPerMinute = 0.0;
$averageLatency = 0.0;
$averageExecutionTime = 0;
- } elseif (count($documents) === 1) {
+ } elseif (1 === count($documents)) {
$throughputPerMinute = (float) $documents[0]['throughput'];
$averageLatency = $documents[0]['latency'] / 1000;
$averageExecutionTime = $documents[0]['execution_time'] / 1000;
} else {
- throw new RuntimeException("Result was not ok: " . var_export($documents, true));
+ throw new \RuntimeException('Result was not ok: ' . var_export($documents, true));
}
return [
'throughput' => [
'value' => $throughputPerMinute,
- 'value_per_second' => $throughputPerMinute/60.0,
+ 'value_per_second' => $throughputPerMinute / 60.0,
],
'latency' => [
'average' => $averageLatency,
@@ -266,35 +266,35 @@ public function recentHistory($group = null, T\Moment $at = null, array $query =
public function countSlowRecentJobs(
T\Moment $lowerLimit,
T\Moment $upperLimit,
- $secondsToConsiderJobAsSlow = 5
+ $secondsToConsiderJobAsSlow = 5,
): int {
return count(
$this->slowArchivedRecentJobs(
$lowerLimit,
$upperLimit,
- $secondsToConsiderJobAsSlow
- )
+ $secondsToConsiderJobAsSlow,
+ ),
) + count(
$this->slowScheduledRecentJobs(
$lowerLimit,
$upperLimit,
- $secondsToConsiderJobAsSlow
- )
+ $secondsToConsiderJobAsSlow,
+ ),
);
}
public function countRecentJobsWithManyAttempts(
T\Moment $lowerLimit,
- T\Moment $upperLimit
+ T\Moment $upperLimit,
): int {
return $this->countRecentArchivedOrScheduledJobsWithManyAttempts(
$lowerLimit,
$upperLimit,
- 'archived'
+ 'archived',
) + $this->countRecentArchivedOrScheduledJobsWithManyAttempts(
$lowerLimit,
$upperLimit,
- 'scheduled'
+ 'scheduled',
);
}
@@ -302,81 +302,83 @@ public function countDelayedScheduledJobs(T\Moment $lowerLimit): int
{
return $this->scheduled->count([
'scheduled_at' => [
- '$lte' => T\MongoDate::from($lowerLimit)
- ]
+ '$lte' => T\MongoDate::from($lowerLimit),
+ ],
]);
}
- public function delayedScheduledJobs(T\Moment $lowerLimit)
+ public function delayedScheduledJobs(T\Moment $lowerLimit): array
{
return $this->map(
$this->scheduled->find([
'scheduled_at' => [
- '$lte' => T\MongoDate::from($lowerLimit)
- ]
- ])
+ '$lte' => T\MongoDate::from($lowerLimit),
+ ],
+ ]),
);
}
public function recentJobsWithManyAttempts(
T\Moment $lowerLimit,
- T\Moment $upperLimit
- ) {
+ T\Moment $upperLimit,
+ ): array {
$archived = $this->map(
$this->recentArchivedOrScheduledJobsWithManyAttempts(
$lowerLimit,
$upperLimit,
- 'archived'
- )
+ 'archived',
+ ),
);
$scheduled = $this->map(
$this->recentArchivedOrScheduledJobsWithManyAttempts(
$lowerLimit,
$upperLimit,
- 'scheduled'
- )
+ 'scheduled',
+ ),
);
+
return array_merge($archived, $scheduled);
}
public function slowRecentJobs(
T\Moment $lowerLimit,
T\Moment $upperLimit,
- $secondsToConsiderJobAsSlow = 5
- ) {
- $archived= [];
+ $secondsToConsiderJobAsSlow = 5,
+ ): array {
+ $archived = [];
$archivedArray = $this->slowArchivedRecentJobs(
$lowerLimit,
$upperLimit,
- $secondsToConsiderJobAsSlow
+ $secondsToConsiderJobAsSlow,
);
foreach ($archivedArray as $archivedJob) {
$archived[] = Job::import($archivedJob, $this);
}
- $scheduled= [];
+ $scheduled = [];
$scheduledArray = $this->slowScheduledRecentJobs(
$lowerLimit,
$upperLimit,
- $secondsToConsiderJobAsSlow
+ $secondsToConsiderJobAsSlow,
);
foreach ($scheduledArray as $scheduledJob) {
$scheduled[] = Job::import($scheduledJob, $this);
}
+
return array_merge($archived, $scheduled);
}
private function slowArchivedRecentJobs(
T\Moment $lowerLimit,
T\Moment $upperLimit,
- $secondsToConsiderJobAsSlow
- ) {
+ $secondsToConsiderJobAsSlow,
+ ): array {
return $this->archived->aggregate([
[
'$match' => [
'last_execution.ended_at' => [
'$gte' => T\MongoDate::from($lowerLimit),
],
- ]
+ ],
],
[
'$project' => [
@@ -384,8 +386,8 @@ private function slowArchivedRecentJobs(
'execution_time' => [
'$subtract' => [
'$last_execution.ended_at',
- '$last_execution.started_at'
- ]
+ '$last_execution.started_at',
+ ],
],
'done' => '$done',
'created_at' => '$created_at',
@@ -397,13 +399,13 @@ private function slowArchivedRecentJobs(
'scheduled_at' => '$scheduled_at',
'last_execution' => '$last_execution',
'retry_policy' => '$retry_policy',
- ]
+ ],
],
[
'$match' => [
'execution_time' => [
- '$gt' => $secondsToConsiderJobAsSlow*1000
- ]
+ '$gt' => $secondsToConsiderJobAsSlow * 1000,
+ ],
],
],
])->toArray();
@@ -412,14 +414,14 @@ private function slowArchivedRecentJobs(
private function slowScheduledRecentJobs(
T\Moment $lowerLimit,
T\Moment $upperLimit,
- $secondsToConsiderJobAsSlow
- ) {
+ $secondsToConsiderJobAsSlow,
+ ): array {
return $this->scheduled->aggregate([
[
'$match' => [
'scheduled_at' => [
'$gte' => T\MongoDate::from($lowerLimit),
- '$lte' => T\MongoDate::from($upperLimit)
+ '$lte' => T\MongoDate::from($upperLimit),
],
'last_execution.started_at' => [
'$exists' => true,
@@ -427,7 +429,7 @@ private function slowScheduledRecentJobs(
'last_execution.ended_at' => [
'$exists' => true,
],
- ]
+ ],
],
[
'$project' => [
@@ -435,8 +437,8 @@ private function slowScheduledRecentJobs(
'execution_time' => [
'$subtract' => [
'$last_execution.ended_at',
- '$last_execution.started_at'
- ]
+ '$last_execution.started_at',
+ ],
],
'done' => '$done',
'created_at' => '$created_at',
@@ -448,13 +450,13 @@ private function slowScheduledRecentJobs(
'scheduled_at' => '$scheduled_at',
'last_execution' => '$last_execution',
'retry_policy' => '$retry_policy',
- ]
+ ],
],
[
'$match' => [
'execution_time' => [
- '$gt' => $secondsToConsiderJobAsSlow*1000
- ]
+ '$gt' => $secondsToConsiderJobAsSlow * 1000,
+ ],
],
],
])->toArray();
@@ -463,32 +465,35 @@ private function slowScheduledRecentJobs(
private function countRecentArchivedOrScheduledJobsWithManyAttempts(
T\Moment $lowerLimit,
T\Moment $upperLimit,
- $collectionName
- ) {
+ $collectionName,
+ ): int {
return count($this->recentArchivedOrScheduledJobsWithManyAttempts(
$lowerLimit,
$upperLimit,
- $collectionName
+ $collectionName,
)->toArray());
}
private function recentArchivedOrScheduledJobsWithManyAttempts(
T\Moment $lowerLimit,
T\Moment $upperLimit,
- $collectionName
+ $collectionName,
) {
return $this->{$collectionName}->find([
'last_execution.ended_at' => [
'$gte' => T\MongoDate::from($lowerLimit),
- '$lte' => T\MongoDate::from($upperLimit)
- ],
- 'attempts' => [
- '$gt' => 1
- ]
+ '$lte' => T\MongoDate::from($upperLimit),
+ ],
+ 'attempts' => [
+ '$gt' => 1,
+ ],
]);
}
- private function map($cursor)
+ /**
+ * @return array
+ */
+ private function map(CursorInterface $cursor): array
{
$jobs = [];
foreach ($cursor as $document) {
diff --git a/src/Recruiter/JobAfterFailure.php b/src/Recruiter/JobAfterFailure.php
index 9f1d60e7..4d552651 100644
--- a/src/Recruiter/JobAfterFailure.php
+++ b/src/Recruiter/JobAfterFailure.php
@@ -2,56 +2,45 @@
namespace Recruiter;
-use Timeless\Moment;
use Timeless\Interval;
-use Timeless\MongoDate;
+use Timeless\Moment;
class JobAfterFailure
{
- /** @var Job */
- private $job;
-
- /** @var JobExecution */
- private $lastJobExecution;
+ private bool $hasBeenScheduled;
- /** @var bool */
- private $hasBeenScheduled;
+ private bool $hasBeenArchived;
- /** @var bool */
- private $hasBeenArchived;
-
- public function __construct(Job $job, JobExecution $lastJobExecution)
+ public function __construct(private readonly Job $job, private readonly JobExecution $lastJobExecution)
{
- $this->job = $job;
- $this->lastJobExecution = $lastJobExecution;
$this->hasBeenScheduled = false;
$this->hasBeenArchived = false;
}
- public function createdAt()
+ public function createdAt(): Moment
{
return $this->job->createdAt();
}
- public function inGroup($group)
+ public function inGroup($group): void
{
$this->job->inGroup($group);
$this->job->save();
}
- public function scheduleIn(Interval $in)
+ public function scheduleIn(Interval $in): void
{
$this->scheduleAt($in->fromNow());
}
- public function scheduleAt(Moment $at)
+ public function scheduleAt(Moment $at): void
{
$this->hasBeenScheduled = true;
$this->job->scheduleAt($at);
$this->job->save();
}
- public function archive($why)
+ public function archive($why): void
{
$this->hasBeenArchived = true;
$this->job->archive($why);
@@ -62,7 +51,7 @@ public function causeOfFailure()
return $this->lastJobExecution->causeOfFailure();
}
- public function lastExecutionDuration()
+ public function lastExecutionDuration(): Interval
{
return $this->lastJobExecution->duration();
}
@@ -72,16 +61,18 @@ public function numberOfAttempts()
return $this->job->numberOfAttempts();
}
- public function archiveIfNotScheduled()
+ public function archiveIfNotScheduled(): bool
{
if (!$this->hasBeenScheduled && !$this->hasBeenArchived) {
$this->archive('not-scheduled-by-retry-policy');
+
return true;
}
+
return false;
}
- public function hasBeenArchived()
+ public function hasBeenArchived(): bool
{
return $this->hasBeenArchived;
}
diff --git a/src/Recruiter/JobExecution.php b/src/Recruiter/JobExecution.php
index 3e111d62..b84373c6 100644
--- a/src/Recruiter/JobExecution.php
+++ b/src/Recruiter/JobExecution.php
@@ -3,35 +3,34 @@
namespace Recruiter;
use Timeless as T;
-use Throwable;
class JobExecution
{
- private $isCrashed;
- private $scheduledAt;
- private $startedAt;
- private $endedAt;
+ private bool $isCrashed = false;
+ private ?T\Moment $scheduledAt = null;
+ private ?T\Moment $startedAt = null;
+ private ?T\Moment $endedAt = null;
private $completedWith;
- private $failedWith;
+ private ?\Throwable $failedWith = null;
- public function isCrashed()
+ public function isCrashed(): bool
{
return $this->isCrashed;
}
- public function started($scheduledAt = null)
+ public function started(?T\Moment $scheduledAt = null): void
{
$this->scheduledAt = $scheduledAt;
$this->startedAt = T\now();
}
- public function failedWith(Throwable $exception)
+ public function failedWith(\Throwable $exception): void
{
$this->endedAt = T\now();
$this->failedWith = $exception;
}
- public function completedWith($result)
+ public function completedWith($result): void
{
$this->endedAt = T\now();
$this->completedWith = $result;
@@ -42,28 +41,29 @@ public function result()
return $this->completedWith;
}
- public function causeOfFailure()
+ public function causeOfFailure(): ?\Throwable
{
return $this->failedWith;
}
- public function isFailed()
+ public function isFailed(): bool
{
return !is_null($this->failedWith) || $this->isCrashed();
}
- public function duration()
+ public function duration(): T\Interval
{
if ($this->startedAt && $this->endedAt && ($this->startedAt <= $this->endedAt)) {
return T\seconds(
$this->endedAt->seconds() -
- $this->startedAt-> seconds()
+ $this->startedAt->seconds(),
);
}
+
return T\seconds(0);
}
- public static function import($document)
+ public static function import(array $document): self
{
$lastExecution = new self();
if (array_key_exists('last_execution', $document)) {
@@ -78,10 +78,11 @@ public static function import($document)
$lastExecution->startedAt = T\MongoDate::toMoment($lastExecutionDocument['started_at']);
}
}
+
return $lastExecution;
}
- public function export()
+ public function export(): array
{
$exported = [];
if ($this->scheduledAt) {
@@ -94,7 +95,7 @@ public function export()
$exported['ended_at'] = T\MongoDate::from($this->endedAt);
}
if ($this->failedWith) {
- $exported['class'] = get_class($this->failedWith);
+ $exported['class'] = $this->failedWith::class;
$exported['message'] = $this->failedWith->getMessage();
$exported['trace'] = $this->traceOf($this->failedWith);
}
@@ -108,18 +109,19 @@ public function export()
}
}
- private function traceOf($result)
+ private function traceOf(mixed $result): string
{
$trace = 'ok';
- if ($result instanceof Throwable) {
+ if ($result instanceof \Throwable) {
$trace = $result->getTraceAsString();
} elseif (is_object($result) && method_exists($result, 'trace')) {
$trace = $result->trace();
} elseif (is_object($result)) {
- $trace = get_class($result);
+ $trace = $result::class;
} elseif (is_string($result) || is_numeric($result)) {
$trace = $result;
}
- return substr($trace, 0, 4096);
+
+ return substr((string) $trace, 0, 4096);
}
}
diff --git a/src/Recruiter/JobToSchedule.php b/src/Recruiter/JobToSchedule.php
index 880b209a..cbc48d98 100644
--- a/src/Recruiter/JobToSchedule.php
+++ b/src/Recruiter/JobToSchedule.php
@@ -2,71 +2,89 @@
namespace Recruiter;
-use Recruiter\Job;
-use Recruiter\RetryPolicy;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Timeless as T;
use Timeless\Interval;
use Timeless\Moment;
+/**
+ * @method send() to make PHPStan happy in tests
+ */
class JobToSchedule
{
- private $job;
+ private bool $mustBeScheduled;
- /** @var bool */
- private $mustBeScheduled;
-
- public function __construct(Job $job)
+ public function __construct(private readonly Job $job)
{
- $this->job = $job;
$this->mustBeScheduled = false;
}
- public function doNotRetry()
+ public function doNotRetry(): static
{
return $this->retryWithPolicy(new RetryPolicy\DoNotDoItAgain());
}
- public function retryManyTimes($howManyTimes, Interval $timeToWaitBeforeRetry, $retriableExceptionTypes = [])
+ /**
+ * @return $this
+ */
+ public function retryManyTimes($howManyTimes, Interval $timeToWaitBeforeRetry, $retriableExceptionTypes = []): static
{
$this->job->retryWithPolicy(
$this->filterForRetriableExceptions(
new RetryPolicy\RetryManyTimes($howManyTimes, $timeToWaitBeforeRetry),
- $retriableExceptionTypes
- )
+ $retriableExceptionTypes,
+ ),
);
+
return $this;
}
- public function retryWithPolicy(RetryPolicy $retryPolicy, $retriableExceptionTypes = [])
+ /**
+ * @return $this
+ */
+ public function retryWithPolicy(RetryPolicy $retryPolicy, $retriableExceptionTypes = []): static
{
$this->job->retryWithPolicy(
$this->filterForRetriableExceptions(
$retryPolicy,
- $retriableExceptionTypes
- )
+ $retriableExceptionTypes,
+ ),
);
+
return $this;
}
- public function inBackground()
+ /**
+ * @return $this
+ */
+ public function inBackground(): static
{
return $this->scheduleAt(T\now());
}
- public function scheduleIn(Interval $duration)
+ /**
+ * @return $this
+ */
+ public function scheduleIn(Interval $duration): static
{
return $this->scheduleAt($duration->fromNow());
}
- public function scheduleAt(Moment $momentInTime)
+ /**
+ * @return $this
+ */
+ public function scheduleAt(Moment $momentInTime): static
{
$this->mustBeScheduled = true;
$this->job->scheduleAt($momentInTime);
+
return $this;
}
- public function inGroup($group)
+ /**
+ * @return $this
+ */
+ public function inGroup(array|string|null $group): static
{
if (!empty($group)) {
$this->job->inGroup($group);
@@ -75,7 +93,7 @@ public function inGroup($group)
return $this;
}
- public function taggedAs($tags)
+ public function taggedAs(array|string $tags): static
{
if (!empty($tags)) {
$this->job->taggedAs(is_array($tags) ? $tags : [$tags]);
@@ -84,47 +102,52 @@ public function taggedAs($tags)
return $this;
}
- public function withUrn(string $urn)
+ public function withUrn(string $urn): static
{
$this->job->withUrn($urn);
return $this;
}
- public function scheduledBy(string $namespace, string $id, int $nth)
+ public function scheduledBy(string $namespace, string $id, int $nth): static
{
$this->job->scheduledBy($namespace, $id, $nth);
return $this;
}
- public function execute()
+ public function execute(): string
{
if ($this->mustBeScheduled) {
$this->job->save();
} else {
$this->job->execute($this->emptyEventDispatcher());
}
+
return (string) $this->job->id();
}
- private function emptyEventDispatcher()
+ private function emptyEventDispatcher(): EventDispatcher
{
return new EventDispatcher();
}
- public function __call($name, $arguments)
+ /**
+ * @throws \Exception
+ */
+ public function __call(string $name, array $arguments)
{
$this->job->methodToCallOnWorkable($name);
+
return $this->execute();
}
- public function export()
+ public function export(): array
{
return $this->job->export();
}
- public static function import($document, $repository)
+ public static function import($document, $repository): self
{
return new self(Job::import($document, $repository));
}
@@ -137,6 +160,7 @@ private function filterForRetriableExceptions($retryPolicy, $retriableExceptionT
if (!empty($retriableExceptionTypes)) {
$retryPolicy = new RetryPolicy\RetriableExceptionFilter($retryPolicy, $retriableExceptionTypes);
}
+
return $retryPolicy;
}
}
diff --git a/src/Recruiter/Recruiter.php b/src/Recruiter/Recruiter.php
index 12adc429..a0138695 100644
--- a/src/Recruiter/Recruiter.php
+++ b/src/Recruiter/Recruiter.php
@@ -1,13 +1,9 @@
db = $db;
- $this->jobs = new Job\Repository($db);
- $this->workers = new Worker\Repository($db, $this);
- $this->scheduler = new Scheduler\Repository($db);
+ $this->jobs = new Job\Repository($this->db);
+ $this->workers = new Worker\Repository($this->db, $this);
+ $this->scheduler = new Scheduler\Repository($this->db);
$this->eventDispatcher = new EventDispatcher();
}
- public function hire(MemoryLimit $memoryLimit)
+ public function hire(MemoryLimit $memoryLimit): Worker
{
return Worker::workFor($this, $this->workers, $memoryLimit);
}
- public function jobOf(Workable $workable)
+ public function jobOf(Workable $workable): JobToSchedule
{
return new JobToSchedule(
- Job::around($workable, $this->jobs)
+ Job::around($workable, $this->jobs),
);
}
- public function repeatableJobOf(Repeatable $repeatable)
+ public function repeatableJobOf(Repeatable $repeatable): Scheduler
{
return Scheduler::around($repeatable, $this->scheduler, $this);
}
- public function queued()
+ public function queued(): int
{
return $this->jobs->queued();
}
- public function scheduled()
+ public function scheduled(): int
{
return $this->jobs->scheduledCount();
}
- public function queuedGroupedBy($field, array $query = [], $group = null)
+ public function queuedGroupedBy($field, array $query = [], $group = null): array
{
return $this->jobs->queuedGroupedBy($field, $query, $group);
}
- /**
- * @deprecated use the method `analytics` instead.
- */
- public function statistics($group = null, Moment $at = null, array $query = [])
+ #[\Deprecated(message: 'use the method `analytics` instead')]
+ public function statistics($group = null, ?Moment $at = null, array $query = []): array
{
return $this->analytics($group, $at, $query);
}
- public function analytics($group = null, Moment $at = null, array $query = [])
+ /**
+ * @return array
+ */
+ public function analytics($group = null, ?Moment $at = null, array $query = []): array
{
$totalsScheduledJobs = $this->jobs->scheduledCount($group, $query);
- $queued = $this->jobs->queued($group, $at, $at ? $at->before(T\hour(24)) : null, $query);
+ $queued = $this->jobs->queued($group, $at, $at?->before(T\hour(24)), $query);
$postponed = $this->jobs->postponed($group, $at, $query);
return array_merge(
@@ -84,38 +79,40 @@ public function analytics($group = null, Moment $at = null, array $query = [])
'zombies' => $totalsScheduledJobs - ($queued + $postponed),
],
],
- $this->jobs->recentHistory($group, $at, $query)
+ $this->jobs->recentHistory($group, $at, $query),
);
}
- public function getEventDispatcher()
+ public function getEventDispatcher(): EventDispatcher
{
return $this->eventDispatcher;
}
/**
* @step
- * @return integer how many
+ *
+ * @return int how many
*/
- public function rollbackLockedJobs()
+ public function rollbackLockedJobs(): int
{
$assignedJobs = Worker::assignedJobs($this->db->selectCollection('roster'));
+
return Job::rollbackLockedNotIn($this->db->selectCollection('scheduled'), $assignedJobs);
}
/**
* @step
*/
- public function bye()
+ public function bye(): void
{
}
- public function assignJobsToWorkers()
+ public function assignJobsToWorkers(): array
{
return $this->assignLockedJobsToWorkers($this->bookJobsForWorkers());
}
- public function scheduleRepeatableJobs()
+ public function scheduleRepeatableJobs(): void
{
$schedulers = $this->scheduler->all();
foreach ($schedulers as $scheduler) {
@@ -126,7 +123,7 @@ public function scheduleRepeatableJobs()
/**
* @step
*/
- public function bookJobsForWorkers()
+ public function bookJobsForWorkers(): array
{
$roster = $this->db->selectCollection('roster');
$scheduled = $this->db->selectCollection('scheduled');
@@ -134,46 +131,45 @@ public function bookJobsForWorkers()
$bookedJobs = [];
foreach (Worker::pickAvailableWorkers($roster, $workersPerUnit) as $resultRow) {
- list ($worksOn, $workers) = $resultRow;
+ [$worksOn, $workers] = $resultRow;
$result = Job::pickReadyJobsForWorkers($scheduled, $worksOn, $workers);
if ($result) {
- list($worksOn, $workers, $jobs) = $result;
- list($assignments, $jobs, $workers) = $this->combineJobsWithWorkers($jobs, $workers);
+ [$worksOn, $workers, $jobs] = $result;
+ [$assignments, $jobs, $workers] = $this->combineJobsWithWorkers($jobs, $workers);
Job::lockAll($scheduled, $jobs);
$bookedJobs[] = [$jobs, $workers];
}
}
+
return $bookedJobs;
}
/**
* @step
*/
- public function assignLockedJobsToWorkers($bookedJobs)
+ public function assignLockedJobsToWorkers(array $bookedJobs): array
{
$assignments = [];
$totalActualAssignments = 0;
$roster = $this->db->selectCollection('roster');
foreach ($bookedJobs as $row) {
- list ($jobs, $workers, ) = $row;
- list ($newAssignments, $actualAssignmentsNumber) = Worker::tryToAssignJobsToWorkers($roster, $jobs, $workers);
+ [$jobs, $workers] = $row;
+ [$newAssignments, $actualAssignmentsNumber] = Worker::tryToAssignJobsToWorkers($roster, $jobs, $workers);
if (array_intersect_key($assignments, $newAssignments)) {
- throw new RuntimeException("Conflicting assignments: current were " . var_export($assignments, true) . " and we want to also assign " . var_export($newAssignments, true));
+ throw new \RuntimeException('Conflicting assignments: current were ' . var_export($assignments, true) . ' and we want to also assign ' . var_export($newAssignments, true));
}
$assignments = array_merge(
$assignments,
- $newAssignments
+ $newAssignments,
);
$totalActualAssignments += $actualAssignmentsNumber;
}
return [
- array_map(function ($value) {
- return (string) $value;
- }, $assignments),
- $totalActualAssignments
+ array_map(fn ($value) => (string) $value, $assignments),
+ $totalActualAssignments,
];
}
@@ -184,12 +180,13 @@ public function scheduledJob($id)
/**
* @step
- * @return integer how many jobs were unlocked as a result
+ *
+ * @return int how many jobs were unlocked as a result
*/
- public function retireDeadWorkers(DateTimeImmutable $now, Interval $consideredDeadAfter)
+ public function retireDeadWorkers(\DateTimeImmutable $now, Interval $consideredDeadAfter): int
{
return $this->jobs->releaseAll(
- $jobsAssignedToDeadWorkers = Worker::retireDeadWorkers($this->workers, $now, $consideredDeadAfter)
+ $jobsAssignedToDeadWorkers = Worker::retireDeadWorkers($this->workers, $now, $consideredDeadAfter),
);
}
@@ -204,7 +201,7 @@ public function flushJobsSynchronously(): SynchronousExecutionReport
return SynchronousExecutionReport::fromArray($report);
}
- public function createCollectionsAndIndexes()
+ public function createCollectionsAndIndexes(): void
{
$this->db->selectCollection('scheduled')->createIndex(
[
@@ -212,67 +209,68 @@ public function createCollectionsAndIndexes()
'locked' => 1,
'scheduled_at' => 1,
],
- ['background' => true]
+ ['background' => true],
);
$this->db->selectCollection('scheduled')->createIndex(
[
'locked' => 1,
'scheduled_at' => 1,
],
- ['background' => true]
+ ['background' => true],
);
$this->db->selectCollection('scheduled')->createIndex(
[
'locked' => 1,
],
- ['background' => true]
+ ['background' => true],
);
$this->db->selectCollection('scheduled')->createIndex(
[
'tags' => 1,
],
- ['background' => true, 'sparse' => true]
+ ['background' => true, 'sparse' => true],
);
$this->db->selectCollection('archived')->createIndex(
[
'created_at' => 1,
],
- ['background' => true]
+ ['background' => true],
);
$this->db->selectCollection('archived')->createIndex(
[
'created_at' => 1,
'group' => 1,
],
- ['background' => true]
+ ['background' => true],
);
$this->db->selectCollection('archived')->createIndex(
[
'last_execution.ended_at' => 1,
],
- ['background' => true]
+ ['background' => true],
);
$this->db->selectCollection('roster')->createIndex(
[
'available' => 1,
],
- ['background' => true]
+ ['background' => true],
);
$this->db->selectCollection('roster')->createIndex(
[
'last_seen_at' => 1,
],
- ['background' => true]
+ ['background' => true],
);
}
- private function combineJobsWithWorkers($jobs, $workers)
+ private function combineJobsWithWorkers($jobs, $workers): array
{
$assignments = min(count($workers), count($jobs));
$workers = array_slice($workers, 0, $assignments);
$jobs = array_slice($jobs, 0, $assignments);
+
return [$assignments, $jobs, $workers];
}
}
diff --git a/src/Recruiter/Repeatable.php b/src/Recruiter/Repeatable.php
index 54a066d9..80cc6c0b 100644
--- a/src/Recruiter/Repeatable.php
+++ b/src/Recruiter/Repeatable.php
@@ -6,9 +6,7 @@ interface Repeatable extends Workable
{
/**
* Assign an unique name to the scheduler in order to handle idempotency,
- * only one scheduler with the same urn can exists
- *
- * @return string
+ * only one scheduler with the same urn can exists.
*/
public function urn(): string;
@@ -19,8 +17,6 @@ public function urn(): string;
*
* true: only one job at a time can be queued
* false: there may be more concurrent jobs at a time
- *
- * @return boolean
*/
public function unique(): bool;
}
diff --git a/src/Recruiter/RepeatableInJob.php b/src/Recruiter/RepeatableInJob.php
index 40f7af62..e6a23110 100644
--- a/src/Recruiter/RepeatableInJob.php
+++ b/src/Recruiter/RepeatableInJob.php
@@ -1,8 +1,7 @@
self::classNameOf($workable),
'parameters' => $workable->export(),
'method' => $methodToCall,
- ]
+ ],
];
}
@@ -57,10 +56,11 @@ public static function initialize(): array
private static function classNameOf($repeatable): string
{
- $repeatableClassName = get_class($repeatable);
+ $repeatableClassName = $repeatable::class;
if (method_exists($repeatable, 'getClass')) {
$repeatableClassName = $repeatable->getClass();
}
+
return $repeatableClassName;
}
}
diff --git a/src/Recruiter/Retriable.php b/src/Recruiter/Retriable.php
index 89e3d369..67080a88 100644
--- a/src/Recruiter/Retriable.php
+++ b/src/Recruiter/Retriable.php
@@ -5,9 +5,7 @@
interface Retriable
{
/**
- * Declare what instance of `Recruiter\RetryPolicy` should be used for a `Recruiter\Workable`
- *
- * @return RetryPolicy
+ * Declare what instance of `Recruiter\RetryPolicy` should be used for a `Recruiter\Workable`.
*/
public function retryWithPolicy(): RetryPolicy;
}
diff --git a/src/Recruiter/RetryPolicy.php b/src/Recruiter/RetryPolicy.php
index 0c462c65..33f29590 100644
--- a/src/Recruiter/RetryPolicy.php
+++ b/src/Recruiter/RetryPolicy.php
@@ -7,36 +7,27 @@ interface RetryPolicy
/**
* Decide whether or not to reschedule a job. If you want to reschedule the
* job use the appropriate methods on job or do nothing to if you don't
- * want to execute the job again
+ * want to execute the job again.
*
* This method can
* - schedule the job
* - archive the job
* - do nothing (and the job will be archived anyway)
- *
- * @param JobAfterFailure $job
- *
- * @return void
*/
- public function schedule(JobAfterFailure $job);
+ public function schedule(JobAfterFailure $job): void;
/**
- * Export retry policy parameters
- *
- * @return array
+ * Export retry policy parameters.
*/
public function export(): array;
/**
- * Import retry policy parameters
+ * Import retry policy parameters.
*
* @param array $parameters Previously exported parameters
- *
- * @return RetryPolicy
*/
public static function import(array $parameters): RetryPolicy;
-
/**
* @return bool true if is the last retry
*/
diff --git a/src/Recruiter/RetryPolicy/DoNotDoItAgain.php b/src/Recruiter/RetryPolicy/DoNotDoItAgain.php
index b7b28791..fafe975b 100644
--- a/src/Recruiter/RetryPolicy/DoNotDoItAgain.php
+++ b/src/Recruiter/RetryPolicy/DoNotDoItAgain.php
@@ -3,15 +3,15 @@
namespace Recruiter\RetryPolicy;
use Recruiter\Job;
+use Recruiter\JobAfterFailure;
use Recruiter\RetryPolicy;
use Recruiter\RetryPolicyBehaviour;
-use Recruiter\JobAfterFailure;
class DoNotDoItAgain implements RetryPolicy
{
use RetryPolicyBehaviour;
- public function schedule(JobAfterFailure $job)
+ public function schedule(JobAfterFailure $job): void
{
// doing nothing means to avoid to reschedule the job
}
diff --git a/src/Recruiter/RetryPolicy/ExponentialBackoff.php b/src/Recruiter/RetryPolicy/ExponentialBackoff.php
index 4264dd68..b1bcedb7 100644
--- a/src/Recruiter/RetryPolicy/ExponentialBackoff.php
+++ b/src/Recruiter/RetryPolicy/ExponentialBackoff.php
@@ -3,26 +3,24 @@
namespace Recruiter\RetryPolicy;
use Recruiter\Job;
+use Recruiter\JobAfterFailure;
use Recruiter\RetryPolicy;
use Recruiter\RetryPolicyBehaviour;
-use Recruiter\JobAfterFailure;
-
use Timeless as T;
use Timeless\Interval;
class ExponentialBackoff implements RetryPolicy
{
- private $retryHowManyTimes;
- private $timeToInitiallyWaitBeforeRetry;
-
use RetryPolicyBehaviour;
- public static function forTimes($retryHowManyTimes, $timeToInitiallyWaitBeforeRetry = 60)
+ private Interval $timeToInitiallyWaitBeforeRetry;
+
+ public static function forTimes($retryHowManyTimes, $timeToInitiallyWaitBeforeRetry = 60): static
{
return new static($retryHowManyTimes, $timeToInitiallyWaitBeforeRetry);
}
- public function atFirstWaiting($timeToInitiallyWaitBeforeRetry)
+ public function atFirstWaiting($timeToInitiallyWaitBeforeRetry): static
{
return new static($this->retryHowManyTimes, $timeToInitiallyWaitBeforeRetry);
}
@@ -31,31 +29,31 @@ public function atFirstWaiting($timeToInitiallyWaitBeforeRetry)
* @params integer $interval in seconds
* @params integer $timeToWaitBeforeRetry in seconds
*/
- public static function forAnInterval($interval, $timeToInitiallyWaitBeforeRetry)
+ public static function forAnInterval($interval, $timeToInitiallyWaitBeforeRetry): static
{
if (!($timeToInitiallyWaitBeforeRetry instanceof Interval)) {
$timeToInitiallyWaitBeforeRetry = T\seconds($timeToInitiallyWaitBeforeRetry);
}
$numberOfRetries = round(
log($interval / $timeToInitiallyWaitBeforeRetry->seconds())
- / log(2)
+ / log(2),
);
+
return new static($numberOfRetries, $timeToInitiallyWaitBeforeRetry);
}
- public function __construct($retryHowManyTimes, $timeToInitiallyWaitBeforeRetry)
+ public function __construct(private $retryHowManyTimes, int|Interval $timeToInitiallyWaitBeforeRetry)
{
if (!($timeToInitiallyWaitBeforeRetry instanceof Interval)) {
$timeToInitiallyWaitBeforeRetry = T\seconds($timeToInitiallyWaitBeforeRetry);
}
- $this->retryHowManyTimes = $retryHowManyTimes;
$this->timeToInitiallyWaitBeforeRetry = $timeToInitiallyWaitBeforeRetry;
}
- public function schedule(JobAfterFailure $job)
+ public function schedule(JobAfterFailure $job): void
{
if ($job->numberOfAttempts() <= $this->retryHowManyTimes) {
- $retryInterval = T\seconds(pow(2, $job->numberOfAttempts() - 1) * $this->timeToInitiallyWaitBeforeRetry->seconds());
+ $retryInterval = T\seconds(2 ** ($job->numberOfAttempts() - 1) * $this->timeToInitiallyWaitBeforeRetry->seconds());
$job->scheduleIn($retryInterval);
} else {
$job->archive('tried-too-many-times');
@@ -66,7 +64,7 @@ public function export(): array
{
return [
'retry_how_many_times' => $this->retryHowManyTimes,
- 'seconds_to_initially_wait_before_retry' => $this->timeToInitiallyWaitBeforeRetry->seconds()
+ 'seconds_to_initially_wait_before_retry' => $this->timeToInitiallyWaitBeforeRetry->seconds(),
];
}
@@ -74,7 +72,7 @@ public static function import(array $parameters): RetryPolicy
{
return new self(
$parameters['retry_how_many_times'],
- T\seconds($parameters['seconds_to_initially_wait_before_retry'])
+ T\seconds($parameters['seconds_to_initially_wait_before_retry']),
);
}
diff --git a/src/Recruiter/RetryPolicy/RetriableException.php b/src/Recruiter/RetryPolicy/RetriableException.php
index 59087f74..896f32cc 100644
--- a/src/Recruiter/RetryPolicy/RetriableException.php
+++ b/src/Recruiter/RetryPolicy/RetriableException.php
@@ -1,28 +1,23 @@
exceptionClass = $exceptionClass;
- $this->retryPolicy = $retryPolicy;
}
public function exceptionClass(): string
diff --git a/src/Recruiter/RetryPolicy/RetriableExceptionFilter.php b/src/Recruiter/RetryPolicy/RetriableExceptionFilter.php
index b0225307..acfbfa81 100644
--- a/src/Recruiter/RetryPolicy/RetriableExceptionFilter.php
+++ b/src/Recruiter/RetryPolicy/RetriableExceptionFilter.php
@@ -2,19 +2,17 @@
namespace Recruiter\RetryPolicy;
-use InvalidArgumentException;
-
use Recruiter\Job;
-use Recruiter\RetryPolicy;
use Recruiter\JobAfterFailure;
+use Recruiter\RetryPolicy;
class RetriableExceptionFilter implements RetryPolicy
{
- private $filteredRetryPolicy;
private $retriableExceptions;
/**
- * @param string $exceptionClass fully qualified class or interface name
+ * @param string $exceptionClass fully qualified class or interface name
+ *
* @return self
*/
public static function onlyFor($exceptionClass, RetryPolicy $retryPolicy)
@@ -22,13 +20,12 @@ public static function onlyFor($exceptionClass, RetryPolicy $retryPolicy)
return new self($retryPolicy, [$exceptionClass]);
}
- public function __construct(RetryPolicy $filteredRetryPolicy, array $retriableExceptions = ['Exception'])
+ public function __construct(private readonly RetryPolicy $filteredRetryPolicy, array $retriableExceptions = ['Exception'])
{
- $this->filteredRetryPolicy = $filteredRetryPolicy;
$this->retriableExceptions = $this->ensureAreAllExceptions($retriableExceptions);
}
- public function schedule(JobAfterFailure $job)
+ public function schedule(JobAfterFailure $job): void
{
if ($this->isExceptionRetriable($job->causeOfFailure())) {
$this->filteredRetryPolicy->schedule($job);
@@ -42,9 +39,9 @@ public function export(): array
return [
'retriable_exceptions' => $this->retriableExceptions,
'filtered_retry_policy' => [
- 'class' => get_class($this->filteredRetryPolicy),
- 'parameters' => $this->filteredRetryPolicy->export()
- ]
+ 'class' => $this->filteredRetryPolicy::class,
+ 'parameters' => $this->filteredRetryPolicy->export(),
+ ],
];
}
@@ -52,9 +49,10 @@ public static function import(array $parameters): RetryPolicy
{
$filteredRetryPolicy = $parameters['filtered_retry_policy'];
$retriableExceptions = $parameters['retriable_exceptions'];
+
return new self(
$filteredRetryPolicy['class']::import($filteredRetryPolicy['parameters']),
- $retriableExceptions
+ $retriableExceptions,
);
}
@@ -67,24 +65,22 @@ private function ensureAreAllExceptions($exceptions)
{
foreach ($exceptions as $exception) {
if (!is_a($exception, 'Throwable', true)) {
- throw new InvalidArgumentException(
- "Only subclasses of Exception can be retriable exceptions, '{$exception}' is not"
- );
+ throw new \InvalidArgumentException("Only subclasses of Exception can be retriable exceptions, '{$exception}' is not");
}
}
+
return $exceptions;
}
private function isExceptionRetriable($exception)
{
- if (!is_null($exception) && is_object($exception)) {
- return \Recruiter\array_some(
+ if (is_object($exception)) {
+ return array_any(
$this->retriableExceptions,
- function ($retriableExceptionType) use ($exception) {
- return ($exception instanceof $retriableExceptionType);
- }
+ fn ($retriableExceptionType) => $exception instanceof $retriableExceptionType,
);
}
+
return false;
}
}
diff --git a/src/Recruiter/RetryPolicy/RetryForever.php b/src/Recruiter/RetryPolicy/RetryForever.php
index 0eeb491e..4ec6c57d 100644
--- a/src/Recruiter/RetryPolicy/RetryForever.php
+++ b/src/Recruiter/RetryPolicy/RetryForever.php
@@ -1,21 +1,20 @@
timeToWaitBeforeRetry = $timeToWaitBeforeRetry;
}
- public static function afterSeconds($timeToWaitBeforeRetry = 60)
+ public static function afterSeconds(int|Interval $timeToWaitBeforeRetry = 60): self
{
- return new static($timeToWaitBeforeRetry);
+ return new self($timeToWaitBeforeRetry);
}
- public function schedule(JobAfterFailure $job)
+ public function schedule(JobAfterFailure $job): void
{
$job->scheduleIn($this->timeToWaitBeforeRetry);
}
@@ -36,14 +35,14 @@ public function schedule(JobAfterFailure $job)
public function export(): array
{
return [
- 'seconds_to_wait_before_retry' => $this->timeToWaitBeforeRetry->seconds()
+ 'seconds_to_wait_before_retry' => $this->timeToWaitBeforeRetry->seconds(),
];
}
public static function import(array $parameters): RetryPolicy
{
return new self(
- T\seconds($parameters['seconds_to_wait_before_retry'])
+ T\seconds($parameters['seconds_to_wait_before_retry']),
);
}
diff --git a/src/Recruiter/RetryPolicy/RetryManyTimes.php b/src/Recruiter/RetryPolicy/RetryManyTimes.php
index 31e99ba2..d5fa7d02 100644
--- a/src/Recruiter/RetryPolicy/RetryManyTimes.php
+++ b/src/Recruiter/RetryPolicy/RetryManyTimes.php
@@ -1,36 +1,34 @@
retryHowManyTimes = $retryHowManyTimes;
$this->timeToWaitBeforeRetry = $timeToWaitBeforeRetry;
}
- public static function forTimes($retryHowManyTimes, $timeToWaitBeforeRetry = 60)
+ public static function forTimes($retryHowManyTimes, int|Interval $timeToWaitBeforeRetry = 60): static
{
return new static($retryHowManyTimes, $timeToWaitBeforeRetry);
}
- public function schedule(JobAfterFailure $job)
+ public function schedule(JobAfterFailure $job): void
{
if ($job->numberOfAttempts() <= $this->retryHowManyTimes) {
$job->scheduleIn($this->timeToWaitBeforeRetry);
@@ -43,7 +41,7 @@ public function export(): array
{
return [
'retry_how_many_times' => $this->retryHowManyTimes,
- 'seconds_to_wait_before_retry' => $this->timeToWaitBeforeRetry->seconds()
+ 'seconds_to_wait_before_retry' => $this->timeToWaitBeforeRetry->seconds(),
];
}
@@ -51,7 +49,7 @@ public static function import(array $parameters): RetryPolicy
{
return new self(
$parameters['retry_how_many_times'],
- T\seconds($parameters['seconds_to_wait_before_retry'])
+ T\seconds($parameters['seconds_to_wait_before_retry']),
);
}
diff --git a/src/Recruiter/RetryPolicy/SelectByException.php b/src/Recruiter/RetryPolicy/SelectByException.php
index c38835cb..e00bafe6 100644
--- a/src/Recruiter/RetryPolicy/SelectByException.php
+++ b/src/Recruiter/RetryPolicy/SelectByException.php
@@ -3,15 +3,12 @@
namespace Recruiter\RetryPolicy;
use Exception;
-use InvalidArgumentException;
use Recruiter\Job;
use Recruiter\JobAfterFailure;
use Recruiter\RetryPolicy;
-use Throwable;
-use function Recruiter\array_all;
/**
- * Select retry policies based on the raised exception
+ * Select retry policies based on the raised exception.
*
* If a job fails with an exception it's possible to select a retry
* policy instance based on the class of the exception. The exception
@@ -27,25 +24,23 @@
*/
class SelectByException implements RetryPolicy
{
- /**
- * @var array
- */
- private $exceptions;
-
public static function create(): SelectByExceptionBuilder
{
return new SelectByExceptionBuilder();
}
- public function __construct(array $exceptions)
- {
- $this->exceptions = $exceptions;
+ public function __construct(
+ /**
+ * @var array
+ */
+ private readonly array $exceptions,
+ ) {
}
/**
- * {@inheritDoc}
+ * @throws \Exception
*/
- public function schedule(JobAfterFailure $job)
+ public function schedule(JobAfterFailure $job): void
{
$exception = $job->causeOfFailure();
if ($this->isRetriable($exception)) {
@@ -55,56 +50,47 @@ public function schedule(JobAfterFailure $job)
}
}
- /**
- * {@inheritDoc}
- */
public function export(): array
{
return array_map(
- function(RetriableException $retriableException) {
+ function (RetriableException $retriableException) {
$retryPolicy = $retriableException->retryPolicy();
+
return [
'when' => $retriableException->exceptionClass(),
'then' => [
- 'class' => get_class($retryPolicy),
- 'parameters' => $retryPolicy->export()
- ]
+ 'class' => $retryPolicy::class,
+ 'parameters' => $retryPolicy->export(),
+ ],
];
},
- $this->exceptions
+ $this->exceptions,
);
}
- /**
- * {@inheritDoc}
- */
public static function import(array $parameters): RetryPolicy
{
return new self(
array_reduce(
$parameters,
- function($exceptions, $parameters) {
+ function ($exceptions, $parameters) {
$exceptionClass = $parameters['when'];
$retryPolicyClass = $parameters['then']['class'];
$retryPolicyParameters = $parameters['then']['parameters'];
$exceptions[] = new RetriableException($exceptionClass, $retryPolicyClass::import($retryPolicyParameters));
+
return $exceptions;
- }
- )
+ },
+ ),
);
}
- /**
- * {@inheritDoc}
- */
public function isLastRetry(Job $job): bool
{
// I cannot answer to that so... true only if everybody says true
return array_all(
$this->exceptions,
- function(RetriableException $retriableException) use ($job) {
- return $retriableException->retryPolicy()->isLastRetry($job);
- }
+ fn (RetriableException $retriableException) => $retriableException->retryPolicy()->isLastRetry($job),
);
}
@@ -112,12 +98,16 @@ private function isRetriable($exception): bool
{
try {
$this->retryPolicyFor($exception);
+
return true;
- } catch (Exception $e) {
+ } catch (\Exception) {
return false;
}
}
+ /**
+ * @throws \Exception
+ */
private function retryPolicyFor(?object $exception): RetryPolicy
{
if (!is_null($exception) && is_object($exception)) {
@@ -128,14 +118,10 @@ private function retryPolicyFor(?object $exception): RetryPolicy
return $retriableException->retryPolicy();
}
}
- if ($exception instanceof Throwable) {
- throw new Exception(
- 'Unable to find a RetryPolicy associated to exception: ' . get_class($exception), 0, $exception
- );
+ if ($exception instanceof \Throwable) {
+ throw new \Exception('Unable to find a RetryPolicy associated to exception: ' . $exception::class, 0, $exception);
}
}
- throw new Exception(
- 'Unable to find a RetryPolicy associated to: ' . var_export($exception, true)
- );
+ throw new \Exception('Unable to find a RetryPolicy associated to: ' . var_export($exception, true));
}
}
diff --git a/src/Recruiter/RetryPolicy/SelectByExceptionBuilder.php b/src/Recruiter/RetryPolicy/SelectByExceptionBuilder.php
index b02f6602..6fa68c5c 100644
--- a/src/Recruiter/RetryPolicy/SelectByExceptionBuilder.php
+++ b/src/Recruiter/RetryPolicy/SelectByExceptionBuilder.php
@@ -1,12 +1,9 @@
currentException = $exceptionClass;
+
return $this;
}
@@ -39,6 +37,7 @@ public function then(RetryPolicy $retryPolicy): self
}
$this->exceptions[] = new RetriableException($this->currentException, $retryPolicy);
$this->currentException = null;
+
return $this;
}
@@ -50,6 +49,7 @@ public function build(): SelectByException
if (empty($this->exceptions)) {
throw new LogicException('No retry policies has been specified. Use `$builder->when($e)->then($r)`');
}
+
return new SelectByException($this->exceptions);
}
}
diff --git a/src/Recruiter/RetryPolicy/TimeTable.php b/src/Recruiter/RetryPolicy/TimeTable.php
index 0231b57c..eec7a965 100644
--- a/src/Recruiter/RetryPolicy/TimeTable.php
+++ b/src/Recruiter/RetryPolicy/TimeTable.php
@@ -3,23 +3,21 @@
namespace Recruiter\RetryPolicy;
use Recruiter\Job;
+use Recruiter\JobAfterFailure;
use Recruiter\RetryPolicy;
use Recruiter\RetryPolicyBehaviour;
-use Recruiter\JobAfterFailure;
-
use Timeless as T;
-use Exception;
-
class TimeTable implements RetryPolicy
{
- /** @var array */
- private $timeTable;
-
- private $howManyRetries;
-
use RetryPolicyBehaviour;
+ private ?array $timeTable;
+
+ private int $howManyRetries;
+ /**
+ * @throws \Exception
+ */
public function __construct(?array $timeTable)
{
if (is_null($timeTable)) {
@@ -33,7 +31,7 @@ public function __construct(?array $timeTable)
$this->howManyRetries = self::estimateHowManyRetriesIn($timeTable);
}
- public function schedule(JobAfterFailure $job)
+ public function schedule(JobAfterFailure $job): void
{
foreach ($this->timeTable as $timeSpent => $rescheduleIn) {
if ($this->hasBeenCreatedLessThan($job, $timeSpent)) {
@@ -47,6 +45,7 @@ public function isLastRetry(Job $job): bool
{
$timeSpents = array_keys($this->timeTable);
$timeSpent = end($timeSpents);
+
return !$this->hasBeenCreatedLessThan($job, $timeSpent);
}
@@ -63,42 +62,37 @@ public static function import(array $parameters): RetryPolicy
private function hasBeenCreatedLessThan($job, $relativeTime)
{
return $job->createdAt()->isAfter(
- T\Moment::fromTimestamp(strtotime($relativeTime, T\now()->seconds()))
+ T\Moment::fromTimestamp(strtotime((string) $relativeTime, T\now()->seconds())),
);
}
- private function rescheduleIn($job, $relativeTime)
+ private function rescheduleIn($job, $relativeTime): void
{
$job->scheduleAt(
- T\Moment::fromTimestamp(strtotime($relativeTime, T\now()->seconds()))
+ T\Moment::fromTimestamp(strtotime((string) $relativeTime, T\now()->seconds())),
);
}
- private static function estimateHowManyRetriesIn($timeTable)
+ private static function estimateHowManyRetriesIn(array $timeTable): int
{
$now = T\now()->seconds();
$howManyRetries = 0;
$timeWindowInSeconds = 0;
foreach ($timeTable as $timeWindow => $rescheduleTime) {
- $timeWindowInSeconds = ($now - strtotime($timeWindow, $now)) - $timeWindowInSeconds;
+ $timeWindowInSeconds = ($now - strtotime((string) $timeWindow, $now)) - $timeWindowInSeconds;
if ($timeWindowInSeconds <= 0) {
- throw new Exception(
- "Time window `$timeWindow` is invalid, must be in the past"
- );
+ throw new \Exception("Time window `$timeWindow` is invalid, must be in the past");
}
- $rescheduleTimeInSeconds = (strtotime($rescheduleTime, $now) - $now);
+ $rescheduleTimeInSeconds = (strtotime((string) $rescheduleTime, $now) - $now);
if ($rescheduleTimeInSeconds <= 0) {
- throw new Exception(
- "Reschedule time `$rescheduleTime` is invalid, must be in the future"
- );
+ throw new \Exception("Reschedule time `$rescheduleTime` is invalid, must be in the future");
}
if ($rescheduleTimeInSeconds > $timeWindowInSeconds) {
- throw new Exception(
- "Reschedule time `$rescheduleTime` is invalid, must be greater than the time window"
- );
+ throw new \Exception("Reschedule time `$rescheduleTime` is invalid, must be greater than the time window");
}
$howManyRetries += floor($timeWindowInSeconds / $rescheduleTimeInSeconds);
}
+
return $howManyRetries;
}
}
diff --git a/src/Recruiter/RetryPolicyBehaviour.php b/src/Recruiter/RetryPolicyBehaviour.php
index 9944ce04..caea789a 100644
--- a/src/Recruiter/RetryPolicyBehaviour.php
+++ b/src/Recruiter/RetryPolicyBehaviour.php
@@ -2,9 +2,7 @@
namespace Recruiter;
-use Exception;
use Recruiter\RetryPolicy\RetriableExceptionFilter;
-use Recruiter\JobAfterFailure;
trait RetryPolicyBehaviour
{
@@ -25,9 +23,9 @@ public function retryOnlyWhenExceptionsAre($retriableExceptionTypes)
return new RetriableExceptionFilter($this, $retriableExceptionTypes);
}
- public function schedule(JobAfterFailure $job)
+ public function schedule(JobAfterFailure $job): void
{
- throw new Exception('RetryPolicy::schedule(JobAfterFailure) need to be implemented');
+ throw new \Exception('RetryPolicy::schedule(JobAfterFailure) need to be implemented');
}
public function export(): array
diff --git a/src/Recruiter/RetryPolicyInJob.php b/src/Recruiter/RetryPolicyInJob.php
index 5f34814f..dd5b5205 100644
--- a/src/Recruiter/RetryPolicyInJob.php
+++ b/src/Recruiter/RetryPolicyInJob.php
@@ -2,26 +2,24 @@
namespace Recruiter;
-use Exception;
-use Recruiter\RetryPolicy;
-
class RetryPolicyInJob
{
public static function import($document)
{
if (!array_key_exists('retry_policy', $document)) {
- throw new Exception('Unable to import Job without data about RetryPolicy object');
+ throw new \Exception('Unable to import Job without data about RetryPolicy object');
}
$dataAboutRetryPolicyObject = $document['retry_policy'];
if (!array_key_exists('class', $dataAboutRetryPolicyObject)) {
- throw new Exception('Unable to import Job without a class');
+ throw new \Exception('Unable to import Job without a class');
}
if (!class_exists($dataAboutRetryPolicyObject['class'])) {
- throw new Exception('Unable to import Job with unknown RetryPolicy class');
+ throw new \Exception('Unable to import Job with unknown RetryPolicy class');
}
if (!method_exists($dataAboutRetryPolicyObject['class'], 'import')) {
- throw new Exception('Unable to import RetryPolicy without method import');
+ throw new \Exception('Unable to import RetryPolicy without method import');
}
+
return $dataAboutRetryPolicyObject['class']::import($dataAboutRetryPolicyObject['parameters']);
}
@@ -29,9 +27,9 @@ public static function export($retryPolicy)
{
return [
'retry_policy' => [
- 'class' => get_class($retryPolicy),
+ 'class' => $retryPolicy::class,
'parameters' => $retryPolicy->export(),
- ]
+ ],
];
}
diff --git a/src/Recruiter/SchedulePolicy.php b/src/Recruiter/SchedulePolicy.php
index 21aeadd3..f10c701a 100644
--- a/src/Recruiter/SchedulePolicy.php
+++ b/src/Recruiter/SchedulePolicy.php
@@ -7,25 +7,19 @@
interface SchedulePolicy
{
/**
- * Returns the next time the job is to be executed
- *
- * @return Moment
+ * Returns the next time the job is to be executed.
*/
public function next(): Moment;
/**
- * Export schedule policy parameters
- *
- * @return array
+ * Export schedule policy parameters.
*/
public function export(): array;
/**
- * Import schedule policy parameters
+ * Import schedule policy parameters.
*
* @param array $parameters Previously exported parameters
- *
- * @return SchedulePolicy
*/
public static function import(array $parameters): SchedulePolicy;
}
diff --git a/src/Recruiter/SchedulePolicy/Cron.php b/src/Recruiter/SchedulePolicy/Cron.php
index 24afd0cc..73e42f86 100644
--- a/src/Recruiter/SchedulePolicy/Cron.php
+++ b/src/Recruiter/SchedulePolicy/Cron.php
@@ -3,27 +3,19 @@
namespace Recruiter\SchedulePolicy;
use Cron\CronExpression;
-use DateInterval;
-use DateTime;
use Recruiter\SchedulePolicy;
-
use Timeless\Moment;
class Cron implements SchedulePolicy
{
- private $cronExpression;
- private $now;
-
- public function __construct(string $cronExpression, ?DateTime $now = null)
+ public function __construct(private readonly string $cronExpression, private readonly ?\DateTime $now = null)
{
- $this->cronExpression = $cronExpression;
- $this->now = $now;
}
public function next(): Moment
{
return Moment::fromDateTime(
- CronExpression::factory($this->cronExpression)->getNextRunDate($this->now ?? 'now')
+ CronExpression::factory($this->cronExpression)->getNextRunDate($this->now ?? 'now'),
);
}
@@ -39,8 +31,8 @@ public static function import(array $parameters): SchedulePolicy
{
$now = null;
if (isset($parameters['now'])) {
- $now = DateTime::createFromFormat('U', $parameters['now']);
- $now = $now === false ? null : $now;
+ $now = \DateTime::createFromFormat('U', $parameters['now']);
+ $now = false === $now ? null : $now;
}
return new self($parameters['cron_expression'], $now);
diff --git a/src/Recruiter/SchedulePolicy/EveryMinutes.php b/src/Recruiter/SchedulePolicy/EveryMinutes.php
index 7245687e..908e5d55 100644
--- a/src/Recruiter/SchedulePolicy/EveryMinutes.php
+++ b/src/Recruiter/SchedulePolicy/EveryMinutes.php
@@ -2,9 +2,7 @@
namespace Recruiter\SchedulePolicy;
-use DateInterval;
use Recruiter\SchedulePolicy;
-
use Timeless\Moment;
class EveryMinutes implements SchedulePolicy
diff --git a/src/Recruiter/SchedulePolicyInJob.php b/src/Recruiter/SchedulePolicyInJob.php
index d1671ce6..184b9242 100644
--- a/src/Recruiter/SchedulePolicyInJob.php
+++ b/src/Recruiter/SchedulePolicyInJob.php
@@ -2,26 +2,24 @@
namespace Recruiter;
-use Exception;
-use Recruiter\SchedulePolicy;
-
class SchedulePolicyInJob
{
public static function import($document): SchedulePolicy
{
if (!array_key_exists('schedule_policy', $document)) {
- throw new Exception('Unable to import Job without data about SchedulePolicy object');
+ throw new \Exception('Unable to import Job without data about SchedulePolicy object');
}
$dataAboutSchedulePolicyObject = $document['schedule_policy'];
if (!array_key_exists('class', $dataAboutSchedulePolicyObject)) {
- throw new Exception('Unable to import Job without a SchedulePolicy class');
+ throw new \Exception('Unable to import Job without a SchedulePolicy class');
}
if (!class_exists($dataAboutSchedulePolicyObject['class'])) {
- throw new Exception('Unable to import Job with unknown SchedulePolicy class');
+ throw new \Exception('Unable to import Job with unknown SchedulePolicy class');
}
if (!method_exists($dataAboutSchedulePolicyObject['class'], 'import')) {
- throw new Exception('Unable to import SchedulePolicy without method import');
+ throw new \Exception('Unable to import SchedulePolicy without method import');
}
+
return $dataAboutSchedulePolicyObject['class']::import($dataAboutSchedulePolicyObject['parameters']);
}
@@ -29,9 +27,9 @@ public static function export($schedulePolicy)
{
return [
'schedule_policy' => [
- 'class' => get_class($schedulePolicy),
+ 'class' => $schedulePolicy::class,
'parameters' => $schedulePolicy->export(),
- ]
+ ],
];
}
diff --git a/src/Recruiter/Scheduler.php b/src/Recruiter/Scheduler.php
index 01b272db..409c17ff 100644
--- a/src/Recruiter/Scheduler.php
+++ b/src/Recruiter/Scheduler.php
@@ -2,44 +2,26 @@
namespace Recruiter;
-use MongoDB\BSON\ObjectId;
-use Recruiter\Scheduler\Repository;
use Recruiter\Job\Repository as JobsRepository;
-use Recruiter\RetryPolicy;
-use RuntimeException;
-use Symfony\Component\EventDispatcher\EventDispatcher;
-use Throwable;
+use Recruiter\Scheduler\Repository;
use Timeless as T;
-use Timeless\Interval;
-use Timeless\Moment;
class Scheduler
{
private $job;
- private $schedulers;
-
- private $status;
-
- private $schedulePolicy;
-
- private $repeatable;
-
- private $retryPolicy;
-
public static function around(Repeatable $repeatable, Repository $repository, Recruiter $recruiter)
{
$retryPolicy = ($repeatable instanceof Retriable) ?
$repeatable->retryWithPolicy() :
- new RetryPolicy\DoNotDoItAgain()
- ;
+ new RetryPolicy\DoNotDoItAgain();
return new self(
self::initialize(),
$repeatable,
null,
$retryPolicy,
- $repository
+ $repository,
);
}
@@ -50,22 +32,12 @@ public static function import($document, Repository $repository)
RepeatableInJob::import($document['job']),
SchedulePolicyInJob::import($document),
RetryPolicyInJob::import($document['job']),
- $repository
+ $repository,
);
}
- public function __construct(
- array $status,
- Repeatable $repeatable,
- ?SchedulePolicy $schedulePolicy,
- ?RetryPolicy $retryPolicy,
- Repository $schedulers
- ) {
- $this->status = $status;
- $this->repeatable = $repeatable;
- $this->schedulePolicy = $schedulePolicy;
- $this->retryPolicy = $retryPolicy;
- $this->schedulers = $schedulers;
+ public function __construct(private array $status, private readonly Repeatable $repeatable, private ?SchedulePolicy $schedulePolicy, private ?RetryPolicy $retryPolicy, private readonly Repository $schedulers)
+ {
}
public function create()
@@ -103,9 +75,9 @@ public function export()
[
'job' => array_merge(
WorkableInJob::export($this->repeatable, 'execute'),
- RetryPolicyInJob::export($this->retryPolicy)
+ RetryPolicyInJob::export($this->retryPolicy),
),
- ]
+ ],
);
}
@@ -128,8 +100,9 @@ private function aJobIsStillRunning(JobsRepository $jobs)
try {
$alreadyScheduledJob = $jobs->scheduled($this->status['last_scheduling']['job_id']);
+
return true;
- } catch (Throwable $e) {
+ } catch (\Throwable) {
return false;
}
}
@@ -137,7 +110,7 @@ private function aJobIsStillRunning(JobsRepository $jobs)
public function schedule(JobsRepository $jobs)
{
if (!$this->schedulePolicy) {
- throw new RuntimeException('You need to assign a `SchedulePolicy` (use `repeatWithPolicy` to inject it) in order to schedule a job');
+ throw new \RuntimeException('You need to assign a `SchedulePolicy` (use `repeatWithPolicy` to inject it) in order to schedule a job');
}
$nextScheduling = $this->schedulePolicy->next();
@@ -151,10 +124,10 @@ public function schedule(JobsRepository $jobs)
$this->status['last_scheduling']['scheduled_at'] = T\MongoDate::from($nextScheduling);
$this->status['last_scheduling']['job_id'] = null;
- $this->status['attempts'] = $this->status['attempts'] + 1;
+ ++$this->status['attempts'];
$this->schedulers->save($this);
- $jobToSchedule = (new JobToSchedule(Job::around($this->repeatable, $jobs)))
+ $jobToSchedule = new JobToSchedule(Job::around($this->repeatable, $jobs))
->scheduleAt($nextScheduling)
->retryWithPolicy($this->retryPolicy)
->scheduledBy('scheduler', $this->status['urn'], $this->status['attempts'])
@@ -169,7 +142,7 @@ public function retryWithPolicy(RetryPolicy $retryPolicy, $retriableExceptionTyp
{
$this->retryPolicy = $this->filterForRetriableExceptions(
$retryPolicy,
- $retriableExceptionTypes
+ $retriableExceptionTypes,
);
return $this;
diff --git a/src/Recruiter/Scheduler/Repository.php b/src/Recruiter/Scheduler/Repository.php
index df95d856..d22ccb96 100644
--- a/src/Recruiter/Scheduler/Repository.php
+++ b/src/Recruiter/Scheduler/Repository.php
@@ -1,14 +1,9 @@
map(
$this->schedulers->find([], [
'sort' => ['scheduled_at' => -1],
- ])
+ ]),
);
}
@@ -34,7 +29,7 @@ public function save(Scheduler $scheduler)
$this->schedulers->replaceOne(
['_id' => $document['_id']],
$document,
- ['upsert' => true]
+ ['upsert' => true],
);
}
@@ -45,17 +40,15 @@ public function create(Scheduler $scheduler)
if (0 === $this->schedulers->count(['urn' => $document['urn']])) {
$this->schedulers->insertOne($document);
} else {
- $document = array_filter($document, function ($key) {
- return in_array($key, [
- 'job',
- 'schedule_policy',
- 'unique',
- ]);
- }, ARRAY_FILTER_USE_KEY);
+ $document = array_filter($document, fn ($key) => in_array($key, [
+ 'job',
+ 'schedule_policy',
+ 'unique',
+ ]), ARRAY_FILTER_USE_KEY);
$this->schedulers->updateOne(
['urn' => $scheduler->urn()],
- ['$set' => $document]
+ ['$set' => $document],
);
}
}
diff --git a/src/Recruiter/SynchronousExecutionReport.php b/src/Recruiter/SynchronousExecutionReport.php
index ebd1ac46..f2f80cda 100644
--- a/src/Recruiter/SynchronousExecutionReport.php
+++ b/src/Recruiter/SynchronousExecutionReport.php
@@ -1,40 +1,32 @@
data = $data;
}
-
/**
- *. @params array $data : key value array where key are the id of the job and value is the JobExecution
+ *. @params array $data : key value array where key are the id of the job and value is the JobExecution.
*/
public static function fromArray(array $data): SynchronousExecutionReport
{
return new self($data);
}
- public function isThereAFailure()
+ public function isThereAFailure(): bool
{
- return array_some($this->data, function ($jobExecution, $jobId) {
- return $jobExecution->isFailed();
- });
+ return array_any($this->data, fn ($jobExecution, $jobId) => $jobExecution->isFailed());
}
public function toArray()
diff --git a/src/Recruiter/Taggable.php b/src/Recruiter/Taggable.php
index 6ab0d0e0..0d2e37db 100644
--- a/src/Recruiter/Taggable.php
+++ b/src/Recruiter/Taggable.php
@@ -5,9 +5,9 @@
interface Taggable
{
/**
- * A Job can decide its own tags. Tags are useful to correlate jobs
+ * A Job can decide its own tags. Tags are useful to correlate jobs.
*
- * @return array Strings to be used to tag the job
+ * @return array Strings to be used to tag the job
*/
- public function taggedAs();
+ public function taggedAs(): array;
}
diff --git a/src/Recruiter/WaitStrategy.php b/src/Recruiter/WaitStrategy.php
index 4e41d029..8278023c 100644
--- a/src/Recruiter/WaitStrategy.php
+++ b/src/Recruiter/WaitStrategy.php
@@ -9,19 +9,18 @@ class WaitStrategy
private $timeToWaitAtLeast;
private $timeToWaitAtMost;
private $timeToWait;
- private $howToWait;
- public function __construct(Interval $timeToWaitAtLeast, Interval $timeToWaitAtMost, $howToWait = 'usleep')
+ public function __construct(Interval $timeToWaitAtLeast, Interval $timeToWaitAtMost, private $howToWait = 'usleep')
{
$this->timeToWaitAtLeast = $timeToWaitAtLeast->milliseconds();
$this->timeToWaitAtMost = $timeToWaitAtMost->milliseconds();
$this->timeToWait = $timeToWaitAtLeast->milliseconds();
- $this->howToWait = $howToWait;
}
public function reset()
{
$this->timeToWait = $this->timeToWaitAtLeast;
+
return $this;
}
@@ -29,8 +28,9 @@ public function goForward()
{
$this->timeToWait = max(
$this->timeToWait / 2,
- $this->timeToWaitAtLeast
+ $this->timeToWaitAtLeast,
);
+
return $this;
}
@@ -38,14 +38,16 @@ public function backOff()
{
$this->timeToWait = min(
$this->timeToWait * 2,
- $this->timeToWaitAtMost
+ $this->timeToWaitAtMost,
);
+
return $this;
}
public function wait()
{
call_user_func($this->howToWait, $this->timeToWait * 1000);
+
return $this;
}
diff --git a/src/Recruiter/Workable.php b/src/Recruiter/Workable.php
index aefa2471..937317a1 100644
--- a/src/Recruiter/Workable.php
+++ b/src/Recruiter/Workable.php
@@ -5,27 +5,19 @@
interface Workable
{
/**
- * Turn this `Recruiter\Workable` instance into a `Recruiter\Job` instance
- *
- * @param Recruiter $recruiter
- *
- * @return JobToSchedule
+ * Turn this `Recruiter\Workable` instance into a `Recruiter\Job` instance.
*/
- public function asJobOf(Recruiter $recruiter);
+ public function asJobOf(Recruiter $recruiter): JobToSchedule;
/**
- * Export parameters that need to be persisted
- *
- * @return array
+ * Export parameters that need to be persisted.
*/
- public function export();
+ public function export(): array;
/**
- * Import an array of parameters as a Workable instance
+ * Import an array of parameters as a Workable instance.
*
* @param array $parameters Previously exported parameters
- *
- * @return Workable
*/
- public static function import($parameters);
+ public static function import(array $parameters): static;
}
diff --git a/src/Recruiter/Workable/AlwaysFail.php b/src/Recruiter/Workable/AlwaysFail.php
index 485e967e..f0439e11 100644
--- a/src/Recruiter/Workable/AlwaysFail.php
+++ b/src/Recruiter/Workable/AlwaysFail.php
@@ -1,7 +1,7 @@
parameters['howManyItems']);
}
}
-
diff --git a/src/Recruiter/Workable/ThrowsFatalError.php b/src/Recruiter/Workable/ExitsAbruptly.php
similarity index 55%
rename from src/Recruiter/Workable/ThrowsFatalError.php
rename to src/Recruiter/Workable/ExitsAbruptly.php
index f8a16eaa..dd3e5876 100644
--- a/src/Recruiter/Workable/ThrowsFatalError.php
+++ b/src/Recruiter/Workable/ExitsAbruptly.php
@@ -1,15 +1,16 @@
steps = $steps;
}
- public function asJobOf(Recruiter $recruiter)
+ public function asJobOf(Recruiter $recruiter): JobToSchedule
{
return $recruiter->jobOf($this);
}
@@ -58,9 +57,9 @@ public function execute($retryOptions = null)
$callable = [$result, $step['method']];
}
if (!is_callable($callable)) {
- $message = "The following step does not result in a callable: " . var_export($step, true) . ".";
+ $message = 'The following step does not result in a callable: ' . var_export($step, true) . '.';
if (is_object($result)) {
- $message .= ' Reached object: ' . get_class($result);
+ $message .= ' Reached object: ' . $result::class;
} else {
$message .= ' Reached value: ' . var_export($result, true);
}
@@ -72,15 +71,16 @@ public function execute($retryOptions = null)
}
$result = call_user_func_array(
$callable,
- $arguments
+ $arguments,
);
}
+
return $result;
}
private function arguments($step)
{
- $arguments = isset($step['arguments']) ? $step['arguments'] : [];
+ $arguments = $step['arguments'] ?? [];
return $arguments;
}
@@ -94,18 +94,19 @@ public function __call($method, $arguments)
$step['arguments'] = $arguments;
}
$this->steps[] = $step;
+
return $this;
}
- public function export()
+ public function export(): array
{
return [
'steps' => $this->steps,
];
}
- public static function import($document)
+ public static function import(array $parameters): static
{
- return new self($document['steps']);
+ return new self($parameters['steps']);
}
}
diff --git a/src/Recruiter/Workable/FailsInConstructor.php b/src/Recruiter/Workable/FailsInConstructor.php
index 0c1e25df..2d70eb0e 100644
--- a/src/Recruiter/Workable/FailsInConstructor.php
+++ b/src/Recruiter/Workable/FailsInConstructor.php
@@ -1,7 +1,7 @@
usToSleep = $usToSleep;
- $this->usOfDelta = $usOfDelta;
}
- public function execute()
+ public function execute(): void
{
- usleep($this->usToSleep + (rand(intval(-$this->usOfDelta), $this->usOfDelta)));
+ usleep($this->usToSleep + random_int(intval(-$this->usOfDelta), $this->usOfDelta));
}
- public function export()
+ public function export(): array
{
return [
'us_to_sleep' => $this->usToSleep,
@@ -41,11 +36,11 @@ public function export()
];
}
- public static function import($parameters)
+ public static function import(array $parameters): static
{
return new self(
$parameters['us_to_sleep'],
- $parameters['us_of_delta']
+ $parameters['us_of_delta'],
);
}
}
diff --git a/src/Recruiter/Workable/RecoverRepeatableFromException.php b/src/Recruiter/Workable/RecoverRepeatableFromException.php
index 4fcce94c..bb33d04e 100644
--- a/src/Recruiter/Workable/RecoverRepeatableFromException.php
+++ b/src/Recruiter/Workable/RecoverRepeatableFromException.php
@@ -1,7 +1,7 @@
parameters = $parameters;
- $this->recoverForClass = $recoverForClass;
- $this->recoverForException = $recoverForException;
}
- public function execute()
+ public function execute(): never
{
- throw new \Exception(
- 'This job failed while instantiating a workable of class: ' . $this->recoverForClass . PHP_EOL .
- 'Original exception: ' . get_class($this->recoverForException) . PHP_EOL .
- $this->recoverForException->getMessage() . PHP_EOL .
- $this->recoverForException->getTraceAsString() . PHP_EOL
- );
+ throw new \Exception('This job failed while instantiating a workable of class: ' . $this->recoverForClass . PHP_EOL . 'Original exception: ' . $this->recoverForException::class . PHP_EOL . $this->recoverForException->getMessage() . PHP_EOL . $this->recoverForException->getTraceAsString() . PHP_EOL);
}
public function getClass()
@@ -38,6 +28,7 @@ public function urn(): string
{
$recoverForInstance = new $this->recoverForClass($this->parameters);
assert($recoverForInstance instanceof Repeatable);
+
return $recoverForInstance->urn();
}
@@ -45,6 +36,7 @@ public function unique(): bool
{
$recoverForInstance = new $this->recoverForClass($this->parameters);
assert($recoverForInstance instanceof Repeatable);
+
return $recoverForInstance->unique();
}
}
diff --git a/src/Recruiter/Workable/RecoverWorkableFromException.php b/src/Recruiter/Workable/RecoverWorkableFromException.php
index 53e2fc67..ebeacc86 100644
--- a/src/Recruiter/Workable/RecoverWorkableFromException.php
+++ b/src/Recruiter/Workable/RecoverWorkableFromException.php
@@ -1,7 +1,7 @@
parameters = $parameters;
- $this->recoverForClass = $recoverForClass;
- $this->recoverForException = $recoverForException;
}
- public function execute()
+ public function execute(): never
{
- throw new Exception(
- 'This job failed while instantiating a workable of class: ' . $this->recoverForClass . PHP_EOL .
- 'Original exception: ' . get_class($this->recoverForException) . PHP_EOL .
- $this->recoverForException->getMessage() . PHP_EOL .
- $this->recoverForException->getTraceAsString() . PHP_EOL
- );
+ throw new \Exception('This job failed while instantiating a workable of class: ' . $this->recoverForClass . PHP_EOL . 'Original exception: ' . $this->recoverForException::class . PHP_EOL . $this->recoverForException->getMessage() . PHP_EOL . $this->recoverForException->getTraceAsString() . PHP_EOL);
}
public function getClass()
diff --git a/src/Recruiter/Workable/SampleRepeatableCommand.php b/src/Recruiter/Workable/SampleRepeatableCommand.php
index 92a57b5c..07bb97be 100644
--- a/src/Recruiter/Workable/SampleRepeatableCommand.php
+++ b/src/Recruiter/Workable/SampleRepeatableCommand.php
@@ -3,19 +3,18 @@
namespace Recruiter\Workable;
use Recruiter\Repeatable;
-use Recruiter\SchedulePolicy\EveryMinutes;
-use Recruiter\SchedulePolicy;
+use Recruiter\RepeatableBehaviour;
use Recruiter\Workable;
use Recruiter\WorkableBehaviour;
-use Recruiter\RepeatableBehaviour;
class SampleRepeatableCommand implements Workable, Repeatable
{
- use WorkableBehaviour, RepeatableBehaviour;
+ use WorkableBehaviour;
+ use RepeatableBehaviour;
public function execute()
{
- var_export((new \DateTime())->format('c'));
+ var_export(new \DateTime()->format('c'));
}
public function urn(): string
diff --git a/src/Recruiter/Workable/ShellCommand.php b/src/Recruiter/Workable/ShellCommand.php
index 62aea315..c92778cc 100644
--- a/src/Recruiter/Workable/ShellCommand.php
+++ b/src/Recruiter/Workable/ShellCommand.php
@@ -4,40 +4,37 @@
use Recruiter\Workable;
use Recruiter\WorkableBehaviour;
-use RuntimeException;
class ShellCommand implements Workable
{
use WorkableBehaviour;
- private $commandLine;
-
public static function fromCommandLine($commandLine)
{
return new self($commandLine);
}
- private function __construct($commandLine)
+ private function __construct(private $commandLine)
{
- $this->commandLine = $commandLine;
}
public function execute()
{
exec($this->commandLine, $output, $returnCode);
$output = implode(PHP_EOL, $output);
- if ($returnCode != 0) {
- throw new RuntimeException("Command execution failed (return code $returnCode). Output: " . $output);
+ if (0 != $returnCode) {
+ throw new \RuntimeException("Command execution failed (return code $returnCode). Output: " . $output);
}
+
return $output;
}
- public function export()
+ public function export(): array
{
return ['command' => $this->commandLine];
}
- public static function import($parameters)
+ public static function import(array $parameters): static
{
return new self($parameters['command']);
}
diff --git a/src/Recruiter/WorkableBehaviour.php b/src/Recruiter/WorkableBehaviour.php
index 8cfbc88b..dd6f40e3 100644
--- a/src/Recruiter/WorkableBehaviour.php
+++ b/src/Recruiter/WorkableBehaviour.php
@@ -2,33 +2,28 @@
namespace Recruiter;
-use Exception;
-
trait WorkableBehaviour
{
- protected $parameters;
-
- public function __construct($parameters = [])
+ final public function __construct(protected array $parameters = [])
{
- $this->parameters = $parameters;
}
- public function asJobOf(Recruiter $recruiter)
+ public function asJobOf(Recruiter $recruiter): JobToSchedule
{
return $recruiter->jobOf($this);
}
- public function execute()
+ public function execute(): never
{
- throw new Exception('Workable::execute() need to be implemented');
+ throw new \Exception('Workable::execute() need to be implemented');
}
- public function export()
+ public function export(): array
{
return $this->parameters;
}
- public static function import($parameters)
+ public static function import(array $parameters): static
{
return new static($parameters);
}
diff --git a/src/Recruiter/WorkableInJob.php b/src/Recruiter/WorkableInJob.php
index 4ec2f421..f09b7f87 100644
--- a/src/Recruiter/WorkableInJob.php
+++ b/src/Recruiter/WorkableInJob.php
@@ -1,9 +1,8 @@
self::classNameOf($workable),
'parameters' => $workable->export(),
'method' => $methodToCall,
- ]
+ ],
];
}
@@ -56,10 +55,11 @@ public static function initialize()
private static function classNameOf($workable)
{
- $workableClassName = get_class($workable);
+ $workableClassName = $workable::class;
if (method_exists($workable, 'getClass')) {
$workableClassName = $workable->getClass();
}
+
return $workableClassName;
}
}
diff --git a/src/Recruiter/Worker.php b/src/Recruiter/Worker.php
index 8ea0c97c..072d5e3c 100644
--- a/src/Recruiter/Worker.php
+++ b/src/Recruiter/Worker.php
@@ -2,8 +2,6 @@
namespace Recruiter;
-use DateInterval;
-use DateTimeImmutable;
use MongoDB\BSON\ObjectId;
use MongoDB\Collection as MongoCollection;
use Recruiter\Infrastructure\Memory\MemoryLimit;
@@ -11,35 +9,22 @@
use Recruiter\Worker\Repository;
use Timeless as T;
use Timeless\Interval;
-use Timeless\Moment;
class Worker
{
- private $status;
- private $recruiter;
- private $repository;
- private $memoryLimit;
-
public static function workFor(
Recruiter $recruiter,
Repository $repository,
- MemoryLimit $memoryLimit
+ MemoryLimit $memoryLimit,
) {
$worker = new self(self::initialize(), $recruiter, $repository, $memoryLimit);
$worker->save();
+
return $worker;
}
- public function __construct(
- $status,
- Recruiter $recruiter,
- Repository $repository,
- MemoryLimit $memoryLimit
- ) {
- $this->status = $status;
- $this->recruiter = $recruiter;
- $this->repository = $repository;
- $this->memoryLimit = $memoryLimit;
+ public function __construct(private $status, private readonly Recruiter $recruiter, private readonly Repository $repository, private readonly MemoryLimit $memoryLimit)
+ {
}
public function id()
@@ -58,12 +43,14 @@ public function work()
if ($this->hasBeenAssignedToDoSomething()) {
$this->workOn(
$job = $this->recruiter->scheduledJob(
- $this->status['assigned_to'][(string)$this->status['_id']]
- )
+ $this->status['assigned_to'][(string) $this->status['_id']],
+ ),
);
+
return (string) $job->id();
} else {
$this->stillHere();
+
return false;
}
}
@@ -131,8 +118,8 @@ private function afterExecutionOf($job)
date('c'),
$this->id(),
$job->id(),
- get_class($e),
- $e->getMessage()
+ $e::class,
+ $e->getMessage(),
);
$this->retireAfterMemoryLimitIsExceeded();
@@ -154,7 +141,6 @@ private function retireAfterMemoryLimitIsExceeded()
$this->repository->retireWorkerWithId($this->id());
}
-
private function hasBeenAssignedToDoSomething()
{
if (is_null($this->status)) {
@@ -164,6 +150,7 @@ private function hasBeenAssignedToDoSomething()
// thing to do seems like terminate the process
exit(1);
}
+
return array_key_exists('assigned_to', $this->status);
}
@@ -192,13 +179,13 @@ private static function initialize()
'last_seen_at' => T\MongoDate::now(),
'created_at' => T\MongoDate::now(),
'working' => false,
- 'pid' => getmypid()
+ 'pid' => getmypid(),
];
}
public static function canWorkOnAnyJobs($worksOn)
{
- return $worksOn === '*';
+ return '*' === $worksOn;
}
public static function pickAvailableWorkers(MongoCollection $collection, $workersPerUnit)
@@ -208,9 +195,7 @@ public static function pickAvailableWorkers(MongoCollection $collection, $worker
if (count($workers) > 0) {
$unitsOfWorkers = array_group_by(
$workers,
- function ($worker) {
- return $worker['work_on'];
- }
+ fn ($worker) => $worker['work_on'],
);
foreach ($unitsOfWorkers as $workOn => $workersInUnit) {
$workersInUnit = array_column($workersInUnit, '_id');
@@ -218,16 +203,15 @@ function ($worker) {
$result[] = [$workOn, $workersInUnit];
}
}
+
return $result;
}
public static function tryToAssignJobsToWorkers(MongoCollection $collection, $jobs, $workers)
{
$assignment = array_combine(
- array_map(function ($id) {
- return (string)$id;
- }, $workers),
- $jobs
+ array_map(fn ($id) => (string) $id, $workers),
+ $jobs,
);
$result = $collection->updateMany(
@@ -235,8 +219,8 @@ public static function tryToAssignJobsToWorkers(MongoCollection $collection, $jo
$update = ['$set' => [
'available' => false,
'assigned_to' => $assignment,
- 'assigned_since' => T\MongoDate::now()
- ]]
+ 'assigned_since' => T\MongoDate::now(),
+ ]],
);
return [$assignment, $result->getModifiedCount()];
@@ -258,7 +242,7 @@ public static function assignedJobs(MongoCollection $collection)
return array_values(array_unique($jobs));
}
- public static function retireDeadWorkers(Repository $roster, DateTimeImmutable $now, Interval $consideredDeadAfter)
+ public static function retireDeadWorkers(Repository $roster, \DateTimeImmutable $now, Interval $consideredDeadAfter)
{
$consideredDeadAt = $now->sub($consideredDeadAfter->toDateInterval());
$deadWorkers = $roster->deadWorkers($consideredDeadAt);
@@ -266,8 +250,8 @@ public static function retireDeadWorkers(Repository $roster, DateTimeImmutable $
foreach ($deadWorkers as $deadWorker) {
$roster->retireWorkerWithId($deadWorker['_id']);
if (array_key_exists('assigned_to', $deadWorker)) {
- if (array_key_exists((string)$deadWorker['_id'], $deadWorker['assigned_to'])) {
- $jobsToReassign[] = $deadWorker['assigned_to'][(string)$deadWorker['_id']];
+ if (array_key_exists((string) $deadWorker['_id'], $deadWorker['assigned_to'])) {
+ $jobsToReassign[] = $deadWorker['assigned_to'][(string) $deadWorker['_id']];
}
}
}
diff --git a/src/Recruiter/Worker/Process.php b/src/Recruiter/Worker/Process.php
index 064f52ee..815be80f 100644
--- a/src/Recruiter/Worker/Process.php
+++ b/src/Recruiter/Worker/Process.php
@@ -3,38 +3,35 @@
namespace Recruiter\Worker;
use Sink\BlackHole;
-use Recruiter\Worker\Repository;
class Process
{
- private $pid;
-
- public static function withPid($pid)
+ public static function withPid(int $pid)
{
return new self($pid);
}
- public function __construct($pid)
+ public function __construct(private readonly int $pid)
{
- $this->pid = $pid;
}
- public function cleanUp(Repository $repository)
+ public function cleanUp(Repository $repository): void
{
if (!$this->isAlive()) {
$repository->retireWorkerWithPid($this->pid);
}
}
- public function ifDead()
+ public function ifDead(): BlackHole|static
{
if ($this->isAlive()) {
return new BlackHole();
}
+
return $this;
}
- protected function isAlive()
+ protected function isAlive(): bool
{
return posix_kill($this->pid, 0);
}
diff --git a/src/Recruiter/Worker/Repository.php b/src/Recruiter/Worker/Repository.php
index b54ad992..74fd14a9 100644
--- a/src/Recruiter/Worker/Repository.php
+++ b/src/Recruiter/Worker/Repository.php
@@ -5,17 +5,14 @@
use MongoDB;
use MongoDB\BSON\UTCDateTime as MongoUTCDateTime;
use Recruiter\Recruiter;
-use Recruiter\Worker;
class Repository
{
private $roster;
- private $recruiter;
- public function __construct(MongoDB\Database $db, Recruiter $recruiter)
+ public function __construct(MongoDB\Database $db, private readonly Recruiter $recruiter)
{
$this->roster = $db->selectCollection('roster');
- $this->recruiter = $recruiter;
}
public function save($worker)
@@ -24,7 +21,7 @@ public function save($worker)
$result = $this->roster->replaceOne(
['_id' => $document['_id']],
$document,
- ['upsert' => true]
+ ['upsert' => true],
);
}
@@ -32,14 +29,14 @@ public function atomicUpdate($worker, array $changeSet)
{
$this->roster->updateOne(
['_id' => $worker->id()],
- ['$set' => $changeSet]
+ ['$set' => $changeSet],
);
}
public function refresh($worker)
{
$worker->updateWith(
- $this->roster->findOne(['_id' => $worker->id()])
+ $this->roster->findOne(['_id' => $worker->id()]),
);
}
@@ -47,9 +44,9 @@ public function deadWorkers($consideredDeadAt)
{
return $this->roster->find(
['last_seen_at' => [
- '$lt' => new MongoUTCDateTime($consideredDeadAt->format('U') * 1000)]
+ '$lt' => new MongoUTCDateTime($consideredDeadAt->format('U') * 1000)],
],
- ['projection' => ['_id' => true, 'assigned_to' => true]]
+ ['projection' => ['_id' => true, 'assigned_to' => true]],
);
}
diff --git a/src/Recruiter/WorkerDiedInTheLineOfDutyException.php b/src/Recruiter/WorkerDiedInTheLineOfDutyException.php
index 7a3d83e0..6543f13b 100644
--- a/src/Recruiter/WorkerDiedInTheLineOfDutyException.php
+++ b/src/Recruiter/WorkerDiedInTheLineOfDutyException.php
@@ -1,8 +1,7 @@
$value) {
- if (!call_user_func($predicate, $value, $key, $array)) {
- return false;
- }
- }
- return true;
-}
+ $f = $f ?: (fn ($value) => $value);
-function array_some($array, callable $predicate)
-{
- foreach ($array as $key => $value) {
- if (call_user_func($predicate, $value, $key, $array)) {
- return true;
- }
- }
- return false;
-}
-
-function array_group_by($array, callable $f = null)
-{
- $f = $f ?: function ($value) {
- return $value;
- };
return array_reduce(
$array,
function ($buckets, $x) use ($f) {
@@ -34,8 +14,9 @@ function ($buckets, $x) use ($f) {
$buckets[$key] = [];
}
$buckets[$key][] = $x;
+
return $buckets;
},
- []
+ [],
);
}
diff --git a/src/Sink/BlackHole.php b/src/Sink/BlackHole.php
index 154fe15a..e4571ee6 100644
--- a/src/Sink/BlackHole.php
+++ b/src/Sink/BlackHole.php
@@ -2,10 +2,10 @@
namespace Sink;
-use Iterator;
use ArrayAccess;
+use Iterator;
-class BlackHole implements Iterator, ArrayAccess
+class BlackHole implements \Iterator, \ArrayAccess, \Stringable
{
public function __construct()
{
@@ -15,90 +15,90 @@ public function __destruct()
{
}
- public function __set($name, $value)
+ public function __set(string $name, mixed $value)
{
}
- public function __get($name)
+ public function __get(string $name): self
{
return $this;
}
- public function __isset($name)
+ public function __isset(string $name): bool
{
return false;
}
- public function __unset($name)
+ public function __unset(string $name): void
{
}
- public function __call($name, $arguments)
+ public function __call(string $name, array $arguments)
{
return $this;
}
- public function __toString()
+ public function __toString(): string
{
return '';
}
- public function __invoke()
+ public function __invoke(): self
{
return $this;
}
- public function __clone()
+ public function __clone(): void
{
}
- public static function __callStatic($name, $args)
+ public static function __callStatic(string $name, array $args): self
{
return new self();
}
// Iterator Interface
- public function current()
+ public function current(): self
{
return $this;
}
- public function key()
+ public function key(): self
{
return $this;
}
- public function next()
+ public function next(): void
{
}
- public function rewind()
+ public function rewind(): void
{
}
- public function valid()
+ public function valid(): bool
{
return false;
}
// ArrayAccess Interface
- public function offsetExists($offset)
+ public function offsetExists(mixed $offset): bool
{
return false;
}
- public function offsetGet($offset)
+ public function offsetGet(mixed $offset): self
{
return $this;
}
- public function offsetSet($offset, $value)
+ public function offsetSet(mixed $offset, mixed $value): void
{
}
- public function offsetUnset($offset)
+ public function offsetUnset(mixed $offset): void
{
}
}
diff --git a/src/Timeless/Clock.php b/src/Timeless/Clock.php
index eb8cc7c0..0c90fa27 100644
--- a/src/Timeless/Clock.php
+++ b/src/Timeless/Clock.php
@@ -2,15 +2,24 @@
namespace Timeless;
-class Clock
+class Clock implements ClockInterface
{
- public function stop()
+ public function start(): self
{
- return clock(new StoppedClock($this->now()));
+ clock($this);
+
+ return $this;
+ }
+
+ public function stop(): StoppedClock
+ {
+ clock($clock = new StoppedClock($this->now()));
+
+ return $clock;
}
- public function now()
+ public function now(): Moment
{
- return new Moment(round(microtime(true) * 1000));
+ return new Moment((int) round(microtime(true) * 1000));
}
}
diff --git a/src/Timeless/ClockInterface.php b/src/Timeless/ClockInterface.php
new file mode 100644
index 00000000..65faa620
--- /dev/null
+++ b/src/Timeless/ClockInterface.php
@@ -0,0 +1,12 @@
+ms = intval($ms);
}
public function us(): int
@@ -101,7 +96,7 @@ public function from(Moment $reference): Moment
return $reference->after($this);
}
- public function multiplyBy($multiplier): self
+ public function multiplyBy(int $multiplier): self
{
return new self($this->ms * $multiplier);
}
@@ -111,48 +106,46 @@ public function add(Interval $interval): self
return new self($this->ms + $interval->ms);
}
- public function format($format)
- {
- if (is_string($format)) {
- $availableFormatsTable = [
- 'ms' => ['milliseconds', 'ms', 'ms'],
- 's' => ['seconds', 's', 's'],
- 'm' => ['minutes', 'm', 'm'],
- 'h' => ['hours', 'h', 'h'],
- 'd' => ['days', 'd', 'd'],
- 'w' => ['weeks', 'w', 'w'],
- 'mo' => ['months', 'mo', 'mo'],
- 'y' => ['years', 'y', 'y'],
- 'milliseconds' => ['milliseconds', ' milliseconds', ' millisecond'],
- 'seconds' => ['seconds', ' seconds', ' second'],
- 'minutes' => ['minutes', ' minutes', ' minute'],
- 'hours' => ['hours', ' hours', ' hour'],
- 'days' => ['days', ' days', ' day'],
- 'weeks' => ['weeks', ' weeks', ' week'],
- 'months' => ['months', ' months', ' month'],
- 'years' => ['years', ' years', ' year'],
- ];
- $format = trim($format);
- if (array_key_exists($format, $availableFormatsTable)) {
- $callable = [$this, $availableFormatsTable[$format][0]];
- if (!is_callable($callable)) {
- throw new \RuntimeException("function `{$availableFormatsTable[$format][0]}` does not exists");
- }
-
- $amountOfTime = call_user_func($callable);
- $unitOfTime = $amountOfTime === 1 ?
- $availableFormatsTable[$format][2] :
- $availableFormatsTable[$format][1];
- return sprintf('%d%s', $amountOfTime, $unitOfTime);
+ public function format(string $format): string
+ {
+ $availableFormatsTable = [
+ 'ms' => ['milliseconds', 'ms', 'ms'],
+ 's' => ['seconds', 's', 's'],
+ 'm' => ['minutes', 'm', 'm'],
+ 'h' => ['hours', 'h', 'h'],
+ 'd' => ['days', 'd', 'd'],
+ 'w' => ['weeks', 'w', 'w'],
+ 'mo' => ['months', 'mo', 'mo'],
+ 'y' => ['years', 'y', 'y'],
+ 'milliseconds' => ['milliseconds', ' milliseconds', ' millisecond'],
+ 'seconds' => ['seconds', ' seconds', ' second'],
+ 'minutes' => ['minutes', ' minutes', ' minute'],
+ 'hours' => ['hours', ' hours', ' hour'],
+ 'days' => ['days', ' days', ' day'],
+ 'weeks' => ['weeks', ' weeks', ' week'],
+ 'months' => ['months', ' months', ' month'],
+ 'years' => ['years', ' years', ' year'],
+ ];
+ $format = trim($format);
+ if (array_key_exists($format, $availableFormatsTable)) {
+ $callable = [$this, $availableFormatsTable[$format][0]];
+ if (!is_callable($callable)) {
+ throw new \RuntimeException("function `{$availableFormatsTable[$format][0]}` does not exists");
}
- throw new InvalidIntervalFormat("'{$format}' is not a valid Interval format");
+
+ $amountOfTime = call_user_func($callable);
+ $unitOfTime = 1 === $amountOfTime ?
+ $availableFormatsTable[$format][2] :
+ $availableFormatsTable[$format][1];
+
+ return sprintf('%d%s', $amountOfTime, $unitOfTime);
}
- throw new InvalidIntervalFormat('You need to use strings');
+ throw new InvalidIntervalFormat("'{$format}' is not a valid Interval format");
}
public function toDateInterval()
{
- return new DateInterval("PT{$this->seconds()}S");
+ return new \DateInterval("PT{$this->seconds()}S");
}
public static function parse($string)
@@ -169,7 +162,7 @@ public static function parse($string)
'years' => 'years', 'year' => 'years', 'y' => 'years',
];
$units = implode('|', array_keys($tokenToFunction));
- if (preg_match("/^[^\d]*(?P\d+)\s*(?P{$units})(?:\W.*|$)/", $string, $matches)) {
+ if (preg_match("/^[^\\d]*(?P\\d+)\\s*(?P{$units})(?:\\W.*|$)/", $string, $matches)) {
$callable = 'Timeless\\' . $tokenToFunction[$matches['unit']];
if (is_callable($callable)) {
return call_user_func($callable, $matches['quantity']);
@@ -188,10 +181,11 @@ public static function parse($string)
throw new InvalidIntervalFormat('You need to use strings');
}
- public static function fromDateInterval(DateInterval $interval)
+ public static function fromDateInterval(\DateInterval $interval): self
{
- $startTime = new DateTimeImmutable();
+ $startTime = new \DateTimeImmutable();
$endTime = $startTime->add($interval);
+
return new self(($endTime->getTimestamp() - $startTime->getTimestamp()) * 1000);
}
}
diff --git a/src/Timeless/InvalidIntervalFormat.php b/src/Timeless/InvalidIntervalFormat.php
index 55ca83b4..01175de2 100644
--- a/src/Timeless/InvalidIntervalFormat.php
+++ b/src/Timeless/InvalidIntervalFormat.php
@@ -2,8 +2,6 @@
namespace Timeless;
-use Exception;
-
-class InvalidIntervalFormat extends Exception
+class InvalidIntervalFormat extends \Exception
{
}
diff --git a/src/Timeless/Moment.php b/src/Timeless/Moment.php
index 94eeeabe..4e394126 100644
--- a/src/Timeless/Moment.php
+++ b/src/Timeless/Moment.php
@@ -2,80 +2,74 @@
namespace Timeless;
-use DateTime;
-use DateTimeZone;
-
-class Moment
+readonly class Moment
{
- private $ms;
-
- public static function fromTimestamp($ts)
+ public static function fromTimestamp(int $ts): self
{
return new self($ts * 1000);
}
- public static function fromDateTime(DateTime $dateTime)
+ public static function fromDateTime(\DateTime $dateTime): self
{
- return static::fromTimestamp($dateTime->getTimestamp());
+ return self::fromTimestamp($dateTime->getTimestamp());
}
- public function __construct($ms)
+ public function __construct(private int $ms)
{
- $this->ms = $ms;
}
- public function milliseconds()
+ public function milliseconds(): int
{
return $this->ms;
}
- public function ms()
+ public function ms(): int
{
return $this->ms;
}
- public function seconds()
+ public function seconds(): int
{
return $this->s();
}
- public function s()
+ public function s(): int
{
- return round($this->ms / 1000.0);
+ return (int) round($this->ms / 1000.0);
}
- public function after(Interval $d)
+ public function after(Interval $d): self
{
return new self($this->ms + $d->ms());
}
- public function before(Interval $d)
+ public function before(Interval $d): self
{
return new self($this->ms - $d->ms());
}
- public function isAfter(Moment $m)
+ public function isAfter(Moment $m): bool
{
return $this->ms >= $m->ms();
}
- public function isBefore(Moment $m)
+ public function isBefore(Moment $m): bool
{
return $this->ms <= $m->ms();
}
- public function toSecondPrecision()
+ public function toSecondPrecision(): Moment
{
return new self($this->s() * 1000);
}
- public function format()
+ public function format(): string
{
- return (new DateTime('@' . $this->s(), new DateTimeZone('UTC')))->format(DateTime::RFC3339);
+ return new \DateTime('@' . $this->s(), new \DateTimeZone('UTC'))->format(\DateTime::RFC3339);
}
- public function toDateTime()
+ public function toDateTime(): \DateTime
{
- return new DateTime('@' . $this->s(), new DateTimeZone('UTC'));
+ return new \DateTime('@' . $this->s(), new \DateTimeZone('UTC'));
}
}
diff --git a/src/Timeless/MongoDate.php b/src/Timeless/MongoDate.php
index 255ccdc2..d61714de 100644
--- a/src/Timeless/MongoDate.php
+++ b/src/Timeless/MongoDate.php
@@ -1,4 +1,5 @@
now = $now;
}
- public function now()
+ public function now(): Moment
{
return $this->now;
}
- public function driftForwardBySeconds($seconds)
+ public function driftForwardBySeconds(int $seconds): void
{
$this->now = $this->now->after(seconds($seconds));
}
- public function start()
+ public function start(): Clock
+ {
+ clock($clock = new Clock());
+
+ return $clock;
+ }
+
+ public function stop(): self
{
- return clock(new Clock());
+ clock($this);
+
+ return $this;
}
}
diff --git a/src/Timeless/functions.php b/src/Timeless/functions.php
index 26b0a0ab..74e2ec13 100644
--- a/src/Timeless/functions.php
+++ b/src/Timeless/functions.php
@@ -2,102 +2,104 @@
namespace Timeless;
-function clock($clock = null)
+function clock(?ClockInterface $clock = null): ClockInterface
{
global $__2852bec4cda046fca0e5e21dc007935c;
+ /** @var ClockInterface $__2852bec4cda046fca0e5e21dc007935c */
$__2852bec4cda046fca0e5e21dc007935c =
$clock ?: (
$__2852bec4cda046fca0e5e21dc007935c ?: new Clock()
);
+
return $__2852bec4cda046fca0e5e21dc007935c;
}
-function now()
+function now(): Moment
{
return clock()->now();
}
-function millisecond($numberOf)
+function millisecond(int $numberOf): Interval
{
return milliseconds($numberOf);
}
-function milliseconds($numberOf)
+function milliseconds(int $numberOf): Interval
{
return new Interval($numberOf);
}
-function second($numberOf)
+function second(int $numberOf): Interval
{
return seconds($numberOf);
}
-function seconds($numberOf)
+function seconds(int $numberOf): Interval
{
return new Interval($numberOf * Interval::MILLISECONDS_IN_SECONDS);
}
-function minute($numberOf)
+function minute(int $numberOf): Interval
{
return minutes($numberOf);
}
-function minutes($numberOf)
+function minutes(int $numberOf): Interval
{
return new Interval($numberOf * Interval::MILLISECONDS_IN_MINUTES);
}
-function hour($numberOf)
+function hour(int $numberOf): Interval
{
return hours($numberOf);
}
-function hours($numberOf)
+function hours(int $numberOf): Interval
{
return new Interval($numberOf * Interval::MILLISECONDS_IN_HOURS);
}
-function day($numberOf)
+function day(int $numberOf): Interval
{
return days($numberOf);
}
-function days($numberOf)
+function days(int $numberOf): Interval
{
return new Interval($numberOf * Interval::MILLISECONDS_IN_DAYS);
}
-function week($numberOf)
+function week(int $numberOf): Interval
{
return weeks($numberOf);
}
-function weeks($numberOf)
+function weeks(int $numberOf): Interval
{
return new Interval($numberOf * Interval::MILLISECONDS_IN_WEEKS);
}
-function month($numberOf)
+function month(int $numberOf): Interval
{
return months($numberOf);
}
-function months($numberOf)
+function months(int $numberOf): Interval
{
return new Interval($numberOf * Interval::MILLISECONDS_IN_MONTHS);
}
-function year($numberOf)
+function year(int $numberOf): Interval
{
return years($numberOf);
}
-function years($numberOf)
+function years(int $numberOf): Interval
{
return new Interval($numberOf * Interval::MILLISECONDS_IN_YEARS);
}
-function fromDateInterval(\DateInterval $interval)
+function fromDateInterval(\DateInterval $interval): Interval
{
$seconds = (string) $interval->s;
if ($interval->i) {