Completed
Push — 9.0-dev ( bf07f2...007112 )
by Radu
01:34
created

Application::router()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
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) { //php > 7
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
89
            return $this->shutdown($e, true);
90
        } catch (\Exception $e) {
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
            
110
            Err::restore();
111
        }
112
        exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method shutdown() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
113
    }
114
    
115
    /**
116
     * Runs the application.
117
     */
118
    final public function run()
119
    {
120
        if (Fw::isCLI()) {
121
            return $this->runCli();
122
        } else {
123
            return $this->runHttp();
124
        }
125
    }
126
    
127
    public function runHttp()
128
    {
129
        try {
130
            list($class, $method, $args) =
131
            $this->router()->getRoute(
132
                $this->request()->target,
133
                $this->router()->setting('routes')
134
            );
135
            $className = "\\Project\\Domain\\{$class}\\{$class}Controller";
136
            if (!class_exists($className)) {
137
                throw new \ErrorException('No matching controller found', 404);
138
            }
139
            $object = new $className;
140
            $parent = get_parent_class($object);
141
            if (method_exists($parent, $method) ||
142
                !is_callable([$className, $method])) {
143
                throw new \ErrorException('No matching action found', 404);
144
            }
145
            return call_user_func_array([$object, $method], $args);
146
        } catch (\Throwable $e) { //php > 7
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
147
            return $this->shutdown($e, true);
148
        } catch (\Exception $e) {
149
            return $this->shutdown($e, true);
150
        }
151
        return true;
0 ignored issues
show
Unused Code introduced by
return true; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
152
    }
153
    
154
    public function runCli()
155
    {
156
        try {
157
            throw new \ErrorException(
158
                'CLI support not implemented yet,'.
159
                ' please open this resource in a web browser.'
160
            );
161
        } catch (\Throwable $e) { //php > 7
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
162
            return $this->shutdown($e, true);
163
        } catch (\Exception $e) {
164
            return $this->shutdown($e, true);
165
        }
166
        return true;
0 ignored issues
show
Unused Code introduced by
return true; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
167
    }
168
    
169
    /**
170
     * Handle Errors.
171
     *
172
     * @param mixed $exception An Error or Exception object.
173
     */
174
    final private function handleErrors($exception = null)
175
    {
176
        $errorInfo = [
177
            'code' => 0,
178
            'severity' => null,
179
            'message' => null,
180
            'file' => null,
181
            'line' => null,
182
            'trace' => null,
183
        ];
184
        if (is_object($exception)) {
185
            $errorInfo['code'] = $exception->getCode();
186
            if ($exception instanceof \ErrorException) {
187
                $errorInfo['severity'] = $exception->getSeverity();
188
            }
189
            $errorInfo['message'] = $exception->getMessage();
190
            $errorInfo['file'] = $exception->getFile();
191
            $errorInfo['line'] = $exception->getLine();
192
            $errorInfo['trace'] = $exception->getTraceAsString();
193
        } else {
194
            $last_error = error_get_last();
195
            if (!empty($last_error['message'])) {
196
                $errorInfo['message'] = $last_error['message'];
197
            }
198
            if (!empty($last_error['file'])) {
199
                $errorInfo['file'] = $last_error['file'];
200
            }
201
            if (!empty($last_error['line'])) {
202
                $errorInfo['line'] = $last_error['line'];
203
            }
204
        }
205
        if (!empty($errorInfo['message'])) {
206
            return $this->halt($errorInfo);
207
        }
208
        return false;
209
    }
210
    
211
    final private function halt($errorInfo = [])
212
    {
213
        if (Fw::isCLI()) {
214
            return $this->haltCli($errorInfo);
215
        } else {
216
            return $this->haltHttp($errorInfo);
217
        }
218
    }
219
    
220
    public function haltHttp($errorInfo = [])
221
    {
222
        switch ($errorInfo['code']) {
223
            case 404:
224
                $statusCode = 404;
225
                $title = 'Resource not found';
226
                break;
227
            case 500:
228
            default:
229
                $statusCode = 500;
230
                $title = 'The App made a boo boo';
231
                break;
232
        }
233
        $this->response()->setStatusHeader($statusCode);
234
        
235
        echo '<!doctype html>
236
        <html>
237
        <head>
238
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
239
            <title>Sorry</title>
240
            <style>
241
            * {
242
                background: #f2dede; color: #a94442;
243
            }
244
            .o {
245
                display: table; position: absolute; height: 100%; width: 100%;
246
            }
247
248
            .m {
249
                display: table-cell; vertical-align: middle;
250
            }
251
252
            .i {
253
                margin-left: auto; margin-right: auto; width: 680px; text-align: center;
254
            }
255
            small {
256
                font-size: 0.7em;
257
            }
258
            </style>
259
        </head>
260
        <body>
261
            <div class="o">
262
                <div class="m">
263
                    <div class="i">';
264
        echo "<h1>{$title}</h1>";
265
        if (Env::ENV_DEV === $this->config()->getEnv()) {
266
            echo "<p>$errorInfo[message]</p>";
267
            echo "<p><small>$errorInfo[file]:$errorInfo[line]</small></p>";
268
        }
269
        echo '      </div>
270
                </div>
271
            </div>
272
        </body>
273
        </html>';
274
        return true;
275
    }
276
    
277
    public function haltCli($errorInfo = [])
278
    {
279
        echo 'The App made a boo boo' . PHP_EOL;
280
        if (Env::ENV_DEV === $this->config()->getEnv()) {
281
            echo $errorInfo['message'] . PHP_EOL;
282
            echo "$errorInfo[file]:$errorInfo[line]" . PHP_EOL;
283
        }
284
        return true;
285
    }
286
}
287