From 505307a276380f8b2de2b50702eb8805d4c3e701 Mon Sep 17 00:00:00 2001 From: Nico Date: Sun, 24 Jan 2021 00:22:25 +0100 Subject: [PATCH 01/10] Release for PHP 8 --- .travis.yml | 1 + README.md | 2 +- composer.json | 2 +- phpunit.xml.dist | 23 ++-- src/Compiler/StandardCompiler.php | 33 +++-- src/Evaluator/Evaluator.php | 12 +- src/Expression/BaseExpression.php | 7 +- src/Expression/EqualExpression.php | 2 +- src/Expression/EqualStrictExpression.php | 2 +- src/Expression/ExpressionFactory.php | 30 +++-- src/Expression/GreaterThanEqualExpression.php | 2 +- src/Expression/GreaterThanExpression.php | 2 +- src/Expression/InExpression.php | 4 +- src/Expression/LessThanEqualExpression.php | 2 +- src/Expression/LessThanExpression.php | 2 +- src/Expression/NotEqualExpression.php | 2 +- src/Expression/NotEqualStrictExpression.php | 2 +- src/Grammar/CallableFunction.php | 6 +- src/Grammar/Grammar.php | 2 +- .../JavaScript/Functions/ParseFloat.php | 2 +- src/Grammar/JavaScript/Functions/ParseInt.php | 2 +- src/Grammar/JavaScript/JavaScript.php | 22 ++-- src/Grammar/JavaScript/Methods/CharAt.php | 2 +- src/Grammar/JavaScript/Methods/EndsWith.php | 4 +- src/Grammar/JavaScript/Methods/IndexOf.php | 2 +- src/Grammar/JavaScript/Methods/Join.php | 2 +- src/Grammar/JavaScript/Methods/Replace.php | 4 +- src/Grammar/JavaScript/Methods/Split.php | 4 +- src/Grammar/JavaScript/Methods/StartsWith.php | 4 +- src/Grammar/JavaScript/Methods/Substr.php | 4 +- src/Grammar/JavaScript/Methods/Test.php | 6 +- src/Highlighter/Highlighter.php | 8 +- src/Parser/Exception/ParserException.php | 5 + src/Parser/Parser.php | 89 +++++-------- src/Rule.php | 15 +-- src/TokenStream/AST.php | 21 ++-- src/TokenStream/CallableUserMethod.php | 28 ++--- src/TokenStream/CallableUserMethodFactory.php | 2 +- src/TokenStream/Node/BaseNode.php | 9 +- src/TokenStream/Token/BaseToken.php | 17 +-- src/TokenStream/Token/Token.php | 59 +++++---- src/TokenStream/Token/TokenBool.php | 2 +- src/TokenStream/Token/TokenEncapsedString.php | 2 +- src/TokenStream/Token/TokenFactory.php | 118 ++++++++---------- src/TokenStream/Token/TokenFloat.php | 2 +- src/TokenStream/Token/TokenNull.php | 2 +- src/TokenStream/Token/TokenType.php | 26 ++-- src/TokenStream/TokenStream.php | 21 ++-- src/Tokenizer/Tokenizer.php | 29 ++--- src/autoload.php | 2 +- src/container.php | 24 ++-- 51 files changed, 287 insertions(+), 390 deletions(-) diff --git a/.travis.yml b/.travis.yml index a1dd956..8bff45b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ php: - 7.2 - 7.3 - 7.4 + - 8.0 script: - composer install --dev diff --git a/README.md b/README.md index 0d91d2d..a656d84 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## PHP Rule Engine for PHP 7 +## PHP Rule Engine [![Latest Stable Version](https://travis-ci.org/nicoSWD/php-rule-parser.svg?branch=master)](https://travis-ci.org/nicoSWD/php-rule-parser) diff --git a/composer.json b/composer.json index d3bd301..317bb38 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "nicoswd/php-rule-parser", "type": "library", - "description": "PHP 7 Rule Engine - Rule Parser & Evaluator", + "description": "Rule Engine - Rule Parser & Evaluator", "keywords": [ "rule", "parser", diff --git a/phpunit.xml.dist b/phpunit.xml.dist index b2d0a0c..db6f484 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,16 +1,15 @@ - - - tests/ - - - - - - src - - + + + src + + + + + tests/ + + diff --git a/src/Compiler/StandardCompiler.php b/src/Compiler/StandardCompiler.php index 49fe657..5f5b80a 100644 --- a/src/Compiler/StandardCompiler.php +++ b/src/Compiler/StandardCompiler.php @@ -15,21 +15,18 @@ class StandardCompiler implements CompilerInterface { - const BOOL_TRUE = '1'; - const BOOL_FALSE = '0'; + private const BOOL_TRUE = '1'; + private const BOOL_FALSE = '0'; - const LOGICAL_AND = '&'; - const LOGICAL_OR = '|'; + private const LOGICAL_AND = '&'; + private const LOGICAL_OR = '|'; - const OPENING_PARENTHESIS = '('; - const CLOSING_PARENTHESIS = ')'; + private const OPENING_PARENTHESIS = '('; + private const CLOSING_PARENTHESIS = ')'; - /** @var string */ - private $output = ''; - /** @var int */ - private $openParenthesis = 0; - /** @var int */ - private $closedParenthesis = 0; + private string $output = ''; + private int $openParenthesis = 0; + private int $closedParenthesis = 0; public function getCompiledRule(): string { @@ -105,26 +102,24 @@ private function isIncompleteCondition(): bool { $lastChar = $this->getLastChar(); - return ( + return $lastChar === self::LOGICAL_AND || - $lastChar === self::LOGICAL_OR - ); + $lastChar === self::LOGICAL_OR; } private function expectOpeningParenthesis(): bool { $lastChar = $this->getLastChar(); - return ( + return $lastChar === '' || $lastChar === self::LOGICAL_AND || $lastChar === self::LOGICAL_OR || - $lastChar === self::OPENING_PARENTHESIS - ); + $lastChar === self::OPENING_PARENTHESIS; } private function getLastChar(): string { - return substr($this->output, -1); + return substr($this->output, offset: -1); } } diff --git a/src/Evaluator/Evaluator.php b/src/Evaluator/Evaluator.php index 69cfb7c..4d1797a 100644 --- a/src/Evaluator/Evaluator.php +++ b/src/Evaluator/Evaluator.php @@ -9,11 +9,11 @@ final class Evaluator implements EvaluatorInterface { - const LOGICAL_AND = '&'; - const LOGICAL_OR = '|'; + private const LOGICAL_AND = '&'; + private const LOGICAL_OR = '|'; - const BOOL_TRUE = '1'; - const BOOL_FALSE = '0'; + private const BOOL_TRUE = '1'; + private const BOOL_FALSE = '0'; public function evaluate(string $group): bool { @@ -25,8 +25,8 @@ public function evaluate(string $group): bool '~\(([^()]+)\)~', $evalGroup, $group, - -1, - $count + limit: -1, + count: $count ); } while ($count > 0); diff --git a/src/Expression/BaseExpression.php b/src/Expression/BaseExpression.php index 4f2e972..30561d9 100644 --- a/src/Expression/BaseExpression.php +++ b/src/Expression/BaseExpression.php @@ -9,10 +9,5 @@ abstract class BaseExpression { - /** - * @param mixed $leftValue - * @param mixed $rightValue - * @return bool - */ - abstract public function evaluate($leftValue, $rightValue): bool; + abstract public function evaluate(mixed $leftValue, mixed $rightValue): bool; } diff --git a/src/Expression/EqualExpression.php b/src/Expression/EqualExpression.php index 315c791..dbf7a84 100644 --- a/src/Expression/EqualExpression.php +++ b/src/Expression/EqualExpression.php @@ -9,7 +9,7 @@ final class EqualExpression extends BaseExpression { - public function evaluate($leftValue, $rightValue): bool + public function evaluate(mixed $leftValue, mixed $rightValue): bool { return $leftValue == $rightValue; } diff --git a/src/Expression/EqualStrictExpression.php b/src/Expression/EqualStrictExpression.php index e25deba..a8f39e6 100644 --- a/src/Expression/EqualStrictExpression.php +++ b/src/Expression/EqualStrictExpression.php @@ -11,7 +11,7 @@ final class EqualStrictExpression extends BaseExpression { - public function evaluate($leftValue, $rightValue): bool + public function evaluate(mixed $leftValue, mixed $rightValue): bool { if ($leftValue instanceof TokenCollection) { $leftValue = $leftValue->toArray(); diff --git a/src/Expression/ExpressionFactory.php b/src/Expression/ExpressionFactory.php index a6bfb6a..f999394 100644 --- a/src/Expression/ExpressionFactory.php +++ b/src/Expression/ExpressionFactory.php @@ -8,25 +8,23 @@ namespace nicoSWD\Rule\Expression; use nicoSWD\Rule\TokenStream\Token; +use nicoSWD\Rule\TokenStream\Token\BaseToken; class ExpressionFactory implements ExpressionFactoryInterface { - /** @var array */ - protected $classLookup = [ - Token\TokenEqual::class => EqualExpression::class, - Token\TokenEqualStrict::class => EqualStrictExpression::class, - Token\TokenNotEqual::class => NotEqualExpression::class, - Token\TokenNotEqualStrict::class => NotEqualStrictExpression::class, - Token\TokenGreater::class => GreaterThanExpression::class, - Token\TokenSmaller::class => LessThanExpression::class, - Token\TokenSmallerEqual::class => LessThanEqualExpression::class, - Token\TokenGreaterEqual::class => GreaterThanEqualExpression::class, - Token\TokenIn::class => InExpression::class, - Token\TokenNotIn::class => NotInExpression::class, - ]; - - public function createFromOperator(Token\BaseToken $operator): BaseExpression + public function createFromOperator(BaseToken $operator): BaseExpression { - return new $this->classLookup[get_class($operator)](); + return match (get_class($operator)) { + Token\TokenEqual::class => new EqualExpression(), + Token\TokenEqualStrict::class => new EqualStrictExpression(), + Token\TokenNotEqual::class => new NotEqualExpression(), + Token\TokenNotEqualStrict::class => new NotEqualStrictExpression(), + Token\TokenGreater::class => new GreaterThanExpression(), + Token\TokenSmaller::class => new LessThanExpression(), + Token\TokenSmallerEqual::class => new LessThanEqualExpression(), + Token\TokenGreaterEqual::class => new GreaterThanEqualExpression(), + Token\TokenIn::class => new InExpression(), + Token\TokenNotIn::class => new NotInExpression(), + }; } } diff --git a/src/Expression/GreaterThanEqualExpression.php b/src/Expression/GreaterThanEqualExpression.php index bb4c9a7..3bffd26 100644 --- a/src/Expression/GreaterThanEqualExpression.php +++ b/src/Expression/GreaterThanEqualExpression.php @@ -9,7 +9,7 @@ final class GreaterThanEqualExpression extends BaseExpression { - public function evaluate($leftValue, $rightValue): bool + public function evaluate(mixed $leftValue, mixed $rightValue): bool { return $leftValue >= $rightValue; } diff --git a/src/Expression/GreaterThanExpression.php b/src/Expression/GreaterThanExpression.php index e07b8be..4814872 100644 --- a/src/Expression/GreaterThanExpression.php +++ b/src/Expression/GreaterThanExpression.php @@ -9,7 +9,7 @@ final class GreaterThanExpression extends BaseExpression { - public function evaluate($leftValue, $rightValue): bool + public function evaluate(mixed $leftValue, mixed $rightValue): bool { return $leftValue > $rightValue; } diff --git a/src/Expression/InExpression.php b/src/Expression/InExpression.php index 928c896..61ab5e2 100644 --- a/src/Expression/InExpression.php +++ b/src/Expression/InExpression.php @@ -12,7 +12,7 @@ final class InExpression extends BaseExpression { - public function evaluate($leftValue, $rightValue): bool + public function evaluate(mixed $leftValue, mixed $rightValue): bool { if ($rightValue instanceof TokenCollection) { $rightValue = $rightValue->toArray(); @@ -25,6 +25,6 @@ public function evaluate($leftValue, $rightValue): bool )); } - return in_array($leftValue, $rightValue, true); + return in_array($leftValue, $rightValue, strict: true); } } diff --git a/src/Expression/LessThanEqualExpression.php b/src/Expression/LessThanEqualExpression.php index 153862c..f76bbc9 100644 --- a/src/Expression/LessThanEqualExpression.php +++ b/src/Expression/LessThanEqualExpression.php @@ -9,7 +9,7 @@ final class LessThanEqualExpression extends BaseExpression { - public function evaluate($leftValue, $rightValue): bool + public function evaluate(mixed $leftValue, mixed $rightValue): bool { return $leftValue <= $rightValue; } diff --git a/src/Expression/LessThanExpression.php b/src/Expression/LessThanExpression.php index 6fdce78..8618aa7 100644 --- a/src/Expression/LessThanExpression.php +++ b/src/Expression/LessThanExpression.php @@ -9,7 +9,7 @@ final class LessThanExpression extends BaseExpression { - public function evaluate($leftValue, $rightValue): bool + public function evaluate(mixed $leftValue, mixed $rightValue): bool { return $leftValue < $rightValue; } diff --git a/src/Expression/NotEqualExpression.php b/src/Expression/NotEqualExpression.php index 1f6de49..4c4b3c4 100644 --- a/src/Expression/NotEqualExpression.php +++ b/src/Expression/NotEqualExpression.php @@ -9,7 +9,7 @@ final class NotEqualExpression extends BaseExpression { - public function evaluate($leftValue, $rightValue): bool + public function evaluate(mixed $leftValue, mixed $rightValue): bool { return $leftValue != $rightValue; } diff --git a/src/Expression/NotEqualStrictExpression.php b/src/Expression/NotEqualStrictExpression.php index 0974eca..f839e7e 100644 --- a/src/Expression/NotEqualStrictExpression.php +++ b/src/Expression/NotEqualStrictExpression.php @@ -9,7 +9,7 @@ final class NotEqualStrictExpression extends BaseExpression { - public function evaluate($leftValue, $rightValue): bool + public function evaluate(mixed $leftValue, mixed $rightValue): bool { return $leftValue !== $rightValue; } diff --git a/src/Grammar/CallableFunction.php b/src/Grammar/CallableFunction.php index ddd9c52..5ac80da 100644 --- a/src/Grammar/CallableFunction.php +++ b/src/Grammar/CallableFunction.php @@ -11,12 +11,8 @@ abstract class CallableFunction implements CallableUserFunctionInterface { - /** @var ?BaseToken */ - protected $token; - - public function __construct(BaseToken $token = null) + public function __construct(protected ?BaseToken $token = null) { - $this->token = $token; } protected function parseParameter(array $parameters, int $numParam): ?BaseToken diff --git a/src/Grammar/Grammar.php b/src/Grammar/Grammar.php index 860886e..cf79bf4 100644 --- a/src/Grammar/Grammar.php +++ b/src/Grammar/Grammar.php @@ -9,7 +9,7 @@ abstract class Grammar { - /** @return array */ + /** @return array */ abstract public function getDefinition(): array; /** @return array */ diff --git a/src/Grammar/JavaScript/Functions/ParseFloat.php b/src/Grammar/JavaScript/Functions/ParseFloat.php index a479cfa..c9238bc 100644 --- a/src/Grammar/JavaScript/Functions/ParseFloat.php +++ b/src/Grammar/JavaScript/Functions/ParseFloat.php @@ -16,7 +16,7 @@ final class ParseFloat extends CallableFunction implements CallableUserFunctionI { public function call(?BaseToken ...$parameters): BaseToken { - $value = $this->parseParameter($parameters, 0); + $value = $this->parseParameter($parameters, numParam: 0); if (!isset($value)) { return new TokenFloat(NAN); diff --git a/src/Grammar/JavaScript/Functions/ParseInt.php b/src/Grammar/JavaScript/Functions/ParseInt.php index 62f67d3..08edf11 100644 --- a/src/Grammar/JavaScript/Functions/ParseInt.php +++ b/src/Grammar/JavaScript/Functions/ParseInt.php @@ -16,7 +16,7 @@ final class ParseInt extends CallableFunction implements CallableUserFunctionInt { public function call(?BaseToken ...$parameters): BaseToken { - $value = $this->parseParameter($parameters, 0); + $value = $this->parseParameter($parameters, numParam: 0); if (!isset($value)) { return new TokenInteger(NAN); diff --git a/src/Grammar/JavaScript/JavaScript.php b/src/Grammar/JavaScript/JavaScript.php index dda52df..874f14a 100644 --- a/src/Grammar/JavaScript/JavaScript.php +++ b/src/Grammar/JavaScript/JavaScript.php @@ -51,7 +51,7 @@ public function getDefinition(): array public function getInternalFunctions(): array { return [ - 'parseInt' => Functions\ParseInt::class, + 'parseInt' => Functions\ParseInt::class, 'parseFloat' => Functions\ParseFloat::class, ]; } @@ -59,18 +59,18 @@ public function getInternalFunctions(): array public function getInternalMethods(): array { return [ - 'charAt' => Methods\CharAt::class, - 'concat' => Methods\Concat::class, - 'indexOf' => Methods\IndexOf::class, - 'join' => Methods\Join::class, - 'replace' => Methods\Replace::class, - 'split' => Methods\Split::class, - 'substr' => Methods\Substr::class, - 'test' => Methods\Test::class, + 'charAt' => Methods\CharAt::class, + 'concat' => Methods\Concat::class, + 'indexOf' => Methods\IndexOf::class, + 'join' => Methods\Join::class, + 'replace' => Methods\Replace::class, + 'split' => Methods\Split::class, + 'substr' => Methods\Substr::class, + 'test' => Methods\Test::class, 'toLowerCase' => Methods\ToLowerCase::class, 'toUpperCase' => Methods\ToUpperCase::class, - 'startsWith' => Methods\StartsWith::class, - 'endsWith' => Methods\EndsWith::class, + 'startsWith' => Methods\StartsWith::class, + 'endsWith' => Methods\EndsWith::class, ]; } } diff --git a/src/Grammar/JavaScript/Methods/CharAt.php b/src/Grammar/JavaScript/Methods/CharAt.php index f8e71b9..2276caa 100644 --- a/src/Grammar/JavaScript/Methods/CharAt.php +++ b/src/Grammar/JavaScript/Methods/CharAt.php @@ -17,7 +17,7 @@ final class CharAt extends CallableFunction public function call(?BaseToken ...$parameters): BaseToken { $tokenValue = $this->token->getValue(); - $offset = $this->parseParameter($parameters, 0); + $offset = $this->parseParameter($parameters, numParam: 0); if (!$offset) { $offset = 0; diff --git a/src/Grammar/JavaScript/Methods/EndsWith.php b/src/Grammar/JavaScript/Methods/EndsWith.php index 15cb310..802265c 100644 --- a/src/Grammar/JavaScript/Methods/EndsWith.php +++ b/src/Grammar/JavaScript/Methods/EndsWith.php @@ -21,14 +21,14 @@ public function call(?BaseToken ...$parameters): BaseToken throw new ParserException('Call to undefined method "endsWith" on non-string'); } - $needle = $this->parseParameter($parameters, 0); + $needle = $this->parseParameter($parameters, numParam: 0); $haystack = $this->token->getValue(); if (!$needle) { $result = false; } else { $needle = $needle->getValue(); - $result = substr($haystack, -strlen($needle)) === $needle; + $result = str_ends_with($haystack, $needle); } return new TokenBool($result); diff --git a/src/Grammar/JavaScript/Methods/IndexOf.php b/src/Grammar/JavaScript/Methods/IndexOf.php index 9cd9a53..d70366b 100644 --- a/src/Grammar/JavaScript/Methods/IndexOf.php +++ b/src/Grammar/JavaScript/Methods/IndexOf.php @@ -15,7 +15,7 @@ final class IndexOf extends CallableFunction { public function call(?BaseToken ...$parameters): BaseToken { - $needle = $this->parseParameter($parameters, 0); + $needle = $this->parseParameter($parameters, numParam: 0); if (!$needle) { $value = -1; diff --git a/src/Grammar/JavaScript/Methods/Join.php b/src/Grammar/JavaScript/Methods/Join.php index e38ab3e..0ce5f33 100644 --- a/src/Grammar/JavaScript/Methods/Join.php +++ b/src/Grammar/JavaScript/Methods/Join.php @@ -22,7 +22,7 @@ public function call(?BaseToken ...$parameters): BaseToken throw new ParserException(sprintf('%s.join is not a function', $this->token->getValue())); } - $glue = $this->parseParameter($parameters, 0); + $glue = $this->parseParameter($parameters, numParam: 0); if ($glue) { $glue = $glue->getValue(); diff --git a/src/Grammar/JavaScript/Methods/Replace.php b/src/Grammar/JavaScript/Methods/Replace.php index b57b13f..38c4318 100644 --- a/src/Grammar/JavaScript/Methods/Replace.php +++ b/src/Grammar/JavaScript/Methods/Replace.php @@ -17,7 +17,7 @@ final class Replace extends CallableFunction public function call(?BaseToken ...$parameters): BaseToken { $isRegExpr = false; - $search = $this->parseParameter($parameters, 0); + $search = $this->parseParameter($parameters, numParam: 0); if (!$search) { $search = ''; @@ -26,7 +26,7 @@ public function call(?BaseToken ...$parameters): BaseToken $search = $search->getValue(); } - $replace = $this->parseParameter($parameters, 1); + $replace = $this->parseParameter($parameters, numParam: 1); if (!$replace) { $replace = 'undefined'; diff --git a/src/Grammar/JavaScript/Methods/Split.php b/src/Grammar/JavaScript/Methods/Split.php index d07dcba..279e70e 100644 --- a/src/Grammar/JavaScript/Methods/Split.php +++ b/src/Grammar/JavaScript/Methods/Split.php @@ -16,13 +16,13 @@ final class Split extends CallableFunction { public function call(?BaseToken ...$parameters): BaseToken { - $separator = $this->parseParameter($parameters, 0); + $separator = $this->parseParameter($parameters, numParam: 0); if (!$separator || !is_string($separator->getValue())) { $newValue = [$this->token->getValue()]; } else { $params = [$separator->getValue(), $this->token->getValue()]; - $limit = $this->parseParameter($parameters, 1); + $limit = $this->parseParameter($parameters, numParam: 1); if ($limit !== null) { $params[] = (int) $limit->getValue(); diff --git a/src/Grammar/JavaScript/Methods/StartsWith.php b/src/Grammar/JavaScript/Methods/StartsWith.php index a2494fc..aeb6f8a 100644 --- a/src/Grammar/JavaScript/Methods/StartsWith.php +++ b/src/Grammar/JavaScript/Methods/StartsWith.php @@ -22,8 +22,8 @@ public function call(?BaseToken ...$parameters): BaseToken throw new ParserException('Call to undefined method "startsWith" on non-string'); } - $needle = $this->parseParameter($parameters, 0); - $offset = $this->getOffset($this->parseParameter($parameters, 1)); + $needle = $this->parseParameter($parameters, numParam: 0); + $offset = $this->getOffset($this->parseParameter($parameters, numParam: 1)); $position = strpos($this->token->getValue(), $needle->getValue(), $offset); return new TokenBool($position === $offset); diff --git a/src/Grammar/JavaScript/Methods/Substr.php b/src/Grammar/JavaScript/Methods/Substr.php index bf20f8f..14b2722 100644 --- a/src/Grammar/JavaScript/Methods/Substr.php +++ b/src/Grammar/JavaScript/Methods/Substr.php @@ -16,7 +16,7 @@ final class Substr extends CallableFunction public function call(?BaseToken ...$parameters): BaseToken { $params = []; - $start = $this->parseParameter($parameters, 0); + $start = $this->parseParameter($parameters, numParam: 0); if (!$start) { $params[] = 0; @@ -24,7 +24,7 @@ public function call(?BaseToken ...$parameters): BaseToken $params[] = (int) $start->getValue(); } - $offset = $this->parseParameter($parameters, 1); + $offset = $this->parseParameter($parameters, numParam: 1); if ($offset) { $params[] = (int) $offset->getValue(); diff --git a/src/Grammar/JavaScript/Methods/Test.php b/src/Grammar/JavaScript/Methods/Test.php index c3a5ac0..59997a5 100644 --- a/src/Grammar/JavaScript/Methods/Test.php +++ b/src/Grammar/JavaScript/Methods/Test.php @@ -22,7 +22,7 @@ public function call(?BaseToken ...$parameters): BaseToken throw new ParserException('undefined is not a function'); } - $string = $this->parseParameter($parameters, 0); + $string = $this->parseParameter($parameters, numParam: 0); if (!$string) { $bool = false; @@ -31,9 +31,7 @@ public function call(?BaseToken ...$parameters): BaseToken // It's also irrelevant in .test() but allowed in JS here $pattern = preg_replace_callback( '~/[igm]{0,3}$~', - function (array $modifiers) { - return str_replace('g', '', $modifiers[0]); - }, + fn (array $modifiers) => str_replace('g', '', $modifiers[0]), $this->token->getValue() ); diff --git a/src/Highlighter/Highlighter.php b/src/Highlighter/Highlighter.php index e1b9c04..ba2b772 100644 --- a/src/Highlighter/Highlighter.php +++ b/src/Highlighter/Highlighter.php @@ -14,7 +14,7 @@ final class Highlighter { /** @var string[] */ - private $styles = [ + private array $styles = [ TokenType::COMMENT => 'color: #948a8a; font-style: italic;', TokenType::LOGICAL => 'color: #c72d2d;', TokenType::OPERATOR => 'color: #000;', @@ -30,12 +30,8 @@ final class Highlighter TokenType::INT_VALUE => '', ]; - /** @var TokenizerInterface */ - private $tokenizer; - - public function __construct(TokenizerInterface $tokenizer) + public function __construct(private TokenizerInterface $tokenizer) { - $this->tokenizer = $tokenizer; } public function setStyle(int $group, string $style): void diff --git a/src/Parser/Exception/ParserException.php b/src/Parser/Exception/ParserException.php index 3000971..9a0220e 100644 --- a/src/Parser/Exception/ParserException.php +++ b/src/Parser/Exception/ParserException.php @@ -55,4 +55,9 @@ public static function unexpectedEndOfString(): self { return new self('Unexpected end of string'); } + + public static function unsupportedType(string $type): self + { + return new self(sprintf('Unsupported PHP type: "%s"', $type)); + } } diff --git a/src/Parser/Parser.php b/src/Parser/Parser.php index 402059b..f572781 100644 --- a/src/Parser/Parser.php +++ b/src/Parser/Parser.php @@ -17,27 +17,14 @@ class Parser { - /** @var AST */ - private $ast; - /** @var ExpressionFactoryInterface */ - private $expressionFactory; - /** @var CompilerFactoryInterface */ - private $compilerFactory; - /** @var ?BaseToken */ - private $operator; - /** @var mixed[] */ - private $values = []; - /** @var Closure[] */ - private $handlers; + private ?BaseToken $operator; + private array $values = []; public function __construct( - AST $ast, - ExpressionFactoryInterface $expressionFactory, - CompilerFactoryInterface $compilerFactory + private AST $ast, + private ExpressionFactoryInterface $expressionFactory, + private CompilerFactoryInterface $compilerFactory ) { - $this->ast = $ast; - $this->expressionFactory = $expressionFactory; - $this->compilerFactory = $compilerFactory; } public function parse(string $rule): string @@ -59,20 +46,14 @@ public function parse(string $rule): string private function getHandlerForType(int $tokenType): Closure { - if (!isset($this->handlers)) { - $this->handlers = [ - TokenType::VALUE => $this->handleValueToken(), - TokenType::INT_VALUE => $this->handleValueToken(), - TokenType::OPERATOR => $this->handleOperatorToken(), - TokenType::LOGICAL => $this->handleLogicalToken(), - TokenType::PARENTHESIS => $this->handleParenthesisToken(), - TokenType::SPACE => $this->handleDummyToken(), - TokenType::COMMENT => $this->handleDummyToken(), - TokenType::UNKNOWN => $this->handleUnknownToken(), - ]; - } - - return $this->handlers[$tokenType] ?? $this->handlers[TokenType::UNKNOWN]; + return match ($tokenType) { + TokenType::VALUE, TokenType::INT_VALUE => $this->handleValueToken(), + TokenType::OPERATOR => $this->handleOperatorToken(), + TokenType::LOGICAL => $this->handleLogicalToken(), + TokenType::PARENTHESIS => $this->handleParenthesisToken(), + TokenType::COMMENT, TokenType::SPACE => $this->handleDummyToken(), + default => $this->handleUnknownToken(), + }; } private function evaluateExpression(CompilerInterface $compiler): void @@ -88,53 +69,45 @@ private function evaluateExpression(CompilerInterface $compiler): void private function expressionCanBeEvaluated(): bool { - return isset($this->operator) && count($this->values) === 2; + return count($this->values) === 2; } private function handleValueToken(): Closure { - return function (BaseToken $token) { - $this->values[] = $token->getValue(); - }; - } - - private function handleOperatorToken(): Closure - { - return function (BaseToken $token) { - if (isset($this->operator)) { - throw Exception\ParserException::unexpectedToken($token); - } elseif (empty($this->values)) { - throw Exception\ParserException::incompleteExpression($token); - } - - $this->operator = $token; - }; + return fn (BaseToken $token) => $this->values[] = $token->getValue(); } private function handleLogicalToken(): Closure { - return function (BaseToken $token, CompilerInterface $compiler) { - $compiler->addLogical($token); - }; + return fn (BaseToken $token, CompilerInterface $compiler) => $compiler->addLogical($token); } private function handleParenthesisToken(): Closure { - return function (BaseToken $token, CompilerInterface $compiler) { - $compiler->addParentheses($token); - }; + return fn (BaseToken $token, CompilerInterface $compiler) => $compiler->addParentheses($token); } private function handleUnknownToken(): Closure { - return function (BaseToken $token) { - throw Exception\ParserException::unknownToken($token); + return fn (BaseToken $token) => throw Exception\ParserException::unknownToken($token); + } + + private function handleOperatorToken(): Closure + { + return function (BaseToken $token): void { + if (isset($this->operator)) { + throw Exception\ParserException::unexpectedToken($token); + } elseif (empty($this->values)) { + throw Exception\ParserException::incompleteExpression($token); + } + + $this->operator = $token; }; } private function handleDummyToken(): Closure { - return function () { + return function (): void { // Do nothing }; } diff --git a/src/Rule.php b/src/Rule.php index bc55742..b860482 100644 --- a/src/Rule.php +++ b/src/Rule.php @@ -12,16 +12,11 @@ class Rule { - /** @var string */ - private $rule; - /** @var Parser\Parser */ - private $parser; - /** @var string */ - private $parsedRule = ''; - /** @var string */ - private $error = ''; - /** @var object */ - private static $container; + private string $rule; + private Parser\Parser $parser; + private string $parsedRule = ''; + private string $error = ''; + private static object $container; public function __construct(string $rule, array $variables = []) { diff --git a/src/TokenStream/AST.php b/src/TokenStream/AST.php index 7eec312..f9c39b6 100644 --- a/src/TokenStream/AST.php +++ b/src/TokenStream/AST.php @@ -19,20 +19,13 @@ class AST { - /** @var TokenizerInterface */ - private $tokenizer; - /** @var TokenFactory */ - private $tokenFactory; - /** @var TokenStreamFactory */ - private $tokenStreamFactory; - /** @var CallableUserMethodFactoryInterface */ - private $userMethodFactory; - /** @var Closure[] */ - private $functions = []; - /** @var string[] */ - private $methods = []; - /** @var mixed[] */ - private $variables = []; + private TokenizerInterface $tokenizer; + private TokenFactory $tokenFactory; + private TokenStreamFactory $tokenStreamFactory; + private CallableUserMethodFactoryInterface $userMethodFactory; + private array $functions = []; + private array $methods = []; + private array $variables = []; public function __construct( TokenizerInterface $tokenizer, diff --git a/src/TokenStream/CallableUserMethod.php b/src/TokenStream/CallableUserMethod.php index 1134bdf..bb3ecec 100644 --- a/src/TokenStream/CallableUserMethod.php +++ b/src/TokenStream/CallableUserMethod.php @@ -7,20 +7,18 @@ */ namespace nicoSWD\Rule\TokenStream; +use Closure; use nicoSWD\Rule\Grammar\CallableUserFunctionInterface; use nicoSWD\Rule\TokenStream\Token\BaseToken; use nicoSWD\Rule\TokenStream\Token\TokenFactory; final class CallableUserMethod implements CallableUserFunctionInterface { - const MAGIC_METHOD_PREFIX = '__'; + private const MAGIC_METHOD_PREFIX = '__'; - /** @var TokenFactory */ - private $tokenFactory; - /** @var callable */ - private $callable; - /** @var string[] */ - private $methodPrefixes = ['', 'get', 'is', 'get_', 'is_']; + private TokenFactory $tokenFactory; + private Closure $callable; + private array $methodPrefixes = ['', 'get', 'is', 'get_', 'is_']; /** * @throws Exception\UndefinedMethodException @@ -34,10 +32,10 @@ public function __construct(BaseToken $token, TokenFactory $tokenFactory, string public function call(?BaseToken ...$param): BaseToken { - $callable = $this->callable; + $callableCopy = $this->callable; return $this->tokenFactory->createFromPHPType( - $callable(...$param) + $callableCopy(...$param) ); } @@ -50,16 +48,14 @@ private function getCallable(BaseToken $token, string $methodName): callable $object = $token->getValue(); if (property_exists($object, $methodName)) { - return function () use ($object, $methodName) { - return $object->{$methodName}; - }; + return fn () => $object->{$methodName}; } $method = $this->findCallableMethod($object, $methodName); - return function (?BaseToken ...$params) use ($method) { - return $method(...$this->getTokenValues($params)); - }; + return fn (?BaseToken ...$params) => $method( + ...$this->getTokenValues($params) + ); } /** @@ -98,7 +94,7 @@ private function getTokenValues(array $params): array /** @throws Exception\ForbiddenMethodException */ private function assertNonMagicMethod(string $methodName): void { - if (substr($methodName, 0, 2) === self::MAGIC_METHOD_PREFIX) { + if (str_starts_with($methodName, self::MAGIC_METHOD_PREFIX)) { throw new Exception\ForbiddenMethodException(); } } diff --git a/src/TokenStream/CallableUserMethodFactory.php b/src/TokenStream/CallableUserMethodFactory.php index cae71bb..c168b56 100644 --- a/src/TokenStream/CallableUserMethodFactory.php +++ b/src/TokenStream/CallableUserMethodFactory.php @@ -10,7 +10,7 @@ use nicoSWD\Rule\TokenStream\Token\BaseToken; use nicoSWD\Rule\TokenStream\Token\TokenFactory; -final class CallableUserMethodFactory implements CallableUserMethodFactoryInterface +final class CallableUserMethodFactory implements CallableUserMethodFactoryInterface { public function create(BaseToken $token, TokenFactory $tokenFactory, string $methodName): CallableUserMethod { diff --git a/src/TokenStream/Node/BaseNode.php b/src/TokenStream/Node/BaseNode.php index 369eb8d..2ec7332 100644 --- a/src/TokenStream/Node/BaseNode.php +++ b/src/TokenStream/Node/BaseNode.php @@ -17,12 +17,9 @@ abstract class BaseNode { - /** @var TokenStream */ - protected $tokenStream; - /** @var string */ - private $methodName = ''; - /** @var int */ - private $methodOffset = 0; + protected TokenStream $tokenStream; + private string $methodName = ''; + private int $methodOffset = 0; public function __construct(TokenStream $tokenStream) { diff --git a/src/TokenStream/Token/BaseToken.php b/src/TokenStream/Token/BaseToken.php index 8a55b2c..4456fff 100644 --- a/src/TokenStream/Token/BaseToken.php +++ b/src/TokenStream/Token/BaseToken.php @@ -12,25 +12,20 @@ abstract class BaseToken { - /** @var mixed */ - private $value; - /** @var int */ - private $offset = 0; - abstract public function getType(): int; - public function __construct($value, int $offset = 0) - { - $this->value = $value; - $this->offset = $offset; + public function __construct( + private mixed $value, + private int $offset = 0 + ) { } - public function getValue() + public function getValue(): mixed { return $this->value; } - final public function getOriginalValue() + final public function getOriginalValue(): mixed { return $this->value; } diff --git a/src/TokenStream/Token/Token.php b/src/TokenStream/Token/Token.php index 559a57e..9320894 100644 --- a/src/TokenStream/Token/Token.php +++ b/src/TokenStream/Token/Token.php @@ -9,34 +9,33 @@ class Token { - const AND = 'And'; - const OR = 'Or'; - const NOT_EQUAL_STRICT = 'NotEqualStrict'; - const NOT_EQUAL = 'NotEqual'; - const EQUAL_STRICT = 'EqualStrict'; - const EQUAL = 'Equal'; - const NOT_IN = 'NotIn'; - const IN = 'In'; - const BOOL = 'Bool'; - const NULL = 'Null'; - const METHOD = 'Method'; - const FUNCTION = 'Function'; - const VARIABLE = 'Variable'; - const FLOAT = 'Float'; - const INTEGER = 'Integer'; - const ENCAPSED_STRING = 'EncapsedString'; - const SMALLER_EQUAL = 'SmallerEqual'; - const GREATER_EQUAL = 'GreaterEqual'; - const SMALLER = 'Smaller'; - const GREATER = 'Greater'; - const OPENING_PARENTHESIS = 'OpeningParentheses'; - const CLOSING_PARENTHESIS = 'ClosingParentheses'; - const OPENING_ARRAY = 'OpeningArray'; - const CLOSING_ARRAY = 'ClosingArray'; - const COMMA = 'Comma'; - const REGEX = 'Regex'; - const COMMENT = 'Comment'; - const NEWLINE = 'Newline'; - const SPACE = 'Space'; - const UNKNOWN = 'Unknown'; + public const AND = 'And'; + public const OR = 'Or'; + public const NOT_EQUAL_STRICT = 'NotEqualStrict'; + public const NOT_EQUAL = 'NotEqual'; + public const EQUAL_STRICT = 'EqualStrict'; + public const EQUAL = 'Equal'; + public const IN = 'In'; + public const BOOL = 'Bool'; + public const NULL = 'Null'; + public const METHOD = 'Method'; + public const FUNCTION = 'Function'; + public const VARIABLE = 'Variable'; + public const FLOAT = 'Float'; + public const INTEGER = 'Integer'; + public const ENCAPSED_STRING = 'EncapsedString'; + public const SMALLER_EQUAL = 'SmallerEqual'; + public const GREATER_EQUAL = 'GreaterEqual'; + public const SMALLER = 'Smaller'; + public const GREATER = 'Greater'; + public const OPENING_PARENTHESIS = 'OpeningParentheses'; + public const CLOSING_PARENTHESIS = 'ClosingParentheses'; + public const OPENING_ARRAY = 'OpeningArray'; + public const CLOSING_ARRAY = 'ClosingArray'; + public const COMMA = 'Comma'; + public const REGEX = 'Regex'; + public const COMMENT = 'Comment'; + public const NEWLINE = 'Newline'; + public const SPACE = 'Space'; + public const UNKNOWN = 'Unknown'; } diff --git a/src/TokenStream/Token/TokenBool.php b/src/TokenStream/Token/TokenBool.php index 7d27939..7150935 100644 --- a/src/TokenStream/Token/TokenBool.php +++ b/src/TokenStream/Token/TokenBool.php @@ -18,7 +18,7 @@ public function getType(): int * @return bool * @SuppressWarnings(PHPMD.BooleanGetMethodName) */ - public function getValue() + public function getValue(): bool { $value = parent::getValue(); diff --git a/src/TokenStream/Token/TokenEncapsedString.php b/src/TokenStream/Token/TokenEncapsedString.php index ada32d9..738858c 100644 --- a/src/TokenStream/Token/TokenEncapsedString.php +++ b/src/TokenStream/Token/TokenEncapsedString.php @@ -9,7 +9,7 @@ final class TokenEncapsedString extends TokenString { - public function getValue() + public function getValue(): string { return substr(parent::getValue(), 1, -1); } diff --git a/src/TokenStream/Token/TokenFactory.php b/src/TokenStream/Token/TokenFactory.php index eab65a3..211b812 100644 --- a/src/TokenStream/Token/TokenFactory.php +++ b/src/TokenStream/Token/TokenFactory.php @@ -7,87 +7,73 @@ */ namespace nicoSWD\Rule\TokenStream\Token; +use InvalidArgumentException; use nicoSWD\Rule\Parser\Exception\ParserException; use nicoSWD\Rule\TokenStream\TokenCollection; class TokenFactory { - /** @var array */ - private $tokenMap = [ - Token::AND => TokenAnd::class, - Token::OR => TokenOr::class, - Token::NOT_EQUAL_STRICT => TokenNotEqualStrict::class, - Token::NOT_EQUAL => TokenNotEqual::class, - Token::EQUAL_STRICT => TokenEqualStrict::class, - Token::EQUAL => TokenEqual::class, - Token::NOT_IN => TokenNotIn::class, - Token::IN => TokenIn::class, - Token::BOOL => TokenBool::class, - Token::NULL => TokenNull::class, - Token::METHOD => TokenMethod::class, - Token::FUNCTION => TokenFunction::class, - Token::VARIABLE => TokenVariable::class, - Token::FLOAT => TokenFloat::class, - Token::INTEGER => TokenInteger::class, - Token::ENCAPSED_STRING => TokenEncapsedString::class, - Token::SMALLER_EQUAL => TokenSmallerEqual::class, - Token::GREATER_EQUAL => TokenGreaterEqual::class, - Token::SMALLER => TokenSmaller::class, - Token::GREATER => TokenGreater::class, - Token::OPENING_PARENTHESIS => TokenOpeningParenthesis::class, - Token::CLOSING_PARENTHESIS => TokenClosingParenthesis::class, - Token::OPENING_ARRAY => TokenOpeningArray::class, - Token::CLOSING_ARRAY => TokenClosingArray::class, - Token::COMMA => TokenComma::class, - Token::REGEX => TokenRegex::class, - Token::COMMENT => TokenComment::class, - Token::NEWLINE => TokenNewline::class, - Token::SPACE => TokenSpace::class, - Token::UNKNOWN => TokenUnknown::class, - ]; - - public function createFromPHPType($value): BaseToken + /** @throws ParserException */ + public function createFromPHPType(mixed $value): BaseToken { - switch ($type = gettype($value)) { - case 'string': - return new TokenString($value); - case 'integer': - return new TokenInteger($value); - case 'boolean': - return new TokenBool($value); - case 'NULL': - return new TokenNull($value); - case 'double': - return new TokenFloat($value); - case 'array': - return $this->buildTokenCollection($value); - case 'object': - return new TokenObject($value); - default: - throw new ParserException(sprintf( - 'Unsupported PHP type: "%s"', - $type - )); - } + return match (gettype($value)) { + 'string' => new TokenString($value), + 'integer' => new TokenInteger($value), + 'boolean' => new TokenBool($value), + 'NULL' => new TokenNull($value), + 'double' => new TokenFloat($value), + 'object' => new TokenObject($value), + 'array' => $this->buildTokenCollection($value), + default => throw ParserException::unsupportedType(gettype($value)) + }; } public function createFromTokenName(string $tokenName): string { - if (!isset($this->tokenMap[$tokenName])) { - throw new \InvalidArgumentException("Unknown token $tokenName"); - } - - return $this->tokenMap[$tokenName]; + return match ($tokenName) { + Token::AND => TokenAnd::class, + Token::OR => TokenOr::class, + Token::NOT_EQUAL_STRICT => TokenNotEqualStrict::class, + Token::NOT_EQUAL => TokenNotEqual::class, + Token::EQUAL_STRICT => TokenEqualStrict::class, + Token::EQUAL => TokenEqual::class, + Token::IN => TokenIn::class, + Token::NOT_IN => TokenNotIn::class, + Token::BOOL => TokenBool::class, + Token::NULL => TokenNull::class, + Token::METHOD => TokenMethod::class, + Token::FUNCTION => TokenFunction::class, + Token::VARIABLE => TokenVariable::class, + Token::FLOAT => TokenFloat::class, + Token::INTEGER => TokenInteger::class, + Token::ENCAPSED_STRING => TokenEncapsedString::class, + Token::SMALLER_EQUAL => TokenSmallerEqual::class, + Token::GREATER_EQUAL => TokenGreaterEqual::class, + Token::SMALLER => TokenSmaller::class, + Token::GREATER => TokenGreater::class, + Token::OPENING_PARENTHESIS => TokenOpeningParenthesis::class, + Token::CLOSING_PARENTHESIS => TokenClosingParenthesis::class, + Token::OPENING_ARRAY => TokenOpeningArray::class, + Token::CLOSING_ARRAY => TokenClosingArray::class, + Token::COMMA => TokenComma::class, + Token::REGEX => TokenRegex::class, + Token::COMMENT => TokenComment::class, + Token::NEWLINE => TokenNewline::class, + Token::SPACE => TokenSpace::class, + Token::UNKNOWN => TokenUnknown::class, + default => throw new InvalidArgumentException("Unknown token $tokenName") + }; } - private function buildTokenCollection($value): TokenArray + /** @throws ParserException */ + private function buildTokenCollection(array $items): TokenArray { - $params = new TokenCollection(); + $tokenCollection = new TokenCollection(); - foreach ($value as $item) { - $params->attach($this->createFromPHPType($item)); + foreach ($items as $item) { + $tokenCollection->attach($this->createFromPHPType($item)); } - return new TokenArray($params); + return new TokenArray($tokenCollection); } } diff --git a/src/TokenStream/Token/TokenFloat.php b/src/TokenStream/Token/TokenFloat.php index 19993ae..018c92b 100644 --- a/src/TokenStream/Token/TokenFloat.php +++ b/src/TokenStream/Token/TokenFloat.php @@ -14,7 +14,7 @@ public function getType(): int return TokenType::VALUE; } - public function getValue() + public function getValue(): float { return (float) parent::getValue(); } diff --git a/src/TokenStream/Token/TokenNull.php b/src/TokenStream/Token/TokenNull.php index 1328726..452719c 100644 --- a/src/TokenStream/Token/TokenNull.php +++ b/src/TokenStream/Token/TokenNull.php @@ -14,7 +14,7 @@ public function getType(): int return TokenType::VALUE; } - public function getValue() + public function getValue(): mixed { return null; } diff --git a/src/TokenStream/Token/TokenType.php b/src/TokenStream/Token/TokenType.php index 6ff5e5e..76c79e0 100644 --- a/src/TokenStream/Token/TokenType.php +++ b/src/TokenStream/Token/TokenType.php @@ -9,17 +9,17 @@ class TokenType { - const OPERATOR = 1; - const INT_VALUE = 2; - const VALUE = 4; - const LOGICAL = 8; - const VARIABLE = 16; - const COMMENT = 32; - const SPACE = 64; - const UNKNOWN = 128; - const PARENTHESIS = 256; - const SQUARE_BRACKET = 512; - const COMMA = 1024; - const METHOD = 2048; - const FUNCTION = 4098; + public const OPERATOR = 1; + public const INT_VALUE = 2; + public const VALUE = 4; + public const LOGICAL = 8; + public const VARIABLE = 16; + public const COMMENT = 32; + public const SPACE = 64; + public const UNKNOWN = 128; + public const PARENTHESIS = 256; + public const SQUARE_BRACKET = 512; + public const COMMA = 1024; + public const METHOD = 2048; + public const FUNCTION = 4098; } diff --git a/src/TokenStream/TokenStream.php b/src/TokenStream/TokenStream.php index dccb824..14ba919 100644 --- a/src/TokenStream/TokenStream.php +++ b/src/TokenStream/TokenStream.php @@ -15,15 +15,10 @@ class TokenStream extends ArrayIterator { - /** @var ArrayIterator */ - private $stack; - /** @var AST */ - private $ast; - - public function __construct(ArrayIterator $stack, AST $ast) - { - $this->stack = $stack; - $this->ast = $ast; + public function __construct( + private ArrayIterator $stack, + private AST $ast + ) { } public function next(): void @@ -68,7 +63,7 @@ public function getVariable(string $variableName): BaseToken { try { return $this->ast->getVariable($variableName); - } catch (Exception\UndefinedVariableException $e) { + } catch (Exception\UndefinedVariableException) { throw ParserException::undefinedVariable($variableName, $this->getCurrentToken()); } } @@ -78,7 +73,7 @@ public function getFunction(string $functionName): Closure { try { return $this->ast->getFunction($functionName); - } catch (Exception\UndefinedFunctionException $e) { + } catch (Exception\UndefinedFunctionException) { throw ParserException::undefinedFunction($functionName, $this->getCurrentToken()); } } @@ -88,9 +83,9 @@ public function getMethod(string $methodName, BaseToken $token): CallableUserFun { try { return $this->ast->getMethod($methodName, $token); - } catch (Exception\UndefinedMethodException $e) { + } catch (Exception\UndefinedMethodException) { throw ParserException::undefinedMethod($methodName, $this->getCurrentToken()); - } catch (Exception\ForbiddenMethodException $e) { + } catch (Exception\ForbiddenMethodException) { throw ParserException::forbiddenMethod($methodName, $this->getCurrentToken()); } } diff --git a/src/Tokenizer/Tokenizer.php b/src/Tokenizer/Tokenizer.php index 48c3b1c..7bba82b 100644 --- a/src/Tokenizer/Tokenizer.php +++ b/src/Tokenizer/Tokenizer.php @@ -15,21 +15,14 @@ final class Tokenizer implements TokenizerInterface { - /** @var TokenFactory */ - private $tokenFactory; - /** @var Grammar */ - private $grammar; - /** @var stdClass[] */ - private $tokens = []; - /** @var string */ - private $regex = ''; - - public function __construct(Grammar $grammar, TokenFactory $tokenFactory) - { - $this->tokenFactory = $tokenFactory; - $this->grammar = $grammar; - - foreach ($grammar->getDefinition() as list($class, $regex, $priority)) { + private array $tokens = []; + private string $compiledRegex = ''; + + public function __construct( + private Grammar $grammar, + private TokenFactory $tokenFactory + ) { + foreach ($grammar->getDefinition() as [$class, $regex, $priority]) { $this->registerToken($class, $regex, $priority); } } @@ -79,17 +72,17 @@ private function getMatchedToken(array $matches): string private function getRegex(): string { - if (!$this->regex) { + if (!$this->compiledRegex) { $regex = []; foreach ($this->getQueue() as $token) { $regex[] = "(?<$token->class>$token->regex)"; } - $this->regex = '~(' . implode('|', $regex) . ')~As'; + $this->compiledRegex = '~(' . implode('|', $regex) . ')~As'; } - return $this->regex; + return $this->compiledRegex; } private function getQueue(): SplPriorityQueue diff --git a/src/autoload.php b/src/autoload.php index ef3f717..3accf9f 100644 --- a/src/autoload.php +++ b/src/autoload.php @@ -4,7 +4,7 @@ require_once $autoLoader; } else { spl_autoload_register(function (string $className): void { - if (strncmp('nicoSWD\\Rule\\', $className, 13) === 0) { + if (str_starts_with($className, 'nicoSWD\\Rule\\')) { if (is_file($file = __DIR__ . '/' . str_replace('\\', '/', substr($className, 13)) . '.php')) { require $file; } diff --git a/src/container.php b/src/container.php index f5de26a..c356af5 100644 --- a/src/container.php +++ b/src/container.php @@ -19,22 +19,14 @@ use nicoSWD\Rule\TokenStream\CallableUserMethodFactory; return new class { - /** @var TokenStreamFactory */ - private static $tokenStreamFactory; - /** @var TokenFactory */ - private static $tokenFactory; - /** @var CompilerFactory */ - private static $compiler; - /** @var JavaScript */ - private static $javaScript; - /** @var ExpressionFactory */ - private static $expressionFactory; - /** @var CallableUserMethodFactory */ - private static $userMethodFactory; - /** @var Tokenizer */ - private static $tokenizer; - /** @var Evaluator */ - private static $evaluator; + private static TokenStreamFactory $tokenStreamFactory; + private static TokenFactory $tokenFactory; + private static CompilerFactory $compiler; + private static JavaScript $javaScript; + private static ExpressionFactory $expressionFactory; + private static CallableUserMethodFactory $userMethodFactory; + private static Tokenizer $tokenizer; + private static Evaluator $evaluator; public function parser(array $variables): Parser\Parser { From 9d0aaafe3ef3c89a0f840802138ca69c139bfe23 Mon Sep 17 00:00:00 2001 From: Nico Date: Sun, 24 Jan 2021 00:28:51 +0100 Subject: [PATCH 02/10] Remove extra space --- src/TokenStream/CallableUserMethodFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TokenStream/CallableUserMethodFactory.php b/src/TokenStream/CallableUserMethodFactory.php index c168b56..cae71bb 100644 --- a/src/TokenStream/CallableUserMethodFactory.php +++ b/src/TokenStream/CallableUserMethodFactory.php @@ -10,7 +10,7 @@ use nicoSWD\Rule\TokenStream\Token\BaseToken; use nicoSWD\Rule\TokenStream\Token\TokenFactory; -final class CallableUserMethodFactory implements CallableUserMethodFactoryInterface +final class CallableUserMethodFactory implements CallableUserMethodFactoryInterface { public function create(BaseToken $token, TokenFactory $tokenFactory, string $methodName): CallableUserMethod { From 4da0ce6163ee5b7c173bbdb280bfcbf74dd033ee Mon Sep 17 00:00:00 2001 From: Nico Date: Sun, 24 Jan 2021 00:41:26 +0100 Subject: [PATCH 03/10] Update PHP version --- .styleci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.styleci.yml b/.styleci.yml index 953e379..03b0848 100644 --- a/.styleci.yml +++ b/.styleci.yml @@ -1,6 +1,7 @@ --- preset: psr2 risky: true +version: 8 finder: name: - "*.php" From 318b97be9f34df32fe4cd9dac286b13098c9ac33 Mon Sep 17 00:00:00 2001 From: Nico Date: Sun, 24 Jan 2021 00:44:51 +0100 Subject: [PATCH 04/10] Update PHP version --- .scrutinizer.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.scrutinizer.yml b/.scrutinizer.yml index cb990ef..37f56a0 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -35,6 +35,8 @@ tools: enabled: true excluded_dirs: [tests] build: + environment: + php: 8.0.1 nodes: analysis: tests: From 6cf1f17483ddb20ae49e0088903ea3a40d08ac73 Mon Sep 17 00:00:00 2001 From: Nico Date: Sun, 24 Jan 2021 22:14:43 +0100 Subject: [PATCH 05/10] Require PHP >= 8.0 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 317bb38..eabf79b 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ } }, "require": { - "php": ">=7.1" + "php": ">=8.0" }, "require-dev": { "phpunit/phpunit": "^7.0|^9.0", From f4949b66b4fcc7a87fe371157e8ea4d8542325b0 Mon Sep 17 00:00:00 2001 From: Nico Date: Sun, 24 Jan 2021 22:15:03 +0100 Subject: [PATCH 06/10] Add not in token type --- src/TokenStream/Token/Token.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/TokenStream/Token/Token.php b/src/TokenStream/Token/Token.php index 9320894..43b6fd9 100644 --- a/src/TokenStream/Token/Token.php +++ b/src/TokenStream/Token/Token.php @@ -16,6 +16,7 @@ class Token public const EQUAL_STRICT = 'EqualStrict'; public const EQUAL = 'Equal'; public const IN = 'In'; + public const NOT_IN = 'NotIn'; public const BOOL = 'Bool'; public const NULL = 'Null'; public const METHOD = 'Method'; From d7f269100eab28411a37a54a72b1c1fc27062e33 Mon Sep 17 00:00:00 2001 From: Nico Date: Mon, 25 Jan 2021 21:22:09 +0100 Subject: [PATCH 07/10] Separate booleans into two different tokens to fix token overriding bug --- src/Grammar/JavaScript/Methods/EndsWith.php | 2 +- src/Grammar/JavaScript/Methods/StartsWith.php | 2 +- src/Grammar/JavaScript/Methods/Test.php | 2 +- src/TokenStream/Token/Token.php | 3 ++- src/TokenStream/Token/TokenBool.php | 19 ++++++++----------- src/TokenStream/Token/TokenBoolFalse.php | 16 ++++++++++++++++ src/TokenStream/Token/TokenBoolTrue.php | 16 ++++++++++++++++ src/TokenStream/Token/TokenFactory.php | 5 +++-- tests/unit/Token/TokenFactoryTest.php | 5 ++--- tests/unit/Tokenizer/TokenizerTest.php | 13 ++++++------- 10 files changed, 56 insertions(+), 27 deletions(-) create mode 100644 src/TokenStream/Token/TokenBoolFalse.php create mode 100644 src/TokenStream/Token/TokenBoolTrue.php diff --git a/src/Grammar/JavaScript/Methods/EndsWith.php b/src/Grammar/JavaScript/Methods/EndsWith.php index 802265c..ed22fd1 100644 --- a/src/Grammar/JavaScript/Methods/EndsWith.php +++ b/src/Grammar/JavaScript/Methods/EndsWith.php @@ -31,6 +31,6 @@ public function call(?BaseToken ...$parameters): BaseToken $result = str_ends_with($haystack, $needle); } - return new TokenBool($result); + return TokenBool::fromBool($result); } } diff --git a/src/Grammar/JavaScript/Methods/StartsWith.php b/src/Grammar/JavaScript/Methods/StartsWith.php index aeb6f8a..980d7e2 100644 --- a/src/Grammar/JavaScript/Methods/StartsWith.php +++ b/src/Grammar/JavaScript/Methods/StartsWith.php @@ -26,7 +26,7 @@ public function call(?BaseToken ...$parameters): BaseToken $offset = $this->getOffset($this->parseParameter($parameters, numParam: 1)); $position = strpos($this->token->getValue(), $needle->getValue(), $offset); - return new TokenBool($position === $offset); + return TokenBool::fromBool($position === $offset); } private function getOffset(?BaseToken $offset): int diff --git a/src/Grammar/JavaScript/Methods/Test.php b/src/Grammar/JavaScript/Methods/Test.php index 59997a5..1701e77 100644 --- a/src/Grammar/JavaScript/Methods/Test.php +++ b/src/Grammar/JavaScript/Methods/Test.php @@ -44,6 +44,6 @@ public function call(?BaseToken ...$parameters): BaseToken $bool = (bool) preg_match($pattern, (string) $subject); } - return new TokenBool($bool); + return TokenBool::fromBool($bool); } } diff --git a/src/TokenStream/Token/Token.php b/src/TokenStream/Token/Token.php index 43b6fd9..8112716 100644 --- a/src/TokenStream/Token/Token.php +++ b/src/TokenStream/Token/Token.php @@ -17,7 +17,8 @@ class Token public const EQUAL = 'Equal'; public const IN = 'In'; public const NOT_IN = 'NotIn'; - public const BOOL = 'Bool'; + public const BOOL_TRUE = 'True'; + public const BOOL_FALSE = 'False'; public const NULL = 'Null'; public const METHOD = 'Method'; public const FUNCTION = 'Function'; diff --git a/src/TokenStream/Token/TokenBool.php b/src/TokenStream/Token/TokenBool.php index 7150935..4ce14dc 100644 --- a/src/TokenStream/Token/TokenBool.php +++ b/src/TokenStream/Token/TokenBool.php @@ -7,21 +7,18 @@ */ namespace nicoSWD\Rule\TokenStream\Token; -final class TokenBool extends BaseToken +abstract class TokenBool extends BaseToken { - public function getType(): int + public static function fromBool(bool $bool): static { - return TokenType::VALUE; + return match ($bool) { + true => new TokenBoolTrue(true), + false => new TokenBoolFalse(false), + }; } - /** - * @return bool - * @SuppressWarnings(PHPMD.BooleanGetMethodName) - */ - public function getValue(): bool + public function getType(): int { - $value = parent::getValue(); - - return $value === true || strtolower((string) $value) === 'true'; + return TokenType::VALUE; } } diff --git a/src/TokenStream/Token/TokenBoolFalse.php b/src/TokenStream/Token/TokenBoolFalse.php new file mode 100644 index 0000000..7451660 --- /dev/null +++ b/src/TokenStream/Token/TokenBoolFalse.php @@ -0,0 +1,16 @@ + + */ +namespace nicoSWD\Rule\TokenStream\Token; + +final class TokenBoolFalse extends TokenBool +{ + public function getValue(): bool + { + return false; + } +} diff --git a/src/TokenStream/Token/TokenBoolTrue.php b/src/TokenStream/Token/TokenBoolTrue.php new file mode 100644 index 0000000..d6f9965 --- /dev/null +++ b/src/TokenStream/Token/TokenBoolTrue.php @@ -0,0 +1,16 @@ + + */ +namespace nicoSWD\Rule\TokenStream\Token; + +final class TokenBoolTrue extends TokenBool +{ + public function getValue(): bool + { + return true; + } +} diff --git a/src/TokenStream/Token/TokenFactory.php b/src/TokenStream/Token/TokenFactory.php index 211b812..1195dc5 100644 --- a/src/TokenStream/Token/TokenFactory.php +++ b/src/TokenStream/Token/TokenFactory.php @@ -19,7 +19,7 @@ public function createFromPHPType(mixed $value): BaseToken return match (gettype($value)) { 'string' => new TokenString($value), 'integer' => new TokenInteger($value), - 'boolean' => new TokenBool($value), + 'boolean' => TokenBool::fromBool($value), 'NULL' => new TokenNull($value), 'double' => new TokenFloat($value), 'object' => new TokenObject($value), @@ -39,7 +39,8 @@ public function createFromTokenName(string $tokenName): string Token::EQUAL => TokenEqual::class, Token::IN => TokenIn::class, Token::NOT_IN => TokenNotIn::class, - Token::BOOL => TokenBool::class, + Token::BOOL_TRUE => TokenBoolTrue::class, + Token::BOOL_FALSE => TokenBoolFalse::class, Token::NULL => TokenNull::class, Token::METHOD => TokenMethod::class, Token::FUNCTION => TokenFunction::class, diff --git a/tests/unit/Token/TokenFactoryTest.php b/tests/unit/Token/TokenFactoryTest.php index 1bbb07e..ccf8ecc 100755 --- a/tests/unit/Token/TokenFactoryTest.php +++ b/tests/unit/Token/TokenFactoryTest.php @@ -15,8 +15,7 @@ final class TokenFactoryTest extends TestCase { - /** @var Token\TokenFactory */ - private $tokenFactory; + private Token\TokenFactory $tokenFactory; protected function setUp(): void { @@ -30,7 +29,7 @@ public function simpleTypeReturnsCorrectInstance(): void $this->assertInstanceOf(Token\TokenString::class, $this->tokenFactory->createFromPHPType('string sample')); $this->assertInstanceOf(Token\TokenFloat::class, $this->tokenFactory->createFromPHPType(0.3)); $this->assertInstanceOf(Token\TokenInteger::class, $this->tokenFactory->createFromPHPType(4)); - $this->assertInstanceOf(Token\TokenBool::class, $this->tokenFactory->createFromPHPType(true)); + $this->assertInstanceOf(Token\TokenBoolTrue::class, $this->tokenFactory->createFromPHPType(true)); $this->assertInstanceOf(Token\TokenArray::class, $this->tokenFactory->createFromPHPType([1, 2])); } diff --git a/tests/unit/Tokenizer/TokenizerTest.php b/tests/unit/Tokenizer/TokenizerTest.php index 703b7e1..6d93527 100755 --- a/tests/unit/Tokenizer/TokenizerTest.php +++ b/tests/unit/Tokenizer/TokenizerTest.php @@ -18,16 +18,16 @@ final class TokenizerTest extends TestCase public function givenAGrammarWithCollidingRegexItShouldTakeThePriorityIntoAccount(): void { $tokens = $this->tokenizeWithGrammar('yes somevar', [ - [Token\Token::BOOL, '\b(?:yes|no)\b', 20], + [Token\Token::BOOL_TRUE, '\byes\b', 20], [Token\Token::VARIABLE, '\b[a-z]+\b', 10], [Token\Token::SPACE, '\s+', 5], ]); $this->assertCount(3, $tokens); - $this->assertSame('yes', $tokens[0]->getOriginalValue()); + $this->assertTrue($tokens[0]->getValue()); $this->assertSame(0, $tokens[0]->getOffset()); - $this->assertInstanceOf(Token\TokenBool::class, $tokens[0]); + $this->assertInstanceOf(Token\TokenBoolTrue::class, $tokens[0]); $this->assertSame(' ', $tokens[1]->getValue()); $this->assertSame(3, $tokens[1]->getOffset()); @@ -43,7 +43,7 @@ public function givenAGrammarWithCollidingRegexWhenPriorityIsWrongItShouldNeverM { $tokens = $this->tokenizeWithGrammar('somevar yes', [ [Token\Token::VARIABLE, '\b[a-z]+\b', 20], - [Token\Token::BOOL, '\b(?:yes|no)\b', 10], + [Token\Token::BOOL_TRUE, '\byes\b', 10], [Token\Token::SPACE, '\s+', 5], ]); @@ -65,7 +65,7 @@ public function givenAGrammarWithCollidingRegexWhenPriorityIsWrongItShouldNeverM /** @test */ public function givenAGrammarItShouldBeAvailableThroughGetter(): void { - $grammar = $this->getTokenizer([[Token\Token::BOOL, '\b(?:yes|no)\b', 10]])->getGrammar(); + $grammar = $this->getTokenizer([[Token\Token::BOOL_TRUE, '\byes\b', 10]])->getGrammar(); $this->assertInstanceOf(Grammar::class, $grammar); $this->assertIsArray($grammar->getDefinition()); @@ -89,8 +89,7 @@ private function tokenizeWithGrammar(string $rule, array $definition): array private function getTokenizer(array $definition): Tokenizer { $grammar = new class($definition) extends Grammar { - /** @var array */ - private $definition = []; + private array $definition; public function __construct(array $definition) { From 3d9025088b7a2ec5ee9a523128d9dd4faeb17fe0 Mon Sep 17 00:00:00 2001 From: Nico Date: Mon, 25 Jan 2021 21:22:34 +0100 Subject: [PATCH 08/10] Cleanup --- src/Compiler/StandardCompiler.php | 4 ++ src/Expression/BaseExpression.php | 3 ++ src/Expression/NotInExpression.php | 4 +- src/Grammar/JavaScript/JavaScript.php | 3 +- src/Parser/Exception/ParserException.php | 40 +++++++++---------- tests/integration/HighlighterTest.php | 3 +- tests/integration/ObjectTest.php | 3 +- tests/integration/TokenizerTest.php | 6 +-- tests/integration/methods/JoinTest.php | 2 +- tests/integration/methods/SplitTest.php | 2 +- tests/unit/Evaluator/EvaluatorTest.php | 3 +- .../unit/Expression/ExpressionFactoryTest.php | 3 +- tests/unit/Parser/ParserTest.php | 12 ++---- tests/unit/TokenStream/ASTTest.php | 15 +++---- .../TokenStream/CallableUserMethodTest.php | 12 +++--- tests/unit/TokenStream/TokenStreamTest.php | 13 ++---- 16 files changed, 59 insertions(+), 69 deletions(-) diff --git a/src/Compiler/StandardCompiler.php b/src/Compiler/StandardCompiler.php index 5f5b80a..e566c92 100644 --- a/src/Compiler/StandardCompiler.php +++ b/src/Compiler/StandardCompiler.php @@ -45,6 +45,7 @@ private function openParenthesis(): void $this->output .= self::OPENING_PARENTHESIS; } + /** @throws ParserException */ private function closeParenthesis(): void { if ($this->openParenthesis < 1) { @@ -55,6 +56,7 @@ private function closeParenthesis(): void $this->output .= self::CLOSING_PARENTHESIS; } + /** @throws ParserException */ public function addParentheses(BaseToken $token): void { if ($token instanceof TokenOpeningParenthesis) { @@ -67,6 +69,7 @@ public function addParentheses(BaseToken $token): void } } + /** @throws ParserException */ public function addLogical(BaseToken $token): void { $lastChar = $this->getLastChar(); @@ -82,6 +85,7 @@ public function addLogical(BaseToken $token): void } } + /** @throws MissingOperatorException */ public function addBoolean(bool $bool): void { $lastChar = $this->getLastChar(); diff --git a/src/Expression/BaseExpression.php b/src/Expression/BaseExpression.php index 30561d9..74a269d 100644 --- a/src/Expression/BaseExpression.php +++ b/src/Expression/BaseExpression.php @@ -7,7 +7,10 @@ */ namespace nicoSWD\Rule\Expression; +use nicoSWD\Rule\Parser\Exception\ParserException; + abstract class BaseExpression { + /** @throws ParserException */ abstract public function evaluate(mixed $leftValue, mixed $rightValue): bool; } diff --git a/src/Expression/NotInExpression.php b/src/Expression/NotInExpression.php index 38a07df..58e8f5e 100644 --- a/src/Expression/NotInExpression.php +++ b/src/Expression/NotInExpression.php @@ -12,7 +12,7 @@ final class NotInExpression extends BaseExpression { - public function evaluate($leftValue, $rightValue): bool + public function evaluate(mixed $leftValue, mixed $rightValue): bool { if ($rightValue instanceof TokenCollection) { $rightValue = $rightValue->toArray(); @@ -25,6 +25,6 @@ public function evaluate($leftValue, $rightValue): bool )); } - return !in_array($leftValue, $rightValue, true); + return !in_array($leftValue, $rightValue, strict: true); } } diff --git a/src/Grammar/JavaScript/JavaScript.php b/src/Grammar/JavaScript/JavaScript.php index 874f14a..6314217 100644 --- a/src/Grammar/JavaScript/JavaScript.php +++ b/src/Grammar/JavaScript/JavaScript.php @@ -23,7 +23,8 @@ public function getDefinition(): array [Token::EQUAL, '==', 120], [Token::IN, '\bin\b', 115], [Token::NOT_IN, '\bnot\s+in\b', 116], - [Token::BOOL, '\b(?:true|false)\b', 110], + [Token::BOOL_TRUE, '\btrue\b', 110], + [Token::BOOL_FALSE, '\bfalse\b', 111], [Token::NULL, '\bnull\b', 105], [Token::METHOD, '\.\s*[a-zA-Z_]\w*\s*\(', 100], [Token::FUNCTION, '[a-zA-Z_]\w*\s*\(', 95], diff --git a/src/Parser/Exception/ParserException.php b/src/Parser/Exception/ParserException.php index 9a0220e..6fa05b4 100644 --- a/src/Parser/Exception/ParserException.php +++ b/src/Parser/Exception/ParserException.php @@ -11,53 +11,53 @@ class ParserException extends \Exception { - public static function unexpectedToken(BaseToken $token): self + public static function unexpectedToken(BaseToken $token): static { - return new self(sprintf('Unexpected "%s" at position %d', $token->getValue(), $token->getOffset())); + return new static(sprintf('Unexpected "%s" at position %d', $token->getValue(), $token->getOffset())); } - public static function unknownToken(BaseToken $token): self + public static function unknownToken(BaseToken $token): static { - return new self(sprintf('Unknown token "%s" at position %d', $token->getValue(), $token->getOffset())); + return new static(sprintf('Unknown token "%s" at position %d', $token->getValue(), $token->getOffset())); } - public static function incompleteExpression(BaseToken $token): self + public static function incompleteExpression(BaseToken $token): static { - return new self(sprintf('Incomplete expression for token "%s"', $token->getValue())); + return new static(sprintf('Incomplete expression for token "%s"', $token->getValue())); } - public static function undefinedVariable(string $name, BaseToken $token): self + public static function undefinedVariable(string $name, BaseToken $token): static { - return new self(sprintf('Undefined variable "%s" at position %d', $name, $token->getOffset())); + return new static(sprintf('Undefined variable "%s" at position %d', $name, $token->getOffset())); } - public static function undefinedMethod(string $name, BaseToken $token): self + public static function undefinedMethod(string $name, BaseToken $token): static { - return new self(sprintf('Undefined method "%s" at position %d', $name, $token->getOffset())); + return new static(sprintf('Undefined method "%s" at position %d', $name, $token->getOffset())); } - public static function forbiddenMethod(string $name, BaseToken $token): self + public static function forbiddenMethod(string $name, BaseToken $token): static { - return new self(sprintf('Forbidden method "%s" at position %d', $name, $token->getOffset())); + return new static(sprintf('Forbidden method "%s" at position %d', $name, $token->getOffset())); } - public static function undefinedFunction(string $name, BaseToken $token): self + public static function undefinedFunction(string $name, BaseToken $token): static { - return new self(sprintf('%s is not defined at position %d', $name, $token->getOffset())); + return new static(sprintf('%s is not defined at position %d', $name, $token->getOffset())); } - public static function unexpectedComma(BaseToken $token): self + public static function unexpectedComma(BaseToken $token): static { - return new self(sprintf('Unexpected "," at position %d', $token->getOffset())); + return new static(sprintf('Unexpected "," at position %d', $token->getOffset())); } - public static function unexpectedEndOfString(): self + public static function unexpectedEndOfString(): static { - return new self('Unexpected end of string'); + return new static('Unexpected end of string'); } - public static function unsupportedType(string $type): self + public static function unsupportedType(string $type): static { - return new self(sprintf('Unsupported PHP type: "%s"', $type)); + return new static(sprintf('Unsupported PHP type: "%s"', $type)); } } diff --git a/tests/integration/HighlighterTest.php b/tests/integration/HighlighterTest.php index 8af7a3b..3f44e1a 100755 --- a/tests/integration/HighlighterTest.php +++ b/tests/integration/HighlighterTest.php @@ -17,8 +17,7 @@ final class HighlighterTest extends TestCase { - /** @var Highlighter */ - private $highlighter; + private Highlighter $highlighter; protected function setUp(): void { diff --git a/tests/integration/ObjectTest.php b/tests/integration/ObjectTest.php index 23a66c8..e04a257 100755 --- a/tests/integration/ObjectTest.php +++ b/tests/integration/ObjectTest.php @@ -45,8 +45,7 @@ public function cat() public function givenAnObjectHasPropertiesWhenPublicTheyShouldBeAccessible(): void { $myObj = new class { - /** @var string */ - public $test = 'my string'; + public string $test = 'my string'; }; $variables = [ diff --git a/tests/integration/TokenizerTest.php b/tests/integration/TokenizerTest.php index 6326115..614e031 100755 --- a/tests/integration/TokenizerTest.php +++ b/tests/integration/TokenizerTest.php @@ -11,11 +11,11 @@ use nicoSWD\Rule\Tokenizer\Tokenizer; use nicoSWD\Rule\TokenStream\Token\TokenFactory; use PHPUnit\Framework\TestCase; +use ReflectionMethod; final class TokenizerTest extends TestCase { - /** @var Tokenizer */ - private $tokenizer; + private Tokenizer $tokenizer; protected function setUp(): void { @@ -25,7 +25,7 @@ protected function setUp(): void /** @test */ public function getMatchedTokenReturnsFalseOnFailure(): void { - $reflection = new \ReflectionMethod($this->tokenizer, 'getMatchedToken'); + $reflection = new ReflectionMethod($this->tokenizer, 'getMatchedToken'); $reflection->setAccessible(true); $result = $reflection->invoke($this->tokenizer, []); diff --git a/tests/integration/methods/JoinTest.php b/tests/integration/methods/JoinTest.php index 80039f2..8363ccb 100755 --- a/tests/integration/methods/JoinTest.php +++ b/tests/integration/methods/JoinTest.php @@ -11,7 +11,7 @@ final class JoinTest extends AbstractTestBase { - protected $array = ['foo' => ['foo', 'bar']]; + protected array $array = ['foo' => ['foo', 'bar']]; /** @test */ public function ifOmittedDelimiterFallsBackToDefault(): void diff --git a/tests/integration/methods/SplitTest.php b/tests/integration/methods/SplitTest.php index 4bf687e..c3bf4cd 100755 --- a/tests/integration/methods/SplitTest.php +++ b/tests/integration/methods/SplitTest.php @@ -11,7 +11,7 @@ final class SplitTest extends AbstractTestBase { - protected $var = ['foo' => 'bar,baz,foo']; + protected array $var = ['foo' => 'bar,baz,foo']; /** @test */ public function ifOmittedSeparatorFallsBackToDefault(): void diff --git a/tests/unit/Evaluator/EvaluatorTest.php b/tests/unit/Evaluator/EvaluatorTest.php index c0b59be..8ce9d36 100755 --- a/tests/unit/Evaluator/EvaluatorTest.php +++ b/tests/unit/Evaluator/EvaluatorTest.php @@ -13,8 +13,7 @@ final class EvaluatorTest extends TestCase { - /** @var Evaluator */ - private $evaluator; + private Evaluator $evaluator; protected function setUp(): void { diff --git a/tests/unit/Expression/ExpressionFactoryTest.php b/tests/unit/Expression/ExpressionFactoryTest.php index 371895a..98758c8 100755 --- a/tests/unit/Expression/ExpressionFactoryTest.php +++ b/tests/unit/Expression/ExpressionFactoryTest.php @@ -14,8 +14,7 @@ final class ExpressionFactoryTest extends TestCase { - /** @var ExpressionFactory */ - private $factory; + private ExpressionFactory $factory; protected function setUp(): void { diff --git a/tests/unit/Parser/ParserTest.php b/tests/unit/Parser/ParserTest.php index f237ea3..95546c2 100755 --- a/tests/unit/Parser/ParserTest.php +++ b/tests/unit/Parser/ParserTest.php @@ -23,14 +23,10 @@ final class ParserTest extends TestCase { use MockeryPHPUnitIntegration; - /** @var AST|m\Mock */ - private $ast; - /** @var ExpressionFactoryInterface|m\Mock */ - private $expressionFactory; - /** @var CompilerFactoryInterface|m\Mock */ - private $compilerFactory; - /** @var Parser */ - private $parser; + private AST|m\Mock $ast; + private ExpressionFactoryInterface|m\Mock $expressionFactory; + private CompilerFactoryInterface|m\Mock $compilerFactory; + private Parser $parser; protected function setUp(): void { diff --git a/tests/unit/TokenStream/ASTTest.php b/tests/unit/TokenStream/ASTTest.php index 248f4e7..04f2281 100755 --- a/tests/unit/TokenStream/ASTTest.php +++ b/tests/unit/TokenStream/ASTTest.php @@ -25,16 +25,11 @@ final class ASTTest extends TestCase { use MockeryPHPUnitIntegration; - /** @var TokenizerInterface|MockInterface */ - private $tokenizer; - /** @var TokenFactory|MockInterface */ - private $tokenFactory; - /** @var TokenStreamFactory|MockInterface */ - private $tokenStreamFactory; - /** @var AST */ - private $ast; - /** @var CallableUserMethodFactory */ - private $userMethodFactory; + private TokenizerInterface|MockInterface $tokenizer; + private TokenFactory|MockInterface $tokenFactory; + private TokenStreamFactory|MockInterface $tokenStreamFactory; + private AST $ast; + private CallableUserMethodFactory $userMethodFactory; protected function setUp(): void { diff --git a/tests/unit/TokenStream/CallableUserMethodTest.php b/tests/unit/TokenStream/CallableUserMethodTest.php index 0e377fa..fe18bdd 100755 --- a/tests/unit/TokenStream/CallableUserMethodTest.php +++ b/tests/unit/TokenStream/CallableUserMethodTest.php @@ -22,7 +22,7 @@ public function givenAnObjectWithAPublicPropertyItShouldBeAccessible(): void $object = new stdClass(); $object->my_test = 123; - $this->assertSame(123, $this->callMethod($object, 'my_test')->getOriginalValue()); + $this->assertSame(123, $this->callMethod($object, 'my_test')->getValue()); } /** @test */ @@ -35,7 +35,7 @@ public function my_test() } }; - $this->assertSame(123, $this->callMethod($object, 'my_test')->getOriginalValue()); + $this->assertSame(123, $this->callMethod($object, 'my_test')->getValue()); } /** @test */ @@ -53,8 +53,8 @@ public function isMyTest() } }; - $this->assertSame(123, $this->callMethod($object, 'my_test')->getOriginalValue()); - $this->assertSame(456, $this->callMethod($object, 'myTest')->getOriginalValue()); + $this->assertSame(123, $this->callMethod($object, 'my_test')->getValue()); + $this->assertSame(456, $this->callMethod($object, 'myTest')->getValue()); } /** @test */ @@ -72,8 +72,8 @@ public function getMyTest() } }; - $this->assertSame(123, $this->callMethod($object, 'my_test')->getOriginalValue()); - $this->assertSame(456, $this->callMethod($object, 'myTest')->getOriginalValue()); + $this->assertSame(123, $this->callMethod($object, 'my_test')->getValue()); + $this->assertSame(456, $this->callMethod($object, 'myTest')->getValue()); } private function callMethod($object, string $methodName): BaseToken diff --git a/tests/unit/TokenStream/TokenStreamTest.php b/tests/unit/TokenStream/TokenStreamTest.php index d76601c..757a634 100755 --- a/tests/unit/TokenStream/TokenStreamTest.php +++ b/tests/unit/TokenStream/TokenStreamTest.php @@ -28,12 +28,9 @@ final class TokenStreamTest extends TestCase { use MockeryPHPUnitIntegration; - /** @var ArrayIterator|MockInterface */ - private $stack; - /** @var AST|MockInterface */ - private $ast; - /** @var TokenStream */ - private $tokenStream; + private ArrayIterator|MockInterface $stack; + private AST|MockInterface $ast; + private TokenStream $tokenStream; protected function setUp(): void { @@ -91,9 +88,7 @@ public function givenAVariableNameWhenNotFoundItShouldThrowAnException() /** @test */ public function givenAFunctionNameWhenFoundItShouldACallableClosure() { - $this->ast->shouldReceive('getFunction')->once()->with('foo')->andReturn(function (): int { - return 42; - }); + $this->ast->shouldReceive('getFunction')->once()->with('foo')->andReturn(fn () => 42); $function = $this->tokenStream->getFunction('foo'); $this->assertSame(42, $function()); From c3affc4d3347853ebea80254f87f38ce2916cbc7 Mon Sep 17 00:00:00 2001 From: Nico Date: Mon, 25 Jan 2021 23:02:54 +0100 Subject: [PATCH 09/10] Fix phpstan issues --- src/Expression/ExpressionFactory.php | 3 ++ src/Grammar/Grammar.php | 2 +- src/Parser/Exception/ParserException.php | 47 ++++++++++++++---------- src/TokenStream/CallableUserMethod.php | 12 +++--- src/TokenStream/Node/BaseNode.php | 5 +-- src/TokenStream/Token/TokenBool.php | 2 +- src/Tokenizer/Tokenizer.php | 15 ++++---- tests/integration/ObjectTest.php | 5 +-- 8 files changed, 50 insertions(+), 41 deletions(-) diff --git a/src/Expression/ExpressionFactory.php b/src/Expression/ExpressionFactory.php index f999394..9a3ba34 100644 --- a/src/Expression/ExpressionFactory.php +++ b/src/Expression/ExpressionFactory.php @@ -7,11 +7,13 @@ */ namespace nicoSWD\Rule\Expression; +use nicoSWD\Rule\Parser\Exception\ParserException; use nicoSWD\Rule\TokenStream\Token; use nicoSWD\Rule\TokenStream\Token\BaseToken; class ExpressionFactory implements ExpressionFactoryInterface { + /** @throws ParserException */ public function createFromOperator(BaseToken $operator): BaseExpression { return match (get_class($operator)) { @@ -25,6 +27,7 @@ public function createFromOperator(BaseToken $operator): BaseExpression Token\TokenGreaterEqual::class => new GreaterThanEqualExpression(), Token\TokenIn::class => new InExpression(), Token\TokenNotIn::class => new NotInExpression(), + default => throw ParserException::unknownOperator($operator), }; } } diff --git a/src/Grammar/Grammar.php b/src/Grammar/Grammar.php index cf79bf4..536278d 100644 --- a/src/Grammar/Grammar.php +++ b/src/Grammar/Grammar.php @@ -9,7 +9,7 @@ abstract class Grammar { - /** @return array */ + /** @return array> */ abstract public function getDefinition(): array; /** @return array */ diff --git a/src/Parser/Exception/ParserException.php b/src/Parser/Exception/ParserException.php index 6fa05b4..a9dfa00 100644 --- a/src/Parser/Exception/ParserException.php +++ b/src/Parser/Exception/ParserException.php @@ -11,53 +11,60 @@ class ParserException extends \Exception { - public static function unexpectedToken(BaseToken $token): static + public static function unexpectedToken(BaseToken $token): self { - return new static(sprintf('Unexpected "%s" at position %d', $token->getValue(), $token->getOffset())); + return new self(sprintf('Unexpected "%s" at position %d', $token->getValue(), $token->getOffset())); } - public static function unknownToken(BaseToken $token): static + public static function unknownToken(BaseToken $token): self { - return new static(sprintf('Unknown token "%s" at position %d', $token->getValue(), $token->getOffset())); + return new self(sprintf('Unknown token "%s" at position %d', $token->getValue(), $token->getOffset())); } - public static function incompleteExpression(BaseToken $token): static + public static function incompleteExpression(BaseToken $token): self { - return new static(sprintf('Incomplete expression for token "%s"', $token->getValue())); + return new self(sprintf('Incomplete expression for token "%s"', $token->getValue())); } - public static function undefinedVariable(string $name, BaseToken $token): static + public static function undefinedVariable(string $name, BaseToken $token): self { - return new static(sprintf('Undefined variable "%s" at position %d', $name, $token->getOffset())); + return new self(sprintf('Undefined variable "%s" at position %d', $name, $token->getOffset())); } - public static function undefinedMethod(string $name, BaseToken $token): static + public static function undefinedMethod(string $name, BaseToken $token): self { - return new static(sprintf('Undefined method "%s" at position %d', $name, $token->getOffset())); + return new self(sprintf('Undefined method "%s" at position %d', $name, $token->getOffset())); } - public static function forbiddenMethod(string $name, BaseToken $token): static + public static function forbiddenMethod(string $name, BaseToken $token): self { - return new static(sprintf('Forbidden method "%s" at position %d', $name, $token->getOffset())); + return new self(sprintf('Forbidden method "%s" at position %d', $name, $token->getOffset())); } - public static function undefinedFunction(string $name, BaseToken $token): static + public static function undefinedFunction(string $name, BaseToken $token): self { - return new static(sprintf('%s is not defined at position %d', $name, $token->getOffset())); + return new self(sprintf('%s is not defined at position %d', $name, $token->getOffset())); } - public static function unexpectedComma(BaseToken $token): static + public static function unexpectedComma(BaseToken $token): self { - return new static(sprintf('Unexpected "," at position %d', $token->getOffset())); + return new self(sprintf('Unexpected "," at position %d', $token->getOffset())); } - public static function unexpectedEndOfString(): static + public static function unexpectedEndOfString(): self { - return new static('Unexpected end of string'); + return new self('Unexpected end of string'); } - public static function unsupportedType(string $type): static + public static function unsupportedType(string $type): self { - return new static(sprintf('Unsupported PHP type: "%s"', $type)); + return new self(sprintf('Unsupported PHP type: "%s"', $type)); + } + + public static function unknownOperator(BaseToken $token): self + { + return new self( + sprintf('Unexpected operator %s at position %d', $token->getOriginalValue(), $token->getOffset()) + ); } } diff --git a/src/TokenStream/CallableUserMethod.php b/src/TokenStream/CallableUserMethod.php index bb3ecec..0b6ff75 100644 --- a/src/TokenStream/CallableUserMethod.php +++ b/src/TokenStream/CallableUserMethod.php @@ -43,7 +43,7 @@ public function call(?BaseToken ...$param): BaseToken * @throws Exception\UndefinedMethodException * @throws Exception\ForbiddenMethodException */ - private function getCallable(BaseToken $token, string $methodName): callable + private function getCallable(BaseToken $token, string $methodName): Closure { $object = $token->getValue(); @@ -62,11 +62,11 @@ private function getCallable(BaseToken $token, string $methodName): callable * @throws Exception\UndefinedMethodException * @throws Exception\ForbiddenMethodException */ - private function findCallableMethod($object, string $methodName): callable + private function findCallableMethod(object $object, string $methodName): callable { $this->assertNonMagicMethod($methodName); - $callable = [$object, $methodName]; + $callableMethod = [$object, $methodName]; $index = 0; do { @@ -74,10 +74,10 @@ private function findCallableMethod($object, string $methodName): callable throw new Exception\UndefinedMethodException(); } - $callable[1] = $this->methodPrefixes[$index++] . $methodName; - } while (!is_callable($callable)); + $callableMethod[1] = $this->methodPrefixes[$index++] . $methodName; + } while (!is_callable($callableMethod)); - return $callable; + return $callableMethod; } private function getTokenValues(array $params): array diff --git a/src/TokenStream/Node/BaseNode.php b/src/TokenStream/Node/BaseNode.php index 2ec7332..c9748e8 100644 --- a/src/TokenStream/Node/BaseNode.php +++ b/src/TokenStream/Node/BaseNode.php @@ -47,9 +47,8 @@ protected function hasMethodCall(): bool $this->methodName = $token->getValue(); $this->methodOffset = $stack->key(); $hasMethod = true; - } elseif ($token->isWhitespace()) { - continue; - } else { + break; + } elseif (!$token->isWhitespace()) { break; } } diff --git a/src/TokenStream/Token/TokenBool.php b/src/TokenStream/Token/TokenBool.php index 4ce14dc..1e35584 100644 --- a/src/TokenStream/Token/TokenBool.php +++ b/src/TokenStream/Token/TokenBool.php @@ -9,7 +9,7 @@ abstract class TokenBool extends BaseToken { - public static function fromBool(bool $bool): static + public static function fromBool(bool $bool): TokenBool { return match ($bool) { true => new TokenBoolTrue(true), diff --git a/src/Tokenizer/Tokenizer.php b/src/Tokenizer/Tokenizer.php index 7bba82b..b9bcb91 100644 --- a/src/Tokenizer/Tokenizer.php +++ b/src/Tokenizer/Tokenizer.php @@ -11,7 +11,6 @@ use nicoSWD\Rule\Grammar\Grammar; use nicoSWD\Rule\TokenStream\Token\TokenFactory; use SplPriorityQueue; -use stdClass; final class Tokenizer implements TokenizerInterface { @@ -51,12 +50,14 @@ public function getGrammar(): Grammar private function registerToken(string $class, string $regex, int $priority): void { - $token = new stdClass(); - $token->class = $class; - $token->regex = $regex; - $token->priority = $priority; - - $this->tokens[$class] = $token; + $this->tokens[$class] = new class ($class, $regex, $priority) { + public function __construct( + public string $class, + public string $regex, + public int $priority + ) { + } + }; } private function getMatchedToken(array $matches): string diff --git a/tests/integration/ObjectTest.php b/tests/integration/ObjectTest.php index e04a257..3d89b76 100755 --- a/tests/integration/ObjectTest.php +++ b/tests/integration/ObjectTest.php @@ -9,7 +9,6 @@ use nicoSWD\Rule\Parser\Exception\ParserException; use nicoSWD\Rule\Rule; -use stdClass; final class ObjectTest extends AbstractTestBase { @@ -120,7 +119,7 @@ public function givenAnObjectWhenMagicMethodsAreCalledDirectlyItShouldThrowAnExc $this->expectException(ParserException::class); $this->expectExceptionMessage("Forbidden method \"{$magicMethod}\" at position 6"); - $myObj = new stdClass(); + $myObj = new class() {}; $variables = [ 'my_obj' => $myObj, @@ -132,7 +131,7 @@ public function givenAnObjectWhenMagicMethodsAreCalledDirectlyItShouldThrowAnExc /** @test */ public function undefinedMethodsShouldThrowAnError(): void { - $myObj = new stdClass(); + $myObj = new class() {}; $variables = [ 'my_obj' => $myObj, From 0f294e3af5bf454d3caf6651458207b9160bd752 Mon Sep 17 00:00:00 2001 From: Nico Date: Mon, 25 Jan 2021 23:07:53 +0100 Subject: [PATCH 10/10] Throw ParserException --- src/Parser/Exception/ParserException.php | 5 +++++ src/TokenStream/Token/TokenFactory.php | 2 +- tests/unit/Token/TokenFactoryTest.php | 3 +-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Parser/Exception/ParserException.php b/src/Parser/Exception/ParserException.php index a9dfa00..84a909a 100644 --- a/src/Parser/Exception/ParserException.php +++ b/src/Parser/Exception/ParserException.php @@ -67,4 +67,9 @@ public static function unknownOperator(BaseToken $token): self sprintf('Unexpected operator %s at position %d', $token->getOriginalValue(), $token->getOffset()) ); } + + public static function unknownTokenName(string $tokenName): self + { + return new self("Unknown token $tokenName"); + } } diff --git a/src/TokenStream/Token/TokenFactory.php b/src/TokenStream/Token/TokenFactory.php index 1195dc5..0a27085 100644 --- a/src/TokenStream/Token/TokenFactory.php +++ b/src/TokenStream/Token/TokenFactory.php @@ -62,7 +62,7 @@ public function createFromTokenName(string $tokenName): string Token::NEWLINE => TokenNewline::class, Token::SPACE => TokenSpace::class, Token::UNKNOWN => TokenUnknown::class, - default => throw new InvalidArgumentException("Unknown token $tokenName") + default => throw ParserException::unknownTokenName($tokenName) }; } diff --git a/tests/unit/Token/TokenFactoryTest.php b/tests/unit/Token/TokenFactoryTest.php index ccf8ecc..fcf59f3 100755 --- a/tests/unit/Token/TokenFactoryTest.php +++ b/tests/unit/Token/TokenFactoryTest.php @@ -7,7 +7,6 @@ */ namespace nicoSWD\Rule\tests\unit\Token; -use InvalidArgumentException; use nicoSWD\Rule\Parser\Exception\ParserException; use nicoSWD\Rule\TokenStream\Token; use nicoSWD\Rule\TokenStream\Token\TokenEqualStrict; @@ -45,7 +44,7 @@ public function unsupportedTypeThrowsException(): void /** @test */ public function givenAnInvalidTokenNameItShouldThrowAnException(): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException(ParserException::class); $this->tokenFactory->createFromTokenName('betrunken'); }