如果 PHP 程式的速行速度无法满足需求
将其改写成 PHP extension 或许可以处理这个议题
传统上,要写 PHP extension 要用 C,而且要懂 Zend C API
说实在,还蛮费心力的
幸好有工程师开发了替代的方案,让后人不需再和 Zend C API 奋战
经 google 可知大致上有两种方案:
1. Zephir:一个新的 PHP-like 编译语言,写完后程式码可编译成 PHP extension
是由 Phalcon 团队开发的,Phalcon 本身也有使用这个语言
2. PHP-CPP:一个 C++ 函式库,加入特有的 PHP 相关物件
由于 PHP-CPP 的写法,和原来的 C++ 略有不同,笔者将其视为一种 DSL
使用这个方案的好处在易于桥接外部 C/C++ 函式库
预计会写两篇,这是第一篇
如果想直接研究程式码,可到这个 repo
https://github.com/cwchentw/doubler-php-extension-demo
接下来,笔者会提示要点
首先,要实作 doubler 这个 toy library
这个函式库是用 Rust 撰写,输出成 C shared library
如果不想用 Rust,也可用其他语言,能输出 C/C++ shared library 即可
目前这个 library 已完成
但是,Rust 不会自动生成 header,要自己撰写
撰写 header 时要注意,我们会输出到 C++ 中
要用 extern "C" 避免 name mangling
#ifndef _DOUBLER_H_
#define _DOUBLER_H_
#ifdef __cplusplus
extern "C" {
#endif
int double_int(int);
double double_float(double);
char* double_str(char*);
void str_free(char*);
#ifdef __cplusplus
}
#endif
#endif // _DOUBLER_H_
接着,要到 PHP-CPP 官网下载 EmptyExtension 这个专案骨架,再自行修改
目前仍要手动改 Makefile,还没有自动化的流程
这部分请板友参考小弟的 repo 自行修改
接着,实作 C++ 程式码的部分
要注意的是,PHP-CPP 的撰写方式,和一般的 C++ 程式码略有不同
我们看一下实际的范例
#include <phpcpp.h>
#include <string>
#include "doubler.h"
using std::string;
class Doubler : public Php::Base
{
public:
Doubler() = default;
virtual ~Doubler() = default;
static Php::Value int_number(Php::Parameters&);
static Php::Value float_number(Php::Parameters&);
static Php::Value str(Php::Parameters&);
};
Php::Value Doubler::int_number(Php::Parameters& params)
{
return double_int((int32_t)params[0]);
}
Php::Value Doubler::float_number(Php::Parameters& params)
{
return double_float((double)params[0]);
}
Php::Value Doubler::str(Php::Parameters& params)
{
char* s = double_str((char*)params[0].rawValue());
string output = string(s);
str_free(s);
return output;
}
由以上程式码,可发现撰写 PHP-CPP 专案时,使用了特别的 Php::Parameters 表示
PHP 参数,回传值也是特别的 Php::Value。另外,非基本型别要另外释放内存。
其实我们这个 library 没有物件,都是 static method,用类别只是当成命名空间
PHP-CPP 也支援非物件的函式和 PHP 的命名空间,可自行参考官网的说明
最后,要输出该物件到 PHP。以下的 get_module 函式是每个 PHP-CPP 专案都有的
extern "C" {
PHPCPP_EXPORT void *get_module()
{
static Php::Extension extension("doubler", "1.0");
// Put your library here
Php::Class<Doubler> doubler("Doubler");
doubler.method<&Doubler::int_number>("int_number", {
Php::ByVal("x", Php::Type::Numeric)
});
doubler.method<&Doubler::float_number>("float_number", {
Php::ByVal("x", Php::Type::Float)
});
doubler.method<&Doubler::str>("str", {
Php::ByVal("x", Php::Type::String)
});
extension.add(std::move(doubler));
return extension;
}
}
撰写完相关程式码后,再编译即可得到 PHP extension。
可以写一个简单的 PHP 程式呼叫此 extension
<?php
// main.php
echo Doubler::int_number(2), "\n";
echo Doubler::float_number(1.3), "\n";
echo Doubler::str("Hi"), "\n";
如果只是要测试,不想安装 extension,可从命令列呼叫此 extension
$ php -dextension=`pwd`/doubler.so main.php
这个范例到这里算是告一段落了
接下来,我们会用另一个范例继续展示 PHP-CPP 的使用