Completed
Pull Request — master (#40)
by
unknown
10:59
created

Action::_loadExtensions()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 3
nop 0
dl 0
loc 12
ccs 10
cts 10
cp 1
crap 3
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Copyright 2016 - 2017, 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 - 2017, 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 123
    public function __construct(array $config = [])
94
    {
95 123
        if (!empty($config['name'])) {
96 81
            $this->setName($config['name']);
97 81
        }
98 123
        if (!empty($config['service'])) {
99 123
            $this->setService($config['service']);
100 123
        }
101 123
        if (!empty($config['route'])) {
102 64
            $this->setRoute($config['route']);
103 64
        }
104 123
        if (!empty($config['Extension'])) {
105 49
            $this->extensions = (Hash::merge($this->extensions, $config['Extension']));
106 49
        }
107 123
        $extensionRegistry = $eventManager = null;
108 123
        if (!empty($config['eventManager'])) {
109
            $eventManager = $config['eventManager'];
110
        }
111 123
        $this->_eventManager = $eventManager ?: new EventManager();
112 123
        $this->setConfig($config);
113 123
        $this->initialize($config);
114 123
        $this->_eventManager->on($this);
115 123
        $this->extensions($extensionRegistry);
116 123
        $this->_loadExtensions();
117 123
    }
118
119
    /**
120
     * Initialize an action instance
121
     *
122
     * @param array $config Configuration options passed to the constructor
123
     * @return void
124
     */
125 123
    public function initialize(array $config)
126
    {
127 123
        $this->Auth = $this->_initializeAuth();
128 123
    }
129
130
    /**
131
     * Gets an action name.
132
     *
133
     * @return string
134
     */
135 79
    public function getName()
136
    {
137 79
        return $this->_name;
138
    }
139
140
    /**
141
     * Sets an action name.
142
     *
143
     * @param string $name An action name.
144
     * @return $this
145
     */
146 81
    public function setName($name)
147
    {
148 81
        $this->_name = $name;
149
150 81
        return $this;
151
    }
152
153
    /**
154
     * Get and set service name.
155
     *
156
     * @param string $name Action name.
157
     * @deprecated 3.4.0 Use setName()/getName() instead.
158
     * @return string
159
     */
160
    public function name($name = null)
161
    {
162
        if ($name !== null) {
163
            return $this->setName($name);
164
        }
165
166
        return $this->getName();
167
    }
168
169
    /**
170
     * Returns activated route.
171
     *
172
     * @return array
173
     */
174 13
    public function getRoute()
175
    {
176 13
        return $this->_route;
177
    }
178
179
    /**
180
     * Sets activated route.
181
     *
182
     * @param array $route Route config.
183
     * @return $this
184
     */
185 64
    public function setRoute(array $route)
186
    {
187 64
        $this->_route = $route;
188
189 64
        return $this;
190
    }
191
192
    /**
193
     * Api method for activated route.
194
     *
195
     * @param array $route Activated route.
196
     * @return array
197
     */
198 8
    public function route($route = null)
199
    {
200 8
        if ($route !== null) {
201
            return $this->setRoute($route);
202
        }
203
204 8
        return $this->getRoute();
205
    }
206
207
    /**
208
     * @return Service
209
     */
210 9
    public function getService()
211
    {
212 9
        return $this->_service;
213
    }
214
215
    /**
216
     * Set a service
217
     *
218
     * @param Service $service service
219
     */
220 123
    public function setService(Service $service)
221
    {
222 123
        $this->_service = $service;
223 123
    }
224
225
    /**
226
     * Set or get service.
227
     *
228
     * @param Service $service An Service instance.
229
     * @return Service
230
     */
231 28
    public function service($service = null)
232
    {
233 28
        if ($service !== null) {
234
            return $this->setService($service);
235
        }
236
237 28
        return $this->getService();
238
    }
239
240
    /**
241
     * Apply validation process.
242
     *
243
     * @return bool
244
     */
245 47
    public function validates()
246
    {
247 47
        return true;
248
    }
249
250
    /**
251
     * Execute action.
252
     *
253
     * @return mixed
254
     */
255
    abstract public function execute();
256
257
    /**
258
     * Action execution life cycle.
259
     *
260
     * @return mixed
261
     */
262 60
    public function process()
263
    {
264 60
        $event = $this->dispatchEvent('Action.beforeProcess', ['action' => $this]);
265
266 60
        if ($event->isStopped()) {
267
            return $event->result;
268
        }
269
270 60
        $event = new Event('Action.onAuth', $this, ['action' => $this]);
271 60
        $this->Auth->authCheck($event);
272
273 59
        $event = $this->dispatchEvent('Action.beforeValidate', compact('data'));
274
275 59
        if ($event->isStopped()) {
276
            $this->dispatchEvent('Action.beforeValidateStopped', []);
277
278
            return $event->result;
279
        }
280
281 59
        if (!$this->validates()) {
282
            $this->dispatchEvent('Action.validationFailed', []);
283
            throw new ValidationException(__('Validation failed'), 0, null, []);
284
        }
285
286 58
        $event = $this->dispatchEvent('Action.beforeExecute', compact('data'));
287
288 58
        if ($event->isStopped()) {
289
            $this->dispatchEvent('Action.beforeExecuteStopped', []);
290
291
            return $event->result;
292
        }
293
294
        // thrown before execute action event (with stop on false)
295 58
        if (method_exists($this, 'action')) {
296 1
            $result = $this->_executeAction();
297 1
        } else {
298 57
            $result = $this->execute();
299
        }
300 56
        $this->dispatchEvent('Action.afterProcess', compact('result'));
301
302 56
        return $result;
303
    }
304
305
    /**
306
     * Execute action call to the method.
307
     *
308
     * This method pass action params as method params.
309
     *
310
     * @param string $methodName Method name.
311
     * @return mixed
312
     * @throws Exception
313
     */
314 1
    protected function _executeAction($methodName = 'action')
315
    {
316 1
        $parser = $this->getService()->getParser();
317 1
        $params = $parser->params();
318 1
        $arguments = [];
319 1
        $reflection = new ReflectionMethod($this, $methodName);
320 1
        foreach ($reflection->getParameters() as $param) {
321 1
            $paramName = $param->getName();
322 1
            if (!isset($params[$paramName])) {
323
                if ($param->isOptional()) {
324
                    $arguments[] = $param->getDefaultValue();
325
                } else {
326
                    throw new Exception('Missing required param: ' . $paramName, 409);
327
                }
328
            } else {
329 1
                if ($params[$paramName] === '' && $param->isOptional()) {
330
                    $arguments[] = $param->getDefaultValue();
331
                } else {
332 1
                    $value = $params[$paramName];
333 1
                    $arguments[] = (is_numeric($value)) ? 0 + $value : $value;
334
                }
335
            }
336 1
        }
337 1
        $result = call_user_func_array([$this, $methodName], $arguments);
338
339 1
        return $result;
340
    }
341
342
    /**
343
     * @return array
344
     */
345 123
    public function implementedEvents()
346
    {
347
        $eventMap = [
348 123
            'Action.beforeProcess' => 'beforeProcess',
349 123
            'Action.beforeValidate' => 'beforeValidate',
350 123
            'Action.beforeExecute' => 'beforeExecute',
351 123
            'Action.afterProcess' => 'afterProcess',
352 123
        ];
353 123
        $events = [];
354
355 123
        foreach ($eventMap as $event => $method) {
356 123
            if (!method_exists($this, $method)) {
357 123
                continue;
358
            }
359
            $events[$event] = $method;
360 123
        }
361
362 123
        return $events;
363
    }
364
365
    /**
366
     * Returns action input params
367
     *
368
     * @return mixed
369
     */
370 66
    public function data()
371
    {
372 66
        return $this->getService()->getParser()->params();
373
    }
374
375
    /**
376
     * Get the extension registry for this action.
377
     *
378
     * If called with the first parameter, it will be set as the action $this->_extensions property
379
     *
380
     * @param \CakeDC\Api\Service\Action\ExtensionRegistry|null $extensions Extension registry.
381
     *
382
     * @return \CakeDC\Api\Service\Action\ExtensionRegistry
383
     */
384 123
    public function extensions($extensions = null)
385
    {
386 123
        if ($extensions === null && $this->_extensions === null) {
387 123
            $this->_extensions = new ExtensionRegistry($this);
388 123
        }
389 123
        if ($extensions !== null) {
390
            $this->_extensions = $extensions;
391
        }
392
393 123
        return $this->_extensions;
394
    }
395
396
    /**
397
     * Loads the defined extensions using the Extension factory.
398
     *
399
     * @return void
400
     */
401 123
    protected function _loadExtensions()
402
    {
403 123
        if (empty($this->extensions)) {
404 74
            return;
405
        }
406 49
        $registry = $this->extensions();
407 49
        $extensions = $registry->normalizeArray($this->extensions);
408 49
        foreach ($extensions as $properties) {
409 49
            $instance = $registry->load($properties['class'], $properties['config']);
410 49
            $this->_eventManager->on($instance);
411 49
        }
412 49
    }
413
414
    /**
415
     * Initialize auth.
416
     *
417
     * @return Auth
418
     */
419 123
    protected function _initializeAuth()
420
    {
421 123
        $config = $this->_authConfig();
422 123
        $auth = new Auth($config);
423 123
        if (array_key_exists('allow', $config)) {
424
            $auth->allow($config['allow']);
425
426
            return $auth;
427
        }
428
429 123
        return $auth;
430
    }
431
432
    /**
433
     * Prepare Auth configuration.
434
     *
435
     * @return array
436
     */
437 123
    protected function _authConfig()
438
    {
439 123
        $defaultConfig = (array)$this->getConfig('Auth');
440
441 123
        return Hash::merge($defaultConfig, [
442 123
            'service' => $this->_service,
443 123
            'request' => $this->_service->getRequest(),
444 123
            'response' => $this->_service->getResponse(),
445 123
            'action' => $this,
446 123
        ]);
447
    }
448
}
449