Completed
Push — master ( a20e35...2a3e96 )
by Greg
03:21
created

src/Task/Filesystem/FilesystemStack.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
namespace Robo\Task\Filesystem;
3
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;
9
10
/**
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.
13
 *
14
 * ``` php
15
 * <?php
16
 * $this->taskFilesystemStack()
17
 *      ->mkdir('logs')
18
 *      ->touch('logs/.gitignore')
19
 *      ->chgrp('www', 'www-data')
20
 *      ->symlink('/var/log/nginx/error.log', 'logs/error.log')
21
 *      ->run();
22
 *
23
 * // one line
24
 * $this->_touch('.gitignore');
25
 * $this->_mkdir('logs');
26
 *
27
 * ?>
28
 * ```
29
 *
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 = [])
40
 */
41
class FilesystemStack extends StackBasedTask implements BuilderAwareInterface
42
{
43
    use BuilderAwareTrait;
44
45
    /**
46
     * @var \Symfony\Component\Filesystem\Filesystem
47
     */
48
    protected $fs;
49
50
    public function __construct()
51
    {
52
        $this->fs = new sfFilesystem();
53
    }
54
55
    /**
56
     * @return \Symfony\Component\Filesystem\Filesystem
57
     */
58
    protected function getDelegate()
59
    {
60
        return $this->fs;
61
    }
62
63
    /**
64
     * @param string $from
65
     * @param string $to
66
     * @param bool $force
67
     */
68
    protected function _copy($from, $to, $force = false)
69
    {
70
        $this->fs->copy($from, $to, $force);
71
    }
72
73
    /**
74
     * @param string|string[]|\Traversable $file
75
     * @param int $permissions
76
     * @param int $umask
77
     * @param bool $recursive
78
     */
79
    protected function _chmod($file, $permissions, $umask = 0000, $recursive = false)
80
    {
81
        $this->fs->chmod($file, $permissions, $umask, $recursive);
82
    }
83
84
    /**
85
     * @param string|string[]|\Traversable $file
86
     * @param string $group
87
     * @param bool $recursive
88
     */
89
    protected function _chgrp($file, $group, $recursive = null)
90
    {
91
        $this->fs->chgrp($file, $group, $recursive);
92
    }
93
94
    /**
95
     * @param string|string[]|\Traversable $file
96
     * @param string $user
97
     * @param bool $recursive
98
     */
99
    protected function _chown($file, $user, $recursive = null)
100
    {
101
        $this->fs->chown($file, $user, $recursive);
102
    }
103
104
    /**
105
     * @param string $origin
106
     * @param string $target
107
     * @param bool $overwrite
108
     *
109
     * @return null|true|\Robo\Result
110
     */
111
    protected function _rename($origin, $target, $overwrite = false)
112
    {
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);
116
        }
117
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);
122
        }
123
        return true;
124
    }
125
126
    /**
127
     * @param string $origin
128
     * @param string $target
129
     *
130
     * @return null|\Robo\Result
131
     */
132
    protected function crossVolumeRename($origin, $target)
133
    {
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)) {
137
            unlink($target);
138
        }
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);
145
        }
146
147
        /** @var \Robo\Result $result */
148
        $result = $this->collectionBuilder()->taskCopyDir([$origin => $target])->run();
0 ignored issues
show
Documentation Bug introduced by
The method taskCopyDir does not exist on object<Robo\Collection\CollectionBuilder>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
149
        if (!$result->wasSuccessful()) {
150
            return $result;
151
        }
152
        $this->fs->remove($origin);
153
    }
154
}
155