Passed
Push — develop ( e4c6d1...c945b6 )
by Brent
03:08
created

Manager::handleProcessSuccess()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 1
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
1
<?php
2
3
namespace Pageon\Pcntl;
4
5
use Brendt\Stitcher\Event\Event;
6
use Symfony\Component\EventDispatcher\EventDispatcher;
7
8
class Manager
9
{
10
    /**
11
     * @var EventDispatcher
12
     */
13
    private $eventDispatcher;
14
15
    private $children;
16
17
    public function __construct(EventDispatcher $eventDispatcher) {
18
        pcntl_signal_dispatch();
19
        $this->eventDispatcher = $eventDispatcher;
20
        $this->children = [];
21
22
        pcntl_signal(SIGCHLD, function () {
23
24
        });
25
    }
26
27
    public function async(Process $process) : Process {
28
        socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets);
29
30
        list($parentSocket, $childSocket) = $sockets;
31
32
        if (($pid = pcntl_fork()) == 0) {
33
            socket_close($childSocket);
34
            $output = serialize($process->execute());
35
            socket_write($parentSocket, substr($output, 0, 4095));
36
            socket_close($parentSocket);
37
38
            exit;
39
        }
40
41
        socket_close($parentSocket);
42
43
        $process
44
            ->setStartTime(time())
45
            ->setPid($pid)
46
            ->setSocket($childSocket);
47
48
        return $process;
49
    }
50
51
    public function wait(ProcessCollection $processCollection) {
52
        $output = [];
53
        $processes = $processCollection->toArray();
54
55
        while (count($processes)) {
56
            /** @var Process $process */
57
            foreach ($processes as $key => $process) {
58
                $processStatus = pcntl_waitpid($process->getPid(), $status, WNOHANG | WUNTRACED);
59
60
                switch ($processStatus) {
61
                    case $process->getPid():
62
                        $this->handleProcessSuccess($process);
63
                        unset($processes[$key]);
64
65
                        break;
66
                    case 0:
67
                        if ($process->getStartTime() + $process->getMaxRunTime() < time() || pcntl_wifstopped($status)) {
68
                            $this->handleProcessStop($process);
69
70
                            unset($processes[$key]);
71
                        }
72
73
                        break;
74
                    default:
75
                        throw new \Exception("Could not reliably manage {$process->getPid()}");
76
                }
77
            }
78
79
            if (count($processes)) {
80
                usleep(100000);
81
            }
82
        }
83
84
        return $output;
85
    }
86
87
    private function handleProcessSuccess(Process $process) {
88
        $output[] = unserialize(socket_read($process->getSocket(), 4096));
0 ignored issues
show
Coding Style Comprehensibility introduced by
$output was never initialized. Although not strictly required by PHP, it is generally a good practice to add $output = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
89
        socket_close($process->getSocket());
90
91
        $success = $process->getSuccess();
92
        if ($success) {
93
            call_user_func_array($success, [$process]);
94
        }
95
    }
96
97
    private function handleProcessStop(Process $process) {
98
        if (!posix_kill($process->getPid(), SIGKILL)) {
99
            throw new \Exception('Failed to kill ' . $process->getPid() . ': ' . posix_strerror(posix_get_last_error()));
100
        }
101
    }
102
}
103