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

使用TGM Plugin Activation带Token验证的自定义插件下载失败求助

自定义WordPress主题TGM插件激活Token验证下载问题排查解决

问题场景

我在自定义WordPress主题中集成了TGM Plugin Activation库,配置了自有服务器上的自定义必需插件,通过source URL传递Token做下载前验证。

TGM插件配置代码

[
    'name'     => esc_html__( 'My Theme Toolkit', 'text-domain' ),
    'slug'     => 'my-theme-toolkit',
    'source'   => 'https://example.com/tooklkit-plugins/?token=here_is_token',
    'required' => true,
    'version'  => '1.0.0',
]

服务器端Token验证与文件输出代码

$expected_token = 'here_is_token';

// Check if the token is valid.
if ( isset( $_GET['token'] ) && $_GET['token'] === $expected_token ) {

    // Token is valid, allow user to download plugin.
    $plugin_url = 'https://example.com/plugins/my-theme-toolkit.zip';

    header( 'Content-Type: application/octet-stream' );
    header( "Content-Transfer-Encoding: Binary" );
    header( "Content-disposition: attachment; filename=\"my-theme-toolkit.zip\"" );

    readfile( $plugin_url );

} else {

    echo 'Invalid or missing token. Please contact support for assistance.';
}

执行后返回错误:

The package could not be installed. PCLZIP_ERR_BAD_FORMAT (-10): Unable to find End of Central Dir Record signature


问题原因

  1. 远程URL读取的额外输出:使用readfile()读取远程HTTPS URL时,会引入HTTP响应头或服务器额外输出,导致返回内容不是纯ZIP文件,破坏了ZIP的结构。
  2. 输出缓冲未处理:脚本可能存在未清理的输出(比如PHP文件首尾的空格、BOM头),混入ZIP内容中导致PCLZIP无法识别。
  3. 文件类型与长度缺失:application/octet-stream类型不够明确,且未添加Content-Length头,可能导致文件传输不完整。

修复方案

方案1:使用本地文件路径(推荐)

如果插件ZIP在当前服务器本地,直接用本地绝对路径读取,避免远程请求的问题:

$expected_token = 'here_is_token';

// 清空所有输出缓冲
ob_start();
ob_clean();

if ( isset( $_GET['token'] ) && $_GET['token'] === $expected_token ) {
    // 替换为服务器上插件ZIP的本地绝对路径
    $plugin_path = '/home/your-user/public_html/plugins/my-theme-toolkit.zip';

    if (!file_exists($plugin_path)) {
        http_response_code(404);
        echo 'Plugin file not found.';
        exit;
    }

    // 设置明确的ZIP类型头
    header( 'Content-Type: application/zip' );
    header( "Content-Transfer-Encoding: Binary" );
    header( "Content-disposition: attachment; filename=\"" . basename($plugin_path) . "\"" );
    // 添加文件长度,确保完整传输
    header( 'Content-Length: ' . filesize($plugin_path) );

    readfile( $plugin_path );
    // 终止脚本,防止后续输出
    exit;
} else {
    http_response_code(403);
    echo 'Invalid or missing token. Please contact support for assistance.';
}

ob_end_flush();

方案2:用cURL读取远程文件(若必须远程获取)

如果插件文件只能从远程服务器获取,使用cURL替代readfile(),确保只获取纯ZIP内容:

$expected_token = 'here_is_token';

ob_start();
ob_clean();

if ( isset( $_GET['token'] ) && $_GET['token'] === $expected_token ) {
    $plugin_url = 'https://example.com/plugins/my-theme-toolkit.zip';

    $ch = curl_init($plugin_url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    // 生产环境建议开启SSL验证,仅当证书问题时临时关闭
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

    $zip_content = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($http_code !== 200 || empty($zip_content)) {
        http_response_code(404);
        echo 'Failed to retrieve plugin file.';
        exit;
    }

    header( 'Content-Type: application/zip' );
    header( "Content-Transfer-Encoding: Binary" );
    header( "Content-disposition: attachment; filename=\"my-theme-toolkit.zip\"" );
    header( 'Content-Length: ' . strlen($zip_content) );

    echo $zip_content;
    exit;
} else {
    http_response_code(403);
    echo 'Invalid or missing token. Please contact support for assistance.';
}

ob_end_flush();

额外注意事项

  • 确保服务器端脚本无任何额外输出(比如PHP文件首尾的空格、多余echo语句),这些都会破坏ZIP结构。
  • 优先使用application/zip作为Content-Type,帮助TGM正确识别文件类型。
  • 必须添加Content-Length头,保证客户端完整接收文件。
  • 脚本末尾一定要用exit或die终止执行,防止后续代码输出混入ZIP内容。

内容的提问来源于stack exchange,提问作者Emon Ahmed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 09:57:17