Problem/Motivation

After some code changes in #3423310: Change !isset to ??= assignment operator: multi-line case. using ??= operator the MR faced obstructions in #3423310-#11 from phpstan: 4 = 2x2 errors: 2 files,

--------------------------------------------------------------------------------- 
  Line   core/modules/views/src/Plugin/views/display/PathPluginBase.php
 --------------------------------------------------------------------------------- 
         Ignored error pattern #^Variable \$access_plugin in isset\(\) always
         exists and is not nullable\.$# in path                                   
         /builds/issue/drupal-3423310/core/modules/views/src/Plugin/views/display/PathPluginBase.php
         was not matched in reported errors.                                      
  199    Variable $access_plugin on left side of ??= always exists and is not     
         nullable.                                                                   
 ----------------------------------------------------------------------------------
 ----------------------------------------------------------------------------------
  Line   core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php
 ---------------------------------------------------------------------------------- 
         Ignored error pattern #^Variable \$message in isset\(\) always exists       
         and is not nullable\.$# in path                                             
         /builds/issue/drupal-3423310/core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php
         was not matched in reported errors.                                         
  309    Variable $message on left side of ??= always exists and is not              
         nullable.                                                                 
 ----------------------------------------------------------------------------------
 [ERROR] Found 4 errors

The two code changes are,

--- a/core/modules/views/src/Plugin/views/display/PathPluginBase.php
+++ b/core/modules/views/src/Plugin/views/display/PathPluginBase.php
@@ -195,10 +195,8 @@ protected function getRoute($view_id, $display_id) {
 
     // Add access check parameters to the route.
     $access_plugin = $this->getPlugin('access');
-    if (!isset($access_plugin)) {
-      // @todo Do we want to support a default plugin in getPlugin itself?
-      $access_plugin = Views::pluginManager('access')->createInstance('none');
-    }
+    // @todo Do we want to support a default plugin in getPlugin itself?
+    $access_plugin ??= Views::pluginManager('access')->createInstance('none');
     $access_plugin->alterRouteDefinition($route);
--- a/core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php
+++ b/core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php
@@ -306,9 +306,7 @@ protected function assertPreviewAJAX(int $row_count): void {
    * @internal
    */
   protected function assertClass(NodeElement $element, string $class, string $message = ''): void {
-    if (!isset($message)) {
-      $message = "Class .$class found.";
-    }
+    $message ??= "Class .$class found.";
     $this->assertStringContainsString($class, $element->getAttribute('class'), $message);
   }

Steps to reproduce

Proposed resolution

The changed codes seem regular. Though what happens and the message are clear: the problem is not the change, but the concerned variables, $access_plugin and $message. With the changes the two files come in the scope of phpstan during the MR process. And phpstand thinks that the two variables are not nullable, since some thing is wrong in their origin. They should be explicitly declared as nullable.

- No use of ??= in this issue, only phpstan problems (see #5 and #6).

- For $message, it's an argument of the function. In the same file core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php in

   * @param string $message
   *   (optional) A verbose message to output.
   *
   * @internal
   */
  protected function assertClass(NodeElement $element, string $class, string $message = ''): void {

change,
* @param string $message
to
* @param string|null $message
Or,
\Drupal\Tests\views_ui\FunctionalJavascript\PreviewTest::assertClass
should match
\Drupal\Tests\system\Functional\Pager\PagerTest::assertClass
to do that change
protected function assertClass(NodeElement $element, string $class, string $message = ''): void {
to
protected function assertClass(NodeElement $element, string $class, string $message = NULL): void {
in core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php. (see #6 and #14)
We use the second one.

- For $access_plugin, it isn't an argument. Since it comes from $this->getPlugin('access') and for the class,
abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInterface, DependentPluginInterface {
the right file is core/modules/views/src/Plugin/views/display/DisplayPluginInterface.php. Here, in,

  /**
   * Get the instance of a plugin, for example style or row.
   *
   * @param string $type
   *   The type of the plugin.
   *
   * @return \Drupal\views\Plugin\views\ViewsPluginInterface
   */
  public function getPlugin($type);

change,
* @return \Drupal\views\Plugin\views\ViewsPluginInterface
to
* @return \Drupal\views\Plugin\views\ViewsPluginInterface|null
(see #5 and #15)

- After #20 the core/phpstan-baseline.neon should be update: remove the 5 items.

- Following the messages,

in empty\(\) always exists and is not falsy
in isset\(\) always exists and is not nullable

since always exists, make some changes empty($A) to !$A and isset($A) to $A !== NULL.
(Not always, to keep the ??= transformations still possible.)

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

CommentFileSizeAuthor
#26 3424107-nr-bot.txt90 bytesneeds-review-queue-bot

Issue fork drupal-3424107

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

Chris64 created an issue. See original summary.

chris64’s picture

Issue summary: View changes
chris64’s picture

Issue summary: View changes
dpi’s picture

Status: Active » Closed (won't fix)

Variable $access_plugin on left side of ??= always exists and is not nullable.

Ignored error pattern #^Variable \$access_plugin in isset\(\) always exists and is not nullable\.$# in path

If either error is true, then the line should not be converted to ??=. Nor should it be left as isset().

If the change is correct, then the code leading to it should be changed. With better typehints, for example.

I'd suggest changing the code in the other issue. Just make sure you are justifying the change clearly in documenting a issue/MR comment.

See next comments

dpi’s picture

For PathPluginBase, \Drupal\views\Plugin\views\display\DisplayPluginInterface::getPlugin the docs say it can never return null.

However I can see there a return void;, so lets create an issue just for this, which changes the documentation, and explicitly return NULL;

This issue could be used as a template: #3391355: \Drupal\Core\Config\StorageInterface::read is typehinted as possibly returning bool, but never returns true

In this case I wouldnt recommend changing the relevant lines in the ??= issue.

dpi’s picture

For PreviewTest.php, lets keep the change in the ??= issue.

However, lets change \Drupal\Tests\views_ui\FunctionalJavascript\PreviewTest::assertClass so the method signature (and docs) match \Drupal\Tests\system\Functional\Pager\PagerTest::assertClass.

chris64’s picture

Issue summary: View changes
chris64’s picture

@dpi, thank you for your answer, We can discuss.

For PreviewTest.php, lets keep the change in the ??= issue.

Well, this case block the MR, because of the phpstan error. I face this before.

For PathPluginBase, \Drupal\views\Plugin\views\display\DisplayPluginInterface::getPlugin the docs say it can never return null.

So, is not nullable, so,

    if (!isset($access_plugin)) {
      // @todo Do we want to support a default plugin in getPlugin itself?
      $access_plugin = Views::pluginManager('access')->createInstance('none');
    }

is inconsistent: never go in the block. Or is nullable, and this code is okay (...and could be changed to ??=).

chris64’s picture

@dpi, I think the problem is not ??=. The problem, as phpstan tells us, is not nullable + isset.

chris64’s picture

I push the work, since done. It can be refused.

chris64’s picture

An error exists.

chris64’s picture

Okay @dpi, after having a look your requests about null are clear for me.

chris64’s picture

About #6, on one side, in core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php,

  /**
   * Asserts that an element has a given class.
   *
   * @param \Behat\Mink\Element\NodeElement $element
   *   The element to test.
   * @param string $class
   *   The class to assert.
   * @param string $message
   *   (optional) A verbose message to output.
   *
   * @internal
   */
  protected function assertClass(NodeElement $element, string $class, string $message = ''): void {
    if (!isset($message)) {
      $message = "Class .$class found.";
    }
    $this->assertStringContainsString($class, $element->getAttribute('class'), $message);

On the other side, in core/modules/system/tests/src/Functional/Pager/PagerTest.php,

  /**
   * Asserts that an element has a given class.
   *
   * @param \Behat\Mink\Element\NodeElement $element
   *   The element to test.
   * @param string $class
   *   The class to assert.
   * @param string $message
   *   (optional) A verbose message to output.
   *
   * @internal
   */
  protected function assertClass(NodeElement $element, string $class, string $message = NULL): void {
    if (!isset($message)) {
      $message = "Class .$class found.";
    }
    $this->assertTrue($element->hasClass($class), $message);
  }

To make the two match change,
, string $message = ''): void {
to
, string $message = NULL): void {

chris64’s picture

@dpi, for #6 and $access_plugin,

However I can see there a return void;, so lets create an issue just for this, which changes the documentation, and explicitly return NULL;

I believe you have in head,

    // Return now if no options have been loaded.
    if (empty($options) || !isset($options['type'])) {
      return;
    }

in getPlugin in core/modules/views/src/Plugin/views/display/DisplayPluginBase.php. And if I follow #3391355: \Drupal\Core\Config\StorageInterface::read is typehinted as possibly returning bool, but never returns true I think the change needed should be,
* @return \Drupal\views\Plugin\views\ViewsPluginInterface
to
* @return \Drupal\views\Plugin\views\ViewsPluginInterface|null
in,

  /**
   * Get the instance of a plugin, for example style or row.
   *
   * @param string $type
   *   The type of the plugin.
   *
   * @return \Drupal\views\Plugin\views\ViewsPluginInterface
   */
  public function getPlugin($type);

in core/modules/views/src/Plugin/views/display/DisplayPluginInterface.php since,
abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInterface, DependentPluginInterface {

chris64’s picture

Finally the issue perimeter could be restricted to the nullable problems,

so lets create an issue just for this

using this issue for these problems.

chris64’s picture

Title: Use null coalescing assignment operator ??=: phpstan obstruction case. » ??=/isset : 2 phpstan error always exists and is not nullable for PathPluginBase::getPlugin $access_plugin, PreviewTest::assertClass $message.
Issue summary: View changes
chris64’s picture

Issue summary: View changes
chris64’s picture

Title: ??=/isset : 2 phpstan error always exists and is not nullable for PathPluginBase::getPlugin $access_plugin, PreviewTest::assertClass $message. » ??=/isset: 2 phpstan errors: always exists and is not nullable for PathPluginBase::getPlugin $access_plugin, PreviewTest::assertClass $message.
chris64’s picture

Still some phpstan errors,

 --------------------------------------------------------------------------------------
  Line   core/modules/views/src/Plugin/views/display/DisplayPluginBase.php                               
--------------------------------------------------------------------------------------
         Ignored error pattern #^Variable \$pager in isset\(\) always exists                             
         and is not nullable\.$# in path                                                                 
         /builds/issue/drupal-3424107/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php  
         was not matched in reported errors.                                                             
         Ignored error pattern #^Variable \$style in empty\(\) always exists                             
         and is not falsy\.$# in path                                                                    
         /builds/issue/drupal-3424107/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php  
         was not matched in reported errors.                                                             
------------------------------------------------------------------------------------- 
------------------------------------------------------------------------------------- 
  Line   core/modules/views/src/Plugin/views/display/PathPluginBase.php                               
------------------------------------------------------------------------------------- 
         Ignored error pattern #^Variable \$access_plugin in isset\(\) always                         
         exists and is not nullable\.$# in path                                                       
         /builds/issue/drupal-3424107/core/modules/views/src/Plugin/views/display/PathPluginBase.php  
         was not matched in reported errors.                                                          
------------------------------------------------------------------------------------- 
------------------------------------------------------------------------------------- 
  Line   core/modules/views/src/Plugin/views/style/StylePluginBase.php                               
------------------------------------------------------------------------------------- 
         Ignored error pattern #^Variable \$plugin in empty\(\) always exists                        
         and is not falsy\.$# in path                                                                
         /builds/issue/drupal-3424107/core/modules/views/src/Plugin/views/style/StylePluginBase.php  
         was not matched in reported errors.                                                         
------------------------------------------------------------------------------------- 
------------------------------------------------------------------------------------- 
  Line   core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php                               
------------------------------------------------------------------------------------- 
         Ignored error pattern #^Variable \$message in isset\(\) always exists                              
         and is not nullable\.$# in path                                                                    
         /builds/issue/drupal-3424107/core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php  
         was not matched in reported errors.                                                                
------------------------------------------------------------------------------------- 
 [ERROR] Found 5 errors
chris64’s picture

Status: Closed (won't fix) » Needs work
chris64’s picture

Issue summary: View changes

- The phpstan errors have no line number. The corresponding items should be removed from core/phpstan-baseline.neon file.
- Following the messages,

in empty\(\) always exists and is not falsy
in isset\(\) always exists and is not nullable

since always exists, make some changes empty($A) to !$A and isset($A) to $A !== NULL. (Not always, to keep the ??= transformations still possible.)

chris64’s picture

Status: Needs work » Needs review

MR mergeable. NR.

quietone’s picture

Component: base system » other
Issue tags: +Needs title update

The title is also the commit message, so this should be easier to understand when searching history in the future.

chris64’s picture

Title: ??=/isset: 2 phpstan errors: always exists and is not nullable for PathPluginBase::getPlugin $access_plugin, PreviewTest::assertClass $message. » Change !isset to ??= assignment operator: phpstan errors "always exists and is not nullable" case.

Title changed. Less detailed but more clear.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new90 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

smustgrave’s picture

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.