CuyZ / Valinor

PHP library that helps to map any input into a strongly-typed value object structure.

Home Page:https://valinor.cuyz.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

simPod opened this issue · comments

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?

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).

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

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;
    }
}