Document why and when to use FileSystemInterface instead of native php functions

Created on 6 November 2023, about 1 year ago
Updated 7 November 2023, about 1 year ago

Problem/Motivation

The Drupal\Core\File\FileSystemInterface contains methods that are meant to be used instead of native php functions to deal with files.

It was originally created to replace procedural code from Drupal 7, see #2050759: Move drupal_chmod and other code in file.inc to a Drupal\Core\File\FileSystem class β†’ .
(The motivation of the Drupal 7 functions is so far in the past that I don't want to look)

Unfortunately, I could not find any centralized documentation about when and why to use these methods over native functions.
The documentation on the interface and the default implementation are quite short, and I also did not find anything online.
(I created this stackexchange question, https://drupal.stackexchange.com/questions/317517/why-filesysteminterfac...)

I can imagine a lot of reasons why we have FileSystemInterface, and I am sure other developers could provide their own arguments.
I also know of code quality tools that complain about regular filesystem functions, when FileSystemInterface methods could be used instead.

But I would really like to be able to point to authoritative documentation, instead of having to speculate about it, and possibly spreading myths.

Steps to reproduce

Proposed resolution

Document the motivation on the interface itself, optionally with a link to more documentation in a wiki page.

Possible reasons I can think of:

  • Support for stream wrappers.
    (but it seems native calls also support protocols like "public://" or "private://")
  • Support for different filesystems.
  • Support for alternative directory separator (this is a consequence of alternative filesystem).
  • Ability to swap out and mock the filesystem during tests.

General questions to be covered:

  • Why was this introduced? (beyond just "to replace Drupal 7 functions")
  • When does it need to be used? Are there conditions when it is not needed?
  • What are the consequences of using native filesystem calls?

More detailed questions that should be covered:

  • Is this needed for filesystem operations on all paths, even if the path is known/predictable? (e.g. when using `__DIR__` constant) Or is it mostly relevant for unknown paths, e.g. passed in as a parameter from somewhere?
  • What about functions that are not covered in FileSystemInterface, such as is_dir() and file_exists()? Would these fail in scenarios where FileSystemInterface is required for other operations?

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

πŸ“Œ Task
Status

Active

Version

11.0 πŸ”₯

Component
DocumentationΒ  β†’

Last updated 1 day ago

No maintainer
Created by

πŸ‡©πŸ‡ͺGermany donquixote

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @donquixote
  • πŸ‡ΊπŸ‡ΈUnited States mfb San Francisco

    I think the tldr is: These methods exist to either to provide improved logging or to work around bugs, cross-platform issues, unexpected weirdness, etc. in the php functions.

    Many methods in FileSystemInterface do document why they exist / why to use them:

    • "PHP's basename() does not properly support streams or filenames beginning with a non-US-ASCII character."
    • "PHP's dirname() does not properly pass streams, so this function fills that gap. It is backwards compatible with normal paths and will use PHP's dirname() as a fallback."
    • "When PHP's mkdir() creates a directory, the requested mode is affected by the process's umask. This function overrides the umask and sets the mode explicitly for all directory components created"
    • "PHP's unlink() is broken on Windows, as it can fail to remove a file when it has a read-only flag set"

    Admittedly, some of this is a little unclear, e.g. I think the dirname() documentation is supposed to say "parse streams", or rather "parse stream URIs", as dirname() is basically a string-parsing function; e.g. dirname('public://') === '.' whereas Drupal::service('file_system')->dirname('public://') === 'public://'

  • πŸ‡©πŸ‡ͺGermany donquixote

    Thanks a lot!
    So, it seems we are mostly trying to fix niche problems of specific functions, instead of trying to have a "swappable filesystem implementation".
    Which also means that there are scenarios where the native functions are good enough.

    I might look into this more and propose improvements to the documentation.

Production build 0.71.5 2024