Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Refactor Archive to be OO and add Documentation #89

Merged
merged 5 commits into from

2 participants

@dongilbert
Collaborator

This refactors the Archive package to be Object Oriented instead of just static wrappers of functionality. I've also added the ability to override any particular adapter type with your own implementation if you prefer via the setAdapter method.

I've also started the documentation on it, but need to add in how to use setAdapter. If you like the way this is going, I'll continue with it.

@eddieajau eddieajau merged commit 5d5383f into joomla:master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
200 vendor/Joomla/Archive/Archive.php
@@ -8,8 +8,6 @@
namespace Joomla\Archive;
-use Joomla\Factory;
-use Joomla\Filesystem\Path;
use Joomla\Filesystem\File;
use Joomla\Filesystem\Folder;
@@ -26,7 +24,31 @@ class Archive
* @var array
* @since 1.0
*/
- protected static $adapters = array();
+ protected $adapters = array();
+
+ /**
+ * Holds the options array.
+ *
+ * @var mixed Array or object that implements \ArrayAccess
+ * @since 1.0
+ */
+ public $options = array();
+
+ /**
+ * Create a new Archive object.
+ *
+ * @param mixed $options An array of options or an object that implements \ArrayAccess
+ *
+ * @since 1.0
+ */
+ public function __construct($options = array())
+ {
+ // Make sure we have a tmp directory.
+ isset($options['tmp_path']) or $options['tmp_path'] = realpath(sys_get_temp_dir());
+
+ $this->options = $options;
+ }
+
/**
* Extract an archive file to a directory.
@@ -39,126 +61,81 @@ class Archive
* @since 1.0
* @throws \InvalidArgumentException
*/
- public static function extract($archivename, $extractdir)
+ public function extract($archivename, $extractdir)
{
- $untar = false;
$result = false;
- $ext = File::getExt(strtolower($archivename));
-
- // Check if a tar is embedded...gzip/bzip2 can just be plain files!
- if (File::getExt(File::stripExt(strtolower($archivename))) == 'tar')
- {
- $untar = true;
- }
+ $ext = pathinfo($archivename, PATHINFO_EXTENSION);
+ $path = pathinfo($archivename, PATHINFO_DIRNAME);
+ $filename = pathinfo($archivename, PATHINFO_FILENAME);
switch ($ext)
{
case 'zip':
- $adapter = self::getAdapter('zip');
-
- if ($adapter)
- {
- $result = $adapter->extract($archivename, $extractdir);
- }
+ $result = $this->getAdapter('zip')->extract($archivename, $extractdir);
break;
case 'tar':
- $adapter = self::getAdapter('tar');
-
- if ($adapter)
- {
- $result = $adapter->extract($archivename, $extractdir);
- }
+ $result = $this->getAdapter('tar')->extract($archivename, $extractdir);
break;
case 'tgz':
- // This format is a tarball gzip'd
- $untar = true;
-
case 'gz':
case 'gzip':
// This may just be an individual file (e.g. sql script)
- $adapter = self::getAdapter('gzip');
+ $tmpfname = $this->options['tmp_path'] . '/' . uniqid('gzip');
+ $gzresult = $this->getAdapter('gzip')->extract($archivename, $tmpfname);
- if ($adapter)
+ if ($gzresult instanceof \Exception)
{
- $config = Factory::getConfig();
- $tmpfname = $config->get('tmp_path') . '/' . uniqid('gzip');
- $gzresult = $adapter->extract($archivename, $tmpfname);
-
- if ($gzresult instanceof \Exception)
- {
- @unlink($tmpfname);
-
- return false;
- }
-
- if ($untar)
- {
- // Try to untar the file
- $tadapter = self::getAdapter('tar');
-
- if ($tadapter)
- {
- $result = $tadapter->extract($tmpfname, $extractdir);
- }
- }
- else
- {
- $path = Path::clean($extractdir);
- Folder::create($path);
- $result = File::copy($tmpfname, $path . '/' . File::stripExt(basename(strtolower($archivename))), null, 1);
- }
-
@unlink($tmpfname);
+
+ return false;
+ }
+
+ if ($ext === 'tgz' || stripos($filename, '.tar') !== false)
+ {
+ $result = $this->getAdapter('tar')->extract($tmpfname, $extractdir);
+ }
+ else
+ {
+ Folder::create($path);
+ $result = File::copy($tmpfname, $extractdir, null, 0);
}
+
+ @unlink($tmpfname);
+
break;
case 'tbz2':
- // This format is a tarball bzip2'd
- $untar = true;
-
case 'bz2':
case 'bzip2':
// This may just be an individual file (e.g. sql script)
- $adapter = self::getAdapter('bzip2');
+ $tmpfname = $this->options['tmp_path'] . '/' . uniqid('bzip2');
+ $bzresult = $this->getAdapter('bzip2')->extract($archivename, $tmpfname);
- if ($adapter)
+ if ($bzresult instanceof \Exception)
{
- $config = Factory::getConfig();
- $tmpfname = $config->get('tmp_path') . '/' . uniqid('bzip2');
- $bzresult = $adapter->extract($archivename, $tmpfname);
-
- if ($bzresult instanceof \Exception)
- {
- @unlink($tmpfname);
-
- return false;
- }
-
- if ($untar)
- {
- // Try to untar the file
- $tadapter = self::getAdapter('tar');
-
- if ($tadapter)
- {
- $result = $tadapter->extract($tmpfname, $extractdir);
- }
- }
- else
- {
- $path = Path::clean($extractdir);
- Folder::create($path);
- $result = File::copy($tmpfname, $path . '/' . File::stripExt(basename(strtolower($archivename))), null, 1);
- }
-
@unlink($tmpfname);
+
+ return false;
}
+
+ if ($ext === 'tbz2' || stripos($filename, '.tar') !== false)
+ {
+ $result = $this->getAdapter('tar')->extract($tmpfname, $extractdir);
+ }
+ else
+ {
+ Folder::create($path);
+ $result = File::copy($tmpfname, $extractdir, null, 0);
+ }
+
+ @unlink($tmpfname);
+
break;
default:
- throw new \InvalidArgumentException('Unknown Archive Type');
+ throw new \InvalidArgumentException(sprintf('Unknown archive type: %s', $ext));
}
if (!$result || $result instanceof \Exception)
@@ -170,6 +147,33 @@ public static function extract($archivename, $extractdir)
}
/**
+ * Method to override the provided adapter with your own implementation.
+ *
+ * @param string $type Name of the adapter to set.
+ * @param string $class FQCN of your class which implements ExtractableInterface.
+ * @param object $override True to force override the adapter type.
+ *
+ * @return Archive This object for chaining.
+ *
+ * @since 1.0
+ * @throws \InvalidArgumentException
+ */
+ public function setAdapter($type, $class, $override = true)
+ {
+ if (!($class instanceof ExtractableInterface))
+ {
+ throw new \InvalidArgumentException(sprintf('The provided %s adapter %s must implement Joomla\\Archive\\ExtractableInterface', $type), 500);
+ }
+
+ if ($override || !isset($this->adapters[$type]))
+ {
+ $this->adapters[$type] = new $class($this->options);
+ }
+
+ return $this;
+ }
+
+ /**
* Get a file compression adapter.
*
* @param string $type The type of adapter (bzip2|gzip|tar|zip).
@@ -179,21 +183,23 @@ public static function extract($archivename, $extractdir)
* @since 1.0
* @throws \UnexpectedValueException
*/
- public static function getAdapter($type)
+ public function getAdapter($type)
{
- if (!isset(self::$adapters[$type]))
+ $type = strtolower($type);
+
+ if (!isset($this->adapters[$type]))
{
// Try to load the adapter object
$class = 'Joomla\\Archive\\' . ucfirst($type);
- if (!class_exists($class))
+ if (!class_exists($class) || !$class::isSupported())
{
- throw new \UnexpectedValueException(sprintf('Unable to load archive adapter %s.', $type) , 500);
+ throw new \UnexpectedValueException(sprintf('Archive adapter %s not found or supported.', $type), 500);
}
- self::$adapters[$type] = new $class;
+ $this->adapters[$type] = new $class($this->options);
}
- return self::$adapters[$type];
+ return $this->adapters[$type];
}
}
View
30 vendor/Joomla/Archive/Bzip2.php
@@ -27,27 +27,41 @@ class Bzip2 implements ExtractableInterface
private $data = null;
/**
+ * Holds the options array.
+ *
+ * @var mixed Array or object that implements \ArrayAccess
+ * @since 1.0
+ */
+ protected $options = array();
+
+ /**
+ * Create a new Archive object.
+ *
+ * @param mixed $options An array of options or an object that implements \ArrayAccess
+ *
+ * @since 1.0
+ */
+ public function __construct($options = array())
+ {
+ $this->options = $options;
+ }
+
+ /**
* Extract a Bzip2 compressed file to a given path
*
* @param string $archive Path to Bzip2 archive to extract
* @param string $destination Path to extract archive to
- * @param array $options Extraction options [unused]
*
* @return boolean True if successful
*
* @since 1.0
* @throws \RuntimeException
*/
- public function extract($archive, $destination, array $options = array ())
+ public function extract($archive, $destination)
{
$this->data = null;
- if (!extension_loaded('bz2'))
- {
- throw new \RuntimeException('The bz2 extension is not available.');
- }
-
- if (!isset($options['use_streams']) || $options['use_streams'] == false)
+ if (!isset($this->options['use_streams']) || $this->options['use_streams'] == false)
{
// Old style: read the whole file and then parse it
$this->data = file_get_contents($archive);
View
2  vendor/Joomla/Archive/ExtractableInterface.php
@@ -26,7 +26,7 @@
*
* @since 1.0
*/
- public function extract($archive, $destination, array $options = array());
+ public function extract($archive, $destination);
/**
* Tests whether this adapter can unpack files on this computer.
View
31 vendor/Joomla/Archive/Gzip.php
@@ -41,27 +41,41 @@ class Gzip implements ExtractableInterface
private $data = null;
/**
+ * Holds the options array.
+ *
+ * @var mixed Array or object that implements \ArrayAccess
+ * @since 1.0
+ */
+ protected $options = array();
+
+ /**
+ * Create a new Archive object.
+ *
+ * @param mixed $options An array of options or an object that implements \ArrayAccess
+ *
+ * @since 1.0
+ */
+ public function __construct($options = array())
+ {
+ $this->options = $options;
+ }
+
+ /**
* Extract a Gzip compressed file to a given path
*
* @param string $archive Path to ZIP archive to extract
* @param string $destination Path to extract archive to
- * @param array $options Extraction options [unused]
*
* @return boolean True if successful
*
* @since 1.0
* @throws \RuntimeException
*/
- public function extract($archive, $destination, array $options = array ())
+ public function extract($archive, $destination)
{
$this->data = null;
- if (!extension_loaded('zlib'))
- {
- throw new \RuntimeException('The zlib extension is not available.');
- }
-
- if (!isset($options['use_streams']) || $options['use_streams'] == false)
+ if (!isset($this->options['use_streams']) || $this->options['use_streams'] == false)
{
$this->data = file_get_contents($archive);
@@ -124,6 +138,7 @@ public function extract($archive, $destination, array $options = array ())
$output->close();
$input->close();
}
+
return true;
}
View
55 vendor/Joomla/Archive/README.md
@@ -1 +1,56 @@
# The Archive Package
+
+The archive package will intelligently load the correct adapter for the specified archive type. It knows how to properly handle the following archive types:
+
+- zip
+- tar | tgz | tbz2
+- gz | gzip
+- bz2 | bzip2
+
+Loading files of the `t*` archive type will uncompress the archive using the appropriate adapter, and then extract via tar.
+
+## Requirements
+
+- PHP 5.3+
+- zlib extension for GZip support
+- bz2 extension for BZip2 support
+
+## Usage
+
+```php
+
+$options = array('tmp_path' => '/tmp');
+
+$archive = new Archive($options)
+
+$archive->extract(__DIR__ . '/archive.zip', __DIR__ . '/destination');
+```
+
+## Overriding Adapters
+
+If you have a custom adapter you would like to use for extracting, this package allows you to override the defaults. Just implement `ExtractableInterface` when creating your adapter, and then use the `setAdapter` method to override.
+
+```php
+
+class MyZipAdapter implements \Joomla\Archive\ExtractableInterface
+{
+ public static function isSupported()
+ {
+ // Do you test
+ return true;
+ }
+
+ public function extract($archive, $destination)
+ {
+ // Your code
+ }
+}
+
+$archive = new Archive;
+
+// You need to pass the fully qualified class name.
+$archive->setAdapter('zip', '\\MyZipAdapter');
+
+// This will use your
+$archive->extract('archive.zip', 'destination');
+```
View
23 vendor/Joomla/Archive/Tar.php
@@ -59,18 +59,37 @@ class Tar implements ExtractableInterface
private $metadata = null;
/**
+ * Holds the options array.
+ *
+ * @var mixed Array or object that implements \ArrayAccess
+ * @since 1.0
+ */
+ protected $options = array();
+
+ /**
+ * Create a new Archive object.
+ *
+ * @param mixed $options An array of options or an object that implements \ArrayAccess
+ *
+ * @since 1.0
+ */
+ public function __construct($options = array())
+ {
+ $this->options = $options;
+ }
+
+ /**
* Extract a ZIP compressed file to a given path
*
* @param string $archive Path to ZIP archive to extract
* @param string $destination Path to extract archive into
- * @param array $options Extraction options [unused]
*
* @return boolean True if successful
*
* @since 1.0
* @throws \RuntimeException
*/
- public function extract($archive, $destination, array $options = array())
+ public function extract($archive, $destination)
{
$this->data = null;
$this->metadata = null;
View
66 vendor/Joomla/Archive/Tests/ArchiveTest.php
@@ -11,7 +11,6 @@
use Joomla\Archive\Tar as ArchiveTar;
use Joomla\Archive\Gzip as ArchiveGzip;
use Joomla\Archive\Bzip2 as ArchiveBzip2;
-use Joomla\Factory;
/**
* Test class for Joomla\Archive\Archive.
@@ -20,7 +19,8 @@
*/
class ArchiveTest extends \PHPUnit_Framework_TestCase
{
- protected static $outputPath;
+ protected $fixture;
+ protected $outputPath;
/**
* Sets up the fixture.
@@ -33,12 +33,14 @@ protected function setUp()
{
parent::setUp();
- self::$outputPath = __DIR__ . '/output';
+ $this->outputPath = __DIR__ . '/output';
- if (!is_dir(self::$outputPath))
+ if (!is_dir($this->outputPath))
{
- mkdir(self::$outputPath, 0777);
+ mkdir($this->outputPath, 0777);
}
+
+ $this->fixture = new Archive;
}
/**
@@ -50,7 +52,7 @@ protected function setUp()
*/
public function testExtractZip()
{
- if (!is_dir(self::$outputPath))
+ if (!is_dir($this->outputPath))
{
$this->markTestSkipped("Couldn't create folder.");
@@ -64,12 +66,12 @@ public function testExtractZip()
return;
}
- Archive::extract(__DIR__ . '/logo.zip', self::$outputPath);
- $this->assertTrue(is_file(self::$outputPath . '/logo-zip.png'));
+ $this->fixture->extract(__DIR__ . '/logo.zip', $this->outputPath);
+ $this->assertTrue(is_file($this->outputPath . '/logo-zip.png'));
- if (is_file(self::$outputPath . '/logo-zip.png'))
+ if (is_file($this->outputPath . '/logo-zip.png'))
{
- unlink(self::$outputPath . '/logo-zip.png');
+ unlink($this->outputPath . '/logo-zip.png');
}
}
@@ -82,7 +84,7 @@ public function testExtractZip()
*/
public function testExtractTar()
{
- if (!is_dir(self::$outputPath))
+ if (!is_dir($this->outputPath))
{
$this->markTestSkipped("Couldn't create folder.");
@@ -96,12 +98,12 @@ public function testExtractTar()
return;
}
- Archive::extract(__DIR__ . '/logo.tar', self::$outputPath);
- $this->assertTrue(is_file(self::$outputPath . '/logo-tar.png'));
+ $this->fixture->extract(__DIR__ . '/logo.tar', $this->outputPath);
+ $this->assertTrue(is_file($this->outputPath . '/logo-tar.png'));
- if (is_file(self::$outputPath . '/logo-tar.png'))
+ if (is_file($this->outputPath . '/logo-tar.png'))
{
- unlink(self::$outputPath . '/logo-tar.png');
+ unlink($this->outputPath . '/logo-tar.png');
}
}
@@ -114,14 +116,14 @@ public function testExtractTar()
*/
public function testExtractGzip()
{
- if (!is_dir(self::$outputPath))
+ if (!is_dir($this->outputPath))
{
$this->markTestSkipped("Couldn't create folder.");
return;
}
- if (!is_writable(self::$outputPath) || !is_writable(Factory::getConfig()->get('tmp_path')))
+ if (!is_writable($this->outputPath) || !is_writable($this->fixture->options['tmp_path']))
{
$this->markTestSkipped("Folder not writable.");
@@ -135,12 +137,12 @@ public function testExtractGzip()
return;
}
- Archive::extract(__DIR__ . '/logo.gz', self::$outputPath . '/logo-gz.png');
- $this->assertTrue(is_file(self::$outputPath . '/logo-gz.png'));
+ $this->fixture->extract(__DIR__ . '/logo.gz', $this->outputPath . '/logo-gz.png');
+ $this->assertTrue(is_file($this->outputPath . '/logo-gz.png'));
- if (is_file(self::$outputPath . '/logo-gz.png'))
+ if (is_file($this->outputPath . '/logo-gz.png'))
{
- unlink(self::$outputPath . '/logo-gz.png');
+ unlink($this->outputPath . '/logo-gz.png');
}
}
@@ -153,14 +155,14 @@ public function testExtractGzip()
*/
public function testExtractBzip2()
{
- if (!is_dir(self::$outputPath))
+ if (!is_dir($this->outputPath))
{
$this->markTestSkipped("Couldn't create folder.");
return;
}
- if (!is_writable(self::$outputPath) || !is_writable(Factory::getConfig()->get('tmp_path')))
+ if (!is_writable($this->outputPath) || !is_writable($this->fixture->options['tmp_path']))
{
$this->markTestSkipped("Folder not writable.");
@@ -174,12 +176,12 @@ public function testExtractBzip2()
return;
}
- Archive::extract(__DIR__ . '/logo.bz2', self::$outputPath . '/logo-bz2.png');
- $this->assertTrue(is_file(self::$outputPath . '/logo-bz2.png'));
+ $this->fixture->extract(__DIR__ . '/logo.bz2', $this->outputPath . '/logo-bz2.png');
+ $this->assertTrue(is_file($this->outputPath . '/logo-bz2.png'));
- if (is_file(self::$outputPath . '/logo-bz2.png'))
+ if (is_file($this->outputPath . '/logo-bz2.png'))
{
- unlink(self::$outputPath . '/logo-bz2.png');
+ unlink($this->outputPath . '/logo-bz2.png');
}
}
@@ -192,13 +194,13 @@ public function testExtractBzip2()
*/
public function testGetAdapter()
{
- $zip = Archive::getAdapter('zip');
+ $zip = $this->fixture->getAdapter('zip');
$this->assertInstanceOf('Joomla\\Archive\\Zip', $zip);
- $bzip2 = Archive::getAdapter('bzip2');
+ $bzip2 = $this->fixture->getAdapter('bzip2');
$this->assertInstanceOf('Joomla\\Archive\\Bzip2', $bzip2);
- $gzip = Archive::getAdapter('gzip');
+ $gzip = $this->fixture->getAdapter('gzip');
$this->assertInstanceOf('Joomla\\Archive\\Gzip', $gzip);
- $tar = Archive::getAdapter('tar');
+ $tar = $this->fixture->getAdapter('tar');
$this->assertInstanceOf('Joomla\\Archive\\Tar', $tar);
}
@@ -212,6 +214,6 @@ public function testGetAdapter()
*/
public function testGetAdapterException()
{
- $zip = Archive::getAdapter('unknown');
+ $zip = $this->fixture->getAdapter('unknown');
}
}
View
40 vendor/Joomla/Archive/Zip.php
@@ -42,8 +42,16 @@ class Zip implements ExtractableInterface
* @var array
* @since 1.0
*/
- private $methods = array(0x0 => 'None', 0x1 => 'Shrunk', 0x2 => 'Super Fast', 0x3 => 'Fast', 0x4 => 'Normal', 0x5 => 'Maximum', 0x6 => 'Imploded',
- 0x8 => 'Deflated');
+ private $methods = array(
+ 0x0 => 'None',
+ 0x1 => 'Shrunk',
+ 0x2 => 'Super Fast',
+ 0x3 => 'Fast',
+ 0x4 => 'Normal',
+ 0x5 => 'Maximum',
+ 0x6 => 'Imploded',
+ 0x8 => 'Deflated'
+ );
/**
* Beginning of central directory record.
@@ -86,6 +94,26 @@ class Zip implements ExtractableInterface
private $metadata = null;
/**
+ * Holds the options array.
+ *
+ * @var mixed Array or object that implements \ArrayAccess
+ * @since 1.0
+ */
+ protected $options = array();
+
+ /**
+ * Create a new Archive object.
+ *
+ * @param mixed $options An array of options or an object that implements \ArrayAccess
+ *
+ * @since 1.0
+ */
+ public function __construct($options = array())
+ {
+ $this->options = $options;
+ }
+
+ /**
* Create a ZIP compressed file from an array of file data.
*
* @param string $archive Path to save archive.
@@ -115,14 +143,13 @@ public function create($archive, $files)
*
* @param string $archive Path to ZIP archive to extract
* @param string $destination Path to extract archive into
- * @param array $options Extraction options [unused]
*
* @return boolean True if successful
*
* @since 1.0
* @throws \RuntimeException
*/
- public function extract($archive, $destination, array $options = array())
+ public function extract($archive, $destination)
{
if (!is_file($archive))
{
@@ -200,11 +227,6 @@ protected function extractCustom($archive, $destination)
$this->data = null;
$this->metadata = null;
- if (!extension_loaded('zlib'))
- {
- throw new \RuntimeException('Zlib not supported');
- }
-
$this->data = file_get_contents($archive);
if (!$this->data)
Something went wrong with that request. Please try again.