Skip to content

Commit 55f682c

Browse files
committed
merged branch romainneutron/FilesystemExceptions (PR #4330)
Commits ------- a20fc68 Merge pull request #1 from SamsonIT/FilesystemExceptions 8eca661 [FileSystem] explains possible failure of symlink creation in windows b1f8744 Add Changelog BC Break note 24eb396 [Filesystem] Added few new behaviors: Discussion ---------- [Filesystem] Consistence and enhancements for Filesystem Bug fix: no Feature addition: yes Backwards compatibility break: **yes** Symfony2 tests pass: yes Fixes the following tickets: None License of the code: MIT This PR adds features and introduce a backward compatibility break. features : - whenever an action fails, a \RuntimeException is thrown - add access to the second and third arguments of ``touch`` function - add a recursive option for chmod - add a chown method - add a chgrp method The backward compatibility break happens in the mkdir method : Before this PR, a boolean is returned ; true if all directories were created, false otherwise. It now returns nothing. --------------------------------------------------------------------------- by travisbot at 2012-05-18T14:26:42Z This pull request [passes](http://travis-ci.org/symfony/symfony/builds/1367000) (merged 83cdd622 into 1e15f21). --------------------------------------------------------------------------- by fabpot at 2012-05-20T02:40:28Z To be consistent, we should throw exception whenever some operation fails. --------------------------------------------------------------------------- by romainneutron at 2012-05-20T21:10:23Z I fix the consistency ; mkdir now throws an exception if a directory creation fails. This introduce a BC break, see PR message which has been updated with all features and BC break. Added chgrp and chown methods Add options for touch Add recursive option for chmod --------------------------------------------------------------------------- by travisbot at 2012-05-20T21:11:47Z This pull request [passes](http://travis-ci.org/symfony/symfony/builds/1383619) (merged a4d1eeb8 into 1407f11). --------------------------------------------------------------------------- by travisbot at 2012-05-22T10:49:06Z This pull request [passes](http://travis-ci.org/symfony/symfony/builds/1399027) (merged 7e14b6bd into 517ae43). --------------------------------------------------------------------------- by travisbot at 2012-05-22T10:58:10Z This pull request [passes](http://travis-ci.org/symfony/symfony/builds/1399083) (merged 71852653 into 517ae43). --------------------------------------------------------------------------- by travisbot at 2012-05-22T11:18:44Z This pull request [passes](http://travis-ci.org/symfony/symfony/builds/1399194) (merged 7645bad3 into 517ae43). --------------------------------------------------------------------------- by travisbot at 2012-05-23T18:21:47Z This pull request [fails](http://travis-ci.org/symfony/symfony/builds/1414091) (merged b049d5b1 into 517ae43). --------------------------------------------------------------------------- by travisbot at 2012-05-23T18:26:19Z This pull request [fails](http://travis-ci.org/symfony/symfony/builds/1414123) (merged 34903466 into 517ae43). --------------------------------------------------------------------------- by travisbot at 2012-05-29T16:07:26Z This pull request [passes](http://travis-ci.org/symfony/symfony/builds/1467173) (merged b1d1eb2e into adf07f1). --------------------------------------------------------------------------- by travisbot at 2012-05-29T16:19:38Z This pull request [passes](http://travis-ci.org/symfony/symfony/builds/1467261) (merged 42015ffa into adf07f1). --------------------------------------------------------------------------- by romainneutron at 2012-06-01T14:30:45Z Any news about this PR ? --------------------------------------------------------------------------- by stloyd at 2012-06-08T09:57:39Z @romainneutron You need to [squash](http://www.silverwareconsulting.com/index.cfm/2010/6/6/Using-Git-Rebase-to-Squash-Commits) your commits, and add more proper message in squashed commit i.e.: > [Filesystem] Added few new behaviors: * whenever an action fails, a `RuntimeException` is thrown * add access to the second and third arguments of `touch()` function * add a recursive option for `chmod()` * add a `chown()` method * add a `chgrp()` method > BC break: `mkdir()` function throw exception in case of failture instead of returning Boolean value. --------------------------------------------------------------------------- by romainneutron at 2012-06-08T10:59:55Z @stloyd squash done ! --------------------------------------------------------------------------- by travisbot at 2012-06-08T11:26:20Z This pull request [passes](http://travis-ci.org/symfony/symfony/builds/1565540) (merged 8f55ddb6 into f8e68e5). --------------------------------------------------------------------------- by travisbot at 2012-06-08T11:41:45Z This pull request [passes](http://travis-ci.org/symfony/symfony/builds/1566247) (merged 880312b6 into f8e68e5). --------------------------------------------------------------------------- by romainneutron at 2012-06-09T11:42:24Z I've added documentation to the Filesystem component : symfony/symfony-docs#1439 --------------------------------------------------------------------------- by travisbot at 2012-06-09T16:47:20Z This pull request [passes](http://travis-ci.org/symfony/symfony/builds/1577754) (merged 5647ad41 into f8a09db). --------------------------------------------------------------------------- by stloyd at 2012-06-13T14:47:31Z @romainneutron You probably need to rebase your code as some changes were merge into master for `Filesystem`. --------------------------------------------------------------------------- by romainneutron at 2012-06-13T15:17:31Z @stloyd rebase OK ! by the way, do you have any idea when/if this PR will be merged ? --------------------------------------------------------------------------- by travisbot at 2012-06-13T15:20:44Z This pull request [passes](http://travis-ci.org/symfony/symfony/builds/1611591) (merged c8b86c68 into c07e916). --------------------------------------------------------------------------- by fabpot at 2012-06-16T16:40:50Z You need to add a note about the BC breaks in the CHANGELOG file. --------------------------------------------------------------------------- by fabpot at 2012-06-16T16:43:20Z Also, instead of using `\RuntimeException`, I would create a custom exception like we have done in other components (an interface + a RuntimeException that implements the interface and extends \RuntimeException). The exception name can be something like `IOException`. --------------------------------------------------------------------------- by travisbot at 2012-06-18T10:11:20Z This pull request [fails](http://travis-ci.org/symfony/symfony/builds/1645757) (merged 925a8234 into 0b8b76b). --------------------------------------------------------------------------- by stloyd at 2012-06-18T10:14:52Z @fabpot Anything blocking merge of this PR ? (tests are failing because of issue in master, not releted to this PR) --------------------------------------------------------------------------- by romainneutron at 2012-06-18T10:29:20Z @fabpot @stloyd the latest push was just a rebase push for PR 4577 (#4577) I'm currently fixing the Exception and changelog things, I'll push very soon --------------------------------------------------------------------------- by romainneutron at 2012-06-18T10:44:38Z @fabpot I've added the exception and the exception interface, add the changelog info --------------------------------------------------------------------------- by travisbot at 2012-06-18T10:53:34Z This pull request [fails](http://travis-ci.org/symfony/symfony/builds/1645981) (merged 634d6fb9 into 0b8b76b). --------------------------------------------------------------------------- by romainneutron at 2012-06-18T11:08:43Z As reported by @stloyd the PR is failing due to an issue in the master, I re-push and trig the PR build when this issue is solved --------------------------------------------------------------------------- by travisbot at 2012-06-18T11:16:58Z This pull request [fails](http://travis-ci.org/symfony/symfony/builds/1646006) (merged 2f65945a into 0b8b76b).
2 parents fcf7afc + a20fc68 commit 55f682c

File tree

6 files changed

+411
-61
lines changed

6 files changed

+411
-61
lines changed

src/Symfony/Component/Filesystem/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ CHANGELOG
44
2.1.0
55
-----
66

7+
* 24eb396 : BC Break : mkdir() function now throws exception in case of failure instead of returning Boolean value
78
* created the component
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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\Filesystem\Exception;
13+
14+
/**
15+
* Exception interface for all exceptions thrown by the component.
16+
*
17+
* @author Romain Neutron <imprec@gmail.com>
18+
*
19+
* @api
20+
*/
21+
interface ExceptionInterface
22+
{
23+
24+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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\Filesystem\Exception;
13+
14+
/**
15+
* Exception class thrown when a filesystem operation failure happens
16+
*
17+
* @author Romain Neutron <imprec@gmail.com>
18+
*
19+
* @api
20+
*/
21+
class IOException extends \RuntimeException implements ExceptionInterface
22+
{
23+
24+
}

src/Symfony/Component/Filesystem/Filesystem.php

Lines changed: 122 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\Filesystem;
1313

14+
use Symfony\Component\Filesystem\Exception\IOException;
15+
1416
/**
1517
* Provides basic utility to manipulate the file system.
1618
*
@@ -28,6 +30,8 @@ class Filesystem
2830
* @param string $originFile The original filename
2931
* @param string $targetFile The target filename
3032
* @param array $override Whether to override an existing file or not
33+
*
34+
* @throws IOException When copy fails
3135
*/
3236
public function copy($originFile, $targetFile, $override = false)
3337
{
@@ -40,30 +44,31 @@ public function copy($originFile, $targetFile, $override = false)
4044
}
4145

4246
if ($doCopy) {
43-
copy($originFile, $targetFile);
47+
if (true !== @copy($originFile, $targetFile)) {
48+
throw new IOException(sprintf('Failed to copy %s to %s', $originFile, $targetFile));
49+
}
4450
}
4551
}
4652

4753
/**
4854
* Creates a directory recursively.
4955
*
5056
* @param string|array|\Traversable $dirs The directory path
51-
* @param int $mode The directory mode
57+
* @param integer $mode The directory mode
5258
*
53-
* @return Boolean true if the directory has been created, false otherwise
59+
* @throws IOException On any directory creation failure
5460
*/
5561
public function mkdir($dirs, $mode = 0777)
5662
{
57-
$ret = true;
5863
foreach ($this->toIterator($dirs) as $dir) {
5964
if (is_dir($dir)) {
6065
continue;
6166
}
6267

63-
$ret = @mkdir($dir, $mode, true) && $ret;
68+
if (true !== @mkdir($dir, $mode, true)) {
69+
throw new IOException(sprintf('Failed to create %s', $dir));
70+
}
6471
}
65-
66-
return $ret;
6772
}
6873

6974
/**
@@ -85,21 +90,33 @@ public function exists($files)
8590
}
8691

8792
/**
88-
* Creates empty files.
93+
* Sets access and modification time of file.
8994
*
9095
* @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to create
96+
* @param integer $time The touch time as a unix timestamp
97+
* @param integer $atime The access time as a unix timestamp
98+
*
99+
* @throws IOException When touch fails
91100
*/
92-
public function touch($files)
101+
public function touch($files, $time = null, $atime = null)
93102
{
103+
if (null === $time) {
104+
$time = time();
105+
}
106+
94107
foreach ($this->toIterator($files) as $file) {
95-
touch($file);
108+
if (true !== @touch($file, $time, $atime)) {
109+
throw new IOException(sprintf('Failed to touch %s', $file));
110+
}
96111
}
97112
}
98113

99114
/**
100115
* Removes files or directories.
101116
*
102117
* @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove
118+
*
119+
* @throws IOException When removal fails
103120
*/
104121
public function remove($files)
105122
{
@@ -113,13 +130,19 @@ public function remove($files)
113130
if (is_dir($file) && !is_link($file)) {
114131
$this->remove(new \FilesystemIterator($file));
115132

116-
rmdir($file);
133+
if (true !== @rmdir($file)) {
134+
throw new IOException(sprintf('Failed to remove directory %s', $file));
135+
}
117136
} else {
118137
// https://bugs.php.net/bug.php?id=52176
119138
if (defined('PHP_WINDOWS_VERSION_MAJOR') && is_dir($file)) {
120-
rmdir($file);
139+
if (true !== @rmdir($file)) {
140+
throw new IOException(sprintf('Failed to remove file %s', $file));
141+
}
121142
} else {
122-
unlink($file);
143+
if (true !== @unlink($file)) {
144+
throw new IOException(sprintf('Failed to remove file %s', $file));
145+
}
123146
}
124147
}
125148
}
@@ -128,14 +151,76 @@ public function remove($files)
128151
/**
129152
* Change mode for an array of files or directories.
130153
*
131-
* @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change mode
132-
* @param integer $mode The new mode (octal)
133-
* @param integer $umask The mode mask (octal)
154+
* @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change mode
155+
* @param integer $mode The new mode (octal)
156+
* @param integer $umask The mode mask (octal)
157+
* @param Boolean $recursive Whether change the mod recursively or not
158+
*
159+
* @throws IOException When the change fail
134160
*/
135-
public function chmod($files, $mode, $umask = 0000)
161+
public function chmod($files, $mode, $umask = 0000, $recursive = false)
136162
{
137163
foreach ($this->toIterator($files) as $file) {
138-
@chmod($file, $mode & ~$umask);
164+
if ($recursive && is_dir($file) && !is_link($file)) {
165+
$this->chmod(new \FilesystemIterator($file), $mode, $umask, true);
166+
}
167+
if (true !== @chmod($file, $mode & ~$umask)) {
168+
throw new IOException(sprintf('Failed to chmod file %s', $file));
169+
}
170+
}
171+
}
172+
173+
/**
174+
* Change the owner of an array of files or directories
175+
*
176+
* @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change owner
177+
* @param string $user The new owner user name
178+
* @param Boolean $recursive Whether change the owner recursively or not
179+
*
180+
* @throws IOException When the change fail
181+
*/
182+
public function chown($files, $user, $recursive = false)
183+
{
184+
foreach ($this->toIterator($files) as $file) {
185+
if ($recursive && is_dir($file) && !is_link($file)) {
186+
$this->chown(new \FilesystemIterator($file), $user, true);
187+
}
188+
if (is_link($file) && function_exists('lchown')) {
189+
if (true !== @lchown($file, $user)) {
190+
throw new IOException(sprintf('Failed to chown file %s', $file));
191+
}
192+
} else {
193+
if (true !== @chown($file, $user)) {
194+
throw new IOException(sprintf('Failed to chown file %s', $file));
195+
}
196+
}
197+
}
198+
}
199+
200+
/**
201+
* Change the group of an array of files or directories
202+
*
203+
* @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change group
204+
* @param string $group The group name
205+
* @param Boolean $recursive Whether change the group recursively or not
206+
*
207+
* @throws IOException When the change fail
208+
*/
209+
public function chgrp($files, $group, $recursive = false)
210+
{
211+
foreach ($this->toIterator($files) as $file) {
212+
if ($recursive && is_dir($file) && !is_link($file)) {
213+
$this->chgrp(new \FilesystemIterator($file), $group, true);
214+
}
215+
if (is_link($file) && function_exists('lchgrp')) {
216+
if (true !== @lchgrp($file, $group)) {
217+
throw new IOException(sprintf('Failed to chgrp file %s', $file));
218+
}
219+
} else {
220+
if (true !== @chgrp($file, $group)) {
221+
throw new IOException(sprintf('Failed to chgrp file %s', $file));
222+
}
223+
}
139224
}
140225
}
141226

@@ -145,18 +230,18 @@ public function chmod($files, $mode, $umask = 0000)
145230
* @param string $origin The origin filename
146231
* @param string $target The new filename
147232
*
148-
* @throws \RuntimeException When target file already exists
149-
* @throws \RuntimeException When origin cannot be renamed
233+
* @throws IOException When target file already exists
234+
* @throws IOException When origin cannot be renamed
150235
*/
151236
public function rename($origin, $target)
152237
{
153238
// we check that target does not exist
154239
if (is_readable($target)) {
155-
throw new \RuntimeException(sprintf('Cannot rename because the target "%s" already exist.', $target));
240+
throw new IOException(sprintf('Cannot rename because the target "%s" already exist.', $target));
156241
}
157242

158-
if (false === @rename($origin, $target)) {
159-
throw new \RuntimeException(sprintf('Cannot rename "%s" to "%s".', $origin, $target));
243+
if (true !== @rename($origin, $target)) {
244+
throw new IOException(sprintf('Cannot rename "%s" to "%s".', $origin, $target));
160245
}
161246
}
162247

@@ -166,6 +251,8 @@ public function rename($origin, $target)
166251
* @param string $originDir The origin directory path
167252
* @param string $targetDir The symbolic link name
168253
* @param Boolean $copyOnWindows Whether to copy files if on Windows
254+
*
255+
* @throws IOException When symlink fails
169256
*/
170257
public function symlink($originDir, $targetDir, $copyOnWindows = false)
171258
{
@@ -180,14 +267,22 @@ public function symlink($originDir, $targetDir, $copyOnWindows = false)
180267
$ok = false;
181268
if (is_link($targetDir)) {
182269
if (readlink($targetDir) != $originDir) {
183-
unlink($targetDir);
270+
$this->remove($targetDir);
184271
} else {
185272
$ok = true;
186273
}
187274
}
188275

189276
if (!$ok) {
190-
symlink($originDir, $targetDir);
277+
if (true !== @symlink($originDir, $targetDir)) {
278+
$report = error_get_last();
279+
if (is_array($report)) {
280+
if (defined('PHP_WINDOWS_VERSION_MAJOR') && false !== strpos($report['message'], 'error code(1314)')) {
281+
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?');
282+
}
283+
}
284+
throw new IOException(sprintf('Failed to create symbolic link from %s to %s', $originDir, $targetDir));
285+
}
191286
}
192287
}
193288

@@ -235,7 +330,7 @@ public function makePathRelative($endPath, $startPath)
235330
* - $options['override'] Whether to override an existing file on copy or not (see copy())
236331
* - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink())
237332
*
238-
* @throws \RuntimeException When file type is unknown
333+
* @throws IOException When file type is unknown
239334
*/
240335
public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array())
241336
{
@@ -262,7 +357,7 @@ public function mirror($originDir, $targetDir, \Traversable $iterator = null, $o
262357
} elseif (is_file($file) || ($copyOnWindows && is_link($file))) {
263358
$this->copy($file, $target, isset($options['override']) ? $options['override'] : false);
264359
} else {
265-
throw new \RuntimeException(sprintf('Unable to guess "%s" file type.', $file));
360+
throw new IOException(sprintf('Unable to guess "%s" file type.', $file));
266361
}
267362
}
268363
}

src/Symfony/Component/Filesystem/README.md

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,37 @@ Filesystem Component
33

44
Filesystem provides basic utility to manipulate the file system:
55

6-
use Symfony\Component\Filesystem\Filesystem;
6+
```php
7+
<?php
78

8-
$filesystem = new Filesystem();
9+
use Symfony\Component\Filesystem\Filesystem;
910

10-
$filesystem->copy($originFile, $targetFile, $override = false);
11+
$filesystem = new Filesystem();
1112

12-
$filesystem->mkdir($dirs, $mode = 0777);
13+
$filesystem->copy($originFile, $targetFile, $override = false);
1314

14-
$filesystem->touch($files);
15+
$filesystem->mkdir($dirs, $mode = 0777);
1516

16-
$filesystem->remove($files);
17+
$filesystem->touch($files, $time = null, $atime = null);
1718

18-
$filesystem->chmod($files, $mode, $umask = 0000);
19+
$filesystem->remove($files);
1920

20-
$filesystem->rename($origin, $target);
21+
$filesystem->chmod($files, $mode, $umask = 0000, $recursive = false);
2122

22-
$filesystem->symlink($originDir, $targetDir, $copyOnWindows = false);
23+
$filesystem->chown($files, $user, $recursive = false);
2324

24-
$filesystem->makePathRelative($endPath, $startPath);
25+
$filesystem->chgrp($files, $group, $recursive = false);
2526

26-
$filesystem->mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array());
27+
$filesystem->rename($origin, $target);
2728

28-
$filesystem->isAbsolutePath($file);
29+
$filesystem->symlink($originDir, $targetDir, $copyOnWindows = false);
30+
31+
$filesystem->makePathRelative($endPath, $startPath);
32+
33+
$filesystem->mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array());
34+
35+
$filesystem->isAbsolutePath($file);
36+
```
2937

3038
Resources
3139
---------

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy