I’m currently in middle of refactoring the API code that I used for my PHP extension. The initial version was very low effort. For text strings I have this in php.zig:
pub const String = c.zend_string;
And associated functions:
pub fn getStringContent(str: *const String) [:0]const u8 {
const s: [*]const u8 = @ptrCast(&str.*.val[0]);
const len = str.*.len;
return @ptrCast(s[0..len]);
}
pub fn compareStrings(s1: *const String, s2: *const String) bool {
const sc1 = getStringContent(s1);
const sc2 = getStringContent(s2);
return std.mem.eql(u8, sc1, sc2);
}
pub fn parseBool(s: *String) bool {
return c.zend_ini_parse_bool(s);
}
pub fn parseLong(s: *String) Long {
return c.zend_atol(&s.val[0], s.len);
}
Just a straight mapping to the C API basically. Extremely low-effort.
When the refactoring, I want to make the API more idiomatic Zig. More OOPish. To that end, I decided to wrap the PHP/Zend string type in a struct and attach various helper methods to it:
pub const String = struct {
impl: c.zend_string,
pub fn slice(self: *const @This()) [:0]const u8 {
const s: [*]const u8 = @ptrCast(&self.impl.val[0]);
const len = self.impl.len;
return @ptrCast(s[0..len]);
}
pub fn length(self: *const @This()) usize {
return self.impl.len;
}
// etc...
}
At the binary level, String contains exactly the data as c.zend_string. That allows me to freely between *c.zend_string and *String. Every times where this occurs though, it requires the use of @ptrCast(). That includes situations involving pointers to functions accepting strings as arguments.
It would be nice if the compiler would recognize that String and c.zend_string are the same thing and convert pointers automatically.