Issues (273)

Security Analysis    not enabled

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/Controller/Controller.php (2 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
 * This file contains the skeleton for all of the controllers
5
 *
6
 * @package    BZiON\Controllers
7
 * @license    https://github.com/allejo/bzion/blob/master/LICENSE.md GNU General Public License Version 3
8
 */
9
10
use BZIon\Debug\Debug;
11
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
12
use Symfony\Component\EventDispatcher\Event;
13
use Symfony\Component\HttpFoundation\ParameterBag;
14
use Symfony\Component\HttpFoundation\Response;
15
16
/**
17
 * The Controller class represents a bunch of pages relating to the same
18
 * subject (Model in most cases) - for example, there is a PlayerController,
19
 * a TeamController and a HomeController.
20
 *
21
 * Controllers contain special methods called 'actions', which are essentially
22
 * different pages performing different actions - for example, the
23
 * TeamController might contain a 'show' action, which renders the team's page,
24
 * and a 'new' action, which renders the page that is shown to the user when
25
 * they want to create a new team.
26
 *
27
 * Actions have some unique characteristics. Take a look at this sample action:
28
 *
29
 * <pre><code>public function showAction(Request $request, Team $team) {
30
 *   return array('team' => $team);
31
 * }
32
 * </code></pre>
33
 *
34
 * The following route will make sure that `showAction()` handles the request:
35
 *
36
 * <pre><code>team_show:
37
 *   pattern:    /teams/{team}
38
 *   defaults: { _controller: 'Team', _action: 'show' }
39
 * </code></pre>
40
 *
41
 * First of all, the method's name should end with `Action`. The parameters
42
 * are passed dynamically, and the order is insignificant.
43
 *
44
 * You can request Symfony's Request or Session class, or even a model, which
45
 * will be generated based on the route parameters. For example, the route
46
 * pattern `/posts/{post}/comments/{commentId}` (note how you can use both
47
 * `comment` and `commentId` as parameters - just make sure to use the correct
48
 * variable name on the method later) and can be used with actions like these:
49
 *
50
 * <code>
51
 * public function sampleAction
52
 *   (Request $request, NewsArticle $post, Comment $comment)
53
 * </code>
54
 *
55
 * <code>
56
 * public function sampleAction
57
 *   (NewsArticle $post, Session $session, Request $request, Comment $comment)
58
 * </code>
59
 *
60
 * A method's return value can be:
61
 * - Symfony's Response Class
62
 * - A string representing the text you want the user to see
63
 * - An array representing the variables you want to pass to the controller's
64
 *   view, so that it can be rendered
65
 *
66
 * @package BZiON\Controllers
67
 */
68
abstract class Controller
69
{
70
    use ContainerAwareTrait;
71
72
    /**
73
     * Parameters specified by the route
74
     * @var ParameterBag
75
     */
76
    protected $parameters;
77
78
    /**
79
     * The first controller that was invoked
80
     * @var Controller
81
     */
82
    protected $parent;
83
84
    /*
85
     * An array of data to pass between different parts of the application
86
     *
87
     * @var ParameterBag
88
     */
89
    public $data;
90
91
    /**
92
     * @param ParameterBag    $parameters The array returned by $request->attributes
93
     * @param Controller|null $parent     The controller who invoked this controller
94
     */
95 23
    public function __construct($parameters, Controller $parent = null)
96
    {
97 23
        $this->parameters = $parameters;
98 23
        $this->parent = $parent ?: $this;
99 23
        $this->data = new ParameterBag();
100
101 23
        $this->setContainer(Service::getContainer());
102 23
    }
103
104
    /**
105
     * Returns the controller that is assigned to a route
106
     *
107
     * @param  ParameterBag $parameters The array returned by $request->attributes
108
     * @return Controller   The controller
109
     */
110 23
    public static function getController($parameters)
111
    {
112 23
        $ref = new ReflectionClass($parameters->get('_controller') . 'Controller');
113 23
        $controller = $ref->newInstance($parameters);
114
115 23
        return $controller;
116
    }
117
118
    /**
119
     * Call the controller's action specified by the $parameters array
120
     *
121
     * @param  string|null $action The action name to call (e.g. `show`), null to invoke the default one
122
     * @return Response    The action's response
123
     */
124 23
    public function callAction($action = null)
125
    {
126 23
        $this->setup();
127 23
        $response = $this->forward($action);
128 22
        $this->cleanup();
129
130 22
        return $response->prepare($this->getRequest());
131
    }
132
133
    /**
134
     * Get the controller's default action name
135
     *
136
     * @return string The action's name without the `Action` suffix
137
     */
138 23
    protected function getDefaultActionName()
139
    {
140 23
        return $this->parameters->get('_action') ?: 'default';
141
    }
142
143
    /**
144
     * Get a controller's action
145
     *
146
     * @param  string|null       $action The action name to call (e.g. `show`), null to invoke the default one
147
     * @return \ReflectionMethod The action method
148
     */
149 23
    public function getAction($action = null)
150
    {
151 23
        if (!$action) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $action of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
152
            $action = $this->getDefaultActionName();
153
        }
154
155 23
        return new ReflectionMethod($this, $action . 'Action');
156
    }
157
158
    /**
159
     * Forward the request to another action
160
     *
161
     * Please note that this doesn't generate an HTTP redirect, but an
162
     * internal one - the user sees the original URL, but a different page
163
     *
164
     * @param  string $action The action to forward the request to
165
     * @param  array  $params An additional associative array of parameters to
166
     *                        provide to the action
167
     * @param  string|null $controllerName The name of the controller of the
168
     *                                     action, without the 'Controller'
169
     *                                     suffix (defaults to the current
170
     *                                     controller)
171
     * @return Response
172
     */
173 23
    protected function forward($action, $params = array(), $controllerName = null)
174
    {
175 23
        if (!$action) {
176 23
            $action = $this->getDefaultActionName();
177
        }
178
179 23
        $args = clone $this->parameters;
180 23
        $args->add($params);
181
182 23
        if ($controllerName === null) {
183 23
            $controller = $this;
184
        } else {
185
            $ref = new ReflectionClass($controllerName . 'Controller');
186
            $controller = $ref->newInstance($args, $this->parent);
187
        }
188
189 23
        $ret = $controller->callMethod($controller->getAction($action), $args);
190
191 22
        return $controller->handleReturnValue($ret, $action);
192
    }
193
194
    /**
195
     * Method that will be called before any action
196
     *
197
     * @return void
198
     */
199 1
    public function setup()
200
    {
201 1
    }
202
203
    /**
204
     * Method that will be called after all actions
205
     *
206
     * @return void
207
     */
208 22
    public function cleanup()
209
    {
210 22
    }
211
212
    /**
213
     * Call one of the controller's methods
214
     *
215
     * The arguments are passed dynamically to the method, based on its
216
     * definition - check the description of the Controller class for more
217
     * information
218
     *
219
     * @param  \ReflectionMethod $method     The method
220
     * @param  ParameterBag      $parameters The parameter bag representing the route's parameters
221
     * @return mixed             The return value of the called method
222
     */
223 23
    protected function callMethod($method, $parameters)
224
    {
225 23
        $params = array();
226
227 23
        foreach ($method->getParameters() as $p) {
228 23
            if ($model = $this->getObjectFromParameters($p, $parameters)) {
229 23
                $params[] = $model;
230 1
            } elseif ($parameters->has($p->name)) {
231
                $params[] = $parameters->get($p->name);
232 1
            } elseif ($p->isOptional()) {
233 1
                $params[] = $p->getDefaultValue();
234
            } else {
235
                throw new MissingArgumentException(
236
                    "Missing parameter '$p->name' for " . $this->getName() . "::"
237 23
                    . $method->getName() . "()"
238
                );
239
            }
240
        }
241
242 23
        return $method->invokeArgs($this, $params);
243
    }
244
245
    /**
246
     * Find what to pass as an argument on an action
247
     *
248
     * @param ReflectionParameter $modelParameter  The model's parameter we want to investigate
249
     * @param ParameterBag        $routeParameters The route's parameters
250
     *
251
     * @return object|null
252
     */
253 23
    protected function getObjectFromParameters($modelParameter, $routeParameters)
254
    {
255 23
        $refClass = $modelParameter->getClass();
256 23
        $paramName  = $modelParameter->getName();
257
258 23
        if ($refClass !== null && $refClass->isSubclassOf("Model")) {
259
            // Look for the object's ID/slugs in the routeParameters array
260 1
            $model = $this->findModelInParameters($modelParameter, $routeParameters);
261
262 1
            if ($model !== null) {
263 1
                return $model;
264
            }
265
        }
266
267
        // $me -> currently logged in user
268 23
        if ($paramName == "me") {
269 1
            return self::getMe();
270
        }
271
272 23
        if ($refClass === null) {
273
            // No class provived by the method's definition, we don't know
274
            // what we should pass
275
            return null;
276
        }
277
278 23
        switch ($refClass->getName()) {
279 23
            case 'Symfony\Component\HttpFoundation\Request':
280 23
                return $this->getRequest();
281 23
            case 'Symfony\Component\HttpFoundation\Session\Session':
282 1
                return $this->getRequest()->getSession();
283 23
            case 'Symfony\Component\HttpFoundation\Session\Flash\FlashBag':
284
                return $this->getRequest()->getSession()->getFlashBag();
285 23
            case 'Monolog\Logger':
286 22
                return $this->getLogger();
287 1
            case 'Symfony\Component\Form\FormFactory':
288
                return Service::getFormFactory();
289
        }
290
291 1
        return null;
292
    }
293
294
    /**
295
     * Try locating a method's parameter in an array
296
     *
297
     * @param  ReflectionParameter $modelParameter  The model's parameter we want to investigate
298
     * @param  ParameterBag        $routeParameters The route's parameters
299
     * @return Model|null          A Model or null if it couldn't be found
300
     */
301 1
    protected function findModelInParameters($modelParameter, $routeParameters)
302
    {
303 1
        $refClass = $modelParameter->getClass();
304 1
        $paramName  = $modelParameter->getName();
305
306 1
        if ($routeParameters->has($paramName)) {
307 1
            $parameter = $routeParameters->get($paramName);
308 1
            if (is_object($parameter) && $refClass->getName() === get_class($parameter)) {
309
                // The model has already been instantiated - we don't need to do anything
310
                return $parameter;
311
            }
312
313 1
            return $refClass->getMethod("fetchFromSlug")->invoke(null, $parameter);
314
        }
315
316 1
        if ($routeParameters->has($paramName . 'Id')) {
317
            return $refClass->getMethod('get')
318
                            ->invoke(null, $routeParameters->get($paramName . 'Id'));
319
        }
320
321 1
        return null;
322
    }
323
324
    /**
325
     * Render the action's template
326
     * @param  array  $params The variables to pass to the template
327
     * @param  string $action The controller's action
328
     * @return string The content
329
     */
330 1
    protected function renderDefault($params, $action)
331
    {
332 1
        $templatePath = $this->getName() . "/$action.html.twig";
333
334 1
        return $this->render($templatePath, $params);
335
    }
336
337
    /**
338
     * Get a Response from the return value of an action
339
     * @param  mixed    $return Whatever the method returned
340
     * @param  string   $action The name of the action
341
     * @return Response The response that the controller wants us to send to the client
342
     */
343 22
    protected function handleReturnValue($return, $action)
344
    {
345 22
        if ($return instanceof Response) {
346 22
            return $return;
347
        }
348
349 22
        $content = null;
350 22
        if (is_array($return)) {
351
            // The controller is probably expecting us to show a view to the
352
            // user, using the array provided to set variables for the template
353 1
            $content = $this->renderDefault($return, $action);
354 22
        } elseif (is_string($return)) {
355 22
            $content = $return;
356
        }
357
358 22
        return new Response($content);
359
    }
360
361
    /**
362
     * Returns the name of the controller without the "Controller" part
363
     * @return string
364
     */
365 1
    public static function getName()
366
    {
367 1
        return preg_replace('/Controller$/', '', get_called_class());
368
    }
369
370
    /**
371
     * Returns a configured QueryBuilder for the corresponding model
372
     *
373
     * The returned QueryBuilder will only show models visible to the currently
374
     * logged in user
375
     *
376
     * @param string $type The model whose query builder we should get (null
377
     *                     to get the builder of the controller's model)
378
     * @return QueryBuilder
379
     */
380
    public static function getQueryBuilder($type = null)
381 1
    {
382
        $type = ($type) ?: static::getName();
383 1
384
        return $type::getQueryBuilder()
385 1
            ->visibleTo(static::getMe(), static::getRequest()->get('showDeleted'));
386 1
    }
387
388
     /**
389
      * Generates a URL from the given parameters.
390
      * @param  string  $name       The name of the route
391
      * @param  mixed   $parameters An array of parameters
392
      * @param  bool $absolute   Whether to generate an absolute URL
393
      * @return string  The generated URL
394
      */
395
     public static function generate($name, $parameters = array(), $absolute = false)
396
     {
397
         return Service::getGenerator()->generate($name, $parameters, $absolute);
398
     }
399
400
    /**
401
     * Gets the browser's request
402
     * @return Symfony\Component\HttpFoundation\Request
403
     */
404
    public static function getRequest()
405 23
    {
406
        return Service::getRequest();
407 23
    }
408
409
    /**
410
     * Gets the currently logged in player
411
     *
412
     * If the user is not logged in, a Player object that is invalid will be
413
     * returned
414
     *
415
     * @return Player
416
     */
417
    public static function getMe()
418 1
    {
419
        return Player::get(self::getRequest()->getSession()->get('playerId'));
420 1
    }
421
422
    /**
423
     * Find out whether debugging is enabled
424
     *
425
     * @return bool
426
     */
427
    public function isDebug()
428 23
    {
429
        return $this->container->getParameter('kernel.debug');
430 23
    }
431
432
    /**
433
     * Find out whether the site is in demo mode
434
     *
435
     * @return bool
436
     */
437
    public function isDemoMode()
438
    {
439 22
        return $this->container->getParameter('bzion.miscellaneous.demo_mode');
440
    }
441 22
442 22
    /**
443
     * Gets the monolog logger
444
     *
445 22
     * @param  string         $channel The log channel, defaults to the Controller's default
446
     * @return Monolog\Logger
447
     */
448
    protected static function getLogger($channel = null)
449
    {
450
        if (!$channel) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $channel of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
451
            $channel = static::getLogChannel();
452
        }
453
454
        return Service::getContainer()->get("monolog.logger.$channel");
455
    }
456
457
    /**
458
     * Gets the logging channel for monolog
459
     * @return string
460
     */
461
    protected static function getLogChannel()
462
    {
463 1
        return 'app';
464
    }
465 1
466
    /**
467
     * Uses symfony's dispatcher to announce an event
468
     * @param  string $eventName The name of the event to dispatch.
469
     * @param  Event  $event     The event to pass to the event handlers/listeners.
470
     * @return Event
471
     */
472
    protected function dispatch($eventName, Event $event = null)
473
    {
474 1
        return $this->container->get('event_dispatcher')->dispatch($eventName, $event);
475
    }
476 1
477
    /**
478 1
     * Renders a view
479
     * @param  string $view       The view name
480 1
     * @param  array  $parameters An array of parameters to pass to the view
481
     * @return string The rendered view
482 1
     */
483
    protected function render($view, $parameters = array())
484
    {
485
        Debug::startStopwatch('view.render');
486
487
        $ret = $this->container->get('twig')->render($view, $parameters);
488
489
        Debug::finishStopwatch('view.render');
490
491
        return $ret;
492
    }
493
}
494
495
class MissingArgumentException extends Exception
496
{
497
}
498