Completed
Push — dev-master ( 3d2c20...b7e24f )
by Derek Stephen
08:18 queued 03:13
created

PlatesStrategy::invokeRouteCallable()   B

Complexity

Conditions 6
Paths 108

Size

Total Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
dl 0
loc 37
ccs 0
cts 30
cp 0
rs 8.6524
c 0
b 0
f 0
cc 6
nc 108
nop 2
crap 42
1
<?php
2
3
namespace Bone\Mvc\Router;
4
5
use Bone\Http\Response as BoneResponse;
6
use Bone\Mvc\Router\Decorator\ExceptionDecorator;
7
use Bone\Mvc\Router\Decorator\NotAllowedDecorator;
8
use Bone\Mvc\Router\Decorator\NotFoundDecorator;
9
use Bone\Mvc\View\PlatesEngine;
10
use Bone\Mvc\View\ViewEngine;
11
use Bone\Mvc\View\ViewRenderer;
12
use Bone\Server\I18nHandler;
13
use Bone\Traits\LayoutAwareTrait;
14
use Exception;
15
use League\Route\Http\Exception\{MethodNotAllowedException, NotFoundException};
16
use League\Route\Route;
17
use League\Route\Strategy\ApplicationStrategy;
18
use League\Route\Strategy\StrategyInterface;
19
use Psr\Http\Server\RequestHandlerInterface;
20
use Psr\Http\Message\ResponseInterface;
21
use Psr\Http\Message\ServerRequestInterface;
22
use Psr\Http\Server\MiddlewareInterface;
23
use Zend\Diactoros\Response;
24
use Zend\Diactoros\Response\HtmlResponse;
25
use Zend\Diactoros\Stream;
26
27
class PlatesStrategy extends ApplicationStrategy implements StrategyInterface
28
{
29
    use LayoutAwareTrait;
30
31
    /** @var PlatesEngine $viewEngine */
32
    private $viewEngine;
33
34
    /** @var NotFoundDecorator $notFoundDecorator */
35
    private $notFoundDecorator;
36
37
    /** @var NotAllowedDecorator $notAllowedDecorator */
38
    private $notAllowedDecorator;
39
40
    /** @var bool  */
41
    private $i18nEnabled = false;
42
43
    /** @var array  */
44
    private $supportedLocales = [];
45
46
    /**
47
     * PlatesStrategy constructor.
48
     * @param PlatesEngine $viewEngine
49
     * @param NotFoundDecorator $notFound
50
     * @param NotAllowedDecorator $notAllowed
51
     * @param string $layout
52
     */
53
    public function __construct(PlatesEngine $viewEngine, NotFoundDecorator $notFound, NotAllowedDecorator $notAllowed, string $layout)
54
    {
55
        $this->viewEngine = $viewEngine;
56
        $this->notFoundDecorator = $notFound;
57
        $this->notAllowedDecorator = $notAllowed;
58
        $this->setLayout($layout);
59
    }
60
61
    /**
62
     * @param bool $i18nEnabled
63
     */
64
    public function setI18nEnabled(bool $i18nEnabled): void
65
    {
66
        $this->i18nEnabled = $i18nEnabled;
67
    }
68
69
    /**
70
     * @param array $locales
71
     */
72
    public function setSupportedLocales(array $locales): void
73
    {
74
        $this->supportedLocales = $locales;
75
    }
76
77
78
    /**
79
     * Invoke the route callable based on the strategy.
80
     *
81
     * @param \League\Route\Route $route
82
     * @param \Psr\Http\Message\ServerRequestInterface $request
83
     *
84
     * @return \Psr\Http\Message\ResponseInterface
85
     */
86
    public function invokeRouteCallable(Route $route, ServerRequestInterface $request): ResponseInterface
87
    {
88
        try {
89
90
            $controller = $route->getCallable($this->container);
91
            $controllerClass = get_class($controller[0]);
92
            $actionMethod = $controller[1];
93
            if (preg_match('#(?<module>\w+)\\\Controller\\\(?<controller>\w+)Controller$#', $controllerClass, $matches)) {
94
                $module = $matches['module'];
0 ignored issues
show
Unused Code introduced by
$module is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
95
                $controller = $matches['controller'];
0 ignored issues
show
Unused Code introduced by
$controller is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
96
            }
97
98
            if (preg_match('#(?<action>\w+)Action#', $actionMethod, $action)) {
99
                $action = $action['action'];
0 ignored issues
show
Unused Code introduced by
$action is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
100
            }
101
102
            $response = parent::invokeRouteCallable($route, $request);
103
            $body = $this->getBody($response);
104
            $body = $this->viewEngine->render($this->layout, $body);
105
106
            return $this->getResponseWithBodyAndStatus($response, $body, $response->getStatusCode());
0 ignored issues
show
Compatibility introduced by
$response of type object<Psr\Http\Message\ResponseInterface> is not a sub-type of object<Zend\Diactoros\Response>. It seems like you assume a concrete implementation of the interface Psr\Http\Message\ResponseInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
107
108
        } catch (Exception $e) {
109
            $body = $this->viewEngine->render('error/error', [
110
                'message' => $e->getMessage(),
111
                'code' => $e->getCode(),
112
                'trace' => $e->getTrace(),
113
            ]);
114
            $body = $this->viewEngine->render($this->layout, [
115
                'content' => $body,
116
            ]);
117
            $status = ($e->getCode() >= 100 && $e->getCode() < 600) ? $e->getCode() : 500;
118
119
            return $this->getResponseWithBodyAndStatus(new HtmlResponse($body), $body, $status);
120
        }
121
122
    }
123
124
    /**
125
     * @param ResponseInterface $response
126
     * @return array|mixed
127
     */
128
    private function getBody(ResponseInterface $response)
129
    {
130
        switch (true) {
131
            case $response instanceof Response\JsonResponse:
132
                $contents = $response->getBody()->getContents();
133
                $body = json_decode($contents, true);
134
                $body = ($body === null) ? [] : $body;
135
                break;
136
137
            case $response instanceof Response:
138
                $body = ['content' => $response->getBody()->getContents()];
139
                break;
140
            default:
141
142
        }
143
144
        return $body;
0 ignored issues
show
Bug introduced by
The variable $body 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...
145
    }
146
147
    /**
148
     * @param Response $response
149
     * @param string $body
150
     * @param int $status
151
     * @return \Psr\Http\Message\MessageInterface|Response
152
     */
153
    private function getResponseWithBodyAndStatus(Response $response, string $body, int $status = 200)
154
    {
155
        $stream = new Stream('php://memory', 'r+');
156
        $stream->write($body);
157
        $response = $response->withStatus($status)->withBody($stream);
158
159
        return $response;
160
    }
161
162
    /**
163
     * Get a middleware that will decorate a NotFoundException
164
     *
165
     * @param \League\Route\Http\Exception\NotFoundException $exception
0 ignored issues
show
Bug introduced by
There is no parameter named $exception. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
166
     *
167
     * @return \Psr\Http\Server\MiddlewareInterface
168
     */
169
    public function getNotFoundDecorator(NotFoundException $e): MiddlewareInterface
170
    {
171
        return $this->notFoundDecorator;
172
    }
173
174
    /**
175
     * Get a middleware that will decorate a NotAllowedException
176
     *
177
     * @param \League\Route\Http\Exception\NotFoundException $e
178
     *
179
     * @return \Psr\Http\Server\MiddlewareInterface
180
     */
181
    public function getMethodNotAllowedDecorator(MethodNotAllowedException $e): MiddlewareInterface
182
    {
183
        return $this->notAllowedDecorator;
184
    }
185
}