@@ -20,50 +20,50 @@ |
||
| 20 | 20 | */ |
| 21 | 21 | class ProcessTimedOutException extends RuntimeException |
| 22 | 22 | { |
| 23 | - public const TYPE_GENERAL = 1; |
|
| 24 | - public const TYPE_IDLE = 2; |
|
| 23 | + public const TYPE_GENERAL = 1; |
|
| 24 | + public const TYPE_IDLE = 2; |
|
| 25 | 25 | |
| 26 | - private $process; |
|
| 27 | - private $timeoutType; |
|
| 26 | + private $process; |
|
| 27 | + private $timeoutType; |
|
| 28 | 28 | |
| 29 | - public function __construct(Process $process, int $timeoutType) |
|
| 30 | - { |
|
| 31 | - $this->process = $process; |
|
| 32 | - $this->timeoutType = $timeoutType; |
|
| 29 | + public function __construct(Process $process, int $timeoutType) |
|
| 30 | + { |
|
| 31 | + $this->process = $process; |
|
| 32 | + $this->timeoutType = $timeoutType; |
|
| 33 | 33 | |
| 34 | - parent::__construct(sprintf( |
|
| 35 | - 'The process "%s" exceeded the timeout of %s seconds.', |
|
| 36 | - $process->getCommandLine(), |
|
| 37 | - $this->getExceededTimeout() |
|
| 38 | - )); |
|
| 39 | - } |
|
| 34 | + parent::__construct(sprintf( |
|
| 35 | + 'The process "%s" exceeded the timeout of %s seconds.', |
|
| 36 | + $process->getCommandLine(), |
|
| 37 | + $this->getExceededTimeout() |
|
| 38 | + )); |
|
| 39 | + } |
|
| 40 | 40 | |
| 41 | - public function getProcess() |
|
| 42 | - { |
|
| 43 | - return $this->process; |
|
| 44 | - } |
|
| 41 | + public function getProcess() |
|
| 42 | + { |
|
| 43 | + return $this->process; |
|
| 44 | + } |
|
| 45 | 45 | |
| 46 | - public function isGeneralTimeout() |
|
| 47 | - { |
|
| 48 | - return self::TYPE_GENERAL === $this->timeoutType; |
|
| 49 | - } |
|
| 46 | + public function isGeneralTimeout() |
|
| 47 | + { |
|
| 48 | + return self::TYPE_GENERAL === $this->timeoutType; |
|
| 49 | + } |
|
| 50 | 50 | |
| 51 | - public function isIdleTimeout() |
|
| 52 | - { |
|
| 53 | - return self::TYPE_IDLE === $this->timeoutType; |
|
| 54 | - } |
|
| 51 | + public function isIdleTimeout() |
|
| 52 | + { |
|
| 53 | + return self::TYPE_IDLE === $this->timeoutType; |
|
| 54 | + } |
|
| 55 | 55 | |
| 56 | - public function getExceededTimeout() |
|
| 57 | - { |
|
| 58 | - switch ($this->timeoutType) { |
|
| 59 | - case self::TYPE_GENERAL: |
|
| 60 | - return $this->process->getTimeout(); |
|
| 56 | + public function getExceededTimeout() |
|
| 57 | + { |
|
| 58 | + switch ($this->timeoutType) { |
|
| 59 | + case self::TYPE_GENERAL: |
|
| 60 | + return $this->process->getTimeout(); |
|
| 61 | 61 | |
| 62 | - case self::TYPE_IDLE: |
|
| 63 | - return $this->process->getIdleTimeout(); |
|
| 62 | + case self::TYPE_IDLE: |
|
| 63 | + return $this->process->getIdleTimeout(); |
|
| 64 | 64 | |
| 65 | - default: |
|
| 66 | - throw new \LogicException(sprintf('Unknown timeout type "%d".', $this->timeoutType)); |
|
| 67 | - } |
|
| 68 | - } |
|
| 65 | + default: |
|
| 66 | + throw new \LogicException(sprintf('Unknown timeout type "%d".', $this->timeoutType)); |
|
| 67 | + } |
|
| 68 | + } |
|
| 69 | 69 | } |
@@ -20,35 +20,35 @@ |
||
| 20 | 20 | */ |
| 21 | 21 | class ProcessFailedException extends RuntimeException |
| 22 | 22 | { |
| 23 | - private $process; |
|
| 24 | - |
|
| 25 | - public function __construct(Process $process) |
|
| 26 | - { |
|
| 27 | - if ($process->isSuccessful()) { |
|
| 28 | - throw new InvalidArgumentException('Expected a failed process, but the given process was successful.'); |
|
| 29 | - } |
|
| 30 | - |
|
| 31 | - $error = sprintf('The command "%s" failed.'."\n\nExit Code: %s(%s)\n\nWorking directory: %s", |
|
| 32 | - $process->getCommandLine(), |
|
| 33 | - $process->getExitCode(), |
|
| 34 | - $process->getExitCodeText(), |
|
| 35 | - $process->getWorkingDirectory() |
|
| 36 | - ); |
|
| 37 | - |
|
| 38 | - if (!$process->isOutputDisabled()) { |
|
| 39 | - $error .= sprintf("\n\nOutput:\n================\n%s\n\nError Output:\n================\n%s", |
|
| 40 | - $process->getOutput(), |
|
| 41 | - $process->getErrorOutput() |
|
| 42 | - ); |
|
| 43 | - } |
|
| 44 | - |
|
| 45 | - parent::__construct($error); |
|
| 46 | - |
|
| 47 | - $this->process = $process; |
|
| 48 | - } |
|
| 49 | - |
|
| 50 | - public function getProcess() |
|
| 51 | - { |
|
| 52 | - return $this->process; |
|
| 53 | - } |
|
| 23 | + private $process; |
|
| 24 | + |
|
| 25 | + public function __construct(Process $process) |
|
| 26 | + { |
|
| 27 | + if ($process->isSuccessful()) { |
|
| 28 | + throw new InvalidArgumentException('Expected a failed process, but the given process was successful.'); |
|
| 29 | + } |
|
| 30 | + |
|
| 31 | + $error = sprintf('The command "%s" failed.'."\n\nExit Code: %s(%s)\n\nWorking directory: %s", |
|
| 32 | + $process->getCommandLine(), |
|
| 33 | + $process->getExitCode(), |
|
| 34 | + $process->getExitCodeText(), |
|
| 35 | + $process->getWorkingDirectory() |
|
| 36 | + ); |
|
| 37 | + |
|
| 38 | + if (!$process->isOutputDisabled()) { |
|
| 39 | + $error .= sprintf("\n\nOutput:\n================\n%s\n\nError Output:\n================\n%s", |
|
| 40 | + $process->getOutput(), |
|
| 41 | + $process->getErrorOutput() |
|
| 42 | + ); |
|
| 43 | + } |
|
| 44 | + |
|
| 45 | + parent::__construct($error); |
|
| 46 | + |
|
| 47 | + $this->process = $process; |
|
| 48 | + } |
|
| 49 | + |
|
| 50 | + public function getProcess() |
|
| 51 | + { |
|
| 52 | + return $this->process; |
|
| 53 | + } |
|
| 54 | 54 | } |
@@ -20,22 +20,22 @@ |
||
| 20 | 20 | */ |
| 21 | 21 | final class ProcessSignaledException extends RuntimeException |
| 22 | 22 | { |
| 23 | - private $process; |
|
| 23 | + private $process; |
|
| 24 | 24 | |
| 25 | - public function __construct(Process $process) |
|
| 26 | - { |
|
| 27 | - $this->process = $process; |
|
| 25 | + public function __construct(Process $process) |
|
| 26 | + { |
|
| 27 | + $this->process = $process; |
|
| 28 | 28 | |
| 29 | - parent::__construct(sprintf('The process has been signaled with signal "%s".', $process->getTermSignal())); |
|
| 30 | - } |
|
| 29 | + parent::__construct(sprintf('The process has been signaled with signal "%s".', $process->getTermSignal())); |
|
| 30 | + } |
|
| 31 | 31 | |
| 32 | - public function getProcess(): Process |
|
| 33 | - { |
|
| 34 | - return $this->process; |
|
| 35 | - } |
|
| 32 | + public function getProcess(): Process |
|
| 33 | + { |
|
| 34 | + return $this->process; |
|
| 35 | + } |
|
| 36 | 36 | |
| 37 | - public function getSignal(): int |
|
| 38 | - { |
|
| 39 | - return $this->getProcess()->getTermSignal(); |
|
| 40 | - } |
|
| 37 | + public function getSignal(): int |
|
| 38 | + { |
|
| 39 | + return $this->getProcess()->getTermSignal(); |
|
| 40 | + } |
|
| 41 | 41 | } |
@@ -20,42 +20,42 @@ |
||
| 20 | 20 | */ |
| 21 | 21 | interface PipesInterface |
| 22 | 22 | { |
| 23 | - public const CHUNK_SIZE = 16384; |
|
| 24 | - |
|
| 25 | - /** |
|
| 26 | - * Returns an array of descriptors for the use of proc_open. |
|
| 27 | - */ |
|
| 28 | - public function getDescriptors(): array; |
|
| 29 | - |
|
| 30 | - /** |
|
| 31 | - * Returns an array of filenames indexed by their related stream in case these pipes use temporary files. |
|
| 32 | - * |
|
| 33 | - * @return string[] |
|
| 34 | - */ |
|
| 35 | - public function getFiles(): array; |
|
| 36 | - |
|
| 37 | - /** |
|
| 38 | - * Reads data in file handles and pipes. |
|
| 39 | - * |
|
| 40 | - * @param bool $blocking Whether to use blocking calls or not |
|
| 41 | - * @param bool $close Whether to close pipes if they've reached EOF |
|
| 42 | - * |
|
| 43 | - * @return string[] An array of read data indexed by their fd |
|
| 44 | - */ |
|
| 45 | - public function readAndWrite(bool $blocking, bool $close = false): array; |
|
| 46 | - |
|
| 47 | - /** |
|
| 48 | - * Returns if the current state has open file handles or pipes. |
|
| 49 | - */ |
|
| 50 | - public function areOpen(): bool; |
|
| 51 | - |
|
| 52 | - /** |
|
| 53 | - * Returns if pipes are able to read output. |
|
| 54 | - */ |
|
| 55 | - public function haveReadSupport(): bool; |
|
| 56 | - |
|
| 57 | - /** |
|
| 58 | - * Closes file handles and pipes. |
|
| 59 | - */ |
|
| 60 | - public function close(); |
|
| 23 | + public const CHUNK_SIZE = 16384; |
|
| 24 | + |
|
| 25 | + /** |
|
| 26 | + * Returns an array of descriptors for the use of proc_open. |
|
| 27 | + */ |
|
| 28 | + public function getDescriptors(): array; |
|
| 29 | + |
|
| 30 | + /** |
|
| 31 | + * Returns an array of filenames indexed by their related stream in case these pipes use temporary files. |
|
| 32 | + * |
|
| 33 | + * @return string[] |
|
| 34 | + */ |
|
| 35 | + public function getFiles(): array; |
|
| 36 | + |
|
| 37 | + /** |
|
| 38 | + * Reads data in file handles and pipes. |
|
| 39 | + * |
|
| 40 | + * @param bool $blocking Whether to use blocking calls or not |
|
| 41 | + * @param bool $close Whether to close pipes if they've reached EOF |
|
| 42 | + * |
|
| 43 | + * @return string[] An array of read data indexed by their fd |
|
| 44 | + */ |
|
| 45 | + public function readAndWrite(bool $blocking, bool $close = false): array; |
|
| 46 | + |
|
| 47 | + /** |
|
| 48 | + * Returns if the current state has open file handles or pipes. |
|
| 49 | + */ |
|
| 50 | + public function areOpen(): bool; |
|
| 51 | + |
|
| 52 | + /** |
|
| 53 | + * Returns if pipes are able to read output. |
|
| 54 | + */ |
|
| 55 | + public function haveReadSupport(): bool; |
|
| 56 | + |
|
| 57 | + /** |
|
| 58 | + * Closes file handles and pipes. |
|
| 59 | + */ |
|
| 60 | + public function close(); |
|
| 61 | 61 | } |
@@ -22,145 +22,145 @@ |
||
| 22 | 22 | */ |
| 23 | 23 | class UnixPipes extends AbstractPipes |
| 24 | 24 | { |
| 25 | - private $ttyMode; |
|
| 26 | - private $ptyMode; |
|
| 27 | - private $haveReadSupport; |
|
| 28 | - |
|
| 29 | - public function __construct(?bool $ttyMode, bool $ptyMode, $input, bool $haveReadSupport) |
|
| 30 | - { |
|
| 31 | - $this->ttyMode = $ttyMode; |
|
| 32 | - $this->ptyMode = $ptyMode; |
|
| 33 | - $this->haveReadSupport = $haveReadSupport; |
|
| 34 | - |
|
| 35 | - parent::__construct($input); |
|
| 36 | - } |
|
| 37 | - |
|
| 38 | - /** |
|
| 39 | - * @return array |
|
| 40 | - */ |
|
| 41 | - public function __sleep() |
|
| 42 | - { |
|
| 43 | - throw new \BadMethodCallException('Cannot serialize '.__CLASS__); |
|
| 44 | - } |
|
| 45 | - |
|
| 46 | - public function __wakeup() |
|
| 47 | - { |
|
| 48 | - throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); |
|
| 49 | - } |
|
| 50 | - |
|
| 51 | - public function __destruct() |
|
| 52 | - { |
|
| 53 | - $this->close(); |
|
| 54 | - } |
|
| 55 | - |
|
| 56 | - /** |
|
| 57 | - * {@inheritdoc} |
|
| 58 | - */ |
|
| 59 | - public function getDescriptors(): array |
|
| 60 | - { |
|
| 61 | - if (!$this->haveReadSupport) { |
|
| 62 | - $nullstream = fopen('/dev/null', 'c'); |
|
| 63 | - |
|
| 64 | - return [ |
|
| 65 | - ['pipe', 'r'], |
|
| 66 | - $nullstream, |
|
| 67 | - $nullstream, |
|
| 68 | - ]; |
|
| 69 | - } |
|
| 70 | - |
|
| 71 | - if ($this->ttyMode) { |
|
| 72 | - return [ |
|
| 73 | - ['file', '/dev/tty', 'r'], |
|
| 74 | - ['file', '/dev/tty', 'w'], |
|
| 75 | - ['file', '/dev/tty', 'w'], |
|
| 76 | - ]; |
|
| 77 | - } |
|
| 78 | - |
|
| 79 | - if ($this->ptyMode && Process::isPtySupported()) { |
|
| 80 | - return [ |
|
| 81 | - ['pty'], |
|
| 82 | - ['pty'], |
|
| 83 | - ['pty'], |
|
| 84 | - ]; |
|
| 85 | - } |
|
| 86 | - |
|
| 87 | - return [ |
|
| 88 | - ['pipe', 'r'], |
|
| 89 | - ['pipe', 'w'], // stdout |
|
| 90 | - ['pipe', 'w'], // stderr |
|
| 91 | - ]; |
|
| 92 | - } |
|
| 93 | - |
|
| 94 | - /** |
|
| 95 | - * {@inheritdoc} |
|
| 96 | - */ |
|
| 97 | - public function getFiles(): array |
|
| 98 | - { |
|
| 99 | - return []; |
|
| 100 | - } |
|
| 101 | - |
|
| 102 | - /** |
|
| 103 | - * {@inheritdoc} |
|
| 104 | - */ |
|
| 105 | - public function readAndWrite(bool $blocking, bool $close = false): array |
|
| 106 | - { |
|
| 107 | - $this->unblock(); |
|
| 108 | - $w = $this->write(); |
|
| 109 | - |
|
| 110 | - $read = $e = []; |
|
| 111 | - $r = $this->pipes; |
|
| 112 | - unset($r[0]); |
|
| 113 | - |
|
| 114 | - // let's have a look if something changed in streams |
|
| 115 | - set_error_handler([$this, 'handleError']); |
|
| 116 | - if (($r || $w) && false === stream_select($r, $w, $e, 0, $blocking ? Process::TIMEOUT_PRECISION * 1E6 : 0)) { |
|
| 117 | - restore_error_handler(); |
|
| 118 | - // if a system call has been interrupted, forget about it, let's try again |
|
| 119 | - // otherwise, an error occurred, let's reset pipes |
|
| 120 | - if (!$this->hasSystemCallBeenInterrupted()) { |
|
| 121 | - $this->pipes = []; |
|
| 122 | - } |
|
| 123 | - |
|
| 124 | - return $read; |
|
| 125 | - } |
|
| 126 | - restore_error_handler(); |
|
| 127 | - |
|
| 128 | - foreach ($r as $pipe) { |
|
| 129 | - // prior PHP 5.4 the array passed to stream_select is modified and |
|
| 130 | - // lose key association, we have to find back the key |
|
| 131 | - $read[$type = array_search($pipe, $this->pipes, true)] = ''; |
|
| 132 | - |
|
| 133 | - do { |
|
| 134 | - $data = @fread($pipe, self::CHUNK_SIZE); |
|
| 135 | - $read[$type] .= $data; |
|
| 136 | - } while (isset($data[0]) && ($close || isset($data[self::CHUNK_SIZE - 1]))); |
|
| 137 | - |
|
| 138 | - if (!isset($read[$type][0])) { |
|
| 139 | - unset($read[$type]); |
|
| 140 | - } |
|
| 141 | - |
|
| 142 | - if ($close && feof($pipe)) { |
|
| 143 | - fclose($pipe); |
|
| 144 | - unset($this->pipes[$type]); |
|
| 145 | - } |
|
| 146 | - } |
|
| 147 | - |
|
| 148 | - return $read; |
|
| 149 | - } |
|
| 150 | - |
|
| 151 | - /** |
|
| 152 | - * {@inheritdoc} |
|
| 153 | - */ |
|
| 154 | - public function haveReadSupport(): bool |
|
| 155 | - { |
|
| 156 | - return $this->haveReadSupport; |
|
| 157 | - } |
|
| 158 | - |
|
| 159 | - /** |
|
| 160 | - * {@inheritdoc} |
|
| 161 | - */ |
|
| 162 | - public function areOpen(): bool |
|
| 163 | - { |
|
| 164 | - return (bool) $this->pipes; |
|
| 165 | - } |
|
| 25 | + private $ttyMode; |
|
| 26 | + private $ptyMode; |
|
| 27 | + private $haveReadSupport; |
|
| 28 | + |
|
| 29 | + public function __construct(?bool $ttyMode, bool $ptyMode, $input, bool $haveReadSupport) |
|
| 30 | + { |
|
| 31 | + $this->ttyMode = $ttyMode; |
|
| 32 | + $this->ptyMode = $ptyMode; |
|
| 33 | + $this->haveReadSupport = $haveReadSupport; |
|
| 34 | + |
|
| 35 | + parent::__construct($input); |
|
| 36 | + } |
|
| 37 | + |
|
| 38 | + /** |
|
| 39 | + * @return array |
|
| 40 | + */ |
|
| 41 | + public function __sleep() |
|
| 42 | + { |
|
| 43 | + throw new \BadMethodCallException('Cannot serialize '.__CLASS__); |
|
| 44 | + } |
|
| 45 | + |
|
| 46 | + public function __wakeup() |
|
| 47 | + { |
|
| 48 | + throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); |
|
| 49 | + } |
|
| 50 | + |
|
| 51 | + public function __destruct() |
|
| 52 | + { |
|
| 53 | + $this->close(); |
|
| 54 | + } |
|
| 55 | + |
|
| 56 | + /** |
|
| 57 | + * {@inheritdoc} |
|
| 58 | + */ |
|
| 59 | + public function getDescriptors(): array |
|
| 60 | + { |
|
| 61 | + if (!$this->haveReadSupport) { |
|
| 62 | + $nullstream = fopen('/dev/null', 'c'); |
|
| 63 | + |
|
| 64 | + return [ |
|
| 65 | + ['pipe', 'r'], |
|
| 66 | + $nullstream, |
|
| 67 | + $nullstream, |
|
| 68 | + ]; |
|
| 69 | + } |
|
| 70 | + |
|
| 71 | + if ($this->ttyMode) { |
|
| 72 | + return [ |
|
| 73 | + ['file', '/dev/tty', 'r'], |
|
| 74 | + ['file', '/dev/tty', 'w'], |
|
| 75 | + ['file', '/dev/tty', 'w'], |
|
| 76 | + ]; |
|
| 77 | + } |
|
| 78 | + |
|
| 79 | + if ($this->ptyMode && Process::isPtySupported()) { |
|
| 80 | + return [ |
|
| 81 | + ['pty'], |
|
| 82 | + ['pty'], |
|
| 83 | + ['pty'], |
|
| 84 | + ]; |
|
| 85 | + } |
|
| 86 | + |
|
| 87 | + return [ |
|
| 88 | + ['pipe', 'r'], |
|
| 89 | + ['pipe', 'w'], // stdout |
|
| 90 | + ['pipe', 'w'], // stderr |
|
| 91 | + ]; |
|
| 92 | + } |
|
| 93 | + |
|
| 94 | + /** |
|
| 95 | + * {@inheritdoc} |
|
| 96 | + */ |
|
| 97 | + public function getFiles(): array |
|
| 98 | + { |
|
| 99 | + return []; |
|
| 100 | + } |
|
| 101 | + |
|
| 102 | + /** |
|
| 103 | + * {@inheritdoc} |
|
| 104 | + */ |
|
| 105 | + public function readAndWrite(bool $blocking, bool $close = false): array |
|
| 106 | + { |
|
| 107 | + $this->unblock(); |
|
| 108 | + $w = $this->write(); |
|
| 109 | + |
|
| 110 | + $read = $e = []; |
|
| 111 | + $r = $this->pipes; |
|
| 112 | + unset($r[0]); |
|
| 113 | + |
|
| 114 | + // let's have a look if something changed in streams |
|
| 115 | + set_error_handler([$this, 'handleError']); |
|
| 116 | + if (($r || $w) && false === stream_select($r, $w, $e, 0, $blocking ? Process::TIMEOUT_PRECISION * 1E6 : 0)) { |
|
| 117 | + restore_error_handler(); |
|
| 118 | + // if a system call has been interrupted, forget about it, let's try again |
|
| 119 | + // otherwise, an error occurred, let's reset pipes |
|
| 120 | + if (!$this->hasSystemCallBeenInterrupted()) { |
|
| 121 | + $this->pipes = []; |
|
| 122 | + } |
|
| 123 | + |
|
| 124 | + return $read; |
|
| 125 | + } |
|
| 126 | + restore_error_handler(); |
|
| 127 | + |
|
| 128 | + foreach ($r as $pipe) { |
|
| 129 | + // prior PHP 5.4 the array passed to stream_select is modified and |
|
| 130 | + // lose key association, we have to find back the key |
|
| 131 | + $read[$type = array_search($pipe, $this->pipes, true)] = ''; |
|
| 132 | + |
|
| 133 | + do { |
|
| 134 | + $data = @fread($pipe, self::CHUNK_SIZE); |
|
| 135 | + $read[$type] .= $data; |
|
| 136 | + } while (isset($data[0]) && ($close || isset($data[self::CHUNK_SIZE - 1]))); |
|
| 137 | + |
|
| 138 | + if (!isset($read[$type][0])) { |
|
| 139 | + unset($read[$type]); |
|
| 140 | + } |
|
| 141 | + |
|
| 142 | + if ($close && feof($pipe)) { |
|
| 143 | + fclose($pipe); |
|
| 144 | + unset($this->pipes[$type]); |
|
| 145 | + } |
|
| 146 | + } |
|
| 147 | + |
|
| 148 | + return $read; |
|
| 149 | + } |
|
| 150 | + |
|
| 151 | + /** |
|
| 152 | + * {@inheritdoc} |
|
| 153 | + */ |
|
| 154 | + public function haveReadSupport(): bool |
|
| 155 | + { |
|
| 156 | + return $this->haveReadSupport; |
|
| 157 | + } |
|
| 158 | + |
|
| 159 | + /** |
|
| 160 | + * {@inheritdoc} |
|
| 161 | + */ |
|
| 162 | + public function areOpen(): bool |
|
| 163 | + { |
|
| 164 | + return (bool) $this->pipes; |
|
| 165 | + } |
|
| 166 | 166 | } |
@@ -26,182 +26,182 @@ |
||
| 26 | 26 | */ |
| 27 | 27 | class WindowsPipes extends AbstractPipes |
| 28 | 28 | { |
| 29 | - private $files = []; |
|
| 30 | - private $fileHandles = []; |
|
| 31 | - private $lockHandles = []; |
|
| 32 | - private $readBytes = [ |
|
| 33 | - Process::STDOUT => 0, |
|
| 34 | - Process::STDERR => 0, |
|
| 35 | - ]; |
|
| 36 | - private $haveReadSupport; |
|
| 37 | - |
|
| 38 | - public function __construct($input, bool $haveReadSupport) |
|
| 39 | - { |
|
| 40 | - $this->haveReadSupport = $haveReadSupport; |
|
| 41 | - |
|
| 42 | - if ($this->haveReadSupport) { |
|
| 43 | - // Fix for PHP bug #51800: reading from STDOUT pipe hangs forever on Windows if the output is too big. |
|
| 44 | - // Workaround for this problem is to use temporary files instead of pipes on Windows platform. |
|
| 45 | - // |
|
| 46 | - // @see https://bugs.php.net/51800 |
|
| 47 | - $pipes = [ |
|
| 48 | - Process::STDOUT => Process::OUT, |
|
| 49 | - Process::STDERR => Process::ERR, |
|
| 50 | - ]; |
|
| 51 | - $tmpDir = sys_get_temp_dir(); |
|
| 52 | - $lastError = 'unknown reason'; |
|
| 53 | - set_error_handler(function ($type, $msg) use (&$lastError) { $lastError = $msg; }); |
|
| 54 | - for ($i = 0;; ++$i) { |
|
| 55 | - foreach ($pipes as $pipe => $name) { |
|
| 56 | - $file = sprintf('%s\\sf_proc_%02X.%s', $tmpDir, $i, $name); |
|
| 57 | - |
|
| 58 | - if (!$h = fopen($file.'.lock', 'w')) { |
|
| 59 | - if (file_exists($file.'.lock')) { |
|
| 60 | - continue 2; |
|
| 61 | - } |
|
| 62 | - restore_error_handler(); |
|
| 63 | - throw new RuntimeException('A temporary file could not be opened to write the process output: '.$lastError); |
|
| 64 | - } |
|
| 65 | - if (!flock($h, \LOCK_EX | \LOCK_NB)) { |
|
| 66 | - continue 2; |
|
| 67 | - } |
|
| 68 | - if (isset($this->lockHandles[$pipe])) { |
|
| 69 | - flock($this->lockHandles[$pipe], \LOCK_UN); |
|
| 70 | - fclose($this->lockHandles[$pipe]); |
|
| 71 | - } |
|
| 72 | - $this->lockHandles[$pipe] = $h; |
|
| 73 | - |
|
| 74 | - if (!($h = fopen($file, 'w')) || !fclose($h) || !$h = fopen($file, 'r')) { |
|
| 75 | - flock($this->lockHandles[$pipe], \LOCK_UN); |
|
| 76 | - fclose($this->lockHandles[$pipe]); |
|
| 77 | - unset($this->lockHandles[$pipe]); |
|
| 78 | - continue 2; |
|
| 79 | - } |
|
| 80 | - $this->fileHandles[$pipe] = $h; |
|
| 81 | - $this->files[$pipe] = $file; |
|
| 82 | - } |
|
| 83 | - break; |
|
| 84 | - } |
|
| 85 | - restore_error_handler(); |
|
| 86 | - } |
|
| 87 | - |
|
| 88 | - parent::__construct($input); |
|
| 89 | - } |
|
| 90 | - |
|
| 91 | - /** |
|
| 92 | - * @return array |
|
| 93 | - */ |
|
| 94 | - public function __sleep() |
|
| 95 | - { |
|
| 96 | - throw new \BadMethodCallException('Cannot serialize '.__CLASS__); |
|
| 97 | - } |
|
| 98 | - |
|
| 99 | - public function __wakeup() |
|
| 100 | - { |
|
| 101 | - throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); |
|
| 102 | - } |
|
| 103 | - |
|
| 104 | - public function __destruct() |
|
| 105 | - { |
|
| 106 | - $this->close(); |
|
| 107 | - } |
|
| 108 | - |
|
| 109 | - /** |
|
| 110 | - * {@inheritdoc} |
|
| 111 | - */ |
|
| 112 | - public function getDescriptors(): array |
|
| 113 | - { |
|
| 114 | - if (!$this->haveReadSupport) { |
|
| 115 | - $nullstream = fopen('NUL', 'c'); |
|
| 116 | - |
|
| 117 | - return [ |
|
| 118 | - ['pipe', 'r'], |
|
| 119 | - $nullstream, |
|
| 120 | - $nullstream, |
|
| 121 | - ]; |
|
| 122 | - } |
|
| 123 | - |
|
| 124 | - // We're not using pipe on Windows platform as it hangs (https://bugs.php.net/51800) |
|
| 125 | - // We're not using file handles as it can produce corrupted output https://bugs.php.net/65650 |
|
| 126 | - // So we redirect output within the commandline and pass the nul device to the process |
|
| 127 | - return [ |
|
| 128 | - ['pipe', 'r'], |
|
| 129 | - ['file', 'NUL', 'w'], |
|
| 130 | - ['file', 'NUL', 'w'], |
|
| 131 | - ]; |
|
| 132 | - } |
|
| 133 | - |
|
| 134 | - /** |
|
| 135 | - * {@inheritdoc} |
|
| 136 | - */ |
|
| 137 | - public function getFiles(): array |
|
| 138 | - { |
|
| 139 | - return $this->files; |
|
| 140 | - } |
|
| 141 | - |
|
| 142 | - /** |
|
| 143 | - * {@inheritdoc} |
|
| 144 | - */ |
|
| 145 | - public function readAndWrite(bool $blocking, bool $close = false): array |
|
| 146 | - { |
|
| 147 | - $this->unblock(); |
|
| 148 | - $w = $this->write(); |
|
| 149 | - $read = $r = $e = []; |
|
| 150 | - |
|
| 151 | - if ($blocking) { |
|
| 152 | - if ($w) { |
|
| 153 | - @stream_select($r, $w, $e, 0, Process::TIMEOUT_PRECISION * 1E6); |
|
| 154 | - } elseif ($this->fileHandles) { |
|
| 155 | - usleep(Process::TIMEOUT_PRECISION * 1E6); |
|
| 156 | - } |
|
| 157 | - } |
|
| 158 | - foreach ($this->fileHandles as $type => $fileHandle) { |
|
| 159 | - $data = stream_get_contents($fileHandle, -1, $this->readBytes[$type]); |
|
| 160 | - |
|
| 161 | - if (isset($data[0])) { |
|
| 162 | - $this->readBytes[$type] += \strlen($data); |
|
| 163 | - $read[$type] = $data; |
|
| 164 | - } |
|
| 165 | - if ($close) { |
|
| 166 | - ftruncate($fileHandle, 0); |
|
| 167 | - fclose($fileHandle); |
|
| 168 | - flock($this->lockHandles[$type], \LOCK_UN); |
|
| 169 | - fclose($this->lockHandles[$type]); |
|
| 170 | - unset($this->fileHandles[$type], $this->lockHandles[$type]); |
|
| 171 | - } |
|
| 172 | - } |
|
| 173 | - |
|
| 174 | - return $read; |
|
| 175 | - } |
|
| 176 | - |
|
| 177 | - /** |
|
| 178 | - * {@inheritdoc} |
|
| 179 | - */ |
|
| 180 | - public function haveReadSupport(): bool |
|
| 181 | - { |
|
| 182 | - return $this->haveReadSupport; |
|
| 183 | - } |
|
| 184 | - |
|
| 185 | - /** |
|
| 186 | - * {@inheritdoc} |
|
| 187 | - */ |
|
| 188 | - public function areOpen(): bool |
|
| 189 | - { |
|
| 190 | - return $this->pipes && $this->fileHandles; |
|
| 191 | - } |
|
| 192 | - |
|
| 193 | - /** |
|
| 194 | - * {@inheritdoc} |
|
| 195 | - */ |
|
| 196 | - public function close() |
|
| 197 | - { |
|
| 198 | - parent::close(); |
|
| 199 | - foreach ($this->fileHandles as $type => $handle) { |
|
| 200 | - ftruncate($handle, 0); |
|
| 201 | - fclose($handle); |
|
| 202 | - flock($this->lockHandles[$type], \LOCK_UN); |
|
| 203 | - fclose($this->lockHandles[$type]); |
|
| 204 | - } |
|
| 205 | - $this->fileHandles = $this->lockHandles = []; |
|
| 206 | - } |
|
| 29 | + private $files = []; |
|
| 30 | + private $fileHandles = []; |
|
| 31 | + private $lockHandles = []; |
|
| 32 | + private $readBytes = [ |
|
| 33 | + Process::STDOUT => 0, |
|
| 34 | + Process::STDERR => 0, |
|
| 35 | + ]; |
|
| 36 | + private $haveReadSupport; |
|
| 37 | + |
|
| 38 | + public function __construct($input, bool $haveReadSupport) |
|
| 39 | + { |
|
| 40 | + $this->haveReadSupport = $haveReadSupport; |
|
| 41 | + |
|
| 42 | + if ($this->haveReadSupport) { |
|
| 43 | + // Fix for PHP bug #51800: reading from STDOUT pipe hangs forever on Windows if the output is too big. |
|
| 44 | + // Workaround for this problem is to use temporary files instead of pipes on Windows platform. |
|
| 45 | + // |
|
| 46 | + // @see https://bugs.php.net/51800 |
|
| 47 | + $pipes = [ |
|
| 48 | + Process::STDOUT => Process::OUT, |
|
| 49 | + Process::STDERR => Process::ERR, |
|
| 50 | + ]; |
|
| 51 | + $tmpDir = sys_get_temp_dir(); |
|
| 52 | + $lastError = 'unknown reason'; |
|
| 53 | + set_error_handler(function ($type, $msg) use (&$lastError) { $lastError = $msg; }); |
|
| 54 | + for ($i = 0;; ++$i) { |
|
| 55 | + foreach ($pipes as $pipe => $name) { |
|
| 56 | + $file = sprintf('%s\\sf_proc_%02X.%s', $tmpDir, $i, $name); |
|
| 57 | + |
|
| 58 | + if (!$h = fopen($file.'.lock', 'w')) { |
|
| 59 | + if (file_exists($file.'.lock')) { |
|
| 60 | + continue 2; |
|
| 61 | + } |
|
| 62 | + restore_error_handler(); |
|
| 63 | + throw new RuntimeException('A temporary file could not be opened to write the process output: '.$lastError); |
|
| 64 | + } |
|
| 65 | + if (!flock($h, \LOCK_EX | \LOCK_NB)) { |
|
| 66 | + continue 2; |
|
| 67 | + } |
|
| 68 | + if (isset($this->lockHandles[$pipe])) { |
|
| 69 | + flock($this->lockHandles[$pipe], \LOCK_UN); |
|
| 70 | + fclose($this->lockHandles[$pipe]); |
|
| 71 | + } |
|
| 72 | + $this->lockHandles[$pipe] = $h; |
|
| 73 | + |
|
| 74 | + if (!($h = fopen($file, 'w')) || !fclose($h) || !$h = fopen($file, 'r')) { |
|
| 75 | + flock($this->lockHandles[$pipe], \LOCK_UN); |
|
| 76 | + fclose($this->lockHandles[$pipe]); |
|
| 77 | + unset($this->lockHandles[$pipe]); |
|
| 78 | + continue 2; |
|
| 79 | + } |
|
| 80 | + $this->fileHandles[$pipe] = $h; |
|
| 81 | + $this->files[$pipe] = $file; |
|
| 82 | + } |
|
| 83 | + break; |
|
| 84 | + } |
|
| 85 | + restore_error_handler(); |
|
| 86 | + } |
|
| 87 | + |
|
| 88 | + parent::__construct($input); |
|
| 89 | + } |
|
| 90 | + |
|
| 91 | + /** |
|
| 92 | + * @return array |
|
| 93 | + */ |
|
| 94 | + public function __sleep() |
|
| 95 | + { |
|
| 96 | + throw new \BadMethodCallException('Cannot serialize '.__CLASS__); |
|
| 97 | + } |
|
| 98 | + |
|
| 99 | + public function __wakeup() |
|
| 100 | + { |
|
| 101 | + throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); |
|
| 102 | + } |
|
| 103 | + |
|
| 104 | + public function __destruct() |
|
| 105 | + { |
|
| 106 | + $this->close(); |
|
| 107 | + } |
|
| 108 | + |
|
| 109 | + /** |
|
| 110 | + * {@inheritdoc} |
|
| 111 | + */ |
|
| 112 | + public function getDescriptors(): array |
|
| 113 | + { |
|
| 114 | + if (!$this->haveReadSupport) { |
|
| 115 | + $nullstream = fopen('NUL', 'c'); |
|
| 116 | + |
|
| 117 | + return [ |
|
| 118 | + ['pipe', 'r'], |
|
| 119 | + $nullstream, |
|
| 120 | + $nullstream, |
|
| 121 | + ]; |
|
| 122 | + } |
|
| 123 | + |
|
| 124 | + // We're not using pipe on Windows platform as it hangs (https://bugs.php.net/51800) |
|
| 125 | + // We're not using file handles as it can produce corrupted output https://bugs.php.net/65650 |
|
| 126 | + // So we redirect output within the commandline and pass the nul device to the process |
|
| 127 | + return [ |
|
| 128 | + ['pipe', 'r'], |
|
| 129 | + ['file', 'NUL', 'w'], |
|
| 130 | + ['file', 'NUL', 'w'], |
|
| 131 | + ]; |
|
| 132 | + } |
|
| 133 | + |
|
| 134 | + /** |
|
| 135 | + * {@inheritdoc} |
|
| 136 | + */ |
|
| 137 | + public function getFiles(): array |
|
| 138 | + { |
|
| 139 | + return $this->files; |
|
| 140 | + } |
|
| 141 | + |
|
| 142 | + /** |
|
| 143 | + * {@inheritdoc} |
|
| 144 | + */ |
|
| 145 | + public function readAndWrite(bool $blocking, bool $close = false): array |
|
| 146 | + { |
|
| 147 | + $this->unblock(); |
|
| 148 | + $w = $this->write(); |
|
| 149 | + $read = $r = $e = []; |
|
| 150 | + |
|
| 151 | + if ($blocking) { |
|
| 152 | + if ($w) { |
|
| 153 | + @stream_select($r, $w, $e, 0, Process::TIMEOUT_PRECISION * 1E6); |
|
| 154 | + } elseif ($this->fileHandles) { |
|
| 155 | + usleep(Process::TIMEOUT_PRECISION * 1E6); |
|
| 156 | + } |
|
| 157 | + } |
|
| 158 | + foreach ($this->fileHandles as $type => $fileHandle) { |
|
| 159 | + $data = stream_get_contents($fileHandle, -1, $this->readBytes[$type]); |
|
| 160 | + |
|
| 161 | + if (isset($data[0])) { |
|
| 162 | + $this->readBytes[$type] += \strlen($data); |
|
| 163 | + $read[$type] = $data; |
|
| 164 | + } |
|
| 165 | + if ($close) { |
|
| 166 | + ftruncate($fileHandle, 0); |
|
| 167 | + fclose($fileHandle); |
|
| 168 | + flock($this->lockHandles[$type], \LOCK_UN); |
|
| 169 | + fclose($this->lockHandles[$type]); |
|
| 170 | + unset($this->fileHandles[$type], $this->lockHandles[$type]); |
|
| 171 | + } |
|
| 172 | + } |
|
| 173 | + |
|
| 174 | + return $read; |
|
| 175 | + } |
|
| 176 | + |
|
| 177 | + /** |
|
| 178 | + * {@inheritdoc} |
|
| 179 | + */ |
|
| 180 | + public function haveReadSupport(): bool |
|
| 181 | + { |
|
| 182 | + return $this->haveReadSupport; |
|
| 183 | + } |
|
| 184 | + |
|
| 185 | + /** |
|
| 186 | + * {@inheritdoc} |
|
| 187 | + */ |
|
| 188 | + public function areOpen(): bool |
|
| 189 | + { |
|
| 190 | + return $this->pipes && $this->fileHandles; |
|
| 191 | + } |
|
| 192 | + |
|
| 193 | + /** |
|
| 194 | + * {@inheritdoc} |
|
| 195 | + */ |
|
| 196 | + public function close() |
|
| 197 | + { |
|
| 198 | + parent::close(); |
|
| 199 | + foreach ($this->fileHandles as $type => $handle) { |
|
| 200 | + ftruncate($handle, 0); |
|
| 201 | + fclose($handle); |
|
| 202 | + flock($this->lockHandles[$type], \LOCK_UN); |
|
| 203 | + fclose($this->lockHandles[$type]); |
|
| 204 | + } |
|
| 205 | + $this->fileHandles = $this->lockHandles = []; |
|
| 206 | + } |
|
| 207 | 207 | } |
@@ -20,75 +20,75 @@ |
||
| 20 | 20 | */ |
| 21 | 21 | class InputStream implements \IteratorAggregate |
| 22 | 22 | { |
| 23 | - /** @var callable|null */ |
|
| 24 | - private $onEmpty = null; |
|
| 25 | - private $input = []; |
|
| 26 | - private $open = true; |
|
| 23 | + /** @var callable|null */ |
|
| 24 | + private $onEmpty = null; |
|
| 25 | + private $input = []; |
|
| 26 | + private $open = true; |
|
| 27 | 27 | |
| 28 | - /** |
|
| 29 | - * Sets a callback that is called when the write buffer becomes empty. |
|
| 30 | - */ |
|
| 31 | - public function onEmpty(callable $onEmpty = null) |
|
| 32 | - { |
|
| 33 | - $this->onEmpty = $onEmpty; |
|
| 34 | - } |
|
| 28 | + /** |
|
| 29 | + * Sets a callback that is called when the write buffer becomes empty. |
|
| 30 | + */ |
|
| 31 | + public function onEmpty(callable $onEmpty = null) |
|
| 32 | + { |
|
| 33 | + $this->onEmpty = $onEmpty; |
|
| 34 | + } |
|
| 35 | 35 | |
| 36 | - /** |
|
| 37 | - * Appends an input to the write buffer. |
|
| 38 | - * |
|
| 39 | - * @param resource|string|int|float|bool|\Traversable|null $input The input to append as scalar, |
|
| 40 | - * stream resource or \Traversable |
|
| 41 | - */ |
|
| 42 | - public function write($input) |
|
| 43 | - { |
|
| 44 | - if (null === $input) { |
|
| 45 | - return; |
|
| 46 | - } |
|
| 47 | - if ($this->isClosed()) { |
|
| 48 | - throw new RuntimeException(sprintf('"%s" is closed.', static::class)); |
|
| 49 | - } |
|
| 50 | - $this->input[] = ProcessUtils::validateInput(__METHOD__, $input); |
|
| 51 | - } |
|
| 36 | + /** |
|
| 37 | + * Appends an input to the write buffer. |
|
| 38 | + * |
|
| 39 | + * @param resource|string|int|float|bool|\Traversable|null $input The input to append as scalar, |
|
| 40 | + * stream resource or \Traversable |
|
| 41 | + */ |
|
| 42 | + public function write($input) |
|
| 43 | + { |
|
| 44 | + if (null === $input) { |
|
| 45 | + return; |
|
| 46 | + } |
|
| 47 | + if ($this->isClosed()) { |
|
| 48 | + throw new RuntimeException(sprintf('"%s" is closed.', static::class)); |
|
| 49 | + } |
|
| 50 | + $this->input[] = ProcessUtils::validateInput(__METHOD__, $input); |
|
| 51 | + } |
|
| 52 | 52 | |
| 53 | - /** |
|
| 54 | - * Closes the write buffer. |
|
| 55 | - */ |
|
| 56 | - public function close() |
|
| 57 | - { |
|
| 58 | - $this->open = false; |
|
| 59 | - } |
|
| 53 | + /** |
|
| 54 | + * Closes the write buffer. |
|
| 55 | + */ |
|
| 56 | + public function close() |
|
| 57 | + { |
|
| 58 | + $this->open = false; |
|
| 59 | + } |
|
| 60 | 60 | |
| 61 | - /** |
|
| 62 | - * Tells whether the write buffer is closed or not. |
|
| 63 | - */ |
|
| 64 | - public function isClosed() |
|
| 65 | - { |
|
| 66 | - return !$this->open; |
|
| 67 | - } |
|
| 61 | + /** |
|
| 62 | + * Tells whether the write buffer is closed or not. |
|
| 63 | + */ |
|
| 64 | + public function isClosed() |
|
| 65 | + { |
|
| 66 | + return !$this->open; |
|
| 67 | + } |
|
| 68 | 68 | |
| 69 | - /** |
|
| 70 | - * @return \Traversable |
|
| 71 | - */ |
|
| 72 | - #[\ReturnTypeWillChange] |
|
| 73 | - public function getIterator() |
|
| 74 | - { |
|
| 75 | - $this->open = true; |
|
| 69 | + /** |
|
| 70 | + * @return \Traversable |
|
| 71 | + */ |
|
| 72 | + #[\ReturnTypeWillChange] |
|
| 73 | + public function getIterator() |
|
| 74 | + { |
|
| 75 | + $this->open = true; |
|
| 76 | 76 | |
| 77 | - while ($this->open || $this->input) { |
|
| 78 | - if (!$this->input) { |
|
| 79 | - yield ''; |
|
| 80 | - continue; |
|
| 81 | - } |
|
| 82 | - $current = array_shift($this->input); |
|
| 77 | + while ($this->open || $this->input) { |
|
| 78 | + if (!$this->input) { |
|
| 79 | + yield ''; |
|
| 80 | + continue; |
|
| 81 | + } |
|
| 82 | + $current = array_shift($this->input); |
|
| 83 | 83 | |
| 84 | - if ($current instanceof \Iterator) { |
|
| 85 | - yield from $current; |
|
| 86 | - } else { |
|
| 87 | - yield $current; |
|
| 88 | - } |
|
| 89 | - if (!$this->input && $this->open && null !== $onEmpty = $this->onEmpty) { |
|
| 90 | - $this->write($onEmpty($this)); |
|
| 91 | - } |
|
| 92 | - } |
|
| 93 | - } |
|
| 84 | + if ($current instanceof \Iterator) { |
|
| 85 | + yield from $current; |
|
| 86 | + } else { |
|
| 87 | + yield $current; |
|
| 88 | + } |
|
| 89 | + if (!$this->input && $this->open && null !== $onEmpty = $this->onEmpty) { |
|
| 90 | + $this->write($onEmpty($this)); |
|
| 91 | + } |
|
| 92 | + } |
|
| 93 | + } |
|
| 94 | 94 | } |
@@ -12,31 +12,31 @@ |
||
| 12 | 12 | use Symfony\Polyfill\Php80 as p; |
| 13 | 13 | |
| 14 | 14 | if (\PHP_VERSION_ID >= 80000) { |
| 15 | - return; |
|
| 15 | + return; |
|
| 16 | 16 | } |
| 17 | 17 | |
| 18 | 18 | if (!defined('FILTER_VALIDATE_BOOL') && defined('FILTER_VALIDATE_BOOLEAN')) { |
| 19 | - define('FILTER_VALIDATE_BOOL', \FILTER_VALIDATE_BOOLEAN); |
|
| 19 | + define('FILTER_VALIDATE_BOOL', \FILTER_VALIDATE_BOOLEAN); |
|
| 20 | 20 | } |
| 21 | 21 | |
| 22 | 22 | if (!function_exists('fdiv')) { |
| 23 | - function fdiv(float $num1, float $num2): float { return p\Php80::fdiv($num1, $num2); } |
|
| 23 | + function fdiv(float $num1, float $num2): float { return p\Php80::fdiv($num1, $num2); } |
|
| 24 | 24 | } |
| 25 | 25 | if (!function_exists('preg_last_error_msg')) { |
| 26 | - function preg_last_error_msg(): string { return p\Php80::preg_last_error_msg(); } |
|
| 26 | + function preg_last_error_msg(): string { return p\Php80::preg_last_error_msg(); } |
|
| 27 | 27 | } |
| 28 | 28 | if (!function_exists('str_contains')) { |
| 29 | - function str_contains(?string $haystack, ?string $needle): bool { return p\Php80::str_contains($haystack ?? '', $needle ?? ''); } |
|
| 29 | + function str_contains(?string $haystack, ?string $needle): bool { return p\Php80::str_contains($haystack ?? '', $needle ?? ''); } |
|
| 30 | 30 | } |
| 31 | 31 | if (!function_exists('str_starts_with')) { |
| 32 | - function str_starts_with(?string $haystack, ?string $needle): bool { return p\Php80::str_starts_with($haystack ?? '', $needle ?? ''); } |
|
| 32 | + function str_starts_with(?string $haystack, ?string $needle): bool { return p\Php80::str_starts_with($haystack ?? '', $needle ?? ''); } |
|
| 33 | 33 | } |
| 34 | 34 | if (!function_exists('str_ends_with')) { |
| 35 | - function str_ends_with(?string $haystack, ?string $needle): bool { return p\Php80::str_ends_with($haystack ?? '', $needle ?? ''); } |
|
| 35 | + function str_ends_with(?string $haystack, ?string $needle): bool { return p\Php80::str_ends_with($haystack ?? '', $needle ?? ''); } |
|
| 36 | 36 | } |
| 37 | 37 | if (!function_exists('get_debug_type')) { |
| 38 | - function get_debug_type($value): string { return p\Php80::get_debug_type($value); } |
|
| 38 | + function get_debug_type($value): string { return p\Php80::get_debug_type($value); } |
|
| 39 | 39 | } |
| 40 | 40 | if (!function_exists('get_resource_id')) { |
| 41 | - function get_resource_id($resource): int { return p\Php80::get_resource_id($resource); } |
|
| 41 | + function get_resource_id($resource): int { return p\Php80::get_resource_id($resource); } |
|
| 42 | 42 | } |
@@ -20,86 +20,86 @@ |
||
| 20 | 20 | */ |
| 21 | 21 | final class Php80 |
| 22 | 22 | { |
| 23 | - public static function fdiv(float $dividend, float $divisor): float |
|
| 24 | - { |
|
| 25 | - return @($dividend / $divisor); |
|
| 26 | - } |
|
| 23 | + public static function fdiv(float $dividend, float $divisor): float |
|
| 24 | + { |
|
| 25 | + return @($dividend / $divisor); |
|
| 26 | + } |
|
| 27 | 27 | |
| 28 | - public static function get_debug_type($value): string |
|
| 29 | - { |
|
| 30 | - switch (true) { |
|
| 31 | - case null === $value: return 'null'; |
|
| 32 | - case \is_bool($value): return 'bool'; |
|
| 33 | - case \is_string($value): return 'string'; |
|
| 34 | - case \is_array($value): return 'array'; |
|
| 35 | - case \is_int($value): return 'int'; |
|
| 36 | - case \is_float($value): return 'float'; |
|
| 37 | - case \is_object($value): break; |
|
| 38 | - case $value instanceof \__PHP_Incomplete_Class: return '__PHP_Incomplete_Class'; |
|
| 39 | - default: |
|
| 40 | - if (null === $type = @get_resource_type($value)) { |
|
| 41 | - return 'unknown'; |
|
| 42 | - } |
|
| 28 | + public static function get_debug_type($value): string |
|
| 29 | + { |
|
| 30 | + switch (true) { |
|
| 31 | + case null === $value: return 'null'; |
|
| 32 | + case \is_bool($value): return 'bool'; |
|
| 33 | + case \is_string($value): return 'string'; |
|
| 34 | + case \is_array($value): return 'array'; |
|
| 35 | + case \is_int($value): return 'int'; |
|
| 36 | + case \is_float($value): return 'float'; |
|
| 37 | + case \is_object($value): break; |
|
| 38 | + case $value instanceof \__PHP_Incomplete_Class: return '__PHP_Incomplete_Class'; |
|
| 39 | + default: |
|
| 40 | + if (null === $type = @get_resource_type($value)) { |
|
| 41 | + return 'unknown'; |
|
| 42 | + } |
|
| 43 | 43 | |
| 44 | - if ('Unknown' === $type) { |
|
| 45 | - $type = 'closed'; |
|
| 46 | - } |
|
| 44 | + if ('Unknown' === $type) { |
|
| 45 | + $type = 'closed'; |
|
| 46 | + } |
|
| 47 | 47 | |
| 48 | - return "resource ($type)"; |
|
| 49 | - } |
|
| 48 | + return "resource ($type)"; |
|
| 49 | + } |
|
| 50 | 50 | |
| 51 | - $class = \get_class($value); |
|
| 51 | + $class = \get_class($value); |
|
| 52 | 52 | |
| 53 | - if (false === strpos($class, '@')) { |
|
| 54 | - return $class; |
|
| 55 | - } |
|
| 53 | + if (false === strpos($class, '@')) { |
|
| 54 | + return $class; |
|
| 55 | + } |
|
| 56 | 56 | |
| 57 | - return (get_parent_class($class) ?: key(class_implements($class)) ?: 'class').'@anonymous'; |
|
| 58 | - } |
|
| 57 | + return (get_parent_class($class) ?: key(class_implements($class)) ?: 'class').'@anonymous'; |
|
| 58 | + } |
|
| 59 | 59 | |
| 60 | - public static function get_resource_id($res): int |
|
| 61 | - { |
|
| 62 | - if (!\is_resource($res) && null === @get_resource_type($res)) { |
|
| 63 | - throw new \TypeError(sprintf('Argument 1 passed to get_resource_id() must be of the type resource, %s given', get_debug_type($res))); |
|
| 64 | - } |
|
| 60 | + public static function get_resource_id($res): int |
|
| 61 | + { |
|
| 62 | + if (!\is_resource($res) && null === @get_resource_type($res)) { |
|
| 63 | + throw new \TypeError(sprintf('Argument 1 passed to get_resource_id() must be of the type resource, %s given', get_debug_type($res))); |
|
| 64 | + } |
|
| 65 | 65 | |
| 66 | - return (int) $res; |
|
| 67 | - } |
|
| 66 | + return (int) $res; |
|
| 67 | + } |
|
| 68 | 68 | |
| 69 | - public static function preg_last_error_msg(): string |
|
| 70 | - { |
|
| 71 | - switch (preg_last_error()) { |
|
| 72 | - case \PREG_INTERNAL_ERROR: |
|
| 73 | - return 'Internal error'; |
|
| 74 | - case \PREG_BAD_UTF8_ERROR: |
|
| 75 | - return 'Malformed UTF-8 characters, possibly incorrectly encoded'; |
|
| 76 | - case \PREG_BAD_UTF8_OFFSET_ERROR: |
|
| 77 | - return 'The offset did not correspond to the beginning of a valid UTF-8 code point'; |
|
| 78 | - case \PREG_BACKTRACK_LIMIT_ERROR: |
|
| 79 | - return 'Backtrack limit exhausted'; |
|
| 80 | - case \PREG_RECURSION_LIMIT_ERROR: |
|
| 81 | - return 'Recursion limit exhausted'; |
|
| 82 | - case \PREG_JIT_STACKLIMIT_ERROR: |
|
| 83 | - return 'JIT stack limit exhausted'; |
|
| 84 | - case \PREG_NO_ERROR: |
|
| 85 | - return 'No error'; |
|
| 86 | - default: |
|
| 87 | - return 'Unknown error'; |
|
| 88 | - } |
|
| 89 | - } |
|
| 69 | + public static function preg_last_error_msg(): string |
|
| 70 | + { |
|
| 71 | + switch (preg_last_error()) { |
|
| 72 | + case \PREG_INTERNAL_ERROR: |
|
| 73 | + return 'Internal error'; |
|
| 74 | + case \PREG_BAD_UTF8_ERROR: |
|
| 75 | + return 'Malformed UTF-8 characters, possibly incorrectly encoded'; |
|
| 76 | + case \PREG_BAD_UTF8_OFFSET_ERROR: |
|
| 77 | + return 'The offset did not correspond to the beginning of a valid UTF-8 code point'; |
|
| 78 | + case \PREG_BACKTRACK_LIMIT_ERROR: |
|
| 79 | + return 'Backtrack limit exhausted'; |
|
| 80 | + case \PREG_RECURSION_LIMIT_ERROR: |
|
| 81 | + return 'Recursion limit exhausted'; |
|
| 82 | + case \PREG_JIT_STACKLIMIT_ERROR: |
|
| 83 | + return 'JIT stack limit exhausted'; |
|
| 84 | + case \PREG_NO_ERROR: |
|
| 85 | + return 'No error'; |
|
| 86 | + default: |
|
| 87 | + return 'Unknown error'; |
|
| 88 | + } |
|
| 89 | + } |
|
| 90 | 90 | |
| 91 | - public static function str_contains(string $haystack, string $needle): bool |
|
| 92 | - { |
|
| 93 | - return '' === $needle || false !== strpos($haystack, $needle); |
|
| 94 | - } |
|
| 91 | + public static function str_contains(string $haystack, string $needle): bool |
|
| 92 | + { |
|
| 93 | + return '' === $needle || false !== strpos($haystack, $needle); |
|
| 94 | + } |
|
| 95 | 95 | |
| 96 | - public static function str_starts_with(string $haystack, string $needle): bool |
|
| 97 | - { |
|
| 98 | - return 0 === strncmp($haystack, $needle, \strlen($needle)); |
|
| 99 | - } |
|
| 96 | + public static function str_starts_with(string $haystack, string $needle): bool |
|
| 97 | + { |
|
| 98 | + return 0 === strncmp($haystack, $needle, \strlen($needle)); |
|
| 99 | + } |
|
| 100 | 100 | |
| 101 | - public static function str_ends_with(string $haystack, string $needle): bool |
|
| 102 | - { |
|
| 103 | - return '' === $needle || ('' !== $haystack && 0 === substr_compare($haystack, $needle, -\strlen($needle))); |
|
| 104 | - } |
|
| 101 | + public static function str_ends_with(string $haystack, string $needle): bool |
|
| 102 | + { |
|
| 103 | + return '' === $needle || ('' !== $haystack && 0 === substr_compare($haystack, $needle, -\strlen($needle))); |
|
| 104 | + } |
|
| 105 | 105 | } |