1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
/* |
6
|
|
|
* This file is part of Symplify |
7
|
|
|
* Copyright (c) 2016 Tomas Votruba (http://tomasvotruba.cz). |
8
|
|
|
*/ |
9
|
|
|
|
10
|
|
|
namespace Symplify\PHP7_Sculpin\HttpServer; |
11
|
|
|
|
12
|
|
|
use React\EventLoop\StreamSelectLoop; |
13
|
|
|
use React\Http\Request; |
14
|
|
|
use React\Http\Response; |
15
|
|
|
use React\Http\Server as ReactHttpServer; |
16
|
|
|
use React\Socket\Server as ReactSocketServer; |
17
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
18
|
|
|
|
19
|
|
|
final class HttpServer |
20
|
|
|
{ |
21
|
|
|
/** |
22
|
|
|
* @var string |
23
|
|
|
*/ |
24
|
|
|
private $outputDirectory; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* @var StreamSelectLoop |
28
|
|
|
*/ |
29
|
|
|
private $streamSelectLoop; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @var ReactHttpServer |
33
|
|
|
*/ |
34
|
|
|
private $reactHttpServer; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @var int |
38
|
|
|
*/ |
39
|
|
|
private $port; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @var ReactSocketServer |
43
|
|
|
*/ |
44
|
|
|
private $reactSocketServer; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @var OutputInterface |
48
|
|
|
*/ |
49
|
|
|
private $output; |
50
|
|
|
|
51
|
1 |
|
public function __construct(string $outputDirectory, OutputInterface $output) |
52
|
|
|
{ |
53
|
1 |
|
$this->outputuDirectory = $outputDirectory; |
|
|
|
|
54
|
1 |
|
$this->output = $output; |
55
|
1 |
|
} |
56
|
|
|
|
57
|
1 |
|
public function init(int $port = 8000) |
58
|
|
|
{ |
59
|
1 |
|
$this->setupDependencies(); |
60
|
|
|
|
61
|
1 |
|
$this->port = $port; |
62
|
|
|
|
63
|
1 |
|
$this->reactHttpServer->on('request', function (Request $request, Response $response) { |
64
|
|
|
$path = $this->outputDirectory.'/'.ltrim(rawurldecode($request->getPath()), '/'); |
65
|
|
|
if (is_dir($path)) { |
66
|
|
|
$path .= '/index.html'; |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
if (!file_exists($path)) { |
70
|
|
|
$this->send404Response($request, $response); |
71
|
|
|
} else { |
72
|
|
|
$this->send200Response($request, $response, $path); |
73
|
|
|
} |
74
|
1 |
|
}); |
75
|
|
|
|
76
|
1 |
|
$this->reactSocketServer->listen($this->port, '0.0.0.0'); |
77
|
1 |
|
} |
78
|
|
|
|
79
|
1 |
|
public function addPeriodicTimer(int $interval, callable $callback) |
80
|
|
|
{ |
81
|
1 |
|
$this->streamSelectLoop->addPeriodicTimer($interval, $callback); |
82
|
1 |
|
} |
83
|
|
|
|
84
|
|
|
public function run() |
85
|
|
|
{ |
86
|
|
|
$this->output->write(sprintf( |
87
|
|
|
'Starting Sculpin at <info>http://localhost:%s</info>', |
88
|
|
|
$this->port |
89
|
|
|
)); |
90
|
|
|
$this->output->write('Quit the server with <info>CONTROL-C</info>.'); |
91
|
|
|
|
92
|
|
|
$this->streamSelectLoop->run(); |
93
|
|
|
} |
94
|
|
|
|
95
|
1 |
|
private function setupDependencies() |
96
|
|
|
{ |
97
|
1 |
|
$this->streamSelectLoop = new StreamSelectLoop(); |
98
|
1 |
|
$this->reactSocketServer = new ReactSocketServer($this->streamSelectLoop); |
99
|
1 |
|
$this->reactHttpServer = new ReactHttpServer($this->reactSocketServer); |
100
|
1 |
|
} |
101
|
|
|
|
102
|
|
|
private function send404Response(Request $request, Response $response) |
103
|
|
|
{ |
104
|
|
|
$this->logRequest(404, $request); |
105
|
|
|
$response->writeHead(404, [ |
106
|
|
|
'Content-Type' => 'text/html', |
107
|
|
|
]); |
108
|
|
|
|
109
|
|
|
$response->end( |
110
|
|
|
'<h1>404</h1>'. |
111
|
|
|
'<h2>Not Found</h2>'. |
112
|
|
|
'<p>'. |
113
|
|
|
'The embedded <a href="https://sculpin.io">Sculpin</a> web server could not find the requested resource.'. |
114
|
|
|
'</p>' |
115
|
|
|
); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
private function logRequest(int $responseCode, Request $request) |
119
|
|
|
{ |
120
|
|
|
$message = sprintf( |
121
|
|
|
'%s for path: "%s"', |
122
|
|
|
$responseCode, |
123
|
|
|
$request->getPath() |
124
|
|
|
); |
125
|
|
|
|
126
|
|
|
$this->output->write($this->prepareResponseMessage($responseCode, $message)); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
private function send200Response(Request $request, Response $response, string $path) |
130
|
|
|
{ |
131
|
|
|
$this->logRequest(200, $request); |
132
|
|
|
$response->end(file_get_contents($path)); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
private function prepareResponseMessage(int $responseCode, string $message) : string |
136
|
|
|
{ |
137
|
|
|
$wrapOpen = ''; |
138
|
|
|
$wrapClose = ''; |
139
|
|
|
if ($responseCode >= 400) { |
140
|
|
|
$wrapOpen = '<error>'; |
141
|
|
|
$wrapClose = '</error>'; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
return $wrapOpen.$message.$wrapClose; |
145
|
|
|
} |
146
|
|
|
} |
147
|
|
|
|
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.