找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2605

积分

0

好友

347

主题
发表于 7 小时前 | 查看: 5| 回复: 0

新版本完整复刻 PHP 的所有语法,强化了引用、指向(Indirect)类型方面的实现,新增了对 Closure 匿名函数、异常处理支持。可以使用 C++ 语言代替 PHP 实现各类功能,使得 C++ 成为 PHP 的一种静态版方言。

PHPX 可用于以下场景:

  1. 商业软件项目,不希望分发 PHP 源代码,可基于 PHPX 使用 C++ 编写逻辑,直接分发二进制的 .so.dll 扩展
  2. 高性能运算场景,在性能敏感的密集计算场景使用 C++ 编写
  3. 拓展 PHP 语言边界PHP 标准库或者外部扩展没有提供的能力,可使用 PHPX C++ 实现

2.1 新版本主要变更

新增类型别名

提供了小写的基础类型名称,在 PHP 中通常使用小写的类型名称,例如 arraystringobject

array arr1({1, 2, 3, 4});
object obj1("ArrayObject");
string str1("hello world");

备注:需要在 C++.cc.cpp 源代码中加入 using namespace php 才能使用 phpxAPI

新增 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] = $value
  • offsetUnset(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() 方法

等价于 PHPempty()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);

相比 PHPZendAPIC++ 异常处理更加安全,在捕获异常时会使用 C++ RAII 机制自动销毁栈上对象。与 PHP 代码的异常处理行为完全一致了。

Enum Class 支持

2.1 版本可对 PHPEnum 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 使用更加简单,无需注册,可在任意位置、任意时间创建使用。

PHPBox 依然是 resource 资源类型,实际存储的是 C++ 对象指针,可视为是一种 Opaque ObjectBox 受到 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"));



上一篇:Kuikly跨端框架AI工程化实践:搜狗输入法如何提升编码效率
下一篇:手游逆向实战复盘:Unity IL2CPP Dump与自定义TCP协议完整解析
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-29 11:58 , Processed in 0.517302 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表