Completed
Push — master ( 4ad6bd...3873e4 )
by Ingo
11:53
created

Director::mockRequest()   F

Complexity

Conditions 16
Paths 1728

Size

Total Lines 121
Code Lines 68

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 16
eloc 68
nc 1728
nop 9
dl 0
loc 121
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace SilverStripe\Control;
4
5
use SilverStripe\CMS\Model\SiteTree;
6
use SilverStripe\Core\Config\Configurable;
7
use SilverStripe\Core\Environment;
8
use SilverStripe\Core\Injector\Injector;
9
use SilverStripe\Core\Kernel;
10
use SilverStripe\Dev\Deprecation;
11
use SilverStripe\Versioned\Versioned;
12
use SilverStripe\View\Requirements;
13
use SilverStripe\View\Requirements_Backend;
14
use SilverStripe\View\TemplateGlobalProvider;
15
16
/**
17
 * Director is responsible for processing URLs, and providing environment information.
18
 *
19
 * The most important part of director is {@link Director::direct()}, which is passed a URL and will
20
 * execute the appropriate controller.
21
 *
22
 * Director also has a number of static methods that provide information about the environment, such as
23
 * {@link Director::$environment_type}.
24
 *
25
 * @see Director::direct()
26
 * @see Director::$rules
27
 * @see Director::$environment_type
28
 */
29
class Director implements TemplateGlobalProvider
30
{
31
    use Configurable;
32
33
    /**
34
     * Specifies this url is relative to the base.
35
     *
36
     * @var string
37
     */
38
    const BASE = 'BASE';
39
40
    /**
41
     * Specifies this url is relative to the site root.
42
     *
43
     * @var string
44
     */
45
    const ROOT = 'ROOT';
46
47
    /**
48
     * specifies this url is relative to the current request.
49
     *
50
     * @var string
51
     */
52
    const REQUEST = 'REQUEST';
53
54
    /**
55
     * @config
56
     * @var array
57
     */
58
    private static $rules = array();
59
60
    /**
61
     * Set current page
62
     *
63
     * @internal
64
     * @var SiteTree
65
     */
66
    private static $current_page;
67
68
    /**
69
     * @config
70
     * @var string
71
     */
72
    private static $alternate_base_folder;
73
74
    /**
75
     * Force the base_url to a specific value.
76
     * If assigned, default_base_url and the value in the $_SERVER
77
     * global is ignored.
78
     * Supports back-ticked vars; E.g. '`SS_BASE_URL`'
79
     *
80
     * @config
81
     * @var string
82
     */
83
    private static $alternate_base_url;
84
85
    /**
86
     * Base url to populate if cannot be determined otherwise.
87
     * Supports back-ticked vars; E.g. '`SS_BASE_URL`'
88
     *
89
     * @config
90
     * @var string
91
     */
92
    private static $default_base_url = '`SS_BASE_URL`';
93
94
    /**
95
     * Assigned environment type
96
     *
97
     * @internal
98
     * @var string
99
     */
100
    protected static $environment_type;
101
102
    /**
103
     * Process the given URL, creating the appropriate controller and executing it.
104
     *
105
     * Request processing is handled as follows:
106
     * - Director::direct() creates a new HTTPResponse object and passes this to
107
     *   Director::handleRequest().
108
     * - Director::handleRequest($request) checks each of the Director rules and identifies a controller
109
     *   to handle this request.
110
     * - Controller::handleRequest($request) is then called.  This will find a rule to handle the URL,
111
     *   and call the rule handling method.
112
     * - RequestHandler::handleRequest($request) is recursively called whenever a rule handling method
113
     *   returns a RequestHandler object.
114
     *
115
     * In addition to request processing, Director will manage the session, and perform the output of
116
     * the actual response to the browser.
117
     *
118
     * @uses handleRequest() rule-lookup logic is handled by this.
119
     * @uses TestController::handleRequest() This handles the page logic for a Director::direct() call.
120
     * @param HTTPRequest $request
121
     * @return HTTPResponse
122
     * @throws HTTPResponse_Exception
123
     */
124
    public static function direct(HTTPRequest $request)
125
    {
126
        // check allowed hosts
127
        if (getenv('SS_ALLOWED_HOSTS') && !static::is_cli()) {
128
            $allowedHosts = explode(',', getenv('SS_ALLOWED_HOSTS'));
129
            if (!in_array(static::host(), $allowedHosts)) {
130
                return new HTTPResponse('Invalid Host', 400);
131
            }
132
        }
133
134
        // Pre-request
135
        $output = RequestProcessor::singleton()->preRequest($request);
136
        if ($output === false) {
137
            return new HTTPResponse(_t(__CLASS__.'.INVALID_REQUEST', 'Invalid request'), 400);
138
        }
139
140
        // Generate output
141
        $result = static::handleRequest($request);
142
143
        // Save session data. Note that save() will start/resume the session if required.
144
        $request->getSession()->save();
145
146
        // Post-request handling
147
        $postRequest = RequestProcessor::singleton()->postRequest($request, $result);
148
        if ($postRequest === false) {
149
            return new HTTPResponse(_t(__CLASS__ . '.REQUEST_ABORTED', 'Request aborted'), 500);
150
        }
151
152
        // Return
153
        return $result;
154
    }
155
156
    /**
157
     * Test a URL request, returning a response object. This method is the counterpart of
158
     * Director::direct() that is used in functional testing. It will execute the URL given, and
159
     * return the result as an HTTPResponse object.
160
     *
161
     * @uses TestController::handleRequest() Handles the page logic for a Director::direct() call.
162
     *
163
     * @param string $url The URL to visit.
164
     * @param array $postVars The $_POST & $_FILES variables.
165
     * @param array|Session $session The {@link Session} object representing the current session.
166
     * By passing the same object to multiple  calls of Director::test(), you can simulate a persisted
167
     * session.
168
     * @param string $httpMethod The HTTP method, such as GET or POST.  It will default to POST if
169
     * postVars is set, GET otherwise. Overwritten by $postVars['_method'] if present.
170
     * @param string $body The HTTP body.
171
     * @param array $headers HTTP headers with key-value pairs.
172
     * @param array|Cookie_Backend $cookies to populate $_COOKIE.
173
     * @param HTTPRequest $request The {@see SS_HTTP_Request} object generated as a part of this request.
174
     *
175
     * @return HTTPResponse
176
     *
177
     * @throws HTTPResponse_Exception
178
     */
179
    public static function test(
180
        $url,
181
        $postVars = [],
182
        $session = array(),
183
        $httpMethod = null,
184
        $body = null,
185
        $headers = array(),
186
        $cookies = array(),
187
        &$request = null
188
    ) {
189
        return static::mockRequest(
190
            function (HTTPRequest $request) {
191
                return static::direct($request);
192
            },
193
            $url,
194
            $postVars,
195
            $session,
196
            $httpMethod,
197
            $body,
198
            $headers,
199
            $cookies,
200
            $request
201
        );
202
    }
203
204
    /**
205
     * Mock a request, passing this to the given callback, before resetting.
206
     *
207
     * @param callable $callback Action to pass the HTTPRequst object
208
     * @param string $url The URL to build
209
     * @param array $postVars The $_POST & $_FILES variables.
210
     * @param array|Session $session The {@link Session} object representing the current session.
211
     * By passing the same object to multiple  calls of Director::test(), you can simulate a persisted
212
     * session.
213
     * @param string $httpMethod The HTTP method, such as GET or POST.  It will default to POST if
214
     * postVars is set, GET otherwise. Overwritten by $postVars['_method'] if present.
215
     * @param string $body The HTTP body.
216
     * @param array $headers HTTP headers with key-value pairs.
217
     * @param array|Cookie_Backend $cookies to populate $_COOKIE.
218
     * @param HTTPRequest $request The {@see SS_HTTP_Request} object generated as a part of this request.
219
     * @return mixed Result of callback
220
     */
221
    public static function mockRequest(
0 ignored issues
show
Coding Style introduced by
mockRequest uses the super-global variable $_SESSION 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...
222
        $callback,
0 ignored issues
show
Unused Code introduced by
The parameter $callback 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...
223
        $url,
224
        $postVars = [],
225
        $session = [],
226
        $httpMethod = null,
227
        $body = null,
228
        $headers = [],
229
        $cookies = [],
230
        &$request = null
231
    ) {
232
        // Build list of cleanup promises
233
        $finally = [];
234
235
        /** @var Kernel $kernel */
236
        $kernel = Injector::inst()->get(Kernel::class);
237
        $kernel->nest();
238
        $finally[] = function () use ($kernel) {
239
            $kernel->activate();
240
        };
241
242
        // backup existing vars, and create new vars
243
        $existingVars = Environment::getVariables();
244
        $finally[] = function () use ($existingVars) {
245
            Environment::setVariables($existingVars);
246
        };
247
        $newVars = $existingVars;
248
249
        // These are needed so that calling Director::test() does not muck with whoever is calling it.
250
        // Really, it's some inappropriate coupling and should be resolved by making less use of statics.
251
        if (class_exists(Versioned::class)) {
252
            $oldReadingMode = Versioned::get_reading_mode();
253
            $finally[] = function () use ($oldReadingMode) {
254
                Versioned::set_reading_mode($oldReadingMode);
255
            };
256
        }
257
258
        // Default httpMethod
259
        $newVars['_SERVER']['REQUEST_METHOD'] = $httpMethod ?: ($postVars ? "POST" : "GET");
260
        $newVars['_POST'] = (array)$postVars;
261
262
        // Setup session
263
        if ($session instanceof Session) {
264
            // Note: If passing $session as object, ensure that changes are written back
265
            // This is important for classes such as FunctionalTest which emulate cross-request persistence
266
            $newVars['_SESSION'] = $session->getAll();
267
            $finally[] = function () use ($session) {
268
                if (isset($_SESSION)) {
269
                    foreach ($_SESSION as $key => $value) {
270
                        $session->set($key, $value);
271
                    }
272
                }
273
            };
274
        } else {
275
            $newVars['_SESSION'] = $session ?: [];
276
        }
277
278
        // Setup cookies
279
        $cookieJar = $cookies instanceof Cookie_Backend
280
            ? $cookies
281
            : Injector::inst()->createWithArgs(Cookie_Backend::class, array($cookies ?: []));
282
        $newVars['_COOKIE'] = $cookieJar->getAll(false);
283
        Cookie::config()->update('report_errors', false);
284
        Injector::inst()->registerService($cookieJar, Cookie_Backend::class);
285
286
        // Backup requirements
287
        $existingRequirementsBackend = Requirements::backend();
288
        Requirements::set_backend(Requirements_Backend::create());
289
        $finally[] = function () use ($existingRequirementsBackend) {
290
            Requirements::set_backend($existingRequirementsBackend);
291
        };
292
293
        // Strip any hash
294
        $url = strtok($url, '#');
295
296
        // Handle absolute URLs
297
        if (parse_url($url, PHP_URL_HOST)) {
298
            $bits = parse_url($url);
299
300
            // If a port is mentioned in the absolute URL, be sure to add that into the HTTP host
301
            $newVars['_SERVER']['HTTP_HOST'] = isset($bits['port'])
302
                ? $bits['host'].':'.$bits['port']
303
                : $bits['host'];
304
        }
305
306
        // Ensure URL is properly made relative.
307
        // Example: url passed is "/ss31/my-page" (prefixed with BASE_URL), this should be changed to "my-page"
308
        $url = self::makeRelative($url);
309
        if (strpos($url, '?') !== false) {
310
            list($url, $getVarsEncoded) = explode('?', $url, 2);
311
            parse_str($getVarsEncoded, $newVars['_GET']);
312
        } else {
313
            $newVars['_GET'] = [];
314
        }
315
        $newVars['_SERVER']['REQUEST_URI'] = Director::baseURL() . $url;
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
316
        $newVars['_REQUEST'] = array_merge($newVars['_GET'], $newVars['_POST']);
317
318
        // Normalise vars
319
        $newVars = HTTPRequestBuilder::cleanEnvironment($newVars);
320
321
        // Create new request
322
        $request = HTTPRequestBuilder::createFromVariables($newVars, $body);
323
        if ($headers) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $headers of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
324
            foreach ($headers as $k => $v) {
325
                $request->addHeader($k, $v);
326
            }
327
        }
328
329
        // Apply new vars to environment
330
        Environment::setVariables($newVars);
331
332
        try {
333
            // Normal request handling
334
            return call_user_func($callback, $request);
335
        } finally {
336
            // Restore state in reverse order to assignment
337
            foreach (array_reverse($finally) as $callback) {
338
                call_user_func($callback);
339
            }
340
        }
341
    }
342
343
    /**
344
     * Handle an HTTP request, defined with a HTTPRequest object.
345
     *
346
     * @skipUpgrade
347
     * @param HTTPRequest $request
348
     * @return HTTPResponse
349
     */
350
    protected static function handleRequest(HTTPRequest $request)
351
    {
352
        $rules = Director::config()->uninherited('rules');
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
353
354
        foreach ($rules as $pattern => $controllerOptions) {
355
            // Normalise route rule
356
            if (is_string($controllerOptions)) {
357
                if (substr($controllerOptions, 0, 2) == '->') {
358
                    $controllerOptions = array('Redirect' => substr($controllerOptions, 2));
359
                } else {
360
                    $controllerOptions = array('Controller' => $controllerOptions);
361
                }
362
            }
363
364
            // Match pattern
365
            $arguments = $request->match($pattern, true);
366
            if ($arguments !== false) {
367
                $request->setRouteParams($controllerOptions);
368
                // controllerOptions provide some default arguments
369
                $arguments = array_merge($controllerOptions, $arguments);
370
371
                // Pop additional tokens from the tokenizer if necessary
372
                if (isset($controllerOptions['_PopTokeniser'])) {
373
                    $request->shift($controllerOptions['_PopTokeniser']);
374
                }
375
376
                // Handle redirection
377
                if (isset($arguments['Redirect'])) {
378
                    // Redirection
379
                    $response = new HTTPResponse();
380
                    $response->redirect(static::absoluteURL($arguments['Redirect']));
0 ignored issues
show
Security Bug introduced by
It seems like static::absoluteURL($arguments['Redirect']) targeting SilverStripe\Control\Director::absoluteURL() can also be of type false; however, SilverStripe\Control\HTTPResponse::redirect() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
381
                    return $response;
382
                }
383
384
                // Find the controller name
385
                $controller = $arguments['Controller'];
386
                $controllerObj = Injector::inst()->create($controller);
387
388
                try {
389
                    return $controllerObj->handleRequest($request);
390
                } catch (HTTPResponse_Exception $responseException) {
391
                    return $responseException->getResponse();
392
                }
393
            }
394
        }
395
396
        // No URL rules matched, so return a 404 error.
397
        return new HTTPResponse('No URL rule was matched', 404);
398
    }
