| 1 | <?php |
||
| 2 | |||
| 3 | namespace ByJG\PHPThread\Handler; |
||
| 4 | |||
| 5 | use ByJG\Cache\CacheContext; |
||
|
0 ignored issues
–
show
|
|||
| 6 | use ByJG\Cache\Engine\ShmopCacheEngine; |
||
| 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 $closure; |
||
| 21 | private $pid; |
||
| 22 | |||
| 23 | |||
| 24 | private $maxSharedMemorySize = null; |
||
| 25 | private $defaultPermission = null; |
||
| 26 | |||
| 27 | /** |
||
| 28 | * constructor method |
||
| 29 | * |
||
| 30 | * @param int $maxSharedMemorySize |
||
| 31 | * @param string $defaultPermission |
||
| 32 | */ |
||
| 33 | 3 | public function __construct($maxSharedMemorySize, $defaultPermission) |
|
| 34 | { |
||
| 35 | 3 | if (!function_exists('pcntl_fork')) { |
|
| 36 | throw new RuntimeException('PHP was compiled without --enable-pcntl or you are running on Windows.'); |
||
| 37 | } |
||
| 38 | |||
| 39 | 3 | if (empty($maxSharedMemorySize)) { |
|
| 40 | 3 | $maxSharedMemorySize = 0x100000; |
|
| 41 | } |
||
| 42 | |||
| 43 | 3 | if (empty($defaultPermission)) { |
|
| 44 | 3 | $defaultPermission = '0700'; |
|
| 45 | } |
||
| 46 | |||
| 47 | 3 | $this->maxSharedMemorySize = $maxSharedMemorySize; |
|
| 48 | 3 | $this->defaultPermission = $defaultPermission; |
|
| 49 | } |
||
| 50 | |||
| 51 | /** |
||
| 52 | * Private function for set the method will be forked; |
||
| 53 | * |
||
| 54 | * @param \Closure $closure |
||
| 55 | * @return mixed|void |
||
| 56 | */ |
||
| 57 | 3 | public function setClosure(\Closure $closure) |
|
| 58 | { |
||
| 59 | 3 | $this->closure = $closure; |
|
| 60 | } |
||
| 61 | |||
| 62 | /** |
||
| 63 | * Start the thread |
||
| 64 | * |
||
| 65 | * @throws RuntimeException |
||
| 66 | */ |
||
| 67 | 3 | public function execute() |
|
| 68 | { |
||
| 69 | 3 | $this->threadKey = 'thread_' . rand(1000, 9999) . rand(1000, 9999) . rand(1000, 9999) . rand(1000, 9999); |
|
| 70 | |||
| 71 | 3 | if (($this->pid = pcntl_fork()) == -1) { |
|
| 72 | throw new RuntimeException('Couldn\'t fork the process'); |
||
| 73 | } |
||
| 74 | |||
| 75 | 3 | if ($this->pid) { |
|
| 76 | // Parent |
||
| 77 | //pcntl_wait($status); //Protect against Zombie children |
||
| 78 | } else { |
||
| 79 | // Child. |
||
| 80 | pcntl_signal(SIGTERM, array($this, 'signalHandler')); |
||
| 81 | $args = func_get_args(); |
||
| 82 | |||
| 83 | try { |
||
| 84 | $return = call_user_func_array($this->closure, (array)$args); |
||
| 85 | |||
| 86 | if (!is_null($return)) { |
||
| 87 | $this->saveResult($return); |
||
| 88 | } |
||
| 89 | // Executed only in PHP 7, will not match in PHP 5.x |
||
| 90 | } catch (\Throwable $t) { |
||
| 91 | $this->saveResult($t); |
||
| 92 | // Executed only in PHP 5. Remove when PHP 5.x is no longer necessary. |
||
| 93 | } catch (\Exception $ex) { |
||
| 94 | $this->saveResult($ex); |
||
| 95 | } |
||
| 96 | |||
| 97 | exit(0); |
||
|
0 ignored issues
–
show
|
|||
| 98 | } |
||
| 99 | } |
||
| 100 | |||
| 101 | /** |
||
| 102 | * @return \ByJG\Cache\Engine\ShmopCacheEngine |
||
| 103 | */ |
||
| 104 | 2 | protected function getSharedMemoryEngine() |
|
| 105 | { |
||
| 106 | 2 | return new ShmopCacheEngine( |
|
| 107 | [ |
||
| 108 | 2 | 'max-size' => $this->maxSharedMemorySize, |
|
| 109 | 2 | 'default-permission' => $this->defaultPermission |
|
| 110 | ] |
||
| 111 | ); |
||
| 112 | } |
||
| 113 | |||
| 114 | /** |
||
| 115 | * Save the thread result in a shared memory block |
||
| 116 | * |
||
| 117 | * @param mixed $object Need to be serializable |
||
| 118 | */ |
||
| 119 | protected function saveResult($object) |
||
| 120 | { |
||
| 121 | $this->getSharedMemoryEngine()->set($this->threadKey, $object); |
||
| 122 | } |
||
| 123 | |||
| 124 | /** |
||
| 125 | * Get the thread result from the shared memory block and erase it |
||
| 126 | * |
||
| 127 | * @return mixed |
||
| 128 | * @throws \Error |
||
| 129 | * @throws object |
||
| 130 | */ |
||
| 131 | 2 | public function getResult() |
|
| 132 | { |
||
| 133 | 2 | if (is_null($this->threadKey)) { |
|
| 134 | return null; |
||
| 135 | } |
||
| 136 | |||
| 137 | 2 | $key = $this->threadKey; |
|
| 138 | 2 | $this->threadKey = null; |
|
| 139 | |||
| 140 | 2 | $cache = $this->getSharedMemoryEngine(); |
|
| 141 | 2 | $result = $cache->get($key); |
|
| 142 | 2 | $cache->release($key); |
|
| 143 | |||
| 144 | 2 | if (is_object($result) && |
|
| 145 | ($result instanceof \Exception |
||
| 146 | || $result instanceof \Throwable |
||
| 147 | || $result instanceof \Error |
||
| 148 | ) |
||
| 149 | ) { |
||
| 150 | throw $result; |
||
| 151 | } |
||
| 152 | |||
| 153 | 2 | return $result; |
|
| 154 | } |
||
| 155 | |||
| 156 | /** |
||
| 157 | * Kill a thread |
||
| 158 | * |
||
| 159 | * @param int $signal |
||
| 160 | * @param bool $wait |
||
| 161 | */ |
||
| 162 | 1 | public function stop($signal = SIGKILL, $wait = false) |
|
| 163 | { |
||
| 164 | 1 | if ($this->isAlive()) { |
|
| 165 | 1 | posix_kill($this->pid, $signal); |
|
| 166 | |||
| 167 | 1 | $status = null; |
|
| 168 | 1 | if ($wait) { |
|
| 169 | pcntl_waitpid($this->pid, $status); |
||
| 170 | } |
||
| 171 | } |
||
| 172 | } |
||
| 173 | |||
| 174 | /** |
||
| 175 | * Check if the forked process is alive |
||
| 176 | * @return bool |
||
| 177 | */ |
||
| 178 | 3 | public function isAlive() |
|
| 179 | { |
||
| 180 | 3 | $status = null; |
|
| 181 | 3 | return (pcntl_waitpid($this->pid, $status, WNOHANG) === 0); |
|
| 182 | } |
||
| 183 | |||
| 184 | /** |
||
| 185 | * Handle the signal to the thread |
||
| 186 | * |
||
| 187 | * @param int $signal |
||
| 188 | */ |
||
| 189 | private function signalHandler($signal) |
||
| 190 | { |
||
| 191 | switch ($signal) { |
||
| 192 | case SIGTERM: |
||
| 193 | exit(0); |
||
|
0 ignored issues
–
show
|
|||
| 194 | } |
||
| 195 | } |
||
| 196 | |||
| 197 | 2 | public function waitFinish() |
|
| 198 | { |
||
| 199 | 2 | pcntl_wait($status); |
|
| 200 | 2 | if ($this->isAlive()) { |
|
| 201 | 2 | $this->waitFinish(); |
|
| 202 | } |
||
| 203 | } |
||
| 204 | |||
| 205 | public function getClassName() |
||
| 206 | { |
||
| 207 | return ForkHandler::class; |
||
| 208 | } |
||
| 209 | } |
||
| 210 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths