Skip to content

[Filesystem] Add documentation for the Path class #15759

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 18, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 206 additions & 4 deletions components/filesystem.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
The Filesystem Component
========================

The Filesystem component provides basic utilities for the filesystem.
The Filesystem component provides basic utilities for the filesystem and
paths manipulation.

Installation
------------
Expand All @@ -18,20 +19,30 @@ Installation
Usage
-----

The :class:`Symfony\\Component\\Filesystem\\Filesystem` class is the unique
endpoint for filesystem operations::
The component contains two classes:

- The :class:`Symfony\\Component\\Filesystem\\Filesystem` which provides utilities
for filesystem write operations.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not only write, right?

- The :class:`Symfony\\Component\\Filesystem\\Path` which provides utilities
for paths manipulation.::

use Symfony\Component\Filesystem\Exception\IOExceptionInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Path;

$filesystem = new Filesystem();

try {
$filesystem->mkdir(sys_get_temp_dir().'/'.random_int(0, 1000));
$filesystem->mkdir(
Path::normalize(sys_get_temp_dir().'/'.random_int(0, 1000)),
);
} catch (IOExceptionInterface $exception) {
echo "An error occurred while creating your directory at ".$exception->getPath();
}

Filesystem
----------

``mkdir``
~~~~~~~~~

Expand Down Expand Up @@ -224,6 +235,11 @@ Its behavior is the following::
* if ``$path`` does not exist, it returns null.
* if ``$path`` exists, it returns its absolute fully resolved final version.

.. note::

If you wish to canonicalize the path without checking its existence, you can
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading this might be confusing if you don't know what "canonicalize the path" exactly means.

use :method:`Symfony\\Component\\Filesystem\\Path::canonicalize` method instead.

``makePathRelative``
~~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -309,6 +325,192 @@ creates them before appending the contents.

The third argument of ``appendToFile()`` was introduced in Symfony 5.4.

Path
----

.. versionadded:: 5.4

The :class:`Symfony\\Component\\Filesystem\\Path` class was introduced in Symfony 5.4.

Dealing with file paths usually involves some difficulties:

- System Heterogeneity: file paths look different on different platforms. UNIX
file paths start with a slash ("/"), while Windows file paths start with a
system drive ("C:"). UNIX uses forward slashes, while Windows uses backslashes
by default ("").
- Absolute/relative paths: web applications frequently need to deal with absolute
and relative paths. Converting one to the other properly is tricky and
repetitive.

:class:`Symfony\\Component\\Filesystem\\Path` provides utility methods to tackle
those issues.

Canonicalization
~~~~~~~~~~~~~~~~

Returns the shortest path name equivalent to the given path. It applies the
following rules iteratively until no further processing can be done:

- "." segments are removed;
- ".." segments are resolved;
- backslashes ("\") are converted into forward slashes ("/");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this "backslash to forward-slash" transform applied unconditionally? Even on Windows?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC yes, the idea being you should compare normalized paths (there is a Path::normalize()) method as otherwise you are always susceptible to surprises (e.g. to expect a certain hard-coded unix style path but you are on Windows hence the path you are comparing to is invalid)

- root paths ("/" and "C:/") always terminate with a slash;
- non-root paths never terminate with a slash;
- schemes (such as "phar://") are kept;
- replace "~" with the user's home directory.

You can canonicalize a path with :method:`Symfony\\Component\\Filesystem\\Path::canonicalize`::

echo Path::canonicalize('/var/www/vhost/webmozart/../config.ini');
// => /var/www/vhost/config.ini

You can pass absolute paths and relative paths to the
:method:`Symfony\\Component\\Filesystem\\Path::canonicalize` method. When a
relative path is passed, ".." segments at the beginning of the path are kept::

echo Path::canonicalize('../uploads/../config/config.yaml');
// => ../config/config.yaml

Malformed paths are returned unchanged::

echo Path::canonicalize('C:Programs/PHP/php.ini');
// => C:Programs/PHP/php.ini

Converting Absolute/Relative Paths
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Absolute/relative paths can be converted with the methods
:method:`Symfony\\Component\\Filesystem\\Path::makeAbsolute`
and :method:`Symfony\\Component\\Filesystem\\Path::makeRelative`.

:method:`Symfony\\Component\\Filesystem\\Path::makeAbsolute` method expects a
relative path and a base path to base that relative path upon::

echo Path::makeAbsolute('config/config.yaml', '/var/www/project');
// => /var/www/project/config/config.yaml

If an absolute path is passed in the first argument, the absolute path is
returned unchanged::

echo Path::makeAbsolute('/usr/share/lib/config.ini', '/var/www/project');
// => /usr/share/lib/config.ini

The method resolves ".." segments, if there are any::

echo Path::makeAbsolute('../config/config.yaml', '/var/www/project/uploads');
// => /var/www/project/config/config.yaml

This method is very useful if you want to be able to accept relative paths (for
example, relative to the root directory of your project) and absolute paths at
the same time.

:method:`Symfony\\Component\\Filesystem\\Path::makeRelative` is the inverse
operation to :method:`Symfony\\Component\\Filesystem\\Path::makeAbsolute`::

echo Path::makeRelative('/var/www/project/config/config.yaml', '/var/www/project');
// => config/config.yaml

If the path is not within the base path, the method will prepend ".." segments
as necessary::

echo Path::makeRelative('/var/www/project/config/config.yaml', '/var/www/project/uploads');
// => ../config/config.yaml

Use :method:`Symfony\\Component\\Filesystem\\Path::makeAbsolute` and
:method:`Symfony\\Component\\Filesystem\\Path::makeRelative` to check whether a
path is absolute or relative::

Path::isAbsolute('C:\Programs\PHP\php.ini')
// => true

All four methods internally canonicalize the passed path.

Finding Longest Common Base Paths
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

When you store absolute file paths on the file system, this leads to a lot of
duplicated information::

return [
'/var/www/vhosts/project/httpdocs/config/config.yaml',
'/var/www/vhosts/project/httpdocs/config/routing.yaml',
'/var/www/vhosts/project/httpdocs/config/services.yaml',
'/var/www/vhosts/project/httpdocs/images/banana.gif',
'/var/www/vhosts/project/httpdocs/uploads/images/nicer-banana.gif',
];

Especially when storing many paths, the amount of duplicated information is
noticeable. You can use :method:`Symfony\\Component\\Filesystem\\Path::getLongestCommonBasePath`
to check a list of paths for a common base path::

$paths = [
'/var/www/vhosts/project/httpdocs/config/config.yaml',
'/var/www/vhosts/project/httpdocs/config/routing.yaml',
'/var/www/vhosts/project/httpdocs/config/services.yaml',
'/var/www/vhosts/project/httpdocs/images/banana.gif',
'/var/www/vhosts/project/httpdocs/uploads/images/nicer-banana.gif',
];

Path::getLongestCommonBasePath($paths);
// => /var/www/vhosts/project/httpdocs

Use this path together with :method:`Symfony\\Component\\Filesystem\\Path::makeRelative`
to shorten the stored paths::

$bp = '/var/www/vhosts/project/httpdocs';

return [
$bp.'/config/config.yaml',
$bp.'/config/routing.yaml',
$bp.'/config/services.yaml',
$bp.'/images/banana.gif',
$bp.'/uploads/images/nicer-banana.gif',
];

:method:`Symfony\\Component\\Filesystem\\Path::getLongestCommonBasePath` always
returns canonical paths.

Use :method:`Symfony\\Component\\Filesystem\\Path::isBasePath` to test whether a
path is a base path of another path::

Path::isBasePath("/var/www", "/var/www/project");
// => true

Path::isBasePath("/var/www", "/var/www/project/..");
// => true

Path::isBasePath("/var/www", "/var/www/project/../..");
// => false

Finding Directories/Root Directories
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

PHP offers the function :phpfunction:`dirname` to obtain the directory path of a
file path. This method has a few quirks::

- `dirname()` does not accept backslashes on UNIX
- `dirname("C:/Programs")` returns "C:", not "C:/"
- `dirname("C:/")` returns ".", not "C:/"
- `dirname("C:")` returns ".", not "C:/"
- `dirname("Programs")` returns ".", not ""
- `dirname()` does not canonicalize the result

:method:`Symfony\\Component\\Filesystem\\Path::getDirectory` fixes these
shortcomings::

echo Path::getDirectory("C:\Programs");
// => C:/

Additionally, you can use :method:`Symfony\\Component\\Filesystem\\Path::getRoot`
to obtain the root of a path::

echo Path::getRoot("/etc/apache2/sites-available");
// => /

echo Path::getRoot("C:\Programs\Apache\Config");
// => C:/
>>>>>>> a992f6342 ([Filesystem] Add documentation for the Path class)

Error Handling
--------------

Expand Down