Skip to content

Commit

Permalink
Add v5
Browse files Browse the repository at this point in the history
  • Loading branch information
odan authored Feb 1, 2025
1 parent eab0f5a commit 89b8de3
Show file tree
Hide file tree
Showing 22 changed files with 615 additions and 804 deletions.
10 changes: 5 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `ExceptionLoggingMiddleware` for custom error logging.
- `ExceptionHandlingMiddleware` delegates exceptions to a custom error handler.
- `ErrorHandlingMiddleware` converts errors into `ErrorException` instances that can then be handled by the `ExceptionHandlingMiddleware` and `ExceptionLoggingMiddleware`.
- New custom error handlers using a new `ExceptionHandlerInterface`. See new `ExceptionHandlingMiddleware`.
- New `JsonExceptionRenderer` generates a JSON problem details (rfc7807) response
- New `XmlExceptionRenderer` generates a XML problem details (rfc7807) response
- New custom error handlers using the new `ExceptionLoggingMiddleware` middleware.
- New `JsonExceptionRenderer` generates JSON error response.
- New `XmlExceptionRenderer` generates XML error response.
- New `BasePathMiddleware` for dealing with Apache subdirectories.
- New `HeadMethodMiddleware` ensures that the response body is empty for HEAD requests.
- New `JsonRenderer` utility class for rendering JSON responses.
Expand All @@ -33,11 +33,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- New `CorsMiddleware` for handling CORS requests.
- Support to build a custom middleware pipeline without the Slim App class. See new `ResponseFactoryMiddleware`
- New media type detector
- New Config class and ConfigInterface
- New ContainerFactoryInterface and PhpDiContainerFactory class

### Changed

* Require PHP 8.2 or 8.3. News versions will be supported after a review and test process.
* Require PHP 8.2 or newer. News versions will be supported after a review and test process.
* Migrated all tests to PHPUnit 11
* Update GitHub action and build settings
* Improve DI container integration. Make the DI container a first-class citizen. Require a PSR-11 package.
Expand Down
101 changes: 53 additions & 48 deletions Slim/Builder/AppBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@

namespace Slim\Builder;

use DI\Container;
use Psr\Container\ContainerInterface;
use RuntimeException;
use Slim\App;
use Slim\Container\DefaultDefinitions;
use Slim\Container\HttpDefinitions;
use Slim\Container\PhpDiContainerFactory;
use Slim\Interfaces\ContainerFactoryInterface;

