1
0
Fork 0

Merge remote-tracking branch 'palex-fpt/pear-channel-reader'

pull/862/merge
Jordi Boggiano 2012-07-03 10:27:34 +02:00
commit 6fb667a752
40 changed files with 2229 additions and 477 deletions

View File

@ -202,7 +202,7 @@ should you need to specify one for whatever reason, you can use `git`, `svn` or
It is possible to install packages from any PEAR channel by using the `pear`
repository. Composer will prefix all package names with `pear-{channelName}/` to
avoid conflicts.
avoid conflicts. All packages are also aliased with prefix `pear-{channelAlias}/`
Example using `pear2.php.net`:
@ -214,6 +214,7 @@ Example using `pear2.php.net`:
}
],
"require": {
"pear-pear2.php.net/PEAR2_Text_Markdown": "*",
"pear-pear2/PEAR2_HTTP_Request": "*"
}
}
@ -224,6 +225,41 @@ In this case the short name of the channel is `pear2`, so the
> **Note:** The `pear` repository requires doing quite a few requests per
> package, so this may considerably slow down the installation process.
#### Custom channel alias
It is possible to alias all pear channel packages with custom name.
Example:
You own private pear repository and going to use composer abilities to bring dependencies from vcs or transit to composer repository scheme.
Your repository list of packages:
* BasePackage, requires nothing
* IntermediatePackage, depends on BasePackage
* TopLevelPackage1 and TopLevelPackage2 both dependth on IntermediatePackage.
For composer it looks like:
* "pear-pear.foobar.repo/IntermediatePackage" depends on "pear-pear.foobar.repo/BasePackage",
* "pear-pear.foobar.repo/TopLevelPackage1" depends on "pear-pear.foobar.repo/IntermediatePackage",
* "pear-pear.foobar.repo/TopLevelPackage2" depends on "pear-pear.foobar.repo/IntermediatePackage"
When you update one of your packages to composer naming scheme or made it available through vcs, your older dependencies would not see new version, cause it would be named like "foobar/IntermediatePackage". Specifying 'vendor-alias' for pear repository, you will get all its packages aliased with composer-like names. Following example would take BasePackage, TopLevelPackage1 and TopLevelPackage2 packages from pear repository and IntermediatePackage from github repository:
{
"repositories": [
{
"type": "git",
"https://github.com/foobar/intermediate.git"
},
{
"type": "pear",
"url": "http://pear.foobar.repo",
"vendor-alias": "foobar"
}
],
"require": {
"foobar/TopLevelPackage1": "*",
"foobar/TopLevelPackage2": "*"
}
}
### Package
If you want to use a project that does not support composer through any of the

View File

@ -1,52 +0,0 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* 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;
/**
* Downloader for pear packages
*
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Kirill chEbba Chebunin <iam@chebba.org>
*/
class PearDownloader extends FileDownloader
{
/**
* {@inheritDoc}
*/
public function download(PackageInterface $package, $path)
{
parent::download($package, $path);
$fileName = $this->getFileName($package, $path);
if ($this->io->isVerbose()) {
$this->io->write(' Installing PEAR package');
}
try {
$pearExtractor = new PearPackageExtractor($fileName);
$pearExtractor->extractTo($path);
if ($this->io->isVerbose()) {
$this->io->write(' Cleaning up');
}
unlink($fileName);
} catch (\Exception $e) {
// clean up
$this->filesystem->removeDirectory($path);
throw $e;
}
$this->io->write('');
}
}

View File

@ -35,22 +35,22 @@ class PearPackageExtractor
throw new \UnexpectedValueException('PEAR package file is not found at '.$file);
}
$this->filesystem = new Filesystem();
$this->file = $file;
}
/**
* Installs PEAR source files according to package.xml definitions and removes extracted files
*
* @param $file string path to downloaded PEAR archive file
* @param $target string target install location. all source installation would be performed relative to target path.
* @param $role string type of files to install. default role for PEAR source files are 'php'.
*
* @param string $target target install location. all source installation would be performed relative to target path.
* @param array $roles types of files to install. default role for PEAR source files are 'php'.
* @param array $vars used for replacement tasks
* @throws \RuntimeException
* @throws \UnexpectedValueException
*
*/
public function extractTo($target, $role = 'php')
public function extractTo($target, array $roles = array('php' => '/', 'script' => '/bin'), $vars = array())
{
$this->filesystem = new Filesystem();
$extractionPath = $target.'/tarball';
try {
@ -61,8 +61,8 @@ class PearPackageExtractor
throw new \RuntimeException('Invalid PEAR package. It must contain package.xml file.');
}
$fileCopyActions = $this->buildCopyActions($extractionPath, $role);
$this->copyFiles($fileCopyActions, $extractionPath, $target);
$fileCopyActions = $this->buildCopyActions($extractionPath, $roles, $vars);
$this->copyFiles($fileCopyActions, $extractionPath, $target, $roles, $vars);
$this->filesystem->removeDirectory($extractionPath);
} catch (\Exception $exception) {
throw new \UnexpectedValueException(sprintf('Failed to extract PEAR package %s to %s. Reason: %s', $this->file, $target, $exception->getMessage()), 0, $exception);
@ -72,20 +72,24 @@ class PearPackageExtractor
/**
* Perform copy actions on files
*
* @param $files array array('from', 'to') with relative paths
* @param array $files array of copy actions ('from', 'to') with relative paths
* @param $source string path to source dir.
* @param $target string path to destination dir
* @param array $roles array [role => roleRoot] relative root for files having that role
* @param array $vars list of values can be used for replacement tasks
*/
private function copyFiles($files, $source, $target)
private function copyFiles($files, $source, $target, $roles, $vars)
{
foreach ($files as $file) {
$from = $this->combine($source, $file['from']);
$to = $this->combine($target, $file['to']);
$this->copyFile($from, $to);
$to = $this->combine($target, $roles[$file['role']]);
$to = $this->combine($to, $file['to']);
$tasks = $file['tasks'];
$this->copyFile($from, $to, $tasks, $vars);
}
}
private function copyFile($from, $to)
private function copyFile($from, $to, $tasks, $vars)
{
if (!is_file($from)) {
throw new \RuntimeException('Invalid PEAR package. package.xml defines file that is not located inside tarball.');
@ -93,7 +97,24 @@ class PearPackageExtractor
$this->filesystem->ensureDirectoryExists(dirname($to));
if (!copy($from, $to)) {
if (0 == count($tasks)) {
$copied = copy($from, $to);
} else {
$content = file_get_contents($from);
$replacements = array();
foreach ($tasks as $task) {
$pattern = $task['from'];
$varName = $task['to'];
if (isset($vars[$varName])) {
$replacements[$pattern] = $vars[$varName];
}
}
$content = strtr($content, $replacements);
$copied = file_put_contents($to, $content);
}
if (false === $copied) {
throw new \RuntimeException(sprintf('Failed to copy %s to %s', $from, $to));
}
}
@ -107,7 +128,7 @@ class PearPackageExtractor
* path, and target is destination of file (also relative to $source path)
* @throws \RuntimeException
*/
private function buildCopyActions($source, $role)
private function buildCopyActions($source, array $roles, $vars)
{
/** @var $package \SimpleXmlElement */
$package = simplexml_load_file($this->combine($source, 'package.xml'));
@ -120,13 +141,18 @@ class PearPackageExtractor
$packageName = (string) $package->name;
$packageVersion = (string) $package->release->version;
$sourceDir = $packageName . '-' . $packageVersion;
$result = $this->buildSourceList10($children, $role, $sourceDir);
$result = $this->buildSourceList10($children, $roles, $sourceDir);
} elseif ('2.0' == $packageSchemaVersion || '2.1' == $packageSchemaVersion) {
$children = $package->contents->children();
$packageName = (string) $package->name;
$packageVersion = (string) $package->version->release;
$sourceDir = $packageName . '-' . $packageVersion;
$result = $this->buildSourceList20($children, $role, $sourceDir);
$result = $this->buildSourceList20($children, $roles, $sourceDir);
$namespaces = $package->getNamespaces();
$package->registerXPathNamespace('ns', $namespaces['']);
$releaseNodes = $package->xpath('ns:phprelease');
$this->applyRelease($result, $releaseNodes, $vars);
} else {
throw new \RuntimeException('Unsupported schema version of package definition file.');
}
@ -134,7 +160,35 @@ class PearPackageExtractor
return $result;
}
private function buildSourceList10($children, $targetRole, $source = '', $target = '', $role = null)
private function applyRelease(&$actions, $releaseNodes, $vars)
{
foreach ($releaseNodes as $releaseNode) {
$requiredOs = $releaseNode->installconditions && $releaseNode->installconditions->os && $releaseNode->installconditions->os->name ? (string) $releaseNode->installconditions->os->name : '';
if ($requiredOs && $vars['os'] != $requiredOs) {
continue;
}
if ($releaseNode->filelist) {
foreach ($releaseNode->filelist->children() as $action) {
if ('install' == $action->getName()) {
$name = (string) $action['name'];
$as = (string) $action['as'];
if (isset($actions[$name])) {
$actions[$name]['to'] = $as;
}
} elseif ('ignore' == $action->getName()) {
$name = (string) $action['name'];
unset($actions[$name]);
} else {
// unknown action
}
}
}
break;
}
}
private function buildSourceList10($children, $targetRoles, $source = '', $target = '', $role = null)
{
$result = array();
@ -145,14 +199,15 @@ class PearPackageExtractor
$dirSource = $this->combine($source, (string) $child['name']);
$dirTarget = $child['baseinstalldir'] ? : $target;
$dirRole = $child['role'] ? : $role;
$dirFiles = $this->buildSourceList10($child->children(), $targetRole, $dirSource, $dirTarget, $dirRole);
$dirFiles = $this->buildSourceList10($child->children(), $targetRoles, $dirSource, $dirTarget, $dirRole);
$result = array_merge($result, $dirFiles);
} elseif ($child->getName() == 'file') {
if (($child['role'] ? : $role) == $targetRole) {
$fileRole = (string) $child['role'] ? : $role;
if (isset($targetRoles[$fileRole])) {
$fileName = (string) ($child['name'] ? : $child[0]); // $child[0] means text content
$fileSource = $this->combine($source, $fileName);
$fileTarget = $this->combine((string) $child['baseinstalldir'] ? : $target, $fileName);
$result[] = array('from' => $fileSource, 'to' => $fileTarget);
$result[(string) $child['name']] = array('from' => $fileSource, 'to' => $fileTarget, 'role' => $fileRole, 'tasks' => array());
}
}
}
@ -160,24 +215,31 @@ class PearPackageExtractor
return $result;
}
private function buildSourceList20($children, $targetRole, $source = '', $target = '', $role = null)
private function buildSourceList20($children, $targetRoles, $source = '', $target = '', $role = null)
{
$result = array();
// enumerating files
foreach ($children as $child) {
/** @var $child \SimpleXMLElement */
if ($child->getName() == 'dir') {
if ('dir' == $child->getName()) {
$dirSource = $this->combine($source, $child['name']);
$dirTarget = $child['baseinstalldir'] ? : $target;
$dirRole = $child['role'] ? : $role;
$dirFiles = $this->buildSourceList20($child->children(), $targetRole, $dirSource, $dirTarget, $dirRole);
$dirFiles = $this->buildSourceList20($child->children(), $targetRoles, $dirSource, $dirTarget, $dirRole);
$result = array_merge($result, $dirFiles);
} elseif ($child->getName() == 'file') {
if (($child['role'] ? : $role) == $targetRole) {
} elseif ('file' == $child->getName()) {
$fileRole = (string) $child['role'] ? : $role;
if (isset($targetRoles[$fileRole])) {
$fileSource = $this->combine($source, (string) $child['name']);
$fileTarget = $this->combine((string) ($child['baseinstalldir'] ? : $target), (string) $child['name']);
$result[] = array('from' => $fileSource, 'to' => $fileTarget);
$fileTasks = array();
foreach ($child->children('http://pear.php.net/dtd/tasks-1.0') as $taskNode) {
if ('replace' == $taskNode->getName()) {
$fileTasks[] = array('from' => (string) $taskNode->attributes()->from, 'to' => (string) $taskNode->attributes()->to);
}
}
$result[(string) $child['name']] = array('from' => $fileSource, 'to' => $fileTarget, 'role' => $fileRole, 'tasks' => $fileTasks);
}
}
}

View File

@ -225,7 +225,6 @@ class Factory
$dm->setDownloader('git', new Downloader\GitDownloader($io));
$dm->setDownloader('svn', new Downloader\SvnDownloader($io));
$dm->setDownloader('hg', new Downloader\HgDownloader($io));
$dm->setDownloader('pear', new Downloader\PearDownloader($io));
$dm->setDownloader('zip', new Downloader\ZipDownloader($io));
$dm->setDownloader('tar', new Downloader\TarDownloader($io));
$dm->setDownloader('phar', new Downloader\PharDownloader($io));
@ -251,6 +250,7 @@ class Factory
protected function createDefaultInstallers(Installer\InstallationManager $im, Composer $composer, IOInterface $io)
{
$im->addInstaller(new Installer\LibraryInstaller($io, $composer, null));
$im->addInstaller(new Installer\PearInstaller($io, $composer, 'pear-library'));
$im->addInstaller(new Installer\InstallerInstaller($io, $composer));
$im->addInstaller(new Installer\MetapackageInstaller($io));
}

View File

@ -82,7 +82,7 @@ class LibraryInstaller implements InstallerInterface
$this->removeBinaries($package);
}
$this->downloadManager->download($package, $downloadPath);
$this->installCode($package);
$this->installBinaries($package);
if (!$repo->hasPackage($package)) {
$repo->addPackage(clone $package);
@ -99,10 +99,9 @@ class LibraryInstaller implements InstallerInterface
}
$this->initializeVendorDir();
$downloadPath = $this->getInstallPath($initial);
$this->removeBinaries($initial);
$this->downloadManager->update($initial, $target, $downloadPath);
$this->updateCode($initial, $target);
$this->installBinaries($target);
$repo->removePackage($initial);
if (!$repo->hasPackage($target)) {
@ -123,7 +122,7 @@ class LibraryInstaller implements InstallerInterface
$downloadPath = $this->getInstallPath($package);
$this->downloadManager->remove($package, $downloadPath);
$this->removeCode($package);
$this->removeBinaries($package);
$repo->removePackage($package);
@ -146,12 +145,36 @@ class LibraryInstaller implements InstallerInterface
return ($this->vendorDir ? $this->vendorDir.'/' : '') . $package->getPrettyName() . ($targetDir ? '/'.$targetDir : '');
}
protected function installCode(PackageInterface $package)
{
$downloadPath = $this->getInstallPath($package);
$this->downloadManager->download($package, $downloadPath);
}
protected function updateCode(PackageInterface $initial, PackageInterface $target)
{
$downloadPath = $this->getInstallPath($initial);
$this->downloadManager->update($initial, $target, $downloadPath);
}
protected function removeCode(PackageInterface $package)
{
$downloadPath = $this->getInstallPath($package);
$this->downloadManager->remove($package, $downloadPath);
}
protected function getBinaries(PackageInterface $package)
{
return $package->getBinaries();
}
protected function installBinaries(PackageInterface $package)
{
if (!$package->getBinaries()) {
$binaries = $this->getBinaries($package);
if (!$binaries) {
return;
}
foreach ($package->getBinaries() as $bin) {
foreach ($binaries as $bin) {
$this->initializeBinDir();
$link = $this->binDir.'/'.basename($bin);
if (file_exists($link)) {
@ -193,10 +216,11 @@ class LibraryInstaller implements InstallerInterface
protected function removeBinaries(PackageInterface $package)
{
if (!$package->getBinaries()) {
$binaries = $this->getBinaries($package);
if (!$binaries) {
return;
}
foreach ($package->getBinaries() as $bin) {
foreach ($binaries as $bin) {
$link = $this->binDir.'/'.basename($bin);
if (!file_exists($link)) {
continue;

View File

@ -0,0 +1,95 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Installer;
use Composer\IO\IOInterface;
use Composer\Composer;
use Composer\Downloader\PearPackageExtractor;
use Composer\Downloader\DownloadManager;
use Composer\Repository\InstalledRepositoryInterface;
use Composer\Package\PackageInterface;
use Composer\Util\Filesystem;
/**
* Package installation manager.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*/
class PearInstaller extends LibraryInstaller
{
private $filesystem;
/**
* Initializes library installer.
*
* @param string $vendorDir relative path for packages home
* @param string $binDir relative path for binaries
* @param DownloadManager $dm download manager
* @param IOInterface $io io instance
* @param string $type package type that this installer handles
*/
public function __construct(IOInterface $io, Composer $composer, $type = 'pear-library')
{
$this->filesystem = new Filesystem();
parent::__construct($io, $composer, $type);
}
/**
* {@inheritDoc}
*/
public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target)
{
$this->uninstall($repo, $initial);
$this->install($repo, $target);
}
protected function installCode(PackageInterface $package)
{
parent::installCode($package);
$isWindows = defined('PHP_WINDOWS_VERSION_BUILD') ? true : false;
$vars = array(
'os' => $isWindows ? 'windows' : 'linux',
'php_bin' => ($isWindows ? getenv('PHPRC') .'php.exe' : trim(`which php`)),
'pear_php' => $this->getInstallPath($package),
'bin_dir' => $this->getInstallPath($package) . '/bin',
'php_dir' => $this->getInstallPath($package),
'data_dir' => '@DATA_DIR@',
'version' => $package->getPrettyVersion(),
);
$packageArchive = $this->getInstallPath($package).'/'.pathinfo($package->getDistUrl(), PATHINFO_BASENAME);
$pearExtractor = new PearPackageExtractor($packageArchive);
$pearExtractor->extractTo($this->getInstallPath($package), array('php' => '/', 'script' => '/bin'), $vars);
if ($this->io->isVerbose()) {
$this->io->write(' Cleaning up');
}
unlink($packageArchive);
}
protected function getBinaries(PackageInterface $package)
{
$binariesPath = $this->getInstallPath($package) . '/bin/';
$binaries = array();
if (file_exists($binariesPath)) {
foreach (new \FilesystemIterator($binariesPath, \FilesystemIterator::KEY_AS_FILENAME) as $fileName => $value) {
$binaries[] = 'bin/'.$fileName;
}
}
return $binaries;
}
}

View File

@ -0,0 +1,81 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Repository\Pear;
use Composer\Util\RemoteFilesystem;
/**
* Base PEAR Channel reader.
*
* Provides xml namespaces and red
*
* @author Alexey Prilipko <palex@farpost.com>
*/
abstract class BaseChannelReader
{
/**
* PEAR REST Interface namespaces
*/
const CHANNEL_NS = 'http://pear.php.net/channel-1.0';
const ALL_CATEGORIES_NS = 'http://pear.php.net/dtd/rest.allcategories';
const CATEGORY_PACKAGES_INFO_NS = 'http://pear.php.net/dtd/rest.categorypackageinfo';
const ALL_PACKAGES_NS = 'http://pear.php.net/dtd/rest.allpackages';
const ALL_RELEASES_NS = 'http://pear.php.net/dtd/rest.allreleases';
const PACKAGE_INFO_NS = 'http://pear.php.net/dtd/rest.package';
/** @var RemoteFilesystem */
private $rfs;
protected function __construct(RemoteFilesystem $rfs)
{
$this->rfs = $rfs;
}
/**
* Read content from remote filesystem.
*
* @param $origin string server
* @param $path string relative path to content
* @return \SimpleXMLElement
*/
protected function requestContent($origin, $path)
{
$url = rtrim($origin, '/') . '/' . ltrim($path, '/');
$content = $this->rfs->getContents($origin, $url, false);
if (!$content) {
throw new \UnexpectedValueException('The PEAR channel at ' . $url . ' did not respond.');
}
return $content;
}
/**
* Read xml content from remote filesystem
*
* @param $origin string server
* @param $path string relative path to content
* @return \SimpleXMLElement
*/
protected function requestXml($origin, $path)
{
// http://components.ez.no/p/packages.xml is malformed. to read it we must ignore parsing errors.
$xml = simplexml_load_string($this->requestContent($origin, $path), "SimpleXMLElement", LIBXML_NOERROR);
if (false == $xml) {
$url = rtrim($origin, '/') . '/' . ltrim($path, '/');
throw new \UnexpectedValueException(sprintf('The PEAR channel at ' . $origin . ' is broken. (Invalid XML at file `%s`)', $path));
}
return $xml;
}
}

View File

@ -0,0 +1,67 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Repository\Pear;
/**
* PEAR channel info
*
* @author Alexey Prilipko <palex@farpost.com>
*/
class ChannelInfo
{
private $name;
private $alias;
private $packages;
/**
* @param string $name
* @param string $alias
* @param PackageInfo[] $packages
*/
public function __construct($name, $alias, array $packages)
{
$this->name = $name;
$this->alias = $alias;
$this->packages = $packages;
}
/**
* Name of the channel
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Alias of the channel
*
* @return string
*/
public function getAlias()
{
return $this->alias;
}
/**
* List of channel packages
*
* @return PackageInfo[]
*/
public function getPackages()
{
return $this->packages;
}
}

View File

@ -0,0 +1,91 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Repository\Pear;
use Composer\Util\RemoteFilesystem;
/**
* PEAR Channel package reader.
*
* Reads channel packages info from and builds MemoryPackage's
*
* @author Alexey Prilipko <palex@farpost.com>
*/
class ChannelReader extends BaseChannelReader
{
/** @var array of ('xpath test' => 'rest implementation') */
private $readerMap;
public function __construct(RemoteFilesystem $rfs)
{
parent::__construct($rfs);
$rest10reader = new ChannelRest10Reader($rfs);
$rest11reader = new ChannelRest11Reader($rfs);
$this->readerMap = array(
'REST1.3' => $rest11reader,
'REST1.2' => $rest11reader,
'REST1.1' => $rest11reader,
'REST1.0' => $rest10reader,
);
}
/**
* Reads PEAR channel through REST interface and builds list of packages
*
* @param $url string PEAR Channel url
* @return ChannelInfo
*/
public function read($url)
{
$xml = $this->requestXml($url, "/channel.xml");
$channelName = (string) $xml->name;
$channelSummary = (string) $xml->summary;
$channelAlias = (string) $xml->suggestedalias;
$supportedVersions = array_keys($this->readerMap);
$selectedRestVersion = $this->selectRestVersion($xml, $supportedVersions);
if (!$selectedRestVersion) {
throw new \UnexpectedValueException(sprintf('PEAR repository %s does not supports any of %s protocols.', $url, implode(', ', $supportedVersions)));
}
$reader = $this->readerMap[$selectedRestVersion['version']];
$packageDefinitions = $reader->read($selectedRestVersion['baseUrl']);
return new ChannelInfo($channelName, $channelAlias, $packageDefinitions);
}
/**
* Reads channel supported REST interfaces and selects one of them
*
* @param $channelXml \SimpleXMLElement
* @param $supportedVersions string[] supported PEAR REST protocols
* @return array|null hash with selected version and baseUrl
*/
private function selectRestVersion($channelXml, $supportedVersions)
{
$channelXml->registerXPathNamespace('ns', self::CHANNEL_NS);
foreach ($supportedVersions as $version) {
$xpathTest = "ns:servers/ns:primary/ns:rest/ns:baseurl[@type='{$version}']";
$testResult = $channelXml->xpath($xpathTest);
if (count($testResult) > 0) {
return array('version' => $version, 'baseUrl' => (string) $testResult[0]);
}
}
return null;
}
}

View File

@ -0,0 +1,164 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Repository\Pear;
use Composer\Downloader\TransportException;
/**
* Read PEAR packages using REST 1.0 interface
*
* At version 1.0 package descriptions read from:
* {baseUrl}/p/packages.xml
* {baseUrl}/p/{package}/info.xml
* {baseUrl}/p/{package}/allreleases.xml
* {baseUrl}/p/{package}/deps.{version}.txt
*
* @author Alexey Prilipko <palex@farpost.com>
*/
class ChannelRest10Reader extends BaseChannelReader
{
private $dependencyReader;
public function __construct($rfs)
{
parent::__construct($rfs);
$this->dependencyReader = new PackageDependencyParser();
}
/**
* Reads package descriptions using PEAR Rest 1.0 interface
*
* @param $baseUrl string base Url interface
*
* @return PackageInfo[]
*/
public function read($baseUrl)
{
return $this->readPackages($baseUrl);
}
/**
* Read list of packages from
* {baseUrl}/p/packages.xml
*
* @param $baseUrl string
* @return PackageInfo[]
*/
private function readPackages($baseUrl)
{
$result = array();
$xmlPath = '/p/packages.xml';
$xml = $this->requestXml($baseUrl, $xmlPath);
$xml->registerXPathNamespace('ns', self::ALL_PACKAGES_NS);
foreach ($xml->xpath('ns:p') as $node) {
$packageName = (string) $node;
$packageInfo = $this->readPackage($baseUrl, $packageName);
$result[] = $packageInfo;
}
return $result;
}
/**
* Read package info from
* {baseUrl}/p/{package}/info.xml
*
* @param $baseUrl string
* @param $packageName string
* @return PackageInfo
*/
private function readPackage($baseUrl, $packageName)
{
$xmlPath = '/p/' . strtolower($packageName) . '/info.xml';
$xml = $this->requestXml($baseUrl, $xmlPath);
$xml->registerXPathNamespace('ns', self::PACKAGE_INFO_NS);
$channelName = (string) $xml->c;
$packageName = (string) $xml->n;
$license = (string) $xml->l;
$shortDescription = (string) $xml->s;
$description = (string) $xml->d;
return new PackageInfo(
$channelName,
$packageName,
$license,
$shortDescription,
$description,
$this->readPackageReleases($baseUrl, $packageName)
);
}
/**
* Read package releases from
* {baseUrl}/p/{package}/allreleases.xml
*
* @param $baseUrl string
* @param $packageName string
* @return ReleaseInfo[] hash array with keys as version numbers
*/
private function readPackageReleases($baseUrl, $packageName)
{
$result = array();
try {
$xmlPath = '/r/' . strtolower($packageName) . '/allreleases.xml';
$xml = $this->requestXml($baseUrl, $xmlPath);
$xml->registerXPathNamespace('ns', self::ALL_RELEASES_NS);
foreach ($xml->xpath('ns:r') as $node) {
$releaseVersion = (string) $node->v;
$releaseStability = (string) $node->s;
try {
$result[$releaseVersion] = new ReleaseInfo(
$releaseStability,
$this->readPackageReleaseDependencies($baseUrl, $packageName, $releaseVersion)
);
} catch (TransportException $exception) {
if ($exception->getCode() != 404) {
throw $exception;
}
}
}
} catch (TransportException $exception) {
if ($exception->getCode() != 404) {
throw $exception;
}
}
return $result;
}
/**
* Read package dependencies from
* {baseUrl}/p/{package}/deps.{version}.txt
*
* @param $baseUrl string
* @param $packageName string
* @param $version string
* @return DependencyInfo[]
*/
private function readPackageReleaseDependencies($baseUrl, $packageName, $version)
{
$dependencyReader = new PackageDependencyParser();
$depthPath = '/r/' . strtolower($packageName) . '/deps.' . $version . '.txt';
$content = $this->requestContent($baseUrl, $depthPath);
$dependencyArray = unserialize($content);
$result = $dependencyReader->buildDependencyInfo($dependencyArray);
return $result;
}
}

View File

@ -0,0 +1,136 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Repository\Pear;
/**
* Read PEAR packages using REST 1.1 interface
*
* At version 1.1 package descriptions read from:
* {baseUrl}/c/categories.xml
* {baseUrl}/c/{category}/packagesinfo.xml
*
* @author Alexey Prilipko <palex@farpost.com>
*/
class ChannelRest11Reader extends BaseChannelReader
{
private $dependencyReader;
public function __construct($rfs)
{
parent::__construct($rfs);
$this->dependencyReader = new PackageDependencyParser();
}
/**
* Reads package descriptions using PEAR Rest 1.1 interface
*
* @param $baseUrl string base Url interface
*
* @return PackageInfo[]
*/
public function read($baseUrl)
{
return $this->readChannelPackages($baseUrl);
}
/**
* Read list of channel categories from
* {baseUrl}/c/categories.xml
*
* @param $baseUrl string
* @return PackageInfo[]
*/
private function readChannelPackages($baseUrl)
{
$result = array();
$xml = $this->requestXml($baseUrl, "/c/categories.xml");
$xml->registerXPathNamespace('ns', self::ALL_CATEGORIES_NS);
foreach ($xml->xpath('ns:c') as $node) {
$categoryName = (string) $node;
$categoryPackages = $this->readCategoryPackages($baseUrl, $categoryName);
$result = array_merge($result, $categoryPackages);
}
return $result;
}
/**
* Read packages from
* {baseUrl}/c/{category}/packagesinfo.xml
*
* @param $baseUrl string
* @param $categoryName string
* @return PackageInfo[]
*/
private function readCategoryPackages($baseUrl, $categoryName)
{
$result = array();
$categoryPath = '/c/'.urlencode($categoryName).'/packagesinfo.xml';
$xml = $this->requestXml($baseUrl, $categoryPath);
$xml->registerXPathNamespace('ns', self::CATEGORY_PACKAGES_INFO_NS);
foreach ($xml->xpath('ns:pi') as $node) {
$packageInfo = $this->parsePackage($node);
$result[] = $packageInfo;
}
return $result;
}
/**
* Parses package node.
*
* @param $packageInfo \SimpleXMLElement xml element describing package
* @return PackageInfo
*/
private function parsePackage($packageInfo)
{
$packageInfo->registerXPathNamespace('ns', self::CATEGORY_PACKAGES_INFO_NS);
$channelName = (string) $packageInfo->p->c;
$packageName = (string) $packageInfo->p->n;
$license = (string) $packageInfo->p->l;
$shortDescription = (string) $packageInfo->p->s;
$description = (string) $packageInfo->p->d;
$dependencies = array();
foreach ($packageInfo->xpath('ns:deps') as $node) {
$dependencyVersion = (string) $node->v;
$dependencyArray = unserialize((string) $node->d);
$dependencyInfo = $this->dependencyReader->buildDependencyInfo($dependencyArray);
$dependencies[$dependencyVersion] = $dependencyInfo;
}
$releases = array();
foreach ($packageInfo->xpath('ns:a/ns:r') as $node) {
$releaseVersion = (string) $node->v;
$releaseStability = (string) $node->s;
$releases[$releaseVersion] = new ReleaseInfo(
$releaseStability,
isset($dependencies[$releaseVersion]) ? $dependencies[$releaseVersion] : new DependencyInfo(array(), array())
);
}
return new PackageInfo(
$channelName,
$packageName,
$license,
$shortDescription,
$description,
$releases
);
}
}

View File

@ -0,0 +1,60 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Repository\Pear;
/**
* PEAR package release dependency info
*
* @author Alexey Prilipko <palex@farpost.com>
*/
class DependencyConstraint
{
private $type;
private $constraint;
private $channelName;
private $packageName;
/**
* @param string $type
* @param string $constraint
* @param string $channelName
* @param string $packageName
*/
public function __construct($type, $constraint, $channelName, $packageName)
{
$this->type = $type;
$this->constraint = $constraint;
$this->channelName = $channelName;
$this->packageName = $packageName;
}
public function getChannelName()
{
return $this->channelName;
}
public function getConstraint()
{
return $this->constraint;
}
public function getPackageName()
{
return $this->packageName;
}
public function getType()
{
return $this->type;
}
}

View File

@ -0,0 +1,50 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Repository\Pear;
/**
* PEAR package release dependency info
*
* @author Alexey Prilipko <palex@farpost.com>
*/
class DependencyInfo
{
private $requires;
private $optionals;
/**
* @param DependencyConstraint[] $requires list of requires/conflicts/replaces
* @param array $optionals [groupName => DependencyConstraint[]] list of optional groups
*/
public function __construct($requires, $optionals)
{
$this->requires = $requires;
$this->optionals = $optionals;
}
/**
* @return DependencyConstraint[] list of requires/conflicts/replaces
*/
public function getRequires()
{
return $this->requires;
}
/**
* @return array [groupName => DependencyConstraint[]] list of optional groups
*/
public function getOptionals()
{
return $this->optionals;
}
}

View File

@ -0,0 +1,314 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Repository\Pear;
/**
* Read PEAR packages using REST 1.0 interface
*
* @author Alexey Prilipko <palex@farpost.com>
*/
class PackageDependencyParser
{
/**
* Builds dependency information. It detects used package.xml format.
*
* @param $depArray array
* @return DependencyInfo
*/
public function buildDependencyInfo($depArray)
{
if (!is_array($depArray)) {
return new DependencyInfo(array(), array());
}
if (!$this->isHash($depArray)) {
return new DependencyInfo($this->buildDependency10Info($depArray), array());
}
return $this->buildDependency20Info($depArray);
}
/**
* Builds dependency information from package.xml 1.0 format
*
* http://pear.php.net/manual/en/guide.developers.package2.dependencies.php
*
* package.xml 1.0 format consists of array of
* { type="php|os|sapi|ext|pkg" rel="has|not|eq|ge|gt|le|lt" optional="yes"
* channel="channelName" name="extName|packageName" }
*
* @param $depArray array Dependency data in package.xml 1.0 format
* @return DependencyConstraint[]
*/
private function buildDependency10Info($depArray)
{
static $dep10toOperatorMap = array('has'=>'==', 'eq' => '==', 'ge' => '>=', 'gt' => '>', 'le' => '<=', 'lt' => '<', 'not' => '!=');
$result = array();
foreach ($depArray as $depItem) {
if (empty($depItem['rel']) || !array_key_exists($depItem['rel'], $dep10toOperatorMap)) {
// 'unknown rel type:' . $depItem['rel'];
continue;
}
$depType = !empty($depItem['optional']) && 'yes' == $depItem['optional']
? 'optional'
: 'required';
$depType = 'not' == $depItem['rel']
? 'conflicts'
: $depType;
$depVersion = !empty($depItem['version']) ? $this->parseVersion($depItem['version']) : '*';
// has & not are special operators that does not requires version
$depVersionConstraint = ('has' == $depItem['rel'] || 'not' == $depItem['rel']) && '*' == $depVersion
? '*'
: $dep10toOperatorMap[$depItem['rel']] . $depVersion;
switch ($depItem['type']) {
case 'php':
$depChannelName = 'php';
$depPackageName = '';
break;
case 'pkg':
$depChannelName = !empty($depItem['channel']) ? $depItem['channel'] : 'pear.php.net';
$depPackageName = $depItem['name'];
break;
case 'ext':
$depChannelName = 'ext';
$depPackageName = $depItem['name'];
break;
case 'os':
case 'sapi':
$depChannelName = '';
$depPackageName = '';
break;
default:
$depChannelName = '';
$depPackageName = '';
break;
}
if ('' != $depChannelName) {
$result[] = new DependencyConstraint(
$depType,
$depVersionConstraint,
$depChannelName,
$depPackageName
);
}
}
return $result;
}
/**
* Builds dependency information from package.xml 2.0 format
*
* @param $depArray array Dependency data in package.xml 1.0 format
* @return DependencyInfo
*/
private function buildDependency20Info($depArray)
{
$result = array();
$optionals = array();
$defaultOptionals = array();
foreach ($depArray as $depType => $depTypeGroup) {
if (!is_array($depTypeGroup)) {
continue;
}
if ('required' == $depType || 'optional' == $depType) {
foreach ($depTypeGroup as $depItemType => $depItem) {
switch ($depItemType) {
case 'php':
$result[] = new DependencyConstraint(
$depType,
$this->parse20VersionConstraint($depItem),
'php',
''
);
break;
case 'package':
$deps = $this->buildDepPackageConstraints($depItem, $depType);
$result = array_merge($result, $deps);
break;
case 'extension':
$deps = $this->buildDepExtensionConstraints($depItem, $depType);
$result = array_merge($result, $deps);
break;
case 'subpackage':
$deps = $this->buildDepPackageConstraints($depItem, 'replaces');
$defaultOptionals += $deps;
break;
case 'os':
case 'pearinstaller':
break;
default:
break;
}
}
} elseif ('group' == $depType) {
if ($this->isHash($depTypeGroup)) {
$depTypeGroup = array($depTypeGroup);
}
foreach ($depTypeGroup as $depItem) {
$groupName = $depItem['attribs']['name'];
if (!isset($optionals[$groupName])) {
$optionals[$groupName] = array();
}
if (isset($depItem['subpackage'])) {
$optionals[$groupName] += $this->buildDepPackageConstraints($depItem['subpackage'], 'replaces');
} else {
$result += $this->buildDepPackageConstraints($depItem['package'], 'optional');
}
}
}
}
if (count($defaultOptionals) > 0) {
$optionals['*'] = $defaultOptionals;
}
return new DependencyInfo($result, $optionals);
}
/**
* Builds dependency constraint of 'extension' type
*
* @param $depItem array dependency constraint or array of dependency constraints
* @param $depType string target type of building constraint.
* @return DependencyConstraint[]
*/
private function buildDepExtensionConstraints($depItem, $depType)
{
if ($this->isHash($depItem)) {
$depItem = array($depItem);
}
$result = array();
foreach ($depItem as $subDepItem) {
$depChannelName = 'ext';
$depPackageName = $subDepItem['name'];
$depVersionConstraint = $this->parse20VersionConstraint($subDepItem);
$result[] = new DependencyConstraint(
$depType,
$depVersionConstraint,
$depChannelName,
$depPackageName
);
}
return $result;
}
/**
* Builds dependency constraint of 'package' type
*
* @param $depItem array dependency constraint or array of dependency constraints
* @param $depType string target type of building constraint.
* @return DependencyConstraint[]
*/
private function buildDepPackageConstraints($depItem, $depType)
{
if ($this->isHash($depItem)) {
$depItem = array($depItem);
}
$result = array();
foreach ($depItem as $subDepItem) {
$depChannelName = $subDepItem['channel'];
$depPackageName = $subDepItem['name'];
$depVersionConstraint = $this->parse20VersionConstraint($subDepItem);
if (isset($subDepItem['conflicts'])) {
$depType = 'conflicts';
}
$result[] = new DependencyConstraint(
$depType,
$depVersionConstraint,
$depChannelName,
$depPackageName
);
}
return $result;
}
/**
* Parses version constraint
*
* @param array $data array containing serveral 'min', 'max', 'has', 'exclude' and other keys.
* @return string
*/
private function parse20VersionConstraint(array $data)
{
static $dep20toOperatorMap = array('has'=>'==', 'min' => '>=', 'max' => '<=', 'exclude' => '!=');
$versions = array();
$values = array_intersect_key($data, $dep20toOperatorMap);
if (0 == count($values)) {
return '*';
}
if (isset($values['min']) && isset($values['exclude']) && $data['min'] == $data['exclude']) {
$versions[] = '>' . $this->parseVersion($values['min']);
} elseif (isset($values['max']) && isset($values['exclude']) && $data['max'] == $data['exclude']) {
$versions[] = '<' . $this->parseVersion($values['max']);
} else {
foreach ($values as $op => $version) {
if ('exclude' == $op && is_array($version)) {
foreach ($version as $versionPart) {
$versions[] = $dep20toOperatorMap[$op] . $this->parseVersion($versionPart);
}
} else {
$versions[] = $dep20toOperatorMap[$op] . $this->parseVersion($version);
}
}
}
return implode(',', $versions);
}
/**
* Softened version parser
*
* @param $version
* @return null|string
*/
private function parseVersion($version)
{
if (preg_match('{^v?(\d{1,3})(\.\d+)?(\.\d+)?(\.\d+)?}i', $version, $matches)) {
$version = $matches[1]
.(!empty($matches[2]) ? $matches[2] : '.0')
.(!empty($matches[3]) ? $matches[3] : '.0')
.(!empty($matches[4]) ? $matches[4] : '.0');
return $version;
}
return null;
}
/**
* Test if array is associative or hash type
*
* @param array $array
* @return bool
*/
private function isHash(array $array)
{
return !array_key_exists(1, $array) && !array_key_exists(0, $array);
}
}

View File

@ -0,0 +1,94 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Repository\Pear;
/**
* PEAR Package info
*
* @author Alexey Prilipko <palex@farpost.com>
*/
class PackageInfo
{
private $channelName;
private $packageName;
private $license;
private $shortDescription;
private $description;
private $releases;
/**
* @param string $channelName
* @param string $packageName
* @param string $license
* @param string $shortDescription
* @param string $description
* @param ReleaseInfo[] $releases associative array maps release version to release info
*/
public function __construct($channelName, $packageName, $license, $shortDescription, $description, $releases)
{
$this->channelName = $channelName;
$this->packageName = $packageName;
$this->license = $license;
$this->shortDescription = $shortDescription;
$this->description = $description;
$this->releases = $releases;
}
/**
* @return string the package channel name
*/
public function getChannelName()
{
return $this->channelName;
}
/**
* @return string the package name
*/
public function getPackageName()
{
return $this->packageName;
}
/**
* @return string the package description
*/
public function getDescription()
{
return $this->description;
}
/**
* @return string the package short escription
*/
public function getShortDescription()
{
return $this->shortDescription;
}
/**
* @return string the package licence
*/
public function getLicense()
{
return $this->license;
}
/**
* @return ReleaseInfo[]
*/
public function getReleases()
{
return $this->releases;
}
}

View File

@ -0,0 +1,50 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Repository\Pear;
/**
* PEAR package release info
*
* @author Alexey Prilipko <palex@farpost.com>
*/
class ReleaseInfo
{
private $stability;
private $dependencyInfo;
/**
* @param string $stability
* @param DependencyInfo $dependencies
*/
public function __construct($stability, $dependencyInfo)
{
$this->stability = $stability;
$this->dependencyInfo = $dependencyInfo;
}
/**
* @return DependencyInfo release dependencies
*/
public function getDependencyInfo()
{
return $this->dependencyInfo;
}
/**
* @return string release stability
*/
public function getStability()
{
return $this->stability;
}
}

View File

@ -13,25 +13,35 @@
namespace Composer\Repository;
use Composer\IO\IOInterface;
use Composer\Package\Loader\ArrayLoader;
use Composer\Package\Version\VersionParser;
use Composer\Repository\Pear\ChannelReader;
use Composer\Package\MemoryPackage;
use Composer\Repository\Pear\ChannelInfo;
use Composer\Package\Link;
use Composer\Package\LinkConstraint\VersionConstraint;
use Composer\Util\RemoteFilesystem;
use Composer\Json\JsonFile;
use Composer\Config;
use Composer\Downloader\TransportException;
/**
* Builds list of package from PEAR channel.
*
* Packages read from channel are named as 'pear-{channelName}/{packageName}'
* and has aliased as 'pear-{channelAlias}/{packageName}'
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class PearRepository extends ArrayRepository
{
private static $channelNames = array();
private $url;
private $baseUrl;
private $channel;
private $io;
private $rfs;
private $versionParser;
/** @var string vendor makes additional alias for each channel as {prefix}/{packagename}. It allows smoother
* package transition to composer-like repositories.
*/
private $vendorAlias;
public function __construct(array $repoConfig, IOInterface $io, Config $config, RemoteFilesystem $rfs = null)
{
@ -44,9 +54,10 @@ class PearRepository extends ArrayRepository
}
$this->url = rtrim($repoConfig['url'], '/');
$this->channel = !empty($repoConfig['channel']) ? $repoConfig['channel'] : null;
$this->io = $io;
$this->rfs = $rfs ?: new RemoteFilesystem($this->io);
$this->vendorAlias = isset($repoConfig['vendor-alias']) ? $repoConfig['vendor-alias'] : null;
$this->versionParser = new VersionParser();
}
protected function initialize()
@ -54,341 +65,118 @@ class PearRepository extends ArrayRepository
parent::initialize();
$this->io->write('Initializing PEAR repository '.$this->url);
$this->initializeChannel();
$this->io->write('Packages names will be prefixed with: pear-'.$this->channel.'/');
// try to load as a composer repo
$reader = new ChannelReader($this->rfs);
try {
$json = new JsonFile($this->url.'/packages.json', new RemoteFilesystem($this->io));
$packages = $json->read();
if ($this->io->isVerbose()) {
$this->io->write('Repository is Composer-compatible, loading via packages.json instead of PEAR protocol');
}
$loader = new ArrayLoader();
foreach ($packages as $data) {
foreach ($data['versions'] as $rev) {
if (strpos($rev['name'], 'pear-'.$this->channel) !== 0) {
$rev['name'] = 'pear-'.$this->channel.'/'.$rev['name'];
}
$this->addPackage($loader->load($rev));
if ($this->io->isVerbose()) {
$this->io->write('Loaded '.$rev['name'].' '.$rev['version']);
}
}
}
$channelInfo = $reader->read($this->url);
} catch (\Exception $e) {
$this->io->write('<warning>PEAR repository from '.$this->url.' could not be loaded. '.$e->getMessage().'</warning>');
return;
} catch (\Exception $e) {
}
$this->fetchFromServer();
}
protected function initializeChannel()
{
$channelXML = $this->requestXml($this->url . "/channel.xml");
if (!$this->channel) {
$this->channel = $channelXML->getElementsByTagName("suggestedalias")->item(0)->nodeValue
?: $channelXML->getElementsByTagName("name")->item(0)->nodeValue;
}
if (!$this->baseUrl) {
$this->baseUrl = $channelXML->getElementsByTagName("baseurl")->item(0)->nodeValue
? trim($channelXML->getElementsByTagName("baseurl")->item(0)->nodeValue, '/')
: $this->url . '/rest';
}
self::$channelNames[$channelXML->getElementsByTagName("name")->item(0)->nodeValue] = $this->channel;
}
protected function fetchFromServer()
{
$categoryXML = $this->requestXml($this->baseUrl . "/c/categories.xml");
$categories = $categoryXML->getElementsByTagName("c");
foreach ($categories as $category) {
$link = $this->baseUrl . '/c/' . str_replace(' ', '+', $category->nodeValue);
try {
$packagesLink = $link . "/packagesinfo.xml";
$this->fetchPear2Packages($packagesLink);
} catch (TransportException $e) {
if (false === strpos($e->getMessage(), '404')) {
throw $e;
}
$categoryLink = $link . "/packages.xml";
$this->fetchPearPackages($categoryLink);
}
}
}
/**
* @param string $categoryLink
* @throws TransportException
* @throws \InvalidArgumentException
*/
private function fetchPearPackages($categoryLink)
{
$packagesXML = $this->requestXml($categoryLink);
$packages = $packagesXML->getElementsByTagName('p');
$loader = new ArrayLoader();
$packages = $this->buildComposerPackages($channelInfo, $this->versionParser);
foreach ($packages as $package) {
$packageName = $package->nodeValue;
$fullName = 'pear-'.$this->channel.'/'.$packageName;
$releaseLink = $this->baseUrl . "/r/" . $packageName;
$allReleasesLink = $releaseLink . "/allreleases2.xml";
try {
$releasesXML = $this->requestXml($allReleasesLink);
} catch (TransportException $e) {
if (strpos($e->getMessage(), '404')) {
continue;
}
throw $e;
}
$releases = $releasesXML->getElementsByTagName('r');
foreach ($releases as $release) {
/* @var $release \DOMElement */
$pearVersion = $release->getElementsByTagName('v')->item(0)->nodeValue;
$packageData = array(
'name' => $fullName,
'type' => 'library',
'dist' => array('type' => 'pear', 'url' => $this->url.'/get/'.$packageName.'-'.$pearVersion.".tgz"),
'version' => $pearVersion,
'autoload' => array(
'classmap' => array(''),
),
'include-path' => array('/'),
);
$this->addPackage($package);
}
}
/**
* Builds MemoryPackages from PEAR package definition data.
*
* @param ChannelInfo $channelInfo
* @return MemoryPackage
*/
private function buildComposerPackages(ChannelInfo $channelInfo, VersionParser $versionParser)
{
$result = array();
foreach ($channelInfo->getPackages() as $packageDefinition) {
foreach ($packageDefinition->getReleases() as $version => $releaseInfo) {
try {
$deps = $this->rfs->getContents($this->url, $releaseLink . "/deps.".$pearVersion.".txt", false);
} catch (TransportException $e) {
if (strpos($e->getMessage(), '404')) {
continue;
}
throw $e;
}
$packageData += $this->parseDependencies($deps);
try {
$this->addPackage($loader->load($packageData));
if ($this->io->isVerbose()) {
$this->io->write('Loaded '.$packageData['name'].' '.$packageData['version']);
}
$normalizedVersion = $versionParser->normalize($version);
} catch (\UnexpectedValueException $e) {
if ($this->io->isVerbose()) {
$this->io->write('Could not load '.$packageData['name'].' '.$packageData['version'].': '.$e->getMessage());
$this->io->write('Could not load '.$packageDefinition->getPackageName().' '.$version.': '.$e->getMessage());
}
continue;
}
}
}
}
/**
* @param array $data
* @return string
*/
private function parseVersion(array $data)
{
if (!isset($data['min']) && !isset($data['max'])) {
return '*';
}
$versions = array();
if (isset($data['min'])) {
$versions[] = '>=' . $data['min'];
}
if (isset($data['max'])) {
$versions[] = '<=' . $data['max'];
}
$composerPackageName = $this->buildComposerPackageName($packageDefinition->getChannelName(), $packageDefinition->getPackageName());
return implode(',', $versions);
}
// distribution url must be read from /r/{packageName}/{version}.xml::/r/g:text()
// but this location is 'de-facto' standard
$distUrl = "http://{$packageDefinition->getChannelName()}/get/{$packageDefinition->getPackageName()}-{$version}.tgz";
/**
* @todo Improve dependencies resolution of pear packages.
* @param array $depsOptions
* @return array
*/
private function parseDependenciesOptions(array $depsOptions)
{
$data = array();
foreach ($depsOptions as $name => $options) {
// make sure single deps are wrapped in an array
if (isset($options['name'])) {
$options = array($options);
}
if ('php' == $name) {
$data[$name] = $this->parseVersion($options);
} elseif ('package' == $name) {
foreach ($options as $key => $value) {
if (isset($value['providesextension'])) {
// skip PECL dependencies
continue;
}
if (isset($value['uri'])) {
// skip uri-based dependencies
continue;
}
$requires = array();
$suggests = array();
$conflicts = array();
$replaces = array();
if (is_array($value)) {
$dataKey = $value['name'];
if (false === strpos($dataKey, '/')) {
$dataKey = $this->getChannelShorthand($value['channel']).'/'.$dataKey;
}
$data['pear-'.$dataKey] = $this->parseVersion($value);
}
}
} elseif ('extension' == $name) {
foreach ($options as $key => $value) {
$dataKey = 'ext-' . $value['name'];
$data[$dataKey] = $this->parseVersion($value);
}
}
}
return $data;
}
/**
* @param string $deps
* @return array
* @throws \InvalidArgumentException
*/
private function parseDependencies($deps)
{
if (preg_match('((O:([0-9])+:"([^"]+)"))', $deps, $matches)) {
if (strlen($matches[3]) == $matches[2]) {
throw new \InvalidArgumentException("Invalid dependency data, it contains serialized objects.");
}
}
$deps = (array) @unserialize($deps);
unset($deps['required']['pearinstaller']);
$depsData = array();
if (!empty($deps['required'])) {
$depsData['require'] = $this->parseDependenciesOptions($deps['required']);
}
if (!empty($deps['optional'])) {
$depsData['suggest'] = $this->parseDependenciesOptions($deps['optional']);
}
return $depsData;
}
/**
* @param string $packagesLink
* @return void
* @throws \InvalidArgumentException
*/
private function fetchPear2Packages($packagesLink)
{
$loader = new ArrayLoader();
$packagesXml = $this->requestXml($packagesLink);
$informations = $packagesXml->getElementsByTagName('pi');
foreach ($informations as $information) {
$package = $information->getElementsByTagName('p')->item(0);
$packageName = $package->getElementsByTagName('n')->item(0)->nodeValue;
$fullName = 'pear-'.$this->channel.'/'.$packageName;
$packageData = array(
'name' => $fullName,
'type' => 'library',
'autoload' => array(
'classmap' => array(''),
),
'include-path' => array('/'),
);
$packageKeys = array('l' => 'license', 'd' => 'description');
foreach ($packageKeys as $pear => $composer) {
if ($package->getElementsByTagName($pear)->length > 0
&& ($pear = $package->getElementsByTagName($pear)->item(0)->nodeValue)) {
$packageData[$composer] = $pear;
}
}
$depsData = array();
foreach ($information->getElementsByTagName('deps') as $depElement) {
$depsVersion = $depElement->getElementsByTagName('v')->item(0)->nodeValue;
$depsData[$depsVersion] = $this->parseDependencies(
$depElement->getElementsByTagName('d')->item(0)->nodeValue
);
}
$releases = $information->getElementsByTagName('a')->item(0);
if (!$releases) {
continue;
}
$releases = $releases->getElementsByTagName('r');
$packageUrl = $this->url . '/get/' . $packageName;
foreach ($releases as $release) {
$version = $release->getElementsByTagName('v')->item(0)->nodeValue;
$releaseData = array(
'dist' => array(
'type' => 'pear',
'url' => $packageUrl . '-' . $version . '.tgz'
),
'version' => $version
);
if (isset($depsData[$version])) {
$releaseData += $depsData[$version];
// alias package only when its channel matches repository channel,
// cause we've know only repository channel alias
if ($channelInfo->getName() == $packageDefinition->getChannelName()) {
$composerPackageAlias = $this->buildComposerPackageName($channelInfo->getAlias(), $packageDefinition->getPackageName());
$aliasConstraint = new VersionConstraint('==', $normalizedVersion);
$replaces[] = new Link($composerPackageName, $composerPackageAlias, $aliasConstraint, 'replaces', (string) $aliasConstraint);
}
$package = $packageData + $releaseData;
try {
$this->addPackage($loader->load($package));
if ($this->io->isVerbose()) {
$this->io->write('Loaded '.$package['name'].' '.$package['version']);
}
} catch (\UnexpectedValueException $e) {
if ($this->io->isVerbose()) {
$this->io->write('Could not load '.$package['name'].' '.$package['version'].': '.$e->getMessage());
}
continue;
// alias package with user-specified prefix. it makes private pear channels looks like composer's.
if (!empty($this->vendorAlias)) {
$composerPackageAlias = "{$this->vendorAlias}/{$packageDefinition->getPackageName()}";
$aliasConstraint = new VersionConstraint('==', $normalizedVersion);
$replaces[] = new Link($composerPackageName, $composerPackageAlias, $aliasConstraint, 'replaces', (string) $aliasConstraint);
}
}
}
}
/**
* @param string $url
* @return \DOMDocument
*/
private function requestXml($url)
{
$content = $this->rfs->getContents($this->url, $url, false);
if (!$content) {
throw new \UnexpectedValueException('The PEAR channel at '.$url.' did not respond.');
}
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->loadXML($content);
foreach ($releaseInfo->getDependencyInfo()->getRequires() as $dependencyConstraint) {
$dependencyPackageName = $this->buildComposerPackageName($dependencyConstraint->getChannelName(), $dependencyConstraint->getPackageName());
$constraint = $versionParser->parseConstraints($dependencyConstraint->getConstraint());
$link = new Link($composerPackageName, $dependencyPackageName, $constraint, $dependencyConstraint->getType(), $dependencyConstraint->getConstraint());
switch ($dependencyConstraint->getType()) {
case 'required':
$requires[] = $link;
break;
case 'conflicts':
$conflicts[] = $link;
break;
case 'replaces':
$replaces[] = $link;
break;
}
}
return $dom;
}
foreach ($releaseInfo->getDependencyInfo()->getOptionals() as $group => $dependencyConstraints) {
foreach ($dependencyConstraints as $dependencyConstraint) {
$dependencyPackageName = $this->buildComposerPackageName($dependencyConstraint->getChannelName(), $dependencyConstraint->getPackageName());
$suggests[$group.'-'.$dependencyPackageName] = $dependencyConstraint->getConstraint();
}
}
private function getChannelShorthand($url)
{
if (!isset(self::$channelNames[$url])) {
try {
$channelXML = $this->requestXml('http://'.$url."/channel.xml");
$shorthand = $channelXML->getElementsByTagName("suggestedalias")->item(0)->nodeValue
?: $channelXML->getElementsByTagName("name")->item(0)->nodeValue;
self::$channelNames[$url] = $shorthand;
} catch (\Exception $e) {
self::$channelNames[$url] = substr($url, 0, strpos($url, '.'));
$package = new MemoryPackage($composerPackageName, $normalizedVersion, $version);
$package->setType('pear-library');
$package->setDescription($packageDefinition->getDescription());
$package->setDistType('file');
$package->setDistUrl($distUrl);
$package->setAutoload(array('classmap' => array('')));
$package->setIncludePaths(array('/'));
$package->setRequires($requires);
$package->setConflicts($conflicts);
$package->setSuggests($suggests);
$package->setReplaces($replaces);
$result[] = $package;
}
}
return self::$channelNames[$url];
return $result;
}
private function buildComposerPackageName($channelName, $packageName)
{
if ('php' === $channelName) {
return "php";
}
if ('ext' === $channelName) {
return "ext-{$packageName}";
}
return "pear-{$channelName}/{$packageName}";
}
}

View File

@ -19,7 +19,17 @@
<dir name="/">
<file role="php" name="php/Zend/Authentication/Storage/StorageInterface.php"/>
<file role="php" name="php/Zend/Authentication/Result.php"/>
<file role="script" name="php/Test.php">
<tasks:replace from='@version@' to='version' />
</file>
<file role="php" name="renamedFile.php"/>
<file role="php" name="ignoredFile.php"/>
</dir>
</contents>
<phprelease/>
<phprelease>
<filelist>
<install name='renamedFile.php' as='correctFile.php' />
<ignore name='ignoredFile.php' />
</filelist>
</phprelease>
</package>

View File

@ -1,38 +0,0 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Test\Downloader;
use Composer\Downloader\PearDownloader;
class PearDownloaderTest extends \PHPUnit_Framework_TestCase
{
public function testErrorMessages()
{
$packageMock = $this->getMock('Composer\Package\PackageInterface');
$packageMock->expects($this->any())
->method('getDistUrl')
->will($this->returnValue('file://'.__FILE__))
;
$io = $this->getMock('Composer\IO\IOInterface');
$downloader = new PearDownloader($io);
try {
$downloader->download($packageMock, sys_get_temp_dir().'/composer-pear-test');
$this->fail('Download of invalid pear packages should throw an exception');
} catch (\UnexpectedValueException $e) {
$this->assertContains('Failed to extract PEAR package', $e->getMessage());
}
}
}

View File

@ -22,20 +22,26 @@ class PearPackageExtractorTest extends \PHPUnit_Framework_TestCase
$method = new \ReflectionMethod($extractor, 'buildCopyActions');
$method->setAccessible(true);
$fileActions = $method->invoke($extractor, __DIR__ . '/Fixtures/Package_v1.0', 'php');
$fileActions = $method->invoke($extractor, __DIR__ . '/Fixtures/Package_v1.0', array('php' => '/'), array());
$expectedFileActions = array(
0 => Array(
'Gtk.php' => Array(
'from' => 'PEAR_Frontend_Gtk-0.4.0/Gtk.php',
'to' => 'PEAR/Frontend/Gtk.php',
'role' => 'php',
'tasks' => array(),
),
1 => Array(
'Gtk/Config.php' => Array(
'from' => 'PEAR_Frontend_Gtk-0.4.0/Gtk/Config.php',
'to' => 'PEAR/Frontend/Gtk/Config.php',
'role' => 'php',
'tasks' => array(),
),
2 => Array(
'Gtk/xpm/black_close_icon.xpm' => Array(
'from' => 'PEAR_Frontend_Gtk-0.4.0/Gtk/xpm/black_close_icon.xpm',
'to' => 'PEAR/Frontend/Gtk/xpm/black_close_icon.xpm',
'role' => 'php',
'tasks' => array(),
)
);
$this->assertSame($expectedFileActions, $fileActions);
@ -47,12 +53,14 @@ class PearPackageExtractorTest extends \PHPUnit_Framework_TestCase
$method = new \ReflectionMethod($extractor, 'buildCopyActions');
$method->setAccessible(true);
$fileActions = $method->invoke($extractor, __DIR__ . '/Fixtures/Package_v2.0', 'php');
$fileActions = $method->invoke($extractor, __DIR__ . '/Fixtures/Package_v2.0', array('php' => '/'), array());
$expectedFileActions = array(
0 => Array(
'URL.php' => Array(
'from' => 'Net_URL-1.0.15/URL.php',
'to' => 'Net/URL.php',
'role' => 'php',
'tasks' => array(),
)
);
$this->assertSame($expectedFileActions, $fileActions);
@ -64,18 +72,62 @@ class PearPackageExtractorTest extends \PHPUnit_Framework_TestCase
$method = new \ReflectionMethod($extractor, 'buildCopyActions');
$method->setAccessible(true);
$fileActions = $method->invoke($extractor, __DIR__ . '/Fixtures/Package_v2.1', 'php');
$fileActions = $method->invoke($extractor, __DIR__ . '/Fixtures/Package_v2.1', array('php' => '/', 'script' => '/bin'), array());
$expectedFileActions = array(
0 => Array(
'php/Zend/Authentication/Storage/StorageInterface.php' => Array(
'from' => 'Zend_Authentication-2.0.0beta4/php/Zend/Authentication/Storage/StorageInterface.php',
'to' => '/php/Zend/Authentication/Storage/StorageInterface.php',
'role' => 'php',
'tasks' => array(),
),
1 => Array(
'php/Zend/Authentication/Result.php' => Array(
'from' => 'Zend_Authentication-2.0.0beta4/php/Zend/Authentication/Result.php',
'to' => '/php/Zend/Authentication/Result.php',
)
'role' => 'php',
'tasks' => array(),
),
'php/Test.php' => array (
'from' => 'Zend_Authentication-2.0.0beta4/php/Test.php',
'to' => '/php/Test.php',
'role' => 'script',
'tasks' => array (
array (
'from' => '@version@',
'to' => 'version',
)
)
),
'renamedFile.php' => Array(
'from' => 'Zend_Authentication-2.0.0beta4/renamedFile.php',
'to' => 'correctFile.php',
'role' => 'php',
'tasks' => array(),
),
);
$this->assertSame($expectedFileActions, $fileActions);
}
public function testShouldPerformReplacements()
{
$from = tempnam(sys_get_temp_dir(), 'pear-extract');
$to = $from.'-to';
$original = 'replaced: @placeholder@; not replaced: @another@; replaced again: @placeholder@';
$expected = 'replaced: value; not replaced: @another@; replaced again: value';
file_put_contents($from, $original);
$extractor = new PearPackageExtractor($from);
$method = new \ReflectionMethod($extractor, 'copyFile');
$method->setAccessible(true);
$method->invoke($extractor, $from, $to, array(array('from' => '@placeholder@', 'to' => 'variable')), array('variable' => 'value'));
$result = file_get_contents($to);
unlink($to);
unlink($from);
$this->assertEquals($expected, $result);
}
}

View File

@ -106,7 +106,7 @@ class LibraryInstallerTest extends TestCase
$package = $this->createPackageMock();
$package
->expects($this->once())
->expects($this->any())
->method('getPrettyName')
->will($this->returnValue('some/package'));
@ -136,7 +136,7 @@ class LibraryInstallerTest extends TestCase
$target = $this->createPackageMock();
$initial
->expects($this->once())
->expects($this->any())
->method('getPrettyName')
->will($this->returnValue('package1'));
@ -175,7 +175,7 @@ class LibraryInstallerTest extends TestCase
$package = $this->createPackageMock();
$package
->expects($this->once())
->expects($this->any())
->method('getPrettyName')
->will($this->returnValue('pkg'));

View File

@ -0,0 +1,40 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Test\Mock;
use Composer\Util\RemoteFilesystem;
use Composer\Downloader\TransportException;
/**
* Remote filesystem mock
*/
class RemoteFilesystemMock extends RemoteFilesystem
{
/**
* @param array $contentMap associative array of locations and content
*/
public function __construct(array $contentMap)
{
$this->contentMap = $contentMap;
}
public function getContents($originUrl, $fileUrl, $progress = true)
{
if (!empty($this->contentMap[$fileUrl])) {
return $this->contentMap[$fileUrl];
}
throw new TransportException('The "'.$fileUrl.'" file could not be downloaded (NOT FOUND)', 404);
}
}

View File

@ -0,0 +1,151 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Repository\Pear;
use Composer\Test\TestCase;
use Composer\Package\Version\VersionParser;
use Composer\Package\LinkConstraint\VersionConstraint;
use Composer\Package\Link;
use Composer\Package\MemoryPackage;
use Composer\Test\Mock\RemoteFilesystemMock;
class ChannelReaderTest extends TestCase
{
public function testShouldBuildPackagesFromPearSchema()
{
$rfs = new RemoteFilesystemMock(array(
'http://pear.net/channel.xml' => file_get_contents(__DIR__ . '/Fixtures/channel.1.1.xml'),
'http://test.loc/rest11/c/categories.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.1/categories.xml'),
'http://test.loc/rest11/c/Default/packagesinfo.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.1/packagesinfo.xml'),
));
$reader = new \Composer\Repository\Pear\ChannelReader($rfs);
$channelInfo = $reader->read('http://pear.net/');
$packages = $channelInfo->getPackages();
$this->assertCount(3, $packages);
$this->assertEquals('HTTP_Client', $packages[0]->getPackageName());
$this->assertEquals('HTTP_Request', $packages[1]->getPackageName());
$this->assertEquals('MDB2', $packages[2]->getPackageName());
$mdb2releases = $packages[2]->getReleases();
$this->assertEquals(9, count($mdb2releases['2.4.0']->getDependencyInfo()->getOptionals()));
}
public function testShouldSelectCorrectReader()
{
$rfs = new RemoteFilesystemMock(array(
'http://pear.1.0.net/channel.xml' => file_get_contents(__DIR__ . '/Fixtures/channel.1.0.xml'),
'http://test.loc/rest10/p/packages.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/packages.xml'),
'http://test.loc/rest10/p/http_client/info.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_client_info.xml'),
'http://test.loc/rest10/p/http_request/info.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_request_info.xml'),
'http://pear.1.1.net/channel.xml' => file_get_contents(__DIR__ . '/Fixtures/channel.1.1.xml'),
'http://test.loc/rest11/c/categories.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.1/categories.xml'),
'http://test.loc/rest11/c/Default/packagesinfo.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.1/packagesinfo.xml'),
));
$reader = new \Composer\Repository\Pear\ChannelReader($rfs);
$reader->read('http://pear.1.0.net/');
$reader->read('http://pear.1.1.net/');
}
public function testShouldCreatePackages()
{
$reader = $this->getMockBuilder('\Composer\Repository\PearRepository')
->disableOriginalConstructor()
->getMock();
$ref = new \ReflectionMethod($reader, 'buildComposerPackages');
$ref->setAccessible(true);
$channelInfo = new ChannelInfo(
'test.loc',
'test',
array(
new PackageInfo(
'test.loc',
'sample',
'license',
'shortDescription',
'description',
array(
'1.0.0.1' => new ReleaseInfo(
'stable',
new DependencyInfo(
array(
new DependencyConstraint(
'required',
'> 5.2.0.0',
'php',
''
),
new DependencyConstraint(
'conflicts',
'== 2.5.6.0',
'pear.php.net',
'broken'
),
),
array(
'*' => array(
new DependencyConstraint(
'optional',
'*',
'ext',
'xml'
),
)
)
)
)
)
)
)
);
$packages = $ref->invoke($reader, $channelInfo, new VersionParser());
$expectedPackage = new MemoryPackage('pear-test.loc/sample', '1.0.0.1' , '1.0.0.1');
$expectedPackage->setType('pear-library');
$expectedPackage->setDistType('file');
$expectedPackage->setDescription('description');
$expectedPackage->setDistUrl("http://test.loc/get/sample-1.0.0.1.tgz");
$expectedPackage->setAutoload(array('classmap' => array('')));
$expectedPackage->setIncludePaths(array('/'));
$expectedPackage->setRequires(array(
new Link('pear-test.loc/sample', 'php', $this->createConstraint('>', '5.2.0.0'), 'required', '> 5.2.0.0'),
));
$expectedPackage->setConflicts(array(
new Link('pear-test.loc/sample', 'pear-pear.php.net/broken', $this->createConstraint('==', '2.5.6.0'), 'conflicts', '== 2.5.6.0'),
));
$expectedPackage->setSuggests(array(
'*-ext-xml' => '*',
));
$expectedPackage->setReplaces(array(
new Link('pear-test.loc/sample', 'pear-test/sample', new VersionConstraint('==', '1.0.0.1'), 'replaces', '== 1.0.0.1'),
));
$this->assertCount(1, $packages);
$this->assertEquals($expectedPackage, $packages[0], 0, 1);
}
private function createConstraint($operator, $version)
{
$constraint = new VersionConstraint($operator, $version);
$constraint->setPrettyString($operator.' '.$version);
return $constraint;
}
}

View File

@ -0,0 +1,41 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Repository\Pear;
use Composer\Test\TestCase;
use Composer\Test\Mock\RemoteFilesystemMock;
class ChannelRest10ReaderTest extends TestCase
{
public function testShouldBuildPackagesFromPearSchema()
{
$rfs = new RemoteFilesystemMock(array(
'http://test.loc/rest10/p/packages.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/packages.xml'),
'http://test.loc/rest10/p/http_client/info.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_client_info.xml'),
'http://test.loc/rest10/r/http_client/allreleases.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_client_allreleases.xml'),
'http://test.loc/rest10/r/http_client/deps.1.2.1.txt' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_client_deps.1.2.1.txt'),
'http://test.loc/rest10/p/http_request/info.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_request_info.xml'),
'http://test.loc/rest10/r/http_request/allreleases.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_request_allreleases.xml'),
'http://test.loc/rest10/r/http_request/deps.1.4.0.txt' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_request_deps.1.4.0.txt'),
));
$reader = new \Composer\Repository\Pear\ChannelRest10Reader($rfs);
/** @var $packages \Composer\Package\PackageInterface[] */
$packages = $reader->read('http://test.loc/rest10');
$this->assertCount(2, $packages);
$this->assertEquals('HTTP_Client', $packages[0]->getPackageName());
$this->assertEquals('HTTP_Request', $packages[1]->getPackageName());
}
}

View File

@ -0,0 +1,37 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Repository\Pear;
use Composer\Test\TestCase;
use Composer\Test\Mock\RemoteFilesystemMock;
class ChannelRest11ReaderTest extends TestCase
{
public function testShouldBuildPackagesFromPearSchema()
{
$rfs = new RemoteFilesystemMock(array(
'http://pear.1.1.net/channel.xml' => file_get_contents(__DIR__ . '/Fixtures/channel.1.1.xml'),
'http://test.loc/rest11/c/categories.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.1/categories.xml'),
'http://test.loc/rest11/c/Default/packagesinfo.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.1/packagesinfo.xml'),
));
$reader = new \Composer\Repository\Pear\ChannelRest11Reader($rfs);
/** @var $packages \Composer\Package\PackageInterface[] */
$packages = $reader->read('http://test.loc/rest11');
$this->assertCount(3, $packages);
$this->assertEquals('HTTP_Client', $packages[0]->getPackageName());
$this->assertEquals('HTTP_Request', $packages[1]->getPackageName());
}
}

View File

@ -0,0 +1,167 @@
[
{
"expected": [
{
"type" : "required",
"constraint" : "*",
"channel" : "pear.php.net",
"name" : "Foo"
}
],
"1.0": [
{ "type": "pkg", "rel": "has", "name": "Foo" }
],
"2.0": {
"required": {
"package": {
"name": "Foo",
"channel": "pear.php.net"
}
}
}
},
{
"expected": [
{
"type" : "required",
"constraint" : ">1.0.0.0",
"channel" : "pear.php.net",
"name" : "Foo"
}
],
"1.0": [
{ "type": "pkg", "rel": "gt", "version": "1.0.0", "name": "Foo" }
],
"2.0": {
"required": {
"package": {
"name": "Foo",
"channel": "pear.php.net",
"min": "1.0.0",
"exclude": "1.0.0"
}
}
}
},
{
"expected": [
{
"type" : "conflicts",
"constraint" : "*",
"channel" : "pear.php.net",
"name" : "Foo"
}
],
"1.0": [
{ "type": "pkg", "rel": "not", "name": "Foo" }
],
"2.0": {
"required": {
"package": {
"name": "Foo",
"channel": "pear.php.net",
"conflicts": true
}
}
}
},
{
"expected": [
{
"type" : "required",
"constraint" : ">=1.0.0.0",
"channel" : "pear.php.net",
"name" : "Foo"
},
{
"type" : "required",
"constraint" : "<2.0.0.0",
"channel" : "pear.php.net",
"name" : "Foo"
}
],
"1.0": [
{ "type": "pkg", "rel": "ge", "version": "1.0.0", "name": "Foo" },
{ "type": "pkg", "rel": "lt", "version": "2.0.0", "name": "Foo" }
],
"2.0": {
"required": {
"package": [
{
"name": "Foo",
"channel": "pear.php.net",
"min": "1.0.0"
},
{
"name": "Foo",
"channel": "pear.php.net",
"max": "2.0.0",
"exclude": "2.0.0"
}
]
}
}
},
{
"expected": [
{
"type" : "required",
"constraint" : ">=5.3.0.0",
"channel" : "php",
"name" : ""
}
],
"1.0": [
{ "type": "php", "rel": "ge", "version": "5.3"}
],
"2.0": {
"required": {
"php": {
"min": "5.3"
}
}
}
},
{
"expected": [
{
"type" : "required",
"constraint" : "*",
"channel" : "ext",
"name" : "xmllib"
}
],
"1.0": [
{ "type": "ext", "rel": "has", "name": "xmllib"}
],
"2.0": {
"required": {
"extension": [
{
"name": "xmllib"
}
]
}
}
},
{
"expected": [
{
"type" : "optional",
"constraint" : "*",
"channel" : "ext",
"name" : "xmllib"
}
],
"1.0": false,
"2.0": {
"optional": {
"extension": [
{
"name": "xmllib"
}
]
}
}
}
]

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<a xmlns="http://pear.php.net/dtd/rest.allreleases" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://pear.php.net/dtd/rest.allreleases http://pear.php.net/dtd/rest.allreleases.xsd">
<p>HTTP_Client</p>
<c>pear.net</c>
<r>
<v>1.2.1</v>
<s>stable</s>
</r>
</a>

View File

@ -0,0 +1 @@
a:1:{s:8:"required";a:3:{s:3:"php";a:1:{s:3:"min";s:5:"4.3.0";}s:13:"pearinstaller";a:1:{s:3:"min";s:5:"1.4.3";}s:7:"package";a:3:{s:4:"name";s:12:"HTTP_Request";s:7:"channel";s:8:"pear.net";s:3:"min";s:5:"1.4.0";}}}

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<p xmlns="http://pear.php.net/dtd/rest.package" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://pear.php.net/dtd/rest.package http://pear.php.net/dtd/rest.package.xsd">
<n>HTTP_Client</n>
<c>pear.net</c>
<ca xlink:href="/rest/c/Default">Default</ca>
<l>BSD</l>
<s>
Easy way to perform multiple HTTP requests and process their results
</s>
<d>
The HTTP_Client class wraps around HTTP_Request and provides a higher level interface for performing multiple HTTP requests. Features: * Manages cookies and referrers between requests * Handles HTTP redirection * Has methods to set default headers and request parameters * Implements the Subject-Observer design pattern: the base class sends events to listeners that do the response processing.
</d>
<r xlink:href="/rest/r/http_client"/>
</p>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<a xmlns="http://pear.php.net/dtd/rest.allreleases" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://pear.php.net/dtd/rest.allreleases http://pear.php.net/dtd/rest.allreleases.xsd">
<p>HTTP_Request</p>
<c>pear.net</c>
<r>
<v>1.4.0</v>
<s>stable</s>
</r>
</a>

View File

@ -0,0 +1 @@
a:1:{s:8:"required";a:3:{s:3:"php";a:1:{s:3:"min";s:5:"4.0.0";}s:13:"pearinstaller";a:1:{s:3:"min";s:7:"1.4.0b1";}s:7:"package";a:2:{i:0;a:3:{s:4:"name";s:7:"Net_URL";s:7:"channel";s:12:"pear.dev.loc";s:3:"min";s:6:"1.0.12";}i:1;a:3:{s:4:"name";s:10:"Net_Socket";s:7:"channel";s:8:"pear.net";s:3:"min";s:5:"1.0.2";}}}}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<p xmlns="http://pear.php.net/dtd/rest.package" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://pear.php.net/dtd/rest.package http://pear.php.net/dtd/rest.package.xsd">
<n>HTTP_Request</n>
<c>pear.net</c>
<ca xlink:href="/rest/c/Default">Default</ca>
<l>BSD</l>
<s>Provides an easy way to perform HTTP requests</s>
<d>
Supports GET/POST/HEAD/TRACE/PUT/DELETE, Basic authentication, Proxy, Proxy Authentication, SSL, file uploads etc.
</d>
<r xlink:href="/rest/r/http_request"/>
</p>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<a xmlns="http://pear.php.net/dtd/rest.allpackages" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://pear.php.net/dtd/rest.allpackages http://pear.php.net/dtd/rest.allpackages.xsd">
<c>pear.net</c>
<p>HTTP_Client</p>
<p>HTTP_Request</p>
</a>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<a xmlns="http://pear.php.net/dtd/rest.allcategories" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://pear.php.net/dtd/rest.allcategories http://pear.php.net/dtd/rest.allcategories.xsd">
<ch>pear.net</ch>
<c xlink:href="/rest/c/Default/info.xml">Default</c>
</a>

View File

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<f xmlns="http://pear.php.net/dtd/rest.categorypackageinfo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://pear.php.net/dtd/rest.categorypackageinfo http://pear.php.net/dtd/rest.categorypackageinfo.xsd">
<pi>
<p>
<n>HTTP_Client</n>
<c>pear.net</c>
<ca xlink:href="/rest/c/Default">Default</ca>
<l>BSD</l>
<s>
Easy way to perform multiple HTTP requests and process their results
</s>
<d>
The HTTP_Client class wraps around HTTP_Request and provides a higher level interface for performing multiple HTTP requests. Features: * Manages cookies and referrers between requests * Handles HTTP redirection * Has methods to set default headers and request parameters * Implements the Subject-Observer design pattern: the base class sends events to listeners that do the response processing.
</d>
<r xlink:href="/rest/r/http_client"/>
</p>
<a>
<r>
<v>1.2.1</v>
<s>stable</s>
</r>
</a>
<deps>
<v>1.2.1</v>
<d>a:1:{s:8:"required";a:3:{s:3:"php";a:1:{s:3:"min";s:5:"4.3.0";}s:13:"pearinstaller";a:1:{s:3:"min";s:5:"1.4.3";}s:7:"package";a:3:{s:4:"name";s:12:"HTTP_Request";s:7:"channel";s:8:"pear.net";s:3:"min";s:5:"1.4.0";}}}</d>
</deps>
</pi>
<pi>
<p>
<n>HTTP_Request</n>
<c>pear.net</c>
<ca xlink:href="/rest/c/Default">Default</ca>
<l>BSD</l>
<s>Provides an easy way to perform HTTP requests</s>
<d>
Supports GET/POST/HEAD/TRACE/PUT/DELETE, Basic authentication, Proxy, Proxy Authentication, SSL, file uploads etc.
</d>
<r xlink:href="/rest/r/http_request"/>
</p>
<a>
<r>
<v>1.4.0</v>
<s>stable</s>
</r>
</a>
<deps>
<v>1.4.0</v>
<d>a:1:{s:8:&quot;required&quot;;a:3:{s:3:&quot;php&quot;;a:1:{s:3:&quot;min&quot;;s:5:&quot;4.0.0&quot;;}s:13:&quot;pearinstaller&quot;;a:1:{s:3:&quot;min&quot;;s:7:&quot;1.4.0b1&quot;;}s:7:&quot;package&quot;;a:2:{i:0;a:3:{s:4:&quot;name&quot;;s:7:&quot;Net_URL&quot;;s:7:&quot;channel&quot;;s:12:&quot;pear.php.net&quot;;s:3:&quot;min&quot;;s:6:&quot;1.0.12&quot;;}i:1;a:3:{s:4:&quot;name&quot;;s:10:&quot;Net_Socket&quot;;s:7:&quot;channel&quot;;s:12:&quot;pear.php.net&quot;;s:3:&quot;min&quot;;s:5:&quot;1.0.2&quot;;}}}}</d>
</deps>
</pi>
<pi>
<p><n>MDB2</n>
<c>pear.net</c>
<ca xlink:href="/rest/c/Database">Database</ca>
<l>BSD License</l>
<s>database abstraction layer</s>
<d>PEAR MDB2 is a merge of the PEAR DB and Metabase php database abstraction layers.
It provides a common API for all supported RDBMS. The main difference to most
other DB abstraction packages is that MDB2 goes much further to ensure
portability. MDB2 provides most of its many features optionally that
can be used to construct portable SQL statements:
* Object-Oriented API
* A DSN (data source name) or array format for specifying database servers
* Datatype abstraction and on demand datatype conversion
* Various optional fetch modes to fix portability issues
* Portable error codes
* Sequential and non sequential row fetching as well as bulk fetching
* Ability to make buffered and unbuffered queries
* Ordered array and associative array for the fetched rows
* Prepare/execute (bind) named and unnamed placeholder emulation
* Sequence/autoincrement emulation
* Replace emulation
* Limited sub select emulation
* Row limit emulation
* Transactions/savepoint support
* Large Object support
* Index/Unique Key/Primary Key support
* Pattern matching abstraction
* Module framework to load advanced functionality on demand
* Ability to read the information schema
* RDBMS management methods (creating, dropping, altering)
* Reverse engineering schemas from an existing database
* SQL function call abstraction
* Full integration into the PEAR Framework
* PHPDoc API documentation</d>
<r xlink:href="/rest/r/mdb2"/>
</p>
<a>
<r><v>2.4.0</v><s>stable</s></r>
</a>
<deps>
<v>2.4.0</v>
<d>a:2:{s:8:&quot;required&quot;;a:3:{s:3:&quot;php&quot;;a:1:{s:3:&quot;min&quot;;s:5:&quot;4.3.2&quot;;}s:13:&quot;pearinstaller&quot;;a:1:{s:3:&quot;min&quot;;s:7:&quot;1.4.0b1&quot;;}s:7:&quot;package&quot;;a:3:{s:4:&quot;name&quot;;s:4:&quot;PEAR&quot;;s:7:&quot;channel&quot;;s:12:&quot;pear.php.net&quot;;s:3:&quot;min&quot;;s:5:&quot;1.3.6&quot;;}}s:5:&quot;group&quot;;a:9:{i:0;a:2:{s:7:&quot;attribs&quot;;a:2:{s:4:&quot;hint&quot;;s:29:&quot;Frontbase SQL driver for MDB2&quot;;s:4:&quot;name&quot;;s:5:&quot;fbsql&quot;;}s:10:&quot;subpackage&quot;;a:3:{s:4:&quot;name&quot;;s:17:&quot;MDB2_Driver_fbsql&quot;;s:7:&quot;channel&quot;;s:12:&quot;pear.php.net&quot;;s:3:&quot;min&quot;;s:5:&quot;0.3.0&quot;;}}i:1;a:2:{s:7:&quot;attribs&quot;;a:2:{s:4:&quot;hint&quot;;s:34:&quot;Interbase/Firebird driver for MDB2&quot;;s:4:&quot;name&quot;;s:5:&quot;ibase&quot;;}s:10:&quot;subpackage&quot;;a:3:{s:4:&quot;name&quot;;s:17:&quot;MDB2_Driver_ibase&quot;;s:7:&quot;channel&quot;;s:12:&quot;pear.php.net&quot;;s:3:&quot;min&quot;;s:5:&quot;1.4.0&quot;;}}i:2;a:2:{s:7:&quot;attribs&quot;;a:2:{s:4:&quot;hint&quot;;s:21:&quot;MySQL driver for MDB2&quot;;s:4:&quot;name&quot;;s:5:&quot;mysql&quot;;}s:10:&quot;subpackage&quot;;a:3:{s:4:&quot;name&quot;;s:17:&quot;MDB2_Driver_mysql&quot;;s:7:&quot;channel&quot;;s:12:&quot;pear.php.net&quot;;s:3:&quot;min&quot;;s:5:&quot;1.4.0&quot;;}}i:3;a:2:{s:7:&quot;attribs&quot;;a:2:{s:4:&quot;hint&quot;;s:22:&quot;MySQLi driver for MDB2&quot;;s:4:&quot;name&quot;;s:6:&quot;mysqli&quot;;}s:10:&quot;subpackage&quot;;a:3:{s:4:&quot;name&quot;;s:18:&quot;MDB2_Driver_mysqli&quot;;s:7:&quot;channel&quot;;s:12:&quot;pear.php.net&quot;;s:3:&quot;min&quot;;s:5:&quot;1.4.0&quot;;}}i:4;a:2:{s:7:&quot;attribs&quot;;a:2:{s:4:&quot;hint&quot;;s:29:&quot;MS SQL Server driver for MDB2&quot;;s:4:&quot;name&quot;;s:5:&quot;mssql&quot;;}s:10:&quot;subpackage&quot;;a:3:{s:4:&quot;name&quot;;s:17:&quot;MDB2_Driver_mssql&quot;;s:7:&quot;channel&quot;;s:12:&quot;pear.php.net&quot;;s:3:&quot;min&quot;;s:5:&quot;1.2.0&quot;;}}i:5;a:2:{s:7:&quot;attribs&quot;;a:2:{s:4:&quot;hint&quot;;s:22:&quot;Oracle driver for MDB2&quot;;s:4:&quot;name&quot;;s:4:&quot;oci8&quot;;}s:10:&quot;subpackage&quot;;a:3:{s:4:&quot;name&quot;;s:16:&quot;MDB2_Driver_oci8&quot;;s:7:&quot;channel&quot;;s:12:&quot;pear.php.net&quot;;s:3:&quot;min&quot;;s:5:&quot;1.4.0&quot;;}}i:6;a:2:{s:7:&quot;attribs&quot;;a:2:{s:4:&quot;hint&quot;;s:26:&quot;PostgreSQL driver for MDB2&quot;;s:4:&quot;name&quot;;s:5:&quot;pgsql&quot;;}s:10:&quot;subpackage&quot;;a:3:{s:4:&quot;name&quot;;s:17:&quot;MDB2_Driver_pgsql&quot;;s:7:&quot;channel&quot;;s:12:&quot;pear.php.net&quot;;s:3:&quot;min&quot;;s:5:&quot;1.4.0&quot;;}}i:7;a:2:{s:7:&quot;attribs&quot;;a:2:{s:4:&quot;hint&quot;;s:24:&quot;Querysim driver for MDB2&quot;;s:4:&quot;name&quot;;s:8:&quot;querysim&quot;;}s:10:&quot;subpackage&quot;;a:3:{s:4:&quot;name&quot;;s:20:&quot;MDB2_Driver_querysim&quot;;s:7:&quot;channel&quot;;s:12:&quot;pear.php.net&quot;;s:3:&quot;min&quot;;s:5:&quot;0.6.0&quot;;}}i:8;a:2:{s:7:&quot;attribs&quot;;a:2:{s:4:&quot;hint&quot;;s:23:&quot;SQLite2 driver for MDB2&quot;;s:4:&quot;name&quot;;s:6:&quot;sqlite&quot;;}s:10:&quot;subpackage&quot;;a:3:{s:4:&quot;name&quot;;s:18:&quot;MDB2_Driver_sqlite&quot;;s:7:&quot;channel&quot;;s:12:&quot;pear.php.net&quot;;s:3:&quot;min&quot;;s:5:&quot;1.4.0&quot;;}}}}</d>
</deps>
</pi>
</f>

View File

@ -0,0 +1,12 @@
<channel xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0" xsi:schemaLocation="http://pear.php.net/channel-1.0 http://pear.php.net/dtd/channel-1.0.xsd">
<name>pear.net</name>
<summary>Test PEAR channel</summary>
<suggestedalias>test_alias</suggestedalias>
<servers>
<primary>
<rest>
<baseurl type="REST1.0">http://test.loc/rest10/</baseurl>
</rest>
</primary>
</servers>
</channel>

View File

@ -0,0 +1,12 @@
<channel xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0" xsi:schemaLocation="http://pear.php.net/channel-1.0 http://pear.php.net/dtd/channel-1.0.xsd">
<name>pear.net</name>
<summary>Test PEAR channel</summary>
<suggestedalias>test_alias</suggestedalias>
<servers>
<primary>
<rest>
<baseurl type="REST1.1">http://test.loc/rest11/</baseurl>
</rest>
</primary>
</servers>
</channel>

View File

@ -0,0 +1,58 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Repository\Pear;
use Composer\Test\TestCase;
class PackageDependencyParserTest extends TestCase
{
/**
* @dataProvider dataProvider10
* @param $expected
* @param $data
*/
public function testShouldParseDependencies($expected, $data10, $data20)
{
$expectedDependencies = array();
foreach ($expected as $expectedItem) {
$expectedDependencies[] = new DependencyConstraint(
$expectedItem['type'],
$expectedItem['constraint'],
$expectedItem['channel'],
$expectedItem['name']
);
}
$parser = new PackageDependencyParser();
if (false !== $data10) {
$result = $parser->buildDependencyInfo($data10);
$this->assertEquals($expectedDependencies, $result->getRequires() + $result->getOptionals(), "Failed for package.xml 1.0 format");
}
if (false !== $data20) {
$result = $parser->buildDependencyInfo($data20);
$this->assertEquals($expectedDependencies, $result->getRequires() + $result->getOptionals(), "Failed for package.xml 2.0 format");
}
}
public function dataProvider10()
{
$data = json_decode(file_get_contents(__DIR__.'/Fixtures/DependencyParserTestData.json'), true);
if (0 !== json_last_error()) {
throw new \PHPUnit_Framework_Exception('Invalid json file.');
}
return $data;
}
}

View File

@ -29,11 +29,11 @@ class PearRepositoryTest extends TestCase
*/
private $remoteFilesystem;
public function testComposerNonCompatibleRepositoryShouldSetIncludePath()
public function testComposerShouldSetIncludePath()
{
$url = 'pear.phpmd.org';
$expectedPackages = array(
array('name' => 'pear-phpmd/PHP_PMD', 'version' => '1.3.3'),
array('name' => 'pear-pear.phpmd.org/PHP_PMD', 'version' => '1.3.3'),
);
$repoConfig = array(
@ -78,53 +78,47 @@ class PearRepositoryTest extends TestCase
public function repositoryDataProvider()
{
return array(
array(
array(
'pear.phpunit.de',
array(
array('name' => 'pear-phpunit/PHPUnit_MockObject', 'version' => '1.1.1'),
array('name' => 'pear-phpunit/PHPUnit', 'version' => '3.6.10'),
array('name' => 'pear-pear.phpunit.de/PHPUnit_MockObject', 'version' => '1.1.1'),
array('name' => 'pear-pear.phpunit.de/PHPUnit', 'version' => '3.6.10'),
)
),
array(
'pear.php.net',
array(
array('name' => 'pear-pear/PEAR', 'version' => '1.9.4'),
array('name' => 'pear-pear.php.net/PEAR', 'version' => '1.9.4'),
)
),
array(
'pear.pdepend.org',
array(
array('name' => 'pear-pdepend/PHP_Depend', 'version' => '1.0.5'),
array('name' => 'pear-pear.pdepend.org/PHP_Depend', 'version' => '1.0.5'),
)
),
array(
'pear.phpmd.org',
array(
array('name' => 'pear-phpmd/PHP_PMD', 'version' => '1.3.3'),
array('name' => 'pear-pear.phpmd.org/PHP_PMD', 'version' => '1.3.3'),
)
),
array(
'pear.doctrine-project.org',
array(
array('name' => 'pear-doctrine/DoctrineORM', 'version' => '2.2.2'),
array('name' => 'pear-pear.doctrine-project.org/DoctrineORM', 'version' => '2.2.2'),
)
),
array(
'pear.symfony-project.com',
array(
array('name' => 'pear-symfony/YAML', 'version' => '1.0.6'),
array('name' => 'pear-pear.symfony-project.com/YAML', 'version' => '1.0.6'),
)
),
array(
'pear.pirum-project.org',
array(
array('name' => 'pear-pirum/Pirum', 'version' => '1.1.4'),
)
),
array(
'packages.zendframework.com',
array(
array('name' => 'pear-zf2/Zend_Code', 'version' => '2.0.0.0-beta3'),
array('name' => 'pear-pear.pirum-project.org/Pirum', 'version' => '1.1.4'),
)
),
);

View File

@ -10,5 +10,7 @@
* file that was distributed with this source code.
*/
error_reporting(E_ALL);
$loader = require __DIR__.'/../src/bootstrap.php';
$loader->add('Composer\Test', __DIR__);