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; |
13
|
|
|
|
14
|
|
|
use CakeDC\Api\Exception\ValidationException; |
15
|
|
|
use CakeDC\Api\Routing\ApiRouter; |
16
|
|
|
use CakeDC\Api\Service\Action\DummyAction; |
17
|
|
|
use CakeDC\Api\Service\Action\Result; |
18
|
|
|
use CakeDC\Api\Service\Exception\MissingActionException; |
19
|
|
|
use CakeDC\Api\Service\Exception\MissingParserException; |
20
|
|
|
use CakeDC\Api\Service\Exception\MissingRendererException; |
21
|
|
|
use CakeDC\Api\Service\Renderer\BaseRenderer; |
22
|
|
|
use CakeDC\Api\Service\RequestParser\BaseParser; |
23
|
|
|
|
24
|
|
|
use Cake\Core\App; |
25
|
|
|
use Cake\Core\Configure; |
26
|
|
|
use Cake\Datasource\Exception\RecordNotFoundException; |
27
|
|
|
|
28
|
|
|
use Cake\Event\EventDispatcherInterface; |
29
|
|
|
use Cake\Event\EventDispatcherTrait; |
30
|
|
|
use Cake\Event\EventListenerInterface; |
31
|
|
|
use Cake\Event\EventManager; |
32
|
|
|
use Cake\Http\Response; |
33
|
|
|
use Cake\Http\ServerRequest; |
34
|
|
|
use Cake\Routing\RouteBuilder; |
35
|
|
|
use Cake\Utility\Hash; |
36
|
|
|
use Cake\Utility\Inflector; |
37
|
|
|
use Exception; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* Class Service |
41
|
|
|
*/ |
42
|
|
|
abstract class Service implements EventListenerInterface, EventDispatcherInterface |
43
|
|
|
{ |
44
|
|
|
use EventDispatcherTrait; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Extensions to load and attach to listener |
48
|
|
|
* |
49
|
|
|
* @var array |
50
|
|
|
*/ |
51
|
|
|
public $extensions = []; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Actions routes description map, indexed by action name. |
55
|
|
|
* |
56
|
|
|
* @var array |
57
|
|
|
*/ |
58
|
|
|
protected $_actions = []; |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* Actions classes map, indexed by action name. |
62
|
|
|
* |
63
|
|
|
* @var array |
64
|
|
|
*/ |
65
|
|
|
protected $_actionsClassMap = []; |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Service url acceptable extensions list. |
69
|
|
|
* |
70
|
|
|
* @var array |
71
|
|
|
*/ |
72
|
|
|
protected $_routeExtensions = ['json']; |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* |
76
|
|
|
* |
77
|
|
|
* @var string |
78
|
|
|
*/ |
79
|
|
|
protected $_routePrefix = ''; |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Service name |
83
|
|
|
* |
84
|
|
|
* @var string |
85
|
|
|
*/ |
86
|
|
|
protected $_name = null; |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* Service version. |
90
|
|
|
* |
91
|
|
|
* @var int |
92
|
|
|
*/ |
93
|
|
|
protected $_version; |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* Parser class to process the HTTP request. |
97
|
|
|
* |
98
|
|
|
* @var BaseParser |
99
|
|
|
*/ |
100
|
|
|
protected $_parser; |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* Renderer class to build the HTTP response. |
104
|
|
|
* |
105
|
|
|
* @var BaseRenderer |
106
|
|
|
*/ |
107
|
|
|
protected $_renderer; |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* The parser class. |
111
|
|
|
* |
112
|
|
|
* @var string |
113
|
|
|
*/ |
114
|
|
|
protected $_parserClass = null; |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* The Renderer class. |
118
|
|
|
* |
119
|
|
|
* @var string |
120
|
|
|
*/ |
121
|
|
|
protected $_rendererClass = null; |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* Dependent services names list |
125
|
|
|
* |
126
|
|
|
* @var array<string> |
127
|
|
|
*/ |
128
|
|
|
protected $_innerServices = []; |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* Parent service instance. |
132
|
|
|
* |
133
|
|
|
* @var Service |
134
|
|
|
*/ |
135
|
|
|
protected $_parentService; |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* Service Action Result object. |
139
|
|
|
* |
140
|
|
|
* @var Result |
141
|
|
|
*/ |
142
|
|
|
protected $_result; |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* Base url for service. |
146
|
|
|
* |
147
|
|
|
* @var string |
148
|
|
|
*/ |
149
|
|
|
protected $_baseUrl; |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* Request |
153
|
|
|
* |
154
|
|
|
* @var \Cake\Http\ServerRequest |
155
|
|
|
*/ |
156
|
|
|
protected $_request; |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* Request |
160
|
|
|
* |
161
|
|
|
* @var \Cake\Http\Response |
162
|
|
|
*/ |
163
|
|
|
protected $_response; |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* @var string |
167
|
|
|
*/ |
168
|
|
|
protected $_corsSuffix = '_cors'; |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* Extension registry. |
172
|
|
|
* |
173
|
|
|
* @var \CakeDC\Api\Service\ExtensionRegistry |
174
|
|
|
*/ |
175
|
|
|
protected $_extensions; |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* Service constructor. |
179
|
|
|
* |
180
|
|
|
* @param array $config Service configuration. |
181
|
|
|
*/ |
182
|
140 |
|
public function __construct(array $config = []) |
183
|
|
|
{ |
184
|
140 |
|
if (isset($config['request'])) { |
185
|
140 |
|
$this->setRequest($config['request']); |
186
|
140 |
|
} |
187
|
140 |
|
if (isset($config['response'])) { |
188
|
140 |
|
$this->setResponse($config['response']); |
189
|
140 |
|
} |
190
|
140 |
|
if (isset($config['baseUrl'])) { |
191
|
93 |
|
$this->_baseUrl = $config['baseUrl']; |
192
|
93 |
|
} |
193
|
140 |
|
if (isset($config['service'])) { |
194
|
111 |
|
$this->setName($config['service']); |
195
|
111 |
|
} |
196
|
140 |
|
if (isset($config['version'])) { |
197
|
|
|
$this->setVersion($config['version']); |
198
|
|
|
} |
199
|
140 |
|
if (isset($config['classMap'])) { |
200
|
1 |
|
$this->_actionsClassMap = Hash::merge($this->_actionsClassMap, $config['classMap']); |
201
|
1 |
|
} |
202
|
|
|
|
203
|
140 |
|
if (!empty($config['Extension'])) { |
204
|
|
|
$this->extensions = (Hash::merge($this->extensions, $config['Extension'])); |
205
|
|
|
} |
206
|
140 |
|
$extensionRegistry = $eventManager = null; |
207
|
140 |
|
if (!empty($config['eventManager'])) { |
208
|
|
|
$eventManager = $config['eventManager']; |
209
|
|
|
} |
210
|
140 |
|
$this->_eventManager = $eventManager ?: new EventManager(); |
211
|
|
|
|
212
|
140 |
|
$this->initialize(); |
213
|
140 |
|
$this->_initializeParser($config); |
214
|
140 |
|
$this->_initializeRenderer($config); |
215
|
140 |
|
$this->_eventManager->on($this); |
216
|
140 |
|
$this->setExtensions($extensionRegistry); |
|
|
|
|
217
|
140 |
|
$this->_loadExtensions(); |
218
|
140 |
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* Initialize method |
222
|
|
|
* |
223
|
|
|
* @return void |
224
|
|
|
*/ |
225
|
140 |
|
public function initialize() |
226
|
|
|
{ |
227
|
140 |
|
if ($this->_name === null) { |
228
|
46 |
|
$className = (new \ReflectionClass($this))->getShortName(); |
229
|
46 |
|
$this->setName(Inflector::underscore(str_replace('Service', '', $className))); |
|
|
|
|
230
|
46 |
|
} |
231
|
140 |
|
} |
232
|
|
|
|
233
|
|
|
/** |
234
|
|
|
* Gets service name. |
235
|
|
|
* |
236
|
|
|
* @return string |
237
|
|
|
*/ |
238
|
139 |
|
public function getName() |
239
|
|
|
{ |
240
|
139 |
|
return $this->_name; |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
/** |
244
|
|
|
* Sets service name. |
245
|
|
|
* |
246
|
|
|
* @param string $name Service name. |
247
|
|
|
* @return $this |
248
|
|
|
*/ |
249
|
140 |
|
public function setName($name) |
250
|
|
|
{ |
251
|
140 |
|
$this->_name = $name; |
252
|
|
|
|
253
|
140 |
|
return $this; |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
/** |
257
|
|
|
* Get and set service name. |
258
|
|
|
* |
259
|
|
|
* @param string $name Service name. |
260
|
|
|
* @deprecated 3.4.0 Use setName()/getName() instead. |
261
|
|
|
* @return string |
262
|
|
|
*/ |
263
|
|
|
public function name($name = null) |
264
|
|
|
{ |
265
|
|
|
if ($name !== null) { |
266
|
|
|
return $this->setName($name); |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
return $this->getName(); |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* Gets service version number. |
274
|
|
|
* |
275
|
|
|
* @return int |
276
|
|
|
*/ |
277
|
81 |
|
public function getVersion() |
278
|
|
|
{ |
279
|
81 |
|
return $this->_version; |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
/** |
283
|
|
|
* Sets service version. |
284
|
|
|
* |
285
|
|
|
* @param int $version Version number. |
286
|
|
|
* @return void |
287
|
|
|
*/ |
288
|
|
|
public function setVersion($version) |
289
|
|
|
{ |
290
|
|
|
$this->_version = $version; |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
/** |
294
|
|
|
* Get and set service version. |
295
|
|
|
* |
296
|
|
|
* @param int $version Version number. |
297
|
|
|
* @deprecated 3.4.0 Use setVersion()/getVersion() instead. |
298
|
|
|
* @return int|$this |
299
|
|
|
*/ |
300
|
|
|
public function version($version = null) |
301
|
|
|
{ |
302
|
|
|
if ($version !== null) { |
303
|
|
|
return $this->setVersion($version); |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
return $this->getVersion(); |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
/** |
310
|
|
|
* Gets the service parser. |
311
|
|
|
* |
312
|
|
|
* @return BaseParser |
313
|
|
|
*/ |
314
|
66 |
|
public function getParser() |
315
|
|
|
{ |
316
|
66 |
|
return $this->_parser; |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* Sets the service parser. |
321
|
|
|
* |
322
|
|
|
* @param BaseParser $parser A Parser instance. |
323
|
|
|
* @return $this |
324
|
|
|
*/ |
325
|
|
|
public function setParser(BaseParser $parser) |
326
|
|
|
{ |
327
|
|
|
$this->_parser = $parser; |
328
|
|
|
|
329
|
|
|
return $this; |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
/** |
333
|
|
|
* Service parser configuration method. |
334
|
|
|
* |
335
|
|
|
* @param BaseParser $parser A Parser instance. |
336
|
|
|
* @deprecated 3.4.0 Use getParser()/setParser() instead. |
337
|
|
|
* @return BaseParser|$this |
338
|
|
|
*/ |
339
|
|
|
public function parser(BaseParser $parser = null) |
340
|
|
|
{ |
341
|
|
|
if ($parser !== null) { |
342
|
|
|
return $this->setParser($parser); |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
return $this->getParser(); |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
/** |
349
|
|
|
* Gets the Request. |
350
|
|
|
* |
351
|
|
|
* @return \Cake\Http\ServerRequest |
352
|
|
|
*/ |
353
|
123 |
|
public function getRequest() |
354
|
|
|
{ |
355
|
123 |
|
return $this->_request; |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
/** |
359
|
|
|
* Sets the Request. |
360
|
|
|
* |
361
|
|
|
* @param \Cake\Http\ServerRequest $request A Request object. |
362
|
|
|
* @return void |
363
|
|
|
*/ |
364
|
140 |
|
public function setRequest(ServerRequest $request) |
365
|
|
|
{ |
366
|
140 |
|
$this->_request = $request; |
367
|
140 |
|
} |
368
|
|
|
|
369
|
|
|
/** |
370
|
|
|
* Get and set request. |
371
|
|
|
* |
372
|
|
|
* @param \Cake\Http\ServerRequest $request A Request object. |
373
|
|
|
* @deprecated 3.4.0 Use getRequest()/setRequest() instead. |
374
|
|
|
* @return \Cake\Http\ServerRequest|$this |
375
|
|
|
*/ |
376
|
1 |
|
public function request($request = null) |
377
|
|
|
{ |
378
|
1 |
|
if ($request !== null) { |
379
|
|
|
return $this->setRequest($request); |
380
|
|
|
} |
381
|
|
|
|
382
|
1 |
|
return $this->getRequest(); |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
/** |
386
|
|
|
* Get the service route scopes and their connected routes. |
387
|
|
|
* |
388
|
|
|
* @return array |
389
|
|
|
*/ |
390
|
3 |
|
public function routes() |
391
|
|
|
{ |
392
|
|
|
return $this->_routesWrapper(function () { |
393
|
3 |
|
return ApiRouter::routes(); |
394
|
3 |
|
}); |
395
|
|
|
} |
396
|
|
|
|
397
|
|
|
/** |
398
|
|
|
* @param callable $callable Wrapped router instance. |
399
|
|
|
* @return mixed |
400
|
|
|
*/ |
401
|
65 |
|
protected function _routesWrapper(callable $callable) |
402
|
|
|
{ |
403
|
65 |
|
$this->resetRoutes(); |
404
|
65 |
|
$this->loadRoutes(); |
405
|
65 |
|
ApiRouter::$initialized = true; |
406
|
65 |
|
$result = $callable(); |
407
|
64 |
|
$this->resetRoutes(); |
408
|
|
|
|
409
|
64 |
|
return $result; |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
/** |
413
|
|
|
* Reset to default application routes. |
414
|
|
|
* |
415
|
|
|
* @return void |
416
|
|
|
*/ |
417
|
65 |
|
public function resetRoutes() |
418
|
|
|
{ |
419
|
65 |
|
ApiRouter::reload(); |
420
|
65 |
|
} |
421
|
|
|
|
422
|
|
|
/** |
423
|
|
|
* Initialize service level routes |
424
|
|
|
* |
425
|
|
|
* @return void |
426
|
|
|
*/ |
427
|
8 |
|
public function loadRoutes() |
428
|
|
|
{ |
429
|
8 |
|
$defaultOptions = $this->routerDefaultOptions(); |
430
|
|
|
ApiRouter::scope('/', $defaultOptions, function (RouteBuilder $routes) use ($defaultOptions) { |
431
|
8 |
|
if (is_array($this->_routeExtensions)) { |
432
|
8 |
|
$routes->extensions($this->_routeExtensions); |
|
|
|
|
433
|
8 |
|
} |
434
|
8 |
|
if (!empty($defaultOptions['map'])) { |
435
|
8 |
|
$routes->resources($this->getName(), $defaultOptions); |
436
|
8 |
|
} |
437
|
8 |
|
}); |
438
|
8 |
|
} |
439
|
|
|
|
440
|
|
|
/** |
441
|
|
|
* Build router settings. |
442
|
|
|
* This implementation build action map for resource routes based on Service actions. |
443
|
|
|
* |
444
|
|
|
* @return array |
445
|
|
|
*/ |
446
|
64 |
|
public function routerDefaultOptions() |
447
|
|
|
{ |
448
|
64 |
|
$mapList = []; |
449
|
64 |
|
foreach ($this->_actions as $alias => $map) { |
450
|
43 |
|
if (is_numeric($alias)) { |
451
|
|
|
$alias = $map; |
452
|
|
|
$map = []; |
453
|
|
|
} |
454
|
43 |
|
$mapCors = false; |
455
|
43 |
|
if (!empty($map['mapCors'])) { |
456
|
9 |
|
$mapCors = $map['mapCors']; |
457
|
9 |
|
unset($map['mapCors']); |
458
|
9 |
|
} |
459
|
43 |
|
$mapList[$alias] = $map; |
460
|
43 |
|
$mapList[$alias] += ['method' => 'GET', 'path' => '', 'action' => $alias]; |
461
|
43 |
|
if ($mapCors) { |
462
|
9 |
|
$map['method'] = 'OPTIONS'; |
463
|
9 |
|
$map += ['path' => '', 'action' => $alias . $this->_corsSuffix]; |
464
|
9 |
|
$mapList[$alias . $this->_corsSuffix] = $map; |
465
|
9 |
|
} |
466
|
64 |
|
} |
467
|
|
|
|
468
|
|
|
return [ |
469
|
|
|
'map' => $mapList |
470
|
64 |
|
]; |
471
|
|
|
} |
472
|
|
|
|
473
|
|
|
/** |
474
|
|
|
* Finds URL for specified action. |
475
|
|
|
* |
476
|
|
|
* Returns an URL pointing to a combination of controller and action. |
477
|
|
|
* |
478
|
|
|
* @param string|array|null $route An array specifying any of the following: |
479
|
|
|
* 'controller', 'action', 'plugin' additionally, you can provide routed |
480
|
|
|
* elements or query string parameters. If string it can be name any valid url |
481
|
|
|
* string. |
482
|
|
|
* @return string Full translated URL with base path. |
483
|
|
|
* @throws \Cake\Core\Exception\Exception When the route name is not found |
484
|
|
|
*/ |
485
|
|
|
public function routeUrl($route) |
486
|
|
|
{ |
487
|
|
|
return $this->_routesWrapper(function () use ($route) { |
488
|
|
|
return ApiRouter::url($route); |
489
|
|
|
}); |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
/** |
493
|
|
|
* Reverses a parsed parameter array into a string. |
494
|
|
|
* |
495
|
|
|
* @param \Cake\Http\ServerRequest|array $params The params array or |
496
|
|
|
* Cake\Http\ServerRequest object that needs to be reversed. |
497
|
|
|
* @return string The string that is the reversed result of the array |
498
|
|
|
*/ |
499
|
13 |
|
public function routeReverse($params) |
500
|
|
|
{ |
501
|
|
|
return $this->_routesWrapper(function () use ($params) { |
502
|
|
|
try { |
503
|
13 |
|
return ApiRouter::reverse($params); |
504
|
1 |
|
} catch (Exception $e) { |
505
|
1 |
|
return null; |
506
|
|
|
} |
507
|
13 |
|
}); |
508
|
|
|
} |
509
|
|
|
|
510
|
|
|
/** |
511
|
|
|
* Dispatch service call. |
512
|
|
|
* |
513
|
|
|
* @return \CakeDC\Api\Service\Action\Result |
514
|
|
|
*/ |
515
|
56 |
|
public function dispatch() |
516
|
|
|
{ |
517
|
|
|
try { |
518
|
56 |
|
$result = $this->_dispatch(); |
519
|
|
|
|
520
|
52 |
|
if ($result instanceof Result) { |
521
|
|
|
$this->setResult($result); |
522
|
|
|
} else { |
523
|
52 |
|
$this->getResult()->data($result); |
524
|
52 |
|
$this->getResult()->code(200); |
525
|
|
|
} |
526
|
56 |
|
} catch (RecordNotFoundException $e) { |
527
|
6 |
|
$this->getResult()->code(404); |
528
|
6 |
|
$this->getResult()->exception($e); |
529
|
8 |
|
} catch (ValidationException $e) { |
530
|
1 |
|
$this->getResult()->code(422); |
531
|
1 |
|
$this->getResult()->exception($e); |
532
|
2 |
|
} catch (Exception $e) { |
533
|
1 |
|
$code = $e->getCode(); |
534
|
1 |
|
if (!is_int($code) || $code < 100 || $code >= 600) { |
535
|
|
|
$this->getResult()->code(500); |
536
|
|
|
} |
537
|
1 |
|
$this->getResult()->exception($e); |
538
|
|
|
} |
539
|
56 |
|
$this->dispatchEvent('Service.afterDispatch', ['service' => $this]); |
540
|
|
|
|
541
|
56 |
|
return $this->getResult(); |
542
|
|
|
} |
543
|
|
|
|
544
|
|
|
/** |
545
|
|
|
* Dispatch service call through callbacks and action. |
546
|
|
|
* |
547
|
|
|
* @return Result|mixed |
548
|
|
|
*/ |
549
|
56 |
|
protected function _dispatch() |
550
|
|
|
{ |
551
|
56 |
|
$event = $this->dispatchEvent('Service.beforeDispatch', ['service' => $this]); |
552
|
56 |
|
if ($event->result instanceof Result) { |
553
|
|
|
return $event->result; |
554
|
|
|
} |
555
|
|
|
|
556
|
56 |
|
$action = $this->buildAction(); |
557
|
56 |
|
$this->dispatchEvent('Service.beforeProcess', ['service' => $this, 'action' => $this]); |
558
|
56 |
|
if ($event->result instanceof Result) { |
559
|
|
|
return $event->result; |
560
|
|
|
} |
561
|
|
|
|
562
|
56 |
|
return $action->process(); |
563
|
|
|
} |
564
|
|
|
|
565
|
|
|
/** |
566
|
|
|
* Build action instance |
567
|
|
|
* |
568
|
|
|
* @return \CakeDC\Api\Service\Action\Action |
569
|
|
|
* @throws Exception |
570
|
|
|
*/ |
571
|
64 |
|
public function buildAction() |
572
|
|
|
{ |
573
|
64 |
|
$route = $this->parseRoute($this->getBaseUrl()); |
574
|
63 |
|
if (empty($route)) { |
575
|
|
|
throw new MissingActionException('Invalid Action Route:' . $this->getBaseUrl()); // InvalidActionException |
576
|
|
|
} |
577
|
63 |
|
$service = null; |
578
|
63 |
|
$serviceName = Inflector::underscore($route['controller']); |
579
|
63 |
|
if ($serviceName == $this->getName()) { |
580
|
54 |
|
$service = $this; |
581
|
54 |
|
} |
582
|
63 |
|
if (in_array($serviceName, $this->_innerServices)) { |
583
|
|
|
$options = [ |
584
|
9 |
|
'version' => $this->getVersion(), |
585
|
9 |
|
'request' => $this->getRequest(), |
586
|
9 |
|
'response' => $this->getResponse(), |
587
|
9 |
|
'refresh' => true, |
588
|
9 |
|
]; |
589
|
9 |
|
$service = ServiceRegistry::get($serviceName, $options); |
|
|
|
|
590
|
9 |
|
$service->setParentService($this); |
591
|
9 |
|
} |
592
|
63 |
|
$action = $route['action']; |
593
|
63 |
|
list($namespace, $serviceClass) = namespaceSplit(get_class($service)); |
594
|
63 |
|
$actionPrefix = substr($serviceClass, 0, -7); |
595
|
63 |
|
$actionClass = $namespace . '\\Action\\' . $actionPrefix . Inflector::camelize($action) . 'Action'; |
596
|
63 |
|
if (class_exists($actionClass)) { |
597
|
2 |
|
return $service->buildActionClass($actionClass, $route); |
598
|
|
|
} |
599
|
61 |
|
$actionsClassMap = $service->getActionsClassMap(); |
600
|
61 |
|
if (array_key_exists($action, $actionsClassMap)) { |
601
|
61 |
|
return $service->buildActionClass($actionsClassMap[$action], $route); |
602
|
|
|
} |
603
|
|
|
throw new MissingActionException(['class' => $actionClass]); |
604
|
|
|
} |
605
|
|
|
|
606
|
|
|
/** |
607
|
|
|
* Parses given URL string. Returns 'routing' parameters for that URL. |
608
|
|
|
* |
609
|
|
|
* @param string $url URL to be parsed |
610
|
|
|
* @return array Parsed elements from URL |
611
|
|
|
* @throws \Cake\Routing\Exception\MissingRouteException When a route cannot be handled |
612
|
|
|
*/ |
613
|
|
|
public function parseRoute($url) |
614
|
|
|
{ |
615
|
64 |
|
return $this->_routesWrapper(function () use ($url) { |
616
|
64 |
|
return ApiRouter::parseRequest(new ServerRequest([ |
617
|
64 |
|
'url' => $url, |
618
|
|
|
'environment' => [ |
619
|
64 |
|
'REQUEST_METHOD' => $this->_request->env('REQUEST_METHOD') |
|
|
|
|
620
|
64 |
|
] |
621
|
64 |
|
])); |
622
|
64 |
|
}); |
623
|
|
|
} |
624
|
|
|
|
625
|
|
|
/** |
626
|
|
|
* Returns action class map. |
627
|
|
|
* |
628
|
|
|
* @return array |
629
|
|
|
*/ |
630
|
61 |
|
public function getActionsClassMap() |
631
|
|
|
{ |
632
|
61 |
|
return $this->_actionsClassMap; |
633
|
|
|
} |
634
|
|
|
|
635
|
|
|
/** |
636
|
|
|
* Build base url |
637
|
|
|
* |
638
|
|
|
* @return string |
639
|
|
|
*/ |
640
|
65 |
|
public function getBaseUrl() |
641
|
|
|
{ |
642
|
65 |
|
if (!empty($this->_baseUrl)) { |
643
|
65 |
|
return $this->_baseUrl; |
644
|
|
|
} |
645
|
|
|
|
646
|
|
|
$result = '/' . $this->getName(); |
647
|
|
|
|
648
|
|
|
return $result; |
649
|
|
|
} |
650
|
|
|
|
651
|
|
|
/** |
652
|
|
|
* Gets the parent service method. |
653
|
|
|
* |
654
|
|
|
* @return Service |
655
|
|
|
*/ |
656
|
55 |
|
public function getParentService() |
657
|
|
|
{ |
658
|
55 |
|
return $this->_parentService; |
659
|
|
|
} |
660
|
|
|
|
661
|
|
|
/** |
662
|
|
|
* Sets the parent service method. |
663
|
|
|
* |
664
|
|
|
* @param Service $parentService Parent Service |
665
|
|
|
* @return $this |
666
|
|
|
*/ |
667
|
9 |
|
public function setParentService(Service $parentService) |
668
|
|
|
{ |
669
|
9 |
|
$this->_parentService = $parentService; |
670
|
|
|
|
671
|
9 |
|
return $this; |
672
|
|
|
} |
673
|
|
|
|
674
|
|
|
/** |
675
|
|
|
* Parent service get and set methods. |
676
|
|
|
* |
677
|
|
|
* @param Service $service Parent Service instance. |
678
|
|
|
* @deprecated 3.4.0 Use getParentService()/setParentService() instead. |
679
|
|
|
* @return Service|$this |
680
|
|
|
*/ |
681
|
4 |
|
public function parent(Service $service = null) |
682
|
|
|
{ |
683
|
4 |
|
if ($service !== null) { |
684
|
|
|
return $this->setParentService($service); |
685
|
|
|
} |
686
|
|
|
|
687
|
4 |
|
return $this->getParentService(); |
688
|
|
|
} |
689
|
|
|
|
690
|
|
|
/** |
691
|
|
|
* Build action class |
692
|
|
|
* |
693
|
|
|
* @param string $class Class name. |
694
|
|
|
* @param array $route Activated route. |
695
|
|
|
* @return mixed |
696
|
|
|
*/ |
697
|
64 |
|
public function buildActionClass($class, $route) |
698
|
|
|
{ |
699
|
64 |
|
$instance = new $class($this->_actionOptions($route)); |
700
|
|
|
|
701
|
64 |
|
return $instance; |
702
|
|
|
} |
703
|
|
|
|
704
|
|
|
/** |
705
|
|
|
* Action constructor options. |
706
|
|
|
* |
707
|
|
|
* @param array $route Activated route. |
708
|
|
|
* @return array |
709
|
|
|
*/ |
710
|
64 |
|
protected function _actionOptions($route) |
711
|
|
|
{ |
712
|
64 |
|
$actionName = $route['action']; |
713
|
|
|
|
714
|
|
|
$options = [ |
715
|
64 |
|
'name' => $actionName, |
716
|
64 |
|
'service' => $this, |
717
|
64 |
|
'route' => $route, |
718
|
64 |
|
]; |
719
|
64 |
|
$options += (new ConfigReader())->actionOptions($this->getName(), $actionName, $this->getVersion()); |
720
|
|
|
|
721
|
64 |
|
return $options; |
722
|
|
|
} |
723
|
|
|
|
724
|
|
|
/** |
725
|
|
|
* Gets the result for service. |
726
|
|
|
* |
727
|
|
|
* @return Result |
728
|
|
|
*/ |
729
|
56 |
|
public function getResult() |
730
|
|
|
{ |
731
|
56 |
|
if ($this->_parentService !== null) { |
732
|
2 |
|
return $this->_parentService->getResult(); |
733
|
|
|
} |
734
|
56 |
|
if ($this->_result === null) { |
735
|
56 |
|
$this->_result = new Result(); |
736
|
56 |
|
} |
737
|
|
|
|
738
|
56 |
|
return $this->_result; |
739
|
|
|
} |
740
|
|
|
|
741
|
|
|
/** |
742
|
|
|
* Sets the result for service. |
743
|
|
|
* |
744
|
|
|
* @param Result $result A Result object. |
745
|
|
|
* @return $this |
746
|
|
|
*/ |
747
|
|
|
public function setResult(Result $result) |
748
|
|
|
{ |
749
|
|
|
if ($this->_parentService !== null) { |
750
|
|
|
$this->_parentService->setResult($result); |
751
|
|
|
|
752
|
|
|
return $this; |
753
|
|
|
} |
754
|
|
|
$this->_result = $result; |
755
|
|
|
|
756
|
|
|
return $this; |
757
|
|
|
} |
758
|
|
|
|
759
|
|
|
/** |
760
|
|
|
* @param null $value value |
761
|
|
|
* @deprecated 3.4.0 Use getResult()/setResult() instead. |
762
|
|
|
* @return Result |
763
|
|
|
*/ |
764
|
30 |
|
public function result($value = null) |
765
|
|
|
{ |
766
|
30 |
|
if ($value !== null) { |
767
|
|
|
return $this->setResult($value); |
|
|
|
|
768
|
|
|
} |
769
|
|
|
|
770
|
30 |
|
return $this->getResult(); |
771
|
|
|
} |
772
|
|
|
|
773
|
|
|
/** |
774
|
|
|
* Fill up response and stop execution. |
775
|
|
|
* |
776
|
|
|
* @param Result $result A Result instance. |
777
|
|
|
* @return Response |
778
|
|
|
*/ |
779
|
56 |
|
public function respond($result = null) |
780
|
|
|
{ |
781
|
56 |
|
if ($result === null) { |
782
|
|
|
$result = $this->getResult(); |
783
|
|
|
} |
784
|
56 |
|
$this->setResponse($this->getResponse()->withStatus($result->code())); |
785
|
56 |
|
if ($result->exception() !== null) { |
786
|
8 |
|
$this->getRenderer() |
787
|
8 |
|
->error($result->exception()); |
788
|
8 |
|
} else { |
789
|
52 |
|
$this->getRenderer() |
790
|
52 |
|
->response($result); |
791
|
|
|
} |
792
|
|
|
|
793
|
56 |
|
return $this->getResponse(); |
794
|
|
|
} |
795
|
|
|
|
796
|
|
|
/** |
797
|
|
|
* Gets the response. |
798
|
|
|
* |
799
|
|
|
* @return \Cake\Http\Response |
800
|
|
|
*/ |
801
|
131 |
|
public function getResponse() |
802
|
|
|
{ |
803
|
131 |
|
return $this->_response; |
804
|
|
|
} |
805
|
|
|
|
806
|
|
|
/** |
807
|
|
|
* Sets the response. |
808
|
|
|
* |
809
|
|
|
* @param \Cake\Http\Response $response Response |
810
|
|
|
* @return $this |
811
|
|
|
*/ |
812
|
140 |
|
public function setResponse(Response $response) |
813
|
|
|
{ |
814
|
140 |
|
$this->_response = $response; |
815
|
|
|
|
816
|
140 |
|
return $this; |
817
|
|
|
} |
818
|
|
|
|
819
|
|
|
/** |
820
|
|
|
* Get and set response. |
821
|
|
|
* |
822
|
|
|
* @param \Cake\Http\Response $response A Response object. |
823
|
|
|
* @deprecated 3.4.0 Use getResponse()/setResponse() instead. |
824
|
|
|
* @return \Cake\Http\Response |
825
|
|
|
*/ |
826
|
1 |
|
public function response(Response $response = null) |
827
|
|
|
{ |
828
|
1 |
|
if ($response !== null) { |
829
|
|
|
return $this->setResponse($response); |
|
|
|
|
830
|
|
|
} |
831
|
|
|
|
832
|
1 |
|
return $this->getResponse(); |
833
|
|
|
} |
834
|
|
|
|
835
|
|
|
/** |
836
|
|
|
* Gets the service renderer. |
837
|
|
|
* |
838
|
|
|
* @return BaseRenderer |
839
|
|
|
*/ |
840
|
68 |
|
public function getRenderer() |
841
|
|
|
{ |
842
|
68 |
|
return $this->_renderer; |
843
|
|
|
} |
844
|
|
|
|
845
|
|
|
/** |
846
|
|
|
* Sets the service renderer. |
847
|
|
|
* |
848
|
|
|
* @param BaseRenderer $renderer Rendered |
849
|
|
|
* @return $this |
850
|
|
|
*/ |
851
|
140 |
|
public function setRenderer(BaseRenderer $renderer) |
852
|
|
|
{ |
853
|
140 |
|
$this->_renderer = $renderer; |
854
|
|
|
|
855
|
140 |
|
return $this; |
856
|
|
|
} |
857
|
|
|
|
858
|
|
|
/** |
859
|
|
|
* Service renderer configuration method. |
860
|
|
|
* |
861
|
|
|
* @param BaseRenderer $renderer A Renderer instance. |
862
|
|
|
* @deprecated 3.4.0 Use getRenderer()/setRenderer() instead. |
863
|
|
|
* @return BaseRenderer|$this |
864
|
|
|
*/ |
865
|
|
|
public function renderer(BaseRenderer $renderer = null) |
866
|
|
|
{ |
867
|
|
|
if ($renderer !== null) { |
868
|
|
|
return $this->setRenderer($renderer); |
869
|
|
|
} |
870
|
|
|
|
871
|
|
|
return $this->getRenderer(); |
872
|
|
|
} |
873
|
|
|
|
874
|
|
|
/** |
875
|
|
|
* Define action config. |
876
|
|
|
* |
877
|
|
|
* @param string $actionName Action name. |
878
|
|
|
* @param string $className Class name. |
879
|
|
|
* @param array $route Route config. |
880
|
|
|
* @return void |
881
|
|
|
*/ |
882
|
23 |
|
public function mapAction($actionName, $className, $route) |
883
|
|
|
{ |
884
|
23 |
|
$route += ['mapCors' => false]; |
885
|
23 |
|
$this->_actionsClassMap[$actionName] = $className; |
886
|
23 |
|
if ($route['mapCors']) { |
887
|
23 |
|
$this->_actionsClassMap[$actionName . $this->_corsSuffix] = DummyAction::class; |
888
|
23 |
|
} |
889
|
23 |
|
if (!isset($route['path'])) { |
890
|
8 |
|
$route['path'] = $actionName; |
891
|
8 |
|
} |
892
|
23 |
|
$this->_actions[$actionName] = $route; |
893
|
23 |
|
} |
894
|
|
|
|
895
|
|
|
/** |
896
|
|
|
* Lists supported events. |
897
|
|
|
* |
898
|
|
|
* @return array |
899
|
|
|
*/ |
900
|
140 |
|
public function implementedEvents() |
901
|
|
|
{ |
902
|
|
|
$eventMap = [ |
903
|
140 |
|
'Service.beforeDispatch' => 'beforeDispatch', |
904
|
140 |
|
'Service.beforeProcess' => 'beforeProcess', |
905
|
140 |
|
'Service.afterDispatch' => 'afterDispatch', |
906
|
140 |
|
]; |
907
|
140 |
|
$events = []; |
908
|
|
|
|
909
|
140 |
|
foreach ($eventMap as $event => $method) { |
910
|
140 |
|
if (!method_exists($this, $method)) { |
911
|
140 |
|
continue; |
912
|
|
|
} |
913
|
|
|
$events[$event] = $method; |
914
|
140 |
|
} |
915
|
|
|
|
916
|
140 |
|
return $events; |
917
|
|
|
} |
918
|
|
|
|
919
|
|
|
/** |
920
|
|
|
* Gets the extension registry instance. |
921
|
|
|
* |
922
|
|
|
* @return \CakeDC\Api\Service\ExtensionRegistry |
923
|
|
|
*/ |
924
|
|
|
public function getExtensions() |
925
|
|
|
{ |
926
|
|
|
if ($this->_extensions === null) { |
927
|
|
|
$this->_extensions = new ExtensionRegistry($this); |
928
|
|
|
} |
929
|
|
|
|
930
|
|
|
return $this->_extensions; |
931
|
|
|
} |
932
|
|
|
|
933
|
|
|
/** |
934
|
|
|
* Sets the extension registry for this service. |
935
|
|
|
* |
936
|
|
|
* @param \CakeDC\Api\Service\ExtensionRegistry $extensions The extension registry instance. |
937
|
|
|
* @return $this |
938
|
|
|
*/ |
939
|
140 |
|
public function setExtensions($extensions) |
940
|
|
|
{ |
941
|
140 |
|
if ($extensions === null) { |
942
|
140 |
|
$extensions = new ExtensionRegistry($this); |
943
|
140 |
|
} |
944
|
140 |
|
$this->_extensions = $extensions; |
945
|
|
|
|
946
|
140 |
|
return $this; |
947
|
|
|
} |
948
|
|
|
|
949
|
|
|
/** |
950
|
|
|
* Get the extension registry for this service. |
951
|
|
|
* |
952
|
|
|
* If called with the first parameter, it will be set as the action $this->_extensions property |
953
|
|
|
* |
954
|
|
|
* @param \CakeDC\Api\Service\ExtensionRegistry|null $extensions Extension registry. |
955
|
|
|
* @deprecated 3.4.0 Use getExtensions()/setExtensions() instead. |
956
|
|
|
* @return \CakeDC\Api\Service\ExtensionRegistry|$this |
957
|
|
|
*/ |
958
|
|
|
public function extensions($extensions = null) |
959
|
|
|
{ |
960
|
|
|
if ($extensions !== null) { |
961
|
|
|
$this->setExtensions($extensions); |
962
|
|
|
} |
963
|
|
|
|
964
|
|
|
return $this->getExtensions(); |
965
|
|
|
} |
966
|
|
|
|
967
|
|
|
/** |
968
|
|
|
* Loads the defined extensions using the Extension factory. |
969
|
|
|
* |
970
|
|
|
* @return void |
971
|
|
|
*/ |
972
|
140 |
|
protected function _loadExtensions() |
973
|
|
|
{ |
974
|
140 |
|
if (empty($this->extensions)) { |
975
|
140 |
|
return; |
976
|
|
|
} |
977
|
|
|
$registry = $this->getExtensions(); |
978
|
|
|
$extensions = $registry->normalizeArray($this->extensions); |
979
|
|
|
foreach ($extensions as $properties) { |
980
|
|
|
$instance = $registry->load($properties['class'], $properties['config']); |
981
|
|
|
$this->_eventManager->on($instance); |
982
|
|
|
} |
983
|
|
|
} |
984
|
|
|
|
985
|
|
|
/** |
986
|
|
|
* Initialize parser. |
987
|
|
|
* |
988
|
|
|
* @param array $config Service options |
989
|
|
|
* @return void |
990
|
|
|
*/ |
991
|
140 |
|
protected function _initializeParser(array $config) |
992
|
|
|
{ |
993
|
140 |
|
if (empty($this->_parserClass) && isset($config['parserClass'])) { |
994
|
|
|
$this->_parserClass = $config['parserClass']; |
995
|
|
|
} |
996
|
140 |
|
$parserClass = Configure::read('Api.parser'); |
997
|
140 |
|
if (empty($this->_parserClass) && !empty($parserClass)) { |
998
|
140 |
|
$this->_parserClass = $parserClass; |
999
|
140 |
|
} |
1000
|
|
|
|
1001
|
140 |
|
$class = App::className($this->_parserClass, 'Service/RequestParser', 'Parser'); |
1002
|
140 |
|
if (!class_exists($class)) { |
1003
|
|
|
throw new MissingParserException(['class' => $this->_parserClass]); |
1004
|
|
|
} |
1005
|
140 |
|
$this->_parser = new $class($this); |
1006
|
140 |
|
} |
1007
|
|
|
|
1008
|
|
|
/** |
1009
|
|
|
* Initialize renderer. |
1010
|
|
|
* |
1011
|
|
|
* @param array $config Service options. |
1012
|
|
|
* @return void |
1013
|
|
|
*/ |
1014
|
140 |
|
protected function _initializeRenderer(array $config) |
1015
|
|
|
{ |
1016
|
140 |
|
if (empty($this->_rendererClass) && isset($config['rendererClass'])) { |
1017
|
13 |
|
$this->_rendererClass = $config['rendererClass']; |
1018
|
13 |
|
} |
1019
|
140 |
|
$rendererClass = Configure::read('Api.renderer'); |
1020
|
140 |
|
if (empty($this->_rendererClass) && !empty($rendererClass)) { |
1021
|
127 |
|
$this->_rendererClass = $rendererClass; |
1022
|
127 |
|
} |
1023
|
|
|
|
1024
|
140 |
|
$class = App::className($this->_rendererClass, 'Service/Renderer', 'Renderer'); |
1025
|
140 |
|
if (!class_exists($class)) { |
1026
|
|
|
throw new MissingRendererException(['class' => $this->_rendererClass]); |
1027
|
|
|
} |
1028
|
140 |
|
$this->setRenderer(new $class($this)); |
1029
|
140 |
|
} |
1030
|
|
|
} |
1031
|
|
|
|
It seems like the type of the argument is not accepted by the function/method which you are calling.
In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.
We suggest to add an explicit type cast like in the following example: