Completed
Push — di ( eef893...256938 )
by Tim
06:01
created

ControllerServlet::getPathDescriptors()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
/**
4
 * AppserverIo\Routlt\ControllerServlet
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Tim Wagner <[email protected]>
15
 * @copyright 2015 TechDivision GmbH <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      http://github.com/appserver-io/routlt
18
 * @link      http://www.appserver.io
19
 */
20
21
namespace AppserverIo\Routlt;
22
23
use AppserverIo\Http\HttpProtocol;
24
use AppserverIo\Properties\Properties;
25
use AppserverIo\Psr\Di\ProviderInterface;
26
use AppserverIo\Psr\Di\ObjectManagerInterface;
27
use AppserverIo\Psr\Servlet\Http\HttpServlet;
28
use AppserverIo\Psr\Servlet\ServletException;
29
use AppserverIo\Psr\Servlet\ServletConfigInterface;
30
use AppserverIo\Psr\Servlet\ServletRequestInterface;
31
use AppserverIo\Psr\Servlet\ServletResponseInterface;
32
use AppserverIo\Routlt\Util\ContextKeys;
33
use AppserverIo\Routlt\Util\ActionAware;
34
use AppserverIo\Routlt\Util\ServletContextAware;
35
use AppserverIo\Routlt\Results\ResultInterface;
36
use AppserverIo\Routlt\Description\PathDescriptorInterface;
37
use AppserverIo\Routlt\Description\ResultDescriptorInterface;
38
39
/**
40
 * Abstract example implementation that provides some kind of basic MVC functionality
41
 * to handle requests by subclasses action methods.
42
 *
43
 * @author    Tim Wagner <[email protected]>
44
 * @copyright 2015 TechDivision GmbH <[email protected]>
45
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
46
 * @link      http://github.com/appserver-io/routlt
47
 * @link      http://www.appserver.io
48
 */
49
class ControllerServlet extends HttpServlet implements ControllerInterface
50
{
51
52
    /**
53
     * The key for the init parameter with the action namespace.
54
     *
55
     * @var string
56
     */
57
    const INIT_PARAMETER_ACTION_NAMESPACE = 'action.namespace';
58
59
    /**
60
     * The key for the init parameter with the path to the configuration file.
61
     *
62
     * @var string
63
     */
64
    const INIT_PARAMETER_ROUTLT_CONFIGURATION_FILE = 'routlt.configuration.file';
65
66
    /**
67
     * The default action if no valid action name was found in the path info.
68
     *
69
     * @var string
70
     */
71
    const DEFAULT_ROUTE = '/index';
72
73
    /**
74
     * The array with the initialized routes.
75
     *
76
     * @var array
77
     */
78
    protected $routes = array();
79
80
    /**
81
     * The array with request method action -> route mappings.
82
     *
83
     * @var array
84
     */
85
    protected $actionMappings = array();
86
87
    /**
88
     * Initializes the servlet with the passed configuration.
89
     *
90
     * @param \AppserverIo\Psr\Servlet\ServletConfigInterface $config The configuration to initialize the servlet with
91
     *
92
     * @return void
93
     */
94
    public function init(ServletConfigInterface $config)
95
    {
96
97
        // call parent method
98
        parent::init($config);
99
100
        // load the values from the configuration file
101
        $this->initConfiguration();
102
103
        // initialize the routing
104
        $this->initRoutes();
105
    }
106
107
    /**
108
     * Returns the available routes.
109
     *
110
     * @return array The array with the available routes
111
     */
112
    public function getRoutes()
113
    {
114
        return $this->routes;
115
    }
116
117
    /**
118
     * Returns the array with request method action -> route mappings.
119
     *
120
     * @return array The request method action -> route mappings
121
     */
122
    public function getActionMappings()
123
    {
124
        return $this->actionMappings;
125
    }
126
127
    /**
128
     * Returns the naming directoy instance (the application).
129
     *
130
     * @return \AppserverIo\Psr\Naming\NamingDirectoryInterface The naming directory instance
131
     */
132
    public function getNamingDirectory()
133
    {
134
        return $this->getServletContext()->getApplication();
0 ignored issues
show
Bug introduced by
The method getApplication() does not seem to exist on object<AppserverIo\Psr\S...ervletContextInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
135
    }
136
137
    /**
138
     * Returns the object manager instance
139
     *
140
     * @return \AppserverIo\Psr\Di\ObjectManagerInterface The object manager instance
141
     */
142
    public function getObjectManager()
143
    {
144
        return $this->getNamingDirectory()->search(ObjectManagerInterface::IDENTIFIER);
145
    }
146
147
    /**
148
     * Returns the DI provider instance.
149
     *
150
     * @return \AppserverIo\Psr\Di\ProviderInterface The DI provider instance
151
     */
152
    public function getProvider()
153
    {
154
        return $this->getNamingDirectory()->search(ProviderInterface::IDENTIFIER);
155
    }
156
157
    /**
158
     * This method returns the default route we'll invoke if the path info doesn't contain one.
159
     *
160
     * @return string The default route
161
     */
162
    public function getDefaultRoute()
163
    {
164
        return ControllerServlet::DEFAULT_ROUTE;
165
    }
166
167
    /**
168
     * Returns the array with the path descriptors.
169
     *
170
     * @return array The array with the path descriptors
171
     */
172
    public function getPathDescriptors()
173
    {
174
        return $this->paths;
0 ignored issues
show
Bug introduced by
The property paths 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...
175
    }
176
177
    /**
178
     * Adds a path descriptor to the controller.
179
     *
180
     * @param \AppserverIo\Routlt\Description\PathDescriptorInterface $pathDescriptor The path descriptor to add
181
     *
182
     * @return void
183
     */
184
    public function addPathDescriptor(PathDescriptorInterface $pathDescriptor)
185
    {
186
        $this->paths[] = $pathDescriptor;
187
    }
188
189
    /**
190
     * Loads the values found in the configuration file and merges
191
     * them with the servlet context initialization parameters.
192
     *
193
     * @return void
194
     */
195
    protected function initConfiguration()
196
    {
197
198
        // load the relative path to the Routlt configuration file
199
        $configurationFileName = $this->getInitParameter(ControllerServlet::INIT_PARAMETER_ROUTLT_CONFIGURATION_FILE);
200
201
        // load the path to the configuration file
202
        $configurationFile = $this->getServletConfig()->getWebappPath() . DIRECTORY_SEPARATOR . ltrim($configurationFileName, '/');
203
204
        // if the file is readable
205
        if (is_file($configurationFile) && is_readable($configurationFile)) {
206
            // load the  properties from the file
207
            $properties = new Properties();
208
            $properties->load($configurationFile);
209
210
            // append the properties to the servlet context
211
            foreach ($properties as $paramName => $paramValue) {
212
                $this->getServletContext()->addInitParameter($paramName, $paramValue);
213
            }
214
        }
215
    }
216
217
    /**
218
     * Initializes the available routes.
219
     *
220
     * @return void
221
     */
222
    protected function initRoutes()
223
    {
224
225
        // load the action namespace
226
        $actionNamespace = strtolower($this->getInitParameter(ControllerServlet::INIT_PARAMETER_ACTION_NAMESPACE));
227
228
        // register the beans located by annotations and the XML configuration
229
        foreach ($this->getObjectManager()->getObjectDescriptors() as $descriptor) {
230
            // check if we've found a servlet descriptor
231
            if ($descriptor instanceof PathDescriptorInterface) {
232
                // register the action's references
233
                $this->getServletContext()->registerReferences($descriptor);
0 ignored issues
show
Bug introduced by
The method registerReferences() does not seem to exist on object<AppserverIo\Psr\S...ervletContextInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
234
235
                // initialize a new action instance
236
                $action = $this->initActionInstance($descriptor);
237
238
                // prepare the action's the result descriptors
239
                /** @var \AppserverIo\Routlt\Description\ResultDescriptorInterface $resultDescriptor */
240
                foreach ($descriptor->getResults() as $resultDescriptor) {
241
                    $action->addResult($this->initResultInstance($resultDescriptor));
242
                }
243
244
                // prepare the route, e. g. /index/index
245
                $controllerName = str_replace($actionNamespace, '', $descriptor->getName());
246
247
                // initialize the action mappings
248
                foreach ($descriptor->getActions() as $actionDescriptors) {
249
                    // iterate over all request methods
250
                    /** @var \AppserverIo\Routlt\Description\ActionDescriptorInterface $actionDescriptor */
251
                    foreach ($actionDescriptors as $requestMethod => $actionDescriptor) {
252
                        // prepare the real action method name
253
                        $methodName = $actionDescriptor->getMethodName();
254
                        // prepare the action path -> concatenate route + action name
255
                        $actionPath = sprintf('%s%s', $controllerName, $actionDescriptor->getName());
256
257
                        // initialize the action mapping for the actual route
258
                        $actionMapping = new ActionMapping();
259
                        $actionMapping->setControllerName($controllerName);
260
                        $actionMapping->setMethodName($methodName);
261
                        $actionMapping->compile(
262
                            $actionPath,
263
                            $actionDescriptor->getRestrictions(),
264
                            $actionDescriptor->getDefaults()
265
                        );
266
267
                        // add the action path -> route mapping for the request method
268
                        $this->actionMappings[$requestMethod][$actionPath] = $actionMapping;
269
270
                        // add an alias for the route for the action's default method
271
                        if ($actionDescriptor->getMethodName() === $action->getDefaultMethod()) {
272
                            // initialize the action mapping for the default route
273
                            $actionMapping = new ActionMapping();
274
                            $actionMapping->setControllerName($controllerName);
275
                            $actionMapping->setMethodName($methodName);
276
                            $actionMapping->compile(
277
                                $controllerName,
278
                                $actionDescriptor->getRestrictions(),
279
                                $actionDescriptor->getDefaults()
280
                            );
281
                            // add the action mapping for the default route
282
                            $this->actionMappings[$requestMethod][$controllerName] = $actionMapping;
283
                        }
284
                    }
285
                }
286
287
                // add the initialized action
288
                $this->routes[$controllerName] = $action;
289
            }
290
        }
291
    }
292
293
    /**
294
     * Creates a new instance of the action from the passed path descriptor instance.
295
     *
296
     * @param \AppserverIo\Routlt\Description\PathDescriptorInterface $pathDescriptor The path descriptor to create the action from
297
     *
298
     * @return \AppserverIo\Routlt\ActionInterface The action instance
299
     */
300
    protected function initActionInstance(PathDescriptorInterface $pathDescriptor)
301
    {
302
303
        // create a new action instance
304
        $actionInstance = $this->getProvider()->get($pathDescriptor->getClassName());
305
306
        // if the action is servlet context aware
307
        if ($actionInstance instanceof ServletContextAware) {
308
            $actionInstance->setServletContext($this->getServletContext());
309
        }
310
311
        // return the action instance
312
        return $actionInstance;
313
    }
314
315
    /**
316
     * Creates a new instance of the action result the passed descriptor.
317
     *
318
     * @param \AppserverIo\Routlt\Description\ResultDescriptorInterface $resultDescriptor The action result descriptor
319
     *
320
     * @return \AppserverIo\Routlt\Results\ResultInterface The result instance
321
     */
322
    protected function initResultInstance(ResultDescriptorInterface $resultDescriptor)
323
    {
324
325
        // create the result instance
326
        $resultInstance = $this->getProvider()->get($resultDescriptor->getType());
327
328
        // initialize the instance from the result descriptor
329
        $resultInstance->init($resultDescriptor);
330
331
        // if the result is servlet context aware
332
        if ($resultInstance instanceof ServletContextAware) {
333
            $resultInstance->setServletContext($this->getServletContext());
334
        }
335
336
        // return the result instance
337
        return $resultInstance;
338
    }
339
340
    /**
341
     * Checks whether or not an action is generally available for any request method.
342
     * Will return TRUE if so, FALSE otherwise.
343
     * This method replicates a lot of the checks generally necessary but omits the request method check.
344
     * Still best called in exception- or edge-cases
345
     *
346
     * @param string $pathInfo The action path which has been requested
347
     *
348
     * @return boolean
349
     */
350
    public function checkGeneralActionAvailability($pathInfo)
351
    {
352
353
        // iterate the request methods we have mappings for and check if we can find the requested action
354
        foreach ($this->getActionMappings() as $actionMapping) {
355
            $run = true;
356
            $requestedAction = $pathInfo;
357
            do {
358
                if (isset($actionMapping[$requestedAction])) {
359
                    return true;
360
                }
361
                // strip the last directory
362
                $requestedAction = dirname($requestedAction);
363
364
                // query whether we've to stop dispatching
365
                if ($requestedAction === '/' || $requestedAction === false) {
366
                    $run = false;
367
                }
368
            } while ($run === true);
369
        }
370
371
        // nothing found? Return false then
372
        return false;
373
    }
374
375
    /**
376
     * Returns the array with request method action -> route mappings
377
     * for the passed servlet request.
378
     *
379
     * @param \AppserverIo\Psr\Servlet\ServletRequestInterface $servletRequest The request instance
380
     *
381
     * @return array The request method action -> route mappings for the passed request method
382
     */
383
    public function getActionMappingsForServletRequest(ServletRequestInterface $servletRequest)
384
    {
385
        // load the servlet request method
386
        $requestMethod = $servletRequest->getMethod();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface AppserverIo\Psr\Servlet\ServletRequestInterface as the method getMethod() does only exist in the following implementations of said interface: AppserverIo\Psr\Servlet\...tpServletRequestWrapper.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
387
388
        // load the action mappings
389
        $actionMappings = $this->getActionMappings();
390
391
        // query whether we've action mappings for the request method or not
392
        if (isset($actionMappings[$requestMethod])) {
393
            return $actionMappings[$requestMethod];
394
        }
395
396
        // nothing found? Method must not be allowed then
397
        throw new DispatchException(sprintf('Method %s not allowed', $requestMethod), 405);
398
    }
399
400
    /**
401
     * Delegates to HTTP method specific functions like doPost() for POST e.g.
402
     *
403
     * @param \AppserverIo\Psr\Servlet\ServletRequestInterface  $servletRequest  The request instance
404
     * @param \AppserverIo\Psr\Servlet\ServletResponseInterface $servletResponse The response sent back to the client
405
     *
406
     * @return void
407
     *
408
     * @throws \AppserverIo\Psr\Servlet\ServletException If no action has been found for the requested path
409
     */
410
    public function service(ServletRequestInterface $servletRequest, ServletResponseInterface $servletResponse)
411
    {
412
413
        try {
414
            // pre-initialize response
415
            $servletResponse->addHeader(HttpProtocol::HEADER_X_POWERED_BY, get_class($this));
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface AppserverIo\Psr\Servlet\ServletResponseInterface as the method addHeader() does only exist in the following implementations of said interface: AppserverIo\Psr\Servlet\...pServletResponseWrapper.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
416
417
            // load the path info from the servlet request
418
            $pathInfo = $servletRequest->getPathInfo();
419
420
            // if the requested action has been found in the path info
421
            if ($pathInfo == null) {
422
                $pathInfo = $this->getDefaultRoute();
423
            }
424
425
            // prepare the path of the requested action
426
            $requestedAction = $pathInfo;
427
428
            // load the routes
429
            $routes = $this->getRoutes();
430
431
            // load the action mappings for the actual servlet request
432
            $actionMappings = $this->getActionMappingsForServletRequest($servletRequest);
433
434
            // initialize the parameter map with the values from the request
435
            if ($servletRequest->getParameterMap()) {
436
                $parameterMap = $servletRequest->getParameterMap();
437
            } else {
438
                $parameterMap = array();
439
            }
440
441
            // iterate over the action mappings and try to find a mapping
442
            foreach ($actionMappings as $actionMapping) {
443
                // try to match actual request by the tokenizer
444
                if ($actionMapping->match($requestedAction)) {
445
                    // initialize the request attributes with the values from the action mapping
446
                    $servletRequest->setParameterMap(
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface AppserverIo\Psr\Servlet\ServletRequestInterface as the method setParameterMap() does only exist in the following implementations of said interface: AppserverIo\Psr\Servlet\...tpServletRequestWrapper.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
447
                        array_merge($parameterMap, $actionMapping->getRequestParameters())
448
                    );
449
450
                    // resolve the action with the found mapping
451
                    $action = $routes[$actionMapping->getControllerName()];
452
453
                    // set the method that has to be invoked in the action context
454
                    $action->setAttribute(ContextKeys::METHOD_NAME, $actionMapping->getMethodName());
455
456
                    // pre-dispatch the action
457
                    $action->preDispatch($servletRequest, $servletResponse);
458
459
                    // if the action has been dispatched, we're done
460
                    if ($servletRequest->isDispatched()) {
461
                        return;
462
                    }
463
464
                    // initialize the result with the default value
465
                    $result = ActionInterface::INPUT;
466
467
                    // if not dispatch the action
468
                    if ($newResult = $action->perform($servletRequest, $servletResponse)) {
469
                        $result = $newResult;
470
                    }
471
472
                    // process the result if available
473
                    if (($instance = $action->findResult($result)) instanceof ResultInterface) {
474
                        // query whether or not the result is action aware
475
                        if ($instance instanceof ActionAware) {
476
                            $instance->setAction($action);
477
                        }
478
479
                        // process the result
480
                        $instance->process($servletRequest, $servletResponse);
481
                    }
482
483
                    // post-dispatch the action instance
484
                    $action->postDispatch($servletRequest, $servletResponse);
485
486
                    // stop processing
487
                    return;
488
                }
489
            }
490
491
            // We did not find anything for this method/URI connection. We have to evaluate if there simply
492
            // is a method restriction. This replicates a lot of the checks we did before but omits extra
493
            // iterations in a positive dispatch event, 4xx's should be the exception and can handle that
494
            // penalty therefore
495
            if ($this->checkGeneralActionAvailability($pathInfo)) {
496
                // nothing found? Method must not be allowed then
497
                throw new DispatchException(sprintf('Method %s not allowed', $servletRequest->getMethod()), 405);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface AppserverIo\Psr\Servlet\ServletRequestInterface as the method getMethod() does only exist in the following implementations of said interface: AppserverIo\Psr\Servlet\...tpServletRequestWrapper.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
498
            }
499
500
            // throw an action, because we can't find an action mapping
501
            throw new DispatchException(sprintf('Can\'t find action to dispatch path info %s', $pathInfo), 404);
502
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
503
        } catch (DispatchException $de) {
504
            // results in a 4xx error
505
            throw new ServletException($de->__toString(), $de->getCode());
506
        } catch (\Exception $e) {
507
            // results in a 500 error page
508
            throw new ServletException($e->__toString(), 500);
509
        }
510
    }
511
}
512