-
-
Notifications
You must be signed in to change notification settings - Fork 565
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
Executor fetches data twice: offsetExists on ArrayAccess #759
Comments
What about: $property = $source[$fieldName] ?? null; |
No, that gives the same result. Apparently it's just syntactic sugar and PHP calls offsetExists() by itself. Besides, PHP docs for offsetGet says So, is there anything wrong with ignoring the check? Here's what I propose: if (is_array($source)) {
if (isset($source[$fieldName])) {
$property = $source[$fieldName];
}
} elseif ($source instanceof ArrayAccess) {
$property = $source[$fieldName];
} elseif (is_object($source)) {
if (property_exists($source, $fieldName)) {
$property = $source->{$fieldName};
}
} Rationale:
I tested this code and it works as expected, avoiding the double call. It's a bit hard to send a test case to reproduce it since it involves so much stuff, but I'm testing it like this:
|
This is a proposal to fix webonyx#759
Ah, it seems my PR breaks some tests. Let me know if you have a better idea. |
Nah, it was a typo. It seems to pass everything. |
The crucial point is what assumptions we make about The PHP documentation does not specify what should happen on nonexisting offsets. There is a code example which returns At the moment, the default resolver simply defaults to if (is_array($source) || $source instanceof ArrayAccess) {
$property = $source[$fieldName];
} elseif (is_object($source)) {
$property = $source->{$fieldName};
} |
Yes, the PHP docs are not good at all about this. But they state clearly that offsetExists calls offsetGet, which in my opinion is a very very very very dumb spec. I'd violate that spec in any implementation of mine with a clear conscience 🤪 But regarding your suggestion, there's another problem in how PHP handles plain array access. If you access an undefined index on a plain array, you get a non-catchable (with try/catch) notice. In an ArrayAccess that throws a regular exception, a try/catch would work. So I don't think If you want to be safe, my suggestion is: if (is_array($objectValue)) {
if (isset($objectValue[$fieldName])) {
$property = $objectValue[$fieldName];
}
} elseif ($objectValue instanceof ArrayAccess) {
try {
$property = $objectValue[$fieldName];
} catch (\Exception $e) {
// pass
}
} |
This is correct and expected behavior for |
I just checked, actually that is not the case. See https://3v4l.org/OLqvr |
I'm closing this. I won't argue anymore and hopefully this will be at least fixed in Lighthouse, but you're expecting good implementations from 3rd party code -- that have been shown not to exist by at least three people -- and then you suggest a non-trivial change to fix the libraries in the user code. I understand the point of expecting decent code and not the crazy Laravel implementation, but then users end up in this "not my fault" business, and a bug that is certainly affecting a lot of people, with a report that is now a month old is still open. |
I'm finding a few duplicated queries on my logs and debugging. I'm using lighthouse-php but the stack trace shows they seem related to graphql-php, so I'm posting them here. I think they have different causes so I'll open multiple issues.
First chapter: I'm using Laravel with an accessor that hits the DB. The query is called twice.
More or less this:
I fetch this on graphql, which is like this:
So when I query I see two selects, caused by calling
Model::offsetExists
andModel::offsetGet
. Here are the relevant stack traces for both calls:And then:
Now, offsetExists on laravel is really dumb:
but then again this call on defaultFieldResolver assumes a lot of an ArrayAccessor:
I'm not sure what I can propose to fix this.
property_exists()
does not work. I think this might be only properly solvable in Laravel. A temporary workaround was implemented a smarter "offsetExists()" that replicatesgetAttribute()
checks on the model but doesn't actually call the data fetching.The text was updated successfully, but these errors were encountered: