'npm -v', 'cnpm' => 'cnpm -v', 'yarn' => 'yarn -v', 'pnpm' => 'pnpm -v', 'node' => 'node -v', ]; /** * 比较两个版本号 * @param string $v1 要求的版本号 * @param bool|string $v2 被比较版本号 * @return bool 是否达到要求的版本号 */ public static function compare(string $v1, bool|string $v2): bool { if (!$v2) { return false; } if (strtolower($v1[0]) == 'v') { $v1 = substr($v1, 1); } if (strtolower($v2[0]) == 'v') { $v2 = substr($v2, 1); } if ($v1 == "*" || $v1 == $v2) { return true; } if (str_contains($v1, '-')) $v1 = explode('-', $v1)[0]; if (str_contains($v2, '-')) $v2 = explode('-', $v2)[0]; $v1 = explode('.', $v1); $v2 = explode('.', $v2); for ($i = 0; $i < count($v1); $i++) { if (!isset($v2[$i])) { break; } if ($v1[$i] == $v2[$i]) { continue; } if ($v1[$i] > $v2[$i]) { return false; } if ($v1[$i] < $v2[$i]) { return true; } } if (count($v1) != count($v2)) { return !(count($v1) > count($v2)); } return false; } /** * 是否是一个数字版本号 * @param $version * @return bool */ public static function checkDigitalVersion($version): bool { if (!$version) { return false; } if (strtolower($version[0]) == 'v') { $version = substr($version, 1); } $rule1 = '/\.{2,10}/'; $rule2 = '/^\d+(\.\d+){0,10}$/'; if (!preg_match($rule1, (string)$version)) { return !!preg_match($rule2, (string)$version); } return false; } /** * @return string */ public static function getCnpmVersion(): string { $execOut = self::getOutputFromProc('cnpm'); if ($execOut) { $preg = '/cnpm@(.+?) \(/is'; preg_match($preg, $execOut, $result); return $result[1] ?? ''; } return ''; } /** * 获取依赖版本号 * @param string $name 支持:npm、cnpm、yarn、pnpm、node * @return string */ public static function getVersion(string $name): string { if ($name == 'cnpm') { return self::getCnpmVersion(); } if (!in_array($name, ['npm', 'yarn', 'pnpm', 'node'])) { return ''; } $execOut = self::getOutputFromProc($name); if (!$execOut) { return ''; } if (strripos($execOut, 'npm WARN') !== false) { $preg = '/\d+(\.\d+){0,2}/'; preg_match($preg, $execOut, $matches); if (isset($matches[0]) && self::checkDigitalVersion($matches[0])) { return $matches[0]; } } $execOut = preg_split('/\r\n|\r|\n/', $execOut); for ($i = 0; $i < 2; $i++) { if (isset($execOut[$i]) && self::checkDigitalVersion($execOut[$i])) { return $execOut[$i]; } } return ''; } private static function getOutputFromProc(string $name): bool|string { if (!function_exists('proc_open') || !function_exists('proc_close')) { return false; } $command = self::VERSION_COMMANDS[$name] ?? ''; if (!$command) { return false; } if (DIRECTORY_SEPARATOR === '\\') { $command = 'cmd /c "' . $command . '"'; } $descriptorsPec = [1 => ['pipe', 'w'], 2 => ['pipe', 'w']]; $process = proc_open($command, $descriptorsPec, $pipes, root_path(), null); if (!is_resource($process)) { return ''; } $info = stream_get_contents($pipes[1]); $info .= stream_get_contents($pipes[2]); fclose($pipes[1]); fclose($pipes[2]); proc_close($process); return self::outputFilter($info); } private static function outputFilter($str): string { $str = trim($str); $preg = '/\x1b\[(.*?)m/i'; $str = preg_replace($preg, '', $str); $str = str_replace(["\r\n", "\r", "\n"], "\n", $str); return mb_convert_encoding($str, 'UTF-8', 'UTF-8,GBK,GB2312,BIG5'); } }