Handling "Unsafe usage of new static()"

Last updated on
10 July 2024

This documentation needs review. See "Help improve this page" in the sidebar.

PHPStan warns you of using new static() in your code when instantiating the current class from within a factory method. This is an extremely common pattern in Drupal core and contributed projects for classes that implement ContainerInjectionInterface. This allows the classes to be extended and a proper instance instantiated.

Although this is not the best approach In terms of defensive programming, it is needed when writing classes that plan to be extended, or "open by default", as much of the Drupal codebase is expected to be.

Fix the problem

For custom code (code that is not hosted on drupal.org), it is recommended to fix the problem as described in the PHPStan documentation.

In your ContainerInjectionInterface::create method, do return new self() instead of return new static().

Set the class to final as well:

final class Foo
{
    ...
}

Remember, if your class cannot be extended, you limit the backward compatibility promises of your class.

Ignoring the issue

If you choose not to make the class final, add the error to the ignored errors in your configuration. This is what Drupal core has done:

https://git.drupalcode.org/project/drupal/-/blob/10.0.x/core/phpstan.neo...

parameters:
  ignoreErrors:
    # new static() is a best practice in Drupal, so we cannot fix that.
    - "#^Unsafe usage of new static#"

Also add the parameter reportUnmatchedIgnoredErrors: false to prevent phpstan from warning you that the ignored errors did not occur.

So ultimately your ./phpstan.neon file in your contrib project root might look like this:

includes:
  - phar://phpstan.phar/conf/bleedingEdge.neon

parameters:
  level: 1
  reportUnmatchedIgnoredErrors: false
  ignoreErrors:
    # new static() is a best practice in Drupal, so we cannot fix that.
    - "#^Unsafe usage of new static#"

Additional root causes

For situations unrelated to ContainerInjectionInterface where you receive the Unsafe usage of new static() warning, be aware that the PHPStan documentation does provide other alternatives for resolving the warning.

These include:

  • Making the constructor Final instead of the entire class.
  • Making the constructor abstract.
  • Enforcing the constructor signature through an interface.
  • Using the @phpstan-consistent-constructor tag.

Each solution has implications on how your class can be extended, and you should consider each one carefully depending on your particular use case. Making either the class or constructor final will enforce constructor consistency by preventing child classes from overriding the constructor. Marking the constructor Final will resolve the unsafe usage warning while allowing the rest of the class to be modified by child classes.

Using an abstract constructor or implementing the constructor in an interface will enforce consistency in child classes, resolving the warning. However, classes that extend the original class or implement the interface will need to define their own constructor. This approach allows for more flexibility in child classes at the cost of more boilerplate code. Note: if you run into a rare edge case where enforcing consistency through an interface conflicts with PHPStan generics, consider resolving the warning with any of the other solutions in the section.

Finally, using the PHPStan @phpstan-consistent-constructor tag in the class comment will both resolve the warning and enforce constructor consistency through static validation with PHPStan. This method relies on using a PHPStan comment instead of native PHP language constructs for consistency enforcement. Using this method also requires PHPStan 1.7 or higher. PHPStan 1.7 is available on the Drupal @core-dev Composer package as of version 10.0.0-alpha7 or higher.

Help improve this page

Page status: Needs review

You can: