Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrading Skeleton to PHP 8.4 #345

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ jobs:
strategy:
fail-fast: false
matrix:
php: [7.4, 8.0, 8.1, 8.2]
php: [8.2, 8.3, 8.4]
experimental: [false]
JimTools marked this conversation as resolved.
Show resolved Hide resolved
include:
- php: 8.1
- php: 8.3
analysis: true

steps:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This skeleton application was built for Composer. This makes setting up a new Sl

## Install the Application

Run this command from the directory in which you want to install your new Slim Framework application. You will require PHP 7.4 or newer.
Run this command from the directory in which you want to install your new Slim Framework application. You will require PHP 8.3 or newer.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minimum is 8.2


```bash
composer create-project slim/slim-skeleton [my-app-name]
Expand Down
3 changes: 2 additions & 1 deletion app/settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use App\Application\Settings\Settings;
use App\Application\Settings\SettingsInterface;
use DI\ContainerBuilder;
use Monolog\Level;
use Monolog\Logger;

return function (ContainerBuilder $containerBuilder) {
Expand All @@ -19,7 +20,7 @@
'logger' => [
'name' => 'slim-app',
'path' => isset($_ENV['docker']) ? 'php://stdout' : __DIR__ . '/../logs/app.log',
'level' => Logger::DEBUG,
'level' => Level::fromName('DEBUG'),
Copy link

@odan odan Jan 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method call produces overhead. Instead we should use the generic PSR-4 LogLevel instead. Example: \Psr\Log\LogLevel::DEBUG.

https://github.com/php-fig/log/blob/master/src/LogLevel.php

],
]);
}
Expand Down
16 changes: 8 additions & 8 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,20 @@
}
],
"require": {
"php": "^7.4 || ^8.0",
"php": "^8.2.0 || ^8.3.0 || ^8.4.0",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also covers higher minor versions then 8.4, which should not be the case. Instead define only a list of 8.2.* || 8.3.* || 8.4.*.

"ext-json": "*",
"monolog/monolog": "^2.9",
"php-di/php-di": "^6.4",
"monolog/monolog": "^3.8",
"php-di/php-di": "^7.0",
"slim/psr7": "^1.6",
"slim/slim": "^4.12"
},
"require-dev": {
"jangregor/phpstan-prophecy": "^1.0.0",
"phpspec/prophecy-phpunit": "^2.2",
"jangregor/phpstan-prophecy": "^2.0",
"phpspec/prophecy-phpunit": "^2.3",
"phpstan/extension-installer": "^1.3.1",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.6.17",
"squizlabs/php_codesniffer": "^3.9"
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.5",
"squizlabs/php_codesniffer": "^3.11"
},
"config": {
"allow-plugins": {
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ volumes:

services:
slim:
image: php:8-alpine
image: php:8.4-alpine
working_dir: /var/www
command: php -S 0.0.0.0:8080 -t public
environment:
Expand Down
38 changes: 12 additions & 26 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -1,27 +1,13 @@
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/7.1/phpunit.xsd"
backupGlobals="false"
backupStaticAttributes="false"
beStrictAboutTestsThatDoNotTestAnything="true"
beStrictAboutChangesToGlobalState="true"
beStrictAboutOutputDuringTests="true"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
bootstrap="tests/bootstrap.php"
>
<testsuites>
<testsuite name="Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./src/</directory>
</whitelist>
</filter>
<?xml version="1.0"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.5/phpunit.xsd" beStrictAboutChangesToGlobalState="true" beStrictAboutOutputDuringTests="true" colors="true">
<testsuites>
<testsuite name="Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory>./src/</directory>
</include>
</source>
</phpunit>
2 changes: 1 addition & 1 deletion public/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
$containerBuilder = new ContainerBuilder();

if (false) { // Should be set to true in production
$containerBuilder->enableCompilation(__DIR__ . '/../var/cache');
$containerBuilder->enableCompilation(__DIR__ . '/../var/cache');
}

// Set up settings
Expand Down
20 changes: 7 additions & 13 deletions src/Application/Actions/Action.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@

abstract class Action
{
protected LoggerInterface $logger;

protected Request $request;

protected Response $response;

/**
* @var array<string, mixed>
*/
protected array $args;

public function __construct(LoggerInterface $logger)
public function __construct(protected LoggerInterface $logger)
{
$this->logger = $logger;
}

/**
Expand All @@ -49,10 +49,7 @@ public function __invoke(Request $request, Response $response, array $args): Res
*/
abstract protected function action(): Response;

/**
* @return array|object
*/
protected function getFormData()
protected function getFormData(): array|object|null
{
return $this->request->getParsedBody();
}
Expand All @@ -70,10 +67,7 @@ protected function resolveArg(string $name)
return $this->args[$name];
}

/**
* @param array|object|null $data
*/
protected function respondWithData($data = null, int $statusCode = 200): Response
protected function respondWithData(array|object|null $data = null, int $statusCode = 200): Response
{
$payload = new ActionPayload($statusCode, $data);

Expand All @@ -87,6 +81,6 @@ protected function respond(ActionPayload $payload): Response

return $this->response
->withHeader('Content-Type', 'application/json')
->withStatus($payload->getStatusCode());
->withStatus($payload->statusCode);
}
}
34 changes: 4 additions & 30 deletions src/Application/Actions/ActionError.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,10 @@ class ActionError implements JsonSerializable
public const VALIDATION_ERROR = 'VALIDATION_ERROR';
public const VERIFICATION_ERROR = 'VERIFICATION_ERROR';

private string $type;

private ?string $description;

public function __construct(string $type, ?string $description = null)
{
$this->type = $type;
$this->description = $description;
}

public function getType(): string
{
return $this->type;
}

public function setType(string $type): self
{
$this->type = $type;
return $this;
}

public function getDescription(): ?string
{
return $this->description;
}

public function setDescription(?string $description = null): self
{
$this->description = $description;
return $this;
public function __construct(
public readonly string $type,
public readonly ?string $description = null
) {
}

#[\ReturnTypeWillChange]
Expand Down
36 changes: 3 additions & 33 deletions src/Application/Actions/ActionPayload.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,11 @@

class ActionPayload implements JsonSerializable
{
private int $statusCode;

/**
* @var array|object|null
*/
private $data;

private ?ActionError $error;

public function __construct(
int $statusCode = 200,
$data = null,
?ActionError $error = null
public readonly int $statusCode = 200,
public readonly array|object|null $data = null,
public ?ActionError $error = null
) {
$this->statusCode = $statusCode;
$this->data = $data;
$this->error = $error;
}

public function getStatusCode(): int
{
return $this->statusCode;
}

/**
* @return array|null|object
*/
public function getData()
{
return $this->data;
}

public function getError(): ?ActionError
{
return $this->error;
}

#[\ReturnTypeWillChange]
Expand Down
9 changes: 4 additions & 5 deletions src/Application/Actions/User/UserAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@

abstract class UserAction extends Action
{
protected UserRepository $userRepository;

public function __construct(LoggerInterface $logger, UserRepository $userRepository)
{
public function __construct(
LoggerInterface $logger,
protected UserRepository $userRepository
) {
parent::__construct($logger);
$this->userRepository = $userRepository;
}
}
24 changes: 11 additions & 13 deletions src/Application/Handlers/HttpErrorHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,38 +26,36 @@ protected function respond(): Response
{
$exception = $this->exception;
$statusCode = 500;
$error = new ActionError(
ActionError::SERVER_ERROR,
'An internal error has occurred while processing your request.'
);
$errorType = ActionError::SERVER_ERROR;
$description = 'An internal error has occurred while processing your request.';

if ($exception instanceof HttpException) {
$statusCode = $exception->getCode();
$error->setDescription($exception->getMessage());
$description = $exception->getMessage();

if ($exception instanceof HttpNotFoundException) {
$error->setType(ActionError::RESOURCE_NOT_FOUND);
$errorType = ActionError::RESOURCE_NOT_FOUND;
} elseif ($exception instanceof HttpMethodNotAllowedException) {
$error->setType(ActionError::NOT_ALLOWED);
$errorType = ActionError::NOT_ALLOWED;
} elseif ($exception instanceof HttpUnauthorizedException) {
$error->setType(ActionError::UNAUTHENTICATED);
$errorType = ActionError::UNAUTHENTICATED;
} elseif ($exception instanceof HttpForbiddenException) {
$error->setType(ActionError::INSUFFICIENT_PRIVILEGES);
$errorType = ActionError::INSUFFICIENT_PRIVILEGES;
} elseif ($exception instanceof HttpBadRequestException) {
$error->setType(ActionError::BAD_REQUEST);
$errorType = ActionError::BAD_REQUEST;
} elseif ($exception instanceof HttpNotImplementedException) {
$error->setType(ActionError::NOT_IMPLEMENTED);
$errorType = ActionError::NOT_IMPLEMENTED;
}
}

if (
!($exception instanceof HttpException)
&& $exception instanceof Throwable
&& $this->displayErrorDetails
) {
$error->setDescription($exception->getMessage());
$description = $exception->getMessage();
}

$error = new ActionError($errorType, $description);
$payload = new ActionPayload($statusCode, null, $error);
$encodedPayload = json_encode($payload, JSON_PRETTY_PRINT);

Expand Down
46 changes: 16 additions & 30 deletions src/Application/Handlers/ShutdownHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,11 @@

class ShutdownHandler
{
private Request $request;

private HttpErrorHandler $errorHandler;

private bool $displayErrorDetails;

public function __construct(
Request $request,
HttpErrorHandler $errorHandler,
bool $displayErrorDetails
private Request $request,
private HttpErrorHandler $errorHandler,
private bool $displayErrorDetails
) {
$this->request = $request;
$this->errorHandler = $errorHandler;
$this->displayErrorDetails = $displayErrorDetails;
}

public function __invoke()
Expand All @@ -47,29 +38,24 @@ public function __invoke()
$responseEmitter->emit($response);
}

private function getErrorMessage(array $error): string
/**
* @param array{type: int, message: string, file: string, line: int}|null$error
*/
private function getErrorMessage(?array $error = null): string
{
if (!$this->displayErrorDetails) {
return 'An error while processing your request. Please try again later.';
}

$errorFile = $error['file'];
$errorLine = $error['line'];
$errorMessage = $error['message'];
$errorType = $error['type'];

if ($errorType === E_USER_ERROR) {
return "FATAL ERROR: {$errorMessage}. on line {$errorLine} in file {$errorFile}.";
}

if ($errorType === E_USER_WARNING) {
return "WARNING: {$errorMessage}";
}

if ($errorType === E_USER_NOTICE) {
return "NOTICE: {$errorMessage}";
}
$errorFile = $error['file'] ?? null;
$errorLine = $error['line'] ?? null;
$errorMessage = $error['message'] ?? null;
$errorType = $error['type'] ?? null;

return "FATAL ERROR: {$errorMessage}. on line {$errorLine} in file {$errorFile}.";
return match ($errorType) {
E_USER_WARNING => "WARNING: {$errorMessage}",
E_USER_NOTICE => "NOTICE: {$errorMessage}",
default => "FATAL ERROR: {$errorMessage}. on line {$errorLine} in file {$errorFile}."
};
}
}
Loading