Review fixes

main
Alexey Prilipko 12 years ago
parent e173f11b37
commit f2853c842b

@ -26,17 +26,17 @@ abstract class BaseChannelReader
/** /**
* PEAR REST Interface namespaces * PEAR REST Interface namespaces
*/ */
const channelNS = 'http://pear.php.net/channel-1.0'; const CHANNEL_NS = 'http://pear.php.net/channel-1.0';
const allCategoriesNS = 'http://pear.php.net/dtd/rest.allcategories'; const ALL_CATEGORIES_NS = 'http://pear.php.net/dtd/rest.allcategories';
const categoryPackagesInfoNS = 'http://pear.php.net/dtd/rest.categorypackageinfo'; const CATEGORY_PACKAGES_INFO_NS = 'http://pear.php.net/dtd/rest.categorypackageinfo';
const allPackagesNS = 'http://pear.php.net/dtd/rest.allpackages'; const ALL_PACKAGES_NS = 'http://pear.php.net/dtd/rest.allpackages';
const allReleasesNS = 'http://pear.php.net/dtd/rest.allreleases'; const ALL_RELEASES_NS = 'http://pear.php.net/dtd/rest.allreleases';
const packageInfoNS = 'http://pear.php.net/dtd/rest.package'; const PACKAGE_INFO_NS = 'http://pear.php.net/dtd/rest.package';
/** @var RemoteFilesystem */ /** @var RemoteFilesystem */
private $rfs; private $rfs;
protected function __construct($rfs) protected function __construct(RemoteFilesystem $rfs)
{ {
$this->rfs = $rfs; $this->rfs = $rfs;
} }
@ -53,7 +53,7 @@ abstract class BaseChannelReader
$url = rtrim($origin, '/') . '/' . ltrim($path, '/'); $url = rtrim($origin, '/') . '/' . ltrim($path, '/');
$content = $this->rfs->getContents($origin, $url, false); $content = $this->rfs->getContents($origin, $url, false);
if (!$content) { if (!$content) {
throw new \UnexpectedValueException('The PEAR channel at '.$url.' did not respond.'); throw new \UnexpectedValueException('The PEAR channel at ' . $url . ' did not respond.');
} }
return $content; return $content;
@ -73,7 +73,7 @@ abstract class BaseChannelReader
if (false == $xml) { if (false == $xml) {
$url = rtrim($origin, '/') . '/' . ltrim($path, '/'); $url = rtrim($origin, '/') . '/' . ltrim($path, '/');
throw new \UnexpectedValueException('The PEAR channel at '.$origin.' is broken.'); throw new \UnexpectedValueException('The PEAR channel at ' . $origin . ' is broken.');
} }
return $xml; return $xml;

@ -43,28 +43,6 @@ class ChannelReader extends BaseChannelReader
); );
} }
/**
* 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 * Reads PEAR channel through REST interface and builds list of packages
* *
@ -81,7 +59,7 @@ class ChannelReader extends BaseChannelReader
$supportedVersions = array_keys($this->readerMap); $supportedVersions = array_keys($this->readerMap);
$selectedRestVersion = $this->selectRestVersion($xml, $supportedVersions); $selectedRestVersion = $this->selectRestVersion($xml, $supportedVersions);
if (false === $selectedRestVersion) { if (!$selectedRestVersion) {
throw new \UnexpectedValueException(sprintf('PEAR repository $s does not supports any of %s protocols.', $url, implode(', ', $supportedVersions))); throw new \UnexpectedValueException(sprintf('PEAR repository $s does not supports any of %s protocols.', $url, implode(', ', $supportedVersions)));
} }
@ -92,109 +70,24 @@ class ChannelReader extends BaseChannelReader
} }
/** /**
* Builds MemoryPackages from PEAR package definition data. * Reads channel supported REST interfaces and selects one of them
* *
* @param $channelName string channel name * @param $channelXml \SimpleXMLElement
* @param $channelAlias string channel alias * @param $supportedVersions string[] supported PEAR REST protocols
* @param $packageDefinitions PackageInfo[] package definition * @return array|null hash with selected version and baseUrl
* @return array
*/ */
private function buildComposerPackages($channelName, $channelAlias, $packageDefinitions) private function selectRestVersion($channelXml, $supportedVersions)
{ {
$versionParser = new \Composer\Package\Version\VersionParser(); $channelXml->registerXPathNamespace('ns', self::CHANNEL_NS);
$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()); foreach ($supportedVersions as $version) {
$xpathTest = "ns:servers/ns:primary/ns:rest/ns:baseurl[@type='{$version}']";
// distribution url must be read from /r/{packageName}/{version}.xml::/r/g:text() $testResult = $channelXml->xpath($xpathTest);
$distUrl = "http://{$packageDefinition->getChannelName()}/get/{$packageDefinition->getPackageName()}-{$version}.tgz"; if (count($testResult) > 0) {
return array('version' => $version, 'baseUrl' => (string) $testResult[0]);
$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; return null;
}
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;
}
} }
} }

