Skip to content

Commit

Permalink
Add taskExtract, to extract zip and tar archives.
Browse files Browse the repository at this point in the history
  • Loading branch information
greg-1-anderson committed Feb 6, 2016
1 parent 32fe7e4 commit 88c08bb
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 0 deletions.
158 changes: 158 additions & 0 deletions src/Task/Archive/Extract.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<?php
namespace Robo\Task\Archive;

use Robo\Result;
use Robo\Task\BaseTask;

/**
* Extracts an archive.
*
* ``` php
* <?php
* $this->taskExtract($archivePath)
* ->to($destination)
* ->run();
* ?>
* ```
*
* @method to(string) location to store extracted files
*/
class Extract extends BaseTask
{
use \Robo\Common\DynamicParams;
use \Robo\Common\Timer;

protected $filename;
protected $to;

public function __construct($filename)
{
$this->filename = $filename;
}

function run() {
if (!file_exists($this->filename)) {
$this->printTaskError("File {$this->filename} does not exist");
return false;
}
if (!($mimetype = static::archiveType($this->filename))) {
$this->printTaskError("Could not determine type of archive for {$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));

// Perform the extraction of a zip file.
if (($mimetype == 'application/zip') || ($mimetype == 'application/x-zip')) {
$command = "unzip {$this->filename} -d --destination=$extractLocation";
}
// Extract a tar archive
else {
$tar_compression_flag = '';
if ($mimetype == 'application/x-gzip') {
$tar_compression_flag = 'z';
}
elseif ($mimetype == 'application/x-bzip2') {
$tar_compression_flag = 'j';
}
$command = "tar -C $extractLocation -x{$tar_compression_flag}f {$this->filename}";
}
$this->printTaskInfo("Running <info>{$command}</info>");
$this->startTimer();
exec($command, $output, $status);
$this->stopTimer();
if ($status == 0) {
// 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) {
rename($filesInExtractLocation[0], $this->to);
rmdir($extractLocation);
}
else {
rename($extractLocation, $this->to);
}
}
return new Result($this, $status, '', ['time' => $this->getExecutionTime()]);
}

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;
}

protected static function getTmpDir() {
return getcwd() . '/tmp' . rand() . time();
}

}
14 changes: 14 additions & 0 deletions src/Task/Archive/loadTasks.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php
namespace Robo\Task\Archive;

trait loadTasks
{
/**
* @param $files
* @return Concat
*/
protected function taskExtract($filename)
{
return new Extract($filename);
}
}
1 change: 1 addition & 0 deletions src/Tasks.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class Tasks
use Task\Development\loadTasks;
use Task\FileSystem\loadTasks;
use Task\File\loadTasks;
use Task\Archive\loadTasks;
use Task\Vcs\loadTasks;

// package managers
Expand Down

0 comments on commit 88c08bb

Please sign in to comment.