Completed
Push — master ( dc3660...ce54b6 )
by Vítor
03:05
created

AbstractModule::getCommandsPath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
/**
3
 * This file is part of the PPI Framework.
4
 *
5
 * @copyright  Copyright (c) 2011-2016 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 Aura\Router\Router as AuraRouter;
14
use Aura\Router\RouterFactory as AuraRouterFactory;
15
use Illuminate\Events\Dispatcher;
16
use PPI\FastRoute\Wrapper\FastRouteWrapper;
17
use PPI\Framework\Config\ConfigLoader;
18
use PPI\Framework\Console\Application;
19
use PPI\Framework\Router\Loader\YamlFileLoader;
20
use PPI\LaravelRouting\LaravelRouter;
21
use PPI\LaravelRouting\Loader\LaravelRoutesLoader;
22
use Symfony\Component\Config\FileLocator;
23
use Symfony\Component\Finder\Finder;
24
use Zend\ModuleManager\Feature\ConfigProviderInterface;
25
use Zend\Stdlib\ArrayUtils;
26
27
/**
28
 * The base PPI module class.
29
 *
30
 * @author Paul Dragoonis <[email protected]>
31
 */
32
abstract class AbstractModule implements ModuleInterface, ConfigProviderInterface
33
{
34
    /**
35
     * @var string The Module name.
36
     */
37
    protected $name;
38
39
    /**
40
     * @var \ReflectionObject
41
     */
42
    protected $reflected;
43
44
    /**
45
     * @todo Add inline documentation.
46
     *
47
     * @var null
48
     */
49
    protected $config;
50
51
    /**
52
     * Configuration loader.
53
     *
54
     * @var null|\PPI\Framework\Config\ConfigLoader
55
     */
56
    protected $configLoader;
57
58
    /**
59
     * @todo Add inline documentation.
60
     *
61
     * @var null
62
     */
63
    protected $routes;
64
65
    /**
66
     * @todo Add inline documentation.
67
     *
68
     * @var null
69
     */
70
    protected $services;
71
72
    /**
73
     * Load up our routes.
74
     *
75
     * @param string $path
76
     *
77
     * @return \Symfony\Component\Routing\RouteCollection
78
     */
79
    protected function loadSymfonyRoutes($path)
80
    {
81
        if ($this->routes === null) {
82
            $loader = new YamlFileLoader(new FileLocator(array(dirname($path))));
83
            $loader->setDefaults(array('_module' => $this->getName()));
84
85
            $routesCollection = $loader->load(pathinfo($path, PATHINFO_FILENAME) . '.' . pathinfo($path, PATHINFO_EXTENSION));
86
            $this->routes     = $routesCollection;
0 ignored issues
show
Documentation Bug introduced by
It seems like $routesCollection of type object<Symfony\Component\Routing\RouteCollection> is incompatible with the declared type null of property $routes.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
87
        }
88
89
        return $this->routes;
90
    }
91
92
    /**
93
     * Load up our routes.
94
     *
95
     * @deprecated Please use loadSymfonyRoutes instead
96
     *
97
     * @param string $path
98
     *
99
     * @return \Symfony\Component\Routing\RouteCollection
100
     */
101
    protected function loadYamlRoutes($path)
102
    {
103
        return $this->loadSymfonyRoutes($path);
104
    }
105
106
    /**
107
     * @param string $path
108
     *
109
     * @throws \Exception when the included routes file doesn't return an AuraRouter back
110
     *
111
     * @return AuraRouter
112
     */
113
    protected function loadLaravelRoutes($path)
114
    {
115
        $router = (new LaravelRoutesLoader(
116
            new LaravelRouter(new Dispatcher())
117
        ))->load($path);
118
        $router->setModuleName($this->getName());
119
120
        return $router;
121
    }
122
123
    /**
124
     * @param $path
125
     *
126
     * @return FastRouteWrapper
127
     */
128
    protected function loadFastRouteRoutes($path)
129
    {
130
        $routeParser = new \FastRoute\RouteParser\Std();
131
        $dataGenerator = new \FastRoute\DataGenerator\GroupCountBased();
132
        $routeCollector = new \FastRoute\RouteCollector($routeParser, $dataGenerator);
133
134
        if (!is_readable($path)) {
135
            throw new \InvalidArgumentException('Invalid fast route routes path found: ' . $path);
136
        }
137
138
        // The included file must return the laravel router
139
        $getRouteCollector = function () use ($routeCollector, $path) {
140
            $r = $routeCollector;
141
            include $path;
142
143
            return $r;
144
        };
145
146
        $routeCollector = $getRouteCollector();
147
        if (!($routeCollector instanceof \FastRoute\RouteCollector)) {
0 ignored issues
show
Bug introduced by
The class FastRoute\RouteCollector does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
148
            throw new \Exception('Invalid return value from '
149
                . pathinfo($path, PATHINFO_FILENAME)
150
                . ' expected instance of RouteCollector'
151
            );
152
        }
153
154
        $dispatcher = new \FastRoute\Dispatcher\GroupCountBased($routeCollector->getData());
155
        $router = new \PPI\FastRoute\Wrapper\FastRouteWrapper(
156
            $dispatcher
157
        );
158
        $router->setModuleName($this->getName());
159
160
        return $router;
161
    }
162
163
    /**
164
     * @todo - move part of this into AuraRouterWrapper->load()
165
     * @todo - consider adding a setModuleName() to the AuraRouterWrapper instead of _module to each route.
166
     *
167
     * @param string $path
168
     *
169
     * @throws \Exception when the included routes file doesn't return an AuraRouter back
170
     *
171
     * @return AuraRouter
172
     */
173
    protected function loadAuraRoutes($path)
174
    {
175
        if (!is_readable($path)) {
176
            throw new \InvalidArgumentException('Invalid aura routes path found: ' . $path);
177
        }
178
179
        $router = (new AuraRouterFactory())->newInstance();
180
181
        // The included file must return the aura router
182
        $router = include $path;
183
184 View Code Duplication
        if (!($router instanceof AuraRouter)) {
0 ignored issues
show
Bug introduced by
The class Aura\Router\Router does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
185
            throw new \Exception('Invalid return value from '
186
                . pathinfo($path, PATHINFO_FILENAME)
187
                . ' expected instance of AuraRouter'
188
            );
189
        }
190
191
        foreach ($router->getRoutes() as $route) {
192
            $route->addValues(array('_module' => $this->getName()));
193
        }
194
195
        return $router;
196
    }
197
198
    /**
199
     * Load up our config results from the specific yaml file.
200
     *
201
     * @param string $path
202
     *
203
     * @return array
204
     *
205
     * @deprecated since version 2.1, to be removed in 2.2. Use "loadConfig()" instead.
206
     */
207
    protected function loadYamlConfig($path)
208
    {
209
        return $this->loadConfig($path);
210
    }
211
212
    /**
213
     * Set services for our module.
214
     *
215
     * @param string $services
216
     *
217
     * @return Module
218
     */
219
    public function setServices($services)
220
    {
221
        $this->services = $services;
0 ignored issues
show
Documentation Bug introduced by
It seems like $services of type string is incompatible with the declared type null of property $services.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
222
223
        return $this;
224
    }
225
226
    /**
227
     * Get the services.
228
     *
229
     * @return array
230
     */
231
    public function getServices()
232
    {
233
        return $this->services;
234
    }
235
236
    /**
237
     * Get a particular service.
238
     *
239
     * @param string $serviceName
240
     *
241
     * @return mixed
242
     */
243
    public function getService($serviceName)
244
    {
245
        return isset($this->services[$serviceName]) ? $this->services : null;
246
    }
247
248
    /**
249
     * Loads a configuration file (PHP, YAML) or PHP array.
250
     *
251
     * @param string      $resource The resource
252
     * @param null|string $type     The resource type
253
     *
254
     * @return array
255
     */
256
    public function loadConfig($resource, $type = null)
257
    {
258
        return $this->getConfigLoader()->load($resource, $type);
259
    }
260
261
    /**
262
     * Loads and merges the configuration.
263
     *
264
     * @param mixed $resources
265
     *
266
     * @return array
267
     */
268
    public function mergeConfig($resources)
269
    {
270
        $configs = array();
271
        foreach (is_array($resources) ? $resources : func_get_args() as $resource) {
272
            $configs = ArrayUtils::merge($configs, $this->loadConfig($resource));
273
        }
274
275
        return $configs;
276
    }
277
278
    /**
279
     * Set the module name.
280
     *
281
     * @param string $Name
282
     *
283
     * @return $this
284
     */
285
    public function setName($Name)
286
    {
287
        $this->name = $Name;
288
289
        return $this;
290
    }
291
292
    /**
293
     * Returns the module name. Defaults to the module namespace stripped of backslashes.
294
     *
295
     * @return string The Module name
296
     */
297
    public function getName()
298
    {
299
        if (null !== $this->name) {
300
            return $this->name;
301
        }
302
303
        $this->name = str_replace('\\', '', $this->getNamespace());
304
305
        return $this->name;
306
    }
307
308
    /**
309
     * Gets the Module namespace.
310
     *
311
     * @return string The Module namespace
312
     *
313
     * @api
314
     */
315
    public function getNamespace()
316
    {
317
        if (null === $this->reflected) {
318
            $this->reflected = new \ReflectionObject($this);
319
        }
320
321
        return $this->reflected->getNamespaceName();
322
    }
323
324
    /**
325
     * Gets the Module directory path.
326
     *
327
     * @return string The Module absolute path
328
     *
329
     * @api
330
     */
331
    public function getPath()
332
    {
333
        if (null === $this->reflected) {
334
            $this->reflected = new \ReflectionObject($this);
335
        }
336
337
        return dirname($this->reflected->getFileName());
338
    }
339
340
    /**
341
     * Returns configuration to merge with application configuration.
342
     *
343
     * @return array|\Traversable
344
     */
345
    public function getConfig()
346
    {
347
        return array();
348
    }
349
350
    /**
351
     * Returns the default location of where Command classes are registered.
352
     * Override this in your child module if it differs from this default convention.
353
     *
354
     * @return string
355
     */
356
    public function getCommandsPath()
357
    {
358
        return sprintf("%s/src/Command", $this->getPath());
359
    }
360
361
    /**
362
     * Finds and registers Commands.
363
     *
364
     * Override this method if your module commands do not follow the conventions:
365
     *
366
     * * Commands are in the 'Command' sub-directory
367
     * * Commands extend PPI\Framework\Console\Command\AbstractCommand
368
     *
369
     * @param Application $application An Application instance
370
     */
371
    public function registerCommands(Application $application)
372
    {
373
        if (!is_dir($dir = $this->getCommandsPath())) {
374
            return;
375
        }
376
377
        $finder = new Finder();
378
        $finder->files()->name('*Command.php')->in($dir);
379
380
        $prefix = $this->getNamespace() . '\\Command';
381
        foreach ($finder as $file) {
382
            $ns = $prefix;
383
            if ($relativePath = $file->getRelativePath()) {
384
                $ns .= '\\' . strtr($relativePath, '/', '\\');
385
            }
386
            $r = new \ReflectionClass($ns . '\\' . $file->getBasename('.php'));
387
            if ($r->isSubclassOf('PPI\Framework\\Console\\Command\\AbstractCommand') && !$r->isAbstract()) {
388
                $application->add($r->newInstance());
389
            }
390
        }
391
    }
392
393
    /**
394
     * Returns a ConfigLoader instance.
395
     *
396
     * @return \PPI\Framework\Config\ConfigLoader
397
     */
398
    protected function getConfigLoader()
399
    {
400
        if (null === $this->configLoader) {
401
            $this->configLoader = new ConfigLoader($this->getPath() . '/resources/config');
402
        }
403
404
        return $this->configLoader;
405
    }
406
}
407