Drupal investigation

usage.md 5.7KB

Painfree Handling of File Paths

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.

This package provides few, but robust utility methods to simplify your life when dealing with file paths.

Canonicalization

Canonicalization is the transformation of a path into a normalized (the "canonical") format. You can canonicalize a path with Path::canonicalize():

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

The following modifications happen during canonicalization:

  • "." 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 pass absolute paths and relative paths to canonicalize(). When a relative path is passed, ".." segments at the beginning of the path are kept:

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

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 Path::makeAbsolute() and Path::makeRelative().

makeAbsolute() expects a relative path and a base path to base that relative path upon:

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

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.yml', '/var/www/project/uploads');
// => /var/www/project/config/config.yml

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.

makeRelative() is the inverse operation to makeAbsolute():

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

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

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

Use isAbsolute() and isRelative() 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 array(
    '/var/www/vhosts/project/httpdocs/config/config.yml',
    '/var/www/vhosts/project/httpdocs/config/routing.yml',
    '/var/www/vhosts/project/httpdocs/config/services.yml',
    '/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 Path::getLongestCommonBasePath() to check a list of paths for a common base path:

$paths = array(
    '/var/www/vhosts/project/httpdocs/config/config.yml',
    '/var/www/vhosts/project/httpdocs/config/routing.yml',
    '/var/www/vhosts/project/httpdocs/config/services.yml',
    '/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 Path::makeRelative() to shorten the stored paths:

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

return array(
    $bp.'/config/config.yml',
    $bp.'/config/routing.yml',
    $bp.'/config/services.yml',
    $bp.'/images/banana.gif',
    $bp.'/uploads/images/nicer-banana.gif',
);

getLongestCommonBasePath() always returns canonical paths.

Use 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 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

Path::getDirectory() fixes these shortcomings:

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

Additionally, you can use Path::getRoot() to obtain the root of a path:

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

echo Path::getRoot("C:\Programs\Apache\Config");
// => C:/