Completed
Push — master ( 9f721d...dab6df )
by Alex
02:17
created

ServiceTransport::getSecurityProvider()   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
4
// TODO exclude it in separate package
5
6
/**
7
 * Base class for all transports
8
 *
9
 * @package Service
10
 * @subpackage ServiceTransport
11
 * @author Dodonov A.A.
12
 * @version v.1.0 (2019/08/17)
13
 * @copyright Copyright (c) 2019, aeon.org
14
 */
15
16
/**
17
 * Base class for all transports
18
 *
19
 * @author Dodonov A.A.
20
 */
21
abstract class ServiceTransport implements \Mezon\Service\ServiceTransportInterface
22
{
23
24
    /**
25
     * Request params fetcher
26
     *
27
     * @var \Mezon\Service\ServiceRequestParamsInterface
28
     */
29
    private $paramsFetcher = false;
30
31
    /**
32
     * Service's logic
33
     *
34
     * @var \Mezon\Service\ServiceLogic
35
     */
36
    private $serviceLogic = false;
37
38
    /**
39
     * Router
40
     *
41
     * @var \Mezon\Router\Router
42
     */
43
    private $router = false;
44
45
    /**
46
     * Security provider
47
     * 
48
     * @var \Mezon\Service\ServiceSecurityProviderInterface
49
     */
50
    private $securityProvider = null;
51
52
    /**
53
     * Constructor
54
     *
55
     * @param mixed $securityProvider
56
     *            Security provider
57
     */
58
    public function __construct($securityProvider = \Mezon\Service\ServiceMockSecurityProvider::class)
59
    {
60
        $this->router = new \Mezon\Router\Router();
61
62
        $this->router->setNoProcessorFoundErrorHandler(
63
            function (string $route) {
64
                $exception = new \Exception('Route ' . $route . ' was not found', - 1);
65
66
                $this->handleException($exception);
67
            });
68
69
        if (is_string($securityProvider)) {
70
            $this->securityProvider = new $securityProvider($this->getParamsFetcher());
71
        } else {
72
            $this->securityProvider = $securityProvider;
73
        }
74
    }
75
76
    /**
77
     * Method searches necessary logic object
78
     *
79
     * @param string $method
80
     *            Necessary method
81
     * @return \Mezon\Service\ServiceBaseLogicInterface Logic object
82
     */
83
    protected function getNecessaryLogic(string $method): \Mezon\Service\ServiceBaseLogicInterface
84
    {
85
        if (is_object($this->serviceLogic)) {
86
            if (method_exists($this->serviceLogic, $method)) {
87
                return $this->serviceLogic;
88
            } else {
89
                throw (new \Exception(
90
                    'The method "' . $method . '" was not found in the "' . get_class($this->serviceLogic) . '"',
91
                    - 1));
92
            }
93
        } elseif (is_array($this->serviceLogic)) {
94
            foreach ($this->serviceLogic as $logic) {
95
                if (method_exists($logic, $method)) {
96
                    return $logic;
97
                }
98
            }
99
100
            throw (new \Exception('The method "' . $method . '" was not found in the set of logic objects', - 1));
101
        } else {
102
            throw (new \Exception('Logic was not found', - 2));
103
        }
104
        // @codeCoverageIgnoreStart
105
    }
106
107
    // @codeCoverageIgnoreEnd
108
109
    /**
110
     * Method creates session
111
     *
112
     * @param bool|string $token
113
     *            Session token
114
     */
115
    public abstract function createSession(string $token): string;
116
117
    /**
118
     * Method adds's route
119
     *
120
     * @param string $route
121
     *            Route
122
     * @param string $callback
123
     *            Logic method to be called
124
     * @param string|array $requestMethod
125
     *            HTTP request method
126
     * @param string $callType
127
     *            Type of the call
128
     */
129
    public function addRoute(string $route, string $callback, $requestMethod, string $callType = 'callLogic'): void
130
    {
131
        $localServiceLogic = $this->getNecessaryLogic($callback);
132
133
        if ($callType == 'public_call') {
134
            $this->router->addRoute(
135
                $route,
136
                function () use ($localServiceLogic, $callback) {
137
                    return $this->callPublicLogic($localServiceLogic, $callback, []);
138
                },
139
                $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

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

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