{"id":36981300,"url":"https://github.com/fusonic/php-http-kernel-extensions","last_synced_at":"2026-01-13T22:51:07.774Z","repository":{"id":58308674,"uuid":"265203908","full_name":"fusonic/php-http-kernel-extensions","owner":"fusonic","description":"This library contains a variety of extensions to the Symfony HttpKernel component. ","archived":true,"fork":false,"pushed_at":"2023-10-02T09:26:13.000Z","size":142,"stargazers_count":20,"open_issues_count":0,"forks_count":0,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-12-26T16:48:24.380Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fusonic.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2020-05-19T09:29:14.000Z","updated_at":"2024-06-20T01:13:03.000Z","dependencies_parsed_at":"2023-02-16T04:50:21.547Z","dependency_job_id":null,"html_url":"https://github.com/fusonic/php-http-kernel-extensions","commit_stats":{"total_commits":35,"total_committers":4,"mean_commits":8.75,"dds":0.5428571428571429,"last_synced_commit":"cabb15b4e2e7c65687ead88d11bd137beaa26803"},"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/fusonic/php-http-kernel-extensions","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fusonic%2Fphp-http-kernel-extensions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fusonic%2Fphp-http-kernel-extensions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fusonic%2Fphp-http-kernel-extensions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fusonic%2Fphp-http-kernel-extensions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fusonic","download_url":"https://codeload.github.com/fusonic/php-http-kernel-extensions/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fusonic%2Fphp-http-kernel-extensions/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28402159,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-13T14:36:09.778Z","status":"ssl_error","status_checked_at":"2026-01-13T14:35:19.697Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-13T22:51:07.046Z","updated_at":"2026-01-13T22:51:07.765Z","avatar_url":"https://github.com/fusonic.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# http-kernel-extensions\n\n[![License](https://img.shields.io/packagist/l/fusonic/http-kernel-extensions?color=blue)](https://github.com/fusonic/php-http-kernel-extensions/blob/master/LICENSE)\n[![Latest Version](https://img.shields.io/github/tag/fusonic/php-http-kernel-extensions.svg?color=blue)](https://github.com/fusonic/php-http-kernel-extensions/releases)\n[![Total Downloads](https://img.shields.io/packagist/dt/fusonic/http-kernel-extensions.svg?color=blue)](https://packagist.org/packages/fusonic/http-kernel-extensions)\n![php 8.1+](https://img.shields.io/badge/php-min%208.1-blue.svg)\n\n* [About](#about)\n* [Install](#install)\n* [Usage](#usage)\n\n## ⚠️ Discontinuation notice ⚠️\n\n\u003e [!WARNING]\n\u003e The `fusonic/http-kernel-extensions` package has been discontinued in favour of a dedicated Symfony bundle located at\n\u003e `fusonic/http-kernel-bundle` (see [fusonic/php-http-kernel-bundle](https://github.com/fusonic/php-http-kernel-bundle)).\n\u003e \n\u003e This package is only compatible up to and including Symfony 6.3 and will not receive any further feature updates.\n\u003e Please consider switching to the new bundle to benefit from future changes.\n\u003e \n\u003e Please refer to [UPGRADE-1.0.md](https://github.com/fusonic/php-http-kernel-bundle/tree/master/UPGRADE-1.0.md)\n\u003e for guidance on how to upgrade to the new bundle.\n\n## About\n\nThis library contains a variety of extensions to\nthe [Symfony HttpKernel component](https://symfony.com/doc/current/components/http_kernel.html). See below for details\non each extension this lib provides and how it works.\n\nCurrently primary development takes place at a private repository at Gitlab.com. The project on Github.com is updated\nregularly, but does not include any issues managed at Gitlab. However, we are happily accepting issues and pull requests\non Github as well! Feel free to open an issue or merge request. If we see broader community engagement in the future, we\nmay consider switching our primary development to Github.\n\n## Install\n\nUse composer to install the lib from packagist.\n\n```bash\ncomposer require fusonic/http-kernel-extensions\n```\n\n## Usage\n\n### The RequestDtoResolver\n\nIn Symfony there exists a thing\ncalled [argument resolvers](https://symfony.com/doc/current/controller/argument_value_resolver.html). They can be used\nto set the value of controller action arguments before the actions get called. There exists e.g.\nthe `RequestValueResolver` which will inject the current request as an argument in the called action. Similar to this we\ncreated our own argument resolver, but it does a few more things than just injecting an object.\n\n#### What does it do?\n\nOur `RequestDtoResolver` can be used to map requests data directly to objects. Instead of manually getting all the\ninformation from your request and putting it in an object or - god forbid - passing around generic data arrays, this\nclass will leverage the Symfony Serializer to map requests to objects and by that enable you to have custom objects to\ntransport the request data (aka data transfer objects) from your controller to your business logic. In addition, it will\nalso validate the resulting object with Symfony Validation if you set validation annotations.\n\n- Mapping will happen for parameters accompanied by\n  the `Fusonic\\HttpKernelExtensions\\Attribute\\FromRequest` [attribute](src/Attribute/FromRequest.php). Alternatively the\n  attribute can also be set on the class of the parameter (see example below).\n- Strong type checks will be enforced for PUT, POST, PATCH and DELETE during serialization and it will result in an\n  error if the types in the request body don't match the expected ones in the DTO.\n- Type enforcement will be disabled for all other requests e.g. GET as query parameters will always be transferred as\n  string.\n- The request body will be combined with route parameters for PUT, POST, PATCH and DELETE requests (query parameters\n  will be ignored in this case).\n- The query parameters will be combined with route parameters for all other requests (request body will be ignored in\n  this case).\n- Route parameters will always override query parameters or request body values with the same name.\n- After deserializing the request to an object, validation will take place.\n- A `BadRequestHttpException` will be thrown when\n  - the resulting DTO object is invalid according to Symfony Validation\n  - the request body can't be deserialized\n  - the request contains invalid JSON\n  - the request contains valid JSON but the hierarchy levels exceeds 512\n- If you are using the [ConstraintViolationErrorHandler](src/ErrorHandler/ConstraintViolationErrorHandler.php) error handler, a\n  [ConstraintViolationException](src/Exception/ConstraintViolationException.php) will be thrown if the validation of your object\n  fails. You can also implement your own handler by implementing the [ErrorHandlerInterface](src/ErrorHandler/ErrorHandlerInterface.php).\n- Depending on the given content type it will either parse the request body as a regular form or parse the content as JSON\n  if the content type is set accordingly.\n\n### How to use?\n\nSupposing you are using a full Symfony setup you have to register the resolver as a service in your `services.yaml` as\nshown below to be called by Symfony.\n\n```yaml\n    Fusonic\\HttpKernelExtensions\\Controller\\RequestDtoResolver:\n        tags:\n            - { name: controller.argument_value_resolver, priority: 50 }\n```\n\nCreate your DTO like e.g. our `UpdateFooDto` here. All the validation stuff is optional but getters and setters are\nneeded by the serializer.\n\n```php\n\n// ...\n\nfinal class UpdateFooDto\n{\n    #[Assert\\NotNull]\n    #[Assert\\Positive]\n    private int $id;\n\n    #[Assert\\NotBlank]\n    private string $clientVersion;\n\n    #[Assert\\NotNull]\n    private array $browserInfo;\n\n    public function getClientVersion(): string\n    {\n        return $this-\u003eclientVersion;\n    }\n\n    public function setClientVersion(string $clientVersion): void\n    {\n        $this-\u003eclientVersion = $clientVersion;\n    }\n\n    public function getBrowserInfo(): array\n    {\n        return $this-\u003ebrowserInfo;\n    }\n\n    public function setBrowserInfo(array $browserInfo): void\n    {\n        $this-\u003ebrowserInfo = $browserInfo;\n    }\n\n    public function getId(): int\n    {\n        return $this-\u003eid;\n    }\n\n    public function setId(int $id): void\n    {\n        $this-\u003eid = $id;\n    }\n}\n```\n\n#### Parameter attribute\n\nFinally, add the DTO with the `RequestDtoArgument` to your controller action. Routing parameters are optional as well of\ncourse.\n\n```php\n\n// ...\nuse Fusonic\\HttpKernelExtensions\\Attribute\\FromRequest;\n\nfinal class FooController extends AbstractController\n{\n    /**\n     * @Route(\"/{id}/update\", methods={\"POST\"}, requirements={\"id\"=\"\\d+\"})\n     */\n    public function updateAction(#[FromRequest] UpdateFooDto $dto): Response\n    {\n        // do something with your $dto here\n    }\n}\n```\n\n#### Class attribute\n\nAlternatively you can also add the attribute to the DTO class itself instead of the parameter in the controller action\nif you prefer it this way.\n\n```php\n\n// ...\nuse Fusonic\\HttpKernelExtensions\\Attribute\\FromRequest;\n\n#[FromRequest]\nfinal class UpdateFooDto\n{\n// ...\n}\n```\n\n```php\n\n// ...\n\nfinal class FooController extends AbstractController\n{\n    /**\n     * @Route(\"/{id}/update\", methods={\"POST\"}, requirements={\"id\"=\"\\d+\"})\n     */\n    public function updateAction(UpdateFooDto $dto): Response\n    {\n        // do something with your $dto here\n    }\n}\n```\n\n#### Parsing and collecting data for models\nBy default, any `json` or `form` request body types will be parsed accordingly. To override this behaviour you could\ninject your own request body parsers (by implementing `Fusonic\\HttpKernelExtensions\\Request\\BodyParser\\RequestBodyParserInterface`)\ninto an implementation of `Fusonic\\HttpKernelExtensions\\Request\\RequestDataCollectorInterface`, which is injected into the\n`Fusonic\\HttpKernelExtensions\\Controller\\RequestDtoResolver`. Inside the `RequestDataCollectorInterface` you can\nalso modify the behaviour of how and which values are used from the `Request` object.\n\n#### Error handling\n\nThe extension provides a default error handler (`http-kernel-extensions/src/ErrorHandler/ConstraintViolationErrorHandler.php`) which\nhandles common de-normalization errors that should be considered type errors. It will create a\n`Fusonic\\HttpKernelExtensions\\Exception\\ConstraintViolationException` [ConstraintViolationException](src/Exception/ConstraintViolationException.php)\nwhich can be used with the provided `Fusonic\\HttpKernelExtensions\\Normalizer\\ConstraintViolationExceptionNormalizer` [ConstraintViolationExceptionNormalizer](src/Normalizer/ConstraintViolationExceptionNormalizer.php).\nThis normalizer is uses on Symfony's built-in `Symfony\\Component\\Serializer\\Normalizer\\ConstraintViolationListNormalizer` and enhances it\nwith some extra information: an `errorCode` and `messageTemplate`. Both useful for parsing validation errors on the client side.\nIf that does not match your needs you can simply provide your own error handler by implementing\nthe `Fusonic\\HttpKernelExtensions\\ErrorHandler\\ErrorHandlerInterface` and passing it to the `RequestDtoResolver`.\n\nYou have to register the normalizer as a service like this:\n\n```yaml\n  Fusonic\\HttpKernelExtensions\\Normalizer\\ConstraintViolationExceptionNormalizer:\n        arguments:\n            - \"@serializer.normalizer.constraint_violation_list\"\n        tags:\n          - { name: serializer.normalizer }\n```\n\n##### Using an exception listener/subscriber\nIn Symfony you can use an exception listener or subscriber to eventually convert the `ConstraintViolationException` into an actual response using\nthe `Fusonic\\HttpKernelExtensions\\Normalizer\\ConstraintViolationExceptionNormalizer`. For example:\n\n```php\n\nuse Symfony\\Component\\Serializer\\Normalizer\\NormalizerInterface;\nuse Symfony\\Component\\EventDispatcher\\EventSubscriberInterface;\nuse Fusonic\\HttpKernelExtensions\\Exception\\ConstraintViolationException;\nuse Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent;\n\nfinal class ExceptionSubscriber implements EventSubscriberInterface {\n\n    public function __construct(private NormalizerInterface $normalizer) \n    {\n    }\n\n    public static function getSubscribedEvents(): array\n    {\n        return [\n            KernelEvents::EXCEPTION =\u003e 'onKernelException',\n        ];\n    }\n\n    public function onKernelException(ExceptionEvent $event): void\n    {\n        $throwable = $event-\u003egetThrowable();\n        \n        if ($throwable instanceof ConstraintViolationException) {\n            $data = $this-\u003enormalizer-\u003enormalize($throwable);\n            $event-\u003esetResponse(new JsonResponse($data, 422));\n        }\n    }\n}\n```\n\nCheck the [Events and Event Listeners](https://symfony.com/doc/current/event_dispatcher.html) for details.\n\n#### ContextAwareProvider\n\nThere are cases where you want to add data to your DTOs but not through the consumer of the API but, for example, depending on the currently logged in user. You could do that manually after you received your DTO in the controller, get the user, set the user for the DTO and then move on with the processing. As you set it after the creation of the DTO you cannot work with the validation and have to make it nullable as well. And you might have to do some additional checks in your business logic afterwards to ensure everything you need is set.\n\nOr you just create and register a provider, implement (and test) it once and be done with it. All providers will be called by the `RequestDtoResolver`, retrieve the needed data for the supported DTO, set it in your DTO and then the validation will take place. By the time you get it in your controller it's complete and validated. How do you do that?\n\n1. Create a provider and implement the two methods of the `ContextAwareProvideInterface`.\n\n```php\n\u003c?php\n\n// ...\n\nfinal class UserIdAwareProvider implements ContextAwareProviderInterface\n{\n    public function __construct(private UserProviderInterface $userProvider)\n    {\n    }\n\n    public function supports(object $dto): bool\n    {\n        return $dto instanceof UserIdAwareInterface;\n    }\n\n    public function provide(object $dto): void\n    {\n        if(!($dto instanceof UserIdAwareInterface)){\n            throw new \\LogicException('Object is no instance of '.UserIdAwareInterface::class);\n        }\n\n        $user = $this-\u003euserProvider-\u003egetUser();\n        $dto-\u003ewithUserId($user-\u003egetId());\n    }\n}\n```\n\n2. Create the interface to mark the class you support and set the data.\n\n```php\n\u003c?php\n\n//... \n\ninterface UserIdAwareInterface\n{\n    public function withUserId(int $id): void;\n}\n\n```\n\n3. Implement the interface in the DTO.\n4. Finally, pass the providers into the resolver. If you are using Symfony you will be doing that in the `services.yaml` and it will look similar to this.\n```yaml\n#...\n    _instanceof:\n        Fusonic\\HttpKernelExtensions\\Provider\\ContextAwareProviderInterface:\n            tags:\n                - {name: fusonic.http_kernel_extensions.context_aware_provider}\n\n    Fusonic\\HttpKernelExtensions\\Controller\\RequestDtoResolver:\n        tags:\n            - { name: controller.argument_value_resolver, priority: 50 }\n        arguments:\n            - '@serializer'\n            - '@validator'\n            - null\n            - !tagged_iterator fusonic.http_kernel_extensions.context_aware_provider\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffusonic%2Fphp-http-kernel-extensions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffusonic%2Fphp-http-kernel-extensions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffusonic%2Fphp-http-kernel-extensions/lists"}