diff --git a/.github/workflows/laravel.yml b/.github/workflows/laravel.yml index 69a2aa1..3fc426e 100644 --- a/.github/workflows/laravel.yml +++ b/.github/workflows/laravel.yml @@ -26,6 +26,8 @@ jobs: run: php -r "file_exists('.env') || copy('.env.example', '.env');" - name: Install Dependencies run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist + - name: Run Static Analysis + run: vendor/bin/phpstan analyse --memory-limit=512M - name: Execute tests (Unit and Feature tests) via PHPUnit run: vendor/bin/phpunit - name: Upload coverage reports to Codecov diff --git a/composer.json b/composer.json index 2aa4596..e9dc63d 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,8 @@ ], "require-dev": { "orchestra/testbench": "^6.28", - "phpunit/phpunit": "^9.6" + "phpunit/phpunit": "^9.6", + "phpstan/phpstan": "^2.1" }, "extra": { "laravel": { @@ -36,12 +37,13 @@ "scripts": { "test":"./vendor/bin/phpunit", "test-random-time-macros":"@test --filter RandomTimeMacros", - "test-random-date-macros":"@test --filter RandomDateMacrosTest" + "test-random-date-macros":"@test --filter RandomDateMacrosTest", + "static-analysis": "vendor/bin/phpstan analyse" }, "minimum-stability": "stable", "prefer-stable": true, "require": { - "php": ">=7.4", + "php": ">=8.1", "paragonie/seedspring": "^1.2" } } diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..020db25 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,8 @@ +parameters: + level: 6 + paths: + - src + - config + + excludePaths: + - src/RNGs/MersenneTwister.php diff --git a/src/Enums/RandomDateScheduleBasis.php b/src/Enums/RandomDateScheduleBasis.php index 65fbe19..b266ff0 100644 --- a/src/Enums/RandomDateScheduleBasis.php +++ b/src/Enums/RandomDateScheduleBasis.php @@ -6,6 +6,7 @@ class RandomDateScheduleBasis { + //TODO: Change to PHP 8.X Enum public const WEEK=10; public const MONTH=20; @@ -32,6 +33,9 @@ public static function isValid(int $basis):bool{ return in_array($basis,self::getAll()); } + /** + * @return int[] + */ public static function getAll():array{ return[ 'WEEK'=>self::WEEK, diff --git a/src/Exceptions/IncorrectRangeException.php b/src/Exceptions/IncorrectRangeException.php index 586a0a5..cea4522 100644 --- a/src/Exceptions/IncorrectRangeException.php +++ b/src/Exceptions/IncorrectRangeException.php @@ -5,20 +5,27 @@ class IncorrectRangeException extends \Exception { - private string $min; - private string $max; - /** * @param string $min * @param string $max */ - public function __construct(string $min, string $max) + public function __construct(private string $min, private string $max) { - $this->min = $min; - $this->max = $max; - $msg="${min} is bigger/later than ${max}! Please correct your parameters."; + $msg="{$this->getMin()} is bigger/later than {$this->getMax()}! Please correct your parameters."; parent::__construct($msg); } + public function getMax(): string + { + return $this->max; + } + + public function getMin(): string + { + return $this->min; + } + + + } \ No newline at end of file diff --git a/src/Exceptions/MissingSeedException.php b/src/Exceptions/MissingSeedException.php index d9fe850..67eb065 100644 --- a/src/Exceptions/MissingSeedException.php +++ b/src/Exceptions/MissingSeedException.php @@ -2,8 +2,13 @@ namespace Skywarth\ChaoticSchedule\Exceptions; +use Throwable; + class MissingSeedException extends \Exception { - protected $message='RNGAdapter is missing seed value! Set the seed first, before accessing'; + public function __construct(string $message = 'RNGAdapter is missing seed value! Set the seed first, before accessing', int $code = 0, ?Throwable $previous = null) + { + parent::__construct($message, $code, $previous); + } } \ No newline at end of file diff --git a/src/RNGs/Adapters/AbstractRNGAdapter.php b/src/RNGs/Adapters/AbstractRNGAdapter.php index 615afce..56e36a1 100644 --- a/src/RNGs/Adapters/AbstractRNGAdapter.php +++ b/src/RNGs/Adapters/AbstractRNGAdapter.php @@ -10,7 +10,10 @@ abstract class AbstractRNGAdapter implements RandomNumberGeneratorAdapter protected int $seed; - public function __construct(int $seed=null) + /** + * @throws InvalidSeedFormatException + */ + public function __construct(?int $seed=null) { if(!is_null($seed)){ $this->setSeed($seed); @@ -31,7 +34,9 @@ public function getSeed(): int } - + /** + * @throws InvalidSeedFormatException + */ public final function setSeed(int $seed):RandomNumberGeneratorAdapter { if(!$this->validateSeed($seed)){//Maybe another method for padding the missing bytes/length ? diff --git a/src/RNGs/Adapters/RandomNumberGeneratorAdapter.php b/src/RNGs/Adapters/RandomNumberGeneratorAdapter.php index 8f4c300..c13fe32 100644 --- a/src/RNGs/Adapters/RandomNumberGeneratorAdapter.php +++ b/src/RNGs/Adapters/RandomNumberGeneratorAdapter.php @@ -6,7 +6,7 @@ interface RandomNumberGeneratorAdapter { - public function __construct(int $seed=null);//TODO: maybe make seed into string ? + public function __construct(?int $seed=null);//TODO: maybe make seed into string ? public function setSeed(int $seed):RandomNumberGeneratorAdapter; public function getSeed():int; diff --git a/src/RNGs/Adapters/SeedSpringAdapter.php b/src/RNGs/Adapters/SeedSpringAdapter.php index 1ce443d..71fb8d6 100644 --- a/src/RNGs/Adapters/SeedSpringAdapter.php +++ b/src/RNGs/Adapters/SeedSpringAdapter.php @@ -3,6 +3,7 @@ namespace Skywarth\ChaoticSchedule\RNGs\Adapters; +use Exception; use ParagonIE\SeedSpring\SeedSpring; class SeedSpringAdapter extends AbstractRNGAdapter @@ -12,6 +13,9 @@ class SeedSpringAdapter extends AbstractRNGAdapter private SeedSpring $seedSpring; + /** + * @throws Exception + */ public function intBetween(int $floor, int $ceil): int { //Boundaries are inclusive @@ -32,12 +36,12 @@ public function validateSeed(int $seed): bool { // Check the length of the binary representation - return strlen($seed) === (self::PROVIDER_SEED_BYTES); + return strlen((string)$seed) === (self::PROVIDER_SEED_BYTES); } protected function setProviderSeed(int $seed): RandomNumberGeneratorAdapter { - $this->seedSpring=new SeedSpring($seed); + $this->seedSpring=new SeedSpring((string)$seed); return $this; } } \ No newline at end of file diff --git a/src/RNGs/RNGFactory.php b/src/RNGs/RNGFactory.php index 80527fd..edae650 100644 --- a/src/RNGs/RNGFactory.php +++ b/src/RNGs/RNGFactory.php @@ -17,7 +17,6 @@ class RNGFactory public function __construct(string $rngEngineSlug) { $this->rngEngineSlug = $rngEngineSlug; - return $this; } /** @@ -29,7 +28,7 @@ public function getRngEngineSlug(): string } - public function getRngEngine(int $seed=null):RandomNumberGeneratorAdapter{ + public function getRngEngine(?int $seed=null):RandomNumberGeneratorAdapter{ //TODO: array containing ::class for comparison, you don't really need if-else if($this->getRngEngineSlug()===MersenneTwisterAdapter::getAdapterSlug()){ return new MersenneTwisterAdapter($seed); diff --git a/src/Services/ChaoticSchedule.php b/src/Services/ChaoticSchedule.php index 142df53..b644aa6 100644 --- a/src/Services/ChaoticSchedule.php +++ b/src/Services/ChaoticSchedule.php @@ -4,9 +4,8 @@ use Carbon\Carbon; use Carbon\CarbonPeriod; -use Carbon\Exceptions\InvalidFormatException; +use Exception; use Illuminate\Console\Scheduling\Event; -use Illuminate\Console\Scheduling\Schedule; use InvalidArgumentException; use LogicException; use OutOfRangeException; @@ -70,7 +69,7 @@ public function getBasisDate():Carbon{ /** * @throws IncompatibleClosureResponse */ - private function validateClosureResponse($closureResponse, string $expected){ + private function validateClosureResponse(mixed $closureResponse, string $expected):void { //TODO: expected should also be used as closure. Both applicable, closure and primitive types $type=gettype($closureResponse); if($type!==$expected){ @@ -107,7 +106,7 @@ public function randomTimeSchedule(Event $schedule, string $minTime, string $max try{ $minTimeCasted=Carbon::createFromFormat('H:i',$minTime); $maxTimeCasted=Carbon::createFromFormat('H:i',$maxTime); - }catch (InvalidFormatException $ex){ + }catch (Exception $ex){ throw new InvalidDateFormatException("Given time format is invalid. minTime and maxTime parameters should be in 'H:i' format.",0,$ex); } if($minTimeCasted->isAfter($maxTimeCasted)){ @@ -140,7 +139,7 @@ public function randomTimeSchedule(Event $schedule, string $minTime, string $max public function randomMinuteSchedule(Event $schedule, int $minMinutes=0, int $maxMinutes=59, ?string $uniqueIdentifier=null,?callable $closure=null):Event{ if($minMinutes>$maxMinutes){ - throw new IncorrectRangeException($minMinutes,$maxMinutes); + throw new IncorrectRangeException((string)$minMinutes,(string)$maxMinutes); } if($minMinutes<0 || $maxMinutes>59){ throw new OutOfRangeException('Provide min-max minute parameters between 0 and 59.'); @@ -190,7 +189,7 @@ public function randomMultipleMinutesSchedule(Event $schedule, int $minMinutes=0 //TODO: merging this method and randomMinute() kinda makes sense, not sure if I should. Open to discussion. if($minMinutes>$maxMinutes){ - throw new IncorrectRangeException($minMinutes,$maxMinutes); + throw new IncorrectRangeException((string)$minMinutes,(string)$maxMinutes); } if($minMinutes<0 || $maxMinutes>59){ throw new OutOfRangeException('Provide min-max minute parameters between 0 and 59.'); @@ -199,7 +198,7 @@ public function randomMultipleMinutesSchedule(Event $schedule, int $minMinutes=0 throw new LogicException('TimesMin and TimesMax has to be non-negative numbers!');//TODO: duplicate, refactor } if($timesMin>$timesMax){ - throw new IncorrectRangeException($timesMin,$timesMax); //TODO: duplicate, refactor + throw new IncorrectRangeException((string)$timesMin,(string)$timesMax); //TODO: duplicate, refactor } @@ -266,17 +265,19 @@ public function randomMultipleMinutesSchedule(Event $schedule, int $minMinutes=0 /** + * @param int[] $daysOfTheWeek * @throws IncorrectRangeException * @throws InvalidScheduleBasisProvided * @throws IncompatibleClosureResponse|RunTimesExpectationCannotBeMet */ - public function randomDaysSchedule(Event $schedule, int $periodType, ?array $daysOfTheWeek, int $timesMin, int $timesMax, ?string $uniqueIdentifier=null,?callable $closure=null):Event{ + public function randomDaysSchedule(Event $schedule, int $periodType, ?array $daysOfTheWeek, int $timesMin, int $timesMax, ?string $uniqueIdentifier=null, ?callable $closure=null):Event{ if(empty($daysOfTheWeek)){ $daysOfTheWeek=self::ALL_DOW; }else{ //Validate DOW foreach ($daysOfTheWeek as $dayNum){ - if(gettype($dayNum)!=='integer'){ + // @phpstan-ignore notIdentical.alwaysFalse + if(gettype($dayNum) !== 'integer'){ throw new InvalidArgumentException('daysOfTheWeek contains non-integer value! It should contain only integer values which represent days of the week.'); } if(!in_array($dayNum,self::ALL_DOW)){ @@ -291,7 +292,7 @@ public function randomDaysSchedule(Event $schedule, int $periodType, ?array $day throw new LogicException('TimesMin and TimesMax has to be non-negative numbers!'); } if($timesMin>$timesMax){ - throw new IncorrectRangeException($timesMin,$timesMax); + throw new IncorrectRangeException((string)$timesMin,(string)$timesMax); } RandomDateScheduleBasis::validate($periodType); @@ -460,6 +461,7 @@ private function registerAtRandomMacro(){ Event::macro('atRandom', function (string $minTime, string $maxTime,?string $uniqueIdentifier=null,?callable $closure=null) use($chaoticSchedule){ //Laravel automatically injects and replaces $this in the context /** @var Event $this */ + // @phpstan-ignore varTag.nativeType return $chaoticSchedule->randomTimeSchedule($this,$minTime,$maxTime,$uniqueIdentifier,$closure); }); @@ -473,6 +475,7 @@ private function registerDailyAtRandomRandomMacro(){ Event::macro('dailyAtRandom', function (string $minTime, string $maxTime,?string $uniqueIdentifier=null,?callable $closure=null) use($chaoticSchedule){ //Laravel automatically injects and replaces $this in the context /** @var Event $this */ + // @phpstan-ignore varTag.nativeType return $chaoticSchedule->randomTimeSchedule($this,$minTime,$maxTime,$uniqueIdentifier,$closure); }); @@ -486,6 +489,7 @@ private function registerHourlyAtRandomMacro(){ Event::macro('hourlyAtRandom', function (int $minMinutes=0, int $maxMinutes=59,?string $uniqueIdentifier=null,?callable $closure=null) use($chaoticSchedule){ //Laravel automatically injects and replaces $this in the context /** @var Event $this */ + // @phpstan-ignore varTag.nativeType return $chaoticSchedule->randomMinuteSchedule($this,$minMinutes,$maxMinutes,$uniqueIdentifier,$closure); }); @@ -503,6 +507,7 @@ private function registerHourlyMultipleAtRandomMacro(){ Event::macro('hourlyMultipleAtRandom', function (int $minMinutes=0, int $maxMinutes=59, int $timesMin=1, int $timesMax=1, ?string $uniqueIdentifier=null,?callable $closure=null) use($chaoticSchedule){ //Laravel automatically injects and replaces $this in the context /** @var Event $this */ + // @phpstan-ignore varTag.nativeType return $chaoticSchedule->randomMultipleMinutesSchedule($this,$minMinutes,$maxMinutes,$timesMin,$timesMax,$uniqueIdentifier,$closure); }); @@ -517,6 +522,7 @@ private function registerRandomDaysMacro(){ Event::macro('randomDays', function (int $periodType, ?array $daysOfTheWeek, int $timesMin, int $timesMax, ?string $uniqueIdentifier=null,?callable $closure=null) use($chaoticSchedule){ //Laravel automatically injects and replaces $this in the context /** @var Event $this */ + // @phpstan-ignore varTag.nativeType return $chaoticSchedule->randomDaysSchedule($this,$periodType,$daysOfTheWeek,$timesMin,$timesMax,$uniqueIdentifier,$closure); }); diff --git a/src/Services/SeedGenerationService.php b/src/Services/SeedGenerationService.php index 3e219a0..0f1ad5f 100644 --- a/src/Services/SeedGenerationService.php +++ b/src/Services/SeedGenerationService.php @@ -74,7 +74,7 @@ private function castToSeedFormat(string $hash):int{ private function hash(string $raw):string{ //return hash('crc32',$raw); //Produces hexadecimal format, containing characters. - return crc32($raw); + return (string)crc32($raw); }