V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
heguangyu5
V2EX  ›  PHP

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

  •  1
     
  •   heguangyu5 · 2023-10-20 15:44:38 +08:00 · 884 次点击
    这是一个创建于 419 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

    在线 Demo 在这里: https://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 上传时,会生成一个临时文件,然后再把临时文件rename public 目录,当以 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 以哪个用户身份运行
    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2297 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 15:45 · PVG 23:45 · LAX 07:45 · JFK 10:45
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.