4

I have a project that use attributes :

    #[OperationQueryParam('id', IntegerOperation::class, requirements: new Numeric(min: 1, max: 2_147_483_647))]

Everything works fine but there are multiple attribute that have the same requirement argument new Numeric(min: 1, max: 2_147_483_647) so i wanted to create a static function in my numeric class :

    final public static function createPostiveInt(): self {
       return new self(min:1, max:2_147_483_647);
    }

then call the static function instead of the direct instanciation of the Numeric object to avoid repeating the constructor parameters.

    #[OperationQueryParam('id', IntegerOperation::class, requirements: Numeric::createPostiveInt())]

but i have this error : Compile Error: Constant expression contains invalid operations

Am i missing something ? Why does a direct instanciation works but not the static function ?

EDIT :

using define(POSITIVE_INT,Numeric::createPostiveInt()); works but it seems dirty.

1
  • It looks like calling a function is not allowed. new is a keyword. That is different. Commented Jun 17, 2024 at 11:15

2 Answers 2

2

From https://www.php.net/manual/en/functions.arguments.php:

Default parameter values may be scalar values, arrays, the special type null, and as of PHP 8.1.0, objects using the new ClassName() syntax.

In other words, you can't use a run-time construct like a function call as a default value for a parameter. You could extend the Numeric class here, to build-in your common defaults:

class MyNumeric extends Numeric
{
    public function __construct(int $min = 1, int $max = 2_147_483_647) {
        parent::__construct(min: $min, max: $max);
    }
}

Or even just remove the args completely:

class MyNumeric extends Numeric
{
    public function __construct() {
        parent::__construct(min: 1, max: 2_147_483_647);
    }
}

And then your definition becomes:

#[OperationQueryParam(
    'id',
    IntegerOperation::class,
    requirements: new MyNumeric()
)]
Sign up to request clarification or add additional context in comments.

1 Comment

I like this one better than mine, much simpler and should work regardless of how the project uses reflection.
0

Depending on how the project uses reflection to get attribute values, it might be possible for you to subclass OperationQueryParam and include your constant values via parent::__construct(). To do this, the reflecting code must not check for simple equality on class name, but check for it being an instance of it. There's a couple of ways to do this, but one option is to use the second parameter to getAttributes() which is ReflectionAttribute::IS_INSTANCEOF

Below is a quick PoC. We have a base attribute which requires a $name parameter, and then our custom attribute which doesn't, and instead includes that in the parent constructor.

#[Attribute]
class BaseAttribute
{
    public function __construct(public readonly string $name)
    {
    }
}

#[Attribute]
class ConstantlyNameAttribute extends BaseAttribute
{
    public function __construct()
    {
        parent::__construct('Chris');
    }
}

#[ConstantlyNameAttribute]
class Thing{}

$reflectionClass = new ReflectionClass(Thing::class);

// Doesn't work
var_dump(getName($reflectionClass->getAttributes(BaseAttribute::class)));

// Works
var_dump(getName($reflectionClass->getAttributes(BaseAttribute::class, ReflectionAttribute::IS_INSTANCEOF)));

// Helper function
function getName(array $reflectionAttributes) : ?string
{
    if(!count($reflectionAttributes)){
        return null;
    }
    
    $attribute = array_pop($reflectionAttributes);
    return $attribute->newInstance()->name;
}

Demo: https://3v4l.org/SWTa6#v8.3.8

If the project is a third-party and you can't modify things, it might be worth it kicking this request upstream to see if they are willing to modify it.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.