diff --git a/components/filesystem.rst b/components/filesystem.rst index a5023a270e1..c274c78f921 100644 --- a/components/filesystem.rst +++ b/components/filesystem.rst @@ -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 ------------ @@ -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. +- 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`` ~~~~~~~~~ @@ -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 + use :method:`Symfony\\Component\\Filesystem\\Path::canonicalize` method instead. + ``makePathRelative`` ~~~~~~~~~~~~~~~~~~~~ @@ -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 ("/"); +- 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 --------------