Completed
Push — master ( 8357fb...08edfb )
by Kévin
07:43
created

DunglasActionExtension::getClasses()   D

Complexity

Conditions 9
Paths 16

Size

Total Lines 44
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 44
rs 4.909
c 0
b 0
f 0
cc 9
eloc 27
nc 16
nop 1
1
<?php
2
3
/*
4
 * (c) Kévin Dunglas <[email protected]>
5
 *
6
 * This source file is subject to the MIT license that is bundled
7
 * with this source code in the file LICENSE.
8
 */
9
10
namespace Dunglas\ActionBundle\DependencyInjection;
11
12
use Symfony\Component\Config\Resource\DirectoryResource;
13
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
14
use Symfony\Component\DependencyInjection\ContainerBuilder;
15
use Symfony\Component\DependencyInjection\Reference;
16
use Symfony\Component\Finder\Finder;
17
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
18
19
/**
20
 * {@inheritdoc}
21
 *
22
 * @author Kévin Dunglas <[email protected]>
23
 */
24
class DunglasActionExtension extends Extension
25
{
26
    /**
27
     * {@inheritdoc}
28
     */
29
    public function load(array $configs, ContainerBuilder $container)
30
    {
31
        $configuration = new Configuration();
32
        $config = $this->processConfiguration($configuration, $configs);
33
        $kernelRootDir = $container->getParameter('kernel.root_dir');
34
35
        $directoryList = [];
36
        foreach ($config['directories'] as $pattern) {
37
            list($classes, $directories) = $this->getClasses($this->getDirectory($kernelRootDir, $pattern));
38
            $directoryList = array_merge($directoryList, $directories);
39
40
            foreach ($classes as $class) {
41
                $this->registerClass($container, $class, $config['tags'], $config['methods']);
42
            }
43
        }
44
45
        $directories = [];
46
        foreach ($directoryList as $directory => $v) {
47
            $directory = realpath($directory);
48
            $container->addResource(new DirectoryResource($directory, '/\.php$/'));
49
            $directories[$directory] = true;
50
        }
51
52
        $container->setParameter('dunglas_action.directories', $directories);
53
    }
54
55
    /**
56
     * @param string $kernelRootDir
57
     * @param string $pattern
58
     *
59
     * @return string
60
     */
61
    private function getDirectory($kernelRootDir, $pattern)
62
    {
63
        $firstCharacter = substr($pattern, 0, 1);
64
        if ('/' === $firstCharacter || DIRECTORY_SEPARATOR === $firstCharacter) {
65
            return $pattern;
66
        }
67
68
        return $kernelRootDir.DIRECTORY_SEPARATOR.$pattern;
69
    }
70
71
    /**
72
     * Gets the list of class names in the given directory.
73
     *
74
     * @param string $directory
75
     *
76
     * @return array
77
     */
78
    private function getClasses($directory)
79
    {
80
        $classes = [];
81
        $directoryList = [];
82
        $includedFiles = [];
83
84
        $finder = new Finder();
85
        try {
86
            $finder->in($directory)->files()->name('*.php');
87
        } catch (\InvalidArgumentException $e) {
88
            return [[], []];
89
        }
90
91
        foreach ($finder as $file) {
92
            $directoryList[$file->getPath()] = true;
93
            $sourceFile = $file->getRealpath();
94
            if (!preg_match('(^phar:)i', $sourceFile)) {
95
                $sourceFile = realpath($sourceFile);
96
            }
97
98
            require_once $sourceFile;
99
            $includedFiles[$sourceFile] = true;
100
        }
101
102
        $declared = get_declared_classes();
103
        foreach ($declared as $className) {
104
            $reflectionClass = new \ReflectionClass($className);
105
            $sourceFile = $reflectionClass->getFileName();
106
107
            if ($reflectionClass->isAbstract()) {
108
                continue;
109
            }
110
111
            if (method_exists($reflectionClass, 'isAnonymous') && $reflectionClass->isAnonymous()) {
112
                continue;
113
            }
114
115
            if (isset($includedFiles[$sourceFile])) {
116
                $classes[$className] = true;
117
            }
118
        }
119
120
        return [array_keys($classes), $directoryList];
121
    }
122
123
    /**
124
     * Registers an action in the container.
125
     *
126
     * @param ContainerBuilder $container
127
     * @param string           $className
128
     * @param array            $tags
129
     * @param string[]         $methods
130
     */
131
    private function registerClass(ContainerBuilder $container, $className, array $tags, array $methods)
132
    {
133
        if ($container->has($className)) {
134
            return;
135
        }
136
137
        $definition = $container->register($className, $className);
138
        if (method_exists($definition, 'setAutowiredMethods')) {
139
            $definition->setAutowiredMethods($methods);
140
        } else {
141
            $definition->setAutowired(true);
142
        }
143
144
        // Inject the container if applicable
145
        if (is_a($className, ContainerAwareInterface::class, true)) {
146
            $definition->addMethodCall('setContainer', [new Reference('service_container')]);
147
        }
148
149
        foreach ($tags as $tagClassName => $classTags) {
150
            if (!is_a($className, $tagClassName, true)) {
151
                continue;
152
            }
153
154
            foreach ($classTags as $classTag) {
155
                $definition->addTag($classTag[0], $classTag[1]);
156
            }
157
        }
158
    }
159
}
160