/**
* This class is responsible for building and configuring a Slim application with a dependency injection (DI) container.
Expand All @@ -24,8 +25,6 @@
* Key functionalities include:
* - Building the Slim `App` instance with configured dependencies.
* - Customizing the DI container with user-defined service definitions or a custom container factory.
* - Setting up middleware in a specified order.
* - Configuring application settings.
*/
final class AppBuilder
{
Expand All @@ -35,9 +34,9 @@ final class AppBuilder
private array $definitions = [];

/**
* @var callable|null Factory function for creating a custom DI container
* @var ContainerFactoryInterface|null Factory function for creating a custom DI container
*/
private $containerFactory = null;
private ?ContainerFactoryInterface $containerFactory = null;

/**
* The constructor.
Expand All @@ -46,8 +45,8 @@ final class AppBuilder
*/
public function __construct()
{
$this->addDefinitions(DefaultDefinitions::class);
$this->addDefinitions(HttpDefinitions::class);
$this->addDefinitionsClass(DefaultDefinitions::class);
$this->addDefinitionsClass(HttpDefinitions::class);
}

/**
Expand All @@ -61,83 +60,89 @@ public function build(): App
}

/**
* Creates and configures the DI container.
* Sets the service definitions for the DI container.
*
* If a custom container factory is set, it will be used to create the container;
* otherwise, a default container with the provided definitions will be created.
* @param array $definitions An array of service definitions
*
* @return ContainerInterface The configured DI container
* @return self The current instance
*/
private function buildContainer(): ContainerInterface
public function addDefinitions(array $definitions): self
{
return $this->containerFactory
? call_user_func($this->containerFactory, $this->definitions)
: new Container($this->definitions);
$this->definitions = array_merge($this->definitions, $definitions);

return $this;
}

/**
* Sets the service definitions for the DI container.
*
* The method accepts either an array of definitions or the name of a class that provides definitions.
* If a class name is provided, its definitions are added to the existing ones.
* @param string $class A definition provider class name
*
* @throws RuntimeException
*
* @return self The current instance
*/
public function addDefinitionsClass(string $class): self
{
$definitions = call_user_func(new $class());

if (!is_array($definitions)) {
throw new RuntimeException('Definition file should return an array of definitions');
}

$this->addDefinitions($definitions);

return $this;
}

/**
* Sets the service definitions for the DI container.
*
* @param array|string $definitions An array of service definitions or a class name providing them
* @param string $file A service definitions provider file
*
* @throws RuntimeException
*
* @return self The current AppBuilder instance for method chaining
* @return self The current instance
*/
public function addDefinitions(array|string $definitions): self
public function addDefinitionsFile(string $file): self
{
if (is_string($definitions)) {
if (class_exists($definitions)) {
$definitions = (array)call_user_func(new $definitions());
} else {
$definitions = require $definitions;

if (!is_array($definitions)) {
throw new RuntimeException('Definition file should return an array of definitions');
}
}
$definitions = require $file;

if (!is_array($definitions)) {
throw new RuntimeException('Definition file should return an array of definitions');
}

$this->definitions = array_merge($this->definitions, $definitions);
$this->addDefinitions($definitions);

return $this;
}

/**
* Sets a custom factory for creating the DI container.
*
* @param callable $factory A callable that returns a configured DI container
* @param ContainerFactoryInterface $containerFactory A DI container factory
*
* @return self The current AppBuilder instance for method chaining
* @return self The current instance
*/
public function setContainerFactory(callable $factory): self
public function setContainerFactory(ContainerFactoryInterface $containerFactory): self
{
$this->containerFactory = $factory;
$this->containerFactory = $containerFactory;

return $this;
}

/**
* Sets application-wide settings in the DI container.
*
* This method allows the user to configure various settings for the Slim application,
* by passing an associative array of settings.
* Creates and configures the DI container.
*
* @param array $settings An associative array of application settings
* If a custom container factory is set, it will be used to create the container;
* otherwise, a default container with the provided definitions will be created.
*
* @return self The current AppBuilder instance for method chaining
* @return ContainerInterface The configured DI container
*/
public function setSettings(array $settings): self
private function buildContainer(): ContainerInterface
{
$this->addDefinitions(
[
'settings' => $settings,
]
);
$this->containerFactory = $this->containerFactory ?? new PhpDiContainerFactory();

return $this;
return $this->containerFactory->createContainer($this->definitions);
}
}
41 changes: 0 additions & 41 deletions Slim/Configuration/Config.php

This file was deleted.

70 changes: 0 additions & 70 deletions Slim/Container/DefaultDefinitions.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,10 @@
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Slim\Configuration\Config;
use Slim\Emitter\ResponseEmitter;
use Slim\Error\Handlers\ExceptionHandler;
use Slim\Error\Renderers\HtmlExceptionRenderer;
use Slim\Error\Renderers\JsonExceptionRenderer;
use Slim\Error\Renderers\PlainTextExceptionRenderer;
use Slim\Error\Renderers\XmlExceptionRenderer;
use Slim\Interfaces\ConfigurationInterface;
use Slim\Interfaces\ContainerResolverInterface;
use Slim\Interfaces\EmitterInterface;
use Slim\Interfaces\ExceptionHandlerInterface;
use Slim\Interfaces\RequestHandlerInvocationStrategyInterface;
use Slim\Media\MediaType;
use Slim\Media\MediaTypeDetector;
use Slim\Middleware\BodyParsingMiddleware;
use Slim\Middleware\ExceptionHandlingMiddleware;
use Slim\Middleware\ExceptionLoggingMiddleware;
use Slim\RequestHandler\MiddlewareRequestHandler;
use Slim\Routing\Router;
use Slim\Routing\Strategies\RequestResponse;
Expand All @@ -52,23 +39,6 @@ final class DefaultDefinitions
public function __invoke(): array
{
return [
BodyParsingMiddleware::class => function (ContainerInterface $container) {
$mediaTypeDetector = $container->get(MediaTypeDetector::class);
$middleware = new BodyParsingMiddleware($mediaTypeDetector);

return $middleware
->withDefaultMediaType('text/html')
->withDefaultBodyParsers();
},

Config::class => function (ContainerInterface $container) {
return new Config($container->has('settings') ? (array)$container->get('settings') : []);
},

ConfigurationInterface::class => function (ContainerInterface $container) {
return $container->get(Config::class);
},

ContainerResolverInterface::class => function (ContainerInterface $container) {
return $container->get(ContainerResolver::class);
},
Expand All @@ -77,46 +47,6 @@ public function __invoke(): array
return new ResponseEmitter();
},

ExceptionHandlingMiddleware::class => function (ContainerInterface $container) {
$handler = $container->get(ExceptionHandlerInterface::class);

return (new ExceptionHandlingMiddleware())->withExceptionHandler($handler);
},

ExceptionHandlerInterface::class => function (ContainerInterface $container) {
// Default exception handler
$exceptionHandler = $container->get(ExceptionHandler::class);

// Settings
$displayErrorDetails = (bool)$container->get(ConfigurationInterface::class)
->get('display_error_details', false);

$exceptionHandler = $exceptionHandler
->withDisplayErrorDetails($displayErrorDetails)
->withDefaultMediaType(MediaType::TEXT_HTML);

return $exceptionHandler
->withoutHandlers()
->withHandler(MediaType::APPLICATION_JSON, JsonExceptionRenderer::class)
->withHandler(MediaType::TEXT_HTML, HtmlExceptionRenderer::class)
->withHandler(MediaType::APPLICATION_XHTML_XML, HtmlExceptionRenderer::class)
->withHandler(MediaType::APPLICATION_XML, XmlExceptionRenderer::class)
->withHandler(MediaType::TEXT_XML, XmlExceptionRenderer::class)
->withHandler(MediaType::TEXT_PLAIN, PlainTextExceptionRenderer::class);
},

ExceptionLoggingMiddleware::class => function (ContainerInterface $container) {
// Default logger
$logger = $container->get(LoggerInterface::class);
$middleware = new ExceptionLoggingMiddleware($logger);

// Read settings
$logErrorDetails = (bool)$container->get(ConfigurationInterface::class)
->get('log_error_details', false);

return $middleware->withLogErrorDetails($logErrorDetails);
},

LoggerInterface::class => function () {
return new NullLogger();
},
Expand Down
23 changes: 23 additions & 0 deletions Slim/Container/PhpDiContainerFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

/**
* Slim Framework (https://slimframework.com).
*
* @license https://github.com/slimphp/Slim/blob/5.x/LICENSE.md (MIT License)
*/

declare(strict_types=1);

namespace Slim\Container;

use DI\Container;
use Psr\Container\ContainerInterface;
use Slim\Interfaces\ContainerFactoryInterface;

final class PhpDiContainerFactory implements ContainerFactoryInterface
{
public function createContainer(array $definitions = []): ContainerInterface
{
return new Container($definitions);
}
}
Loading

0 comments on commit 89b8de3

Please sign in to comment.