Completed
Push — master ( d8f31a...9a22d0 )
by Vítor
07:41
created

App::getVersion()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
Bug introduced by
It seems like $response defined by $this->dispatch($request, $response) on line 255 can be null; however, PPI\Framework\App::dispatch() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
256
        $response->send();
257
258
        return $response;
259
    }
260
261
    /**
262
     * Decide on a route to use and dispatch our module's controller action.
263
     *
264
     * @param HttpRequest  $request
265
     * @param HttpResponse $response
266
     *
267
     * @throws \Exception
268
     *
269
     * @return HttpResponse
270
     */
271
    public function dispatch(HttpRequest $request, HttpResponse $response)
272
    {
273
        if (false === $this->booted) {
274
            $this->boot();
275
        }
276
277
        // Routing
278
        $routeParams = $this->handleRouting($request);
279
        $request->attributes->add($routeParams);
280
281
        // Resolve our Controller
282
        $resolver = $this->serviceManager->get('ControllerResolver');
283
        if (false === $controller = $resolver->getController($request)) {
284
            throw new NotFoundHttpException(sprintf('Unable to find the controller for path "%s".', $request->getPathInfo()));
285
        }
286
287
        $controllerArguments = $resolver->getArguments($request, $controller);
288
289
        $result = call_user_func_array(
290
            $controller,
291
            $controllerArguments
292
        );
293
294
        if ($result === null) {
295
            throw new \Exception('Your action returned null. It must always return something');
296
        } elseif (is_string($result)) {
297
            $response->setContent($result);
298
        } elseif ($result instanceof SymfonyResponse || $response instanceof HttpResponse) {
299
            $response = $result;
300
        } else {
301
            throw new \Exception('Invalid response type returned from controller');
302
        }
303
304
        $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...
305
306
        return $response;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $response; (object|integer|double|null|array|boolean) is incompatible with the return type documented by PPI\Framework\App::dispatch of type PPI\Framework\Http\Response|null.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
307
    }
308
309
    /**
310
     * Gets the name of the application.
311
     *
312
     * @return string The application name
313
     *
314
     * @api
315
     */
316
    public function getName()
317
    {
318
        if (null === $this->name) {
319
            $this->name = preg_replace('/[^a-zA-Z0-9_]+/', '', basename($this->rootDir));
320
        }
321
322
        return $this->name;
323
    }
324
325
    /**
326
     * Gets the version of the application.
327
     *
328
     * @return string The application version
329
     *
330
     * @api
331
     */
332
    public function getVersion()
333
    {
334
        return self::VERSION;
335
    }
336
337
    /**
338
     * Get the environment mode the application is in.
339
     *
340
     * @return string The current environment
341
     *
342
     * @api
343
     */
344
    public function getEnvironment()
345
    {
346
        return $this->environment;
347
    }
348
349
    /**
350
     * @param $env
351
     *
352
     * @return bool
353
     */
354
    public function isEnvironment($env)
355
    {
356
        if ('development' == $env) {
357
            $env = 'dev';
358
        } elseif ('production' == $env) {
359
            $env = 'prod';
360
        }
361
362
        return $this->getEnvironment() == $env;
363
    }
364
365
    /**
366
     * Checks if debug mode is enabled.
367
     *
368
     * @return bool true if debug mode is enabled, false otherwise
369
     *
370
     * @api
371
     */
372
    public function isDebug()
373
    {
374
        return $this->debug;
375
    }
376
377
    /**
378
     * Gets the application root dir.
379
     *
380
     * @return string The application root dir
381
     *
382
     * @api
383
     */
384
    public function getRootDir()
385
    {
386
        if (null === $this->rootDir) {
387
            $this->rootDir = realpath(getcwd() . '/app');
388
        }
389
390
        return $this->rootDir;
391
    }
392
393
    /**
394
     * Get the service manager.
395
     *
396
     * @return ServiceManager
397
     */
398
    public function getServiceManager()
399
    {
400
        return $this->serviceManager;
401
    }
402
403
    /**
404
     * @note Added for compatibility with Symfony's HttpKernel\Kernel.
405
     *
406
     * @return null|ServiceManager
407
     */
408
    public function getContainer()
409
    {
410
        return $this->serviceManager;
411
    }
412
413
    /**
414
     * Returns the Module Manager.
415
     *
416
     * @return \Zend\ModuleManager\ModuleManager
417
     */
418
    public function getModuleManager()
419
    {
420
        if (null === $this->moduleManager) {
421
            $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...
422
        }
423
424
        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 424 which is incompatible with the return type documented by PPI\Framework\App::getModuleManager of type Zend\ModuleManager\ModuleManager.
Loading history...
425
    }
426
427
    /**
428
     * Get an array of the loaded modules.
429
     *
430
     * @return array An array of Module objects, keyed by module name
431
     */
432
    public function getModules()
433
    {
434
        return $this->getModuleManager()->getLoadedModules(true);
435
    }
436
437
    /**
438
     * @see PPI\Framework\Module\ModuleManager::locateResource()
439
     *
440
     * @param string $name  A resource name to locate
441
     * @param string $dir   A directory where to look for the resource first
442
     * @param bool   $first Whether to return the first path or paths for all matching bundles
443
     *
444
     * @throws \InvalidArgumentException if the file cannot be found or the name is not valid
445
     * @throws \RuntimeException         if the name contains invalid/unsafe
446
     * @throws \RuntimeException         if a custom resource is hidden by a resource in a derived bundle
447
     *
448
     * @return string|array The absolute path of the resource or an array if $first is false
449
     */
450
    public function locateResource($name, $dir = null, $first = true)
451
    {
452
        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...
453
    }
454
455
    /**
456
     * Gets the request start time (not available if debug is disabled).
457
     *
458
     * @return int The request start timestamp
459
     *
460
     * @api
461
     */
462
    public function getStartTime()
463
    {
464
        return $this->debug ? $this->startTime : -INF;
465
    }
466
467
    /**
468
     * Gets the cache directory.
469
     *
470
     * @return string The cache directory
471
     *
472
     * @api
473
     */
474
    public function getCacheDir()
475
    {
476
        return $this->rootDir . '/cache/' . $this->environment;
477
    }
478
479
    /**
480
     * Gets the log directory.
481
     *
482
     * @return string The log directory
483
     *
484
     * @api
485
     */
486
    public function getLogDir()
487
    {
488
        return $this->rootDir . '/logs';
489
    }
490
491
    /**
492
     * Gets the charset of the application.
493
     *
494
     * @return string The charset
495
     *
496
     * @api
497
     */
498
    public function getCharset()
499
    {
500
        return 'UTF-8';
501
    }
502
503
    /**
504
     * Returns a ConfigManager instance.
505
     *
506
     * @return \PPI\Framework\Config\ConfigManager
507
     */
508
    public function getConfigManager()
509
    {
510
        if (null === $this->configManager) {
511
            $cachePath = $this->getCacheDir() . '/application-config-cache.' . $this->getName() . '.php';
512
            $this->configManager = new ConfigManager($cachePath, !$this->debug, $this->rootDir . '/config');
513
        }
514
515
        return $this->configManager;
516
    }
517
518
    /**
519
     * Loads a configuration file or PHP array.
520
     *
521
     * @param  $resource
522
     * @param null $type
523
     *
524
     * @return App The current instance
525
     */
526
    public function loadConfig($resource, $type = null)
527
    {
528
        $this->getConfigManager()->addConfig($resource, $type);
529
530
        return $this;
531
    }
532
533
    /**
534
     * Returns the application configuration.
535
     *
536
     * @throws \RuntimeException
537
     *
538
     * @return array|object
539
     */
540
    public function getConfig()
541
    {
542
        if (!$this->booted) {
543
            throw new \RuntimeException('The "Config" service is only available after the App boot()');
544
        }
545
546
        return $this->serviceManager->get('Config');
547
    }
548
549
    public function serialize()
550
    {
551
        return serialize(array($this->environment, $this->debug));
552
    }
553
554
    public function unserialize($data)
555
    {
556
        list($environment, $debug) = unserialize($data);
557
558
        $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...
559
    }
560
561
    /**
562
     * Returns the application parameters.
563
     *
564
     * @return array An array of application parameters
565
     */
566
    protected function getAppParameters()
567
    {
568
        return array_merge(
569
            array(
570
                'app.root_dir' => $this->rootDir,
571
                'app.environment' => $this->environment,
572
                'app.debug' => $this->debug,
573
                'app.name' => $this->name,
574
                'app.cache_dir' => $this->getCacheDir(),
575
                'app.logs_dir' => $this->getLogDir(),
576
                'app.charset' => $this->getCharset(),
577
            ),
578
            $this->getEnvParameters()
579
        );
580
    }
581
582
    /**
583
     * Gets the environment parameters.
584
     *
585
     * Only the parameters starting with "PPI__" are considered.
586
     *
587
     * @return array An array of parameters
588
     */
589
    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...
590
    {
591
        $parameters = array();
592
        foreach ($_SERVER as $key => $value) {
593
            if (0 === strpos($key, 'PPI__')) {
594
                $parameters[strtolower(str_replace('__', '.', substr($key, 5)))] = $value;
595
            }
596
        }
597
598
        return $parameters;
599
    }
600
601
    /**
602
     * Creates and initializes a ServiceManager instance.
603
     *
604
     * @return ServiceManager The compiled service manager
605
     */
606
    protected function buildServiceManager()
607
    {
608
        // ServiceManager creation
609
        $serviceManager = new ServiceManagerBuilder($this->getConfigManager()->getMergedConfig());
610
        $serviceManager->build($this->getAppParameters());
611
        $serviceManager->set('app', $this);
612
613
        return $serviceManager;
614
    }
615
616
    /**
617
     * Perform the matching of a route and return a set of routing parameters if a valid one is found.
618
     * Otherwise exceptions get thrown.
619
     *
620
     * @param HttpRequest $request
621
     *
622
     * @throws \Exception
623
     *
624
     * @return array
625
     */
626
    protected function handleRouting(HttpRequest $request)
627
    {
628
        $this->router = $this->serviceManager->get('Router');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->serviceManager->get('Router') can also be of type array. However, the property $router is declared as type object<PPI\Framework\Router\ChainRouter>. 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...
629
        $this->router->warmUp($this->getCacheDir());
630
631
        try {
632
            // Lets load up our router and match the appropriate route
633
            $parameters = $this->router->matchRequest($request);
634 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...
635
                if (null !== $this->logger) {
636
                    $this->logger->info(sprintf('Matched route "%s" (parameters: %s)', $parameters['_route'], $this->router->parametersToString($parameters)));
637
                }
638
            }
639
        } catch (ResourceNotFoundException $e) {
640
            $routeUri = $this->router->generate('Framework_404');
641
            $parameters = $this->router->matchRequest($request::create($routeUri));
642
        } catch (\Exception $e) {
643
            throw $e;
644
        }
645
646
        $parameters['_route_params'] = $parameters;
647
648
        return $parameters;
649
    }
650
651
    /**
652
     * Logs with an arbitrary level.
653
     *
654
     * @param mixed  $level
655
     * @param string $message
656
     * @param array  $context
657
     */
658
    protected function log($level, $message, array $context = array())
659
    {
660
        if (null === $this->logger && $this->getServiceManager()->has('logger')) {
661
            $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...
662
        }
663
664
        if ($this->logger) {
665
            $this->logger->log($level, $message, $context);
666
        }
667
    }
668
}
669