Press "Enter" to skip to content

关于PHP的编译和执行分离

关于让"PHP的编译和执行分离"这个问题, 一直有人提, 也一直有人尝试. 提的人认为编译执行分离以后, 可以得到性能提升, 可以做代码保护等.
我本身并不是对这个特性很感冒, 因为这里面存在一个投入产出比. 让我来给大家解释一下, 然而不管怎么样, 在最后我会给大家提供一种方案来实现这个功能.
1. PHP的编译并不是很耗时
我之前的文章也介绍过, PHP的编译是线性的编译过程, 不做优化, 所以这个过程非常之快. 而编译和执行分离这个特性的提出着认为分离以后, 可以省掉编译过程, 会有很大的性能提升.
后记, 有同学提出, 编译和执行分离以后, 就可以在编译的时候做各种优化, 从而影响执行速度. 这个确实是个值得研究的方向.
2. 开发速度
PHP的一个优点就是开发/部署/调试非常方便, 快速, 更改立即见效, 这个当大家在十万火急的情况下修复线上bug的时候会感受更加深刻, 🙂 而如果我们采用了编译/执行分离以后, 那么更改就需要首先编译, 然后部署, 然后才能生效, 这对于开发来说, 并不是什么好事.
3. 我们有APC/Zend O+等第三方的代码缓存工具
APC等第三方的代码缓存工具(Opcodes Cache)已经相对比较成熟, 并对开发者透明, 大家只要在服务器上安装了APC, 就可以得到编译/执行分离的性能提升.
4. 简单的编译/执行分离, 并不能很好的实现代码保护
原因很简单, PHP的编译不做优化, 所以很容易被反编译. 当然, 我也不否认, 采用二进制内容确实有一些作用.
另外, 还有一些因素, 比如编译/执行分离这个方案是有人在做的, 但是还不成熟等等.
最后呢, 其实我们目前也是可以做到的, 在这里我给大家提供一个类似的解决方案.
首先, 我要打个广告, 以后APC将由我来维护, 大家以后在APC的使用中如果有问题, 可以直接联系我. 🙂
回归正题: 要实现编译和执行分离, 其实我们借助APC就能做到, APC提供了一族apc_bin_dump, apc_bin_load函数, 能把Opcodes缓存导出到外部文件中.
然而, 可惜的是, 这部分功能以前一直不能很好的正常工作, 这和之前的开发者因为时间原因不在投精力在这个上面是有关系的.
经过我对apc_bin系列函数的重新梳理, 修复以后, 这部分功能现在终于可以正常工作了(从APC-3.1.12开始), 那么基于这些函数, 我们就可以实现编译执行分离.
思路很简单, 在本地通过apc_bin_dumpfile把我们的php文件, 导出成bin文件, 然后在服务器上通过apc_bin_loadfile来读取这些bin文件. 就可以实现编译和执行分离啦, 一个简单的示意代码如下:

$ find ./ -name "*.php" -exec php -r "apc_bin_dumpfile(array('{}'), array(), '{}' . '.bin');" \;

然后在服务器端的文件自动加载部分:

<?php
    function __autoload($name) {
       /*首先计算出文件名字*/
       $file =  根据类名得到PHP文件路径();
       if (!file_exists($file)) {
          //文件不存在, 说明我们还没有load过, 那么创建一个空文件.
          file_put_contents($file, '');
          apc_bin_loadfile($file . '.bin');
       } else {
          //我们已经load过了, 理论上应该已经被服务器的APC缓存处理Cache住了.
        }
       include ($file);
   }

当然, 这里只是一个简单的示意, 如果要实际使用, 你还要考虑缓存被换出的可能, 那么一个解决方案就是设置俩个自动加载函数, 第一个如上, 第二个如果被调用, 就说明缓存被换出, 导致include了一个空文件, 于是就再次load一次bin文件就可以了.
当然, 你也可以把所有的文件打包到一个bin文件中, 然后只load一次, 后续就交给服务器上的APC Cache来做就可以了. 但是这里有一个要注意的点就是,
那么对于这部分希望"代码保护"功能的人来说, 就可以使用APC来免费的完成这些事情了. 当然, 因为是内存镜像dump, 所以要受PHP版本和系统的大小端影响, 不过对于一般的应用来说, 这个倒可以很容易做到匹配.
最后, thanks to @cfc4n同学, 在这件事情上的推动, 呵呵

48 Comments

  1. star
    star June 2, 2018

    This extension is considered unmaintained and dead. However, the source code for this extension is still available within PECL GIT here
    额。。。
    是看了opcache后又看到鸟哥这篇文章的,但php.net上已经说apc不维护了,尴尬……
    感觉opcache的方法少了些,仅能自动缓存,但没法手动控制

  2. yyzs
    yyzs August 1, 2016

    APC在windows下并没有5.5的版本啊

  3. eechen
    eechen November 15, 2015

    64位Ubuntu 14.04, PHP-5.4.39 非线程安全版本, APC版本为官方APC-3.1.13.tgz, 导出大一点的文件时(约大于10KB),会发生错误:
    <?php
    $file = '/path/file.php';
    var_dump(apc_compile_file($file));
    var_dump(apc_bin_dumpfile(array($file), null, $file));
    错误:
    apc_bin_dumpfile(): Exceeded bounds check in apc_bd_alloc_ex by 280 bytes.
    C源代码:
    apc_bin.c:147 Exceeded bounds check in apc_bd_alloc_ex by %d bytes.

  4. eechen
    eechen November 15, 2015

    APC已经不再维护的.鸟哥现在维护的ZendOpcache是否考虑加入类似APC的 apc_bin_dumpfile 和 apc_bin_loadfile 相关函数和配置用于导出和载入二进制文件实现代码保护呢?

  5. Dur
    Dur August 7, 2015

    其实我觉得编译还有一个好处,可以避免一些sb的错误,比如变量写错啊,引用一个未定义变量之类的,编译的时候通通可以找出来

  6. php_4年生
    php_4年生 May 5, 2014

    hip-hop不是可以吗

  7. www.tonitech.com的站长
    www.tonitech.com的站长 January 2, 2014

    我最近也在考虑这个问题,如果把php这个解释型语言变成编译型语言是不是运行效率会很高?考虑到开发的效率可以再开发的时候继续扮演解释型语言,到正式环境的时候摇身一变成为编译型语言,这样如何?

  8. lein
    lein July 31, 2013

    @鸟哥 啊
    为什么要这句?
    include($file);
    并且$file是个空文件,怎么确定apc会从缓存中找这个$file而不是去找这个空文件呢?

  9. lein
    lein July 31, 2013

    请教关于apc的使用
    bin_dump的文件怎么使用呢,网上找到的代码都不够详细,手册上也没有示例。
    我的代码:
    1 被dump的文件(/www/f1.php):
    ‘.chr(10);
    function dosome(){
    echo date(‘Y-m-d H:i:s’);
    }
    2 执行dump的文件(/www/f2.php):
    <?php
    $filename = '/www/f1.php';
    $cache_filename = $filename.'.bin';
    apc_bin_dumpfile(array($filename),null,$cache_filename);
    apc_bin_loadfile($cache_filename);
    dosome();
    echo $cache_filename.' loaded';
    通过浏览器访问:
    http://aa.com/f2.php
    报错:
    PHP Fatal error: Call to undefined function dosome() in /www/f2.php on line 8
    =====================================
    这两个函数到底该怎么用?!
    谢谢!

  10. php
    php March 5, 2013

    真的受教了,原来PHP也这么强大

  11. Anonymous
    Anonymous January 27, 2013

    我用的时候遇到一个问题
    //t.php
    class a{
    public b(){
    return ‘a’;
    }
    }
    //dump.php
    apc_bin_dumpfile(array(dirname(__FILE__).’\t.php’),array(),’t2.bin’);
    //test.php
    $res = apc_bin_load(file_get_contents(‘t2.bin’));
    var_dump($res);//exit;
    $c = new a();
    echo $c->b();
    加载成功 但是报告 Class ‘a’ not found 这是为什么啊

  12. dailingang
    dailingang January 27, 2013

    我用的时候遇到一个问题
    b();
    ?>
    加载成功 但是报告 Class ‘a’ not found 这是为什么啊

  13. gaodi07
    gaodi07 January 19, 2013

    相信APC越来越好

  14. dawsonJ
    dawsonJ December 6, 2012

    请问php以后会实现类似c语言#ifdef之类的预编译指令吗?

  15. […] 早在3-4个月之前,鸟哥博客上一篇文章《关于PHP的编译和执行分离》中提到APC来作为PHP代码保护的方案。从文中可以看出,鸟哥的想法是每个php文件,导出一个opcode 的bin文件,加载时,也是挨个加载,这样也实现了代码保护,但一个项目几百个php文件的话,也得相应存在几百个bin文件,量比较大,操作比较复杂,管理不方便,不好做版本验证(以后会提到)。末学比较倾向于单个bin文件的导出,单个opcode bin文件的加载。而且,单个bin文件的加载,可以避免项目中出现部分文件跟整体版本不一致的情况发生,运维同事再也不用担心个别文件跟整个项目版本不一致的情况了。 […]

  16. […] 早在3-4个月之前,鸟哥博客上一篇文章《关于PHP的编译和执行分离》中提到APC来作为PHP代码保护的方案。从文中可以看出,鸟哥的想法是每个php文件,导出一个opcode 的bin文件,加载时,也是挨个加载,这样也实现了代码保护,但一个项目几百个php文件的话,也得相应存在几百个bin文件,量比较大,操作比较复杂,管理不方便,不好做版本验证(以后会提到)。末学比较倾向于单个bin文件的导出,单个opcode bin文件的加载。而且,单个bin文件的加载,可以避免项目中出现部分文件跟整体版本不一致的情况发生,运维同事再也不用担心个别文件跟整个项目版本不一致的情况了。 […]

  17. 张 健
    张 健 November 10, 2012

    崇拜前辈啊

  18. jianwu
    jianwu October 29, 2012

    不错!
    以后就基本直接执行底层C代码了~
    不过以后应该增加相应管理工具了。比如每次修改php自动编译成apc.bin等

  19. Anonymous
    Anonymous August 31, 2012

    apc相比eaccelerator性能如何, 我之前做过测试, 使用apc的程序qps要比ea慢了1倍所有.

  20. smallyang
    smallyang August 29, 2012

    水一个:想起一句话,聊天止与呵呵

  21. wee
    wee August 29, 2012

    看完前一篇和这一篇之后,忽然想到一个略有些离题的事情:
    php对windows环境的支持。
    自从windows.php.net出现之后,php本身对于windows的支持已经大致上没什么严重的问题,现在的矛盾卡在扩展上。
    php的扩展,除了windows版本的发行包中间已经附带的那些之外,其他的想找到供windows环境用的.dll文件那是相当的困难,PECL For Windows已经说了N年了,始终不见动静,到目前为止基本上只能指望http://downloads.php.net/pierre/这个页面上的那些文件。问题是这里面基本就没有PHP5.4可用的扩展……于是我只好一边对着5.4的几个新特性流口水一边咬牙继续用5.3。为啥?项目里对几个扩展依赖很大,比如http,imagick,oauth……5.4的新特性诱惑很大,但是还不足以抵消解决这几个扩展带来的成本。
    肯定有人说为啥服务器要用windows环境?理由类似于上一篇的“关于语言的选用”,理由就是“易用”。尤其是考虑到安全因素之后。windows server 2k8r2的环境可以很放心的交给开发人员花一点点时间打理即可,linux环境,就必须认真考虑养一个专职的维护人员。
    所以我是不太明白为什么pecl在对windows的支持这上面特别不给力,也许某些扩展在实现上导致了很难提供对windows的支持,但是已经在5.2和5.3上有提供的那些,要提供对应5.4的版本应该不难吧?
    也曾经在之前的某个文章里看到鸟哥提到编译windows版本的扩展并不难,但是我就一直找不到一个像样的教程,鸟哥能写一个不?对于并不精通C的PHP用户来说,怎样做才能编译出windows版本的pecl扩展来?

  22. sevensoile
    sevensoile August 24, 2012

    前辈你好。我有个问题想问下,现在我在网站后台架构上产生了好大的疑问。两个选择:1。php的现有框架(yii,zf,yaf)2。后台使用java和c语言来做逻辑处理和数据库操作,中转用thrift给php
    重点是并行处理上和速度上,我不知道怎么平衡这个问题。
    希望前辈指点下,新浪微薄后台也是用php的么?

  23. MC
    MC August 20, 2012

    见教了,非常好

  24. sesehai
    sesehai August 17, 2012

    APC 目前是否支持 PHP-5.4.4呢?

  25. hellokitty
    hellokitty August 17, 2012

    貌似没有for windows的版本下载,最新的是3.1.10

  26. wclssdn
    wclssdn August 16, 2012

    就算不用这种方法, 直接开启apc. 不也会缓存住opcode么? 一直也不是很了解apc. 一直认为的就是开启了它, 只要php文件没发生修改. 那只要执行过一次,再下一次执行的时候, apc就会略过它的编译过程. 直接执行上次编译过的结果.. 是这样么?

  27. hileon
    hileon August 16, 2012

    PHP的一个优点就是开发/部署/调试非常`方案`

    `方便`吧

  28. 大力水手
    大力水手 August 16, 2012

    APC终于有希望了,哈哈哈

  29. loki
    loki August 16, 2012

    拿到这些bin文件,再apc_bin_loadfile,那也起不到代码保护的左右- –

  30. 大草原
    大草原 August 16, 2012

    在一起!在一起!在一起!在一起!在一起!在一起!在一起!在一起!在一起!

  31. beimuaihui
    beimuaihui August 16, 2012

    太好了,又可加速又可保护代码.

  32. Demon
    Demon August 16, 2012

    如果我说在一起,鸟哥会不会生气。在一起!在一起!在一起!

  33. 烽火
    烽火 August 16, 2012

    老黑你幼稚不

  34. 非洲黑馒头
    非洲黑馒头 August 16, 2012

    抢占沙发

Comments are closed.