Completed
Push — 4.0 ( bf6233...a1234e )
by Marco
13:59
created

Parser::read()   C

Complexity

Conditions 10
Paths 20

Size

Total Lines 84
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 28
CRAP Score 10.0296

Importance

Changes 6
Bugs 1 Features 1
Metric Value
c 6
b 1
f 1
dl 0
loc 84
ccs 28
cts 30
cp 0.9333
rs 5.3846
cc 10
eloc 29
nc 20
nop 3
crap 10.0296

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 namespace Comodojo\Dispatcher\Router;
2
3
use \Comodojo\Dispatcher\Router\Route;
4
use \Comodojo\Exception\DispatcherException;
5
use \Exception;
6
7
/**
8
 * @package     Comodojo Dispatcher
9
 * @author      Marco Giovinazzi <[email protected]>
10
 * @author      Marco Castiello <[email protected]>
11
 * @license     GPL-3.0+
12
 *
13
 * LICENSE:
14
 *
15
 * This program is free software: you can redistribute it and/or modify
16
 * it under the terms of the GNU Affero General Public License as
17
 * published by the Free Software Foundation, either version 3 of the
18
 * License, or (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License
26
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27
 */
28
29
class Parser {
30
31
    // This method read the route (folder by folder recursively) and build
32
    // the global regular expression against which all the request URI will be compared
33
    public function read($folders = array(), Route $value = null, $regex = '') {
34
35
        if (is_null($value)) {
36 1
37
            $value = new Route();
38
39
        }
40 1
41
        // if the first 'folder' is empty is removed
42 1
        while (!empty($folders) && empty($folders[0])) {
43
44 1
            array_shift($folders);
45
46
        }
47
48 1
        // if the 'folder' array is empty, the route has been fully analyzed
49
        // this is the exit condition from the recursive loop.
50 1
        if (empty($folders)) {
51
52 1
            return '^'.$regex.'[\/]?$';
53
54 1
        } else {
55
56
            // The first element of the array 'folders' is taken in order to be analyzed
57 1
            $folder  = array_shift($folders);
58
59
            // All the parameters of the route must be json strings
60
            $decoded = json_decode($folder, true);
61
62
            if (!is_null($decoded) && is_array($decoded)) {
63
64
                $param_regex    = '';
65 1
66
                $param_required = false;
67 1
68
                /* All the folders can include more than one parameter
69
                 * Eg: /service_name/{'param1': 'regex1', 'param2': 'regex2'}/
70
                 *     /calendar/{'ux_timestamp*': '\d{10}', 'microseconds': '\d{4}'}/
71
                 *
72 1
                 * The '*' at the end of the paramerter name implies that the parameter is required
73
                 * This example can be read as a calendar service that accepts both
74
                 * timestamps in unix or javascript format.
75 1
                 *
76
                 * This is the reason of the following 'foreach'
77 1
                 */
78
                foreach ($decoded as $key => $string) {
79 1
80
                    $this->logger->debug("PARAMETER KEY: " . $key);
0 ignored issues
show
Bug introduced by
The property logger 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...
81 1
82
                    $this->logger->debug("PARAMETER STRING: " . $string);
83
84
                    /* The key and the regex of every paramater is passed to the 'param'
85
                     * method which will build an appropriate regular expression and will understand
86
                     * if the parameter is required and will build the Route query object
87
                     */
88
                    $param_regex .= $this->param($key, $string, $value);
89
90
                    if ($value->isQueryRequired($key)) $param_required = true;
91
92
                    $this->logger->debug("PARAMETER REGEX: " . $param_regex);
93 1
94
                }
95 1
                // Once the parameter is analyzed, the result is passed to the next iteration
96
                return $this->read(
97 1
                    $folders,
98
                    $value,
99
                    $regex.'(?:\/'.$param_regex.')'. (($param_required)?'{1}':'?')
100
                );
101
102
            } else {
103 1
                // if the element is not a json string, I assume it's the service name
104
                $value->addService($folder);
105 1
106
                return $this->read(
107 1
                    $folders,
108
                    $value,
109 1
                    $regex.'\/'.$folder
110
                );
111 1
112 1
            }
113 1
114 1
        }
115 1
116
    }
117
118
    // This method read a single parameter and build the regular expression
119 1
    private function param($key, $string, $value) {
120
121 1
        $field_required = false;
122 1
123 1
        // If the field name ends with a '*', the parameter is considered as required
124 1
        if (preg_match('/^(.+)\*$/', $key, $bits)) {
125 1
126
            $key            = $bits[1];
127
            $field_required = true;
128
129
        }
130
131
        // The $value query object contains all regex which will be used by the collector to parse the route fields
132
        $value->setQuery($key, $string, $field_required);
133
134 1
        /* Every parameter can include it's own logic into the regular expression,
135
         * it can use backreferences and it's expected to be used against a single parameter.
136 1
         * This means that it can't be used as is to build the route regular expression,
137
         * Backreferences are not useful at this point and can make the regular expression more time consuming
138
         * and resource hungry. This is why they are replaced with the grouping parenthesis.
139 1
         * Eg: (value) changes in (?:value)
140
         *
141 1
         * Delimiting characters like '^' and '$' are also meaningless in the complete regular expression and
142 1
         * need to be removed. Contrariwise, wildcards must be delimited in order to keet the whole regular
143
         * expression consistent, hence a '?' is added to all the '.*' or '.+' that don't already have one.
144 1
         */
145
        $string = preg_replace("/(?<!\\\\)\\((?!\\?)/", '(?:', $string);
146
        $string = preg_replace("/\\.([\\*\\+])(?!\\?)/", '.${1}?', $string);
147 1
        $string = preg_replace("/^[\\^]/", '', $string);
148
        $string = preg_replace("/[\\$]$/", '', $string);
149
150
        /* The produced regular expression is grouped and associated with its key (this means that the 'preg_match'
151
         * function will generate an associative array where the key/value association is preserved).
152
         * If the field is required, the regular expression is completed with a '{1}' (which make it compulsory),
153
         * otherwise a '?' is added.
154
         */
155
        return '(?P<' . $key . '>' . $string . ')' . (($field_required)?'{1}':'?');
156
157
    }
158
159
}
160