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