Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
I didn't see this one filed yet, so filing it myself. Close if it's a dupe:
Running Core 8.4.2 with PHP 7.2, going to admin/content/comment gives the following fatal error:
Fatal error: Declaration of Drupal\comment\Plugin\Menu\LocalTask\UnapprovedComments::getTitle() must be compatible with Drupal\Core\Menu\LocalTaskDefault::getTitle(?Symfony\Component\HttpFoundation\Request $request = NULL) in /app/web/core/modules/comment/src/Plugin/Menu/LocalTask/UnapprovedComments.php on line 14
The same error does not appear on 7.1. The only contrib modules installed are Google Analytics and Token.
This seems related to the PHP 7.2 type widening support. I'm guessing the $request parameter is no longer safe to omit entirely from the method declaration, even if it's going to be unused.
Comment | File | Size | Author |
---|---|---|---|
#12 | 2927173-12.patch | 24.88 KB | alexpott |
#12 | 10-12-interdiff.txt | 454 bytes | alexpott |
#10 | 2927173-10-interdiff.txt | 2.94 KB | cburschka |
#10 | 2927173-10.patch | 24.83 KB | cburschka |
#7 | 2927173-7.patch | 27.77 KB | cburschka |
Comments
Comment #2
cburschkaWow, that's an odd breakage.
I tested locally with RC6 and got this:
And with 7.1:
So this is a warning for me locally, rather than a fatal error, but on both 7.1 and 7.2.
(Edit: I didn't add a type hint on the arg here, but doing so doesn't change the result.)
Comment #3
cburschkaHowever, I can confirm the fatal error in Drupal on PHP72, and no warning in PHP71, unlike with the sample code above.
Comment #4
cburschkaThe difference is the interface.
This is a warning on PHP71 and PHP72:
This is fine on PHP71 and fatal on PHP72:
I'm not sure, but I get the feeling this is a PHP bug - even if it's intended, this introduces a BC-breaking fatal error rather than a deprecation.
Comment #5
cburschkaI went ahead and wrote a PHP bug report (https://bugs.php.net/bug.php?id=75590), but I'm not sure the result won't be "this code was always incorrect, and should never have worked in the first place". :P
Comment #6
cburschkaBy the way, I think I have an idea why PHP wants to allow extending the argument signature with optional arguments, but not reducing it. Each change on their own is transitive in fulfilling all superclasses contracts, but both together allow us to violate that transitivity, because subclasses can remove and then re-add differently-typed arguments to the signature.
If
A::f(X $x = NULL)
is extended byB::f()
which is extended byC::f(Y $x = NULL)
, then C::f(?Y) is incompatible with A::f(?X)) even though each method is compatible with its immediate parent. To safely allow this, PHP would have to search the entire inheritance chain for type mismatches, and developers would have to be told: "Yes, you can extend B and override B::f, and you can add an optional argument to it, but it has to be of type X because some superclass of B already used that."Edit:
This is obviously not ideal, but I suspect our only good option (even if PHP7.2 removes the fatal error) is to go through core and ensure strict compatibility in all method declarations, even those that are silent in PHP7.1.
In 8.5.x, IntelliJ currently recognizes 35 violations, which fall into these categories:
-
\Drupal\Core\Database\Statement::fetchAll(?, ?, ?)
double-implements\PDOStatement::fetchAll(?, ?, ?Array)
and\Drupal\Core\Database\StatementInterface::fetchAll(?, ?, ?)
. Fixed by adding array hint both in the interface and the class.- 22 subclasses of
\Drupal\views\Tests\ViewTestBase
override::setup()
, to which ViewTestBase adds an optional flag$import_test_views
. Quick fix: Add the argument to all subclasses, but the flag should really be refactored to a separateimportTestViews()
method.-
\Drupal\forum\Form\Overview::buildForm(array, FormStateInterface)
overridesDrupal\taxonomy\Form\OverviewTerms::buildForm(array, FormStateInterface, ?VocabularyInterface)
. Probably no way around taking the VocabularyInterface argument there too, and then simply overwriting it with the configured forum vocabulary.- 9 classes extend
\Drupal\Core\Menu\LocalTaskDefault
orLocalActionDefault
orContextualLinkDefault
, overriding::getTitle(?Request)
with::getTitle
. (Includes the original crash.) Fixed by adding the missing argument.- 2 subclasses extend
\Drupal\migrate\Plugin\migrate\source\SourcePluginBase::count(?)
which implements\Countable::count()
and adds a$refresh
argument. Same as ViewTestBase: This shouldn't be a flag, especially because this method overloads PHP'scount()
. Just use a separate::refresh()
method.-
\Drupal\layout_builder\Form\UpdateBlockForm::buildForm(array, FormStateInterface, EntityInterface, ?, ?, ?)
overrides\Drupal\layout_builder\Form\ConfigureBlockFormBase::buildForm(array, FormStateInterface, EntityInterface, ?, ?, ?, ?array)
. The extra$configuration
argument should be added to the subclass signature.Comment #7
cburschkaThis patch fixes all the inspection errors (based on 8.5.x) and should therefore fix all the fatals, without doing any additional refactoring.
Actual refactoring is probably out of scope, but the following are a bit iffy:
- The
::setup($import_test_views = TRUE)
. The only case where it's set to FALSE when a test needs to set up other stuff (content types, taxonomy) before creating the views. I suspect that better ways to do this is to split the setup function:-- Either move the views import to an extra createTestViews() method and call that from the child's setup after doing the pre-views setup.
-- Or move the pre-views-import steps to an extra preSetup() method and call that from the parent's setup before creating views.
- The
::count($refresh = FALSE)
. Use a::refreshCount()
or something.- The LocalActionDefault, LocalTaskDefault and ContextualLinkDefault ::getTitle(Request $request). The argument was added by #2120235: Regression: routing / tabs / actions / contextual links lack way to attach replacement arguments to UI strings, but I don't see any class in core which actually uses it (it was briefly used while the patch evolved, but seemingly not in the final product), and the respective interfaces don't define it at all. Are we sure this isn't detritus?
Comment #8
cburschkaComment #10
cburschkaProbably jumped the gun on the PDO stuff. It wasn't actually a fatal error before, and it seems that (unlike my local environment) the CI is using a version of PDOStatement where the $constructor_argument isn't type-hinted. Reverted.
Comment #12
alexpottThe FALSE in FieldApiDataTest was never doing anything lol. PHP 7.2 is certainly going to make our code better and more consistent!
Comment #14
alexpottThis needs to be fixed in 8.5.x first...
Comment #15
xjmThis sounds critical.
Comment #16
cburschkaYeah, especially now that 7.2.0 is out. Aside from the comment moderation queue, enabling config_translation seemingly breaks almost all config forms because of the local-task thing...
Comment #17
alexpottAs per #14 we need this in 8.5.x first - doesn't apply to 8.4.x so that's why testbot set back to needs work...
Comment #18
plach@catch, @effulgentsia, @larowlan, @xjm and I discussed this issue today. We agreed it should be considered a duplicate of #2923015: [PHP 7.2] Incompatible method declarations.
Comment #19
remyyyyyTo fix the issue in drupal 8.4.x, edit the file core/modules/comment/src/Plugin/Menu/LocalTask/UnapprovedComments.php
on line 10 add:
use Symfony\Component\HttpFoundation\Request;
near line 55 replace :
public function getTitle() {
with this :
public function getTitle(Request $request = NULL) {
Comment #20
soumyapsadanandan CreditAttribution: soumyapsadanandan as a volunteer commented#19 fixed the issue for me.