Skip to content

[PropertyInfo] Cannot create union with "mixed" standalone type, when using mixed|type in docblock. #63276

@crtl

Description

@crtl

Symfony version(s) affected

7.3

Description

After upgrading from symfony/property-info 7.2 to any version starting from 7.3 I will get the following error when attempting to describe properties with union mixed types, for example mixed|string.

Symfony\Component\TypeInfo\Exception\InvalidArgumentException: Cannot create union with "mixed" standalone type.
/app/vendor/symfony/type-info/Type/UnionType.php:53
/app/vendor/symfony/type-info/TypeFactoryTrait.php:332
/app/vendor/symfony/property-info/Util/PhpDocTypeHelper.php:182
/app/vendor/symfony/property-info/Extractor/PhpDocExtractor.php:231
/app/vendor/symfony/property-info/PropertyInfoExtractor.php:70
/app/test/Integration/PropertyInfoTest.php:72

How to reproduce

<?php

namespace App\Test\Integration;


use PHPUnit\Framework\TestCase;
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;

class TestClass {
    /**
     * @var string|mixed
     */
    public mixed $property;
}


class PropertyInfoExtractorFactory
{
    private PropertyInfoExtractor $extractor;

    public function __construct(
        private readonly PhpDocExtractor $phpDocExtractor,
        private readonly ReflectionExtractor $reflectionExtractor,
    ) {
    }

    public function create(): PropertyInfoExtractor
    {
        if (!isset($this->extractor)) {
            // list of PropertyListExtractorInterface (any iterable)
            $listExtractors = [$this->reflectionExtractor];

            // list of PropertyTypeExtractorInterface (any iterable)
            $typeExtractors = [$this->phpDocExtractor, $this->reflectionExtractor];

            // list of PropertyDescriptionExtractorInterface (any iterable)
            $descriptionExtractors = [$this->phpDocExtractor];

            // list of PropertyAccessExtractorInterface (any iterable)
            $accessExtractors = [$this->reflectionExtractor];

            // list of PropertyInitializableExtractorInterface (any iterable)
            $propertyInitializableExtractors = [$this->reflectionExtractor];

            $this->extractor = new PropertyInfoExtractor(
                $listExtractors,
                $typeExtractors,
                $descriptionExtractors,
                $accessExtractors,
                $propertyInitializableExtractors,
            );
        }

        return $this->extractor;
    }
}


class PropertyInfoTest extends TestCase
{

    public function testReproduction(): void {
        $factory = new PropertyInfoExtractorFactory(
            new PhpDocExtractor(),
            new ReflectionExtractor(),
        );

        $propertyInfo = $factory->create();
        // will throw here
        $type = $propertyInfo->getType(TestClass::class, "property");
    }
}

Possible Solution

Removing union and using string only solves the problem but i was expecting union with mixed.

Additional Context

{
    "type": "symfony-bundle",
    "require": {
        "php": ">=8.2",
        "symfony/http-kernel": "^7.2 | ^8.0",
        "symfony/validator": "^7.2 | ^8.0",
        "symfony/dependency-injection": "^7.2 | ^8.0",
        "symfony/property-info": "^7.2 | ^8.0",
        "symfony/cache": "^7.1 | ^8.0",
        "symfony/framework-bundle": "^7.1 | ^8.0",
        "psr/log": "^3.0",
        "phpdocumentor/reflection-docblock": "^5.6"
    },
    "require-dev": {
        "phpunit/phpunit": "^10",
        "friendsofphp/php-cs-fixer": "^v3.93.1",
        "jaschilz/php-coverage-badger": "^2.0",
        "phpstan/phpstan": "^2.1",
        "phpstan/phpstan-phpunit": "^2.0",
        "phpstan/phpstan-symfony": "^2.0",
        "symfony/phpunit-bridge": "^7.1 | ^8.0"
    }
}

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