Completed
Push — master ( 62946e...cfb4fd )
by
unknown
07:23
created

Action::implementedEvents()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 3.004

Importance

Changes 0
Metric Value
cc 3
eloc 12
nc 3
nop 0
dl 0
loc 19
ccs 12
cts 13
cp 0.9231
crap 3.004
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Copyright 2016, Cake Development Corporation (http://cakedc.com)
4
 *
5
 * Licensed under The MIT License
6
 * Redistributions of files must retain the above copyright notice.
7
 *
8
 * @copyright Copyright 2016, Cake Development Corporation (http://cakedc.com)
9
 * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
10
 */
11
12
namespace CakeDC\Api\Service\Action;
13
14
use CakeDC\Api\Exception\ValidationException;
15
use CakeDC\Api\Service\Auth\Auth;
16
use CakeDC\Api\Service\Service;
17
use Cake\Core\InstanceConfigTrait;
18
use Cake\Event\Event;
19
use Cake\Event\EventDispatcherInterface;
20
use Cake\Event\EventDispatcherTrait;
21
use Cake\Event\EventListenerInterface;
22
use Cake\Event\EventManager;
23
use Cake\Utility\Hash;
24
use Cake\Validation\ValidatorAwareTrait;
25
use Exception;
26
use ReflectionMethod;
27
28
/**
29
 * Class Action
30
 *
31
 * @package CakeDC\Api\Service\Action
32
 */
33
abstract class Action implements EventListenerInterface, EventDispatcherInterface
34
{
35
    use EventDispatcherTrait;
36
    use InstanceConfigTrait;
37
    use ValidatorAwareTrait;
38
39
    /**
40
     * Extensions to load and attach to listener
41
     *
42
     * @var array
43
     */
44
    public $extensions = [];
45
46
    /**
47
     * An Auth instance.
48
     *
49
     * @var Auth
50
     */
51
    public $Auth;
52
53
    /**
54
     * Default Action options.
55
     *
56
     * @var array
57
     */
58
    protected $_defaultConfig = [];
59
60
    /**
61
     * A Service reference.
62
     *
63
     * @var Service
64
     */
65
    protected $_service;
66
67
    /**
68
     * Activated route.
69
     *
70
     * @var array
71
     */
72
    protected $_route = null;
73
74
    /**
75
     * Extension registry.
76
     *
77
     * @var \CakeDC\Api\Service\Action\ExtensionRegistry
78
     */
79
    protected $_extensions;
80
81
    /**
82
     * Action name.
83
     *
84
     * @var string
85
     */
86
    protected $_name;
87
88
    /**
89
     * Action constructor.
90
     *
91
     * @param array $config Configuration options passed to the constructor
92
     */
93 101
    public function __construct(array $config = [])
94
    {
95 101
        if (!empty($config['name'])) {
96 80
            $this->name($config['name']);
97 80
        }
98 101
        if (!empty($config['service'])) {
99 101
            $this->service($config['service']);
100 101
        }
101 101
        if (!empty($config['route'])) {
102 63
            $this->_route = $config['route'];
103 63
        }
104 101
        if (!empty($config['Extension'])) {
105 49
            $this->extensions = (Hash::merge($this->extensions, $config['Extension']));
106 49
        }
107 101
        $extensionRegistry = $eventManager = null;
108 101
        if (!empty($config['eventManager'])) {
109
            $eventManager = $config['eventManager'];
110
        }
111 101
        $this->_eventManager = $eventManager ?: new EventManager();
112 101
        $this->config($config);
113 101
        $this->initialize($config);
114 101
        $this->_eventManager->on($this);
115 101
        $this->extensions($extensionRegistry);
116 101
        $this->_loadExtensions();
117 101
    }
118
119
120
    /**
121
     * Initialize an action instance
122
     *
123
     * @param array $config Configuration options passed to the constructor
124
     * @return void
125
     */
126 101
    public function initialize(array $config)
127
    {
128 101
        $this->Auth = $this->_initializeAuth();
129 101
    }
130
131
132
    /**
133
     * Api method for action name.
134
     *
135
     * @param string $name Action name.
136
     * @return string
137
     */
138 81
    public function name($name = null)
139
    {
140 81
        if ($name === null) {
141 78
            return $this->_name;
142
        }
143 80
        $this->_name = $name;
144
145 80
        return $this->_name;
146
    }
147
148
    /**
149
     * Api method for activated route.
150
     *
151
     * @param array $route Activated route.
152
     * @return array
153
     */
154 13
    public function route($route = null)
155
    {
156 13
        if ($route === null) {
157 13
            return $this->_route;
158
        }
159
        $this->_route = $route;
160
161
        return $this->_route;
162
    }
163
164
    /**
165
     * Set or get service.
166
     *
167
     * @param Service $service An Service instance.
168
     * @return Service
169
     */
170 101
    public function service($service = null)
171
    {
172 101
        if ($service === null) {
173 99
            return $this->_service;
174
        }
175 101
        $this->_service = $service;
176
177 101
        return $this->_service;
178
    }
179
180
    /**
181
     * Apply validation process.
182
     *
183
     * @return bool
184
     */
185 47
    public function validates()
186
    {
187 47
        return true;
188
    }
189
190
    /**
191
     * Execute action.
192
     *
193
     * @return mixed
194
     */
195
    abstract public function execute();
196
197
    /**
198
     * Action execution life cycle.
199
     *
200
     * @return mixed
201
     */
202 59
    public function process()
203
    {
204 59
        $event = $this->dispatchEvent('Action.beforeProcess', ['action' => $this]);
205
206 59
        if ($event->isStopped()) {
207
            return $event->result;
208
        }
209
210 59
        $event = new Event('Action.onAuth', $this, ['action' => $this]);
211 59
        $this->Auth->authCheck($event);
212
213 58
        $event = $this->dispatchEvent('Action.beforeValidate', compact('data'));
214
215 58
        if ($event->isStopped()) {
216
            $this->dispatchEvent('Action.beforeValidateStopped', []);
217
            return $event->result;
218
        }
219
220 58
        if (!$this->validates()) {
221
            $this->dispatchEvent('Action.validationFailed', []);
222
            throw new ValidationException(__('Validation failed'), 0, null, []);
223
        }
224
225 57
        $event = $this->dispatchEvent('Action.beforeExecute', compact('data'));
226
227 57
        if ($event->isStopped()) {
228
            $this->dispatchEvent('Action.beforeExecuteStopped', []);
229
            return $event->result;
230
        }
231
232
        // thrown before execute action event (with stop on false)
233 57
        if (method_exists($this, 'action')) {
234 1
            $result = $this->_executeAction();
235 1
        } else {
236 56
            $result = $this->execute();
237
        }
238 55
        $this->dispatchEvent('Action.afterProcess', compact('result'));
239
240 55
        return $result;
241
    }
242
243
    /**
244
     * Execute action call to the method.
245
     *
246
     * This method pass action params as method params.
247
     *
248
     * @param string $methodName Method name.
249
     * @return mixed
250
     * @throws Exception
251
     */
252 1
    protected function _executeAction($methodName = 'action')
253
    {
254 1
        $parser = $this->service()->parser();
255 1
        $params = $parser->params();
256 1
        $arguments = [];
257 1
        $reflection = new ReflectionMethod($this, $methodName);
258 1
        foreach ($reflection->getParameters() as $param) {
259 1
            $paramName = $param->getName();
260 1
            if (!isset($params[$paramName])) {
261
                if ($param->isOptional()) {
262
                    $arguments[] = $param->getDefaultValue();
263
                } else {
264
                    throw new Exception('Missing required param: ' . $paramName, 409);
265
                }
266
            } else {
267 1
                if ($params[$paramName] === '' && $param->isOptional()) {
268
                    $arguments[] = $param->getDefaultValue();
269
                } else {
270 1
                    $value = $params[$paramName];
271 1
                    $arguments[] = (is_numeric($value)) ? 0 + $value : $value;
272
                }
273
            }
274 1
        }
275 1
        $result = call_user_func_array([$this, $methodName], $arguments);
276
277 1
        return $result;
278
    }
279
280
    /**
281
     * @return array
282
     */
283 101
    public function implementedEvents()
284
    {
285
        $eventMap = [
286 101
            'Action.beforeProcess' => 'beforeProcess',
287 101
            'Action.beforeValidate' => 'beforeValidate',
288 101
            'Action.beforeExecute' => 'beforeExecute',
289 101
            'Action.afterProcess' => 'afterProcess',
290 101
        ];
291 101
        $events = [];
292
293 101
        foreach ($eventMap as $event => $method) {
294 101
            if (!method_exists($this, $method)) {
295 101
                continue;
296
            }
297
            $events[$event] = $method;
298 101
        }
299
300 101
        return $events;
301
    }
302
303
    /**
304
     * Returns action input params
305
     *
306
     * @return mixed
307
     */
308 51
    public function data()
309
    {
310 51
        return $this->service()->parser()->params();
311
    }
312
313
    /**
314
     * Get the extension registry for this action.
315
     *
316
     * If called with the first parameter, it will be set as the action $this->_extensions property
317
     *
318
     * @param \CakeDC\Api\Service\Action\ExtensionRegistry|null $extensions Extension registry.
319
     *
320
     * @return \CakeDC\Api\Service\Action\ExtensionRegistry
321
     */
322 101
    public function extensions($extensions = null)
323
    {
324 101
        if ($extensions === null && $this->_extensions === null) {
325 101
            $this->_extensions = new ExtensionRegistry($this);
326 101
        }
327 101
        if ($extensions !== null) {
328
            $this->_extensions = $extensions;
329
        }
330
331 101
        return $this->_extensions;
332
    }
333
334
    /**
335
     * Loads the defined extensions using the Extension factory.
336
     *
337
     * @return void
338
     */
339 101
    protected function _loadExtensions()
340
    {
341 101
        if (empty($this->extensions)) {
342 52
            return;
343
        }
344 49
        $registry = $this->extensions();
345 49
        $extensions = $registry->normalizeArray($this->extensions);
346 49
        foreach ($extensions as $properties) {
347 49
            $instance = $registry->load($properties['class'], $properties['config']);
348 49
            $this->_eventManager->on($instance);
349 49
        }
350 49
    }
351
352
    /**
353
     * Initialize auth.
354
     *
355
     * @return Auth
356
     */
357 101
    protected function _initializeAuth()
358
    {
359 101
        $config = $this->_authConfig();
360 101
        $auth = new Auth($config);
361 101
        if (array_key_exists('allow', $config)) {
362
            $auth->allow($config['allow']);
363
364
            return $auth;
365
        }
366
367 101
        return $auth;
368
    }
369
370
    /**
371
     * Prepare Auth configuration.
372
     *
373
     * @return array
374
     */
375 101
    protected function _authConfig()
376
    {
377 101
        $defaultConfig = (array)$this->config('Auth');
378
379 101
        return Hash::merge($defaultConfig, [
380 101
            'service' => $this->_service,
381 101
            'request' => $this->_service->request(),
382 101
            'response' => $this->_service->response(),
383 101
            'action' => $this,
384 101
        ]);
385
    }
386
}
387