Passed
Push — master ( 0d72e8...44be60 )
by Gabor
04:58
created

ServiceAdapter::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 7
cts 7
cp 1
rs 9.2
c 0
b 0
f 0
cc 1
eloc 13
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 * WebHemi.
4
 *
5
 * PHP version 7.1
6
 *
7
 * @copyright 2012 - 2017 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
declare(strict_types = 1);
13
14
namespace WebHemi\MiddlewarePipeline\ServiceAdapter\Base;
15
16
use RuntimeException;
17
use WebHemi\Configuration\ServiceInterface as ConfigurationInterface;
18
use WebHemi\Middleware\Common;
19
use WebHemi\Middleware\MiddlewareInterface;
20
use WebHemi\MiddlewarePipeline\ServiceInterface;
21
22
/**
23
 * Class ServiceAdapter.
24
 */
25
class ServiceAdapter implements ServiceInterface
26
{
27
    /** @var ConfigurationInterface */
28
    private $configuration;
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
     * ServiceAdapter constructor.
40
     *
41
     * @param ConfigurationInterface $configuration
42
     */
43 11
    public function __construct(ConfigurationInterface $configuration)
44
    {
45 11
        $this->configuration = $configuration->getConfig('middleware_pipeline');
46 11
        $this->keyMiddlewareList = [
47
            Common\RoutingMiddleware::class,
48
            Common\DispatcherMiddleware::class,
49
            Common\FinalMiddleware::class
50
        ];
51
52
        // The FinalMiddleware should not be part of the queue.
53 11
        $this->priorityList = [
54
            0   => [Common\RoutingMiddleware::class],
55
            100 => [Common\DispatcherMiddleware::class]
56
        ];
57
58 11
        $this->pipelineList = [
59
            Common\RoutingMiddleware::class,
60
            Common\DispatcherMiddleware::class,
61
        ];
62
63 11
        $this->buildPipeline();
64 11
    }
65
66
    /**
67
     * Add middleware definitions to the pipeline.
68
     *
69
     * @param string $moduleName
70
     * @return void
71
     */
72 11
    private function buildPipeline(string $moduleName = 'Global') : void
73
    {
74 11
        $pipelineConfig = $this->configuration->getData($moduleName);
75
76 11
        foreach ($pipelineConfig as $middlewareData) {
77 11
            if (!isset($middlewareData['priority'])) {
78 11
                $middlewareData['priority'] = 50;
79
            }
80
81 11
            $this->queueMiddleware($middlewareData['service'], $middlewareData['priority']);
82
        }
83 11
    }
84
85
    /**
86
     * Checks the given class against Middleware Criteria.
87
     *
88
     * @param string $middleWareClass
89
     * @throws RuntimeException
90
     * @return bool
91
     */
92 11
    private function checkMiddleware(string $middleWareClass) : bool
93
    {
94 11
        if (isset($this->index)) {
95 1
            throw new RuntimeException('You are forbidden to add new middleware after start.', 1000);
96
        }
97
98 11
        if (in_array($middleWareClass, $this->pipelineList)) {
99 1
            throw new RuntimeException(
100 1
                sprintf('The service "%s" is already added to the pipeline.', $middleWareClass),
101 1
                1001
102
            );
103
        }
104
105 11
        if (class_exists($middleWareClass)
106 11
            && !array_key_exists(MiddlewareInterface::class, class_implements($middleWareClass))
107
        ) {
108 1
            throw new RuntimeException(
109 1
                sprintf('The service "%s" is not a middleware.', $middleWareClass),
110 1
                1002
111
            );
112
        }
113
114 11
        return true;
115
    }
116
117
    /**
118
     * Adds module specific pipeline.
119
     *
120
     * @param string $moduleName
121
     * @return ServiceInterface
122
     */
123 1
    public function addModulePipeLine(string $moduleName) : ServiceInterface
124
    {
125 1
        $this->buildPipeline($moduleName);
126
127 1
        return $this;
128
    }
129
130
    /**
131
     * Adds a new middleware to the pipeline queue.
132
     *
133
     * @param string $middleWareClass
134
     * @param int    $priority
135
     * @throws RuntimeException
136
     * @return ServiceInterface
137
     */
138 11
    public function queueMiddleware(string $middleWareClass, int $priority = 50) : ServiceInterface
139
    {
140 11
        $this->checkMiddleware($middleWareClass);
141
142 11
        if (in_array($middleWareClass, $this->keyMiddlewareList)) {
143
            // Don't throw error if the user defines the default middleware classes.
144 11
            return $this;
145
        }
146
147 11
        if ($priority === 0 || $priority == 100) {
148 11
            $priority++;
149
        }
150
151 11
        if (!isset($this->priorityList[$priority])) {
152 11
            $this->priorityList[$priority] = [];
153
        }
154
155 11
        if (!in_array($middleWareClass, $this->pipelineList)) {
156 11
            $this->priorityList[$priority][] = $middleWareClass;
157 11
            $this->pipelineList[] = $middleWareClass;
158
        }
159
160 11
        return $this;
161
    }
162
163
    /**
164
     * Sorts the pipeline elements according to the priority.
165
     *
166
     * @return void
167
     */
168 7
    private function sortPipeline() : void
169
    {
170 7
        ksort($this->priorityList);
171 7
        $this->pipelineList = [];
172
173 7
        foreach ($this->priorityList as $middlewareList) {
174 7
            $this->pipelineList = array_merge($this->pipelineList, $middlewareList);
175
        }
176 7
    }
177
178
    /**
179
     * Starts the pipeline.
180
     *
181
     * @return null|string
182
     */
183 7
    public function start() : ? string
184
    {
185 7
        $this->index = 0;
186 7
        $this->sortPipeline();
187
188 7
        return $this->next();
189
    }
190
191
    /**
192
     * Gets next element from the pipeline.
193
     *
194
     * @return null|string
195
     */
196 8
    public function next() : ? string
197
    {
198 8
        if (!isset($this->index)) {
199 1
            throw new RuntimeException('Unable to get the next element until the pipeline is not started.', 1003);
200
        }
201
202 7
        return isset($this->pipelineList[$this->index]) ? $this->pipelineList[$this->index++] : null;
203
    }
204
205
    /**
206
     * Gets the full pipeline list.
207
     *
208
     * @return array
209
     */
210 1
    public function getPipelineList() : array
211
    {
212 1
        return $this->pipelineList;
213
    }
214
}
215