Completed
Push — master ( a12146...64a44a )
by Joao
02:25
created

ForkHandler::stop()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 6
c 1
b 0
f 0
nc 3
nop 2
dl 0
loc 11
rs 9.4285
1
<?php
2
3
namespace ByJG\PHPThread\Handler;
4
5
use ByJG\Cache\CacheContext;
6
use InvalidArgumentException;
7
use RuntimeException;
8
9
/**
10
 * Native Implementation of Threads in PHP.
11
 *
12
 * A class to spawn a thread. Only works in *nix environments,
13
 * as Windows platform is missing libpcntl.
14
 *
15
 * Forks the process.
16
 */
17
class ForkHandler implements ThreadInterface
18
{
19
    protected $_threadKey;
20
    private $callable;
21
    private $_pid;
22
23
    /**
24
     * constructor method
25
     *
26
     * @throws RuntimeException
27
     * @throws InvalidArgumentException
28
     */
29
    public function __construct()
30
    {
31
        if (!function_exists('pcntl_fork')) {
32
            throw new RuntimeException('PHP was compiled without --enable-pcntl or you are running on Windows.');
33
        }
34
35
        /** Check if is configured */
36
        CacheContext::factory('phpthread');
37
    }
38
39
    /**
40
     * Private function for set the method will be forked;
41
     *
42
     * @param callable $callable string with the function name or a array with the instance and the method name
43
     * @return mixed|void
44
     */
45
    public function setCallable(callable $callable)
46
    {
47
        $this->callable = $callable;
48
    }
49
50
    /**
51
     * Start the thread
52
     *
53
     * @throws RuntimeException
54
     */
55
    public function execute()
56
    {
57
        $this->_threadKey = 'thread_' . rand(1000, 9999) . rand(1000, 9999) . rand(1000, 9999) . rand(1000, 9999);
58
59
        if (($this->_pid = pcntl_fork()) == -1) {
60
            throw new RuntimeException('Couldn\'t fork the process');
61
        }
62
63
        if ($this->_pid) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
64
            // Parent
65
            //pcntl_wait($status); //Protect against Zombie children
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
66
        } else {
67
            // Child.
68
            pcntl_signal(SIGTERM, array($this, 'signalHandler'));
69
            $args = func_get_args();
70
            if (!empty($args)) {
71
                $return = call_user_func_array($this->callable, $args);
72
            } else {
73
                $return = call_user_func($this->callable);
74
            }
75
76
            if (!is_null($return)) {
77
                $this->saveResult($return);
78
            }
79
80
            exit(0);
0 ignored issues
show
Coding Style Compatibility introduced by
The method execute() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
81
        }
82
83
        // Parent.
84
    }
85
86
    /**
87
     * Save the thread result in a shared memory block
88
     *
89
     * @param mixed $object Need to be serializable
90
     */
91
    protected function saveResult($object)
92
    {
93
        $cache = CacheContext::factory('phpthread');
94
        $cache->set($this->_threadKey, $object);
95
    }
96
97
    /**
98
     * Get the thread result from the shared memory block and erase it
99
     *
100
     * @return mixed
101
     */
102
    public function getResult()
103
    {
104
        if (is_null($this->_threadKey)) {
105
            return null;
106
        }
107
108
        $key = $this->_threadKey;
109
        $this->_threadKey = null;
110
111
        $cache = CacheContext::factory('phpthread');
112
        $result = $cache->get($key);
113
        $cache->release($key);
114
115
        return $result;
116
    }
117
118
    /**
119
     * Kill a thread
120
     *
121
     * @param int $signal
122
     * @param bool $wait
123
     */
124
    public function stop($signal = SIGKILL, $wait = false)
125
    {
126
        if ($this->isAlive()) {
127
            posix_kill($this->_pid, $signal);
128
129
            $status = null;
130
            if ($wait) {
131
                pcntl_waitpid($this->_pid, $status);
132
            }
133
        }
134
    }
135
136
    /**
137
     * Check if the forked process is alive
138
     * @return bool
139
     */
140
    public function isAlive()
141
    {
142
        $status = null;
143
        return (pcntl_waitpid($this->_pid, $status, WNOHANG) === 0);
144
    }
145
146
    /**
147
     * Handle the signal to the thread
148
     *
149
     * @param int $signal
150
     */
151
    private function signalHandler($signal)
152
    {
153
        switch ($signal) {
154
            case SIGTERM:
155
                exit(0);
0 ignored issues
show
Coding Style Compatibility introduced by
The method signalHandler() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
156
        }
157
    }
158
159
    public function waitFinish()
160
    {
161
        while ($this->isAlive()) {}
0 ignored issues
show
Unused Code introduced by
This while loop is empty and can be removed.

This check looks for while loops that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

Consider removing the loop.

Loading history...
162
    }
163
}
164