Completed
Push — master ( 644ae6...bba86b )
by Daniel
10:38
created

Controller::afterHandleRequest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Control;
4
5
use SilverStripe\Core\ClassInfo;
6
use SilverStripe\Core\Object;
7
use SilverStripe\Core\Injector\Injector;
8
use SilverStripe\Dev\Debug;
9
use SilverStripe\ORM\DataModel;
10
use SilverStripe\ORM\FieldType\DBHTMLText;
11
use SilverStripe\Security\BasicAuth;
12
use SilverStripe\Security\Member;
13
use SilverStripe\View\SSViewer;
14
use SilverStripe\View\TemplateGlobalProvider;
15
16
/**
17
 * Controllers are the cornerstone of all site functionality in SilverStripe. The {@link Director}
18
 * selects a controller to pass control to, and then calls {@link handleRequest()}. This method will execute
19
 * the appropriate action - either by calling the action method, or displaying the action's template.
20
 *
21
 * See {@link getTemplate()} for information on how the template is chosen.
22
 */
23
class Controller extends RequestHandler implements TemplateGlobalProvider
24
{
25
26
    /**
27
     * An array of arguments extracted from the URL.
28
     *
29
     * @var array
30
     */
31
    protected $urlParams;
32
33
    /**
34
     * Contains all GET and POST parameters passed to the current {@link HTTPRequest}.
35
     *
36
     * @var array
37
     */
38
    protected $requestParams;
39
40
    /**
41
     * The URL part matched on the current controller as determined by the "$Action" part of the
42
     * {@link $url_handlers} definition. Should correlate to a public method on this controller.
43
     *
44
     * Used in {@link render()} and {@link getViewer()} to determine action-specific templates.
45
     *
46
     * @var string
47
     */
48
    protected $action;
49
50
    /**
51
     * The {@link Session} object for this controller.
52
     *
53
     * @var Session
54
     */
55
    protected $session;
56
57
    /**
58
     * Stack of current controllers. Controller::$controller_stack[0] is the current controller.
59
     *
60
     * @var array
61
     */
62
    protected static $controller_stack = array();
63
64
    /**
65
     * @var bool
66
     */
67
    protected $basicAuthEnabled = true;
68
69
    /**
70
     * The response object that the controller returns.
71
     *
72
     * Set in {@link handleRequest()}.
73
     *
74
     * @var HTTPResponse
75
     */
76
    protected $response;
77
78
    /**
79
     * Default URL handlers.
80
     *
81
     * @var array
82
     */
83
    private static $url_handlers = array(
84
        '$Action//$ID/$OtherID' => 'handleAction',
85
    );
86
87
    /**
88
     * @var array
89
     */
90
    private static $allowed_actions = array(
91
        'handleAction',
92
        'handleIndex',
93
    );
94
95
    /**
96
     * Initialisation function that is run before any action on the controller is called.
97
     *
98
     * @uses BasicAuth::requireLogin()
99
     */
100
    protected function init()
101
    {
102
        if ($this->basicAuthEnabled) {
103
            BasicAuth::protect_site_if_necessary();
104
        }
105
106
        // This is used to test that subordinate controllers are actually calling parent::init() - a common bug
107
        $this->baseInitCalled = true;
108
    }
109
110
    /**
111
     * A stand in function to protect the init function from failing to be called as well as providing before and
112
     * after hooks for the init function itself
113
     *
114
     * This should be called on all controllers before handling requests
115
     */
116
    public function doInit()
117
    {
118
        //extension hook
119
        $this->extend('onBeforeInit');
120
121
        // Safety call
122
        $this->baseInitCalled = false;
123
        $this->init();
124
        if (!$this->baseInitCalled) {
125
            user_error(
126
                "init() method on class '$this->class' doesn't call Controller::init()."
127
                . "Make sure that you have parent::init() included.",
128
                E_USER_WARNING
129
            );
130
        }
131
132
        $this->extend('onAfterInit');
133
    }
134
135
    /**
136
     * {@inheritdoc}
137
     *
138
     * Also set the URLParams
139
     */
140
    public function setRequest($request)
141
    {
142
        $return = parent::setRequest($request);
143
        $this->setURLParams($this->getRequest()->allParams());
144
145
        return $return;
146
    }
147
148
    /**
149
     * A bootstrap for the handleRequest method
150
     *
151
     * @todo setDataModel and setRequest are redundantly called in parent::handleRequest() - sort this out
152
     *
153
     * @param HTTPRequest $request
154
     * @param DataModel $model
155
     */
156
    protected function beforeHandleRequest(HTTPRequest $request, DataModel $model)
157
    {
158
        //Push the current controller to protect against weird session issues
159
        $this->pushCurrent();
160
        //Set up the internal dependencies (request, response, datamodel)
161
        $this->setRequest($request);
162
        $this->setResponse(new HTTPResponse());
163
        $this->setDataModel($model);
164
        //kick off the init functionality
165
        $this->doInit();
166
    }
167
168
    /**
169
     * Cleanup for the handleRequest method
170
     */
171
    protected function afterHandleRequest()
172
    {
173
        //Pop the current controller from the stack
174
        $this->popCurrent();
175
    }
176
177
    /**
178
     * Executes this controller, and return an {@link HTTPResponse} object with the result.
179
     *
180
     * This method defers to {@link RequestHandler->handleRequest()} to determine which action
181
     *    should be executed
182
     *
183
     * Note: You should rarely need to overload handleRequest() -
184
     * this kind of change is only really appropriate for things like nested
185
     * controllers - {@link ModelAsController} and {@link RootURLController}
186
     * are two examples here.  If you want to make more
187
     * orthodox functionality, it's better to overload {@link init()} or {@link index()}.
188
     *
189
     * Important: If you are going to overload handleRequest,
190
     * make sure that you start the method with $this->beforeHandleRequest()
191
     * and end the method with $this->afterHandleRequest()
192
     *
193
     * @param HTTPRequest $request
194
     * @param DataModel $model
195
     *
196
     * @return HTTPResponse
197
     */
198
    public function handleRequest(HTTPRequest $request, DataModel $model)
199
    {
200
        if (!$request) {
201
            user_error("Controller::handleRequest() not passed a request!", E_USER_ERROR);
202
        }
203
204
        //set up the controller for the incoming request
205
        $this->beforeHandleRequest($request, $model);
206
207
        //if the before handler manipulated the response in a way that we shouldn't proceed, then skip our request
208
        // handling
209
        if (!$this->getResponse()->isFinished()) {
210
            //retrieve the response for the request
211
            $response = parent::handleRequest($request, $model);
212
213
            //prepare the response (we can receive an assortment of response types (strings/objects/HTTPResponses)
214
            $this->prepareResponse($response);
215
        }
216
217
        //after request work
218
        $this->afterHandleRequest();
219
220
        //return the response
221
        return $this->getResponse();
222
    }
223
224
    /**
225
     * Prepare the response (we can receive an assortment of response types (strings/objects/HTTPResponses) and
226
     * changes the controller response object appropriately
227
     *
228
     * @param HTTPResponse|Object $response
229
     */
230
    protected function prepareResponse($response)
0 ignored issues
show
Coding Style introduced by
prepareResponse 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...
231
    {
232
        if ($response instanceof HTTPResponse) {
233
            if (isset($_REQUEST['debug_request'])) {
234
                Debug::message(
235
                    "Request handler returned HTTPResponse object to $this->class controller;"
236
                    . "returning it without modification."
237
                );
238
            }
239
            $this->setResponse($response);
240
        } else {
241
            if ($response instanceof Object && $response->hasMethod('getViewer')) {
242
                if (isset($_REQUEST['debug_request'])) {
243
                    Debug::message(
244
                        "Request handler $response->class object to $this->class controller;"
245
                        . "rendering with template returned by $response->class::getViewer()"
246
                    );
247
                }
248
                $response = $response->getViewer($this->getAction())->process($response);
249
            }
250
251
            $this->getResponse()->setBody($response);
252
        }
253
254
        //deal with content if appropriate
255
        ContentNegotiator::process($this->getResponse());
256
257
        //add cache headers
258
        HTTP::add_cache_headers($this->getResponse());
259
    }
260
261
    /**
262
     * Controller's default action handler.  It will call the method named in "$Action", if that method
263
     * exists. If "$Action" isn't given, it will use "index" as a default.
264
     *
265
     * @param HTTPRequest $request
266
     * @param string $action
267
     *
268
     * @return DBHTMLText|HTTPResponse
269
     */
270
    protected function handleAction($request, $action)
271
    {
272
        foreach ($request->latestParams() as $k => $v) {
273
            if ($v || !isset($this->urlParams[$k])) {
274
                $this->urlParams[$k] = $v;
275
            }
276
        }
277
278
        $this->action = $action;
279
        $this->requestParams = $request->requestVars();
280
281
        if ($this->hasMethod($action)) {
282
            $result = parent::handleAction($request, $action);
283
284
            // If the action returns an array, customise with it before rendering the template.
285
            if (is_array($result)) {
286
                return $this->getViewer($action)->process($this->customise($result));
287
            } else {
288
                return $result;
289
            }
290
        }
291
292
        // Fall back to index action with before/after handlers
293
        $beforeResult = $this->extend('beforeCallActionHandler', $request, $action);
294
        if ($beforeResult) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $beforeResult 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...
295
            return reset($beforeResult);
296
        }
297
298
        $result = $this->getViewer($action)->process($this);
299
300
        $afterResult = $this->extend('afterCallActionHandler', $request, $action, $result);
301
        if ($afterResult) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $afterResult 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...
302
            return reset($afterResult);
303
        }
