Chris@0
|
1 Painfree Handling of File Paths
|
Chris@0
|
2 ===============================
|
Chris@0
|
3
|
Chris@0
|
4 Dealing with file paths usually involves some difficulties:
|
Chris@0
|
5
|
Chris@0
|
6 * **System Heterogeneity**: File paths look different on different platforms.
|
Chris@0
|
7 UNIX file paths start with a slash ("/"), while Windows file paths start with
|
Chris@0
|
8 a system drive ("C:"). UNIX uses forward slashes, while Windows uses
|
Chris@0
|
9 backslashes by default ("\").
|
Chris@0
|
10
|
Chris@0
|
11 * **Absolute/Relative Paths**: Web applications frequently need to deal with
|
Chris@0
|
12 absolute and relative paths. Converting one to the other properly is tricky
|
Chris@0
|
13 and repetitive.
|
Chris@0
|
14
|
Chris@0
|
15 This package provides few, but robust utility methods to simplify your life
|
Chris@0
|
16 when dealing with file paths.
|
Chris@0
|
17
|
Chris@0
|
18 Canonicalization
|
Chris@0
|
19 ----------------
|
Chris@0
|
20
|
Chris@0
|
21 *Canonicalization* is the transformation of a path into a normalized (the
|
Chris@0
|
22 "canonical") format. You can canonicalize a path with `Path::canonicalize()`:
|
Chris@0
|
23
|
Chris@0
|
24 ```php
|
Chris@0
|
25 echo Path::canonicalize('/var/www/vhost/webmozart/../config.ini');
|
Chris@0
|
26 // => /var/www/vhost/config.ini
|
Chris@0
|
27 ```
|
Chris@0
|
28
|
Chris@0
|
29 The following modifications happen during canonicalization:
|
Chris@0
|
30
|
Chris@0
|
31 * "." segments are removed;
|
Chris@0
|
32 * ".." segments are resolved;
|
Chris@0
|
33 * backslashes ("\") are converted into forward slashes ("/");
|
Chris@0
|
34 * root paths ("/" and "C:/") always terminate with a slash;
|
Chris@0
|
35 * non-root paths never terminate with a slash;
|
Chris@0
|
36 * schemes (such as "phar://") are kept;
|
Chris@0
|
37 * replace "~" with the user's home directory.
|
Chris@0
|
38
|
Chris@0
|
39 You can pass absolute paths and relative paths to `canonicalize()`. When a
|
Chris@0
|
40 relative path is passed, ".." segments at the beginning of the path are kept:
|
Chris@0
|
41
|
Chris@0
|
42 ```php
|
Chris@0
|
43 echo Path::canonicalize('../uploads/../config/config.yml');
|
Chris@0
|
44 // => ../config/config.yml
|
Chris@0
|
45 ```
|
Chris@0
|
46
|
Chris@0
|
47 Malformed paths are returned unchanged:
|
Chris@0
|
48
|
Chris@0
|
49 ```php
|
Chris@0
|
50 echo Path::canonicalize('C:Programs/PHP/php.ini');
|
Chris@0
|
51 // => C:Programs/PHP/php.ini
|
Chris@0
|
52 ```
|
Chris@0
|
53
|
Chris@0
|
54 Converting Absolute/Relative Paths
|
Chris@0
|
55 ----------------------------------
|
Chris@0
|
56
|
Chris@0
|
57 Absolute/relative paths can be converted with the methods `Path::makeAbsolute()`
|
Chris@0
|
58 and `Path::makeRelative()`.
|
Chris@0
|
59
|
Chris@0
|
60 `makeAbsolute()` expects a relative path and a base path to base that relative
|
Chris@0
|
61 path upon:
|
Chris@0
|
62
|
Chris@0
|
63 ```php
|
Chris@0
|
64 echo Path::makeAbsolute('config/config.yml', '/var/www/project');
|
Chris@0
|
65 // => /var/www/project/config/config.yml
|
Chris@0
|
66 ```
|
Chris@0
|
67
|
Chris@0
|
68 If an absolute path is passed in the first argument, the absolute path is
|
Chris@0
|
69 returned unchanged:
|
Chris@0
|
70
|
Chris@0
|
71 ```php
|
Chris@0
|
72 echo Path::makeAbsolute('/usr/share/lib/config.ini', '/var/www/project');
|
Chris@0
|
73 // => /usr/share/lib/config.ini
|
Chris@0
|
74 ```
|
Chris@0
|
75
|
Chris@0
|
76 The method resolves ".." segments, if there are any:
|
Chris@0
|
77
|
Chris@0
|
78 ```php
|
Chris@0
|
79 echo Path::makeAbsolute('../config/config.yml', '/var/www/project/uploads');
|
Chris@0
|
80 // => /var/www/project/config/config.yml
|
Chris@0
|
81 ```
|
Chris@0
|
82
|
Chris@0
|
83 This method is very useful if you want to be able to accept relative paths (for
|
Chris@0
|
84 example, relative to the root directory of your project) and absolute paths at
|
Chris@0
|
85 the same time.
|
Chris@0
|
86
|
Chris@0
|
87 `makeRelative()` is the inverse operation to `makeAbsolute()`:
|
Chris@0
|
88
|
Chris@0
|
89 ```php
|
Chris@0
|
90 echo Path::makeRelative('/var/www/project/config/config.yml', '/var/www/project');
|
Chris@0
|
91 // => config/config.yml
|
Chris@0
|
92 ```
|
Chris@0
|
93
|
Chris@0
|
94 If the path is not within the base path, the method will prepend ".." segments
|
Chris@0
|
95 as necessary:
|
Chris@0
|
96
|
Chris@0
|
97 ```php
|
Chris@0
|
98 echo Path::makeRelative('/var/www/project/config/config.yml', '/var/www/project/uploads');
|
Chris@0
|
99 // => ../config/config.yml
|
Chris@0
|
100 ```
|
Chris@0
|
101
|
Chris@0
|
102 Use `isAbsolute()` and `isRelative()` to check whether a path is absolute or
|
Chris@0
|
103 relative:
|
Chris@0
|
104
|
Chris@0
|
105 ```php
|
Chris@0
|
106 Path::isAbsolute('C:\Programs\PHP\php.ini')
|
Chris@0
|
107 // => true
|
Chris@0
|
108 ```
|
Chris@0
|
109
|
Chris@0
|
110 All four methods internally canonicalize the passed path.
|
Chris@0
|
111
|
Chris@0
|
112 Finding Longest Common Base Paths
|
Chris@0
|
113 ---------------------------------
|
Chris@0
|
114
|
Chris@0
|
115 When you store absolute file paths on the file system, this leads to a lot of
|
Chris@0
|
116 duplicated information:
|
Chris@0
|
117
|
Chris@0
|
118 ```php
|
Chris@0
|
119 return array(
|
Chris@0
|
120 '/var/www/vhosts/project/httpdocs/config/config.yml',
|
Chris@0
|
121 '/var/www/vhosts/project/httpdocs/config/routing.yml',
|
Chris@0
|
122 '/var/www/vhosts/project/httpdocs/config/services.yml',
|
Chris@0
|
123 '/var/www/vhosts/project/httpdocs/images/banana.gif',
|
Chris@0
|
124 '/var/www/vhosts/project/httpdocs/uploads/images/nicer-banana.gif',
|
Chris@0
|
125 );
|
Chris@0
|
126 ```
|
Chris@0
|
127
|
Chris@0
|
128 Especially when storing many paths, the amount of duplicated information is
|
Chris@0
|
129 noticeable. You can use `Path::getLongestCommonBasePath()` to check a list of
|
Chris@0
|
130 paths for a common base path:
|
Chris@0
|
131
|
Chris@0
|
132 ```php
|
Chris@0
|
133 $paths = array(
|
Chris@0
|
134 '/var/www/vhosts/project/httpdocs/config/config.yml',
|
Chris@0
|
135 '/var/www/vhosts/project/httpdocs/config/routing.yml',
|
Chris@0
|
136 '/var/www/vhosts/project/httpdocs/config/services.yml',
|
Chris@0
|
137 '/var/www/vhosts/project/httpdocs/images/banana.gif',
|
Chris@0
|
138 '/var/www/vhosts/project/httpdocs/uploads/images/nicer-banana.gif',
|
Chris@0
|
139 );
|
Chris@0
|
140
|
Chris@0
|
141 Path::getLongestCommonBasePath($paths);
|
Chris@0
|
142 // => /var/www/vhosts/project/httpdocs
|
Chris@0
|
143 ```
|
Chris@0
|
144
|
Chris@0
|
145 Use this path together with `Path::makeRelative()` to shorten the stored paths:
|
Chris@0
|
146
|
Chris@0
|
147 ```php
|
Chris@0
|
148 $bp = '/var/www/vhosts/project/httpdocs';
|
Chris@0
|
149
|
Chris@0
|
150 return array(
|
Chris@0
|
151 $bp.'/config/config.yml',
|
Chris@0
|
152 $bp.'/config/routing.yml',
|
Chris@0
|
153 $bp.'/config/services.yml',
|
Chris@0
|
154 $bp.'/images/banana.gif',
|
Chris@0
|
155 $bp.'/uploads/images/nicer-banana.gif',
|
Chris@0
|
156 );
|
Chris@0
|
157 ```
|
Chris@0
|
158
|
Chris@0
|
159 `getLongestCommonBasePath()` always returns canonical paths.
|
Chris@0
|
160
|
Chris@0
|
161 Use `Path::isBasePath()` to test whether a path is a base path of another path:
|
Chris@0
|
162
|
Chris@0
|
163 ```php
|
Chris@0
|
164 Path::isBasePath("/var/www", "/var/www/project");
|
Chris@0
|
165 // => true
|
Chris@0
|
166
|
Chris@0
|
167 Path::isBasePath("/var/www", "/var/www/project/..");
|
Chris@0
|
168 // => true
|
Chris@0
|
169
|
Chris@0
|
170 Path::isBasePath("/var/www", "/var/www/project/../..");
|
Chris@0
|
171 // => false
|
Chris@0
|
172 ```
|
Chris@0
|
173
|
Chris@0
|
174 Finding Directories/Root Directories
|
Chris@0
|
175 ------------------------------------
|
Chris@0
|
176
|
Chris@0
|
177 PHP offers the function `dirname()` to obtain the directory path of a file path.
|
Chris@0
|
178 This method has a few quirks:
|
Chris@0
|
179
|
Chris@0
|
180 * `dirname()` does not accept backslashes on UNIX
|
Chris@0
|
181 * `dirname("C:/Programs")` returns "C:", not "C:/"
|
Chris@0
|
182 * `dirname("C:/")` returns ".", not "C:/"
|
Chris@0
|
183 * `dirname("C:")` returns ".", not "C:/"
|
Chris@0
|
184 * `dirname("Programs")` returns ".", not ""
|
Chris@0
|
185 * `dirname()` does not canonicalize the result
|
Chris@0
|
186
|
Chris@0
|
187 `Path::getDirectory()` fixes these shortcomings:
|
Chris@0
|
188
|
Chris@0
|
189 ```php
|
Chris@0
|
190 echo Path::getDirectory("C:\Programs");
|
Chris@0
|
191 // => C:/
|
Chris@0
|
192 ```
|
Chris@0
|
193
|
Chris@0
|
194 Additionally, you can use `Path::getRoot()` to obtain the root of a path:
|
Chris@0
|
195
|
Chris@0
|
196 ```php
|
Chris@0
|
197 echo Path::getRoot("/etc/apache2/sites-available");
|
Chris@0
|
198 // => /
|
Chris@0
|
199
|
Chris@0
|
200 echo Path::getRoot("C:\Programs\Apache\Config");
|
Chris@0
|
201 // => C:/
|
Chris@0
|
202 ```
|
Chris@0
|
203
|