$part) { $key = self::_quote($key); $val = self::_quote($part); $parts[] = "[$key]=$val"; } return "(".implode(" ", $parts).")"; } } else { return self::_quote(strval($value)); } } /** * obtenir une commande shell à partir du tableau des arguments. * à utiliser avec exec() */ static final function join(array $parts): string { $count = count($parts); for($i = 0; $i < $count; $i++) { $parts[$i] = self::_quote(strval($parts[$i])); } return implode(" ", $parts); } private static final function add_redir(string &$cmd, ?string $redir, ?string $input, ?string $output): void { if ($redir !== null) { switch ($redir) { case "outonly": case "noerr": $redir = "2>/dev/null"; break; case "erronly": case "noout": $redir = "2>&1 >/dev/null"; break; case "both": case "err2out": $redir = "2>&1"; break; case "none": case "null": $redir = ">/dev/null 2>&1"; break; case "default": $redir = null; break; } } if ($input !== null) { $redir = $redir !== null? "$redir ": ""; $redir .= "<".escapeshellarg($input); } if ($output !== null) { $redir = $redir !== null? "$redir ": ""; $redir .= ">".escapeshellarg($output); } if ($redir !== null) $cmd .= " $redir"; } /** * Corriger la commande $cmd: * - si c'est tableau, joindre les arguments avec {@link join()} * - sinon, mettre les caractères en échappement avec {@link escapeshellarg()} */ static final function fix_cmd(&$cmd, ?string $redir=null, ?string $input=null, ?string $output=null): void { if (is_array($cmd)) $cmd = self::join($cmd); else $cmd = escapeshellcmd(strval($cmd)); self::add_redir($cmd, $redir, $input, $output); } /** * Lancer la commande spécifiée avec passthru() et retourner le code de retour * dans la variable $retcode. $cmd doit déjà être formaté comme il convient * * voici la différence entre system(), passthru() et exec() * +----------------+-----------------+----------------+----------------+ * | Command | Displays Output | Can Get Output | Gets Exit Code | * +----------------+-----------------+----------------+----------------+ * | passthru() | Yes (raw) | No | Yes | * | system() | Yes (as text) | Last line only | Yes | * | exec() | No | Yes (array) | Yes | * +----------------+-----------------+----------------+----------------+ * * @return bool true si la commande s'est lancée sans erreur, false sinon */ static final function _passthru(string $cmd, int &$retcode=null): bool { passthru($cmd, $retcode); return $retcode == 0; } /** * Comme {@link _passthru()} mais lancer la commande spécifiée avec system(). * cf la doc de {@link _passthru()} pour les autres détails */ static final function _system(string $cmd, string &$output=null, int &$retcode=null): bool { $last_line = system($cmd, $retcode); if ($last_line !== false) $output = $last_line; return $retcode == 0; } /** * Comme {@link _passthru()} mais lancer la commande spécifiée avec exec(). * cf la doc de {@link _passthru()} pour les autres détails */ static final function _exec(string $cmd, array &$output=null, int &$retcode=null): bool { exec($cmd, $output, $retcode); return $retcode == 0; } /** * Lancer la commande $cmd dans un processus fils via un shell et attendre la * fin de son exécution. * * $cmd doit déjà être formaté comme il convient */ static final function _fork_exec(string $cmd, int &$retcode=null): bool { $pid = pcntl_fork(); if ($pid == -1) { // parent, impossible de forker throw new IllegalAccessException("unable to fork"); } elseif ($pid) { // parent, fork ok pcntl_waitpid($pid, $status); if (pcntl_wifexited($status)) { $retcode = pcntl_wexitstatus($status); } else { $retcode = 127; } return $retcode == 0; } // child, fork ok pcntl_exec("/bin/sh", ["-c", $cmd]); return false; } /** * Corriger la commande spécifiée avec {@link fix_cmd()} puis la lancer * avec passthru() et retourner le code de retour dans la variable $retcode * * $redir spécifie le type de redirection demandée: * - "default" | null: $output reçoit STDOUT et STDERR n'est pas redirigé * - "outonly" | "noerr": $output ne reçoit que STDOUT et STDERR est perdu * - "erronly" | "noout": $output ne reçoit que STDERR et STDOUT est perdu * - "both" | "err2out": $output reçoit STDOUT et STDERR * - "none" | "null": STDOUT et STDERR sont perdus * - sinon c'est une redirection spécifique, et la valeur est rajoutée telle * quelle à la ligne de commande * * @return bool true si la commande s'est lancée sans erreur, false sinon */ static final function passthru($cmd, int &$retcode=null, ?string $redir=null): bool { self::fix_cmd($cmd, $redir); return self::_passthru($cmd, $retcode); } /** * Comme {@link passthru()} mais lancer la commande spécifiée avec system(). * Cf la doc de {@link passthru()} pour les autres détails */ static final function system($cmd, string &$output=null, int &$retcode=null, ?string $redir=null): bool { self::fix_cmd($cmd, $redir); return self::_system($cmd, $output, $retcode); } /** * Comme {@link passthru()} mais lancer la commande spécifiée avec exec(). * Cf la doc de {@link passthru()} pour les autres détails */ static final function exec($cmd, array &$output=null, int &$retcode=null, ?string $redir=null): bool { self::fix_cmd($cmd, $redir); return self::_exec($cmd, $output, $retcode); } /** * Corriger la commande spécifiée avec {@link fix_cmd()}, la préfixer de * "exec" puis la lancer avec {@link _fork_exec()} */ static final function fork_exec($cmd, int &$retcode=null, ?string $redir=null): bool { self::fix_cmd($cmd, $redir); return self::_fork_exec("exec $cmd", $retcode); } ############################################################################# static final function is_diff_file(string $f, string $g): bool { if (!is_file($f) || !is_file($g)) return true; self::exec(array("diff", "-q", $f, $g), $output, $retcode); return $retcode !== 0; } static final function is_same_file(string $f, string $g): bool { if (!is_file($f) || !is_file($g)) return false; self::exec(array("diff", "-q", $f, $g), $output, $retcode); return $retcode === 0; } static final function is_diff_link(string $f, string $g): bool { if (!is_link($f) || !is_link($g)) return true; return @readlink($f) !== @readlink($g); } static final function is_same_link(string $f, string $g): bool { if (!is_link($f) || !is_link($g)) return false; return @readlink($f) === @readlink($g); } }