昨天看到 peachpie 的 issue 里有一个 can not work woth tinyfilemanager , 就点进去看了下这个 tinyfilemanager.
Tiny File Manager 是一个 PHP 实现的单文件版的文件管理器, 4300 行代码, 实现了文件/目录管理,上传本地/网络文件,文件搜索,多语言/主题切换,压缩/解压 zip 文件等功能.
在线 Demo 在这里: https://tinyfilemanager.github.io/demo/
于是就想着拿 BPC 来编译一下,继而发现这是一个很好的 BPC 编译示例:
接下来就分享下编译过程,如果你能成功编译这个项目,那么大多数的其它 PHP 项目都能自己编译了.
编译第 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.
要编译 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
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
@see git commit
FM_Config::save()
tinyfilemanager.php 的原本逻辑是将设置参数保存在 $CONFIG
变量中, 当参数变化时,直接修改 tinyfilemanager.php
文件自身来完成保存.
BPC 编译后,只有一个二进制文件,显然这个逻辑需要调整,我们增加了一个 const CONFIG_FILE = '../config.json';
将设置参数保存到 ../config.json
文件中.
然后在一开始读取这个文件来初始化 $CONFIG
.
__DIR__.'/config.php'
tinyfilemanager.php 支持使用 config.php
来覆盖默认选项,通过is_readable($config_file)
来判断是否 include
进来.
BPC 可以通过调用内置函数 include_silent($config_file)
来实现同样的目的.
我们知道 PHP include 一个文件时,如果这个文件不存在,会报一个 warning,然后继续执行,BPC 的这个include_silent()
函数如果遇到文件不存在,不会报 warning,然后继续执行.
if (defined('__BPC__')) { /* BPC code*/ } else { /* PHP code */ }
这个结构在 BPC 编译时只会保留下 BPC code
, if/else
和 PHP code
直接丢掉了,类似于 C 语言的#ifdef #else #endif
.
当你需要在 PHP 环境下和在 BPC 编译时执行不同代码时,就可以用它.
$_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(32));
我们不想 link openssl 扩展,因为整个 tinyfilemanager.php 里只有这里用到.
[]
=> array()
BPC 自身不支持[]
这样的数组写法,如果不想改代码,可以通过 phptobpc 进行转换.
$use_curl = true;
BPC 的copy
不支持 copy url,所以从 URL 上传文件功能要用 curl 实现.
FM_Zipper_Tar
BPC 尚未实现 class PharData
, 所以不支持压缩/解压tar
文件.
这里使用 if (defined('__BPC__'))
在 BPC 编译时消掉$tar = new FM_Zipper_Tar();
代码是因为在 BPC 编译时,如果一个 class 从未声明过,那么它不能出现在代码里.
translation.json
BPC 支持将资源文件编译进二进制,默认后缀是.php,.inc,.phtml
的文件被识别为代码文件,其它文件都认为是资源文件.编译时可以通过参数 --php-exts
来设置后缀.
资源文件被 url 请求到时会返回其内容,如果要在 PHP 代码中获取资源文件,可以使用 BPC 内置函数resource_get_contents($resource_file)
来获取.
if ($iswin && class_exists("COM")) {
BPC 不支持 windows,也没有class COM
,所以new COM()
不能出现在代码里.
@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 时可以减小最终生成的二进制文件大小.
@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 目录有两个原因:
rename
到 public
目录,当以 docker 方式运行时,从 docker 内部 rename 显然会失败.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
以哪个用户身份运行