399
400
    /**
401
     * Return the {@link SiteTree} object that is currently being viewed. If there is no SiteTree
402
     * object to return, then this will return the current controller.
403
     *
404
     * @return SiteTree|Controller
405
     */
406
    public static function get_current_page()
407
    {
408
        return self::$current_page ? self::$current_page : Controller::curr();
409
    }
410
411
    /**
412
     * Set the currently active {@link SiteTree} object that is being used to respond to the request.
413
     *
414
     * @param SiteTree $page
415
     */
416
    public static function set_current_page($page)
417
    {
418
        self::$current_page = $page;
419
    }
420
421
    /**
422
     * Turns the given URL into an absolute URL. By default non-site root relative urls will be
423
     * evaluated relative to the current base_url.
424
     *
425
     * @param string $url URL To transform to absolute.
426
     * @param string $relativeParent Method to use for evaluating relative urls.
427
     * Either one of BASE (baseurl), ROOT (site root), or REQUEST (requested page).
428
     * Defaults to BASE, which is the same behaviour as template url resolution.
429
     * Ignored if the url is absolute or site root.
430
     *
431
     * @return string
432
     */
433
    public static function absoluteURL($url, $relativeParent = self::BASE)
0 ignored issues
show
Coding Style introduced by
absoluteURL 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...
434
    {
435
        if (is_bool($relativeParent)) {
436
            // Deprecate old boolean second parameter
437
            Deprecation::notice('5.0', 'Director::absoluteURL takes an explicit parent for relative url');
438
            $relativeParent = $relativeParent ? self::BASE : self::REQUEST;
439
        }
440
441
        // Check if there is already a protocol given
442
        if (preg_match('/^http(s?):\/\//', $url)) {
443
            return $url;
444
        }
445
446
        // Absolute urls without protocol are added
447
        // E.g. //google.com -> http://google.com
448
        if (strpos($url, '//') === 0) {
449
            return self::protocol() . substr($url, 2);
450
        }
451
452
        // Determine method for mapping the parent to this relative url
453
        if ($relativeParent === self::ROOT || self::is_root_relative_url($url)) {
454
            // Root relative urls always should be evaluated relative to the root
455
            $parent = self::protocolAndHost();
456
        } elseif ($relativeParent === self::REQUEST) {
457
            // Request relative urls rely on the REQUEST_URI param (old default behaviour)
458
            if (!isset($_SERVER['REQUEST_URI'])) {
459
                return false;
460
            }
461
            $parent = dirname($_SERVER['REQUEST_URI'] . 'x');
462
        } else {
463
            // Default to respecting site base_url
464
            $parent = self::absoluteBaseURL();
465
        }
466
467
        // Map empty urls to relative slash and join to base
468
        if (empty($url) || $url === '.' || $url === './') {
469
            $url = '/';
470
        }
471
        return Controller::join_links($parent, $url);
472
    }
473
474
    /**
475
     * A helper to determine the current hostname used to access the site.
476
     * The following are used to determine the host (in order)
477
     *  - Director.alternate_base_url (if it contains a domain name)
478
     *  - Trusted proxy headers
479
     *  - HTTP Host header
480
     *  - SS_BASE_URL env var
481
     *  - SERVER_NAME
482
     *  - gethostname()
483
     *
484
     * @return string
485
     */
486
    public static function host()
0 ignored issues
show
Coding Style introduced by
host 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...
487
    {
488
        // Check if overridden by alternate_base_url
489
        if ($baseURL = self::config()->get('alternate_base_url')) {
490
            $baseURL = Injector::inst()->convertServiceProperty($baseURL);
491
            $host = parse_url($baseURL, PHP_URL_HOST);
492
            if ($host) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $host of type string|false is loosely compared to true; 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...
493
                return $host;
494
            }
495
        }
496
497
        // Validate proxy-specific headers
498
        if (TRUSTED_PROXY) {
499
            // Check headers to validate
500
            $headers = getenv('SS_TRUSTED_PROXY_HOST_HEADER')
501
                ? explode(',', getenv('SS_TRUSTED_PROXY_HOST_HEADER'))
502
                : ['HTTP_X_FORWARDED_HOST']; // Backwards compatible defaults
503
            foreach ($headers as $header) {
504
                if (!empty($_SERVER[$header])) {
505
                    // Get the first host, in case there's multiple separated through commas
506
                    return strtok($_SERVER[$header], ',');
507
                }
508
            }
509
        }
510
511
        // Check given header
512
        if (isset($_SERVER['HTTP_HOST'])) {
513
            return $_SERVER['HTTP_HOST'];
514
        }
