Failed Conditions
Pull Request — master (#8)
by Arnold
03:48
created

RouteAction::after()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 0
crap 2
1
<?php
2
3
namespace Jasny\Controller;
4
5
use Psr\Http\Message\ServerRequestInterface;
6
use Psr\Http\Message\ResponseInterface;
7
8
/**
9
 * Execute controller on given route
10
 */
11
trait RouteAction
12
{
13
    /**
14
     * @var boolean
15
     */
16
    protected $actionCancelled = false;
17
    
18
    
19
    /**
20
     * Get request, set for controller
21
     *
22
     * @return ServerRequestInterface
23
     */
24
    abstract public function getRequest();
25
26
    /**
27
     * Get response. set for controller
28
     *
29
     * @return ResponseInterface
30
     */
31
    abstract public function getResponse();
32
33
    /**
34
     * Respond with a server error
35
     *
36
     * @param string $message
37
     * @param int    $code     HTTP status code
38
     */
39
    abstract public function notFound($message = '', $code = 404);
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
40
    
41
42
    /**
43
     * Get the route
44
     * 
45
     * @return \stdClass
46
     */
47 13
    protected function getRoute()
48
    {
49 13
        $route = $this->getRequest()->getAttribute('route');
50
        
51 13
        if (!isset($route)) {
52 1
            throw new \LogicException("Route has not been set");
53
        }
54
        
55 12
        if (is_array($route)) {
56 1
            $route = (object)$route;
57 1
        }
58
        
59 12
        if (!$route instanceof \stdClass) {
60 1
            $type = (is_object($route) ? get_class($route) . ' ' : '') . gettype($route);
61 1
            throw new \UnexpectedValueException("Expected route to be a stdClass object, not a $type");
62
        }
63
        
64 11
        return $route;
65
    }
66
67
    /**
68
     * Get the method name of the action
69
     * 
70
     * @param string $action
71
     * @return string
72
     */
73 11
    protected function getActionMethod($action)
74
    {
75 11
        return \Jasny\camelcase($action) . 'Action';
76
    }
77
    
78
    /**
79
     * Called before executing the action.
80
     * @codeCoverageIgnore
81
     * 
82
     * <code>
83
     * protected function beforeAction()
84
     * {
85
     *    $this->respondWith('json'); // Respond with JSON by default
86
     * 
87
     *    if ($this->auth->getUser()->getCredits() <= 0) {
88
     *        $this->paymentRequired();
89
     *    }
90
     * }
91
     * </code>
92
     */
93
    protected function before()
94
    {
95
    }
96
    
97
    /**
98
     * Called before executing the action.
99
     * @codeCoverageIgnore
100
     */
101
    protected function after()
102
    {
103
    }
104
    
105
    /**
106
     * Cancel the action
107
     * 
108
     * @return boolean
109
     */
110 1
    public function cancel()
111
    {
112 1
        $this->actionCancelled = true;
113 1
    }
114
115
    /**
116
     * Check if the action is cancelled
117
     * 
118
     * @return boolean
119
     */
120 10
    public function isCancelled()
121
    {
122 10
        return $this->actionCancelled;
123
    }
124
    
125
    /**
126
     * Run the controller
127
     *
128
     * @return ResponseInterface
129
     */
130 13
    public function run()
131
    {
132 13
        $route = $this->getRoute();
133 11
        $method = $this->getActionMethod(isset($route->action) ? $route->action : 'default');
134
135 11
        if (!method_exists($this, $method)) {
136 1
            return $this->notFound();
137
        }
138
139 10
        $this->before();
140
        
141 10
        if (!$this->isCancelled()) {
142 9
            $args = isset($route->args) ? $route->args
143 9
                : $this->getFunctionArgs($route, new \ReflectionMethod($this, $method)); 
144
145 8
            call_user_func_array([$this, $method], $args);
146 8
        }
147
        
148 9
        $this->after();
149 9
    }
150
151
    /**
152
     * Get the arguments for a function from a route using reflection
153
     * 
154
     * @param object $route
155
     * @param \ReflectionFunctionAbstract $refl
156
     * @return array
157
     */
158 6
    protected function getFunctionArgs($route, \ReflectionFunctionAbstract $refl)
159
    {
160 6
        $args = [];
161 6
        $params = $refl->getParameters();
162
163 6
        foreach ($params as $param) {
164 6
            $key = $param->name;
165
166 6
            if (property_exists($route, $key)) {
167 5
                $value = $route->$key;
168 5
            } else {
169 4
                if (!$param->isOptional()) {
170 1
                    $fn = $refl instanceof \ReflectionMethod ? $refl->class . '::' . $refl->name : $refl->name;
171 1
                    throw new \RuntimeException("Missing argument '$key' for {$fn}()");
172
                }
173
                
174 3
                $value = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null;
175
            }
176
177 5
            $args[$key] = $value;
178 5
        }
179
        
180 5
        return $args;
181
    }
182
}
183
184