PHPX 2.1 新版本详解:完整复刻 PHP 语法 + C++ 扩展开发实战指南

新版本完整复刻了 PHP 的所有语法,强化了引用、指向(Indirect)类型方面的实现,新增了对 Closure 匿名函数、异常处理支持。可以使用 C++语言代替 PHP 实现各类功能,使得 C++成为 PHP 的一种静态版方言。
2.1 PHPX 可用于以下场景
- 商业软件项目,不希望分发 PHP 源代码,可基于 PHPX 使用 C++编写逻辑,直接分发二进制的.so 或.dll 扩展
- 高性能运算场景,在性能敏感的密集计算场景使用 C++编写
- 拓展 PHP 语言边界,PHP 标准库或者外部扩展没有提供的能力,可使用 PHPX C++ 实现
新版本主要变更
新增类型别名
提供了小写的基础类型名称,在 PHP 中通常使用小写的类型名称,例如 array、string、object:
array arr1({1, 2, 3, 4});
object obj1("ArrayObject");
string str1("hello world");
备注:需要在 C++的.cc 或.cpp 源代码中加入 using namespace php 才能使用 phpx 的 API
新增 same() 函数,用于绝对相等比较
// 等价于 $a === $b same(a, b); // 等价于 $a == $b equals(a, b);
新增 compare 函数,等价于太空船操作符
// 等价于 $a <=> $b compare(a, b);
强化 Variant 基类
现在可以直接对 var 类型类型使用数组访问、属性访问、方法调用语法了。
全新的 offset 操作方法
支持字符串、数组、对象。key 可以是字符串或数字类型。
offsetGet(key):等价于$var[$key]offsetSet(key, value):等价于$var[$key] = $valueoffsetUnset(key):等价于unset($var[$key])offsetExists(key):等价于isset($var[$key])
数组访问:item() 函数
可直接对 var 使用数组下标访问语法,不需要创建 array 类型的中间变量
// rs 是函数调用的返回值,可能是任意类型
var rs = test();
// 等价于 $rs["key"]
var_dump(rs.item("key"));
// 旧版本写法,需要先转为数组类型
array tmp_array = rs;
var_dump(tmp_array["key"]);
除了读取操作,还可以直接写入数据。
// 等价于 $rs["key"] = 1000
rs.item("key") = 1000;
字符串和对象也可以使用 item()方法。
// 对象必须实现 __offsetGet 和 __offsetSet 魔术方法
obj.item("key1");
// 字符串只能使用数字下标,读取或修改其中一个字节
var str("hello");
var_dump(str.item(1)); // 返回单字母 "e"
str.item(1) = "_"; // 将字符串修改为 h_llo
属性访问:attr() 函数
可直接对 var 使用属性访问语法,不需要创建 object 类型的中间变量
// 等价于 $rs->property
var_dump(rs.attr("property"));
// 旧版本写法,需要先转为对象类型
object tmp_obj = rs;
var_dump(tmp_obj.get("property"));
除了读取操作,还可以直接写入数据。
// 等价于 $rs->property = 1000
rs.attr("property") = array({"test", "value"});
方法调用:call() 函数
可直接对 var 使用对象方法调用语法,不需要创建 object 类型的中间变量
// 等价于 $rs->foo($arg1, $arg2, $arg3, ...)
var_dump(rs.call("foo", {arg1, arg2, arg3, ...}));
其他函数
newItem()新增元素,可操作数组或对象itemRef()获取数组元素,并作为引用attrRef()获取对象属性,并作为引用
新增 empty() 和 exists() 方法
等价于 PHP 的empty()和isset(),支持链式操作。
empty 判断是否为空
empty(v, {{ArrayDimFetch, "nested"}, {PropertyFetch, "prop"}, {ArrayDimFetch, "key2"}});
等价于
empty($v['nested']->prop['key2']);
exists 判断数组元素或者对象属性是否存在且不为 null
exists(v, {{ArrayDimFetch, "nested"}, {PropertyFetch, "prop"}, {ArrayDimFetch, "key2"}});
等价于
isset($v['nested']->prop['key2']);
异常处理
2.1 版本可以直接使用 C++的 try/catch 语法来处理 PHP 的异常。新版本还增加了throwException()和catchException()两个函数用于抛出、捕获异常。
bool done = false;
try {
auto e = newObject("RuntimeException", {"phpx exception test", 1999});
throwException(e);
} catch (zend_object *ex) {
auto e = catchException();
ASSERT_TRUE(e.getClassName().equals("RuntimeException"));
auto msg = e.call("getMessage");
ASSERT_TRUE(msg.isString());
ASSERT_TRUE(str_contains(msg, "phpx exception test").isTrue());
done = true;
}
ASSERT_TRUE(done);
相比 PHP 的 ZendAPI,C++异常处理更加安全,在捕获异常时会使用 C++ RAII 机制自动销毁栈上对象。与 PHP 代码的异常处理行为完全一致了。
Enum Class 支持
2.1 版本可对 PHP 的 Enum Class 进行读取操作。
请注意只能在 C++代码中操作 Enum Class,暂时无法创建
// 使用 PHP 代码创建了一个 enum class
eval("enum Suit{case Hearts; case Diamonds; case Clubs; case Spades;}");
auto ce = getClassEntry("Suit");
auto case1 = getEnumCase(ce, "Spades");
auto name = getEnumCaseName(case1);
ASSERT_STREQ(name.data(), "Spades");
Closure 匿名函数支持
新版本支持了 Closure,可以编写一个 C++函数作为 Closure 对象传递给 PHP 代码,作为回调函数使用。
ClosureFn fn = [](INTERNAL_FUNCTION_PARAMETERS, Object &this_, Args &vars_) -> Variant {
auto arg_0 = getCallArg(0);
EXPECT_STREQ(arg_0.toCString(), "java");
auto arg_1 = getCallArg(1);
EXPECT_STREQ(arg_1.toCString(), "php");
auto arg_0_2 = getCallArg(0, "golang");
EXPECT_STREQ(arg_0_2.toCString(), "java");
return1000;
};
// 这里的 fn 就是一个 Closure 对象,可以传递至 PHP 代码中,也可以直接在 C++ 中调用
var fn = newClosure(fn, {"hello", "swoole"});
var rs = fn({"java", "php"});
ASSERT_EQ(rs.toInt(), 1000, this_);
newClosure 接收 3 个参数,第一个参数是要执行的 C++函数,类型必须是 ClosureFn,第二个参数是 use 变量,第三个参数是需要绑定的对象,相当于 PHP 匿名函数自动绑定的$this 对象。
仅支持 8.2 或更高版本
引用强化
现在可以直接对引用进行所有操作,不再需要解除引用了。
Array arr; auto ref = &arr; array_push(ref, "hello");
2.1 版本可以直接对引用进行各种操作,直接作用与被引用的对象了。旧版本需要使用*符号解除引用后才可以使用。
// 对数组 arr 新增元素
ref.append("world");
ref.item(0); // 返回 "hello"
新增 Box 类型
在 2.1 之前的版本,如果要传递一个 C++对象指针,传递到 PHP 层使用,只能预先注册 resource 资源类型。使用非常繁琐。在新版本中新增了一个全新的 Box 实现。相比 resource 使用更加简单,无需注册,可在任意位置、任意时间创建使用。
在 PHP 层 Box 依然是 resource 资源类型,实际存储的是 C++对象指针,可视为是一种 Opaque Object。Box 受到 PHP GC 管理,当引用计数为 0 后,该 C++对象会自动被 delete,释放内存,无需手动管理。
class VectorBox : public Box {
public:
std::vector<bool> vec;
VectorBox(size_t size, bool init) {
vec.resize(size, init);
}
};
只需要编写一个 C++类,继承 Box 基类即可。被管理的 C++对象可以作为 Box 类的成员变量。
var box(new VectorBox(size, init));
必须使用 new 操作符创建 Box 对象,将 Box 对象指针作为构造方法参数,构造 var 变量。这个变量就可以作为 PHP 的变量,在 PHP 层面使用,可作为局部变量或全局变量、对象属性或者数组元素。
Box 对象指针,在 new 创建后,将由 GC 自动管理,不能手动 delete,否则会出现 double-free
var get = global("_GET");
get.offsetSet("cpp-box", box);
从 PHP 层传递的变量,可使用 toBox()函数转为 C++对象指针。
VectorBox *vecbox = box.toBox<VectorBox>(); bool value = vecbox->vec[100];
全新的 Facade API
2.1 版本对 PHP 内置扩展提供的函数和类,自动生成了 Facade API,在 C++代码中可以像 PHP 一样,调用这些内置函数和类。
新版本强化了参数默认值、引用类型、变长参数。使用方式与 PHP 代码是完全一致的。
内置函数
Array arr; auto ref = arr.toReference(); // 追加 3 个元素,当前长度为 3 array_push(ref, "php", "java", "go"); // 追加 5 个元素,当前长度为 7 array_push(ref, "c++", "rust", "erlang", "node.js"); // 追加 5 个元素,当前长度为 12 array_push(ref, "python", "ruby", "lua", "perl", "vue");
底层使用了 C++ 的变长参数模版实现,可以支持任意长度的参数输入。
template <typename... Args>
Variant array_push(const Reference &array, const Args &...values) {
return call(LITERAL_STRING[1873], {&array, values...});
}
类和对象
Redis redis{};
// 实际参数数量是 1, 其他参数使用默认参数填充
redis.connect("127.0.0.1");
ASSERT_TRUE(redis.set("key1", "value1"));
ASSERT_TRUE(redis.set("key2", "value2"));
ASSERT_TRUE(redis.set("key3", "value3"));
ASSERT_TRUE(redis.exists("key1"));
ASSERT_TRUE(redis.exists("key2"));
ASSERT_TRUE(redis.del("key2", "key3"));
ASSERT_TRUE(redis.exists("key1"));
ASSERT_FALSE(redis.exists("key2"));
ASSERT_FALSE(redis.exists("key3"));
以上关于PHPX 2.1 新版本详解:完整复刻 PHP 语法 + C++ 扩展开发实战指南的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » PHPX 2.1 新版本详解:完整复刻 PHP 语法 + C++ 扩展开发实战指南
微信
支付宝