515
516
        // Check base url
517
        if ($baseURL = self::config()->uninherited('default_base_url')) {
518
            $baseURL = Injector::inst()->convertServiceProperty($baseURL);
519
            $host = parse_url($baseURL, PHP_URL_HOST);
520
            if ($host) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $host of type string|false is loosely compared to true; 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...
521
                return $host;
522
            }
523
        }
524
525
        // Fail over to server_name (least reliable)
526
        return isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : gethostname();
527
    }
528
529
    /**
530
     * Returns the domain part of the URL 'http://www.mysite.com'. Returns FALSE is this environment
531
     * variable isn't set.
532
     *
533
     * @return bool|string
534
     */
535
    public static function protocolAndHost()
536
    {
537
        return static::protocol() . static::host();
538
    }
539
540
    /**
541
     * Return the current protocol that the site is running under.
542
     *
543
     * @return string
544
     */
545
    public static function protocol()
546
    {
547
        return (self::is_https()) ? 'https://' : 'http://';
548
    }
549
550
    /**
551
     * Return whether the site is running as under HTTPS.
552
     *
553
     * @return bool
554
     */
555
    public static function is_https()
0 ignored issues
show
Coding Style introduced by
is_https 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...
556
    {
557
        // Check override from alternate_base_url
558
        if ($baseURL = self::config()->uninherited('alternate_base_url')) {
559
            $baseURL = Injector::inst()->convertServiceProperty($baseURL);
560
            $protocol = parse_url($baseURL, PHP_URL_SCHEME);
561
            if ($protocol) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $protocol of type string|false is loosely compared to true; 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...
562
                return $protocol === 'https';
563
            }
564
        }
565
566
        // See https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
567
        // See https://support.microsoft.com/en-us/kb/307347
568
        if (TRUSTED_PROXY) {
569
            $headers = getenv('SS_TRUSTED_PROXY_PROTOCOL_HEADER')
570
                ? explode(',', getenv('SS_TRUSTED_PROXY_PROTOCOL_HEADER'))
571
                : ['HTTP_X_FORWARDED_PROTO', 'HTTP_X_FORWARDED_PROTOCOL', 'HTTP_FRONT_END_HTTPS'];
572
            foreach ($headers as $header) {
573
                $headerCompareVal = ($header === 'HTTP_FRONT_END_HTTPS' ? 'on' : 'https');
574
                if (!empty($_SERVER[$header]) && strtolower($_SERVER[$header]) == $headerCompareVal) {
575
                    return true;
576
                }
577
            }
578
        }
579
580
        // Check common $_SERVER
581
        if ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')) {
582
            return true;
583
        }
584
        if (isset($_SERVER['SSL'])) {
585
            return true;
586
        }
587
588
        // Check default_base_url
589
        if ($baseURL = self::config()->uninherited('default_base_url')) {
590
            $baseURL = Injector::inst()->convertServiceProperty($baseURL);
591
            $protocol = parse_url($baseURL, PHP_URL_SCHEME);
592
            if ($protocol) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $protocol of type string|false is loosely compared to true; 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...
593
                return $protocol === 'https';
594
            }
595
        }
596
597
        return false;
598
    }
599
600
    /**
601
     * Return the root-relative url for the baseurl
602
     *
603
     * @return string Root-relative url with trailing slash.
604
     */
605
    public static function baseURL()
606
    {
607
        // Check override base_url
608
        $alternate = self::config()->get('alternate_base_url');
609
        if ($alternate) {
610
            $alternate = Injector::inst()->convertServiceProperty($alternate);
611
            return rtrim(parse_url($alternate, PHP_URL_PATH), '/') . '/';
612
        }
613
614
        // Get env base url
615
        $baseURL = rtrim(BASE_URL, '/') . '/';
616
617
        // Check if BASE_SCRIPT_URL is defined
618
        // e.g. `index.php/`
619
        if (defined('BASE_SCRIPT_URL')) {
620
            return $baseURL . BASE_SCRIPT_URL;
621
        }
622
623
        return $baseURL;
624
    }
625
626
    /**
627
     * Returns the root filesystem folder for the site. It will be automatically calculated unless
628
     * it is overridden with {@link setBaseFolder()}.
629
     *
630
     * @return string
631
     */
632
    public static function baseFolder()
633
    {
634
        $alternate = Director::config()->uninherited('alternate_base_folder');
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
635
        return ($alternate) ? $alternate : BASE_PATH;
636
    }
637
638
    /**
639
     * Turns an absolute URL or folder into one that's relative to the root of the site. This is useful
640
     * when turning a URL into a filesystem reference, or vice versa.
641
     *
642
     * @param string $url Accepts both a URL or a filesystem path.
643
     *
644
     * @return string
645
     */
646
    public static function makeRelative($url)
