Skip to content

property-of<...> Utility type #3849

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed

property-of<...> Utility type #3849

wants to merge 2 commits into from

Conversation

supun-io
Copy link

@supun-io supun-io commented Mar 2, 2025

Problem: an easy way to access the names of the properties of an object or a class.

Solution: property-of<MyObject> utility type

Example:

class CreateSubscriberDto
{

    public string $email;
    public int $listId;

    /**
     * @param property-of<self> $property
     * @return bool
     */
    public function hasProperty(string $property): bool
    {
        dumpType($property); // 'email'|'listId'

        // ...
    }

}

$dto = new CreateSubscriberDto();
$dto->hasProperty('email'); // no error
$dto->hasProperty('listId'); // no error
$dto->hasProperty('notExisting'); // shows static error

TODO:

  • Implement a template-supported version in PHPStan\Type\Generic

Notes:

Most of the code was taken from KeyOfType.

If the idea is accepted, I will work on the implementation of the template-supported version.

@herndlm
Copy link
Contributor

herndlm commented Mar 3, 2025

jfyi this is also related to what I was asking recently in phpstan/phpstan#12240. consider this a neutral comment, I don't mean that adding it here is good or bad.

this is a highly specific thing potentially, but having a utility for this in phpstan itself might make sense. in my case I needed something even more special for our app in the end anyway, because I also had to check if the property has a default value assigned or not. so I ended up with a properties-without-default-value-of<> utility to make some risky behaviour we have more (type-)safe 😊

@ondrejmirtes
Copy link
Member

I've rejected this in the past because different users might have different needs. There are many decisions about object properties you need to make:

  • Only native properties, or with magic properties (@method, __get etc.) as well?
  • Only public properties, or private+protected as well?
  • Only instance properties, or static properties too?
  • What about universalObjectCratesClasses? Does this return a general array<string, mixed> for them or not?

Because of these uncertainties, every project can implement their own version using https://phpstan.org/developing-extensions/custom-phpdoc-types.

@supun-io
Copy link
Author

supun-io commented Mar 3, 2025

yes, I understand that there are variations of the problem, but still, this is a very common case in PHP, especially when using DTOs.

Would it make sense to have a second parameter that accepts a ReflectionProperty constant to cover all the cases including static virtual, set, final, etc.

// all
property-of<self>

// static only
property-of<self, ReflectionProperty::IS_STATIC>

// protected or private
property-of<self, ReflectionProperty::IS_PROTECTED | ReflectionProperty::IS_PRIVATE>

// static and public
property-of<self, ReflectionProperty::IS_STATIC & ReflectionProperty::IS_PUBLIC>

We can simply pass the second parameter to getProperties of the class reflection. This is assuming PHPDoc can parse bitwise operators correctly (which I am not sure)

This solution solves:

  • Only native properties,
  • Only public properties, or private+protected as well?
  • Only instance properties, or static properties too?

But not

  • magic properties (@method, __get etc.) as well?
    I think we could somehow support @method, but __get is going to be difficult

  • What about universalObjectCratesClasses? Does this return a general array<string, mixed> for them or not?
    Just mixed for them?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants