diff --git a/includes/archiver.inc b/includes/archiver.inc new file mode 100644 index 0000000000000000000000000000000000000000..8d514794a2c9669e448be9196481b345dbc74ca4 --- /dev/null +++ b/includes/archiver.inc @@ -0,0 +1,67 @@ +<?php + +/** + * @file + * Shared classes and interfaces for the archiver system. + */ + +/** + * Common interface for all Archiver classes. + */ +interface ArchiverInterface { + + /** + * Contructor for a new archiver instance. + * + * @param $file_path + * The full system path of the archive to manipulate. Only local files + * are supported. If the file does not yet exist, it will be created if + * appropriate. + */ + public function __construct($file_path); + + /** + * Add the specified file or directory to the archive. + * + * @param $file_path + * The full system path of the file or directory to add. Only local files + * and directories are supported. + * @return + * The called object. + */ + public function add($file_path); + + /** + * Remove the specified file from the archive. + * + * @param $path + * The file name relative to the root of the archive to remove. + * @return + * The called object. + */ + public function remove($path); + + /** + * Extract multiple files in the archive to the specified path. + * + * @param $path + * A full system path of the directory to which to extract files. + * @param $files + * Optionally specify a list of files to be extracted. Files are + * relative to the root of the archive. If not specified, all files + * in the archive will be extracted + * @return + * The called object. + */ + public function extract($path, Array $files = array()); + + /** + * List all files in the archive. + * + * @return + * An array of file names relative to the root of the archive, or + * an iteratable object that resolves to such a list. + */ + public function listContents(); +} + diff --git a/includes/common.inc b/includes/common.inc index c3939b9cba2e7378c0c2798f9cfc90542f1b3807..a720395663ae8b12f65e485a931420fc7cb802cf 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -5939,3 +5939,53 @@ function xmlrpc($url) { return call_user_func_array('_xmlrpc', $args); } +/** + * Retrieve a list of all available archivers. + */ +function archiver_get_info() { + $archiver_info = &drupal_static(__FUNCTION__, array()); + + if (empty($archiver_info)) { + $cache = cache_get('archiver_info'); + if ($cache === FALSE) { + // Rebuild the cache and save it. + $archiver_info = module_invoke_all('archiver_info'); + drupal_alter('archiver_info', $archiver_info); + uasort($archiver_info, 'drupal_sort_weight'); + cache_set('archiver_info', $archiver_info); + } + else { + $archiver_info = $cache->data; + } + } + + return $archiver_info; +} + +/** + * Create the appropriate archiver for the specified file. + * + * @param $file + * The full path of the archive file. Note that stream wrapper + * paths are supported. + * @return + * A newly created instance of the archiver class appropriate + * for the specified file, already bound to that file. + */ +function archiver_get_archiver($file) { + $archiver_info = archiver_get_info(); + + foreach ($archiver_info as $implementation) { + foreach ($implementation['extensions'] as $extension) { + // Because extensions may be multi-part, such as .tar.gz, + // we cannot use simpler approaches like substr() or pathinfo(). + // This method isn't quite as clean but gets the job done. + // Also note that the file may not yet exist, so we cannot rely + // on fileinfo() or other disk-level utilities. + if (strrpos($file, '.' . $extension) === strlen($file) - strlen('.' . $extension)) { + return new $implementation['class']($file); + } + } + } +} + diff --git a/modules/system/system.api.php b/modules/system/system.api.php index c1f59785549cf22bfcf8076f2f8d23fb283edef2..1f4d1d39442591345af78c4e1344b3379f7ae60b 100644 --- a/modules/system/system.api.php +++ b/modules/system/system.api.php @@ -2398,6 +2398,34 @@ function hook_action_info_alter(&$actions) { $actions['node_unpublish_action']['label'] = t('Unpublish and remove from public view.'); } +/** + * Declare archivers to the system. + * + * An archiver is a class that is able to package and unpackage one or more files + * into a single possibly compressed file. Common examples of such files are + * zip files and tar.gz files. All archiver classes must implement + * ArchiverInterface. + * + * When mapping a + * + * Each entry should be keyed on a unique value, and specify three + * additional keys: + * - class: The name of the PHP class for this archiver. + * - extensions: An array of file extensions that this archiver supports. + * - weight: This optional key specifies the weight of this archiver. + * When mapping file extensions to archivers, the first archiver by + * weight found that supports the requested extension will be used. + */ +function system_archiver_info() { + return array( + 'tar' => array( + 'class' => 'ArchiverTar', + 'extensions' => array('tar', 'tar.gz', 'tar.bz2'), + ), + ); +} + + /** * Defines additional date types. * diff --git a/modules/system/system.archiver.inc b/modules/system/system.archiver.inc new file mode 100644 index 0000000000000000000000000000000000000000..65bf63e3a7638e8e84ce51feb43a99410450c4e2 --- /dev/null +++ b/modules/system/system.archiver.inc @@ -0,0 +1,67 @@ +<?php + +/** + * @file + * Archiver implementations provided by the system module. + */ + +/** + * Archiver for .tar files. + */ +class ArchiverTar implements ArchiverInterface { + + /** + * The underlying Archive_Tar instance that does the heavy lifting. + * + * @var Archive_Tar + */ + protected $tar; + + public function __construct($file_path) { + $this->tar = new Archive_Tar($file_path); + } + + public function add($file_path) { + $this->tar->add($file_path); + + return $this; + } + + public function remove($path) { + // @todo Archive_Tar doesn't have a remove operation + // so we'll have to simulate it somehow, probably by + // creating a new archive with everything but the removed + // file. + + return $this; + } + + public function extract($path, Array $files = array()) { + if ($files) { + $this->tar->extractList($files, $path); + } + else { + $this->tar->extract($path); + } + + return $this; + } + + public function listContents() { + return $this->tar->listContent(); + } + + /** + * Retrieve the tar engine itself. + * + * In some cases it may be necessary to directly access the underlying + * Archive_Tar object for implementation-specific logic. This is for advanced + * use only as it is not shared by other implementations of ArchiveInterface. + * + * @return + * The Archive_Tar object used by this object. + */ + public function getArchive() { + return $this->tar; + } +} diff --git a/modules/system/system.info b/modules/system/system.info index fa9a77d59ac4ff8f2dded37a617fe5f6288f9233..2f288d9d714d0168bccfa516ca940fff7e78fde6 100644 --- a/modules/system/system.info +++ b/modules/system/system.info @@ -6,6 +6,7 @@ version = VERSION core = 7.x files[] = system.module files[] = system.admin.inc +files[] = system.archiver.inc files[] = system.queue.inc files[] = image.gd.inc files[] = system.install diff --git a/modules/system/system.module b/modules/system/system.module index 70c789f8b9e03e4ff2ab9d196daf50f1699010ee..e89eb625b3b35dbe9666ace06f7511f35396134e 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -3379,6 +3379,18 @@ function system_date_format_delete($dfid) { ->execute(); } +/** + * Implement hook_archiver_info(). + */ +function system_archiver_info() { + return array( + 'tar' => array( + 'class' => 'ArchiverTar', + 'extensions' => array('tar', 'tar.gz', 'tar.bz2'), + ), + ); +} + /** * Theme confirmation forms. *