2 namespace Robo\Task\Filesystem;
4 use Robo\Task\StackBasedTask;
5 use Symfony\Component\Filesystem\Filesystem as sfFilesystem;
6 use Symfony\Component\Filesystem\Exception\IOException;
7 use Robo\Contract\BuilderAwareInterface;
8 use Robo\Common\BuilderAwareTrait;
11 * Wrapper for [Symfony Filesystem](http://symfony.com/doc/current/components/filesystem.html) Component.
12 * Comands are executed in stack and can be stopped on first fail with `stopOnFail` option.
16 * $this->taskFilesystemStack()
18 * ->touch('logs/.gitignore')
19 * ->chgrp('www', 'www-data')
20 * ->symlink('/var/log/nginx/error.log', 'logs/error.log')
24 * $this->_touch('.gitignore');
25 * $this->_mkdir('logs');
30 * @method $this mkdir(string|array|\Traversable $dir, int $mode = 0777)
31 * @method $this touch(string|array|\Traversable $file, int $time = null, int $atime = null)
32 * @method $this copy(string $from, string $to, bool $force = false)
33 * @method $this chmod(string|array|\Traversable $file, int $permissions, int $umask = 0000, bool $recursive = false)
34 * @method $this chgrp(string|array|\Traversable $file, string $group, bool $recursive = false)
35 * @method $this chown(string|array|\Traversable $file, string $user, bool $recursive = false)
36 * @method $this remove(string|array|\Traversable $file)
37 * @method $this rename(string $from, string $to, bool $force = false)
38 * @method $this symlink(string $from, string $to, bool $copyOnWindows = false)
39 * @method $this mirror(string $from, string $to, \Traversable $iterator = null, array $options = [])
41 class FilesystemStack extends StackBasedTask implements BuilderAwareInterface
43 use BuilderAwareTrait;
46 * @var \Symfony\Component\Filesystem\Filesystem
50 public function __construct()
52 $this->fs = new sfFilesystem();
56 * @return \Symfony\Component\Filesystem\Filesystem
58 protected function getDelegate()
68 protected function _copy($from, $to, $force = false)
70 $this->fs->copy($from, $to, $force);
74 * @param string|string[]|\Traversable $file
75 * @param int $permissions
77 * @param bool $recursive
79 protected function _chmod($file, $permissions, $umask = 0000, $recursive = false)
81 $this->fs->chmod($file, $permissions, $umask, $recursive);
85 * @param string|string[]|\Traversable $file
86 * @param string $group
87 * @param bool $recursive
89 protected function _chgrp($file, $group, $recursive = null)
91 $this->fs->chgrp($file, $group, $recursive);
95 * @param string|string[]|\Traversable $file
97 * @param bool $recursive
99 protected function _chown($file, $user, $recursive = null)
101 $this->fs->chown($file, $user, $recursive);
105 * @param string $origin
106 * @param string $target
107 * @param bool $overwrite
109 * @return null|true|\Robo\Result
111 protected function _rename($origin, $target, $overwrite = false)
113 // we check that target does not exist
114 if ((!$overwrite && is_readable($target)) || (file_exists($target) && !is_writable($target))) {
115 throw new IOException(sprintf('Cannot rename because the target "%s" already exists.', $target), 0, null, $target);
118 // Due to a bug (limitation) in PHP, cross-volume renames do not work.
119 // See: https://bugs.php.net/bug.php?id=54097
120 if (true !== @rename($origin, $target)) {
121 return $this->crossVolumeRename($origin, $target);
127 * @param string $origin
128 * @param string $target
130 * @return null|\Robo\Result
132 protected function crossVolumeRename($origin, $target)
134 // First step is to try to get rid of the target. If there
135 // is a single, deletable file, then we will just unlink it.
136 if (is_file($target)) {
139 // If the target still exists, we will try to delete it.
140 // TODO: Note that if this fails partway through, then we cannot
141 // adequately rollback. Perhaps we need to preflight the operation
142 // and determine if everything inside of $target is writable.
143 if (file_exists($target)) {
144 $this->fs->remove($target);
147 /** @var \Robo\Result $result */
148 $result = $this->collectionBuilder()->taskCopyDir([$origin => $target])->run();
149 if (!$result->wasSuccessful()) {
152 $this->fs->remove($origin);