-
Notifications
You must be signed in to change notification settings - Fork 67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Anonymous class FromZval
derive macro
#78
Conversation
5711ec3
to
6d5166e
Compare
Also started work on the inverse |
I really like this, but I think there are some interesting semantics to explore. I've been thinking about this for a few days and trying to get all my thoughts down, so apologies of this is somewhat unstructured. My first through was that if I was to see the rust code that this feature would example, for example something like this: #[derive(FromZVal)]
struct Args {
name: String
}
#[php_function]
fn printer(args: Args) {
println!("{}", args.name)
} I might intuit that the following PHP code to work: printer(['name' => 'Simon']) Rather than this: class Args {
public function __construct(public string $name) {}
}
printer(new Args) However both would be useful, and should work given PHP's own casting rules. But the catch is its part of PHP's casting rules. To write a PHP version than can take either I have to explicitly cast: function printer(object|array $args) {
$args = (object)$args
echo "{$args->name}\n";
} or function printer(object|array $args) {
$args = (array)$args
echo "{$args['name']}\n";
} I cannot rely on implicit conversion here.
|
PHP Type | Rust Type |
---|---|
array | HashMap |
callable | Closure |
bool | bool |
float | f32 |
int | u32/u64 |
string | String |
iterable | ? |
object | ZendObject |
mixed | ZVal |
Then nullable types turn into option types: ?string
is represented as Option<String>
And union types are represented as enums: string|object
would be represented as:
#[derive(FromZVal)]
enum CustomType {
String(String),
Object(ZendObject)
}
FromZVal
should round trip by default
The nice part of keeping it simple like this is that all types can predictably round trip. Any type that can have FromZVal
could also have a valid and expected IntoZVal
(maybe we don't even need an explicit IntoZVal
).
Represent casts with a different API, make them explicit
I like this functionality in this PR, but I'd suggest making is an explicit function call with a district trait/derive name. For example:
// Define this so function can take either array or object.
// Represents array|object
#[derive(FromZVal)]
enum Args {
Object(ZendObject),
Array(HashMap),
}
// Tentative name, bikeshedable
#[derive(CastFromZVal)]
struct Props {
name: String
}
#[php_function]
fn printer(args: Args) {
// Unpack either the object or the array into `Props`
let props = php::cast(&args);
println!("{}", props.name)
}
Current FromZVal
attaches behaviour to the wrong layer
One other note I think is worth considering with the current design is that this FromZVal
implementation currently first unpacks the ZVal
to an object (ZendObject
) and then to into the struct. This means if I just have an object, I can't unpack it without turning it into a ZVal
first.
My other PR introduces FromZendObject
trait and this is probably better organized under that trait. Within the current design, I'd advocate its more correct for from FromZVal
to delegate to FromZendObject
rather than having it all in one. Then you could get ZVal
to String
(via __toString
) for free as well.
declare_strict
We might also want to consider strict vs not strict conversion rules in FromZVal
. We might want to support both strict and non-strict versions of FromZVal
:
let zval = 2.3f.into_zval().unwrap();
let a: Result<u64> = zval.extract_loosly(); // Holds Ok(2)
let b: Result<u64> = zval.extract(); // Holds Err(...)
let c: Result<String> = php::cast(&zval); // Holds "2.3"
On that note, this is something I just learned, that __toString
allows classes to be implicitly cast to strings. The following works unless its strict mode:
class Args {
public function __construct(public string $name) {}
public function __toString() { return $this->name; }
}
function printer(string $msg) {
echo "$msg\n";
}
printer('Hello World');
// Not a type error
printer(new Args('Hello World'))
So to reiterate the above:
let obj: ZVal = ...;
let a: Result<String> = obj.extract_loosly(); // Calls `__toString`
let b: Result<String> = obj.extract(); // Err(...)
let c: Result<String> = php::cast(&obj); // Calls `__toString`
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Merge it in. The finer details of how casting and coersion work are better dealt with this in tree rather than out of tree.
I like it.
8864849
to
eef726e
Compare
eef726e
to
4140757
Compare
Progress towards #75
Added the
#[derive(FromZval)]
macro. Can be used on enums and structs.from an objects properties.
Both support generics and lifetimes so
&'a str
is valid. Next step in aseperate PR is the corresponding
IntoZval
macro, which converts theclass into a
Zval
.