symfony: [Config] ClassExistenceResource::isFresh() throws fatal error on php 7.2.20/7.3.7 if a parent class is missing

Symfony version(s) affected: 3.4.29, 4.2.10, 4.3.2, 4.4-dev, 5.0-dev

Description
After upgrading to php 7.2.20 or 7.3.7, AutowirePass starts to throw ReflectionExceptions on cache warmup on installations with DoctrineBundle but without Twig and the Form and Validator components. This has been reported to the DoctrineBundle repository, but to me it rather looks like a DI issue.

How to reproduce

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 20
  • Comments: 82 (52 by maintainers)

Commits related to this issue

Most upvoted comments

I think we should ask @nikic to consider reverting the patch on PHP. There are many situations like this one in Symfony apps that used to work seamlessly, but won’t now.

That depends on what you’re asking for here. I can revert this change from 7.2 and 7.3. Enforcing this is not critical for those branches and if it causes breakage in Symfony I have no problem with reverting it.

For PHP 7.4 though this change is going to stay in some form. I can invest some effort to avoid throwing a fatal error, but if the request here is to restore the old behavior where you say Foo implements Bar and you can end up with a class Foo that does not actually implement Bar by throwing an exception during autoloading, then no, I’m not going to restore that behavior.

This topic looks like collection of complaints, but I for one am glad this PHP fix helped us discover this. It uncovered wrongly configured DI, with missing exclude folders in our case, unnecessarily making DI crawl through them. Additionally, these classes shouldn’t have even be available in production composer classmap, since they are working only when no --no-dev is used. So I applaud @nikic for keeping this in some form in PHP 7.4

All right, before this evolves into a StackOverflow thread on how to install specific php versions on various platforms:

  • If you encounter this problem, stay on php 7.2.19 or 7.3.6 untill 7.2.21 and 7.3.8 are released and packaged by your favorite package manager.
  • If you are brave enough, you can download the RC from here and compile it yourself. 😃
  • I you have already upgraded to 7.2.20/7.3.7 and cannot roll back or (or don’t know how to do that), here’s a workaround that allows you to use php 7.2.20/7.3.7 in the meantime.
  • This is still broken with php 7.4, but I am certain that we will find a solution before 7.4 gets a stable release.

@derrabus I don’t think so. Let me try to give a specific example of the problem I have in mind, though it’s somewhat convoluted:

// A.php
class A {
    public function foo($x): B {}
}
// B.php
class B extends A {
    public function foo($x): C {}
}
// C.php
class C extends B implements DoesNotExist {
}
// main.php
new C;

What happens now is the following:

  1. Autoload C. The class C is registered provisionally.
  2. Autoload the parent B. The class B is registered provisionally.
  3. Autoload the parent A. The class A is registered provisionally.
  4. The class A is verified (trivially) and registered fully.
  5. The class B is verified and registered fully (and may already be used). During the verification it makes use of the fact that C is a subclass of B, otherwise the return types would not be covariant.
  6. Autoload the interface DoesNotExist, which throws an exception.

After these steps have happened … what can we do now? We can’t fully register the class C, because the interface it implements does not exist. We can’t remove the provisionally registered class C either, because then a new class C that is not a subclass of B could be registered, and thus violate the variance assumption we made above.

I don’t really see a solution here that would allow us to gracefully recover while performing inheritance 😦

@elmariachi111 I don’t know how Heroku handles a situation like this, but there has to be a way to deploy to an older php release. Have you contacted their support yet?

If there really isn’t another way, my suggestion would be:

  • Have a look at the error message you’re getting. It should complain about a missing class or interface.
  • Find out, which package it belongs to (symfony/form, symfony/validator and psr/simple-cache are hot candidates).
  • Add that package to your dependencies.
  • If Flex makes any changes to your app because of the added packages, revert them.

That’ll bloat your vendors folder, but you should be able to run your application again. Please remember to revert that change once php 7.2.21 and 7.3.8 are out.

Remove Entity from exclude list in service.yaml fix for me.

`e̶x̶c̶l̶u̶d̶e̶:̶ ̶'̶.̶.̶/̶s̶r̶c̶/̶{̶D̶e̶p̶e̶n̶d̶e̶n̶c̶y̶I̶n̶j̶e̶c̶t̶i̶o̶n̶,̶E̶n̶t̶i̶t̶y̶,̶M̶i̶g̶r̶a̶t̶i̶o̶n̶s̶,̶T̶e̶s̶t̶s̶,̶K̶e̶r̶n̶e̶l̶.̶p̶h̶p̶}̶'̶`

`exclude: '../src/{DependencyInjection,Migrations,Tests,Kernel.php}'`

This bug takes 5 hours to get me here ;/

For all people using homebrew (on MacOS), you can use this formula. Copying it to your Formula folder will allow you to install php@7.2.19 (instead of only the php@7.2 option which installs 7.2.20).

Simply run this:

curl https://gist.githubusercontent.com/bossan/1cad9a8819808d1e684898af38541e52/raw/e05632cf98d9aff4f489422f4413d2a0678abfd0/php@7.2.19.rb > /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/php@7.2.19.rb && brew install php@7.2.19

For Ubuntu and Debian users you can now update your php binaries, oerdnj fix it : https://github.com/oerdnj/deb.sury.org/issues/1208#issuecomment-515014129

@arderyp The issue is already resolved in the upcoming PHP 7.2 and 7.3 releases. The remaining discussion is about what to do for PHP 7.4, where under my current understanding a change on the Symfony side will be necessary.

Reverted from 7.2 and 7.3 via https://github.com/php/php-src/commit/22ed362810c1b3a5ecb54ebd1d50d804c7fc3159, this change is only in 7.4 now.

I don’t see why, it’s ok to make DI crash when trying to crawl through invalid classes

@teohhanhui The class B cannot be removed at the point where we realize that the interface does not exist, because the class could already be in use. While mixing declarations and code goes against modern coding guidelines, from the language perspective writing code like this is allowed:

// B.php
class B extends A {
    public function foo($x): C {}
}
$b = new B;

Of course, we can’t drop the class B after someone has already created an object of it, or similar (a more “typical” use is via class_alias.)

I can invest some effort to avoid throwing a fatal error

Having thought about this for a bit, I think that covariance support in PHP 7.4 effectively makes this impossible. Due to cyclic dependencies, we sometimes have to link classes under the provision that other classes will either also successfully link or else be unusable. This means that we can’t just unregister the class stub if a parent/interface is not found, because then it would be possible to register a class with different methods under the same name and thus violate variance assumptions. The only thing we could do is leave behind a dead class stub, such that the class can neither be used, nor a class using the same name declared, which seems like a pretty bad idea.

When a class implements an interface that can not be found, PHP 7.2.20/7.3.7 throws an exception as well now. See https://github.com/symfony/symfony/issues/32396

I’ve managed to build a minimal reproducer.

https://github.com/derrabus/symfony-32395-reproducer

The problem seems to be ClassExistenceResource from the Config component. When calling isFresh(), a temporary class loader is registered:

https://github.com/symfony/symfony/blob/6811aaa8e087665769dd66c69fb3109d002196ef/src/Symfony/Component/Config/Resource/ClassExistenceResource.php#L71-L73

If a class cannot be loaded because its parent is not found, a ReflectionException is thrown:

https://github.com/symfony/symfony/blob/6811aaa8e087665769dd66c69fb3109d002196ef/src/Symfony/Component/Config/Resource/ClassExistenceResource.php#L121

Previously, this exception could be caught and handled. Since php 7.2.20/7.3.7 however, php seems to catch exceptions thrown during class loading and turns them into a fatal error that cannot be caught.

released two days ago, and yet still now showing on the official PHP downloads page: https://www.php.net/downloads.php

hopefully the various distros will distribute the updates soon.

It’s tagged but not released just yet. Releases tend to happen on Thursdays if I’m not mistaken, so likely tomorrow 😃

For reference, the core logic we need here is a way to make class checks resilient to missing parent classes. This means only these functions are concerned: get_class_methods, get_class_vars, get_parent_class, is_a, is_subclass_of, class_exists, class_implements, class_parents, trait_exists, defined, interface_exists, method_exists, property_exists, is_callable. We need to call them and have them return false in the case that currently crashes.

@nikic regarding your example from above https://github.com/symfony/symfony/issues/32395#issuecomment-509593220

Would it be possible to mark a provisionally registered class as “in use” as soon as it is referenced somewhere?

This way we could determine in an exception case if it is OK to remove the provisionally registered class and gracefully recover or if we have to throw a fatal error.

In your example this would mean that once class B is verified it would mark class C as “in use”. When loading DoesNotExist throws the exception then, we would throw a fatal error because class C is “in use”.

This way we could gracefully recover for most cases while resulting in a fatal error for the impossible cases, which sounds like a perfect trade-off to me.

Instead of using PHP RefelctionClass/class_exists which triggering autoload we might could adapt https://github.com/Roave/BetterReflection. It analyse code without triggering autoload.

You can reflect on classes that are not already loaded, without loading them Ability to reflect on classes directly from a string of PHP code

The new php bahavior seems fine to me. Can’t we fix our code to not rely on such strange behavior?

@teohhanhui reported that the unit tests of API platform 2.4 are failing because of a similar problem. This is what I get when I run them locally with php 7.3.7:

PHP Fatal error:  During class fetch: Uncaught ReflectionException: Class Psr\SimpleCache\CacheInterface not found in /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/cache/Psr16Cache.php:27
Stack trace:
#0 /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/debug/DebugClassLoader.php(160): require('/Volumes/Projec...')
#1 [internal function]: Symfony\Component\Debug\DebugClassLoader->loadClass('Symfony\\Compone...')
#2 /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/cache/Simple/Psr6Cache.php(21): spl_autoload_call('Symfony\\Compone...')
#3 /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/debug/DebugClassLoader.php(160): require('/Volumes/Projec...')
#4 [internal function]: Symfony\Component\Debug\DebugClassLoader->loadClass('Symfony\\Compone...')
#5 [internal function]: spl_autoload_call('Symfony\\Compone...')
#6 /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/config/Resource/ClassExistenceResource.php(78): class_exists('Symfony\\Compone...')
#7 /Volumes/Project in /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/cache/Psr16Cache.php on line 27
PHP Stack trace:
PHP   1. {main}() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/phpunit:0
PHP   2. PHPUnit\TextUI\Command::main() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/phpunit:61
PHP   3. PHPUnit\TextUI\Command->run() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/src/TextUI/Command.php:162
PHP   4. PHPUnit\TextUI\TestRunner->doRun() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/src/TextUI/Command.php:206
PHP   5. PHPUnit\Framework\TestSuite->run() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/src/TextUI/TestRunner.php:652
PHP   6. PHPUnit\Framework\TestSuite->run() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/src/Framework/TestSuite.php:746
PHP   7. PHPUnit\Framework\DataProviderTestSuite->run() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/src/Framework/TestSuite.php:746
PHP   8. ApiPlatform\Core\Tests\Bridge\Doctrine\Orm\Filter\BooleanFilterTest->run() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/src/Framework/TestSuite.php:746
PHP   9. PHPUnit\Framework\TestResult->run() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/src/Framework/TestCase.php:796
PHP  10. ApiPlatform\Core\Tests\Bridge\Doctrine\Orm\Filter\BooleanFilterTest->runBare() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/src/Framework/TestResult.php:693
PHP  11. ApiPlatform\Core\Tests\Bridge\Doctrine\Orm\Filter\BooleanFilterTest->setUp() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/src/Framework/TestCase.php:838
PHP  12. Symfony\Bundle\FrameworkBundle\Test\KernelTestCase::bootKernel() /Volumes/Projects/OpenSource/api-platform-core/src/Test/DoctrineOrmFilterTestCase.php:58
PHP  13. AppKernel->boot() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/framework-bundle/Test/KernelTestCase.php:69
PHP  14. AppKernel->initializeContainer() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/http-kernel/Kernel.php:133
PHP  15. AppKernel->dumpContainer() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/http-kernel/Kernel.php:571
PHP  16. Symfony\Component\DependencyInjection\Dumper\PhpDumper->dump() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/http-kernel/Kernel.php:740
PHP  17. Symfony\Component\DependencyInjection\Dumper\PhpDumper->addServices() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Dumper/PhpDumper.php:220
PHP  18. Symfony\Component\DependencyInjection\Dumper\PhpDumper->addService() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Dumper/PhpDumper.php:860
PHP  19. Symfony\Component\DependencyInjection\Dumper\PhpDumper->addInlineService() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Dumper/PhpDumper.php:725
PHP  20. Symfony\Component\DependencyInjection\Dumper\PhpDumper->addServiceInstance() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Dumper/PhpDumper.php:831
PHP  21. Symfony\Component\DependencyInjection\Dumper\PhpDumper->addNewInstance() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Dumper/PhpDumper.php:529
PHP  22. Symfony\Component\DependencyInjection\Dumper\PhpDumper->dumpValue() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Dumper/PhpDumper.php:913
PHP  23. Symfony\Component\DependencyInjection\Definition->getErrors() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Dumper/PhpDumper.php:1581
PHP  24. Symfony\Component\DependencyInjection\Compiler\AutowirePass->Symfony\Component\DependencyInjection\Compiler\{closure:/Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Compiler/AutowirePass.php:384-386}() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Definition.php:908
PHP  25. Symfony\Component\DependencyInjection\Compiler\AutowirePass->createTypeNotFoundMessage() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Compiler/AutowirePass.php:385
PHP  26. Symfony\Component\DependencyInjection\Compiler\AutowirePass->createTypeAlternatives() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Compiler/AutowirePass.php:404
PHP  27. Symfony\Component\DependencyInjection\Compiler\AutowirePass->populateAvailableTypes() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Compiler/AutowirePass.php:430
PHP  28. Symfony\Component\DependencyInjection\Compiler\AutowirePass->populateAvailableType() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Compiler/AutowirePass.php:322
PHP  29. Symfony\Component\DependencyInjection\ContainerBuilder->getReflectionClass() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Compiler/AutowirePass.php:336
PHP  30. Symfony\Component\Config\Resource\ClassExistenceResource->isFresh() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/ContainerBuilder.php:353
PHP  31. class_exists() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/config/Resource/ClassExistenceResource.php:78
PHP  32. spl_autoload_call() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/config/Resource/ClassExistenceResource.php:78
PHP  33. Symfony\Component\Debug\DebugClassLoader->loadClass() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/config/Resource/ClassExistenceResource.php:78
PHP  34. require() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/debug/DebugClassLoader.php:160
PHP  35. spl_autoload_call() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/cache/Simple/Psr6Cache.php:21
PHP  36. Symfony\Component\Debug\DebugClassLoader->loadClass() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/cache/Simple/Psr6Cache.php:21
PHP  37. require() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/debug/DebugClassLoader.php:160

Fatal error: During class fetch: Uncaught ReflectionException: Class Psr\SimpleCache\CacheInterface not found in /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/cache/Psr16Cache.php:27
Stack trace:
#0 /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/debug/DebugClassLoader.php(160): require('/Volumes/Projec...')
#1 [internal function]: Symfony\Component\Debug\DebugClassLoader->loadClass('Symfony\\Compone...')
#2 /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/cache/Simple/Psr6Cache.php(21): spl_autoload_call('Symfony\\Compone...')
#3 /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/debug/DebugClassLoader.php(160): require('/Volumes/Projec...')
#4 [internal function]: Symfony\Component\Debug\DebugClassLoader->loadClass('Symfony\\Compone...')
#5 [internal function]: spl_autoload_call('Symfony\\Compone...')
#6 /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/config/Resource/ClassExistenceResource.php(78): class_exists('Symfony\\Compone...')
#7 /Volumes/Project in /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/cache/Psr16Cache.php on line 27

