Parser::read()   B
last analyzed

Complexity

Conditions 10
Paths 20

Size

Total Lines 79
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 10.178

Importance

Changes 0
Metric Value
eloc 29
dl 0
loc 79
c 0
b 0
f 0
ccs 29
cts 33
cp 0.8788
rs 7.6666
cc 10
nc 20
nop 3
crap 10.178

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 \Psr\Log\LoggerInterface;
5
use \Comodojo\Exception\DispatcherException;
6
use \Exception;
7
8
/**
9
 * @package     Comodojo Dispatcher
10
 * @author      Marco Giovinazzi <[email protected]>
11
 * @author      Marco Castiello <[email protected]>
12
 * @license     MIT
13
 *
14
 * LICENSE:
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
 * THE SOFTWARE.
23
 */
24
25
class Parser {
26
27
    public $logger;
28
29 1
    public function __construct(LoggerInterface $logger) {
30
31 1
        $this->logger = $logger;
32
33 1
    }
34
35
    // This method read the route (folder by folder recursively) and build
36
    // the global regular expression against which all the request URI will be compared
37 1
    public function read($folders = array(), Route $value = null, $regex = '') {
38
39 1
        if (is_null($value)) {
40
41 1
            $value = new Route();
42
43 1
        }
44
45
        // if the first 'folder' is empty is removed
46 1
        while (!empty($folders) && empty($folders[0])) {
47
48
            array_shift($folders);
49
50
        }
51
52
        // if the 'folder' array is empty, the route has been fully analyzed
53
        // this is the exit condition from the recursive loop.
54 1
        if (empty($folders)) {
55
56 1
            return '^'.$regex.'[\/]?$';
57
58
        } else {
59
60
            // The first element of the array 'folders' is taken in order to be analyzed
61 1
            $folder  = array_shift($folders);
62
63
            // All the parameters of the route must be json strings
64 1
            $decoded = json_decode($folder, true);
65
66 1
            if (!is_null($decoded) && is_array($decoded)) {
67
68 1
                $param_regex = '';
69
70 1
                $param_required = false;
71
72
                /* All the folders can include more than one parameter
73
                 * Eg: /service_name/{'param1': 'regex1', 'param2': 'regex2'}/
74
                 *     /calendar/{'ux_timestamp*': '\d{10}', 'microseconds': '\d{4}'}/
75
                 *
76
                 * The '*' at the end of the paramerter name implies that the parameter is required
77
                 * This example can be read as a calendar service that accepts both
78
                 * timestamps in unix or javascript format.
79
                 *
80
                 * This is the reason of the following 'foreach'
81
                 */
82 1
                foreach ($decoded as $key => $string) {
83
84 1
                    $this->logger->debug("Route parser - parameter key: $key");
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $key instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
85
86 1
                    $this->logger->debug("Route parser - parameter string: $string");
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $string instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
87
88
                    /* The key and the regex of every paramater is passed to the 'param'
89
                     * method which will build an appropriate regular expression and will understand
90
                     * if the parameter is required and will build the Route query object
91
                     */
92 1
                    $param_regex .= $this->param($key, $string, $value);
93
94 1
                    if ($value->isQueryRequired($key)) {
95
                        $param_required = true;
96
                    }
97
98 1
                    $this->logger->debug("Route parser - parameter regex: $param_regex");
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $param_regex instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
99
100 1
                }
101
                // Once the parameter is analyzed, the result is passed to the next iteration
102 1
                return $this->read(
103 1
                    $folders,
104 1
                    $value,
105 1
                    $regex.'(?:\/'.$param_regex.')'.(($param_required) ? '{1}' : '?')
106 1
                );
107
108
            } else {
109
                // if the element is not a json string, I assume it's the service name
110 1
                $value->addService($folder);
111
112 1
                return $this->read(
113 1
                    $folders,
114 1
                    $value,
115 1
                    $regex.'\/'.$folder
116 1
                );
117
118
            }
119
120
        }
121
122
    }
123
124
    // This method read a single parameter and build the regular expression
125 1
    private function param($key, $string, $value) {
126
127 1
        $field_required = false;
128
129
        // If the field name ends with a '*', the parameter is considered as required
130 1
        if (preg_match('/^(.+)\*$/', $key, $bits)) {
131
132 1
            $key = $bits[1];
133 1
            $field_required = true;
134
135 1
        }
136
137
        // The $value query object contains all regex which will be used by the collector to parse the route fields
138 1
        $value->setQuery($key, $string, $field_required);
139
140
        /* Every parameter can include it's own logic into the regular expression,
141
         * it can use backreferences and it's expected to be used against a single parameter.
142
         * This means that it can't be used as is to build the route regular expression,
143
         * Backreferences are not useful at this point and can make the regular expression more time consuming
144
         * and resource hungry. This is why they are replaced with the grouping parenthesis.
145
         * Eg: (value) changes in (?:value)
146
         *
147
         * Delimiting characters like '^' and '$' are also meaningless in the complete regular expression and
148
         * need to be removed. Contrariwise, wildcards must be delimited in order to keep the whole regular
149
         * expression consistent, hence a '?' is added to all the '.*' or '.+' that don't already have one.
150
         */
151 1
        $string = preg_replace("/(?<!\\\\)\\((?!\\?)/", '(?:', $string);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal /(?<!\\\\)\\((?!\\?)/ does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
152 1
        $string = preg_replace("/\\.([\\*\\+])(?!\\?)/", '.${1}?', $string);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal /\\.([\\*\\+])(?!\\?)/ does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
153 1
        $string = preg_replace("/^[\\^]/", '', $string);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal /^[\\^]/ does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
154 1
        $string = preg_replace("/[\\$]$/", '', $string);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal /[\\$]$/ does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
155
156
        /* The produced regular expression is grouped and associated with its key (this means that the 'preg_match'
157
         * function will generate an associative array where the key/value association is preserved).
158
         * If the field is required, the regular expression is completed with a '{1}' (which make it compulsory),
159
         * otherwise a '?' is added.
160
         */
161 1
        return '(?P<'.$key.'>'.$string.')'.(($field_required) ? '{1}' : '?');
162
163
    }
164
165
}
166