Hello,
(I'm writing to the internals list because I don't think that at this
point, FFI usage is wide-spread enough to get an opinion on other venues)
maybe I'm just stupid, but I think there has been a slight oversight in the
API design for the FFI interface.
My problem is with functions that return a pointer to a struct as an out
parameter.
So in C, it would look like this:
Error* err;
func(&err);
if (err != NULL){
do_error_handling();
}
If I translate this to PHP FFI, I would do
$err = $ffi->new("Error*");
$ffi->func(FFI::addr($err));
if I var_dump
$err, I do see a public {0} property be set to NULL
or to
actual error data.
My issue is though: How do I check whether err* as been assgined to?
isset($err)
$err[0] != null
-> is always true, regardless of whether func as assigned an error or not
isset($err->{0})
isset($err[0])
-> Cannot use object of type FFI\CData as array
$err->{0} != NULL;
-> FFI\Exception: NULL
pointer dereference
$err[0];
-> segfault
I'm sure I must be doing something wrong, but I don't know what. Also, the
documentation could do with an example on how to achieve the desired result
and, TBH, I think some of the various attempts produce a somewhat
unexpected output.
Any comments?
Philip
var_dump(isset($r->{0}));
-> Error: Cannot use object of type FFI\CData as array
$a = '{0}';
var_dump(isset($r->$a));
-> Error: Cannot use object of type FFI\CData as array
var_dump(isset($r[0]));
var_dump($r[0]);
Hi!
isset($err)
$err[0] != null
-> is always true, regardless of whether func as assigned an error or not$err[0];
-> segfault
This is a bit confusing - if $err[0] segfaults, how it can be != null?
Does this behave differently in different contexts? Anyway, it shouldn't
probably segfault in any case, so this looks like a bug.
--
Stas Malyshev
smalyshev@gmail.com
On Mon, Sep 9, 2019 at 10:13 AM Stanislav Malyshev smalyshev@gmail.com
wrote:
isset($err)
$err[0] != null
-> is always true, regardless of whether func as assigned an error or not$err[0];
-> segfaultThis is a bit confusing - if $err[0] segfaults, how it can be != null?
Does this behave differently in different contexts? Anyway, it shouldn't
probably segfault in any case, so this looks like a bug.
I think there are a few disconnected issues. One is that checking something
for being NULL
is a bit tricky with the given API and the other is that the
API around FFI\CData is a bit inconsistent and tricky to use.
Given
$err = $ffi->new("Error*");
what the library does is assume that $err is now a pointer to an array
(which is what C does I guess), but yet, there seems to be a bit of an
issue with actually accessing that array (if it's set to NULL) - and also,
don't use var_dump()
to inspect it - that's deceptive because there's a
debug_info handler that knows ore than the actual field accessors (which
too makes this more complicated than needed).
So. Let's assume that in
$err = $ffi->new("Error*");
$ffi->func(FFI::addr($err));
func() sets that argument to NULL, then
$f = $err[0];
var_dump(gettype($f));
returns "object" - so the zeroth index exists (hence the isset being true).
However there's also
$f = $err->{0};
which normally would throw "Attempt to read undefined field '0' of C
struct/union" but because $err is seen as an array, accessing a property
named 0 is possible, but then a second null check will happen via
https://github.com/php/php-src/blob/09e9c4f3c11a875c67334578621084f156155307/ext/ffi/ffi.c#L1103-L1107
and
you get "NULL pointer dereference" instead.
I believe this difference between [0] and {0} shouldn't exist.
And even aside of that, I see no way in which user-code can ever check any
reference for NULL
because any access of a Cdata property or index that's
happen to be set to NULL
will either segfault, throw or fail a comparison
in PHP because Cdata instances, according to PHP's type conversion rules,
always convert to something that compares truthy when put in boolean
context.
The inconsistency between [] and {} aside, I believe there should be an
FFI::isNull() static method or a Cdata::isNull() instance method that allow
user-code to check things for being NULL. Or the throw in the code I liked
to above is removed, but that would leave the inconsistency between [] and
{}
Or maybe I'm just overlooking something and there's a simple way to check
something for being NULL, but if there is, it IMHO needs better docs.
Philip
https://github.com/php/php-src/pull/4691
that adds an FFI::isNull() method that returns true if the passed CData
instance points to NULL.
If you agree that something like this is indeed needed, I will gladly
update the PR and add unit tests.
Philip
On Mon, Sep 9, 2019 at 8:03 AM Philip Hofstetter
wrote:
> Hello,
>
> (I'm writing to the internals list because I don't think that at this
> point, FFI usage is wide-spread enough to get an opinion on other venues)
>
> maybe I'm just stupid, but I think there has been a slight oversight in
> the API design for the FFI interface.
>
> My problem is with functions that return a pointer to a struct as an out
> parameter.
>
> So in C, it would look like this:
>
> Error* err;
> func(&err);
>
> if (err != NULL){
> do_error_handling();
> }
>
> If I translate this to PHP FFI, I would do
>
> $err = $ffi->new("Error*");
> $ffi->func(FFI::addr($err));
>
> if I `var_dump` $err, I do see a public {0} property be set to `NULL` or to
> actual error data.
>
> My issue is though: How do I check whether err* as been assgined to?
>
> isset($err)
> $err[0] != null
> -> is always true, regardless of whether func as assigned an error or not
>
> isset($err->{0})
> isset($err[0])
> -> Cannot use object of type FFI\CData as array
>
> $err->{0} != NULL;
> -> FFI\Exception: `NULL` pointer dereference
>
> $err[0];
> -> segfault
>
> I'm sure I must be doing something wrong, but I don't know what. Also, the
> documentation could do with an example on how to achieve the desired result
> and, TBH, I think some of the various attempts produce a somewhat
> unexpected output.
>
> Any comments?
>
> Philip
>
>
> var_dump(isset($r->{0}));
> -> Error: Cannot use object of type FFI\CData as array
>
> $a = '{0}';
> var_dump(isset($r->$a));
> -> Error: Cannot use object of type FFI\CData as array
>
>
> var_dump(isset($r[0]));
> var_dump($r[0]);
>
>
>
--
Sensational AG
Giesshübelstrasse 62c, Postfach 1966, 8021 Zürich
Tel. +41 43 544 09 60, Mobile +41 79 341 01 99
info@sensational.ch, http://www.sensational.ch
(I'm writing to the internals list because I don't think that at this
point, FFI usage is wide-spread enough to get an opinion on other venues)maybe I'm just stupid, but I think there has been a slight oversight in the
API design for the FFI interface.My problem is with functions that return a pointer to a struct as an out
parameter.So in C, it would look like this:
Error* err;
func(&err);if (err != NULL){
do_error_handling();
}If I translate this to PHP FFI, I would do
$err = $ffi->new("Error*");
$ffi->func(FFI::addr($err));if I
var_dump
$err, I do see a public {0} property be set toNULL
or to
actual error data.My issue is though: How do I check whether err* as been assgined to?
You could check FFI::addr($err)[0]:
$err = $ffi->new("Error*");
$perr = FFI::addr($err);
$ffi->func($perr);
var_dump(is_null($perr[0]));
FFI::free($perr);
Not sure if it's supposed to require this indirection, though.
--
Christoph M. Becker