sonata-project /
SonataAdminBundle
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 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 | final class ExtensionCompilerPass implements CompilerPassInterface |
||
| 25 | { |
||
| 26 | public function process(ContainerBuilder $container): void |
||
| 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(sprintf( |
||
| 70 | 'Unable to find extension service for id %s', |
||
| 71 | $extension |
||
| 72 | )); |
||
| 73 | } |
||
| 74 | |||
| 75 | $this->addExtension($targets, $id, $extension, $attributes); |
||
| 76 | } |
||
| 77 | } |
||
| 78 | |||
| 79 | foreach ($targets as $target => $extensions) { |
||
| 80 | $extensions = iterator_to_array($extensions); |
||
| 81 | krsort($extensions); |
||
| 82 | $admin = $container->getDefinition($target); |
||
| 83 | |||
| 84 | foreach (array_values($extensions) as $extension) { |
||
| 85 | $admin->addMethodCall('addExtension', [$extension]); |
||
| 86 | } |
||
| 87 | } |
||
| 88 | } |
||
| 89 | |||
| 90 | /** |
||
| 91 | * @param string $id |
||
| 92 | * |
||
| 93 | * @return array |
||
| 94 | */ |
||
| 95 | private function getExtensionsForAdmin($id, Definition $admin, ContainerBuilder $container, array $extensionMap) |
||
| 96 | { |
||
| 97 | $extensions = []; |
||
| 98 | $classReflection = $subjectReflection = null; |
||
| 99 | |||
| 100 | $excludes = $extensionMap['excludes']; |
||
| 101 | unset($extensionMap['excludes']); |
||
| 102 | |||
| 103 | foreach ($extensionMap as $type => $subjects) { |
||
| 104 | foreach ($subjects as $subject => $extensionList) { |
||
| 105 | if ('admins' === $type) { |
||
| 106 | if ($id === $subject) { |
||
| 107 | $extensions = array_merge($extensions, $extensionList); |
||
| 108 | } |
||
| 109 | } else { |
||
| 110 | $class = $this->getManagedClass($admin, $container); |
||
| 111 | if (!class_exists($class)) { |
||
| 112 | continue; |
||
| 113 | } |
||
| 114 | $classReflection = new \ReflectionClass($class); |
||
| 115 | $subjectReflection = new \ReflectionClass($subject); |
||
| 116 | } |
||
| 117 | |||
| 118 | if ('instanceof' === $type) { |
||
| 119 | if ($subjectReflection->getName() === $classReflection->getName() || $classReflection->isSubclassOf($subject)) { |
||
| 120 | $extensions = array_merge($extensions, $extensionList); |
||
| 121 | } |
||
| 122 | } |
||
| 123 | |||
| 124 | if ('implements' === $type) { |
||
| 125 | if ($classReflection->implementsInterface($subject)) { |
||
| 126 | $extensions = array_merge($extensions, $extensionList); |
||
| 127 | } |
||
| 128 | } |
||
| 129 | |||
| 130 | if ('extends' === $type) { |
||
| 131 | if ($classReflection->isSubclassOf($subject)) { |
||
| 132 | $extensions = array_merge($extensions, $extensionList); |
||
| 133 | } |
||
| 134 | } |
||
| 135 | |||
| 136 | if ('uses' === $type) { |
||
| 137 | if ($this->hasTrait($classReflection, $subject)) { |
||
|
0 ignored issues
–
show
|
|||
| 138 | $extensions = array_merge($extensions, $extensionList); |
||
| 139 | } |
||
| 140 | } |
||
| 141 | } |
||
| 142 | } |
||
| 143 | |||
| 144 | if (isset($excludes[$id])) { |
||
| 145 | $extensions = array_diff_key($extensions, $excludes[$id]); |
||
| 146 | } |
||
| 147 | |||
| 148 | return $extensions; |
||
| 149 | } |
||
| 150 | |||
| 151 | /** |
||
| 152 | * Resolves the class argument of the admin to an actual class (in case of %parameter%). |
||
| 153 | * |
||
| 154 | * @return string |
||
| 155 | */ |
||
| 156 | private function getManagedClass(Definition $admin, ContainerBuilder $container) |
||
| 157 | { |
||
| 158 | return $container->getParameterBag()->resolveValue($admin->getArgument(1)); |
||
| 159 | } |
||
| 160 | |||
| 161 | /** |
||
| 162 | * @return array an array with the following structure. |
||
| 163 | * |
||
| 164 | * [ |
||
| 165 | * 'excludes' => ['<admin_id>' => ['<extension_id>' => ['priority' => <int>]]], |
||
| 166 | * 'admins' => ['<admin_id>' => ['<extension_id>' => ['priority' => <int>]]], |
||
| 167 | * 'implements' => ['<interface>' => ['<extension_id>' => ['priority' => <int>]]], |
||
| 168 | * 'extends' => ['<class>' => ['<extension_id>' => ['priority' => <int>]]], |
||
| 169 | * 'instanceof' => ['<class>' => ['<extension_id>' => ['priority' => <int>]]], |
||
| 170 | * 'uses' => ['<trait>' => ['<extension_id>' => ['priority' => <int>]]], |
||
| 171 | * ] |
||
| 172 | */ |
||
| 173 | private function flattenExtensionConfiguration(array $config) |
||
| 174 | { |
||
| 175 | $extensionMap = [ |
||
| 176 | 'excludes' => [], |
||
| 177 | 'admins' => [], |
||
| 178 | 'implements' => [], |
||
| 179 | 'extends' => [], |
||
| 180 | 'instanceof' => [], |
||
| 181 | 'uses' => [], |
||
| 182 | ]; |
||
| 183 | |||
| 184 | foreach ($config as $extension => $options) { |
||
| 185 | $optionsMap = array_intersect_key($options, $extensionMap); |
||
| 186 | |||
| 187 | foreach ($optionsMap as $key => $value) { |
||
| 188 | foreach ($value as $source) { |
||
| 189 | if (!isset($extensionMap[$key][$source])) { |
||
| 190 | $extensionMap[$key][$source] = []; |
||
| 191 | } |
||
| 192 | $extensionMap[$key][$source][$extension]['priority'] = $options['priority']; |
||
| 193 | } |
||
| 194 | } |
||
| 195 | } |
||
| 196 | |||
| 197 | return $extensionMap; |
||
| 198 | } |
||
| 199 | |||
| 200 | /** |
||
| 201 | * @return bool |
||
| 202 | */ |
||
| 203 | private function hasTrait(\ReflectionClass $class, $traitName) |
||
| 204 | { |
||
| 205 | if (\in_array($traitName, $class->getTraitNames(), true)) { |
||
| 206 | return true; |
||
| 207 | } |
||
| 208 | |||
| 209 | if (!$parentClass = $class->getParentClass()) { |
||
| 210 | return false; |
||
| 211 | } |
||
| 212 | |||
| 213 | return $this->hasTrait($parentClass, $traitName); |
||
| 214 | } |
||
| 215 | |||
| 216 | /** |
||
| 217 | * Add extension configuration to the targets array. |
||
| 218 | */ |
||
| 219 | private function addExtension( |
||
| 220 | array &$targets, |
||
| 221 | string $target, |
||
| 222 | string $extension, |
||
| 223 | array $attributes |
||
| 224 | ): void { |
||
| 225 | if (!isset($targets[$target])) { |
||
| 226 | $targets[$target] = new \SplPriorityQueue(); |
||
| 227 | } |
||
| 228 | |||
| 229 | $priority = $attributes['priority'] ?? 0; |
||
| 230 | $targets[$target]->insert(new Reference($extension), $priority); |
||
| 231 | } |
||
| 232 | } |
||
| 233 |
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: