1

Playing a little bit with the new PHP 8 attributes (annotations) (https://stitcher.io/blog/attributes-in-php-8) I started to create my own ones.

Simple example:

namespace CisTools\Attribute;

use Attribute;
use CisTools\Exception\BadAttributeMetadataException;

/**
 * For defining a description.
 */
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::TARGET_FUNCTION)]
class Description
{
    /**
     * @param string $description
     * @throws BadAttributeMetadataException
     */
    public function __construct(string $description)
    {
        if (strlen($description) < 10) {
            throw new BadAttributeMetadataException("This is description is too short.");
        }
    }
}

Hoping that the following code would throw an BadAttributeMetadataException unfortunately it finishes successfully:

use CisTools\Attribute\Description;

#[Description("quack")]
class Test {

    public function __construct() {
        echo "hello world";
    }

}

new Test();

Is there away to validate the metadata passed for (custom) attributes? Most probably the attributes have to be instantiated automatically somehow.

Blackbam
  • 17,496
  • 26
  • 97
  • 150
  • 1
    As described in the mentioned blog post, you have to instantiate the attribute somewhere: `First there's the $attribute->newInstance() call. This is actually the place where our custom attribute class is instantiated. It will take the parameters listed in the attribute definition in our subscriber class, and pass them to the constructor.` I expect the presented call will produce your `BadAttributeMetadataException` – thehennyy Jun 29 '21 at 13:21
  • 1
    @thehennyy That means I am responsible for creating an instance of my attributes via reflection to make sure that they are used correctly? Sounds a little bit clumsy is there a way to let PHP do it by default? Thanks a lot for your help so far anyway! – Blackbam Jun 29 '21 at 14:44
  • @thehennyy A special dislike about this is when I make a library containing attributes I can not force people using the libraries to use my attributes correctly? – Blackbam Jun 29 '21 at 14:53
  • The php runtime will never use your custom attributes. Attributes are somewhat 'optional' metadata, someone has to read/instantiate them. To enforce usage, you have to include the logic to use them inside your own library. Others may, or maybe not use your attributes. – thehennyy Jun 29 '21 at 15:05
  • @thehennyy Makes sense. The issue is with using them wrong, a description should have at least 10 chars (simple example). Basically I could get all declared classes with https://www.php.net/manual/de/function.get-declared-classes.php loop through the methods with reflection, check for declared attributes, instantiate them and check if instantiation works. But is there really no easier way to achieve this? – Blackbam Jun 29 '21 at 15:18
  • 1
    No, there is no other way. Reflection is the only option to use attributes. (php runtime extensions could use them too, but that is probably out of your scope) – thehennyy Jun 29 '21 at 15:43
  • Thanks a lot thats an answer then. Regarding runtime extensions I will not do it now and hope somebody else will make one, but maybe if I find time ... ;-) – Blackbam Jun 29 '21 at 16:27
  • @thehennyy Also with respect to your input I have found a solution. – Blackbam Jun 30 '21 at 08:56

1 Answers1

0

Everything is possible - as of today I have implemented a working solution for this problem which can very well be used in libraries (just in case somebody needs that too):

function cis_shutdown_validate_attributes()
{
    if(!defined('CIS_DISABLE_ATTRIBUTE_VALIDATION')) {
        foreach(get_declared_classes() as $class) {
            try {
                $reflection = new ReflectionClass($class);
            } catch (\Throwable $throwable) {
                continue;
            }
            $attributes = $reflection->getAttributes();
    
            foreach ($attributes as $attribute) {
                $attribute->newInstance();
            }
        }
    }
}

register_shutdown_function('cis_shutdown_validate_attributes');

(You may also autoload it: Composer/PSR - How to autoload functions?)

Blackbam
  • 17,496
  • 26
  • 97
  • 150