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 |
|
|
|
|
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; |
|
|
|
|
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 |
|
|
|
|
147
|
|
|
return $this->shutdown($e, true); |
148
|
|
|
} catch (\Exception $e) { |
149
|
|
|
return $this->shutdown($e, true); |
150
|
|
|
} |
151
|
|
|
return true; |
|
|
|
|
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 |
|
|
|
|
162
|
|
|
return $this->shutdown($e, true); |
163
|
|
|
} catch (\Exception $e) { |
164
|
|
|
return $this->shutdown($e, true); |
165
|
|
|
} |
166
|
|
|
return true; |
|
|
|
|
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
|
|
|
|
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.