你的项目需要交付源码给客户,自己又不想暴露逻辑,保护源码那就是刚需求了。可是IonCube这类加密工具我又不想花钱买,怎么办?其实PHP原生的功能就能解决问题,而且还能提升运行性能一举两得,赶紧来看看吧OPcache为啥能保护源码
PHP代码执行时默认会经历,解析源码→编译成操作码→执行这三个步骤。OPcache的作用就是把编译后的操作码缓存起来,下次直接用操作码执行,跳过解析和编译步骤.
这意味着如果我们提前把源码编译成操作码并存好,再把原始源码清空,PHP依然能通过缓存的操作码运行,源码没了自然就起到了保护作用.
实操步骤以Laravel为例
我们以 Laravel 项目为例,核心要保护的是 /app 目录,其他开源依赖如 /vendor不用管.
第一步:手动编译源码生成操作码
在项目根目录新建 warm-opcache.php,作用是遍历 /app 目录下的所有 PHP 文件,强制OPcache编译并缓存它们.
<?php// 遍历 app 目录下的所有 PHP 文件$directory = new RecursiveDirectoryIterator('/var/www/app');$iterator = new RecursiveIteratorIterator($directory);foreach ($iterator as $file) {// 只处理 .php 文件if (pathinfo($file, PATHINFO_EXTENSION) === 'php') {echo"正在编译: {$file}\n"; opcache_compile_file($file); // 编译并缓存操作码 }}
第二步:清空源码但保留时间戳
编译完成后需要把原始的PHP文件内容清空,但必须保留文件的修改时间戳,因为OPcache会通过时间戳判断文件是否更新,若时间戳变了会重新编译,导致清空的文件失效.
新建 empty-preserve-time.sh 脚本(记得执行 chmod +x 赋予权限)
#!/bin/bash# 遍历 app 目录下的所有 PHP 文件for file in $(find ./app -type f -name "*.php"); do timestamp=$(stat -c %Y "$file") # 获取文件原始修改时间(秒数) : > "$file"# 清空文件内容 touch -d "@$timestamp""$file"# 恢复原始时间戳done
第三步:配置OPcache参数
新建 zz-opcache.ini 配置文件,放到PHP的 conf.d 目录,比如 /usr/local/etc/php/conf.d,关键配置如下
opcache.enable=1 # 启用 OPcacheopcache.enable_cli=1 # CLI 模式也启用(命令行执行时用)opcache.validate_timestamps=1 # 检查文件时间戳opcache.revalidate_freq=10 # 10秒检查一次时间戳opcache.file_cache=/var/www/.opcache # 操作码缓存目录opcache.file_cache_only=1 # 只使用文件缓存(不依赖内存)
注意:先备份代码下一步会清空 /app 目录的文件内容.
执行后效果
- 运行
php warm-opcache.php:编译 /app 下的所有PHP文件,操作码会存到 /var/www/.opcache 目录; - 运行
./empty-preserve-time.sh:清空 /app 下的PHP文件内容,但保留目录结构和时间戳;
此时打开 /app 目录的PHP文件会发现内容是空的,但Laravel项目依然能正常运行.
打包成Docker镜像,方便分发
要把处理后的项目发给客户,最安全的方式是打包成Docker镜像
基础版Dockerfile
FROM php:8.3-fpm-alpine # 基础镜像,根据项目PHP版本调整# 安装项目需要的PHP扩展(如pdo、mbstring等)RUN docker-php-ext-install opcache pdo_mysqlWORKDIR /var/wwwRUN mkdir -p /var/www/.opcache # 创建OPcache缓存目录# 复制项目文件COPY app ./appCOPY artisan ./artisanCOPY bootstrap ./bootstrapCOPY config ./configCOPY public ./publicCOPY routes ./routesCOPY composer.* .COPY .env.example .env # 复制示例环境变量(避免composer报错)# 复制OPcache配置COPY zz-opcache.ini /usr/local/etc/php/conf.d# 安装依赖(生产环境,不安装dev依赖)RUN composer install --no-dev --optimize-autoloader# 编译操作码并清空源码RUN php warm-opcache.phpRUN ./empty-preserve-time.sh# 启动命令(根据项目需求调整,如用fpm或artisan serve)CMD ["./artisan", "serve", "--host=0.0.0.0"]
多阶段构建
上面的 Dockerfile 有个隐患,Docker镜像的每一层都会保留历史记录,通过 COPY 复制的原始代码可能被提取出来。解决办法是用多阶段构建,只保留最终处理后的文件
# 第一阶段:编译代码并处理FROM php:8.3-fpm-alpine as buildRUN docker-php-ext-install opcache pdo_mysqlWORKDIR /var/wwwRUN mkdir -p /var/www/.opcacheCOPY app ./appCOPY artisan ./artisanCOPY bootstrap ./bootstrapCOPY config ./configCOPY public ./publicCOPY routes ./routesCOPY composer.* .COPY .env.example .envCOPY zz-opcache.ini /usr/local/etc/php/conf.dRUN composer install --no-dev --optimize-autoloaderRUN php warm-opcache.phpRUN ./empty-preserve-time.sh# 第二阶段:只复制处理后的文件FROM php:8.3-fpm-alpineWORKDIR /var/wwwRUN docker-php-ext-install opcache pdo_mysqlCOPY zz-opcache.ini /usr/local/etc/php/conf.d# 从第一阶段复制最终文件(不含原始代码层)COPY --from=build /var/www .CMD ["./artisan", "serve", "--host=0.0.0.0"]
注意事项
- 不能删除
/app 目录或 PHP 文件:OPcache会检查文件是否存在,文件没了会报错; - 适用于所有 PHP 项目:不管是用PSR-4自动加载(如 Laravel)还是直接
require,只要提前编译就能生效; - 性能提升:因为跳过了解析和编译步骤,项目运行速度会明显加快;
这套方案不用花钱依赖PHP原生功能,既保护了源码又提升了性能,还能通过Docker轻松分发
如果你的项目需要交付源码给客户又不想暴露逻辑,不妨试试