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\NotificationBundle\DependencyInjection; |
||
15 | |||
16 | use Sonata\Doctrine\Mapper\DoctrineCollector; |
||
17 | use Sonata\EasyExtendsBundle\Mapper\DoctrineCollector as DeprecatedDoctrineCollector; |
||
18 | use Sonata\NotificationBundle\Backend\AMQPBackend; |
||
19 | use Sonata\NotificationBundle\Backend\MessageManagerBackend; |
||
20 | use Sonata\NotificationBundle\Model\MessageInterface; |
||
21 | use Symfony\Component\Config\FileLocator; |
||
22 | use Symfony\Component\DependencyInjection\ContainerBuilder; |
||
23 | use Symfony\Component\DependencyInjection\Definition; |
||
24 | use Symfony\Component\DependencyInjection\Loader; |
||
25 | use Symfony\Component\DependencyInjection\Reference; |
||
26 | use Symfony\Component\HttpKernel\DependencyInjection\Extension; |
||
27 | |||
28 | class SonataNotificationExtension extends Extension |
||
29 | { |
||
30 | /** |
||
31 | * @var int |
||
32 | */ |
||
33 | protected $amqpCounter = 0; |
||
34 | |||
35 | /** |
||
36 | * {@inheritdoc} |
||
37 | */ |
||
38 | public function load(array $configs, ContainerBuilder $container): void |
||
39 | { |
||
40 | $configuration = new Configuration(); |
||
41 | $config = $this->processConfiguration($configuration, $configs); |
||
42 | |||
43 | $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); |
||
44 | |||
45 | $this->checkConfiguration($config); |
||
46 | |||
47 | $loader->load('core.xml'); |
||
48 | |||
49 | $loader->load('backend.xml'); |
||
50 | $loader->load('consumer.xml'); |
||
51 | $loader->load('command.xml'); |
||
52 | |||
53 | $bundles = $container->getParameter('kernel.bundles'); |
||
54 | |||
55 | if ('sonata.notification.backend.doctrine' === $config['backend']) { |
||
56 | $loader->load('doctrine_orm.xml'); |
||
57 | $loader->load('selector.xml'); |
||
58 | $loader->load('event.xml'); |
||
59 | |||
60 | if (isset($bundles['FOSRestBundle'], $bundles['NelmioApiDocBundle'])) { |
||
61 | $loader->load('api_controllers.xml'); |
||
62 | $loader->load('api_form.xml'); |
||
63 | } |
||
64 | |||
65 | // for now, only support for ORM |
||
66 | if ($config['admin']['enabled'] && isset($bundles['SonataDoctrineORMAdminBundle'])) { |
||
67 | $loader->load('admin.xml'); |
||
68 | } |
||
69 | } |
||
70 | |||
71 | if ($config['consumers']['register_default']) { |
||
72 | $loader->load('default_consumers.xml'); |
||
73 | } |
||
74 | |||
75 | if (isset($bundles['LiipMonitorBundle'])) { |
||
76 | $loader->load('checkmonitor.xml'); |
||
77 | } |
||
78 | |||
79 | $container->setAlias('sonata.notification.backend', $config['backend'])->setPublic(true); |
||
80 | $container->setParameter('sonata.notification.backend', $config['backend']); |
||
81 | |||
82 | if (isset($bundles['SonataDoctrineBundle'])) { |
||
83 | $this->registerSonataDoctrineMapping($config); |
||
84 | } else { |
||
85 | // NEXT MAJOR: Remove next line and throw error when not registering SonataDoctrineBundle |
||
86 | $this->registerDoctrineMapping($config); |
||
87 | } |
||
88 | |||
89 | $this->registerParameters($container, $config); |
||
90 | $this->configureBackends($container, $config); |
||
91 | $this->configureClass($container, $config); |
||
92 | $this->configureListeners($container, $config); |
||
93 | $this->configureAdmin($container, $config); |
||
94 | } |
||
95 | |||
96 | /** |
||
97 | * @param array $config |
||
98 | */ |
||
99 | public function configureClass(ContainerBuilder $container, $config): void |
||
100 | { |
||
101 | // admin configuration |
||
102 | $container->setParameter('sonata.notification.admin.message.entity', $config['class']['message']); |
||
103 | |||
104 | // manager configuration |
||
105 | $container->setParameter('sonata.notification.manager.message.entity', $config['class']['message']); |
||
106 | } |
||
107 | |||
108 | /** |
||
109 | * @param array $config |
||
110 | */ |
||
111 | public function configureAdmin(ContainerBuilder $container, $config): void |
||
112 | { |
||
113 | $container->setParameter('sonata.notification.admin.message.class', $config['admin']['message']['class']); |
||
114 | $container->setParameter('sonata.notification.admin.message.controller', $config['admin']['message']['controller']); |
||
115 | $container->setParameter('sonata.notification.admin.message.translation_domain', $config['admin']['message']['translation']); |
||
116 | } |
||
117 | |||
118 | /** |
||
119 | * @param array $config |
||
120 | */ |
||
121 | public function registerParameters(ContainerBuilder $container, $config): void |
||
122 | { |
||
123 | $container->setParameter('sonata.notification.message.class', $config['class']['message']); |
||
124 | $container->setParameter('sonata.notification.admin.message.entity', $config['class']['message']); |
||
125 | } |
||
126 | |||
127 | /** |
||
128 | * @param array $config |
||
129 | */ |
||
130 | public function configureBackends(ContainerBuilder $container, $config): void |
||
131 | { |
||
132 | if (isset($config['backends']['rabbitmq']) && 'sonata.notification.backend.rabbitmq' === $config['backend']) { |
||
133 | $this->configureRabbitmq($container, $config); |
||
134 | } else { |
||
135 | $container->removeDefinition('sonata.notification.backend.rabbitmq'); |
||
136 | } |
||
137 | |||
138 | if (isset($config['backends']['doctrine']) && 'sonata.notification.backend.doctrine' === $config['backend']) { |
||
139 | $checkLevel = [ |
||
140 | MessageInterface::STATE_DONE => $config['backends']['doctrine']['states']['done'], |
||
141 | MessageInterface::STATE_ERROR => $config['backends']['doctrine']['states']['error'], |
||
142 | MessageInterface::STATE_IN_PROGRESS => $config['backends']['doctrine']['states']['in_progress'], |
||
143 | MessageInterface::STATE_OPEN => $config['backends']['doctrine']['states']['open'], |
||
144 | ]; |
||
145 | |||
146 | $pause = $config['backends']['doctrine']['pause']; |
||
147 | $maxAge = $config['backends']['doctrine']['max_age']; |
||
148 | $batchSize = $config['backends']['doctrine']['batch_size']; |
||
149 | $container |
||
150 | ->setAlias('sonata.notification.manager.message', $config['backends']['doctrine']['message_manager']) |
||
151 | ->setPublic(true); |
||
152 | |||
153 | $this->configureDoctrineBackends($container, $config, $checkLevel, $pause, $maxAge, $batchSize); |
||
0 ignored issues
–
show
|
|||
154 | } else { |
||
155 | $container->removeDefinition('sonata.notification.backend.doctrine'); |
||
156 | } |
||
157 | } |
||
158 | |||
159 | /** |
||
160 | * NEXT_MAJOR: Remove this method. |
||
161 | */ |
||
162 | public function registerDoctrineMapping(array $config): void |
||
163 | { |
||
164 | @trigger_error( |
||
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
![]() |
|||
165 | 'Using SonataEasyExtendsBundle is deprecated since sonata-project/notification-bundle 3.9. Please register SonataDoctrineBundle as a bundle instead.', |
||
166 | E_USER_DEPRECATED |
||
167 | ); |
||
168 | |||
169 | $collector = DeprecatedDoctrineCollector::getInstance(); |
||
170 | |||
171 | $collector->addIndex($config['class']['message'], 'idx_state', [ |
||
172 | 'state', |
||
173 | ]); |
||
174 | |||
175 | $collector->addIndex($config['class']['message'], 'idx_created_at', [ |
||
176 | 'created_at', |
||
177 | ]); |
||
178 | } |
||
179 | |||
180 | protected function checkConfiguration(array $config): void |
||
181 | { |
||
182 | if (isset($config['backends']) && \count($config['backends']) > 1) { |
||
183 | throw new \RuntimeException('more than one backend configured, you can have only one backend configuration'); |
||
184 | } |
||
185 | |||
186 | if (!isset($config['backends']['rabbitmq']) && 'sonata.notification.backend.rabbitmq' === $config['backend']) { |
||
187 | throw new \RuntimeException('Please configure the sonata_notification.backends.rabbitmq section'); |
||
188 | } |
||
189 | |||
190 | if (!isset($config['backends']['doctrine']) && 'sonata.notification.backend.doctrine' === $config['backend']) { |
||
191 | throw new \RuntimeException('Please configure the sonata_notification.backends.doctrine section'); |
||
192 | } |
||
193 | } |
||
194 | |||
195 | protected function configureListeners(ContainerBuilder $container, array $config): void |
||
196 | { |
||
197 | $ids = $config['iteration_listeners']; |
||
198 | |||
199 | if ('sonata.notification.backend.doctrine' === $config['backend']) { |
||
200 | // this one clean the unit of work after every iteration |
||
201 | $ids[] = 'sonata.notification.event.doctrine_optimize'; |
||
202 | |||
203 | if ($config['backends']['doctrine']['batch_size'] > 1) { |
||
204 | // if the backend is doctrine and the batch size > 1, then |
||
205 | // the unit of work must be cleaned wisely to avoid any issue |
||
206 | // while persisting entities |
||
207 | $ids = [ |
||
208 | 'sonata.notification.event.doctrine_backend_optimize', |
||
209 | ]; |
||
210 | } |
||
211 | } |
||
212 | |||
213 | $container->setParameter('sonata.notification.event.iteration_listeners', $ids); |
||
214 | } |
||
215 | |||
216 | /** |
||
217 | * @param bool $checkLevel |
||
218 | * @param int $pause |
||
219 | * @param int $maxAge |
||
220 | * @param int $batchSize |
||
221 | * |
||
222 | * @throws \RuntimeException |
||
223 | */ |
||
224 | protected function configureDoctrineBackends(ContainerBuilder $container, array $config, $checkLevel, $pause, $maxAge, $batchSize): void |
||
225 | { |
||
226 | $queues = $config['queues']; |
||
227 | $qBackends = []; |
||
228 | |||
229 | $definition = $container->getDefinition('sonata.notification.backend.doctrine'); |
||
230 | |||
231 | // no queue defined, set a default one |
||
232 | if (0 === \count($queues)) { |
||
233 | $queues = [[ |
||
234 | 'queue' => 'default', |
||
235 | 'default' => true, |
||
236 | 'types' => [], |
||
237 | ]]; |
||
238 | } |
||
239 | |||
240 | $defaultSet = false; |
||
241 | $declaredQueues = []; |
||
242 | |||
243 | foreach ($queues as $pos => &$queue) { |
||
244 | if (\in_array($queue['queue'], $declaredQueues, true)) { |
||
245 | throw new \RuntimeException('The doctrine backend does not support 2 identicals queue name, please rename one queue'); |
||
246 | } |
||
247 | |||
248 | $declaredQueues[] = $queue['queue']; |
||
249 | |||
250 | // make the configuration compatible with old code and rabbitmq |
||
251 | if (isset($queue['routing_key']) && \strlen($queue['routing_key']) > 0) { |
||
252 | $queue['types'] = [$queue['routing_key']]; |
||
253 | } |
||
254 | |||
255 | if (empty($queue['types']) && false === $queue['default']) { |
||
256 | throw new \RuntimeException('You cannot declared a doctrine queue with no type defined with default = false'); |
||
257 | } |
||
258 | |||
259 | if (!empty($queue['types']) && true === $queue['default']) { |
||
260 | throw new \RuntimeException('You cannot declared a doctrine queue with types defined with default = true'); |
||
261 | } |
||
262 | |||
263 | $id = $this->createDoctrineQueueBackend($container, $definition->getArgument(0), $checkLevel, $pause, $maxAge, $batchSize, $queue['queue'], $queue['types']); |
||
264 | $qBackends[$pos] = [ |
||
265 | 'types' => $queue['types'], |
||
266 | 'backend' => new Reference($id), |
||
267 | ]; |
||
268 | |||
269 | if (true === $queue['default']) { |
||
270 | if (true === $defaultSet) { |
||
271 | throw new \RuntimeException('You can only set one doctrine default queue in your sonata notification configuration.'); |
||
272 | } |
||
273 | |||
274 | $defaultSet = true; |
||
275 | $defaultQueue = $queue['queue']; |
||
276 | } |
||
277 | } |
||
278 | |||
279 | if (false === $defaultSet) { |
||
280 | throw new \RuntimeException('You need to specify a valid default queue for the doctrine backend!'); |
||
281 | } |
||
282 | |||
283 | $definition |
||
284 | ->replaceArgument(1, $queues) |
||
285 | ->replaceArgument(2, $defaultQueue) |
||
0 ignored issues
–
show
The variable
$defaultQueue does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
![]() |
|||
286 | ->replaceArgument(3, $qBackends) |
||
287 | ; |
||
288 | } |
||
289 | |||
290 | /** |
||
291 | * @param string $manager |
||
292 | * @param bool $checkLevel |
||
293 | * @param int $pause |
||
294 | * @param int $maxAge |
||
295 | * @param int $batchSize |
||
296 | * @param string $key |
||
297 | * |
||
298 | * @return string |
||
299 | */ |
||
300 | protected function createDoctrineQueueBackend(ContainerBuilder $container, $manager, $checkLevel, $pause, $maxAge, $batchSize, $key, array $types = []) |
||
301 | { |
||
302 | if ('' === $key) { |
||
303 | $id = 'sonata.notification.backend.doctrine.default_'.$this->amqpCounter++; |
||
304 | } else { |
||
305 | $id = 'sonata.notification.backend.doctrine.'.$key; |
||
306 | } |
||
307 | |||
308 | $definition = new Definition(MessageManagerBackend::class, [$manager, $checkLevel, $pause, $maxAge, $batchSize, $types]); |
||
309 | $definition->setPublic(false); |
||
310 | |||
311 | $container->setDefinition($id, $definition); |
||
312 | |||
313 | return $id; |
||
314 | } |
||
315 | |||
316 | protected function configureRabbitmq(ContainerBuilder $container, array $config): void |
||
317 | { |
||
318 | $queues = $config['queues']; |
||
319 | $connection = $config['backends']['rabbitmq']['connection']; |
||
320 | $baseExchange = $config['backends']['rabbitmq']['exchange']; |
||
321 | $amqBackends = []; |
||
322 | |||
323 | if (0 === \count($queues)) { |
||
324 | $queues = [[ |
||
325 | 'queue' => 'default', |
||
326 | 'default' => true, |
||
327 | 'routing_key' => '', |
||
328 | 'recover' => false, |
||
329 | 'dead_letter_exchange' => null, |
||
330 | 'dead_letter_routing_key' => null, |
||
331 | 'ttl' => null, |
||
332 | 'prefetch_count' => null, |
||
333 | ]]; |
||
334 | } |
||
335 | |||
336 | $deadLetterRoutingKeys = $this->getQueuesParameters('dead_letter_routing_key', $queues); |
||
337 | $routingKeys = $this->getQueuesParameters('routing_key', $queues); |
||
338 | |||
339 | foreach ($deadLetterRoutingKeys as $key) { |
||
340 | if (!\in_array($key, $routingKeys, true)) { |
||
341 | throw new \RuntimeException(sprintf( |
||
342 | 'You must configure the queue having the routing_key "%s" same as dead_letter_routing_key', |
||
343 | $key |
||
344 | )); |
||
345 | } |
||
346 | } |
||
347 | |||
348 | $declaredQueues = []; |
||
349 | |||
350 | $defaultSet = false; |
||
351 | foreach ($queues as $pos => $queue) { |
||
352 | if (\in_array($queue['queue'], $declaredQueues, true)) { |
||
353 | throw new \RuntimeException('The RabbitMQ backend does not support 2 identicals queue name, please rename one queue'); |
||
354 | } |
||
355 | |||
356 | $declaredQueues[] = $queue['queue']; |
||
357 | |||
358 | if ($queue['dead_letter_routing_key']) { |
||
359 | if (null === $queue['dead_letter_exchange']) { |
||
360 | throw new \RuntimeException( |
||
361 | 'dead_letter_exchange must be configured when dead_letter_routing_key is set' |
||
362 | ); |
||
363 | } |
||
364 | } |
||
365 | |||
366 | if (\in_array($queue['routing_key'], $deadLetterRoutingKeys, true)) { |
||
367 | $exchange = $this->getAMQPDeadLetterExchangeByRoutingKey($queue['routing_key'], $queues); |
||
368 | } else { |
||
369 | $exchange = $baseExchange; |
||
370 | } |
||
371 | |||
372 | $id = $this->createAMQPBackend( |
||
373 | $container, |
||
374 | $exchange, |
||
375 | $queue['queue'], |
||
376 | $queue['recover'], |
||
377 | $queue['routing_key'], |
||
378 | $queue['dead_letter_exchange'], |
||
379 | $queue['dead_letter_routing_key'], |
||
380 | $queue['ttl'], |
||
381 | $queue['prefetch_count'] |
||
382 | ); |
||
383 | |||
384 | $amqBackends[$pos] = [ |
||
385 | 'type' => $queue['routing_key'], |
||
386 | 'backend' => new Reference($id), |
||
387 | ]; |
||
388 | |||
389 | if (true === $queue['default']) { |
||
390 | if (true === $defaultSet) { |
||
391 | throw new \RuntimeException('You can only set one rabbitmq default queue in your sonata notification configuration.'); |
||
392 | } |
||
393 | $defaultSet = true; |
||
394 | $defaultQueue = $queue['routing_key']; |
||
395 | } |
||
396 | } |
||
397 | |||
398 | if (false === $defaultSet) { |
||
399 | throw new \RuntimeException('You need to specify a valid default queue for the rabbitmq backend!'); |
||
400 | } |
||
401 | |||
402 | $container->getDefinition('sonata.notification.backend.rabbitmq') |
||
403 | ->replaceArgument(0, $connection) |
||
404 | ->replaceArgument(1, $queues) |
||
405 | ->replaceArgument(2, $defaultQueue) |
||
0 ignored issues
–
show
The variable
$defaultQueue does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
![]() |
|||
406 | ->replaceArgument(3, $amqBackends) |
||
407 | ; |
||
408 | } |
||
409 | |||
410 | /** |
||
411 | * @param string $exchange |
||
412 | * @param string $name |
||
413 | * @param string $recover |
||
414 | * @param string $key |
||
415 | * @param string $deadLetterExchange |
||
416 | * @param string $deadLetterRoutingKey |
||
417 | * @param int|null $ttl |
||
418 | * @param int|null $prefetchCount |
||
419 | * |
||
420 | * @return string |
||
421 | */ |
||
422 | protected function createAMQPBackend(ContainerBuilder $container, $exchange, $name, $recover, $key = '', $deadLetterExchange = null, $deadLetterRoutingKey = null, $ttl = null, $prefetchCount = null) |
||
423 | { |
||
424 | $id = 'sonata.notification.backend.rabbitmq.'.$this->amqpCounter++; |
||
425 | |||
426 | $definition = new Definition( |
||
427 | AMQPBackend::class, |
||
428 | [ |
||
429 | $exchange, |
||
430 | $name, |
||
431 | $recover, |
||
432 | $key, |
||
433 | $deadLetterExchange, |
||
434 | $deadLetterRoutingKey, |
||
435 | $ttl, |
||
436 | $prefetchCount, |
||
437 | ] |
||
438 | ); |
||
439 | $definition->setPublic(false); |
||
440 | $container->setDefinition($id, $definition); |
||
441 | |||
442 | return $id; |
||
443 | } |
||
444 | |||
445 | private function registerSonataDoctrineMapping(array $config): void |
||
446 | { |
||
447 | $collector = DoctrineCollector::getInstance(); |
||
448 | |||
449 | $collector->addIndex($config['class']['message'], 'idx_state', ['state']); |
||
450 | $collector->addIndex($config['class']['message'], 'idx_created_at', ['created_at']); |
||
451 | } |
||
452 | |||
453 | /** |
||
454 | * @param string $name |
||
455 | * |
||
456 | * @return string[] |
||
457 | */ |
||
458 | private function getQueuesParameters($name, array $queues) |
||
459 | { |
||
460 | $params = array_unique(array_map(static function ($q) use ($name) { |
||
461 | return $q[$name]; |
||
462 | }, $queues)); |
||
463 | |||
464 | $idx = array_search(null, $params, true); |
||
465 | if (false !== $idx) { |
||
466 | unset($params[$idx]); |
||
467 | } |
||
468 | |||
469 | return $params; |
||
470 | } |
||
471 | |||
472 | /** |
||
473 | * @param string $key |
||
474 | * |
||
475 | * @return string |
||
476 | */ |
||
477 | private function getAMQPDeadLetterExchangeByRoutingKey($key, array $queues) |
||
478 | { |
||
479 | foreach ($queues as $queue) { |
||
480 | if ($queue['dead_letter_routing_key'] === $key) { |
||
481 | return $queue['dead_letter_exchange']; |
||
482 | } |
||
483 | } |
||
484 | } |
||
485 | } |
||
486 |
It seems like the type of the argument is not accepted by the function/method which you are calling.
In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.
We suggest to add an explicit type cast like in the following example: