Passed
Push — 5.1 ( 2420e5...326bc3 )
by liu
09:30 queued 01:11
created

Process::addOutput()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
// +----------------------------------------------------------------------
1 ignored issue
show
Coding Style introduced by
You must use "/**" style comments for a file comment
Loading history...
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 <[email protected]>
10
// +----------------------------------------------------------------------
11
12
namespace think;
13
14
use think\process\exception\Failed as ProcessFailedException;
15
use think\process\exception\Timeout as ProcessTimeoutException;
16
use think\process\pipes\Pipes;
17
use think\process\pipes\Unix as UnixPipes;
18
use think\process\pipes\Windows as WindowsPipes;
19
use think\process\Utils;
20
21
class Process
1 ignored issue
show
Coding Style introduced by
Missing class doc comment
Loading history...
22
{
23
24
    const ERR = 'err';
25
    const OUT = 'out';
26
27
    const STATUS_READY      = 'ready';
28
    const STATUS_STARTED    = 'started';
29
    const STATUS_TERMINATED = 'terminated';
30
31
    const STDIN  = 0;
32
    const STDOUT = 1;
33
    const STDERR = 2;
34
35
    const TIMEOUT_PRECISION = 0.2;
36
37
    private $callback;
0 ignored issues
show
Coding Style introduced by
Private member variable "callback" must be prefixed with an underscore
Loading history...
38
    private $commandline;
0 ignored issues
show
Coding Style introduced by
Private member variable "commandline" must be prefixed with an underscore
Loading history...
39
    private $cwd;
0 ignored issues
show
Coding Style introduced by
Private member variable "cwd" must be prefixed with an underscore
Loading history...
40
    private $env;
0 ignored issues
show
Coding Style introduced by
Private member variable "env" must be prefixed with an underscore
Loading history...
41
    private $input;
0 ignored issues
show
Coding Style introduced by
Private member variable "input" must be prefixed with an underscore
Loading history...
42
    private $starttime;
0 ignored issues
show
Coding Style introduced by
Private member variable "starttime" must be prefixed with an underscore
Loading history...
43
    private $lastOutputTime;
0 ignored issues
show
Coding Style introduced by
Private member variable "lastOutputTime" must be prefixed with an underscore
Loading history...
44
    private $timeout;
0 ignored issues
show
Coding Style introduced by
Private member variable "timeout" must be prefixed with an underscore
Loading history...
45
    private $idleTimeout;
0 ignored issues
show
Coding Style introduced by
Private member variable "idleTimeout" must be prefixed with an underscore
Loading history...
46
    private $options;
0 ignored issues
show
Coding Style introduced by
Private member variable "options" must be prefixed with an underscore
Loading history...
47
    private $exitcode;
0 ignored issues
show
Coding Style introduced by
Private member variable "exitcode" must be prefixed with an underscore
Loading history...
48
    private $fallbackExitcode;
0 ignored issues
show
Coding Style introduced by
Private member variable "fallbackExitcode" must be prefixed with an underscore
Loading history...
49
    private $processInformation;
0 ignored issues
show
Coding Style introduced by
Private member variable "processInformation" must be prefixed with an underscore
Loading history...
50
    private $outputDisabled = false;
0 ignored issues
show
Coding Style introduced by
Private member variable "outputDisabled" must be prefixed with an underscore
Loading history...
51
    private $stdout;
0 ignored issues
show
Coding Style introduced by
Private member variable "stdout" must be prefixed with an underscore
Loading history...
52
    private $stderr;
0 ignored issues
show
Coding Style introduced by
Private member variable "stderr" must be prefixed with an underscore
Loading history...
53
    private $enhanceWindowsCompatibility = true;
0 ignored issues
show
Coding Style introduced by
Private member variable "enhanceWindowsCompatibility" must be prefixed with an underscore
Loading history...
54
    private $enhanceSigchildCompatibility;
0 ignored issues
show
Coding Style introduced by
Private member variable "enhanceSigchildCompatibility" must be prefixed with an underscore
Loading history...
55
    private $process;
0 ignored issues
show
Coding Style introduced by
Private member variable "process" must be prefixed with an underscore
Loading history...
56
    private $status                       = self::STATUS_READY;
0 ignored issues
show
Coding Style introduced by
Private member variable "status" must be prefixed with an underscore
Loading history...
57
    private $incrementalOutputOffset      = 0;
0 ignored issues
show
Coding Style introduced by
Private member variable "incrementalOutputOffset" must be prefixed with an underscore
Loading history...
58
    private $incrementalErrorOutputOffset = 0;
0 ignored issues
show
Coding Style introduced by
Private member variable "incrementalErrorOutputOffset" must be prefixed with an underscore
Loading history...
59
    private $tty;
0 ignored issues
show
Coding Style introduced by
Private member variable "tty" must be prefixed with an underscore
Loading history...
60
    private $pty;
0 ignored issues
show
Coding Style introduced by
Private member variable "pty" must be prefixed with an underscore
Loading history...
61
62
    private $useFileHandles = false;
0 ignored issues
show
Coding Style introduced by
Private member variable "useFileHandles" must be prefixed with an underscore
Loading history...
63
64
    /** @var Pipes */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
65
    private $processPipes;
0 ignored issues
show
Coding Style introduced by
Private member variable "processPipes" must be prefixed with an underscore
Loading history...
66
67
    private $latestSignal;
0 ignored issues
show
Coding Style introduced by
Private member variable "latestSignal" must be prefixed with an underscore
Loading history...
68
69
    private static $sigchild;
0 ignored issues
show
Coding Style introduced by
Private member variable "sigchild" must be prefixed with an underscore
Loading history...
70
71
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
72
     * @var array
73
     */
74
    public static $exitCodes = [
75
        0   => 'OK',
76
        1   => 'General error',
77
        2   => 'Misuse of shell builtins',
78
        126 => 'Invoked command cannot execute',
79
        127 => 'Command not found',
80
        128 => 'Invalid exit argument',
81
        // signals
82
        129 => 'Hangup',
83
        130 => 'Interrupt',
84
        131 => 'Quit and dump core',
85
        132 => 'Illegal instruction',
86
        133 => 'Trace/breakpoint trap',
87
        134 => 'Process aborted',
88
        135 => 'Bus error: "access to undefined portion of memory object"',
89
        136 => 'Floating point exception: "erroneous arithmetic operation"',
90
        137 => 'Kill (terminate immediately)',
91
        138 => 'User-defined 1',
92
        139 => 'Segmentation violation',
93
        140 => 'User-defined 2',
94
        141 => 'Write to pipe with no one reading',
95
        142 => 'Signal raised by alarm',
96
        143 => 'Termination (request to terminate)',
97
        // 144 - not defined
98
        145 => 'Child process terminated, stopped (or continued*)',
99
        146 => 'Continue if stopped',
100
        147 => 'Stop executing temporarily',
101
        148 => 'Terminal stop signal',
102
        149 => 'Background process attempting to read from tty ("in")',
103
        150 => 'Background process attempting to write to tty ("out")',
104
        151 => 'Urgent data available on socket',
105
        152 => 'CPU time limit exceeded',
106
        153 => 'File size limit exceeded',
107
        154 => 'Signal raised by timer counting virtual time: "virtual timer expired"',
108
        155 => 'Profiling timer expired',
109
        // 156 - not defined
110
        157 => 'Pollable event',
111
        // 158 - not defined
112
        159 => 'Bad syscall',
113
    ];
114
115
    /**
116
     * 构造方法
117
     * @access public
118
     * @param  string         $commandline 指令
119
     * @param  string|null    $cwd         工作目录
120
     * @param  array|null     $env         环境变量
121
     * @param  string|null    $input       输入
122
     * @param  int|float|null $timeout     超时时间
123
     * @param  array          $options     proc_open的选项
124
     * @throws \RuntimeException
125
     * @api
126
     */
127
    public function __construct($commandline, $cwd = null, array $env = null, $input = null, $timeout = 60, array $options = [])
128
    {
129
        if (!function_exists('proc_open')) {
130
            throw new \RuntimeException('The Process class relies on proc_open, which is not available on your PHP installation.');
131
        }
132
133
        $this->commandline = $commandline;
134
        $this->cwd         = $cwd;
135
136
        if (null === $this->cwd && (defined('ZEND_THREAD_SAFE') || '\\' === DIRECTORY_SEPARATOR)) {
137
            $this->cwd = getcwd();
138
        }
139
        if (null !== $env) {
140
            $this->setEnv($env);
141
        }
142
143
        $this->input = $input;
144
        $this->setTimeout($timeout);
145
        $this->useFileHandles               = '\\' === DIRECTORY_SEPARATOR;
146
        $this->pty                          = false;
147
        $this->enhanceWindowsCompatibility  = true;
148
        $this->enhanceSigchildCompatibility = '\\' !== DIRECTORY_SEPARATOR && $this->isSigchildEnabled();
149
        $this->options                      = array_replace([
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
150
            'suppress_errors' => true,
151
            'binary_pipes'    => true,
152
        ], $options);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
153
    }
154
155
    public function __destruct()
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
156
    {
157
        $this->stop();
158
    }
159
160
    public function __clone()
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
161
    {
162
        $this->resetProcessData();
163
    }
164
165
    /**
166
     * 运行指令
167
     * @access public
168
     * @param  callback|null $callback
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
169
     * @return int
170
     */
171
    public function run($callback = null)
172
    {
173
        $this->start($callback);
174
175
        return $this->wait();
176
    }
177
178
    /**
179
     * 运行指令
180
     * @access public
181
     * @param  callable|null $callback
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
182
     * @return self
183
     * @throws \RuntimeException
184
     * @throws ProcessFailedException
185
     */
186
    public function mustRun($callback = null)
187
    {
188
        if ($this->isSigchildEnabled() && !$this->enhanceSigchildCompatibility) {
189
            throw new \RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.');
190
        }
191
192
        if (0 !== $this->run($callback)) {
193
            throw new ProcessFailedException($this);
194
        }
195
196
        return $this;
197
    }
198
199
    /**
200
     * 启动进程并写到 STDIN 输入后返回。
201
     * @access public
202
     * @param  callable|null $callback
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
203
     * @throws \RuntimeException
204
     * @throws \RuntimeException
205
     * @throws \LogicException
206
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
207
    public function start($callback = null)
208
    {
209
        if ($this->isRunning()) {
210
            throw new \RuntimeException('Process is already running');
211
        }
212
        if ($this->outputDisabled && null !== $callback) {
213
            throw new \LogicException('Output has been disabled, enable it to allow the use of a callback.');
214
        }
215
216
        $this->resetProcessData();
217
        $this->starttime = $this->lastOutputTime = microtime(true);
218
        $this->callback  = $this->buildCallback($callback);
219
        $descriptors     = $this->getDescriptors();
220
221
        $commandline = $this->commandline;
222
223
        if ('\\' === DIRECTORY_SEPARATOR && $this->enhanceWindowsCompatibility) {
224
            $commandline = 'cmd /V:ON /E:ON /C "(' . $commandline . ')';
225
            foreach ($this->processPipes->getFiles() as $offset => $filename) {
226
                $commandline .= ' ' . $offset . '>' . Utils::escapeArgument($filename);
227
            }
228
            $commandline .= '"';
229
230
            if (!isset($this->options['bypass_shell'])) {
231
                $this->options['bypass_shell'] = true;
232
            }
233
        }
234
235
        $this->process = proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $this->env, $this->options);
236
237
        if (!is_resource($this->process)) {
238
            throw new \RuntimeException('Unable to launch a new process.');
239
        }
240
        $this->status = self::STATUS_STARTED;
241
242
        if ($this->tty) {
243
            return;
244
        }
245
246
        $this->updateStatus(false);
247
        $this->checkTimeout();
248
    }
249
250
    /**
251
     * 重启进程
252
     * @access public
253
     * @param  callable|null $callback
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
254
     * @return Process
255
     * @throws \RuntimeException
256
     * @throws \RuntimeException
257
     */
258
    public function restart($callback = null)
259
    {
260
        if ($this->isRunning()) {
261
            throw new \RuntimeException('Process is already running');
262
        }
263
264
        $process = clone $this;
265
        $process->start($callback);
266
267
        return $process;
268
    }
269
270
    /**
271
     * 等待要终止的进程
272
     * @access public
273
     * @param  callable|null $callback
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
274
     * @return int
275
     */
276
    public function wait($callback = null)
277
    {
278
        $this->requireProcessIsStarted(__FUNCTION__);
279
280
        $this->updateStatus(false);
281
        if (null !== $callback) {
282
            $this->callback = $this->buildCallback($callback);
283
        }
284
285
        do {
286
            $this->checkTimeout();
287
            $running = '\\' === DIRECTORY_SEPARATOR ? $this->isRunning() : $this->processPipes->areOpen();
288
            $close   = '\\' !== DIRECTORY_SEPARATOR || !$running;
289
            $this->readPipes(true, $close);
290
        } while ($running);
291
292
        while ($this->isRunning()) {
293
            usleep(1000);
294
        }
295
296
        if ($this->processInformation['signaled'] && $this->processInformation['termsig'] !== $this->latestSignal) {
297
            throw new \RuntimeException(sprintf('The process has been signaled with signal "%s".', $this->processInformation['termsig']));
298
        }
299
300
        return $this->exitcode;
301
    }
302
303
    /**
304
     * 获取PID
305
     * @access public
306
     * @return int|null
307
     * @throws \RuntimeException
308
     */
309
    public function getPid()
310
    {
311
        if ($this->isSigchildEnabled()) {
312
            throw new \RuntimeException('This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved.');
313
        }
314
315
        $this->updateStatus(false);
316
317
        return $this->isRunning() ? $this->processInformation['pid'] : null;
318
    }
319
320
    /**
321
     * 将一个 POSIX 信号发送到进程中
322
     * @access public
323
     * @param  int $signal
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
324
     * @return Process
325
     */
326
    public function signal($signal)
327
    {
328
        $this->doSignal($signal, true);
329
330
        return $this;
331
    }
332
333
    /**
334
     * 禁用从底层过程获取输出和错误输出。
335
     * @access public
336
     * @return Process
337
     */
338
    public function disableOutput()
339
    {
340
        if ($this->isRunning()) {
341
            throw new \RuntimeException('Disabling output while the process is running is not possible.');
342
        }
343
        if (null !== $this->idleTimeout) {
344
            throw new \LogicException('Output can not be disabled while an idle timeout is set.');
345
        }
346
347
        $this->outputDisabled = true;
348
349
        return $this;
350
    }
351
352
    /**
353
     * 开启从底层过程获取输出和错误输出。
354
     * @access public
355
     * @return Process
356
     * @throws \RuntimeException
357
     */
358
    public function enableOutput()
359
    {
360
        if ($this->isRunning()) {
361
            throw new \RuntimeException('Enabling output while the process is running is not possible.');
362
        }
363
364
        $this->outputDisabled = false;
365
366
        return $this;
367
    }
368
369
    /**
370
     * 输出是否禁用
371
     * @access public
372
     * @return bool
373
     */
374
    public function isOutputDisabled()
375
    {
376
        return $this->outputDisabled;
377
    }
378
379
    /**
380
     * 获取当前的输出管道
381
     * @access public
382
     * @return string
383
     * @throws \LogicException
384
     * @api
385
     */
386
    public function getOutput()
387
    {
388
        if ($this->outputDisabled) {
389
            throw new \LogicException('Output has been disabled.');
390
        }
391
392
        $this->requireProcessIsStarted(__FUNCTION__);
393
394
        $this->readPipes(false, '\\' === DIRECTORY_SEPARATOR ? !$this->processInformation['running'] : true);
395
396
        return $this->stdout;
397
    }
398
399
    /**
400
     * 以增量方式返回的输出结果。
401
     * @access public
402
     * @return string
403
     */
404
    public function getIncrementalOutput()
405
    {
406
        $this->requireProcessIsStarted(__FUNCTION__);
407
408
        $data = $this->getOutput();
409
410
        $latest = substr($data, $this->incrementalOutputOffset);
411
412
        if (false === $latest) {
413
            return '';
414
        }
415
416
        $this->incrementalOutputOffset = strlen($data);
417
418
        return $latest;
419
    }
420
421
    /**
422
     * 清空输出
423
     * @access public
424
     * @return Process
425
     */
426
    public function clearOutput()
427
    {
428
        $this->stdout                  = '';
429
        $this->incrementalOutputOffset = 0;
430
431
        return $this;
432
    }
433
434
    /**
435
     * 返回当前的错误输出的过程 (STDERR)。
436
     * @access public
437
     * @return string
438
     */
439
    public function getErrorOutput()
440
    {
441
        if ($this->outputDisabled) {
442
            throw new \LogicException('Output has been disabled.');
443
        }
444
445
        $this->requireProcessIsStarted(__FUNCTION__);
446
447
        $this->readPipes(false, '\\' === DIRECTORY_SEPARATOR ? !$this->processInformation['running'] : true);
448
449
        return $this->stderr;
450
    }
451
452
    /**
453
     * 以增量方式返回 errorOutput
454
     * @access public
455
     * @return string
456
     */
457
    public function getIncrementalErrorOutput()
458
    {
459
        $this->requireProcessIsStarted(__FUNCTION__);
460
461
        $data = $this->getErrorOutput();
462
463
        $latest = substr($data, $this->incrementalErrorOutputOffset);
464
465
        if (false === $latest) {
466
            return '';
467
        }
468
469
        $this->incrementalErrorOutputOffset = strlen($data);
470
471
        return $latest;
472
    }
473
474
    /**
475
     * 清空 errorOutput
476
     * @access public
477
     * @return Process
478
     */
479
    public function clearErrorOutput()
480
    {
481
        $this->stderr                       = '';
482
        $this->incrementalErrorOutputOffset = 0;
483
484
        return $this;
485
    }
486
487
    /**
488
     * 获取退出码
489
     * @access public
490
     * @return null|int
491
     */
492
    public function getExitCode()
493
    {
494
        if ($this->isSigchildEnabled() && !$this->enhanceSigchildCompatibility) {
495
            throw new \RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.');
496
        }
497
498
        $this->updateStatus(false);
499
500
        return $this->exitcode;
501
    }
502
503
    /**
504
     * 获取退出文本
505
     * @access public
506
     * @return null|string
507
     */
508
    public function getExitCodeText()
509
    {
510
        if (null === $exitcode = $this->getExitCode()) {
511
            return;
512
        }
513
514
        return isset(self::$exitCodes[$exitcode]) ? self::$exitCodes[$exitcode] : 'Unknown error';
515
    }
516
517
    /**
518
     * 检查是否成功
519
     * @access public
520
     * @return bool
521
     */
522
    public function isSuccessful()
523
    {
524
        return 0 === $this->getExitCode();
525
    }
526
527
    /**
528
     * 是否未捕获的信号已被终止子进程
529
     * @access public
530
     * @return bool
531
     */
532
    public function hasBeenSignaled()
533
    {
534
        $this->requireProcessIsTerminated(__FUNCTION__);
535
536
        if ($this->isSigchildEnabled()) {
537
            throw new \RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.');
538
        }
539
540
        $this->updateStatus(false);
541
542
        return $this->processInformation['signaled'];
543
    }
544
545
    /**
546
     * 返回导致子进程终止其执行的数。
547
     * @access public
548
     * @return int
549
     */
550
    public function getTermSignal()
551
    {
552
        $this->requireProcessIsTerminated(__FUNCTION__);
553
554
        if ($this->isSigchildEnabled()) {
555
            throw new \RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.');
556
        }
557
558
        $this->updateStatus(false);
559
560
        return $this->processInformation['termsig'];
561
    }
562
563
    /**
564
     * 检查子进程信号是否已停止
565
     * @access public
566
     * @return bool
567
     */
568
    public function hasBeenStopped()
569
    {
570
        $this->requireProcessIsTerminated(__FUNCTION__);
571
572
        $this->updateStatus(false);
573
574
        return $this->processInformation['stopped'];
575
    }
576
577
    /**
578
     * 返回导致子进程停止其执行的数。
579
     * @access public
580
     * @return int
581
     */
582
    public function getStopSignal()
583
    {
584
        $this->requireProcessIsTerminated(__FUNCTION__);
585
586
        $this->updateStatus(false);
587
588
        return $this->processInformation['stopsig'];
589
    }
590
591
    /**
592
     * 检查是否正在运行
593
     * @access public
594
     * @return bool
595
     */
596
    public function isRunning()
597
    {
598
        if (self::STATUS_STARTED !== $this->status) {
599
            return false;
600
        }
601
602
        $this->updateStatus(false);
603
604
        return $this->processInformation['running'];
605
    }
606
607
    /**
608
     * 检查是否已开始
609
     * @access public
610
     * @return bool
611
     */
612
    public function isStarted()
613
    {
614
        return self::STATUS_READY != $this->status;
615
    }
616
617
    /**
618
     * 检查是否已终止
619
     * @access public
620
     * @return bool
621
     */
622
    public function isTerminated()
623
    {
624
        $this->updateStatus(false);
625
626
        return self::STATUS_TERMINATED == $this->status;
627
    }
628
629
    /**
630
     * 获取当前的状态
631
     * @access public
632
     * @return string
633
     */
634
    public function getStatus()
635
    {
636
        $this->updateStatus(false);
637
638
        return $this->status;
639
    }
640
641
    /**
642
     * 终止进程
643
     * @access public
644
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
645
    public function stop()
646
    {
647
        if ($this->isRunning()) {
648
            if ('\\' === DIRECTORY_SEPARATOR && !$this->isSigchildEnabled()) {
649
                exec(sprintf('taskkill /F /T /PID %d 2>&1', $this->getPid()), $output, $exitCode);
650
                if ($exitCode > 0) {
651
                    throw new \RuntimeException('Unable to kill the process');
652
                }
653
            } else {
654
                $pids = preg_split('/\s+/', `ps -o pid --no-heading --ppid {$this->getPid()}`);
655
                foreach ($pids as $pid) {
656
                    if (is_numeric($pid)) {
657
                        posix_kill($pid, 9);
658
                    }
659
                }
660
            }
661
        }
662
663
        $this->updateStatus(false);
664
        if ($this->processInformation['running']) {
665
            $this->close();
666
        }
667
668
        return $this->exitcode;
669
    }
670
671
    /**
672
     * 添加一行输出
673
     * @access public
674
     * @param string $line
1 ignored issue
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
675
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
676
    public function addOutput($line)
677
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
678
        $this->lastOutputTime = microtime(true);
679
        $this->stdout .= $line;
680
    }
681
682
    /**
683
     * 添加一行错误输出
684
     * @access public
685
     * @param string $line
1 ignored issue
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
686
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
687
    public function addErrorOutput($line)
688
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
689
        $this->lastOutputTime = microtime(true);
690
        $this->stderr .= $line;
691
    }
692
693
    /**
694
     * 获取被执行的指令
695
     * @access public
696
     * @return string
697
     */
698
    public function getCommandLine()
699
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
700
        return $this->commandline;
701
    }