304
305
        return $result;
306
    }
307
308
    /**
309
     * @param array $urlParams
310
     * @return $this
311
     */
312
    public function setURLParams($urlParams)
313
    {
314
        $this->urlParams = $urlParams;
315
        return $this;
316
    }
317
318
    /**
319
     * Returns the parameters extracted from the URL by the {@link Director}.
320
     *
321
     * @return array
322
     */
323
    public function getURLParams()
324
    {
325
        return $this->urlParams;
326
    }
327
328
    /**
329
     * Returns the HTTPResponse object that this controller is building up. Can be used to set the
330
     * status code and headers.
331
     *
332
     * @return HTTPResponse
333
     */
334
    public function getResponse()
335
    {
336
        if (!$this->response) {
337
            $this->setResponse(new HTTPResponse());
338
        }
339
        return $this->response;
340
    }
341
342
    /**
343
     * Sets the HTTPResponse object that this controller is building up.
344
     *
345
     * @param HTTPResponse $response
346
     *
347
     * @return $this
348
     */
349
    public function setResponse(HTTPResponse $response)
350
    {
351
        $this->response = $response;
352
        return $this;
353
    }
354
355
    /**
356
     * @var bool
357
     */
358
    protected $baseInitCalled = false;
359
360
    /**
361
     * This is the default action handler used if a method doesn't exist. It will process the
362
     * controller object with the template returned by {@link getViewer()}.
363
     *
364
     * @param string $action
365
     * @return DBHTMLText
366
     */
