Completed
Pull Request — master (#4)
by Tomáš
04:16
created

HttpServer::init()   B

Complexity

Conditions 5
Paths 1

Size

Total Lines 43
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 43
ccs 0
cts 35
cp 0
rs 8.439
cc 5
eloc 25
nc 1
nop 1
crap 30
1
<?php
2
3
/*
4
 * This file is a part of Sculpin.
5
 *
6
 * (c) Dragonfly Development In.
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sculpin\Bundle\SculpinBundle\HttpServer;
13
14
use Dflydev\ApacheMimeTypes\PhpRepository;
15
use React\EventLoop\StreamSelectLoop;
16
use React\Http\Request;
17
use React\Http\Response;
18
use React\Http\Server as ReactHttpServer;
19
use React\Socket\Server as ReactSocketServer;
20
use Sculpin\Core\Io\ConsoleIo;
21
22
final class HttpServer
23
{
24
    /**
25
     * @var ConsoleIo
26
     */
27
    private $consoleIo;
28
29
    /**
30
     * @var string
31
     */
32
    private $outputDirectory;
33
34
    /**
35
     * @var PhpRepository
36
     */
37
    private $repository;
38
39
    /**
40
     * @var PhpRepository
41
     */
42
    private $phpRepository;
43
44
    /**
45
     * @var StreamSelectLoop
46
     */
47
    private $streamSelectLoop;
48
49
    /**
50
     * @var int
51
     */
52
    private $port;
53
54
    public function __construct(
55
        string $outputDirectory,
56
        ConsoleIo $consoleIo,
57
        PhpRepository $phpRepository,
58
        StreamSelectLoop $streamSelectLoop,
59
        ReactSocketServer $reactSocketServer,
60
        ReactHttpServer $reactHttpServer
61
    ) {
62
        $this->outputDirectory = $outputDirectory;
63
        $this->consoleIo = $consoleIo;
64
        $this->phpRepository = $phpRepository;
65
        $this->streamSelectLoop = $streamSelectLoop;
66
        $this->socketServer = $reactSocketServer;
0 ignored issues
show
Bug introduced by
The property socketServer does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
67
        $this->reactHttpServer = $reactHttpServer;
0 ignored issues
show
Bug introduced by
The property reactHttpServer does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
68
    }
69
70
    public function init(int $port = 8000)
71
    {
72
        $this->port = $port;
73
74
        $this->reactHttpServer->on('request', function (Request $request, Response $response) {
75
            $path = $this->outputDirectory.'/'.ltrim(rawurldecode($request->getPath()), '/');
76
            if (is_dir($path)) {
77
                $path .= '/index.html';
78
            }
79
80
            if (!file_exists($path)) {
81
                $this->logRequest(404, $request);
82
                $response->writeHead(404, [
83
                    'Content-Type' => 'text/html',
84
                ]);
85
86
                return $response->end(
87
                    '<h1>404</h1>'.
88
                    '<h2>Not Found</h2>'.
89
                    '<p>'.
90
                    'The embedded <a href="https://sculpin.io">Sculpin</a> web server could not find the requested resource.'.
91
                    '</p>'
92
                );
93
            }
94
95
            $type = 'application/octet-stream';
96
97
            if ('' !== $extension = pathinfo($path, PATHINFO_EXTENSION)) {
98
                if ($guessedType = $this->repository->findType($extension)) {
99
                    $type = $guessedType;
100
                }
101
            }
102
103
            $this->logRequest(200, $request);
104
105
            $response->writeHead(200, [
106
                'Content-Type' => $type,
107
            ]);
108
            $response->end(file_get_contents($path));
109
        });
110
111
        $this->socketServer->listen($port, '0.0.0.0');
112
    }
113
114
    public function addPeriodicTimer(int $interval, callable $callback)
115
    {
116
        $this->streamSelectLoop->addPeriodicTimer($interval, $callback);
117
    }
118
119
    public function run()
120
    {
121
        $this->consoleIo->write('Starting Sculpin server.');
122
        $this->consoleIo->write(sprintf(
123
            'Server is running at <info>http://%s:%s</info>',
124
            'localhost',
125
            $this->port
126
        ));
127
        $this->consoleIo->write('Quit the server with CONTROL-C.');
128
129
        $this->streamSelectLoop->run();
130
    }
131
132
    private function logRequest(string $responseCode, Request $request)
133
    {
134
        $wrapOpen = '';
135
        $wrapClose = '';
136
        if ($responseCode >= 400) {
137
            $wrapOpen = '<comment>';
138
            $wrapClose = '</comment>';
139
        }
140
141
        $message = sprintf(
142
            '[%s] "%s %s HTTP/%s" %s',
143
            date('d/M/Y H:i:s'),
144
            $request->getMethod(),
145
            $request->getPath(),
146
            $request->getHttpVersion(),
147
            $responseCode
148
        );
149
150
        $this->consoleIo->write($wrapOpen.$message.$wrapClose);
151
    }
152
}
153