1 | <?php |
||
2 | |||
3 | /* |
||
4 | * This file is part of CacheTool. |
||
5 | * |
||
6 | * (c) Samuel Gordalina <[email protected]> |
||
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 CacheTool\Adapter; |
||
13 | |||
14 | use CacheTool\Code; |
||
15 | use hollodotme\FastCGI\Client; |
||
16 | use hollodotme\FastCGI\Interfaces\ConfiguresSocketConnection; |
||
17 | use hollodotme\FastCGI\SocketConnections\NetworkSocket; |
||
18 | use hollodotme\FastCGI\SocketConnections\UnixDomainSocket; |
||
19 | use hollodotme\FastCGI\Requests\PostRequest; |
||
20 | |||
21 | class FastCGI extends AbstractAdapter |
||
22 | { |
||
23 | /** |
||
24 | * @var Client |
||
25 | */ |
||
26 | protected $client; |
||
27 | |||
28 | /** |
||
29 | * @var ConfiguresSocketConnection |
||
30 | */ |
||
31 | protected $connection; |
||
32 | |||
33 | /** |
||
34 | * @var Array of patterns matching php socket files |
||
35 | */ |
||
36 | protected $possibleSocketFilePatterns = [ |
||
37 | '/var/run/php*.sock', |
||
38 | '/var/run/php/*.sock' |
||
39 | ]; |
||
40 | |||
41 | /** |
||
42 | * @var string |
||
43 | */ |
||
44 | protected $host; |
||
45 | |||
46 | /** |
||
47 | * @var string |
||
48 | */ |
||
49 | protected $chroot; |
||
50 | |||
51 | /** |
||
52 | * @param string $host 127.0.0.1:9000 or /var/run/php5-fpm.sock |
||
53 | * @param string $chroot |
||
54 | */ |
||
55 | 10 | public function __construct($host = null, $chroot = null) |
|
56 | { |
||
57 | // try to guess where it is |
||
58 | 10 | if ($host === null) { |
|
59 | 6 | foreach ($this->possibleSocketFilePatterns as $possibleSocketFilePattern) { |
|
60 | 6 | $possibleSocketFile = current(glob($possibleSocketFilePattern)); |
|
61 | 6 | if (file_exists($possibleSocketFile)) { |
|
62 | 6 | $host = $possibleSocketFile; |
|
63 | break; |
||
64 | } |
||
65 | } |
||
66 | 6 | if ($host === null) { |
|
67 | $host = '127.0.0.1:9000'; |
||
68 | } |
||
69 | } |
||
70 | |||
71 | 10 | $this->host = $host; |
|
72 | |||
73 | 10 | if (false !== strpos($host, ':')) { |
|
74 | 4 | $last = strrpos($host, ':'); |
|
75 | 4 | $port = substr($host, $last + 1, strlen($host)); |
|
76 | 4 | $host = substr($host, 0, $last); |
|
77 | |||
78 | 4 | $IPv6 = '/^(?:[A-F0-9]{0,4}:){1,7}[A-F0-9]{0,4}$/'; |
|
79 | 4 | if (preg_match($IPv6, $host) === 1) { |
|
80 | // IPv6 addresses need to be surrounded by brackets |
||
81 | // see: https://www.php.net/manual/en/function.stream-socket-client.php#refsect1-function.stream-socket-client-notes |
||
82 | 1 | $host = "[${host}]"; |
|
83 | } |
||
84 | |||
85 | 4 | $this->connection = new NetworkSocket( |
|
86 | $host, # Hostname |
||
87 | $port, # Port |
||
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||
88 | 4 | 5000, # Connect timeout in milliseconds (default: 5000) |
|
89 | 4 | 120000 # Read/write timeout in milliseconds (default: 5000) |
|
90 | ); |
||
91 | } else { |
||
92 | 6 | $this->connection = new UnixDomainSocket( |
|
93 | $host, # Socket path to php-fpm |
||
94 | 6 | 5000, # Connect timeout in milliseconds (default: 5000) |
|
95 | 6 | 120000 # Read/write timeout in milliseconds (default: 5000) |
|
96 | ); |
||
97 | } |
||
98 | |||
99 | 10 | $this->client = new Client(); |
|
100 | |||
101 | 10 | if ($chroot !== null) { |
|
102 | 3 | $this->chroot = rtrim($chroot, '/'); |
|
103 | } |
||
104 | 10 | } |
|
105 | |||
106 | /** |
||
107 | * {@inheritdoc} |
||
108 | */ |
||
109 | 3 | protected function doRun(Code $code) |
|
110 | { |
||
111 | 3 | $body = $this->request($code); |
|
112 | |||
113 | 1 | if (@unserialize($body) === false) { |
|
114 | throw new \RuntimeException(sprintf("Error: %s", $body)); |
||
115 | } |
||
116 | |||
117 | 1 | return $body; |
|
118 | } |
||
119 | |||
120 | 3 | protected function request(Code $code) |
|
121 | { |
||
122 | 3 | $file = $this->createTemporaryFile(); |
|
123 | 3 | $this->logger->info(sprintf('FastCGI: Dumped code to file: %s', $file)); |
|
124 | |||
125 | try { |
||
126 | 3 | $code->writeTo($file); |
|
127 | |||
128 | 1 | $this->logger->info(sprintf('FastCGI: Requesting FPM using socket: %s', $this->host)); |
|
129 | 1 | $request = new PostRequest($this->getScriptFileName($file), ''); |
|
130 | 1 | $response = $this->client->sendRequest($this->connection, $request); |
|
131 | 1 | $this->logger->debug(sprintf('FastCGI: Response: %s', json_encode($response))); |
|
132 | |||
133 | 1 | if (!@unlink($file)) { |
|
134 | $this->logger->debug(sprintf('FastCGI: Could not delete file: %s', $file)); |
||
135 | } |
||
136 | |||
137 | 1 | return $response->getBody(); |
|
138 | 2 | } catch (\Exception $e) { |
|
139 | 2 | if (!@unlink($file)) { |
|
140 | 2 | $this->logger->debug(sprintf('FastCGI: Could not delete file: %s', $file)); |
|
141 | } |
||
142 | |||
143 | 2 | throw new \RuntimeException( |
|
144 | 2 | sprintf('FastCGI error: %s (%s)', $e->getMessage(), $this->host), |
|
145 | 2 | $e->getCode(), |
|
146 | $e |
||
147 | ); |
||
148 | } |
||
149 | } |
||
150 | |||
151 | /** |
||
152 | * @param string $file |
||
153 | * @return string |
||
154 | * @throws \RuntimeException |
||
155 | */ |
||
156 | 2 | protected function getScriptFileName($file) |
|
157 | { |
||
158 | 2 | if ($this->chroot) { |
|
159 | 1 | if (substr($file, 0, strlen($this->chroot)) === $this->chroot) { |
|
160 | 1 | return substr($file, strlen($this->chroot)); |
|
161 | } |
||
162 | throw new \RuntimeException('FastCGI configured to be chrooted, but file not in chroot directory.'); |
||
163 | } |
||
164 | 1 | return $file; |
|
165 | } |
||
166 | } |
||
167 |