Skip to content

[PropertyInfo] Extend the allowed array syntax to list<> in PHPDoc #40142

@christian-kolb

Description

@christian-kolb

Description
In the serialization process with the symfony/serializer it's common to define the PhpDocExtractor as part of the PropertyNormalizer. With the PhpDocExtractor it's possible to have the following PHPDoc block on our properties:

<?php

final class Company
{
    /**
     * @var Employee[]
     */
    public array $employees;

    /**
     * @param Employee[] $employees
     */
    public function __construct(array $employees)
    {
        $this->employees = $employees;
    }
}

final class Employee
{
    public string $firstName;
    public string $lastName;

    public function __construct(
        string $firstName,
        string $lastName
    ) {
        $this->firstName = $firstName;
        $this->lastName = $lastName;
    }
}

When supplying the following JSON, the objects are deserialized:

{
  "employees": [
    { 
      "firstName": "Max",
      "lastName": "Mustermann"
    },
    {
      "firstName": "Clara",
      "lastName": "Carlston"
    }
  ]
}

An alternative Annotation would be the usage of list<Employee>. Which has the following advantages:

  • It contains more information as array<Employee> (the key is an int)
  • It contains more information then array<int, Employee> (the key has to start with 0 and has no missing steps)

list<...> is supported by Psalm, PHPStan and PHPStorm, but isn't by the PhpDocExtractor. When used with the serializer, it result's in a ReflectionException with Class App\...\list does not exist.

I searched through the logic used and found the following snippet in PhpDocTypeHelper:

if (0 === strpos($docType, 'array<') && $type instanceof Array_) {
    // array<value> is converted to x[] which is handled above
    // so it's only necessary to handle array<key, value> here
    $collectionKeyType = $this->getTypes($type->getKeyType())[0];

    $collectionValueTypes = $this->getTypes($type->getValueType());
    if (\count($collectionValueTypes) != 1) {
        // the Type class does not support union types yet, so assume that no type was defined
        $collectionValueType = null;
    } else {
        $collectionValueType = $collectionValueTypes[0];
    }

    return new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, $collectionKeyType, $collectionValueType);
}

It's my understanding, that it would be enough to extend that with the list type like the following:

if ((0 === strpos($docType, 'array<') || 0 === strpos($docType, 'list<')) && $type instanceof Array_) {

But I'm not 100% sure.

Would it be possible to extend it like this?

If so, would be happy to supply the Pull Request for it.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions