Passed
Push — 9.0-dev ( 95a6ae...5fff6b )
by Radu
02:04
created

Application   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 249
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 249
rs 8.8
c 1
b 0
f 0
wmc 36

14 Methods

Rating   Name   Duplication   Size   Complexity  
A setEnvironmentValue() 0 12 1
A config() 0 3 1
A router() 0 3 1
A response() 0 3 1
A date() 0 3 1
A start() 0 21 3
A request() 0 3 1
A __construct() 0 13 3
A shutdown() 0 12 2
A halt() 0 6 2
C run() 0 24 7
A haltHttp() 0 55 4
C handleErrors() 0 33 7
A haltCli() 0 8 2
1
<?php
2
namespace WebServCo\Framework;
3
4
use WebServCo\Framework\Settings as S;
5
use WebServCo\Framework\Framework as Fw;
6
use WebServCo\Framework\Environment as Env;
7
use WebServCo\Framework\ErrorHandler as Err;
8
9
class Application
10
{
11
    public function __construct($publicPath, $projectPath)
12
    {
13
        $publicPath = rtrim($publicPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
14
        $projectPath = rtrim($projectPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
15
        
16
        if (!is_readable("{$publicPath}index.php") || !is_readable("{$projectPath}.env")) {
17
            throw new \ErrorException(
18
                'Invalid paths specified when initializing Application.'
19
            );
20
        }
21
        
22
        $this->config()->set(sprintf('app%1$spath%1$sweb', S::DIVIDER), $publicPath);
23
        $this->config()->set(sprintf('app%1$spath%1$sproject', S::DIVIDER), $projectPath);
24
    }
25
    
26
    final public function config()
27
    {
28
        return Fw::getLibrary('Config');
29
    }
30
    
31
    final protected function date()
32
    {
33
        return Fw::getLibrary('Date');
34
    }
35
    
36
    final protected function router()
37
    {
38
        return Fw::getLibrary('Router');
39
    }
40
    
41
    final protected function request()
42
    {
43
        return Fw::getLibrary('Request');
44
    }
45
    
46
    final protected function response()
47
    {
48
        return Fw::getLibrary('Response');
49
    }
50
    
51
    /**
52
     * Sets the env value from the project .env file.
53
     */
54
    final public function setEnvironmentValue()
55
    {
56
        /**
57
         * Project path is set in the constructor.
58
         */
59
        $pathProject = $this->config()->get(sprintf('app%1$spath%1$sproject', S::DIVIDER));
60
        /**
61
         * Env file existence is verified in the controller.
62
         */
63
        $this->config()->setEnv(trim(file_get_contents("{$pathProject}.env")));
64
        
65
        return true;
66
    }
67
    
68
    /**
69
     * Starts the execution of the application.
70
     */
71
    final public function start()
72
    {
73
        Err::set();
74
        register_shutdown_function([$this, 'shutdown']);
75
        
76
        try {
77
            $this->setEnvironmentValue();
78
            
79
            /**
80
             * With no argument, timezone will be set from the configuration.
81
             */
82
            $this->date()->setTimezone();
83
            /**
84
             * @todo i18n, log, session (if not cli), users (if not cli)
85
             */
86
            
87
            return true;
88
        } catch (\Throwable $e) { // php7
89
            return $this->shutdown($e, true);
90
        } catch (\Exception $e) { // php5
91
            return $this->shutdown($e, true);
92
        }
93
    }
94
    
95
    /**
96
     * Finishes the execution of the Application.
97
     *
98
     * This method is also registered as a shutdown handler.
99
     */
100
    final public function shutdown($exception = null, $manual = false)
101
    {
102
        $this->handleErrors($exception);
103
        
104
        if (!$manual) { //if shutdown handler
105
            /**
106
             * Warning: this part will always be executed,
107
             * independent of the outcome of the script.
108
             */
109
            Err::restore();
110
        }
111
        exit;
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...
112
    }
113
    
114
    /**
115
     * Runs the application.
116
     */
117
    final public function run()
118
    {
119
        try {
120
            $classType = Fw::isCLI() ? 'Command' : 'Controller';
121
            list($class, $method, $args) =
122
            $this->router()->getRoute(
123
                $this->request()->target,
124
                $this->router()->setting('routes')
125
            );
126
            $className = "\\Project\\Domain\\{$class}\\{$class}{$classType}";
127
            if (!class_exists($className)) {
128
                throw new \ErrorException("No matching {$classType} found", 404);
129
            }
130
            $object = new $className;
131
            $parent = get_parent_class($object);
132
            if (method_exists($parent, $method) ||
133
                !is_callable([$className, $method])) {
134
                throw new \ErrorException('No matching action found', 404);
135
            }
136
            return call_user_func_array([$object, $method], $args);
137
        } catch (\Throwable $e) { // php7
138
            return $this->shutdown($e, true);
139
        } catch (\Exception $e) { // php5
140
            return $this->shutdown($e, true);
141
        }
142
    }
143
    
144
    /**
145
     * Handle Errors.
146
     *
147
     * @param mixed $exception An \Error or \Exception object.
148
     */
149
    final private function handleErrors($exception = null)
150
    {
151
        $errorInfo = [
152
            'code' => 0,
153
            'message' => null,
154
            'file' => null,
155
            'line' => null,
156
            'trace' => null,
157
        ];
158
        if ($exception instanceof \Throwable ||
159
            $exception instanceof \Exception
160
        ) {
161
            $errorInfo['code'] = $exception->getCode();
162
            $errorInfo['message'] = $exception->getMessage();
163
            $errorInfo['file'] = $exception->getFile();
164
            $errorInfo['line'] = $exception->getLine();
165
            $errorInfo['trace'] = $exception->getTraceAsString();
166
        } else {
167
            $last_error = error_get_last();
168
            if (!empty($last_error['message'])) {
169
                $errorInfo['message'] = $last_error['message'];
170
            }
171
            if (!empty($last_error['file'])) {
172
                $errorInfo['file'] = $last_error['file'];
173
            }
174
            if (!empty($last_error['line'])) {
175
                $errorInfo['line'] = $last_error['line'];
176
            }
177
        }
178
        if (!empty($errorInfo['message'])) {
179
            return $this->halt($errorInfo);
180
        }
181
        return false;
182
    }
183
    
184
    final private function halt($errorInfo = [])
185
    {
186
        if (Fw::isCLI()) {
187
            return $this->haltCli($errorInfo);
188
        } else {
189
            return $this->haltHttp($errorInfo);
190
        }
191
    }
192
    
193
    protected function haltHttp($errorInfo = [])
194
    {
195
        switch ($errorInfo['code']) {
196
            case 404:
197
                $statusCode = 404;
198
                $title = 'Resource not found';
199
                break;
200
            case 500:
201
            default:
202
                $statusCode = 500;
203
                $title = 'The App made a boo boo';
204
                break;
205
        }
206
        $this->response()->setStatusHeader($statusCode);
207
        
208
        echo '<!doctype html>
209
        <html>
210
        <head>
211
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
212
            <title>Oups</title>
213
            <style>
214
            * {
215
                background: #f2dede; color: #a94442;
216
            }
217
            .o {
218
                display: table; position: absolute; height: 100%; width: 100%;
219
            }
220
221
            .m {
222
                display: table-cell; vertical-align: middle;
223
            }
224
225
            .i {
226
                margin-left: auto; margin-right: auto; width: 680px; text-align: center;
227
            }
228
            small {
229
                font-size: 0.7em;
230
            }
231
            </style>
232
        </head>
233
        <body>
234
            <div class="o">
235
                <div class="m">
236
                    <div class="i">';
237
        echo "<h1>{$title}</h1>";
238
        if (Env::ENV_DEV === $this->config()->getEnv()) {
239
            echo "<p>$errorInfo[message]</p>";
240
            echo "<p><small>$errorInfo[file]:$errorInfo[line]</small></p>";
241
        }
242
        echo '      </div>
243
                </div>
244
            </div>
245
        </body>
246
        </html>';
247
        return true;
248
    }
249
    
250
    protected function haltCli($errorInfo = [])
251
    {
252
        echo 'The App made a boo boo' . PHP_EOL;
253
        if (Env::ENV_DEV === $this->config()->getEnv()) {
254
            echo $errorInfo['message'] . PHP_EOL;
255
            echo "$errorInfo[file]:$errorInfo[line]" . PHP_EOL;
256
        }
257
        return true;
258
    }
259
}
260