Update Composer, update everything

This commit is contained in:
Oliver Davies 2018-11-23 12:29:20 +00:00
parent ea3e94409f
commit dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions

17
.editorconfig Normal file
View file

@ -0,0 +1,17 @@
# Drupal editor configuration normalization
# @see http://editorconfig.org/
# This is the top-most .editorconfig file; do not search in parent directories.
root = true
# All files.
[*]
end_of_line = LF
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[composer.json]
indent_size = 4

57
.gitattributes vendored Normal file
View file

@ -0,0 +1,57 @@
# Drupal git normalization
# @see https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html
# @see https://www.drupal.org/node/1542048
# Normally these settings would be done with macro attributes for improved
# readability and easier maintenance. However macros can only be defined at the
# repository root directory. Drupal avoids making any assumptions about where it
# is installed.
# Define text file attributes.
# - Treat them as text.
# - Ensure no CRLF line-endings, neither on checkout nor on checkin.
# - Detect whitespace errors.
# - Exposed by default in `git diff --color` on the CLI.
# - Validate with `git diff --check`.
# - Deny applying with `git apply --whitespace=error-all`.
# - Fix automatically with `git apply --whitespace=fix`.
*.config text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
*.css text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
*.dist text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
*.engine text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
*.html text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=html
*.inc text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
*.install text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
*.js text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
*.json text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
*.lock text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
*.map text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
*.md text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
*.module text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
*.php text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
*.po text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
*.profile text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
*.script text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
*.sh text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
*.sql text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
*.svg text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
*.theme text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
*.twig text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
*.txt text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
*.xml text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
*.yml text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
# Define binary file attributes.
# - Do not treat them as text.
# - Include binary diff in patches instead of "binary files differ."
*.gif -text diff
*.gz -text diff
*.ico -text diff
*.jpeg -text diff
*.jpg -text diff
*.png -text diff
*.phar -text diff
*.exe -text diff
*.svgz -text diff
*.ttf -text diff

44
.gitignore vendored Normal file
View file

@ -0,0 +1,44 @@
# This file contains default .gitignore rules. To use it, copy it to .gitignore,
# and it will cause files like your settings.php and user-uploaded files to be
# excluded from Git version control. This is a common strategy to avoid
# accidentally including private information in public repositories and patch
# files.
#
# Because .gitignore can be specific to your site, this file has a different
# name; updating Drupal core will not override your custom .gitignore file.
# Ignore core when managing all of a project's dependencies with Composer
# including Drupal core.
# /web/core
# Core's dependencies are managed with Composer.
/web/vendor
# Ignore configuration files that may contain sensitive information.
/web/sites/*/settings*.php
!/web/sites/*/settings.docker.php
/web/sites/*/services*.yml
# Ignore paths that contain user-generated content.
/web/sites/*/files
/web/sites/*/private
# Ignore SimpleTest multi-site environment.
/web/sites/simpletest
# If you prefer to store your .gitignore file in the sites/ folder, comment
# or delete the previous settings and uncomment the following ones, instead.
# Ignore configuration files that may contain sensitive information.
# /web/*/settings*.php
# Ignore paths that contain user-generated content.
# /web/*/files
# /web/*/private
# Ignore SimpleTest multi-site environment.
# /web/simpletest
# Ignore core phpcs.xml and phpunit.xml.
/web/core/phpcs.xml
/web/core/phpunit.xml

73
composer.json Normal file
View file

@ -0,0 +1,73 @@
{
"name": "drupal-composer/drupal-project",
"description": "Project template for Drupal 8 projects with composer",
"type": "project",
"license": "GPL-2.0-or-later",
"authors": [
{
"name": "",
"role": ""
}
],
"repositories": [
{
"type": "composer",
"url": "https://packages.drupal.org/8"
}
],
"require": {
"composer/installers": "^1.2",
"cweagans/composer-patches": "^1.6.5",
"drupal-composer/drupal-scaffold": "^2.5",
"drupal/admin_toolbar": "^1.25",
"drupal/console": "^1.0.2",
"drupal/core": "^8.6",
"drupal/ctools": "^3.0",
"drupal/pathauto": "^1.3",
"drupal/stage_file_proxy": "^1.0@alpha",
"drupal/token": "^1.5",
"drupal/webform": "^5.0@RC",
"drush/drush": "^9.0.0",
"vlucas/phpdotenv": "^2.4",
"webflo/drupal-finder": "^1.0.0",
"webmozart/path-util": "^2.3"
},
"require-dev": {
"webflo/drupal-core-require-dev": "^8.6"
},
"conflict": {
"drupal/drupal": "*"
},
"minimum-stability": "dev",
"prefer-stable": true,
"config": {
"sort-packages": true
},
"scripts": {
"post-install-cmd": [
"DrupalProject\\composer\\ScriptHandler::createRequiredFiles"
],
"post-update-cmd": [
"DrupalProject\\composer\\ScriptHandler::createRequiredFiles"
]
},
"extra": {
"patchLevel": {
"drupal/core": "-p2"
},
"installer-paths": {
"web/core": ["type:drupal-core"],
"web/libraries/{$name}": ["type:drupal-library"],
"web/modules/contrib/{$name}": ["type:drupal-module"],
"web/profiles/contrib/{$name}": ["type:drupal-profile"],
"web/themes/contrib/{$name}": ["type:drupal-theme"],
"drush/Commands/{$name}": ["type:drupal-drush"]
},
"drupal-scaffold": {
"initial": {
".editorconfig": "../.editorconfig",
".gitattributes": "../.gitattributes"
}
}
}
}

8107
composer.lock generated Normal file

File diff suppressed because it is too large Load diff

21
vendor/alchemy/zippy/LICENSE vendored Normal file
View file

@ -0,0 +1,21 @@
Zippy is released under the MIT License :
Copyright (c) 2012 Alchemy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

17
vendor/alchemy/zippy/Makefile vendored Normal file
View file

@ -0,0 +1,17 @@
adapters:="ZipAdapter" "ZipExtensionAdapter" "GNUTar\\TarGNUTarAdapter" "GNUTar\\TarGzGNUTarAdapter" "GNUTar\\TarBz2GNUTarAdapter" "BSDTar\\TarBSDTarAdapter" "BSDTar\\TarGzBSDTarAdapter" "BSDTar\\TarBz2BSDTarAdapter"
.PHONY: test clean
test: node_modules
-./tests/bootstrap.sh stop
./tests/bootstrap.sh start
sleep 1
./vendor/bin/phpunit
FAILURES="";$(foreach adapter,$(adapters),ZIPPY_ADAPTER=$(adapter) ./vendor/bin/phpunit -c phpunit-functional.xml.dist || FAILURES=1;)test -z "$$FAILURES"
-./tests/bootstrap.sh stop
node_modules:
npm install connect serve-static
clean:
rm -rf node_modules

49
vendor/alchemy/zippy/composer.json vendored Normal file
View file

@ -0,0 +1,49 @@
{
"name": "alchemy/zippy",
"type": "library",
"description": "Zippy, the archive manager companion",
"keywords": ["zip", "tar", "bzip", "compression"],
"license": "MIT",
"authors": [
{
"name": "Alchemy",
"email": "dev.team@alchemy.fr",
"homepage": "http://www.alchemy.fr/"
}
],
"require": {
"php": ">=5.5",
"ext-mbstring": "*",
"doctrine/collections": "~1.0",
"symfony/filesystem": "^2.0.5|^3.0",
"symfony/process": "^2.1|^3.0"
},
"require-dev": {
"ext-zip": "*",
"guzzle/guzzle": "~3.0",
"guzzlehttp/guzzle": "^6.0",
"phpunit/phpunit": "^4.0|^5.0",
"symfony/finder": "^2.0.5|^3.0"
},
"suggest": {
"ext-zip": "To use the ZipExtensionAdapter",
"guzzlehttp/guzzle": "To use the GuzzleTeleporter with Guzzle 6",
"guzzle/guzzle": "To use the GuzzleTeleporter with Guzzle 3"
},
"autoload": {
"psr-4": {
"Alchemy\\Zippy\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Alchemy\\Zippy\\Functional\\": "tests/Functional/",
"Alchemy\\Zippy\\Tests\\": "tests/Tests/"
}
},
"extra": {
"branch-alias": {
"dev-master": "0.4.x-dev"
}
}
}

View file

@ -0,0 +1,273 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
*/
namespace Alchemy\Zippy\Adapter;
use Alchemy\Zippy\Adapter\Resource\ResourceInterface;
use Alchemy\Zippy\Adapter\VersionProbe\VersionProbeInterface;
use Alchemy\Zippy\Archive\Archive;
use Alchemy\Zippy\Archive\ArchiveInterface;
use Alchemy\Zippy\Exception\RuntimeException;
use Alchemy\Zippy\Exception\InvalidArgumentException;
use Alchemy\Zippy\Resource\PathUtil;
use Alchemy\Zippy\Resource\ResourceManager;
abstract class AbstractAdapter implements AdapterInterface
{
/** @var ResourceManager */
protected $manager;
/**
* The version probe
*
* @var VersionProbeInterface
*/
protected $probe;
public function __construct(ResourceManager $manager)
{
$this->manager = $manager;
}
/**
* @inheritdoc
*/
public function open($path)
{
$this->requireSupport();
return new Archive($this->createResource($path), $this, $this->manager);
}
/**
* @inheritdoc
*/
public function create($path, $files = null, $recursive = true)
{
$this->requireSupport();
return $this->doCreate($this->makeTargetAbsolute($path), $files, $recursive);
}
/**
* @inheritdoc
*/
public function listMembers(ResourceInterface $resource)
{
$this->requireSupport();
return $this->doListMembers($resource);
}
/**
* @inheritdoc
*/
public function add(ResourceInterface $resource, $files, $recursive = true)
{
$this->requireSupport();
return $this->doAdd($resource, $files, $recursive);
}
/**
* @inheritdoc
*/
public function remove(ResourceInterface $resource, $files)
{
$this->requireSupport();
return $this->doRemove($resource, $files);
}
/**
* @inheritdoc
*/
public function extract(ResourceInterface $resource, $to = null)
{
$this->requireSupport();
return $this->doExtract($resource, $to);
}
/**
* @inheritdoc
*/
public function extractMembers(ResourceInterface $resource, $members, $to = null, $overwrite = false)
{
$this->requireSupport();
return $this->doExtractMembers($resource, $members, $to, $overwrite);
}
/**
* Returns the version probe used by this adapter
*
* @return VersionProbeInterface
*/
public function getVersionProbe()
{
return $this->probe;
}
/**
* Sets the version probe used by this adapter
*
* @param VersionProbeInterface $probe
*
* @return VersionProbeInterface
*/
public function setVersionProbe(VersionProbeInterface $probe)
{
$this->probe = $probe;
return $this;
}
/**
* @inheritdoc
*/
public function isSupported()
{
if (!$this->probe) {
throw new RuntimeException(sprintf(
'No version probe has been set on %s whereas it is required', get_class($this)
));
}
return VersionProbeInterface::PROBE_OK === $this->probe->getStatus();
}
/**
* Throws an exception is the current adapter is not supported
*
* @throws RuntimeException
*/
protected function requireSupport()
{
if (false === $this->isSupported()) {
throw new RuntimeException(sprintf('%s is not supported on your system', get_class($this)));
}
}
/**
* Change current working directory to another
*
* @param string $target the target directory
*
* @return AdapterInterface
*
* @throws RuntimeException In case of failure
*/
protected function chdir($target)
{
if (false === @chdir($target)) {
throw new RuntimeException(sprintf('Unable to chdir to `%s`', $target));
}
return $this;
}
/**
* Creates a resource given a path
*
* @param string $path
*
* @return ResourceInterface
*/
abstract protected function createResource($path);
/**
* Do the removal after having check that the current adapter is supported
*
* @param ResourceInterface $resource
* @param array $files
*
* @return array
*/
abstract protected function doRemove(ResourceInterface $resource, $files);
/**
* Do the add after having check that the current adapter is supported
*
* @param ResourceInterface $resource
* @param array $files
* @param bool $recursive
*
* @return array
*/
abstract protected function doAdd(ResourceInterface $resource, $files, $recursive);
/**
* Do the extract after having check that the current adapter is supported
*
* @param ResourceInterface $resource
* @param $to
*
* @return \SplFileInfo The extracted archive
*/
abstract protected function doExtract(ResourceInterface $resource, $to);
/**
* Do the extract members after having check that the current adapter is supported
*
* @param ResourceInterface $resource
* @param string|string[] $members
* @param string $to
* @param bool $overwrite
*
* @return \SplFileInfo The extracted archive
*/
abstract protected function doExtractMembers(ResourceInterface $resource, $members, $to, $overwrite = false);
/**
* Do the list members after having check that the current adapter is supported
*
* @param ResourceInterface $resource
*
* @return array
*/
abstract protected function doListMembers(ResourceInterface $resource);
/**
* Do the create after having check that the current adapter is supported
*
* @param string $path
* @param string $file
* @param bool $recursive
*
* @return ArchiveInterface
*/
abstract protected function doCreate($path, $file, $recursive);
/**
* Makes the target path absolute as the adapters might have a different directory
*
* @param string $path The path to convert
*
* @return string The absolute path
*
* @throws InvalidArgumentException In case the path is not writable or does not exist
*/
private function makeTargetAbsolute($path)
{
$directory = dirname($path);
if (!is_dir($directory)) {
throw new InvalidArgumentException(sprintf('Target path %s does not exist.', $directory));
}
if (!is_writable($directory)) {
throw new InvalidArgumentException(sprintf('Target path %s is not writeable.', $directory));
}
return realpath($directory) . '/' . PathUtil::basename($path);
}
}

View file

@ -0,0 +1,240 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
*/
namespace Alchemy\Zippy\Adapter;
use Alchemy\Zippy\Adapter\Resource\FileResource;
use Alchemy\Zippy\Archive\MemberInterface;
use Alchemy\Zippy\Exception\RuntimeException;
use Alchemy\Zippy\Exception\InvalidArgumentException;
use Alchemy\Zippy\Parser\ParserFactory;
use Alchemy\Zippy\Parser\ParserInterface;
use Alchemy\Zippy\ProcessBuilder\ProcessBuilderFactory;
use Alchemy\Zippy\ProcessBuilder\ProcessBuilderFactoryInterface;
use Alchemy\Zippy\Resource\ResourceManager;
use Symfony\Component\Process\ExecutableFinder;
use Symfony\Component\Process\ProcessBuilder;
abstract class AbstractBinaryAdapter extends AbstractAdapter implements BinaryAdapterInterface
{
/**
* The parser to use to parse command output
*
* @var ParserInterface
*/
protected $parser;
/**
* The deflator process builder factory to use to build binary command line
*
* @var ProcessBuilderFactoryInterface
*/
protected $deflator;
/**
* The inflator process builder factory to use to build binary command line
*
* @var ProcessBuilderFactoryInterface
*/
protected $inflator;
/**
* Constructor
*
* @param ParserInterface $parser An output parser
* @param ResourceManager $manager A resource manager
* @param ProcessBuilderFactoryInterface $inflator A process builder factory for the inflator binary
* @param ProcessBuilderFactoryInterface $deflator A process builder factory for the deflator binary
*/
public function __construct(
ParserInterface $parser,
ResourceManager $manager,
ProcessBuilderFactoryInterface $inflator,
ProcessBuilderFactoryInterface $deflator
) {
$this->parser = $parser;
parent::__construct($manager);
$this->deflator = $deflator;
$this->inflator = $inflator;
}
/**
* @inheritdoc
*/
public function getParser()
{
return $this->parser;
}
/**
* @inheritdoc
*/
public function setParser(ParserInterface $parser)
{
$this->parser = $parser;
return $this;
}
/**
* @inheritdoc
*/
public function getDeflator()
{
return $this->deflator;
}
/**
* @inheritdoc
*/
public function getInflator()
{
return $this->inflator;
}
/**
* @inheritdoc
*/
public function setDeflator(ProcessBuilderFactoryInterface $processBuilder)
{
$this->deflator = $processBuilder;
return $this;
}
public function setInflator(ProcessBuilderFactoryInterface $processBuilder)
{
$this->inflator = $processBuilder;
return $this;
}
/**
* @inheritdoc
*/
public function getInflatorVersion()
{
$this->requireSupport();
return $this->doGetInflatorVersion();
}
/**
* @inheritdoc
*/
public function getDeflatorVersion()
{
$this->requireSupport();
return $this->doGetDeflatorVersion();
}
/**
* Returns a new instance of the invoked adapter
*
* @param ExecutableFinder $finder
* @param ResourceManager $manager
* @param string|null $inflatorBinaryName The inflator binary name to use
* @param string|null $deflatorBinaryName The deflator binary name to use
*
* @return AbstractBinaryAdapter
*/
public static function newInstance(
ExecutableFinder $finder,
ResourceManager $manager,
$inflatorBinaryName = null,
$deflatorBinaryName = null
) {
$inflator = $inflatorBinaryName instanceof ProcessBuilderFactoryInterface ? $inflatorBinaryName : self::findABinary($inflatorBinaryName,
static::getDefaultInflatorBinaryName(), $finder);
$deflator = $deflatorBinaryName instanceof ProcessBuilderFactoryInterface ? $deflatorBinaryName : self::findABinary($deflatorBinaryName,
static::getDefaultDeflatorBinaryName(), $finder);
try {
$outputParser = ParserFactory::create(static::getName());
} catch (InvalidArgumentException $e) {
throw new RuntimeException(sprintf(
'Failed to get a new instance of %s',
get_called_class()), $e->getCode(), $e
);
}
if (null === $inflator) {
throw new RuntimeException(sprintf('Unable to create the inflator'));
}
if (null === $deflator) {
throw new RuntimeException(sprintf('Unable to create the deflator'));
}
return new static($outputParser, $manager, $inflator, $deflator);
}
private static function findABinary($wish, array $defaults, ExecutableFinder $finder)
{
$possibles = $wish ? (array) $wish : $defaults;
$binary = null;
foreach ($possibles as $possible) {
if (null !== $found = $finder->find($possible)) {
$binary = new ProcessBuilderFactory($found);
break;
}
}
return $binary;
}
/**
* Adds files to argument list
*
* @param MemberInterface[]|\SplFileInfo[]|string[] $files An array of files
* @param ProcessBuilder $builder A Builder instance
*
* @return bool
*/
protected function addBuilderFileArgument(array $files, ProcessBuilder $builder)
{
$iterations = 0;
array_walk($files, function($file) use ($builder, &$iterations) {
$builder->add(
$file instanceof \SplFileInfo ?
$file->getRealPath() : ($file instanceof MemberInterface ? $file->getLocation() : $file)
);
$iterations++;
});
return 0 !== $iterations;
}
protected function createResource($path)
{
return new FileResource($path);
}
/**
* Fetch the inflator version after having check that the current adapter is supported
*
* @return string
*/
abstract protected function doGetInflatorVersion();
/**
* Fetch the Deflator version after having check that the current adapter is supported
*
* @return string
*/
abstract protected function doGetDeflatorVersion();
}

View file

@ -0,0 +1,438 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Adapter;
use Alchemy\Zippy\Adapter\Resource\ResourceInterface;
use Alchemy\Zippy\Archive\Archive;
use Alchemy\Zippy\Archive\Member;
use Alchemy\Zippy\Exception\RuntimeException;
use Alchemy\Zippy\Exception\InvalidArgumentException;
use Alchemy\Zippy\Resource\Resource as ZippyResource;
use Symfony\Component\Process\Exception\ExceptionInterface as ProcessException;
abstract class AbstractTarAdapter extends AbstractBinaryAdapter
{
/**
* @inheritdoc
*/
protected function doCreate($path, $files, $recursive)
{
return $this->doTarCreate($this->getLocalOptions(), $path, $files, $recursive);
}
/**
* @inheritdoc
*/
protected function doListMembers(ResourceInterface $resource)
{
return $this->doTarListMembers($this->getLocalOptions(), $resource);
}
/**
* @inheritdoc
*/
protected function doAdd(ResourceInterface $resource, $files, $recursive)
{
return $this->doTarAdd($this->getLocalOptions(), $resource, $files, $recursive);
}
/**
* @inheritdoc
*/
protected function doRemove(ResourceInterface $resource, $files)
{
return $this->doTarRemove($this->getLocalOptions(), $resource, $files);
}
/**
* @inheritdoc
*/
protected function doExtractMembers(ResourceInterface $resource, $members, $to, $overwrite = false)
{
return $this->doTarExtractMembers($this->getLocalOptions(), $resource, $members, $to, $overwrite);
}
/**
* @inheritdoc
*/
protected function doExtract(ResourceInterface $resource, $to)
{
return $this->doTarExtract($this->getLocalOptions(), $resource, $to);
}
/**
* @inheritdoc
*/
protected function doGetInflatorVersion()
{
$process = $this
->inflator
->create()
->add('--version')
->getProcess();
$process->run();
if (!$process->isSuccessful()) {
throw new RuntimeException(sprintf(
'Unable to execute the following command %s {output: %s}',
$process->getCommandLine(), $process->getErrorOutput()
));
}
return $this->parser->parseInflatorVersion($process->getOutput() ?: '');
}
/**
* @inheritdoc
*/
protected function doGetDeflatorVersion()
{
return $this->getInflatorVersion();
}
protected function doTarCreate($options, $path, $files = null, $recursive = true)
{
$files = (array) $files;
$builder = $this
->inflator
->create();
if (!$recursive) {
$builder->add('--no-recursion');
}
$builder->add('-c');
foreach ((array) $options as $option) {
$builder->add((string) $option);
}
if (0 === count($files)) {
$nullFile = defined('PHP_WINDOWS_VERSION_BUILD') ? 'NUL' : '/dev/null';
$builder->add('-f');
$builder->add($path);
$builder->add('-T');
$builder->add($nullFile);
$process = $builder->getProcess();
$process->run();
} else {
$builder->add(sprintf('--file=%s', $path));
if (!$recursive) {
$builder->add('--no-recursion');
}
$collection = $this->manager->handle(getcwd(), $files);
$builder->setWorkingDirectory($collection->getContext());
$collection->forAll(function($i, ZippyResource $resource) use ($builder) {
return $builder->add($resource->getTarget());
});
$process = $builder->getProcess();
try {
$process->run();
} catch (ProcessException $e) {
$this->manager->cleanup($collection);
throw $e;
}
$this->manager->cleanup($collection);
}
if (!$process->isSuccessful()) {
throw new RuntimeException(sprintf(
'Unable to execute the following command %s {output: %s}',
$process->getCommandLine(),
$process->getErrorOutput()
));
}
return new Archive($this->createResource($path), $this, $this->manager);
}
protected function doTarListMembers($options, ResourceInterface $resource)
{
$builder = $this
->inflator
->create();
foreach ($this->getListMembersOptions() as $option) {
$builder->add($option);
}
$builder
->add('--list')
->add('-v')
->add(sprintf('--file=%s', $resource->getResource()));
foreach ((array) $options as $option) {
$builder->add((string) $option);
}
$process = $builder->getProcess();
$process->run();
if (!$process->isSuccessful()) {
throw new RuntimeException(sprintf(
'Unable to execute the following command %s {output: %s}',
$process->getCommandLine(),
$process->getErrorOutput()
));
}
$members = array();
foreach ($this->parser->parseFileListing($process->getOutput() ?: '') as $member) {
$members[] = new Member(
$resource,
$this,
$member['location'],
$member['size'],
$member['mtime'],
$member['is_dir']
);
}
return $members;
}
protected function doTarAdd($options, ResourceInterface $resource, $files, $recursive = true)
{
$files = (array) $files;
$builder = $this
->inflator
->create();
if (!$recursive) {
$builder->add('--no-recursion');
}
$builder
->add('--append')
->add(sprintf('--file=%s', $resource->getResource()));
foreach ((array) $options as $option) {
$builder->add((string) $option);
}
// there will be an issue if the file starts with a dash
// see --add-file=FILE
$collection = $this->manager->handle(getcwd(), $files);
$builder->setWorkingDirectory($collection->getContext());
$collection->forAll(function($i, ZippyResource $resource) use ($builder) {
return $builder->add($resource->getTarget());
});
$process = $builder->getProcess();
try {
$process->run();
} catch (ProcessException $e) {
$this->manager->cleanup($collection);
throw $e;
}
$this->manager->cleanup($collection);
if (!$process->isSuccessful()) {
throw new RuntimeException(sprintf(
'Unable to execute the following command %s {output: %s}',
$process->getCommandLine(),
$process->getErrorOutput()
));
}
return $files;
}
protected function doTarRemove($options, ResourceInterface $resource, $files)
{
$files = (array) $files;
$builder = $this
->inflator
->create();
$builder
->add('--delete')
->add(sprintf('--file=%s', $resource->getResource()));
foreach ((array) $options as $option) {
$builder->add((string) $option);
}
if (!$this->addBuilderFileArgument($files, $builder)) {
throw new InvalidArgumentException('Invalid files');
}
$process = $builder->getProcess();
$process->run();
if (!$process->isSuccessful()) {
throw new RuntimeException(sprintf(
'Unable to execute the following command %s {output: %s}',
$process->getCommandLine(),
$process->getErrorOutput()
));
}
return $files;
}
protected function doTarExtract($options, ResourceInterface $resource, $to = null)
{
if (null !== $to && !is_dir($to)) {
throw new InvalidArgumentException(sprintf("%s is not a directory", $to));
}
$builder = $this
->inflator
->create();
$builder
->add('--extract')
->add(sprintf('--file=%s', $resource->getResource()));
foreach ($this->getExtractOptions() as $option) {
$builder
->add($option);
}
foreach ((array) $options as $option) {
$builder->add((string) $option);
}
if (null !== $to) {
$builder
->add('--directory')
->add($to);
}
$process = $builder->getProcess();
$process->run();
if (!$process->isSuccessful()) {
throw new RuntimeException(sprintf(
'Unable to execute the following command %s {output: %s}',
$process->getCommandLine(),
$process->getErrorOutput()
));
}
return new \SplFileInfo($to ?: $resource->getResource());
}
/**
* @param array $options
* @param ResourceInterface $resource
* @param array $members
* @param string $to
* @param bool $overwrite
*
* @return array
*/
protected function doTarExtractMembers($options, ResourceInterface $resource, $members, $to = null, $overwrite = false)
{
if (null !== $to && !is_dir($to)) {
throw new InvalidArgumentException(sprintf("%s is not a directory", $to));
}
$members = (array) $members;
$builder = $this
->inflator
->create();
if ($overwrite == false) {
$builder->add('-k');
}
$builder
->add('--extract')
->add(sprintf('--file=%s', $resource->getResource()));
foreach ($this->getExtractMembersOptions() as $option) {
$builder
->add($option);
}
foreach ((array) $options as $option) {
$builder->add((string) $option);
}
if (null !== $to) {
$builder
->add('--directory')
->add($to);
}
if (!$this->addBuilderFileArgument($members, $builder)) {
throw new InvalidArgumentException('Invalid files');
}
$process = $builder->getProcess();
$process->run();
if (!$process->isSuccessful()) {
throw new RuntimeException(sprintf(
'Unable to execute the following command %s {output: %s}',
$process->getCommandLine(),
$process->getErrorOutput()
));
}
return $members;
}
/**
* Returns an array of option for the listMembers command
*
* @return array
*/
abstract protected function getListMembersOptions();
/**
* Returns an array of option for the extract command
*
* @return array
*/
abstract protected function getExtractOptions();
/**
* Returns an array of option for the extractMembers command
*
* @return array
*/
abstract protected function getExtractMembersOptions();
/**
* Gets adapter specific additional options
*
* @return array
*/
abstract protected function getLocalOptions();
}

View file

@ -0,0 +1,222 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Adapter;
use Alchemy\Zippy\Adapter\BSDTar\TarBSDTarAdapter;
use Alchemy\Zippy\Adapter\BSDTar\TarBz2BSDTarAdapter;
use Alchemy\Zippy\Adapter\BSDTar\TarGzBSDTarAdapter;
use Alchemy\Zippy\Adapter\GNUTar\TarBz2GNUTarAdapter;
use Alchemy\Zippy\Adapter\GNUTar\TarGNUTarAdapter;
use Alchemy\Zippy\Adapter\GNUTar\TarGzGNUTarAdapter;
use Alchemy\Zippy\Resource\RequestMapper;
use Alchemy\Zippy\Resource\ResourceManager;
use Alchemy\Zippy\Resource\ResourceTeleporter;
use Alchemy\Zippy\Resource\TargetLocator;
use Alchemy\Zippy\Resource\TeleporterContainer;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Process\ExecutableFinder;
class AdapterContainer implements \ArrayAccess
{
private $items = array();
/**
* Builds the adapter container
*
* @return AdapterContainer
*/
public static function load()
{
$container = new static();
$container['zip.inflator'] = null;
$container['zip.deflator'] = null;
$container['resource-manager'] = function($container) {
return new ResourceManager(
$container['request-mapper'],
$container['resource-teleporter'],
$container['filesystem']
);
};
$container['executable-finder'] = function($container) {
return new ExecutableFinder();
};
$container['request-mapper'] = function($container) {
return new RequestMapper($container['target-locator']);
};
$container['target-locator'] = function() {
return new TargetLocator();
};
$container['teleporter-container'] = function($container) {
return TeleporterContainer::load();
};
$container['resource-teleporter'] = function($container) {
return new ResourceTeleporter($container['teleporter-container']);
};
$container['filesystem'] = function() {
return new Filesystem();
};
$container['Alchemy\\Zippy\\Adapter\\ZipAdapter'] = function($container) {
return ZipAdapter::newInstance(
$container['executable-finder'],
$container['resource-manager'],
$container['zip.inflator'],
$container['zip.deflator']
);
};
$container['gnu-tar.inflator'] = null;
$container['gnu-tar.deflator'] = null;
$container['Alchemy\\Zippy\\Adapter\\GNUTar\\TarGNUTarAdapter'] = function($container) {
return TarGNUTarAdapter::newInstance(
$container['executable-finder'],
$container['resource-manager'],
$container['gnu-tar.inflator'],
$container['gnu-tar.deflator']
);
};
$container['Alchemy\\Zippy\\Adapter\\GNUTar\\TarGzGNUTarAdapter'] = function($container) {
return TarGzGNUTarAdapter::newInstance(
$container['executable-finder'],
$container['resource-manager'],
$container['gnu-tar.inflator'],
$container['gnu-tar.deflator']
);
};
$container['Alchemy\\Zippy\\Adapter\\GNUTar\\TarBz2GNUTarAdapter'] = function($container) {
return TarBz2GNUTarAdapter::newInstance(
$container['executable-finder'],
$container['resource-manager'],
$container['gnu-tar.inflator'],
$container['gnu-tar.deflator']
);
};
$container['bsd-tar.inflator'] = null;
$container['bsd-tar.deflator'] = null;
$container['Alchemy\\Zippy\\Adapter\\BSDTar\\TarBSDTarAdapter'] = function($container) {
return TarBSDTarAdapter::newInstance(
$container['executable-finder'],
$container['resource-manager'],
$container['bsd-tar.inflator'],
$container['bsd-tar.deflator']
);
};
$container['Alchemy\\Zippy\\Adapter\\BSDTar\\TarGzBSDTarAdapter'] = function($container) {
return TarGzBSDTarAdapter::newInstance(
$container['executable-finder'],
$container['resource-manager'],
$container['bsd-tar.inflator'],
$container['bsd-tar.deflator']
);
};
$container['Alchemy\\Zippy\\Adapter\\BSDTar\\TarBz2BSDTarAdapter'] = function($container) {
return TarBz2BSDTarAdapter::newInstance(
$container['executable-finder'],
$container['resource-manager'],
$container['bsd-tar.inflator'],
$container['bsd-tar.deflator']);
};
$container['Alchemy\\Zippy\\Adapter\\ZipExtensionAdapter'] = function() {
return ZipExtensionAdapter::newInstance();
};
return $container;
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Whether a offset exists
*
* @link http://php.net/manual/en/arrayaccess.offsetexists.php
*
* @param mixed $offset <p>
* An offset to check for.
* </p>
*
* @return bool true on success or false on failure.
* <p>The return value will be casted to boolean if non-boolean was returned.</p>
*/
public function offsetExists($offset)
{
return isset($this->items[$offset]);
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Offset to retrieve
* @link http://php.net/manual/en/arrayaccess.offsetget.php
* @param mixed $offset <p>
* The offset to retrieve.
* </p>
* @return mixed Can return all value types.
*/
public function offsetGet($offset)
{
if (array_key_exists($offset, $this->items) && is_callable($this->items[$offset])) {
$this->items[$offset] = call_user_func($this->items[$offset], $this);
}
if (array_key_exists($offset, $this->items)) {
return $this->items[$offset];
}
throw new \InvalidArgumentException();
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Offset to set
* @link http://php.net/manual/en/arrayaccess.offsetset.php
* @param mixed $offset <p>
* The offset to assign the value to.
* </p>
* @param mixed $value <p>
* The value to set.
* </p>
* @return void
*/
public function offsetSet($offset, $value)
{
$this->items[$offset] = $value;
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Offset to unset
* @link http://php.net/manual/en/arrayaccess.offsetunset.php
* @param mixed $offset <p>
* The offset to unset.
* </p>
* @return void
*/
public function offsetUnset($offset)
{
unset($this->items[$offset]);
}
}

View file

@ -0,0 +1,133 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Adapter;
use Alchemy\Zippy\Adapter\Resource\ResourceInterface;
use Alchemy\Zippy\Archive\ArchiveInterface;
use Alchemy\Zippy\Exception\NotSupportedException;
use Alchemy\Zippy\Exception\RuntimeException;
use Alchemy\Zippy\Exception\InvalidArgumentException;
Interface AdapterInterface
{
/**
* Opens an archive
*
* @param string $path The path to the archive
*
* @return ArchiveInterface
*
* @throws InvalidArgumentException In case the provided path is not valid
* @throws RuntimeException In case of failure
*/
public function open($path);
/**
* Creates a new archive
*
* Please note some adapters can not create empty archives.
* They would throw a `NotSupportedException` in case you ask to create an archive without files
*
* @param string $path The path to the archive
* @param string|string[]|\Traversable|null $files A filename, an array of files, or a \Traversable instance
* @param bool $recursive Whether to recurse or not in the provided directories
*
* @return ArchiveInterface
*
* @throws RuntimeException In case of failure
* @throws NotSupportedException In case the operation in not supported
* @throws InvalidArgumentException In case no files could be added
*/
public function create($path, $files = null, $recursive = true);
/**
* Tests if the adapter is supported by the current environment
*
* @return bool
*/
public function isSupported();
/**
* Returns the list of all archive members
*
* @param ResourceInterface $resource The path to the archive
*
* @return array
*
* @throws RuntimeException In case of failure
*/
public function listMembers(ResourceInterface $resource);
/**
* Adds a file to the archive
*
* @param ResourceInterface $resource The path to the archive
* @param string|array|\Traversable $files An array of paths to add, relative to cwd
* @param bool $recursive Whether or not to recurse in the provided directories
*
* @return array
*
* @throws RuntimeException In case of failure
* @throws InvalidArgumentException In case no files could be added
*/
public function add(ResourceInterface $resource, $files, $recursive = true);
/**
* Removes a member of the archive
*
* @param ResourceInterface $resource The path to the archive
* @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance
*
* @return array
*
* @throws RuntimeException In case of failure
* @throws InvalidArgumentException In case no files could be removed
*/
public function remove(ResourceInterface $resource, $files);
/**
* Extracts an entire archive
*
* Note that any existing files will be overwritten by the adapter
*
* @param ResourceInterface $resource The path to the archive
* @param string|null $to The path where to extract the archive
*
* @return \SplFileInfo The extracted archive
*
* @throws RuntimeException In case of failure
* @throws InvalidArgumentException In case the provided path where to extract the archive is not valid
*/
public function extract(ResourceInterface $resource, $to = null);
/**
* Extracts specific members of the archive
*
* @param ResourceInterface $resource The path to the archive
* @param string|string[] $members A path or array of paths matching the members to extract from the resource.
* @param string|null $to The path where to extract the members
* @param bool $overwrite Whether to overwrite existing files in target directory
*
* @return \SplFileInfo The extracted archive
*
* @throws RuntimeException In case of failure
* @throws InvalidArgumentException In case no members could be removed or providedd extract target directory is not valid
*/
public function extractMembers(ResourceInterface $resource, $members, $to = null, $overwrite = false);
/**
* Returns the adapter name
*
* @return string
*/
public static function getName();
}

View file

@ -0,0 +1,79 @@
<?php
namespace Alchemy\Zippy\Adapter\BSDTar;
use Alchemy\Zippy\Adapter\AbstractTarAdapter;
use Alchemy\Zippy\Adapter\VersionProbe\BSDTarVersionProbe;
use Alchemy\Zippy\Parser\ParserInterface;
use Alchemy\Zippy\Resource\ResourceManager;
use Alchemy\Zippy\ProcessBuilder\ProcessBuilderFactoryInterface;
/**
* BSDTAR allows you to create and extract files from archives using BSD tar
*
* @see http://people.freebsd.org/~kientzle/libarchive/man/bsdtar.1.txt
*/
class TarBSDTarAdapter extends AbstractTarAdapter
{
public function __construct(ParserInterface $parser, ResourceManager $manager, ProcessBuilderFactoryInterface $inflator, ProcessBuilderFactoryInterface $deflator)
{
parent::__construct($parser, $manager, $inflator, $deflator);
$this->probe = new BSDTarVersionProbe($inflator, $deflator);
}
/**
* @inheritdoc
*/
protected function getLocalOptions()
{
return array();
}
/**
* @inheritdoc
*/
public static function getName()
{
return 'bsd-tar';
}
/**
* @inheritdoc
*/
public static function getDefaultDeflatorBinaryName()
{
return array('bsdtar', 'tar');
}
/**
* @inheritdoc
*/
public static function getDefaultInflatorBinaryName()
{
return array('bsdtar', 'tar');
}
/**
* {@inheritdoc}
*/
protected function getListMembersOptions()
{
return array();
}
/**
* {@inheritdoc}
*/
protected function getExtractOptions()
{
return array();
}
/**
* {@inheritdoc}
*/
protected function getExtractMembersOptions()
{
return array();
}
}

View file

@ -0,0 +1,25 @@
<?php
namespace Alchemy\Zippy\Adapter\BSDTar;
use Alchemy\Zippy\Adapter\Resource\ResourceInterface;
use Alchemy\Zippy\Exception\NotSupportedException;
class TarBz2BSDTarAdapter extends TarBSDTarAdapter
{
/**
* @inheritdoc
*/
protected function doAdd(ResourceInterface $resource, $files, $recursive)
{
throw new NotSupportedException('Updating a compressed tar archive is not supported.');
}
/**
* @inheritdoc
*/
protected function getLocalOptions()
{
return array('--bzip2');
}
}

View file

@ -0,0 +1,25 @@
<?php
namespace Alchemy\Zippy\Adapter\BSDTar;
use Alchemy\Zippy\Adapter\Resource\ResourceInterface;
use Alchemy\Zippy\Exception\NotSupportedException;
class TarGzBSDTarAdapter extends TarBSDTarAdapter
{
/**
* @inheritdoc
*/
protected function doAdd(ResourceInterface $resource, $files, $recursive)
{
throw new NotSupportedException('Updating a compressed tar archive is not supported.');
}
/**
* @inheritdoc
*/
protected function getLocalOptions()
{
return array('--gzip');
}
}

View file

@ -0,0 +1,94 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Adapter;
use Alchemy\Zippy\Parser\ParserInterface;
use Alchemy\Zippy\ProcessBuilder\ProcessBuilderFactoryInterface;
interface BinaryAdapterInterface
{
/**
* Gets the output parser
*
* @return ParserInterface
*/
public function getParser();
/**
* Sets the parser
*
* @param ParserInterface $parser The parser to use
*
* @return AbstractBinaryAdapter
*/
public function setParser(ParserInterface $parser);
/**
* Returns the inflator process builder
*
* @return ProcessBuilderFactoryInterface
*/
public function getInflator();
/**
* Sets the inflator process builder
*
* @param ProcessBuilderFactoryInterface $processBuilder The parser to use
*
* @return AbstractBinaryAdapter
*/
public function setInflator(ProcessBuilderFactoryInterface $processBuilder);
/**
* Returns the deflator process builder
*
* @return ProcessBuilderFactoryInterface
*/
public function getDeflator();
/**
* Sets the deflator process builder
*
* @param ProcessBuilderFactoryInterface $processBuilder The parser to use
*
* @return AbstractBinaryAdapter
*/
public function setDeflator(ProcessBuilderFactoryInterface $processBuilder);
/**
* Returns the inflator binary version
*
* @return string
*/
public function getInflatorVersion();
/**
* Returns the deflator binary version
*
* @return string
*/
public function getDeflatorVersion();
/**
* Gets the inflator adapter binary name
*
* @return array
*/
public static function getDefaultInflatorBinaryName();
/**
* Gets the deflator adapter binary name
*
* @return array
*/
public static function getDefaultDeflatorBinaryName();
}

View file

@ -0,0 +1,25 @@
<?php
namespace Alchemy\Zippy\Adapter\GNUTar;
use Alchemy\Zippy\Adapter\Resource\ResourceInterface;
use Alchemy\Zippy\Exception\NotSupportedException;
class TarBz2GNUTarAdapter extends TarGNUTarAdapter
{
/**
* @inheritdoc
*/
protected function doAdd(ResourceInterface $resource, $files, $recursive)
{
throw new NotSupportedException('Updating a compressed tar archive is not supported.');
}
/**
* @inheritdoc
*/
protected function getLocalOptions()
{
return array('--bzip2');
}
}

View file

@ -0,0 +1,79 @@
<?php
namespace Alchemy\Zippy\Adapter\GNUTar;
use Alchemy\Zippy\Adapter\AbstractTarAdapter;
use Alchemy\Zippy\Adapter\VersionProbe\GNUTarVersionProbe;
use Alchemy\Zippy\Parser\ParserInterface;
use Alchemy\Zippy\Resource\ResourceManager;
use Alchemy\Zippy\ProcessBuilder\ProcessBuilderFactoryInterface;
/**
* GNUTarAdapter allows you to create and extract files from archives using GNU tar
*
* @see http://www.gnu.org/software/tar/manual/tar.html
*/
class TarGNUTarAdapter extends AbstractTarAdapter
{
public function __construct(ParserInterface $parser, ResourceManager $manager, ProcessBuilderFactoryInterface $inflator, ProcessBuilderFactoryInterface $deflator)
{
parent::__construct($parser, $manager, $inflator, $deflator);
$this->probe = new GNUTarVersionProbe($inflator, $deflator);
}
/**
* @inheritdoc
*/
protected function getLocalOptions()
{
return array();
}
/**
* @inheritdoc
*/
public static function getName()
{
return 'gnu-tar';
}
/**
* @inheritdoc
*/
public static function getDefaultDeflatorBinaryName()
{
return array('gnutar', 'tar');
}
/**
* @inheritdoc
*/
public static function getDefaultInflatorBinaryName()
{
return array('gnutar', 'tar');
}
/**
* {@inheritdoc}
*/
protected function getListMembersOptions()
{
return array('--utc');
}
/**
* {@inheritdoc}
*/
protected function getExtractOptions()
{
return array('--overwrite');
}
/**
* {@inheritdoc}
*/
protected function getExtractMembersOptions()
{
return array('--overwrite');
}
}

View file

@ -0,0 +1,25 @@
<?php
namespace Alchemy\Zippy\Adapter\GNUTar;
use Alchemy\Zippy\Adapter\Resource\ResourceInterface;
use Alchemy\Zippy\Exception\NotSupportedException;
class TarGzGNUTarAdapter extends TarGNUTarAdapter
{
/**
* @inheritdoc
*/
protected function doAdd(ResourceInterface $resource, $files, $recursive)
{
throw new NotSupportedException('Updating a compressed tar archive is not supported.');
}
/**
* @inheritdoc
*/
protected function getLocalOptions()
{
return array('--gzip');
}
}

View file

@ -0,0 +1,31 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
*/
namespace Alchemy\Zippy\Adapter\Resource;
class FileResource implements ResourceInterface
{
private $path;
public function __construct($path)
{
$this->path = $path;
}
/**
* {@inheritdoc}
*/
public function getResource()
{
return $this->path;
}
}

View file

@ -0,0 +1,23 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
*/
namespace Alchemy\Zippy\Adapter\Resource;
interface ResourceInterface
{
/**
* Returns the actual resource used by an adapter
*
* @return mixed
*/
public function getResource();
}

View file

@ -0,0 +1,31 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
*/
namespace Alchemy\Zippy\Adapter\Resource;
class ZipArchiveResource implements ResourceInterface
{
private $archive;
public function __construct(\ZipArchive $archive)
{
$this->archive = $archive;
}
/**
* {@inheritdoc}
*/
public function getResource()
{
return $this->archive;
}
}

View file

@ -0,0 +1,77 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
*/
namespace Alchemy\Zippy\Adapter\VersionProbe;
use Alchemy\Zippy\ProcessBuilder\ProcessBuilderFactoryInterface;
use Symfony\Component\Process\Process;
abstract class AbstractTarVersionProbe implements VersionProbeInterface
{
private $isSupported;
private $inflator;
private $deflator;
public function __construct(ProcessBuilderFactoryInterface $inflator, ProcessBuilderFactoryInterface $deflator)
{
$this->inflator = $inflator;
$this->deflator = $deflator;
}
/**
* {@inheritdoc}
*/
public function getStatus()
{
if (null !== $this->isSupported) {
return $this->isSupported;
}
if (null === $this->inflator || null === $this->deflator) {
return $this->isSupported = VersionProbeInterface::PROBE_NOTSUPPORTED;
}
$good = true;
foreach (array($this->inflator, $this->deflator) as $builder) {
/** @var Process $process */
$process = $builder
->create()
->add('--version')
->getProcess();
$process->run();
if (!$process->isSuccessful()) {
return $this->isSupported = VersionProbeInterface::PROBE_NOTSUPPORTED;
}
$lines = explode("\n", $process->getOutput(), 2);
$good = false !== stripos($lines[0], $this->getVersionSignature());
if (!$good) {
break;
}
}
$this->isSupported = $good ? VersionProbeInterface::PROBE_OK : VersionProbeInterface::PROBE_NOTSUPPORTED;
return $this->isSupported;
}
/**
* Returns the signature of inflator/deflator
*
* @return string
*/
abstract protected function getVersionSignature();
}

View file

@ -0,0 +1,24 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
*/
namespace Alchemy\Zippy\Adapter\VersionProbe;
class BSDTarVersionProbe extends AbstractTarVersionProbe
{
/**
* {@inheritdoc}
*/
protected function getVersionSignature()
{
return 'bsdtar';
}
}

View file

@ -0,0 +1,24 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
*/
namespace Alchemy\Zippy\Adapter\VersionProbe;
class GNUTarVersionProbe extends AbstractTarVersionProbe
{
/**
* {@inheritdoc}
*/
protected function getVersionSignature()
{
return '(gnu tar)';
}
}

View file

@ -0,0 +1,26 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
*/
namespace Alchemy\Zippy\Adapter\VersionProbe;
interface VersionProbeInterface
{
const PROBE_OK = 0;
const PROBE_NOTSUPPORTED = 1;
/**
* Probes for the support of an adapter.
*
* @return integer One of the self::PROBE_* constants
*/
public function getStatus();
}

View file

@ -0,0 +1,24 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
*/
namespace Alchemy\Zippy\Adapter\VersionProbe;
class ZipExtensionVersionProbe implements VersionProbeInterface
{
/**
* {@inheritdoc}
*/
public function getStatus()
{
return class_exists('\ZipArchive') ? VersionProbeInterface::PROBE_OK : VersionProbeInterface::PROBE_NOTSUPPORTED;
}
}

View file

@ -0,0 +1,96 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
*/
namespace Alchemy\Zippy\Adapter\VersionProbe;
use Alchemy\Zippy\ProcessBuilder\ProcessBuilderFactoryInterface;
class ZipVersionProbe implements VersionProbeInterface
{
private $isSupported;
private $inflator;
private $deflator;
public function __construct(ProcessBuilderFactoryInterface $inflator, ProcessBuilderFactoryInterface $deflator)
{
$this->inflator = $inflator;
$this->deflator = $deflator;
}
/**
* Set the inflator to zip
*
* @param ProcessBuilderFactoryInterface $inflator
* @return ZipVersionProbe
*/
public function setInflator(ProcessBuilderFactoryInterface $inflator)
{
$this->inflator = $inflator;
return $this;
}
/**
* Set the deflator to unzip
*
* @param ProcessBuilderFactoryInterface $deflator
* @return ZipVersionProbe
*/
public function setDeflator(ProcessBuilderFactoryInterface $deflator)
{
$this->deflator = $deflator;
return $this;
}
/**
* {@inheritdoc}
*/
public function getStatus()
{
if (null !== $this->isSupported) {
return $this->isSupported;
}
if (null === $this->inflator || null === $this->deflator) {
return $this->isSupported = VersionProbeInterface::PROBE_NOTSUPPORTED;
}
$processDeflate = $this
->deflator
->create()
->add('-h')
->getProcess();
$processDeflate->run();
$processInflate = $this
->inflator
->create()
->add('-h')
->getProcess();
$processInflate->run();
if (false === $processDeflate->isSuccessful() || false === $processInflate->isSuccessful()) {
return $this->isSupported = VersionProbeInterface::PROBE_NOTSUPPORTED;
}
$lines = explode("\n", $processInflate->getOutput(), 2);
$inflatorOk = false !== stripos($lines[0], 'Info-ZIP');
$lines = explode("\n", $processDeflate->getOutput(), 2);
$deflatorOk = false !== stripos($lines[0], 'Info-ZIP');
return $this->isSupported = ($inflatorOk && $deflatorOk) ? VersionProbeInterface::PROBE_OK : VersionProbeInterface::PROBE_NOTSUPPORTED;
}
}

View file

@ -0,0 +1,370 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Adapter;
use Alchemy\Zippy\Adapter\Resource\ResourceInterface;
use Alchemy\Zippy\Adapter\VersionProbe\ZipVersionProbe;
use Alchemy\Zippy\Archive\Archive;
use Alchemy\Zippy\Archive\Member;
use Alchemy\Zippy\Exception\InvalidArgumentException;
use Alchemy\Zippy\Exception\NotSupportedException;
use Alchemy\Zippy\Exception\RuntimeException;
use Alchemy\Zippy\Parser\ParserInterface;
use Alchemy\Zippy\ProcessBuilder\ProcessBuilderFactoryInterface;
use Alchemy\Zippy\Resource\Resource as ZippyResource;
use Alchemy\Zippy\Resource\ResourceManager;
use Symfony\Component\Process\Exception\ExceptionInterface as ProcessException;
/**
* ZipAdapter allows you to create and extract files from archives using Zip
*
* @see http://www.gnu.org/software/tar/manual/tar.html
*/
class ZipAdapter extends AbstractBinaryAdapter
{
public function __construct(
ParserInterface $parser,
ResourceManager $manager,
ProcessBuilderFactoryInterface $inflator,
ProcessBuilderFactoryInterface $deflator
) {
parent::__construct($parser, $manager, $inflator, $deflator);
$this->probe = new ZipVersionProbe($inflator, $deflator);
}
/**
* @inheritdoc
*/
protected function doCreate($path, $files, $recursive)
{
$files = (array) $files;
$builder = $this
->inflator
->create();
if (0 === count($files)) {
throw new NotSupportedException('Can not create empty zip archive');
}
if ($recursive) {
$builder->add('-r');
}
$builder->add($path);
$collection = $this->manager->handle(getcwd(), $files);
$builder->setWorkingDirectory($collection->getContext());
$collection->forAll(function($i, ZippyResource $resource) use ($builder) {
return $builder->add($resource->getTarget());
});
$process = $builder->getProcess();
try {
$process->run();
} catch (ProcessException $e) {
$this->manager->cleanup($collection);
throw $e;
}
$this->manager->cleanup($collection);
if (!$process->isSuccessful()) {
throw new RuntimeException(sprintf(
'Unable to execute the following command %s {output: %s}',
$process->getCommandLine(),
$process->getErrorOutput()
));
}
return new Archive($this->createResource($path), $this, $this->manager);
}
/**
* @inheritdoc
*/
protected function doListMembers(ResourceInterface $resource)
{
$process = $this
->deflator
->create()
->add('-l')
->add($resource->getResource())
->getProcess();
$process->run();
if (!$process->isSuccessful()) {
throw new RuntimeException(sprintf(
'Unable to execute the following command %s {output: %s}',
$process->getCommandLine(),
$process->getErrorOutput()
));
}
$members = array();
foreach ($this->parser->parseFileListing($process->getOutput() ?: '') as $member) {
$members[] = new Member(
$resource,
$this,
$member['location'],
$member['size'],
$member['mtime'],
$member['is_dir']
);
}
return $members;
}
/**
* @inheritdoc
*/
protected function doAdd(ResourceInterface $resource, $files, $recursive)
{
$files = (array) $files;
$builder = $this
->inflator
->create();
if ($recursive) {
$builder->add('-r');
}
$builder
->add('-u')
->add($resource->getResource());
$collection = $this->manager->handle(getcwd(), $files);
$builder->setWorkingDirectory($collection->getContext());
$collection->forAll(function($i, ZippyResource $resource) use ($builder) {
return $builder->add($resource->getTarget());
});
$process = $builder->getProcess();
try {
$process->run();
} catch (ProcessException $e) {
$this->manager->cleanup($collection);
throw $e;
}
$this->manager->cleanup($collection);
if (!$process->isSuccessful()) {
throw new RuntimeException(sprintf(
'Unable to execute the following command %s {output: %s}',
$process->getCommandLine(),
$process->getErrorOutput()
));
}
}
/**
* @inheritdoc
*/
protected function doGetDeflatorVersion()
{
$process = $this
->deflator
->create()
->add('-h')
->getProcess();
$process->run();
if (!$process->isSuccessful()) {
throw new RuntimeException(sprintf(
'Unable to execute the following command %s {output: %s}',
$process->getCommandLine(),
$process->getErrorOutput()
));
}
return $this->parser->parseDeflatorVersion($process->getOutput() ?: '');
}
/**
* @inheritdoc
*/
protected function doGetInflatorVersion()
{
$process = $this
->inflator
->create()
->add('-h')
->getProcess();
$process->run();
if (!$process->isSuccessful()) {
throw new RuntimeException(sprintf(
'Unable to execute the following command %s {output: %s}',
$process->getCommandLine(),
$process->getErrorOutput()
));
}
return $this->parser->parseInflatorVersion($process->getOutput() ?: '');
}
/**
* @inheritdoc
*/
protected function doRemove(ResourceInterface $resource, $files)
{
$files = (array) $files;
$builder = $this
->inflator
->create();
$builder
->add('-d')
->add($resource->getResource());
if (!$this->addBuilderFileArgument($files, $builder)) {
throw new InvalidArgumentException('Invalid files');
}
$process = $builder->getProcess();
$process->run();
if (!$process->isSuccessful()) {
throw new RuntimeException(sprintf(
'Unable to execute the following command %s {output: %s}',
$process->getCommandLine(),
$process->getErrorOutput()
));
}
return $files;
}
/**
* @inheritdoc
*/
public static function getName()
{
return 'zip';
}
/**
* @inheritdoc
*/
public static function getDefaultDeflatorBinaryName()
{
return array('unzip');
}
/**
* @inheritdoc
*/
public static function getDefaultInflatorBinaryName()
{
return array('zip');
}
/**
* @inheritdoc
*/
protected function doExtract(ResourceInterface $resource, $to)
{
if (null !== $to && !is_dir($to)) {
throw new InvalidArgumentException(sprintf("%s is not a directory", $to));
}
$builder = $this
->deflator
->create();
$builder
->add('-o')
->add($resource->getResource());
if (null !== $to) {
$builder
->add('-d')
->add($to);
}
$process = $builder->getProcess();
$process->run();
if (!$process->isSuccessful()) {
throw new RuntimeException(sprintf(
'Unable to execute the following command %s {output: %s}',
$process->getCommandLine(),
$process->getErrorOutput()
));
}
return new \SplFileInfo($to ?: $resource->getResource());
}
/**
* @inheritdoc
*/
protected function doExtractMembers(ResourceInterface $resource, $members, $to, $overwrite = false)
{
if (null !== $to && !is_dir($to)) {
throw new InvalidArgumentException(sprintf("%s is not a directory", $to));
}
$members = (array) $members;
$builder = $this
->deflator
->create();
if ((bool) $overwrite) {
$builder->add('-o');
}
$builder
->add($resource->getResource());
if (null !== $to) {
$builder
->add('-d')
->add($to);
}
if (!$this->addBuilderFileArgument($members, $builder)) {
throw new InvalidArgumentException('Invalid files');
}
$process = $builder->getProcess();
$process->run();
if (!$process->isSuccessful()) {
throw new RuntimeException(sprintf(
'Unable to execute the following command %s {output: %s}',
$process->getCommandLine(),
$process->getErrorOutput()
));
}
return $members;
}
}

View file

@ -0,0 +1,361 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Adapter;
use Alchemy\Zippy\Adapter\Resource\ResourceInterface;
use Alchemy\Zippy\Adapter\Resource\ZipArchiveResource;
use Alchemy\Zippy\Adapter\VersionProbe\ZipExtensionVersionProbe;
use Alchemy\Zippy\Archive\Archive;
use Alchemy\Zippy\Archive\Member;
use Alchemy\Zippy\Exception\NotSupportedException;
use Alchemy\Zippy\Exception\RuntimeException;
use Alchemy\Zippy\Exception\InvalidArgumentException;
use Alchemy\Zippy\Resource\Resource as ZippyResource;
use Alchemy\Zippy\Resource\ResourceManager;
/**
* ZipExtensionAdapter allows you to create and extract files from archives
* using PHP Zip extension
*
* @see http://www.php.net/manual/en/book.zip.php
*/
class ZipExtensionAdapter extends AbstractAdapter
{
private $errorCodesMapping = array(
\ZipArchive::ER_EXISTS => "File already exists",
\ZipArchive::ER_INCONS => "Zip archive inconsistent",
\ZipArchive::ER_INVAL => "Invalid argument",
\ZipArchive::ER_MEMORY => "Malloc failure",
\ZipArchive::ER_NOENT => "No such file",
\ZipArchive::ER_NOZIP => "Not a zip archive",
\ZipArchive::ER_OPEN => "Can't open file",
\ZipArchive::ER_READ => "Read error",
\ZipArchive::ER_SEEK => "Seek error"
);
public function __construct(ResourceManager $manager)
{
parent::__construct($manager);
$this->probe = new ZipExtensionVersionProbe();
}
/**
* @inheritdoc
*/
protected function doListMembers(ResourceInterface $resource)
{
$members = array();
for ($i = 0; $i < $resource->getResource()->numFiles; $i++) {
$stat = $resource->getResource()->statIndex($i);
$members[] = new Member(
$resource,
$this,
$stat['name'],
$stat['size'],
new \DateTime('@' . $stat['mtime']),
0 === strlen($resource->getResource()->getFromIndex($i, 1))
);
}
return $members;
}
/**
* @inheritdoc
*/
public static function getName()
{
return 'zip-extension';
}
/**
* @inheritdoc
*/
protected function doExtract(ResourceInterface $resource, $to)
{
return $this->extractMembers($resource, null, $to);
}
/**
* @inheritdoc
*/
protected function doExtractMembers(ResourceInterface $resource, $members, $to, $overwrite = false)
{
if (null === $to) {
// if no destination is given, will extract to zip current folder
$to = dirname(realpath($resource->getResource()->filename));
}
if (!is_dir($to)) {
$resource->getResource()->close();
throw new InvalidArgumentException(sprintf("%s is not a directory", $to));
}
if (!is_writable($to)) {
$resource->getResource()->close();
throw new InvalidArgumentException(sprintf("%s is not writable", $to));
}
if (null !== $members) {
$membersTemp = (array) $members;
if (empty($membersTemp)) {
$resource->getResource()->close();
throw new InvalidArgumentException("no members provided");
}
$members = array();
// allows $members to be an array of strings or array of Members
foreach ($membersTemp as $member) {
if ($member instanceof Member) {
$member = $member->getLocation();
}
if ($resource->getResource()->locateName($member) === false) {
$resource->getResource()->close();
throw new InvalidArgumentException(sprintf('%s is not in the zip file', $member));
}
if ($overwrite == false) {
if (file_exists($member)) {
$resource->getResource()->close();
throw new RuntimeException('Target file ' . $member . ' already exists.');
}
}
$members[] = $member;
}
}
if (!$resource->getResource()->extractTo($to, $members)) {
$resource->getResource()->close();
throw new InvalidArgumentException(sprintf('Unable to extract archive : %s', $resource->getResource()->getStatusString()));
}
return new \SplFileInfo($to);
}
/**
* @inheritdoc
*/
protected function doRemove(ResourceInterface $resource, $files)
{
$files = (array) $files;
if (empty($files)) {
throw new InvalidArgumentException("no files provided");
}
// either remove all files or none in case of error
foreach ($files as $file) {
if ($resource->getResource()->locateName($file) === false) {
$resource->getResource()->unchangeAll();
$resource->getResource()->close();
throw new InvalidArgumentException(sprintf('%s is not in the zip file', $file));
}
if (!$resource->getResource()->deleteName($file)) {
$resource->getResource()->unchangeAll();
$resource->getResource()->close();
throw new RuntimeException(sprintf('unable to remove %s', $file));
}
}
$this->flush($resource->getResource());
return $files;
}
/**
* @inheritdoc
*/
protected function doAdd(ResourceInterface $resource, $files, $recursive)
{
$files = (array) $files;
if (empty($files)) {
$resource->getResource()->close();
throw new InvalidArgumentException("no files provided");
}
$this->addEntries($resource, $files, $recursive);
return $files;
}
/**
* @inheritdoc
*/
protected function doCreate($path, $files, $recursive)
{
$files = (array) $files;
if (empty($files)) {
throw new NotSupportedException("Cannot create an empty zip");
}
$resource = $this->getResource($path, \ZipArchive::CREATE);
$this->addEntries($resource, $files, $recursive);
return new Archive($resource, $this, $this->manager);
}
/**
* Returns a new instance of the invoked adapter
*
* @return AbstractAdapter
*
* @throws RuntimeException In case object could not be instanciated
*/
public static function newInstance()
{
return new ZipExtensionAdapter(ResourceManager::create());
}
protected function createResource($path)
{
return $this->getResource($path, \ZipArchive::CHECKCONS);
}
private function getResource($path, $mode)
{
$zip = new \ZipArchive();
$res = $zip->open($path, $mode);
if ($res !== true) {
throw new RuntimeException($this->errorCodesMapping[$res]);
}
return new ZipArchiveResource($zip);
}
private function addEntries(ResourceInterface $zipResource, array $files, $recursive)
{
$stack = new \SplStack();
$error = null;
$cwd = getcwd();
$collection = $this->manager->handle($cwd, $files);
$this->chdir($collection->getContext());
$adapter = $this;
try {
$collection->forAll(function($i, ZippyResource $resource) use ($zipResource, $stack, $recursive, $adapter) {
$adapter->checkReadability($zipResource->getResource(), $resource->getTarget());
if (is_dir($resource->getTarget())) {
if ($recursive) {
$stack->push($resource->getTarget() . ((substr($resource->getTarget(), -1) === DIRECTORY_SEPARATOR) ? '' : DIRECTORY_SEPARATOR));
} else {
$adapter->addEmptyDir($zipResource->getResource(), $resource->getTarget());
}
} else {
$adapter->addFileToZip($zipResource->getResource(), $resource->getTarget());
}
return true;
});
// recursively add dirs
while (!$stack->isEmpty()) {
$dir = $stack->pop();
// removes . and ..
$files = array_diff(scandir($dir), array(".", ".."));
if (count($files) > 0) {
foreach ($files as $file) {
$file = $dir . $file;
$this->checkReadability($zipResource->getResource(), $file);
if (is_dir($file)) {
$stack->push($file . DIRECTORY_SEPARATOR);
} else {
$this->addFileToZip($zipResource->getResource(), $file);
}
}
} else {
$this->addEmptyDir($zipResource->getResource(), $dir);
}
}
$this->flush($zipResource->getResource());
$this->manager->cleanup($collection);
} catch (\Exception $e) {
$error = $e;
}
$this->chdir($cwd);
if ($error) {
throw $error;
}
}
/**
* @info is public for PHP 5.3 compatibility, should be private
*
* @param \ZipArchive $zip
* @param string $file
*/
public function checkReadability(\ZipArchive $zip, $file)
{
if (!is_readable($file)) {
$zip->unchangeAll();
$zip->close();
throw new InvalidArgumentException(sprintf('could not read %s', $file));
}
}
/**
* @info is public for PHP 5.3 compatibility, should be private
*
* @param \ZipArchive $zip
* @param string $file
*/
public function addFileToZip(\ZipArchive $zip, $file)
{
if (!$zip->addFile($file)) {
$zip->unchangeAll();
$zip->close();
throw new RuntimeException(sprintf('unable to add %s to the zip file', $file));
}
}
/**
* @info is public for PHP 5.3 compatibility, should be private
*
* @param \ZipArchive $zip
* @param string $dir
*/
public function addEmptyDir(\ZipArchive $zip, $dir)
{
if (!$zip->addEmptyDir($dir)) {
$zip->unchangeAll();
$zip->close();
throw new RuntimeException(sprintf('unable to add %s to the zip file', $dir));
}
}
/**
* Flushes changes to the archive
*
* @param \ZipArchive $zip
*/
private function flush(\ZipArchive $zip) // flush changes by reopening the file
{
$path = $zip->filename;
$zip->close();
$zip->open($path, \ZipArchive::CHECKCONS);
}
}

View file

@ -0,0 +1,136 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Archive;
use Alchemy\Zippy\Adapter\AdapterInterface;
use Alchemy\Zippy\Resource\ResourceManager;
use Alchemy\Zippy\Adapter\Resource\ResourceInterface;
/**
* Represents an archive
*/
class Archive implements ArchiveInterface
{
/**
* The path to the archive
*
* @var string
*/
protected $path;
/**
* The archive adapter
*
* @var AdapterInterface
*/
protected $adapter;
/**
* An array of archive members
*
* @var MemberInterface[]
*/
protected $members = array();
/**
* @var ResourceInterface
*/
protected $resource;
/**
*
* @var ResourceManager
*/
protected $manager;
/**
* Constructor
*
* @param ResourceInterface $resource Path to the archive
* @param AdapterInterface $adapter An archive adapter
* @param ResourceManager $manager The resource manager
*/
public function __construct(ResourceInterface $resource, AdapterInterface $adapter, ResourceManager $manager)
{
$this->resource = $resource;
$this->adapter = $adapter;
$this->manager = $manager;
}
/**
* @inheritdoc
*/
public function count()
{
return count($this->getMembers());
}
/**
* Returns an Iterator for the current archive
*
* This method implements the IteratorAggregate interface.
*
* @return \ArrayIterator An iterator
*/
public function getIterator()
{
return new \ArrayIterator($this->getMembers());
}
/**
* @inheritdoc
*/
public function getMembers()
{
return $this->members = $this->adapter->listMembers($this->resource);
}
/**
* @inheritdoc
*/
public function addMembers($sources, $recursive = true)
{
$this->adapter->add($this->resource, $sources, $recursive);
return $this;
}
/**
* @inheritdoc
*/
public function removeMembers($sources)
{
$this->adapter->remove($this->resource, $sources);
return $this;
}
/**
* @inheritdoc
*/
public function extract($toDirectory)
{
$this->adapter->extract($this->resource, $toDirectory);
return $this;
}
/**
* @inheritdoc
*/
public function extractMembers($members, $toDirectory = null)
{
$this->adapter->extractMembers($this->resource, $members, $toDirectory);
return $this;
}
}

View file

@ -0,0 +1,74 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Archive;
use Alchemy\Zippy\Exception\RuntimeException;
use Alchemy\Zippy\Exception\InvalidArgumentException;
interface ArchiveInterface extends \IteratorAggregate, \Countable
{
/**
* Adds a file or a folder into the archive
*
* @param string|array|\SplFileInfo $sources The path to the file or a folder
* @param bool $recursive Recurse into sub-directories
*
* @return ArchiveInterface
*
* @throws InvalidArgumentException In case the provided source path is not valid
* @throws RuntimeException In case of failure
*/
public function addMembers($sources, $recursive = true);
/**
* Removes a file from the archive
*
* @param string|array $sources The path to an archive or a folder member
*
* @return ArchiveInterface
*
* @throws RuntimeException In case of failure
*/
public function removeMembers($sources);
/**
* Lists files and directories archive members
*
* @return MemberInterface[] An array of File
*
* @throws RuntimeException In case archive could not be read
*/
public function getMembers();
/**
* Extracts current archive to the given directory
*
* @param string $toDirectory The path the extracted archive
*
* @return ArchiveInterface
*
* @throws RuntimeException In case archive could not be extracted
*/
public function extract($toDirectory);
/**
* Extracts specific members from the archive
*
* @param string|MemberInterface[] $members An array of members path
* @param string $toDirectory The destination $directory
*
* @return ArchiveInterface
*
* @throws RuntimeException In case member could not be extracted
*/
public function extractMembers($members, $toDirectory = null);
}

View file

@ -0,0 +1,147 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Archive;
use Alchemy\Zippy\Adapter\AdapterInterface;
use Alchemy\Zippy\Adapter\Resource\ResourceInterface;
/**
* Represents a member of an archive.
*/
class Member implements MemberInterface
{
/**
* The location of the file
*
* @var string
*/
private $location;
/**
* Tells whether the archive member is a directory or not
*
* @var bool
*/
private $isDir;
/**
* The uncompressed size of the file
*
* @var int
*/
private $size;
/**
* The last modified date of the file
*
* @var \DateTime
*/
private $lastModifiedDate;
/**
* The resource to the actual archive
*
* @var string
*/
private $resource;
/**
* An adapter
*
* @var AdapterInterface
*/
private $adapter;
/**
* Constructor
*
* @param ResourceInterface $resource The path of the archive which contain the member
* @param AdapterInterface $adapter The archive adapter interface
* @param string $location The path of the archive member
* @param int $fileSize The uncompressed file size
* @param \DateTime $lastModifiedDate The last modified date of the member
* @param bool $isDir Tells whether the member is a directory or not
*/
public function __construct(
ResourceInterface $resource,
AdapterInterface $adapter,
$location,
$fileSize,
\DateTime $lastModifiedDate,
$isDir
) {
$this->resource = $resource;
$this->adapter = $adapter;
$this->location = $location;
$this->isDir = $isDir;
$this->size = $fileSize;
$this->lastModifiedDate = $lastModifiedDate;
}
/**
* {@inheritdoc}
*/
public function getLocation()
{
return $this->location;
}
/**
* {@inheritdoc}
*/
public function isDir()
{
return $this->isDir;
}
/**
* {@inheritdoc}
*/
public function getLastModifiedDate()
{
return $this->lastModifiedDate;
}
/**
* {@inheritdoc}
*/
public function getSize()
{
return $this->size;
}
/**
* {@inheritdoc}
*/
public function __toString()
{
return $this->location;
}
/**
* {@inheritdoc}
*/
public function extract($to = null, $overwrite = false)
{
$this->adapter->extractMembers($this->resource, $this->location, $to, (bool) $overwrite);
return new \SplFileInfo(sprintf('%s%s', rtrim(null === $to ? getcwd() : $to, '/'), $this->location));
}
/**
* @inheritdoc
* */
public function getResource()
{
return $this->resource;
}
}

View file

@ -0,0 +1,78 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Archive;
use Alchemy\Zippy\Adapter\Resource\ResourceInterface;
use Alchemy\Zippy\Exception\InvalidArgumentException;
use Alchemy\Zippy\Exception\RuntimeException;
interface MemberInterface
{
/**
* Gets the location of an archive member
*
* @return string
*/
public function getLocation();
/**
* Tells whether the member is a directory or not
*
* @return bool
*/
public function isDir();
/**
* Returns the last modified date of the member
*
* @return \DateTime
*/
public function getLastModifiedDate();
/**
* Returns the (uncompressed) size of the member
*
* If the size is unknown, returns -1
*
* @return integer
*/
public function getSize();
/**
* Extract the member from its archive
*
* Be careful using this method within a loop
* This will execute one extraction process for each file
* Prefer the use of ArchiveInterface::extractMembers in that use case
*
* @param string|null $to The path where to extract the member, if no path is not provided the member is extracted in the same directory of its archive
* @param bool $overwrite Whether to overwrite destination file if it already exists. Defaults to false
*
* @return \SplFileInfo The extracted file
*
* @throws RuntimeException In case of failure
* @throws InvalidArgumentException In case no members could be removed or provide extract target directory is not valid
*/
public function extract($to = null, $overwrite = false);
/**
* Get resource.
*
* @return ResourceInterface
* */
public function getResource();
/**
* @inheritdoc
*/
public function __toString();
}

View file

@ -0,0 +1,16 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Exception;
interface ExceptionInterface
{
}

View file

@ -0,0 +1,16 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Exception;
class FormatNotSupportedException extends RuntimeException
{
}

View file

@ -0,0 +1,16 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Exception;
class IOException extends RuntimeException
{
}

View file

@ -0,0 +1,16 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Exception;
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}

View file

@ -0,0 +1,16 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Exception;
class NoAdapterOnPlatformException extends RuntimeException
{
}

View file

@ -0,0 +1,16 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Exception;
class NotSupportedException extends RuntimeException implements ExceptionInterface
{
}

View file

@ -0,0 +1,16 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Exception;
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}

View file

@ -0,0 +1,28 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Exception;
class TargetLocatorException extends RuntimeException
{
private $resource;
public function __construct($resource, $message, $code = 0, $previous = null)
{
$this->resource = $resource;
parent::__construct($message, $code, $previous);
}
public function getResource()
{
return $this->resource;
}
}

View file

@ -0,0 +1,49 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\FileStrategy;
use Alchemy\Zippy\Adapter\AdapterContainer;
use Alchemy\Zippy\Exception\RuntimeException;
abstract class AbstractFileStrategy implements FileStrategyInterface
{
protected $container;
public function __construct(AdapterContainer $container)
{
$this->container = $container;
}
/**
* {@inheritdoc}
*/
public function getAdapters()
{
$services = array();
foreach ($this->getServiceNames() as $serviceName) {
try {
$services[] = $this->container[$serviceName];
} catch (RuntimeException $e) {
}
}
return $services;
}
/**
* Returns an array of service names that defines adapters.
*
* @return string[]
*/
abstract protected function getServiceNames();
}

View file

@ -0,0 +1,31 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\FileStrategy;
use Alchemy\Zippy\Adapter\AdapterInterface;
interface FileStrategyInterface
{
/**
* Returns an array of adapters that match the strategy
*
* @return AdapterInterface[]
*/
public function getAdapters();
/**
* Returns the file extension that match the strategy
*
* @return string
*/
public function getFileExtension();
}

View file

@ -0,0 +1,34 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\FileStrategy;
class TB2FileStrategy extends AbstractFileStrategy
{
/**
* {@inheritdoc}
*/
protected function getServiceNames()
{
return array(
'Alchemy\\Zippy\\Adapter\\GNUTar\\TarBz2GNUTarAdapter',
'Alchemy\\Zippy\\Adapter\\BSDTar\\TarBz2BSDTarAdapter'
);
}
/**
* {@inheritdoc}
*/
public function getFileExtension()
{
return 'tb2';
}
}

View file

@ -0,0 +1,34 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\FileStrategy;
class TBz2FileStrategy extends AbstractFileStrategy
{
/**
* {@inheritdoc}
*/
protected function getServiceNames()
{
return array(
'Alchemy\\Zippy\\Adapter\\GNUTar\\TarBz2GNUTarAdapter',
'Alchemy\\Zippy\\Adapter\\BSDTar\\TarBz2BSDTarAdapter'
);
}
/**
* {@inheritdoc}
*/
public function getFileExtension()
{
return 'tbz2';
}
}

View file

@ -0,0 +1,34 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\FileStrategy;
class TGzFileStrategy extends AbstractFileStrategy
{
/**
* {@inheritdoc}
*/
protected function getServiceNames()
{
return array(
'Alchemy\\Zippy\\Adapter\\GNUTar\\TarGzGNUTarAdapter',
'Alchemy\\Zippy\\Adapter\\BSDTar\\TarGzBSDTarAdapter'
);
}
/**
* {@inheritdoc}
*/
public function getFileExtension()
{
return 'tgz';
}
}

View file

@ -0,0 +1,34 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\FileStrategy;
class TarBz2FileStrategy extends AbstractFileStrategy
{
/**
* {@inheritdoc}
*/
protected function getServiceNames()
{
return array(
'Alchemy\\Zippy\\Adapter\\GNUTar\\TarBz2GNUTarAdapter',
'Alchemy\\Zippy\\Adapter\\BSDTar\\TarBz2BSDTarAdapter'
);
}
/**
* {@inheritdoc}
*/
public function getFileExtension()
{
return 'tar.bz2';
}
}

View file

@ -0,0 +1,34 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\FileStrategy;
class TarFileStrategy extends AbstractFileStrategy
{
/**
* {@inheritdoc}
*/
protected function getServiceNames()
{
return array(
'Alchemy\\Zippy\\Adapter\\GNUTar\\TarGNUTarAdapter',
'Alchemy\\Zippy\\Adapter\\BSDTar\\TarBSDTarAdapter'
);
}
/**
* {@inheritdoc}
*/
public function getFileExtension()
{
return 'tar';
}
}

View file

@ -0,0 +1,34 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\FileStrategy;
class TarGzFileStrategy extends AbstractFileStrategy
{
/**
* {@inheritdoc}
*/
protected function getServiceNames()
{
return array(
'Alchemy\\Zippy\\Adapter\\GNUTar\\TarGzGNUTarAdapter',
'Alchemy\\Zippy\\Adapter\\BSDTar\\TarGzBSDTarAdapter'
);
}
/**
* {@inheritdoc}
*/
public function getFileExtension()
{
return 'tar.gz';
}
}

View file

@ -0,0 +1,34 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\FileStrategy;
class ZipFileStrategy extends AbstractFileStrategy
{
/**
* {@inheritdoc}
*/
protected function getServiceNames()
{
return array(
'Alchemy\\Zippy\\Adapter\\ZipAdapter',
'Alchemy\\Zippy\\Adapter\\ZipExtensionAdapter'
);
}
/**
* {@inheritdoc}
*/
public function getFileExtension()
{
return 'zip';
}
}

View file

@ -0,0 +1,115 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Parser;
use Alchemy\Zippy\Exception\RuntimeException;
/**
* This class is responsible of parsing GNUTar command line output
*/
class BSDTarOutputParser implements ParserInterface
{
const PERMISSIONS = '([ldrwx-]+)';
const HARD_LINK = '(\d+)';
const OWNER = '([a-z][-a-z0-9]*)';
const GROUP = '([a-z][-a-z0-9]*)';
const FILESIZE = '(\d*)';
const DATE = '([a-zA-Z0-9]+\s+[a-z0-9]+\s+[a-z0-9:]+)';
const FILENAME = '(.*)';
/**
* @inheritdoc
*/
public function parseFileListing($output)
{
$lines = array_values(array_filter(explode("\n", $output)));
$members = array();
// BSDTar outputs two differents format of date according to the mtime
// of the member. If the member is younger than six months the year is not shown.
// On 4.5+ FreeBSD system the day is displayed first
$dateFormats = array('M d Y', 'M d H:i', 'd M Y', 'd M H:i');
foreach ($lines as $line) {
$matches = array();
// drw-rw-r-- 0 toto titi 0 Jan 3 1980 practice/
// -rw-rw-r-- 0 toto titi 10240 Jan 22 13:31 practice/records
if (!preg_match_all("#" .
self::PERMISSIONS . "\s+" . // match (drw-r--r--)
self::HARD_LINK . "\s+" . // match (1)
self::OWNER . "\s" . // match (toto)
self::GROUP . "\s+" . // match (titi)
self::FILESIZE . "\s+" . // match (0)
self::DATE . "\s+" . // match (Jan 3 1980)
self::FILENAME . // match (practice)
"#", $line, $matches, PREG_SET_ORDER
)) {
continue;
}
$chunks = array_shift($matches);
if (8 !== count($chunks)) {
continue;
}
$date = null;
foreach ($dateFormats as $format) {
$date = \DateTime::createFromFormat($format, $chunks[6]);
if (false === $date) {
continue;
} else {
break;
}
}
if (false === $date) {
throw new RuntimeException(sprintf('Failed to parse mtime date from %s', $line));
}
$members[] = array(
'location' => $chunks[7],
'size' => $chunks[5],
'mtime' => $date,
'is_dir' => 'd' === $chunks[1][0]
);
}
return $members;
}
/**
* @inheritdoc
*/
public function parseInflatorVersion($output)
{
$chunks = explode(' ', $output, 3);
if (2 > count($chunks)) {
return null;
}
list(, $version) = explode(' ', $output, 3);
return $version;
}
/**
* @inheritdoc
*/
public function parseDeflatorVersion($output)
{
return $this->parseInflatorVersion($output);
}
}

View file

@ -0,0 +1,98 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Parser;
use Alchemy\Zippy\Exception\RuntimeException;
/**
* This class is responsible of parsing GNUTar command line output
*/
class GNUTarOutputParser implements ParserInterface
{
const PERMISSIONS = '([ldrwx-]+)';
const OWNER = '([a-z][-a-z0-9]*)';
const GROUP = '([a-z][-a-z0-9]*)';
const FILESIZE = '(\d*)';
const ISO_DATE = '([0-9]+-[0-9]+-[0-9]+\s+[0-9]+:[0-9]+)';
const FILENAME = '(.*)';
/**
* @inheritdoc
*/
public function parseFileListing($output)
{
$lines = array_values(array_filter(explode("\n", $output)));
$members = array();
foreach ($lines as $line) {
$matches = array();
// -rw-r--r-- gray/staff 62373 2006-06-09 12:06 apple
if (!preg_match_all("#".
self::PERMISSIONS . "\s+" . // match (-rw-r--r--)
self::OWNER . "/" . // match (gray)
self::GROUP . "\s+" . // match (staff)
self::FILESIZE . "\s+" . // match (62373)
self::ISO_DATE . "\s+" . // match (2006-06-09 12:06)
self::FILENAME . // match (apple)
"#",
$line, $matches, PREG_SET_ORDER
)) {
continue;
}
$chunks = array_shift($matches);
if (7 !== count($chunks)) {
continue;
}
$date = \DateTime::createFromFormat("Y-m-d H:i", $chunks[5]);
if (false === $date) {
throw new RuntimeException(sprintf('Failed to parse mtime date from %s', $line));
}
$members[] = array(
'location' => $chunks[6],
'size' => $chunks[4],
'mtime' => $date,
'is_dir' => 'd' === $chunks[1][0]
);
}
return $members;
}
/**
* @inheritdoc
*/
public function parseInflatorVersion($output)
{
$chunks = explode(' ', $output, 3);
if (2 > count($chunks)) {
return null;
}
list(, $version) = $chunks;
return $version;
}
/**
* @inheritdoc
*/
public function parseDeflatorVersion($output)
{
return $this->parseInflatorVersion($output);
}
}

View file

@ -0,0 +1,56 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Parser;
use Alchemy\Zippy\Exception\InvalidArgumentException;
class ParserFactory
{
private static $zipDateFormat = 'Y-m-d H:i';
/**
* @param string $format Date format used to parse ZIP file listings
*/
public static function setZipDateFormat($format)
{
self::$zipDateFormat = $format;
}
/**
* Maps the corresponding parser to the selected adapter
*
* @param string $adapterName An adapter name
*
* @return ParserInterface
*
* @throws InvalidArgumentException In case no parser were found
*/
public static function create($adapterName)
{
switch ($adapterName) {
case 'gnu-tar':
return new GNUTarOutputParser();
break;
case 'bsd-tar':
return new BSDTarOutputParser();
break;
case 'zip':
return new ZipOutputParser(self::$zipDateFormat);
break;
default:
throw new InvalidArgumentException(sprintf('No parser available for %s adapter', $adapterName));
break;
}
}
}

View file

@ -0,0 +1,46 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Parser;
use Alchemy\Zippy\Exception\RuntimeException;
interface ParserInterface
{
/**
* Parses a file listing
*
* @param string $output The string to parse
*
* @return array An array of Member properties (location, mtime, size & is_dir)
*
* @throws RuntimeException In case the parsing process failed
*/
public function parseFileListing($output);
/**
* Parses the inflator binary version
*
* @param string $output
*
* @return string The version
*/
public function parseInflatorVersion($output);
/**
* Parses the deflator binary version
*
* @param string $output
*
* @return string The version
*/
public function parseDeflatorVersion($output);
}

View file

@ -0,0 +1,109 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Parser;
/**
* This class is responsible of parsing GNUTar command line output
*/
class ZipOutputParser implements ParserInterface
{
const LENGTH = '(\d*)';
const ISO_DATE = '([0-9]+-[0-9]+-[0-9]+\s+[0-9]+:[0-9]+)';
const FILENAME = '(.*)';
/**
* @var string
*/
private $dateFormat;
/**
* @param string $dateFormat
*/
public function __construct($dateFormat = "Y-m-d H:i")
{
$this->dateFormat = $dateFormat;
}
/**
* @inheritdoc
*/
public function parseFileListing($output)
{
$lines = array_values(array_filter(explode("\n", $output)));
$members = array();
foreach ($lines as $line) {
$matches = array();
// 785 2012-10-24 10:39 file
if (!preg_match_all("#" .
self::LENGTH . "\s+" . // match (785)
self::ISO_DATE . "\s+" . // match (2012-10-24 10:39)
self::FILENAME . // match (file)
"#",
$line, $matches, PREG_SET_ORDER
)) {
continue;
}
$chunks = array_shift($matches);
if (4 !== count($chunks)) {
continue;
}
$members[] = array(
'location' => $chunks[3],
'size' => $chunks[1],
'mtime' => \DateTime::createFromFormat($this->dateFormat, $chunks[2]),
'is_dir' => '/' === substr($chunks[3], -1)
);
}
return $members;
}
/**
* @inheritdoc
*/
public function parseInflatorVersion($output)
{
$lines = array_values(array_filter(explode("\n", $output, 3)));
$chunks = explode(' ', $lines[1], 3);
if (2 > count($chunks)) {
return null;
}
list(, $version) = $chunks;
return $version;
}
/**
* @inheritdoc
*/
public function parseDeflatorVersion($output)
{
$lines = array_values(array_filter(explode("\n", $output, 2)));
$firstLine = array_shift($lines);
$chunks = explode(' ', $firstLine, 3);
if (2 > count($chunks)) {
return null;
}
list(, $version) = $chunks;
return $version;
}
}

View file

@ -0,0 +1,71 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\ProcessBuilder;
use Alchemy\Zippy\Exception\InvalidArgumentException;
use Symfony\Component\Process\ProcessBuilder;
class ProcessBuilderFactory implements ProcessBuilderFactoryInterface
{
/**
* The binary path
*
* @var string
*/
protected $binary;
/**
* Constructor
*
* @param string $binary The path to the binary
*
* @throws InvalidArgumentException In case binary path is invalid
*/
public function __construct($binary)
{
$this->useBinary($binary);
}
/**
* @inheritdoc
*/
public function getBinary()
{
return $this->binary;
}
/**
* @inheritdoc
*/
public function useBinary($binary)
{
if (!is_executable($binary)) {
throw new InvalidArgumentException(sprintf('`%s` is not an executable binary', $binary));
}
$this->binary = $binary;
return $this;
}
/**
* @inheritdoc
*/
public function create()
{
if (null === $this->binary) {
throw new InvalidArgumentException('No binary set');
}
return ProcessBuilder::create(array($this->binary))->setTimeout(null);
}
}

View file

@ -0,0 +1,45 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\ProcessBuilder;
use Alchemy\Zippy\Exception\InvalidArgumentException;
use Symfony\Component\Process\ProcessBuilder;
interface ProcessBuilderFactoryInterface
{
/**
* Returns a new instance of Symfony ProcessBuilder
*
* @return ProcessBuilder
*
* @throws InvalidArgumentException
*/
public function create();
/**
* Returns the binary path
*
* @return string
*/
public function getBinary();
/**
* Sets the binary path
*
* @param string $binary A binary path
*
* @return ProcessBuilderFactoryInterface
*
* @throws InvalidArgumentException In case binary is not executable
*/
public function useBinary($binary);
}

View file

@ -0,0 +1,20 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Resource;
abstract class PathUtil
{
public static function basename($path)
{
return (false === $pos = strrpos(strtr($path, '\\', '/'), '/')) ? $path : substr($path, $pos + 1);
}
}

View file

@ -0,0 +1,59 @@
<?php
namespace Alchemy\Zippy\Resource\Reader\Guzzle;
use Alchemy\Zippy\Resource\Resource as ZippyResource;
use Alchemy\Zippy\Resource\ResourceReader;
use GuzzleHttp\ClientInterface;
class GuzzleReader implements ResourceReader
{
/**
* @var ClientInterface
*/
private $client;
/**
* @var \Alchemy\Zippy\Resource\Resource
*/
private $resource;
/**
* @param ZippyResource $resource
* @param ClientInterface $client
*/
public function __construct(ZippyResource $resource, ClientInterface $client = null)
{
$this->resource = $resource;
$this->client = $client;
}
/**
* @return string
*/
public function getContents()
{
return $this->buildRequest()->getBody()->getContents();
}
/**
* @return resource
*/
public function getContentsAsStream()
{
$response = $this->buildRequest()->getBody()->getContents();
$stream = fopen('php://temp', 'r+');
if ($response != '') {
fwrite($stream, $response);
fseek($stream, 0);
}
return $stream;
}
private function buildRequest()
{
return $this->client->request('GET', $this->resource->getOriginal());
}
}

View file

@ -0,0 +1,37 @@
<?php
namespace Alchemy\Zippy\Resource\Reader\Guzzle;
use Alchemy\Zippy\Resource\Resource as ZippyResource;
use Alchemy\Zippy\Resource\ResourceReader;
use Alchemy\Zippy\Resource\ResourceReaderFactory;
use GuzzleHttp\Client;
use GuzzleHttp\ClientInterface;
class GuzzleReaderFactory implements ResourceReaderFactory
{
/**
* @var ClientInterface|null
*/
private $client = null;
public function __construct(ClientInterface $client = null)
{
$this->client = $client;
if (! $this->client) {
$this->client = new Client();
}
}
/**
* @param ZippyResource $resource
* @param string $context
*
* @return ResourceReader
*/
public function getReader(ZippyResource $resource, $context)
{
return new GuzzleReader($resource, $this->client);
}
}

View file

@ -0,0 +1,67 @@
<?php
namespace Alchemy\Zippy\Resource\Reader\Guzzle;
use Alchemy\Zippy\Resource\Resource as ZippyResource;
use Alchemy\Zippy\Resource\ResourceReader;
use Guzzle\Http\Client;
use Guzzle\Http\ClientInterface;
use Guzzle\Http\EntityBodyInterface;
class LegacyGuzzleReader implements ResourceReader
{
/**
* @var ClientInterface
*/
private $client;
/**
* @var \Alchemy\Zippy\Resource\Resource $resource
*/
private $resource;
/**
* This is necessary to prevent the underlying PHP stream from being destroyed
* @link https://github.com/guzzle/guzzle/issues/366#issuecomment-20295409
* @var EntityBodyInterface|null
*/
private $stream = null;
/**
* @param ZippyResource $resource
* @param ClientInterface $client
*/
public function __construct(ZippyResource $resource, ClientInterface $client = null)
{
$this->client = $client ?: new Client();
$this->resource = $resource;
}
/**
* @return string
*/
public function getContents()
{
return $this->buildRequest()->send()->getBody(true);
}
/**
* @return resource
*/
public function getContentsAsStream()
{
if (!$this->stream) {
$this->stream = $this->buildRequest()->send()->getBody(false);
}
return $this->stream->getStream();
}
/**
* @return \Guzzle\Http\Message\RequestInterface
*/
private function buildRequest()
{
return $this->client->get($this->resource->getOriginal());
}
}

View file

@ -0,0 +1,47 @@
<?php
namespace Alchemy\Zippy\Resource\Reader\Guzzle;
use Alchemy\Zippy\Resource\Resource as ZippyResource;
use Alchemy\Zippy\Resource\ResourceReader;
use Alchemy\Zippy\Resource\ResourceReaderFactory;
use Guzzle\Http\Client;
use Guzzle\Http\ClientInterface;
use Guzzle\Plugin\Backoff\BackoffPlugin;
use Symfony\Component\EventDispatcher\Event;
class LegacyGuzzleReaderFactory implements ResourceReaderFactory
{
/**
* @var ClientInterface|null
*/
private $client = null;
public function __construct(ClientInterface $client = null)
{
$this->client = $client;
if (!$this->client) {
$this->client = new Client();
$this->client->getEventDispatcher()->addListener('request.error', function(Event $event) {
// override guzzle default behavior of throwing exceptions
// when 4xx & 5xx responses are encountered
$event->stopPropagation();
}, -254);
$this->client->addSubscriber(BackoffPlugin::getExponentialBackoff(5, array(500, 502, 503, 408)));
}
}
/**
* @param ZippyResource $resource
* @param string $context
*
* @return ResourceReader
*/
public function getReader(ZippyResource $resource, $context)
{
return new LegacyGuzzleReader($resource, $this->client);
}
}

View file

@ -0,0 +1,41 @@
<?php
namespace Alchemy\Zippy\Resource\Reader\Stream;
use Alchemy\Zippy\Resource\Resource as ZippyResource;
use Alchemy\Zippy\Resource\ResourceReader;
class StreamReader implements ResourceReader
{
/**
* @var ZippyResource
*/
private $resource;
/**
* @param ZippyResource $resource
*/
public function __construct(ZippyResource $resource)
{
$this->resource = $resource;
}
/**
* @return string
*/
public function getContents()
{
return file_get_contents($this->resource->getOriginal());
}
/**
* @return resource
*/
public function getContentsAsStream()
{
$stream = is_resource($this->resource->getOriginal()) ?
$this->resource->getOriginal() : @fopen($this->resource->getOriginal(), 'rb');
return $stream;
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace Alchemy\Zippy\Resource\Reader\Stream;
use Alchemy\Zippy\Resource\Resource as ZippyResource;
use Alchemy\Zippy\Resource\ResourceReader;
use Alchemy\Zippy\Resource\ResourceReaderFactory;
class StreamReaderFactory implements ResourceReaderFactory
{
/**
* @param ZippyResource $resource
* @param string $context
*
* @return ResourceReader
*/
public function getReader(ZippyResource $resource, $context)
{
return new StreamReader($resource);
}
}

View file

@ -0,0 +1,66 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Resource;
class RequestMapper
{
private $locator;
/**
* Constructor
*
* @param TargetLocator $locator
*/
public function __construct(TargetLocator $locator)
{
$this->locator = $locator;
}
/**
* Maps resources request to a ResourceCollection
*
* @param $context
* @param array $resources
*
* @return ResourceCollection
*/
public function map($context, array $resources)
{
$data = array();
foreach ($resources as $location => $resource) {
if (is_int($location)) {
$data[] = new Resource($resource, $this->locator->locate($context, $resource));
} else {
$data[] = new Resource($resource, ltrim($location, '/'));
}
}
if (count($data) === 1) {
$context = $data[0]->getOriginal();
}
$collection = new ResourceCollection($context, $data, false);
return $collection;
}
/**
* Creates the default RequestMapper
*
* @return RequestMapper
*/
public static function create()
{
return new static(new TargetLocator());
}
}

View file

@ -0,0 +1,116 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Resource;
class Resource
{
private $original;
private $target;
/**
* Constructor
*
* @param string $original
* @param string $target
*/
public function __construct($original, $target)
{
$this->original = $original;
$this->target = $target;
}
/**
* Returns the target
*
* @return string
*/
public function getTarget()
{
return $this->target;
}
/**
* Returns the original path
*
* @return string
*/
public function getOriginal()
{
return $this->original;
}
/**
* Returns whether the resource can be processed in place given a context or not.
*
* For example :
* - /path/to/file1 can be processed to file1 in /path/to context
* - /path/to/subdir/file2 can be processed to subdir/file2 in /path/to context
*
* @param string $context
*
* @return bool
*/
public function canBeProcessedInPlace($context)
{
if (!is_string($this->original)) {
return false;
}
if (!$this->isLocal()) {
return false;
}
$data = parse_url($this->original);
return sprintf('%s/%s', rtrim($context, '/'), $this->target) === $data['path'];
}
/**
* Returns a context for computing this resource in case it is possible.
*
* Useful to avoid teleporting.
*
* @return null|string
*/
public function getContextForProcessInSinglePlace()
{
if (!is_string($this->original)) {
return null;
}
if (!$this->isLocal()) {
return null;
}
if (PathUtil::basename($this->original) === $this->target) {
return dirname($this->original);
}
return null;
}
/**
* Returns true if the resource is local.
*
* @return bool
*/
private function isLocal()
{
if (!is_string($this->original)) {
return false;
}
$data = parse_url($this->original);
return isset($data['path']);
}
}

View file

@ -0,0 +1,89 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Resource;
use Alchemy\Zippy\Exception\InvalidArgumentException;
use Doctrine\Common\Collections\ArrayCollection;
class ResourceCollection extends ArrayCollection
{
private $context;
/**
* @var bool
*/
private $temporary;
/**
* Constructor
* @param string $context
* @param Resource[] $elements An array of Resource
* @param bool $temporary
*/
public function __construct($context, array $elements, $temporary)
{
array_walk($elements, function($element) {
if (!$element instanceof Resource) {
throw new InvalidArgumentException('ResourceCollection only accept Resource elements');
}
});
$this->context = $context;
$this->temporary = (bool) $temporary;
parent::__construct($elements);
}
/**
* Returns the context related to the collection
*
* @return string
*/
public function getContext()
{
return $this->context;
}
/**
* Tells whether the collection is temporary or not.
*
* A ResourceCollection is temporary when it required a temporary folder to
* fetch data
*
* @return bool
*/
public function isTemporary()
{
return $this->temporary;
}
/**
* Returns true if all resources can be processed in place, false otherwise
*
* @return bool
*/
public function canBeProcessedInPlace()
{
if (count($this) === 1) {
if (null !== $context = $this->first()->getContextForProcessInSinglePlace()) {
$this->context = $context;
return true;
}
}
foreach ($this as $resource) {
if (!$resource->canBeProcessedInPlace($this->context)) {
return false;
}
}
return true;
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Alchemy\Zippy\Resource;
use Alchemy\Zippy\Resource\Resource AS ZippyResource;
class ResourceLocator
{
public function mapResourcePath(ZippyResource $resource, $context)
{
return rtrim($context, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $resource->getTarget();
}
}

View file

@ -0,0 +1,105 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Resource;
use Alchemy\Zippy\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Exception\IOException as SfIOException;
class ResourceManager
{
private $mapper;
private $teleporter;
private $filesystem;
/**
* Constructor
*
* @param RequestMapper $mapper
* @param ResourceTeleporter $teleporter
* @param Filesystem $filesystem
*/
public function __construct(RequestMapper $mapper, ResourceTeleporter $teleporter, Filesystem $filesystem)
{
$this->mapper = $mapper;
$this->filesystem = $filesystem;
$this->teleporter = $teleporter;
}
/**
* Handles an archival request.
*
* The request is an array of string|streams to compute in a context (current
* working directory most of the time)
* Some keys can be associative. In these cases, the key is used the target
* for the file.
*
* @param string $context
* @param array $request
*
* @return ResourceCollection
*
* @throws IOException In case of write failure
*/
public function handle($context, array $request)
{
$collection = $this->mapper->map($context, $request);
if (!$collection->canBeProcessedInPlace()) {
$context = sprintf('%s/%s', sys_get_temp_dir(), uniqid('zippy_'));
try {
$this->filesystem->mkdir($context);
} catch (SfIOException $e) {
throw new IOException(sprintf('Could not create temporary folder %s', $context), $e->getCode(), $e);
}
foreach ($collection as $resource) {
$this->teleporter->teleport($context, $resource);
}
$collection = new ResourceCollection($context, $collection->toArray(), true);
}
return $collection;
}
/**
* This method must be called once the ResourceCollection has been processed.
*
* It will remove temporary files
*
* @todo this should be done in the __destruct method of ResourceCollection
*
* @param ResourceCollection $collection
*/
public function cleanup(ResourceCollection $collection)
{
if ($collection->isTemporary()) {
try {
$this->filesystem->remove($collection->getContext());
} catch (IOException $e) {
// log this ?
}
}
}
/**
* Creates a default ResourceManager
*
* @return ResourceManager
*/
public static function create()
{
return new static(RequestMapper::create(), ResourceTeleporter::create(), new Filesystem());
}
}

View file

@ -0,0 +1,16 @@
<?php
namespace Alchemy\Zippy\Resource;
interface ResourceReader
{
/**
* @return string
*/
public function getContents();
/**
* @return resource
*/
public function getContentsAsStream();
}

View file

@ -0,0 +1,16 @@
<?php
namespace Alchemy\Zippy\Resource;
use Alchemy\Zippy\Resource\Resource as ZippyResource;
interface ResourceReaderFactory
{
/**
* @param ZippyResource $resource
* @param string $context
*
* @return ResourceReader
*/
public function getReader(ZippyResource $resource, $context);
}

View file

@ -0,0 +1,55 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Resource;
use Alchemy\Zippy\Resource\Resource as ZippyResource;
class ResourceTeleporter
{
private $container;
/**
* Constructor
*
* @param TeleporterContainer $container
*/
public function __construct(TeleporterContainer $container)
{
$this->container = $container;
}
/**
* Teleports a resource to its target in the context
*
* @param string $context
* @param ZippyResource $resource
*
* @return ResourceTeleporter
*/
public function teleport($context, ZippyResource $resource)
{
$this
->container
->fromResource($resource)
->teleport($resource, $context);
return $this;
}
/**
* Creates the ResourceTeleporter with the default TeleporterContainer
*
* @return ResourceTeleporter
*/
public static function create()
{
return new static(TeleporterContainer::load());
}
}

View file

@ -0,0 +1,8 @@
<?php
namespace Alchemy\Zippy\Resource;
interface ResourceWriter
{
public function writeFromReader(ResourceReader $reader, $target);
}

View file

@ -0,0 +1,158 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Resource;
use Alchemy\Zippy\Exception\TargetLocatorException;
class TargetLocator
{
/**
* Locates the target for a resource in a context
*
* For example, adding /path/to/file where the context (current working
* directory) is /path/to will return `file` as target
*
* @param string $context
* @param string|resource $resource
*
* @return string
*
* @throws TargetLocatorException when the resource is invalid
*/
public function locate($context, $resource)
{
switch (true) {
case is_resource($resource):
return $this->locateResource($resource);
case is_string($resource):
return $this->locateString($context, $resource);
case $resource instanceof \SplFileInfo:
return $this->locateString($context, $resource->getRealPath());
default:
throw new TargetLocatorException($resource, 'Unknown resource format');
}
}
/**
* Locate the target for a resource.
*
* @param resource $resource
*
* @return string
*
* @throws TargetLocatorException
*/
private function locateResource($resource)
{
$meta = stream_get_meta_data($resource);
$data = parse_url($meta['uri']);
if (!isset($data['path'])) {
throw new TargetLocatorException($resource, 'Unable to retrieve path from resource');
}
return PathUtil::basename($data['path']);
}
/**
* Locate the target for a string.
*
* @param $context
* @param string $resource
*
* @return string
*
* @throws TargetLocatorException
*/
private function locateString($context, $resource)
{
$url = parse_url($resource);
if (isset($url['scheme']) && $this->isLocalFilesystem($url['scheme'])) {
$resource = $url['path'] = $this->cleanupPath($url['path']);
}
// resource is a URI
if (isset($url['scheme'])) {
if ($this->isLocalFilesystem($url['scheme']) && $this->isFileInContext($url['path'], $context)) {
return $this->getRelativePathFromContext($url['path'], $context);
}
return PathUtil::basename($resource);
}
// resource is a local path
if ($this->isFileInContext($resource, $context)) {
$resource = $this->cleanupPath($resource);
return $this->getRelativePathFromContext($resource, $context);
} else {
return PathUtil::basename($resource);
}
}
/**
* Removes backward path sequences (..)
*
* @param string $path
*
* @return string
*
* @throws TargetLocatorException In case the path is invalid
*/
private function cleanupPath($path)
{
if (false === $cleanPath = realpath($path)) {
throw new TargetLocatorException($path, sprintf('%s is an invalid location', $path));
}
return $cleanPath;
}
/**
* Checks whether the path belong to the context
*
* @param string $path A resource path
* @param string $context
*
* @return bool
*/
private function isFileInContext($path, $context)
{
return 0 === strpos($path, $context);
}
/**
* Gets the relative path from the context for the given path
*
* @param string $path A resource path
* @param string $context
*
* @return string
*/
private function getRelativePathFromContext($path, $context)
{
return ltrim(str_replace($context, '', $path), '/\\');
}
/**
* Checks if a scheme refers to a local filesystem
*
* @param string $scheme
*
* @return bool
*/
private function isLocalFilesystem($scheme)
{
return 'plainfile' === $scheme || 'file' === $scheme;
}
}

View file

@ -0,0 +1,64 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Resource\Teleporter;
use Alchemy\Zippy\Exception\IOException;
use Alchemy\Zippy\Resource\Resource as ZippyResource;
/**
* Class AbstractTeleporter
* @package Alchemy\Zippy\Resource\Teleporter
*
* @deprecated Typehint against TeleporterInterface instead and use GenericTeleporter
* with custom reader/writers instead. This class will be removed in v0.5.x
*/
abstract class AbstractTeleporter implements TeleporterInterface
{
/**
* Writes the target
*
* @param string $data
* @param ZippyResource $resource
* @param string $context
*
* @return TeleporterInterface
*
* @throws IOException
*/
protected function writeTarget($data, ZippyResource $resource, $context)
{
$target = $this->getTarget($context, $resource);
if (!file_exists(dirname($target)) && false === mkdir(dirname($target))) {
throw new IOException(sprintf('Could not create parent directory %s', dirname($target)));
}
if (false === file_put_contents($target, $data)) {
throw new IOException(sprintf('Could not write to %s', $target));
}
return $this;
}
/**
* Returns the relative target of a Resource
*
* @param string $context
* @param ZippyResource $resource
*
* @return string
*/
protected function getTarget($context, ZippyResource $resource)
{
return sprintf('%s/%s', rtrim($context, '/'), $resource->getTarget());
}
}

View file

@ -0,0 +1,60 @@
<?php
namespace Alchemy\Zippy\Resource\Teleporter;
use Alchemy\Zippy\Exception\InvalidArgumentException;
use Alchemy\Zippy\Exception\IOException;
use Alchemy\Zippy\Resource\Resource as ZippyResource;
use Alchemy\Zippy\Resource\ResourceLocator;
use Alchemy\Zippy\Resource\ResourceReaderFactory;
use Alchemy\Zippy\Resource\ResourceWriter;
class GenericTeleporter implements TeleporterInterface
{
/**
* @var ResourceReaderFactory
*/
private $readerFactory;
/**
* @var ResourceWriter
*/
private $resourceWriter;
/**
* @var ResourceLocator
*/
private $resourceLocator;
/**
* @param ResourceReaderFactory $readerFactory
* @param ResourceWriter $resourceWriter
* @param ResourceLocator $resourceLocator
*/
public function __construct(
ResourceReaderFactory $readerFactory,
ResourceWriter $resourceWriter,
ResourceLocator $resourceLocator = null
) {
$this->readerFactory = $readerFactory;
$this->resourceWriter = $resourceWriter;
$this->resourceLocator = $resourceLocator ?: new ResourceLocator();
}
/**
* Teleports a file from a destination to an other
*
* @param ZippyResource $resource A Resource
* @param string $context The current context
*
* @throws IOException when file could not be written on local
* @throws InvalidArgumentException when path to file is not valid
*/
public function teleport(ZippyResource $resource, $context)
{
$reader = $this->readerFactory->getReader($resource, $context);
$target = $this->resourceLocator->mapResourcePath($resource, $context);
$this->resourceWriter->writeFromReader($reader, $target);
}
}

View file

@ -0,0 +1,45 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Resource\Teleporter;
use Alchemy\Zippy\Resource\Reader\Guzzle\GuzzleReaderFactory;
use Alchemy\Zippy\Resource\ResourceLocator;
use Alchemy\Zippy\Resource\ResourceReaderFactory;
use Alchemy\Zippy\Resource\Writer\FilesystemWriter;
/**
* Guzzle Teleporter implementation for HTTP resources
*
* @deprecated Use \Alchemy\Zippy\Resource\GenericTeleporter instead. This class will be removed in v0.5.x
*/
class GuzzleTeleporter extends GenericTeleporter
{
/**
* @param ResourceReaderFactory $readerFactory
* @param ResourceLocator $resourceLocator
*/
public function __construct(ResourceReaderFactory $readerFactory = null, ResourceLocator $resourceLocator = null)
{
parent::__construct($readerFactory ?: new GuzzleReaderFactory(), new FilesystemWriter(), $resourceLocator);
}
/**
* Creates the GuzzleTeleporter
*
* @deprecated This method will be removed in v0.5.x
* @return GuzzleTeleporter
*/
public static function create()
{
return new static();
}
}

View file

@ -0,0 +1,51 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Resource\Teleporter;
use Alchemy\Zippy\Resource\Reader\Guzzle\LegacyGuzzleReaderFactory;
use Alchemy\Zippy\Resource\ResourceLocator;
use Alchemy\Zippy\Resource\ResourceReaderFactory;
use Alchemy\Zippy\Resource\Writer\FilesystemWriter;
use Guzzle\Http\Client;
/**
* Guzzle Teleporter implementation for HTTP resources
*
* @deprecated Use \Alchemy\Zippy\Resource\GenericTeleporter instead. This class will be removed in v0.5.x
*/
class LegacyGuzzleTeleporter extends GenericTeleporter
{
/**
* @param Client $client
* @param ResourceReaderFactory $readerFactory
* @param ResourceLocator $resourceLocator
*/
public function __construct(
Client $client = null,
ResourceReaderFactory $readerFactory = null,
ResourceLocator $resourceLocator = null
) {
parent::__construct($readerFactory ?: new LegacyGuzzleReaderFactory($client), new FilesystemWriter(),
$resourceLocator);
}
/**
* Creates the GuzzleTeleporter
*
* @deprecated
* @return LegacyGuzzleTeleporter
*/
public static function create()
{
return new static();
}
}

View file

@ -0,0 +1,82 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Resource\Teleporter;
use Alchemy\Zippy\Exception\InvalidArgumentException;
use Alchemy\Zippy\Exception\IOException;
use Alchemy\Zippy\Resource\Resource as ZippyResource;
use Alchemy\Zippy\Resource\ResourceLocator;
use Symfony\Component\Filesystem\Exception\IOException as SfIOException;
use Symfony\Component\Filesystem\Filesystem;
/**
* This class transports an object using the local filesystem
*/
class LocalTeleporter extends AbstractTeleporter
{
/**
* @var Filesystem
*/
private $filesystem;
/**
* @var ResourceLocator
*/
private $resourceLocator;
/**
* Constructor
*
* @param Filesystem $filesystem
*/
public function __construct(Filesystem $filesystem)
{
$this->filesystem = $filesystem;
$this->resourceLocator = new ResourceLocator();
}
/**
* {@inheritdoc}
*/
public function teleport(ZippyResource $resource, $context)
{
$target = $this->resourceLocator->mapResourcePath($resource, $context);
$path = $resource->getOriginal();
if (!file_exists($path)) {
throw new InvalidArgumentException(sprintf('Invalid path %s', $path));
}
try {
if (is_file($path)) {
$this->filesystem->copy($path, $target);
} elseif (is_dir($path)) {
$this->filesystem->mirror($path, $target);
} else {
throw new InvalidArgumentException(sprintf('Invalid file or directory %s', $path));
}
} catch (SfIOException $e) {
throw new IOException(sprintf('Could not write %s', $target), $e->getCode(), $e);
}
}
/**
* Creates the LocalTeleporter
*
* @return LocalTeleporter
* @deprecated This method will be removed in a future release (0.5.x)
*/
public static function create()
{
return new static(new Filesystem());
}
}

View file

@ -0,0 +1,38 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Resource\Teleporter;
use Alchemy\Zippy\Resource\Reader\Stream\StreamReaderFactory;
use Alchemy\Zippy\Resource\ResourceLocator;
use Alchemy\Zippy\Resource\Writer\StreamWriter;
/**
* This class transport an object using php stream wrapper
*/
class StreamTeleporter extends GenericTeleporter
{
public function __construct()
{
parent::__construct(new StreamReaderFactory(), new StreamWriter(), new ResourceLocator());
}
/**
* Creates the StreamTeleporter
*
* @return StreamTeleporter
* @deprecated This method will be removed in a future release (0.5.x)
*/
public static function create()
{
return new static();
}
}

View file

@ -0,0 +1,30 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Resource\Teleporter;
use Alchemy\Zippy\Exception\InvalidArgumentException;
use Alchemy\Zippy\Exception\IOException;
use Alchemy\Zippy\Resource\Resource as ZippyResource;
interface TeleporterInterface
{
/**
* Teleports a file from a destination to an other
*
* @param ZippyResource $resource A Resource
* @param string $context The current context
*
* @throws IOException when file could not be written on local
* @throws InvalidArgumentException when path to file is not valid
*/
public function teleport(ZippyResource $resource, $context);
}

View file

@ -0,0 +1,189 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy\Resource;
use Alchemy\Zippy\Exception\InvalidArgumentException;
use Alchemy\Zippy\Resource\Reader\Guzzle\GuzzleReaderFactory;
use Alchemy\Zippy\Resource\Reader\Guzzle\LegacyGuzzleReaderFactory;
use Alchemy\Zippy\Resource\Resource as ZippyResource;
use Alchemy\Zippy\Resource\Teleporter\GenericTeleporter;
use Alchemy\Zippy\Resource\Teleporter\LocalTeleporter;
use Alchemy\Zippy\Resource\Teleporter\StreamTeleporter;
use Alchemy\Zippy\Resource\Teleporter\TeleporterInterface;
use Alchemy\Zippy\Resource\Writer\FilesystemWriter;
use Symfony\Component\Filesystem\Filesystem;
/**
* A container of TeleporterInterface
*/
class TeleporterContainer implements \ArrayAccess, \Countable
{
/**
* @var TeleporterInterface[]
*/
private $teleporters = array();
/**
* @var callable[]
*/
private $factories = array();
/**
* Returns the appropriate TeleporterInterface for a given Resource
*
* @param ZippyResource $resource
*
* @return TeleporterInterface
*/
public function fromResource(ZippyResource $resource)
{
switch (true) {
case is_resource($resource->getOriginal()):
$teleporter = 'stream-teleporter';
break;
case is_string($resource->getOriginal()):
$data = parse_url($resource->getOriginal());
if (!isset($data['scheme']) || 'file' === $data['scheme']) {
$teleporter = 'local-teleporter';
} elseif (in_array($data['scheme'], array('http', 'https')) && isset($this->factories['guzzle-teleporter'])) {
$teleporter = 'guzzle-teleporter';
} else {
$teleporter = 'stream-teleporter';
}
break;
default:
throw new InvalidArgumentException('No teleporter found');
}
return $this->getTeleporter($teleporter);
}
private function getTeleporter($typeName)
{
if (!isset($this->teleporters[$typeName])) {
$factory = $this->factories[$typeName];
$this->teleporters[$typeName] = $factory();
}
return $this->teleporters[$typeName];
}
/**
* Instantiates TeleporterContainer and register default teleporters
*
* @return TeleporterContainer
*/
public static function load()
{
$container = new static();
$container->factories['stream-teleporter'] = function () {
return new StreamTeleporter();
};
$container->factories['local-teleporter'] = function () {
return new LocalTeleporter(new Filesystem());
};
if (class_exists('GuzzleHttp\Client')) {
$container->factories['guzzle-teleporter'] = function () {
return new GenericTeleporter(
new GuzzleReaderFactory(),
new FilesystemWriter(),
new ResourceLocator()
);
};
}
elseif (class_exists('Guzzle\Http\Client')) {
$container->factories['guzzle-teleporter'] = function () {
return new GenericTeleporter(
new LegacyGuzzleReaderFactory(),
new FilesystemWriter(),
new ResourceLocator()
);
};
}
return $container;
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Whether a offset exists
*
* @link http://php.net/manual/en/arrayaccess.offsetexists.php
*
* @param mixed $offset <p>
* An offset to check for.
* </p>
*
* @return bool true on success or false on failure.
* </p>
* <p>
* The return value will be casted to boolean if non-boolean was returned.
*/
public function offsetExists($offset)
{
return isset($this->teleporters[$offset]);
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Offset to retrieve
* @link http://php.net/manual/en/arrayaccess.offsetget.php
* @param mixed $offset <p>
* The offset to retrieve.
* </p>
* @return mixed Can return all value types.
*/
public function offsetGet($offset)
{
return $this->getTeleporter($offset);
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Offset to set
* @link http://php.net/manual/en/arrayaccess.offsetset.php
* @param mixed $offset <p>
* The offset to assign the value to.
* </p>
* @param mixed $value <p>
* The value to set.
* </p>
* @return void
*/
public function offsetSet($offset, $value)
{
throw new \BadMethodCallException();
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Offset to unset
* @link http://php.net/manual/en/arrayaccess.offsetunset.php
* @param mixed $offset <p>
* The offset to unset.
* </p>
* @return void
*/
public function offsetUnset($offset)
{
throw new \BadMethodCallException();
}
public function count()
{
return count($this->teleporters);
}
}

View file

@ -0,0 +1,23 @@
<?php
namespace Alchemy\Zippy\Resource\Writer;
use Alchemy\Zippy\Resource\ResourceReader;
use Alchemy\Zippy\Resource\ResourceWriter;
class FilesystemWriter implements ResourceWriter
{
/**
* @param ResourceReader $reader
* @param string $target
*/
public function writeFromReader(ResourceReader $reader, $target)
{
$directory = dirname($target);
if (!is_dir($directory)) {
mkdir($directory, 0777, true);
}
file_put_contents($target, $reader->getContentsAsStream());
}
}

View file

@ -0,0 +1,27 @@
<?php
namespace Alchemy\Zippy\Resource\Writer;
use Alchemy\Zippy\Resource\ResourceReader;
use Alchemy\Zippy\Resource\ResourceWriter;
class StreamWriter implements ResourceWriter
{
/**
* @param ResourceReader $reader
* @param string $target
*/
public function writeFromReader(ResourceReader $reader, $target)
{
$directory = dirname($target);
if (!is_dir($directory)) {
mkdir($directory, 0777, true);
}
$targetResource = fopen($target, 'w+');
$sourceResource = $reader->getContentsAsStream();
stream_copy_to_stream($sourceResource, $targetResource);
fclose($targetResource);
}
}

220
vendor/alchemy/zippy/src/Zippy.php vendored Normal file
View file

@ -0,0 +1,220 @@
<?php
/*
* This file is part of Zippy.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Zippy;
use Alchemy\Zippy\Adapter\AdapterContainer;
use Alchemy\Zippy\Adapter\AdapterInterface;
use Alchemy\Zippy\Archive\ArchiveInterface;
use Alchemy\Zippy\Exception\ExceptionInterface;
use Alchemy\Zippy\Exception\FormatNotSupportedException;
use Alchemy\Zippy\Exception\NoAdapterOnPlatformException;
use Alchemy\Zippy\Exception\RuntimeException;
use Alchemy\Zippy\FileStrategy\FileStrategyInterface;
use Alchemy\Zippy\FileStrategy\TarBz2FileStrategy;
use Alchemy\Zippy\FileStrategy\TarFileStrategy;
use Alchemy\Zippy\FileStrategy\TarGzFileStrategy;
use Alchemy\Zippy\FileStrategy\TB2FileStrategy;
use Alchemy\Zippy\FileStrategy\TBz2FileStrategy;
use Alchemy\Zippy\FileStrategy\TGzFileStrategy;
use Alchemy\Zippy\FileStrategy\ZipFileStrategy;
class Zippy
{
/**
* @var AdapterContainer
*/
public $adapters;
/**
* @var FileStrategyInterface[][]
*/
private $strategies = array();
public function __construct(AdapterContainer $adapters)
{
$this->adapters = $adapters;
}
/**
* Creates an archive
*
* @param string $path
* @param string|array|\Traversable|null $files
* @param bool $recursive
* @param string|null $type
*
* @return ArchiveInterface
*
* @throws RuntimeException In case of failure
*/
public function create($path, $files = null, $recursive = true, $type = null)
{
if (null === $type) {
$type = $this->guessAdapterExtension($path);
}
try {
return $this
->getAdapterFor($this->sanitizeExtension($type))
->create($path, $files, $recursive);
} catch (ExceptionInterface $e) {
throw new RuntimeException('Unable to create archive', $e->getCode(), $e);
}
}
/**
* Opens an archive.
*
* @param string $path
*
* @return ArchiveInterface
*
* @throws RuntimeException In case of failure
*/
public function open($path)
{
$type = $this->guessAdapterExtension($path);
try {
return $this
->getAdapterFor($this->sanitizeExtension($type))
->open($path);
} catch (ExceptionInterface $e) {
throw new RuntimeException('Unable to open archive', $e->getCode(), $e);
}
}
/**
* Adds a strategy.
*
* The last strategy added is preferred over the other ones.
* You can add a strategy twice ; when doing this, the first one is removed
* when inserting the second one.
*
* @param FileStrategyInterface $strategy
*
* @return Zippy
*/
public function addStrategy(FileStrategyInterface $strategy)
{
$extension = $this->sanitizeExtension($strategy->getFileExtension());
if (!isset($this->strategies[$extension])) {
$this->strategies[$extension] = array();
}
if (false !== $key = array_search($strategy, $this->strategies[$extension], true)) {
unset($this->strategies[$extension][$key]);
}
array_unshift($this->strategies[$extension], $strategy);
return $this;
}
/**
* Returns the strategies as they are stored
*
* @return array
*/
public function getStrategies()
{
return $this->strategies;
}
/**
* Returns an adapter for a file extension
*
* @param string $extension The extension
*
* @return AdapterInterface
*
* @throws FormatNotSupportedException When no strategy is defined for this extension
* @throws NoAdapterOnPlatformException When no adapter is supported for this extension on this platform
*/
public function getAdapterFor($extension)
{
$extension = $this->sanitizeExtension($extension);
if (!$extension || !isset($this->strategies[$extension])) {
throw new FormatNotSupportedException(sprintf('No strategy for %s extension', $extension));
}
foreach ($this->strategies[$extension] as $strategy) {
foreach ($strategy->getAdapters() as $adapter) {
if ($adapter->isSupported()) {
return $adapter;
}
}
}
throw new NoAdapterOnPlatformException(sprintf('No adapter available for %s on this platform', $extension));
}
/**
* Creates Zippy and loads default strategies
*
* @return Zippy
*/
public static function load()
{
$adapters = AdapterContainer::load();
$factory = new static($adapters);
$factory->addStrategy(new ZipFileStrategy($adapters));
$factory->addStrategy(new TarFileStrategy($adapters));
$factory->addStrategy(new TarGzFileStrategy($adapters));
$factory->addStrategy(new TarBz2FileStrategy($adapters));
$factory->addStrategy(new TB2FileStrategy($adapters));
$factory->addStrategy(new TBz2FileStrategy($adapters));
$factory->addStrategy(new TGzFileStrategy($adapters));
return $factory;
}
/**
* Sanitize an extension.
*
* Strips dot from the beginning, converts to lowercase and remove trailing
* whitespaces
*
* @param string $extension
*
* @return string
*/
private function sanitizeExtension($extension)
{
return ltrim(trim(mb_strtolower($extension)), '.');
}
/**
* Finds an extension that has strategy registered given a file path
*
* Returns null if no matching strategy found.
*
* @param string $path
*
* @return string|null
*/
private function guessAdapterExtension($path)
{
$path = strtolower(trim($path));
foreach ($this->strategies as $extension => $strategy) {
if ($extension === substr($path, (strlen($extension) * -1))) {
return $extension;
}
}
return null;
}
}

83
vendor/asm89/stack-cors/README.md vendored Normal file
View file

@ -0,0 +1,83 @@
# Stack/Cors
Library and middleware enabling cross-origin resource sharing for your
http-{foundation,kernel} using application. It attempts to implement the
[W3C Recommendation] for cross-origin resource sharing.
[W3C Recommendation]: http://www.w3.org/TR/cors/
Master [![Build Status](https://secure.travis-ci.org/asm89/stack-cors.png?branch=master)](http://travis-ci.org/asm89/stack-cors)
## Installation
Require `asm89/stack-cors` using composer.
## Usage
This package can be used as a library or as [stack middleware].
[stack middleware]: http://stackphp.com/
### Options
| Option | Description | Default value |
|------------------------|------------------------------------------------------------|---------------|
| allowedMethods | Matches the request method. | `array()` |
| allowedOrigins | Matches the request origin. | `array()` |
| allowedOriginsPatterns | Matches the request origin with `preg_match`. | `array()` |
| allowedHeaders | Sets the Access-Control-Allow-Headers response header. | `array()` |
| exposedHeaders | Sets the Access-Control-Expose-Headers response header. | `false` |
| maxAge | Sets the Access-Control-Max-Age response header. | `false` |
| supportsCredentials | Sets the Access-Control-Allow-Credentials header. | `false` |
The _allowedMethods_ and _allowedHeaders_ options are case-insensitive.
You don't need to provide both _allowedOrigins_ and _allowedOriginsPatterns_. If one of the strings passed matches, it is considered a valid origin.
If `array('*')` is provided to _allowedMethods_, _allowedOrigins_ or _allowedHeaders_ all methods / origins / headers are allowed.
### Example: using the library
```php
<?php
use Asm89\Stack\CorsService;
$cors = new CorsService(array(
'allowedHeaders' => array('x-allowed-header', 'x-other-allowed-header'),
'allowedMethods' => array('DELETE', 'GET', 'POST', 'PUT'),
'allowedOrigins' => array('localhost'),
'allowedOriginsPatterns' => array('/localhost:\d/'),
'exposedHeaders' => false,
'maxAge' => false,
'supportsCredentials' => false,
));
$cors->addActualRequestHeaders(Response $response, $origin);
$cors->handlePreflightRequest(Request $request);
$cors->isActualRequestAllowed(Request $request);
$cors->isCorsRequest(Request $request);
$cors->isPreflightRequest(Request $request);
```
## Example: using the stack middleware
```php
<?php
use Asm89\Stack\Cors;
$app = new Cors($app, array(
// you can use array('*') to allow any headers
'allowedHeaders' => array('x-allowed-header', 'x-other-allowed-header'),
// you can use array('*') to allow any methods
'allowedMethods' => array('DELETE', 'GET', 'POST', 'PUT'),
// you can use array('*') to allow requests from any origin
'allowedOrigins' => array('localhost'),
// you can enter regexes that are matched to the origin request header
'allowedOriginsPatterns' => array('/localhost:\d/'),
'exposedHeaders' => false,
'maxAge' => false,
'supportsCredentials' => false,
));
```

43
vendor/asm89/stack-cors/composer.json vendored Normal file
View file

@ -0,0 +1,43 @@
{
"name": "asm89/stack-cors",
"description": "Cross-origin resource sharing library and stack middleware",
"keywords": ["stack", "cors"],
"homepage": "https://github.com/asm89/stack-cors",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Alexander",
"email": "iam.asm89@gmail.com"
}
],
"require": {
"php": ">=5.5.9",
"symfony/http-foundation": "~2.7|~3.0|~4.0",
"symfony/http-kernel": "~2.7|~3.0|~4.0"
},
"require-dev": {
"phpunit/phpunit": "^5.0 || ^4.8.10",
"squizlabs/php_codesniffer": "^2.3"
},
"autoload": {
"psr-4": {
"Asm89\\Stack\\": "src/Asm89/Stack/"
}
},
"autoload-dev": {
"psr-4": {
"Asm89\\Stack\\": "test/Asm89/Stack/"
}
},
"scripts": {
"test": "phpunit",
"check-style": "phpcs -p --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src",
"fix-style": "phpcbf -p --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src"
},
"extra": {
"branch-alias": {
"dev-master": "1.2-dev"
}
}
}

View file

@ -28,12 +28,13 @@ class Cors implements HttpKernelInterface
private $cors; private $cors;
private $defaultOptions = array( private $defaultOptions = array(
'allowedHeaders' => array(), 'allowedHeaders' => array(),
'allowedMethods' => array(), 'allowedMethods' => array(),
'allowedOrigins' => array(), 'allowedOrigins' => array(),
'exposedHeaders' => false, 'allowedOriginsPatterns' => array(),
'maxAge' => false, 'exposedHeaders' => false,
'supportsCredentials' => false, 'maxAge' => false,
'supportsCredentials' => false,
); );
public function __construct(HttpKernelInterface $app, array $options = array()) public function __construct(HttpKernelInterface $app, array $options = array())

View file

@ -28,6 +28,7 @@ class CorsService
{ {
$options += array( $options += array(
'allowedOrigins' => array(), 'allowedOrigins' => array(),
'allowedOriginsPatterns' => array(),
'supportsCredentials' => false, 'supportsCredentials' => false,
'allowedHeaders' => array(), 'allowedHeaders' => array(),
'exposedHeaders' => array(), 'exposedHeaders' => array(),
@ -176,7 +177,17 @@ class CorsService
} }
$origin = $request->headers->get('Origin'); $origin = $request->headers->get('Origin');
return in_array($origin, $this->options['allowedOrigins']); if (in_array($origin, $this->options['allowedOrigins'])) {
return true;
}
foreach ($this->options['allowedOriginsPatterns'] as $pattern) {
if (preg_match($pattern, $origin)) {
return true;
}
}
return false;
} }
private function checkMethod(Request $request) private function checkMethod(Request $request)

7
vendor/autoload.php vendored Normal file
View file

@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit5b1c1b80ca16f098d2571547d6c6045f::getLoader();

1
vendor/bin/dcg vendored Symbolic link
View file

@ -0,0 +1 @@
../chi-teck/drupal-code-generator/bin/dcg

1
vendor/bin/drupal vendored Symbolic link
View file

@ -0,0 +1 @@
../drupal/console/bin/drupal

1
vendor/bin/drush vendored Symbolic link
View file

@ -0,0 +1 @@
../drush/drush/drush

1
vendor/bin/php-parse vendored Symbolic link
View file

@ -0,0 +1 @@
../nikic/php-parser/bin/php-parse

1
vendor/bin/psysh vendored Symbolic link
View file

@ -0,0 +1 @@
../psy/psysh/bin/psysh

1
vendor/bin/release vendored Symbolic link
View file

@ -0,0 +1 @@
../consolidation/self-update/scripts/release

1
vendor/bin/robo vendored Symbolic link
View file

@ -0,0 +1 @@
../consolidation/robo/robo

Some files were not shown because too many files have changed in this diff Show more