367
    public function defaultAction($action)
368
    {
369
        return $this->getViewer($action)->process($this);
370
    }
371
372
    /**
373
     * Returns the action that is being executed on this controller.
374
     *
375
     * @return string
376
     */
377
    public function getAction()
378
    {
379
        return $this->action;
380
    }
381
382
    /**
383
     * Return the viewer identified being the default handler for this Controller/Action combination.
384
     *
385
     * @param string $action
386
     *
387
     * @return SSViewer
388
     */
389
    public function getViewer($action)
390
    {
391
        // Hard-coded templates
392
        if (isset($this->templates[$action]) && $this->templates[$action]) {
393
            $templates = $this->templates[$action];
394
        } elseif (isset($this->templates['index']) && $this->templates['index']) {
395
            $templates = $this->templates['index'];
396
        } elseif ($this->template) {
397
            $templates = $this->template;
0 ignored issues
show
Documentation introduced by
The property template does not exist on object<SilverStripe\Control\Controller>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
398
        } else {
399
            // Add action-specific templates for inheritance chain
400
            $templates = array();
401
            if ($action && $action != 'index') {
402
                $parentClass = $this->class;
403
                while ($parentClass != __CLASS__) {
404
                    $templates[] = strtok($parentClass, '_') . '_' . $action;
405
                    $parentClass = get_parent_class($parentClass);
406
                }
407
            }
408
            // Add controller templates for inheritance chain
409
            $parentClass = $this->class;
410
            while ($parentClass != __CLASS__) {
411
                $templates[] = strtok($parentClass, '_');
412
                $parentClass = get_parent_class($parentClass);
413
            }
414
415
            $templates[] = __CLASS__;
416
417
            // remove duplicates
418
            $templates = array_unique($templates);
419
        }
420
421
        return new SSViewer($templates);
422
    }
423
424
    /**
425
     * @param string $action
426
     *
427
     * @return bool
428
     */
429
    public function hasAction($action)
430
    {
431
        return parent::hasAction($action) || $this->hasActionTemplate($action);
432
    }
433
434
    /**
435
     * Removes all the "action" part of the current URL and returns the result. If no action parameter
436
     * is present, returns the full URL.
437
     *
438
     * @param string $fullURL
439
     * @param null|string $action
440
     *
441
     * @return string
442
     */
443
    public function removeAction($fullURL, $action = null)
444
    {
445
        if (!$action) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $action of type null|string 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...
446
            $action = $this->getAction();    //default to current action
447
        }
448
        $returnURL = $fullURL;
449
450
        if (($pos = strpos($fullURL, $action)) !== false) {
451
            $returnURL = substr($fullURL, 0, $pos);
452
        }
453
454
        return $returnURL;
455
    }
456
457
    /**
458
     * Return the class that defines the given action, so that we know where to check allowed_actions.
459
     * Overrides RequestHandler to also look at defined templates.
460
     *
461
     * @param string $action
462
     *
463
     * @return string
464
     */
465
    protected function definingClassForAction($action)
466
    {
467
        $definingClass = parent::definingClassForAction($action);
468
        if ($definingClass) {
469
            return $definingClass;
470
        }
471
472
        $class = get_class($this);
473
        while ($class != 'SilverStripe\\Control\\RequestHandler') {
474
            $templateName = strtok($class, '_') . '_' . $action;
475
            if (SSViewer::hasTemplate($templateName)) {
476
                return $class;
477
            }
478
479
            $class = get_parent_class($class);
480
        }
481
482
        return null;
483
    }
484
485
    /**
486
     * Returns TRUE if this controller has a template that is specifically designed to handle a
487
     * specific action.
488
     *
489
     * @param string $action
490
     *
491
     * @return bool
492
     */
493
    public function hasActionTemplate($action)
494
    {
495
        if (isset($this->templates[$action])) {
496
            return true;
497
        }
498
499
        $parentClass = $this->class;
500
        $templates   = array();
501
502
        while ($parentClass != __CLASS__) {
503
            $templates[] = strtok($parentClass, '_') . '_' . $action;
504
            $parentClass = get_parent_class($parentClass);
505
        }
506
507
        return SSViewer::hasTemplate($templates);
508
    }
509
510
    /**
511
     * Render the current controller with the templates determined by {@link getViewer()}.
512
     *
513
     * @param array $params
514
     *
515
     * @return string
516
     */
517
    public function render($params = null)
518
    {
519
        $template = $this->getViewer($this->getAction());
520
521
        // if the object is already customised (e.g. through Controller->run()), use it
522
        $obj = ($this->customisedObj) ? $this->customisedObj : $this;
0 ignored issues
show
Bug introduced by
The property customisedObj does not seem to exist. Did you mean customisedObject?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
523
524
        if ($params) {
525
            $obj = $this->customise($params);
526
        }
527
528
        return $template->process($obj);
529
    }
530
531
    /**
532
     * Call this to disable site-wide basic authentication for a specific controller. This must be
533
     * called before Controller::init(). That is, you must call it in your controller's init method
534
     * before it calls parent::init().
535
     */
536
    public function disableBasicAuth()
537
    {
538
        $this->basicAuthEnabled = false;
539
    }
540
541
    /**
542
     * Returns the current controller.
543
     *
544
     * @return Controller
545
     */
546
    public static function curr()
