1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* This file is part of the PPI Framework. |
4
|
|
|
* |
5
|
|
|
* @copyright Copyright (c) 2011-2013 Paul Dragoonis <[email protected]> |
6
|
|
|
* @license http://opensource.org/licenses/mit-license.php MIT |
7
|
|
|
* |
8
|
|
|
* @link http://www.ppi.io |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace PPI\Framework\Module; |
12
|
|
|
|
13
|
|
|
use PPI\Framework\Config\ConfigLoader; |
14
|
|
|
use PPI\Framework\Console\Application; |
15
|
|
|
use PPI\LaravelRouting\Loader\LaravelRoutesLoader; |
16
|
|
|
use PPI\LaravelRouting\LaravelRouter; |
17
|
|
|
use PPI\FastRoute\Wrapper\FastRouteWrapper; |
18
|
|
|
use PPI\Framework\Router\Loader\YamlFileLoader; |
19
|
|
|
|
20
|
|
|
use Symfony\Component\Config\FileLocator; |
21
|
|
|
use Symfony\Component\Finder\Finder; |
22
|
|
|
|
23
|
|
|
use Zend\ModuleManager\Feature\ConfigProviderInterface; |
24
|
|
|
use Zend\Stdlib\ArrayUtils; |
25
|
|
|
|
26
|
|
|
use Aura\Router\Router as AuraRouter; |
27
|
|
|
use Aura\Router\RouterFactory as AuraRouterFactory; |
28
|
|
|
|
29
|
|
|
use Illuminate\Events\Dispatcher; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* The base PPI module class. |
33
|
|
|
* |
34
|
|
|
* @author Paul Dragoonis <[email protected]> |
35
|
|
|
*/ |
36
|
|
|
abstract class AbstractModule implements ModuleInterface, ConfigProviderInterface |
37
|
|
|
{ |
38
|
|
|
/** |
39
|
|
|
* @var string The Module name. |
40
|
|
|
*/ |
41
|
|
|
protected $name; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @var \ReflectionObject |
45
|
|
|
*/ |
46
|
|
|
protected $reflected; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @todo Add inline documentation. |
50
|
|
|
* |
51
|
|
|
* @var null |
52
|
|
|
*/ |
53
|
|
|
protected $config; |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* Configuration loader. |
57
|
|
|
* |
58
|
|
|
* @var null|\PPI\Framework\Config\ConfigLoader |
59
|
|
|
*/ |
60
|
|
|
protected $configLoader; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* @todo Add inline documentation. |
64
|
|
|
* |
65
|
|
|
* @var null |
66
|
|
|
*/ |
67
|
|
|
protected $routes; |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* @todo Add inline documentation. |
71
|
|
|
* |
72
|
|
|
* @var null |
73
|
|
|
*/ |
74
|
|
|
protected $services; |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* Load up our routes. |
78
|
|
|
* |
79
|
|
|
* @param string $path |
80
|
|
|
* |
81
|
|
|
* @return \Symfony\Component\Routing\RouteCollection |
82
|
|
|
*/ |
83
|
|
View Code Duplication |
protected function loadSymfonyRoutes($path) |
|
|
|
|
84
|
|
|
{ |
85
|
|
|
if ($this->routes === null) { |
86
|
|
|
$loader = new YamlFileLoader(new FileLocator(array(dirname($path)))); |
87
|
|
|
$loader->setDefaults(array('_module' => $this->getName())); |
88
|
|
|
|
89
|
|
|
$routesCollection = $loader->load(pathinfo($path, PATHINFO_FILENAME) . '.' . pathinfo($path, PATHINFO_EXTENSION)); |
90
|
|
|
$this->routes = $routesCollection; |
|
|
|
|
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
return $this->routes; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* Load up our routes. |
98
|
|
|
* |
99
|
|
|
* @deprecated Please use loadSymfonyRoutes instead |
100
|
|
|
* @param string $path |
101
|
|
|
* @return \Symfony\Component\Routing\RouteCollection |
102
|
|
|
*/ |
103
|
|
View Code Duplication |
protected function loadYamlRoutes($path) |
|
|
|
|
104
|
|
|
{ |
105
|
|
|
if ($this->routes === null) { |
106
|
|
|
$loader = new YamlFileLoader(new FileLocator(array(dirname($path)))); |
107
|
|
|
$loader->setDefaults(array('_module' => $this->getName())); |
108
|
|
|
|
109
|
|
|
$routesCollection = $loader->load(pathinfo($path, PATHINFO_FILENAME) . '.' . pathinfo($path, PATHINFO_EXTENSION)); |
110
|
|
|
$this->routes = $routesCollection; |
|
|
|
|
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
return $this->routes; |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* @param string $path |
118
|
|
|
* @return AuraRouter |
119
|
|
|
* @throws \Exception when the included routes file doesn't return an AuraRouter back |
120
|
|
|
*/ |
121
|
|
|
protected function loadLaravelRoutes($path) |
122
|
|
|
{ |
123
|
|
|
$router = (new LaravelRoutesLoader( |
124
|
|
|
new LaravelRouter(new Dispatcher) |
125
|
|
|
))->load($path); |
126
|
|
|
$router->setModuleName($this->getName()); |
127
|
|
|
return $router; |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* @param $path |
132
|
|
|
* @return FastRouteWrapper |
133
|
|
|
*/ |
134
|
|
|
protected function loadFastRouteRoutes($path) |
135
|
|
|
{ |
136
|
|
|
$routeParser = new \FastRoute\RouteParser\Std(); |
137
|
|
|
$dataGenerator = new \FastRoute\DataGenerator\GroupCountBased(); |
138
|
|
|
$routeCollector = new \FastRoute\RouteCollector($routeParser, $dataGenerator); |
139
|
|
|
|
140
|
|
|
if(!is_readable($path)) { |
141
|
|
|
throw new \InvalidArgumentException('Invalid fast route routes path found: ' . $path); |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
// The included file must return the laravel router |
145
|
|
|
$getRouteCollector = function() use ($routeCollector, $path) { |
146
|
|
|
$r = $routeCollector; |
147
|
|
|
include $path; |
148
|
|
|
return $r; |
149
|
|
|
}; |
150
|
|
|
|
151
|
|
|
$routeCollector = $getRouteCollector(); |
152
|
|
|
if(!($routeCollector instanceof \FastRoute\RouteCollector)) { |
|
|
|
|
153
|
|
|
throw new \Exception('Invalid return value from ' |
154
|
|
|
. pathinfo($path, PATHINFO_FILENAME) |
155
|
|
|
. ' expected instance of RouteCollector' |
156
|
|
|
); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
$dispatcher = new \FastRoute\Dispatcher\GroupCountBased($routeCollector->getData()); |
160
|
|
|
$router = new \PPI\FastRoute\Wrapper\FastRouteWrapper( |
161
|
|
|
$dispatcher |
162
|
|
|
); |
163
|
|
|
$router->setModuleName($this->getName()); |
164
|
|
|
|
165
|
|
|
return $router; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* @todo - move part of this into AuraRouterWrapper->load() |
170
|
|
|
* @todo - consider adding a setModuleName() to the AuraRouterWrapper instead of _module to each route. |
171
|
|
|
* @param string $path |
172
|
|
|
* @return AuraRouter |
173
|
|
|
* @throws \Exception when the included routes file doesn't return an AuraRouter back |
174
|
|
|
*/ |
175
|
|
|
protected function loadAuraRoutes($path) |
176
|
|
|
{ |
177
|
|
|
|
178
|
|
|
if(!is_readable($path)) { |
179
|
|
|
throw new \InvalidArgumentException('Invalid aura routes path found: ' . $path); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
$router = (new AuraRouterFactory())->newInstance(); |
183
|
|
|
|
184
|
|
|
// The included file must return the aura router |
185
|
|
|
$router = include $path; |
186
|
|
|
|
187
|
|
View Code Duplication |
if(!($router instanceof AuraRouter)) { |
|
|
|
|
188
|
|
|
throw new \Exception('Invalid return value from ' |
189
|
|
|
. pathinfo($path, PATHINFO_FILENAME) |
190
|
|
|
. ' expected instance of AuraRouter' |
191
|
|
|
); |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
foreach($router->getRoutes() as $route) { |
195
|
|
|
$route->addValues(array('_module' => $this->getName())); |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
return $router; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
/** |
202
|
|
|
* Load up our config results from the specific yaml file. |
203
|
|
|
* |
204
|
|
|
* @param string $path |
205
|
|
|
* |
206
|
|
|
* @return array |
207
|
|
|
* |
208
|
|
|
* @deprecated since version 2.1, to be removed in 2.2. Use "loadConfig()" instead. |
209
|
|
|
*/ |
210
|
|
|
protected function loadYamlConfig($path) |
211
|
|
|
{ |
212
|
|
|
return $this->loadConfig($path); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* Set services for our module. |
217
|
|
|
* |
218
|
|
|
* @param string $services |
219
|
|
|
* |
220
|
|
|
* @return Module |
221
|
|
|
*/ |
222
|
|
|
public function setServices($services) |
223
|
|
|
{ |
224
|
|
|
$this->services = $services; |
|
|
|
|
225
|
|
|
|
226
|
|
|
return $this; |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
/** |
230
|
|
|
* Get the services. |
231
|
|
|
* |
232
|
|
|
* @return array |
233
|
|
|
*/ |
234
|
|
|
public function getServices() |
235
|
|
|
{ |
236
|
|
|
return $this->services; |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* Get a particular service. |
241
|
|
|
* |
242
|
|
|
* @param string $serviceName |
243
|
|
|
* |
244
|
|
|
* @return mixed |
245
|
|
|
*/ |
246
|
|
|
public function getService($serviceName) |
247
|
|
|
{ |
248
|
|
|
return isset($this->services[$serviceName]) ? $this->services : null; |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
/** |
252
|
|
|
* Loads a configuration file (PHP, YAML) or PHP array. |
253
|
|
|
* |
254
|
|
|
* @param string $resource The resource |
255
|
|
|
* @param null|string $type The resource type |
256
|
|
|
* |
257
|
|
|
* @return array |
258
|
|
|
*/ |
259
|
|
|
public function loadConfig($resource, $type = null) |
260
|
|
|
{ |
261
|
|
|
return $this->getConfigLoader()->load($resource, $type); |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
/** |
265
|
|
|
* Loads and merges the configuration. |
266
|
|
|
* |
267
|
|
|
* @param mixed $resources |
268
|
|
|
* |
269
|
|
|
* @return array |
270
|
|
|
*/ |
271
|
|
|
public function mergeConfig($resources) |
272
|
|
|
{ |
273
|
|
|
$configs = array(); |
274
|
|
|
foreach (is_array($resources) ? $resources : func_get_args() as $resource) { |
275
|
|
|
$configs = ArrayUtils::merge($configs, $this->loadConfig($resource)); |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
return $configs; |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
/** |
282
|
|
|
* Set the module name. |
283
|
|
|
* |
284
|
|
|
* @param string $Name |
285
|
|
|
* |
286
|
|
|
* @return $this |
287
|
|
|
*/ |
288
|
|
|
public function setName($Name) |
289
|
|
|
{ |
290
|
|
|
$this->name = $Name; |
291
|
|
|
|
292
|
|
|
return $this; |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
/** |
296
|
|
|
* Returns the module name. Defaults to the module namespace stripped of backslashes. |
297
|
|
|
* |
298
|
|
|
* @return string The Module name |
299
|
|
|
*/ |
300
|
|
|
public function getName() |
301
|
|
|
{ |
302
|
|
|
if (null !== $this->name) { |
303
|
|
|
return $this->name; |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
$this->name = str_replace('\\', '', $this->getNamespace()); |
307
|
|
|
|
308
|
|
|
return $this->name; |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
/** |
312
|
|
|
* Gets the Module namespace. |
313
|
|
|
* |
314
|
|
|
* @return string The Module namespace |
315
|
|
|
* |
316
|
|
|
* @api |
317
|
|
|
*/ |
318
|
|
|
public function getNamespace() |
319
|
|
|
{ |
320
|
|
|
if (null === $this->reflected) { |
321
|
|
|
$this->reflected = new \ReflectionObject($this); |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
return $this->reflected->getNamespaceName(); |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
/** |
328
|
|
|
* Gets the Module directory path. |
329
|
|
|
* |
330
|
|
|
* @return string The Module absolute path |
331
|
|
|
* |
332
|
|
|
* @api |
333
|
|
|
*/ |
334
|
|
|
public function getPath() |
335
|
|
|
{ |
336
|
|
|
if (null === $this->reflected) { |
337
|
|
|
$this->reflected = new \ReflectionObject($this); |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
return dirname($this->reflected->getFileName()); |
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
/** |
344
|
|
|
* Returns configuration to merge with application configuration. |
345
|
|
|
* |
346
|
|
|
* @return array|\Traversable |
347
|
|
|
*/ |
348
|
|
|
public function getConfig() |
349
|
|
|
{ |
350
|
|
|
return array(); |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
/** |
354
|
|
|
* Finds and registers Commands. |
355
|
|
|
* |
356
|
|
|
* Override this method if your module commands do not follow the conventions: |
357
|
|
|
* |
358
|
|
|
* * Commands are in the 'Command' sub-directory |
359
|
|
|
* * Commands extend PPI\Framework\Console\Command\AbstractCommand |
360
|
|
|
* |
361
|
|
|
* @param Application $application An Application instance |
362
|
|
|
*/ |
363
|
|
|
public function registerCommands(Application $application) |
364
|
|
|
{ |
365
|
|
|
if (!is_dir($dir = $this->getPath() . '/Command')) { |
366
|
|
|
return; |
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
$finder = new Finder(); |
370
|
|
|
$finder->files()->name('*Command.php')->in($dir); |
371
|
|
|
|
372
|
|
|
$prefix = $this->getNamespace() . '\\Command'; |
373
|
|
|
foreach ($finder as $file) { |
374
|
|
|
$ns = $prefix; |
375
|
|
|
if ($relativePath = $file->getRelativePath()) { |
376
|
|
|
$ns .= '\\' . strtr($relativePath, '/', '\\'); |
377
|
|
|
} |
378
|
|
|
$r = new \ReflectionClass($ns . '\\' . $file->getBasename('.php')); |
379
|
|
|
if ($r->isSubclassOf('PPI\Framework\\Console\\Command\\AbstractCommand') && !$r->isAbstract()) { |
380
|
|
|
$application->add($r->newInstance()); |
381
|
|
|
} |
382
|
|
|
} |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
/** |
386
|
|
|
* Returns a ConfigLoader instance. |
387
|
|
|
* |
388
|
|
|
* @return \PPI\Framework\Config\ConfigLoader |
389
|
|
|
*/ |
390
|
|
|
protected function getConfigLoader() |
391
|
|
|
{ |
392
|
|
|
if (null === $this->configLoader) { |
393
|
|
|
$this->configLoader = new ConfigLoader($this->getPath() . '/resources/config'); |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
return $this->configLoader; |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
} |
400
|
|
|
|
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.