4 * This file is part of composer/semver.
6 * (c) Composer <https://github.com/composer>
8 * For the full copyright and license information, please view
9 * the LICENSE file that was distributed with this source code.
12 namespace Composer\Semver\Constraint;
15 * Defines a constraint.
17 class Constraint implements ConstraintInterface
19 /* operator integer values */
28 * Operator to integer translation table.
32 private static $transOpStr = array(
44 * Integer to operator translation table.
48 private static $transOpInt = array(
64 protected $prettyString;
67 * @param ConstraintInterface $provider
71 public function matches(ConstraintInterface $provider)
73 if ($provider instanceof $this) {
74 return $this->matchSpecific($provider);
77 // turn matching around to find a match
78 return $provider->matches($this);
82 * @param string $prettyString
84 public function setPrettyString($prettyString)
86 $this->prettyString = $prettyString;
92 public function getPrettyString()
94 if ($this->prettyString) {
95 return $this->prettyString;
98 return $this->__toString();
102 * Get all supported comparison operators.
106 public static function getSupportedOperators()
108 return array_keys(self::$transOpStr);
112 * Sets operator and version to compare with.
114 * @param string $operator
115 * @param string $version
117 * @throws \InvalidArgumentException if invalid operator is given.
119 public function __construct($operator, $version)
121 if (!isset(self::$transOpStr[$operator])) {
122 throw new \InvalidArgumentException(sprintf(
123 'Invalid operator "%s" given, expected one of: %s',
125 implode(', ', self::getSupportedOperators())
129 $this->operator = self::$transOpStr[$operator];
130 $this->version = $version;
136 * @param string $operator
137 * @param bool $compareBranches
139 * @throws \InvalidArgumentException if invalid operator is given.
143 public function versionCompare($a, $b, $operator, $compareBranches = false)
145 if (!isset(self::$transOpStr[$operator])) {
146 throw new \InvalidArgumentException(sprintf(
147 'Invalid operator "%s" given, expected one of: %s',
149 implode(', ', self::getSupportedOperators())
153 $aIsBranch = 'dev-' === substr($a, 0, 4);
154 $bIsBranch = 'dev-' === substr($b, 0, 4);
156 if ($aIsBranch && $bIsBranch) {
157 return $operator === '==' && $a === $b;
160 // when branches are not comparable, we make sure dev branches never match anything
161 if (!$compareBranches && ($aIsBranch || $bIsBranch)) {
165 return version_compare($a, $b, $operator);
169 * @param Constraint $provider
170 * @param bool $compareBranches
174 public function matchSpecific(Constraint $provider, $compareBranches = false)
176 $noEqualOp = str_replace('=', '', self::$transOpInt[$this->operator]);
177 $providerNoEqualOp = str_replace('=', '', self::$transOpInt[$provider->operator]);
179 $isEqualOp = self::OP_EQ === $this->operator;
180 $isNonEqualOp = self::OP_NE === $this->operator;
181 $isProviderEqualOp = self::OP_EQ === $provider->operator;
182 $isProviderNonEqualOp = self::OP_NE === $provider->operator;
184 // '!=' operator is match when other operator is not '==' operator or version is not match
185 // these kinds of comparisons always have a solution
186 if ($isNonEqualOp || $isProviderNonEqualOp) {
187 return !$isEqualOp && !$isProviderEqualOp
188 || $this->versionCompare($provider->version, $this->version, '!=', $compareBranches);
191 // an example for the condition is <= 2.0 & < 1.0
192 // these kinds of comparisons always have a solution
193 if ($this->operator !== self::OP_EQ && $noEqualOp === $providerNoEqualOp) {
197 if ($this->versionCompare($provider->version, $this->version, self::$transOpInt[$this->operator], $compareBranches)) {
198 // special case, e.g. require >= 1.0 and provide < 1.0
199 // 1.0 >= 1.0 but 1.0 is outside of the provided interval
200 if ($provider->version === $this->version
201 && self::$transOpInt[$provider->operator] === $providerNoEqualOp
202 && self::$transOpInt[$this->operator] !== $noEqualOp) {
215 public function __toString()
217 return self::$transOpInt[$this->operator] . ' ' . $this->version;