Passed
Push — master ( 8a85d8...f1d9cd )
by Alex
10:07
created

CommonApplication   A

Complexity

Total Complexity 36

Size/Duplication

Total Lines 280
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 83
dl 0
loc 280
rs 9.52
c 0
b 0
f 0
wmc 36

16 Methods

Rating   Name   Duplication   Size   Complexity  
A handleException() 0 5 1
A baseFormatter() 0 12 3
A result() 0 8 3
A getActionMessage() 0 15 3
A getActionMessageCode() 0 3 1
A setSuccessMessage() 0 3 1
A setTemplate() 0 3 1
A setGetVar() 0 4 2
A getTemplate() 0 3 1
A noRouteFoundErrorHandler() 0 3 1
A handleRestException() 0 7 1
B run() 0 26 7
A __construct() 0 12 1
A crossRender() 0 3 1
A createActionFromJsonConfig() 0 27 4
A loadActoinsFromConfig() 0 11 5
1
<?php
2
namespace Mezon\Application;
3
4
use Mezon\HtmlTemplate\HtmlTemplate;
5
use Mezon\Rest;
6
7
/**
8
 * Class CommonApplication
9
 *
10
 * @package Mezon
11
 * @subpackage CommonApplication
12
 * @author Dodonov A.A.
13
 * @version v.1.0 (2019/08/07)
14
 * @copyright Copyright (c) 2019, aeon.org
15
 */
16
17
/**
18
 * Common application with any available template
19
 *
20
 * To load routes from the config call $this->load_routes_from_config('./conf/routes.json');
21
 *
22
 * The format of the *.json config must be like this:
23
 *
24
 * [
25
 * {
26
 * "route" : "/route1" ,
27
 * "callback" : "callback1" ,
28
 * "method" : "POST"
29
 * } ,
30
 * {
31
 * "route" : "/route2" ,
32
 * "callback" : "callback2" ,
33
 * "method" : ["GET" , "POST"]
34
 * }
35
 * ]
36
 */
37
class CommonApplication extends Application
38
{
39
40
    /**
41
     * Application's template
42
     *
43
     * @var HtmlTemplate
44
     */
45
    private $template = false;
46
47
    /**
48
     * Constructor
49
     *
50
     * @param HtmlTemplate $template
51
     *            Template
52
     */
53
    public function __construct(HtmlTemplate $template)
54
    {
55
        parent::__construct();
56
57
        $this->template = $template;
58
59
        $this->getRouter()->setNoProcessorFoundErrorHandler([
60
            $this,
61
            'noRouteFoundErrorHandler'
62
        ]);
63
64
        $this->loadActoinsFromConfig();
65
    }
66
67
    /**
68
     * Method handles 404 errors
69
     *
70
     * @codeCoverageIgnore
71
     */
72
    public function noRouteFoundErrorHandler(): void
73
    {
74
        $this->redirectTo('/404');
75
    }
76
77
    /**
78
     * Method renders common parts of all pages.
79
     *
80
     * @return array List of common parts.
81
     */
82
    public function crossRender(): array
83
    {
84
        return [];
85
    }
86
87
    /**
88
     * Method formats exception object
89
     *
90
     * @param \Exception $e
91
     *            Exception
92
     * @return object Formatted exception object
93
     */
94
    protected function baseFormatter(\Exception $e): object
95
    {
96
        $error = new \stdClass();
97
        $error->message = $e->getMessage();
98
        $error->code = $e->getCode();
99
        $error->call_stack = $this->formatCallStack($e);
100
        if (isset($_SERVER['HTTP_HOST']) && $_SERVER['REQUEST_URI']) {
101
            $error->host = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
102
        } else {
103
            $error->host = 'undefined';
104
        }
105
        return $error;
106
    }
107
108
    /**
109
     * Method processes exception.
110
     *
111
     * @param Rest\Exception $e
112
     *            RestException object.
113
     */
114
    public function handleRestException(Rest\Exception $e): void
115
    {
116
        $error = $this->baseFormatter($e);
117
118
        $error->httpBody = $e->getHttpBody();
119
120
        print('<pre>' . json_encode($error, JSON_PRETTY_PRINT));
121
    }
122
123
    /**
124
     * Method processes exception.
125
     *
126
     * @param \Exception $e
127
     *            Exception object.
128
     */
129
    public function handleException(\Exception $e): void
130
    {
131
        $error = $this->baseFormatter($e);
132
133
        print('<pre>' . json_encode($error, JSON_PRETTY_PRINT));
134
    }
135
136
    /**
137
     * Method sets GET parameter as template var
138
     *
139
     * @param string $fieldName
140
     *            name of the GET parameter
141
     */
142
    protected function setGetVar(string $fieldName): void
143
    {
144
        if (isset($_GET[$fieldName])) {
145
            $this->template->setPageVar($fieldName, $_GET[$fieldName]);
146
        }
147
    }
148
149
    /**
150
     * Running application.
151
     */
152
    public function run(): void
153
    {
154
        try {
155
            $callRouteResult = $this->callRoute();
156
157
            if (is_array($callRouteResult) === false) {
158
                throw (new \Exception('Route was not called properly'));
159
            }
160
161
            $result = array_merge($callRouteResult, $this->crossRender());
0 ignored issues
show
Bug introduced by
It seems like $callRouteResult can also be of type null and string and true; however, parameter $array1 of array_merge() does only seem to accept array, 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

161
            $result = array_merge(/** @scrutinizer ignore-type */ $callRouteResult, $this->crossRender());
Loading history...
162
163
            if (is_array($result)) {
0 ignored issues
show
introduced by
The condition is_array($result) is always true.
Loading history...
164
                foreach ($result as $key => $value) {
165
                    $content = $value instanceof ViewInterface ? $value->render() : $value;
166
167
                    $this->template->setPageVar($key, $content);
168
                }
169
            }
170
171
            $this->setGetVar('redirect-to');
172
173
            print($this->template->compile());
174
        } catch (Rest\Exception $e) {
175
            $this->handleRestException($e);
176
        } catch (\Exception $e) {
177
            $this->handleException($e);
178
        }
179
    }
180
181
    /**
182
     * Getting template
183
     *
184
     * @return HtmlTemplate Application's template
185
     * @codeCoverageIgnore
186
     */
187
    public function getTemplate(): HtmlTemplate
188
    {
189
        return $this->template;
190
    }
191
192
    /**
193
     * Setting template
194
     *
195
     * @param HtmlTemplate $template
196
     *            Template
197
     * @codeCoverageIgnore
198
     */
199
    public function setTemplate(HtmlTemplate $template): void
200
    {
201
        $this->template = $template;
202
    }
203
204
    /**
205
     * Method returns localized error message by it's key
206
     *
207
     * @param string $actionMessageCode
208
     *            key of the message
209
     * @return string localized error message by it's key
210
     */
211
    protected function getActionMessage(string $actionMessageCode): string
212
    {
213
        $classPath = $this->getClassPath();
214
215
        if (file_exists($classPath . '/res/action-messages.json')) {
216
            $messages = json_decode(file_get_contents($classPath . '/res/action-messages.json'), true);
217
218
            if (isset($messages[$actionMessageCode])) {
219
                return $messages[$actionMessageCode];
220
            } else {
221
                throw (new \Exception('The message with locator "' . $actionMessageCode . '" was not found', - 1));
222
            }
223
        }
224
225
        return '';
226
    }
227
228
    /**
229
     * Method returns action-message or '' if not set
230
     *
231
     * @return string
232
     */
233
    protected function getActionMessageCode(): string
234
    {
235
        return $_GET['action-message'] ?? '';
236
    }
237
238
    /**
239
     * Method sets message variable
240
     *
241
     * @param string $actionMessageCode
242
     *            message code
243
     */
244
    protected function setSuccessMessage(string $actionMessageCode): void
245
    {
246
        $this->getTemplate()->setPageVar('action-message', $this->getActionMessage($actionMessageCode));
247
    }
248
249
    /**
250
     * Method compiles result record
251
     *
252
     * @param mixed $presenter
253
     *            main area presenter
254
     * @return array result record
255
     */
256
    public function result($presenter = null): void
257
    {
258
        if ($presenter !== null) {
259
            $presenter->run();
260
        }
261
262
        if ($actionMessage = $this->getActionMessageCode()) {
263
            $this->setSuccessMessage($actionMessage);
264
        }
265
    }
266
267
    /**
268
     * Method creates action from JSON config
269
     *
270
     * @param string $path
271
     *            path to JSON config
272
     */
273
    private function createActionFromJsonConfig(string $path): void
274
    {
275
        $method = 'action' . basename($path, '.json');
276
277
        $this->$method = function () use ($path): array {
278
            $result = [];
279
            $views = [];
280
            $presenter = null;
281
            $config = json_decode(file_get_contents($path), true);
282
283
            foreach ($config as $key => $value) {
284
                if (is_string($value)) {
285
                    $result[$key] = $value;
286
                } elseif ($key === 'presenter') {
287
                    $presenter = new $value['class'](
288
                        $views[$value['view']],
289
                        $value['name'],
290
                        $this->getRequestParamsFetcher());
291
                } else {
292
                    $views[$key] = new $value['class']($this->getTemplate(), $value['name']);
293
                    $result[$value['placeholder']] = $views[$key];
294
                }
295
            }
296
297
            $this->result($presenter);
298
299
            return $result;
300
        };
301
    }
302
303
    /**
304
     * Method loads all actions from ./actions directory
305
     */
306
    private function loadActoinsFromConfig(): void
307
    {
308
        $classPath = $this->getClassPath();
309
310
        if (file_exists($classPath . '/actions')) {
311
            $files = scandir($classPath . '/actions');
312
313
            foreach ($files as $file) {
314
                if (is_file($classPath . '/actions/' . $file)) {
315
                    if (strpos($file, '.json') !== false) {
316
                        $this->createActionFromJsonConfig($classPath . '/actions/' . $file);
317
                    }
318
                }
319
            }
320
        }
321
    }
322
}
323