Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

ServiceSubscriberTrait doesn't fully support PHP8 union types #43913

Copy link
Copy link
Closed
@gmichard

Description

@gmichard
Issue body actions

Symfony version(s) affected

5.3.10

Description

An error occurs when using the Service Subscriber Trait on a service class that have a method with more than one type (i.e. union type) for return type.

Call to undefined method ReflectionUnionType::isBuiltin() at /vendor/symfony/service-contracts/ServiceSubscriberTrait.php:45

How to reproduce

  1. Create a brand new Symfony Application (download documentation)
    symfony new --full my_project
  2. Run the application
  3. Add a new service that use the ServiceSubscriberTrait and that have a method with more that one type for return type. See the following ExampleHandler class.
  4. Clear your cache, run the symfony bin/console
<?php
// File: src/Service/ExampleHandler.php

namespace App\Service;

use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Http\Authentication\UserAuthenticatorInterface;
use Symfony\Contracts\Service\ServiceSubscriberInterface;
use Symfony\Contracts\Service\ServiceSubscriberTrait;

class ExampleHandler implements ServiceSubscriberInterface
{
    use ServiceSubscriberTrait;

    protected function getSomethingOrOther(): UserAuthenticatorInterface|UserInterface|null
    {
        // No body. Return null for example. Only the return type is important for this issue.
        return null;
    }
}

With this example, if I add a xDebug breakpoint on the line 45 of ServiceSubscriberTrait
with the condition of suspension:

self::class === $method->getDeclaringClass()->name && ($returnType = $method->getReturnType()) && !method_exists($returnType, 'isBuiltin')

(Note the !method_exists($returnType, 'isBuiltin'))

I can check the values of $method and $returnType

// xDebug evaluation of two variables on line 45:

$method = ReflectionMethod::__set_state(array(
   'name' => 'getSomethingOrOther',
   'class' => 'App\\Service\\ExampleHandler',
));
// The method "getSomethingOrOther" is the cause of the error.

$returnType = ReflectionUnionType::__set_state(array(
))
// The return type is an instance of `ReflectionUnionType`
// which have no method called "isBuiltin".

Possible Solution

  • We can see in the PHP ReflectionType class "Changelog" section that:

    (Version 8.0.0) ReflectionType has become abstract and ReflectionType::isBuiltin() has been moved to ReflectionNamedType::isBuiltin().

  • We can see that the issue [3.4] Fix support for PHP8 union types #37340 by @nicolas-grekas mentions the "PHP8 Union types" and the isBuilin method.
    But the modifications doesn't seem to fix this use case.

    The reason is that these might return a ReflectionUnionType now, which has no getName() method (same for `isBuiltin() btw.)

Maybe a test is missing before calling the isBuiltin (like $returnType instanceof ReflectionNamedType), but I'm not sure about the expections here and the eventual side-effects.

Additional Context

Error trace in console.

Symfony\Component\ErrorHandler\Error\UndefinedMethodError^ {#25
  #message: "Attempted to call an undefined method named "isBuiltin" of class "ReflectionUnionType"."
  #code: 0
  #file: "./vendor/symfony/contracts/Service/ServiceSubscriberTrait.php"
  #line: 45
  trace: {
    ./vendor/symfony/contracts/Service/ServiceSubscriberTrait.php:45 { …}
    ./vendor/symfony/dependency-injection/Compiler/RegisterServiceSubscribersPass.php:74 { …}
    ./vendor/symfony/dependency-injection/Compiler/AbstractRecursivePass.php:81 { …}
    ./vendor/symfony/dependency-injection/Compiler/RegisterServiceSubscribersPass.php:36 { …}
    ./vendor/symfony/dependency-injection/Compiler/AbstractRecursivePass.php:46 { …}
    ./vendor/symfony/dependency-injection/Compiler/Compiler.php:91 { …}
    ./vendor/symfony/dependency-injection/ContainerBuilder.php:749 { …}
    ./vendor/symfony/http-kernel/Kernel.php:545 { …}
    ./vendor/symfony/http-kernel/Kernel.php:786 { …}
    ./vendor/symfony/http-kernel/Kernel.php:125 { …}
    ./src/Kernel.php:19 {
      App\Kernel->boot()^
      › {
      ›     parent::boot();
      › }
    }
    ./vendor/symfony/framework-bundle/Console/Application.php:168 { …}
    ./vendor/symfony/framework-bundle/Console/Application.php:74 { …}
    ./vendor/symfony/console/Application.php:167 { …}
    ./bin/console:43 { …}
  }
}

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

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.