collectionBuilder(); * $workingPath = $collection->workDir("build")->getPath(); * $collection->taskFilesystemStack() * ->mkdir("$workingPath/log") * ->touch("$workingPath/log/error.txt"); * $collection->run(); * ?> * ``` */ class WorkDir extends TmpDir implements RollbackInterface, BuilderAwareInterface { use BuilderAwareTrait; /** * @var string */ protected $finalDestination; /** * @param string $finalDestination */ public function __construct($finalDestination) { $this->finalDestination = $finalDestination; // Create a temporary directory to work in. We will place our // temporary directory in the same location as the final destination // directory, so that the work directory can be moved into place // without having to be copied, e.g. in a cross-volume rename scenario. parent::__construct(basename($finalDestination), dirname($finalDestination)); } /** * Create our working directory. * * @return \Robo\Result */ public function run() { // Destination cannot be empty if (empty($this->finalDestination)) { return Result::error($this, "Destination directory not specified."); } // Before we do anything else, ensure that any directory in the // final destination is writable, so that we can at a minimum // move it out of the way before placing our results there. if (is_dir($this->finalDestination)) { if (!is_writable($this->finalDestination)) { return Result::error($this, "Destination directory {dir} exists and cannot be overwritten.", ['dir' => $this->finalDestination]); } } return parent::run(); } /** * Move our working directory into its final destination once the * collection it belongs to completes. */ public function complete() { $this->restoreWorkingDirectory(); // Delete the final destination, if it exists. // Move it out of the way first, in case it cannot // be completely deleted. if (file_exists($this->finalDestination)) { $temporaryLocation = static::randomLocation($this->finalDestination . '_TO_DELETE_'); // This should always work, because we already created a temporary // folder in the parent directory of the final destination, and we // have already checked to confirm that the final destination is // writable. rename($this->finalDestination, $temporaryLocation); // This may silently fail, leaving artifacts behind, if there // are permissions problems with some items somewhere inside // the folder being deleted. $this->fs->remove($temporaryLocation); } // Move our working directory over the final destination. // This should never be a cross-volume rename, so this should // always succeed. $workDir = reset($this->dirs); if (file_exists($workDir)) { rename($workDir, $this->finalDestination); } } /** * Delete our working directory */ public function rollback() { $this->restoreWorkingDirectory(); $this->deleteTmpDir(); } /** * Get a reference to the path to the temporary directory, so that * it may be used to create other tasks. Note that the directory * is not actually created until the task runs. * * @return string */ public function getPath() { return $this->dirs[0]; } }