Skip to content
This repository was archived by the owner on Sep 15, 2022. It is now read-only.

Commit 1ca1240

Browse files
committed
Add alice class loader
Add alice class loader Add alice class loader
1 parent 9b992d6 commit 1ca1240

File tree

5 files changed

+187
-0
lines changed

5 files changed

+187
-0
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
namespace spec\Knp\FriendlyExtension\Alice;
4+
5+
use PhpSpec\ObjectBehavior;
6+
use Prophecy\Argument;
7+
use Symfony\Component\DependencyInjection\ContainerInterface;
8+
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
9+
use Symfony\Component\HttpKernel\KernelInterface;
10+
11+
class ClassLoaderSpec extends ObjectBehavior
12+
{
13+
function let(KernelInterface $kernel, ContainerInterface $container, $service)
14+
{
15+
$kernel->getContainer()->willReturn($container);
16+
$container->has('other')->willReturn(false);
17+
$container->has('service')->willReturn(true);
18+
$container->get('service')->willReturn($service);
19+
}
20+
21+
function it_is_initializable()
22+
{
23+
$this->shouldHaveType('Knp\FriendlyExtension\Alice\ClassLoader');
24+
}
25+
26+
function it_can_instanciate_a_class()
27+
{
28+
$this->instanciate('Knp\FriendlyExtension\Alice\ClassLoader')->shouldHaveType('Knp\FriendlyExtension\Alice\ClassLoader');
29+
}
30+
31+
function it_cant_instanciate_onject_with_constructor()
32+
{
33+
$this
34+
->shouldThrow(new \InvalidArgumentException('Constructor of Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException isn\'t supported because it needs arguments.'))
35+
->duringInstanciate('Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException');
36+
}
37+
38+
function it_can_instanciate_existing_services(KernelInterface $kernel, $service)
39+
{
40+
$this->beConstructedWith($kernel);
41+
$this->instanciate('@service')->shouldReturn($service);
42+
}
43+
44+
function it_cant_instaciate_unknown_service(KernelInterface $kernel)
45+
{
46+
$this->beConstructedWith($kernel);
47+
$this
48+
->shouldThrow(new ServiceNotFoundException('other'))
49+
->duringInstanciate('@other')
50+
;
51+
}
52+
53+
function it_cant_instantiate_service_without_kernel()
54+
{
55+
$this
56+
->shouldThrow(new \InvalidArgumentException('@service is not a valide class name'))
57+
->duringInstanciate('@service')
58+
;
59+
}
60+
}

src/Knp/FriendlyExtension.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Knp\FriendlyExtension\DependencyInjection\Compiler\KernelRegistrationPass;
1313
use Knp\FriendlyExtension\DependencyInjection\Compiler\ParameterBuildingPass;
1414
use Knp\FriendlyExtension\DependencyInjection\Compiler\RemoveUnavailableServicesPass;
15+
use Knp\FriendlyExtension\DependencyInjection\Compiler\SetArgumentToNullPass;
1516
use Knp\FriendlyExtension\DependencyInjection\Compiler\SymfonyServicePass;
1617
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
1718
use Symfony\Component\Config\FileLocator;
@@ -111,6 +112,7 @@ public function load(ContainerBuilder $container, array $config)
111112
{
112113
$loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/FriendlyExtension/services'));
113114
$loader->load('context.yml');
115+
$loader->load('alice.yml');
114116
$loader->load('core.yml');
115117
$loader->load('faker.yml');
116118
$loader->load('guesser.yml');
@@ -125,6 +127,7 @@ public function load(ContainerBuilder $container, array $config)
125127
$container->addCompilerPass(new ApiUrlTransitionPass);
126128
$container->addCompilerPass(new SymfonyServicePass);
127129
$container->addCompilerPass(new RemoveUnavailableServicesPass);
130+
$container->addCompilerPass(new SetArgumentToNullPass);
128131
$container->addCompilerPass(new ContextHelperRegistrationPass);
129132
}
130133

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
namespace Knp\FriendlyExtension\Alice;
4+
5+
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
6+
use Symfony\Component\HttpKernel\KernelInterface;
7+
8+
class ClassLoader
9+
{
10+
public function __construct(KernelInterface $kernel = null)
11+
{
12+
$this->kernel = $kernel;
13+
}
14+
15+
public function instanciate($name)
16+
{
17+
if (null !== $instance = $this->getFromClassname($name)) {
18+
19+
return $instance;
20+
}
21+
22+
if (null !== $instance = $this->getFromKernel($name)) {
23+
24+
return $instance;
25+
}
26+
27+
$message = sprintf('%s is not a valide class name', $name);
28+
29+
if (null !== $this->kernel) {
30+
$message .= ' or a valide service name';
31+
}
32+
33+
throw new \InvalidArgumentException($message);
34+
}
35+
36+
private function getFromClassname($class)
37+
{
38+
if ("@" === substr($class, 0, 1)) {
39+
40+
return;
41+
}
42+
43+
if (!class_exists($class)) {
44+
45+
throw new \InvalidArgumentException(sprintf('Can\'t find class "%s"'), $class);
46+
}
47+
48+
$rfl = new \ReflectionClass($class);
49+
$constructor = $rfl->getConstructor();
50+
51+
if (null !== $constructor && 0 !== $constructor->getNumberOfRequiredParameters()) {
52+
throw new \InvalidArgumentException(sprintf('Constructor of %s isn\'t supported because it needs arguments.', $class));
53+
}
54+
55+
return $rfl->newInstance();
56+
}
57+
58+
private function getFromKernel($service)
59+
{
60+
if ("@" !== substr($service, 0, 1)) {
61+
62+
return;
63+
}
64+
65+
if (null === $this->kernel) {
66+
67+
return;
68+
}
69+
70+
$service = substr($service, 1);
71+
72+
if (false === $this->kernel->getContainer()->has($service)) {
73+
74+
throw new ServiceNotFoundException($service);
75+
}
76+
77+
return $this->kernel->getContainer()->get($service);
78+
}
79+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace Knp\FriendlyExtension\DependencyInjection\Compiler;
4+
5+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
6+
use Symfony\Component\DependencyInjection\ContainerBuilder;
7+
8+
class SetArgumentToNullPass implements CompilerPassInterface
9+
{
10+
public function process(ContainerBuilder $container)
11+
{
12+
foreach ($container->findTaggedServiceIds('null-when-missing') as $id => $tags) {
13+
$definition = $container->getDefinition($id);
14+
$arguments = $definition->getArguments();
15+
foreach ($tags as $tag) {
16+
if (!isset($tag['service'])) {
17+
continue;
18+
}
19+
20+
if ($container->hasDefinition($tag['service'])) {
21+
continue;
22+
}
23+
24+
if ($container->hasAlias($tag['service'])) {
25+
continue;
26+
}
27+
28+
foreach ($arguments as $index => $argument) {
29+
if ($tag['service'] === (string)$argument) {
30+
$arguments[$index] = null;
31+
}
32+
}
33+
34+
$definition->setArguments($arguments);
35+
}
36+
}
37+
}
38+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
services:
2+
friendly.alice.class_loader:
3+
class: Knp\FriendlyExtension\Alice\ClassLoader
4+
arguments:
5+
- @friendly.symfony.kernel
6+
tags:
7+
- { name: null-when-missing, service: friendly.symfony.kernel }

0 commit comments

Comments
 (0)