702
703
    /**
704
     * 设置指令
705
     * @access public
706
     * @param string $commandline
1 ignored issue
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
707
     * @return self
708
     */
709
    public function setCommandLine($commandline)
710
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
711
        $this->commandline = $commandline;
712
713
        return $this;
714
    }
715
716
    /**
717
     * 获取超时时间
718
     * @access public
719
     * @return float|null
720
     */
721
    public function getTimeout()
722
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
723
        return $this->timeout;
724
    }
725
726
    /**
727
     * 获取idle超时时间
728
     * @access public
729
     * @return float|null
730
     */
731
    public function getIdleTimeout()
732
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
733
        return $this->idleTimeout;
734
    }
735
736
    /**
737
     * 设置超时时间
738
     * @access public
739
     * @param  int|float|null $timeout
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
740
     * @return self
741
     */
742
    public function setTimeout($timeout)
743
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
744
        $this->timeout = $this->validateTimeout($timeout);
745
746
        return $this;
747
    }
748
749
    /**
750
     * 设置idle超时时间
751
     * @access public
752
     * @param  int|float|null $timeout
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
753
     * @return self
754
     */
755
    public function setIdleTimeout($timeout)
756
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
757
        if (null !== $timeout && $this->outputDisabled) {
758
            throw new \LogicException('Idle timeout can not be set while the output is disabled.');
759
        }
760
761
        $this->idleTimeout = $this->validateTimeout($timeout);
762
763
        return $this;
764
    }
765
766
    /**
767
     * 设置TTY
768
     * @access public
769
     * @param  bool $tty
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
770
     * @return self
771
     */
772
    public function setTty($tty)
773
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
774
        if ('\\' === DIRECTORY_SEPARATOR && $tty) {
775
            throw new \RuntimeException('TTY mode is not supported on Windows platform.');
776
        }
777
        if ($tty && (!file_exists('/dev/tty') || !is_readable('/dev/tty'))) {
778
            throw new \RuntimeException('TTY mode requires /dev/tty to be readable.');
779
        }
780
781
        $this->tty = (bool) $tty;
782
783
        return $this;
784
    }
785
786
    /**
787
     * 检查是否是tty模式
788
     * @access public
789
     * @return bool
790
     */
791
    public function isTty()
792
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
793
        return $this->tty;
794
    }
795
796
    /**
797
     * 设置pty模式
798
     * @access public
799
     * @param  bool $bool
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
800
     * @return self
801
     */
802
    public function setPty($bool)
803
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
804
        $this->pty = (bool) $bool;
805
806
        return $this;
807
    }
808
809
    /**
810
     * 是否是pty模式
811
     * @access public
812
     * @return bool
813
     */
814
    public function isPty()
815
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
816
        return $this->pty;
817
    }
818
819
    /**
820
     * 获取工作目录
821
     * @access public
822
     * @return string|null
823
     */
824
    public function getWorkingDirectory()
825
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
826
        if (null === $this->cwd) {
827
            return getcwd() ?: null;
828
        }
829
830
        return $this->cwd;
831
    }
832
833
    /**
834
     * 设置工作目录
835
     * @access public
836
     * @param  string $cwd
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
837
     * @return self
838
     */
839
    public function setWorkingDirectory($cwd)
840
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
841
        $this->cwd = $cwd;
842
843
        return $this;
844
    }
845
846
    /**
847
     * 获取环境变量
848
     * @access public
849
     * @return array
850
     */
851
    public function getEnv()
852
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
853
        return $this->env;
854
    }
855
856
    /**
857
     * 设置环境变量
858
     * @access public
859
     * @param  array $env
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
860
     * @return self
861
     */
862
    public function setEnv(array $env)
863
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
864
        $env = array_filter($env, function ($value) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
865
            return !is_array($value);
866
        });
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
867
868
        $this->env = [];
869
        foreach ($env as $key => $value) {
870
            $this->env[(binary) $key] = (binary) $value;
871
        }
872
873
        return $this;
