Completed
Push — master ( 8efac5...a0c6e2 )
by Gabor
04:36
created

Pipeline::buildPipeline()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 12
ccs 0
cts 10
cp 0
rs 9.4285
cc 3
eloc 6
nc 3
nop 0
crap 12
1
<?php
2
/**
3
 * WebHemi.
4
 *
5
 * PHP version 5.6
6
 *
7
 * @copyright 2012 - 2016 Gixx-web (http://www.gixx-web.com)
8
 * @license   https://opensource.org/licenses/MIT The MIT License (MIT)
9
 *
10
 * @link      http://www.gixx-web.com
11
 */
12
namespace WebHemi\Middleware\Pipeline;
13
14
use InvalidArgumentException;
15
use RuntimeException;
16
use WebHemi\Config\ConfigInterface;
17
use WebHemi\Middleware\MiddlewareInterface;
18
use WebHemi\Middleware\DispatcherMiddleware;
19
use WebHemi\Middleware\FinalMiddleware;
20
use WebHemi\Middleware\RoutingMiddleware;
21
22
/**
23
 * Class Pipeline.
24
 */
25
class Pipeline implements MiddlewarePipelineInterface
26
{
27
    /** @var ConfigInterface */
28
    private $config;
29
    /** @var array */
30
    private $priorityList;
31
    /** @var array */
32
    private $pipelineList;
33
    /** @var array */
34
    private $keyMiddlewareList;
35
    /** @var int */
36
    private $index;
37
38
    /**
39
     * Pipeline constructor.
40
     *
41
     * @param ConfigInterface $pipelineConfig
42
     */
43
    public function __construct(ConfigInterface $pipelineConfig)
44
    {
45
        $this->config = $pipelineConfig;
46
47
        $this->keyMiddlewareList = [
48
            RoutingMiddleware::class,
49
            DispatcherMiddleware::class,
50
            FinalMiddleware::class
51
        ];
52
53
        // The FinalMiddleware should not be part of the queue.
54
        $this->priorityList = [
55
            0   => [RoutingMiddleware::class],
56
            100 => [DispatcherMiddleware::class]
57
        ];
58
59
        $this->pipelineList = [
60
            RoutingMiddleware::class,
61
            DispatcherMiddleware::class,
62
        ];
63
64
        $this->buildPipeline();
65
    }
66
67
    /**
68
     * Add middleware definitions to the pipeline.
69
     */
70
    private function buildPipeline()
71
    {
72
        $pipelineConfig = $this->config->toArray();
73
74
        foreach ($pipelineConfig as $middlewareData) {
75
            if (!isset($middlewareData['priority'])) {
76
                $middlewareData['priority'] = 50;
77
            }
78
79
            $this->queueMiddleware($middlewareData['class'], $middlewareData['priority']);
80
        }
81
    }
82
83
    /**
84
     * Checks the given class against Middleware Criterias.
85
     *
86
     * @param $middleWareClass
87
     *
88
     * @throws RuntimeException
89
     *
90
     * @return bool
91
     */
92
    private function checkMiddleware($middleWareClass)
93
    {
94
        if (isset($this->index)) {
95
            throw new RuntimeException('You are forbidden to add new middleware after start.');
96
        }
97
98
        if (in_array($middleWareClass, $this->pipelineList)) {
99
            throw new RuntimeException(
100
                sprintf('The class "%s" is already added to the pipeline.', $middleWareClass)
101
            );
102
        }
103
104
        $interfaces = class_implements($middleWareClass);
105
106
        if (empty($interfaces) || !in_array(MiddlewareInterface::class, $interfaces)) {
107
            throw new RuntimeException(
108
                sprintf('The class "%s" does not implement MiddlewareInterface.', $middleWareClass)
109
            );
110
        }
111
112
        return true;
113
    }
114
115
    /**
116
     * Adds a new middleware to the pipeline queue.
117
     *
118
     * @param string $middleWareClass
119
     * @param int    $priority
120
     *
121
     * @throws RuntimeException
122
     *
123
     * @return $this
124
     */
125
    public function queueMiddleware($middleWareClass, $priority = 50)
126
    {
127
        $this->checkMiddleware($middleWareClass);
128
129
        if (in_array($middleWareClass, $this->keyMiddlewareList)) {
130
            // Don't throw error if the user defines the default middleware classes.
131
            return $this;
132
        }
133
134
        if ($priority === 0 || $priority == 100) {
135
            $priority++;
136
        }
137
138
        if (!isset($this->priorityList[$priority])) {
139
            $this->priorityList[$priority] = [];
140
        }
141
142
        if (!in_array($middleWareClass, $this->priorityList[$priority])) {
143
            $this->priorityList[$priority][] = $middleWareClass;
144
        }
145
146
        return $this;
147
    }
148
149
    /**
150
     * Sorts the pipeline elements according to the priority.
151
     */
152
    private function sortPipeline()
153
    {
154
        ksort($this->priorityList);
155
        $this->pipelineList = [];
156
157
        foreach ($this->priorityList as $middlewareList) {
158
            $this->pipelineList = array_merge($this->pipelineList, $middlewareList);
159
        }
160
    }
161
162
    /**
163
     * Starts the pipeline.
164
     *
165
     * @return null|string
166
     */
167
    public function start()
168
    {
169
        $this->index = 0;
170
        $this->sortPipeline();
171
172
        return $this->next();
173
    }
174
175
    /**
176
     * Gets next element from the pipeline.
177
     *
178
     * @return null|string
179
     */
180
    public function next()
181
    {
182
        if (!isset($this->index)) {
183
            throw new RuntimeException('Unable to get the next element until the pipeline is not started.');
184
        }
185
186
        return isset($this->pipelineList[$this->index]) ? $this->pipelineList[$this->index++] : null;
187
    }
188
}
189