| Total Complexity | 55 | 
| Total Lines | 344 | 
| Duplicated Lines | 0 % | 
| Changes | 0 | ||
Complex classes like TProcessHelper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use TProcessHelper, and based on these observations, apply Extract Interface, too.
| 1 | <?php  | 
            ||
| 50 | class TProcessHelper  | 
            ||
| 51 | { | 
            ||
| 52 | /** @var string When running a Pipe or Process, this is replaced with PHP_BINARY */  | 
            ||
| 53 | public const PHP_COMMAND = "@php";  | 
            ||
| 54 | |||
| 55 | /** @var string The global event prior to forking a process. */  | 
            ||
| 56 | public const FX_PREPARE_FOR_FORK = 'fxPrepareForFork';  | 
            ||
| 57 | |||
| 58 | /** @var string The global event after forking a process. */  | 
            ||
| 59 | public const FX_RESTORE_AFTER_FORK = 'fxRestoreAfterFork';  | 
            ||
| 60 | |||
| 61 | /**  | 
            ||
| 62 | * Linux priority is -20 (for "real time") to 19 (for idle).  | 
            ||
| 63 | * The WINDOWS_*_PRIORITY is what the windows priority would map into the PRADO  | 
            ||
| 64 | * and linux priority numbering. Windows will only have these priorities.  | 
            ||
| 65 | */  | 
            ||
| 66 | public const WINDOWS_IDLE_PRIORITY = 19;  | 
            ||
| 67 | public const WINDOWS_BELOW_NORMAL_PRIORITY = 8;  | 
            ||
| 68 | public const WINDOWS_NORMAL_PRIORITY = 0;  | 
            ||
| 69 | public const WINDOWS_ABOVE_NORMAL_PRIORITY = -5;  | 
            ||
| 70 | public const WINDOWS_HIGH_PRIORITY = -10;  | 
            ||
| 71 | public const WINDOWS_REALTIME_PRIORITY = -17;  | 
            ||
| 72 | |||
| 73 | /**  | 
            ||
| 74 | * Checks if the system that PHP is run on is Windows.  | 
            ||
| 75 | * @return bool Is the system Windows.  | 
            ||
| 76 | */  | 
            ||
| 77 | public static function isSystemWindows(): bool  | 
            ||
| 78 | 	{ | 
            ||
| 79 | static $isWindows = null;  | 
            ||
| 80 | 		if ($isWindows === null) { | 
            ||
| 81 | 			$isWindows = strncasecmp(php_uname('s'), 'win', 3) === 0; | 
            ||
| 82 | }  | 
            ||
| 83 | return $isWindows;  | 
            ||
| 84 | }  | 
            ||
| 85 | |||
| 86 | /**  | 
            ||
| 87 | * Checks if the system that PHP is run on is MacOS (Darwin).  | 
            ||
| 88 | * @return bool Is the system MacOS (Darwin).  | 
            ||
| 89 | */  | 
            ||
| 90 | public static function isSystemMacOS(): bool  | 
            ||
| 97 | }  | 
            ||
| 98 | |||
| 99 | /**  | 
            ||
| 100 | * Checks if the system that PHP is run on is Linux.  | 
            ||
| 101 | * @return bool Is the system Linux.  | 
            ||
| 102 | */  | 
            ||
| 103 | public static function isSystemLinux(): bool  | 
            ||
| 104 | 	{ | 
            ||
| 105 | static $isLinux = null;  | 
            ||
| 106 | 		if ($isLinux === null) { | 
            ||
| 107 | 			$isLinux = strncasecmp(php_uname('s'), 'linux', 5) === 0; | 
            ||
| 108 | }  | 
            ||
| 109 | return $isLinux;  | 
            ||
| 110 | }  | 
            ||
| 111 | |||
| 112 | /**  | 
            ||
| 113 | * @return bool Can PHP fork the process.  | 
            ||
| 114 | */  | 
            ||
| 115 | public static function isForkable(): bool  | 
            ||
| 116 | 	{ | 
            ||
| 117 | 		return function_exists('pcntl_fork'); | 
            ||
| 118 | }  | 
            ||
| 119 | |||
| 120 | /**  | 
            ||
| 121 | 	 * This forks the current process.  When specified, it will install a {@see \Prado\Util\Behaviors\TCaptureForkLog}. | 
            ||
| 122 | * Before forking, `fxPrepareForFork` is raised and after forking `fxRestoreAfterFork` is raised.  | 
            ||
| 123 | *  | 
            ||
| 124 | * `fxPrepareForFork` handlers should return null or an array of data that it will  | 
            ||
| 125 | * receive in `fxRestoreAfterFork`.  | 
            ||
| 126 | * ```php  | 
            ||
| 127 | 	 *	public function fxPrepareForFork ($sender, $param) { | 
            ||
| 128 | * return ['mydata' => 'value'];  | 
            ||
| 129 | * }  | 
            ||
| 130 | *  | 
            ||
| 131 | 	 *	public function fxRestoreAfterFork ($sender, $param) { | 
            ||
| 132 | * $param['mydata'] === 'value';  | 
            ||
| 133 | * $param['pid'];  | 
            ||
| 134 | * }  | 
            ||
| 135 | * ```  | 
            ||
| 136 | 	 * @param bool $captureForkLog Installs {@see \Prado\Util\Behaviors\TCaptureForkLog} behavior on the application | 
            ||
| 137 | * so the fork log is stored by the forking process. Default false.  | 
            ||
| 138 | * @throws TNotSupportedException When PHP Forking `pcntl_fork` is not supporting  | 
            ||
| 139 | * @return int The Child Process ID. For children processes, they receive 0. Failure is -1.  | 
            ||
| 140 | */  | 
            ||
| 141 | public static function fork(bool $captureForkLog = false): int  | 
            ||
| 142 | 	{ | 
            ||
| 143 | 		if (!static::isForkable()) { | 
            ||
| 144 | 			throw new TNotSupportedException('processhelper_no_forking'); | 
            ||
| 145 | }  | 
            ||
| 146 | $app = Prado::getApplication();  | 
            ||
| 147 | 		if ($captureForkLog && !$app->asa(TCaptureForkLog::class)) { | 
            ||
| 148 | $app->attachBehavior(TCaptureForkLog::BEHAVIOR_NAME, TCaptureForkLog::class);  | 
            ||
| 149 | }  | 
            ||
| 150 | $responses = $app->raiseEvent(static::FX_PREPARE_FOR_FORK, $app, null);  | 
            ||
| 151 | $restore = array_merge(...$responses);  | 
            ||
| 152 | $restore['pid'] = $pid = pcntl_fork();  | 
            ||
| 153 | $app->raiseEvent(static::FX_RESTORE_AFTER_FORK, $app, $restore);  | 
            ||
| 154 | 		if ($pid > 0) { | 
            ||
| 155 | 			Prado::info("Fork child: $pid", static::class); | 
            ||
| 156 | 		} elseif ($pid === 0) { | 
            ||
| 157 | 			Prado::info("Executing child fork", static::class); | 
            ||
| 158 | TSignalsDispatcher::singleton();  | 
            ||
| 159 | 		} elseif ($pid === -1) { | 
            ||
| 160 | 			Prado::notice("failed fork", static::class); | 
            ||
| 161 | }  | 
            ||
| 162 | return $pid;  | 
            ||
| 163 | }  | 
            ||
| 164 | |||
| 165 | /**  | 
            ||
| 166 | * If the exitCode is an exit code, returns the exit Status.  | 
            ||
| 167 | * @param int $exitCode  | 
            ||
| 168 | * @return int The exit Status  | 
            ||
| 169 | */  | 
            ||
| 170 | public static function exitStatus(int $exitCode): int  | 
            ||
| 171 | 	{ | 
            ||
| 172 | 		if (function_exists('pcntl_wifexited') && pcntl_wifexited($exitCode)) { | 
            ||
| 173 | $exitCode = pcntl_wexitstatus($exitCode);  | 
            ||
| 174 | }  | 
            ||
| 175 | return $exitCode;  | 
            ||
| 176 | }  | 
            ||
