diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4f08560..7541cfc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,9 +11,10 @@ jobs: strategy: matrix: os: - - ubuntu-22.04 + - ubuntu-24.04 - windows-2022 php: + - 8.4 - 8.3 - 8.2 - 8.1 @@ -43,10 +44,11 @@ jobs: PHPStan: name: PHPStan (PHP ${{ matrix.php }}) - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: matrix: php: + - 8.4 - 8.3 - 8.2 - 8.1 @@ -66,10 +68,11 @@ jobs: Built-in-webserver: name: Built-in webserver (PHP ${{ matrix.php }}) - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: matrix: php: + - 8.4 - 8.3 - 8.2 - 8.1 @@ -90,7 +93,7 @@ jobs: Docker: name: Docker (${{ matrix.dockerfile }}) - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: matrix: dockerfile: @@ -100,7 +103,7 @@ jobs: - uses: actions/checkout@v4 - uses: shivammathur/setup-php@v2 with: - php-version: 8.3 + php-version: 8.4 - run: composer install -d tests/integration/ - run: docker build -f tests/integration/${{ matrix.dockerfile }} tests/integration/ - run: docker run -d -p 8080:8080 -v "$PWD/composer.json":/app/composer.json $(docker images -q | head -n1) @@ -112,7 +115,7 @@ jobs: nginx-reverse-proxy: name: nginx (${{ matrix.config.name }}) - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: matrix: config: @@ -124,7 +127,7 @@ jobs: - uses: actions/checkout@v4 - uses: shivammathur/setup-php@v2 with: - php-version: 8.3 + php-version: 8.4 - run: composer install -d tests/integration/ - run: docker build -f tests/integration/Dockerfile-basics tests/integration/ - run: docker run -d -p 8080:8080 -v "$PWD/composer.json":/app/composer.json $(docker images -q | head -n1) @@ -139,10 +142,11 @@ jobs: nginx-fpm: name: nginx + PHP-FPM (PHP ${{ matrix.php }}) - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: matrix: php: + - 8.4 - 8.3 - 8.2 - 8.1 @@ -166,10 +170,11 @@ jobs: Apache-webserver: name: Apache webserver (PHP ${{ matrix.php }}) - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: matrix: php: + - 8.4 - 8.3 - 8.2 - 8.1 @@ -192,10 +197,11 @@ jobs: PHP-webserver: name: PHP webserver (PHP ${{ matrix.php }}) - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: matrix: php: + - 8.4 - 8.3 - 8.2 - 8.1 diff --git a/composer.json b/composer.json index d444a4d..51e2dbf 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ "react/socket": "^1.15" }, "require-dev": { - "phpstan/phpstan": "1.10.47 || 1.4.10", + "phpstan/phpstan": "1.12.11 || 1.4.10", "phpunit/phpunit": "^9.6 || ^7.5", "psr/container": "^2 || ^1", "react/promise-timer": "^1.11" diff --git a/docs/best-practices/deployment.md b/docs/best-practices/deployment.md index 62ca3a1..5dc2680 100644 --- a/docs/best-practices/deployment.md +++ b/docs/best-practices/deployment.md @@ -229,7 +229,7 @@ If you're not already running an Apache server, you can run your X project with Apache in a temporary Docker container like this: ```bash -$ docker run -it --rm -p 80:80 -v "$PWD":/srv php:8.3-apache sh -c "rmdir /var/www/html;ln -s /srv/public /var/www/html;ln -s /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled; apache2-foreground" +$ docker run -it --rm -p 80:80 -v "$PWD":/srv php:8.4-apache sh -c "rmdir /var/www/html;ln -s /srv/public /var/www/html;ln -s /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled; apache2-foreground" ``` In order to check your web application responds as expected, you can use your @@ -313,10 +313,10 @@ limit to match your concurrency workload. On Ubuntu- or Debian-based systems, you may change your PHP configuration like this: ```bash -$ sudoedit /etc/php/8.3/cli/php.ini +$ sudoedit /etc/php/8.4/cli/php.ini ``` -```diff title="/etc/php/8.3/cli/php.ini" +```diff title="/etc/php/8.4/cli/php.ini" - memory_limit = 128M + memory_limit = -1 ``` @@ -552,7 +552,7 @@ be achieved by using a `Dockerfile` with the following contents: ```docker title="Dockerfile" # syntax=docker/dockerfile:1 - FROM php:8.3-cli + FROM php:8.4-cli WORKDIR /app/ COPY public/ public/ @@ -574,7 +574,7 @@ be achieved by using a `Dockerfile` with the following contents: COPY composer.json composer.lock ./ RUN composer install --no-dev --ignore-platform-reqs --optimize-autoloader - FROM php:8.3-alpine + FROM php:8.4-alpine # recommended: install optional extensions ext-ev and ext-sockets RUN apk --no-cache add ${PHPIZE_DEPS} libev linux-headers \ diff --git a/tests/AppTest.php b/tests/AppTest.php index a664012..ab7a4ae 100644 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -1437,9 +1437,11 @@ public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhe { $app = $this->createAppWithoutLogger(); - $line = __LINE__ + 3; + // expect error on next line (should yield PromiseInterface) + // return on same line because PHP < 8.4 reports error on statement *after* invalid yield + $line = __LINE__ + 2; $app->get('/users', function () { - yield null; + return yield null; }); $request = new ServerRequest('GET', 'http://localhost/users'); diff --git a/tests/ContainerTest.php b/tests/ContainerTest.php index 93829d7..a3f7899 100644 --- a/tests/ContainerTest.php +++ b/tests/ContainerTest.php @@ -1284,7 +1284,7 @@ public function __construct(\stdClass $data) $callable = $container->callable(get_class($controller)); $this->expectException(\BadMethodCallException::class); - $this->expectExceptionMessage('Argument 1 ($username) of {closure}() is not defined'); + $this->expectExceptionMessageMatches('/Argument 1 \(\$username\) of {closure(:[^{}]+)?}\(\) is not defined$/'); $callable($request); } @@ -1770,7 +1770,7 @@ public function testCallableReturnsCallableThatThrowsWhenFactoryRequiresUntypedA $callable = $container->callable(\stdClass::class); $this->expectException(\BadMethodCallException::class); - $this->expectExceptionMessage('Argument 1 ($undefined) of {closure}() has no type'); + $this->expectExceptionMessageMatches('/Argument 1 \(\$undefined\) of {closure(:[^{}]+)?}\(\) has no type$/'); $callable($request); } @@ -1788,7 +1788,7 @@ public function testCallableReturnsCallableThatThrowsWhenFactoryRequiresUndefine $callable = $container->callable(\stdClass::class); $this->expectException(\BadMethodCallException::class); - $this->expectExceptionMessage('Argument 1 ($undefined) of {closure}() is not defined'); + $this->expectExceptionMessageMatches('/Argument 1 \(\$undefined\) of {closure(:[^{}]+)?}\(\) is not defined$/'); $callable($request); } @@ -1803,7 +1803,7 @@ public function testCallableReturnsCallableThatThrowsWhenFactoryRequiresRecursiv $callable = $container->callable(\stdClass::class); $this->expectException(\BadMethodCallException::class); - $this->expectExceptionMessage('Argument 1 ($data) of {closure}() is recursive'); + $this->expectExceptionMessageMatches('/Argument 1 \(\$data\) of {closure(:[^{}]+)?}\(\) is recursive$/'); $callable($request); } @@ -2233,4 +2233,16 @@ public function testCtorWithInvalidValueThrows(): void $this->expectExceptionMessage('Argument #1 ($loader) must be of type array|Psr\Container\ContainerInterface, stdClass given'); new Container((object) []); // @phpstan-ignore-line } + + public function expectExceptionMessageMatches(string $regularExpression): void + { + if (method_exists(parent::class, 'expectExceptionMessageMatches')) { + // @phpstan-ignore-next-line PHPUnit 8.4+ + parent::expectExceptionMessageMatches($regularExpression); + } else { + // legacy PHPUnit + assert(method_exists($this, 'expectExceptionMessageRegExp')); + $this->expectExceptionMessageRegExp($regularExpression); + } + } } diff --git a/tests/Io/LogStreamHandlerTest.php b/tests/Io/LogStreamHandlerTest.php index f4a77ad..2e8248a 100644 --- a/tests/Io/LogStreamHandlerTest.php +++ b/tests/Io/LogStreamHandlerTest.php @@ -257,6 +257,7 @@ public function testLogWithPathToExistingFileWillAppendLogMessageWithCurrentDate assert(is_resource($stream)); fwrite($stream, 'First' . PHP_EOL); + /** @var array{uri:string} */ $meta = stream_get_meta_data($stream); assert(is_string($meta['uri'])); diff --git a/tests/integration/Dockerfile-basics b/tests/integration/Dockerfile-basics index 270aa38..b54d9cf 100644 --- a/tests/integration/Dockerfile-basics +++ b/tests/integration/Dockerfile-basics @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1 -FROM php:8.3-cli +FROM php:8.4-cli WORKDIR /app/ COPY public/ public/ diff --git a/tests/integration/Dockerfile-production b/tests/integration/Dockerfile-production index ccf3b48..71211d0 100644 --- a/tests/integration/Dockerfile-production +++ b/tests/integration/Dockerfile-production @@ -8,7 +8,7 @@ COPY composer.json composer.lock ./ # dev environment already has dependencies installed: COPY vendor/ vendor/ -FROM php:8.3-alpine +FROM php:8.4-alpine # recommended: install optional extensions ext-ev and ext-sockets RUN apk --no-cache add ${PHPIZE_DEPS} libev linux-headers \