diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 6528b58..79502d9 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -9,7 +9,7 @@ jobs: fail-fast: true matrix: os: [ubuntu-latest] - php: [8.0, 8.1, 8.2] + php: [8.2, 8.3, 8.4] dependency-version: [prefer-lowest, prefer-stable] name: P${{ matrix.php }} - ${{ matrix.dependency-version }} - ${{ matrix.os }} diff --git a/.gitignore b/.gitignore index f12bc46..c1b5059 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .idea +cache/* .php_cs .php_cs.cache .phpunit.result.cache diff --git a/composer.json b/composer.json index b5b30e0..40f77e5 100644 --- a/composer.json +++ b/composer.json @@ -16,15 +16,14 @@ } ], "require": { - "php": "^7.4|^8.0", - "twig/twig": "^3.0" + "php": "^8.2", + "twig/twig": "^3.21" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.0", - "pestphp/pest": "^1.0", - "phpunit/phpunit": "^9.3", - "symfony/var-dumper": "^5.2", - "vimeo/psalm": "^3.11" + "friendsofphp/php-cs-fixer": "^3.75", + "pestphp/pest": "^3.8", + "symfony/var-dumper": "^7.2", + "vimeo/psalm": "^6.10" }, "autoload": { "psr-4": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 1d5bedd..914d54e 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,31 +1,20 @@ - - + tests - + - ./src + src - - - - - - - - - + + vendor + + diff --git a/src/Configuration.php b/src/Configuration.php index 4077aa5..de709c9 100644 --- a/src/Configuration.php +++ b/src/Configuration.php @@ -157,8 +157,8 @@ public function setup() $this->twig->setLexer(new ComponentLexer($this->twig)); } - /** @var \Twig\Extension\EscaperExtension */ - $escaper = $this->twig->getExtension(\Twig\Extension\EscaperExtension::class); + /** @var \Twig\Runtime\EscaperRuntime */ + $escaper = $this->twig->getRuntime(\Twig\Runtime\EscaperRuntime::class); $escaper->addSafeClass(ComponentAttributeBag::class, ['all']); $escaper->addSafeClass(ComponentSlot::class, ['all']); } diff --git a/src/Node/ComponentNode.php b/src/Node/ComponentNode.php index 77e0f95..456b891 100644 --- a/src/Node/ComponentNode.php +++ b/src/Node/ComponentNode.php @@ -61,15 +61,12 @@ protected function addGetTemplate(Compiler $compiler) $repr = $this->isDynamicComponent() ? 'raw' : 'repr'; $compiler - ->raw('$this->loadTemplate(' . PHP_EOL) + ->raw('$this->load(' . PHP_EOL) ->indent(1) ->write('') ->$repr($this->getTemplateName()) ->raw(', ' . PHP_EOL) ->write('') - ->$repr($this->getTemplateName()) - ->raw(', ' . PHP_EOL) - ->write('') ->repr($this->getTemplateLine()) ->indent(-1) ->raw(PHP_EOL . ');' . PHP_EOL . PHP_EOL); @@ -97,17 +94,19 @@ public function getDynamicComponent() if ($value->hasAttribute('value')) { // Returns the component string value $component = '\'' . $value->getAttribute('value') . '\''; + break; } if ($value->hasAttribute('name')) { // Uses the context to get the component value $component = '($context[\'' . $value->getAttribute('name') . '\'] ?? null)'; + break; } } - if (!$component) { + if (! $component) { throw new Exception('Dynamic component must have a component attribute'); } @@ -130,6 +129,7 @@ public static function parseDynamicComponent($path, $component) // Strip anything from the path before the dynamic component name, so it begins with the namespace $dynamicComponentEndPosition = strpos($path, self::DYNAMIC_COMPONENT_NAME) + strlen(self::DYNAMIC_COMPONENT_NAME); $pathEnd = substr($path, $dynamicComponentEndPosition); + return $component . $pathEnd; } @@ -185,7 +185,7 @@ protected function addTemplateArguments(Compiler $compiler) public function filterVariables() { - if (!$this->isDynamicComponent()) { + if (! $this->isDynamicComponent()) { return; } diff --git a/src/Node/SlotNode.php b/src/Node/SlotNode.php index e90b903..79c5455 100644 --- a/src/Node/SlotNode.php +++ b/src/Node/SlotNode.php @@ -13,7 +13,7 @@ final class SlotNode extends Node implements NodeOutputInterface { public function __construct($name, $body, ?AbstractExpression $variables, int $lineno = 0) { - parent::__construct(['body' => $body], ['name' => $name], $lineno, null); + parent::__construct(['body' => $body], ['name' => $name], $lineno); if ($variables) { $this->setNode('variables', $variables); diff --git a/src/TokenParser/ComponentTokenParser.php b/src/TokenParser/ComponentTokenParser.php index 5485e5c..bfcef98 100644 --- a/src/TokenParser/ComponentTokenParser.php +++ b/src/TokenParser/ComponentTokenParser.php @@ -66,7 +66,7 @@ protected function parseArguments() } if ($stream->nextIf(/* Token::NAME_TYPE */5, 'with')) { - $variables = $this->parser->getExpressionParser()->parseExpression(); + $variables = $this->parser->parseExpression(); } $stream->expect(/* Token::BLOCK_END_TYPE */3); @@ -80,9 +80,9 @@ public function parseComponentName(): string $path = []; - if ($this->parser->getCurrentToken()->getType() != /** Token::NAME_TYPE */ 5) { - throw new Exception('First token must be a name type'); - } + // if ($this->parser->getCurrentToken()->getType() !== Token::NAME_TYPE) { + // throw new Exception('First token must be a name type'); + // } $name = $this->getNameSection(); @@ -93,7 +93,7 @@ public function parseComponentName(): string $path[] = $name; - while ($stream->nextIf(9 /** Token::PUNCTUATION_TYPE */, '.')) { + while ($stream->nextIf(8 /** Token::PUNCTUATION_TYPE */, '.')) { $path[] = $this->getNameSection(); } diff --git a/src/TokenParser/SlotTokenParser.php b/src/TokenParser/SlotTokenParser.php index 22b6789..f1e5a49 100644 --- a/src/TokenParser/SlotTokenParser.php +++ b/src/TokenParser/SlotTokenParser.php @@ -33,7 +33,7 @@ protected function parseArguments(): array } if ($stream->nextIf(/* Token::NAME_TYPE */5, 'with')) { - $variables = $this->parser->getExpressionParser()->parseExpression(); + $variables = $this->parser->parseExpression(); } $stream->expect(/* Token::BLOCK_END_TYPE */3); @@ -45,9 +45,10 @@ public function parseSlotName(): string { $stream = $this->parser->getStream(); - if ($this->parser->getCurrentToken()->getType() != /** Token::NAME_TYPE */ 5) { - throw new Exception('First token must be a name type'); - } + //$stream->expect(5); + // if ($this->parser->getCurrentToken()->getType() != /** Token::NAME_TYPE */ 5) { + // throw new Exception('First token must be a name type'); + // } return $stream->next()->getValue(); } diff --git a/tests/ComponentTokenParserTest.php b/tests/ComponentTokenParserTest.php index 0fad788..26a6f9b 100644 --- a/tests/ComponentTokenParserTest.php +++ b/tests/ComponentTokenParserTest.php @@ -4,10 +4,12 @@ use Performing\TwigComponents\Configuration; use Performing\TwigComponents\TokenParser\ComponentTokenParser; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; class ComponentTokenParserTest extends TestCase { + #[Test] public function testGetComponentPathWithHintPath() { $loader = new \Twig\Loader\FilesystemLoader(__DIR__ . '/templates'); @@ -27,6 +29,7 @@ public function testGetComponentPathWithHintPath() $this->assertEquals('mynamespace.myplugin::components.test.component', $componentPath); } + #[Test] public function testGetComponentPathWithoutHintPath() { $loader = new \Twig\Loader\FilesystemLoader(__DIR__ . '/templates'); diff --git a/tests/ComponentsTestTrait.php b/tests/ComponentsTestTrait.php index 549bfd5..0efcc15 100644 --- a/tests/ComponentsTestTrait.php +++ b/tests/ComponentsTestTrait.php @@ -2,9 +2,11 @@ namespace Performing\TwigComponents\Tests; +use PHPUnit\Framework\Attributes\Test; + trait ComponentsTestTrait { - /** @test */ + #[Test] public function render_simple_component() { $html = $this->twig->render('test_simple_component.twig'); @@ -14,7 +16,7 @@ public function render_simple_component() HTML, $html); } - /** @test */ + #[Test] public function render_simple_component_with_dash() { $html = $this->twig->render('test_simple_component_with_dash.twig'); @@ -24,7 +26,7 @@ public function render_simple_component_with_dash() HTML, $html); } - /** @test */ + #[Test] public function render_simple_component_in_folder() { $html = $this->twig->render('test_simple_component_in_folder.twig'); @@ -34,7 +36,7 @@ public function render_simple_component_in_folder() HTML, $html); } - /** @test */ + #[Test] public function render_component_with_slots() { $html = $this->twig->render('test_with_slots.twig'); @@ -44,7 +46,7 @@ public function render_component_with_slots() HTML, $html); } - /** @test */ + #[Test] public function render_xtags_with_slots() { $html = $this->twig->render('test_xtags_with_slots.twig'); @@ -54,7 +56,7 @@ public function render_xtags_with_slots() HTML, $html); } - /** @test */ + #[Test] public function render_nested_xtags_with_slots() { $html = $this->twig->render('test_nested_xtags_with_slots.twig'); @@ -64,7 +66,7 @@ public function render_nested_xtags_with_slots() HTML, $html); } - /** @test */ + #[Test] public function render_deeply_nested_xtags_with_slots() { $html = $this->twig->render('test_deeply_nested_xtags_with_slots.twig'); @@ -75,7 +77,7 @@ public function render_deeply_nested_xtags_with_slots() HTML, $html); } - /** @test */ + #[Test] public function render_component_with_xtags() { $html = $this->twig->render('test_xtags_component.twig'); @@ -87,7 +89,7 @@ public function render_component_with_xtags() HTML, $html); } - /** @test */ + #[Test] public function render_component_with_attributes() { $html = $this->twig->render('test_with_attributes.twig'); @@ -101,7 +103,7 @@ public function render_component_with_attributes() HTML, $html); } - /** @test */ + #[Test] public function render_namespaced_component() { $html = $this->twig->render('test_namespaced_component.twig'); @@ -111,7 +113,7 @@ public function render_namespaced_component() HTML, $html); } - /** @test */ + #[Test] public function render_namespaced_xtags_component() { $html = $this->twig->render('test_namespaced_xtags_component.twig'); @@ -123,7 +125,7 @@ public function render_namespaced_xtags_component() HTML, $html); } - /** @test */ + #[Test] public function test_class_merge_works_with_components_in_components() { $template = $this->twig->createTemplate(<<assertEquals('', $html); } - /** @test */ + #[Test] public function test_attributes_dont_conflict_with_components_in_components() { $template = $this->twig->createTemplate(<<assertEquals('
', $html); } - /** @test */ + #[Test] public function render_simple_dynamic_component() { $html = $this->twig->render('test_simple_dynamic_component.twig'); @@ -156,7 +158,7 @@ public function render_simple_dynamic_component() HTML, $html); } - /** @test */ + #[Test] public function render_dynamic_component_with_xtags() { $html = $this->twig->render('test_xtags_dynamic_component.twig'); @@ -168,7 +170,7 @@ public function render_dynamic_component_with_xtags() HTML, $html); } - /** @test */ + #[Test] public function render_namespaced_dynamic_component() { $html = $this->twig->render('test_namespaced_dynamic_component.twig'); @@ -178,7 +180,7 @@ public function render_namespaced_dynamic_component() HTML, $html); } - /** @test */ + #[Test] public function render_namespaced_xtags_dynamic_component() { $html = $this->twig->render('test_namespaced_xtags_dynamic_component.twig'); diff --git a/tests/GlobalContextTest.php b/tests/GlobalContextTest.php index a113d50..75ee7f9 100644 --- a/tests/GlobalContextTest.php +++ b/tests/GlobalContextTest.php @@ -3,6 +3,7 @@ namespace Performing\TwigComponents\Tests; use Performing\TwigComponents\Configuration; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; class GlobalContextTest extends TestCase @@ -36,7 +37,7 @@ public function setUp(): void $this->twig = $this->setupTwig(); } - /** @test */ + #[Test] public function share_global_context_inside_components() { $template = $this->twig->createTemplate(<<