Skip to content

Commit

Permalink
Fix #3 Symfony entries can reference PHP-DI entries
Browse files Browse the repository at this point in the history
  • Loading branch information
mnapoli committed Mar 28, 2016
1 parent 9cca3f9 commit 03b59e0
Show file tree
Hide file tree
Showing 14 changed files with 308 additions and 40 deletions.
1 change: 1 addition & 0 deletions .gitattributes
@@ -1,6 +1,7 @@
# .gitattributes
tests/ export-ignore
.travis.yml export-ignore
demo/ export-ignore

# Auto detect text files and perform LF normalization
* text=auto
3 changes: 1 addition & 2 deletions .travis.yml
@@ -1,15 +1,14 @@
language: php

php:
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm

matrix:
include:
- php: 5.4
- php: 5.5
env: dependencies=lowest

before_script:
Expand Down
16 changes: 11 additions & 5 deletions composer.json
Expand Up @@ -11,16 +11,22 @@
},
"autoload-dev": {
"psr-0": {
"UnitTest\\DI\\Bridge\\Symfony": "tests/",
"FunctionalTest\\DI\\Bridge\\Symfony": "tests/"
"UnitTest\\DI\\Bridge\\Symfony\\": "tests/",
"FunctionalTest\\DI\\Bridge\\Symfony\\": "tests/"
}
},
"require": {
"php": "~5.4|~7.0",
"php": "~5.5|~7.0",
"php-di/php-di": "^5.0",
"symfony/dependency-injection": "~2.0"
"symfony/dependency-injection": "~3.0",
"symfony/http-kernel": "~3.0",
"symfony/proxy-manager-bridge": "~3.0",
"symfony/config": "~3.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
"phpunit/phpunit": "~4.0",
"symfony/filesystem": "~3.0",
"symfony/yaml": "~3.0",
"symfony/debug": "~3.0"
}
}
112 changes: 112 additions & 0 deletions src/Kernel.php
@@ -0,0 +1,112 @@
<?php
/**
* PHP-DI
*
* @link http://php-di.org/
* @copyright Matthieu Napoli (http://mnapoli.fr/)
* @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
*/

namespace DI\Bridge\Symfony;

use Interop\Container\ContainerInterface;
use Symfony\Component\Debug\DebugClassLoader;
use Symfony\Component\DependencyInjection\Compiler\CheckExceptionOnInvalidReferenceBehaviorPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
* Customization of Symfony's kernel to setup PHP-DI.
*
* @author Matthieu Napoli <matthieu@mnapoli.fr>
*/
abstract class Kernel extends \Symfony\Component\HttpKernel\Kernel
{
/**
* @var ContainerInterface
*/
private $phpdiContainer;

public function __construct($environment, $debug)
{
parent::__construct($environment, $debug);
$this->disableDebugClassLoader();
}

/**
* @return ContainerInterface
*/
abstract protected function buildPHPDIContainer(\DI\ContainerBuilder $builder);

protected function getContainerBaseClass()
{
return 'DI\Bridge\Symfony\SymfonyContainerBridge';
}

protected function buildContainer()
{
$containerBuilder = parent::buildContainer();

$this->removeInvalidReferenceBehaviorPass($containerBuilder);

return $containerBuilder;
}

protected function initializeContainer()
{
parent::initializeContainer();

/** @var SymfonyContainerBridge $rootContainer */
$rootContainer = $this->getContainer();

$rootContainer->setFallbackContainer($this->getPHPDIContainer());
}

/**
* Remove the CheckExceptionOnInvalidReferenceBehaviorPass because
* it was not looking into PHP-DI's entries and thus throwing exceptions.
*
* @todo Replace it by an alternative that can search into PHP-DI too
* Problem: PHP-DI is not initialized when Symfony's container is compiled, because
* it depends on Symfony's container for fallback (cycle…)
*
* @param ContainerBuilder $container
*/
private function removeInvalidReferenceBehaviorPass(ContainerBuilder $container)
{
$passConfig = $container->getCompilerPassConfig();
$compilationPasses = $passConfig->getRemovingPasses();

foreach ($compilationPasses as $i => $pass) {
if ($pass instanceof CheckExceptionOnInvalidReferenceBehaviorPass) {
unset($compilationPasses[$i]);
break;
}
}

$passConfig->setRemovingPasses($compilationPasses);
}

private function disableDebugClassLoader()
{
if (!class_exists('Symfony\Component\Debug\DebugClassLoader')) {
return;
}

DebugClassLoader::disable();
}

/**
* @return ContainerInterface
*/
private function getPHPDIContainer()
{
if ($this->phpdiContainer === null) {
$builder = new \DI\ContainerBuilder();
$builder->wrapContainer($this->getContainer());

$this->phpdiContainer = $this->buildPHPDIContainer($builder);
}

return $this->phpdiContainer;
}
}
2 changes: 1 addition & 1 deletion src/SymfonyContainerBridge.php
Expand Up @@ -10,11 +10,11 @@
namespace DI\Bridge\Symfony;

use DI\NotFoundException;
use Interop\Container\ContainerInterface;
use Symfony\Component\DependencyInjection\Container as SymfonyContainer;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Interop\Container\ContainerInterface;

/**
* Replacement for the Symfony service container.
Expand Down
22 changes: 22 additions & 0 deletions tests/FunctionalTest/DI/Bridge/Symfony/AbstractFunctionalTest.php
@@ -0,0 +1,22 @@
<?php


namespace FunctionalTest\DI\Bridge\Symfony;

use FunctionalTest\DI\Bridge\Symfony\Fixtures\AppKernel;
use Symfony\Component\Filesystem\Filesystem;

abstract class AbstractFunctionalTest extends \PHPUnit_Framework_TestCase
{
protected function createKernel($configFile = 'empty.yml')
{
// Clear the cache
$fs = new Filesystem();
$fs->remove(__DIR__ . '/Fixtures/cache/dev');

$kernel = new AppKernel($configFile);
$kernel->boot();

return $kernel;
}
}
17 changes: 8 additions & 9 deletions tests/FunctionalTest/DI/Bridge/Symfony/ContainerAwareTest.php
Expand Up @@ -9,25 +9,24 @@

namespace FunctionalTest\DI\Bridge\Symfony;

use DI\Bridge\Symfony\SymfonyContainerBridge;
use DI\ContainerBuilder;
use FunctionalTest\DI\Bridge\Symfony\Fixtures\ContainerAwareController;

class ContainerAwareInterfaceTest extends \PHPUnit_Framework_TestCase
/**
* @coversNothing
*/
class ContainerAwareInterfaceTest extends AbstractFunctionalTest
{
/**
* @link https://github.com/PHP-DI/Symfony2-Bridge/issues/2
*/
public function testContainerAware()
{
$wrapper = new SymfonyContainerBridge();
$builder = new ContainerBuilder();
$builder->wrapContainer($wrapper);
$wrapper->setFallbackContainer($builder->build());
$kernel = $this->createKernel();
$container = $kernel->getContainer();

/** @var ContainerAwareController $class */
$class = $wrapper->get('FunctionalTest\DI\Bridge\Symfony\Fixtures\ContainerAwareController');
$class = $container->get('FunctionalTest\DI\Bridge\Symfony\Fixtures\ContainerAwareController');

$this->assertSame($wrapper, $class->container);
$this->assertSame($container, $class->container);
}
}
66 changes: 43 additions & 23 deletions tests/FunctionalTest/DI/Bridge/Symfony/ContainerInteractionTest.php
Expand Up @@ -10,49 +10,69 @@
namespace FunctionalTest\DI\Bridge\Symfony;

use DI\Bridge\Symfony\SymfonyContainerBridge;
use DI\ContainerBuilder;
use DI\Container;
use FunctionalTest\DI\Bridge\Symfony\Fixtures\Class1;
use FunctionalTest\DI\Bridge\Symfony\Fixtures\Class2;

