Completed
Push — develop ( dec153...c04063 )
by Schlaefer
07:30 queued 05:03
created

Phile::process()   C

Complexity

Conditions 7
Paths 5

Size

Total Lines 29
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 7

Importance

Changes 0
Metric Value
dl 0
loc 29
ccs 17
cts 17
cp 1
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 17
nc 5
nop 2
crap 7
1
<?php
2
/**
3
 * @author  PhileCMS
4
 * @link    https://philecms.com
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
Bug introduced by
This use statement conflicts with another class in this namespace, Phile\Event.

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:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/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 before OtherDir/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:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
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
23
/**
24
 * Phile Core class
25
 */
26
class Phile implements MiddlewareInterface
27
{
28
    /** @var Config Phile configuration */
29
    protected $config;
30
31
    /** @var Event event-bus */
32
    protected $eventBus;
33
34
    /** @var array callbacks run at bootstrap */
35
    protected $bootstrapConfigs = [];
36
37
    /** @var array callbacks run on middleware-setup */
38
    protected $middlewareConfigs = [];
39
40
    /**
41
     * Constructor sets-up base Phile environment
42
     */
43 29
    public function __construct(Event $eventBus, Config $config)
44
    {
45 29
        $this->eventBus = $eventBus;
46 29
        $this->config = $config;
47 29
    }
48
49
    /**
50
     * Adds bootstrap-setup
51
     */
52 29
    public function addBootstrap(callable $bootstrap): self
53
    {
54 29
        $this->bootstrapConfigs[] = $bootstrap;
55 29
        return $this;
56
    }
57
58
    /**
59
     * Adds middleware-setup
60
     */
61 29
    public function addMiddleware(callable $middleware): self
62
    {
63 29
        $this->middlewareConfigs[] = $middleware;
64 29
        return $this;
65
    }
66
67
    /**
68
     * Performs bootstrap
69
     */
70 28
    public function bootstrap(): self
71
    {
72 28
        foreach ($this->bootstrapConfigs as $config) {
73 28
            call_user_func_array($config, [$this->eventBus, $this->config]);
74
        }
75 28
        return $this;
76
    }
77
78
    /**
79
     * Populates Phile controlled middle-ware-queue
80
     */
81 5
    public function middleware(MiddlewareQueue $queue): MiddlewareQueue
82
    {
83 5
        foreach ($this->middlewareConfigs as $config) {
84 5
            call_user_func_array($config, [$queue, $this->eventBus, $this->config]);
85
        }
86 5
        return $queue;
87
    }
88
89
    /**
90
     * Run a request through Phile and create a response
91
     */
92 5
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
93
    {
94 5
        $this->config->lock();
95
96 5
        $router = new Router($request->getServerParams());
97 5
        Container::getInstance()->set('Phile_Router', $router);
98
99 5
        $response = $this->triggerEventWithResponse('after_init_core');
100 5
        if ($response) {
101 1
            return $response;
102
        }
103
104 5
        $page = $this->resolveCurrentPage($router);
105 5
        if ($page instanceof ResponseInterface) {
106 2
            return $page;
107
        }
108
109 4
        $notFound = $page->getPageId() == $this->config->get('not_found_page');
110 4
        if ($notFound && !$this->config->get('handle_not_found')) {
111 1
            return $handler->handle($request);
112
        }
113
114 3
        $html = $this->renderHtml($page);
115 3
        if ($html instanceof ResponseInterface) {
116 1
            return $html;
117
        }
118
119 3
        return $this->createResponse($html, $notFound ? 404 : 200);
120
    }
121
122
    /**
123
     * Resolves request into the current page
124
     *
125
     * @return Page|ResponseInterface
126
     */
127 5
    protected function resolveCurrentPage(Router $router)
128
    {
129 5
        $pageId = $router->getCurrentUrl();
130 5
        $response = $this->triggerEventWithResponse('request_uri', ['uri' => $pageId]);
131 5
        if ($response) {
132 1
            return $response;
133
        }
134
135 5
        $repository = new Repository();
136 5
        $page = $repository->findByPath($pageId);
137 5
        $found = $page instanceof Page;
138
139 5
        if ($found && $pageId !== $page->getPageId()) {
140 1
            $url = $router->urlForPage($page->getPageId());
141 1
            return (new Response)->createRedirectResponse($url, 301);
142
        }
143
144 4
        if (!$found) {
145 3
            $page = $repository->findByPath($this->config->get('not_found_page'));
146 3
            $this->eventBus->trigger('after_404');
147
        }
148
149 4
        $response = $this->triggerEventWithResponse(
150 4
            'after_resolve_page',
151 4
            ['pageId' => $pageId, 'page' => &$page]
152
        );
153 4
        if ($response) {
154 1
            return $response;
155
        }
156
157 4
        return $page;
158
    }
159
160
    /**
161
     * Renders page into output format (HTML)
162
     *
163
     * @return string|ResponseInterface
164
     */
165 3
    protected function renderHtml(Page $page)
166
    {
167 3
        $this->eventBus->trigger('before_init_template');
168 3
        $engine = ServiceLocator::getService('Phile_Template');
169
170 3
        $coreVars = $this->config->getTemplateVars();
171 3
        $templateVars = Registry::get('templateVars') + $coreVars;
172 3
        Registry::set('templateVars', $templateVars);
173
174 3
        $response = $this->triggerEventWithResponse(
175 3
            'before_render_template',
176 3
            ['templateEngine' => &$engine]
177
        );
178 3
        if ($response) {
179 1
            return $response;
180
        }
181
182 3
        $engine->setCurrentPage($page);
183 3
        $html = $engine->render();
184
185 3
        $this->eventBus->trigger(
186 3
            'after_render_template',
187 3
            ['templateEngine' => &$engine, 'output' => &$html]
188
        );
189
190 3
        return $html;
191
    }
192
193
    /**
194
     * Creates response
195
     */
196 3
    protected function createResponse(string $output, int $status): ResponseInterface
197
    {
198 3
        $charset = $this->config->get('charset');
199 3
        $response = (new Response)
200 3
            ->createHtmlResponse($output)
201 3
            ->withHeader('Content-Type', 'text/html; charset=' . $charset)
202 3
            ->withStatus($status);
203 3
        return $this->triggerEventWithResponse('after_response_created', ['response' => &$response]);
204
    }
205
206
    /**
207
     * Triggers event injecting a response parameter and returning it if set
208
     */
209 5
    protected function triggerEventWithResponse(string $eventName, array $eventData = []): ?ResponseInterface
210
    {
211 5
        if (empty($eventData['response'])) {
212 5
            $response = null;
213 5
            $eventData = array_merge(['response' => &$response], $eventData);
214
        }
215 5
        $this->eventBus->trigger($eventName, $eventData);
216 5
        return $eventData['response'];
217
    }
218
}
219