ExecutionContainer::__wakeup()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 4
nop 0
dl 0
loc 15
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
// +---------------------------------------------------------------------------+
4
// | This file is part of the Agavi package.                                   |
5
// | Copyright (c) 2005-2011 the Agavi Project.                                |
6
// |                                                                           |
7
// | For the full copyright and license information, please view the LICENSE   |
8
// | file that was distributed with this source code. You can also view the    |
9
// | LICENSE file online at http://www.agavi.org/LICENSE.txt                   |
10
// |   vi: set noexpandtab:                                                    |
11
// |   Local Variables:                                                        |
12
// |   indent-tabs-mode: t                                                     |
13
// |   End:                                                                    |
14
// +---------------------------------------------------------------------------+
15
16
namespace Agavi\Dispatcher;
17
18
use Agavi\Controller\Controller;
19
use Agavi\Config\ConfigCache;
20
use Agavi\Exception\AgaviException;
21
use Agavi\Exception\ViewException;
22
use Agavi\Filter\FilterChain;
23
use Agavi\Request\RequestDataHolder;
24
use Agavi\Response\Response;
25
use Agavi\Util\AttributeHolder;
26
use Agavi\Core\Context;
27
use Agavi\Config\Config;
28
use Agavi\Exception\DisabledModuleException;
29
use Agavi\Exception\FileNotFoundException;
30
use Agavi\Exception\ConfigurationException;
31
use Agavi\Util\Toolkit;
32
use Agavi\Validator\ValidationManager;
33
use Agavi\View\View;
34
35
/**
36
 * A container used for each controller execution that holds necessary information,
37
 * such as the output type, the response etc.
38
 *
39
 * @package    agavi
40
 * @subpackage Dispatcher
41
 *
42
 * @author     David Zülke <[email protected]>
43
 * @copyright  Authors
44
 * @copyright  The Agavi Project
45
 *
46
 * @since      0.11.0
47
 *
48
 * @version    $Id$
49
 */
50
51
class ExecutionContainer extends AttributeHolder
52
{
53
    /**
54
     * @var        Context The context instance.
55
     */
56
    protected $context = null;
57
58
    /**
59
     * @var        string The context name.
60
     */
61
    protected $contextName = null;
62
63
    /**
64
     * @var        string The output type name.
65
     */
66
    protected $outputTypeName = null;
67
68
    /**
69
     * @var        FilterChain The container's filter chain.
70
     */
71
    protected $filterChain = null;
72
73
    /**
74
     * @var        ValidationManager The validation manager instance.
75
     */
76
    protected $validationManager = null;
77
78
    /**
79
     * @var        string The request method for this container.
80
     */
81
    protected $requestMethod = null;
82
83
    /**
84
     * @var        RequestDataHolder A request data holder with request info.
85
     */
86
    protected $requestData = null; // TODO: check if this can actually be protected
87
                                   // or whether it should be private (would break controller tests though)
88
89
    /**
90
     * @var        RequestDataHolder A pointer to the global request data.
91
     */
92
    private $globalRequestData = null;
93
94
    /**
95
     * @var        RequestDataHolder A request data holder with arguments.
96
     */
97
    protected $arguments = null;
98
99
    /**
100
     * @var        Response A response instance holding the Controller's output.
101
     */
102
    protected $response = null;
103
104
    /**
105
     * @var        OutputType The output type for this container.
106
     */
107
    protected $outputType = null;
108
109
    /**
110
     * @var        float The microtime at which this container was initialized.
111
     */
112
    protected $microtime = null;
113
114
    /**
115
     * @var        Controller The Controller instance that belongs to this container.
116
     */
117
    protected $controllerInstance = null;
118
119
    /**
120
     * @var        ViewException The View instance that belongs to this container.
121
     */
122
    protected $viewInstance = null;
123
124
    /**
125
     * @var        string The name of the Controller's Module.
126
     */
127
    protected $moduleName = null;
128
129
    /**
130
     * @var        string The name of the Controller.
131
     */
132
    protected $controllerName = null;
133
134
    /**
135
     * @var        string Name of the module of the View returned by the Controller.
136
     */
137
    protected $viewModuleName = null;
138
139
    /**
140
     * @var        string The name of the View returned by the Controller.
141
     */
142
    protected $viewName = null;
143
144
    /**
145
     * @var        ExecutionContainer The next container to execute.
146
     */
147
    protected $next = null;
148
149
    /**
150
     * Controller names may contain any valid PHP token, as well as dots and slashes
151
     * (for sub-controllers).
152
     */
153
    const SANE_CONTROLLER_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff\/.]*/';
154
    
155
    /**
156
     * View names may contain any valid PHP token, as well as dots and slashes
157
     * (for sub-controllers).
158
     */
159
    const SANE_VIEW_NAME   = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff\/.]*/';
160
    
161
    /**
162
     * Only valid PHP tokens are allowed in module names.
163
     */
164
    const SANE_MODULE_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/';
165
    
166
    /**
167
     * Pre-serialization callback.
168
     *
169
     * Will set the name of the context instead of the instance, and the name of
170
     * the output type instead of the instance. Both will be restored by __wakeup
171
     *
172
     * @author     David Zülke <[email protected]>
173
     * @since      0.11.0
174
     */
175
    public function __sleep()
176
    {
177
        $this->contextName = $this->context->getName();
178
        if (!empty($this->outputType)) {
179
            $this->outputTypeName = $this->outputType->getName();
180
        }
181
        $arr = get_object_vars($this);
182
        unset($arr['context'], $arr['outputType'], $arr['requestData'], $arr['globalRequestData']);
183
        return array_keys($arr);
184
    }
185
186
    /**
187
     * Post-unserialization callback.
188
     *
189
     * Will restore the context and output type instances based on their names set
190
     * by __sleep.
191
     *
192
     * @author     David Zülke <[email protected]>
193
     * @since      0.11.0
194
     */
195
    public function __wakeup()
196
    {
197
        $this->context = Context::getInstance($this->contextName);
198
        
199
        if (!empty($this->outputTypeName)) {
200
            $this->outputType = $this->context->getDispatcher()->getOutputType($this->outputTypeName);
201
        }
202
        
203
        try {
204
            $this->globalRequestData = $this->context->getRequest()->getRequestData();
205
        } catch (AgaviException $e) {
206
            $this->globalRequestData = new RequestDataHolder();
207
        }
208
        unset($this->contextName, $this->outputTypeName);
209
    }
210
211
    /**
212
     * Initialize the container. This will create a response instance.
213
     *
214
     * @param      Context $context    The current Context instance.
215
     * @param      array   $parameters An array of initialization parameters.
216
     *
217
     * @author     David Zülke <[email protected]>
218
     * @since      0.11.0
219
     */
220
    public function initialize(Context $context, array $parameters = array())
221
    {
222
        $this->microtime = microtime(true);
223
224
        $this->context = $context;
225
226
        $this->parameters = $parameters;
227
228
        $this->response = $this->context->createInstanceFor('response');
229
    }
230
231
    /**
232
     * Creates a new container instance with the same output type and request
233
     * method as this one.
234
     *
235
     * @param      string            $moduleName    The name of the module.
236
     * @param      string            $controllerName    The name of the controller.
237
     * @param      RequestDataHolder $arguments     A RequestDataHolder with additional
238
     *                                              request arguments.
239
     * @param      string            $outputType    Optional name of an initial output type
240
     *                                              to set.
241
     * @param      string            $requestMethod Optional name of the request method to
242
     *                                              be used in this container.
243
     *
244
     * @return     ExecutionContainer A new execution container instance,
245
     *                                     fully initialized.
246
     *
247
     * @author     David Zülke <[email protected]>
248
     * @since      0.11.0
249
     */
250
    public function createExecutionContainer($moduleName = null, $controllerName = null, RequestDataHolder $arguments = null, $outputType = null, $requestMethod = null)
251
    {
252
        if ($outputType === null) {
253
            $outputType = $this->getOutputType()->getName();
254
        }
255
        if ($requestMethod === null) {
256
            $requestMethod = $this->getRequestMethod();
257
        }
258
        
259
        $container = $this->context->getDispatcher()->createExecutionContainer($moduleName, $controllerName, $arguments, $outputType, $requestMethod);
260
        
261
        // copy over parameters (could be is_slot, is_forward etc)
262
        $container->setParameters($this->getParameters());
263
        
264
        return $container;
265
    }
266
267
    /**
268
     * Start execution.
269
     *
270
     * This will create an instance of the controller and merge in request parameters.
271
     *
272
     * This method returns a response. It is not necessarily the same response as
273
     * the one of this container, but instead the one that contains the actual
274
     * content that should be used for output etc, since the container's own
275
     * response might be empty or invalid due to a "next" container that has been
276
     * set and executed.
277
     *
278
     * @return     Response The "real" response.
279
     *
280
     * @author     David Zülke <[email protected]>
281
     * @since      0.11.0
282
     */