647
    {
648
        // Allow for the accidental inclusion whitespace and // in the URL
649
        $url = trim(preg_replace('#([^:])//#', '\\1/', $url));
650
651
        $base1 = self::absoluteBaseURL();
652
        $baseDomain = substr($base1, strlen(self::protocol()));
653
654
        // Only bother comparing the URL to the absolute version if $url looks like a URL.
655
        if (preg_match('/^https?[^:]*:\/\//', $url, $matches)) {
656
            $urlProtocol = $matches[0];
657
            $urlWithoutProtocol = substr($url, strlen($urlProtocol));
658
659
            // If we are already looking at baseURL, return '' (substr will return false)
660
            if ($url == $base1) {
661
                return '';
662
            } elseif (substr($url, 0, strlen($base1)) == $base1) {
663
                return substr($url, strlen($base1));
664
            } elseif (substr($base1, -1) == "/" && $url == substr($base1, 0, -1)) {
665
                // Convert http://www.mydomain.com/mysitedir to ''
666
                return "";
667
            }
668
669
            if (substr($urlWithoutProtocol, 0, strlen($baseDomain)) == $baseDomain) {
670
                return substr($urlWithoutProtocol, strlen($baseDomain));
671
            }
672
        }
673
674
        // test for base folder, e.g. /var/www
675
        $base2 = self::baseFolder();
676
        if (substr($url, 0, strlen($base2)) == $base2) {
677
            return substr($url, strlen($base2));
678
        }
679
680
        // Test for relative base url, e.g. mywebsite/ if the full URL is http://localhost/mywebsite/
681
        $base3 = self::baseURL();
682
        if (substr($url, 0, strlen($base3)) == $base3) {
683
            return substr($url, strlen($base3));
684
        }
685
686
        // Test for relative base url, e.g mywebsite/ if the full url is localhost/myswebsite
687
        if (substr($url, 0, strlen($baseDomain)) == $baseDomain) {
688
            return substr($url, strlen($baseDomain));
689
        }
690
691
        // Nothing matched, fall back to returning the original URL
692
        return $url;
693
    }
694
695
    /**
696
     * Returns true if a given path is absolute. Works under both *nix and windows systems.
697
     *
698
     * @param string $path
699
     *
700
     * @return bool
701
     */
702
    public static function is_absolute($path)
703
    {
704
        if (empty($path)) {
705
            return false;
706
        }
707
        if ($path[0] == '/' || $path[0] == '\\') {
708
            return true;
709
        }
710
        return preg_match('/^[a-zA-Z]:[\\\\\/]/', $path) == 1;
711
    }
712
713
    /**
714
     * Determine if the url is root relative (i.e. starts with /, but not with //) SilverStripe
715
     * considers root relative urls as a subset of relative urls.
716
     *
717
     * @param string $url
718
     *
719
     * @return bool
720
     */
721
    public static function is_root_relative_url($url)
722
    {
723
        return strpos($url, '/') === 0 && strpos($url, '//') !== 0;
724
    }
725
726
    /**
727
     * Checks if a given URL is absolute (e.g. starts with 'http://' etc.). URLs beginning with "//"
728
     * are treated as absolute, as browsers take this to mean the same protocol as currently being used.
729
     *
730
     * Useful to check before redirecting based on a URL from user submissions through $_GET or $_POST,
731
     * and avoid phishing attacks by redirecting to an attackers server.
732
     *
733
     * Note: Can't solely rely on PHP's parse_url() , since it is not intended to work with relative URLs
734
     * or for security purposes. filter_var($url, FILTER_VALIDATE_URL) has similar problems.
735
     *
736
     * @param string $url
737
     *
738
     * @return bool
739
     */
740
    public static function is_absolute_url($url)
741
    {
742
        // Strip off the query and fragment parts of the URL before checking
743
        if (($queryPosition = strpos($url, '?')) !== false) {
744
            $url = substr($url, 0, $queryPosition-1);
745
        }
746
        if (($hashPosition = strpos($url, '#')) !== false) {
747
            $url = substr($url, 0, $hashPosition-1);
748
        }
749
        $colonPosition = strpos($url, ':');
750
        $slashPosition = strpos($url, '/');
751
        return (
752
            // Base check for existence of a host on a compliant URL
753
            parse_url($url, PHP_URL_HOST)
754
            // Check for more than one leading slash without a protocol.
755
            // While not a RFC compliant absolute URL, it is completed to a valid URL by some browsers,
756
            // and hence a potential security risk. Single leading slashes are not an issue though.
757
            || preg_match('%^\s*/{2,}%', $url)
758
            || (
759
                // If a colon is found, check if it's part of a valid scheme definition
760
                // (meaning its not preceded by a slash).
761
                $colonPosition !== false
762
                && ($slashPosition === false || $colonPosition < $slashPosition)
763
            )
764
        );
765
    }
766
767
    /**
768
     * Checks if a given URL is relative (or root relative) by checking {@link is_absolute_url()}.
769
     *
770
     * @param string $url
771
     *
772
     * @return bool
773
     */
