Application::handleException()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
namespace Mezon\Application;
3
4
use Mezon\Transport\RequestParamsInterface;
5
use Mezon\Transport\HttpRequestParams;
6
use Mezon\Router\Router;
7
use Mezon\Transport\Request;
8
use Mezon\Redirect\Layer;
9
10
/**
11
 * Class Application
12
 *
13
 * @package Mezon
14
 * @subpackage Application
15
 * @author Dodonov A.A.
16
 * @version v.1.0 (2019/08/13)
17
 * @copyright Copyright (c) 2019, aeon.org
18
 */
19
20
/**
21
 * Base class of the application
22
 */
23
class Application
24
{
25
26
    /**
27
     * Router object
28
     *
29
     * @var Router
30
     */
31
    private $router = null;
32
33
    /**
34
     * Params fetcher
35
     *
36
     * @var HttpRequestParams
37
     */
38
    private $requestParams = null;
39
40
    /**
41
     * Constructor
42
     */
43
    function __construct()
44
    {
45
        // getting application's actions
46
        $this->router = new Router();
47
        Request::registerRouter($this->router);
48
49
        $this->router->fetchActions($this);
50
51
        $classPath = $this->getClassPath();
52
53
        if (file_exists($classPath . '/Conf/routes.php')) {
54
            $this->loadRoutesFromConfig($classPath . '/Conf/routes.php');
55
        }
56
57
        if (file_exists($classPath . '/Conf/routes.json')) {
58
            $this->loadRoutesFromConfig($classPath . '/Conf/routes.json');
59
        }
60
    }
61
62
    /**
63
     * Method returns class path
64
     *
65
     * @return string
66
     */
67
    protected function getClassPath(): string
68
    {
69
        $reflector = new \ReflectionClass(get_class($this));
70
        return dirname($reflector->getFileName());
71
    }
72
73
    /**
74
     * Method returns $this->requestParams and creates this object if necessery
75
     *
76
     * @return RequestParamsInterface
77
     */
78
    public function getRequestParamsFetcher(): RequestParamsInterface
79
    {
80
        if ($this->requestParams === null) {
81
            $this->requestParams = new HttpRequestParams($this->router);
82
        }
83
84
        return $this->requestParams;
85
    }
86
87
    /**
88
     * Method calls route and returns it's content
89
     */
90
    protected function callRoute()
91
    {
92
        $route = explode('/', trim(@$_GET['r'], '/'));
93
94
        if ($this->router === null) {
95
            throw (new \Exception('this->router was not set', - 2));
96
        }
97
98
        return $this->router->callRoute($route);
99
    }
100
101
    /**
102
     * Method loads single route
103
     *
104
     * @param array $route
105
     *            Route settings
106
     */
107
    public function loadRoute(array $route): void
108
    {
109
        if (isset($route['route']) === false) {
110
            throw (new \Exception('Field "route" must be set'));
111
        }
112
        if (isset($route['callback']) === false) {
113
            throw (new \Exception('Field "callback" must be set'));
114
        }
115
116
        $callback = $route['callback'];
117
118
        if (is_array($route['callback'])) {
119
            $route['class'] = $route['callback'][0];
120
            $route['callback'] = $route['callback'][1];
121
        } else {
122
            $class = isset($route['class']) ? new $route['class']() : $this;
123
            $callback = [
124
                $class,
125
                $callback
126
            ];
127
        }
128
        $this->router->addRoute($route['route'], $callback, isset($route['method']) ? $route['method'] : 'GET');
129
    }
130
131
    /**
132
     * Method loads routes
133
     *
134
     * @param array $routes
135
     *            List of routes
136
     */
137
    public function loadRoutes(array $routes): void
138
    {
139
        foreach ($routes as $route) {
140
            $this->loadRoute($route);
141
        }
142
    }
143
144
    /**
145
     * Method loads routes from config file in *.php or *.json format
146
     *
147
     * @param string $configPath
148
     *            Path of the config for routes
149
     */
150
    public function loadRoutesFromConfig(string $configPath): void
151
    {
152
        if (file_exists($configPath)) {
153
            if (substr($configPath, - 5) === '.json') {
154
                // load config from json
155
                $routes = json_decode(file_get_contents($configPath), true);
156
            } else {
157
                // loadconfig from php
158
                $routes = (include ($configPath));
159
            }
160
            $this->loadRoutes($routes);
161
        } else {
162
            throw (new \Exception('Route ' . $configPath . ' was not found', 1));
163
        }
164
    }
165
166
    /**
167
     * Method loads list of configs
168
     *
169
     * @param array $configPaths
170
     *            paths to config files
171
     */
172
    public function loadRoutesFromConfigs(array $configPaths): void
173
    {
174
        foreach ($configPaths as $configPath) {
175
            $this->loadRoutesFromConfig($configPath);
176
        }
177
    }
178
179
    /**
180
     * Method loads routes from the directory
181
     *
182
     * @param string $directory
183
     *            path to the directory. Scanninng is recursive.
184
     */
185
    public function loadRoutesFromDirectory(string $directory): void
186
    {
187
        $paths = scandir($directory);
188
189
        foreach ($paths as $path) {
190
            if (is_file($directory . '/' . $path)) {
191
                $this->loadRoutesFromConfig($directory . '/' . $path);
192
            }
193
        }
194
    }
195
196
    /**
197
     * Method processes exception
198
     *
199
     * @param \Exception $e
200
     *            Exception object to be formatted
201
     */
202
    public function handleException(\Exception $e): void
203
    {
204
        print('<pre>' . $e->getMessage() . '<br/>' . implode('<br/>', $this->formatCallStack($e)));
205
    }
206
207
    /**
208
     * Running application
209
     */
210
    public function run(): void
211
    {
212
        try {
213
            print($this->callRoute());
214
        } catch (\Exception $e) {
215
            $this->handleException($e);
216
        }
217
    }
218
219
    /**
220
     * Allowing to call methods added on the fly
221
     *
222
     * @param string $method
223
     *            Method to be called
224
     * @param array $args
225
     *            Arguments
226
     * @return mixed Result of the call
227
     */
228
    public function __call(string $method, array $args)
229
    {
230
        if (isset($this->$method)) {
231
            $function = $this->$method;
232
233
            return call_user_func_array($function, $args);
234
        } else {
235
            throw (new \Exception('Method ' . $method . ' was not found in the application ' . get_class($this)));
236
        }
237
    }
238
239
    /**
240
     * Method redirects user to another page
241
     *
242
     * @param string $url
243
     *            New page
244
     */
245
    public function redirectTo($url): void
246
    {
247
        if (isset($_GET['redirect-to'])) {
248
            $url = str_replace('{redirect-to}', urldecode($_GET['redirect-to']), $url);
249
        }
250
251
        Layer::redirectTo($url);
252
    }
253
254
    /**
255
     * Method validates that route exists
256
     *
257
     * @param string $route
258
     *            route
259
     * @return bool true if the route exists
260
     */
261
    public function routeExists(string $route): bool
262
    {
263
        return $this->router->routeExists($route);
264
    }
265
266
    /**
267
     * Method returns router
268
     *
269
     * @return \Mezon\Router\Router router
270
     */
271
    public function getRouter(): \Mezon\Router\Router
272
    {
273
        return $this->router;
274
    }
275
276
    /**
277
     * Formatting call stack
278
     *
279
     * @param mixed $e
280
     *            Exception object
281
     */
282
    protected function formatCallStack($e): array
283
    {
284
        $stack = $e->getTrace();
285
286
        foreach ($stack as $i => $call) {
287
            $stack[$i] = (@$call['file'] == '' ? 'lambda : ' : @$call['file'] . ' (' . $call['line'] . ') : ') .
288
                (@$call['class'] == '' ? '' : $call['class'] . '->') . $call['function'];
289
        }
290
291
        return array_reverse($stack);
292
    }
293
294
    /**
295
     * Method builds route data
296
     *
297
     * @param string $route
298
     *            route
299
     * @param string $method
300
     *            HTTP method
301
     * @param object $handler
302
     *            object wich handles request
303
     * @param string $function
304
     *            controller's function name
305
     * @return array built route data
306
     */
307
    public static function buildRoute(string $route, string $method, object $handler, string $function): array
308
    {
309
        return [
310
            'route' => $route,
311
            'method' => $method,
312
            'callback' => [
313
                $handler,
314
                $function
315
            ]
316
        ];
317
    }
318
}
319