283
    public function execute()
284
    {
285
        $dispatcher = $this->context->getDispatcher();
286
287
        $dispatcher->countExecution();
288
289
        $moduleName = $this->getModuleName();
290
291
        try {
292
            $controllerInstance = $this->getControllerInstance();
293
        } catch (DisabledModuleException $e) {
294
            $this->setNext($this->createSystemControllerForwardContainer('module_disabled'));
295
            return $this->proceed();
296
        } catch (FileNotFoundException $e) {
297
            $this->setNext($this->createSystemControllerForwardContainer('error_404'));
298
            return $this->proceed();
299
        } // do not catch AgaviClassNotFoundException, we want that to bubble up since it means the class in the controller file is named incorrectly
300
        
301
        // copy and merge request data as required
302
        $this->initRequestData();
303
304
        /** @var FilterChain $filterChain */
305
        $filterChain = $this->getFilterChain();
306
        
307
        if (!$controllerInstance->isSimple()) {
308
            // simple controllers have no filters
309
310
            if (Config::get('core.available', false)) {
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
311
                // the application is available so we'll register
312
                // globally defined and module-specific controller filters, otherwise skip them
313
314
                // does this controller require security?
315
                if (Config::get('core.use_security', false)) {
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
316
                    // register security filter
317
                    $filterChain->register($dispatcher->getFilter('security'), 'agavi_security_filter');
318
                }
319
320
                // load filters
321
                $dispatcher->loadFilters($filterChain, 'controller');
322
                $dispatcher->loadFilters($filterChain, 'controller', $moduleName);
323
            }
324
        }
325
326
        // register the execution filter
327
        $filterChain->register($dispatcher->getFilter('execution'), 'agavi_execution_filter');
328
329
        // process the filter chain
330
        $filterChain->execute($this);
331
        
332
        return $this->proceed();
333
    }
334
    
335
    /**
336
     * Copies and merges the global request data.
337
     *
338
     * @author       Felix Gilcher <[email protected]>
339
     * @since        1.1.0
340
     */
341
    protected function initRequestData()
342
    {
343
        if ($this->getControllerInstance()->isSimple()) {
344
            if ($this->arguments !== null) {
345
                // clone it so mutating it has no effect on the "outside world"
346
                $this->requestData = clone $this->arguments;
347
            } else {
348
                $rdhc = $this->getContext()->getRequest()->getParameter('request_data_holder_class');
349
                $this->requestData = new $rdhc();
350
            }
351
        } else {
352
            // mmmh I smell awesomeness... clone the RD JIT, yay, that's the spirit
353
            $this->requestData = clone $this->globalRequestData;
354
355
            if ($this->arguments !== null) {
356
                $this->requestData->merge($this->arguments);
357
            }
358
        }
359
    }
360
    
361
    /**
362
     * Create a system forward container
363
     *
364
     * Calling this method will set the attributes:
365
     *  - requested_module
366
     *  - requested_controller
367
     *  - (optional) exception
368
     * in the appropriate namespace on the created container as well as the global
369
     * request (for legacy reasons)
370
     *
371
     *
372
     * @param      string          $type The type of forward to create (error_404,
373
     *                                   module_disabled, secure, login, unavailable).
374
     * @param      AgaviException  $e    Optional exception thrown by the Dispatcher
375
     *                                   while resolving the module/controller.
376
     *
377
     * @return     ExecutionContainer The forward container.
378
     *
379
     * @author     Felix Gilcher <[email protected]>
380
     * @since      1.0.0
381
     */
382
    public function createSystemControllerForwardContainer($type, AgaviException $e = null)
