From 8afc2ca6942573ae564786d036b63d8489ad7876 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 8 Jun 2011 00:14:50 +0200 Subject: [PATCH] Add support for PEAR repository and downloading, example composer.json would look like {"repositories":{"doctrine": {"pear":"http://pear.doctrine-project.org"},"requires":{"DoctrineORM":"2.0.5"}}. You can use pear repositories this way for now. PEAR packages are not downloaded via pear, only via their .tgz packages in the PEAR repository. Automatically detecting dependencies will require reverse-engineering the PEAR protocol a little bit more, however from looking at a pirum output it looks simple. --- bin/composer | 2 + src/Composer/Composer.php | 6 +- .../Downloader/AbstractDownloader.php | 41 ++++++++++ src/Composer/Downloader/PearDownloader.php | 47 ++++++++++++ src/Composer/Downloader/ZipDownloader.php | 2 +- src/Composer/Repository/PearRepository.php | 76 +++++++++++++++++++ 6 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 src/Composer/Downloader/AbstractDownloader.php create mode 100644 src/Composer/Downloader/PearDownloader.php create mode 100644 src/Composer/Repository/PearRepository.php diff --git a/bin/composer b/bin/composer index aae84edda..3ecedcf9d 100755 --- a/bin/composer +++ b/bin/composer @@ -5,11 +5,13 @@ require __DIR__.'/../tests/bootstrap.php'; use Composer\Composer; use Composer\Downloader\GitDownloader; +use Composer\Downloader\PearDownloader; use Composer\Command\InstallCommand; use Composer\Installer\LibraryInstaller; $composer = new Composer(); $composer->addDownloader('git', new GitDownloader); +$composer->addDownloader('pear', new PearDownloader); $composer->addInstaller('library', new LibraryInstaller); $cmd = new InstallCommand(); diff --git a/src/Composer/Composer.php b/src/Composer/Composer.php index 6a9b40805..dc662f92e 100644 --- a/src/Composer/Composer.php +++ b/src/Composer/Composer.php @@ -63,7 +63,7 @@ class Composer unset($this->repositories[$name]); } if (is_array($spec) && count($spec)) { - return $this->repositories[$name] = $this->createRepository(key($spec), current($spec)); + return $this->repositories[$name] = $this->createRepository($name, key($spec), current($spec)); } throw new \UnexpectedValueException('Invalid repositories specification '.var_export($spec, true)); } @@ -73,7 +73,7 @@ class Composer return $this->repositories; } - public function createRepository($type, $spec) + public function createRepository($name, $type, $spec) { if (is_string($spec)) { $spec = array('url' => $spec); @@ -94,6 +94,8 @@ class Composer case 'composer': return new ComposerRepository($spec['url']); + case 'pear': + return new Repository\PearRepository($spec['url'], isset($spec['name']) ? $spec['name'] : $name); } } } \ No newline at end of file diff --git a/src/Composer/Downloader/AbstractDownloader.php b/src/Composer/Downloader/AbstractDownloader.php new file mode 100644 index 000000000..3d5cb08e6 --- /dev/null +++ b/src/Composer/Downloader/AbstractDownloader.php @@ -0,0 +1,41 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Downloader; + +use Composer\Package\PackageInterface; + +/** + * @author Jordi Boggiano + * @author Benjamin Eberlei + */ +abstract class AbstractDownloader +{ + protected function downloadFile ($url, $path) + { + $file = fopen($url, "rb"); + if ($file) { + $newf = fopen($path, "wb"); + if ($newf) { + while (!feof($file)) { + fwrite($newf, fread($file, 1024 * 8), 1024 * 8); + } + } + } + if ($file) { + fclose($file); + } + if ($newf) { + fclose($newf); + } + } +} \ No newline at end of file diff --git a/src/Composer/Downloader/PearDownloader.php b/src/Composer/Downloader/PearDownloader.php new file mode 100644 index 000000000..5ef405cea --- /dev/null +++ b/src/Composer/Downloader/PearDownloader.php @@ -0,0 +1,47 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Downloader; + +use Composer\Package\PackageInterface; + +/** + * @author Benjamin Eberlei + */ +class PearDownloader extends AbstractDownloader +{ + public function download(PackageInterface $package, $path) + { + $path = $path . "/" . $package->getName(); + if (!is_dir($path)) { + if (file_exists($path)) { + throw new \UnexpectedValueException($path.' exists and is not a directory.'); + } + if (!mkdir($path, 0777, true)) { + throw new \UnexpectedValueException($path.' does not exist and could not be created.'); + } + } + + $tmpName = tempnam(sys_get_temp_dir(), ''); + $this->downloadFile($package->getSourceUrl(), $tmpName); + + if (!file_exists($tmpName)) { + throw new \UnexpectedValueException($package->getName().' could not be saved into '.$tmpName.', make sure the' + .' directory is writable and you have internet connectivity.'); + } + + $cwd = getcwd(); + chdir($path); + system('tar xzf '.escapeshellarg($tmpName)); + chdir($cwd); + } +} \ No newline at end of file diff --git a/src/Composer/Downloader/ZipDownloader.php b/src/Composer/Downloader/ZipDownloader.php index 9fef0f9e8..7333cb75b 100644 --- a/src/Composer/Downloader/ZipDownloader.php +++ b/src/Composer/Downloader/ZipDownloader.php @@ -17,7 +17,7 @@ use Composer\Package\PackageInterface; /** * @author Jordi Boggiano */ -class ZipDownloader +class ZipDownloader extends AbstractDownloader { public function download(PackageInterface $package, $path) { diff --git a/src/Composer/Repository/PearRepository.php b/src/Composer/Repository/PearRepository.php new file mode 100644 index 000000000..f713ef7e0 --- /dev/null +++ b/src/Composer/Repository/PearRepository.php @@ -0,0 +1,76 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository; + +use Composer\Package\MemoryPackage; +use Composer\Package\BasePackage; +use Composer\Package\Link; +use Composer\Package\LinkConstraint\VersionConstraint; + +/** + * @author Benjamin Eberlei + */ +class PearRepository extends ArrayRepository +{ + private $name; + private $url; + + public function __construct($url, $name) + { + $this->url = $url; + $this->name = $name; + + if (!filter_var($this->url, FILTER_VALIDATE_URL)) { + throw new \UnexpectedValueException("Invalid url given for PEAR repository " . $name); + } + } + + protected function initialize() + { + parent::initialize(); + + exec("pear remote-list -c ".escapeshellarg($this->name), $output, $return); + + if ($return != 0) { + throw new \BadMethodCallException("Could not execute pear channel-list, an error occured."); + } + + $headersDone = false; + foreach ($output AS $line) { + $parts = explode(" ", preg_replace('(\s{2,})', ' ', trim($line))); + if (count($parts) != 2) { + continue; + } + list($packageName, $pearVersion) = $parts; + + if (!$headersDone) { + if ($packageName == "PACKAGE" && $pearVersion == "VERSION") { + $headersDone = true; + } + continue; + } + + if ($pearVersion == "-n/a-") { + continue; // Preferred stability is set to a level that this package can't fullfil. + } + + $version = BasePackage::parseVersion($pearVersion); + + $package = new MemoryPackage($packageName, $version['version'], $version['type']); + $package->setSourceType('pear'); + $package->setSourceUrl($this->url.'/get/'.$packageName.'-'.$pearVersion.".tgz"); + + $this->addPackage($package); + } + } +}