Finding a Missing Function

I talked a couple of weeks ago about updating to Drupal 9 (and if you were ready to do it). If you, like me, have started that process (and maybe actually have a site running D9) then you may run into a situation like this one. You get a fatal error about an undefined function. Maybe something like:

Error: Call to undefined method Drupal\Core\File\FileSystem::uriScheme()

The error was accompanied (in the full backtrace) by a line number in a file (which I have committed here intentionally). Looking, I found this code:

 \Drupal::service('file_system')->uriScheme($uri);

Great, now I have something to look for. Usually you can chase this type of service call down in one of two ways:

  1. If you already know what the service is calling, just go find the class in question

  2. If you do NOT know what the service is calling, you have to chase that down first

Let’s assume case 2 (we don’t know what the file_system service is or where it lives). Here a few quick ways to find it!

Check Drupal Core’s services.yml file

In this case, since this is a Drupal core service, it should live in the core.services.yml file (found at docroot/core/core.services.yml). If you search in the core.services.yml file for “file_system” you should find the following:

  file_system:
    class: Drupal\Core\File\FileSystem
    arguments: ['@stream_wrapper_manager', '@settings', '@logger.channel.file']

This tells you that the file_system service calls the FileSystem class and where to find it. If you have a look at this class in Drupal 9.x, you’ll find that the code that is erring out above is trying to call a non-existent method (uriScheme). (Yes, I realize that is literally what the error message says. But sometimes it’s useful to find these things for yourself so you can understand what’s going on.)

Let’s now look at the same class in Drupal 8.9.x. It’s in the same place, named the same thing, called via the same service. But! That method uriScheme is still there. Here is the interface from that class from version 8.9.1:

/**
   * Returns the scheme of a URI (e.g. a stream).
   *
   * @param string $uri
   *   A stream, referenced as "scheme://target" or "data:target".
   *
   * @return string|bool
   *   A string containing the name of the scheme, or FALSE if none. For
   *   example, the URI "public://example.txt" would return "public".
   *
   * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
   *   Drupal\Core\StreamWrapper\StreamWrapperManagerInterface::getScheme()
   *   instead.
   *
   * @see https://www.drupal.org/node/3035273
   */
  public function uriScheme($uri);



Specifically, check out the @deprecated notice. In this case, file_system has a uriScheme method, but it’s deprecated (so it was removed in 9.0). You can still “use” this functionality, but you have to switch from the file_system service over to the stream_wrapper_manager service.

This “may” be literally as simple as swapping file_system to stream_wrapper_manager in your \Drupal service call. It may be a bit more complex than that. But that’s the next step, refactoring the code (if it’s yours) or working through the D.O issue queue to resolve the issue in core/contrib respectively if you’re using someone else’s module.

Note: this is why carefully and fully testing your code is so critical before jumping too far into the Drupal 9 upgrade. This code that is throwing the error passes automated code sniffing / deployment scanning for D9 compatibility because the method in question is called via a \Drupal:service() call (so the @deprecated tag isn’t noticed). Still, you should aggressively scan all of your custom and contrib modules, themes, and profiles using automated tools like the upgrade status module to avoid these errors as much as possible.