| 177 | |||
| 178 | /**  | 
            ||
| 179 | 	 * Filters a {@see popen} or {@see proc_open} command. | 
            ||
| 180 | 	 * The string "@php" is replaced by {@see PHP_BINARY} and in Windows the string | 
            ||
| 181 | * is surrounded by double quotes.  | 
            ||
| 182 | *  | 
            ||
| 183 | * @param mixed $command  | 
            ||
| 184 | */  | 
            ||
| 185 | public static function filterCommand($command)  | 
            ||
| 186 | 	{ | 
            ||
| 187 | $command = str_replace(static::PHP_COMMAND, PHP_BINARY, $command);  | 
            ||
| 188 | |||
| 189 | 		if (TProcessHelper::isSystemWindows()) { | 
            ||
| 190 | 			if (is_string($command)) { | 
            ||
| 191 | $command = '"' . $command . '"'; //Windows, better command support  | 
            ||
| 192 | }  | 
            ||
| 193 | }  | 
            ||
| 194 | return $command;  | 
            ||
| 195 | }  | 
            ||
| 196 | |||
| 197 | /**  | 
            ||
| 198 | * Sends a process signal on posix or linux systems.  | 
            ||
| 199 | * @param int $signal The signal to be sent.  | 
            ||
| 200 | * @param ?int $pid The process to send the signal, default null for the current  | 
            ||
| 201 | * process.  | 
            ||
| 202 | * @throws TNotSupportedException When running on Windows.  | 
            ||
| 203 | */  | 
            ||
| 204 | public static function sendSignal(int $signal, ?int $pid = null): bool  | 
            ||
| 205 | 	{ | 
            ||
| 206 | 		if (static::isSystemWindows()) { | 
            ||
| 207 | 			throw new TNotSupportedException('processhelper_no_signals'); | 
            ||
| 208 | }  | 
            ||
| 209 | 		if ($pid === null) { | 
            ||
| 210 | $pid = getmypid();  | 
            ||
| 211 | }  | 
            ||
| 212 | 		if (function_exists("posix_kill")) { | 
            ||
| 213 | return posix_kill($pid, $signal);  | 
            ||
| 214 | }  | 
            ||
| 215 | 		exec("/usr/bin/kill -s $signal $pid 2>&1", $output, $return_code); | 
            ||
| 216 | return !$return_code;  | 
            ||
| 217 | }  | 
            ||
| 218 | |||
| 219 | /**  | 
            ||
| 220 | * Kills a process.  | 
            ||
| 221 | * @param int $pid The PID to kill.  | 
            ||
| 222 | * @return bool Was the signal successfully sent.  | 
            ||
| 223 | */  | 
            ||
| 224 | public static function kill(int $pid): bool  | 
            ||
| 230 | }  | 
            ||
| 231 | |||
| 232 | /**  | 
            ||
| 233 | * @param int $pid The Process ID to check if it is running.  | 
            ||
| 234 | * @return bool Is the PID running.  | 
            ||
| 235 | */  | 
            ||
| 236 | public static function isRunning(int $pid): bool  | 
            ||
| 237 | 	{ | 
            ||
| 238 | 		if (static::isSystemWindows()) { | 
            ||
| 239 | $out = [];  | 
            ||
| 240 | 			exec("TASKLIST /FO LIST /FI \"PID eq $pid\"", $out); | 
            ||
| 241 | return count($out) > 1;  | 
            ||
| 242 | }  | 
            ||
| 243 | |||
| 244 | return static::sendSignal(0, $pid);  | 
            ||
| 245 | }  | 
            ||
| 246 | |||
| 247 | /**  | 
            ||
| 248 | * @param ?int $pid The process id to get the priority of, default null for current  | 
            ||
| 249 | * process.  | 
            ||
| 250 | * @return ?int The priority of the process.  | 
            ||
| 251 | */  | 
            ||
| 252 | public static function getProcessPriority(?int $pid = null): ?int  | 
            ||
| 253 | 	{ | 
            ||
| 254 | 		if ($pid === null) { | 
            ||
| 255 | $pid = getmypid();  | 
            ||
| 256 | }  | 
            ||
| 257 | 		if (static::isSystemWindows()) { | 
            ||
| 258 | 			$output = shell_exec("wmic process where ProcessId={$pid} get priority"); | 
            ||
| 259 | 			preg_match('/^\s*Priority\s*\r?\n\s*(\d+)/m', $output, $matches); | 
            ||
| 260 | 			if (isset($matches[1])) { | 
            ||
| 261 | $priorityValues = [ // Map Windows Priority Numbers to Linux style Numbers  | 
            ||
| 262 | TProcessWindowsPriority::Idle => static::WINDOWS_IDLE_PRIORITY,  | 
            ||
| 263 | TProcessWindowsPriority::BelowNormal => static::WINDOWS_BELOW_NORMAL_PRIORITY,  | 
            ||
| 264 | TProcessWindowsPriority::Normal => static::WINDOWS_NORMAL_PRIORITY,  | 
            ||
| 265 | TProcessWindowsPriority::AboveNormal => static::WINDOWS_ABOVE_NORMAL_PRIORITY,  | 
            ||
| 266 | TProcessWindowsPriority::HighPriority => static::WINDOWS_HIGH_PRIORITY,  | 
            ||
| 267 | TProcessWindowsPriority::Realtime => static::WINDOWS_REALTIME_PRIORITY,  | 
            ||
| 268 | ];  | 
            ||
| 269 | return $priorityValues[$matches[1]] ?? null;  | 
            ||
| 270 | 			} else { | 
            ||
| 271 | return null;  | 
            ||
| 272 | }  | 
            ||
| 273 | 		} else { | 
            ||
| 274 | 			if (strlen($priority = trim(shell_exec('exec ps -o nice= -p ' . $pid)))) { | 
            ||
| 275 | return (int) $priority;  | 
            ||
| 276 | }  | 
            ||
| 277 | return null;  | 
            ||
| 278 | }  | 
            ||
| 279 | }  | 
            ||
| 280 | |||
| 281 | /**  | 
            ||
| 282 | * In linux systems, the priority can only go up (and have less priority).  | 
            ||
| 283 | * @param int $priority The priority of the PID.  | 
            ||
| 284 | * @param ?int $pid The PID to change the priority, default null for current process.  | 
            ||
| 285 | * @return bool Was successful.  | 
            ||
| 286 | */  | 
            ||
| 287 | public static function setProcessPriority(int $priority, ?int $pid = null): bool  | 
            ||
| 288 | 	{ | 
            ||
| 289 | 		if ($pid === null) { | 
            ||
| 290 | $pid = getmypid();  | 
            ||
| 291 | }  | 
            ||
| 292 | 		if (static::isSystemWindows()) { | 
            ||
| 293 | $priorityValues = [ // The priority cap to windows text priority.  | 
            ||
| 294 | -15 => TProcessWindowsPriorityName::Realtime,  | 
            ||
| 295 | -10 => TProcessWindowsPriorityName::HighPriority,  | 
            ||
| 296 | -5 => TProcessWindowsPriorityName::AboveNormal,  | 
            ||
| 297 | 4 => TProcessWindowsPriorityName::Normal,  | 
            ||
| 298 | 9 => TProcessWindowsPriorityName::BelowNormal,  | 
            ||
| 299 | PHP_INT_MAX => TProcessWindowsPriorityName::Idle,  | 
            ||
| 300 | ];  | 
            ||
| 301 | 			foreach($priorityValues as $keyPriority => $priorityName) { | 
            ||
| 302 | 				if ($priority <= $keyPriority) { | 
            ||
| 303 | break;  | 
            ||
| 304 | }  | 
            ||
| 305 | }  | 
            ||
| 306 | 			$command = "wmic process where ProcessId={$pid} CALL setpriority \"$priorityName\""; | 
            ||
| 307 | $result = shell_exec($command);  | 
            ||
| 308 | 			if (strpos($result, 'successful') !== false) { | 
            ||
| 309 | return true;  | 
            ||
| 310 | }  | 
            ||
| 311 | 			if (!preg_match('/ReturnValue\s*=\s*(\d+);/m', $result, $matches)) { | 
            ||
| 312 | return false;  | 
            ||
| 313 | }  | 
            ||
| 314 | return $matches[1] === 0;  | 
            ||
| 315 | 		} else { | 
            ||
| 316 | 			if (static::isSystemMacOS()) { | 
            ||
| 317 | 				if (($pp = static::getProcessPriority($pid)) === null) { | 
            ||
| 318 | return false;  | 
            ||
| 319 | }  | 
            ||
| 320 | |||
| 321 | $priority -= $pp;  | 
            ||
| 322 | }  | 
            ||
| 323 | |||
| 324 | 			$result = shell_exec("exec renice -n $priority -p $pid"); | 
            ||
| 325 | |||
| 326 | // On MacOS, working properly consists of returning nothing.  | 
            ||
| 327 | // only errors return "renice: 40812: setpriority: Permission denied" (when lowering priority without permission)  | 
            ||
| 328 | // (Lowering the priority is increasing its importance)  | 
            ||
| 329 | // On the github linux test system it return "3539 (process ID) old priority 0, new priority 8"  | 
            ||
| 330 | // for an error, it return: "renice: failed to set priority for 3612 (process ID): Permission denied"  | 
            ||
| 331 | 			if (is_string($result) && str_contains($result, 'denied')) { | 
            ||
| 332 | return false;  | 
            ||
| 333 | }  | 
            ||
| 334 | |||
| 335 | return true;  | 
            ||
| 336 | }  | 
            ||
| 337 | }  | 
            ||
| 338 | |||
| 339 | /**  | 
            ||
| 340 | * Escapes a string to be used as a shell argument.  | 
            ||
| 341 | * @param string $argument  | 
            ||
| 342 | * @return string  | 
            ||
| 343 | */  | 
            ||
| 344 | public static function escapeShellArg(string $argument): string  | 
            ||
| 345 | 	{ | 
            ||
| 346 | // Fix for PHP bug #43784 escapeshellarg removes % from given string  | 
            ||
| 347 | // Fix for PHP bug #49446 escapeshellarg doesn't work on Windows  | 
            ||
| 348 | // @see https://bugs.php.net/bug.php?id=43784  | 
            ||
| 349 | // @see https://bugs.php.net/bug.php?id=49446  | 
            ||
| 350 | 		if (static::isSystemWindows()) { | 
            ||
| 351 | 			if ($argument === '') { | 
            ||
| 352 | return '""';  | 
            ||
| 353 | }  | 
            ||
| 354 | |||
| 355 | $escapedArgument = '';  | 
            ||
| 356 | $addQuote = false;  | 
            ||
| 357 | |||
| 358 | 			foreach (preg_split('/(")/', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) { | 
            ||
| 359 | 				if ($part === '"') { | 
            ||
| 360 | $escapedArgument .= '\\"';  | 
            ||
| 361 | 				} elseif (static::isSurroundedBy($part, '%')) { | 
            ||
| 362 | // environment variables  | 
            ||
| 363 | $escapedArgument .= '^%"' . substr($part, 1, -1) . '"^%';  | 
            ||
| 364 | 				} else { | 
            ||
| 365 | // escape trailing backslash  | 
            ||
| 366 | 					if (str_ends_with($part, '\\')) { | 
            ||
| 367 | $part .= '\\';  | 
            ||
| 368 | }  | 
            ||
| 369 | $addQuote = true;  | 
            ||
| 370 | $escapedArgument .= $part;  | 
            ||
| 371 | }  | 
            ||
| 372 | }  | 
            ||
| 373 | |||
| 374 | 			if ($addQuote) { | 
            ||
| 375 | $escapedArgument = '"' . $escapedArgument . '"';  | 
            ||
| 376 | }  | 
            ||
| 377 | |||
| 378 | return $escapedArgument;  | 
            ||
| 379 | }  | 
            ||
| 380 | |||
| 381 | 		return "'" . str_replace("'", "'\\''", $argument) . "'"; | 
            ||
| 382 | }  | 
            ||
| 383 | |||
| 384 | /**  | 
            ||
| 385 | * Is the string surrounded by the prefix and reversed in appendix.  | 
            ||
| 386 | * @param string $string  | 
            ||
| 387 | * @param string $prefix  | 
            ||
| 388 | * @return bool Is the string surrounded by the string  | 
            ||
| 389 | */  | 
            ||
| 390 | public static function isSurroundedBy(string $string, string $prefix): bool  | 
            ||
| 394 | }  | 
            ||
| 395 | }  | 
            ||
| 396 | 
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