Completed
Push — master ( 6d83df...27e3a7 )
by Vítor
03:24
created

App   D

Complexity

Total Complexity 80

Size/Duplication

Total Lines 658
Duplicated Lines 0.76 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 20
Bugs 2 Features 0
Metric Value
wmc 80
c 20
b 2
f 0
lcom 1
cbo 11
dl 5
loc 658
rs 4.2273

33 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 15 10
A setOption() 0 16 3
A getOption() 0 10 2
A __clone() 0 9 2
B boot() 0 29 5
A run() 0 14 4
C dispatch() 0 37 7
A getName() 0 8 2
A getVersion() 0 4 1
A getEnvironment() 0 4 1
A isEnvironment() 0 10 3
A isDebug() 0 4 1
A getRootDir() 0 8 2
A getServiceManager() 0 4 1
A getContainer() 0 4 1
A getModuleManager() 0 8 2
A getModules() 0 4 1
A locateResource() 0 4 1
A getStartTime() 0 4 2
A getCacheDir() 0 4 1
A getLogDir() 0 4 1
A getCharset() 0 4 1
A getConfigManager() 0 9 2
A loadConfig() 0 6 1
A getConfig() 0 8 2
A serialize() 0 4 1
A unserialize() 0 6 1
A getAppParameters() 0 15 1
A getEnvParameters() 0 11 3
A buildServiceManager() 0 9 1
B handleRouting() 5 25 5
A log() 0 10 4
B enableDebug() 0 16 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like App often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use App, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * This file is part of the PPI Framework.
4
 *
5
 * @copyright  Copyright (c) 2011-2015 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;
12
13
use PPI\Framework\Config\ConfigManager;
14
use PPI\Framework\Debug\ExceptionHandler;
15
use PPI\Framework\Http\Response;
16
use PPI\Framework\ServiceManager\ServiceManagerBuilder;
17
use Psr\Http\Message\RequestInterface;
18
use Psr\Http\Message\ResponseInterface;
19
use Symfony\Component\ClassLoader\DebugClassLoader;
20
use Symfony\Component\Debug\ErrorHandler;
21
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
22
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
23
use PPI\Framework\Http\Request as HttpRequest;
24
use PPI\Framework\Http\Response as HttpResponse;
25
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
26
27
/**
28
 * The PPI App bootstrap class.
29
 *
30
 * This class sets various app settings, and allows you to override classes used in the bootup process.
31
 *
32
 * @author     Paul Dragoonis <[email protected]>
33
 * @author     Vítor Brandão <[email protected]>
34
 */
35
class App implements AppInterface
36
{
37
    /**
38
     * Version string.
39
     *
40
     * @var string
41
     */
42
    const VERSION = '2.1.0-DEV';
43
44
    /**
45
     * @var boolean
46
     */
47
    protected $booted = false;
48
49
    /**
50
     * @var boolean
51
     */
52
    protected $debug;
53
54
    /**
55
     * Application environment: "dev|development" vs "prod|production".
56
     *
57
     * @var string
58
     */
59
    protected $environment;
60
61
    /**
62
     * @var \Psr\Log\LoggerInterface
63
     */
64
    protected $logger;
65
66
    /**
67
     * Unix timestamp with microseconds.
68
     *
69
     * @var float
70
     */
71
    protected $startTime;
72
73
    /**
74
     * Configuration loader.
75
     *
76
     * @var \PPI\Framework\Config\ConfigManager
77
     */
78
    protected $configManager;
79
80
    /**
81
     * The Module Manager.
82
     *
83
     * @var \Zend\ModuleManager\ModuleManager
84
     */
85
    protected $moduleManager;
86
87
    /**
88
     * @param integer $errorReportingLevel The level of error reporting you want
89
     */
90
    protected $errorReportingLevel;
91
92
    /**
93
     * @var null|array
94
     */
95
    protected $matchedRoute;
96
97
    /**
98
     * @var \PPI\Framework\Module\Controller\ControllerResolver
99
     */
100
    protected $resolver;
101
102
    /**
103
     * @var string
104
     */
105
    protected $name;
106
107
    /**
108
     * Path to the application root dir aka the "app" directory.
109
     *
110
     * @var null|string
111
     */
112
    protected $rootDir;
113
114
    /**
115
     * Service Manager.
116
     *
117
     * @var \PPI\Framework\ServiceManager\ServiceManager
118
     */
119
    protected $serviceManager;
120
121
    /**
122
     * App constructor.
123
     *
124
     * @param array $options
125
     */
126
    public function __construct(array $options = array())
127
    {
128
        // Default options
129
        $this->environment = isset($options['environment']) && $options['environment'] ? (string)$options['environment'] : 'prod';
130
        $this->debug = isset($options['debug']) && null !== $options['debug'] ? (boolean)$options['debug'] : false;
131
        $this->rootDir = isset($options['rootDir']) && $options['rootDir'] ? (string)$options['rootDir'] : $this->getRootDir();
132
        $this->name = isset($options['name']) && $options['name'] ? (string)$options['name'] : $this->getName();
133
134
        if ($this->debug) {
135
            $this->startTime = microtime(true);
136
            $this->enableDebug();
137
        } else {
138
            ini_set('display_errors', 0);
139
        }
140
    }
141
142
    /**
143
     * Set an App option.
144
     *
145
     * @param $option
146
     * @param $value
147
     *
148
     * @throws \RuntimeException
149
     *
150
     * @return $this
151
     */
152
    public function setOption($option, $value)
153
    {
154
        if (true === $this->booted) {
155
            throw new \RuntimeException('Setting App options after boot() is now allowed');
156
        }
157
158
        // "root_dir" to "rootDir"
159
        $property = preg_replace('/_(.?)/e', "strtoupper('$1')", $option);
160
        if (!property_exists($this, $property)) {
161
            throw new \RuntimeException(sprintf('App property "%s" (option "%s") does not exist', $property, $option));
162
        }
163
164
        $this->$property = $value;
165
166
        return $this;
167
    }
168
169
    /**
170
     * Get an App option.
171
     *
172
     * @param $option
173
     *
174
     * @throws \RuntimeException
175
     *
176
     * @return string
177
     */
178
    public function getOption($option)
179
    {
180
        // "root_dir" to "rootDir"
181
        $property = preg_replace('/_(.?)/e', "strtoupper('$1')", $option);
182
        if (!property_exists($this, $property)) {
183
            throw new \RuntimeException(sprintf('App property "%s" (option "%s") does not exist', $property, $option));
184
        }
185
186
        return $property;
187
    }
188
189
    public function __clone()
190
    {
191
        if ($this->debug) {
192
            $this->startTime = microtime(true);
193
        }
194
195
        $this->booted = false;
196
        $this->serviceManager = null;
197
    }
198
199
    /**
200
     * Run the boot process, load our modules and their dependencies.
201
     *
202
     * This method is automatically called by dispatch(), but you can use it
203
     * to build all services when not handling a request.
204
     *
205
     * @return $this
206
     */
207
    public function boot()
208
    {
209
        if (true === $this->booted) {
210
            return $this;
211
        }
212
213
        $this->serviceManager = $this->buildServiceManager();
214
        $this->log('debug', sprintf('Booting %s ...', $this->name));
215
216
        // Loading our Modules
217
        $this->getModuleManager()->loadModules();
218
        if ($this->debug) {
219
            $modules = $this->getModuleManager()->getModules();
220
            $this->log('debug', sprintf('All modules online (%d): "%s"', count($modules), implode('", "', $modules)));
221
        }
222
223
        // Lets get all the services our of our modules and start setting them in the ServiceManager
224
        $moduleServices = $this->serviceManager->get('ModuleDefaultListener')->getServices();
225
        foreach ($moduleServices as $key => $service) {
226
            $this->serviceManager->setFactory($key, $service);
227
        }
228
229
        $this->booted = true;
230
        if ($this->debug) {
231
            $this->log('debug', sprintf('%s has booted (in %.3f secs)', $this->name, microtime(true) - $this->startTime));
232
        }
233
234
        return $this;
235
    }
236
237
    /**
238
     * Run the application and send the response.
239
     *
240
     * @param RequestInterface|null $request
241
     * @param ResponseInterface|null $response
242
     * @return Response
243
     * @throws \Exception
244
     */
245
    public function run(RequestInterface $request = null, ResponseInterface $response = null)
246
    {
247
        if (false === $this->booted) {
248
            $this->boot();
249
        }
250
251
        $request = $request ?: HttpRequest::createFromGlobals();
252
        $response = $response ?: new Response();
253
254
        $response = $this->dispatch($request, $response);
0 ignored issues
show
Bug introduced by
It seems like $request defined by $request ?: \PPI\Framewo...st::createFromGlobals() on line 251 can also be of type object<Symfony\Component\HttpFoundation\Request>; however, PPI\Framework\App::dispatch() does only seem to accept object<Psr\Http\Message\RequestInterface>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
255
        $response->send();
256
257
        return $response;
258
    }
259
260
    /**
261
     *
262
     * Decide on a route to use and dispatch our module's controller action.
263
     *
264
     * @param RequestInterface $request
265
     * @param ResponseInterface $response
266
     * @return Response
267
     * @throws \Exception
268
     */
269
    public function dispatch(RequestInterface $request, ResponseInterface $response)
270
    {
271
        if (false === $this->booted) {
272
            $this->boot();
273
        }
274
275
        // Routing
276
        $routeParams = $this->handleRouting($request);
277
        $request->attributes->add($routeParams);
0 ignored issues
show
Bug introduced by
Accessing attributes on the interface Psr\Http\Message\RequestInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
278
279
        // Resolve our Controller
280
        $resolver = $this->serviceManager->get('ControllerResolver');
281
        if (false === $controller = $resolver->getController($request)) {
282
            throw new NotFoundHttpException(sprintf('Unable to find the controller for path "%s".', $request->getPathInfo()));
283
        }
284
285
        $controllerArguments = $resolver->getArguments($request, $controller);
286
287
        $result = call_user_func_array(
288
            $controller,
289
            $controllerArguments
290
        );
291
292
        if($result === null) {
293
            throw new \Exception('Your action returned null. It must always return something');
294
        } else if(is_string($result)) {
295
            $response->setContent($result);
296
        } else if ($result instanceof ResponseInterface || $result instanceof SymfonyResponse) {
297
            $response = $result;
298
        } else {
299
            throw new \Exception('Invalid response type returned from controller');
300
        }
301
302
        $this->response = $response;
0 ignored issues
show
Bug introduced by
The property response does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
303
304
        return $response;
305
    }
306
307
    /**
308
     * Gets the name of the application.
309
     *
310
     * @return string The application name
311
     *
312
     * @api
313
     */
314
    public function getName()
315
    {
316
        if (null === $this->name) {
317
            $this->name = preg_replace('/[^a-zA-Z0-9_]+/', '', basename($this->rootDir));
318
        }
319
320
        return $this->name;
321
    }
322
323
    /**
324
     * Gets the version of the application.
325
     *
326
     * @return string The application version
327
     *
328
     * @api
329
     */
330
    public function getVersion()
331
    {
332
        return self::VERSION;
333
    }
334
335
    /**
336
     * Get the environment mode the application is in.
337
     *
338
     * @return string The current environment
339
     *
340
     * @api
341
     */
342
    public function getEnvironment()
343
    {
344
        return $this->environment;
345
    }
346
347
    /**
348
     * @param $env
349
     *
350
     * @return bool
351
     */
352
    public function isEnvironment($env)
353
    {
354
        if ('development' == $env) {
355
            $env = 'dev';
356
        } elseif ('production' == $env) {
357
            $env = 'prod';
358
        }
359
360
        return $this->getEnvironment() == $env;
361
    }
362
363
    /**
364
     * Checks if debug mode is enabled.
365
     *
366
     * @return boolean true if debug mode is enabled, false otherwise
367
     *
368
     * @api
369
     */
370
    public function isDebug()
371
    {
372
        return $this->debug;
373
    }
374
375
    /**
376
     * Gets the application root dir.
377
     *
378
     * @return string The application root dir
379
     *
380
     * @api
381
     */
382
    public function getRootDir()
383
    {
384
        if (null === $this->rootDir) {
385
            $this->rootDir = realpath(getcwd() . '/app');
386
        }
387
388
        return $this->rootDir;
389
    }
390
391
    /**
392
     * Get the service manager.
393
     *
394
     * @return ServiceManager\ServiceManager
395
     */
396
    public function getServiceManager()
397
    {
398
        return $this->serviceManager;
399
    }
400
401
    /**
402
     * @note Added for compatibility with Symfony's HttpKernel\Kernel.
403
     *
404
     * @return null|ServiceManager\ServiceManager
405
     */
406
    public function getContainer()
407
    {
408
        return $this->serviceManager;
409
    }
410
411
    /**
412
     * Returns the Module Manager.
413
     *
414
     * @return \Zend\ModuleManager\ModuleManager
415
     */
416
    public function getModuleManager()
417
    {
418
        if (null === $this->moduleManager) {
419
            $this->moduleManager = $this->serviceManager->get('ModuleManager');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->serviceManager->get('ModuleManager') can also be of type array. However, the property $moduleManager is declared as type object<Zend\ModuleManager\ModuleManager>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
420
        }
421
422
        return $this->moduleManager;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->moduleManager; of type object|array adds the type array to the return on line 422 which is incompatible with the return type documented by PPI\Framework\App::getModuleManager of type Zend\ModuleManager\ModuleManager.
Loading history...
423
    }
424
425
    /**
426
     * Get an array of the loaded modules.
427
     *
428
     * @return array An array of Module objects, keyed by module name
429
     */
430
    public function getModules()
431
    {
432
        return $this->getModuleManager()->getLoadedModules(true);
433
    }
434
435
    /**
436
     * @see PPI\Framework\Module\ModuleManager::locateResource()
437
     *
438
     * @param string $name A resource name to locate
439
     * @param string $dir A directory where to look for the resource first
440
     * @param Boolean $first Whether to return the first path or paths for all matching bundles
441
     *
442
     * @throws \InvalidArgumentException if the file cannot be found or the name is not valid
443
     * @throws \RuntimeException         if the name contains invalid/unsafe
444
     * @throws \RuntimeException         if a custom resource is hidden by a resource in a derived bundle
445
     *
446
     * @return string|array The absolute path of the resource or an array if $first is false
447
     */
448
    public function locateResource($name, $dir = null, $first = true)
449
    {
450
        return $this->getModuleManager()->locateResource($name, $dir, $first);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Zend\ModuleManager\ModuleManager as the method locateResource() does only exist in the following sub-classes of Zend\ModuleManager\ModuleManager: PPI\Framework\Module\ModuleManager. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
451
    }
452
453
    /**
454
     * Gets the request start time (not available if debug is disabled).
455
     *
456
     * @return integer The request start timestamp
457
     *
458
     * @api
459
     */
460
    public function getStartTime()
461
    {
462
        return $this->debug ? $this->startTime : -INF;
463
    }
464
465
    /**
466
     * Gets the cache directory.
467
     *
468
     * @return string The cache directory
469
     *
470
     * @api
471
     */
472
    public function getCacheDir()
473
    {
474
        return $this->rootDir . '/cache/' . $this->environment;
475
    }
476
477
    /**
478
     * Gets the log directory.
479
     *
480
     * @return string The log directory
481
     *
482
     * @api
483
     */
484
    public function getLogDir()
485
    {
486
        return $this->rootDir . '/logs';
487
    }
488
489
    /**
490
     * Gets the charset of the application.
491
     *
492
     * @return string The charset
493
     *
494
     * @api
495
     */
496
    public function getCharset()
497
    {
498
        return 'UTF-8';
499
    }
500
501
    /**
502
     * Returns a ConfigManager instance.
503
     *
504
     * @return \PPI\Framework\Config\ConfigManager
505
     */
506
    public function getConfigManager()
507
    {
508
        if (null === $this->configManager) {
509
            $cachePath = $this->getCacheDir() . '/application-config-cache.' . $this->getName() . '.php';
510
            $this->configManager = new ConfigManager($cachePath, !$this->debug, $this->rootDir . '/config');
511
        }
512
513
        return $this->configManager;
514
    }
515
516
    /**
517
     * Loads a configuration file or PHP array.
518
     *
519
     * @param  $resource
520
     * @param null $type
521
     *
522
     * @return App The current instance
523
     */
524
    public function loadConfig($resource, $type = null)
525
    {
526
        $this->getConfigManager()->addConfig($resource, $type);
527
528
        return $this;
529
    }
530
531
    /**
532
     * Returns the application configuration.
533
     *
534
     * @throws \RuntimeException
535
     *
536
     * @return array|object
537
     */
538
    public function getConfig()
539
    {
540
        if (!$this->booted) {
541
            throw new \RuntimeException('The "Config" service is only available after the App boot()');
542
        }
543
544
        return $this->serviceManager->get('Config');
545
    }
546
547
    public function serialize()
548
    {
549
        return serialize(array($this->environment, $this->debug));
550
    }
551
552
    public function unserialize($data)
553
    {
554
        list($environment, $debug) = unserialize($data);
555
556
        $this->__construct($environment, $debug);
0 ignored issues
show
Unused Code introduced by
The call to App::__construct() has too many arguments starting with $debug.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
557
    }
558
559
    /**
560
     * Returns the application parameters.
561
     *
562
     * @return array An array of application parameters
563
     */
564
    protected function getAppParameters()
565
    {
566
        return array_merge(
567
            array(
568
                'app.root_dir' => $this->rootDir,
569
                'app.environment' => $this->environment,
570
                'app.debug' => $this->debug,
571
                'app.name' => $this->name,
572
                'app.cache_dir' => $this->getCacheDir(),
573
                'app.logs_dir' => $this->getLogDir(),
574
                'app.charset' => $this->getCharset(),
575
            ),
576
            $this->getEnvParameters()
577
        );
578
    }
579
580
    /**
581
     * Gets the environment parameters.
582
     *
583
     * Only the parameters starting with "PPI__" are considered.
584
     *
585
     * @return array An array of parameters
586
     */
587
    protected function getEnvParameters()
0 ignored issues
show
Coding Style introduced by
getEnvParameters uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
588
    {
589
        $parameters = array();
590
        foreach ($_SERVER as $key => $value) {
591
            if (0 === strpos($key, 'PPI__')) {
592
                $parameters[strtolower(str_replace('__', '.', substr($key, 5)))] = $value;
593
            }
594
        }
595
596
        return $parameters;
597
    }
598
599
    /**
600
     * Creates and initializes a ServiceManager instance.
601
     *
602
     * @return ServiceManager The compiled service manager
603
     */
604
    protected function buildServiceManager()
605
    {
606
        // ServiceManager creation
607
        $serviceManager = new ServiceManagerBuilder($this->getConfigManager()->getMergedConfig());
608
        $serviceManager->build($this->getAppParameters());
609
        $serviceManager->set('app', $this);
610
611
        return $serviceManager;
612
    }
613
614
    /**
615
     *
616
     * Perform the matching of a route and return a set of routing parameters if a valid one is found.
617
     * Otherwise exceptions get thrown
618
     *
619
     * @param RequestInterface $request
620
     * @return array
621
     *
622
     * @throws \Exception
623
     */
624
    protected function handleRouting(RequestInterface $request)
625
    {
626
        $this->router = $this->serviceManager->get('Router');
0 ignored issues
show
Bug introduced by
The property router does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
627
        $this->router->warmUp($this->getCacheDir());
628
629
        try {
630
            // Lets load up our router and match the appropriate route
631
            $parameters = $this->router->matchRequest($request);
632 View Code Duplication
            if (!empty($parameters)) {
0 ignored issues
show
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...
633
                if (null !== $this->logger) {
634
                    $this->logger->info(sprintf('Matched route "%s" (parameters: %s)', $parameters['_route'], $this->router->parametersToString($parameters)));
635
                }
636
            }
637
        } catch (ResourceNotFoundException $e) {
638
639
            $routeUri = $this->router->generate('Framework_404');
640
            $parameters = $this->router->matchRequest($request::create($routeUri));
641
642
        } catch (\Exception $e) {
643
            throw $e;
644
        }
645
646
        $parameters['_route_params'] = $parameters;
647
        return $parameters;
648
    }
649
650
    /**
651
     * Logs with an arbitrary level.
652
     *
653
     * @param mixed $level
654
     * @param string $message
655
     * @param array $context
656
     */
657
    protected function log($level, $message, array $context = array())
658
    {
659
        if (null === $this->logger && $this->getServiceManager()->has('logger')) {
660
            $this->logger = $this->getServiceManager()->get('logger');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getServiceManager()->get('logger') can also be of type array. However, the property $logger is declared as type object<Psr\Log\LoggerInterface>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
661
        }
662
663
        if ($this->logger) {
664
            $this->logger->log($level, $message, $context);
665
        }
666
    }
667
668
    /**
669
     * Enables the debug tools.
670
     *
671
     * This method registers an error handler and an exception handler.
672
     *
673
     * If the Symfony ClassLoader component is available, a special
674
     * class loader is also registered.
675
     */
676
    protected function enableDebug()
677
    {
678
        error_reporting(-1);
679
680
        ErrorHandler::register($this->errorReportingLevel);
681
        if ('cli' !== php_sapi_name()) {
682
            $handler = ExceptionHandler::register();
683
            $handler->setAppVersion($this->getVersion());
684
        } elseif (!ini_get('log_errors') || ini_get('error_log')) {
685
            ini_set('display_errors', 1);
686
        }
687
688
        if (class_exists('Symfony\Component\ClassLoader\DebugClassLoader')) {
689
            DebugClassLoader::enable();
690
        }
691
    }
692
}
693