Call Stack:
    0.0003     404976   1. {main}() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/phpunit:0
    0.0193    1609784   2. PHPUnit\TextUI\Command::main() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/phpunit:61
    0.0193    1609896   3. PHPUnit\TextUI\Command->run() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/src/TextUI/Command.php:162
    1.4202   36554680   4. PHPUnit\TextUI\TestRunner->doRun() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/src/TextUI/Command.php:206
    1.4628   37159168   5. PHPUnit\Framework\TestSuite->run() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/src/TextUI/TestRunner.php:652
   11.2949   67571272   6. PHPUnit\Framework\TestSuite->run() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/src/Framework/TestSuite.php:746
   11.2967   67571800   7. PHPUnit\Framework\DataProviderTestSuite->run() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/src/Framework/TestSuite.php:746
   11.2970   67572328   8. ApiPlatform\Core\Tests\Bridge\Doctrine\Orm\Filter\BooleanFilterTest->run() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/src/Framework/TestSuite.php:746
   11.2970   67572328   9. PHPUnit\Framework\TestResult->run() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/src/Framework/TestCase.php:796
   11.2971   67572328  10. ApiPlatform\Core\Tests\Bridge\Doctrine\Orm\Filter\BooleanFilterTest->runBare() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/src/Framework/TestResult.php:693
   11.2972   67588920  11. ApiPlatform\Core\Tests\Bridge\Doctrine\Orm\Filter\BooleanFilterTest->setUp() /Volumes/Projects/OpenSource/api-platform-core/vendor/phpunit/phpunit/src/Framework/TestCase.php:838
   11.2972   67588920  12. Symfony\Bundle\FrameworkBundle\Test\KernelTestCase::bootKernel() /Volumes/Projects/OpenSource/api-platform-core/src/Test/DoctrineOrmFilterTestCase.php:58
   11.2973   67589320  13. AppKernel->boot() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/framework-bundle/Test/KernelTestCase.php:69
   11.3328   67822144  14. AppKernel->initializeContainer() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/http-kernel/Kernel.php:133
   13.4362   87715560  15. AppKernel->dumpContainer() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/http-kernel/Kernel.php:571
   13.4398   88185024  16. Symfony\Component\DependencyInjection\Dumper\PhpDumper->dump() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/http-kernel/Kernel.php:740
   13.8365   92596272  17. Symfony\Component\DependencyInjection\Dumper\PhpDumper->addServices() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Dumper/PhpDumper.php:220
   13.8404   92758624  18. Symfony\Component\DependencyInjection\Dumper\PhpDumper->addService() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Dumper/PhpDumper.php:860
   13.8405   92760224  19. Symfony\Component\DependencyInjection\Dumper\PhpDumper->addInlineService() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Dumper/PhpDumper.php:725
   13.8405   92760688  20. Symfony\Component\DependencyInjection\Dumper\PhpDumper->addServiceInstance() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Dumper/PhpDumper.php:831
   13.8406   92761104  21. Symfony\Component\DependencyInjection\Dumper\PhpDumper->addNewInstance() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Dumper/PhpDumper.php:529
   13.8406   92761560  22. Symfony\Component\DependencyInjection\Dumper\PhpDumper->dumpValue() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Dumper/PhpDumper.php:913
   13.8406   92761936  23. Symfony\Component\DependencyInjection\Definition->getErrors() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Dumper/PhpDumper.php:1581
   13.8406   92761936  24. Symfony\Component\DependencyInjection\Compiler\AutowirePass->Symfony\Component\DependencyInjection\Compiler\{closure:/Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Compiler/AutowirePass.php:384-386}() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Definition.php:908
   13.8406   92761936  25. Symfony\Component\DependencyInjection\Compiler\AutowirePass->createTypeNotFoundMessage() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Compiler/AutowirePass.php:385
   13.8406   92762048  26. Symfony\Component\DependencyInjection\Compiler\AutowirePass->createTypeAlternatives() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Compiler/AutowirePass.php:404
   13.8406   92762048  27. Symfony\Component\DependencyInjection\Compiler\AutowirePass->populateAvailableTypes() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Compiler/AutowirePass.php:430
   13.8852   93801152  28. Symfony\Component\DependencyInjection\Compiler\AutowirePass->populateAvailableType() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Compiler/AutowirePass.php:322
   13.8852   93801152  29. Symfony\Component\DependencyInjection\ContainerBuilder->getReflectionClass() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/Compiler/AutowirePass.php:336
   13.8852   93801232  30. Symfony\Component\Config\Resource\ClassExistenceResource->isFresh() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/dependency-injection/ContainerBuilder.php:353
   13.8852   93801416  31. class_exists() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/config/Resource/ClassExistenceResource.php:78
   13.8852   93801496  32. spl_autoload_call() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/config/Resource/ClassExistenceResource.php:78
   13.8852   93801576  33. Symfony\Component\Debug\DebugClassLoader->loadClass() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/config/Resource/ClassExistenceResource.php:78
   13.8856   93804456  34. require('/Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/cache/Simple/Psr6Cache.php') /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/debug/DebugClassLoader.php:160
   13.8858   93805248  35. spl_autoload_call() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/cache/Simple/Psr6Cache.php:21
   13.8858   93805312  36. Symfony\Component\Debug\DebugClassLoader->loadClass() /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/cache/Simple/Psr6Cache.php:21
   13.8866   93845512  37. require('/Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/cache/Psr16Cache.php') /Volumes/Projects/OpenSource/api-platform-core/vendor/symfony/debug/DebugClassLoader.php:160

I’ve ran a quick test with my reproducer on php 7.3.8RC1. Looks like this issue is gone. 🎉

(edit: 7.2.21RC1 is fine as well.)

@arderyp : no it will also roll back php version if you installed 7.2.20 exactly when you execute sudo apt upgrade it will ask you for downgrade your php version to 7.2.19

Hey @karousn , the problem in the PHP version, not in Composer dependencies. Composer config platform version only affects installed dependencies. Just upgrade to 7.2.21 or downgrade to 7.2.19 your PHP version and do nothing in your project

Thanks for explanation @bocharsky-bw , so after some search to downgrade the php version to 7.2.19 you have two choices :

* Wait for [oerdnj/deb.sury.org#1208](https://github.com/oerdnj/deb.sury.org/issues/1208) if you used as ppa under debian fork distribution

* Do the [Apt pinning](https://wiki.debian.org/AptPreferences) by your self, if you realy understand what you do.

As example : apt-cache policy php7.2 # to check if you have php7.2.19 in version table sudo nano /etc/apt/preferences.d/php72 # create a file to add preferences

Put this in

Package: php7.2* Pin: version 7.2.19-0ubuntu0.18.04.1 Pin-Priority: 1200

After that you need to update & upgrade sudo apt update -y sudo apt upgrade

it well be the same for 7.3 version.

@karousn this only works if you haven’t already updated to 7.2.20 though, right? In other words, this won’t roll your PHP back, but prevent the 7.2.19 PHP from being upgraded to 7.2.20. Or is that wrong?

@arturslogins, you might have to uninstall and reinstall, which could be a pain if you have lots of complexity in dependencies and configurations. Hopefully 7.2.21/7.3.8 are tagged and published soon (I don’t know how long it typically takes after RCs are tagged)

I try to find a workaround with composer config platform but it dosn’t work for “php” : “7.2.19” with 7.2.20 installed !

7.3.8RC1 was tagged today

And 7.2.21 RC1 as well. Yay. 🎉

Would it be possible to mark a provisionally registered class as “in use” as soon as it is referenced somewhere?

@nikic this proposal seems like a nice idea, WDYT? The current situation is pretty scary for 7.4: it’s quite common to check for optional dependencies with a class_exists(), with resiliency relying on the possibility to recover from missing parents.

it’s ok to make DI crash when trying to crawl through invalid classes

@ostrolucky this logic exists in Config and in Cache components. Fixing the symptoms in DI still leaves the root issue open, which is what we’re discussing here.

I am experiencing this same issue after updating to 7.2.20 this morning. FYI I do have the Twig, Form, and Validator dependencies that are mentioned in the OP. I am seeing the error thrown by a Test class in a custom vendor dependency that my team is responsible for–I am not sure why a dependency’s test files are being parsed for caching.

Anyways, after reading the comments above, I am slightly confused. Do we think solving this requires making a change to Symfony or to PHP itself? If the later, are we really thinking the fix would only be deployed to 7.4.x and not 7.2.x and 7.3.x, or am I misundertsanding your most recent post @smoench? If I’m not, that would be highly unfortunate…

@smoench that would be an option, yes. However, there are a few more such services that were ok previously because they would be removed from the container and were never used. If this can’t be solved in the DI container, I’m open for such a change, but I’d rather not do that preemptively if it can be avoided.

Another solution would be fixing the doctrine bundle with registering the Symfony\Bridge\Doctrine\Form\DoctrineOrmTypeGuesser only as a service if the FormTypeGuesserInterface exists.

It contains the revert of the fix that caused the issue. https://github.com/php/php-src/commit/22ed362810c1b3a5ecb54ebd1d50d804c7fc3159

@teohhanhui got to downgrade back to php7.2. < 7.20 😦

api-platform users (im on mac using brew 7.3.7) - I had to add this to my composer.json file and all works after an update

"symfony/form": "4.2.*",

Hey @karousn , the problem in the PHP version, not in Composer dependencies. Composer config platform version only affects installed dependencies. Just upgrade to 7.2.21 or downgrade to 7.2.19 your PHP version and do nothing in your project

Thanks for explanation @bocharsky-bw , so after some search to downgrade the php version to 7.2.19 you have two choices :

  • Wait for oerdnj/deb.sury.org#1208 if you used as ppa under debian fork distribution
  • Do the Apt pinning by your self, if you realy understand what you do.

As example : apt-cache policy php7.2 # to check if you have php7.2.19 in version table sudo nano /etc/apt/preferences.d/php72 # create a file to add preferences

Put this in

Package: php7.2* Pin: version 7.2.19-0ubuntu0.18.04.1 Pin-Priority: 1200

After that you need to update & upgrade sudo apt update -y sudo apt upgrade

it well be the same for 7.3 version.

Hey @karousn , the problem in the PHP version, not in Composer dependencies. Composer config platform version only affects installed dependencies. Just upgrade to 7.2.21 or downgrade to 7.2.19 your PHP version and do nothing in your project

How to downgrade 7.2.20 to 7.2.19 ? i can’t find any example 😦 i’m not using docker

Hey @karousn , the problem in the PHP version, not in Composer dependencies. Composer config platform version only affects installed dependencies. Just upgrade to 7.2.21 or downgrade to 7.2.19 your PHP version and do nothing in your project

I have this issue when upgrading to php 7.2.20

PHP Fatal error: During class fetch: Uncaught ReflectionException: Class Symfony\Component\Templating\Helper\Helper not found in /var/www/html/project/vendor/symfony/security-bundle/Templating/Helper/LogoutUrlHelper.php:23

class LogoutUrlHelper extends Helper <<< class Helper does not exists

@teohhanhui it’s not released yet

Just for information, I experience this bug because of symfony/security-bundle which enforce a few services definitions to be loaded for various templating engines. In the SecurityExtension class it calls $loader->load('templating_php.xml'); which leads the container to have services which references classes from the symfony/templating component (which is not installed).

Thank you for the detailed explanation, @nikic! Return type contravariance really complicates this. 😕

@nikic Can we find another way to make autoloading failures recoverable somehow? To stay in your example, what the ClassExistenceResource tries to do here is to find out if the class Foo is available. If the answer is no because the interface Bar is missing, that’s fine. Nobody wants an incomplete class in that case.

When a class implements an interface that can not be found, PHP 7.2.20/7.3.7 throws an exception as well now. See #32396

No, php itself has always triggered an uncatchable fatal error, see https://3v4l.org/jogPE

The trick in ClassExistenceResource was to register a temporary proxy class loader that throws an exception instead that could be caught and handled. This does not work anymore, see https://3v4l.org/sSSJQ