gordalina /
cachetool
| 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 |