1 | <?php |
||
2 | /** |
||
3 | * @author PhileCMS |
||
4 | * @link https://philecms.github.io |
||
5 | * @license http://opensource.org/licenses/MIT |
||
6 | */ |
||
7 | |||
8 | namespace Phile; |
||
9 | |||
10 | use Phile\Core\Config; |
||
11 | use Phile\Core\Container; |
||
12 | use Phile\Core\Event; |
||
0 ignored issues
–
show
|
|||
13 | use Phile\Core\Response; |
||
14 | use Phile\Core\Router; |
||
15 | use Phile\Http\MiddlewareQueue; |
||
16 | use Phile\Model\Page; |
||
17 | use Phile\Repository\Page as Repository; |
||
18 | use Psr\Http\Message\ResponseInterface; |
||
19 | use Psr\Http\Message\ServerRequestInterface; |
||
20 | use Psr\Http\Server\MiddlewareInterface; |
||
21 | use Psr\Http\Server\RequestHandlerInterface; |
||
22 | use Phile\Core\ServiceLocator; |
||
0 ignored issues
–
show
This use statement conflicts with another class in this namespace,
Phile\ServiceLocator . Consider defining an alias.
Let?s assume that you have a directory layout like this: .
|-- OtherDir
| |-- Bar.php
| `-- Foo.php
`-- SomeDir
`-- Foo.php
and let?s assume the following content of // Bar.php
namespace OtherDir;
use SomeDir\Foo; // This now conflicts the class OtherDir\Foo
If both files PHP Fatal error: Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php
However, as // Bar.php
namespace OtherDir;
use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
|
|||
23 | |||
24 | /** |
||
25 | * Phile Core class |
||
26 | */ |
||
27 | class Phile implements MiddlewareInterface |
||
28 | { |
||
29 | /** @var Config Phile configuration */ |
||
30 | protected $config; |
||
31 | |||
32 | /** @var Event event-bus */ |
||
33 | protected $eventBus; |
||
34 | |||
35 | /** @var array callbacks run at bootstrap */ |
||
36 | protected $bootstrapConfigs = []; |
||
37 | |||
38 | /** @var array callbacks run on middleware-setup */ |
||
39 | protected $middlewareConfigs = []; |
||
40 | |||
41 | /** |
||
42 | * Constructor sets-up base Phile environment |
||
43 | */ |
||
44 | 35 | public function __construct(Event $eventBus, Config $config) |
|
45 | { |
||
46 | 35 | $this->eventBus = $eventBus; |
|
47 | 35 | $this->config = $config; |
|
48 | } |
||
49 | |||
50 | /** |
||
51 | * Adds bootstrap-setup |
||
52 | */ |
||
53 | 35 | public function addBootstrap(callable $bootstrap): self |
|
54 | { |
||
55 | 35 | $this->bootstrapConfigs[] = $bootstrap; |
|
56 | 35 | return $this; |
|
57 | } |
||
58 | |||
59 | /** |
||
60 | * Adds middleware-setup |
||
61 | */ |
||
62 | 35 | public function addMiddleware(callable $middleware): self |
|
63 | { |
||
64 | 35 | $this->middlewareConfigs[] = $middleware; |
|
65 | 35 | return $this; |
|
66 | } |
||
67 | |||
68 | /** |
||
69 | * Performs bootstrap |
||
70 | */ |
||
71 | 34 | public function bootstrap(): self |
|
72 | { |
||
73 | 34 | foreach ($this->bootstrapConfigs as $config) { |
|
74 | 34 | call_user_func_array($config, [$this->eventBus, $this->config]); |
|
75 | } |
||
76 | 34 | return $this; |
|
77 | } |
||
78 | |||
79 | /** |
||
80 | * Populates Phile controlled middle-ware-queue |
||
81 | */ |
||
82 | 8 | public function middleware(MiddlewareQueue $queue): MiddlewareQueue |
|
83 | { |
||
84 | 8 | foreach ($this->middlewareConfigs as $config) { |
|
85 | 8 | call_user_func_array($config, [$queue, $this->eventBus, $this->config]); |
|
86 | } |
||
87 | 8 | return $queue; |
|
88 | } |
||
89 | |||
90 | /** |
||
91 | * Run a request through Phile and create a response |
||
92 | */ |
||
93 | 8 | public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface |
|
94 | { |
||
95 | 8 | $this->config->lock(); |
|
96 | |||
97 | 8 | $router = new Router($request->getServerParams()); |
|
98 | 8 | Container::getInstance()->set('Phile_Router', $router); |
|
99 | |||
100 | 8 | $response = $this->triggerEventWithResponse('after_init_core'); |
|
101 | 7 | if ($response) { |
|
102 | 1 | return $response; |
|
103 | } |
||
104 | |||
105 | 7 | $page = $this->resolveCurrentPage($router); |
|
106 | 7 | if ($page instanceof ResponseInterface) { |
|
107 | 2 | return $page; |
|
108 | } |
||
109 | |||
110 | 6 | $notFound = $page->getPageId() == $this->config->get('not_found_page'); |
|
111 | 6 | if ($notFound && !$this->config->get('handle_not_found')) { |
|
112 | 1 | return $handler->handle($request); |
|
113 | } |
||
114 | |||
115 | 5 | $html = $this->renderHtml($page); |
|
116 | 5 | if ($html instanceof ResponseInterface) { |
|
117 | 1 | return $html; |
|
118 | } |
||
119 | |||
120 | 5 | return $this->createResponse($html, $notFound ? 404 : 200); |
|
121 | } |
||
122 | |||
123 | /** |
||
124 | * Resolves request into the current page |
||
125 | * |
||
126 | * @return Page|ResponseInterface |
||
127 | */ |
||
128 | 7 | protected function resolveCurrentPage(Router $router) |
|
129 | { |
||
130 | 7 | $pageId = $router->getCurrentUrl(); |
|
131 | 7 | $response = $this->triggerEventWithResponse('request_uri', ['uri' => $pageId]); |
|
132 | 7 | if ($response) { |
|
133 | 1 | return $response; |
|
134 | } |
||
135 | |||
136 | 7 | $repository = new Repository(); |
|
137 | 7 | $page = $repository->findByPath($pageId); |
|
138 | 7 | $found = $page instanceof Page; |
|
139 | |||
140 | 7 | if ($found && $pageId !== $page->getPageId()) { |
|
141 | 1 | $url = $router->urlForPage($page->getPageId()); |
|
142 | 1 | return (new Response)->createRedirectResponse($url, 301); |
|
143 | } |
||
144 | |||
145 | 6 | if (!$found) { |
|
146 | 5 | $page = $repository->findByPath($this->config->get('not_found_page')); |
|
147 | 5 | $this->eventBus->trigger('after_404'); |
|
148 | } |
||
149 | |||
150 | 6 | $response = $this->triggerEventWithResponse( |
|
151 | 'after_resolve_page', |
||
152 | 6 | ['pageId' => $pageId, 'page' => &$page] |
|
153 | ); |
||
154 | 6 | if ($response) { |
|
155 | 1 | return $response; |
|
156 | } |
||
157 | |||
158 | 6 | return $page; |
|
159 | } |
||
160 | |||
161 | /** |
||
162 | * Renders page into output format (HTML) |
||
163 | * |
||
164 | * @return string|ResponseInterface |
||
165 | */ |
||
166 | 5 | protected function renderHtml(Page $page) |
|
167 | { |
||
168 | 5 | $this->eventBus->trigger('before_init_template'); |
|
169 | 5 | $engine = ServiceLocator::getService('Phile_Template'); |
|
170 | |||
171 | 5 | $coreVars = $this->config->getTemplateVars(); |
|
172 | 5 | $templateVars = Registry::get('templateVars') + $coreVars; |
|
173 | 5 | Registry::set('templateVars', $templateVars); |
|
174 | |||
175 | 5 | $response = $this->triggerEventWithResponse( |
|
176 | 'before_render_template', |
||
177 | 5 | ['templateEngine' => &$engine] |
|
178 | ); |
||
179 | 5 | if ($response) { |
|
180 | 1 | return $response; |
|
181 | } |
||
182 | |||
183 | 5 | $engine->setCurrentPage($page); |
|
184 | 5 | $html = $engine->render(); |
|
185 | |||
186 | 5 | $this->eventBus->trigger( |
|
187 | 'after_render_template', |
||
188 | 5 | ['templateEngine' => &$engine, 'output' => &$html] |
|
189 | ); |
||
190 | |||
191 | 5 | return $html; |
|
192 | } |
||
193 | |||
194 | /** |
||
195 | * Creates response |
||
196 | * |
||
197 | * @throws Exception |
||
198 | */ |
||
199 | 5 | protected function createResponse(string $output, int $status): ResponseInterface |
|
200 | { |
||
201 | 5 | $charset = $this->config->get('charset'); |
|
202 | 5 | $response = (new Response) |
|
203 | 5 | ->createHtmlResponse($output) |
|
204 | 5 | ->withHeader('Content-Type', 'text/html; charset=' . $charset) |
|
205 | 5 | ->withStatus($status); |
|
206 | |||
207 | 5 | $response = $this->triggerEventWithResponse( |
|
208 | 'after_response_created', |
||
209 | 5 | ['response' => &$response] |
|
210 | ); |
||
211 | |||
212 | 4 | if (!($response instanceof ResponseInterface)) { |
|
213 | 1 | throw new Exception('Response not valid.', 1523630697); |
|
214 | } |
||
215 | |||
216 | 3 | return $response; |
|
217 | } |
||
218 | |||
219 | /** |
||
220 | * Triggers event injecting a response parameter and returning it if set |
||
221 | * |
||
222 | * @throws Exception |
||
223 | */ |
||
224 | 8 | protected function triggerEventWithResponse(string $eventName, array $eventData = []): ?ResponseInterface |
|
225 | { |
||
226 | 8 | if (empty($eventData['response'])) { |
|
227 | 8 | $response = null; |
|
228 | 8 | $eventData = array_merge(['response' => &$response], $eventData); |
|
229 | } |
||
230 | |||
231 | 8 | $this->eventBus->trigger($eventName, $eventData); |
|
232 | |||
233 | 7 | if ($eventData['response'] !== null && !($eventData['response'] instanceof ResponseInterface)) { |
|
234 | 1 | throw new Exception( |
|
235 | 1 | "No valid response in \$eventData['response'] for event $eventName.", |
|
236 | 1523630698 |
||
237 | ); |
||
238 | } |
||
239 | |||
240 | 7 | return $eventData['response']; |
|
241 | } |
||
242 | } |
||
243 |
Let?s assume that you have a directory layout like this:
and let?s assume the following content of
Bar.php
:If both files
OtherDir/Foo.php
andSomeDir/Foo.php
are loaded in the same runtime, you will see a PHP error such as the following:PHP Fatal error: Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php
However, as
OtherDir/Foo.php
does not necessarily have to be loaded and the error is only triggered if it is loaded beforeOtherDir/Bar.php
, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias: