这两天在折腾 php。
php 作为世界世界上最好的编程语言,我不过是读研那会给导师做项目的时候撸过一年的代码,水平一般,只了解基本的语法,会进行简单的 php 后台开发,后来就没再用过了。
这次,是为了工作需要,在整理项目模块的时候,同事反映“之前的 php5.x 对 protobuf 的支持不是很好,解码出来的数据结构跟其它语言的对不上……”, 我当时的第一反应是:居然有这种事?至于具体是不是真的如此,我也懒得去重现。那是两年前的事,现在 php 都到 7.x ,protobuf 都到 3.5.1 了,时过境迁,IT 技术日新月异,或许问题早就解决了呢?
报着试一试的态度,我主动提出来调研下 “protobuf 在 php7.0 的用法”。
php
docker 里面现成的 php 镜像多的是,想要快的话,直接 run 一个 php 容器出来就可以,但是我想从零开始,体验下 Linux 配置 php 开发环境的流程,一步一个脚印,给人一种稳重的安全感。
所以,所以还是从一个裸体的 ubuntu 系统开始吧。
启动一个 ubuntu:16.04
容器:
1 | docker run -d -P -it ubuntu:16.04 bash |
先装 php 的,这里我选了 7.0 版本:
1 | root@aae76b5e0ca4:/var/www/html# apt-get update |
装个 nginx 服务器来驱动 php 脚本吧:
1 | root@aae76b5e0ca4:/var/www/html# apt-get intall nginx |
配置下 nginx,开启 php 解释器, 修改 /etc/nginx/sites-enabled/default
这部分如下:
1 | location ~ \.php$ { |
启动 PHP 和 nginx 服务:
1 | root@aae76b5e0ca4:/var/www/html# service php7.0-fpm start |
这样,php 运行环境就搭建好了,可以写个简单的 PHP 脚本 helloworld.php
来验证下,把它放在 nginx 默认的网站根目录 /var/www/html
中:
1 |
|
现在就是见证奇迹的时刻:
1 | root@aae76b5e0ca4:/var/www/html# curl 127.0.0.1/helloworld.php |
只需几步就配置好了 php 运行环境,真是 Excited!
protobuf
php7 要使用 Google protobuf 需要安装两个东西:protoc 编译器
和 php 扩展库
。
protoc 编译器
到 https://github.com/google/protobuf/releases
下载 linux 平台的 protoc ,把 bin
和 include
里面的文件拷贝到 /usr/local
的对应目录,这样 protoc 就算安装完成了:
1 | root@aae76b5e0ca4:/var/www/html# protoc --version |
写个 test.proto
:
1 | syntax = "proto3"; |
这个 proto 文件是什么意思,相信用过 proto 的人都知道。
编译 proto 文件:
1 | root@aae76b5e0ca4:/var/www/html# mkdir foo |
可以看到,protoc 编译出的结果里包含三个 php 文件(对应 proto 文件里面声明的三种结构)和 一个 GPBMetadata
文件夹(下面的 Test.php
描述了 test.proto
文件的元信息)。
php 扩展库
protobuf 的 php 扩展 官方 提供了 c 动态库 和 php 包两种形式。
php 包形式我折腾了半天也不会用,烦死了。还是安装 c 动态库形式的扩展吧:
1 | root@aae76b5e0ca4:/var/www/html# apt-get install php-pear php7.0-dev autoconf automake libtool make gcc |
提示安装成功,但还需要修改 php 的配置文件 /etc/php/7.0/fpm/php.ini
, 开启 protobuf 库支持:
1 | ;extension=php_xsl.dll |
重启 php7.0-fpm 服务。
Test
准备工作做好后,就可以正式进行 protobuf 解析了。
写个测试例子 test.php
:
1 |
|
访问 127.0.0.1/test.php
输出:
1 | root@aae76b5e0ca4:/var/www/html# curl localhost/test.php |
看上去,protobuf 的 serializeToString
和 mergeFromString
都没有问题。
一切看上去都那么自然,但,这里有个问题,不得不提下。测试发现:
在
test.php
中,必须引入 proto 文件中声明过的所有结构类型(即使你只用到了里面的一个类型),否则会报错。
比如,注释掉这行 require_once "foo/AddressBook.php";
(事实上,AddressBook 这个类也没有用到), nginx-error 日志输出:
1 | 2018/01/19 08:32:10 [error] 6880#6880: *39 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 127.0.0.1, server: _, request: "GET /test.php HTTP/1.1", upstream: "fastcgi://unix:/run/php/php7.0-fpm.sock:", host: "localhost" |
这个就很恶心了,“我只想要个苹果,你却给我一车梨?”。具体原因我还不知道,谁能帮我解释下吗?
总结
php 本身的确是门很简单易用的语言,加上我当年学到的东西还没有忘光,所以重新拾起来还是有故人重逢的感觉。
小试牛刀发现:php7.0 下 protobuf 扩展是可以用的,配置起来也不是很难。
然后,我在调研过程中,还是留了 2 个坑:
- php 扩展包的另外一种安装方式
- 拔一发而动全身:必须引入 proto 文件中声明过的所有结构类型
有收获,也有遗憾,这次 “php7.0+protobuf” 的填坑之旅就这样告一段落吧,具体的业务代码,就让 phper 们去撸吧, 我还是喜欢用 go。