Completed
Pull Request — master (#75)
by Vladimir
02:16
created

RouteDispatcher::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
/**
4
 * @copyright 2018 Vladimir Jimenez
5
 * @license   https://github.com/stakx-io/stakx/blob/master/LICENSE.md MIT
6
 */
7
8
namespace allejo\stakx\Server;
9
10
use allejo\stakx\Compiler;
11
use allejo\stakx\Document\BasePageView;
12
use allejo\stakx\Document\CollectableItem;
13
use allejo\stakx\Document\DynamicPageView;
14
use allejo\stakx\Document\ReadableDocument;
15
use allejo\stakx\Document\RepeaterPageView;
16
use allejo\stakx\Document\StaticPageView;
17
use allejo\stakx\Document\TemplateReadyDocument;
18
use allejo\stakx\Filesystem\FilesystemLoader as fs;
19
use allejo\stakx\Service;
20
use FastRoute\RouteCollector;
21
use Psr\Http\Message\ServerRequestInterface;
22
use React\Http\Response;
23
24
class RouteDispatcher
0 ignored issues
show
Coding Style introduced by
Since you have declared the constructor as private, maybe you should also declare the class as final.
Loading history...
25
{
26
    private static $baseUrl = '';
27
    private $lastModified = [];
28
29
    /**
30
     * @internal
31
     */
32
    private function __construct()
33
    {
34
    }
35
36
    /**
37
     * Build a controller for handling a Static PageView's URL.
38
     *
39
     * @param StaticPageView $pageView
40
     * @param Compiler       $compiler
41
     *
42
     * @return \Closure
43
     */
44
    private function staticPageViewController(StaticPageView $pageView, Compiler $compiler)
45
    {
46
        return function () use ($pageView, $compiler) {
47
            Service::setOption('currentTemplate', $pageView->getAbsoluteFilePath());
48
49
            if ($this->hasBeenTouched($pageView))
50
            {
51
                $pageView->readContent();
52
            }
53
54
            $mimeType = MimeDetector::getMimeType(fs::getExtension($pageView->getTargetFile()));
0 ignored issues
show
Documentation introduced by
$pageView->getTargetFile() is of type string, but the function expects a object<string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
55
56
            return new Response(
57
                200,
58
                ['Content-Type' => $mimeType],
59
                $compiler->renderStaticPageView($pageView)
60
            );
61
        };
62
    }
63
64
    /**
65
     * Build a controller for handling a Dynamic PageView's URL.
66
     *
67
     * @param DynamicPageView $pageView
68
     * @param Compiler        $compiler
69
     *
70
     * @return \Closure
71
     */
72
    private function dynamicPageViewController(DynamicPageView $pageView, Compiler $compiler)
73
    {
74
        return function (ServerRequestInterface $request) use ($pageView, $compiler) {
75
            Service::setOption('currentTemplate', $pageView->getAbsoluteFilePath());
76
77
            $contentItem = self::getContentItem($pageView, self::normalizeUrl($request->getUri()->getPath()));
78
79
            if ($contentItem === null)
80
            {
81
                return DevServer::return404();
82
            }
83
84
            if ($this->hasBeenTouched($pageView))
85
            {
86
                $pageView->readContent();
87
            }
88
            if ($this->hasBeenTouched($contentItem))
0 ignored issues
show
Documentation introduced by
$contentItem is of type object<allejo\stakx\Document\CollectableItem>, but the function expects a object<allejo\stakx\Document\ReadableDocument>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
89
            {
90
                $contentItem->readContent();
91
            }
92
93
            return DevServer::return200($compiler->renderDynamicPageView($pageView, $contentItem));
0 ignored issues
show
Documentation introduced by
$contentItem is of type object<allejo\stakx\Document\CollectableItem>, but the function expects a object<allejo\stakx\Docu...\TemplateReadyDocument>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
94
        };
95
    }
96
97
    /**
98
     * Build a controller for handling a Repeater PageView's URL.
99
     *
100
     * @param RepeaterPageView $pageView
101
     * @param Compiler         $compiler
102
     *
103
     * @return \Closure
104
     */
105
    private function repeaterPageViewController(RepeaterPageView $pageView, Compiler $compiler)
106
    {
107
        return function (ServerRequestInterface $request) use ($pageView, $compiler) {
108
            $permalinks = $pageView->getRepeaterPermalinks();
109
            $url = self::normalizeUrl($request->getUri()->getPath());
110
111
            foreach ($permalinks as $permalink)
112
            {
113
                if ($permalink->getEvaluated() === $url)
114
                {
115
                    return DevServer::return200(
116
                        $compiler->renderRepeaterPageView($pageView, $permalink)
117
                    );
118
                }
119
            }
120
121
            return DevServer::return404();
122
        };
123
    }
124
125
    /**
126
     * Return the appropriate controller based on a PageView's type.
127
     *
128
     * @param BasePageView|DynamicPageView|RepeaterPageView|StaticPageView $pageView
129
     * @param Compiler                                                     $compiler
130
     *
131
     * @return \Closure
132
     */
133
    private function createController(BasePageView $pageView, Compiler $compiler)
134
    {
135
        switch ($pageView->getType())
136
        {
137
            case BasePageView::STATIC_TYPE:
138
                return $this->staticPageViewController($pageView, $compiler);
0 ignored issues
show
Compatibility introduced by
$pageView of type object<allejo\stakx\Document\BasePageView> is not a sub-type of object<allejo\stakx\Document\StaticPageView>. It seems like you assume a child class of the class allejo\stakx\Document\BasePageView to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
139
140
            case BasePageView::DYNAMIC_TYPE:
141
                return $this->dynamicPageViewController($pageView, $compiler);
0 ignored issues
show
Compatibility introduced by
$pageView of type object<allejo\stakx\Document\BasePageView> is not a sub-type of object<allejo\stakx\Document\DynamicPageView>. It seems like you assume a child class of the class allejo\stakx\Document\BasePageView to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
142
143
            case BasePageView::REPEATER_TYPE:
144
                return $this->repeaterPageViewController($pageView, $compiler);
0 ignored issues
show
Compatibility introduced by
$pageView of type object<allejo\stakx\Document\BasePageView> is not a sub-type of object<allejo\stakx\Document\RepeaterPageView>. It seems like you assume a child class of the class allejo\stakx\Document\BasePageView to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
145
146
            default:
147
                return function () {
148
                    $errMsg = 'This URL type has not yet been implemented.';
149
150
                    return new Response(501, ['Content-Type' => 'text/plain'], $errMsg);
151
                };
152
        }
153
    }
154
155
    /**
156
     * Check to see if a file has been touched since we last read it.
157
     *
158
     * @param ReadableDocument $document
159
     *
160
     * @return bool True if the file has been modified since it was last accessed
161
     */
162
    private function hasBeenTouched(ReadableDocument $document)
163
    {
164
        $rPath = $document->getRelativeFilePath();
165
166
        if (!isset($this->lastModified[$rPath]))
167
        {
168
            $this->lastModified[$rPath] = $document->getLastModified();
169
170
            return true;
171
        }
172
173
        return $document->getLastModified() > $this->lastModified[$rPath];
174
    }
175
176
    /**
177
     * Create a FastRoute Dispatcher.
178
     *
179
     * @param PageViewRouter $router
180
     * @param Compiler       $compiler
181
     *
182
     * @return \FastRoute\Dispatcher
183
     */
184
    public static function create(PageViewRouter $router, Compiler $compiler)
185
    {
186
        self::$baseUrl = $router->getBaseUrl();
187
188
        return \FastRoute\simpleDispatcher(function (RouteCollector $r) use ($router, $compiler) {
189
            $dispatcher = new RouteDispatcher();
190
191
            foreach ($router->getRouteMapping() as $route => $pageView)
192
            {
193
                $r->get($route, $dispatcher->createController($pageView, $compiler));
194
            }
195
        });
196
    }
197
198
    public static function normalizeUrl($url)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
199
    {
200
        return str_replace(self::$baseUrl, '/', $url);
201
    }
202
203
    /**
204
     * Find a ContentItem from a Dynamic PageView or null if it doesn't exist.
205
     *
206
     * @param DynamicPageView $pageView
207
     * @param                 $permalink
208
     *
209
     * @return CollectableItem|ReadableDocument|TemplateReadyDocument|null
210
     */
211
    private static function getContentItem(DynamicPageView $pageView, $permalink)
212
    {
213
        foreach ($pageView->getCollectableItems() as $collectableItem)
214
        {
215
            if ($collectableItem['permalink'] === $permalink)
216
            {
217
                return $collectableItem;
218
            }
219
        }
220
221
        return null;
222
    }
223
}
224