Remove PEAR installer/downloader/repos/..., fixes #8778

main
Jordi Boggiano 4 years ago
parent feefa9be71
commit 44a4429978
No known key found for this signature in database
GPG Key ID: 7BBD42C429EC80BC

@ -310,7 +310,7 @@ Example:
All links are optional fields.
`require` and `require-dev` additionally support _stability flags_ ([root-only](04-schema.md#root-package)).
They take the form "_constraint_@_stability flag_".
They take the form "_constraint_@_stability flag_".
These allow you to further restrict or expand the stability of a package beyond
the scope of the [minimum-stability](#minimum-stability) setting. You can apply
them to a constraint, or apply them to an empty _constraint_ if you want to
@ -770,8 +770,6 @@ The following repository types are supported:
using the `options` parameter.
* **vcs:** The version control system repository can fetch packages from git,
svn, fossil and hg repositories.
* **pear:** With this you can import any pear repository into your Composer
project.
* **package:** If you depend on a project that does not have any support for
composer whatsoever you can define the package inline using a `package`
repository. You basically inline the `composer.json` object.
@ -800,10 +798,6 @@ Example:
"type": "vcs",
"url": "https://github.com/Seldaek/monolog"
},
{
"type": "pear",
"url": "https://pear2.php.net"
},
{
"type": "package",
"package": {

@ -395,92 +395,6 @@ for this server will be overwritten. To change this behavior by setting the
}
```
### PEAR
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. All packages are also aliased with prefix
`pear-{channelAlias}/`.
Example using `pear2.php.net`:
```json
{
"repositories": [
{
"type": "pear",
"url": "https://pear2.php.net"
}
],
"require": {
"pear-pear2.php.net/PEAR2_Text_Markdown": "*",
"pear-pear2/PEAR2_HTTP_Request": "*"
}
}
```
In this case the short name of the channel is `pear2`, so the
`PEAR2_HTTP_Request` package name becomes `pear-pear2/PEAR2_HTTP_Request`.
> **Note:** The `pear` repository requires doing quite a few requests per
> package, so this may considerably slow down the installation process.
#### Custom vendor alias
It is possible to alias PEAR channel packages with a custom vendor name.
Example:
Suppose you have a private PEAR repository and wish to use Composer to
incorporate dependencies from a VCS. Your PEAR repository contains the
following packages:
* `BasePackage`
* `IntermediatePackage`, which depends on `BasePackage`
* `TopLevelPackage1` and `TopLevelPackage2` which both depend
on `IntermediatePackage`
Without a vendor alias, Composer will use the PEAR channel name as the
vendor portion of the package name:
* `pear-pear.foobar.repo/BasePackage`
* `pear-pear.foobar.repo/IntermediatePackage`
* `pear-pear.foobar.repo/TopLevelPackage1`
* `pear-pear.foobar.repo/TopLevelPackage2`
Suppose at a later time you wish to migrate your PEAR packages to a
Composer repository and naming scheme, and adopt the vendor name of `foobar`.
Projects using your PEAR packages would not see the updated packages, since
they have a different vendor name (`foobar/IntermediatePackage` vs
`pear-pear.foobar.repo/IntermediatePackage`).
By specifying `vendor-alias` for the PEAR repository from the start, you can
avoid this scenario and future-proof your package names.
To illustrate, the following example would get the `BasePackage`,
`TopLevelPackage1`, and `TopLevelPackage2` packages from your PEAR repository
and `IntermediatePackage` from a Github repository:
```json
{
"repositories": [
{
"type": "git",
"url": "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

@ -1,266 +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\Util\Filesystem;
/**
* Extractor for pear packages.
*
* Composer cannot rely on tar files structure when place it inside package target dir. Correct source files
* disposition must be read from package.xml
* This extract pear package source files to target dir.
*
* @author Alexey Prilipko <palex@farpost.com>
*/
class PearPackageExtractor
{
private static $rolesWithoutPackageNamePrefix = array('php', 'script', 'www');
/** @var Filesystem */
private $filesystem;
private $file;
public function __construct($file)
{
if (!is_file($file)) {
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 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, array $roles = array('php' => '/', 'script' => '/bin'), $vars = array())
{
$extractionPath = $target.'/tarball';
try {
$archive = new \PharData($this->file);
$archive->extractTo($extractionPath, null, true);
if (!is_file($this->combine($extractionPath, '/package.xml'))) {
throw new \RuntimeException('Invalid PEAR package. It must contain package.xml file.');
}
$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);
}
}
/**
* Perform copy actions on files
*
* @param array $files array of copy actions ('from', 'to') with relative paths
* @param string $source path to source dir.
* @param string $target 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, $roles, $vars)
{
foreach ($files as $file) {
$from = $this->combine($source, $file['from']);
$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, $tasks, $vars)
{
if (!is_file($from)) {
throw new \RuntimeException('Invalid PEAR package. package.xml defines file that is not located inside tarball.');
}
$this->filesystem->ensureDirectoryExists(dirname($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])) {
if ($varName === 'php_bin' && false === strpos($to, '.bat')) {
$replacements[$pattern] = preg_replace('{\.bat$}', '', $vars[$varName]);
} else {
$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));
}
}
/**
* Builds list of copy and list of remove actions that would transform extracted PEAR tarball into installed package.
*
* @param string $source string path to extracted files
* @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
* @throws \RuntimeException
* @return array array of 'source' => 'target', where source is location of file in the tarball (relative to source
* path, and target is destination of file (also relative to $source path)
*/
private function buildCopyActions($source, array $roles, $vars)
{
/** @var \SimpleXmlElement $package */
$package = simplexml_load_string(file_get_contents($this->combine($source, 'package.xml')));
if (false === $package) {
throw new \RuntimeException('Package definition file is not valid.');
}
$packageSchemaVersion = $package['version'];
if ('1.0' == $packageSchemaVersion) {
$children = $package->release->filelist->children();
$packageName = (string) $package->name;
$packageVersion = (string) $package->release->version;
$sourceDir = $packageName . '-' . $packageVersion;
$result = $this->buildSourceList10($children, $roles, $sourceDir, '', null, $packageName);
} 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, $roles, $sourceDir, '', null, $packageName);
$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.');
}
return $result;
}
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, $packageName)
{
$result = array();
// enumerating files
foreach ($children as $child) {
/** @var $child \SimpleXMLElement */
if ($child->getName() == 'dir') {
$dirSource = $this->combine($source, (string) $child['name']);
$dirTarget = $child['baseinstalldir'] ?: $target;
$dirRole = $child['role'] ?: $role;
$dirFiles = $this->buildSourceList10($child->children(), $targetRoles, $dirSource, $dirTarget, $dirRole, $packageName);
$result = array_merge($result, $dirFiles);
} elseif ($child->getName() == 'file') {
$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);
if (!in_array($fileRole, self::$rolesWithoutPackageNamePrefix)) {
$fileTarget = $packageName . '/' . $fileTarget;
}
$result[(string) $child['name']] = array('from' => $fileSource, 'to' => $fileTarget, 'role' => $fileRole, 'tasks' => array());
}
}
}
return $result;
}
private function buildSourceList20($children, $targetRoles, $source, $target, $role, $packageName)
{
$result = array();
// enumerating files
foreach ($children as $child) {
/** @var $child \SimpleXMLElement */
if ('dir' == $child->getName()) {
$dirSource = $this->combine($source, $child['name']);
$dirTarget = $child['baseinstalldir'] ?: $target;
$dirRole = $child['role'] ?: $role;
$dirFiles = $this->buildSourceList20($child->children(), $targetRoles, $dirSource, $dirTarget, $dirRole, $packageName);
$result = array_merge($result, $dirFiles);
} 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']);
$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);
}
}
if (!in_array($fileRole, self::$rolesWithoutPackageNamePrefix)) {
$fileTarget = $packageName . '/' . $fileTarget;
}
$result[(string) $child['name']] = array('from' => $fileSource, 'to' => $fileTarget, 'role' => $fileRole, 'tasks' => $fileTasks);
}
}
}
return $result;
}
private function combine($left, $right)
{
return rtrim($left, '/') . '/' . ltrim($right, '/');
}
}

@ -545,7 +545,6 @@ 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\PluginInstaller($io, $composer));
$im->addInstaller(new Installer\MetapackageInstaller($io));
}

@ -1,144 +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\Installer;
use Composer\IO\IOInterface;
use Composer\Package\PackageInterface;
use Composer\Util\Filesystem;
use Composer\Util\ProcessExecutor;
/**
* Utility to handle installation of package "bin"/binaries for PEAR packages
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class PearBinaryInstaller extends BinaryInstaller
{
private $installer;
private $vendorDir;
/**
* @param IOInterface $io
* @param string $binDir
* @param string $vendorDir
* @param string $binCompat
* @param Filesystem $filesystem
* @param PearInstaller $installer
*/
public function __construct(IOInterface $io, $binDir, $vendorDir, $binCompat, Filesystem $filesystem, PearInstaller $installer)
{
parent::__construct($io, $binDir, $binCompat, $filesystem);
$this->installer = $installer;
$this->vendorDir = $vendorDir;
}
protected function getBinaries(PackageInterface $package)
{
$binariesPath = $this->installer->getInstallPath($package) . '/bin/';
$binaries = array();
if (file_exists($binariesPath)) {
foreach (new \FilesystemIterator($binariesPath, \FilesystemIterator::KEY_AS_FILENAME | \FilesystemIterator::CURRENT_AS_FILEINFO) as $fileName => $value) {
if (!$value->isDir()) {
$binaries[] = 'bin/'.$fileName;
}
}
}
return $binaries;
}
protected function initializeBinDir()
{
parent::initializeBinDir();
file_put_contents($this->binDir.'/composer-php', $this->generateUnixyPhpProxyCode());
@chmod($this->binDir.'/composer-php', 0777 & ~umask());
file_put_contents($this->binDir.'/composer-php.bat', $this->generateWindowsPhpProxyCode());
@chmod($this->binDir.'/composer-php.bat', 0777 & ~umask());
}
protected function generateWindowsProxyCode($bin, $link)
{
$binPath = $this->filesystem->findShortestPath($link, $bin);
if ('.bat' === substr($bin, -4)) {
$caller = 'call';
} else {
$handle = fopen($bin, 'r');
$line = fgets($handle);
fclose($handle);
if (preg_match('{^#!/(?:usr/bin/env )?(?:[^/]+/)*(.+)$}m', $line, $match)) {
$caller = trim($match[1]);
} else {
$caller = 'php';
}
if ($caller === 'php') {
return "@echo off\r\n".
"pushd .\r\n".
"cd %~dp0\r\n".
"set PHP_PROXY=%CD%\\composer-php.bat\r\n".
"cd ".ProcessExecutor::escape(dirname($binPath))."\r\n".
"set BIN_TARGET=%CD%\\".basename($binPath)."\r\n".
"popd\r\n".
"%PHP_PROXY% \"%BIN_TARGET%\" %*\r\n";
}
}
return "@echo off\r\n".
"pushd .\r\n".
"cd %~dp0\r\n".
"cd ".ProcessExecutor::escape(dirname($binPath))."\r\n".
"set BIN_TARGET=%CD%\\".basename($binPath)."\r\n".
"popd\r\n".
$caller." \"%BIN_TARGET%\" %*\r\n";
}
private function generateWindowsPhpProxyCode()
{
$binToVendor = $this->filesystem->findShortestPath($this->binDir, $this->vendorDir, true);
return
"@echo off\r\n" .
"setlocal enabledelayedexpansion\r\n" .
"set BIN_DIR=%~dp0\r\n" .
"set VENDOR_DIR=%BIN_DIR%\\".$binToVendor."\r\n" .
"set DIRS=.\r\n" .
"FOR /D %%V IN (%VENDOR_DIR%\\*) DO (\r\n" .
" FOR /D %%P IN (%%V\\*) DO (\r\n" .
" set DIRS=!DIRS!;%%~fP\r\n" .
" )\r\n" .
")\r\n" .
"php.exe -d include_path=!DIRS! %*\r\n";
}
private function generateUnixyPhpProxyCode()
{
$binToVendor = $this->filesystem->findShortestPath($this->binDir, $this->vendorDir, true);
return
"#!/usr/bin/env sh\n".
"SRC_DIR=`pwd`\n".
"BIN_DIR=`dirname $0`\n".
"VENDOR_DIR=\$BIN_DIR/".escapeshellarg($binToVendor)."\n".
"DIRS=\"\"\n".
"for vendor in \$VENDOR_DIR/*; do\n".
" if [ -d \"\$vendor\" ]; then\n".
" for package in \$vendor/*; do\n".
" if [ -d \"\$package\" ]; then\n".
" DIRS=\"\${DIRS}:\${package}\"\n".
" fi\n".
" done\n".
" fi\n".
"done\n".
"php -d include_path=\".\$DIRS\" $@\n";
}
}

@ -1,84 +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\Installer;
use Composer\IO\IOInterface;
use Composer\Composer;
use Composer\Downloader\PearPackageExtractor;
use Composer\Repository\InstalledRepositoryInterface;
use Composer\Package\PackageInterface;
use Composer\Util\Platform;
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
{
/**
* Initializes library installer.
*
* @param IOInterface $io io instance
* @param Composer $composer
* @param string $type package type that this installer handles
*/
public function __construct(IOInterface $io, Composer $composer, $type = 'pear-library')
{
$filesystem = new Filesystem();
$binaryInstaller = new PearBinaryInstaller($io, rtrim($composer->getConfig()->get('bin-dir'), '/'), rtrim($composer->getConfig()->get('vendor-dir'), '/'), $composer->getConfig()->get('bin-compat'), $filesystem, $this);
parent::__construct($io, $composer, $type, $filesystem, $binaryInstaller);
}
/**
* {@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 = Platform::isWindows();
$php_bin = $this->binDir . ($isWindows ? '/composer-php.bat' : '/composer-php');
if (!$isWindows) {
$php_bin = '/usr/bin/env ' . $php_bin;
}
$installPath = $this->getInstallPath($package);
$vars = array(
'os' => $isWindows ? 'windows' : 'linux',
'php_bin' => $php_bin,
'pear_php' => $installPath,
'php_dir' => $installPath,
'bin_dir' => $installPath . '/bin',
'data_dir' => $installPath . '/data',
'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', 'data' => '/data'), $vars);
$this->io->writeError(' Cleaning up', true, IOInterface::VERBOSE);
$this->filesystem->unlink($packageArchive);
}
}

@ -1,86 +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\Repository\Pear;
use Composer\Util\HttpDownloader;
/**
* 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 HttpDownloader */
private $httpDownloader;
protected function __construct(HttpDownloader $httpDownloader)
{
$this->httpDownloader = $httpDownloader;
}
/**
* Read content from remote filesystem.
*
* @param string $origin server
* @param string $path relative path to content
* @throws \UnexpectedValueException
* @return string
*/
protected function requestContent($origin, $path)
{
$url = rtrim($origin, '/') . '/' . ltrim($path, '/');
try {
$content = $this->httpDownloader->get($url)->getBody();
} catch (\Exception $e) {
throw new \UnexpectedValueException('The PEAR channel at ' . $url . ' did not respond.', 0, $e);
}
if (!$content) {
throw new \UnexpectedValueException('The PEAR channel at ' . $url . ' did not respond.');
}
return str_replace('http://pear.php.net/rest/', 'https://pear.php.net/rest/', $content);
}
/**
* Read xml content from remote filesystem
*
* @param string $origin server
* @param string $path relative path to content
* @throws \UnexpectedValueException
* @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) {
throw new \UnexpectedValueException(sprintf('The PEAR channel at ' . $origin . ' is broken. (Invalid XML at file `%s`)', $path));
}
return $xml;
}
}

@ -1,67 +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\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;
}
}

@ -1,101 +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\Repository\Pear;
use Composer\Util\HttpDownloader;
/**
* PEAR Channel package reader.
*
* Reads channel packages info from and builds Package's
*
* @author Alexey Prilipko <palex@farpost.com>
*/
class ChannelReader extends BaseChannelReader
{
/** @var array of ('xpath test' => 'rest implementation') */
private $readerMap;
public function __construct(HttpDownloader $httpDownloader)
{
parent::__construct($httpDownloader);
$rest10reader = new ChannelRest10Reader($httpDownloader);
$rest11reader = new ChannelRest11Reader($httpDownloader);
$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 string $url PEAR Channel url
* @throws \UnexpectedValueException
* @return ChannelInfo
*/
public function read($url)
{
$xml = $this->requestXml($url, "/channel.xml");
$channelName = (string) $xml->name;
$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 \SimpleXMLElement $channelXml
* @param string[] $supportedVersions 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:*/ns:rest/ns:baseurl[@type='{$version}']";
$testResult = $channelXml->xpath($xpathTest);
foreach ($testResult as $result) {
// Choose first https:// option.
$result = (string) $result;
if (preg_match('{^https://}i', $result)) {
return array('version' => $version, 'baseUrl' => $result);
}
}
// Fallback to non-https if it does not exist.
if (count($testResult) > 0) {
return array('version' => $version, 'baseUrl' => (string) $testResult[0]);
}
}
return null;
}
}

@ -1,165 +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\Repository\Pear;
use Composer\Downloader\TransportException;
use Composer\Util\HttpDownloader;
/**
* 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(HttpDownloader $httpDownloader)
{
parent::__construct($httpDownloader);
$this->dependencyReader = new PackageDependencyParser();
}
/**
* Reads package descriptions using PEAR Rest 1.0 interface
*
* @param string $baseUrl base Url interface
*
* @return PackageInfo[]
*/
public function read($baseUrl)
{
return $this->readPackages($baseUrl);
}
/**
* Read list of packages from
* {baseUrl}/p/packages.xml
*
* @param string $baseUrl
* @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 string $baseUrl
* @param string $packageName
* @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 string $baseUrl
* @param string $packageName
* @throws \Composer\Downloader\TransportException|\Exception
* @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 string $baseUrl
* @param string $packageName
* @param string $version
* @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);
return $dependencyReader->buildDependencyInfo($dependencyArray);
}
}

@ -1,141 +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\Repository\Pear;
use Composer\Util\HttpDownloader;
/**
* 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(HttpDownloader $httpDownloader)
{
parent::__construct($httpDownloader);
$this->dependencyReader = new PackageDependencyParser();
}
/**
* Reads package descriptions using PEAR Rest 1.1 interface
*
* @param string $baseUrl base Url interface
*
* @return PackageInfo[]
*/
public function read($baseUrl)
{
return $this->readChannelPackages($baseUrl);
}
/**
* Read list of channel categories from
* {baseUrl}/c/categories.xml
*
* @param string $baseUrl
* @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 string $baseUrl
* @param string $categoryName
* @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 \SimpleXMLElement $packageInfo 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();
$releasesInfo = $packageInfo->xpath('ns:a/ns:r');
if ($releasesInfo) {
foreach ($releasesInfo 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
);
}
}

@ -1,60 +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\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;
}
}

@ -1,50 +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\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;
}
}

@ -1,317 +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\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 array $depArray
* @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
*
* https://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 array $depArray 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 array $depArray 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 array $depItem dependency constraint or array of dependency constraints
* @param string $depType 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 array $depItem dependency constraint or array of dependency constraints
* @param string $depType 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) {
if (!array_key_exists('channel', $subDepItem)) {
$subDepItem['channel'] = $subDepItem['uri'];
}
$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 several '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 string $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);
}
}

@ -1,94 +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\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 description
*/
public function getShortDescription()
{
return $this->shortDescription;
}
/**
* @return string the package license
*/
public function getLicense()
{
return $this->license;
}
/**
* @return ReleaseInfo[]
*/
public function getReleases()
{
return $this->releases;
}
}

@ -1,50 +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\Repository\Pear;
/**
* PEAR package release info
*
* @author Alexey Prilipko <palex@farpost.com>
*/
class ReleaseInfo
{
private $stability;
private $dependencyInfo;
/**
* @param string $stability
* @param DependencyInfo $dependencyInfo
*/
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;
}
}

@ -15,9 +15,7 @@ namespace Composer\Repository;
use Composer\IO\IOInterface;
use Composer\Semver\VersionParser as SemverVersionParser;
use Composer\Package\Version\VersionParser;
use Composer\Repository\Pear\ChannelReader;
use Composer\Package\CompletePackage;
use Composer\Repository\Pear\ChannelInfo;
use Composer\EventDispatcher\EventDispatcher;
use Composer\Package\Link;
use Composer\Semver\Constraint\Constraint;
@ -33,171 +31,13 @@ use Composer\Factory;
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @deprecated
* @private
*/
class PearRepository extends ArrayRepository implements ConfigurableRepositoryInterface
class PearRepository extends ArrayRepository
{
private $url;
private $io;
private $httpDownloader;
private $versionParser;
private $repoConfig;
/** @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, HttpDownloader $httpDownloader, EventDispatcher $dispatcher = null)
{
parent::__construct();
if (!preg_match('{^https?://}', $repoConfig['url'])) {
$repoConfig['url'] = 'http://'.$repoConfig['url'];
}
$urlBits = parse_url($repoConfig['url']);
if (empty($urlBits['scheme']) || empty($urlBits['host'])) {
throw new \UnexpectedValueException('Invalid url given for PEAR repository: '.$repoConfig['url']);
}
$this->url = rtrim($repoConfig['url'], '/');
$this->io = $io;
$this->httpDownloader = $httpDownloader;
$this->vendorAlias = isset($repoConfig['vendor-alias']) ? $repoConfig['vendor-alias'] : null;
$this->versionParser = new VersionParser();
$this->repoConfig = $repoConfig;
}
public function getRepoName()
{
return 'pear repo ('.$this->url.')';
}
public function getRepoConfig()
{
return $this->repoConfig;
}
protected function initialize()
{
parent::initialize();
$this->io->writeError('Initializing PEAR repository '.$this->url);
$reader = new ChannelReader($this->httpDownloader);
try {
$channelInfo = $reader->read($this->url);
} catch (\Exception $e) {
$this->io->writeError('<warning>PEAR repository from '.$this->url.' could not be loaded. '.$e->getMessage().'</warning>');
return;
}
$packages = $this->buildComposerPackages($channelInfo, $this->versionParser);
foreach ($packages as $package) {
$this->addPackage($package);
}
}
/**
* Builds CompletePackages from PEAR package definition data.
*
* @param ChannelInfo $channelInfo
* @param SemverVersionParser $versionParser
* @return CompletePackage[]
*/
private function buildComposerPackages(ChannelInfo $channelInfo, SemverVersionParser $versionParser)
{
$result = array();
foreach ($channelInfo->getPackages() as $packageDefinition) {
foreach ($packageDefinition->getReleases() as $version => $releaseInfo) {
try {
$normalizedVersion = $versionParser->normalize($version);
} catch (\UnexpectedValueException $e) {
$this->io->writeError('Could not load '.$packageDefinition->getPackageName().' '.$version.': '.$e->getMessage(), true, IOInterface::VERBOSE);
continue;
}
$composerPackageName = $this->buildComposerPackageName($packageDefinition->getChannelName(), $packageDefinition->getPackageName());
// distribution url must be read from /r/{packageName}/{version}.xml::/r/g:text()
// but this location is 'de-facto' standard
$urlBits = parse_url($this->url);
$scheme = (isset($urlBits['scheme']) && 'https' === $urlBits['scheme'] && extension_loaded('openssl')) ? 'https' : 'http';
$distUrl = "{$scheme}://{$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 ($channelInfo->getName() == $packageDefinition->getChannelName()) {
$composerPackageAlias = $this->buildComposerPackageName($channelInfo->getAlias(), $packageDefinition->getPackageName());
$aliasConstraint = new Constraint('==', $normalizedVersion);
$replaces[] = new Link($composerPackageName, $composerPackageAlias, $aliasConstraint, 'replaces', (string) $aliasConstraint);
}
// alias package with user-specified prefix. it makes private pear channels looks like composer's.
if (!empty($this->vendorAlias)
&& ($this->vendorAlias != 'pear-'.$channelInfo->getAlias() || $channelInfo->getName() != $packageDefinition->getChannelName())
) {
$composerPackageAlias = "{$this->vendorAlias}/{$packageDefinition->getPackageName()}";
$aliasConstraint = new Constraint('==', $normalizedVersion);
$replaces[] = new Link($composerPackageName, $composerPackageAlias, $aliasConstraint, 'replaces', (string) $aliasConstraint);
}
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;
}
}
foreach ($releaseInfo->getDependencyInfo()->getOptionals() as $group => $dependencyConstraints) {
foreach ($dependencyConstraints as $dependencyConstraint) {
$dependencyPackageName = $this->buildComposerPackageName($dependencyConstraint->getChannelName(), $dependencyConstraint->getPackageName());
$suggests[$group.'-'.$dependencyPackageName] = $dependencyConstraint->getConstraint();
}
}
$package = new CompletePackage($composerPackageName, $normalizedVersion, $version);
$package->setType('pear-library');
$package->setDescription($packageDefinition->getDescription());
$package->setLicense(array($packageDefinition->getLicense()));
$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 $result;
}
private function buildComposerPackageName($channelName, $packageName)
{
if ('php' === $channelName) {
return "php";
}
if ('ext' === $channelName) {
return "ext-{$packageName}";
}
return "pear-{$channelName}/{$packageName}";
throw new \RuntimeException('The PEAR repository has been removed from Composer 2.0');
}
}

@ -1,146 +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\PearPackageExtractor;
use Composer\Test\TestCase;
class PearPackageExtractorTest extends TestCase
{
public function testShouldExtractPackage_1_0()
{
$state = libxml_disable_entity_loader(true);
$extractor = $this->getMockForAbstractClass('Composer\Downloader\PearPackageExtractor', array(), '', false);
$method = new \ReflectionMethod($extractor, 'buildCopyActions');
$method->setAccessible(true);
$fileActions = $method->invoke($extractor, __DIR__ . '/Fixtures/Package_v1.0', array('php' => '/'), array());
libxml_disable_entity_loader($state);
$expectedFileActions = array(
'Gtk.php' => array(
'from' => 'PEAR_Frontend_Gtk-0.4.0/Gtk.php',
'to' => 'PEAR/Frontend/Gtk.php',
'role' => 'php',
'tasks' => 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(),
),
'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);
}
public function testShouldExtractPackage_2_0()
{
$state = libxml_disable_entity_loader(true);
$extractor = $this->getMockForAbstractClass('Composer\Downloader\PearPackageExtractor', array(), '', false);
$method = new \ReflectionMethod($extractor, 'buildCopyActions');
$method->setAccessible(true);
$fileActions = $method->invoke($extractor, __DIR__ . '/Fixtures/Package_v2.0', array('php' => '/'), array());
libxml_disable_entity_loader($state);
$expectedFileActions = array(
'URL.php' => array(
'from' => 'Net_URL-1.0.15/URL.php',
'to' => 'Net/URL.php',
'role' => 'php',
'tasks' => array(),
),
);
$this->assertSame($expectedFileActions, $fileActions);
}
public function testShouldExtractPackage_2_1()
{
$state = libxml_disable_entity_loader(true);
$extractor = $this->getMockForAbstractClass('Composer\Downloader\PearPackageExtractor', array(), '', false);
$method = new \ReflectionMethod($extractor, 'buildCopyActions');
$method->setAccessible(true);
$fileActions = $method->invoke($extractor, __DIR__ . '/Fixtures/Package_v2.1', array('php' => '/', 'script' => '/bin'), array());
libxml_disable_entity_loader($state);
$expectedFileActions = 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(),
),
'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($this->getUniqueTmpDirectory(), '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);
}
}

@ -1,163 +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\Repository\Pear;
use Composer\Repository\Pear\ChannelInfo;
use Composer\Repository\Pear\DependencyConstraint;
use Composer\Repository\Pear\DependencyInfo;
use Composer\Repository\Pear\PackageInfo;
use Composer\Repository\Pear\ReleaseInfo;
use Composer\Test\TestCase;
use Composer\Semver\VersionParser;
use Composer\Semver\Constraint\Constraint;
use Composer\Package\Link;
use Composer\Package\CompletePackage;
use Composer\Test\Mock\HttpDownloaderMock;
class ChannelReaderTest extends TestCase
{
public function testShouldBuildPackagesFromPearSchema()
{
$httpDownloader = new HttpDownloaderMock(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($httpDownloader);
$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->assertCount(9, $mdb2releases['2.4.0']->getDependencyInfo()->getOptionals());
}
public function testShouldSelectCorrectReader()
{
$httpDownloader = new HttpDownloaderMock(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/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'),
'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($httpDownloader);
$pear10 = $reader->read('http://pear.1.0.net/');
$this->assertCount(2, $pear10->getPackages());
$pear11 = $reader->read('http://pear.1.1.net/');
$this->assertCount(3, $pear11->getPackages());
}
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 CompletePackage('pear-test.loc/sample', '1.0.0.1', '1.0.0.1');
$expectedPackage->setType('pear-library');
$expectedPackage->setDistType('file');
$expectedPackage->setDescription('description');
$expectedPackage->setLicense(array('license'));
$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 Constraint('==', '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 Constraint($operator, $version);
$constraint->setPrettyString($operator.' '.$version);
return $constraint;
}
}

@ -1,41 +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\Repository\Pear;
use Composer\Test\TestCase;
use Composer\Test\Mock\HttpDownloaderMock;
class ChannelRest10ReaderTest extends TestCase
{
public function testShouldBuildPackagesFromPearSchema()
{
$httpDownloader = new HttpDownloaderMock(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($httpDownloader);
/** @var \Composer\Package\PackageInterface[] $packages */
$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());
}
}

@ -1,37 +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\Repository\Pear;
use Composer\Test\TestCase;
use Composer\Test\Mock\HttpDownloaderMock;
class ChannelRest11ReaderTest extends TestCase
{
public function testShouldBuildPackagesFromPearSchema()
{
$httpDownloader = new HttpDownloaderMock(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($httpDownloader);
/** @var \Composer\Package\PackageInterface[] $packages */
$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());
}
}

@ -1,167 +0,0 @@
[
{
"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"
}
]
}
}
}
]

@ -1,9 +0,0 @@
<?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>

@ -1 +0,0 @@
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";}}}

@ -1,14 +0,0 @@
<?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>

@ -1,9 +0,0 @@
<?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>

@ -1 +0,0 @@
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";}}}}

@ -1,12 +0,0 @@
<?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>

@ -1,6 +0,0 @@
<?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>

@ -1,5 +0,0 @@
<?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>

@ -1,97 +0,0 @@
<?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>

@ -1,12 +0,0 @@
<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>

@ -1,12 +0,0 @@
<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>

@ -1,61 +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\Repository\Pear;
use Composer\Repository\Pear\DependencyConstraint;
use Composer\Repository\Pear\PackageDependencyParser;
use Composer\Test\TestCase;
class PackageDependencyParserTest extends TestCase
{
/**
* @dataProvider dataProvider10
* @param $expected
* @param $data10
* @param $data20
*/
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;
}
}

@ -1,148 +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\Repository;
use Composer\Repository\PearRepository;
use Composer\Test\TestCase;
/**
* @group legacy
*/
class PearRepositoryTest extends TestCase
{
/**
* @var PearRepository
*/
private $repository;
/**
* @var \PHPUnit_Framework_MockObject_MockObject
*/
private $httpDownloader;
public function testComposerShouldSetIncludePath()
{
$url = 'pear.phpmd.org';
if (!@file_get_contents('http://'.$url)) {
$this->markTestSkipped('Repository '.$url.' appears to be unreachable');
}
$expectedPackages = array(
array('name' => 'pear-pear.phpmd.org/PHP_PMD', 'version' => '1.3.3'),
);
$repoConfig = array(
'url' => $url,
);
$this->createRepository($repoConfig);
foreach ($expectedPackages as $expectedPackage) {
$package = $this->repository->findPackage($expectedPackage['name'], $expectedPackage['version']);
$this->assertInstanceOf(
'Composer\Package\PackageInterface',
$package,
'Expected package ' . $expectedPackage['name'] . ', version ' . $expectedPackage['version'] .
' not found in pear channel ' . $url
);
$this->assertSame(array('/'), $package->getIncludePaths());
}
}
/**
* @dataProvider repositoryDataProvider
* @param string $url
* @param array $expectedPackages
*/
public function testRepositoryRead($url, array $expectedPackages)
{
$repoConfig = array(
'url' => $url,
);
if (!@file_get_contents('http://'.$url)) {
$this->markTestSkipped('Repository '.$url.' appears to be unreachable');
}
$this->createRepository($repoConfig);
foreach ($expectedPackages as $expectedPackage) {
$this->assertInstanceOf(
'Composer\Package\PackageInterface',
$this->repository->findPackage($expectedPackage['name'], $expectedPackage['version']),
'Expected package ' . $expectedPackage['name'] . ', version ' . $expectedPackage['version'] .
' not found in pear channel ' . $url
);
}
}
public function repositoryDataProvider()
{
return array(
array(
'pear.php.net',
array(
array('name' => 'pear-pear.php.net/PEAR', 'version' => '1.9.4'),
),
),
array(
'pear.pdepend.org',
array(
array('name' => 'pear-pear.pdepend.org/PHP_Depend', 'version' => '1.0.5'),
),
),
array(
'pear.phpmd.org',
array(
array('name' => 'pear-pear.phpmd.org/PHP_PMD', 'version' => '1.3.3'),
),
),
array(
'pear.doctrine-project.org',
array(
array('name' => 'pear-pear.doctrine-project.org/DoctrineORM', 'version' => '2.2.2'),
),
),
array(
'pear.symfony-project.com',
array(
array('name' => 'pear-pear.symfony-project.com/YAML', 'version' => '1.0.6'),
),
),
array(
'pear.pirum-project.org',
array(
array('name' => 'pear-pear.pirum-project.org/Pirum', 'version' => '1.1.4'),
),
),
);
}
private function createRepository($repoConfig)
{
$ioInterface = $this->getMockBuilder('Composer\IO\IOInterface')
->getMock();
$config = new \Composer\Config();
$this->httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')
->disableOriginalConstructor()
->getMock();
$this->repository = new PearRepository($repoConfig, $ioInterface, $config, null);
}
protected function tearDown()
{
$this->repository = null;
$this->httpDownloader = null;
}
}

@ -97,7 +97,7 @@ class RepositoryManagerTest extends TestCase
array('git', array('url' => 'http://github.com/foo/bar')),
array('git', array('url' => 'git@example.org:foo/bar.git')),
array('svn', array('url' => 'svn://example.org/foo/bar')),
array('pear', array('url' => 'http://pear.example.org/foo')),
array('pear', array('url' => 'http://pear.example.org/foo'), 'RuntimeException'),
array('package', array('package' => array())),
array('invalid', array(), 'InvalidArgumentException'),
);

Loading…
Cancel
Save