Skip to content
Open
Show file tree
Hide file tree
Changes from 7 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.3]
experimental: [false]
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
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
}
],
"require": {
"php": "^7.4 || ^8.0",
"php": "^8.3",
"ext-json": "*",
"monolog/monolog": "^2.9",
"php-di/php-di": "^6.4",
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;
}
}
23 changes: 11 additions & 12 deletions src/Application/Handlers/HttpErrorHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,25 @@ 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;
}
}

Expand All @@ -55,9 +53,10 @@ protected function respond(): Response
&& $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
38 changes: 12 additions & 26 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,7 +38,10 @@ public function __invoke()
$responseEmitter->emit($response);
}

private function getErrorMessage(array $error): string
/**
* @param array{type: int, message: string, file: string, line: int} $error
*/
private function getErrorMessage(array $error = null): string
{
if (!$this->displayErrorDetails) {
return 'An error while processing your request. Please try again later.';
Expand All @@ -58,18 +52,10 @@ private function getErrorMessage(array $error): string
$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}";
}

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}."
};
}
}
14 changes: 6 additions & 8 deletions src/Application/Settings/Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,16 @@

class Settings implements SettingsInterface
{
private array $settings;

public function __construct(array $settings)
/**
* @param array<string, mixed> $settings
*/
public function __construct(private array $settings)
{
$this->settings = $settings;
}

/**
* @return mixed
*/
public function get(string $key = '')
public function get(string $key = ''): mixed
{
return (empty($key)) ? $this->settings : $this->settings[$key];
return empty($key) ? $this->settings : $this->settings[$key];
}
}
6 changes: 1 addition & 5 deletions src/Application/Settings/SettingsInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,5 @@

interface SettingsInterface
{
/**
* @param string $key
* @return mixed
*/
public function get(string $key = '');
public function get(string $key = ''): mixed;
}
11 changes: 6 additions & 5 deletions src/Domain/User/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,18 @@

class User implements JsonSerializable
{
private ?int $id;

private string $username;

private string $firstName;

private string $lastName;

public function __construct(?int $id, string $username, string $firstName, string $lastName)
{
$this->id = $id;
public function __construct(
private int|null $id,
Copy link

Choose a reason for hiding this comment

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

We should keep property definitions separate from constructor parameter declarations. Mixing them makes the code less clean and harder to follow. Please re-apply this approach for all changed classes to keep things consistent and readable.

string $username,
string $firstName,
string $lastName
) {
$this->username = strtolower($username);
$this->firstName = ucfirst($firstName);
$this->lastName = ucfirst($lastName);
Expand Down