PHP编译器BPC编译实战: tinyfilemanager (一个非常好的编译入门示例)

昨天看到 peachpie 的 issue 里有一个 can not work woth tinyfilemanager , 就点进去看了下这个 tinyfilemanager.

Tiny File Manager 是一个PHP实现的单文件版的文件管理器, 4300行代码, 实现了文件/目录管理,上传本地/网络文件,文件搜索,多语言/主题切换,压缩/解压zip文件等功能.

在线Demo在这里: tinyfilemanager.github.io/demo/

于是就想着拿BPC来编译一下,继而发现这是一个很好的BPC编译示例:

  1. 文件个数少,最多3个文件,容易说清楚
  2. BPC编译时的常见问题都有
  3. 编译结果可以真的拿来用,不是个 hello world

接下来就分享下编译过程,如果你能成功编译这个项目,那么大多数的其它PHP项目都能自己编译了.

1.安装BPC编译器

编译第1步当然是要安装BPC编译器,如果你是 ubuntu 18.04 系统,可以参考 Manual Installation 手动安装,使用起来也方便些.

考虑到不少网友应该不是 ubuntu 18.04 的系统,这里采用 docker 安装:

root@hgydebian12:~# docker run -it heguangyu5/bpc-compiler
...
Status: Downloaded newer image for heguangyu5/bpc-compiler:latest
root@1fb84d5cb51d:/bpc-workspace# bpc
bpc/6.5.0
Usage: bpc [options] <input-files> [-- script args]
see bpc -h for help with command line options

root@1fb84d5cb51d:/bpc-workspace# exit

安装好之后退出docker.

2. 编译 tinyfilemanager

要编译 tinyfilemanager, 需要对其源码做一些修改,具体修改了哪些地方稍后再说,现在先来编译运行.

root@hgydebian12:~# git clone https://github.com/heguangyu5/tinyfilemanager.git
Cloning into 'tinyfilemanager'...
...
root@hgydebian12:~# cd tinyfilemanager/
root@hgydebian12:~# docker run -v `pwd`:/bpc-workspace -it heguangyu5/bpc-compiler
root@d7107d2fabef:/bpc-workspace# ls

注意,这里多加了参数

-v `pwd`:/bpc-workspace

将本机的 tinyfilemanager 目录映射到了 docker image 里的 /bpc-workspace,接下来执行一下 make 就编译好了.

root@d7107d2fabef:/bpc-workspace# make
...
generate code
  [1/2] /bpc-workspace/tinyfilemanager.php
  [2/2] /bpc-workspace/translation.json
output prologue
copy althttpd.c althttpd.h althttpd-bpc.scm
generate build.ninja
run ninja
[7/7] link ../tinyfilemanager (statically linked)
root@d7107d2fabef:/bpc-workspace# exit

退出docker, 编译好的 tinyfilemanager 就在当前目录下:

root@hgydebian12:~/tinyfilemanager# ls -lh tinyfilemanager
-rwxr-xr-x 1 root root 19M Oct 20 12:22 tinyfilemanager

3. 运行 tinyfilemanager

ldd 一下会发现缺少一些类库,当然可能在你的机器上不缺或者缺少的不一样:

root@hgydebian12:~/tinyfilemanager# ldd tinyfilemanager
    ...
    libgmodule-2.0.so.0 => not found
    libgio-2.0.so.0 => not found
    libgobject-2.0.so.0 => not found
    libglib-2.0.so.0 => not found
    ...
    libcurl.so.4 => not found
    ...

在 ubuntu 18.04 / 20.04 / 22.04 以及 Debian 12 上,补上缺少的类库 tinyfilemanager 就能正常运行了, 运行命令是在当前目录执行:

root@hgydebian12:~/tinyfilemanager# mkdir -p /tmp/tinyfilemanager/tmp && mkdir -p /tmp/tinyfilemanager/public && chown -R www-data:www-data /tmp/tinyfilemanager && ./tinyfilemanager -project-name bpc-workspace -port 7878 -home-page tinyfilemanager.php -root /tmp/tinyfilemanager/public -user www-data
Listening for HTTP requests on 0.0.0.0:7878

在其它 linux 发行版上,或者不想安装缺少的类库,也可以使用 docker 来运行:

root@hgydebian12:~/tinyfilemanager# make run-docker-docker-build
...
Status: Downloaded newer image for heguangyu5/bpc-base:latest
Listening for HTTP requests on 0.0.0.0:7878

此时访问 http://localhost:7878 或者 http://IP:7878 就能看到登录界面了.

tinyfilemanager 默认有两个用户:

admin admin@123
user 12345

4. 对 tinyfilemanager.php 做了哪些修改?

@see git commit

  1. FM_Config::save()

    tinyfilemanager.php 的原本逻辑是将设置参数保存在 $CONFIG 变量中, 当参数变化时,直接修改 tinyfilemanager.php 文件自身来完成保存.

    BPC编译后,只有一个二进制文件,显然这个逻辑需要调整,我们增加了一个 const CONFIG_FILE = '../config.json'; 将设置参数保存到 ../config.json 文件中.

    然后在一开始读取这个文件来初始化 $CONFIG.

  2. __DIR__.'/config.php'

    tinyfilemanager.php 支持使用 config.php 来覆盖默认选项,通过is_readable($config_file) 来判断是否 include 进来.

    BPC可以通过调用内置函数 include_silent($config_file) 来实现同样的目的.

    我们知道PHP include一个文件时,如果这个文件不存在,会报一个warning,然后继续执行,BPC的这个include_silent() 函数如果遇到文件不存在,不会报warning,然后继续执行.

  3. if (defined('__BPC__')) { /* BPC code*/ } else { /* PHP code */ }

    这个结构在BPC编译时只会保留下 BPC code, if/elsePHP code 直接丢掉了,类似于C语言的#ifdef #else #endif.

    当你需要在PHP环境下和在BPC编译时执行不同代码时,就可以用它.

  4. $_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(32));

    我们不想 link openssl 扩展,因为整个 tinyfilemanager.php 里只有这里用到.

  5. [] => array()

    BPC自身不支持[]这样的数组写法,如果不想改代码,可以通过 phptobpc 进行转换.

  6. $use_curl = true;

    BPC的copy不支持copy url,所以从URL上传文件功能要用curl实现.

  7. FM_Zipper_Tar

    BPC尚未实现 class PharData, 所以不支持压缩/解压tar文件.

    这里使用 if (defined('__BPC__')) 在BPC编译时消掉$tar = new FM_Zipper_Tar();代码是因为在BPC编译时,如果一个class从未声明过,那么它不能出现在代码里.

  8. translation.json

    BPC支持将资源文件编译进二进制,默认后缀是.php,.inc,.phtml的文件被识别为代码文件,其它文件都认为是资源文件.编译时可以通过参数 --php-exts 来设置后缀.

    资源文件被url请求到时会返回其内容,如果要在PHP代码中获取资源文件,可以使用BPC内置函数resource_get_contents($resource_file) 来获取.

  9. if ($iswin && class_exists("COM")) {

    BPC不支持windows,也没有class COM,所以new COM()不能出现在代码里.

5. 关于 bpc.conf

@see git commit

bpc.conf 定义了一个项目用到的扩展和static link时需要额外添加的link参数.

BPC 6.5支持的所有扩展列表如下:

(extensions php-std
            php-posix
            php-date
            php-pcre
            php-mbstring
            php-json
            php-fileinfo
            php-curl
            php-sysvsem
            php-zlib
            php-session
            php-filter
            php-pdo
            php-pdo_mysql
            php-pdo_sqlite
            php-openssl
            php-ctype
            php-pcntl
            php-tinycdb
            php-hash
            php-scws
            php-xml
            php-iconv
            php-gd
            php-zip
            php-event
            php-mysqli
            php-gmp
        )

tinyfilemanager.php 只用到了一部分,把没用到的扩展去掉,在static link时可以减小最终生成的二进制文件大小.

6. 关于 Makefile

@see git commit

tinyfilemanager:
    bpc -v                                          \
        --static                                    \
        --althttpd                                  \
        -c bpc.conf                                 \
        -d max_execution_time=30                    \
        -d upload_max_filesize=50M                  \
        -d post_max_size=60M                        \
        -d memory_limit=512M                        \
        -d log_errors=on                            \
        -d date.timezone=Asia/Shanghai              \
        -d sys_temp_dir=/tmp/tinyfilemanager/tmp    \
        -d session.gc_maxlifetime=604800            \
        -d session.cookie_httponly=1                \
        tinyfilemanager.php                         \
        translation.json
  • -v 显示编译过程,-v2 -v3 -v4 可以看到更多细节
  • --static 表示要 static link
  • --althttpd 表示要编译成 althttpd server, 详见PHP编译器BPC 6.2发布,直接编译php文件为web server!
  • -c 指定此次编译要用的bpc.conf,如果不指定,默认使用 /usr/local/etc/bpc.conf
  • -d 设定 php.ini 选项
  • tinyfilemanager.php translation.json 要编译的PHP源码文件和资源文件可以当作参数传入,也可以一行一个写在一个文件里(比如src.list)通过参数 --input-file src.list 传入.
clean:
    @rm -rf .bpc-build-* md5.map

编译过程中生成的scheme代码 .scm.bpc-build-PID 目录里, 编译过程中会将 php 文件名,函数名进行 md5, md5对照表保存在 md5.map 里.

run-docker-docker-build:
    mkdir -p /tmp/tinyfilemanager/tmp && mkdir -p /tmp/tinyfilemanager/public && chown -R www-data:www-data /tmp/tinyfilemanager && docker run -v `pwd`:/bpc-app -v /tmp/tinyfilemanager:/tmp/tinyfilemanager -p 7878:7878 -it heguangyu5/bpc-base ./tinyfilemanager -project-name bpc-workspace -port 7878 -home-page tinyfilemanager.php -root /tmp/tinyfilemanager/public -user www-data

注意前边编译时设定了php.ini 选项 sys_temp_dir=/tmp/tinyfilemanager/tmp, 这个目录用于保存临时文件和session文件.

之所以要设定一个tmp目录有两个原因:

  1. tinyfilemanager处理URL上传时,会生成一个临时文件,然后再把临时文件renamepublic 目录,当以docker方式运行时,从docker内部rename显然会失败.
  2. 登录时会生成session,session文件当没有指定 session.save_path 时会保存到 sys_tem_dir 中,这样我们在docker外部就能看到这些session文件.
./tinyfilemanager -project-name bpc-workspace -port 7878 -home-page tinyfilemanager.php -root /tmp/tinyfilemanager/public -user www-data
  • -project-name 编译时PHP源代码所在的目录名
  • -port althttpd 监听的端口
  • -home-page 默认首页,不指定时为 index.php
  • -root 运行根目录
  • -user 以哪个用户身份运行
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!