phpphp-attributes

How to create a preset of attributes using an attributes?


I'm using symfony 5.4 with php 8.1 and I would like to factor a set of attributes used for Swagger documentation, before each controllers method, I have, at least, these 5 attributes:

#[OA\Response(
    response: 400,
    description: 'Bad parameters',
    content: new OA\JsonContent(example: ['code' => 400, 'message' => 'Bad Request', 'appCode' => 5000])
)]
#[OA\Response(
    response: 401,
    description: 'JWT Token not found (appCode = 5001) or expired (appCode = 5002)',
    content: new OA\JsonContent(example: ['code' => 401, 'message' => 'JWT Token not found', 'appCode' => 5001])
)]
#[OA\Response(
    response: 403,
    description: 'Insufficient privileges',
    content: new OA\JsonContent(example: ['code' => 403, 'message' => 'Forbidden', 'appCode' => 5003])
)]
#[Security(name: 'Bearer')]
#[OA\Tag(name: 'User')]

It takes up a lot of space and is duplicated, so I would like to create an attribute that I could use as :

#[SwaggerUtils(tag: 'User')]

which will do the same job as writing the previous 5 attributes, like the decorator composition in Nestjs (https://docs.nestjs.com/custom-decorators#decorator-composition).

Is it possible in PHP?

The best post I've found on PHP attributes is https://www.amitmerchant.com/how-to-use-php-80-attributes/. But I have no clue how I can achieve what I want.


Solution

  • Although they can be used for a lot of the same purposes, decorators (as found in Python, and on the standardisation track for ECMAScript) and attributes/annotations (as found in PHP) are quite different in implementation, so this kind of functionality doesn't easily translate from one to the other.

    With a decorator, there is a callback function which takes the definition of an element (e.g. a function or class), and manipulates it in various ways. For instance, decorating a function with @auth might return a function definition that checks authentication before running the main body of the code. That makes "decorator composition" as shown in your example relatively trivial: if a decorator is just a callback function, you can write a decorator that runs two existing callback functions, and it's equivalent to adding both of them at once.

    Attributes don't directly add any behaviour; instead, they attach some arbitrary metadata to elements of the program, which can be read via the reflection API. If nothing looks for an attribute, it does nothing; on the other hand, two different pieces of code can both look for an attribute and make different decisions based on it. That means your new SwaggerUtils attribute would have to mean something to the code that's generating the Swagger documentation.

    In theory, you could have some code that ran before the existing Swagger documentation code, which looked for the SwaggerUtils attribute and added the "composed" attributes. However, as far as I know, there's no way to write attributes via reflection, only by rewriting the actual source code.