547
    {
548
        if (Controller::$controller_stack) {
0 ignored issues
show
Bug Best Practice introduced by
The expression \SilverStripe\Control\Co...ller::$controller_stack 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...
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...
549
            return Controller::$controller_stack[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...
550
        }
551
        user_error("No current controller available", E_USER_WARNING);
552
        return null;
553
    }
554
555
    /**
556
     * Tests whether we have a currently active controller or not. True if there is at least 1
557
     * controller in the stack.
558
     *
559
     * @return bool
560
     */
561
    public static function has_curr()
562
    {
563
        return Controller::$controller_stack ? true : false;
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...
564
    }
565
566
    /**
567
     * Returns true if the member is allowed to do the given action. Defaults to the currently logged
568
     * in user.
569
     *
570
     * @param string $perm
571
     * @param null|member $member
572
     *
573
     * @return bool
574
     */
575
    public function can($perm, $member = null)
576
    {
577
        if (!$member) {
578
            $member = Member::currentUser();
579
        }
580
        if (is_array($perm)) {
581
            $perm = array_map(array($this, 'can'), $perm, array_fill(0, count($perm), $member));
582
            return min($perm);
583
        }
584
        if ($this->hasMethod($methodName = 'can' . $perm)) {
585
            return $this->$methodName($member);
586
        } else {
587
            return true;
588
        }
589
    }
590
591
    /**
592
     * Pushes this controller onto the stack of current controllers. This means that any redirection,
593
     * session setting, or other things that rely on Controller::curr() will now write to this
594
     * controller object.
595
     */
596
    public function pushCurrent()
597
    {
598
        array_unshift(self::$controller_stack, $this);
599
        // Create a new session object
600
        if (!$this->session) {
601
            if (isset(self::$controller_stack[1])) {
602
                $this->session = self::$controller_stack[1]->getSession();
603
            } else {
604
                $this->session = Injector::inst()->create('SilverStripe\\Control\\Session', array());
605
            }
606
        }
607
    }
608
609
    /**
610
     * Pop this controller off the top of the stack.
611
     */
612
    public function popCurrent()
613
    {
614
        if ($this === self::$controller_stack[0]) {
615
            array_shift(self::$controller_stack);
616
        } else {
617
            user_error(
618
                "popCurrent called on $this->class controller, but it wasn't at the top of the stack",
619
                E_USER_WARNING
620
            );
621
        }
622
    }
623
624
    /**
625
     * Redirect to the given URL.
626
     *
627
     * @param string $url
628
     * @param int $code
629
     * @return HTTPResponse
630
     */
631
    public function redirect($url, $code = 302)
632
    {
633
        if ($this->getResponse()->getHeader('Location') && $this->getResponse()->getHeader('Location') != $url) {
634
            user_error("Already directed to " . $this->getResponse()->getHeader('Location')
635
                . "; now trying to direct to $url", E_USER_WARNING);
636
            return null;
637
        }
638
        $response = parent::redirect($url, $code);
639
        $this->setResponse($response);
640
        return $response;
641
    }
642
643
    /**
644
     * Tests whether a redirection has been requested. If redirect() has been called, it will return
645
     * the URL redirected to. Otherwise, it will return null.
646
     *
647
     * @return null|string
648
     */
649
    public function redirectedTo()
650
    {
651
        return $this->getResponse() && $this->getResponse()->getHeader('Location');
652
    }
653
654
    /**
655
     * Get the Session object representing this Controller's session.
656
     *
657
     * @return Session
658
     */
659
    public function getSession()
660
    {
661
        return $this->session;
662
    }
663
664
    /**
665
     * Set the Session object.
666
     *
667
     * @param Session $session
668
     */
669
    public function setSession(Session $session)
670
    {
671
        $this->session = $session;
672
    }
673
674
    /**
675
     * Joins two or more link segments together, putting a slash between them if necessary. Use this
676
     * for building the results of {@link Link()} methods. If either of the links have query strings,
677
     * then they will be combined and put at the end of the resulting url.
678
     *
679
     * Caution: All parameters are expected to be URI-encoded already.
680
     *
681
     * @param string|array $arg,.. One or more link segments, or list of link segments as an array
0 ignored issues
show
Bug introduced by
There is no parameter named $arg,... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
682
     * @return string
683
     */
684
    public static function join_links($arg = null)
685
    {
686
        if (func_num_args() === 1 && is_array($arg)) {
687
            $args = $arg;
688
        } else {
689
            $args = func_get_args();
690
        }
691
        $result = "";
692
        $queryargs = array();
693
        $fragmentIdentifier = null;
694
695
        foreach ($args as $arg) {
696
            // Find fragment identifier - keep the last one
697
            if (strpos($arg, '#') !== false) {
698
                list($arg, $fragmentIdentifier) = explode('#', $arg, 2);
699
            }
700
            // Find querystrings
701
            if (strpos($arg, '?') !== false) {
702
                list($arg, $suffix) = explode('?', $arg, 2);
703
                parse_str($suffix, $localargs);
704
                $queryargs = array_merge($queryargs, $localargs);
705
            }
706
            if ((is_string($arg) && $arg) || is_numeric($arg)) {
707
                $arg = (string) $arg;
708
                if ($result && substr($result, -1) != '/' && $arg[0] != '/') {
709
                    $result .= "/$arg";
710
                } else {
711
                    $result .= (substr($result, -1) == '/' && $arg[0] == '/') ? ltrim($arg, '/') : $arg;
712
                }
713
            }
714
        }
715
716
        if ($queryargs) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $queryargs 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...
717
            $result .= '?' . http_build_query($queryargs);
718
        }
719
720
        if ($fragmentIdentifier) {
721
            $result .= "#$fragmentIdentifier";
722
        }
723
724
        return $result;
725
    }
726
727
    /**
728
     * @return array
729
     */
730
    public static function get_template_global_variables()
731
    {
732
        return array(
733
            'CurrentPage' => 'curr',
734
        );
735
    }
736
}
737