@ -61,7 +61,7 @@ class ChannelRest10Reader extends BaseChannelReader
$xmlPath = '/p/packages.xml'; $xmlPath = '/p/packages.xml';
$xml = $this->requestXml($baseUrl, $xmlPath); $xml = $this->requestXml($baseUrl, $xmlPath);
$xml->registerXPathNamespace('ns', self::allPackagesNS); $xml->registerXPathNamespace('ns', self::ALL_PACKAGES_NS);
foreach ($xml->xpath('ns:p') as $node) { foreach ($xml->xpath('ns:p') as $node) {
$packageName = (string) $node; $packageName = (string) $node;
$packageInfo = $this->readPackage($baseUrl, $packageName); $packageInfo = $this->readPackage($baseUrl, $packageName);
@ -83,7 +83,7 @@ class ChannelRest10Reader extends BaseChannelReader
{ {
$xmlPath = '/p/' . strtolower($packageName) . '/info.xml'; $xmlPath = '/p/' . strtolower($packageName) . '/info.xml';
$xml = $this->requestXml($baseUrl, $xmlPath); $xml = $this->requestXml($baseUrl, $xmlPath);
$xml->registerXPathNamespace('ns', self::packageInfoNS); $xml->registerXPathNamespace('ns', self::PACKAGE_INFO_NS);
$channelName = (string) $xml->c; $channelName = (string) $xml->c;
$packageName = (string) $xml->n; $packageName = (string) $xml->n;
@ -116,7 +116,7 @@ class ChannelRest10Reader extends BaseChannelReader
try { try {
$xmlPath = '/r/' . strtolower($packageName) . '/allreleases.xml'; $xmlPath = '/r/' . strtolower($packageName) . '/allreleases.xml';
$xml = $this->requestXml($baseUrl, $xmlPath); $xml = $this->requestXml($baseUrl, $xmlPath);
$xml->registerXPathNamespace('ns', self::allReleasesNS); $xml->registerXPathNamespace('ns', self::ALL_RELEASES_NS);
foreach ($xml->xpath('ns:r') as $node) { foreach ($xml->xpath('ns:r') as $node) {
$releaseVersion = (string) $node->v; $releaseVersion = (string) $node->v;
$releaseStability = (string) $node->s; $releaseStability = (string) $node->s;

@ -56,7 +56,7 @@ class ChannelRest11Reader extends BaseChannelReader
$result = array(); $result = array();
$xml = $this->requestXml($baseUrl, "/c/categories.xml"); $xml = $this->requestXml($baseUrl, "/c/categories.xml");
$xml->registerXPathNamespace('ns', self::allCategoriesNS); $xml->registerXPathNamespace('ns', self::ALL_CATEGORIES_NS);
foreach ($xml->xpath('ns:c') as $node) { foreach ($xml->xpath('ns:c') as $node) {
$categoryName = (string) $node; $categoryName = (string) $node;
$categoryPackages = $this->readCategoryPackages($baseUrl, $categoryName); $categoryPackages = $this->readCategoryPackages($baseUrl, $categoryName);
@ -80,7 +80,7 @@ class ChannelRest11Reader extends BaseChannelReader
$categoryPath = '/c/'.urlencode($categoryName).'/packagesinfo.xml'; $categoryPath = '/c/'.urlencode($categoryName).'/packagesinfo.xml';
$xml = $this->requestXml($baseUrl, $categoryPath); $xml = $this->requestXml($baseUrl, $categoryPath);
$xml->registerXPathNamespace('ns', self::categoryPackagesInfoNS); $xml->registerXPathNamespace('ns', self::CATEGORY_PACKAGES_INFO_NS);
foreach ($xml->xpath('ns:pi') as $node) { foreach ($xml->xpath('ns:pi') as $node) {
$packageInfo = $this->parsePackage($node); $packageInfo = $this->parsePackage($node);
$result[] = $packageInfo; $result[] = $packageInfo;
@ -97,7 +97,7 @@ class ChannelRest11Reader extends BaseChannelReader
*/ */
private function parsePackage($packageInfo) private function parsePackage($packageInfo)
{ {
$packageInfo->registerXPathNamespace('ns', self::categoryPackagesInfoNS); $packageInfo->registerXPathNamespace('ns', self::CATEGORY_PACKAGES_INFO_NS);
$channelName = (string) $packageInfo->p->c; $channelName = (string) $packageInfo->p->c;
$packageName = (string) $packageInfo->p->n; $packageName = (string) $packageInfo->p->n;
$license = (string) $packageInfo->p->l; $license = (string) $packageInfo->p->l;

@ -19,6 +19,23 @@ namespace Composer\Repository\Pear;
*/ */
class PackageDependencyParser 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 * Builds dependency information from package.xml 1.0 format
* *
@ -229,23 +246,6 @@ class PackageDependencyParser
return $result; 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 * Parses version constraint
* *
@ -260,7 +260,8 @@ class PackageDependencyParser
$values = array_intersect_key($data, $dep20toOperatorMap); $values = array_intersect_key($data, $dep20toOperatorMap);
if (0 == count($values)) { if (0 == count($values)) {
return '*'; return '*';
} elseif (isset($values['min']) && isset($values['exclude']) && $data['min'] == $data['exclude']) { }
if (isset($values['min']) && isset($values['exclude']) && $data['min'] == $data['exclude']) {
$versions[] = '>' . $this->parseVersion($values['min']); $versions[] = '>' . $this->parseVersion($values['min']);
} elseif (isset($values['max']) && isset($values['exclude']) && $data['max'] == $data['exclude']) { } elseif (isset($values['max']) && isset($values['exclude']) && $data['max'] == $data['exclude']) {
$versions[] = '<' . $this->parseVersion($values['max']); $versions[] = '<' . $this->parseVersion($values['max']);
@ -283,7 +284,7 @@ class PackageDependencyParser
* Softened version parser * Softened version parser
* *
* @param $version * @param $version
* @return bool|string * @return null|string
*/ */
private function parseVersion($version) private function parseVersion($version)
{ {
@ -294,9 +295,9 @@ class PackageDependencyParser
.(!empty($matches[4]) ? $matches[4] : '.0'); .(!empty($matches[4]) ? $matches[4] : '.0');
return $version; return $version;
} else {
return false;
} }
return null;
} }
/** /**

@ -13,6 +13,8 @@
namespace Composer\Repository; namespace Composer\Repository;
use Composer\IO\IOInterface; use Composer\IO\IOInterface;
use Composer\Package\Version\VersionParser;
use Composer\Repository\Pear\ChannelReader;
use Composer\Package\MemoryPackage; use Composer\Package\MemoryPackage;
use Composer\Repository\Pear\ChannelInfo; use Composer\Repository\Pear\ChannelInfo;
use Composer\Package\Link; use Composer\Package\Link;
@ -34,6 +36,7 @@ class PearRepository extends ArrayRepository
private $url; private $url;
private $io; private $io;
private $rfs; private $rfs;
private $versionParser;
/** @var string vendor makes additional alias for each channel as {prefix}/{packagename}. It allows smoother /** @var string vendor makes additional alias for each channel as {prefix}/{packagename}. It allows smoother
* package transition to composer-like repositories. * package transition to composer-like repositories.
@ -54,6 +57,7 @@ class PearRepository extends ArrayRepository
$this->io = $io; $this->io = $io;
$this->rfs = $rfs ?: new RemoteFilesystem($this->io); $this->rfs = $rfs ?: new RemoteFilesystem($this->io);
$this->vendorAlias = isset($repoConfig['vendor-alias']) ? $repoConfig['vendor-alias'] : null; $this->vendorAlias = isset($repoConfig['vendor-alias']) ? $repoConfig['vendor-alias'] : null;
$this->versionParser = new VersionParser();
} }
protected function initialize() protected function initialize()
@ -62,14 +66,14 @@ class PearRepository extends ArrayRepository
$this->io->write('Initializing PEAR repository '.$this->url); $this->io->write('Initializing PEAR repository '.$this->url);
$reader = new \Composer\Repository\Pear\ChannelReader($this->rfs); $reader = new ChannelReader($this->rfs);
try { try {
$channelInfo = $reader->read($this->url); $channelInfo = $reader->read($this->url);
} catch (\Exception $e) { } catch (\Exception $e) {
$this->io->write('<warning>PEAR repository from '.$this->url.' could not be loaded. '.$e->getMessage().'</warning>'); $this->io->write('<warning>PEAR repository from '.$this->url.' could not be loaded. '.$e->getMessage().'</warning>');
return; return;
} }
$packages = $this->buildComposerPackages($channelInfo); $packages = $this->buildComposerPackages($channelInfo, $this->versionParser);
foreach ($packages as $package) { foreach ($packages as $package) {
$this->addPackage($package); $this->addPackage($package);
} }
@ -81,14 +85,13 @@ class PearRepository extends ArrayRepository
* @param ChannelInfo $channelInfo * @param ChannelInfo $channelInfo
* @return MemoryPackage * @return MemoryPackage
*/ */
private function buildComposerPackages(ChannelInfo $channelInfo) private function buildComposerPackages(ChannelInfo $channelInfo, VersionParser $versionParser)
{ {
$versionParser = new \Composer\Package\Version\VersionParser();
$result = array(); $result = array();
foreach ($channelInfo->getPackages() as $packageDefinition) { foreach ($channelInfo->getPackages() as $packageDefinition) {
foreach ($packageDefinition->getReleases() as $version => $releaseInfo) { foreach ($packageDefinition->getReleases() as $version => $releaseInfo) {
$normalizedVersion = $this->parseVersion($version); $normalizedVersion = $this->parseVersion($version);
if (false === $normalizedVersion) { if (!$normalizedVersion) {
continue; // skip packages with unparsable versions continue; // skip packages with unparsable versions
} }
@ -160,18 +163,25 @@ class PearRepository extends ArrayRepository
return $result; return $result;
} }
private function buildComposerPackageName($pearChannelName, $pearPackageName) private function buildComposerPackageName($channelName, $packageName)
{ {
if ($pearChannelName == 'php') { if ('php' === $channelName) {
return "php"; return "php";
} elseif ($pearChannelName == 'ext') {
return "ext-{$pearPackageName}";
} else {
return "pear-{$pearChannelName}/{$pearPackageName}";
} }
if ('ext' === $channelName) {
return "ext-{$packageName}";
}
return "pear-{$channelName}/{$packageName}";
} }
protected function parseVersion($version) /**
* Softened version parser.
*
* @param string $version
* @return null|string
*/
private function parseVersion($version)
{ {
if (preg_match('{^v?(\d{1,3})(\.\d+)?(\.\d+)?(\.\d+)?}i', $version, $matches)) { if (preg_match('{^v?(\d{1,3})(\.\d+)?(\.\d+)?(\.\d+)?}i', $version, $matches)) {
$version = $matches[1] $version = $matches[1]
@ -180,8 +190,8 @@ class PearRepository extends ArrayRepository
.(!empty($matches[4]) ? $matches[4] : '.0'); .(!empty($matches[4]) ? $matches[4] : '.0');
return $version; return $version;
} else {
return false;
} }
return null;
} }
} }

@ -13,6 +13,7 @@
namespace Composer\Repository\Pear; namespace Composer\Repository\Pear;
use Composer\Test\TestCase; use Composer\Test\TestCase;
use Composer\Package\Version\VersionParser;
use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Package\LinkConstraint\VersionConstraint;
use Composer\Package\Link; use Composer\Package\Link;
use Composer\Package\MemoryPackage; use Composer\Package\MemoryPackage;
@ -62,45 +63,51 @@ class ChannelReaderTest extends TestCase
public function testShouldCreatePackages() public function testShouldCreatePackages()
{ {
$reader = $this->getMockBuilder('\Composer\Repository\Pear\ChannelReader') $reader = $this->getMockBuilder('\Composer\Repository\PearRepository')
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
$ref = new \ReflectionMethod($reader, 'buildComposerPackages'); $ref = new \ReflectionMethod($reader, 'buildComposerPackages');
$ref->setAccessible(true); $ref->setAccessible(true);
$packageInfo = new PackageInfo( $channelInfo = new ChannelInfo(
'test.loc', 'test.loc',
'sample', 'test',
'license',
'shortDescription',
'description',
array( array(
'1.0.0.1' => new ReleaseInfo( new PackageInfo(
'stable', 'test.loc',
new DependencyInfo( 'sample',
array( 'license',
new DependencyConstraint( 'shortDescription',
'required', 'description',
'> 5.2.0.0', array(
'php', '1.0.0.1' => new ReleaseInfo(
'' 'stable',
), new DependencyInfo(
new DependencyConstraint( array(
'conflicts', new DependencyConstraint(
'== 2.5.6.0', 'required',
'pear.php.net', '> 5.2.0.0',
'broken' 'php',
), ''
), ),
array( new DependencyConstraint(
'*' => array( 'conflicts',
new DependencyConstraint( '== 2.5.6.0',
'optional', 'pear.php.net',
'*', 'broken'
'ext', ),
'xml'
), ),
array(
'*' => array(
new DependencyConstraint(
'optional',
'*',
'ext',
'xml'
),
)
)
) )
) )
) )
@ -108,7 +115,7 @@ class ChannelReaderTest extends TestCase
) )
); );
$packages = $ref->invoke($reader, 'test.loc', 'test', array($packageInfo)); $packages = $ref->invoke($reader, $channelInfo, new VersionParser());
$expectedPackage = new MemoryPackage('pear-test.loc/sample', '1.0.0.1' , '1.0.0.1'); $expectedPackage = new MemoryPackage('pear-test.loc/sample', '1.0.0.1' , '1.0.0.1');
$expectedPackage->setType('library'); $expectedPackage->setType('library');

Loading…
Cancel
Save