From 925254426007c122fe5b025dcaf08aebf8ff33f6 Mon Sep 17 00:00:00 2001 From: Andrea Sprega Date: Mon, 18 Dec 2023 09:29:12 +0100 Subject: [PATCH] Bump to min PHP 8, drop doctrine/annotations dependency. --- .github/workflows/test.yml | 4 +- README.md | 50 +++------- UPGRADE-2.0.md | 23 +++++ composer.json | 13 ++- src/{Annotation => Attribute}/Breadcrumb.php | 15 +-- src/DependencyInjection/Configuration.php | 3 - .../SlopeItBreadcrumbExtension.php | 6 -- src/EventListener/BreadcrumbListener.php | 26 +----- src/Model/BreadcrumbItem.php | 26 ++---- src/Model/ProcessedBreadcrumbItem.php | 10 +- src/Resources/config/services.yml | 1 - src/Service/BreadcrumbBuilder.php | 21 +---- src/Service/BreadcrumbItemProcessor.php | 24 +---- src/SlopeItBreadcrumbBundle.php | 3 - src/Twig/BreadcrumbExtension.php | 16 +--- tests/Fixtures/TestKernel.php | 4 +- tests/Fixtures/config/config.yml | 3 - tests/Unit/Annotation/BreadcrumbTest.php | 93 ------------------- tests/Unit/Attribute/BreadcrumbTest.php | 51 ++++++++++ .../EventListener/BreadcrumbListenerTest.php | 12 +-- .../Service/BreadcrumbItemProcessorTest.php | 3 +- 21 files changed, 130 insertions(+), 277 deletions(-) create mode 100644 UPGRADE-2.0.md rename src/{Annotation => Attribute}/Breadcrumb.php (82%) delete mode 100644 tests/Unit/Annotation/BreadcrumbTest.php create mode 100644 tests/Unit/Attribute/BreadcrumbTest.php diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9c9c197..6023051 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,10 +14,10 @@ jobs: strategy: matrix: php-version: - - "7.3" - - "7.4" - "8.0" - "8.1" + - "8.2" + - "8.3" steps: - name: "Checkout" diff --git a/README.md b/README.md index 5f26b8e..3b9a75a 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ class AppKernel extends Kernel ### 2. Define breadcrumbs -There are three ways to create a breadcrumb: via code (1), via attributes (2) (PHP 8.0+) or via annotations (3). +There are two ways to create a breadcrumb: via code (1) or via attributes (2). *Via code*: you can inject the breadcrumb builder in your controller and add breadcrumb items: @@ -77,7 +77,7 @@ class CoolController extends Controller ```php 'home', @@ -98,32 +98,6 @@ class CoolController extends Controller } ``` -*Via annotations*: just use the `@Breadcrumb` annotation at the class and/or method level. They will be merged (class comes first). - -*NOTE:* The annotation can take either a single item (in the example it's done at the class level) or multiple items (in the example, at the method level). - -```php - 'parents', 'route' => 'parent_list'], + ['label' => '$parent.name', 'route' => 'parent_view'], + ['label' => 'children', 'route' => 'children_list'], + ['label' => '$child.name', 'route' => 'child_view'], + ['label' => 'edit'], +])] public function childrenEditAction($parentID, $childrenID) { // ... diff --git a/UPGRADE-2.0.md b/UPGRADE-2.0.md new file mode 100644 index 0000000..e4bdae3 --- /dev/null +++ b/UPGRADE-2.0.md @@ -0,0 +1,23 @@ +# UPGRADE FROM 1.x to 2.0 + +- Removed annotations in favor of attributes. Migration should be straightforward, here's a quick example: + +Before: +```php +use SlopeIt\BreadcrumbBundle\Annotation\Breadcrumb; + +/** + * @Breadcrumb({ + * { "label" = "home", "route" = "home_route" }, + * }) + */ +class CoolController extends Controller +``` + +After: +```php +use SlopeIt\BreadcrumbBundle\Attribute\Breadcrumb; + +#[Breadcrumb(['label' => 'home', 'route' => 'home_route'])] +class CoolController extends Controller +``` diff --git a/composer.json b/composer.json index 8a724c6..c60b401 100644 --- a/composer.json +++ b/composer.json @@ -19,18 +19,17 @@ } ], "require": { - "php": ">=7.3.0", - "doctrine/annotations": "^1.0", - "symfony/framework-bundle": "^4.0|^5.0|^6.0", - "symfony/property-access": "^4.0|^5.0|^6.0", - "symfony/translation": "^4.0|^5.0|^6.0", - "symfony/yaml": "^4.0|^5.0|^6.0", + "php": ">=8.0.0", + "symfony/framework-bundle": "^5.4|^6.3|^7.0", + "symfony/property-access": "^5.4|^6.3|^7.0", + "symfony/translation": "^5.4|^6.3|^7.0", + "symfony/yaml": "^5.4|^6.3|^7.0", "twig/twig": "^2.10|^3.0" }, "require-dev": { "mockery/mockery": "^1.3", "phpunit/phpunit": "8.5.15", - "symfony/dependency-injection": "^4.0|^5.0", + "symfony/dependency-injection": "^5.4|^6.3|^7.0", "symfony/monolog-bundle": "^3.7" }, "autoload": { diff --git a/src/Annotation/Breadcrumb.php b/src/Attribute/Breadcrumb.php similarity index 82% rename from src/Annotation/Breadcrumb.php rename to src/Attribute/Breadcrumb.php index 21ba696..9bfa077 100644 --- a/src/Annotation/Breadcrumb.php +++ b/src/Attribute/Breadcrumb.php @@ -1,11 +1,7 @@ breadcrumbBuilder = $breadcrumbBuilder; - $this->annotationReader = $annotationReader; } public function onKernelController(ControllerEvent $event): void @@ -38,16 +28,10 @@ public function onKernelController(ControllerEvent $event): void $method = new \ReflectionMethod($controller, $action); $breadcrumbs = []; - if (($classAnnotation = $this->annotationReader->getClassAnnotation($class, Breadcrumb::class))) { - $breadcrumbs[] = $classAnnotation; - } - if (\PHP_VERSION_ID >= 80000 && ($classAttribute = $class->getAttributes(Breadcrumb::class)[0] ?? null)) { + if (($classAttribute = $class->getAttributes(Breadcrumb::class)[0] ?? null)) { $breadcrumbs[] = $classAttribute->newInstance(); } - if (($methodAnnotation = $this->annotationReader->getMethodAnnotation($method, Breadcrumb::class))) { - $breadcrumbs[] = $methodAnnotation; - } - if (\PHP_VERSION_ID >= 80000 && ($methodAttribute = $method->getAttributes(Breadcrumb::class)[0] ?? null)) { + if (($methodAttribute = $method->getAttributes(Breadcrumb::class)[0] ?? null)) { $breadcrumbs[] = $methodAttribute->newInstance(); } diff --git a/src/Model/BreadcrumbItem.php b/src/Model/BreadcrumbItem.php index 3ab0af4..3ee2aa7 100644 --- a/src/Model/BreadcrumbItem.php +++ b/src/Model/BreadcrumbItem.php @@ -8,25 +8,14 @@ */ class BreadcrumbItem { - /** - * @var string|null - */ - private $label; + private ?string $label; - /** - * @var string|null - */ - private $route; + private ?string $route; - /** - * @var array|null - */ - private $routeParams; + private ?array $routeParams; - /** - * @var string|null|false - Null uses the default transation domain, passing "false" avoids translation altogether. - */ - private $translationDomain; + /** Null uses the default transation domain, passing "false" avoids translation altogether. */ + private string|null|false $translationDomain; public function __construct( ?string $label = null, @@ -55,10 +44,7 @@ public function getRouteParams(): ?array return $this->routeParams; } - /** - * @return false|string|null - */ - public function getTranslationDomain() + public function getTranslationDomain(): string|null|false { return $this->translationDomain; } diff --git a/src/Model/ProcessedBreadcrumbItem.php b/src/Model/ProcessedBreadcrumbItem.php index 2b61dc0..33efc40 100644 --- a/src/Model/ProcessedBreadcrumbItem.php +++ b/src/Model/ProcessedBreadcrumbItem.php @@ -7,15 +7,9 @@ */ class ProcessedBreadcrumbItem { - /** - * @var string|null - */ - private $translatedLabel; + private ?string $translatedLabel; - /** - * @var string|null - */ - private $url; + private ?string $url; public function __construct(?string $translatedLabel = null, ?string $url = null) { diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml index 28fe81a..bdc96bb 100644 --- a/src/Resources/config/services.yml +++ b/src/Resources/config/services.yml @@ -5,7 +5,6 @@ services: - { name: kernel.event_listener, event: kernel.controller, method: onKernelController } arguments: - '@slope_it.breadcrumb.builder' - - '@annotation_reader' slope_it.breadcrumb.builder: class: SlopeIt\BreadcrumbBundle\Service\BreadcrumbBuilder diff --git a/src/Service/BreadcrumbBuilder.php b/src/Service/BreadcrumbBuilder.php index a98b660..268029f 100644 --- a/src/Service/BreadcrumbBuilder.php +++ b/src/Service/BreadcrumbBuilder.php @@ -5,29 +5,21 @@ use SlopeIt\BreadcrumbBundle\Model\BreadcrumbItem; /** - * Provides a minimal interface to create a breadcrumb. This is used by the event listener if annotation are used, but + * Provides a minimal interface to create a breadcrumb. This is used by the event listener if attributes are used, but * can also be used straight from controllers which want to customize their breadcrumb. */ class BreadcrumbBuilder { - /** - * @var BreadcrumbItemFactory - */ - private $itemFactory; + private BreadcrumbItemFactory $itemFactory; - /** - * @var BreadcrumbItem[] - */ - private $items = []; + /** @var BreadcrumbItem[] */ + private array $items = []; public function __construct(BreadcrumbItemFactory $itemFactory) { $this->itemFactory = $itemFactory; } - /** - * Adds a new breadcrumb item. - */ public function addItem( ?string $label = null, ?string $route = null, @@ -38,11 +30,6 @@ public function addItem( return $this; } - /** - * Returns the current breadcrumb items. - * - * @return BreadcrumbItem[] - */ public function getItems(): array { return $this->items; diff --git a/src/Service/BreadcrumbItemProcessor.php b/src/Service/BreadcrumbItemProcessor.php index 67f41c2..dd4330a 100644 --- a/src/Service/BreadcrumbItemProcessor.php +++ b/src/Service/BreadcrumbItemProcessor.php @@ -15,25 +15,13 @@ */ class BreadcrumbItemProcessor { - /** - * @var PropertyAccessorInterface - */ - private $propertyAccessor; + private PropertyAccessorInterface $propertyAccessor; - /** - * @var RequestStack - */ - private $requestStack; + private RequestStack $requestStack; - /** - * @var UrlGeneratorInterface - */ - private $urlGenerator; + private UrlGeneratorInterface $urlGenerator; - /** - * @var TranslatorInterface - */ - private $translator; + private TranslatorInterface $translator; public function __construct( PropertyAccessorInterface $propertyAccessor, @@ -118,10 +106,8 @@ private function processItem(BreadcrumbItem $item, array $variables): ProcessedB /** * Returns the value contained in the variable name (with optional property path) of the given expression. - * - * @return mixed */ - private function parseValue(string $expression, array $variables) + private function parseValue(string $expression, array $variables): mixed { $components = explode('.', $expression, 2); $variableName = substr($components[0], 1); // Remove the $ prefix; diff --git a/src/SlopeItBreadcrumbBundle.php b/src/SlopeItBreadcrumbBundle.php index 6e8e451..517dd90 100644 --- a/src/SlopeItBreadcrumbBundle.php +++ b/src/SlopeItBreadcrumbBundle.php @@ -8,9 +8,6 @@ class SlopeItBreadcrumbBundle extends Bundle { - /** - * {@inheritdoc} - */ public function getContainerExtension(): ?ExtensionInterface { return new SlopeItBreadcrumbExtension(); diff --git a/src/Twig/BreadcrumbExtension.php b/src/Twig/BreadcrumbExtension.php index 30fc33d..9ebf5e6 100644 --- a/src/Twig/BreadcrumbExtension.php +++ b/src/Twig/BreadcrumbExtension.php @@ -10,20 +10,11 @@ class BreadcrumbExtension extends AbstractExtension { - /** - * @var BreadcrumbBuilder - */ - private $builder; + private BreadcrumbBuilder $builder; - /** - * @var BreadcrumbItemProcessor - */ - private $itemProcessor; + private BreadcrumbItemProcessor $itemProcessor; - /** - * @var string - */ - private $template; + private string $template; public function __construct(BreadcrumbBuilder $builder, BreadcrumbItemProcessor $itemProcessor, string $template) { @@ -33,7 +24,6 @@ public function __construct(BreadcrumbBuilder $builder, BreadcrumbItemProcessor } /** - * {@inheritDoc} * @return TwigFunction[] */ public function getFunctions(): array diff --git a/tests/Fixtures/TestKernel.php b/tests/Fixtures/TestKernel.php index 58444a2..cf1527f 100644 --- a/tests/Fixtures/TestKernel.php +++ b/tests/Fixtures/TestKernel.php @@ -22,7 +22,7 @@ public function registerBundles(): iterable ]; } - public function build(ContainerBuilder $container) + public function build(ContainerBuilder $container): void { $container->addCompilerPass(new class() implements CompilerPassInterface { public function process(ContainerBuilder $container): void @@ -34,7 +34,7 @@ public function process(ContainerBuilder $container): void }); } - public function registerContainerConfiguration(LoaderInterface $loader) + public function registerContainerConfiguration(LoaderInterface $loader): void { $loader->load(__DIR__.'/config/config.yml'); } diff --git a/tests/Fixtures/config/config.yml b/tests/Fixtures/config/config.yml index 5b0daac..abb4ebc 100644 --- a/tests/Fixtures/config/config.yml +++ b/tests/Fixtures/config/config.yml @@ -6,6 +6,3 @@ framework: translator: logging: true cache: true - annotations: - enabled: true - cache: file diff --git a/tests/Unit/Annotation/BreadcrumbTest.php b/tests/Unit/Annotation/BreadcrumbTest.php deleted file mode 100644 index c6cff13..0000000 --- a/tests/Unit/Annotation/BreadcrumbTest.php +++ /dev/null @@ -1,93 +0,0 @@ - [ - ['label' => 'aLabel', 'route' => 'aRoute', 'params' => ['param' => 'value']], - ['label' => 'anotherLabel', 'route' => 'anotherRoute'] - ] - ]); - - $this->assertEquals('aLabel', $annotation->items[0]['label']); - $this->assertEquals('aRoute', $annotation->items[0]['route']); - $this->assertEquals(['param' => 'value'], $annotation->items[0]['params']); - $this->assertEquals('anotherLabel', $annotation->items[1]['label']); - $this->assertEquals('anotherRoute', $annotation->items[1]['route']); - $this->assertArrayNotHasKey('params', $annotation->items[1]); - } - - /** - * The constructor can receive a single breadcrumb item. When used as annotation, its parameter is passed via an - * array with the 'value' key. - * - * @covers ::__construct - */ - public function test_constructor_asAnnotation_withSingleItem() - { - $annotation = new Breadcrumb([ - 'value' => [ - 'label' => 'aLabel', - 'route' => 'aRoute' - ] - ]); - - $this->assertEquals('aLabel', $annotation->items[0]['label']); - $this->assertEquals('aRoute', $annotation->items[0]['route']); - } - - /** - * The constructor can also receive an array of breadcrumb items. - * - * @covers ::__construct - */ - public function test_constructor_asAttribute_withMultipleItems() - { - $annotation = new Breadcrumb([ - ['label' => 'aLabel', 'route' => 'aRoute', 'params' => ['param' => 'value']], - ['label' => 'anotherLabel', 'route' => 'anotherRoute'] - ]); - - $this->assertEquals('aLabel', $annotation->items[0]['label']); - $this->assertEquals('aRoute', $annotation->items[0]['route']); - $this->assertEquals(['param' => 'value'], $annotation->items[0]['params']); - $this->assertEquals('anotherLabel', $annotation->items[1]['label']); - $this->assertEquals('anotherRoute', $annotation->items[1]['route']); - $this->assertArrayNotHasKey('params', $annotation->items[1]); - } - - /** - * The annotation constructor can receive a single breadcrumb item. - * - * @covers ::__construct - */ - public function test_constructor_asAttribute_withSingleItem() - { - $annotation = new Breadcrumb([ - 'label' => 'aLabel', - 'route' => 'aRoute' - ]); - - $this->assertEquals('aLabel', $annotation->items[0]['label']); - $this->assertEquals('aRoute', $annotation->items[0]['route']); - } -} diff --git a/tests/Unit/Attribute/BreadcrumbTest.php b/tests/Unit/Attribute/BreadcrumbTest.php new file mode 100644 index 0000000..29c37a6 --- /dev/null +++ b/tests/Unit/Attribute/BreadcrumbTest.php @@ -0,0 +1,51 @@ + 'aLabel', 'route' => 'aRoute', 'params' => ['param' => 'value']], + ['label' => 'anotherLabel', 'route' => 'anotherRoute'] + ]); + + $this->assertEquals('aLabel', $attribute->items[0]['label']); + $this->assertEquals('aRoute', $attribute->items[0]['route']); + $this->assertEquals(['param' => 'value'], $attribute->items[0]['params']); + $this->assertEquals('anotherLabel', $attribute->items[1]['label']); + $this->assertEquals('anotherRoute', $attribute->items[1]['route']); + $this->assertArrayNotHasKey('params', $attribute->items[1]); + } + + /** + * The constructor can receive a single breadcrumb item. + * + * @covers ::__construct + */ + public function test_constructor_withSingleItem() + { + $attribute = new Breadcrumb([ + 'label' => 'aLabel', + 'route' => 'aRoute' + ]); + + $this->assertEquals('aLabel', $attribute->items[0]['label']); + $this->assertEquals('aRoute', $attribute->items[0]['route']); + } +} diff --git a/tests/Unit/EventListener/BreadcrumbListenerTest.php b/tests/Unit/EventListener/BreadcrumbListenerTest.php index eacbab5..90438b4 100644 --- a/tests/Unit/EventListener/BreadcrumbListenerTest.php +++ b/tests/Unit/EventListener/BreadcrumbListenerTest.php @@ -2,7 +2,6 @@ namespace SlopeIt\Tests\BreadcrumbBundle\Unit\EventListener; -use Doctrine\Common\Annotations\Reader; use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; use PHPUnit\Framework\TestCase; use SlopeIt\BreadcrumbBundle\EventListener\BreadcrumbListener; @@ -21,22 +20,19 @@ class BreadcrumbListenerTest extends TestCase /** * @test */ - public function it_does_not_try_to_parse_annotations_when_controller_is_not_an_array() + public function it_does_not_try_to_parse_attributes_when_controller_is_not_an_array() { $breadcrumbBuilder = \Mockery::mock(BreadcrumbBuilder::class); - $annotationReader = \Mockery::mock(Reader::class); - $SUT = new BreadcrumbListener($breadcrumbBuilder, $annotationReader); + $SUT = new BreadcrumbListener($breadcrumbBuilder); $event = new ControllerEvent( \Mockery::spy(HttpKernelInterface::class), - function () {}, + function () {}, // Controller is a callable \Mockery::mock(Request::class), - HttpKernelInterface::MASTER_REQUEST + HttpKernelInterface::MAIN_REQUEST ); - $annotationReader->shouldReceive('getClassAnnotation')->never(); - $annotationReader->shouldReceive('getMethodAnnotation')->never(); $breadcrumbBuilder->shouldReceive('addItem')->never(); $SUT->onKernelController($event); diff --git a/tests/Unit/Service/BreadcrumbItemProcessorTest.php b/tests/Unit/Service/BreadcrumbItemProcessorTest.php index 2a50844..140e739 100644 --- a/tests/Unit/Service/BreadcrumbItemProcessorTest.php +++ b/tests/Unit/Service/BreadcrumbItemProcessorTest.php @@ -6,6 +6,7 @@ use PHPUnit\Framework\TestCase; use SlopeIt\BreadcrumbBundle\Model\BreadcrumbItem; use SlopeIt\BreadcrumbBundle\Service\BreadcrumbItemProcessor; +use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; @@ -58,7 +59,7 @@ protected function setUp(): void $this->requestStack = \Mockery::mock(RequestStack::class); $currentRequest = \Mockery::mock(Request::class); - $currentRequest->attributes = []; + $currentRequest->attributes = new ParameterBag(); $this->requestStack->allows('getCurrentRequest')->andReturn($currentRequest); $this->SUT = new BreadcrumbItemProcessor(