Completed
Push — 3.x ( 097117...746d06 )
by Grégoire
04:29
created

CRUDController::createAction()   D

Complexity

Conditions 15
Paths 30

Size

Total Lines 114

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 114
rs 4.7333
c 0
b 0
f 0
cc 15
nc 30
nop 0

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
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 56 and the first side effect is on line 50.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
3
/*
4
 * This file is part of the Sonata Project package.
5
 *
6
 * (c) Thomas Rabaix <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sonata\AdminBundle\Controller;
13
14
use Doctrine\Common\Inflector\Inflector;
15
use Psr\Log\LoggerInterface;
16
use Psr\Log\NullLogger;
17
use Sonata\AdminBundle\Admin\AdminInterface;
18
use Sonata\AdminBundle\Admin\FieldDescriptionCollection;
19
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
20
use Sonata\AdminBundle\Exception\LockException;
21
use Sonata\AdminBundle\Exception\ModelManagerException;
22
use Sonata\AdminBundle\Templating\TemplateRegistryInterface;
23
use Sonata\AdminBundle\Util\AdminObjectAclData;
24
use Sonata\AdminBundle\Util\AdminObjectAclManipulator;
25
use Symfony\Bridge\Twig\AppVariable;
26
use Symfony\Bridge\Twig\Command\DebugCommand;
27
use Symfony\Bridge\Twig\Extension\FormExtension;
28
use Symfony\Bridge\Twig\Form\TwigRenderer;
29
use Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait;
30
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
31
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
32
use Symfony\Component\DependencyInjection\ContainerInterface;
33
use Symfony\Component\Form\Form;
34
use Symfony\Component\Form\FormRenderer;
35
use Symfony\Component\Form\FormView;
36
use Symfony\Component\HttpFoundation\JsonResponse;
37
use Symfony\Component\HttpFoundation\RedirectResponse;
38
use Symfony\Component\HttpFoundation\Request;
39
use Symfony\Component\HttpFoundation\Response;
40
use Symfony\Component\HttpKernel\Exception\HttpException;
41
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
42
use Symfony\Component\PropertyAccess\PropertyAccess;
43
use Symfony\Component\PropertyAccess\PropertyPath;
44
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
45
use Symfony\Component\Security\Csrf\CsrfToken;
46
47
// BC for Symfony < 3.3 where this trait does not exist
48
// NEXT_MAJOR: Remove the polyfill and inherit from \Symfony\Bundle\FrameworkBundle\Controller\Controller again
49
if (!trait_exists(ControllerTrait::class)) {
50
    require_once __DIR__.'/PolyfillControllerTrait.php';
51
}
52
53
/**
54
 * @author Thomas Rabaix <[email protected]>
55
 */
56
class CRUDController implements ContainerAwareInterface
57
{
58
    // NEXT_MAJOR: Don't use these traits anymore (inherit from Controller instead)
59
    use ControllerTrait, ContainerAwareTrait {
60
        ControllerTrait::render as originalRender;
61
    }
62
63
    /**
64
     * The related Admin class.
65
     *
66
     * @var AdminInterface
67
     */
68
    protected $admin;
69
70
    /**
71
     * The template registry of the related Admin class.
72
     *
73
     * @var TemplateRegistryInterface
74
     */
75
    private $templateRegistry;
76
77
    // BC for Symfony 3.3 where ControllerTrait exists but does not contain get() and has() methods.
78
    public function __call($method, $arguments)
79
    {
80
        if (\in_array($method, ['get', 'has'])) {
81
            return \call_user_func_array([$this->container, $method], $arguments);
82
        }
83
84
        if (method_exists($this, 'proxyToControllerClass')) {
85
            return $this->proxyToControllerClass($method, $arguments);
0 ignored issues
show
Documentation Bug introduced by
The method proxyToControllerClass does not exist on object<Sonata\AdminBundl...troller\CRUDController>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
86
        }
87
88
        throw new \LogicException('Call to undefined method '.__CLASS__.'::'.$method);
89
    }
90
91
    public function setContainer(ContainerInterface $container = null)
92
    {
93
        $this->container = $container;
94
95
        $this->configure();
96
    }
97
98
    /**
99
     * NEXT_MAJOR: Remove this method.
100
     *
101
     * @see renderWithExtraParams()
102
     *
103
     * @param string $view       The view name
104
     * @param array  $parameters An array of parameters to pass to the view
105
     *
106
     * @return Response A Response instance
107
     *
108
     * @deprecated since version 3.27, to be removed in 4.0. Use Sonata\AdminBundle\Controller\CRUDController::renderWithExtraParams() instead.
109
     */
110
    public function render($view, array $parameters = [], Response $response = null)
111
    {
112
        @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
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.');
}
Loading history...
113
            'Method '.__CLASS__.'::render has been renamed to '.__CLASS__.'::renderWithExtraParams.',
114
            E_USER_DEPRECATED
115
        );
116
117
        return $this->renderWithExtraParams($view, $parameters, $response);
118
    }
119
120
    /**
121
     * Renders a view while passing mandatory parameters on to the template.
122
     *
123
     * @param string $view The view name
124
     *
125
     * @return Response A Response instance
126
     */
127
    public function renderWithExtraParams($view, array $parameters = [], Response $response = null)
128
    {
129
        if (!$this->isXmlHttpRequest()) {
130
            $parameters['breadcrumbs_builder'] = $this->get('sonata.admin.breadcrumbs_builder');
131
        }
132
        $parameters['admin'] = isset($parameters['admin']) ?
133
            $parameters['admin'] :
134
            $this->admin;
135
136
        $parameters['base_template'] = isset($parameters['base_template']) ?
137
            $parameters['base_template'] :
138
            $this->getBaseTemplate();
139
140
        $parameters['admin_pool'] = $this->get('sonata.admin.pool');
141
142
        //NEXT_MAJOR: Remove method alias and use $this->render() directly.
143
        return $this->originalRender($view, $parameters, $response);
144
    }
145
146
    /**
147
     * List action.
148
     *
149
     * @throws AccessDeniedException If access is not granted
150
     *
151
     * @return Response
152
     */
153
    public function listAction()
154
    {
155
        $request = $this->getRequest();
156
157
        $this->admin->checkAccess('list');
158
159
        $preResponse = $this->preList($request);
160
        if (null !== $preResponse) {
161
            return $preResponse;
162
        }
163
164
        if ($listMode = $request->get('_list_mode')) {
165
            $this->admin->setListMode($listMode);
166
        }
167
168
        $datagrid = $this->admin->getDatagrid();
169
        $formView = $datagrid->getForm()->createView();
170
171
        // set the theme for the current Admin Form
172
        $this->setFormTheme($formView, $this->admin->getFilterTheme());
173
174
        // NEXT_MAJOR: Remove this line and use commented line below it instead
175
        $template = $this->admin->getTemplate('list');
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
176
        // $template = $this->templateRegistry->getTemplate('list');
177
178
        return $this->renderWithExtraParams($template, [
179
            'action' => 'list',
180
            'form' => $formView,
181
            'datagrid' => $datagrid,
182
            'csrf_token' => $this->getCsrfToken('sonata.batch'),
183
            'export_formats' => $this->has('sonata.admin.admin_exporter') ?
184
                $this->get('sonata.admin.admin_exporter')->getAvailableFormats($this->admin) :
185
                $this->admin->getExportFormats(),
186
        ], null);
187
    }
188
189
    /**
190
     * Execute a batch delete.
191
     *
192
     * @throws AccessDeniedException If access is not granted
193
     *
194
     * @return RedirectResponse
195
     */
196
    public function batchActionDelete(ProxyQueryInterface $query)
197
    {
198
        $this->admin->checkAccess('batchDelete');
199
200
        $modelManager = $this->admin->getModelManager();
201
202
        try {
203
            $modelManager->batchDelete($this->admin->getClass(), $query);
204
            $this->addFlash(
205
                'sonata_flash_success',
206
                $this->trans('flash_batch_delete_success', [], 'SonataAdminBundle')
207
            );
208
        } catch (ModelManagerException $e) {
209
            $this->handleModelManagerException($e);
210
            $this->addFlash(
211
                'sonata_flash_error',
212
                $this->trans('flash_batch_delete_error', [], 'SonataAdminBundle')
213
            );
214
        }
215
216
        return $this->redirectToList();
217
    }
218
219
    /**
220
     * Delete action.
221
     *
222
     * @param int|string|null $id
223
     *
224
     * @throws NotFoundHttpException If the object does not exist
225
     * @throws AccessDeniedException If access is not granted
226
     *
227
     * @return Response|RedirectResponse
228
     */
229
    public function deleteAction($id)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
230
    {
231
        $request = $this->getRequest();
232
        $id = $request->get($this->admin->getIdParameter());
233
        $object = $this->admin->getObject($id);
234
235
        if (!$object) {
236
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
237
        }
238
239
        $this->checkParentChildAssociation($request, $object);
240
241
        $this->admin->checkAccess('delete', $object);
242
243
        $preResponse = $this->preDelete($request, $object);
244
        if (null !== $preResponse) {
245
            return $preResponse;
246
        }
247
248
        if ('DELETE' == $this->getRestMethod()) {
249
            // check the csrf token
250
            $this->validateCsrfToken('sonata.delete');
251
252
            $objectName = $this->admin->toString($object);
253
254
            try {
255
                $this->admin->delete($object);
256
257
                if ($this->isXmlHttpRequest()) {
258
                    return $this->renderJson(['result' => 'ok'], 200, []);
259
                }
260
261
                $this->addFlash(
262
                    'sonata_flash_success',
263
                    $this->trans(
264
                        'flash_delete_success',
265
                        ['%name%' => $this->escapeHtml($objectName)],
266
                        'SonataAdminBundle'
267
                    )
268
                );
269
            } catch (ModelManagerException $e) {
270
                $this->handleModelManagerException($e);
271
272
                if ($this->isXmlHttpRequest()) {
273
                    return $this->renderJson(['result' => 'error'], 200, []);
274
                }
275
276
                $this->addFlash(
277
                    'sonata_flash_error',
278
                    $this->trans(
279
                        'flash_delete_error',
280
                        ['%name%' => $this->escapeHtml($objectName)],
281
                        'SonataAdminBundle'
282
                    )
283
                );
284
            }
285
286
            return $this->redirectTo($object);
287
        }
288
289
        // NEXT_MAJOR: Remove this line and use commented line below it instead
290
        $template = $this->admin->getTemplate('delete');
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
291
        // $template = $this->templateRegistry->getTemplate('delete');
292
293
        return $this->renderWithExtraParams($template, [
294
            'object' => $object,
295
            'action' => 'delete',
296
            'csrf_token' => $this->getCsrfToken('sonata.delete'),
297
        ], null);
298
    }
299
300
    /**
301
     * Edit action.
302
     *
303
     * @param int|string|null $id
304
     *
305
     * @throws NotFoundHttpException If the object does not exist
306
     * @throws \RuntimeException     If no editable field is defined
307
     * @throws AccessDeniedException If access is not granted
308
     *
309
     * @return Response|RedirectResponse
310
     */
311
    public function editAction($id = null)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
312
    {
313
        $request = $this->getRequest();
314
        // the key used to lookup the template
315
        $templateKey = 'edit';
316
317
        $id = $request->get($this->admin->getIdParameter());
318
        $existingObject = $this->admin->getObject($id);
319
320
        if (!$existingObject) {
321
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
322
        }
323
324
        $this->checkParentChildAssociation($request, $existingObject);
325
326
        $this->admin->checkAccess('edit', $existingObject);
327
328
        $preResponse = $this->preEdit($request, $existingObject);
329
        if (null !== $preResponse) {
330
            return $preResponse;
331
        }
332
333
        $this->admin->setSubject($existingObject);
334
        $objectId = $this->admin->getNormalizedIdentifier($existingObject);
335
336
        $form = $this->admin->getForm();
337
        \assert($form instanceof Form);
338
339
        if (!\is_array($fields = $form->all()) || 0 === \count($fields)) {
340
            throw new \RuntimeException(
341
                'No editable field defined. Did you forget to implement the "configureFormFields" method?'
342
            );
343
        }
344
345
        $form->setData($existingObject);
346
        $form->handleRequest($request);
347
348
        if ($form->isSubmitted()) {
349
            $isFormValid = $form->isValid();
350
351
            // persist if the form was valid and if in preview mode the preview was approved
352
            if ($isFormValid && (!$this->isInPreviewMode() || $this->isPreviewApproved())) {
353
                $submittedObject = $form->getData();
354
                $this->admin->setSubject($submittedObject);
355
356
                try {
357
                    $existingObject = $this->admin->update($submittedObject);
358
359
                    if ($this->isXmlHttpRequest()) {
360
                        return $this->renderJson([
361
                            'result' => 'ok',
362
                            'objectId' => $objectId,
363
                            'objectName' => $this->escapeHtml($this->admin->toString($existingObject)),
364
                        ], 200, []);
365
                    }
366
367
                    $this->addFlash(
368
                        'sonata_flash_success',
369
                        $this->trans(
370
                            'flash_edit_success',
371
                            ['%name%' => $this->escapeHtml($this->admin->toString($existingObject))],
372
                            'SonataAdminBundle'
373
                        )
374
                    );
375
376
                    // redirect to edit mode
377
                    return $this->redirectTo($existingObject);
378
                } catch (ModelManagerException $e) {
379
                    $this->handleModelManagerException($e);
380
381
                    $isFormValid = false;
382
                } catch (LockException $e) {
383
                    $this->addFlash('sonata_flash_error', $this->trans('flash_lock_error', [
384
                        '%name%' => $this->escapeHtml($this->admin->toString($existingObject)),
385
                        '%link_start%' => '<a href="'.$this->admin->generateObjectUrl('edit', $existingObject).'">',
386
                        '%link_end%' => '</a>',
387
                    ], 'SonataAdminBundle'));
388
                }
389
            }
390
391
            // show an error message if the form failed validation
392
            if (!$isFormValid) {
393
                if (!$this->isXmlHttpRequest()) {
394
                    $this->addFlash(
395
                        'sonata_flash_error',
396
                        $this->trans(
397
                            'flash_edit_error',
398
                            ['%name%' => $this->escapeHtml($this->admin->toString($existingObject))],
399
                            'SonataAdminBundle'
400
                        )
401
                    );
402
                }
403
            } elseif ($this->isPreviewRequested()) {
404
                // enable the preview template if the form was valid and preview was requested
405
                $templateKey = 'preview';
406
                $this->admin->getShow();
407
            }
408
        }
409
410
        $formView = $form->createView();
411
        // set the theme for the current Admin Form
412
        $this->setFormTheme($formView, $this->admin->getFormTheme());
413
414
        // NEXT_MAJOR: Remove this line and use commented line below it instead
415
        $template = $this->admin->getTemplate($templateKey);
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
416
        // $template = $this->templateRegistry->getTemplate($templateKey);
417
418
        return $this->renderWithExtraParams($template, [
419
            'action' => 'edit',
420
            'form' => $formView,
421
            'object' => $existingObject,
422
            'objectId' => $objectId,
423
        ], null);
424
    }
425
426
    /**
427
     * Batch action.
428
     *
429
     * @throws NotFoundHttpException If the HTTP method is not POST
430
     * @throws \RuntimeException     If the batch action is not defined
431
     *
432
     * @return Response|RedirectResponse
433
     */
434
    public function batchAction()
435
    {
436
        $request = $this->getRequest();
437
        $restMethod = $this->getRestMethod();
438
439
        if ('POST' !== $restMethod) {
440
            throw $this->createNotFoundException(sprintf('Invalid request type "%s", POST expected', $restMethod));
441
        }
442
443
        // check the csrf token
444
        $this->validateCsrfToken('sonata.batch');
445
446
        $confirmation = $request->get('confirmation', false);
447
448
        if ($data = json_decode($request->get('data'), true)) {
449
            $action = $data['action'];
450
            $idx = $data['idx'];
451
            $allElements = $data['all_elements'];
452
            $request->request->replace(array_merge($request->request->all(), $data));
453
        } else {
454
            $request->request->set('idx', $request->get('idx', []));
455
            $request->request->set('all_elements', $request->get('all_elements', false));
456
457
            $action = $request->get('action');
458
            $idx = $request->get('idx');
459
            $allElements = $request->get('all_elements');
460
            $data = $request->request->all();
461
462
            unset($data['_sonata_csrf_token']);
463
        }
464
465
        // NEXT_MAJOR: Remove reflection check.
466
        $reflector = new \ReflectionMethod($this->admin, 'getBatchActions');
467
        if ($reflector->getDeclaringClass()->getName() === \get_class($this->admin)) {
0 ignored issues
show
introduced by
Consider using $reflector->class. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
468
            @trigger_error('Override Sonata\AdminBundle\Admin\AbstractAdmin::getBatchActions method'
0 ignored issues
show
Security Best Practice introduced by
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.');
}
Loading history...
469
                .' is deprecated since version 3.2.'
470
                .' Use Sonata\AdminBundle\Admin\AbstractAdmin::configureBatchActions instead.'
471
                .' The method will be final in 4.0.', E_USER_DEPRECATED
472
            );
473
        }
474
        $batchActions = $this->admin->getBatchActions();
475
        if (!array_key_exists($action, $batchActions)) {
476
            throw new \RuntimeException(sprintf('The `%s` batch action is not defined', $action));
477
        }
478
479
        $camelizedAction = Inflector::classify($action);
480
        $isRelevantAction = sprintf('batchAction%sIsRelevant', $camelizedAction);
481
482
        if (method_exists($this, $isRelevantAction)) {
483
            $nonRelevantMessage = \call_user_func([$this, $isRelevantAction], $idx, $allElements, $request);
484
        } else {
485
            $nonRelevantMessage = 0 != \count($idx) || $allElements; // at least one item is selected
486
        }
487
488
        if (!$nonRelevantMessage) { // default non relevant message (if false of null)
489
            $nonRelevantMessage = 'flash_batch_empty';
490
        }
491
492
        $datagrid = $this->admin->getDatagrid();
493
        $datagrid->buildPager();
494
495
        if (true !== $nonRelevantMessage) {
496
            $this->addFlash(
497
                'sonata_flash_info',
498
                $this->trans($nonRelevantMessage, [], 'SonataAdminBundle')
499
            );
500
501
            return $this->redirectToList();
502
        }
503
504
        $askConfirmation = isset($batchActions[$action]['ask_confirmation']) ?
505
            $batchActions[$action]['ask_confirmation'] :
506
            true;
507
508
        if ($askConfirmation && 'ok' != $confirmation) {
509
            $actionLabel = $batchActions[$action]['label'];
510
            $batchTranslationDomain = isset($batchActions[$action]['translation_domain']) ?
511
                $batchActions[$action]['translation_domain'] :
512
                $this->admin->getTranslationDomain();
513
514
            $formView = $datagrid->getForm()->createView();
515
            $this->setFormTheme($formView, $this->admin->getFilterTheme());
516
517
            // NEXT_MAJOR: Remove this line and use commented line below it instead
518
            $template = $this->admin->getTemplate('batch_confirmation');
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
519
            // $template = $this->templateRegistry->getTemplate('batch_confirmation');
520
521
            return $this->renderWithExtraParams($template, [
522
                'action' => 'list',
523
                'action_label' => $actionLabel,
524
                'batch_translation_domain' => $batchTranslationDomain,
525
                'datagrid' => $datagrid,
526
                'form' => $formView,
527
                'data' => $data,
528
                'csrf_token' => $this->getCsrfToken('sonata.batch'),
529
            ], null);
530
        }
531
532
        // execute the action, batchActionXxxxx
533
        $finalAction = sprintf('batchAction%s', $camelizedAction);
534
        if (!method_exists($this, $finalAction)) {
535
            throw new \RuntimeException(sprintf('A `%s::%s` method must be callable', \get_class($this), $finalAction));
536
        }
537
538
        $query = $datagrid->getQuery();
539
540
        $query->setFirstResult(null);
541
        $query->setMaxResults(null);
542
543
        $this->admin->preBatchAction($action, $query, $idx, $allElements);
544
545
        if (\count($idx) > 0) {
546
            $this->admin->getModelManager()->addIdentifiersToQuery($this->admin->getClass(), $query, $idx);
547
        } elseif (!$allElements) {
548
            $this->addFlash(
549
                'sonata_flash_info',
550
                $this->trans('flash_batch_no_elements_processed', [], 'SonataAdminBundle')
551
            );
552
553
            return $this->redirectToList();
554
        }
555
556
        return \call_user_func([$this, $finalAction], $query, $request);
557
    }
558
559
    /**
560
     * Create action.
561
     *
562
     * @throws AccessDeniedException If access is not granted
563
     * @throws \RuntimeException     If no editable field is defined
564
     *
565
     * @return Response
566
     */
567
    public function createAction()
568
    {
569
        $request = $this->getRequest();
570
        // the key used to lookup the template
571
        $templateKey = 'edit';
572
573
        $this->admin->checkAccess('create');
574
575
        $class = new \ReflectionClass($this->admin->hasActiveSubClass() ? $this->admin->getActiveSubClass() : $this->admin->getClass());
576
577
        if ($class->isAbstract()) {
578
            return $this->renderWithExtraParams(
579
                '@SonataAdmin/CRUD/select_subclass.html.twig',
580
                [
581
                    'base_template' => $this->getBaseTemplate(),
582
                    'admin' => $this->admin,
583
                    'action' => 'create',
584
                ],
585
                null
586
            );
587
        }
588
589
        $newObject = $this->admin->getNewInstance();
590
591
        $preResponse = $this->preCreate($request, $newObject);
592
        if (null !== $preResponse) {
593
            return $preResponse;
594
        }
595
596
        $this->admin->setSubject($newObject);
597
598
        $form = $this->admin->getForm();
599
        \assert($form instanceof Form);
600
601
        if (!\is_array($fields = $form->all()) || 0 === \count($fields)) {
602
            throw new \RuntimeException(
603
                'No editable field defined. Did you forget to implement the "configureFormFields" method?'
604
            );
605
        }
606
607
        $form->setData($newObject);
608
        $form->handleRequest($request);
609
610
        if ($form->isSubmitted()) {
611
            $isFormValid = $form->isValid();
612
613
            // persist if the form was valid and if in preview mode the preview was approved
614
            if ($isFormValid && (!$this->isInPreviewMode() || $this->isPreviewApproved())) {
615
                $submittedObject = $form->getData();
616
                $this->admin->setSubject($submittedObject);
617
                $this->admin->checkAccess('create', $submittedObject);
618
619
                try {
620
                    $newObject = $this->admin->create($submittedObject);
621
622
                    if ($this->isXmlHttpRequest()) {
623
                        return $this->renderJson([
624
                            'result' => 'ok',
625
                            'objectId' => $this->admin->getNormalizedIdentifier($newObject),
626
                        ], 200, []);
627
                    }
628
629
                    $this->addFlash(
630
                        'sonata_flash_success',
631
                        $this->trans(
632
                            'flash_create_success',
633
                            ['%name%' => $this->escapeHtml($this->admin->toString($newObject))],
634
                            'SonataAdminBundle'
635
                        )
636
                    );
637
638
                    // redirect to edit mode
639
                    return $this->redirectTo($newObject);
640
                } catch (ModelManagerException $e) {
641
                    $this->handleModelManagerException($e);
642
643
                    $isFormValid = false;
644
                }
645
            }
646
647
            // show an error message if the form failed validation
648
            if (!$isFormValid) {
649
                if (!$this->isXmlHttpRequest()) {
650
                    $this->addFlash(
651
                        'sonata_flash_error',
652
                        $this->trans(
653
                            'flash_create_error',
654
                            ['%name%' => $this->escapeHtml($this->admin->toString($newObject))],
655
                            'SonataAdminBundle'
656
                        )
657
                    );
658
                }
659
            } elseif ($this->isPreviewRequested()) {
660
                // pick the preview template if the form was valid and preview was requested
661
                $templateKey = 'preview';
662
                $this->admin->getShow();
663
            }
664
        }
665
666
        $formView = $form->createView();
667
        // set the theme for the current Admin Form
668
        $this->setFormTheme($formView, $this->admin->getFormTheme());
669
670
        // NEXT_MAJOR: Remove this line and use commented line below it instead
671
        $template = $this->admin->getTemplate($templateKey);
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
672
        // $template = $this->templateRegistry->getTemplate($templateKey);
673
674
        return $this->renderWithExtraParams($template, [
675
            'action' => 'create',
676
            'form' => $formView,
677
            'object' => $newObject,
678
            'objectId' => null,
679
        ], null);
680
    }
681
682
    /**
683
     * Show action.
684
     *
685
     * @param int|string|null $id
686
     *
687
     * @throws NotFoundHttpException If the object does not exist
688
     * @throws AccessDeniedException If access is not granted
689
     *
690
     * @return Response
691
     */
692
    public function showAction($id = null)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
693
    {
694
        $request = $this->getRequest();
695
        $id = $request->get($this->admin->getIdParameter());
696
697
        $object = $this->admin->getObject($id);
698
699
        if (!$object) {
700
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
701
        }
702
703
        $this->checkParentChildAssociation($request, $object);
704
705
        $this->admin->checkAccess('show', $object);
706
707
        $preResponse = $this->preShow($request, $object);
708
        if (null !== $preResponse) {
709
            return $preResponse;
710
        }
711
712
        $this->admin->setSubject($object);
713
714
        $fields = $this->admin->getShow();
715
        \assert($fields instanceof FieldDescriptionCollection);
716
717
        // NEXT_MAJOR: replace deprecation with exception
718
        if (!\is_array($fields->getElements()) || 0 === $fields->count()) {
719
            @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
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.');
}
Loading history...
720
                'Calling this method without implementing "configureShowFields"'
721
                .' is not supported since 3.x'
722
                .' and will no longer be possible in 4.0',
723
                E_USER_DEPRECATED
724
            );
725
        }
726
727
        // NEXT_MAJOR: Remove this line and use commented line below it instead
728
        $template = $this->admin->getTemplate('show');
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
729
        //$template = $this->templateRegistry->getTemplate('show');
730
731
        return $this->renderWithExtraParams($template, [
732
            'action' => 'show',
733
            'object' => $object,
734
            'elements' => $fields,
735
        ], null);
736
    }
737
738
    /**
739
     * Show history revisions for object.
740
     *
741
     * @param int|string|null $id
742
     *
743
     * @throws AccessDeniedException If access is not granted
744
     * @throws NotFoundHttpException If the object does not exist or the audit reader is not available
745
     *
746
     * @return Response
747
     */
748
    public function historyAction($id = null)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
749
    {
750
        $request = $this->getRequest();
751
        $id = $request->get($this->admin->getIdParameter());
752
753
        $object = $this->admin->getObject($id);
754
755
        if (!$object) {
756
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
757
        }
758
759
        $this->admin->checkAccess('history', $object);
760
761
        $manager = $this->get('sonata.admin.audit.manager');
762
763
        if (!$manager->hasReader($this->admin->getClass())) {
764
            throw $this->createNotFoundException(
765
                sprintf(
766
                    'unable to find the audit reader for class : %s',
767
                    $this->admin->getClass()
768
                )
769
            );
770
        }
771
772
        $reader = $manager->getReader($this->admin->getClass());
773
774
        $revisions = $reader->findRevisions($this->admin->getClass(), $id);
775
776
        // NEXT_MAJOR: Remove this line and use commented line below it instead
777
        $template = $this->admin->getTemplate('history');
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
778
        // $template = $this->templateRegistry->getTemplate('history');
779
780
        return $this->renderWithExtraParams($template, [
781
            'action' => 'history',
782
            'object' => $object,
783
            'revisions' => $revisions,
784
            'currentRevision' => $revisions ? current($revisions) : false,
785
        ], null);
786
    }
787
788
    /**
789
     * View history revision of object.
790
     *
791
     * @param int|string|null $id
792
     * @param string|null     $revision
793
     *
794
     * @throws AccessDeniedException If access is not granted
795
     * @throws NotFoundHttpException If the object or revision does not exist or the audit reader is not available
796
     *
797
     * @return Response
798
     */
799
    public function historyViewRevisionAction($id = null, $revision = null)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
800
    {
801
        $request = $this->getRequest();
802
        $id = $request->get($this->admin->getIdParameter());
803
804
        $object = $this->admin->getObject($id);
805
806
        if (!$object) {
807
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
808
        }
809
810
        $this->admin->checkAccess('historyViewRevision', $object);
811
812
        $manager = $this->get('sonata.admin.audit.manager');
813
814
        if (!$manager->hasReader($this->admin->getClass())) {
815
            throw $this->createNotFoundException(
816
                sprintf(
817
                    'unable to find the audit reader for class : %s',
818
                    $this->admin->getClass()
819
                )
820
            );
821
        }
822
823
        $reader = $manager->getReader($this->admin->getClass());
824
825
        // retrieve the revisioned object
826
        $object = $reader->find($this->admin->getClass(), $id, $revision);
827
828
        if (!$object) {
829
            throw $this->createNotFoundException(
830
                sprintf(
831
                    'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
832
                    $id,
833
                    $revision,
834
                    $this->admin->getClass()
835
                )
836
            );
837
        }
838
839
        $this->admin->setSubject($object);
840
841
        // NEXT_MAJOR: Remove this line and use commented line below it instead
842
        $template = $this->admin->getTemplate('show');
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
843
        // $template = $this->templateRegistry->getTemplate('show');
844
845
        return $this->renderWithExtraParams($template, [
846
            'action' => 'show',
847
            'object' => $object,
848
            'elements' => $this->admin->getShow(),
849
        ], null);
850
    }
851
852
    /**
853
     * Compare history revisions of object.
854
     *
855
     * @param int|string|null $id
856
     * @param int|string|null $base_revision
857
     * @param int|string|null $compare_revision
858
     *
859
     * @throws AccessDeniedException If access is not granted
860
     * @throws NotFoundHttpException If the object or revision does not exist or the audit reader is not available
861
     *
862
     * @return Response
863
     */
864
    public function historyCompareRevisionsAction($id = null, $base_revision = null, $compare_revision = null)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
865
    {
866
        $request = $this->getRequest();
867
868
        $this->admin->checkAccess('historyCompareRevisions');
869
870
        $id = $request->get($this->admin->getIdParameter());
871
872
        $object = $this->admin->getObject($id);
873
874
        if (!$object) {
875
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
876
        }
877
878
        $manager = $this->get('sonata.admin.audit.manager');
879
880
        if (!$manager->hasReader($this->admin->getClass())) {
881
            throw $this->createNotFoundException(
882
                sprintf(
883
                    'unable to find the audit reader for class : %s',
884
                    $this->admin->getClass()
885
                )
886
            );
887
        }
888
889
        $reader = $manager->getReader($this->admin->getClass());
890
891
        // retrieve the base revision
892
        $base_object = $reader->find($this->admin->getClass(), $id, $base_revision);
893
        if (!$base_object) {
894
            throw $this->createNotFoundException(
895
                sprintf(
896
                    'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
897
                    $id,
898
                    $base_revision,
899
                    $this->admin->getClass()
900
                )
901
            );
902
        }
903
904
        // retrieve the compare revision
905
        $compare_object = $reader->find($this->admin->getClass(), $id, $compare_revision);
906
        if (!$compare_object) {
907
            throw $this->createNotFoundException(
908
                sprintf(
909
                    'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
910
                    $id,
911
                    $compare_revision,
912
                    $this->admin->getClass()
913
                )
914
            );
915
        }
916
917
        $this->admin->setSubject($base_object);
918
919
        // NEXT_MAJOR: Remove this line and use commented line below it instead
920
        $template = $this->admin->getTemplate('show_compare');
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
921
        // $template = $this->templateRegistry->getTemplate('show_compare');
922
923
        return $this->renderWithExtraParams($template, [
924
            'action' => 'show',
925
            'object' => $base_object,
926
            'object_compare' => $compare_object,
927
            'elements' => $this->admin->getShow(),
928
        ], null);
929
    }
930
931
    /**
932
     * Export data to specified format.
933
     *
934
     * @throws AccessDeniedException If access is not granted
935
     * @throws \RuntimeException     If the export format is invalid
936
     *
937
     * @return Response
938
     */
939
    public function exportAction(Request $request)
940
    {
941
        $this->admin->checkAccess('export');
942
943
        $format = $request->get('format');
944
945
        // NEXT_MAJOR: remove the check
946
        if (!$this->has('sonata.admin.admin_exporter')) {
947
            @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
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.');
}
Loading history...
948
                'Not registering the exporter bundle is deprecated since version 3.14.'
949
                .' You must register it to be able to use the export action in 4.0.',
950
                E_USER_DEPRECATED
951
            );
952
            $allowedExportFormats = (array) $this->admin->getExportFormats();
953
954
            $class = $this->admin->getClass();
955
            $filename = sprintf(
956
                'export_%s_%s.%s',
957
                strtolower(substr($class, strripos($class, '\\') + 1)),
958
                date('Y_m_d_H_i_s', strtotime('now')),
959
                $format
960
            );
961
            $exporter = $this->get('sonata.admin.exporter');
962
        } else {
963
            $adminExporter = $this->get('sonata.admin.admin_exporter');
964
            $allowedExportFormats = $adminExporter->getAvailableFormats($this->admin);
965
            $filename = $adminExporter->getExportFilename($this->admin, $format);
966
            $exporter = $this->get('sonata.exporter.exporter');
967
        }
968
969
        if (!\in_array($format, $allowedExportFormats)) {
970
            throw new \RuntimeException(
971
                sprintf(
972
                    'Export in format `%s` is not allowed for class: `%s`. Allowed formats are: `%s`',
973
                    $format,
974
                    $this->admin->getClass(),
975
                    implode(', ', $allowedExportFormats)
976
                )
977
            );
978
        }
979
980
        return $exporter->getResponse(
981
            $format,
982
            $filename,
983
            $this->admin->getDataSourceIterator()
984
        );
985
    }
986
987
    /**
988
     * Returns the Response object associated to the acl action.
989
     *
990
     * @param int|string|null $id
991
     *
992
     * @throws AccessDeniedException If access is not granted
993
     * @throws NotFoundHttpException If the object does not exist or the ACL is not enabled
994
     *
995
     * @return Response|RedirectResponse
996
     */
997
    public function aclAction($id = null)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
998
    {
999
        $request = $this->getRequest();
1000
1001
        if (!$this->admin->isAclEnabled()) {
1002
            throw $this->createNotFoundException('ACL are not enabled for this admin');
1003
        }
1004
1005
        $id = $request->get($this->admin->getIdParameter());
1006
1007
        $object = $this->admin->getObject($id);
1008
1009
        if (!$object) {
1010
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
1011
        }
1012
1013
        $this->admin->checkAccess('acl', $object);
1014
1015
        $this->admin->setSubject($object);
1016
        $aclUsers = $this->getAclUsers();
1017
        $aclRoles = $this->getAclRoles();
1018
1019
        $adminObjectAclManipulator = $this->get('sonata.admin.object.manipulator.acl.admin');
1020
        $adminObjectAclData = new AdminObjectAclData(
1021
            $this->admin,
1022
            $object,
1023
            $aclUsers,
1024
            $adminObjectAclManipulator->getMaskBuilderClass(),
1025
            $aclRoles
1026
        );
1027
1028
        $aclUsersForm = $adminObjectAclManipulator->createAclUsersForm($adminObjectAclData);
1029
        $aclRolesForm = $adminObjectAclManipulator->createAclRolesForm($adminObjectAclData);
1030
1031
        if ('POST' === $request->getMethod()) {
1032
            if ($request->request->has(AdminObjectAclManipulator::ACL_USERS_FORM_NAME)) {
1033
                $form = $aclUsersForm;
1034
                $updateMethod = 'updateAclUsers';
1035
            } elseif ($request->request->has(AdminObjectAclManipulator::ACL_ROLES_FORM_NAME)) {
1036
                $form = $aclRolesForm;
1037
                $updateMethod = 'updateAclRoles';
1038
            }
1039
1040
            if (isset($form)) {
1041
                $form->handleRequest($request);
1042
1043
                if ($form->isValid()) {
1044
                    $adminObjectAclManipulator->$updateMethod($adminObjectAclData);
0 ignored issues
show
Bug introduced by
The variable $updateMethod 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

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1045
                    $this->addFlash(
1046
                        'sonata_flash_success',
1047
                        $this->trans('flash_acl_edit_success', [], 'SonataAdminBundle')
1048
                    );
1049
1050
                    return new RedirectResponse($this->admin->generateObjectUrl('acl', $object));
1051
                }
1052
            }
1053
        }
1054
1055
        // NEXT_MAJOR: Remove this line and use commented line below it instead
1056
        $template = $this->admin->getTemplate('acl');
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1057
        // $template = $this->templateRegistry->getTemplate('acl');
1058
1059
        return $this->renderWithExtraParams($template, [
1060
            'action' => 'acl',
1061
            'permissions' => $adminObjectAclData->getUserPermissions(),
1062
            'object' => $object,
1063
            'users' => $aclUsers,
1064
            'roles' => $aclRoles,
1065
            'aclUsersForm' => $aclUsersForm->createView(),
1066
            'aclRolesForm' => $aclRolesForm->createView(),
1067
        ], null);
1068
    }
1069
1070
    /**
1071
     * @return Request
1072
     */
1073
    public function getRequest()
1074
    {
1075
        return $this->container->get('request_stack')->getCurrentRequest();
1076
    }
1077
1078
    /**
1079
     * Gets a container configuration parameter by its name.
1080
     *
1081
     * @param string $name The parameter name
1082
     *
1083
     * @return mixed
1084
     */
1085
    protected function getParameter($name)
1086
    {
1087
        return $this->container->getParameter($name);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Psr\Container\ContainerInterface as the method getParameter() does only exist in the following implementations of said interface: Container14\ProjectServiceContainer, ProjectServiceContainer, Symfony\Component\Depend...urationContainerBuilder, Symfony\Component\DependencyInjection\Container, Symfony\Component\Depend...ection\ContainerBuilder, Symfony\Component\Depend...\NoConstructorContainer, Symfony\Component\Depend...tainers\CustomContainer, Symfony\Component\Depend...ProjectServiceContainer, Symfony\Component\Depend...ProjectServiceContainer, Symfony_DI_PhpDumper_Test_Almost_Circular_Private, Symfony_DI_PhpDumper_Test_Almost_Circular_Public, Symfony_DI_PhpDumper_Test_Base64Parameters, Symfony_DI_PhpDumper_Test_EnvParameters, Symfony_DI_PhpDumper_Test_Legacy_Privates, Symfony_DI_PhpDumper_Test_Rot13Parameters, Symfony_DI_PhpDumper_Test_Uninitialized_Reference.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1088
    }
1089
1090
    /**
1091
     * Render JSON.
1092
     *
1093
     * @param mixed $data
1094
     * @param int   $status
1095
     * @param array $headers
1096
     *
1097
     * @return Response with json encoded data
1098
     */
1099
    protected function renderJson($data, $status = 200, $headers = [])
1100
    {
1101
        return new JsonResponse($data, $status, $headers);
1102
    }
1103
1104
    /**
1105
     * Returns true if the request is a XMLHttpRequest.
1106
     *
1107
     * @return bool True if the request is an XMLHttpRequest, false otherwise
1108
     */
1109
    protected function isXmlHttpRequest()
1110
    {
1111
        $request = $this->getRequest();
1112
1113
        return $request->isXmlHttpRequest() || $request->get('_xml_http_request');
1114
    }
1115
1116
    /**
1117
     * Returns the correct RESTful verb, given either by the request itself or
1118
     * via the "_method" parameter.
1119
     *
1120
     * @return string HTTP method, either
1121
     */
1122
    protected function getRestMethod()
1123
    {
1124
        $request = $this->getRequest();
1125
1126
        if (Request::getHttpMethodParameterOverride() || !$request->request->has('_method')) {
1127
            return $request->getMethod();
1128
        }
1129
1130
        return $request->request->get('_method');
1131
    }
1132
1133
    /**
1134
     * Contextualize the admin class depends on the current request.
1135
     *
1136
     * @throws \RuntimeException
1137
     */
1138
    protected function configure()
1139
    {
1140
        $request = $this->getRequest();
1141
1142
        $adminCode = $request->get('_sonata_admin');
1143
1144
        if (!$adminCode) {
1145
            throw new \RuntimeException(sprintf(
1146
                'There is no `_sonata_admin` defined for the controller `%s` and the current route `%s`',
1147
                \get_class($this),
1148
                $request->get('_route')
1149
            ));
1150
        }
1151
1152
        $this->admin = $this->container->get('sonata.admin.pool')->getAdminByAdminCode($adminCode);
1153
1154
        if (!$this->admin) {
1155
            throw new \RuntimeException(sprintf(
1156
                'Unable to find the admin class related to the current controller (%s)',
1157
                \get_class($this)
1158
            ));
1159
        }
1160
1161
        $this->templateRegistry = $this->container->get($this->admin->getCode().'.template_registry');
1162
        if (!$this->templateRegistry instanceof TemplateRegistryInterface) {
1163
            throw new \RuntimeException(sprintf(
1164
                'Unable to find the template registry related to the current admin (%s)',
1165
                $this->admin->getCode()
1166
            ));
1167
        }
1168
1169
        $rootAdmin = $this->admin;
1170
1171
        while ($rootAdmin->isChild()) {
1172
            $rootAdmin->setCurrentChild(true);
1173
            $rootAdmin = $rootAdmin->getParent();
1174
        }
1175
1176
        $rootAdmin->setRequest($request);
1177
1178
        if ($request->get('uniqid')) {
1179
            $this->admin->setUniqid($request->get('uniqid'));
1180
        }
1181
    }
1182
1183
    /**
1184
     * Proxy for the logger service of the container.
1185
     * If no such service is found, a NullLogger is returned.
1186
     *
1187
     * @return LoggerInterface
1188
     */
1189
    protected function getLogger()
1190
    {
1191
        if ($this->container->has('logger')) {
1192
            $logger = $this->container->get('logger');
1193
            \assert($logger instanceof LoggerInterface);
1194
1195
            return $logger;
1196
        }
1197
1198
        return new NullLogger();
1199
    }
1200
1201
    /**
1202
     * Returns the base template name.
1203
     *
1204
     * @return string The template name
1205
     */
1206
    protected function getBaseTemplate()