383
    {
384
        if (!in_array($type, array('error_404', 'module_disabled', 'secure', 'login', 'unavailable'))) {
385
            throw new AgaviException(sprintf('Unknown system forward type "%1$s"', $type));
386
        }
387
        
388
        // track the requested module so we have access to the data in the error 404 page
389
        $forwardInfoData = array(
390
            'requested_module' => $this->getModuleName(),
391
            'requested_controller' => $this->getControllerName(),
392
            'exception'        => $e,
393
        );
394
        $forwardInfoNamespace = 'org.agavi.dispatcher.forwards.' . $type;
395
        
396
        $moduleName = Config::get('controllers.' . $type . '_module');
397
        $controllerName = Config::get('controllers.' . $type . '_controller');
398
        
399
        if (false === $this->context->getDispatcher()->checkControllerFile($moduleName, $controllerName)) {
400
            // cannot find unavailable module/controller
401
            $error = 'Invalid configuration settings: controllers.%3$s_module "%1$s", controllers.%3$s_controller "%2$s"';
402
            $error = sprintf($error, $moduleName, $controllerName, $type);
403
            
404
            throw new ConfigurationException($error);
405
        }
406
        
407
        $forwardContainer = $this->createExecutionContainer($moduleName, $controllerName);
408
        
409
        $forwardContainer->setAttributes($forwardInfoData, $forwardInfoNamespace);
410
        // legacy
411
        $this->context->getRequest()->setAttributes($forwardInfoData, $forwardInfoNamespace);
412
        
413
        return $forwardContainer;
414
    }
415
    
416
    /**
417
     * Proceed to the "next" container by running it and returning its response,
418
     * or return our response if there is no "next" container.
419
     *
420
     * @return     Response The "real" response.
421
     *
422
     * @author     David Zülke <[email protected]>
423
     * @since      1.0.0
424
     */
425
    protected function proceed()
426
    {
427
        if ($this->next !== null) {
428
            return $this->next->execute();
429
        } else {
430
            return $this->getResponse();
431
        }
432
    }
433
434
    /**
435
     * Get the Context.
436
     *
437
     * @return     Context The Context.
438
     *
439
     * @author     David Zülke <[email protected]>
440
     * @since      0.11.0
441
     */
442
    final public function getContext()
443
    {
444
        return $this->context;
445
    }
446
447
    /**
448
     * Retrieve the ValidationManager
449
     *
450
     * @return     ValidationManager The container's ValidationManager
451
     *                               implementation instance.
452
     *
453
     * @author     David Zülke <[email protected]>
454
     * @since      0.11.0
455
     */
456
    public function getValidationManager()
457
    {
458
        if ($this->validationManager === null) {
459
            $this->validationManager = $this->context->createInstanceFor('validation_manager');
460
        }
461
        return $this->validationManager;
462
    }
463
    
464
    /**
465
     * Get the container's filter chain.
466
     *
467
     * @return     FilterChain The container's filter chain.
468
     *
469
     * @author     David Zülke <[email protected]>
470
     * @since      1.1.0
471
     */
472 View Code Duplication
    public function getFilterChain()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
473
    {
474
        if ($this->filterChain === null) {
475
            $this->filterChain = $this->context->createInstanceFor('filter_chain');
476
            $this->filterChain->setType(FilterChain::TYPE_CONTROLLER);
477
        }
478
        
479
        return $this->filterChain;
480
    }
481
    
482
    /**
483
     * Execute the Controller.
484
     *
485
     * @return     mixed The processed View information returned by the Controller.
486
     *
487
     * @author     David Zülke <[email protected]>
488
     * @author     Felix Gilcher <[email protected]>
489
     * @since      1.0.0
490
     */
491
    public function runController()
492
    {
493
        $viewName = null;
494
495
        $request = $this->context->getRequest();
496
        $validationManager = $this->getValidationManager();
497
498
        // get the current controller instance
499
        $controllerInstance = $this->getControllerInstance();
500
501
        // get the current controller information
502
        $moduleName = $this->getModuleName();
503
        $controllerName = $this->getControllerName();
504
505
        // get the (already formatted) request method
506
        $method = $this->getRequestMethod();
507
508
        $requestData = $this->getRequestData();
509
510
        $useGenericMethods = false;
511
        $executeMethod = 'execute' . $method;
512
        if (!is_callable(array($controllerInstance, $executeMethod))) {
513
            $executeMethod = 'execute';
514
            $useGenericMethods = true;
515
        }
516
517
        if ($controllerInstance->isSimple() || ($useGenericMethods && !is_callable(array($controllerInstance, $executeMethod)))) {
518
            // this controller will skip validation/execution for this method
519
            // get the default view
520
            $key = $request->toggleLock();
521
            try {
522
                $viewName = $controllerInstance->getDefaultViewName();
523
            } catch (\Exception $e) {
524
                // we caught an exception... unlock the request and rethrow!
525
                $request->toggleLock($key);
0 ignored issues
show
Bug introduced by
It seems like $key defined by $request->toggleLock() on line 520 can also be of type boolean; however, Agavi\Request\Request::toggleLock() does only seem to accept string|null, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
526
                throw $e;
527
            }
528
            $request->toggleLock($key);
0 ignored issues
show
Bug introduced by
It seems like $key defined by $request->toggleLock() on line 520 can also be of type boolean; however, Agavi\Request\Request::toggleLock() does only seem to accept string|null, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
529
            
530
            // run the validation manager - it's going to take care of cleaning up the request data, and retain "conditional" mode behavior etc.
531
            // but only if the controller is not simple; otherwise, the (safe) arguments in the request data holder will all be removed
532
            if (!$controllerInstance->isSimple()) {
533
                $validationManager->execute($requestData);
534
            }
535
        } else {
536
            if ($this->performValidation()) {
537
                // execute the controller
538
                // prevent access to Request::getParameters()
539
                $key = $request->toggleLock();
540
                try {
541
                    $viewName = $controllerInstance->$executeMethod($requestData);
542
                } catch (\Exception $e) {
543
                    // we caught an exception... unlock the request and rethrow!
544
                    $request->toggleLock($key);
0 ignored issues
show
Bug introduced by
It seems like $key defined by $request->toggleLock() on line 539 can also be of type boolean; however, Agavi\Request\Request::toggleLock() does only seem to accept string|null, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
545
                    throw $e;
546
                }
547
                $request->toggleLock($key);
0 ignored issues
show
Bug introduced by
It seems like $key defined by $request->toggleLock() on line 539 can also be of type boolean; however, Agavi\Request\Request::toggleLock() does only seem to accept string|null, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
548
            } else {
549
                // validation failed
550
                $handleErrorMethod = 'handle' . $method . 'Error';
551
                if (!is_callable(array($controllerInstance, $handleErrorMethod))) {
552
                    $handleErrorMethod = 'handleError';
553
                }
554
                $key = $request->toggleLock();
555
                try {
556
                    $viewName = $controllerInstance->$handleErrorMethod($requestData);
557
                } catch (\Exception $e) {
558
                    // we caught an exception... unlock the request and rethrow!
559
                    $request->toggleLock($key);
0 ignored issues
show
Bug introduced by
It seems like $key defined by $request->toggleLock() on line 554 can also be of type boolean; however, Agavi\Request\Request::toggleLock() does only seem to accept string|null, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
560
                    throw $e;
561
                }
562
                $request->toggleLock($key);
0 ignored issues
show
Bug introduced by
It seems like $key defined by $request->toggleLock() on line 554 can also be of type boolean; however, Agavi\Request\Request::toggleLock() does only seem to accept string|null, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
563
            }
564
        }
565
566
        if (is_array($viewName)) {
567
            // we're going to use an entirely different controller for this view
568
            $viewModule = $viewName[0];
569
            $viewName   = $viewName[1];
570
        } elseif ($viewName !== View::NONE) {
571
            // use a view related to this controller
572
            $viewName = Toolkit::evaluateModuleDirective(
573
                $moduleName,
574
                'agavi.view.name',
575
                array(
576
                    'controllerName' => $controllerName,
577
                    'viewName' => $viewName,
578
                )
579
            );
580
            $viewModule = $moduleName;
581
        } else {
582
            $viewName = View::NONE;
583
            $viewModule = View::NONE;
584
        }
585
586
        return array($viewModule, $viewName === View::NONE ? View::NONE : Toolkit::canonicalName($viewName));
587
    }
588
    
589
    /**
590
     * Performs validation for this execution container.
591
     *
592
     * @return     bool true if the data validated successfully, false otherwise.
593
     *
594
     * @author     David Zülke <[email protected]>
595
     * @author     Felix Gilcher <[email protected]>
596
     * @since      1.0.0
597
     */
598
    public function performValidation()
599
    {
600
        $validationManager = $this->getValidationManager();
601
602
        // get the current controller instance
603
        $controllerInstance = $this->getControllerInstance();
604
        // get the (already formatted) request method
605
        $method = $this->getRequestMethod();
606
607
        $requestData = $this->getRequestData();
608
        
609
        // set default validated status
610
        $validated = true;
0 ignored issues
show
Unused Code introduced by
$validated is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
611
612
        $this->registerValidators();
613
614
        // process validators
615
        $validated = $validationManager->execute($requestData);
616
617
        $validateMethod = 'validate' . $method;
618
        if (!is_callable(array($controllerInstance, $validateMethod))) {
619
            $validateMethod = 'validate';
620
        }
621
622
        // process manual validation
623
        return $controllerInstance->$validateMethod($requestData) && $validated;
624
    }
