Add PEAR channel reader & Update PearRepository to use it.

main
Alexey Prilipko 12 years ago
parent ee2834a169
commit e173f11b37

@ -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 channelNS = 'http://pear.php.net/channel-1.0';
const allCategoriesNS = 'http://pear.php.net/dtd/rest.allcategories';
const categoryPackagesInfoNS = 'http://pear.php.net/dtd/rest.categorypackageinfo';
const allPackagesNS = 'http://pear.php.net/dtd/rest.allpackages';
const allReleasesNS = 'http://pear.php.net/dtd/rest.allreleases';
const packageInfoNS = 'http://pear.php.net/dtd/rest.package';
/** @var RemoteFilesystem */
private $rfs;
protected function __construct($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('The PEAR channel at '.$origin.' is broken.');
}
return $xml;
}
}

@ -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;
}
}

@ -0,0 +1,200 @@
<?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;
use Composer\Package\LinkConstraint\VersionConstraint;
use Composer\Package\Link;
/**
* 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 channel supported REST interfaces and selects one of them
*
* @param $channelXml \SimpleXMLElement
* @param $supportedVersions string[] supported PEAR REST protocols
* @return array|bool hash with selected version and baseUrl
*/
private function selectRestVersion($channelXml, $supportedVersions)
{
$channelXml->registerXPathNamespace('ns', self::channelNS);
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 false;
}
/**
* 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 (false === $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);
}
/**
* Builds MemoryPackages from PEAR package definition data.
*
* @param $channelName string channel name
* @param $channelAlias string channel alias
* @param $packageDefinitions PackageInfo[] package definition
* @return array
*/
private function buildComposerPackages($channelName, $channelAlias, $packageDefinitions)
{
$versionParser = new \Composer\Package\Version\VersionParser();
$result = array();
foreach ($packageDefinitions as $packageDefinition) {
foreach ($packageDefinition->getReleases() as $version => $releaseInfo) {
$normalizedVersion = $this->parseVersion($version);
if (false === $normalizedVersion) {
continue; // skip packages with unparsable versions
}
$composerPackageName = $this->buildComposerPackageName($packageDefinition->getChannelName(), $packageDefinition->getPackageName());
// distribution url must be read from /r/{packageName}/{version}.xml::/r/g:text()
$distUrl = "http://{$packageDefinition->getChannelName()}/get/{$packageDefinition->getPackageName()}-{$version}.tgz";
$requires = array();
$suggests = array();
$conflicts = array();
$replaces = array();
// alias package only when its channel matches repository channel,
// cause we've know only repository channel alias
if ($channelName == $packageDefinition->getChannelName()) {
$composerPackageAlias = $this->buildComposerPackageName($channelAlias, $packageDefinition->getPackageName());
$aliasConstraint = new VersionConstraint('==', $normalizedVersion);
$aliasLink = new Link($composerPackageName, $composerPackageAlias, $aliasConstraint, 'replaces', (string) $aliasConstraint);
$replaces[] = $aliasLink;
}
$dependencyInfo = $releaseInfo->getDependencyInfo();
foreach ($dependencyInfo->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;
}
}
foreach ($dependencyInfo->getOptionals() as $groupName => $dependencyConstraints) {
foreach ($dependencyConstraints as $dependencyConstraint) {
$dependencyPackageName = $this->buildComposerPackageName($dependencyConstraint->getChannelName(), $dependencyConstraint->getPackageName());
$suggests[$groupName.'-'.$dependencyPackageName] = $dependencyConstraint->getConstraint();
}
}
$package = new \Composer\Package\MemoryPackage($composerPackageName, $normalizedVersion, $version);
$package->setType('library');
$package->setDescription($packageDefinition->getDescription());
$package->setDistType('pear');
$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 $result;
}
private function buildComposerPackageName($pearChannelName, $pearPackageName)
{
if ($pearChannelName == 'php') {
return "php";
}
if ($pearChannelName == 'ext') {
return "ext-{$pearPackageName}";
}
return "pear-{$pearChannelName}/{$pearPackageName}";
}
protected 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;
} else {
return false;
}
}
}

@ -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::allPackagesNS);
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::packageInfoNS);
$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::allReleasesNS);
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;
}
}

@ -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::allCategoriesNS);
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::categoryPackagesInfoNS);
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::categoryPackagesInfoNS);
$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
);
}
}

@ -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;
}
}

@ -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;
}
}

@ -0,0 +1,312 @@
<?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 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;
}
/**
* 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());
} elseif (!$this->isHash($depArray)) {
return new DependencyInfo($this->buildDependency10Info($depArray), array());
} else {
return $this->buildDependency20Info($depArray);
}
}
/**
* 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 '*';
} elseif (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 bool|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;
} else {
return false;
}
}
/**
* 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);
}
}

@ -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;
}
}

@ -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;
}
}

@ -13,26 +13,33 @@
namespace Composer\Repository;
use Composer\IO\IOInterface;
use Composer\Package\Loader\ArrayLoader;
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;
/** @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)
{
if (!preg_match('{^https?://}', $repoConfig['url'])) {
@ -44,9 +51,9 @@ 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;
}
protected function initialize()
@ -54,341 +61,127 @@ 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 \Composer\Repository\Pear\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']);
}
}
}
return;
$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;
}
$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);
}
$packages = $this->buildComposerPackages($channelInfo);
foreach ($packages as $package) {
$this->addPackage($package);
}
}
/**
* @param string $categoryLink
* @throws TransportException
* @throws \InvalidArgumentException
* Builds MemoryPackages from PEAR package definition data.
*
* @param ChannelInfo $channelInfo
* @return MemoryPackage
*/
private function fetchPearPackages($categoryLink)
private function buildComposerPackages(ChannelInfo $channelInfo)
{
$packagesXML = $this->requestXml($categoryLink);
$packages = $packagesXML->getElementsByTagName('p');
$loader = new ArrayLoader();
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;
$versionParser = new \Composer\Package\Version\VersionParser();
$result = array();
foreach ($channelInfo->getPackages() as $packageDefinition) {
foreach ($packageDefinition->getReleases() as $version => $releaseInfo) {
$normalizedVersion = $this->parseVersion($version);
if (false === $normalizedVersion) {
continue; // skip packages with unparsable versions
}
throw $e;
}
$releases = $releasesXML->getElementsByTagName('r');
$composerPackageName = $this->buildComposerPackageName($packageDefinition->getChannelName(), $packageDefinition->getPackageName());
foreach ($releases as $release) {
/* @var $release \DOMElement */
$pearVersion = $release->getElementsByTagName('v')->item(0)->nodeValue;
// 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";
$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('/'),
);
$requires = array();
$suggests = array();
$conflicts = array();
$replaces = array();
try {
$deps = $this->rfs->getContents($this->url, $releaseLink . "/deps.".$pearVersion.".txt", false);
} catch (TransportException $e) {
if (strpos($e->getMessage(), '404')) {
continue;
}
throw $e;
// 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);
}
$packageData += $this->parseDependencies($deps);
try {
$this->addPackage($loader->load($packageData));
if ($this->io->isVerbose()) {
$this->io->write('Loaded '.$packageData['name'].' '.$packageData['version']);
}
} catch (\UnexpectedValueException $e) {
if ($this->io->isVerbose()) {
$this->io->write('Could not load '.$packageData['name'].' '.$packageData['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 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'];
}
return implode(',', $versions);
}
/**
* @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;
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;
}
}
if (is_array($value)) {
$dataKey = $value['name'];
if (false === strpos($dataKey, '/')) {
$dataKey = $this->getChannelShorthand($value['channel']).'/'.$dataKey;
}
$data['pear-'.$dataKey] = $this->parseVersion($value);
foreach ($releaseInfo->getDependencyInfo()->getOptionals() as $group => $dependencyConstraints) {
foreach ($dependencyConstraints as $dependencyConstraint) {
$dependencyPackageName = $this->buildComposerPackageName($dependencyConstraint->getChannelName(), $dependencyConstraint->getPackageName());
$suggests[$group.'-'.$dependencyPackageName] = $dependencyConstraint->getConstraint();
}
}
} 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.");
$package = new MemoryPackage($composerPackageName, $normalizedVersion, $version);
$package->setType('library');
$package->setDescription($packageDefinition->getDescription());
$package->setDistType('pear');
$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;
}
}
$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;
return $result;
}
/**
* @param string $packagesLink
* @return void
* @throws \InvalidArgumentException
*/
private function fetchPear2Packages($packagesLink)
private function buildComposerPackageName($pearChannelName, $pearPackageName)
{
$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];
}
$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;
}
}
if ($pearChannelName == 'php') {
return "php";
} elseif ($pearChannelName == 'ext') {
return "ext-{$pearPackageName}";
} else {
return "pear-{$pearChannelName}/{$pearPackageName}";
}
}
/**
* @param string $url
* @return \DOMDocument
*/
private function requestXml($url)
protected function parseVersion($version)
{
$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);
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 $dom;
}
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, '.'));
}
return $version;
} else {
return false;
}
return self::$channelNames[$url];
}
}

@ -13,6 +13,7 @@
namespace Composer\Test\Mock;
use Composer\Util\RemoteFilesystem;
use Composer\Downloader\TransportException;
/**
* Remote filesystem mock
@ -29,10 +30,11 @@ class RemoteFilesystemMock extends RemoteFilesystem
public function getContents($originUrl, $fileUrl, $progress = true)
{
if(!empty($this->contentMap[$fileUrl]))
if (!empty($this->contentMap[$fileUrl])) {
return $this->contentMap[$fileUrl];
}
throw new \Composer\Downloader\TransportException('The "'.$fileUrl.'" file could not be downloaded (NOT FOUND)', 404);
throw new TransportException('The "'.$fileUrl.'" file could not be downloaded (NOT FOUND)', 404);
}
}

@ -0,0 +1,144 @@
<?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\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\Pear\ChannelReader')
->disableOriginalConstructor()
->getMock();
$ref = new \ReflectionMethod($reader, 'buildComposerPackages');
$ref->setAccessible(true);
$packageInfo = 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, 'test.loc', 'test', array($packageInfo));
$expectedPackage = new MemoryPackage('pear-test.loc/sample', '1.0.0.1' , '1.0.0.1');
$expectedPackage->setType('library');
$expectedPackage->setDistType('pear');
$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;
}
}

@ -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());
}
}

@ -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());
}
}

@ -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"
}
]
}
}
}
]

@ -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>

@ -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";}}}

@ -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>

@ -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>

@ -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";}}}}

@ -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>

@ -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>

@ -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>

@ -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>

@ -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>

@ -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>

@ -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;
}
}

@ -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'),
)
),
);

Loading…
Cancel
Save