Completed
Push — master ( 48cc3d...5640fa )
by Craig
12:32 queued 05:19
created

EditHandler   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 110
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 110
rs 10
wmc 11
lcom 1
cbo 2

4 Methods

Rating   Name   Duplication   Size   Complexity  
A setSanitizeHelper() 0 4 1
A applyAction() 0 14 2
A sanitizeInput() 0 14 1
C hasConflicts() 0 51 7
1
<?php
2
/**
3
 * Routes.
4
 *
5
 * @copyright Zikula contributors (Zikula)
6
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
7
 * @author Zikula contributors <[email protected]>.
8
 * @link http://www.zikula.org
9
 * @link http://zikula.org
10
 * @version Generated by ModuleStudio 0.7.4 (http://modulestudio.de).
11
 */
12
13
namespace Zikula\RoutesModule\Form\Handler\Route;
14
15
use Symfony\Component\Routing\RouteCollection;
16
use Zikula\RoutesModule\Entity\RouteEntity;
17
use Zikula\RoutesModule\Helper\SanitizeHelper;
18
use Zikula\RoutesModule\Form\Handler\Route\Base\AbstractEditHandler;
19
20
/**
21
 * This handler class handles the page events of the Form called by the zikulaRoutesModule_route_edit() function.
22
 * It aims on the route object type.
23
 */
24
class EditHandler extends AbstractEditHandler
25
{
26
    /**
27
     * @var SanitizeHelper
28
     */
29
    private $sanitizeHelper;
30
31
    /**
32
     * Sets the sanitize helper.
33
     *
34
     * @param SanitizeHelper $sanitizeHelper Sanitize helper
35
     */
36
    public function setSanitizeHelper(SanitizeHelper $sanitizeHelper)
37
    {
38
        $this->sanitizeHelper = $sanitizeHelper;
39
    }
40
41
    /**
42
     * @inheritDoc
43
     */
44
    public function applyAction(array $args = [])
45
    {
46
        $this->sanitizeInput();
47
        if ($this->hasConflicts()) {
48
            return false;
49
        }
50
51
        $return = parent::applyAction($args);
52
53
        $cacheClearer = $this->container->get('zikula.cache_clearer');
0 ignored issues
show
Bug introduced by
The property container does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
54
        $cacheClearer->clear('symfony.routing');
55
56
        return $return;
57
    }
58
59
    /**
60
     * Ensures validity of input data.
61
     */
62
    private function sanitizeInput()
63
    {
64
        $entity = $this->entityRef;
65
66
        list($controller, ) = $this->sanitizeHelper->sanitizeController($entity['controller']);
67
        list($action, ) = $this->sanitizeHelper->sanitizeAction($entity['action']);
68
69
        $entity['controller'] = $controller;
70
        $entity['action'] = $action;
71
        $entity['group'] = RouteEntity::POSITION_MIDDLE;
72
        $entity['sort'] = 0;
73
74
        $this->entityRef = $entity;
75
    }
76
77
    /**
78
     * Checks for potential conflict.
79
     *
80
     * @return boolean True if a critical error occured, else false.
81
     */
82
    private function hasConflicts()
83
    {
84
        $routeEntity = $this->entityRef;
85
86
        $newPath = $routeEntity->getPathWithBundlePrefix();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Zikula\Core\Doctrine\EntityAccess as the method getPathWithBundlePrefix() does only exist in the following sub-classes of Zikula\Core\Doctrine\EntityAccess: Zikula\RoutesModule\Entity\RouteEntity. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

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

class MyUser extends 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 sub-classes 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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
87
88
        /** @var RouteCollection $routeCollection */
89
        $routeCollection = $this->router->getRouteCollection();
90
91
        $errors = [];
92
        foreach ($routeCollection->all() as $route) {
93
            $path = $route->getPath();
94
            if ($path === '/{url}') {
95
                continue;
96
            }
97
98
            if ($path === $newPath) {
99
                $errors[] = [
100
                    'type' => 'SAME',
101
                    'path' => $path
102
                ];
103
                continue;
104
            }
105
106
            $pathRegExp = preg_quote(preg_replace("/{(.+)}/", "____DUMMY____", $path), '/');
107
            $pathRegExp = "#^" . str_replace('____DUMMY____', '(.+)', $pathRegExp) . "$#";
108
109
            $matches = [];
110
            preg_match($pathRegExp, $newPath, $matches);
111
            if (count($matches)) {
112
                $errors[] = [
113
                    'type' => 'SIMILAR',
114
                    'path' => $path
115
                ];
116
            }
117
        }
118
119
        $hasCriticalErrors = false;
120
121
        foreach ($errors as $error) {
122
            if ($error['type'] == 'SAME') {
123
                $message = $this->__('It looks like you created or updated a route with a path which already exists. This is an error in most cases.');
124
                $hasCriticalErrors = true;
125
            } else {
126
                $message = $this->__f('The path of the route you created or updated looks similar to the following already existing path: %s Are you sure you haven\'t just introduced a conflict?', ['%s' => $error['path']]);
127
            }
128
            $this->request->getSession()->getFlashBag()->add('error', $message);
129
        }
130
131
        return $hasCriticalErrors;
132
    }
133
}
134