WebRouting   D
last analyzed

Complexity

Total Complexity 88

Size/Duplication

Total Lines 408
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
dl 0
loc 408
rs 4.8717
c 0
b 0
f 0
wmc 88
lcom 1
cbo 5

6 Methods

Rating   Name   Duplication   Size   Complexity  
A escapeOutputParameter() 0 4 1
B __construct() 0 24 1
F initialize() 0 117 36
A getBasePath() 0 4 1
A getBaseHref() 0 4 1
F gen() 0 166 48

How to fix   Complexity   

Complex Class

Complex classes like WebRouting often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WebRouting, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace Agavi\Routing;
3
4
// +---------------------------------------------------------------------------+
5
// | This file is part of the Agavi package.                                   |
6
// | Copyright (c) 2005-2011 the Agavi Project.                                |
7
// |                                                                           |
8
// | For the full copyright and license information, please view the LICENSE   |
9
// | file that was distributed with this source code. You can also view the    |
10
// | LICENSE file online at http://www.agavi.org/LICENSE.txt                   |
11
// |   vi: set noexpandtab:                                                    |
12
// |   Local Variables:                                                        |
13
// |   indent-tabs-mode: t                                                     |
14
// |   End:                                                                    |
15
// +---------------------------------------------------------------------------+
16
use Agavi\Core\Context;
17
use Agavi\Request\RequestDataHolder;
18
use Agavi\Request\WebRequest;
19
use Agavi\Util\Toolkit;
20
21
/**
22
 * WebRouting sets the prefix and input with some magic from the request
23
 * uri and path_info
24
 *
25
 * @package    agavi
26
 * @subpackage routing
27
 *
28
 * @author     Dominik del Bondio <[email protected]>
29
 * @copyright  Authors
30
 * @copyright  The Agavi Project
31
 *
32
 * @since      0.11.0
33
 *
34
 * @version    $Id$
35
 */
36
class WebRouting extends Routing
37
{
38
    /**
39
     * @var        string The path to the application's root with trailing slash.
40
     */
41
    protected $basePath = '';
42
43
    /**
44
     * @var        string The URL to the application's root with trailing slash.
45
     */
46
    protected $baseHref = '';
47
48
    /**
49
     * @var        array The GET parameters that were passed in the URL.
50
     */
51
    protected $inputParameters = array();
52
53
    /**
54
     * @var        array arg_separator.input as defined in php.ini, exploded
55
     */
56
    protected $argSeparatorInput = array('&');
57
58
    /**
59
     * @var        string arg_separator.output as defined in php.ini
60
     */
61
    protected $argSeparatorOutput = '&amp;';
62
63
    /**
64
     * Constructor.
65
     *
66
     * @author     David Zülke <[email protected]>
67
     * @since      0.11.0
68
     */
69
    public function __construct()
70
    {
71
        parent::__construct();
72
73
        $this->defaultGenOptions = array_merge($this->defaultGenOptions, array(
74
            // separator, typically &amp; for HTML, & otherwise
75
            'separator' => '&amp;',
76
            // whether or not to append the SID if necessary
77
            'use_trans_sid' => false,
78
            // scheme, or true to include, or false to block
79
            'scheme' => null,
80
            // authority, or true to include, or false to block
81
            'authority' => null,
82
            // host, or true to include, or false to block
83
            'host' => null,
84
            // port, or true to include, or false to block
85
            'port' => null,
86
            // fragment identifier (#foo)
87
            'fragment' => null,
88
        ));
89
        
90
        $this->argSeparatorInput = str_split(ini_get('arg_separator.input'));
91
        $this->argSeparatorOutput = ini_get('arg_separator.output');
92
    }
93
94
    /**
95
     * Initialize the routing instance.
96
     *
97
     * @param      Context $context The Context.
98
     * @param      array   $parameters An array of initialization parameters.
99
     *
100
     * @author     David Zülke <[email protected]>
101
     * @author     Veikko Mäkinen <[email protected]>
102
     * @author     Dominik del Bondio <[email protected]>
103
     * @since      0.11.0
104
     */
105
    public function initialize(Context $context, array $parameters = array())
0 ignored issues
show
Coding Style introduced by
initialize uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
initialize uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
initialize uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
initialize uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
initialize uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
106
    {
107
        parent::initialize($context, $parameters);
108
109
        /** @var WebRequest $rq */
110
        $rq = $this->context->getRequest();
111
112
        /** @var RequestDataHolder $rd */
113
        $rd = $rq->getRequestData();
114
115
        // 'scheme://authority' is necessary so parse_url doesn't stumble over '://' in the request URI
116
        $ru = array_merge(array('path' => '', 'query' => ''), parse_url('scheme://authority' . $rq->getRequestUri()));
117
118
        if (isset($_SERVER['QUERY_STRING'])) {
119
            $qs = $_SERVER['QUERY_STRING'];
120
        } else {
121
            $qs = '';
122
        }
123
124
        // when rewriting, apache strips one (not all) trailing ampersand from the end of QUERY_STRING... normalize:
125
        $rewritten = (preg_replace('/&+$/D', '', $qs) !== preg_replace('/&+$/D', '', $ru['query']));
126
127
        if ($this->isEnabled() && $rewritten) {
128
            // strip the one trailing ampersand, see above
129
            $queryWasEmptied = false;
130
            if ($ru['query'] !== '' && isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false) {
131
                $ru['query'] = preg_replace('/&$/D', '', $ru['query']);
132
                if ($ru['query'] == '') {
133
                    $queryWasEmptied = true;
134
                }
135
            }
136
137
            $stripFromQuery = '&' . $ru['query'];
138
            if ($ru['query'] == '' && !$queryWasEmptied && isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false) {
139
                // if the query is empty, simply give apache2 nothing instead of an "&", since that could kill a real trailing ampersand in the path, as Apache strips those from the query string (which has the rewritten path), but not the request uri
140
                $stripFromQuery = '';
141
            }
142
            $this->input = preg_replace('/' . preg_quote($stripFromQuery, '/') . '$/D', '', $qs);
143
144
            if (isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Apache/2') !== false) {
145
                $sru = $_SERVER['REQUEST_URI'];
146
                
147
                if (($fqmp = strpos($sru, '?')) !== false && ($fqmp == strlen($sru)-1)) {
148
                    // strip a trailing question mark, but only if it really is the query string separator (i.e. the only question mark in the URI)
149
                    $sru = substr($sru, 0, -1);
150
                } elseif ($ru['query'] !== '' || $queryWasEmptied) {
151
                    // if there is a trailing ampersand (in query string or path, whatever ends the URL), strip it (but just one)
152
                    $sru = preg_replace('/&$/D', '', $sru);
153
                }
154
                
155
                // multiple consecutive slashes got lost in our input thanks to an apache bug
156
                // let's fix that
157
                $cqs = preg_replace('#/{2,}#', '/', rawurldecode($ru['query']));
158
                $cru = preg_replace('#/{2,}#', '/', rawurldecode($sru));
159
                $tmp = preg_replace('/' . preg_quote($this->input . (($cqs != '' || $queryWasEmptied) ? '?' . $cqs : ''), '/') . '$/D', '', $cru);
160
                $input = preg_replace('/^' . preg_quote($tmp, '/') . '/', '', $sru);
161
                if ($ru['query'] !== '' || $queryWasEmptied) {
162
                    $input = preg_replace('/' . preg_quote('?' . $ru['query'], '/') . '$/D', '', $input);
163
                }
164
                $this->input = $input;
165
            }
166
167
            if (!(isset($_SERVER['SERVER_SOFTWARE']) && (strpos($_SERVER['SERVER_SOFTWARE'], 'Apache/1') !== false || (strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false && isset($_SERVER['UNENCODED_URL']))))) {
168
                // don't do that for Apache 1 or IIS 7 with URL Rewrite Module, it's already rawurldecode()d there
169
                $this->input = rawurldecode($this->input);
170
            }
171
172
            $this->basePath = $this->prefix = preg_replace('/' . preg_quote($this->input, '/') . '$/D', '', rawurldecode($ru['path']));
173
174
            // that was easy. now clean up $_GET and the Request
175
            $parsedRuQuery = $parsedInput = '';
176
            parse_str($ru['query'], $parsedRuQuery);
177
            parse_str($this->input, $parsedInput);
178
            if (get_magic_quotes_gpc()) {
179
                $parsedRuQuery = WebRequest::clearMagicQuotes($parsedRuQuery);
0 ignored issues
show
Bug introduced by
It seems like $parsedRuQuery can also be of type null; however, Agavi\Request\WebRequest::clearMagicQuotes() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
180
                $parsedInput = WebRequest::clearMagicQuotes($parsedInput, false /* start on the first level */);
0 ignored issues
show
Bug introduced by
It seems like $parsedInput can also be of type null; however, Agavi\Request\WebRequest::clearMagicQuotes() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Unused Code introduced by
The call to WebRequest::clearMagicQuotes() has too many arguments starting with false.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
181
            }
182
            foreach (array_diff(array_keys($parsedInput), array_keys($parsedRuQuery)) as $unset) {
183
                // our element is in $_GET
184
                unset($_GET[$unset]);
185
                unset($GLOBALS['HTTP_GET_VARS'][$unset]);
186
                // if it is not also in $_POST, then we need to remove it from the request params
187
                if (!isset($_POST[$unset])) {
188
                    $rd->removeParameter($unset);
189
                    // and from $_REQUEST, too!
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
190
                    unset($_REQUEST[$unset]);
191
                }
192
            }
193
        } else {
194
            $sn = $_SERVER['SCRIPT_NAME'];
195
            $path = rawurldecode($ru['path']);
196
197
            $appendFrom = 0;
198
            $this->prefix = Toolkit::stringBase($sn, $path, $appendFrom);
199
            $this->prefix .= substr($sn, $appendFrom);
200
201
            $this->input = substr($path, $appendFrom);
202
            if (!isset($_SERVER['SERVER_SOFTWARE']) || strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') === false || isset($_SERVER['HTTP_X_REWRITE_URL']) || !isset($_SERVER['GATEWAY_INTERFACE']) || strpos($_SERVER['GATEWAY_INTERFACE'], 'CGI') === false) {
203
                // don't do that for IIS-CGI, it's already rawurldecode()d there
204
                $this->input = rawurldecode($this->input);
205
            }
206
207
            $this->basePath = str_replace('\\', '/', dirname($this->prefix));
208
        }
209
210
        $this->inputParameters = $_GET;
211
212
        if (!$this->input) {
213
            $this->input = "/";
214
        }
215
216
        if (substr($this->basePath, -1, 1) != '/') {
217
            $this->basePath .= '/';
218
        }
219
220
        $this->baseHref = $rq->getUrlScheme() . '://' . $rq->getUrlAuthority() . $this->basePath;
221
    }
222
223
    /**
224
     * Retrieve the base path where the application's root sits
225
     *
226
     * @return     string A path string, including a trailing slash.
227
     *
228
     * @author     David Zülke <[email protected]>
229
     * @since      0.11.0
230
     */
231
    public function getBasePath()
232
    {
233
        return $this->basePath;
234
    }
235
236
    /**
237
     * Retrieve the full URL to the application's root.
238
     *
239
     * @return     string A URL string, including the protocol, the server port
240
      *                   (if necessary) and the path including a trailing slash.
241
     *
242
     * @author     David Zülke <[email protected]>
243
     * @since      0.11.0
244
     */
245
    public function getBaseHref()
246
    {
247
        return $this->baseHref;
248
    }
249
    
250
    /**
251
     * Generate a formatted Agavi URL.
252
     *
253
     * @param      string $route A route name.
254
     * @param      array  $params An associative array of parameters.
255
     * @param      mixed  $options An array of options, or the name of an options preset.
256
     *
257
     * @return     string The generated URL.
258
     *
259
     * @author     David Zülke <[email protected]>
260
     * @since      0.11.0
261
     */
262
    public function gen($route, array $params = array(), $options = array())
0 ignored issues
show
Coding Style introduced by
gen uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
263
    {
264
        /** @var WebRequest $req */
265
        $req = $this->context->getRequest();
266
267
        if (substr($route, -1) == '*') {
268
            $options['refill_all_parameters'] = true;
269
            $route = substr($route, 0, -1);
270
        }
271
272
        $options = $this->resolveGenOptions($options);
273
274
        $aso = $this->argSeparatorOutput;
275
        if ($options['separator'] != $aso) {
276
            $aso = $options['separator'];
277
        }
278
279
        if ($options['use_trans_sid'] === true && defined('SID') && SID !== '') {
280
            $params = array_merge($params, array(session_name() => session_id()));
281
        }
282
283
        if ($route === null && empty($params)) {
284
            $retval = $req->getRequestUri();
285
            $retval = str_replace(array('[', ']', '\''), array('%5B', '%5D', '%27'), $retval);
286
            // much quicker than str_replace($this->argSeparatorInput, array_fill(0, count($this->argSeparatorInput), $aso), $retval)
0 ignored issues
show
Unused Code Comprehensibility introduced by
52% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
287
            foreach ($this->argSeparatorInput as $char) {
288
                $retval = str_replace($char, $aso, $retval);
289
            }
290
        } else {
291
            if ($this->isEnabled()) {
292
                // the route exists and routing is enabled, the parent method handles it
293
294
                $append = '';
295
296
                list($path, $usedParams, $options, $extraParams, $isNullRoute) = parent::gen($route, $params, $options);
0 ignored issues
show
Unused Code introduced by
The assignment to $usedParams is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
297
                
298
                if ($isNullRoute) {
299
                    // add the incoming parameters from the request uri for gen(null) and friends
300
                    $extraParams = array_merge($this->inputParameters, $extraParams);
301
                }
302
                if (count($extraParams) > 0) {
303
                    $append = http_build_query($extraParams, '', $aso);
304
                    if ($append !== '') {
305
                        $append = '?' . $append;
306
                    }
307
                }
308
            } else {
309
                // the route exists, but we must create a normal index.php?foo=bar URL.
310
311
                $isNullRoute = false;
312
                $routes = $this->getAffectedRoutes($route, $isNullRoute);
313
                if ($isNullRoute) {
314
                    $params = array_merge($this->inputParameters, $params);
315
                }
316
                if (count($routes) == 0) {
317
                    $path = $route;
318
                }
319
320
                // we collect the default parameters from the route and make sure
321
                // new parameters don't overwrite already defined parameters
322
                $defaults = array();
323
324
                $ma = $req->getParameter('module_accessor');
325
                $aa = $req->getParameter('controller_accessor');
326
327
                foreach ($routes as $route) {
328
                    if (isset($this->routes[$route])) {
329
                        $r = $this->routes[$route];
330
                        $myDefaults = array();
331
332
                        foreach ($r['opt']['defaults'] as $key => $default) {
333
                            $myDefaults[$key] = $default->getValue();
334
                        }
335
                        if ($r['opt']['module']) {
336
                            $myDefaults[$ma] = $r['opt']['module'];
337
                        }
338
                        if ($r['opt']['controller']) {
339
                            $myDefaults[$aa] = $r['opt']['controller'];
340
                        }
341
342
                        $defaults = array_merge($myDefaults, $defaults);
343
                    }
344
                }
345
346
                $params = array_merge($defaults, $params);
347
            }
348
            
349
            if (!isset($path)) {
350
                // the route does not exist. we generate a normal index.php?foo=bar URL.
351
                $path = $_SERVER['SCRIPT_NAME'];
352
            }
353
            
354
            if (!isset($path)) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
355
                // routing was off; the name of the route is the input
356
            }
357
            if (!isset($append)) {
358
                $append = '?' . http_build_query($params, '', $aso);
359
            }
360
361
            $retval = $path . $append;
362
        }
363
364
        if (!$options['relative'] ||
365
            ($options['relative'] && (
366
                $options['scheme'] !== null ||
367
                $options['authority'] !== null ||
368
                $options['host'] !== null ||
369
                $options['port'] !== null
370
            ))
371
        ) {
372
            $scheme = false;
373
            if ($options['scheme'] !== false) {
374
                $scheme = ($options['scheme'] === null ? $req->getUrlScheme() : $options['scheme']);
375
            }
376
377
            $authority = '';
378
379
            if ($options['authority'] === null) {
380
                if ($options['host'] !== null && $options['host'] !== false) {
381
                    $authority = $options['host'];
382
                } elseif ($options['host'] === false) {
383
                    $authority = '';
384
                } else {
385
                    $authority = $req->getUrlHost();
386
                }
387
                $port = null;
388
                if ($options['port'] !== null && $options['port'] !== false) {
389
                    if (Toolkit::isPortNecessary($options['scheme'] !== null && $options['scheme'] !== false ? $options['scheme'] : $req->getUrlScheme(), $options['port'])) {
390
                        $port = $options['port'];
391
                    } else {
392
                        $port = null;
393
                    }
394
                } elseif ($options['port'] === false) {
395
                    $port = null;
396
                } elseif ($options['scheme'] === null) {
397
                    if (!Toolkit::isPortNecessary($req->getUrlScheme(), $port = $req->getUrlPort())) {
398
                        $port = null;
399
                    }
400
                }
401
                if ($port !== null) {
402
                    $authority .= ':' . $port;
403
                }
404
            } elseif ($options['authority'] !== false) {
405
                $authority = $options['authority'];
406
            }
407
408
            if ($scheme === false) {
409
                // nothing at all, e.g. when displaying a URL without the "http://" prefix
410
                $scheme = '';
411
            } elseif (trim($scheme) === '') {
412
                // a protocol-relative URL (see #1224)
413
                $scheme = '//';
414
            } else {
415
                // given scheme plus "://"
416
                $scheme = $scheme . '://';
417
            }
418
            
419
            $retval = $scheme . $authority . $retval;
420
        }
421
422
        if ($options['fragment'] !== null) {
423
            $retval .= '#' . $options['fragment'];
424
        }
425
426
        return $retval;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $retval; (string) is incompatible with the return type of the parent method Agavi\Routing\Routing::gen of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
427
    }
428
429
    /**
430
     * Escapes an argument to be used in an generated route.
431
     *
432
     * @param      string $string The argument to be escaped.
433
     *
434
     * @return     string The escaped argument.
435
     *
436
     * @author     Dominik del Bondio <[email protected]>
437
     * @since      0.11.0
438
     */
439
    public function escapeOutputParameter($string)
440
    {
441
        return rawurlencode($string);
442
    }
443
}
444