1207
    {
1208
        if ($this->isXmlHttpRequest()) {
1209
            // NEXT_MAJOR: Remove this line and use commented line below it instead
1210
            return $this->admin->getTemplate('ajax');
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1211
            // return $this->templateRegistry->getTemplate('ajax');
1212
        }
1213
1214
        // NEXT_MAJOR: Remove this line and use commented line below it instead
1215
        return $this->admin->getTemplate('layout');
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1216
        // return $this->templateRegistry->getTemplate('layout');
1217
    }
1218
1219
    /**
1220
     * @throws \Exception
1221
     */
1222
    protected function handleModelManagerException(\Exception $e)
1223
    {
1224
        if ($this->get('kernel')->isDebug()) {
1225
            throw $e;
1226
        }
1227
1228
        $context = ['exception' => $e];
1229
        if ($e->getPrevious()) {
1230
            $context['previous_exception_message'] = $e->getPrevious()->getMessage();
1231
        }
1232
        $this->getLogger()->error($e->getMessage(), $context);
1233
    }
1234
1235
    /**
1236
     * Redirect the user depend on this choice.
1237
     *
1238
     * @param object $object
1239
     *
1240
     * @return RedirectResponse
1241
     */
1242
    protected function redirectTo($object)
1243
    {
1244
        $request = $this->getRequest();
1245
1246
        $url = false;
1247
1248
        if (null !== $request->get('btn_update_and_list')) {
1249
            return $this->redirectToList();
1250
        }
1251
        if (null !== $request->get('btn_create_and_list')) {
1252
            return $this->redirectToList();
1253
        }
1254
1255
        if (null !== $request->get('btn_create_and_create')) {
1256
            $params = [];
1257
            if ($this->admin->hasActiveSubClass()) {
1258
                $params['subclass'] = $request->get('subclass');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $params['subclass'] is correct as $request->get('subclass') (which targets Symfony\Component\HttpFoundation\Request::get()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1259
            }
1260
            $url = $this->admin->generateUrl('create', $params);
1261
        }
1262
1263
        if ('DELETE' === $this->getRestMethod()) {
1264
            return $this->redirectToList();
1265
        }
1266
1267
        if (!$url) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $url of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1268
            foreach (['edit', 'show'] as $route) {
1269
                if ($this->admin->hasRoute($route) && $this->admin->hasAccess($route, $object)) {
1270
                    $url = $this->admin->generateObjectUrl($route, $object);
1271
1272
                    break;
1273
                }
1274
            }
1275
        }
1276
1277
        if (!$url) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $url of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1278
            return $this->redirectToList();
1279
        }
1280
1281
        return new RedirectResponse($url);
1282
    }
1283
1284
    /**
1285
     * Redirects the user to the list view.
1286
     *
1287
     * @return RedirectResponse
1288
     */
1289
    final protected function redirectToList()
1290
    {
1291
        $parameters = [];
1292
1293
        if ($filter = $this->admin->getFilterParameters()) {
1294
            $parameters['filter'] = $filter;
1295
        }
1296
1297
        return $this->redirect($this->admin->generateUrl('list', $parameters));
1298
    }
1299
1300
    /**
1301
     * Returns true if the preview is requested to be shown.
1302
     *
1303
     * @return bool
1304
     */
1305
    protected function isPreviewRequested()
1306
    {
1307
        $request = $this->getRequest();
1308
1309
        return null !== $request->get('btn_preview');
1310
    }
1311
1312
    /**
1313
     * Returns true if the preview has been approved.
1314
     *
1315
     * @return bool
1316
     */
1317
    protected function isPreviewApproved()
1318
    {
1319
        $request = $this->getRequest();
1320
1321
        return null !== $request->get('btn_preview_approve');
1322
    }
1323
1324
    /**
1325
     * Returns true if the request is in the preview workflow.
1326
     *
1327
     * That means either a preview is requested or the preview has already been shown
1328
     * and it got approved/declined.
1329
     *
1330
     * @return bool
1331
     */
1332
    protected function isInPreviewMode()
1333
    {
1334
        return $this->admin->supportsPreviewMode()
1335
        && ($this->isPreviewRequested()
1336
            || $this->isPreviewApproved()
1337
            || $this->isPreviewDeclined());
1338
    }
1339
1340
    /**
1341
     * Returns true if the preview has been declined.
1342
     *
1343
     * @return bool
1344
     */
1345
    protected function isPreviewDeclined()
1346
    {
1347
        $request = $this->getRequest();
1348
1349
        return null !== $request->get('btn_preview_decline');
1350
    }
1351
1352
    /**
1353
     * Gets ACL users.
1354
     *
1355
     * @return \Traversable
1356
     */
1357
    protected function getAclUsers()
1358
    {
1359
        $aclUsers = [];
1360
1361
        $userManagerServiceName = $this->container->getParameter('sonata.admin.security.acl_user_manager');
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Psr\Container\ContainerInterface as the method getParameter() does only exist in the following implementations of said interface: Container14\ProjectServiceContainer, ProjectServiceContainer, Symfony\Component\Depend...urationContainerBuilder, Symfony\Component\DependencyInjection\Container, Symfony\Component\Depend...ection\ContainerBuilder, Symfony\Component\Depend...\NoConstructorContainer, Symfony\Component\Depend...tainers\CustomContainer, Symfony\Component\Depend...ProjectServiceContainer, Symfony\Component\Depend...ProjectServiceContainer, Symfony_DI_PhpDumper_Test_Almost_Circular_Private, Symfony_DI_PhpDumper_Test_Almost_Circular_Public, Symfony_DI_PhpDumper_Test_Base64Parameters, Symfony_DI_PhpDumper_Test_EnvParameters, Symfony_DI_PhpDumper_Test_Legacy_Privates, Symfony_DI_PhpDumper_Test_Rot13Parameters, Symfony_DI_PhpDumper_Test_Uninitialized_Reference.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1362
        if (null !== $userManagerServiceName && $this->has($userManagerServiceName)) {
1363
            $userManager = $this->get($userManagerServiceName);
1364
1365
            if (method_exists($userManager, 'findUsers')) {
1366
                $aclUsers = $userManager->findUsers();
1367
            }
1368
        }
1369
1370
        return \is_array($aclUsers) ? new \ArrayIterator($aclUsers) : $aclUsers;
1371
    }
1372
1373
    /**
1374
     * Gets ACL roles.
1375
     *
1376
     * @return \Traversable
1377
     */
1378
    protected function getAclRoles()
