Skip to content

Commit

Permalink
bug #25932 Don't stop PSR-4 service discovery if a parent class is mi…
Browse files Browse the repository at this point in the history
…ssing (derrabus)

This PR was merged into the 3.4 branch.

Discussion
----------

Don't stop PSR-4 service discovery if a parent class is missing

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #25929
| License       | MIT
| Doc PR        | N/A

Commits
-------

3d6c3ba Don't stop PSR-4 service discovery if a parent class is missing.
  • Loading branch information
fabpot committed Jan 29, 2018
2 parents d5ff094 + 3d6c3ba commit a956a76
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 7 deletions.
25 changes: 21 additions & 4 deletions src/Symfony/Component/DependencyInjection/Loader/FileLoader.php
Expand Up @@ -60,11 +60,16 @@ public function registerClasses(Definition $prototype, $namespace, $resource, $e
$interfaces = array();
$singlyImplemented = array();

foreach ($classes as $class) {
foreach ($classes as $class => $errorMessage) {
if (interface_exists($class, false)) {
$interfaces[] = $class;
} else {
$this->setDefinition($class, unserialize($serializedPrototype));
$this->setDefinition($class, $definition = unserialize($serializedPrototype));
if (null !== $errorMessage) {
$definition->addError($errorMessage);

continue;
}
foreach (class_implements($class, false) as $interface) {
$singlyImplemented[$interface] = isset($singlyImplemented[$interface]) ? false : $class;
}
Expand Down Expand Up @@ -139,13 +144,25 @@ private function findClasses($namespace, $pattern, $excludePattern)
if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+$/', $class)) {
continue;
}

try {
$r = $this->container->getReflectionClass($class);
} catch (\ReflectionException $e) {
$classes[$class] = sprintf(
'While discovering services from namespace "%s", an error was thrown when processing the class "%s": "%s".',
$namespace,
$class,
$e->getMessage()
);
continue;
}
// check to make sure the expected class exists
if (!$r = $this->container->getReflectionClass($class)) {
if (!$r) {
throw new InvalidArgumentException(sprintf('Expected to find class "%s" in file "%s" while importing services from resource "%s", but it was not found! Check the namespace prefix used with the resource.', $class, $path, $pattern));
}

if ($r->isInstantiable() || $r->isInterface()) {
$classes[] = $class;
$classes[$class] = null;
}
}

Expand Down
@@ -0,0 +1,7 @@
<?php

namespace Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\BadClasses;

class MissingParent extends MissingClass
{
}
Expand Up @@ -9,7 +9,7 @@
->tag('baz');
$di->load(Prototype::class.'\\', '../Prototype')
->autoconfigure()
->exclude('../Prototype/{OtherDir}')
->exclude('../Prototype/{OtherDir,BadClasses}')
->factory('f')
->deprecate('%service_id%')
->args(array(0))
Expand Down
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\" resource="../Prototype/*" exclude="../Prototype/{OtherDir}" />
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\" resource="../Prototype/*" exclude="../Prototype/{OtherDir,BadClasses}" />
</services>
</container>
@@ -1,4 +1,4 @@
services:
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\:
resource: ../Prototype
exclude: '../Prototype/{OtherDir}'
exclude: '../Prototype/{OtherDir,BadClasses}'
Expand Up @@ -25,6 +25,7 @@
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\BadClasses\MissingParent;
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\OtherDir\AnotherSub\DeeperBaz;
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\OtherDir\Baz;
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo;
Expand Down Expand Up @@ -163,6 +164,26 @@ public function testNestedRegisterClasses()
$this->assertFalse($alias->isPrivate());
}

public function testMissingParentClass()
{
$container = new ContainerBuilder();
$container->setParameter('bad_classes_dir', 'BadClasses');
$loader = new TestFileLoader($container, new FileLocator(self::$fixturesPath.'/Fixtures'));

$loader->registerClasses(
(new Definition())->setPublic(false),
'Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\BadClasses\\',
'Prototype/%bad_classes_dir%/*'
);

$this->assertTrue($container->has(MissingParent::class));

$this->assertSame(
array('While discovering services from namespace "Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\BadClasses\", an error was thrown when processing the class "Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\BadClasses\MissingParent": "Class Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\BadClasses\MissingClass not found".'),
$container->getDefinition(MissingParent::class)->getErrors()
);
}

/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
* @expectedExceptionMessageRegExp /Expected to find class "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\Prototype\\Bar" in file ".+" while importing services from resource "Prototype\/Sub\/\*", but it was not found\! Check the namespace prefix used with the resource/
Expand Down

0 comments on commit a956a76

Please sign in to comment.