You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何调用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秒查询一次
}

方案二:数据库临时表存储进度(更稳定)

如果服务器文件权限受限,或需要长期保留任务日志,可以用数据库临时表:

  1. 创建临时表:在插件激活时执行
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);
}
  1. 主脚本初始化任务记录:把进度写入数据库而非文件
  2. Worker脚本更新进度:用UPDATE语句累加processed_items
  3. AJAX接口查询数据库:通过task_id读取任务进度

关键注意事项

  • 确保worker脚本能正确加载WordPress环境,否则无法使用$wpdb
  • 批量处理时控制批次大小(如100-200条/批),避免数据库锁表
  • 增加错误处理:worker脚本出错时,更新进度的status为failed并写入错误信息
  • 清理无效任务:用WP Cron定时删除超过24小时的已完成/失败任务记录

内容的提问来源于stack exchange,提问作者Александр Савенков

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 13:20:07