774
    public static function is_relative_url($url)
775
    {
776
        return !static::is_absolute_url($url);
777
    }
778
779
    /**
780
     * Checks if the given URL is belonging to this "site" (not an external link). That's the case if
781
     * the URL is relative, as defined by {@link is_relative_url()}, or if the host matches
782
     * {@link protocolAndHost()}.
783
     *
784
     * Useful to check before redirecting based on a URL from user submissions through $_GET or $_POST,
785
     * and avoid phishing attacks by redirecting to an attackers server.
786
     *
787
     * @param string $url
788
     *
789
     * @return bool
790
     */
791
    public static function is_site_url($url)
792
    {
793
        $urlHost = parse_url($url, PHP_URL_HOST);
794
        $actualHost = parse_url(self::protocolAndHost(), PHP_URL_HOST);
795
        if ($urlHost && $actualHost && $urlHost == $actualHost) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $urlHost of type string|false is loosely compared to true; 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...
Bug Best Practice introduced by
The expression $actualHost of type string|false is loosely compared to true; 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...
796
            return true;
797
        } else {
798
            return self::is_relative_url($url);
799
        }
800
    }
801
802
    /**
803
     * Given a filesystem reference relative to the site root, return the full file-system path.
804
     *
805
     * @param string $file
806
     *
807
     * @return string
808
     */
809
    public static function getAbsFile($file)
810
    {
811
        return self::is_absolute($file) ? $file : Director::baseFolder() . '/' . $file;
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
812
    }
813
814
    /**
815
     * Returns true if the given file exists. Filename should be relative to the site root.
816
     *
817
     * @param $file
818
     *
819
     * @return bool
820
     */
821
    public static function fileExists($file)
822
    {
823
        // replace any appended query-strings, e.g. /path/to/foo.php?bar=1 to /path/to/foo.php
824
        $file = preg_replace('/([^\?]*)?.*/', '$1', $file);
825
        return file_exists(Director::getAbsFile($file));
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
826
    }
827
828
    /**
829
     * Returns the Absolute URL of the site root.
830
     *
831
     * @return string
832
     */
833
    public static function absoluteBaseURL()
834
    {
835
        return self::absoluteURL(
836
            self::baseURL(),
837
            self::ROOT
838
        );
839
    }
840
841
    /**
842
     * Returns the Absolute URL of the site root, embedding the current basic-auth credentials into
843
     * the URL.
844
     *
845
     * @return string
846
     */
847
    public static function absoluteBaseURLWithAuth()
0 ignored issues
show
Coding Style introduced by
absoluteBaseURLWithAuth 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...
848
    {
849
        $login = "";
850
851
        if (isset($_SERVER['PHP_AUTH_USER'])) {
852
            $login = "$_SERVER[PHP_AUTH_USER]:$_SERVER[PHP_AUTH_PW]@";
853
        }
854
855
        return Director::protocol() . $login .  static::host() . Director::baseURL();
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
856
    }
857
858
    /**
859
     * Skip any further processing and immediately respond with a redirect to the passed URL.
860
     *
861
     * @param string $destURL
862
     * @throws HTTPResponse_Exception
863
     */
864
    protected static function force_redirect($destURL)
865
    {
866
        // Redirect to installer
867
        $response = new HTTPResponse();
868
        $response->redirect($destURL, 301);
869
        HTTP::add_cache_headers($response);
870
        throw new HTTPResponse_Exception($response);
871
    }
872
873
    /**
874
     * Force the site to run on SSL.
875
     *
876
     * To use, call from _config.php. For example:
877
     * <code>
878
     * if (Director::isLive()) Director::forceSSL();
879
     * </code>
880
     *
881
     * If you don't want your entire site to be on SSL, you can pass an array of PCRE regular expression
882
     * patterns for matching relative URLs. For example:
883
     * <code>
884
     * if (Director::isLive()) Director::forceSSL(array('/^admin/', '/^Security/'));
885
     * </code>
886
     *
887
     * If you want certain parts of your site protected under a different domain, you can specify
888
     * the domain as an argument:
889
     * <code>
890
     * if (Director::isLive()) Director::forceSSL(array('/^admin/', '/^Security/'), 'secure.mysite.com');
891
     * </code>
892
     *
893
     * Note that the session data will be lost when moving from HTTP to HTTPS. It is your responsibility
894
     * to ensure that this won't cause usability problems.
895
     *
896
     * CAUTION: This does not respect the site environment mode. You should check this
897
     * as per the above examples using Director::isLive() or Director::isTest() for example.
898
     *
899
     * @param array $patterns Array of regex patterns to match URLs that should be HTTPS.
900
     * @param string $secureDomain Secure domain to redirect to. Defaults to the current domain.
901
     * @return bool true if already on SSL, false if doesn't match patterns (or cannot redirect)
902
     * @throws HTTPResponse_Exception Throws exception with redirect, if successful
903
     */
