diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3633fe5..10b872c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-versions: [ '8.3', '8.4' ] + php-versions: [ '8.3', '8.4', '8.5' ] steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 8aca7aa..9130fbb 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -1,4 +1,4 @@ -name: CI +name: SonarQube on: push: @@ -41,6 +41,6 @@ jobs: run: make php/tests # To generate the coverage report - name: SonarQube Scan - uses: SonarSource/sonarqube-scan-action@v5.2.0 + uses: SonarSource/sonarqube-scan-action@v7 env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index b0ac8d3..d21eb4c 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -4,7 +4,7 @@ //~ Rules ->setRules( [ - '@PER-CS2.0' => true, + '@PER-CS3x0' => true, ] ) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0c9e26..2892fb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [7.1.0] - 2026-01-05 +[7.1.0]: https://github.com/eureka-framework/component-web/compare/7.0.0...7.1.0 +### Added +- Now support PHP 8.5 +### Changed +- Update CI +- Fix from PHPStan +- Fix code style +- Update dev dependencies + + ## [7.0.0] - 2025-08-15 [7.0.0]: https://github.com/eureka-framework/component-web/compare/6.0.0...7.0.0 ### Added diff --git a/Makefile b/Makefile index 28d874c..8227c3c 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ .PHONY: validate install update php/deps php/check php/fix php/min-compatibility php/max-compatibility php/phpstan php/analyze php/tests php/test php/testdox ci clean +PHP_MIN_VERSION := "8.3" +PHP_MAX_VERSION := "8.5" COMPOSER_BIN := composer -PHP_VERSION_MIN := 8.3 -PHP_VERSION_MAX := 8.4 define header = @if [ -t 1 ]; then printf "\n\e[37m\e[100m \e[104m $(1) \e[0m\n"; else printf "\n### $(1)\n"; fi endef @@ -42,17 +42,17 @@ php/deps: composer.json php/check: vendor/bin/php-cs-fixer $(call header,Checking Code Style) - @./vendor/bin/php-cs-fixer check -v --diff + @XDEBUG_MODE=off ./vendor/bin/php-cs-fixer check php/fix: vendor/bin/php-cs-fixer $(call header,Fixing Code Style) - @./vendor/bin/php-cs-fixer fix -v + @XDEBUG_MODE=off ./vendor/bin/php-cs-fixer fix -v php/min-compatibility: vendor/bin/phpstan build/reports/phpstan - $(call header,Checking PHP ${PHP_VERSION_MIN} compatibility) + $(call header,Checking PHP ${PHP_MIN_VERSION} compatibility) @XDEBUG_MODE=off ./vendor/bin/phpstan analyse --configuration=./ci/phpmin-compatibility.neon --error-format=table php/max-compatibility: vendor/bin/phpstan build/reports/phpstan #ci - $(call header,Checking PHP ${PHP_VERSION_MAX} compatibility) + $(call header,Checking PHP ${PHP_MAX_VERSION} compatibility) @XDEBUG_MODE=off ./vendor/bin/phpstan analyse --configuration=./ci/phpmax-compatibility.neon --error-format=table php/analyze: vendor/bin/phpstan build/reports/phpstan #manual & ci diff --git a/README.md b/README.md index 78a899e..3c35ca7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # component-web [![Current version](https://img.shields.io/packagist/v/eureka/component-web.svg?logo=composer)](https://packagist.org/packages/eureka/component-web) -[![Supported PHP version](https://img.shields.io/static/v1?logo=php&label=PHP&message=8.3%20-%208.4&color=777bb4)](https://packagist.org/packages/eureka/component-web) +[![Supported PHP version](https://img.shields.io/static/v1?logo=php&label=PHP&message=>%3D8.3&color=777bb4)](https://packagist.org/packages/eureka/component-web) ![CI](https://github.com/eureka-framework/component-web/workflows/CI/badge.svg) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=eureka-framework_component-web&metric=alert_status)](https://sonarcloud.io/dashboard?id=eureka-framework_component-web) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=eureka-framework_component-web&metric=coverage)](https://sonarcloud.io/dashboard?id=eureka-framework_component-web) @@ -18,11 +18,10 @@ Provide some utilities to website application: ## Composer ```bash -composer require "eureka/component-password" +composer require "eureka/component-web" ``` - ## Contributing See the [CONTRIBUTING](CONTRIBUTING.md) file. @@ -42,43 +41,65 @@ make update NB: For the components, the `composer.lock` file is not committed. -## Testing & CI (Continuous Integration) +### Testing & CI (Continuous Integration) + +#### Tests +You can run unit tests (with coverage) on your side with following command: +```bash +make php/tests +``` + +You can run integration tests (without coverage) on your side with following command: +```bash +make php/integration +``` -You can run tests on your side with following commands: +For prettier output (but without coverage), you can use the following command: ```bash -make php/tests # run tests with coverage -make php/test # run tests with coverage make php/testdox # run tests without coverage reports but with prettified output ``` -You also can run code style check or code style fixes with following commands: +#### Code Style +You also can run code style check with following commands: +```bash +make php/check +``` + +You also can run code style fixes with following commands: ```bash -make php/check # run checks on check style -make php/fix # run check style auto fix +make php/fix ``` +#### Check for missing explicit dependencies +You can check if any explicit dependency is missing with the following command: +```bash +make php/deps +``` + +#### Static Analysis To perform a static analyze of your code (with phpstan, lvl 9 at default), you can use the following command: ```bash -make php/analyze # Same as phpstan but with CLI output as table +make php/analyse ``` -To ensure you code still compatible with current supported version and futures versions of php, you need to +To ensure you code still compatible with current supported version at Deezer and futures versions of php, you need to run the following commands (both are required for full support): + +Minimal supported version: ```bash -make php/min-compatibility # run compatibility check on current minimal version of php we support -make php/max-compatibility # run compatibility check on last version of php we will support in future +make php/min-compatibility ``` -And the last "helper" commands, you can run before commit and push is: +Maximal supported version: ```bash -make ci +make php/max-compatibility ``` -This command clean the previous reports, install component if needed and run tests (with coverage report), -check the code style and check the php compatibility check, as it would be done in our CI. -## Contributing - -See the [CONTRIBUTING](CONTRIBUTING.md) file. +#### CI Simulation +And the last "helper" commands, you can run before commit and push, is: +```bash +make ci +``` ## License diff --git a/ci/phpmax-compatibility.neon b/ci/phpmax-compatibility.neon index 2c7cc0b..085b5a0 100644 --- a/ci/phpmax-compatibility.neon +++ b/ci/phpmax-compatibility.neon @@ -3,7 +3,7 @@ includes: - ./../vendor/phpstan/phpstan-phpunit/rules.neon parameters: - phpVersion: 80400 + phpVersion: 80500 level: 0 paths: - ./../src diff --git a/composer.json b/composer.json index d953adf..c75f6d7 100644 --- a/composer.json +++ b/composer.json @@ -29,20 +29,21 @@ }, "require": { - "php": "8.3.*||8.4.*", + "php": ">=8.3", "ext-mbstring": "*", "psr/http-message": "^1.0||^2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.86.0", - "phpstan/phpstan": "^2.1.22", - "phpstan/phpstan-phpunit": "^2.0.7", - "phpstan/phpstan-strict-rules": "^2.0.6", - "phpunit/phpcov": "^11.0.1", - "phpunit/phpunit": "^12.3.4", - "shipmonk/composer-dependency-analyser": "^1.8.3" + "friendsofphp/php-cs-fixer": "^3.92.4", + "phpstan/phpstan": "^2.1.33", + "phpstan/phpstan-deprecation-rules": "^2.0.3", + "phpstan/phpstan-phpunit": "^2.0.11", + "phpstan/phpstan-strict-rules": "^2.0.7", + "phpunit/phpcov": "^11.0.3", + "phpunit/phpunit": "^12.5.4", + "shipmonk/composer-dependency-analyser": "^1.8.4" } } diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 020ab94..d1b610a 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,7 +1,9 @@ includes: - ./vendor/phpstan/phpstan-phpunit/extension.neon - ./vendor/phpstan/phpstan-phpunit/rules.neon + - ./vendor/phpstan/phpstan-deprecation-rules/rules.neon - ./vendor/phpstan/phpstan-strict-rules/rules.neon + - phar://phpstan.phar/conf/bleedingEdge.neon parameters: phpVersion: 80300 diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 12fdea8..ec4e1f6 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,23 +1,11 @@ - - - - + cacheDirectory="build/.phpunit.cache" +> diff --git a/src/Collection/AbstractCollection.php b/src/Collection/AbstractCollection.php index b098c05..317542e 100644 --- a/src/Collection/AbstractCollection.php +++ b/src/Collection/AbstractCollection.php @@ -76,6 +76,7 @@ public function count(): int protected function pushItem(BreadcrumbItem|CarouselItem|MenuItem|NotificationInterface $item): self { + /** @var string $itemName */ $itemName = \method_exists($item, 'getName') ? $item->getName() : (string) $this->count(); $this->collection[$this->count] = $item; $this->names[$itemName] = $this->count; diff --git a/src/Session/SessionAwareTrait.php b/src/Session/SessionAwareTrait.php index 15b1906..c703294 100644 --- a/src/Session/SessionAwareTrait.php +++ b/src/Session/SessionAwareTrait.php @@ -15,14 +15,14 @@ trait SessionAwareTrait { - protected Session|null $session = null; + protected ?Session $session = null; public function setSession(Session $session): void { $this->session = $session; } - protected function getSession(): Session|null + protected function getSession(): ?Session { return $this->session; } diff --git a/tests/Unit/BreadcrumbTest.php b/tests/Unit/BreadcrumbTest.php index a7fed47..afcefb2 100644 --- a/tests/Unit/BreadcrumbTest.php +++ b/tests/Unit/BreadcrumbTest.php @@ -25,7 +25,7 @@ public function testICanPushAndPopItemToBreadcrumb(): void $item = $breadcrumb->pop(); - self::assertEquals('item 2', $item->getName()); + self::assertSame('item 2', $item->getName()); } public function testICanIterateOnBreadcrumbInstance(): void @@ -36,7 +36,7 @@ public function testICanIterateOnBreadcrumbInstance(): void foreach ($breadcrumb as $index => $item) { /** @var BreadcrumbItem $item */ - self::assertEquals('item ' . ($index + 1), $item->getName()); + self::assertSame('item ' . ($index + 1), $item->getName()); } } diff --git a/tests/Unit/CarouselTest.php b/tests/Unit/CarouselTest.php index be6220d..af26c13 100644 --- a/tests/Unit/CarouselTest.php +++ b/tests/Unit/CarouselTest.php @@ -25,7 +25,7 @@ public function testICanPushAndPopItemToCarousel(): void $item = $carousel->pop(); - self::assertEquals('item 2', $item->getTitle()); + self::assertSame('item 2', $item->getTitle()); } public function testICanIterateOnCarouselInstance(): void @@ -36,7 +36,7 @@ public function testICanIterateOnCarouselInstance(): void foreach ($carousel as $index => $item) { /** @var CarouselItem $item */ - self::assertEquals('item ' . ($index + 1), $item->getTitle()); + self::assertSame('item ' . ($index + 1), $item->getTitle()); } } diff --git a/tests/Unit/MenuTest.php b/tests/Unit/MenuTest.php index 0b793fc..8440fa6 100644 --- a/tests/Unit/MenuTest.php +++ b/tests/Unit/MenuTest.php @@ -25,7 +25,7 @@ public function testICanPushAndGetItemToMenu(): void $item = $menu->get('item 2'); - self::assertEquals('item 2', $item?->getName()); + self::assertSame('item 2', $item?->getName()); } public function testICanIterateOnMenuInstance(): void @@ -36,7 +36,7 @@ public function testICanIterateOnMenuInstance(): void foreach ($menu as $index => $item) { /** @var MenuItem $item */ - self::assertEquals('item ' . ($index + 1), $item->getName()); + self::assertSame('item ' . ($index + 1), $item->getName()); } } diff --git a/tests/Unit/MockControllerTest.php b/tests/Unit/MockControllerTest.php index 6ef9304..d4e0d27 100644 --- a/tests/Unit/MockControllerTest.php +++ b/tests/Unit/MockControllerTest.php @@ -79,9 +79,9 @@ public function testICanSetAndRetrieveMetaConfig(): void /** @var array{copyright: array{year?: string, name?: string}, title?: string} $meta */ $meta = $this->getMeta(); - self::assertEquals(date('Y'), $meta['copyright']['year'] ?? ''); - self::assertEquals('me', $meta['copyright']['name'] ?? ''); - self::assertEquals('test', $meta['title'] ?? ''); + self::assertSame(date('Y'), $meta['copyright']['year'] ?? ''); + self::assertSame('me', $meta['copyright']['name'] ?? ''); + self::assertSame('test', $meta['title'] ?? ''); } public function testICanSetMenuConfigAndGetPartialMenuCollectionWhenIAmNotLogged(): void @@ -108,57 +108,57 @@ public function testICanSetMenuConfigAndGetCompleteMenuCollectionWhenIAmLogged() $menu = $this->getMenu(true, true); $item1 = $menu->get('Home') ?? new MenuItem('void'); - self::assertEquals('Home', $item1->getName()); - self::assertEquals('', $item1->getIcon()); - self::assertEquals('/home', $item1->getUri()); + self::assertSame('Home', $item1->getName()); + self::assertSame('', $item1->getIcon()); + self::assertSame('/home', $item1->getUri()); self::assertFalse($item1->hasSubmenu()); self::assertFalse($item1->hasIcon()); self::assertFalse($item1->isActive()); $item2 = $menu->get('Test') ?? new MenuItem('void'); - self::assertEquals('Test', $item2->getName()); - self::assertEquals('ico', $item2->getIcon()); - self::assertEquals('javascript:void(0);', $item2->getUri()); + self::assertSame('Test', $item2->getName()); + self::assertSame('ico', $item2->getIcon()); + self::assertSame('javascript:void(0);', $item2->getUri()); self::assertTrue($item2->hasSubmenu()); self::assertTrue($item2->hasIcon()); self::assertTrue($item2->isActive()); $item3 = $menu->get('Logged') ?? new MenuItem('void'); - self::assertEquals('Logged', $item3->getName()); - self::assertEquals('', $item3->getIcon()); - self::assertEquals('/home', $item3->getUri()); + self::assertSame('Logged', $item3->getName()); + self::assertSame('', $item3->getIcon()); + self::assertSame('/home', $item3->getUri()); self::assertFalse($item3->hasSubmenu()); self::assertFalse($item3->hasIcon()); self::assertFalse($item3->isActive()); $subItem1 = $item2->getSubmenu()?->get('Sub Test 1') ?? new MenuItem('void'); - self::assertEquals('Sub Test 1', $subItem1->getName()); - self::assertEquals('', $subItem1->getIcon()); - self::assertEquals('javascript:void(0);', $subItem1->getUri()); + self::assertSame('Sub Test 1', $subItem1->getName()); + self::assertSame('', $subItem1->getIcon()); + self::assertSame('javascript:void(0);', $subItem1->getUri()); self::assertFalse($subItem1->hasSubmenu()); self::assertFalse($subItem1->hasIcon()); self::assertFalse($subItem1->isActive()); $subItem2 = $item2->getSubmenu()?->get('Sub Test 2') ?? new MenuItem('void'); - self::assertEquals('Sub Test 2', $subItem2->getName()); - self::assertEquals('', $subItem2->getIcon()); - self::assertEquals('http://external.com', $subItem2->getUri()); + self::assertSame('Sub Test 2', $subItem2->getName()); + self::assertSame('', $subItem2->getIcon()); + self::assertSame('http://external.com', $subItem2->getUri()); self::assertFalse($subItem2->hasSubmenu()); self::assertFalse($subItem2->hasIcon()); self::assertFalse($subItem2->isActive()); $subItem3 = $item2->getSubmenu()?->get('Sub Test 3') ?? new MenuItem('void'); - self::assertEquals('Sub Test 3', $subItem3->getName()); - self::assertEquals('', $subItem3->getIcon()); - self::assertEquals('/home', $subItem3->getUri()); + self::assertSame('Sub Test 3', $subItem3->getName()); + self::assertSame('', $subItem3->getIcon()); + self::assertSame('/home', $subItem3->getUri()); self::assertFalse($subItem3->hasSubmenu()); self::assertFalse($subItem3->hasIcon()); self::assertTrue($subItem3->isActive()); $subItem4 = $item2->getSubmenu()?->get('Sub Test 4') ?? new MenuItem('void'); - self::assertEquals('Sub Test 4', $subItem4->getName()); - self::assertEquals('', $subItem4->getIcon()); - self::assertEquals('/home', $subItem4->getUri()); + self::assertSame('Sub Test 4', $subItem4->getName()); + self::assertSame('', $subItem4->getIcon()); + self::assertSame('/home', $subItem4->getUri()); self::assertFalse($subItem4->hasSubmenu()); self::assertFalse($subItem4->hasIcon()); self::assertFalse($subItem4->isActive()); diff --git a/tests/Unit/NotificationTest.php b/tests/Unit/NotificationTest.php index 278db65..3029ea3 100644 --- a/tests/Unit/NotificationTest.php +++ b/tests/Unit/NotificationTest.php @@ -28,7 +28,7 @@ public function testICanPushAndGetItemToNotification(): void $item = $notifications->pop(); - self::assertEquals('item 2', $item->getMessage()); + self::assertSame('item 2', $item->getMessage()); } public function testICanIterateOnNotificationInstance(): void @@ -39,7 +39,7 @@ public function testICanIterateOnNotificationInstance(): void /** @var NotificationInterface $item */ foreach ($notifications as $index => $item) { - self::assertEquals('item ' . ($index + 1), $item->getMessage()); + self::assertSame('item ' . ($index + 1), $item->getMessage()); } } diff --git a/tests/Unit/SessionTest.php b/tests/Unit/SessionTest.php index 8caedfd..1bac7e4 100644 --- a/tests/Unit/SessionTest.php +++ b/tests/Unit/SessionTest.php @@ -24,8 +24,8 @@ public function testICanSetAndRetrieveValueInSession(): void $session->set('foo', 'bar'); self::assertTrue($session->has('foo')); - self::assertEquals('bar', $session->get('foo')); - self::assertEquals('baz', $session->get('foz', 'baz')); + self::assertSame('bar', $session->get('foo')); + self::assertSame('baz', $session->get('foz', 'baz')); $session->remove('foo'); self::assertFalse($session->has('foo')); @@ -39,8 +39,8 @@ public function testICanSetAndRetrieveEphemeralValueInSession(): void $session->setFlash('foo', 'bar'); self::assertTrue($session->hasFlash('foo')); - self::assertEquals('bar', $session->getFlash('foo')); - self::assertEquals('baz', $session->getFlash('foz', 'baz')); + self::assertSame('bar', $session->getFlash('foo')); + self::assertSame('baz', $session->getFlash('foz', 'baz')); $session->clearFlash(); $session->clearFlash();