MadelineProto/src/danog/MadelineProto/Ipc/Runner/WebRunner.php

97 lines
3.5 KiB
PHP

<?php
namespace danog\MadelineProto\Ipc\Runner;
use Amp\Parallel\Context\ContextException;
use danog\MadelineProto\Magic;
final class WebRunner extends RunnerAbstract
{
/** @var string|null Cached path to the runner script. */
private static $runPath;
/**
* Resources.
*/
private static array $resources = [];
/**
* Start.
*
* @param string $session Session path
*
* @return void
*/
public static function start(string $session, int $id): void
{
if (!isset($_SERVER['SERVER_NAME'])) {
return;
}
if (!self::$runPath) {
$uri = \parse_url('tcp://'.$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'], PHP_URL_PATH);
if (\substr($uri, -1) === '/') { // http://example.com/path/ (assumed index.php)
$uri .= 'index'; // Add fake file name
}
$uri = \str_replace('//', '/', $uri);
$rootDir = \debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$rootDir = \end($rootDir)['file'] ?? '';
if (!$rootDir) {
throw new ContextException('Could not get entry file!');
}
$rootDir = \dirname($rootDir).DIRECTORY_SEPARATOR;
$uriDir = \dirname($uri);
if ($uriDir !== '/') {
$uriDir .= DIRECTORY_SEPARATOR;
}
if (\substr($rootDir, -\strlen($uriDir)) !== $uriDir) {
throw new ContextException("Mismatch between absolute root dir ($rootDir) and URI dir ($uriDir)");
}
// Absolute root of (presumably) readable document root
$absoluteRootDir = \substr($rootDir, 0, \strlen($rootDir)-\strlen($uriDir)).DIRECTORY_SEPARATOR;
$runPath = self::getScriptPath($absoluteRootDir);
if (\substr($runPath, 0, \strlen($absoluteRootDir)) === $absoluteRootDir) { // Process runner is within readable document root
self::$runPath = \substr($runPath, \strlen($absoluteRootDir)-1);
} else {
$contents = \file_get_contents(self::SCRIPT_PATH);
$contents = \str_replace("__DIR__", \var_export($absoluteRootDir, true), $contents);
$suffix = \bin2hex(\random_bytes(10));
$runPath = $absoluteRootDir."/madeline-ipc-".$suffix.".php";
\file_put_contents($runPath, $contents);
self::$runPath = \substr($runPath, \strlen($absoluteRootDir)-1);
\register_shutdown_function(static function () use ($runPath): void {
@\unlink($runPath);
});
}
self::$runPath = \str_replace(DIRECTORY_SEPARATOR, '/', self::$runPath);
self::$runPath = \str_replace('//', '/', self::$runPath);
}
$params = [
'argv' => ['madeline-ipc', $session, $id],
'cwd' => Magic::getcwd()
];
$params = \http_build_query($params);
$address = ($_SERVER['HTTPS'] ?? false ? 'tls' : 'tcp').'://'.$_SERVER['SERVER_NAME'];
$port = $_SERVER['SERVER_PORT'];
$uri = self::$runPath.'?'.$params;
$payload = "GET $uri HTTP/1.1\r\nHost: ${_SERVER['SERVER_NAME']}\r\n\r\n";
// We don't care for results or timeouts here, PHP doesn't count IOwait time as execution time anyway
// Technically should use amphp/socket, but I guess it's OK to not introduce another dependency just for a socket that will be used once.
\fwrite($res = \fsockopen($address, $port), $payload);
self::$resources []= $res;
}
}