Verzeichnisstruktur phpBB-3.1.0
- Veröffentlicht
- 27.10.2014
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
Filesystem.php
001 <?php
002
003 /*
004 * This file is part of the Symfony package.
005 *
006 * (c) Fabien Potencier <fabien@symfony.com>
007 *
008 * For the full copyright and license information, please view the LICENSE
009 * file that was distributed with this source code.
010 */
011
012 namespace Symfony\Component\Filesystem;
013
014 use Symfony\Component\Filesystem\Exception\IOException;
015
016 /**
017 * Provides basic utility to manipulate the file system.
018 *
019 * @author Fabien Potencier <fabien@symfony.com>
020 */
021 class Filesystem
022 {
023 /**
024 * Copies a file.
025 *
026 * This method only copies the file if the origin file is newer than the target file.
027 *
028 * By default, if the target already exists, it is not overridden.
029 *
030 * @param string $originFile The original filename
031 * @param string $targetFile The target filename
032 * @param bool $override Whether to override an existing file or not
033 *
034 * @throws IOException When copy fails
035 */
036 public function copy($originFile, $targetFile, $override = false)
037 {
038 if (stream_is_local($originFile) && !is_file($originFile)) {
039 throw new IOException(sprintf('Failed to copy %s because file not exists', $originFile));
040 }
041
042 $this->mkdir(dirname($targetFile));
043
044 if (!$override && is_file($targetFile) && null === parse_url($originFile, PHP_URL_HOST)) {
045 $doCopy = filemtime($originFile) > filemtime($targetFile);
046 } else {
047 $doCopy = true;
048 }
049
050 if ($doCopy) {
051 // https://bugs.php.net/bug.php?id=64634
052 $source = fopen($originFile, 'r');
053 // Stream context created to allow files overwrite when using FTP stream wrapper - disabled by default
054 $target = fopen($targetFile, 'w', null, stream_context_create(array('ftp' => array('overwrite' => true))));
055 stream_copy_to_stream($source, $target);
056 fclose($source);
057 fclose($target);
058 unset($source, $target);
059
060 if (!is_file($targetFile)) {
061 throw new IOException(sprintf('Failed to copy %s to %s', $originFile, $targetFile));
062 }
063 }
064 }
065
066 /**
067 * Creates a directory recursively.
068 *
069 * @param string|array|\Traversable $dirs The directory path
070 * @param int $mode The directory mode
071 *
072 * @throws IOException On any directory creation failure
073 */
074 public function mkdir($dirs, $mode = 0777)
075 {
076 foreach ($this->toIterator($dirs) as $dir) {
077 if (is_dir($dir)) {
078 continue;
079 }
080
081 if (true !== @mkdir($dir, $mode, true)) {
082 $error = error_get_last();
083 if (!is_dir($dir)) {
084 // The directory was not created by a concurrent process. Let's throw an exception with a developer friendly error message if we have one
085 if ($error) {
086 throw new IOException(sprintf('Failed to create "%s": %s.', $dir, $error['message']));
087 }
088 throw new IOException(sprintf('Failed to create "%s"', $dir));
089 }
090 }
091 }
092 }
093
094 /**
095 * Checks the existence of files or directories.
096 *
097 * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to check
098 *
099 * @return bool true if the file exists, false otherwise
100 */
101 public function exists($files)
102 {
103 foreach ($this->toIterator($files) as $file) {
104 if (!file_exists($file)) {
105 return false;
106 }
107 }
108
109 return true;
110 }
111
112 /**
113 * Sets access and modification time of file.
114 *
115 * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to create
116 * @param int $time The touch time as a Unix timestamp
117 * @param int $atime The access time as a Unix timestamp
118 *
119 * @throws IOException When touch fails
120 */
121 public function touch($files, $time = null, $atime = null)
122 {
123 foreach ($this->toIterator($files) as $file) {
124 $touch = $time ? @touch($file, $time, $atime) : @touch($file);
125 if (true !== $touch) {
126 throw new IOException(sprintf('Failed to touch %s', $file));
127 }
128 }
129 }
130
131 /**
132 * Removes files or directories.
133 *
134 * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove
135 *
136 * @throws IOException When removal fails
137 */
138 public function remove($files)
139 {
140 $files = iterator_to_array($this->toIterator($files));
141 $files = array_reverse($files);
142 foreach ($files as $file) {
143 if (!file_exists($file) && !is_link($file)) {
144 continue;
145 }
146
147 if (is_dir($file) && !is_link($file)) {
148 $this->remove(new \FilesystemIterator($file));
149
150 if (true !== @rmdir($file)) {
151 throw new IOException(sprintf('Failed to remove directory %s', $file));
152 }
153 } else {
154 // https://bugs.php.net/bug.php?id=52176
155 if (defined('PHP_WINDOWS_VERSION_MAJOR') && is_dir($file)) {
156 if (true !== @rmdir($file)) {
157 throw new IOException(sprintf('Failed to remove file %s', $file));
158 }
159 } else {
160 if (true !== @unlink($file)) {
161 throw new IOException(sprintf('Failed to remove file %s', $file));
162 }
163 }
164 }
165 }
166 }
167
168 /**
169 * Change mode for an array of files or directories.
170 *
171 * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change mode
172 * @param int $mode The new mode (octal)
173 * @param int $umask The mode mask (octal)
174 * @param bool $recursive Whether change the mod recursively or not
175 *
176 * @throws IOException When the change fail
177 */
178 public function chmod($files, $mode, $umask = 0000, $recursive = false)
179 {
180 foreach ($this->toIterator($files) as $file) {
181 if ($recursive && is_dir($file) && !is_link($file)) {
182 $this->chmod(new \FilesystemIterator($file), $mode, $umask, true);
183 }
184 if (true !== @chmod($file, $mode & ~$umask)) {
185 throw new IOException(sprintf('Failed to chmod file %s', $file));
186 }
187 }
188 }
189
190 /**
191 * Change the owner of an array of files or directories
192 *
193 * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change owner
194 * @param string $user The new owner user name
195 * @param bool $recursive Whether change the owner recursively or not
196 *
197 * @throws IOException When the change fail
198 */
199 public function chown($files, $user, $recursive = false)
200 {
201 foreach ($this->toIterator($files) as $file) {
202 if ($recursive && is_dir($file) && !is_link($file)) {
203 $this->chown(new \FilesystemIterator($file), $user, true);
204 }
205 if (is_link($file) && function_exists('lchown')) {
206 if (true !== @lchown($file, $user)) {
207 throw new IOException(sprintf('Failed to chown file %s', $file));
208 }
209 } else {
210 if (true !== @chown($file, $user)) {
211 throw new IOException(sprintf('Failed to chown file %s', $file));
212 }
213 }
214 }
215 }
216
217 /**
218 * Change the group of an array of files or directories
219 *
220 * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change group
221 * @param string $group The group name
222 * @param bool $recursive Whether change the group recursively or not
223 *
224 * @throws IOException When the change fail
225 */
226 public function chgrp($files, $group, $recursive = false)
227 {
228 foreach ($this->toIterator($files) as $file) {
229 if ($recursive && is_dir($file) && !is_link($file)) {
230 $this->chgrp(new \FilesystemIterator($file), $group, true);
231 }
232 if (is_link($file) && function_exists('lchgrp')) {
233 if (true !== @lchgrp($file, $group)) {
234 throw new IOException(sprintf('Failed to chgrp file %s', $file));
235 }
236 } else {
237 if (true !== @chgrp($file, $group)) {
238 throw new IOException(sprintf('Failed to chgrp file %s', $file));
239 }
240 }
241 }
242 }
243
244 /**
245 * Renames a file or a directory.
246 *
247 * @param string $origin The origin filename or directory
248 * @param string $target The new filename or directory
249 * @param bool $overwrite Whether to overwrite the target if it already exists
250 *
251 * @throws IOException When target file or directory already exists
252 * @throws IOException When origin cannot be renamed
253 */
254 public function rename($origin, $target, $overwrite = false)
255 {
256 // we check that target does not exist
257 if (!$overwrite && is_readable($target)) {
258 throw new IOException(sprintf('Cannot rename because the target "%s" already exist.', $target));
259 }
260
261 if (true !== @rename($origin, $target)) {
262 throw new IOException(sprintf('Cannot rename "%s" to "%s".', $origin, $target));
263 }
264 }
265
266 /**
267 * Creates a symbolic link or copy a directory.
268 *
269 * @param string $originDir The origin directory path
270 * @param string $targetDir The symbolic link name
271 * @param bool $copyOnWindows Whether to copy files if on Windows
272 *
273 * @throws IOException When symlink fails
274 */
275 public function symlink($originDir, $targetDir, $copyOnWindows = false)
276 {
277 if (!function_exists('symlink') && $copyOnWindows) {
278 $this->mirror($originDir, $targetDir);
279
280 return;
281 }
282
283 $this->mkdir(dirname($targetDir));
284
285 $ok = false;
286 if (is_link($targetDir)) {
287 if (readlink($targetDir) != $originDir) {
288 $this->remove($targetDir);
289 } else {
290 $ok = true;
291 }
292 }
293
294 if (!$ok) {
295 if (true !== @symlink($originDir, $targetDir)) {
296 $report = error_get_last();
297 if (is_array($report)) {
298 if (defined('PHP_WINDOWS_VERSION_MAJOR') && false !== strpos($report['message'], 'error code(1314)')) {
299 throw new IOException('Unable to create symlink due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?');
300 }
301 }
302 throw new IOException(sprintf('Failed to create symbolic link from %s to %s', $originDir, $targetDir));
303 }
304 }
305 }
306
307 /**
308 * Given an existing path, convert it to a path relative to a given starting path
309 *
310 * @param string $endPath Absolute path of target
311 * @param string $startPath Absolute path where traversal begins
312 *
313 * @return string Path of target relative to starting path
314 */
315 public function makePathRelative($endPath, $startPath)
316 {
317 // Normalize separators on Windows
318 if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
319 $endPath = strtr($endPath, '\\', '/');
320 $startPath = strtr($startPath, '\\', '/');
321 }
322
323 // Split the paths into arrays
324 $startPathArr = explode('/', trim($startPath, '/'));
325 $endPathArr = explode('/', trim($endPath, '/'));
326
327 // Find for which directory the common path stops
328 $index = 0;
329 while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) {
330 $index++;
331 }
332
333 // Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels)
334 $depth = count($startPathArr) - $index;
335
336 // Repeated "../" for each level need to reach the common path
337 $traverser = str_repeat('../', $depth);
338
339 $endPathRemainder = implode('/', array_slice($endPathArr, $index));
340
341 // Construct $endPath from traversing to the common path, then to the remaining $endPath
342 $relativePath = $traverser.(strlen($endPathRemainder) > 0 ? $endPathRemainder.'/' : '');
343
344 return (strlen($relativePath) === 0) ? './' : $relativePath;
345 }
346
347 /**
348 * Mirrors a directory to another.
349 *
350 * @param string $originDir The origin directory
351 * @param string $targetDir The target directory
352 * @param \Traversable $iterator A Traversable instance
353 * @param array $options An array of boolean options
354 * Valid options are:
355 * - $options['override'] Whether to override an existing file on copy or not (see copy())
356 * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink())
357 * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false)
358 *
359 * @throws IOException When file type is unknown
360 */
361 public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array())
362 {
363 $targetDir = rtrim($targetDir, '/\\');
364 $originDir = rtrim($originDir, '/\\');
365
366 // Iterate in destination folder to remove obsolete entries
367 if ($this->exists($targetDir) && isset($options['delete']) && $options['delete']) {
368 $deleteIterator = $iterator;
369 if (null === $deleteIterator) {
370 $flags = \FilesystemIterator::SKIP_DOTS;
371 $deleteIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($targetDir, $flags), \RecursiveIteratorIterator::CHILD_FIRST);
372 }
373 foreach ($deleteIterator as $file) {
374 $origin = str_replace($targetDir, $originDir, $file->getPathname());
375 if (!$this->exists($origin)) {
376 $this->remove($file);
377 }
378 }
379 }
380
381 $copyOnWindows = false;
382 if (isset($options['copy_on_windows']) && !function_exists('symlink')) {
383 $copyOnWindows = $options['copy_on_windows'];
384 }
385
386 if (null === $iterator) {
387 $flags = $copyOnWindows ? \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS : \FilesystemIterator::SKIP_DOTS;
388 $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST);
389 }
390
391 foreach ($iterator as $file) {
392 $target = str_replace($originDir, $targetDir, $file->getPathname());
393
394 if ($copyOnWindows) {
395 if (is_link($file) || is_file($file)) {
396 $this->copy($file, $target, isset($options['override']) ? $options['override'] : false);
397 } elseif (is_dir($file)) {
398 $this->mkdir($target);
399 } else {
400 throw new IOException(sprintf('Unable to guess "%s" file type.', $file));
401 }
402 } else {
403 if (is_link($file)) {
404 $this->symlink($file->getLinkTarget(), $target);
405 } elseif (is_dir($file)) {
406 $this->mkdir($target);
407 } elseif (is_file($file)) {
408 $this->copy($file, $target, isset($options['override']) ? $options['override'] : false);
409 } else {
410 throw new IOException(sprintf('Unable to guess "%s" file type.', $file));
411 }
412 }
413 }
414 }
415
416 /**
417 * Returns whether the file path is an absolute path.
418 *
419 * @param string $file A file path
420 *
421 * @return bool
422 */
423 public function isAbsolutePath($file)
424 {
425 if (strspn($file, '/\\', 0, 1)
426 || (strlen($file) > 3 && ctype_alpha($file[0])
427 && substr($file, 1, 1) === ':'
428 && (strspn($file, '/\\', 2, 1))
429 )
430 || null !== parse_url($file, PHP_URL_SCHEME)
431 ) {
432 return true;
433 }
434
435 return false;
436 }
437
438 /**
439 * @param mixed $files
440 *
441 * @return \Traversable
442 */
443 private function toIterator($files)
444 {
445 if (!$files instanceof \Traversable) {
446 $files = new \ArrayObject(is_array($files) ? $files : array($files));
447 }
448
449 return $files;
450 }
451
452 /**
453 * Atomically dumps content into a file.
454 *
455 * @param string $filename The file to be written to.
456 * @param string $content The data to write into the file.
457 * @param null|int $mode The file mode (octal). If null, file permissions are not modified
458 * Deprecated since version 2.3.12, to be removed in 3.0.
459 * @throws IOException If the file cannot be written to.
460 */
461 public function dumpFile($filename, $content, $mode = 0666)
462 {
463 $dir = dirname($filename);
464
465 if (!is_dir($dir)) {
466 $this->mkdir($dir);
467 } elseif (!is_writable($dir)) {
468 throw new IOException(sprintf('Unable to write in the %s directory.', $dir));
469 }
470
471 $tmpFile = tempnam($dir, basename($filename));
472
473 if (false === @file_put_contents($tmpFile, $content)) {
474 throw new IOException(sprintf('Failed to write file "%s".', $filename));
475 }
476
477 $this->rename($tmpFile, $filename, true);
478 if (null !== $mode) {
479 $this->chmod($filename, $mode);
480 }
481 }
482 }
483