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); |
|||
0 ignored issues
–
show
The class
Phile\Exception has been deprecated: since 1.4 will be removed
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
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( |
|||
0 ignored issues
–
show
The class
Phile\Exception has been deprecated: since 1.4 will be removed
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
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: