如何调用exec执行的PHP脚本中的变量及函数?(WooCommerce插件场景)
批量价格调整任务的进度跟踪方案
要解决exec启动的worker脚本进度无法被AJAX获取的问题,核心思路是让worker把进度数据写入共享存储介质,再通过WordPress的AJAX接口读取这些数据返回给前端。以下是两种最易实现的方案:
方案一:文件存储进度(快速落地)
步骤1:主脚本初始化任务与进度文件
在触发批量调整的主逻辑中,生成唯一任务ID,创建进度文件并写入初始数据,再把任务ID传给worker脚本:
// 生成唯一任务ID $task_id = uniqid('price_adjust_'); // 预查询当前价格区间的商品总数 $total_items = $wpdb->get_var( "SELECT COUNT(ID) FROM {$wpdb->prefix}posts WHERE post_type='product' AND ID IN (SELECT post_id FROM {$wpdb->prefix}postmeta WHERE meta_key='_price' AND meta_value BETWEEN {$min_price} AND {$max_price})" ); // 初始化进度文件(存在uploads目录,确保有读写权限) $progress_file = WP_CONTENT_DIR . "/uploads/price_progress_{$task_id}.json"; file_put_contents($progress_file, json_encode([ 'total' => $total_items, 'processed' => 0, 'status' => 'running', 'error' => '' ])); // 调用worker脚本,传入任务ID、价格区间、调整比例等参数 $worker_path = WP_PLUGIN_DIR . "/your-plugin/db-worker-plugin.php"; // 用PHP_BINARY确保cli环境正确,后台执行避免阻塞主进程 exec(PHP_BINARY . " {$worker_path} {$task_id} {$min_price} {$max_price} {$adjustment} > /dev/null 2>&1 &"); // 返回任务ID给前端,用于后续进度查询 wp_send_json_success(['task_id' => $task_id]);
步骤2:Worker脚本更新进度
在db-worker-plugin.php中,每处理完一批商品就更新进度文件:
// 加载WordPress环境(确保路径正确) require_once(dirname(__DIR__, 3) . '/wp-load.php'); // 获取命令行参数 list(, $task_id, $min_price, $max_price, $adjustment) = $argv; $progress_file = WP_CONTENT_DIR . "/uploads/price_progress_{$task_id}.json"; $progress = json_decode(file_get_contents($progress_file), true); // 分批处理商品(避免一次性加载过多数据) $offset = 0; $batch_size = 100; while (true) { // 查询当前批次的商品ID $product_ids = $wpdb->get_col( "SELECT p.ID FROM {$wpdb->prefix}posts p JOIN {$wpdb->prefix}postmeta pm ON p.ID = pm.post_id WHERE p.post_type='product' AND pm.meta_key='_price' AND pm.meta_value BETWEEN {$min_price} AND {$max_price} LIMIT {$offset}, {$batch_size}" ); if (empty($product_ids)) break; // 批量更新价格 $update_query = "UPDATE {$wpdb->prefix}postmeta SET meta_value = meta_value * (1 + {$adjustment}/100) WHERE post_id IN (" . implode(',', $product_ids) . ") AND meta_key='_price'"; $wpdb->query($update_query); // 更新进度 $progress['processed'] += count($product_ids); file_put_contents($progress_file, json_encode($progress)); $offset += $batch_size; } // 标记任务完成 $progress['status'] = 'completed'; file_put_contents($progress_file, json_encode($progress)); // 可选:任务完成后1小时自动删除进度文件(避免冗余) exec("sleep 3600 && rm {$progress_file} > /dev/null 2>&1 &");
步骤3:AJAX接口读取进度
在主插件中注册AJAX动作,用于前端查询进度:
add_action('wp_ajax_get_price_progress', 'get_price_progress'); add_action('wp_ajax_nopriv_get_price_progress', 'get_price_progress'); function get_price_progress() { $task_id = $_GET['task_id'] ?? ''; $progress_file = WP_CONTENT_DIR . "/uploads/price_progress_{$task_id}.json"; if (!file_exists($progress_file)) { wp_send_json_error(['message' => '任务不存在或已完成']); } $progress = json_decode(file_get_contents($progress_file), true); wp_send_json_success($progress); }
步骤4:前端定时查询进度
用JS定时调用AJAX接口,更新页面进度显示:
function startProgressTracking(taskId) { const interval = setInterval(() => { fetch(`${window.ajaxurl}?action=get_price_progress&task_id=${taskId}`) .then(res => res.json()) .then(data => { if (data.success) { const { total, processed, status } = data.data; const percent = Math.round((processed / total) * 100); // 更新页面上的进度元素 document.getElementById('progress-bar').style.width = `${percent}%`; document.getElementById('progress-text').textContent = `${processed}/${total} 件 (${percent}%)`; if (status === 'completed') { clearInterval(interval); alert('价格调整已完成!'); } } else { clearInterval(interval); alert(data.data.message); } }); }, 2000); // 每2秒查询一次 }
方案二:数据库临时表存储进度(更稳定)
如果服务器文件权限受限,或需要长期保留任务日志,可以用数据库临时表:
- 创建临时表:在插件激活时执行
register_activation_hook(__FILE__, 'create_price_progress_table'); function create_price_progress_table() { global $wpdb; $table_name = $wpdb->prefix . 'price_update_progress'; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE {$table_name} ( task_id VARCHAR(50) PRIMARY KEY, total_items INT NOT NULL, processed_items INT NOT NULL DEFAULT 0, status VARCHAR(20) NOT NULL DEFAULT 'running', error TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) {$charset_collate};"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); }
- 主脚本初始化任务记录:把进度写入数据库而非文件
- Worker脚本更新进度:用
UPDATE语句累加processed_items - AJAX接口查询数据库:通过
task_id读取任务进度
关键注意事项
- 确保worker脚本能正确加载WordPress环境,否则无法使用
$wpdb - 批量处理时控制批次大小(如100-200条/批),避免数据库锁表
- 增加错误处理:worker脚本出错时,更新进度的
status为failed并写入错误信息 - 清理无效任务:用WP Cron定时删除超过24小时的已完成/失败任务记录
内容的提问来源于stack exchange,提问作者Александр Савенков
相关产品推荐
相关产品推荐

