diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 21b809b..8c11f2c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,3 +6,7 @@ updates: interval: daily open-pull-requests-limit: 10 versioning-strategy: widen +- package-ecosystem: github-actions + directory: "/" + schedule: + interval: daily diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 5e4cc6a..7f62f1f 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -18,22 +18,22 @@ jobs: - 8.5 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v7 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - - name: Validate composer.json and composer.lock - run: composer validate + - name: Validate composer.json + run: composer validate --strict - name: Get composer cache directory id: composer-cache run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v3 + uses: actions/cache@v6 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} diff --git a/.gitignore b/.gitignore index 2b305b9..6cb3299 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ composer.lock /coverage.xml /bin /coverage/ +/.php-cs-fixer.cache diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..deeb668 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,29 @@ +in(__DIR__ . '/src') + ->in(__DIR__ . '/spec') + ->in(__DIR__ . '/tests') + ->append([__FILE__]); + +return (new PhpCsFixer\Config()) + ->setRiskyAllowed(true) + ->setRules([ + '@PER-CS' => true, + '@Symfony' => true, + '@Symfony:risky' => true, + '@PHP82Migration' => true, + '@PHP82Migration:risky' => true, + 'binary_operator_spaces' => ['operators' => ['=>' => 'align_single_space_minimal', '=' => 'align_single_space_minimal']], + 'concat_space' => ['spacing' => 'one'], + 'global_namespace_import' => ['import_classes' => true, 'import_constants' => false, 'import_functions' => false], + 'native_constant_invocation' => false, + 'native_function_invocation' => false, + 'ordered_class_elements' => true, + 'phpdoc_var_without_name' => false, + 'self_accessor' => false, + 'single_line_throw' => false, + ]) + ->setFinder($finder); diff --git a/.php_cs.dist b/.php_cs.dist deleted file mode 100644 index 587b5c6..0000000 --- a/.php_cs.dist +++ /dev/null @@ -1,29 +0,0 @@ -in(__DIR__ . '/src') - ->in(__DIR__ . '/spec') - ->in(__DIR__ . '/tests') - ->append([__FILE__]); - -return PhpCsFixer\Config::create() - ->setRiskyAllowed(true) - ->setRules( - [ - '@PSR2' => true, - '@Symfony' => true, - '@Symfony:risky' => true, - '@PHP71Migration' => true, - '@PHP71Migration:risky' => true, - 'array_syntax' => ['syntax' => 'short'], - 'binary_operator_spaces' => ['align_double_arrow' => true, 'align_equals' => true], - 'concat_space' => ['spacing' => 'one'], - 'mb_str_functions' => true, - 'ordered_class_elements' => true, - 'phpdoc_var_without_name' => false, - 'self_accessor' => false, - ] - ) - ->setFinder($finder); diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 5b37782..0000000 --- a/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -FROM ubuntu:16.04 - -MAINTAINER Chris Jenkinson - -RUN apt-get update \ - && apt-get install -y locales \ - && locale-gen en_NZ.UTF-8 - -ENV LANG en_NZ.UTF-8 -ENV LANGUAGE en_NZ:en -ENV LC_ALL en_NZ.UTF-8 - -RUN apt-get update \ - && apt-get install -y zip unzip git software-properties-common - -RUN add-apt-repository -y ppa:ondrej/php \ - && apt-get update \ - && apt-get install -y php7.1-cli php7.1-mbstring php7.1-xml php7.1-xdebug - -RUN php -r "readfile('http://getcomposer.org/installer');" | php -- --install-dir=/usr/bin/ --filename=composer - -RUN apt-get remove -y --purge software-properties-common \ - && apt-get -y autoremove \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* - -COPY docker/xdebug.ini /etc/php/7.1/mods-available/xdebug.ini diff --git a/composer.json b/composer.json index 2c92a64..05a8d76 100644 --- a/composer.json +++ b/composer.json @@ -4,16 +4,16 @@ "type": "library", "require": { "php": "8.2.* || 8.3.* || 8.4.* || 8.5.*", - "ext-libxml": "*" + "ext-mbstring": "*" }, "require-dev": { - "ext-mbstring": "*", "phpspec/phpspec": "8.3.1", "phpunit/phpunit": "9.6.* || 10.5.*", "friends-of-phpspec/phpspec-code-coverage": "7.0.0", "phpstan/phpstan": "1.10.* || 1.11.* || 1.12.*", "phpstan/extension-installer": "1.3.* || 1.4.*", - "jangregor/phpstan-prophecy": "1.0.*" + "jangregor/phpstan-prophecy": "1.0.*", + "friendsofphp/php-cs-fixer": "3.95.*" }, "license": "MIT", "authors": [ @@ -32,8 +32,11 @@ "test": [ "vendor/bin/phpspec run", "vendor/bin/phpunit tests/", - "vendor/bin/phpstan analyse" - ] + "vendor/bin/phpstan analyse", + "@cs" + ], + "cs": "vendor/bin/php-cs-fixer fix --dry-run --diff", + "cs-fix": "vendor/bin/php-cs-fixer fix" }, "config": { "allow-plugins": { diff --git a/docker/xdebug.ini b/docker/xdebug.ini deleted file mode 100644 index 2fefb5f..0000000 --- a/docker/xdebug.ini +++ /dev/null @@ -1,11 +0,0 @@ -zend_extension=xdebug.so - -xdebug.remote_enable=1 -xdebug.remote_port=9000 -xdebug.remote_autostart=1 -xdebug.remote_connect_back=0 - -xdebug.idekey=docker -xdebug.remote_host=172.18.0.1 - -xdebug.remote_log='/tmp/xdebug-remote.log' diff --git a/spec/Lexer/LexerSpec.php b/spec/Lexer/LexerSpec.php index 70746b6..e573a9d 100644 --- a/spec/Lexer/LexerSpec.php +++ b/spec/Lexer/LexerSpec.php @@ -122,7 +122,7 @@ public function it_gives_the_same_tokens_when_tokenising_the_same_text_twice(): $origState = new InitialState(); $newState = new InitialState(); - $origState->registerMatcher(new LexerSpecRegexMatcher('Letter', '/(?a)/A'), function (Lexer $lexer) use ($newState): void { + $origState->registerMatcher(new LexerSpecRegexMatcher('Letter', '/(?a)/A'), static function (Lexer $lexer) use ($newState): void { $lexer->pushState($newState); }); $newState->registerMatcher(new LexerSpecRegexMatcher('Other', '/(?a)/A')); @@ -138,7 +138,7 @@ public function it_restores_the_initial_state_after_tokenising(): void $origState = new InitialState(); $newState = new InitialState(); - $origState->registerMatcher(new LexerSpecRegexMatcher('Letter', '/(?a)/A'), function (Lexer $lexer) use ($newState): void { + $origState->registerMatcher(new LexerSpecRegexMatcher('Letter', '/(?a)/A'), static function (Lexer $lexer) use ($newState): void { $lexer->pushState($newState); }); @@ -169,7 +169,7 @@ public function it_switches_state_on_a_zero_length_lookahead_match(): void $newState = new InitialState(); $origState->registerMatcher(new LexerSpecRegexMatcher('Letter', '/(?a)/A')); - $origState->registerMatcher(new LexerSpecRegexMatcher('Lookahead', '/(?)(?=b)/A'), function (Lexer $lexer) use ($newState): void { + $origState->registerMatcher(new LexerSpecRegexMatcher('Lookahead', '/(?)(?=b)/A'), static function (Lexer $lexer) use ($newState): void { $lexer->pushState($newState); }); $newState->registerMatcher(new LexerSpecRegexMatcher('Rest', '/(?b+)/A')); @@ -194,10 +194,10 @@ public function it_throws_if_zero_length_matches_return_to_a_state_without_advan $firstState = new InitialState(); $secondState = new InitialState(); - $firstState->registerMatcher(new LexerSpecRegexMatcher('ToSecond', '/(?)/A'), function (Lexer $lexer) use ($secondState): void { + $firstState->registerMatcher(new LexerSpecRegexMatcher('ToSecond', '/(?)/A'), static function (Lexer $lexer) use ($secondState): void { $lexer->pushState($secondState); }); - $secondState->registerMatcher(new LexerSpecRegexMatcher('ToFirst', '/(?)/A'), function (Lexer $lexer) use ($firstState): void { + $secondState->registerMatcher(new LexerSpecRegexMatcher('ToFirst', '/(?)/A'), static function (Lexer $lexer) use ($firstState): void { $lexer->pushState($firstState); }); @@ -227,6 +227,6 @@ public function match(string $text): ?MatchedText public function getName(): string { - return $this->type.'Matcher'; + return $this->type . 'Matcher'; } } diff --git a/spec/NodeTraverser/NodeTraverserSpec.php b/spec/NodeTraverser/NodeTraverserSpec.php index 59daa6d..11ea815 100644 --- a/spec/NodeTraverser/NodeTraverserSpec.php +++ b/spec/NodeTraverser/NodeTraverserSpec.php @@ -5,7 +5,6 @@ namespace spec\chrisjenkinson\StructuredDocumentParser\NodeTraverser; use chrisjenkinson\StructuredDocumentParser\Node\NodeInterface; -use chrisjenkinson\StructuredDocumentParser\NodeTraverser\NodeTraverser; use chrisjenkinson\StructuredDocumentParser\NodeVisitor\NodeVisitorAction; use chrisjenkinson\StructuredDocumentParser\NodeVisitor\NodeVisitorInterface; use PhpSpec\ObjectBehavior; diff --git a/spec/State/InitialStateSpec.php b/spec/State/InitialStateSpec.php index d6e7d7e..05fbe9b 100644 --- a/spec/State/InitialStateSpec.php +++ b/spec/State/InitialStateSpec.php @@ -192,7 +192,7 @@ public function it_calls_a_callback(Lexer $lexer, Cursor $cursor, MatcherInterfa $lexer->getState()->shouldBeCalled(); - $this->registerMatcher($matcher, function (Lexer $lexer): void { + $this->registerMatcher($matcher, static function (Lexer $lexer): void { $lexer->getState(); }); diff --git a/spec/State/NoTokenFoundExceptionSpec.php b/spec/State/NoTokenFoundExceptionSpec.php index 3ac6236..2454824 100644 --- a/spec/State/NoTokenFoundExceptionSpec.php +++ b/spec/State/NoTokenFoundExceptionSpec.php @@ -49,7 +49,7 @@ public function it_caps_the_excerpt_at_100_characters(): void { $this->beConstructedWith('stateName', 50, str_repeat('é', 150), new TokenPosition(12, 5)); - $this->getMessage()->shouldReturn('No token found with state stateName at line 12, column 5: "'.str_repeat('é', 100).'…"'); + $this->getMessage()->shouldReturn('No token found with state stateName at line 12, column 5: "' . str_repeat('é', 100) . '…"'); } public function it_shows_a_newline_when_the_text_starts_with_one(): void diff --git a/src/Lexer/Cursor.php b/src/Lexer/Cursor.php index 7cb1066..3fcd038 100644 --- a/src/Lexer/Cursor.php +++ b/src/Lexer/Cursor.php @@ -42,7 +42,7 @@ public function advance(int $length): void $newlines = mb_substr_count($consumed, "\n"); if (0 < $newlines) { - $this->line += $newlines; + $this->line += $newlines; $this->column = mb_strlen($consumed) - mb_strrpos($consumed, "\n"); } else { $this->column += mb_strlen($consumed); diff --git a/src/Matcher/MatchedText.php b/src/Matcher/MatchedText.php index 1e22fe0..5a7c5a9 100644 --- a/src/Matcher/MatchedText.php +++ b/src/Matcher/MatchedText.php @@ -14,8 +14,6 @@ public function __construct(private readonly array $matches) } /** - * @param string $key - * * @return mixed|null */ public function get(string $key) diff --git a/src/Matcher/MatcherInterface.php b/src/Matcher/MatcherInterface.php index 8f7e600..2382afb 100644 --- a/src/Matcher/MatcherInterface.php +++ b/src/Matcher/MatcherInterface.php @@ -15,8 +15,5 @@ interface MatcherInterface */ public function match(string $text): ?MatchedText; - /** - * @return string - */ public function getName(): string; } diff --git a/src/NodeTraverser/NodeTraverser.php b/src/NodeTraverser/NodeTraverser.php index 91d8b2a..a14a141 100644 --- a/src/NodeTraverser/NodeTraverser.php +++ b/src/NodeTraverser/NodeTraverser.php @@ -27,7 +27,7 @@ public function addVisitor(NodeVisitorInterface $visitor): void */ public function traverse(NodeInterface $node): ?NodeInterface { - array_map(function (NodeVisitorInterface $nodeVisitor) use (&$node): void { + array_map(static function (NodeVisitorInterface $nodeVisitor) use (&$node): void { if (null === $before = $nodeVisitor->beforeTraverse($node)) { return; } @@ -40,7 +40,7 @@ public function traverse(NodeInterface $node): ?NodeInterface return null; } - array_map(function (NodeVisitorInterface $nodeVisitor) use (&$node): void { + array_map(static function (NodeVisitorInterface $nodeVisitor) use (&$node): void { if (null === $after = $nodeVisitor->afterTraverse($node)) { return; } @@ -96,7 +96,7 @@ public function traverseChildren(array $children): array private function runEnterNodeVisitors(NodeInterface $node): NodeInterface { - array_map(function (NodeVisitorInterface $nodeVisitor) use (&$node): void { + array_map(static function (NodeVisitorInterface $nodeVisitor) use (&$node): void { if (null === $enter = $nodeVisitor->enterNode($node)) { return; } @@ -107,7 +107,6 @@ private function runEnterNodeVisitors(NodeInterface $node): NodeInterface } /** - * @param NodeInterface $node * @param NodeInterface[] $children */ private function runTraverseNodeOnSubNodes(NodeInterface $node, array $children): void @@ -151,7 +150,7 @@ private function runTraverseChildrenOnAttributes(NodeInterface $node, array $att private function runLeaveNodeVisitors(NodeInterface $node): NodeInterface|NodeVisitorAction { - array_map(function (NodeVisitorInterface $nodeVisitor) use (&$node): void { + array_map(static function (NodeVisitorInterface $nodeVisitor) use (&$node): void { if (NodeVisitorAction::RemoveNode === $node) { return; } diff --git a/src/NodeVisitor/AbstractNodeVisitor.php b/src/NodeVisitor/AbstractNodeVisitor.php index a14fc53..e990eba 100644 --- a/src/NodeVisitor/AbstractNodeVisitor.php +++ b/src/NodeVisitor/AbstractNodeVisitor.php @@ -8,33 +8,21 @@ abstract class AbstractNodeVisitor implements NodeVisitorInterface { - /** - * {@inheritdoc} - */ public function beforeTraverse(NodeInterface $node): ?NodeInterface { return null; } - /** - * {@inheritdoc} - */ public function afterTraverse(NodeInterface $node): ?NodeInterface { return null; } - /** - * {@inheritdoc} - */ public function enterNode(NodeInterface $node): ?NodeInterface { return null; } - /** - * {@inheritdoc} - */ public function leaveNode(NodeInterface $node): NodeInterface|NodeVisitorAction|null { return null; diff --git a/src/State/AbstractState.php b/src/State/AbstractState.php index d28e47c..b846807 100644 --- a/src/State/AbstractState.php +++ b/src/State/AbstractState.php @@ -44,6 +44,11 @@ public function findMatchingToken(Lexer $lexer, Cursor $cursor): TokenInterface ); } + public function getName(): string + { + return (new ReflectionClass($this))->getShortName(); + } + private function getTokenType(string $matcherName): string { if (str_ends_with($matcherName, 'Matcher')) { @@ -79,8 +84,8 @@ private function findSingleMatch(array $matches, string $remainingText, int $cur throw new AmbiguousTokenFoundException( $this->getName(), $remainingText, - array_map(fn (MatcherMatch $match): string => $match->matcherName, $matches), - array_map(fn (MatcherMatch $match): MatchedText => $match->matchedText, $matches), + array_map(static fn (MatcherMatch $match): string => $match->matcherName, $matches), + array_map(static fn (MatcherMatch $match): MatchedText => $match->matchedText, $matches), $position ); } @@ -117,9 +122,4 @@ private function runMatchers(string $text): array return $matches; } - - public function getName(): string - { - return (new ReflectionClass($this))->getShortName(); - } } diff --git a/src/State/AmbiguousTokenFoundException.php b/src/State/AmbiguousTokenFoundException.php index 91d0b65..7776b47 100644 --- a/src/State/AmbiguousTokenFoundException.php +++ b/src/State/AmbiguousTokenFoundException.php @@ -24,9 +24,11 @@ public function __construct( int $code = 0, ?Throwable $previous = null ) { - $matches = array_map(function (string $matcherName, MatchedText $matchedText): string { - return sprintf('%s (%s)', $matcherName, TextExcerpt::of($matchedText->getAll()['all'])); - }, $calledMatchers, $matchedTokens); + $matches = array_map( + static fn (string $matcherName, MatchedText $matchedText): string => sprintf('%s (%s)', $matcherName, TextExcerpt::of($matchedText->getAll()['all'])), + $calledMatchers, + $matchedTokens + ); $message = sprintf( 'Ambiguous token found with state %s at line %d, column %d: matchers %s', diff --git a/src/Token/TokenStream.php b/src/Token/TokenStream.php index 3e20b12..c602728 100644 --- a/src/Token/TokenStream.php +++ b/src/Token/TokenStream.php @@ -51,8 +51,6 @@ public function getCurrentToken(): ?TokenInterface } /** - * @param string $expectedType - * * @return true */ public function expectTokenType(string $expectedType): bool @@ -85,9 +83,7 @@ public function expectTokenTypes(array $expectedTypes): bool $currentToken = $this->getCurrentToken(); - $expected = array_filter($expectedTypes, function ($expectedType) use ($currentToken) { - return $expectedType === $currentToken->getType(); - }); + $expected = array_filter($expectedTypes, static fn ($expectedType) => $expectedType === $currentToken->getType()); if (count($expected) >= 1) { return true;