904
    public static function forceSSL($patterns = null, $secureDomain = null)
0 ignored issues
show
Coding Style introduced by
forceSSL 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...
905
    {
906
        // Already on SSL
907
        if (static::is_https()) {
908
            return true;
909
        }
910
911
        // Can't redirect without a url
912
        if (!isset($_SERVER['REQUEST_URI'])) {
913
            return false;
914
        }
915
916
        if ($patterns) {
917
            $matched = false;
918
            $relativeURL = self::makeRelative(Director::absoluteURL($_SERVER['REQUEST_URI']));
0 ignored issues
show
Security Bug introduced by
It seems like \SilverStripe\Control\Di..._SERVER['REQUEST_URI']) targeting SilverStripe\Control\Director::absoluteURL() can also be of type false; however, SilverStripe\Control\Director::makeRelative() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
919
920
            // protect portions of the site based on the pattern
921
            foreach ($patterns as $pattern) {
922
                if (preg_match($pattern, $relativeURL)) {
923
                    $matched = true;
924
                    break;
925
                }
926
            }
927
            if (!$matched) {
928
                return false;
929
            }
930
        }
931
932
        // if an domain is specified, redirect to that instead of the current domain
933
        if (!$secureDomain) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $secureDomain of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null 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...
934
            $secureDomain = static::host();
935
        }
936
        $url = 'https://' . $secureDomain . $_SERVER['REQUEST_URI'];
937
938
        // Force redirect
939
        self::force_redirect($url);
940
        return true;
941
    }
942
943
    /**
944
     * Force a redirect to a domain starting with "www."
945
     */
946
    public static function forceWWW()
0 ignored issues
show
Coding Style introduced by
forceWWW 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...
947
    {
948
        if (!Director::isDev() && !Director::isTest() && strpos(static::host(), 'www') !== 0) {
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
949
            $destURL = str_replace(
950
                Director::protocol(),
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
951
                Director::protocol() . 'www.',
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
952
                Director::absoluteURL($_SERVER['REQUEST_URI'])
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
953
            );
954
955
            self::force_redirect($destURL);
956
        }
957
    }
958
959
    /**
960
     * Checks if the current HTTP-Request is an "Ajax-Request" by checking for a custom header set by
961
     * jQuery or whether a manually set request-parameter 'ajax' is present.
962
     *
963
     * @return bool
964
     */
965
    public static function is_ajax()
0 ignored issues
show
Coding Style introduced by
is_ajax 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...
Coding Style introduced by
is_ajax 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...
966
    {
967
        if (Controller::has_curr()) {
968
            return Controller::curr()->getRequest()->isAjax();
969
        } else {
970
            return (
971
                isset($_REQUEST['ajax']) ||
972
                (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == "XMLHttpRequest")
973
            );
974
        }
975
    }
976
977
    /**
978
     * Returns true if this script is being run from the command line rather than the web server.
979
     *
980
     * @return bool
981
     */
982
    public static function is_cli()
983
    {
984
        return php_sapi_name() === "cli";
985
    }
986
987
    /**
988
     * Can also be checked with {@link Director::isDev()}, {@link Director::isTest()}, and
989
     * {@link Director::isLive()}.
990
     *
991
     * @return bool
992
     */
993
    public static function get_environment_type()
994
    {
995
        /** @var Kernel $kernel */
996
        $kernel = Injector::inst()->get(Kernel::class);
997
        return $kernel->getEnvironment();
998
    }
999
1000
    /**
1001
     * This function will return true if the site is in a live environment. For information about
1002
     * environment types, see {@link Director::set_environment_type()}.
1003
     *
1004
     * @return bool
1005
     */
1006
    public static function isLive()
1007
    {
1008
        return self::get_environment_type() === 'live';
1009
    }
1010
1011
    /**
1012
     * This function will return true if the site is in a development environment. For information about
1013
     * environment types, see {@link Director::set_environment_type()}.
1014
     *
1015
     * @return bool
1016
     */
1017
    public static function isDev()
1018
    {
1019
        return self::get_environment_type() === 'dev';
1020
    }
1021
1022
    /**
1023
     * This function will return true if the site is in a test environment. For information about
1024
     * environment types, see {@link Director::set_environment_type()}.
1025
     *
1026
     * @return bool
1027
     */
1028
    public static function isTest()
1029
    {
1030
        return self::get_environment_type() === 'test';
1031
    }
1032
1033
    /**
1034
     * Returns an array of strings of the method names of methods on the call that should be exposed
1035
     * as global variables in the templates.
1036
     *
1037
     * @return array
1038
     */
1039
    public static function get_template_global_variables()
1040
    {
1041
        return array(
1042
            'absoluteBaseURL',
1043
            'baseURL',
1044
            'is_ajax',
1045
            'isAjax' => 'is_ajax',
1046
            'BaseHref' => 'absoluteBaseURL',    //@deprecated 3.0
1047
        );
1048
    }
1049
}
1050