Windows.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: yunwuxin <448901948@qq.com>
  10. // +----------------------------------------------------------------------
  11. namespace think\process\pipes;
  12. use think\Process;
  13. class Windows extends Pipes
  14. {
  15. /** @var array */
  16. private $files = [];
  17. /** @var array */
  18. private $fileHandles = [];
  19. /** @var array */
  20. private $readBytes = [
  21. Process::STDOUT => 0,
  22. Process::STDERR => 0,
  23. ];
  24. /** @var bool */
  25. private $disableOutput;
  26. public function __construct($disableOutput, $input)
  27. {
  28. $this->disableOutput = (bool) $disableOutput;
  29. if (!$this->disableOutput) {
  30. $this->files = [
  31. Process::STDOUT => tempnam(sys_get_temp_dir(), 'sf_proc_stdout'),
  32. Process::STDERR => tempnam(sys_get_temp_dir(), 'sf_proc_stderr'),
  33. ];
  34. foreach ($this->files as $offset => $file) {
  35. $this->fileHandles[$offset] = fopen($this->files[$offset], 'rb');
  36. if (false === $this->fileHandles[$offset]) {
  37. throw new \RuntimeException('A temporary file could not be opened to write the process output to, verify that your TEMP environment variable is writable');
  38. }
  39. }
  40. }
  41. if (is_resource($input)) {
  42. $this->input = $input;
  43. } else {
  44. $this->inputBuffer = $input;
  45. }
  46. }
  47. public function __destruct()
  48. {
  49. $this->close();
  50. $this->removeFiles();
  51. }
  52. /**
  53. * {@inheritdoc}
  54. */
  55. public function getDescriptors()
  56. {
  57. if ($this->disableOutput) {
  58. $nullstream = fopen('NUL', 'c');
  59. return [
  60. ['pipe', 'r'],
  61. $nullstream,
  62. $nullstream,
  63. ];
  64. }
  65. return [
  66. ['pipe', 'r'],
  67. ['file', 'NUL', 'w'],
  68. ['file', 'NUL', 'w'],
  69. ];
  70. }
  71. /**
  72. * {@inheritdoc}
  73. */
  74. public function getFiles()
  75. {
  76. return $this->files;
  77. }
  78. /**
  79. * {@inheritdoc}
  80. */
  81. public function readAndWrite($blocking, $close = false)
  82. {
  83. $this->write($blocking, $close);
  84. $read = [];
  85. $fh = $this->fileHandles;
  86. foreach ($fh as $type => $fileHandle) {
  87. if (0 !== fseek($fileHandle, $this->readBytes[$type])) {
  88. continue;
  89. }
  90. $data = '';
  91. $dataread = null;
  92. while (!feof($fileHandle)) {
  93. if (false !== $dataread = fread($fileHandle, self::CHUNK_SIZE)) {
  94. $data .= $dataread;
  95. }
  96. }
  97. if (0 < $length = strlen($data)) {
  98. $this->readBytes[$type] += $length;
  99. $read[$type] = $data;
  100. }
  101. if (false === $dataread || (true === $close && feof($fileHandle) && '' === $data)) {
  102. fclose($this->fileHandles[$type]);
  103. unset($this->fileHandles[$type]);
  104. }
  105. }
  106. return $read;
  107. }
  108. /**
  109. * {@inheritdoc}
  110. */
  111. public function areOpen()
  112. {
  113. return (bool) $this->pipes && (bool) $this->fileHandles;
  114. }
  115. /**
  116. * {@inheritdoc}
  117. */
  118. public function close()
  119. {
  120. parent::close();
  121. foreach ($this->fileHandles as $handle) {
  122. fclose($handle);
  123. }
  124. $this->fileHandles = [];
  125. }
  126. /**
  127. * 创建一个新的 WindowsPipes 实例。
  128. * @param Process $process
  129. * @param $input
  130. * @return self
  131. */
  132. public static function create(Process $process, $input)
  133. {
  134. return new static($process->isOutputDisabled(), $input);
  135. }
  136. /**
  137. * 删除临时文件
  138. */
  139. private function removeFiles()
  140. {
  141. foreach ($this->files as $filename) {
  142. if (file_exists($filename)) {
  143. @unlink($filename);
  144. }
  145. }
  146. $this->files = [];
  147. }
  148. /**
  149. * 写入到 stdin 输入
  150. * @param bool $blocking
  151. * @param bool $close
  152. */
  153. private function write($blocking, $close)
  154. {
  155. if (empty($this->pipes)) {
  156. return;
  157. }
  158. $this->unblock();
  159. $r = null !== $this->input ? ['input' => $this->input] : null;
  160. $w = isset($this->pipes[0]) ? [$this->pipes[0]] : null;
  161. $e = null;
  162. if (false === $n = @stream_select($r, $w, $e, 0, $blocking ? Process::TIMEOUT_PRECISION * 1E6 : 0)) {
  163. if (!$this->hasSystemCallBeenInterrupted()) {
  164. $this->pipes = [];
  165. }
  166. return;
  167. }
  168. if (0 === $n) {
  169. return;
  170. }
  171. if (null !== $r && 0 < count($r)) {
  172. $data = '';
  173. while ($dataread = fread($r['input'], self::CHUNK_SIZE)) {
  174. $data .= $dataread;
  175. }
  176. $this->inputBuffer .= $data;
  177. if (false === $data || (true === $close && feof($r['input']) && '' === $data)) {
  178. $this->input = null;
  179. }
  180. }
  181. if (null !== $w && 0 < count($w)) {
  182. while (strlen($this->inputBuffer)) {
  183. $written = fwrite($w[0], $this->inputBuffer, 2 << 18);
  184. if ($written > 0) {
  185. $this->inputBuffer = (string) substr($this->inputBuffer, $written);
  186. } else {
  187. break;
  188. }
  189. }
  190. }
  191. if ('' === $this->inputBuffer && null === $this->input && isset($this->pipes[0])) {
  192. fclose($this->pipes[0]);
  193. unset($this->pipes[0]);
  194. }
  195. }
  196. }