Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / vendor / consolidation / robo / src / Task / Archive / Extract.php
diff --git a/vendor/consolidation/robo/src/Task/Archive/Extract.php b/vendor/consolidation/robo/src/Task/Archive/Extract.php
new file mode 100644 (file)
index 0000000..a00a0ba
--- /dev/null
@@ -0,0 +1,279 @@
+<?php
+
+namespace Robo\Task\Archive;
+
+use Robo\Result;
+use Robo\Task\BaseTask;
+use Robo\Task\Filesystem\FilesystemStack;
+use Robo\Task\Filesystem\DeleteDir;
+use Robo\Contract\BuilderAwareInterface;
+use Robo\Common\BuilderAwareTrait;
+
+/**
+ * Extracts an archive.
+ *
+ * Note that often, distributions are packaged in tar or zip archives
+ * where the topmost folder may contain variable information, such as
+ * the release date, or the version of the package.  This information
+ * is very useful when unpacking by hand, but arbitrarily-named directories
+ * are much less useful to scripts.  Therefore, by default, Extract will
+ * remove the top-level directory, and instead store all extracted files
+ * into the directory specified by $archivePath.
+ *
+ * To keep the top-level directory when extracting, use
+ * `preserveTopDirectory(true)`.
+ *
+ * ``` php
+ * <?php
+ * $this->taskExtract($archivePath)
+ *  ->to($destination)
+ *  ->preserveTopDirectory(false) // the default
+ *  ->run();
+ * ?>
+ * ```
+ */
+class Extract extends BaseTask implements BuilderAwareInterface
+{
+    use BuilderAwareTrait;
+
+    /**
+     * @var string
+     */
+    protected $filename;
+
+    /**
+     * @var string
+     */
+    protected $to;
+
+    /**
+     * @var bool
+     */
+    private $preserveTopDirectory = false;
+
+    /**
+     * @param string $filename
+     */
+    public function __construct($filename)
+    {
+        $this->filename = $filename;
+    }
+
+    /**
+     * Location to store extracted files.
+     *
+     * @param string $to
+     *
+     * @return $this
+     */
+    public function to($to)
+    {
+        $this->to = $to;
+        return $this;
+    }
+
+    /**
+     * @param bool $preserve
+     *
+     * @return $this
+     */
+    public function preserveTopDirectory($preserve = true)
+    {
+        $this->preserveTopDirectory = $preserve;
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function run()
+    {
+        if (!file_exists($this->filename)) {
+            $this->printTaskError("File {filename} does not exist", ['filename' => $this->filename]);
+
+            return false;
+        }
+        if (!($mimetype = static::archiveType($this->filename))) {
+            $this->printTaskError("Could not determine type of archive for {filename}", ['filename' => $this->filename]);
+
+            return false;
+        }
+
+        // We will first extract to $extractLocation and then move to $this->to
+        $extractLocation = static::getTmpDir();
+        @mkdir($extractLocation);
+        @mkdir(dirname($this->to));
+
+        $this->startTimer();
+
+        $this->printTaskInfo("Extracting {filename}", ['filename' => $this->filename]);
+
+        $result = $this->extractAppropriateType($mimetype, $extractLocation);
+        if ($result->wasSuccessful()) {
+            $this->printTaskInfo("{filename} extracted", ['filename' => $this->filename]);
+            // Now, we want to move the extracted files to $this->to. There
+            // are two possibilities that we must consider:
+            //
+            // (1) Archived files were encapsulated in a folder with an arbitrary name
+            // (2) There was no encapsulating folder, and all the files in the archive
+            //     were extracted into $extractLocation
+            //
+            // In the case of (1), we want to move and rename the encapsulating folder
+            // to $this->to.
+            //
+            // In the case of (2), we will just move and rename $extractLocation.
+            $filesInExtractLocation = glob("$extractLocation/*");
+            $hasEncapsulatingFolder = ((count($filesInExtractLocation) == 1) && is_dir($filesInExtractLocation[0]));
+            if ($hasEncapsulatingFolder && !$this->preserveTopDirectory) {
+                $result = (new FilesystemStack())
+                    ->inflect($this)
+                    ->rename($filesInExtractLocation[0], $this->to)
+                    ->run();
+                (new DeleteDir($extractLocation))
+                    ->inflect($this)
+                    ->run();
+            } else {
+                $result = (new FilesystemStack())
+                    ->inflect($this)
+                    ->rename($extractLocation, $this->to)
+                    ->run();
+            }
+        }
+        $this->stopTimer();
+        $result['time'] = $this->getExecutionTime();
+
+        return $result;
+    }
+
+    /**
+     * @param string $mimetype
+     * @param string $extractLocation
+     *
+     * @return \Robo\Result
+     */
+    protected function extractAppropriateType($mimetype, $extractLocation)
+    {
+        // Perform the extraction of a zip file.
+        if (($mimetype == 'application/zip') || ($mimetype == 'application/x-zip')) {
+            return $this->extractZip($extractLocation);
+        }
+        return $this->extractTar($extractLocation);
+    }
+
+    /**
+     * @param string $extractLocation
+     *
+     * @return \Robo\Result
+     */
+    protected function extractZip($extractLocation)
+    {
+        if (!extension_loaded('zlib')) {
+            return Result::errorMissingExtension($this, 'zlib', 'zip extracting');
+        }
+
+        $zip = new \ZipArchive();
+        if (($status = $zip->open($this->filename)) !== true) {
+            return Result::error($this, "Could not open zip archive {$this->filename}");
+        }
+        if (!$zip->extractTo($extractLocation)) {
+            return Result::error($this, "Could not extract zip archive {$this->filename}");
+        }
+        $zip->close();
+
+        return Result::success($this);
+    }
+
+    /**
+     * @param string $extractLocation
+     *
+     * @return \Robo\Result
+     */
+    protected function extractTar($extractLocation)
+    {
+        if (!class_exists('Archive_Tar')) {
+            return Result::errorMissingPackage($this, 'Archive_Tar', 'pear/archive_tar');
+        }
+        $tar_object = new \Archive_Tar($this->filename);
+        if (!$tar_object->extract($extractLocation)) {
+            return Result::error($this, "Could not extract tar archive {$this->filename}");
+        }
+
+        return Result::success($this);
+    }
+
+    /**
+     * @param string $filename
+     *
+     * @return bool|string
+     */
+    protected static function archiveType($filename)
+    {
+        $content_type = false;
+        if (class_exists('finfo')) {
+            $finfo = new \finfo(FILEINFO_MIME_TYPE);
+            $content_type = $finfo->file($filename);
+            // If finfo cannot determine the content type, then we will try other methods
+            if ($content_type == 'application/octet-stream') {
+                $content_type = false;
+            }
+        }
+        // Examing the file's magic header bytes.
+        if (!$content_type) {
+            if ($file = fopen($filename, 'rb')) {
+                $first = fread($file, 2);
+                fclose($file);
+                if ($first !== false) {
+                    // Interpret the two bytes as a little endian 16-bit unsigned int.
+                    $data = unpack('v', $first);
+                    switch ($data[1]) {
+                        case 0x8b1f:
+                            // First two bytes of gzip files are 0x1f, 0x8b (little-endian).
+                            // See http://www.gzip.org/zlib/rfc-gzip.html#header-trailer
+                            $content_type = 'application/x-gzip';
+                            break;
+
+                        case 0x4b50:
+                            // First two bytes of zip files are 0x50, 0x4b ('PK') (little-endian).
+                            // See http://en.wikipedia.org/wiki/Zip_(file_format)#File_headers
+                            $content_type = 'application/zip';
+                            break;
+
+                        case 0x5a42:
+                            // First two bytes of bzip2 files are 0x5a, 0x42 ('BZ') (big-endian).
+                            // See http://en.wikipedia.org/wiki/Bzip2#File_format
+                            $content_type = 'application/x-bzip2';
+                            break;
+                    }
+                }
+            }
+        }
+        // 3. Lastly if above methods didn't work, try to guess the mime type from
+        // the file extension. This is useful if the file has no identificable magic
+        // header bytes (for example tarballs).
+        if (!$content_type) {
+            // Remove querystring from the filename, if present.
+            $filename = basename(current(explode('?', $filename, 2)));
+            $extension_mimetype = array(
+                '.tar.gz' => 'application/x-gzip',
+                '.tgz' => 'application/x-gzip',
+                '.tar' => 'application/x-tar',
+            );
+            foreach ($extension_mimetype as $extension => $ct) {
+                if (substr($filename, -strlen($extension)) === $extension) {
+                    $content_type = $ct;
+                    break;
+                }
+            }
+        }
+
+        return $content_type;
+    }
+
+    /**
+     * @return string
+     */
+    protected static function getTmpDir()
+    {
+        return getcwd().'/tmp'.rand().time();
+    }
+}