Last active
January 22, 2021 22:31
-
-
Save divinity76/adcf1526d3e6a536fda20fdb1dd86339 to your computer and use it in GitHub Desktop.
pdo async query...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// Warning: this script is suspectible to https://bugs.php.net/bug.php?id=80523 | |
// that bug was actually discovered with this class! | |
class AsyncQuery | |
{ | |
private $proc; | |
private $stdout_handle; | |
private $stderr_handle; | |
private $ready = false; | |
public function fetchAll(): array | |
{ | |
$this->blockUntilReady(); | |
rewind($this->stdout_handle); | |
$code = stream_get_contents($this->stdout_handle); | |
// i want to use json_decode instead, but json can't handle binary data like "\x80" | |
// but var_export() + eval() is both binary safe =/ | |
$ret = unserialize($code); | |
return $ret; | |
} | |
public function blockUntilReady(): void | |
{ | |
while (! $this->isReady()) { | |
usleep(100 * 1000); // 100 ms / 0.1 seconds.. | |
} | |
} | |
public function isReady(): bool | |
{ | |
if ($this->ready) { | |
return true; | |
} | |
$status = proc_get_status($this->proc); | |
if ($status["running"]) { | |
return false; | |
} | |
$exitcode = $status["exitcode"]; | |
rewind($this->stderr_handle); | |
$stderr = stream_get_contents($this->stderr_handle); | |
if ($exitcode !== 0 || ! empty($stderr)) { | |
throw new \RuntimeException("php query error, exitcode: {$exitcode} stderr: {$stderr}"); | |
} | |
$this->ready = true; | |
return true; | |
} | |
function __construct(string $query, string $pdo_dsn, string $username, string $password) | |
{ | |
$code = '$db = new PDO(' . var_export($pdo_dsn, true) . "," . var_export($username, true) . ',' . var_export($password, true) . "," . var_export(array( | |
\PDO::ATTR_EMULATE_PREPARES => false, | |
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, | |
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC | |
), true) . ");"; | |
$code .= 'echo serialize($db->query('; | |
if (1 || ctype_print($query)) { | |
$code .= var_export($query, true); | |
} else { | |
$code .= "hex2bin(" . var_export(bin2hex($query), true) . ")"; | |
} | |
$code .= ")->fetchAll());"; | |
$cmd = 'php -r ' . escapeshellarg($code); | |
$this->stdout_handle = tmpfile(); | |
$this->stderr_handle = tmpfile(); | |
$descriptorspecs = array( | |
// we don't use stdin, but if none is created, the child inherit ours, we don't want that. | |
// so we create a stdin for the child just to close it. | |
0 => array( | |
"pipe", | |
"rb" | |
), | |
1 => $this->stdout_handle, | |
2 => $this->stderr_handle | |
); | |
$pipes = []; | |
$this->proc = proc_open($cmd, $descriptorspecs, $pipes); | |
fclose($pipes[0]); | |
unset($pipes[0], $pipes); | |
} | |
private $clean = false; | |
private function cleanup(): void | |
{ | |
if ($this->clean) { | |
return; | |
} | |
$this->clean = true; | |
proc_close($this->proc); | |
fclose($this->stdout_handle); | |
fclose($this->stderr_handle); | |
} | |
function __destruct() | |
{ | |
$this->cleanup(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment