Completed
Push — 3.x ( fd3361...37d5d4 )
by Grégoire
04:42
created

ExtensionCompilerPass::getExtensionsForAdmin()   F

Complexity

Conditions 16
Paths 492

Size

Total Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 55
rs 2.1055
c 0
b 0
f 0
cc 16
nc 492
nop 4

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\AdminBundle\DependencyInjection\Compiler;
15
16
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
17
use Symfony\Component\DependencyInjection\ContainerBuilder;
18
use Symfony\Component\DependencyInjection\Definition;
19
use Symfony\Component\DependencyInjection\Reference;
20
21
/**
22
 * @author Thomas Rabaix <[email protected]>
23
 */
24
class ExtensionCompilerPass implements CompilerPassInterface
25
{
26
    public function process(ContainerBuilder $container)
27
    {
28
        $universalExtensions = [];
29
        $targets = [];
30
31
        foreach ($container->findTaggedServiceIds('sonata.admin.extension') as $id => $tags) {
32
            foreach ($tags as $attributes) {
33
                $target = false;
34
35
                if (isset($attributes['target'])) {
36
                    $target = $attributes['target'];
37
                }
38
39
                if (isset($attributes['global']) && $attributes['global']) {
40
                    $universalExtensions[$id] = $attributes;
41
                }
42
43
                if (!$target || !$container->hasDefinition($target)) {
44
                    continue;
45
                }
46
47
                $this->addExtension($targets, $target, $id, $attributes);
48
            }
49
        }
50
51
        $extensionConfig = $container->getParameter('sonata.admin.extension.map');
52
        $extensionMap = $this->flattenExtensionConfiguration($extensionConfig);
53
54
        foreach ($container->findTaggedServiceIds('sonata.admin') as $id => $attributes) {
55
            $admin = $container->getDefinition($id);
56
57
            if (!isset($targets[$id])) {
58
                $targets[$id] = new \SplPriorityQueue();
59
            }
60
61
            foreach ($universalExtensions as $extension => $extensionAttributes) {
62
                $this->addExtension($targets, $id, $extension, $extensionAttributes);
63
            }
64
65
            $extensions = $this->getExtensionsForAdmin($id, $admin, $container, $extensionMap);
66
67
            foreach ($extensions as $extension => $attributes) {
68
                if (!$container->has($extension)) {
69
                    throw new \InvalidArgumentException(
70
                        sprintf('Unable to find extension service for id %s', $extension)
71
                    );
72
                }
73
74
                $this->addExtension($targets, $id, $extension, $attributes);
75
            }
76
        }
77
78
        foreach ($targets as $target => $extensions) {
79
            $extensions = iterator_to_array($extensions);
80
            krsort($extensions);
81
            $admin = $container->getDefinition($target);
82
83
            foreach (array_values($extensions) as $extension) {
84
                $admin->addMethodCall('addExtension', [$extension]);
85
            }
86
        }
87
    }
88
89
    /**
90
     * @param string $id
91
     *
92
     * @return array
93
     */
94
    protected function getExtensionsForAdmin($id, Definition $admin, ContainerBuilder $container, array $extensionMap)
95
    {
96
        $extensions = [];
97
        $classReflection = $subjectReflection = null;
98
99
        $excludes = $extensionMap['excludes'];
100
        unset($extensionMap['excludes']);
101
102
        foreach ($extensionMap as $type => $subjects) {
103
            foreach ($subjects as $subject => $extensionList) {
104
                if ('admins' == $type) {
105
                    if ($id == $subject) {
106
                        $extensions = array_merge($extensions, $extensionList);
107
                    }
108
                } else {
109
                    $class = $this->getManagedClass($admin, $container);
110
                    if (!class_exists($class)) {
111
                        continue;
112
                    }
113
                    $classReflection = new \ReflectionClass($class);
114
                    $subjectReflection = new \ReflectionClass($subject);
115
                }
116
117
                if ('instanceof' == $type) {
118
                    if ($subjectReflection->getName() == $classReflection->getName() || $classReflection->isSubclassOf($subject)) {
119
                        $extensions = array_merge($extensions, $extensionList);
120
                    }
121
                }
122
123
                if ('implements' == $type) {
124
                    if ($classReflection->implementsInterface($subject)) {
125
                        $extensions = array_merge($extensions, $extensionList);
126
                    }
127
                }
128
129
                if ('extends' == $type) {
130
                    if ($classReflection->isSubclassOf($subject)) {
131
                        $extensions = array_merge($extensions, $extensionList);
132
                    }
133
                }
134
135
                if ('uses' == $type) {
136
                    if ($this->hasTrait($classReflection, $subject)) {
0 ignored issues
show
Bug introduced by
It seems like $classReflection defined by $subjectReflection = null on line 97 can be null; however, Sonata\AdminBundle\Depen...ompilerPass::hasTrait() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
137
                        $extensions = array_merge($extensions, $extensionList);
138
                    }
139
                }
140
            }
141
        }
142
143
        if (isset($excludes[$id])) {
144
            $extensions = array_diff_key($extensions, $excludes[$id]);
145
        }
146
147
        return $extensions;
148
    }
149
150
    /**
151
     * Resolves the class argument of the admin to an actual class (in case of %parameter%).
152
     *
153
     * @return string
154
     */
155
    protected function getManagedClass(Definition $admin, ContainerBuilder $container)
156
    {
157
        return $container->getParameterBag()->resolveValue($admin->getArgument(1));
158
    }
159
160
    /**
161
     * @return array an array with the following structure.
162
     *
163
     * [
164
     *     'excludes'   => ['<admin_id>'  => ['<extension_id>' => ['priority' => <int>]]],
165
     *     'admins'     => ['<admin_id>'  => ['<extension_id>' => ['priority' => <int>]]],
166
     *     'implements' => ['<interface>' => ['<extension_id>' => ['priority' => <int>]]],
167
     *     'extends'    => ['<class>'     => ['<extension_id>' => ['priority' => <int>]]],
168
     *     'instanceof' => ['<class>'     => ['<extension_id>' => ['priority' => <int>]]],
169
     *     'uses'       => ['<trait>'     => ['<extension_id>' => ['priority' => <int>]]],
170
     * ]
171
     */
172
    protected function flattenExtensionConfiguration(array $config)
173
    {
174
        $extensionMap = [
175
            'excludes' => [],
176
            'admins' => [],
177
            'implements' => [],
178
            'extends' => [],
179
            'instanceof' => [],
180
            'uses' => [],
181
        ];
182
183
        foreach ($config as $extension => $options) {
184
            $optionsMap = array_intersect_key($options, $extensionMap);
185
186
            foreach ($optionsMap as $key => $value) {
187
                foreach ($value as $source) {
188
                    if (!isset($extensionMap[$key][$source])) {
189
                        $extensionMap[$key][$source] = [];
190
                    }
191
                    $extensionMap[$key][$source][$extension]['priority'] = $options['priority'];
192
                }
193
            }
194
        }
195
196
        return $extensionMap;
197
    }
198
199
    /**
200
     * @return bool
201
     */
202
    protected function hasTrait(\ReflectionClass $class, $traitName)
203
    {
204
        if (\in_array($traitName, $class->getTraitNames())) {
205
            return true;
206
        }
207
208
        if (!$parentClass = $class->getParentClass()) {
209
            return false;
210
        }
211
212
        return $this->hasTrait($parentClass, $traitName);
213
    }
214
215
    /**
216
     * Add extension configuration to the targets array.
217
     *
218
     * @param string $target
219
     * @param string $extension
220
     */
221
    private function addExtension(array &$targets, $target, $extension, array $attributes)
222
    {
223
        if (!isset($targets[$target])) {
224
            $targets[$target] = new \SplPriorityQueue();
225
        }
226
227
        $priority = $attributes['priority'] ?? 0;
228
        $targets[$target]->insert(new Reference($extension), $priority);
229
    }
230
}
231