625
626
    /**
627
     * Register validators for this execution container.
628
     *
629
     * @author     David Zülke <[email protected]>
630
     * @author     Felix Gilcher <[email protected]>
631
     * @since      1.0.0
632
     */
633
    public function registerValidators()
634
    {
635
636
        // get the current controller instance
637
        $controllerInstance = $this->getControllerInstance();
638
        
639
        // get the current controller information
640
        $moduleName = $this->getModuleName();
641
        $controllerName = $this->getControllerName();
642
        
643
        // get the (already formatted) request method
644
        $method = $this->getRequestMethod();
645
646
        // get the current controller validation configuration
647
        $validationConfig = Toolkit::evaluateModuleDirective(
648
            $moduleName,
649
            'agavi.validate.path',
650
            array(
651
                'moduleName' => $moduleName,
652
                'controllerName' => $controllerName,
653
            )
654
        );
655
        $validationManager = $this->getValidationManager();
656
657
        if (is_readable($validationConfig)) {
658
            // load validation configuration
659
            // do NOT use require_once
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
660
            require(ConfigCache::checkConfig($validationConfig, $this->context->getName()));
661
        }
662
663
        // manually load validators
664
        $registerValidatorsMethod = 'register' . $method . 'Validators';
665
        if (!is_callable(array($controllerInstance, $registerValidatorsMethod))) {
666
            $registerValidatorsMethod = 'registerValidators';
667
        }
668
        $controllerInstance->$registerValidatorsMethod();
669
    }
670
    
671
    /**
672
     * Retrieve this container's request method name.
673
     *
674
     * @return     string The request method name.
675
     *
676
     * @author     David Zülke <[email protected]>
677
     * @since      1.0.0
678
     */
679
    public function getRequestMethod()
680
    {
681
        return $this->requestMethod;
682
    }
683
684
    /**
685
     * Set this container's request method name.
686
     *
687
     * @param      string $requestMethod The request method name.
688
     *
689
     * @author     David Zülke <[email protected]>
690
     * @since      1.0.0
691
     */
692
    public function setRequestMethod($requestMethod)
693
    {
694
        $this->requestMethod = $requestMethod;
695
    }
696
697
    /**
698
     * Retrieve this container's request data holder instance.
699
     *
700
     * @return     RequestDataHolder The request data holder.
701
     *
702
     * @author     David Zülke <[email protected]>
703
     * @since      0.11.0
704
     */
705
    final public function getRequestData()
706
    {
707
        return $this->requestData;
708
    }
709
710
    /**
711
     * Set this container's global request data holder reference.
712
     *
713
     * @param      RequestDataHolder $rd The request data holder.
714
     *
715
     * @author     David Zülke <[email protected]>
716
     * @since      0.11.0
717
     */
718
    final public function setRequestData(RequestDataHolder $rd)
719
    {
720
        $this->globalRequestData = $rd;
721
    }
722
723
    /**
724
     * Get this container's request data holder instance for additional arguments.
725
     *
726
     * @return     RequestDataHolder The additional arguments.
727
     *
728
     * @author     David Zülke <[email protected]>
729
     * @since      0.11.0
730
     */
731
    public function getArguments()
732
    {
733
        return $this->arguments;
734
    }
735
736
    /**
737
     * Set this container's request data holder instance for additional arguments.
738
     *
739
     * @return     RequestDataHolder The request data holder.
740
     *
741
     * @author     David Zülke <[email protected]>
742
     * @since      0.11.0
743
     */
744
    public function setArguments(RequestDataHolder $arguments)
745
    {
746
        $this->arguments = $arguments;
747
    }
748
749
    /**
750
     * Retrieve this container's response instance.
751
     *
752
     * @return     Response The Response instance for this controller.
753
     *
754
     * @author     David Zülke <[email protected]>
755
     * @since      0.11.0
756
     */
757
    public function getResponse()
758
    {
759
        return $this->response;
760
    }
761
762
    /**
763
     * Set a new response.
764
     *
765
     * @param      Response $response A new Response instance.
766
     *
767
     * @author     David Zülke <[email protected]>
768
     * @since      0.11.0
769
     */
770
    public function setResponse(Response $response)
771
    {
772
        $this->response = $response;
773
        // do not set the output type on the response here!
774
    }
775
776
    /**
777
     * Retrieve the output type of this container.
778
     *
779
     * @return     OutputType The output type object.
780
     *
781
     * @author     David Zülke <[email protected]>
782
     * @since      0.11.0
783
     */
784
    public function getOutputType()
785
    {
786
        return $this->outputType;
787
    }
788
789
    /**
790
     * Set a different output type for this container.
791
     *
792
     * @param      OutputType $outputType An output type object.
793
     *
794
     * @author     David Zülke <[email protected]>
795
     * @since      0.11.0
796
     */
797
    public function setOutputType(OutputType $outputType)
798
    {
799
        $this->outputType = $outputType;
800
        if ($this->response) {
801
            $this->response->setOutputType($outputType);
802
        }
803
    }
804
805
    /**
806
     * Retrieve this container's microtime.
807
     *
808
     * @return     string A string representing the microtime this container was
809
     *                    initialized.
810
     *
811
     * @author     David Zülke <[email protected]>
812
     * @since      0.11.0
813
     */
814
    public function getMicrotime()
815
    {
816
        return $this->microtime;
817
    }
818
819
    /**
820
     * Retrieve this container's controller instance.
821
     *
822
     * @return     Controller An controller implementation instance.
823
     *
824
     * @author     David Zülke <[email protected]>
825
     * @since      0.11.0
826
     */
827
    public function getControllerInstance()
828
    {
829
        if ($this->controllerInstance === null) {
830
            $dispatcher = $this->context->getDispatcher();
831
832
            $moduleName = $this->getModuleName();
833
            $controllerName = $this->getControllerName();
834
            
835
            $this->controllerInstance = $dispatcher->createControllerInstance($moduleName, $controllerName);
836
            
837
            // initialize the controller
838
            $this->controllerInstance->initialize($this);
839
        }
840
        
841
        return $this->controllerInstance;
842
    }
843
844
    /**
845
     * Retrieve this container's view instance.
846
     *
847
     * @return     View A view implementation instance.
848
     *
849
     * @author     Ross Lawley <[email protected]>
850
     * @since      0.11.0
851
     */
852
    public function getViewInstance()
853
    {
854
        if ($this->viewInstance === null) {
855
            // get the view instance
856
            $this->viewInstance = $this->getContext()->getDispatcher()->createViewInstance($this->getViewModuleName(), $this->getViewName());
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getContext()->get..., $this->getViewName()) of type object<Agavi\View\View> is incompatible with the declared type object<Agavi\Exception\ViewException> of property $viewInstance.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
857
            // initialize the view
858
            $this->viewInstance->initialize($this);
859
        }
860
        
861
        return $this->viewInstance;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->viewInstance; of type Agavi\View\View|Agavi\Exception\ViewException adds the type Agavi\Exception\ViewException to the return on line 861 which is incompatible with the return type documented by Agavi\Dispatcher\Executi...tainer::getViewInstance of type Agavi\View\View.
Loading history...
862
    }
863
864
    /**
865
     * Set this container's view instance.
866
     *
867
     * @param      View $viewInstance A view implementation instance.
868
     *
869
     * @author     Ross Lawley <[email protected]>
870
     * @since      0.11.0
871
     */
872
    public function setViewInstance($viewInstance)
873
    {
874
        return $this->viewInstance = $viewInstance;
0 ignored issues
show
Documentation Bug introduced by
It seems like $viewInstance of type object<Agavi\View\View> is incompatible with the declared type object<Agavi\Exception\ViewException> of property $viewInstance.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
875
    }
876
877
    /**
878
     * Retrieve this container's module name.
879
     *
880
     * @return     string A module name.
881
     *
882
     * @author     David Zülke <[email protected]>
883
     * @since      0.11.0
884
     */
885
    public function getModuleName()
886
    {
887
        return $this->moduleName;
888
    }
889
890
    /**
891
     * Retrieve this container's controller name.
892
     *
893
     * @return     string An controller name.
894
     *
895
     * @author     David Zülke <[email protected]>
896
     * @since      0.11.0
897
     */
898
    public function getControllerName()
899
    {
900
        return $this->controllerName;
901
    }
902
903
    /**
904
     * Retrieve this container's view module name. This is the name of the module of
905
     * the View returned by the Controller.
906
     *
907
     * @return     string A view module name.
908
     *
909
     * @author     David Zülke <[email protected]>
910
     * @since      0.11.0
911
     */
912
    public function getViewModuleName()
913
    {
914
        return $this->viewModuleName;
915
    }
916
917
    /**
918
     * Retrieve this container's view name.
919
     *
920
     * @return     string A view name.
921
     *
922
     * @author     David Zülke <[email protected]>
923
     * @since      0.11.0
924
     */
925
    public function getViewName()
926
    {
927
        return $this->viewName;
928
    }
929
930
    /**
931
     * Set the module name for this container.
932
     *
933
     * @param      string $moduleName A module name.
934
     *
935
     * @author     David Zülke <[email protected]>
936
     * @since      0.11.0
937
     */
938
    public function setModuleName($moduleName)
939
    {
940
        if (null === $moduleName) {
941
            $this->moduleName = null;
942
        } elseif (preg_match(self::SANE_MODULE_NAME, $moduleName)) {
943
            $this->moduleName = $moduleName;
944
        } else {
945
            throw new AgaviException(sprintf('Invalid module name "%1$s"', $moduleName));
946
        }
947
    }
948
949
    /**
950
     * Set the controller name for this container.
951
     *
952
     * @param      string $controllerName An controller name.
953
     *
954
     * @author     David Zülke <[email protected]>
955
     * @since      0.11.0
956
     */
957
    public function setControllerName($controllerName)
958
    {
959 View Code Duplication
        if (null === $controllerName) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
960
            $this->controllerName = null;
961
        } elseif (preg_match(self::SANE_CONTROLLER_NAME, $controllerName)) {
962
            $controllerName = Toolkit::canonicalName($controllerName);
963
            $this->controllerName = $controllerName;
964
        } else {
965
            throw new AgaviException(sprintf('Invalid controller name "%1$s"', $controllerName));
966
        }
967
    }
968
969
    /**
970
     * Set the view module name for this container.
971
     *
972
     * @param      string $viewModuleName A view module name.
973
     *
974
     * @author     David Zülke <[email protected]>
975
     * @since      0.11.0
976
     */
977
    public function setViewModuleName($viewModuleName)
978
    {
979
        if (null === $viewModuleName) {
980
            $this->viewModuleName = null;
981
        } elseif (preg_match(self::SANE_MODULE_NAME, $viewModuleName)) {
982
            $this->viewModuleName = $viewModuleName;
983
        } else {
984
            throw new AgaviException(sprintf('Invalid view module name "%1$s"', $viewModuleName));
985
        }
986
    }
987
988
    /**
989
     * Set the module name for this container.
990
     *
991
     * @param      string $viewName A view name.
992
     *
993
     * @author     David Zülke <[email protected]>
994
     * @since      0.11.0
995
     */
996
    public function setViewName($viewName)
997
    {
998 View Code Duplication
        if (null === $viewName) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
999
            $this->viewName = null;
1000
        } elseif (preg_match(self::SANE_VIEW_NAME, $viewName)) {
1001
            $viewName = Toolkit::canonicalName($viewName);
1002
            $this->viewName = $viewName;
1003
        } else {
1004
            throw new AgaviException(sprintf('Invalid view name "%1$s"', $viewName));
1005
        }
1006
    }
1007
1008
     /**
1009
     * Check if a "next" container has been set.
1010
     *
1011
     * @return     bool True, if a container for eventual execution has been set.
1012
     *
1013
     * @author     David Zülke <[email protected]>
1014
     * @since      0.11.0
1015
     */
1016
    public function hasNext()
1017
    {
1018
        return $this->next !== null;
1019
    }
1020
1021
    /**
1022
     * Get the "next" container.
1023
     *
1024
     * @return     ExecutionContainer The "next" container, of null if unset.
1025
     *
1026
     * @author     David Zülke <[email protected]>
1027
     * @since      0.11.0
1028
     */
1029
    public function getNext()
1030
    {
1031
        return $this->next;
1032
    }
1033
1034
    /**
1035
     * Set the container that should be executed once this one finished running.
1036
     *
1037
     * @param      ExecutionContainer $container An execution container instance.
1038
     *
1039
     * @author     David Zülke <[email protected]>
1040
     * @since      0.11.0
1041
     */
1042
    public function setNext(ExecutionContainer $container)
1043
    {
1044
        $this->next = $container;
1045
    }
1046
1047
    /**
1048
     * Remove a possibly set "next" container.
1049
     *
1050
     * @return     ExecutionContainer The removed "next" container, or null
1051
     *                                     if none had been set.
1052
     *
1053
     * @author     David Zülke <[email protected]>
1054
     * @since      0.11.0
1055
     */
1056
    public function clearNext()
1057
    {
1058
        $retval = $this->next;
1059
        $this->next = null;
1060
        return $retval;
1061
    }
1062
}
1063