1379
    {
1380
        $aclRoles = [];
1381
        $roleHierarchy = $this->container->getParameter('security.role_hierarchy.roles');
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Psr\Container\ContainerInterface as the method getParameter() does only exist in the following implementations of said interface: Container14\ProjectServiceContainer, ProjectServiceContainer, Symfony\Component\Depend...urationContainerBuilder, Symfony\Component\DependencyInjection\Container, Symfony\Component\Depend...ection\ContainerBuilder, Symfony\Component\Depend...\NoConstructorContainer, Symfony\Component\Depend...tainers\CustomContainer, Symfony\Component\Depend...ProjectServiceContainer, Symfony\Component\Depend...ProjectServiceContainer, Symfony_DI_PhpDumper_Test_Almost_Circular_Private, Symfony_DI_PhpDumper_Test_Almost_Circular_Public, Symfony_DI_PhpDumper_Test_Base64Parameters, Symfony_DI_PhpDumper_Test_EnvParameters, Symfony_DI_PhpDumper_Test_Legacy_Privates, Symfony_DI_PhpDumper_Test_Rot13Parameters, Symfony_DI_PhpDumper_Test_Uninitialized_Reference.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1382
        $pool = $this->container->get('sonata.admin.pool');
1383
1384
        foreach ($pool->getAdminServiceIds() as $id) {
1385
            try {
1386
                $admin = $pool->getInstance($id);
1387
            } catch (\Exception $e) {
1388
                continue;
1389
            }
1390
1391
            $baseRole = $admin->getSecurityHandler()->getBaseRole($admin);
1392
            foreach ($admin->getSecurityInformation() as $role => $permissions) {
1393
                $role = sprintf($baseRole, $role);
1394
                $aclRoles[] = $role;
1395
            }
1396
        }
1397
1398
        foreach ($roleHierarchy as $name => $roles) {
1399
            $aclRoles[] = $name;
1400
            $aclRoles = array_merge($aclRoles, $roles);
1401
        }
1402
1403
        $aclRoles = array_unique($aclRoles);
1404
1405
        return \is_array($aclRoles) ? new \ArrayIterator($aclRoles) : $aclRoles;
1406
    }
1407
1408
    /**
1409
     * Validate CSRF token for action without form.
1410
     *
1411
     * @param string $intention
1412
     *
1413
     * @throws HttpException
1414
     */
1415
    protected function validateCsrfToken($intention)
1416
    {
1417
        $request = $this->getRequest();
1418
        $token = $request->request->get('_sonata_csrf_token', false);
1419
1420
        if ($this->container->has('security.csrf.token_manager')) {
1421
            $valid = $this->container->get('security.csrf.token_manager')->isTokenValid(new CsrfToken($intention, $token));
1422
        } else {
1423
            return;
1424
        }
1425
1426
        if (!$valid) {
1427
            throw new HttpException(400, 'The csrf token is not valid, CSRF attack?');
1428
        }
1429
    }
1430
1431
    /**
1432
     * Escape string for html output.
1433
     *
1434
     * @param string $s
1435
     *
1436
     * @return string
1437
     */
1438
    protected function escapeHtml($s)
1439
    {
1440
        return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
1441
    }
1442
1443
    /**
1444
     * Get CSRF token.
1445
     *
1446
     * @param string $intention
1447
     *
1448
     * @return string|false
1449
     */
1450
    protected function getCsrfToken($intention)
1451
    {
1452
        if ($this->container->has('security.csrf.token_manager')) {
1453
            return $this->container->get('security.csrf.token_manager')->getToken($intention)->getValue();
1454
        }
1455
1456
        return false;
1457
    }
1458
1459
    /**
1460
     * This method can be overloaded in your custom CRUD controller.
1461
     * It's called from createAction.
1462
     *
1463
     * @param mixed $object
1464
     *
1465
     * @return Response|null
1466
     */
1467
    protected function preCreate(Request $request, $object)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1468
    {
1469
    }
1470
1471
    /**
1472
     * This method can be overloaded in your custom CRUD controller.
1473
     * It's called from editAction.
1474
     *
1475
     * @param mixed $object
1476
     *
1477
     * @return Response|null
1478
     */
1479
    protected function preEdit(Request $request, $object)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1480
    {
1481
    }
1482
1483
    /**
1484
     * This method can be overloaded in your custom CRUD controller.
1485
     * It's called from deleteAction.
1486
     *
1487
     * @param mixed $object
1488
     *
1489
     * @return Response|null
1490
     */
1491
    protected function preDelete(Request $request, $object)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1492
    {
1493
    }
1494
1495
    /**
1496
     * This method can be overloaded in your custom CRUD controller.
1497
     * It's called from showAction.
1498
     *
1499
     * @param mixed $object
1500
     *
1501
     * @return Response|null
1502
     */
1503
    protected function preShow(Request $request, $object)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1504
    {
1505
    }
1506
1507
    /**
1508
     * This method can be overloaded in your custom CRUD controller.
1509
     * It's called from listAction.
1510
     *
1511
     * @return Response|null
1512
     */
1513
    protected function preList(Request $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1514
    {
1515
    }
1516
1517
    /**
1518
     * Translate a message id.
1519
     *
1520
     * @param string $id
1521
     * @param string $domain
1522
     * @param string $locale
1523
     *
1524
     * @return string translated string
1525
     */
1526
    final protected function trans($id, array $parameters = [], $domain = null, $locale = null)
1527
    {
1528
        $domain = $domain ?: $this->admin->getTranslationDomain();
1529
1530
        return $this->get('translator')->trans($id, $parameters, $domain, $locale);
1531
    }
1532
1533
    private function checkParentChildAssociation(Request $request, $object)
1534
    {
1535
        if (!($parentAdmin = $this->admin->getParent())) {
1536
            return;
1537
        }
1538
1539
        // NEXT_MAJOR: remove this check
1540
        if (!$this->admin->getParentAssociationMapping()) {
0 ignored issues
show
Bug introduced by
The method getParentAssociationMapping() does not exist on Sonata\AdminBundle\Admin\AdminInterface. Did you maybe mean getParent()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1541
            return;
1542
        }
1543
1544
        $parentId = $request->get($parentAdmin->getIdParameter());
1545
1546
        $propertyAccessor = PropertyAccess::createPropertyAccessor();
1547
        $propertyPath = new PropertyPath($this->admin->getParentAssociationMapping());
0 ignored issues
show
Bug introduced by
The method getParentAssociationMapping() does not exist on Sonata\AdminBundle\Admin\AdminInterface. Did you maybe mean getParent()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1548
1549
        if ($parentAdmin->getObject($parentId) !== $propertyAccessor->getValue($object, $propertyPath)) {
1550
            // NEXT_MAJOR: make this exception
1551
            @trigger_error("Accessing a child that isn't connected to a given parent is deprecated since 3.34"
0 ignored issues
show
Security Best Practice introduced by
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.');
}
Loading history...
1552
                ." and won't be allowed in 4.0.",
1553
                E_USER_DEPRECATED
1554
            );
1555
        }
1556
    }
1557
1558
    /**
1559
     * Sets the admin form theme to form view. Used for compatibility between Symfony versions.
1560
     */
1561
    private function setFormTheme(FormView $formView, array $theme = null)
1562
    {
1563
        $twig = $this->get('twig');
1564
1565
        // BC for Symfony < 3.2 where this runtime does not exists
1566
        if (!method_exists(AppVariable::class, 'getToken')) {
1567
            $twig->getExtension(FormExtension::class)->renderer->setTheme($formView, $theme);
1568
1569
            return;
1570
        }
1571
1572
        // BC for Symfony < 3.4 where runtime should be TwigRenderer
1573
        if (!method_exists(DebugCommand::class, 'getLoaderPaths')) {
1574
            $twig->getRuntime(TwigRenderer::class)->setTheme($formView, $theme);
1575
1576
            return;
1577
        }
1578
1579
        $twig->getRuntime(FormRenderer::class)->setTheme($formView, $theme);
1580
    }
1581
}
1582