874
    }
875
876
    /**
877
     * 获取输入
878
     * @access public
879
     * @return null|string
880
     */
881
    public function getInput()
882
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
883
        return $this->input;
884
    }
885
886
    /**
887
     * 设置输入
888
     * @access public
889
     * @param  mixed $input
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
890
     * @return self
891
     */
892
    public function setInput($input)
893
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
894
        if ($this->isRunning()) {
895
            throw new \LogicException('Input can not be set while the process is running.');
896
        }
897
898
        $this->input = Utils::validateInput(sprintf('%s::%s', __CLASS__, __FUNCTION__), $input);
899
900
        return $this;
901
    }
902
903
    /**
904
     * 获取proc_open的选项
905
     * @access public
906
     * @return array
907
     */
908
    public function getOptions()
909
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
910
        return $this->options;
911
    }
912
913
    /**
914
     * 设置proc_open的选项
915
     * @access public
916
     * @param  array $options
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
917
     * @return self
918
     */
919
    public function setOptions(array $options)
920
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
921
        $this->options = $options;
922
923
        return $this;
924
    }
925
926
    /**
927
     * 是否兼容windows
928
     * @access public
929
     * @return bool
930
     */
931
    public function getEnhanceWindowsCompatibility()
932
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
933
        return $this->enhanceWindowsCompatibility;
934
    }
935
936
    /**
937
     * 设置是否兼容windows
938
     * @access public
939
     * @param  bool $enhance
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
940
     * @return self
941
     */
942
    public function setEnhanceWindowsCompatibility($enhance)
943
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
944
        $this->enhanceWindowsCompatibility = (bool) $enhance;
945
946
        return $this;
947
    }
948
949
    /**
950
     * 返回是否 sigchild 兼容模式激活
951
     * @access public
952
     * @return bool
953
     */
954
    public function getEnhanceSigchildCompatibility()
955
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
956
        return $this->enhanceSigchildCompatibility;
957
    }
958
959
    /**
960
     * 激活 sigchild 兼容性模式。
961
     * @access public
962
     * @param  bool $enhance
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
963
     * @return self
964
     */
965
    public function setEnhanceSigchildCompatibility($enhance)
966
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
967
        $this->enhanceSigchildCompatibility = (bool) $enhance;
968
969
        return $this;
970
    }
971
972
    /**
973
     * 是否超时
974
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
975
    public function checkTimeout()
976
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
977
        if (self::STATUS_STARTED !== $this->status) {
978
            return;
979
        }
980
981
        if (null !== $this->timeout && $this->timeout < microtime(true) - $this->starttime) {
982
            $this->stop();
983
984
            throw new ProcessTimeoutException($this, ProcessTimeoutException::TYPE_GENERAL);
985
        }
986
987
        if (null !== $this->idleTimeout && $this->idleTimeout < microtime(true) - $this->lastOutputTime) {
988
            $this->stop();
989
990
            throw new ProcessTimeoutException($this, ProcessTimeoutException::TYPE_IDLE);
991
        }
992
    }
993
994
    /**
995
     * 是否支持pty
996
     * @access public
997
     * @return bool
998
     */
999
    public static function isPtySupported()
1000
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
1001
        static $result;
1002
1003
        if (null !== $result) {
1004
            return $result;
1005
        }
1006
1007
        if ('\\' === DIRECTORY_SEPARATOR) {
1008
            return $result = false;
1009
        }
1010
1011
        $proc = @proc_open('echo 1', [['pty'], ['pty'], ['pty']], $pipes);
1012
        if (is_resource($proc)) {
1013
            proc_close($proc);
1014
1015
            return $result = true;
1016
        }
1017
1018
        return $result = false;
1019
    }
1020
1021
    /**
1022
     * 创建所需的 proc_open 的描述符
1023
     * @access private
1024
     * @return array
1025
     */
1026
    private function getDescriptors()
0 ignored issues
show
Coding Style introduced by
Private method name "Process::getDescriptors" must be prefixed with an underscore
Loading history...
1027
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
1028
        if ('\\' === DIRECTORY_SEPARATOR) {
1029
            $this->processPipes = WindowsPipes::create($this, $this->input);
1030
        } else {
1031
            $this->processPipes = UnixPipes::create($this, $this->input);
1032
        }
1033
        $descriptors = $this->processPipes->getDescriptors($this->outputDisabled);
0 ignored issues
show
Unused Code introduced by
The call to think\process\pipes\Windows::getDescriptors() has too many arguments starting with $this->outputDisabled. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1033
        /** @scrutinizer ignore-call */ 
1034
        $descriptors = $this->processPipes->getDescriptors($this->outputDisabled);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Unused Code introduced by
The call to think\process\pipes\Unix::getDescriptors() has too many arguments starting with $this->outputDisabled. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1033
        /** @scrutinizer ignore-call */ 
1034
        $descriptors = $this->processPipes->getDescriptors($this->outputDisabled);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
1034
1035
        if (!$this->useFileHandles && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) {
1036
1037
            $descriptors = array_merge($descriptors, [['pipe', 'w']]);
1038
1039
            $this->commandline = '(' . $this->commandline . ') 3>/dev/null; code=$?; echo $code >&3; exit $code';
1040
        }
1041
1042
        return $descriptors;
1043
    }
1044
1045
    /**
1046
     * 建立 wait () 使用的回调。
1047
     * @access protected
1048
     * @param  callable|null $callback
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1049
     * @return callable
1050
     */
1051
    protected function buildCallback($callback)
1052
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
1053
        $out      = self::OUT;
1054
        $callback = function ($type, $data) use ($callback, $out) {
1055
            if ($out == $type) {
1056
                $this->addOutput($data);
1057
            } else {
1058
                $this->addErrorOutput($data);
1059
            }
1060
1061
            if (null !== $callback) {
1062
                call_user_func($callback, $type, $data);
1063
            }
1064
        };
1065
1066
        return $callback;
1067
    }
1068
1069
    /**
1070
     * 更新状态
1071
     * @access protected
1072
     * @param bool $blocking
1 ignored issue
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
1073
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
1074
    protected function updateStatus($blocking)
1075
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
1076
        if (self::STATUS_STARTED !== $this->status) {
1077
            return;
1078
        }
1079
1080
        $this->processInformation = proc_get_status($this->process);
1081
        $this->captureExitCode();
1082
1083
        $this->readPipes($blocking, '\\' === DIRECTORY_SEPARATOR ? !$this->processInformation['running'] : true);
1084
1085
        if (!$this->processInformation['running']) {
1086
            $this->close();
1087
        }
1088
    }
1089
1090
    /**
1091
     * 是否开启 '--enable-sigchild'
1092
     * @access protected
1093
     * @return bool
1094
     */
1095
    protected function isSigchildEnabled()
1096
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
1097
        if (null !== self::$sigchild) {
1098
            return self::$sigchild;
1099
        }
1100
1101
        if (!function_exists('phpinfo')) {
1102
            return self::$sigchild = false;
1103
        }
1104
1105
        ob_start();
1106
        phpinfo(INFO_GENERAL);
1107
1108
        return self::$sigchild = false !== strpos(ob_get_clean(), '--enable-sigchild');
1109
    }
1110
1111
    /**
1112
     * 验证是否超时
1113
     * @access private
1114
     * @param  int|float|null $timeout
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1115
     * @return float|null
1116
     */
1117
    private function validateTimeout($timeout)
0 ignored issues
show
Coding Style introduced by
Private method name "Process::validateTimeout" must be prefixed with an underscore
Loading history...
1118
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
1119
        $timeout = (float) $timeout;
1120
1121
        if (0.0 === $timeout) {
0 ignored issues
show
introduced by
The condition 0.0 === $timeout is always false.
Loading history...
1122
            $timeout = null;
1123
        } elseif ($timeout < 0) {
1124
            throw new \InvalidArgumentException('The timeout value must be a valid positive integer or float number.');
1125
        }
1126
1127
        return $timeout;
1128
    }
1129
1130
    /**
1131
     * 读取pipes
1132
     * @access private
1133
     * @param  bool $blocking
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1134
     * @param  bool $close
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1135
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
1136
    private function readPipes($blocking, $close)
0 ignored issues
show
Coding Style introduced by
Private method name "Process::readPipes" must be prefixed with an underscore
Loading history...
1137
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
1138
        $result = $this->processPipes->readAndWrite($blocking, $close);
1139
1140
        $callback = $this->callback;
1141
        foreach ($result as $type => $data) {
1142
            if (3 == $type) {
1143
                $this->fallbackExitcode = (int) $data;
1144
            } else {
1145
                $callback(self::STDOUT === $type ? self::OUT : self::ERR, $data);
1146
            }
1147
        }
1148
    }
1149
1150
    /**
1151
     * 捕获退出码
1152
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
1153
    private function captureExitCode()
0 ignored issues
show
Coding Style introduced by
Private method name "Process::captureExitCode" must be prefixed with an underscore
Loading history...
1154
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
1155
        if (isset($this->processInformation['exitcode']) && -1 != $this->processInformation['exitcode']) {
1156
            $this->exitcode = $this->processInformation['exitcode'];
1157
        }
1158
    }
1159
1160
    /**
1161
     * 关闭资源
1162
     * @access private
1163
     * @return int 退出码
1164
     */
1165
    private function close()
0 ignored issues
show
Coding Style introduced by
Private method name "Process::close" must be prefixed with an underscore
Loading history...
1166
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
1167
        $this->processPipes->close();
1168
        if (is_resource($this->process)) {
1169
            $exitcode = proc_close($this->process);
1170
        } else {
1171
            $exitcode = -1;
1172
        }
1173
1174
        $this->exitcode = -1 !== $exitcode ? $exitcode : (null !== $this->exitcode ? $this->exitcode : -1);
1175
        $this->status   = self::STATUS_TERMINATED;
1176
1177
        if (-1 === $this->exitcode && null !== $this->fallbackExitcode) {
1178
            $this->exitcode = $this->fallbackExitcode;
1179
        } elseif (-1 === $this->exitcode && $this->processInformation['signaled']
1180
            && 0 < $this->processInformation['termsig']
1181
        ) {
1182
            $this->exitcode = 128 + $this->processInformation['termsig'];
1183
        }
1184
1185
        return $this->exitcode;
1186
    }
1187
1188
    /**
1189
     * 重置数据
1190
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
1191
    private function resetProcessData()
0 ignored issues
show
Coding Style introduced by
Private method name "Process::resetProcessData" must be prefixed with an underscore
Loading history...
1192
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
1193
        $this->starttime                    = null;
1194
        $this->callback                     = null;
1195
        $this->exitcode                     = null;
1196
        $this->fallbackExitcode             = null;
1197
        $this->processInformation           = null;
1198
        $this->stdout                       = null;
1199
        $this->stderr                       = null;
1200
        $this->process                      = null;
1201
        $this->latestSignal                 = null;
1202
        $this->status                       = self::STATUS_READY;
1203
        $this->incrementalOutputOffset      = 0;
1204
        $this->incrementalErrorOutputOffset = 0;
1205
    }
1206
1207
    /**
1208
     * 将一个 POSIX 信号发送到进程中。
1209
     * @access private
1210
     * @param  int  $signal
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1211
     * @param  bool $throwException
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1212
     * @return bool
1213
     */
1214
    private function doSignal($signal, $throwException)
0 ignored issues
show
Coding Style introduced by
Private method name "Process::doSignal" must be prefixed with an underscore
Loading history...
1215
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
1216
        if (!$this->isRunning()) {
1217
            if ($throwException) {
1218
                throw new \LogicException('Can not send signal on a non running process.');
1219
            }
1220
1221
            return false;
1222
        }
1223
1224
        if ($this->isSigchildEnabled()) {
1225
            if ($throwException) {
1226
                throw new \RuntimeException('This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
1227
            }
1228
1229
            return false;
1230
        }
1231
1232
        if (true !== @proc_terminate($this->process, $signal)) {
1233
            if ($throwException) {
1234
                throw new \RuntimeException(sprintf('Error while sending signal `%s`.', $signal));
1235
            }
1236
1237
            return false;
1238
        }
1239
1240
        $this->latestSignal = $signal;
1241
1242
        return true;
1243
    }
1244
1245
    /**
1246
     * 确保进程已经开启
1247
     * @access private
1248
     * @param  string $functionName
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1249
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
1250
    private function requireProcessIsStarted($functionName)
0 ignored issues
show
Coding Style introduced by
Private method name "Process::requireProcessIsStarted" must be prefixed with an underscore
Loading history...
1251
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
1252
        if (!$this->isStarted()) {
1253
            throw new \LogicException(sprintf('Process must be started before calling %s.', $functionName));
1254
        }
1255
    }
1256
1257
    /**
1258
     * 确保进程已经终止
1259
     * @access private
1260
     * @param  string $functionName
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1261
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
1262
    private function requireProcessIsTerminated($functionName)
0 ignored issues
show
Coding Style introduced by
Private method name "Process::requireProcessIsTerminated" must be prefixed with an underscore
Loading history...
1263
{
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
Coding Style introduced by
Opening brace indented incorrectly; expected 4 spaces, found 0
Loading history...
1264
        if (!$this->isTerminated()) {
1265
            throw new \LogicException(sprintf('Process must be terminated before calling %s.', $functionName));
1266
        }
1267
    }
1268
}
1269