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 (symfony/symfony#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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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)