1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Veto. |
4
|
|
|
* PHP Microframework. |
5
|
|
|
* |
6
|
|
|
* @author Damien Walsh <[email protected]> |
7
|
|
|
* @copyright Damien Walsh 2013-2014 |
8
|
|
|
* @version 0.1 |
9
|
|
|
* @package veto |
10
|
|
|
*/ |
11
|
|
|
namespace Veto\Layer; |
12
|
|
|
|
13
|
|
|
use Veto\DI\AbstractContainerAccessor; |
14
|
|
|
use Veto\HTTP\Request; |
15
|
|
|
use Veto\HTTP\Response; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* LayerChain |
19
|
|
|
* |
20
|
|
|
* Groups layers together to form a Request->Response flow |
21
|
|
|
*/ |
22
|
|
|
class LayerChain extends AbstractContainerAccessor |
23
|
|
|
{ |
24
|
|
|
/** |
25
|
|
|
* An associative, 2D array of layers that are configured. |
26
|
|
|
* Layers are stored here keyed by priority then name. |
27
|
|
|
* |
28
|
|
|
* @var array |
29
|
|
|
*/ |
30
|
|
|
private $layers = array(); |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Add a layer to this LayerChain. |
34
|
|
|
* |
35
|
|
|
* @param object $layer |
36
|
|
|
* @param int $priority |
37
|
|
|
*/ |
38
|
|
|
public function addLayer($layer, $priority = 0) |
39
|
|
|
{ |
40
|
|
|
// Enforce the type of $layer |
41
|
|
|
if (!($layer instanceof InboundLayerInterface || $layer instanceof OutboundLayerInterface)) { |
42
|
|
|
throw new \InvalidArgumentException( |
43
|
|
|
'Argument 1 of ' . __CLASS__ . '::' . __METHOD__ . |
44
|
|
|
' must be either an InboundLayerInterface or an OutboundLayerInterface instance.' |
45
|
|
|
); |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
$this->layers[$priority][] = $layer; |
49
|
|
|
|
50
|
|
|
// TODO: Improve the efficiency of this by _not_ sorting after every insertion |
51
|
|
|
ksort($this->layers); |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* @param Request $request |
56
|
|
|
* @return Response |
57
|
|
|
* @throws \RuntimeException |
58
|
|
|
*/ |
59
|
|
|
public function processLayers(Request $request) |
60
|
|
|
{ |
61
|
|
|
// There must be at least one inbound layer, otherwise our Request will never become a Response |
62
|
|
|
if (0 === count($this->layers)) { |
63
|
|
|
throw new \RuntimeException( |
64
|
|
|
'At least one layer must be defined in order to handle requests.' |
65
|
|
|
); |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
$response = $this->processInboundLayers($request); |
69
|
|
|
|
70
|
|
|
// By the end of the inbound layer list, a response should have been obtained |
71
|
|
|
if (!$response instanceof Response) { |
72
|
|
|
throw new \RuntimeException( |
73
|
|
|
'At least one inbound layer must produce a Response instance. ' . |
74
|
|
|
'The final processed layer returned a "' . gettype($response) . '".' |
75
|
|
|
); |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
// The response should now be processed by the outbound layers |
79
|
|
|
return $this->processOutboundLayers($response); |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* @param Request $request |
84
|
|
|
* @return Request |
85
|
|
|
*/ |
86
|
|
View Code Duplication |
private function processInboundLayers(Request $request) |
|
|
|
|
87
|
|
|
{ |
88
|
|
|
$result = $request; |
89
|
|
|
|
90
|
|
|
// Pass through layers inwards |
91
|
|
|
foreach ($this->layers as $priority => $layers) { |
92
|
|
|
foreach ($layers as $layerName => $layer) { |
93
|
|
|
|
94
|
|
|
if (!($layer instanceof InboundLayerInterface)) { |
95
|
|
|
continue; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
$result = $layer->in($result); |
99
|
|
|
|
100
|
|
|
// If the layer produces a response, no more inbound layers may execute |
101
|
|
|
if ($result instanceof Response) { |
102
|
|
|
return $result; |
|
|
|
|
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
if (!$result instanceof Request) { |
106
|
|
|
throw new \RuntimeException( |
107
|
|
|
'Each inbound layer of the chain must produce a Request or Response type. ' . |
108
|
|
|
'The "' . $layerName . '" layer returned ' . gettype($request) . '.' |
109
|
|
|
); |
110
|
|
|
} |
111
|
|
|
} |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
return $result; |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* @param Response $response |
119
|
|
|
* @return Response |
120
|
|
|
*/ |
121
|
|
View Code Duplication |
private function processOutboundLayers(Response $response) |
|
|
|
|
122
|
|
|
{ |
123
|
|
|
$result = $response; |
124
|
|
|
|
125
|
|
|
foreach ($this->layers as $priority => $layers) { |
126
|
|
|
foreach ($layers as $layerName => $layer) { |
127
|
|
|
|
128
|
|
|
if (!($layer instanceof OutboundLayerInterface)) { |
129
|
|
|
continue; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
$result = $layer->out($result); |
133
|
|
|
|
134
|
|
|
if (!$result instanceof Response) { |
135
|
|
|
throw new \RuntimeException( |
136
|
|
|
'Each outbound layer of the chain must produce a Response type. ' . |
137
|
|
|
'The "' . $layerName . '" layer returned ' . gettype($response) . '.' |
138
|
|
|
); |
139
|
|
|
} |
140
|
|
|
} |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
return $result; |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* Get the layers currently added to this LayerChain, keyed by priority group. |
148
|
|
|
* |
149
|
|
|
* @return array[] |
150
|
|
|
*/ |
151
|
|
|
public function getLayers() |
152
|
|
|
{ |
153
|
|
|
return $this->layers; |
154
|
|
|
} |
155
|
|
|
} |
156
|
|
|
|
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.