Skip to content
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

How can I teach Valinor to understand generics of \Ds\Set? #529

Open
simPod opened this issue Apr 12, 2024 · 3 comments
Open

How can I teach Valinor to understand generics of \Ds\Set? #529

simPod opened this issue Apr 12, 2024 · 3 comments

Comments

@simPod
Copy link
Contributor

simPod commented Apr 12, 2024

My investigation reached DocParser::classTemplates(). It tries to read generics from doc comment of passed class.

My class is \Ds\Set. I guess there are no docblock comments for "native" classes.

That's why some stubs have been created https://github.com/JetBrains/phpstorm-stubs/blob/1650beae521d9dd1aee7dd208a68fbc9ec3765c6/ds/ds.php#L1885

Any idea how can I provide those stubs to Valinor?

@romm
Copy link
Member

romm commented Apr 12, 2024

Hi @simPod, currently there is no way to tell Valinor how to handle this kind of usecase, that's actually the same for Generator or ArrayObject (and probably other).

It's on my todo list, but right now I don't know how it will be handled internally. I'll keep you updated here.

FYI DocParser has been removed during a major refactoring of the type resolver services (which is not released, as of when this message is written).

@simPod
Copy link
Contributor Author

simPod commented Apr 12, 2024

Aight, thanks for the answer. I'll somehow hack on my side until some clear path forward materializes.

@mastir
Copy link

mastir commented Jul 3, 2024

Hi! Got same issue with Ds\Set mapping. As there is no way to handle this types i made a simple dirty way around, just skip type declaration in property and declare type in DocBlock part, add attribute to declare real type and remap output, Here is example:

class TestDto {

    /**
     * @param string $name
     * @param list<string> $refs
     */
    public function __construct(
        public readonly string $name,
        #[Remap(Set::class)]
        public $refs,
    )
    {}

}

And wrapped Mapper:

class MyMapper{

    public function __construct(private $mapper){}
    
    /**
     * @template T of object
     *
     * @param string|class-string<T> $class_name
     * @return T
     * @phpstan-return (
     *     $signature is class-string<T>
     *         ? T
     *         : ($signature is class-string ? object : mixed)
     * )
     *
     * @throws MappingError
     */
    public function map(string $class_name, mixed $data): mixed
    {
        $mapped =  $this->mapper->map($class_name, $data);
        return $this->remap($mapped);
    }
    
    public function remap($result)
    {
        if (!is_object($result)) return $result;
        $ref = new \ReflectionClass($result);
        foreach ($ref->getProperties(\ReflectionProperty::IS_PUBLIC) as $prop) {
            foreach ($prop->getAttributes(Remap::class) as $attr) {
                $target = $attr->newInstance()->type;
                $result->{$prop->getName()} = new $target($result->{$prop->getName()});
            }
            if (!$prop->isReadOnly()) {
                $result->{$prop->getName()} = $this->remap($result->{$prop->getName()});
            }
        }
        return $result;
    }
}

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

No branches or pull requests

3 participants