Action   B
last analyzed

Complexity

Total Complexity 52

Size/Duplication

Total Lines 476
Duplicated Lines 0 %

Coupling/Cohesion

Components 4
Dependencies 11

Test Coverage

Coverage 69.09%

Importance

Changes 0
Metric Value
dl 0
loc 476
ccs 114
cts 165
cp 0.6909
rs 7.44
c 0
b 0
f 0
wmc 52
lcom 4
cbo 11

24 Methods

Rating   Name   Duplication   Size   Complexity  
execute() 0 1 ?
B __construct() 0 25 7
A initialize() 0 4 1
A getName() 0 4 1
A setName() 0 6 1
A name() 0 13 2
A getRoute() 0 4 1
A setRoute() 0 6 1
A route() 0 13 2
A getService() 0 4 1
A setService() 0 4 1
A service() 0 13 2
A validates() 0 4 1
B process() 0 42 6
B _executeAction() 0 27 7
A implementedEvents() 0 19 3
A getData() 0 4 1
A data() 0 9 1
A getExtensions() 0 4 1
A setExtensions() 0 10 3
A extensions() 0 13 3
A _loadExtensions() 0 12 3
A _initializeAuth() 0 12 2
A _authConfig() 0 11 1

How to fix   Complexity   

Complex Class

Complex classes like Action often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Action, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Copyright 2016 - 2018, 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 - 2018, 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->setExtensions($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
        deprecationWarning(
163
            'Action::name() is deprecated. ' .
164
            'Use Action::setName()/getName() instead.'
165
        );
166
167
        if ($name !== null) {
168
            return $this->setName($name);
169
        }
170
171
        return $this->getName();
172
    }
173
174
    /**
175
     * Returns activated route.
176
     *
177
     * @return array
178
     */
179 13
    public function getRoute()
180
    {
181 13
        return $this->_route;
182
    }
183
184
    /**
185
     * Sets activated route.
186
     *
187
     * @param array $route Route config.
188
     * @return $this
189
     */
190 64
    public function setRoute(array $route)
191
    {
192 64
        $this->_route = $route;
193
194 64
        return $this;
195
    }
196
197
    /**
198
     * Api method for activated route.
199
     *
200
     * @param array $route Activated route.
201
     * @deprecated 3.4.0 Use setRoute()/getRoute() instead.
202
     * @return array
203
     */
204
    public function route($route = null)
205
    {
206
        deprecationWarning(
207
            'Action::route() is deprecated. ' .
208
            'Use Action::setRoute()/getRoute() instead.'
209
        );
210
211
        if ($route !== null) {
212
            return $this->setRoute($route);
213
        }
214
215
        return $this->getRoute();
216
    }
217
218
    /**
219
     * @return Service
220
     */
221 9
    public function getService()
222
    {
223 9
        return $this->_service;
224
    }
225
226
    /**
227
     * Set a service
228
     *
229
     * @param Service $service service
230
     */
231 123
    public function setService(Service $service)
232
    {
233 123
        $this->_service = $service;
234 123
    }
235
236
    /**
237
     * Set or get service.
238
     *
239
     * @param Service $service An Service instance.
240
     * @return Service
241
     * @deprecated 3.4.0 Use setService()/getService() instead.
242
     */
243
    public function service($service = null)
244
    {
245
        deprecationWarning(
246
            'Action::service() is deprecated. ' .
247
            'Use Action::setService()/getService() instead.'
248
        );
249
250
        if ($service !== null) {
251
            return $this->setService($service);
252
        }
253
254
        return $this->getService();
255
    }
256
257
    /**
258
     * Apply validation process.
259
     *
260
     * @return bool
261
     */
262 47
    public function validates()
263
    {
264 47
        return true;
265
    }
266
267
    /**
268
     * Execute action.
269
     *
270
     * @return mixed
271
     */
272
    abstract public function execute();
273
274
    /**
275
     * Action execution life cycle.
276
     *
277
     * @return mixed
278
     */
279 60
    public function process()
280
    {
281 60
        $event = $this->dispatchEvent('Action.beforeProcess', ['action' => $this]);
282
283 60
        if ($event->isStopped()) {
284
            return $event->result;
285
        }
286
287 60
        $event = new Event('Action.onAuth', $this, ['action' => $this]);
288 60
        $this->Auth->authCheck($event);
289
290 59
        $event = $this->dispatchEvent('Action.beforeValidate', []);
291
292 59
        if ($event->isStopped()) {
293
            $this->dispatchEvent('Action.beforeValidateStopped', []);
294
295
            return $event->result;
296
        }
297
298 59
        if (!$this->validates()) {
299
            $this->dispatchEvent('Action.validationFailed', []);
300
            throw new ValidationException(__('Validation failed'), 0, null, []);
301
        }
302
303 58
        $event = $this->dispatchEvent('Action.beforeExecute', []);
304
305 58
        if ($event->isStopped()) {
306
            $this->dispatchEvent('Action.beforeExecuteStopped', []);
307
308
            return $event->result;
309
        }
310
311
        // thrown before execute action event (with stop on false)
312 58
        if (method_exists($this, 'action')) {
313 1
            $result = $this->_executeAction();
314 1
        } else {
315 57
            $result = $this->execute();
316
        }
317 56
        $this->dispatchEvent('Action.afterProcess', compact('result'));
318
319 56
        return $result;
320
    }
321
322
    /**
323
     * Execute action call to the method.
324
     *
325
     * This method pass action params as method params.
326
     *
327
     * @param string $methodName Method name.
328
     * @return mixed
329
     * @throws Exception
330
     */
331 1
    protected function _executeAction($methodName = 'action')
332
    {
333 1
        $parser = $this->getService()->getParser();
334 1
        $params = $parser->getParams();
335 1
        $arguments = [];
336 1
        $reflection = new ReflectionMethod($this, $methodName);
337 1
        foreach ($reflection->getParameters() as $param) {
338 1
            $paramName = $param->getName();
0 ignored issues
show
Bug introduced by
Consider using $param->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
339 1
            if (!isset($params[$paramName])) {
340
                if ($param->isOptional()) {
341
                    $arguments[] = $param->getDefaultValue();
342
                } else {
343
                    throw new Exception('Missing required param: ' . $paramName, 409);
344
                }
345
            } else {
346 1
                if ($params[$paramName] === '' && $param->isOptional()) {
347
                    $arguments[] = $param->getDefaultValue();
348
                } else {
349 1
                    $value = $params[$paramName];
350 1
                    $arguments[] = (is_numeric($value)) ? 0 + $value : $value;
351
                }
352
            }
353 1
        }
354 1
        $result = call_user_func_array([$this, $methodName], $arguments);
355
356 1
        return $result;
357
    }
358
359
    /**
360
     * @return array
361
     */
362 123
    public function implementedEvents()
363
    {
364
        $eventMap = [
365 123
            'Action.beforeProcess' => 'beforeProcess',
366 123
            'Action.beforeValidate' => 'beforeValidate',
367 123
            'Action.beforeExecute' => 'beforeExecute',
368 123
            'Action.afterProcess' => 'afterProcess',
369 123
        ];
370 123
        $events = [];
371
372 123
        foreach ($eventMap as $event => $method) {
373 123
            if (!method_exists($this, $method)) {
374 123
                continue;
375
            }
376
            $events[$event] = $method;
377 123
        }
378
379 123
        return $events;
380
    }
381
382
    /**
383
     * Returns action input params
384
     *
385
     * @return mixed
386
     */
387 66
    public function getData()
388
    {
389 66
        return $this->getService()->getParser()->getParams();
390
    }
391
392
    /**
393
     * Returns action input params
394
     *
395
     * @return mixed
396
     * @deprecated 3.6.0 Use getData() instead.
397
     */
398
    public function data()
399
    {
400
        deprecationWarning(
401
            'Action::data() is deprecated. ' .
402
            'Use Action::getData() instead.'
403
        );
404
405
        return $this->getData();
406
    }
407
408
    /**
409
     * @return \CakeDC\Api\Service\Action\ExtensionRegistry
410
     */
411 49
    public function getExtensions()
412
    {
413 49
        return $this->_extensions;
414
    }
415
416
    /**
417
     * Set a service
418
     *
419
     * @param \CakeDC\Api\Service\Action\ExtensionRegistry|null $extensions Extension registry.
420
     */
421 123
    public function setExtensions($extensions = null)
422
    {
423 123
        if ($extensions === null && $this->_extensions === null) {
424 123
            $this->_extensions = new ExtensionRegistry($this);
425 123
        } else {
426
            $this->_extensions = $extensions;
427
        }
428
429 123
        return $this;
430
    }
431
432
    /**
433
     * Get the extension registry for this action.
434
     *
435
     * If called with the first parameter, it will be set as the action $this->_extensions property
436
     *
437
     * @param \CakeDC\Api\Service\Action\ExtensionRegistry|null $extensions Extension registry.
438
     *
439
     * @return \CakeDC\Api\Service\Action\ExtensionRegistry
440
     * @deprecated 3.6.0 Use setExtensions()/getExtensions() instead.
441
     */
442
    public function extensions($extensions = null)
443
    {
444
        deprecationWarning(
445
            'Action::extensions() is deprecated. ' .
446
            'Use Action::setExtensions()/getExtensions() instead.'
447
        );
448
449
        if ($this->_extensions === null && $extensions === null) {
450
            $this->setExtensions($extensions);
451
452
            return $this->getExtensions();
453
        }
454
    }
455
456
    /**
457
     * Loads the defined extensions using the Extension factory.
458
     *
459
     * @return void
460
     */
461
    protected function _loadExtensions()
462
    {
463
        if (empty($this->extensions)) {
464
            return;
465 123
        }
466
        $registry = $this->getExtensions();
467 123
        $extensions = $registry->normalizeArray($this->extensions);
468 74
        foreach ($extensions as $properties) {
469
            $instance = $registry->load($properties['class'], $properties['config']);
470 49
            $this->_eventManager->on($instance);
471 49
        }
472 49
    }
473 49
474 49
    /**
475 49
     * Initialize auth.
476 49
     *
477
     * @return Auth
478
     */
479
    protected function _initializeAuth()
480
    {
481
        $config = $this->_authConfig();
482
        $auth = new Auth($config);
483 123
        if (array_key_exists('allow', $config)) {
484
            $auth->allow($config['allow']);
485 123
486 123
            return $auth;
487 123
        }
488
489
        return $auth;
490
    }
491
492
    /**
493 123
     * Prepare Auth configuration.
494
     *
495
     * @return array
496
     */
497
    protected function _authConfig()
498
    {
499
        $defaultConfig = (array)$this->getConfig('Auth');
500
501 123
        return Hash::merge($defaultConfig, [
502
            'service' => $this->_service,
503 123
            'request' => $this->_service->getRequest(),
504
            'response' => $this->_service->getResponse(),
505 123
            'action' => $this,
506 123
        ]);
507 123
    }
508
}
509