|
1 <?php |
|
2 |
|
3 /* |
|
4 * This file is part of the Symfony package. |
|
5 * |
|
6 * (c) Fabien Potencier <fabien@symfony.com> |
|
7 * |
|
8 * For the full copyright and license information, please view the LICENSE |
|
9 * file that was distributed with this source code. |
|
10 */ |
|
11 |
|
12 namespace Symfony\Component\HttpKernel\Util; |
|
13 |
|
14 /** |
|
15 * Provides basic utility to manipulate the file system. |
|
16 * |
|
17 * @author Fabien Potencier <fabien@symfony.com> |
|
18 */ |
|
19 class Filesystem |
|
20 { |
|
21 /** |
|
22 * Copies a file. |
|
23 * |
|
24 * This method only copies the file if the origin file is newer than the target file. |
|
25 * |
|
26 * By default, if the target already exists, it is not overridden. |
|
27 * |
|
28 * @param string $originFile The original filename |
|
29 * @param string $targetFile The target filename |
|
30 * @param array $override Whether to override an existing file or not |
|
31 */ |
|
32 public function copy($originFile, $targetFile, $override = false) |
|
33 { |
|
34 $this->mkdir(dirname($targetFile)); |
|
35 |
|
36 $mostRecent = false; |
|
37 if (file_exists($targetFile)) { |
|
38 $statTarget = stat($targetFile); |
|
39 $statOrigin = stat($originFile); |
|
40 $mostRecent = $statOrigin['mtime'] > $statTarget['mtime']; |
|
41 } |
|
42 |
|
43 if ($override || !file_exists($targetFile) || $mostRecent) { |
|
44 copy($originFile, $targetFile); |
|
45 } |
|
46 } |
|
47 |
|
48 /** |
|
49 * Creates a directory recursively. |
|
50 * |
|
51 * @param string|array|\Traversable $dirs The directory path |
|
52 * @param int $mode The directory mode |
|
53 * |
|
54 * @return Boolean true if the directory has been created, false otherwise |
|
55 */ |
|
56 public function mkdir($dirs, $mode = 0777) |
|
57 { |
|
58 $ret = true; |
|
59 foreach ($this->toIterator($dirs) as $dir) { |
|
60 if (is_dir($dir)) { |
|
61 continue; |
|
62 } |
|
63 |
|
64 $ret = @mkdir($dir, $mode, true) && $ret; |
|
65 } |
|
66 |
|
67 return $ret; |
|
68 } |
|
69 |
|
70 /** |
|
71 * Creates empty files. |
|
72 * |
|
73 * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove |
|
74 */ |
|
75 public function touch($files) |
|
76 { |
|
77 foreach ($this->toIterator($files) as $file) { |
|
78 touch($file); |
|
79 } |
|
80 } |
|
81 |
|
82 /** |
|
83 * Removes files or directories. |
|
84 * |
|
85 * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove |
|
86 */ |
|
87 public function remove($files) |
|
88 { |
|
89 $files = iterator_to_array($this->toIterator($files)); |
|
90 $files = array_reverse($files); |
|
91 foreach ($files as $file) { |
|
92 if (!file_exists($file)) { |
|
93 continue; |
|
94 } |
|
95 |
|
96 if (is_dir($file) && !is_link($file)) { |
|
97 $this->remove(new \FilesystemIterator($file)); |
|
98 |
|
99 rmdir($file); |
|
100 } else { |
|
101 unlink($file); |
|
102 } |
|
103 } |
|
104 } |
|
105 |
|
106 /** |
|
107 * Change mode for an array of files or directories. |
|
108 * |
|
109 * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove |
|
110 * @param integer $mode The new mode |
|
111 * @param integer $umask The mode mask (octal) |
|
112 */ |
|
113 public function chmod($files, $mode, $umask = 0000) |
|
114 { |
|
115 $currentUmask = umask(); |
|
116 umask($umask); |
|
117 |
|
118 foreach ($this->toIterator($files) as $file) { |
|
119 chmod($file, $mode); |
|
120 } |
|
121 |
|
122 umask($currentUmask); |
|
123 } |
|
124 |
|
125 /** |
|
126 * Renames a file. |
|
127 * |
|
128 * @param string $origin The origin filename |
|
129 * @param string $target The new filename |
|
130 * |
|
131 * @throws \RuntimeException When target file already exists |
|
132 */ |
|
133 public function rename($origin, $target) |
|
134 { |
|
135 // we check that target does not exist |
|
136 if (is_readable($target)) { |
|
137 throw new \RuntimeException(sprintf('Cannot rename because the target "%" already exist.', $target)); |
|
138 } |
|
139 |
|
140 rename($origin, $target); |
|
141 } |
|
142 |
|
143 /** |
|
144 * Creates a symbolic link or copy a directory. |
|
145 * |
|
146 * @param string $originDir The origin directory path |
|
147 * @param string $targetDir The symbolic link name |
|
148 * @param Boolean $copyOnWindows Whether to copy files if on windows |
|
149 */ |
|
150 public function symlink($originDir, $targetDir, $copyOnWindows = false) |
|
151 { |
|
152 if (!function_exists('symlink') && $copyOnWindows) { |
|
153 $this->mirror($originDir, $targetDir); |
|
154 |
|
155 return; |
|
156 } |
|
157 |
|
158 $ok = false; |
|
159 if (is_link($targetDir)) { |
|
160 if (readlink($targetDir) != $originDir) { |
|
161 unlink($targetDir); |
|
162 } else { |
|
163 $ok = true; |
|
164 } |
|
165 } |
|
166 |
|
167 if (!$ok) { |
|
168 symlink($originDir, $targetDir); |
|
169 } |
|
170 } |
|
171 |
|
172 /** |
|
173 * Mirrors a directory to another. |
|
174 * |
|
175 * @param string $originDir The origin directory |
|
176 * @param string $targetDir The target directory |
|
177 * @param \Traversable $iterator A Traversable instance |
|
178 * @param array $options An array of options (see copy()) |
|
179 * |
|
180 * @throws \RuntimeException When file type is unknown |
|
181 */ |
|
182 public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array()) |
|
183 { |
|
184 if (null === $iterator) { |
|
185 $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST); |
|
186 } |
|
187 |
|
188 if ('/' === substr($targetDir, -1) || '\\' === substr($targetDir, -1)) { |
|
189 $targetDir = substr($targetDir, 0, -1); |
|
190 } |
|
191 |
|
192 if ('/' === substr($originDir, -1) || '\\' === substr($originDir, -1)) { |
|
193 $originDir = substr($originDir, 0, -1); |
|
194 } |
|
195 |
|
196 foreach ($iterator as $file) { |
|
197 $target = $targetDir.'/'.str_replace($originDir.DIRECTORY_SEPARATOR, '', $file->getPathname()); |
|
198 |
|
199 if (is_dir($file)) { |
|
200 $this->mkdir($target); |
|
201 } else if (is_file($file)) { |
|
202 $this->copy($file, $target, $options); |
|
203 } else if (is_link($file)) { |
|
204 $this->symlink($file, $target); |
|
205 } else { |
|
206 throw new \RuntimeException(sprintf('Unable to guess "%s" file type.', $file)); |
|
207 } |
|
208 } |
|
209 } |
|
210 |
|
211 /** |
|
212 * Returns whether the file path is an absolute path. |
|
213 * |
|
214 * @param string $file A file path |
|
215 * |
|
216 * @return Boolean |
|
217 */ |
|
218 public function isAbsolutePath($file) |
|
219 { |
|
220 if ($file[0] == '/' || $file[0] == '\\' |
|
221 || (strlen($file) > 3 && ctype_alpha($file[0]) |
|
222 && $file[1] == ':' |
|
223 && ($file[2] == '\\' || $file[2] == '/') |
|
224 ) |
|
225 ) { |
|
226 return true; |
|
227 } |
|
228 |
|
229 return false; |
|
230 } |
|
231 |
|
232 private function toIterator($files) |
|
233 { |
|
234 if (!$files instanceof \Traversable) { |
|
235 $files = new \ArrayObject(is_array($files) ? $files : array($files)); |
|
236 } |
|
237 |
|
238 return $files; |
|
239 } |
|
240 } |