[教學] 用 PHP-CPP 寫 PHP extension (1)

作者: Neisseria (Neisseria)   2016-12-07 11:03:21
如果 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 的使用
作者: xdraculax (首席怪叔叔)   2016-12-07 13:00:00
純推不下
作者: shadowjohn (轉角遇到愛)   2016-12-07 13:04:00
離上一次寫extension是6年前作手寫輸入法...
作者: locklose (允)   2016-12-07 16:30:00

Links booklink

Contact Us: admin [ a t ] ucptt.com