Completed
Push — master ( f8782f...c598c9 )
by Alex
02:46
created

ServiceTransport::callRoute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
namespace Mezon\Service;
3
// TODO exclude it in separate package
4
5
/**
6
 * Base class for all transports
7
 *
8
 * @package Service
9
 * @subpackage ServiceTransport
10
 * @author Dodonov A.A.
11
 * @version v.1.0 (2019/08/17)
12
 * @copyright Copyright (c) 2019, aeon.org
13
 */
14
15
/**
16
 * Base class for all transports
17
 *
18
 * @author Dodonov A.A.
19
 */
20
abstract class ServiceTransport implements \Mezon\Service\ServiceTransportInterface
21
{
22
23
    /**
24
     * Request params fetcher
25
     *
26
     * @var \Mezon\Service\ServiceRequestParamsInterface
27
     */
28
    private $paramsFetcher = false;
29
30
    /**
31
     * Service's logic
32
     *
33
     * @var \Mezon\Service\ServiceLogic
34
     */
35
    private $serviceLogic = false;
36
37
    /**
38
     * Router
39
     *
40
     * @var \Mezon\Router\Router
41
     */
42
    private $router = false;
43
44
    /**
45
     * Constructor
46
     */
47
    public function __construct()
48
    {
49
        $this->router = new \Mezon\Router\Router();
50
51
        $this->router->setNoProcessorFoundErrorHandler(
52
            function (string $route) {
53
                $exception = new \Exception('Route ' . $route . ' was not found', - 1);
54
55
                $this->handleException($exception);
56
            });
57
    }
58
59
    /**
60
     * Method searches necessary logic object
61
     *
62
     * @param string $method
63
     *            Necessary method
64
     * @return \Mezon\Service\ServiceBaseLogicInterface Logic object
65
     */
66
    protected function getNecessaryLogic(string $method): \Mezon\Service\ServiceBaseLogicInterface
67
    {
68
        if (is_object($this->serviceLogic)) {
69
            if (method_exists($this->serviceLogic, $method)) {
70
                return $this->serviceLogic;
71
            } else {
72
                throw (new \Exception(
73
                    'The method "' . $method . '" was not found in the "' . get_class($this->serviceLogic) . '"',
74
                    - 1));
75
            }
76
        } elseif (is_array($this->serviceLogic)) {
77
            foreach ($this->serviceLogic as $logic) {
78
                if (method_exists($logic, $method)) {
79
                    return $logic;
80
                }
81
            }
82
83
            throw (new \Exception('The method "' . $method . '" was not found in the set of logic objects', - 1));
84
        } else {
85
            throw (new \Exception('Logic was not found', - 2));
86
        }
87
        // @codeCoverageIgnoreStart
88
    }
89
90
    // @codeCoverageIgnoreEnd
91
92
    /**
93
     * Method creates session
94
     *
95
     * @param bool|string $token
96
     *            Session token
97
     */
98
    public abstract function createSession(string $token): string;
99
100
    /**
101
     * Method adds's route
102
     *
103
     * @param string $route
104
     *            Route
105
     * @param string $callback
106
     *            Logic method to be called
107
     * @param string|array $requestMethod
108
     *            HTTP request method
109
     * @param string $callType
110
     *            Type of the call
111
     */
112
    public function addRoute(string $route, string $callback, $requestMethod, string $callType = 'callLogic'): void
113
    {
114
        $localServiceLogic = $this->getNecessaryLogic($callback);
115
116
        if ($callType == 'public_call') {
117
            $this->router->addRoute(
118
                $route,
119
                function () use ($localServiceLogic, $callback) {
120
                    return $this->callPublicLogic($localServiceLogic, $callback, []);
121
                },
122
                $requestMethod);
0 ignored issues
show
Bug introduced by
It seems like $requestMethod can also be of type array; however, parameter $requestMethod of Mezon\Router\Router::addRoute() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

122
                /** @scrutinizer ignore-type */ $requestMethod);
Loading history...
123
        } else {
124
            $this->router->addRoute(
125
                $route,
126
                function () use ($localServiceLogic, $callback) {
127
                    return $this->callLogic($localServiceLogic, $callback, []);
128
                },
129
                $requestMethod);
130
        }
131
    }
132
133
    /**
134
     * Method loads single route
135
     *
136
     * @param array $route
137
     *            Route description
138
     */
139
    public function loadRoute(array $route): void
140
    {
141
        if (! isset($route['route'])) {
142
            throw (new \Exception('Field "route" must be set'));
143
        }
144
        if (! isset($route['callback'])) {
145
            throw (new \Exception('Field "callback" must be set'));
146
        }
147
        $method = isset($route['method']) ? $route['method'] : 'GET';
148
        $callType = isset($route['call_type']) ? $route['call_type'] : 'callLogic';
149
150
        $this->addRoute($route['route'], $route['callback'], $method, $callType);
151
    }
152
153
    /**
154
     * Method loads routes
155
     *
156
     * @param array $routes
157
     *            Route descriptions
158
     */
159
    public function loadRoutes(array $routes): void
160
    {
161
        foreach ($routes as $route) {
162
            $this->loadRoute($route);
163
        }
164
    }
165
166
    /**
167
     * Method loads routes from config file
168
     *
169
     * @param string $path
170
     *            Path to the routes description
171
     */
172
    public function loadRoutesFromConfig(string $path = './conf/routes.php')
173
    {
174
        if (file_exists($path)) {
175
            $routes = (include ($path));
176
177
            $this->loadRoutes($routes);
178
        } else {
179
            throw (new \Exception('Route ' . $path . ' was not found', 1));
180
        }
181
    }
182
183
    /**
184
     * Method runs logic functions
185
     *
186
     * @param \Mezon\Service\ServiceLogic $serviceLogic
187
     *            object with all service logic
188
     * @param string $method
189
     *            Logic's method to be executed
190
     * @param array $params
191
     *            Logic's parameters
192
     * @return mixed Result of the called method
193
     */
194
    public function callLogic(\Mezon\Service\ServiceBaseLogicInterface $serviceLogic, string $method, array $params = [])
195
    {
196
        try {
197
            $params['SessionId'] = $this->createSession($this->getParamsFetcher()
198
                ->getParam('session_id'));
199
200
            return call_user_func_array([
201
                $serviceLogic,
202
                $method
203
            ], $params);
204
        } catch (\Exception $e) {
205
            return $this->errorResponse($e);
206
        }
207
    }
208
209
    /**
210
     * Method runs logic functions
211
     *
212
     * @param \Mezon\Service\ServiceBaseLogicInterface $serviceLogic
213
     *            object with all service logic
214
     * @param string $method
215
     *            Logic's method to be executed
216
     * @param array $params
217
     *            Logic's parameters
218
     * @return mixed Result of the called method
219
     */
220
    public function callPublicLogic(
221
        \Mezon\Service\ServiceBaseLogicInterface $serviceLogic,
222
        string $method,
223
        array $params = [])
224
    {
225
        try {
226
            return call_user_func_array([
227
                $serviceLogic,
228
                $method
229
            ], $params);
230
        } catch (\Exception $e) {
231
            return $this->errorResponse($e);
232
        }
233
    }
234
235
    /**
236
     * Method returns true if the debug omde is ON
237
     */
238
    protected function isDebug(): bool
239
    {
240
        return defined('MEZON_DEBUG') && MEZON_DEBUG === true;
241
    }
242
243
    /**
244
     * Error response compilator
245
     *
246
     * @param mixed $e
247
     *            Exception object
248
     * @return array Error data
249
     */
250
    public function errorResponse($e): array
251
    {
252
        $result = [
253
            'message' => $e->getMessage(),
254
            'code' => $e->getCode()
255
        ];
256
257
        if ($this->isDebug()) {
258
            $result['call_stack'] = $this->formatCallStack($e);
259
        }
260
261
        return $result;
262
    }
263
264
    /**
265
     * Method returns parameter
266
     *
267
     * @param string $param
268
     *            Parameter name
269
     * @param mixed $default
270
     *            Default value
271
     * @return string Parameter value
272
     */
273
    public function getParam(string $param, $default = false)
274
    {
275
        return $this->getParamsFetcher()->getParam($param, $default);
276
    }
277
278
    /**
279
     * Formatting call stack
280
     *
281
     * @param mixed $e
282
     *            Exception object
283
     * @return array Call stack
284
     */
285
    protected function formatCallStack($e): array
286
    {
287
        $stack = $e->getTrace();
288
289
        foreach ($stack as $i => $call) {
290
            $stack[$i] = (@$call['file'] == '' ? 'lambda : ' : @$call['file'] . ' (' . $call['line'] . ') : ') .
291
                (@$call['class'] == '' ? '' : $call['class'] . '->') . $call['function'];
292
        }
293
294
        return $stack;
295
    }
296
297
    /**
298
     * Method runs router
299
     *
300
     * @codeCoverageIgnore
301
     */
302
    public function run(): void
303
    {
304
        try {
305
            if (isset($_GET['r']) === false) {
306
                throw (new \Exception('Route name was not found in $_GET[\'r\']'));
307
            }
308
309
            $this->callRoute();
310
        } catch (\Exception $e) {
311
            $this->handleException($e);
312
        }
313
    }
314
315
    /**
316
     * Method calls route in transport specific way
317
     */
318
    protected function callRoute(): void
319
    {
320
        print($this->router->callRoute($_GET['r']));
0 ignored issues
show
Bug introduced by
Are you sure $this->router->callRoute($_GET['r']) of type false|mixed|string can be used in print()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

320
        print(/** @scrutinizer ignore-type */ $this->router->callRoute($_GET['r']));
Loading history...
321
    }
322
323
    /**
324
     * Method kills execution thread
325
     * @codeCoverageIgnore
326
     */
327
    protected function die(): void
328
    {
329
        die(0);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
330
    }
331
332
    /**
333
     * Method outputs exception data
334
     *
335
     * @param array $e
336
     */
337
    public function outputException(array $e): void
338
    {
339
        print(json_encode($e));
340
    }
341
342
    /**
343
     * Method processes exception
344
     *
345
     * @param \Exception $e
346
     *            Exception object
347
     * @codeCoverageIgnore
348
     */
349
    public function handleException($e): void
350
    {
351
        $this->outputException($this->errorResponse($e));
352
353
        $this->die();
354
    }
355
356
    /**
357
     * Method fetches actions for routes
358
     *
359
     * @param \Mezon\Service\ServiceBaseLogicInterface $actionsSource
360
     *            Source of actions
361
     */
362
    public function fetchActions(\Mezon\Service\ServiceBaseLogicInterface $actionsSource): void
363
    {
364
        $methods = get_class_methods($actionsSource);
365
366
        foreach ($methods as $method) {
367
            if (strpos($method, 'action') === 0) {
368
                $route = \Mezon\Router\Utils::convertMethodNameToRoute($method);
369
370
                $this->router->addRoute(
371
                    $route,
372
                    function () use ($actionsSource, $method) {
373
                        return $this->callPublicLogic($actionsSource, $method, []);
374
                    },
375
                    'GET');
376
377
                $this->router->addRoute(
378
                    $route,
379
                    function () use ($actionsSource, $method) {
380
                        return $this->callPublicLogic($actionsSource, $method, []);
381
                    },
382
                    'POST');
383
            }
384
        }
385
    }
386
387
    /**
388
     * Method constructs request data fetcher
389
     *
390
     * @return \Mezon\Service\ServiceRequestParamsInterface Request data fetcher
391
     */
392
    public function getParamsFetcher(): \Mezon\Service\ServiceRequestParamsInterface
393
    {
394
        if ($this->paramsFetcher !== false) {
0 ignored issues
show
introduced by
The condition $this->paramsFetcher !== false is always true.
Loading history...
395
            return $this->paramsFetcher;
396
        }
397
398
        return $this->paramsFetcher = $this->createFetcher();
399
    }
400
401
    /**
402
     * Method constructs request data fetcher
403
     *
404
     * @param \Mezon\Service\ServiceRequestParamsInterface $paramsFetcher
405
     *            Request data fetcher
406
     */
407
    public function setParamsFetcher(\Mezon\Service\ServiceRequestParamsInterface $paramsFetcher): void
408
    {
409
        $this->paramsFetcher = $paramsFetcher;
410
    }
411
412
    /**
413
     * Method returns true if the router exists
414
     *
415
     * @param string $route
416
     *            checking route
417
     * @return bool true if the router exists, false otherwise
418
     */
419
    public function routeExists(string $route): bool
420
    {
421
        return $this->router->routeExists($route);
422
    }
423
424
    /**
425
     * Method returns router
426
     *
427
     * @return \Mezon\Router\Router router
428
     */
429
    public function &getRouter(): \Mezon\Router\Router
430
    {
431
        return $this->router;
432
    }
433
434
    /**
435
     * Method sets service logic
436
     *
437
     * @param
438
     *            array|\Mezon\Service\ServiceBaseLogicInterface base logic object or array
439
     */
440
    public function setServiceLogic($serviceLogic): void
441
    {
442
        $this->serviceLogic = $serviceLogic;
443
    }
444
}
445