Completed
Pull Request — master (#86)
by Vladimir
08:24
created

WebServer::create()   B

Complexity

Conditions 6
Paths 1

Size

Total Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 37
rs 8.7057
c 0
b 0
f 0
cc 6
nc 1
nop 2
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\Filesystem\File;
12
use allejo\stakx\Filesystem\FilesystemPath;
13
use allejo\stakx\Service;
14
use FastRoute\Dispatcher;
15
use Psr\Http\Message\ServerRequestInterface;
16
use React\Http\Response;
17
use React\Http\Server;
18
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
19
20
class WebServer
21
{
22
    /**
23
     * Return a 200 HTML Response.
24
     *
25
     * This is just a utility function available.
26
     *
27
     * @param string $content
28
     * @param mixed  $mimeType
29
     *
30
     * @return Response
31
     */
32
    public static function return200($content, $mimeType = 'text/html')
33
    {
34
        return new Response(200, ['Content-Type' => $mimeType], $content);
35
    }
36
37
    /**
38
     * Return a 404 Response.
39
     *
40
     * This is just a utility function available.
41
     *
42
     * @return Response
43
     */
44
    public static function return404()
45
    {
46
        return new Response(404, ['Content-Type' => 'text/plain'], '404: Page not found');
47
    }
48
49
    /**
50
     * Factory method for creating a React Server instance.
51
     *
52
     * @param RouteMapper $router
53
     * @param Compiler    $compiler
54
     *
55
     * @return Server
56
     */
57
    public static function create(RouteMapper $router, Compiler $compiler)
58
    {
59
        $dispatcher = Controller::create($router, $compiler);
60
61
        return new Server(function (ServerRequestInterface $request) use ($router, $dispatcher) {
62
            $httpMethod = $request->getMethod();
63
            $urlPath = Controller::normalizeUrl($request->getUri()->getPath());
64
65
            // We're a static website, we should never support anything other than GET requests
66
            if ($httpMethod !== 'GET')
67
            {
68
                return new Response(406, ['Content-Type' => 'text/plain'], 'Method not allowed');
69
            }
70
71
            $routeInfo = $dispatcher->dispatch($httpMethod, $urlPath);
72
73
            switch ($routeInfo[0])
74
            {
75
                // We found a known URL meaning it's a PageView
76
                case Dispatcher::FOUND:
77
                    $urlPlaceholders = isset($routeInfo[2]) ? $routeInfo[2] : [];
78
79
                    return $routeInfo[1]($request, ...array_values($urlPlaceholders));
80
81
                case Dispatcher::NOT_FOUND:
82
                    if (($asset = self::searchAsset($urlPath)) !== null)
83
                    {
84
                        return $asset;
85
                    }
86
87
                    return WebServer::return404();
88
89
                default:
90
                    return WebServer::return404();
91
            }
92
        });
93
    }
94
95
    /**
96
     * Search for a static asset for our DevServer to load.
97
     *
98
     * @param string $url
99
     * @param bool   $continueNesting
100
     *
101
     * @return null|Response
102
     */
103
    private static function searchAsset($url, $continueNesting = true)
104
    {
105
        $preparedPath = substr($url, 0, 1) === '/' ? substr($url, 1) : $url;
106
        $path = new FilesystemPath($preparedPath);
107
108
        try
109
        {
110
            $file = new File($path);
111
            $mime = MimeDetector::getMimeType($file->getExtension());
112
113
            return new Response(200, ['Content-Type' => $mime], $file->getContents());
114
        }
115
        catch (FileNotFoundException $e)
116
        {
117
            if (!$continueNesting)
118
            {
119
                // This will evaluate to true if we've searched inside the themes folder and it still doesn't exist
120
                return null;
121
            }
122
123
            // If the asset doesn't exist within our project root, see if it exists in our theme folder
124
            $theme = Service::getOption('theme');
125
            $themeFile = "_themes/${theme}/" . $url;
126
127
            return self::searchAsset($themeFile, false);
128
        }
129
    }
130
}
131