class ContainerInteractionTest extends \PHPUnit_Framework_TestCase
/**
* Tests interactions between containers, i.e. entries that reference other entries in
* other containers.
*
* @coversNothing
*/
class ContainerInteractionTest extends AbstractFunctionalTest
{
/**
* @test Get a Symfony entry from PHP-DI's container
*/
public function phpdiGetInSymfony()
public function phpdi_should_get_entries_from_symfony()
{
$wrapper = new SymfonyContainerBridge();
$kernel = $this->createKernel('class2.yml');

$class2 = new Class2();
$wrapper->set('FunctionalTest\DI\Bridge\Symfony\Fixtures\Class2', $class2);
/** @var SymfonyContainerBridge $container */
$container = $kernel->getContainer();
/** @var Container $phpdiContainer */
$phpdiContainer = $container->getFallbackContainer();

$builder = new ContainerBuilder();
$builder->wrapContainer($wrapper);
$wrapper->setFallbackContainer($builder->build());
$phpdiContainer->set(
'foo',
\DI\object('FunctionalTest\DI\Bridge\Symfony\Fixtures\Class1')
->constructor(\DI\link('class2'))
);

/** @var Class1 $class1 */
$class1 = $wrapper->get('FunctionalTest\DI\Bridge\Symfony\Fixtures\Class1');
$class1 = $container->get('foo');

$this->assertSame($class2, $class1->param1);
$this->assertTrue($class1 instanceof Class1);
}

/**
* @test Get a PHP-DI entry from Symfony's container
*/
public function symfonyGetInPHPDI()
{
$kernel = $this->createKernel('class1.yml');

$class1 = $kernel->getContainer()->get('class1');

$this->assertTrue($class1 instanceof Class1);
}

/**
* @test Alias a Symfony entry from PHP-DI's container
*/
public function phpdiAliasToSymfony()
public function phpdi_aliases_can_reference_symfony_entries()
{
$wrapper = new SymfonyContainerBridge();
$kernel = $this->createKernel('class2.yml');

/** @var SymfonyContainerBridge $container */
$container = $kernel->getContainer();
/** @var Container $phpdiContainer */
$phpdiContainer = $container->getFallbackContainer();

$class2 = new Class2();
$wrapper->set('bar', $class2);
$phpdiContainer->set('foo', \DI\link('class2'));

$builder = new ContainerBuilder();
$builder->wrapContainer($wrapper);
$fallback = $builder->build();
// foo -> bar
$fallback->set('foo', \DI\link('bar'));
$wrapper->setFallbackContainer($fallback);
$class2 = $container->get('foo');

$this->assertSame($class2, $wrapper->get('foo'));
$this->assertTrue($class2 instanceof Class2);
}
}
2 changes: 2 additions & 0 deletions tests/FunctionalTest/DI/Bridge/Symfony/Fixtures/.gitignore
@@ -0,0 +1,2 @@
logs
cache
48 changes: 48 additions & 0 deletions tests/FunctionalTest/DI/Bridge/Symfony/Fixtures/AppKernel.php
@@ -0,0 +1,48 @@
<?php

namespace FunctionalTest\DI\Bridge\Symfony\Fixtures;

use DI\Bridge\Symfony\Kernel;
use DI\ContainerBuilder;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
private $configFile;

public function __construct($configFile)
{
$this->configFile = $configFile;

parent::__construct('dev', true);
}

protected function buildPHPDIContainer(ContainerBuilder $builder)
{
return $builder->build();
}

public function registerBundles()
{
return array();
}

public function registerContainerConfiguration(LoaderInterface $loader)
{
$loader->load(__DIR__ . '/config/' . $this->configFile);
}

protected function getContainerClass()
{
return $this->randomName();
}

private function randomName() {
$characters = 'abcdefghijklmnopqrstuvwxyz';
$str = '';
for ($i = 0; $i < 10; $i++) {
$str .= $characters[rand(0, strlen($characters) - 1)];
}
return $str;
}
}
@@ -0,0 +1,4 @@
services:
class1:
class: FunctionalTest\DI\Bridge\Symfony\Fixtures\Class1
arguments: [ '@FunctionalTest\\DI\\Bridge\\Symfony\\Fixtures\\Class2' ]
@@ -0,0 +1,3 @@
services:
class2:
class: FunctionalTest\DI\Bridge\Symfony\Fixtures\Class2
Empty file.

0 comments on commit 03b59e0

Please sign in to comment.