From 16552b02a3d1d66d224bd5888408017a5d93bf58 Mon Sep 17 00:00:00 2001
From: catch <catch@35733.no-reply.drupal.org>
Date: Thu, 11 Jan 2024 21:45:16 +0000
Subject: [PATCH] Issue #3328457 by xjm, dimitriskr: Replace most substr($a,
 $i) where $i is negative with str_ends_with()

---
 composer/Plugin/Scaffold/ManageGitIgnore.php              | 2 +-
 core/includes/errors.inc                                  | 2 +-
 .../PhpStorage/MTimeProtectedFastFileStorage.php          | 2 +-
 core/lib/Drupal/Core/Command/DbImportCommand.php          | 2 +-
 core/lib/Drupal/Core/Config/Entity/Query/QueryFactory.php | 2 +-
 core/lib/Drupal/Core/Database/Connection.php              | 2 +-
 .../Drupal/Core/DependencyInjection/YamlFileLoader.php    | 2 +-
 .../Core/Entity/EntityLastInstalledSchemaRepository.php   | 2 +-
 core/lib/Drupal/Core/Entity/Query/Sql/QueryAggregate.php  | 2 +-
 .../Discovery/RecursiveExtensionFilterIterator.php        | 4 ++--
 core/lib/Drupal/Core/Extension/ExtensionVersion.php       | 2 +-
 core/lib/Drupal/Core/File/FileSystem.php                  | 4 ++--
 core/lib/Drupal/Core/FileTransfer/FileTransfer.php        | 2 +-
 core/lib/Drupal/Core/Render/Element/ImageButton.php       | 2 +-
 core/lib/Drupal/Core/Routing/UrlGenerator.php             | 4 ++--
 core/lib/Drupal/Core/Test/TestDiscovery.php               | 8 ++++----
 .../lib/Drupal/Core/TypedData/DataReferenceDefinition.php | 2 +-
 core/modules/file/src/Upload/FileUploadHandler.php        | 2 +-
 core/modules/filter/src/Plugin/Filter/FilterHtml.php      | 4 ++--
 core/modules/jsonapi/src/Routing/Routes.php               | 2 +-
 .../migrate/src/Plugin/migrate/process/FileCopy.php       | 2 +-
 .../tests/src/Kernel/MigrateDrupalTestBase.php            | 2 +-
 .../tests/src/Functional/MigrateUpgradeTestBase.php       | 2 +-
 .../Composer/Template/ComposerProjectTemplatesTest.php    | 2 +-
 .../Drupal/FunctionalTests/Update/UpdatePathTestBase.php  | 2 +-
 .../PhpStorage/MTimeProtectedFileStorageBase.php          | 2 +-
 26 files changed, 33 insertions(+), 33 deletions(-)

diff --git a/composer/Plugin/Scaffold/ManageGitIgnore.php b/composer/Plugin/Scaffold/ManageGitIgnore.php
index 078cb3357862..6f2eb09fb009 100644
--- a/composer/Plugin/Scaffold/ManageGitIgnore.php
+++ b/composer/Plugin/Scaffold/ManageGitIgnore.php
@@ -115,7 +115,7 @@ protected function addToGitIgnore($dir, array $entries) {
     // Appending to existing .gitignore files.
     if (file_exists($git_ignore_path)) {
       $contents = file_get_contents($git_ignore_path);
-      if (!empty($contents) && substr($contents, -1) != "\n") {
+      if (!empty($contents) && !str_ends_with($contents, "\n")) {
         $contents .= "\n";
       }
     }
diff --git a/core/includes/errors.inc b/core/includes/errors.inc
index 9820f9fe2ac6..6ec930dd3460 100644
--- a/core/includes/errors.inc
+++ b/core/includes/errors.inc
@@ -67,7 +67,7 @@ function _drupal_error_handler_real($error_level, $message, $filename, $line) {
     // As __toString() methods must not throw exceptions (recoverable errors)
     // in PHP, we allow them to trigger a fatal error by emitting a user error
     // using trigger_error().
-    $to_string = $error_level == E_USER_ERROR && substr($caller['function'], -strlen('__toString()')) == '__toString()';
+    $to_string = $error_level == E_USER_ERROR && str_ends_with($caller['function'], '__toString()');
     _drupal_log_error([
       '%type' => isset($types[$error_level]) ? $severity_msg : 'Unknown error',
       // The standard PHP error handler considers that the error messages
diff --git a/core/lib/Drupal/Component/PhpStorage/MTimeProtectedFastFileStorage.php b/core/lib/Drupal/Component/PhpStorage/MTimeProtectedFastFileStorage.php
index be287f99bfc8..415079376fbf 100644
--- a/core/lib/Drupal/Component/PhpStorage/MTimeProtectedFastFileStorage.php
+++ b/core/lib/Drupal/Component/PhpStorage/MTimeProtectedFastFileStorage.php
@@ -200,7 +200,7 @@ protected function getContainingDirectoryFullPath($name) {
     // file. Thus, when switching between MTimeProtectedFastFileStorage and
     // FileStorage, the subdirectory or the file cannot be created in case the
     // other file type exists already.
-    if (substr($name, -4) === '.php') {
+    if (str_ends_with($name, '.php')) {
       $name = substr($name, 0, -4);
     }
     return $this->directory . '/' . str_replace('/', '#', $name);
diff --git a/core/lib/Drupal/Core/Command/DbImportCommand.php b/core/lib/Drupal/Core/Command/DbImportCommand.php
index cf461df7346f..6da8b5739d1d 100644
--- a/core/lib/Drupal/Core/Command/DbImportCommand.php
+++ b/core/lib/Drupal/Core/Command/DbImportCommand.php
@@ -56,7 +56,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
   protected function runScript(Connection $connection, $script) {
     $old_key = Database::setActiveConnection($connection->getKey());
 
-    if (substr($script, -3) == '.gz') {
+    if (str_ends_with($script, '.gz')) {
       $script = "compress.zlib://$script";
     }
     try {
diff --git a/core/lib/Drupal/Core/Config/Entity/Query/QueryFactory.php b/core/lib/Drupal/Core/Config/Entity/Query/QueryFactory.php
index 3322287c6e28..064e5b4e9f57 100644
--- a/core/lib/Drupal/Core/Config/Entity/Query/QueryFactory.php
+++ b/core/lib/Drupal/Core/Config/Entity/Query/QueryFactory.php
@@ -162,7 +162,7 @@ protected function deleteConfigKeyStore(ConfigEntityTypeInterface $entity_type,
    *   you cannot do fast lookups against this.
    */
   protected function getKeys(Config $config, $key, $get_method, ConfigEntityTypeInterface $entity_type) {
-    if (substr($key, -1) == '*') {
+    if (str_ends_with($key, '*')) {
       throw new InvalidLookupKeyException(strtr('%entity_type lookup key %key ends with a wildcard this can not be used as a lookup', ['%entity_type' => $entity_type->id(), '%key' => $key]));
     }
     $parts = explode('.*', $key);
diff --git a/core/lib/Drupal/Core/Database/Connection.php b/core/lib/Drupal/Core/Database/Connection.php
index cdaf0d19ddd5..9f98b00a2785 100644
--- a/core/lib/Drupal/Core/Database/Connection.php
+++ b/core/lib/Drupal/Core/Database/Connection.php
@@ -886,7 +886,7 @@ protected function expandArguments(&$query, &$args) {
     // If the placeholder indicated the value to use is an array,  we need to
     // expand it out into a comma-delimited set of placeholders.
     foreach ($args as $key => $data) {
-      $is_bracket_placeholder = substr($key, -2) === '[]';
+      $is_bracket_placeholder = str_ends_with($key, '[]');
       $is_array_data = is_array($data);
       if ($is_bracket_placeholder && !$is_array_data) {
         throw new \InvalidArgumentException('Placeholders with a trailing [] can only be expanded with an array of values.');
diff --git a/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php b/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
index f42b6115f8ae..d165ace48a80 100644
--- a/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
+++ b/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
@@ -484,7 +484,7 @@ private function resolveServices($value)
                 $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
             }
 
-            if ('=' === substr($value, -1)) {
+            if (str_ends_with($value, '=')) {
                 $value = substr($value, 0, -1);
             }
 
diff --git a/core/lib/Drupal/Core/Entity/EntityLastInstalledSchemaRepository.php b/core/lib/Drupal/Core/Entity/EntityLastInstalledSchemaRepository.php
index 1b3c096126ce..9f75e2d9aa9f 100644
--- a/core/lib/Drupal/Core/Entity/EntityLastInstalledSchemaRepository.php
+++ b/core/lib/Drupal/Core/Entity/EntityLastInstalledSchemaRepository.php
@@ -69,7 +69,7 @@ public function getLastInstalledDefinitions() {
 
     // Filter out field storage definitions.
     $filtered_keys = array_filter(array_keys($all_definitions), function ($key) {
-        return substr($key, -12) === '.entity_type';
+        return str_ends_with($key, '.entity_type');
     });
     $entity_type_definitions = array_intersect_key($all_definitions, array_flip($filtered_keys));
 
diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/QueryAggregate.php b/core/lib/Drupal/Core/Entity/Query/Sql/QueryAggregate.php
index 3e3cb1e62dc7..fd257ab69390 100644
--- a/core/lib/Drupal/Core/Entity/Query/Sql/QueryAggregate.php
+++ b/core/lib/Drupal/Core/Entity/Query/Sql/QueryAggregate.php
@@ -154,7 +154,7 @@ protected function finish() {
   public function createSqlAlias($field, $sql_field) {
     $alias = str_replace('.', '_', $sql_field);
     // If the alias contains of field_*_value remove the _value at the end.
-    if (str_starts_with($alias, 'field_') && substr($field, -6) !== '_value' && substr($alias, -6) === '_value') {
+    if (str_starts_with($alias, 'field_') && !str_ends_with($field, '_value') && str_ends_with($alias, '_value')) {
       $alias = substr($alias, 0, -6);
     }
     return $alias;
diff --git a/core/lib/Drupal/Core/Extension/Discovery/RecursiveExtensionFilterIterator.php b/core/lib/Drupal/Core/Extension/Discovery/RecursiveExtensionFilterIterator.php
index af8d2d464bfd..f4964c94c38c 100644
--- a/core/lib/Drupal/Core/Extension/Discovery/RecursiveExtensionFilterIterator.php
+++ b/core/lib/Drupal/Core/Extension/Discovery/RecursiveExtensionFilterIterator.php
@@ -156,14 +156,14 @@ public function accept() {
       // config module to be overridden/replaced in a profile/site directory
       // (whereas it must be located directly in a modules directory).
       if ($name == 'config') {
-        return substr($this->current()->getPathname(), -14) == 'modules/config';
+        return str_ends_with($this->current()->getPathname(), 'modules/config');
       }
       // Accept the directory unless the folder is skipped.
       return !in_array($name, $this->skippedFolders, TRUE);
     }
     else {
       // Only accept extension info files.
-      return substr($name, -9) == '.info.yml';
+      return str_ends_with($name, '.info.yml');
     }
   }
 
diff --git a/core/lib/Drupal/Core/Extension/ExtensionVersion.php b/core/lib/Drupal/Core/Extension/ExtensionVersion.php
index a5c064b49f27..589774cfec3f 100644
--- a/core/lib/Drupal/Core/Extension/ExtensionVersion.php
+++ b/core/lib/Drupal/Core/Extension/ExtensionVersion.php
@@ -119,7 +119,7 @@ private function __construct(string $major_version, ?string $minor_version, ?str
    *   The ExtensionVersion instance.
    */
   public static function createFromSupportBranch(string $branch): ExtensionVersion {
-    if (substr($branch, -1) !== '.') {
+    if (!str_ends_with($branch, '.')) {
       throw new \UnexpectedValueException("Invalid support branch: $branch");
     }
     return static::createFromVersionString($branch . '0');
diff --git a/core/lib/Drupal/Core/File/FileSystem.php b/core/lib/Drupal/Core/File/FileSystem.php
index 9d1e66b4bd4b..f88ba9d8f54c 100644
--- a/core/lib/Drupal/Core/File/FileSystem.php
+++ b/core/lib/Drupal/Core/File/FileSystem.php
@@ -596,7 +596,7 @@ public function createFilename($basename, $directory) {
     }
 
     // A URI or path may already have a trailing slash or look like "public://".
-    if (substr($directory, -1) == '/') {
+    if (str_ends_with($directory, '/')) {
       $separator = '';
     }
     else {
@@ -711,7 +711,7 @@ protected function doScanDirectory($dir, $mask, array $options = [], $depth = 0)
       while (FALSE !== ($filename = readdir($handle))) {
         // Skip this file if it matches the nomask or starts with a dot.
         if ($filename[0] != '.' && !(preg_match($options['nomask'], $filename))) {
-          if (substr($dir, -1) == '/') {
+          if (str_ends_with($dir, '/')) {
             $uri = "$dir$filename";
           }
           else {
diff --git a/core/lib/Drupal/Core/FileTransfer/FileTransfer.php b/core/lib/Drupal/Core/FileTransfer/FileTransfer.php
index 2724b0d792ac..f22cfde50328 100644
--- a/core/lib/Drupal/Core/FileTransfer/FileTransfer.php
+++ b/core/lib/Drupal/Core/FileTransfer/FileTransfer.php
@@ -312,7 +312,7 @@ final protected function fixRemotePath($path, $strip_chroot = TRUE) {
   public function sanitizePath($path) {
     // Windows path sanitization.
     $path = str_replace('\\', '/', $path);
-    if (substr($path, -1) == '/') {
+    if (str_ends_with($path, '/')) {
       $path = substr($path, 0, -1);
     }
     return $path;
diff --git a/core/lib/Drupal/Core/Render/Element/ImageButton.php b/core/lib/Drupal/Core/Render/Element/ImageButton.php
index 6f345ece40b7..7289cadafc81 100644
--- a/core/lib/Drupal/Core/Render/Element/ImageButton.php
+++ b/core/lib/Drupal/Core/Render/Element/ImageButton.php
@@ -46,7 +46,7 @@ public static function valueCallback(&$element, $input, FormStateInterface $form
         $input = $form_state->getUserInput();
         foreach (explode('[', $element['#name']) as $element_name) {
           // chop off the ] that may exist.
-          if (substr($element_name, -1) == ']') {
+          if (str_ends_with($element_name, ']')) {
             $element_name = substr($element_name, 0, -1);
           }
 
diff --git a/core/lib/Drupal/Core/Routing/UrlGenerator.php b/core/lib/Drupal/Core/Routing/UrlGenerator.php
index e41fe77d407b..d7d0a2032170 100644
--- a/core/lib/Drupal/Core/Routing/UrlGenerator.php
+++ b/core/lib/Drupal/Core/Routing/UrlGenerator.php
@@ -321,10 +321,10 @@ public function generateFromRoute($name, $parameters = [], $options = [], $colle
       // otherwise we would generate a URI that, when followed by a user agent
       // (e.g. browser), does not match this route
       $path = strtr($path, ['/../' => '/%2E%2E/', '/./' => '/%2E/']);
-      if ('/..' === substr($path, -3)) {
+      if (str_ends_with($path, '/..')) {
         $path = substr($path, 0, -2) . '%2E%2E';
       }
-      elseif ('/.' === substr($path, -2)) {
+      elseif (str_ends_with($path, '/.')) {
         $path = substr($path, 0, -1) . '%2E';
       }
     }
diff --git a/core/lib/Drupal/Core/Test/TestDiscovery.php b/core/lib/Drupal/Core/Test/TestDiscovery.php
index 9e394a4342e8..d6e2336375f8 100644
--- a/core/lib/Drupal/Core/Test/TestDiscovery.php
+++ b/core/lib/Drupal/Core/Test/TestDiscovery.php
@@ -273,10 +273,10 @@ public static function scanDirectory($namespace_prefix, $path) {
       // We don't want to discover abstract TestBase classes, traits or
       // interfaces. They can be deprecated and will call @trigger_error()
       // during discovery.
-      return substr($file_name, -4) === '.php' &&
-        substr($file_name, -12) !== 'TestBase.php' &&
-        substr($file_name, -9) !== 'Trait.php' &&
-        substr($file_name, -13) !== 'Interface.php';
+      return str_ends_with($file_name, '.php') &&
+        !str_ends_with($file_name, 'TestBase.php') &&
+        !str_ends_with($file_name, 'Trait.php') &&
+        !str_ends_with($file_name, 'Interface.php');
     });
     $files = new \RecursiveIteratorIterator($filter);
     $classes = [];
diff --git a/core/lib/Drupal/Core/TypedData/DataReferenceDefinition.php b/core/lib/Drupal/Core/TypedData/DataReferenceDefinition.php
index 3a2a66861c61..fc84856a2765 100644
--- a/core/lib/Drupal/Core/TypedData/DataReferenceDefinition.php
+++ b/core/lib/Drupal/Core/TypedData/DataReferenceDefinition.php
@@ -35,7 +35,7 @@ public static function create($target_data_type) {
    * {@inheritdoc}
    */
   public static function createFromDataType($data_type) {
-    if (substr($data_type, -strlen('_reference')) != '_reference') {
+    if (!str_ends_with($data_type, '_reference')) {
       throw new \InvalidArgumentException('Data type must be of the form "{TARGET_TYPE}_reference"');
     }
     // Cut of the _reference suffix.
diff --git a/core/modules/file/src/Upload/FileUploadHandler.php b/core/modules/file/src/Upload/FileUploadHandler.php
index a2c07911fdb8..835c513e1ef5 100644
--- a/core/modules/file/src/Upload/FileUploadHandler.php
+++ b/core/modules/file/src/Upload/FileUploadHandler.php
@@ -220,7 +220,7 @@ public function handleFileUpload(UploadedFileInterface $uploadedFile, array $val
     }
 
     // A file URI may already have a trailing slash or look like "public://".
-    if (substr($destination, -1) != '/') {
+    if (!str_ends_with($destination, '/')) {
       $destination .= '/';
     }
 
diff --git a/core/modules/filter/src/Plugin/Filter/FilterHtml.php b/core/modules/filter/src/Plugin/Filter/FilterHtml.php
index 0718b73092e0..b24aa06ab5a7 100644
--- a/core/modules/filter/src/Plugin/Filter/FilterHtml.php
+++ b/core/modules/filter/src/Plugin/Filter/FilterHtml.php
@@ -124,7 +124,7 @@ public function filterAttributes($text) {
       $allowed_attributes = ['exact' => [], 'prefix' => []];
       foreach (($global_allowed_attributes + $tag_attributes) as $name => $values) {
         // A trailing * indicates wildcard, but it must have some prefix.
-        if (substr($name, -1) === '*' && $name[0] !== '*') {
+        if (str_ends_with($name, '*') && $name[0] !== '*') {
           $allowed_attributes['prefix'][str_replace('*', '', $name)] = $this->prepareAttributeValues($values);
         }
         else {
@@ -231,7 +231,7 @@ protected function prepareAttributeValues($attribute_values) {
     $result = ['exact' => [], 'prefix' => []];
     foreach ($attribute_values as $name => $allowed) {
       // A trailing * indicates wildcard, but it must have some prefix.
-      if (substr($name, -1) === '*' && $name[0] !== '*') {
+      if (str_ends_with($name, '*') && $name[0] !== '*') {
         $result['prefix'][str_replace('*', '', $name)] = $allowed;
       }
       else {
diff --git a/core/modules/jsonapi/src/Routing/Routes.php b/core/modules/jsonapi/src/Routing/Routes.php
index f4766f5a875b..6a3ab0d90641 100644
--- a/core/modules/jsonapi/src/Routing/Routes.php
+++ b/core/modules/jsonapi/src/Routing/Routes.php
@@ -87,7 +87,7 @@ public function __construct(ResourceTypeRepositoryInterface $resource_type_repos
       sprintf('The provided base path should contain a leading slash "/". Given: "%s".', $jsonapi_base_path)
     );
     assert(
-      substr($jsonapi_base_path, -1) !== '/',
+      !str_ends_with($jsonapi_base_path, '/'),
       sprintf('The provided base path should not contain a trailing slash "/". Given: "%s".', $jsonapi_base_path)
     );
     $this->jsonApiBasePath = $jsonapi_base_path;
diff --git a/core/modules/migrate/src/Plugin/migrate/process/FileCopy.php b/core/modules/migrate/src/Plugin/migrate/process/FileCopy.php
index f4d006c09190..f7701ea27c5b 100644
--- a/core/modules/migrate/src/Plugin/migrate/process/FileCopy.php
+++ b/core/modules/migrate/src/Plugin/migrate/process/FileCopy.php
@@ -210,7 +210,7 @@ protected function writeFile($source, $destination, $replace = FileSystemInterfa
    */
   protected function getDirectory($uri) {
     $dir = $this->fileSystem->dirname($uri);
-    if (substr($dir, -3) == '://') {
+    if (str_ends_with($dir, '://')) {
       return $this->fileSystem->realpath($dir);
     }
     return $dir;
diff --git a/core/modules/migrate_drupal/tests/src/Kernel/MigrateDrupalTestBase.php b/core/modules/migrate_drupal/tests/src/Kernel/MigrateDrupalTestBase.php
index 5cb4a954237e..f7d3b0898f6d 100644
--- a/core/modules/migrate_drupal/tests/src/Kernel/MigrateDrupalTestBase.php
+++ b/core/modules/migrate_drupal/tests/src/Kernel/MigrateDrupalTestBase.php
@@ -57,7 +57,7 @@ protected function loadFixture($path) {
     $default_db = Database::getConnection()->getKey();
     Database::setActiveConnection($this->sourceDatabase->getKey());
 
-    if (substr($path, -3) == '.gz') {
+    if (str_ends_with($path, '.gz')) {
       $path = 'compress.zlib://' . $path;
     }
     require $path;
diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php
index e9485aa3a739..f11e78c6d900 100644
--- a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php
+++ b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php
@@ -82,7 +82,7 @@ protected function loadFixture($path) {
     $default_db = Database::getConnection()->getKey();
     Database::setActiveConnection($this->sourceDatabase->getKey());
 
-    if (substr($path, -3) == '.gz') {
+    if (str_ends_with($path, '.gz')) {
       $path = 'compress.zlib://' . $path;
     }
     require $path;
diff --git a/core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php b/core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php
index cfa493233af0..dc0285bf9f11 100644
--- a/core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php
+++ b/core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php
@@ -425,7 +425,7 @@ protected function getCoreStability() {
       // Strip off "-dev";
       $version_towards = substr($version, 0, -4);
 
-      if (substr($version_towards, -2) !== '.0') {
+      if (!str_ends_with($version_towards, '.0')) {
         // If the current version is developing towards an x.y.z release where
         // z is not 0, it means that the x.y.0 has already been released, and
         // only stable changes are permitted on the branch.
diff --git a/core/tests/Drupal/FunctionalTests/Update/UpdatePathTestBase.php b/core/tests/Drupal/FunctionalTests/Update/UpdatePathTestBase.php
index 35ffe3ad464b..1a57e5539a22 100644
--- a/core/tests/Drupal/FunctionalTests/Update/UpdatePathTestBase.php
+++ b/core/tests/Drupal/FunctionalTests/Update/UpdatePathTestBase.php
@@ -144,7 +144,7 @@ protected function doInstall() {
 
     // Load the database(s).
     foreach ($this->databaseDumpFiles as $file) {
-      if (substr($file, -3) == '.gz') {
+      if (str_ends_with($file, '.gz')) {
         $file = "compress.zlib://$file";
       }
       require $file;
diff --git a/core/tests/Drupal/Tests/Component/PhpStorage/MTimeProtectedFileStorageBase.php b/core/tests/Drupal/Tests/Component/PhpStorage/MTimeProtectedFileStorageBase.php
index d38cdefba60e..0056c4477cfc 100644
--- a/core/tests/Drupal/Tests/Component/PhpStorage/MTimeProtectedFileStorageBase.php
+++ b/core/tests/Drupal/Tests/Component/PhpStorage/MTimeProtectedFileStorageBase.php
@@ -81,7 +81,7 @@ public function testSecurity() {
     $name = 'test.php';
     $php->save($name, '<?php');
     $expected_root_directory = $this->directory . '/test';
-    if (substr($name, -4) === '.php') {
+    if (str_ends_with($name, '.php')) {
       $expected_directory = $expected_root_directory . '/' . substr($name, 0, -4);
     }
     else {
-- 
GitLab