Issues (44)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/AppserverIo/Routlt/ControllerServlet.php (9 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
use AppserverIo\Routlt\Util\DescriptorAware;
39
use AppserverIo\Routlt\Description\ResultConfigurationDescriptorInterface;
40
41
/**
42
 * Abstract example implementation that provides some kind of basic MVC functionality
43
 * to handle requests by subclasses action methods.
44
 *
45
 * @author    Tim Wagner <[email protected]>
46
 * @copyright 2015 TechDivision GmbH <[email protected]>
47
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
48
 * @link      http://github.com/appserver-io/routlt
49
 * @link      http://www.appserver.io
50
 */
51
class ControllerServlet extends HttpServlet implements ControllerInterface
52
{
53
54
    /**
55
     * The key for the init parameter with the action namespace.
56
     *
57
     * @var string
58
     */
59
    const INIT_PARAMETER_ACTION_NAMESPACE = 'action.namespace';
60
61
    /**
62
     * The key for the init parameter with the path to the configuration file.
63
     *
64
     * @var string
65
     */
66
    const INIT_PARAMETER_ROUTLT_CONFIGURATION_FILE = 'routlt.configuration.file';
67
68
    /**
69
     * The default action if no valid action name was found in the path info.
70
     *
71
     * @var string
72
     */
73
    const DEFAULT_ROUTE = '/index';
74
75
    /**
76
     * The array with the initialized routes.
77
     *
78
     * @var array
79
     */
80
    protected $routes = array();
81
82
    /**
83
     * The array with the path descriptors.
84
     *
85
     * @var array
86
     */
87
    protected $paths = array();
88
89
    /**
90
     * The array with request method action -> route mappings.
91
     *
92
     * @var array
93
     */
94
    protected $actionMappings = array();
95
96
    /**
97
     * Initializes the servlet with the passed configuration.
98
     *
99
     * @param \AppserverIo\Psr\Servlet\ServletConfigInterface $config The configuration to initialize the servlet with
100
     *
101
     * @return void
102
     */
103 4
    public function init(ServletConfigInterface $config)
104
    {
105
106
        // call parent method
107 4
        parent::init($config);
108
109
        // load the values from the configuration file
110 4
        $this->initConfiguration();
111
112
        // initialize the routing
113 4
        $this->initRoutes();
114 4
    }
115
116
    /**
117
     * Returns the available routes.
118
     *
119
     * @return array The array with the available routes
120
     */
121 2
    public function getRoutes()
122
    {
123 2
        return $this->routes;
124
    }
125
126
    /**
127
     * Returns the array with request method action -> route mappings.
128
     *
129
     * @return array The request method action -> route mappings
130
     */
131 1
    public function getActionMappings()
132
    {
133 1
        return $this->actionMappings;
134
    }
135
136
    /**
137
     * Returns the naming directoy instance (the application).
138
     *
139
     * @return \AppserverIo\Psr\Naming\NamingDirectoryInterface The naming directory instance
140
     */
141 1
    public function getNamingDirectory()
142
    {
143 1
        return $this->getServletContext()->getApplication();
0 ignored issues
show
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...
144
    }
145
146
    /**
147
     * Returns the object manager instance
148
     *
149
     * @return \AppserverIo\Psr\Di\ObjectManagerInterface The object manager instance
150
     */
151 1
    public function getObjectManager()
152
    {
153 1
        return $this->getNamingDirectory()->search(ObjectManagerInterface::IDENTIFIER);
154
    }
155
156
    /**
157
     * Returns the DI provider instance.
158
     *
159
     * @return \AppserverIo\Psr\Di\ProviderInterface The DI provider instance
160
     */
161 1
    public function getProvider()
162
    {
163 1
        return $this->getNamingDirectory()->search(ProviderInterface::IDENTIFIER);
164
    }
165
166
    /**
167
     * This method returns the default route we'll invoke if the path info doesn't contain one.
168
     *
169
     * @return string The default route
170
     */
171 2
    public function getDefaultRoute()
172
    {
173 2
        return ControllerServlet::DEFAULT_ROUTE;
174
    }
175
176
    /**
177
     * Returns the array with the path descriptors.
178
     *
179
     * @return array The array with the path descriptors
180
     */
181 1
    public function getPathDescriptors()
182
    {
183 1
        return $this->paths;
184
    }
185
186
    /**
187
     * Adds a path descriptor to the controller.
188
     *
189
     * @param \AppserverIo\Routlt\Description\PathDescriptorInterface $pathDescriptor The path descriptor to add
190
     *
191
     * @return void
192
     */
193 1
    public function addPathDescriptor(PathDescriptorInterface $pathDescriptor)
194
    {
195 1
        $this->paths[$pathDescriptor->getName()] = $pathDescriptor;
196 1
    }
197
198
    /**
199
     * Returns the path descriptor with the passed name.
200
     *
201
     * @param string $name The name of the path descriptor to return
202
     *
203
     * @return \AppserverIo\Routlt\Description\PathDescriptorInterface The path descriptor instance
204
     * @throws \Exception
205
     */
206
    public function getPathDescriptor($name)
207
    {
208
209
        // query whether or not the path descriptor exists
210
        if (isset($this->paths[$name])) {
211
            return $this->paths[$name];
212
        }
213
214
        // throw an exception if the requested path descriptor ist NOT available
215
        throw new \Exception(sprintf('Can\'t find path descriptor with name "%s"', $name));
216
    }
217
218
    /**
219
     * Loads the values found in the configuration file and merges
220
     * them with the servlet context initialization parameters.
221
     *
222
     * @return void
223
     */
224 3
    protected function initConfiguration()
225
    {
226
227
        // load the relative path to the Routlt configuration file
228 3
        $configurationFileName = $this->getInitParameter(ControllerServlet::INIT_PARAMETER_ROUTLT_CONFIGURATION_FILE);
229
230
        // load the path to the configuration file
231 3
        $configurationFile = $this->getServletConfig()->getWebappPath() . DIRECTORY_SEPARATOR . ltrim($configurationFileName, '/');
232
233
        // if the file is readable
234 3
        if (is_file($configurationFile) && is_readable($configurationFile)) {
235
            // load the  properties from the file
236 1
            $properties = new Properties();
237 1
            $properties->load($configurationFile);
238
239
            // append the properties to the servlet context
240 1
            foreach ($properties as $paramName => $paramValue) {
241 1
                $this->getServletContext()->addInitParameter($paramName, $paramValue);
242 1
            }
243 1
        }
244 3
    }
245
246
    /**
247
     * Initializes the available routes.
248
     *
249
     * @return void
250
     */
251 3
    protected function initRoutes()
252
    {
253
254
        // load the action namespace
255 3
        $actionNamespace = strtolower($this->getInitParameter(ControllerServlet::INIT_PARAMETER_ACTION_NAMESPACE));
256
257
        // register the actions located by annotations and the XML configuration
258 3
        foreach ($this->getObjectManager()->getObjectDescriptors() as $descriptor) {
0 ignored issues
show
The expression $this->getObjectManager()->getObjectDescriptors() of type object<AppserverIo\Storage\StorageInterface> is not traversable.
Loading history...
259
            // check if we've found a servlet descriptor
260 2
            if ($descriptor instanceof PathDescriptorInterface) {
261
                // register the action's references
262 2
                $this->getServletContext()->registerReferences($descriptor);
0 ignored issues
show
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...
263
                // initialize a new action instance
264 2
                $action = $this->initActionInstance($descriptor);
265
266
                // prepare the action's the result configuration descriptors
267
                /** @var \AppserverIo\Routlt\Description\ResultConfigurationDescriptorInterface $resultConfigurationDescriptor */
268 2
                foreach ($descriptor->getResults() as $resultConfigurationDescriptor) {
269 1
                    $action->addResult($this->initResultInstance($resultConfigurationDescriptor, $action));
270 2
                }
271
272
                // prepare the route, e. g. /index/index
273 2
                $controllerName = str_replace($actionNamespace, '', $descriptor->getName());
274
275
                // initialize the action mappings
276 2
                foreach ($descriptor->getActions() as $actionDescriptors) {
277
                    // iterate over all request methods
278
                    /** @var \AppserverIo\Routlt\Description\ActionDescriptorInterface $actionDescriptor */
279 1
                    foreach ($actionDescriptors as $requestMethod => $actionDescriptor) {
280
                        // prepare the real action method name
281 1
                        $methodName = $actionDescriptor->getMethodName();
282
                        // prepare the action path -> concatenate route + action name
283 1
                        $actionPath = sprintf('%s%s', $controllerName, $actionDescriptor->getName());
284
285
                        // initialize the action mapping for the actual route
286 1
                        $actionMapping = new ActionMapping();
287 1
                        $actionMapping->setControllerName($controllerName);
288 1
                        $actionMapping->setMethodName($methodName);
289 1
                        $actionMapping->compile(
290 1
                            $actionPath,
291 1
                            $actionDescriptor->getRestrictions(),
292 1
                            $actionDescriptor->getDefaults()
293 1
                        );
294
295
                        // add the action path -> route mapping for the request method
296 1
                        $this->actionMappings[$requestMethod][$actionPath] = $actionMapping;
297
298
                        // add an alias for the route for the action's default method
299 1
                        if ($actionDescriptor->getMethodName() === $action->getDefaultMethod()) {
300
                            // initialize the action mapping for the default route
301 1
                            $actionMapping = new ActionMapping();
302 1
                            $actionMapping->setControllerName($controllerName);
303 1
                            $actionMapping->setMethodName($methodName);
304 1
                            $actionMapping->compile(
305 1
                                $controllerName,
306 1
                                $actionDescriptor->getRestrictions(),
307 1
                                $actionDescriptor->getDefaults()
308 1
                            );
309
                            // add the action mapping for the default route
310 1
                            $this->actionMappings[$requestMethod][$controllerName] = $actionMapping;
311 1
                        }
312 1
                    }
313 2
                }
314
315
                // add the initialized action
316 2
                $this->routes[$controllerName] = $action;
317 2
            }
318 3
        }
319 3
    }
320
321
    /**
322
     * Creates a new instance of the action from the passed path descriptor instance.
323
     *
324
     * @param \AppserverIo\Routlt\Description\PathDescriptorInterface $pathDescriptor The path descriptor to create the action from
325
     *
326
     * @return \AppserverIo\Routlt\ActionInterface The action instance
327
     */
328
    protected function initActionInstance(PathDescriptorInterface $pathDescriptor)
329
    {
330
331
        // create a new action instance
332
        $actionInstance = $this->getProvider()->get($pathDescriptor->getName());
333
334
        // if the action is servlet context aware
335
        if ($actionInstance instanceof ServletContextAware) {
336
            $actionInstance->setServletContext($this->getServletContext());
337
        }
338
339
        // if the action is descriptor aware
340
        if ($actionInstance instanceof DescriptorAware) {
341
            $actionInstance->setDescriptor($pathDescriptor);
342
        }
343
344
        // return the action instance
345
        return $actionInstance;
346
    }
347
348
    /**
349
     * Creates a new instance of the action result the passed descriptor.
350
     *
351
     * @param \AppserverIo\Routlt\Description\ResultConfigurationDescriptorInterface $resultConfigurationDescriptor The action result configuration descriptor
352
     * @param \AppserverIo\Routlt\ActionInterface                                    $action                        The action instance the result is bound to
353
     *
354
     * @return \AppserverIo\Routlt\Results\ResultInterface The result instance
355
     */
356
    protected function initResultInstance(ResultConfigurationDescriptorInterface $resultConfigurationDescriptor, ActionInterface $action)
357
    {
358
359
        // load the object manager instance
360
        $objectManager = $this->getObjectManager();
361
362
        // query whether or not we've a real deployment descriptor or not
363
        if ($objectManager->hasObjectDescriptor($lookupName = $resultConfigurationDescriptor->getType())) {
364
            // if not replqce it with the real one
365
            $objectDescriptor = $objectManager->getObjectDescriptor($lookupName);
366
            // register the result's references
367
            $this->getServletContext()->registerReferences($objectDescriptor);
0 ignored issues
show
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...
368
            // now use the result descriptors name for lookup
369
            $lookupName = $objectDescriptor->getName();
370
        }
371
372
        // initialize the result instance by the lookup name
373
        $resultInstance = $this->getProvider()->get($lookupName);
374
375
        // if the result is action aware
376
        if ($resultInstance instanceof ActionAware) {
377
            $resultInstance->setAction($action);
378
        }
379
380
        // if the result is descriptor aware
381
        if ($resultInstance instanceof DescriptorAware && $objectDescriptor instanceof ResultDescriptorInterface) {
382
            $resultInstance->setDescriptor($objectDescriptor);
0 ignored issues
show
The variable $objectDescriptor does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
383
        }
384
385
        // if the result is servlet context aware
386
        if ($resultInstance instanceof ServletContextAware) {
387
            $resultInstance->setServletContext($this->getServletContext());
388
        }
389
390
        // initialize the instance from the result descriptor
391
        $resultInstance->init($resultConfigurationDescriptor);
392
393
        // return the result instance
394
        return $resultInstance;
395
    }
396
397
    /**
398
     * Checks whether or not an action is generally available for any request method.
399
     * Will return TRUE if so, FALSE otherwise.
400
     * This method replicates a lot of the checks generally necessary but omits the request method check.
401
     * Still best called in exception- or edge-cases
402
     *
403
     * @param string $pathInfo The action path which has been requested
404
     *
405
     * @return boolean
406
     */
407 3
    public function checkGeneralActionAvailability($pathInfo)
408
    {
409
410
        // iterate the request methods we have mappings for and check if we can find the requested action
411 3
        foreach ($this->getActionMappings() as $actionMapping) {
412 3
            $run = true;
413 3
            $requestedAction = $pathInfo;
414
            do {
415 3
                if (isset($actionMapping[$requestedAction])) {
416 2
                    return true;
417
                }
418
                // strip the last directory
419 3
                $requestedAction = dirname($requestedAction);
420
421
                // query whether we've to stop dispatching
422 3
                if ($requestedAction === '/' || $requestedAction === false) {
423 2
                    $run = false;
424 2
                }
425 3
            } while ($run === true);
426 2
        }
427
428
        // nothing found? Return false then
429 2
        return false;
430
    }
431
432
    /**
433
     * Returns the array with request method action -> route mappings
434
     * for the passed servlet request.
435
     *
436
     * @param \AppserverIo\Psr\Servlet\ServletRequestInterface $servletRequest The request instance
437
     *
438
     * @return array The request method action -> route mappings for the passed request method
439
     */
440 4
    public function getActionMappingsForServletRequest(ServletRequestInterface $servletRequest)
441
    {
442
        // load the servlet request method
443 4
        $requestMethod = $servletRequest->getMethod();
0 ignored issues
show
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...
444
445
        // load the action mappings
446 4
        $actionMappings = $this->getActionMappings();
447
448
        // query whether we've action mappings for the request method or not
449 4
        if (isset($actionMappings[$requestMethod])) {
450 3
            return $actionMappings[$requestMethod];
451
        }
452
453
        // nothing found? Method must not be allowed then
454 1
        throw new DispatchException(sprintf('Method %s not allowed', $requestMethod), 405);
455
    }
456
457
    /**
458
     * Delegates to HTTP method specific functions like doPost() for POST e.g.
459
     *
460
     * @param \AppserverIo\Psr\Servlet\ServletRequestInterface  $servletRequest  The request instance
461
     * @param \AppserverIo\Psr\Servlet\ServletResponseInterface $servletResponse The response sent back to the client
462
     *
463
     * @return void
464
     *
465
     * @throws \AppserverIo\Psr\Servlet\ServletException If no action has been found for the requested path
466
     */
467 7
    public function service(ServletRequestInterface $servletRequest, ServletResponseInterface $servletResponse)
468
    {
469
470
        try {
471
            // pre-initialize response
472 7
            $servletResponse->addHeader(HttpProtocol::HEADER_X_POWERED_BY, get_class($this));
0 ignored issues
show
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...
473
474
            // load the path info from the servlet request
475 6
            $pathInfo = $servletRequest->getPathInfo();
476
477
            // if the requested action has been found in the path info
478 6
            if ($pathInfo == null) {
479 2
                $pathInfo = $this->getDefaultRoute();
480 2
            }
481
482
            // prepare the path of the requested action
483 6
            $requestedAction = $pathInfo;
484
485
            // load the routes
486 6
            $routes = $this->getRoutes();
487
488
            // load the DI provider
489 6
            $provider = $this->getProvider();
490
491
            // load the action mappings for the actual servlet request
492 6
            $actionMappings = $this->getActionMappingsForServletRequest($servletRequest);
493
494
            // initialize the parameter map with the values from the request
495 5
            if ($servletRequest->getParameterMap()) {
496
                $parameterMap = $servletRequest->getParameterMap();
497
            } else {
498 5
                $parameterMap = array();
499
            }
500
501
            // iterate over the action mappings and try to find a mapping
502 5
            foreach ($actionMappings as $actionMapping) {
503
                // try to match actual request by the tokenizer
504 5
                if ($actionMapping->match($requestedAction)) {
505
                    // initialize the request attributes with the values from the action mapping
506 3
                    $servletRequest->setParameterMap(
0 ignored issues
show
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...
507 3
                        array_merge($parameterMap, $actionMapping->getRequestParameters())
508 3
                    );
509
510
                    // resolve the action with the found mapping
511 3
                    $action = $routes[$actionMapping->getControllerName()];
512
513
                    // query whether or not the action has a descriptor
514 3
                    if ($action instanceof DescriptorAware) {
515 3
                        $provider->injectDependencies($action->getDescriptor(), $action);
516 3
                    }
517
518
                    // set the method that has to be invoked in the action context
519 3
                    $action->setAttribute(ContextKeys::METHOD_NAME, $actionMapping->getMethodName());
520
521
                    // pre-dispatch the action
522 3
                    $action->preDispatch($servletRequest, $servletResponse);
523
524
                    // if the action has been dispatched, we're done
525 3
                    if ($servletRequest->isDispatched()) {
526 1
                        return;
527
                    }
528
529
                    // initialize the result with the default value
530 2
                    $result = ActionInterface::INPUT;
531
532
                    // if not dispatch the action
533 2
                    if ($newResult = $action->perform($servletRequest, $servletResponse)) {
534 1
                        $result = $newResult;
535 1
                    }
536
537
                    // post-dispatch the action instance
538 2
                    $action->postDispatch($servletRequest, $servletResponse);
539
540 1
                    // process the result if available
541 1
                    if (($instance = $action->findResult($result)) instanceof ResultInterface) {
542 1
                        // query whether or not the result has a descriptor
543
                        if ($instance instanceof DescriptorAware && $descriptor = $instance->getDescriptor()) {
544
                            $provider->injectDependencies($descriptor, $instance);
545 1
                        }
546 1
547 1
                        // query whether or not the result is action aware
548
                        if ($instance instanceof ActionAware) {
549
                            $instance->setAction($action);
550 1
                        }
551 1
552
                        // process the result
553
                        $instance->process($servletRequest, $servletResponse);
554 2
                    }
555
556
                    // stop processing
557 2
                    return;
558
                }
559 2
            }
560
561
            // We did not find anything for this method/URI connection. We have to evaluate if there simply
562
            // is a method restriction. This replicates a lot of the checks we did before but omits extra
563
            // iterations in a positive dispatch event, 4xx's should be the exception and can handle that
564
            // penalty therefore
565 2
            if ($this->checkGeneralActionAvailability($pathInfo)) {
566
                // nothing found? Method must not be allowed then
567 1
                throw new DispatchException(sprintf('Method %s not allowed', $servletRequest->getMethod()), 405);
0 ignored issues
show
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...
568
            }
569
570
            // throw an action, because we can't find an action mapping
571 1
            throw new DispatchException(sprintf('Can\'t find action to dispatch path info %s', $pathInfo), 404);
572
573 4
        } catch (DispatchException $de) {
574
            // results in a 4xx error
575 3
            throw new ServletException($de->__toString(), $de->getCode(), $de);
576 1
        } catch (\Exception $e) {
577
            // results in a 500 error page
578 1
            throw new ServletException($e->__toString(), 500, $e);
579
        }
580
    }
581
}
582