Completed
Branch BUG/11268/session-ticket-relea... (71e39e)
by
unknown
27:50 queued 13:47
created

RequestStackBuilder::resolve()   B

Complexity

Conditions 5
Paths 14

Size

Total Lines 28
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 16
c 0
b 0
f 0
nc 14
nop 1
dl 0
loc 28
rs 8.439
1
<?php
2
3
namespace EventEspresso\core\services\request;
4
5
use EventEspresso\core\exceptions\ExceptionStackTraceDisplay;
6
use EventEspresso\core\services\loaders\LoaderInterface;
7
use Exception;
8
use SplDoublyLinkedList;
9
10
defined('EVENT_ESPRESSO_VERSION') || exit;
11
12
13
14
/**
15
 * Class RequestStackBuilder
16
 * Assembles the EventEspresso RequestStack
17
 * ! IMPORTANT ! middleware stack operates FIRST IN FIRST OUT
18
 * so items at the beginning of the final middleware array will run last
19
 *
20
 * @package EventEspresso\core\services\request
21
 * @author  Brent Christensen
22
 * @since   4.9.53
23
 */
24
class RequestStackBuilder extends SplDoublyLinkedList
25
{
26
27
    /**
28
     * @type LoaderInterface $loader
29
     */
30
    private $loader;
31
32
33
    /**
34
     * RequestStackBuilder constructor.
35
     *
36
     * @param LoaderInterface $loader
37
     */
38
    public function __construct(LoaderInterface $loader)
39
    {
40
        $this->loader = $loader;
41
        $this->setIteratorMode(SplDoublyLinkedList::IT_MODE_LIFO | SplDoublyLinkedList::IT_MODE_KEEP);
42
    }
43
44
45
    /**
46
     * builds decorated middleware stack
47
     * by continuously injecting previous middleware app into the next
48
     *
49
     * @param RequestStackCoreAppInterface $application
50
     * @return RequestStack
51
     * @throws Exception
52
     */
53
    public function resolve(RequestStackCoreAppInterface $application)
54
    {
55
        $core_app = $application;
56
        // NOW... because the RequestStack is following the decorator pattern,
57
        // the first stack app we add will end up at the center of the stack,
58
        // and will end up being the last item to actually run, but we don't want that!
59
        // Basically we're dealing with TWO stacks, and transferring items from one to the other,
60
        // BUT... we want the final stack to be in the same order as the first.
61
        // So we need to reverse the iterator mode when transferring items,
62
        // because if we don't, the second stack will end  up in the incorrect order.
63
        $this->setIteratorMode(SplDoublyLinkedList::IT_MODE_FIFO | SplDoublyLinkedList::IT_MODE_KEEP);
64
        for ($this->rewind(); $this->valid(); $this->next()) {
65
            try {
66
                $middleware_app       = $this->validateMiddlewareAppDetails($this->current(), true);
67
                $middleware_app_class = array_shift($middleware_app);
68
                $middleware_app_args  = is_array($middleware_app) ? $middleware_app : array();
69
                $middleware_app_args  = array($application, $this->loader) + $middleware_app_args;
70
                $application          = $this->loader->getShared($middleware_app_class, $middleware_app_args);
71
            } catch (InvalidRequestStackMiddlewareException $exception) {
72
                if(WP_DEBUG) {
73
                    new ExceptionStackTraceDisplay($exception);
74
                    continue;
75
                }
76
                error_log($exception->getMessage());
77
            }
78
        }
79
        return new RequestStack($application, $core_app);
80
    }
81
82
83
    /**
84
     * Ensures that the app details that have been pushed onto RequestStackBuilder
85
     * are all ordered correctly so that the middleware can be properly constructed
86
     *
87
     * @param array $middleware_app
88
     * @param bool  $recurse
89
     * @return array
90
     * @throws InvalidRequestStackMiddlewareException
91
     */
92
    protected function validateMiddlewareAppDetails(array $middleware_app, $recurse = false)
93
    {
94
        $middleware_app_class = reset($middleware_app);
95
        // is array empty ?
96
        if($middleware_app_class === false) {
97
            throw new InvalidRequestStackMiddlewareException($middleware_app_class);
98
        }
99
        // are the class and arguments in the wrong order ?
100
        if(is_array($middleware_app_class)) {
101
            if ($recurse === true) {
102
                return $this->validateMiddlewareAppDetails(array_reverse($middleware_app));
103
            }
104
            throw new InvalidRequestStackMiddlewareException($middleware_app_class);
105
        }
106
        // is filter callback working like legacy middleware and sending a numerically indexed array ?
107
        if(is_int($middleware_app_class)) {
108
            if ($recurse === true) {
109
                $middleware_app = array_reverse($middleware_app);
110
                return $this->validateMiddlewareAppDetails(array(reset($middleware_app), array()));
111
            }
112
            throw new InvalidRequestStackMiddlewareException($middleware_app_class);
113
        }
114
        // is $middleware_app_class a valid FQCN (or class is already loaded) ?
115
        if(! class_exists($middleware_app_class)) {
116
            throw new InvalidRequestStackMiddlewareException($middleware_app_class);
117
        }
118
        return $middleware_app;
119
    }
120
}
121