Files
CLOAK/lib/CloakPhpCompat.php
T
2026-06-14 14:00:24 +08:00

140 lines
4.0 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* PHP 8 运行环境检测(版本、扩展、可选语法扫描)
*/
class CloakPhpCompat
{
/** @var string 项目要求的最低 PHP 版本 */
public const MIN_PHP_VERSION = '8.0.0';
/** @var string[] 必需扩展 */
public const REQUIRED_EXTENSIONS = [
'curl',
'json',
'mbstring',
'mysqli',
'session',
'dom',
];
/** @var string[] 推荐扩展(缺失时警告,不阻断) */
public const RECOMMENDED_EXTENSIONS = [
'redis',
'openssl',
];
/**
* 入口引导时调用:不满足最低版本则终止并输出说明
*
* @return void
*/
public static function ensureRuntime()
{
$report = self::buildReport();
if ($report['ok']) {
return;
}
$lines = ['Cloaka 需要 PHP ' . self::MIN_PHP_VERSION . ' 或更高版本。', ''];
foreach ($report['errors'] as $err) {
$lines[] = ' - ' . $err;
}
$lines[] = '';
$lines[] = '当前 PHP' . PHP_VERSION;
$lines[] = '请升级 PHP 或运行:php tools/regression_test.php';
$message = implode("\n", $lines);
if (PHP_SAPI === 'cli') {
fwrite(STDERR, $message . "\n");
exit(1);
}
if (!headers_sent()) {
header('Content-Type: text/plain; charset=utf-8', true, 503);
}
echo $message;
exit(1);
}
/**
* @return array{ok:bool,php_version:string,errors:array,warnings:array,extensions:array}
*/
public static function buildReport()
{
$errors = [];
$warnings = [];
if (version_compare(PHP_VERSION, self::MIN_PHP_VERSION, '<')) {
$errors[] = 'PHP 版本过低(需要 >= ' . self::MIN_PHP_VERSION . ',当前 ' . PHP_VERSION . '';
}
foreach (self::REQUIRED_EXTENSIONS as $ext) {
if (!extension_loaded($ext)) {
$errors[] = '缺少必需扩展:' . $ext;
}
}
foreach (self::RECOMMENDED_EXTENSIONS as $ext) {
if (!extension_loaded($ext)) {
$warnings[] = '缺少推荐扩展:' . $ext . '(部分功能将降级)';
}
}
return [
'ok' => empty($errors),
'php_version' => PHP_VERSION,
'errors' => $errors,
'warnings' => $warnings,
'extensions' => array_values(array_filter(
array_merge(self::REQUIRED_EXTENSIONS, self::RECOMMENDED_EXTENSIONS),
'extension_loaded'
)),
];
}
/**
* 对项目 PHP 文件执行 php -l(仅 CLI
*
* @param string|null $root 项目根目录
* @return array{ok:bool,failures:array<int,string>}
*/
public static function lintProject($root = null)
{
$root = $root ?: dirname(__DIR__);
$failures = [];
if (PHP_SAPI !== 'cli') {
return ['ok' => true, 'failures' => [], 'skipped' => 'not_cli'];
}
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS)
);
$skipDirs = ['vendor', 'node_modules', '.git'];
foreach ($iterator as $file) {
/** @var SplFileInfo $file */
if (!$file->isFile() || $file->getExtension() !== 'php') {
continue;
}
$path = $file->getPathname();
foreach ($skipDirs as $skip) {
if (strpos($path, DIRECTORY_SEPARATOR . $skip . DIRECTORY_SEPARATOR) !== false) {
continue 2;
}
}
$cmd = escapeshellarg(PHP_BINARY) . ' -l ' . escapeshellarg($path) . ' 2>&1';
$out = [];
exec($cmd, $out, $code);
if ($code !== 0) {
$failures[] = $path . ': ' . implode(' ', $out);
}
}
return ['ok' => empty($failures), 'failures' => $failures];
}
}