Pull merge.
[yaffs-website] / vendor / consolidation / robo / src / Task / Filesystem / WorkDir.php
1 <?php
2
3 namespace Robo\Task\Filesystem;
4
5 use Robo\Result;
6 use Robo\Contract\RollbackInterface;
7 use Robo\Contract\BuilderAwareInterface;
8 use Robo\Common\BuilderAwareTrait;
9
10 /**
11  * Create a temporary working directory that is automatically renamed to its
12  * final desired location if all of the tasks in the collection succeed.  If
13  * there is a rollback, then the working directory is deleted.
14  *
15  * ``` php
16  * <?php
17  * $collection = $this->collectionBuilder();
18  * $workingPath = $collection->workDir("build")->getPath();
19  * $collection->taskFilesystemStack()
20  *           ->mkdir("$workingPath/log")
21  *           ->touch("$workingPath/log/error.txt");
22  * $collection->run();
23  * ?>
24  * ```
25  */
26 class WorkDir extends TmpDir implements RollbackInterface, BuilderAwareInterface
27 {
28     use BuilderAwareTrait;
29
30     /**
31      * @var string
32      */
33     protected $finalDestination;
34
35     /**
36      * @param string $finalDestination
37      */
38     public function __construct($finalDestination)
39     {
40         $this->finalDestination = $finalDestination;
41
42         // Create a temporary directory to work in. We will place our
43         // temporary directory in the same location as the final destination
44         // directory, so that the work directory can be moved into place
45         // without having to be copied, e.g. in a cross-volume rename scenario.
46         parent::__construct(basename($finalDestination), dirname($finalDestination));
47     }
48
49     /**
50      * Create our working directory.
51      *
52      * @return \Robo\Result
53      */
54     public function run()
55     {
56         // Destination cannot be empty
57         if (empty($this->finalDestination)) {
58             return Result::error($this, "Destination directory not specified.");
59         }
60
61         // Before we do anything else, ensure that any directory in the
62         // final destination is writable, so that we can at a minimum
63         // move it out of the way before placing our results there.
64         if (is_dir($this->finalDestination)) {
65             if (!is_writable($this->finalDestination)) {
66                 return Result::error($this, "Destination directory {dir} exists and cannot be overwritten.", ['dir' => $this->finalDestination]);
67             }
68         }
69
70         return parent::run();
71     }
72
73     /**
74      * Move our working directory into its final destination once the
75      * collection it belongs to completes.
76      */
77     public function complete()
78     {
79         $this->restoreWorkingDirectory();
80
81         // Delete the final destination, if it exists.
82         // Move it out of the way first, in case it cannot
83         // be completely deleted.
84         if (file_exists($this->finalDestination)) {
85             $temporaryLocation = static::randomLocation($this->finalDestination . '_TO_DELETE_');
86             // This should always work, because we already created a temporary
87             // folder in the parent directory of the final destination, and we
88             // have already checked to confirm that the final destination is
89             // writable.
90             rename($this->finalDestination, $temporaryLocation);
91             // This may silently fail, leaving artifacts behind, if there
92             // are permissions problems with some items somewhere inside
93             // the folder being deleted.
94             $this->fs->remove($temporaryLocation);
95         }
96
97         // Move our working directory over the final destination.
98         // This should never be a cross-volume rename, so this should
99         // always succeed.
100         $workDir = reset($this->dirs);
101         if (file_exists($workDir)) {
102             rename($workDir, $this->finalDestination);
103         }
104     }
105
106     /**
107      * Delete our working directory
108      */
109     public function rollback()
110     {
111         $this->restoreWorkingDirectory();
112         $this->deleteTmpDir();
113     }
114
115     /**
116      * Get a reference to the path to the temporary directory, so that
117      * it may be used to create other tasks.  Note that the directory
118      * is not actually created until the task runs.
119      *
120      * @return string
121      */
122     public function getPath()
123     {
124         return $this->dirs[0];
125     }
126 }