Problem/Motivation
Drupal\Core\Database\StatementPrefetch implements Drupal\Core\Database\StatementInterface.
StatementInterface::fetchObject method signature is empty.
/**
* Fetches the next row and returns it as an object.
*
* The object will be of the class specified by StatementInterface::setFetchMode()
* or stdClass if not specified.
*/
public function fetchObject();
StatementPrefetch implementation:
/**
* {@inheritdoc}
*/
public function fetchObject($class_name = NULL, $constructor_args = []) {
This is wrong and should be fixed.
The method fetchObject() is mimicking the PHP PDO implementation of the method fetchObject(). See: https://www.php.net/manual/en/pdostatement.fetchobject.php. That method has the 2 parameters (as optional).
Proposed resolution
Add the 2 optional parameters to the method Drupal\Core\Database\StatementPrefetch::fetchObject() and its interface Drupal\Core\Database\StatementInterface::fetchObject().
Remaining tasks
TBD
User interface changes
None
API changes
The method Drupal\Core\Database\StatementPrefetch::fetchObject() and its interface Drupal\Core\Database\StatementInterface::fetchObject() get 2 optional parameters. The parameters are:
1. The class name to be use for the created class as the return object;
2. The constructor arguments for the created class as the return object.
Data model changes
None
Release notes snippet
TBD
| Comment | File | Size | Author |
|---|---|---|---|
| #59 | interdiff_55_59.txt | 1.67 KB | anmolgoyal74 |
| #59 | 3128548-59.patch | 5.22 KB | anmolgoyal74 |
| #55 | interdiff_51_55.txt | 1.63 KB | anmolgoyal74 |
| #55 | 3128548-55-StatementInterface.patch | 5.23 KB | anmolgoyal74 |
Comments
Comment #2
johnwebdev commentedThis is also true for
Drupal\Core\Database\StatementEmptyDrupal\Core\Database\Statementalso implementsStatementInterface.Given how the interface looks to be designed from \PDOStatement I would assume that the interface should be updated with these two parameters and not the other way around.
Comment #3
johnwebdev commentedComment #4
daffie commentedThe patch looks good.
The change are inline with how the method
fetchObject()is used.The 2 parameters are also part of PDOStatement::fetchObject(). See: https://www.php.net/manual/en/pdostatement.fetchobject.php.
There are no other implementations of the method
fetchObject().For me it is RTBC.
Good find @johnwebdev!
Comment #5
xjmSo:The fact that the parameters are optional allows us to avoid a BC break. However, I'd be interested to know why these parameter were not part of the inteface before, and are not part of several child implementations of
fetchObject()nor of the other fetch methods. Maybe time to do a little git blame.Anyone up to look into that and decide why this is worth doing for these few cases in the API, and why the one case of it was added in the first place?
Also, where's the test coverage?
Finally, if we do go ahead with this issue, it should get a change record as well.
Thanks for your consideration!
Comment #6
daffie commentedComment #7
daffie commentedUpdated the IS and created a CR.
Comment #8
daffie commentedTest coverage was added in #2956556: class isn't set in FETCH_OBJECT when class_name isn't set. The only test coverage we are missing is with the second parameter.
Comment #9
johnwebdev commentedFrom 27 Dec, 2011 to 28 Jul, 2015 the
was commented. In #2521832: Uncomment StatementInterface methods that changed. The change record reveals some details:
https://www.drupal.org/node/2539326
So, the intent was to design the interface based on the PDOStatement object.
I cannot find any reason why the method signatures are missing in that issue, and there doesn't seem to be any issue references in the first commits by Crell back in the day, in the git commit history. (https://git.drupalcode.org/project/drupal/-/commit/67429ab19fd6bc0329583...) (Or maybe, I'm just missing them?).
I decided to look at the PHP documentation and I did find references for the two optional parameters already back in 2007 (https://github.com/php/doc-en/blob/3eb838771ffe6afdeb6185f3c214b9f6e670f...), so they are not any addition now in later years.
Comment #11
duneblI think we should add those parameters as well in
StatementWrapperlike the following.core/lib/Drupal/Core/Database/StatementWrapper.php
Comment #12
duneblComment #13
gnumatrix commentedWorked after applying patch in #3 and removing 'string' from the first line in #11
public function fetchObject($class_name = NULL, $constructor_args = []) {
Comment #14
joelpittet@DuneBL I don't understand why we'd want to do the conditional you mentioned in #11Edit, ignore me, that conditional is already there... rolling a patchComment #15
joelpittetHere's a patch from #11 and #13 thank you both it's working.
The interface now matches
\Drupal\Core\Database\StatementPrefetch::fetchObject#8/#9 cover where the tests are at. FakeRecord::class doesn't take constructor args, is it ok to add a constructor that class, or do we need to create a new one for the test?
Comment #16
daffie commentedI have no problems with adding a class variable to the class FakeRecord. Something like:
The test Drupal\KernelTests\Core\Database\FetchTest::testQueryFetchObjectClass() can then add a constructor argument and also add an assertion to make sure variable:
$result->variableis equal to the given constructor argument.Comment #17
maxmendez commentedI've tested patch #15 on Drupal 9.1.0 and fixed some problems created for ultimate_cron (https://www.drupal.org/project/ultimate_cron/issues/3184623#comment-1392...)
Comment #18
terminator727 commentedthanks guys! patch #15 works in my site after the drupal core update from 9.0.9 to 9.1.0 :) i hope future updates works better than this :/
Comment #19
yonailo commentedHello,
I am working on a patch for this.
Comment #20
mradcliffeI'm mentoring yonailo on this patch. I added the Novice tag and Europe2020 tags.
Comment #21
yonailo commentedHere there are 2 patches :
Comment #22
yonailo commentedComment #23
mradcliffeTest looks good, @yonailo! Thank you.
I removed the Needs tests tag because @yonailo added a test, but I'm going to change this back to Needs work for some minor code standard issues with the patch in #21:
I don't thinks matters too much, but we prefer the short name for variables.
We should use single quotes rather than double quotes because we don't need string interpolation.
Comment #24
mradcliffeI think using
assertSame(1, $result->fakeArg)would be preferable here.Comment #25
yonailo commentedHello,
Thank you very much for taking your time for reviewing.
I have applied your suggestions and re-uploaded the files. I hope this time is all good :)
Comment #26
yonailo commentedComment #28
daffie commentedThe added test looks good.
As all the other changes in the patch.
There is a CR added for the API addition.
The patch does what it should do according to the IS.
For me it is RTBC.
Re-uploading the patch to be committed. So that the testbot does not fail on the patch without the fix.
Comment #29
rajab natshahThank you :)
Patch #28 is working with Drupal 9.1.x too
Comment #30
catchI think we need to grep contrib and see if anyone is implementing StatementInterface directly, and open issues if so, because it'll still break that case - just not for calling code. Change seems fine to me under the 1-1 rule in 9.2.x.
Comment #31
daffie commentedAs requested by @catch I did search for the use of the method Statement::fetchObject() with the use of parameters in core and I found 2 modules who are using it.
The first is the Oracle database driver that is overriding the default implementation of the method in the same way as we are doing here, only with different parameter names. The project information indicates that nobody is using the Drupal 8 version of this module.
The second is the ultimate_cron module which is using it in the way we are implementing it. See: https://git.drupalcode.org/project/ultimate_cron/-/blob/8.x-2.x/src/Plug.... The Drupal 8 version of that module is used by 33.000 sites, according the projects info page.
I have contacted maintainers from both projects and I am waiting for their replies.
Comment #32
daffie commentedFixing this issue will fix a bug on the ultimate_cron module. See: #3184623: ArgumentCountError: Too few arguments to function LogEntry::__construct().
Comment #33
catchI think we should be adding these parameters commented out, with a note that implementing them will be required in Drupal 10 (and a follow-up to fix that) - this is what Symfony has done for adding optional parameters to interface methods. Oracle might not have a Drupal 9 release yet, but it shows that the current change could break contrib drivers.
Presumably this would still fix things for ultimate_cron since the implementations will accept the extra arguments. Also discussed with @xjm who confirmed that #5 didn't mean adding them to the interface now, but finding out why they weren't before.
Comment #34
sokru commentedPatch for comment on #33. I tested that after removal of the parameters on
StatementInterface::fetchObject(), ultimate_cron module issue is resolved.Comment #35
sokru commentedMissing patch. Couldn't provide an interdiff because ran into
interdiff: Whitespace damage detected in inputerror.Comment #36
daffie commentedThe testbot failed with:
Comment #37
sokru commentedResolving issues from #36.
Comment #38
daffie commentedThe points of @catch have been addressed.
Back to RTBC.
@sokru: Thanks.
Comment #39
alexpottAre we sure about [] as the default value... see PHP 8.0 code...
public function fetchObject(?string $class = "stdClass", ?array $ctorArgs = null) {}https://github.com/php/php-src/blob/1954e5975846b3952ce1d2d6506e6d7134c8...
We can do a bit better here. Something like:
Will make it skip the phpcs errors whilst still providing better IDE documentation.
Seems a shame to be removing the scalar type hint. Is that necessary?
Comment #40
anmolgoyal74 commentedMAde suggested changes in #39.
Also changed argument name from
$constructor_argsto$constructor_argumentsto keep it uniform as used inComment #41
anmolgoyal74 commentedForgot to update the argument name in
StatementInterface.phpComment #44
anmolgoyal74 commentedComment #45
daffie commentedCan we remove
?stringand?array.Can we change this to:
* @param array|null $constructor_arguments. The default value is NULL.Comment #46
anmolgoyal74 commentedAddressed #45.
Comment #47
alexpottHow come we're now adding the param to the interface? I thought the idea was to add them properly in D10? This is a breaking change. If we don't add them here we can add the correct typehints to the implementation and not remove the string typehint.
Also in PHP 7 the constructor arguments is typehinted with array as far as I can see.
I think we should be adding string
$class_name = NULL, array $constructor_arguments = NULLto all of the concrete implementations and not adding arguments to the interface until D10.Doing what Symfony does would make the interface look like:
which is what I think we want to do. Then anything in contrib can copy the concrete implementations in core and implement the new interface now and not have to change anything in D10.
Comment #48
daffie commentedComment #49
sokru commentedAddressing #47.
Comment #51
anmolgoyal74 commentedFixed the remaining failing test cases.
Comment #52
anmolgoyal74 commentedComment #53
daffie commentedFixed in the way that @alexpott wants.
Back to RTBC.
Comment #54
alexpottLets add the string and array typehints.
I think we can add the typehints.
There is no need for the string typehint to be removed here. In fact I would argue we can add the array typehint here.
Comment #55
anmolgoyal74 commentedAdded the type hints.
Comment #56
daffie commentedAll points of @alexpott have been addressed.
Back to RTBC.
Comment #57
mondrakeI do not think we should also add the nullability operator here. In other words, you will not explicitly pass a NULL here, only fallback to NULL if you won't pass one, so
would be fine IMHO.
BTW - In some way this goes in the opposite direction of trying to decouple the db API from PDO, which we are trying in other issues. Not a big deal really, but wanted to mention this.
Comment #58
alexpott?string $var = NULL- either the?or the= NULLis redundant. I think I would dostring $var = NULLas the question mark syntax is relatively recent. And is not used in the commented out text on the interface.Comment #59
anmolgoyal74 commentedComment #60
daffie commentedAnd the question marks have been removed.
Back to RTBC.
Comment #61
alexpottCommitted cc41d94 and pushed to 9.2.x. Thanks!
I tested the latest patch with Ultimate Cron and it fixes the module. Going to ping release managers about backporting.
I considered asking for $class_name to be defaulted to "stdClass" as per https://github.com/php/php-src/blob/1954e5975846b3952ce1d2d6506e6d7134c8...
But reading the code - https://github.com/php/php-src/blob/1954e5975846b3952ce1d2d6506e6d7134c8... - this param is optional and it does the same thing.
Comment #62
alexpottDiscussed with @catch - we agreed to backport this because it fixes a 9.1.x bug in a well used contrib module that can only be fixed in core.