@@ -24,38 +24,38 @@ |
||
| 24 | 24 | */ |
| 25 | 25 | final class IntegerRange extends Integer implements PseudoType |
| 26 | 26 | { |
| 27 | - /** @var string */ |
|
| 28 | - private $minValue; |
|
| 29 | - |
|
| 30 | - /** @var string */ |
|
| 31 | - private $maxValue; |
|
| 32 | - |
|
| 33 | - public function __construct(string $minValue, string $maxValue) |
|
| 34 | - { |
|
| 35 | - $this->minValue = $minValue; |
|
| 36 | - $this->maxValue = $maxValue; |
|
| 37 | - } |
|
| 38 | - |
|
| 39 | - public function underlyingType(): Type |
|
| 40 | - { |
|
| 41 | - return new Integer(); |
|
| 42 | - } |
|
| 43 | - |
|
| 44 | - public function getMinValue(): string |
|
| 45 | - { |
|
| 46 | - return $this->minValue; |
|
| 47 | - } |
|
| 48 | - |
|
| 49 | - public function getMaxValue(): string |
|
| 50 | - { |
|
| 51 | - return $this->maxValue; |
|
| 52 | - } |
|
| 53 | - |
|
| 54 | - /** |
|
| 55 | - * Returns a rendered output of the Type as it would be used in a DocBlock. |
|
| 56 | - */ |
|
| 57 | - public function __toString(): string |
|
| 58 | - { |
|
| 59 | - return 'int<' . $this->minValue . ', ' . $this->maxValue . '>'; |
|
| 60 | - } |
|
| 27 | + /** @var string */ |
|
| 28 | + private $minValue; |
|
| 29 | + |
|
| 30 | + /** @var string */ |
|
| 31 | + private $maxValue; |
|
| 32 | + |
|
| 33 | + public function __construct(string $minValue, string $maxValue) |
|
| 34 | + { |
|
| 35 | + $this->minValue = $minValue; |
|
| 36 | + $this->maxValue = $maxValue; |
|
| 37 | + } |
|
| 38 | + |
|
| 39 | + public function underlyingType(): Type |
|
| 40 | + { |
|
| 41 | + return new Integer(); |
|
| 42 | + } |
|
| 43 | + |
|
| 44 | + public function getMinValue(): string |
|
| 45 | + { |
|
| 46 | + return $this->minValue; |
|
| 47 | + } |
|
| 48 | + |
|
| 49 | + public function getMaxValue(): string |
|
| 50 | + { |
|
| 51 | + return $this->maxValue; |
|
| 52 | + } |
|
| 53 | + |
|
| 54 | + /** |
|
| 55 | + * Returns a rendered output of the Type as it would be used in a DocBlock. |
|
| 56 | + */ |
|
| 57 | + public function __toString(): string |
|
| 58 | + { |
|
| 59 | + return 'int<' . $this->minValue . ', ' . $this->maxValue . '>'; |
|
| 60 | + } |
|
| 61 | 61 | } |
@@ -26,25 +26,25 @@ |
||
| 26 | 26 | */ |
| 27 | 27 | final class List_ extends Array_ implements PseudoType |
| 28 | 28 | { |
| 29 | - public function underlyingType(): Type |
|
| 30 | - { |
|
| 31 | - return new Array_(); |
|
| 32 | - } |
|
| 33 | - |
|
| 34 | - public function __construct(?Type $valueType = null) |
|
| 35 | - { |
|
| 36 | - parent::__construct($valueType, new Integer()); |
|
| 37 | - } |
|
| 38 | - |
|
| 39 | - /** |
|
| 40 | - * Returns a rendered output of the Type as it would be used in a DocBlock. |
|
| 41 | - */ |
|
| 42 | - public function __toString(): string |
|
| 43 | - { |
|
| 44 | - if ($this->valueType instanceof Mixed_) { |
|
| 45 | - return 'list'; |
|
| 46 | - } |
|
| 47 | - |
|
| 48 | - return 'list<' . $this->valueType . '>'; |
|
| 49 | - } |
|
| 29 | + public function underlyingType(): Type |
|
| 30 | + { |
|
| 31 | + return new Array_(); |
|
| 32 | + } |
|
| 33 | + |
|
| 34 | + public function __construct(?Type $valueType = null) |
|
| 35 | + { |
|
| 36 | + parent::__construct($valueType, new Integer()); |
|
| 37 | + } |
|
| 38 | + |
|
| 39 | + /** |
|
| 40 | + * Returns a rendered output of the Type as it would be used in a DocBlock. |
|
| 41 | + */ |
|
| 42 | + public function __toString(): string |
|
| 43 | + { |
|
| 44 | + if ($this->valueType instanceof Mixed_) { |
|
| 45 | + return 'list'; |
|
| 46 | + } |
|
| 47 | + |
|
| 48 | + return 'list<' . $this->valueType . '>'; |
|
| 49 | + } |
|
| 50 | 50 | } |
@@ -44,241 +44,241 @@ |
||
| 44 | 44 | */ |
| 45 | 45 | class Inflector |
| 46 | 46 | { |
| 47 | - /** |
|
| 48 | - * @var LanguageInflectorFactory|null |
|
| 49 | - */ |
|
| 50 | - private static $factory; |
|
| 47 | + /** |
|
| 48 | + * @var LanguageInflectorFactory|null |
|
| 49 | + */ |
|
| 50 | + private static $factory; |
|
| 51 | 51 | |
| 52 | - /** @var InflectorObject|null */ |
|
| 53 | - private static $instance; |
|
| 52 | + /** @var InflectorObject|null */ |
|
| 53 | + private static $instance; |
|
| 54 | 54 | |
| 55 | - private static function getInstance() : InflectorObject |
|
| 56 | - { |
|
| 57 | - if (self::$factory === null) { |
|
| 58 | - self::$factory = self::createFactory(); |
|
| 59 | - } |
|
| 55 | + private static function getInstance() : InflectorObject |
|
| 56 | + { |
|
| 57 | + if (self::$factory === null) { |
|
| 58 | + self::$factory = self::createFactory(); |
|
| 59 | + } |
|
| 60 | 60 | |
| 61 | - if (self::$instance === null) { |
|
| 62 | - self::$instance = self::$factory->build(); |
|
| 63 | - } |
|
| 61 | + if (self::$instance === null) { |
|
| 62 | + self::$instance = self::$factory->build(); |
|
| 63 | + } |
|
| 64 | 64 | |
| 65 | - return self::$instance; |
|
| 66 | - } |
|
| 65 | + return self::$instance; |
|
| 66 | + } |
|
| 67 | 67 | |
| 68 | - private static function createFactory() : LanguageInflectorFactory |
|
| 69 | - { |
|
| 70 | - return InflectorFactory::create(); |
|
| 71 | - } |
|
| 68 | + private static function createFactory() : LanguageInflectorFactory |
|
| 69 | + { |
|
| 70 | + return InflectorFactory::create(); |
|
| 71 | + } |
|
| 72 | 72 | |
| 73 | - /** |
|
| 74 | - * Converts a word into the format for a Doctrine table name. Converts 'ModelName' to 'model_name'. |
|
| 75 | - * |
|
| 76 | - * @deprecated |
|
| 77 | - */ |
|
| 78 | - public static function tableize(string $word) : string |
|
| 79 | - { |
|
| 80 | - @trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED); |
|
| 73 | + /** |
|
| 74 | + * Converts a word into the format for a Doctrine table name. Converts 'ModelName' to 'model_name'. |
|
| 75 | + * |
|
| 76 | + * @deprecated |
|
| 77 | + */ |
|
| 78 | + public static function tableize(string $word) : string |
|
| 79 | + { |
|
| 80 | + @trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED); |
|
| 81 | 81 | |
| 82 | - return self::getInstance()->tableize($word); |
|
| 83 | - } |
|
| 82 | + return self::getInstance()->tableize($word); |
|
| 83 | + } |
|
| 84 | 84 | |
| 85 | - /** |
|
| 86 | - * Converts a word into the format for a Doctrine class name. Converts 'table_name' to 'TableName'. |
|
| 87 | - */ |
|
| 88 | - public static function classify(string $word) : string |
|
| 89 | - { |
|
| 90 | - @trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED); |
|
| 85 | + /** |
|
| 86 | + * Converts a word into the format for a Doctrine class name. Converts 'table_name' to 'TableName'. |
|
| 87 | + */ |
|
| 88 | + public static function classify(string $word) : string |
|
| 89 | + { |
|
| 90 | + @trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED); |
|
| 91 | 91 | |
| 92 | - return self::getInstance()->classify($word); |
|
| 93 | - } |
|
| 92 | + return self::getInstance()->classify($word); |
|
| 93 | + } |
|
| 94 | 94 | |
| 95 | - /** |
|
| 96 | - * Camelizes a word. This uses the classify() method and turns the first character to lowercase. |
|
| 97 | - * |
|
| 98 | - * @deprecated |
|
| 99 | - */ |
|
| 100 | - public static function camelize(string $word) : string |
|
| 101 | - { |
|
| 102 | - @trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED); |
|
| 95 | + /** |
|
| 96 | + * Camelizes a word. This uses the classify() method and turns the first character to lowercase. |
|
| 97 | + * |
|
| 98 | + * @deprecated |
|
| 99 | + */ |
|
| 100 | + public static function camelize(string $word) : string |
|
| 101 | + { |
|
| 102 | + @trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED); |
|
| 103 | 103 | |
| 104 | - return self::getInstance()->camelize($word); |
|
| 105 | - } |
|
| 104 | + return self::getInstance()->camelize($word); |
|
| 105 | + } |
|
| 106 | 106 | |
| 107 | - /** |
|
| 108 | - * Uppercases words with configurable delimiters between words. |
|
| 109 | - * |
|
| 110 | - * Takes a string and capitalizes all of the words, like PHP's built-in |
|
| 111 | - * ucwords function. This extends that behavior, however, by allowing the |
|
| 112 | - * word delimiters to be configured, rather than only separating on |
|
| 113 | - * whitespace. |
|
| 114 | - * |
|
| 115 | - * Here is an example: |
|
| 116 | - * <code> |
|
| 117 | - * <?php |
|
| 118 | - * $string = 'top-o-the-morning to all_of_you!'; |
|
| 119 | - * echo \Doctrine\Common\Inflector\Inflector::ucwords($string); |
|
| 120 | - * // Top-O-The-Morning To All_of_you! |
|
| 121 | - * |
|
| 122 | - * echo \Doctrine\Common\Inflector\Inflector::ucwords($string, '-_ '); |
|
| 123 | - * // Top-O-The-Morning To All_Of_You! |
|
| 124 | - * ?> |
|
| 125 | - * </code> |
|
| 126 | - * |
|
| 127 | - * @param string $string The string to operate on. |
|
| 128 | - * @param string $delimiters A list of word separators. |
|
| 129 | - * |
|
| 130 | - * @return string The string with all delimiter-separated words capitalized. |
|
| 131 | - * |
|
| 132 | - * @deprecated |
|
| 133 | - */ |
|
| 134 | - public static function ucwords(string $string, string $delimiters = " \n\t\r\0\x0B-") : string |
|
| 135 | - { |
|
| 136 | - @trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please use the "ucwords" function instead.', __METHOD__), E_USER_DEPRECATED); |
|
| 107 | + /** |
|
| 108 | + * Uppercases words with configurable delimiters between words. |
|
| 109 | + * |
|
| 110 | + * Takes a string and capitalizes all of the words, like PHP's built-in |
|
| 111 | + * ucwords function. This extends that behavior, however, by allowing the |
|
| 112 | + * word delimiters to be configured, rather than only separating on |
|
| 113 | + * whitespace. |
|
| 114 | + * |
|
| 115 | + * Here is an example: |
|
| 116 | + * <code> |
|
| 117 | + * <?php |
|
| 118 | + * $string = 'top-o-the-morning to all_of_you!'; |
|
| 119 | + * echo \Doctrine\Common\Inflector\Inflector::ucwords($string); |
|
| 120 | + * // Top-O-The-Morning To All_of_you! |
|
| 121 | + * |
|
| 122 | + * echo \Doctrine\Common\Inflector\Inflector::ucwords($string, '-_ '); |
|
| 123 | + * // Top-O-The-Morning To All_Of_You! |
|
| 124 | + * ?> |
|
| 125 | + * </code> |
|
| 126 | + * |
|
| 127 | + * @param string $string The string to operate on. |
|
| 128 | + * @param string $delimiters A list of word separators. |
|
| 129 | + * |
|
| 130 | + * @return string The string with all delimiter-separated words capitalized. |
|
| 131 | + * |
|
| 132 | + * @deprecated |
|
| 133 | + */ |
|
| 134 | + public static function ucwords(string $string, string $delimiters = " \n\t\r\0\x0B-") : string |
|
| 135 | + { |
|
| 136 | + @trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please use the "ucwords" function instead.', __METHOD__), E_USER_DEPRECATED); |
|
| 137 | 137 | |
| 138 | - return ucwords($string, $delimiters); |
|
| 139 | - } |
|
| 138 | + return ucwords($string, $delimiters); |
|
| 139 | + } |
|
| 140 | 140 | |
| 141 | - /** |
|
| 142 | - * Clears Inflectors inflected value caches, and resets the inflection |
|
| 143 | - * rules to the initial values. |
|
| 144 | - * |
|
| 145 | - * @deprecated |
|
| 146 | - */ |
|
| 147 | - public static function reset() : void |
|
| 148 | - { |
|
| 149 | - @trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED); |
|
| 141 | + /** |
|
| 142 | + * Clears Inflectors inflected value caches, and resets the inflection |
|
| 143 | + * rules to the initial values. |
|
| 144 | + * |
|
| 145 | + * @deprecated |
|
| 146 | + */ |
|
| 147 | + public static function reset() : void |
|
| 148 | + { |
|
| 149 | + @trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED); |
|
| 150 | 150 | |
| 151 | - self::$factory = null; |
|
| 152 | - self::$instance = null; |
|
| 153 | - } |
|
| 151 | + self::$factory = null; |
|
| 152 | + self::$instance = null; |
|
| 153 | + } |
|
| 154 | 154 | |
| 155 | - /** |
|
| 156 | - * Adds custom inflection $rules, of either 'plural' or 'singular' $type. |
|
| 157 | - * |
|
| 158 | - * ### Usage: |
|
| 159 | - * |
|
| 160 | - * {{{ |
|
| 161 | - * Inflector::rules('plural', array('/^(inflect)or$/i' => '\1ables')); |
|
| 162 | - * Inflector::rules('plural', array( |
|
| 163 | - * 'rules' => array('/^(inflect)ors$/i' => '\1ables'), |
|
| 164 | - * 'uninflected' => array('dontinflectme'), |
|
| 165 | - * 'irregular' => array('red' => 'redlings') |
|
| 166 | - * )); |
|
| 167 | - * }}} |
|
| 168 | - * |
|
| 169 | - * @param string $type The type of inflection, either 'plural' or 'singular' |
|
| 170 | - * @param array<string,mixed>|iterable<string,mixed> $rules An array of rules to be added. |
|
| 171 | - * @param boolean $reset If true, will unset default inflections for all |
|
| 172 | - * new rules that are being defined in $rules. |
|
| 173 | - * |
|
| 174 | - * @return void |
|
| 175 | - * |
|
| 176 | - * @deprecated |
|
| 177 | - */ |
|
| 178 | - public static function rules(string $type, iterable $rules, bool $reset = false) : void |
|
| 179 | - { |
|
| 180 | - @trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED); |
|
| 155 | + /** |
|
| 156 | + * Adds custom inflection $rules, of either 'plural' or 'singular' $type. |
|
| 157 | + * |
|
| 158 | + * ### Usage: |
|
| 159 | + * |
|
| 160 | + * {{{ |
|
| 161 | + * Inflector::rules('plural', array('/^(inflect)or$/i' => '\1ables')); |
|
| 162 | + * Inflector::rules('plural', array( |
|
| 163 | + * 'rules' => array('/^(inflect)ors$/i' => '\1ables'), |
|
| 164 | + * 'uninflected' => array('dontinflectme'), |
|
| 165 | + * 'irregular' => array('red' => 'redlings') |
|
| 166 | + * )); |
|
| 167 | + * }}} |
|
| 168 | + * |
|
| 169 | + * @param string $type The type of inflection, either 'plural' or 'singular' |
|
| 170 | + * @param array<string,mixed>|iterable<string,mixed> $rules An array of rules to be added. |
|
| 171 | + * @param boolean $reset If true, will unset default inflections for all |
|
| 172 | + * new rules that are being defined in $rules. |
|
| 173 | + * |
|
| 174 | + * @return void |
|
| 175 | + * |
|
| 176 | + * @deprecated |
|
| 177 | + */ |
|
| 178 | + public static function rules(string $type, iterable $rules, bool $reset = false) : void |
|
| 179 | + { |
|
| 180 | + @trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED); |
|
| 181 | 181 | |
| 182 | - if (self::$factory === null) { |
|
| 183 | - self::$factory = self::createFactory(); |
|
| 184 | - } |
|
| 182 | + if (self::$factory === null) { |
|
| 183 | + self::$factory = self::createFactory(); |
|
| 184 | + } |
|
| 185 | 185 | |
| 186 | - self::$instance = null; |
|
| 186 | + self::$instance = null; |
|
| 187 | 187 | |
| 188 | - switch ($type) { |
|
| 189 | - case 'singular': |
|
| 190 | - self::$factory->withSingularRules(self::buildRuleset($rules), $reset); |
|
| 191 | - break; |
|
| 192 | - case 'plural': |
|
| 193 | - self::$factory->withPluralRules(self::buildRuleset($rules), $reset); |
|
| 194 | - break; |
|
| 195 | - default: |
|
| 196 | - throw new InvalidArgumentException(sprintf('Cannot define custom inflection rules for type "%s".', $type)); |
|
| 197 | - } |
|
| 198 | - } |
|
| 188 | + switch ($type) { |
|
| 189 | + case 'singular': |
|
| 190 | + self::$factory->withSingularRules(self::buildRuleset($rules), $reset); |
|
| 191 | + break; |
|
| 192 | + case 'plural': |
|
| 193 | + self::$factory->withPluralRules(self::buildRuleset($rules), $reset); |
|
| 194 | + break; |
|
| 195 | + default: |
|
| 196 | + throw new InvalidArgumentException(sprintf('Cannot define custom inflection rules for type "%s".', $type)); |
|
| 197 | + } |
|
| 198 | + } |
|
| 199 | 199 | |
| 200 | - /** |
|
| 201 | - * @param array<string,mixed>|iterable<string,mixed> $rules An array of rules to be added. |
|
| 202 | - */ |
|
| 203 | - private static function buildRuleset(iterable $rules) : Ruleset |
|
| 204 | - { |
|
| 205 | - $regular = []; |
|
| 206 | - $irregular = []; |
|
| 207 | - $uninflected = []; |
|
| 200 | + /** |
|
| 201 | + * @param array<string,mixed>|iterable<string,mixed> $rules An array of rules to be added. |
|
| 202 | + */ |
|
| 203 | + private static function buildRuleset(iterable $rules) : Ruleset |
|
| 204 | + { |
|
| 205 | + $regular = []; |
|
| 206 | + $irregular = []; |
|
| 207 | + $uninflected = []; |
|
| 208 | 208 | |
| 209 | - foreach ($rules as $rule => $pattern) { |
|
| 210 | - if ( ! is_array($pattern)) { |
|
| 211 | - $regular[$rule] = $pattern; |
|
| 209 | + foreach ($rules as $rule => $pattern) { |
|
| 210 | + if ( ! is_array($pattern)) { |
|
| 211 | + $regular[$rule] = $pattern; |
|
| 212 | 212 | |
| 213 | - continue; |
|
| 214 | - } |
|
| 213 | + continue; |
|
| 214 | + } |
|
| 215 | 215 | |
| 216 | - switch ($rule) { |
|
| 217 | - case 'uninflected': |
|
| 218 | - $uninflected = $pattern; |
|
| 219 | - break; |
|
| 220 | - case 'irregular': |
|
| 221 | - $irregular = $pattern; |
|
| 222 | - break; |
|
| 223 | - case 'rules': |
|
| 224 | - $regular = $pattern; |
|
| 225 | - break; |
|
| 226 | - } |
|
| 227 | - } |
|
| 216 | + switch ($rule) { |
|
| 217 | + case 'uninflected': |
|
| 218 | + $uninflected = $pattern; |
|
| 219 | + break; |
|
| 220 | + case 'irregular': |
|
| 221 | + $irregular = $pattern; |
|
| 222 | + break; |
|
| 223 | + case 'rules': |
|
| 224 | + $regular = $pattern; |
|
| 225 | + break; |
|
| 226 | + } |
|
| 227 | + } |
|
| 228 | 228 | |
| 229 | - return new Ruleset( |
|
| 230 | - new Transformations(...array_map( |
|
| 231 | - static function (string $pattern, string $replacement) : Transformation { |
|
| 232 | - return new Transformation(new Pattern($pattern), $replacement); |
|
| 233 | - }, |
|
| 234 | - array_keys($regular), |
|
| 235 | - array_values($regular) |
|
| 236 | - )), |
|
| 237 | - new Patterns(...array_map( |
|
| 238 | - static function (string $pattern) : Pattern { |
|
| 239 | - return new Pattern($pattern); |
|
| 240 | - }, |
|
| 241 | - $uninflected |
|
| 242 | - )), |
|
| 243 | - new Substitutions(...array_map( |
|
| 244 | - static function (string $word, string $to) : Substitution { |
|
| 245 | - return new Substitution(new Word($word), new Word($to)); |
|
| 246 | - }, |
|
| 247 | - array_keys($irregular), |
|
| 248 | - array_values($irregular) |
|
| 249 | - )) |
|
| 250 | - ); |
|
| 251 | - } |
|
| 229 | + return new Ruleset( |
|
| 230 | + new Transformations(...array_map( |
|
| 231 | + static function (string $pattern, string $replacement) : Transformation { |
|
| 232 | + return new Transformation(new Pattern($pattern), $replacement); |
|
| 233 | + }, |
|
| 234 | + array_keys($regular), |
|
| 235 | + array_values($regular) |
|
| 236 | + )), |
|
| 237 | + new Patterns(...array_map( |
|
| 238 | + static function (string $pattern) : Pattern { |
|
| 239 | + return new Pattern($pattern); |
|
| 240 | + }, |
|
| 241 | + $uninflected |
|
| 242 | + )), |
|
| 243 | + new Substitutions(...array_map( |
|
| 244 | + static function (string $word, string $to) : Substitution { |
|
| 245 | + return new Substitution(new Word($word), new Word($to)); |
|
| 246 | + }, |
|
| 247 | + array_keys($irregular), |
|
| 248 | + array_values($irregular) |
|
| 249 | + )) |
|
| 250 | + ); |
|
| 251 | + } |
|
| 252 | 252 | |
| 253 | - /** |
|
| 254 | - * Returns a word in plural form. |
|
| 255 | - * |
|
| 256 | - * @param string $word The word in singular form. |
|
| 257 | - * |
|
| 258 | - * @return string The word in plural form. |
|
| 259 | - * |
|
| 260 | - * @deprecated |
|
| 261 | - */ |
|
| 262 | - public static function pluralize(string $word) : string |
|
| 263 | - { |
|
| 264 | - @trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED); |
|
| 253 | + /** |
|
| 254 | + * Returns a word in plural form. |
|
| 255 | + * |
|
| 256 | + * @param string $word The word in singular form. |
|
| 257 | + * |
|
| 258 | + * @return string The word in plural form. |
|
| 259 | + * |
|
| 260 | + * @deprecated |
|
| 261 | + */ |
|
| 262 | + public static function pluralize(string $word) : string |
|
| 263 | + { |
|
| 264 | + @trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED); |
|
| 265 | 265 | |
| 266 | - return self::getInstance()->pluralize($word); |
|
| 267 | - } |
|
| 266 | + return self::getInstance()->pluralize($word); |
|
| 267 | + } |
|
| 268 | 268 | |
| 269 | - /** |
|
| 270 | - * Returns a word in singular form. |
|
| 271 | - * |
|
| 272 | - * @param string $word The word in plural form. |
|
| 273 | - * |
|
| 274 | - * @return string The word in singular form. |
|
| 275 | - * |
|
| 276 | - * @deprecated |
|
| 277 | - */ |
|
| 278 | - public static function singularize(string $word) : string |
|
| 279 | - { |
|
| 280 | - @trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED); |
|
| 269 | + /** |
|
| 270 | + * Returns a word in singular form. |
|
| 271 | + * |
|
| 272 | + * @param string $word The word in plural form. |
|
| 273 | + * |
|
| 274 | + * @return string The word in singular form. |
|
| 275 | + * |
|
| 276 | + * @deprecated |
|
| 277 | + */ |
|
| 278 | + public static function singularize(string $word) : string |
|
| 279 | + { |
|
| 280 | + @trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED); |
|
| 281 | 281 | |
| 282 | - return self::getInstance()->singularize($word); |
|
| 283 | - } |
|
| 282 | + return self::getInstance()->singularize($word); |
|
| 283 | + } |
|
| 284 | 284 | } |
@@ -28,424 +28,424 @@ |
||
| 28 | 28 | */ |
| 29 | 29 | class PoolOptimizer |
| 30 | 30 | { |
| 31 | - /** |
|
| 32 | - * @var PolicyInterface |
|
| 33 | - */ |
|
| 34 | - private $policy; |
|
| 35 | - |
|
| 36 | - /** |
|
| 37 | - * @var array<int, true> |
|
| 38 | - */ |
|
| 39 | - private $irremovablePackages = array(); |
|
| 40 | - |
|
| 41 | - /** |
|
| 42 | - * @var array<string, array<string, ConstraintInterface>> |
|
| 43 | - */ |
|
| 44 | - private $requireConstraintsPerPackage = array(); |
|
| 45 | - |
|
| 46 | - /** |
|
| 47 | - * @var array<string, array<string, ConstraintInterface>> |
|
| 48 | - */ |
|
| 49 | - private $conflictConstraintsPerPackage = array(); |
|
| 50 | - |
|
| 51 | - /** |
|
| 52 | - * @var array<int, true> |
|
| 53 | - */ |
|
| 54 | - private $packagesToRemove = array(); |
|
| 55 | - |
|
| 56 | - /** |
|
| 57 | - * @var array<int, BasePackage[]> |
|
| 58 | - */ |
|
| 59 | - private $aliasesPerPackage = array(); |
|
| 60 | - |
|
| 61 | - /** |
|
| 62 | - * @var array<string, array<string, string>> |
|
| 63 | - */ |
|
| 64 | - private $removedVersionsByPackage = array(); |
|
| 65 | - |
|
| 66 | - public function __construct(PolicyInterface $policy) |
|
| 67 | - { |
|
| 68 | - $this->policy = $policy; |
|
| 69 | - } |
|
| 70 | - |
|
| 71 | - /** |
|
| 72 | - * @return Pool |
|
| 73 | - */ |
|
| 74 | - public function optimize(Request $request, Pool $pool) |
|
| 75 | - { |
|
| 76 | - $this->prepare($request, $pool); |
|
| 77 | - |
|
| 78 | - $this->optimizeByIdenticalDependencies($request, $pool); |
|
| 79 | - |
|
| 80 | - $this->optimizeImpossiblePackagesAway($request, $pool); |
|
| 81 | - |
|
| 82 | - $optimizedPool = $this->applyRemovalsToPool($pool); |
|
| 83 | - |
|
| 84 | - // No need to run this recursively at the moment |
|
| 85 | - // because the current optimizations cannot provide |
|
| 86 | - // even more gains when ran again. Might change |
|
| 87 | - // in the future with additional optimizations. |
|
| 88 | - |
|
| 89 | - $this->irremovablePackages = array(); |
|
| 90 | - $this->requireConstraintsPerPackage = array(); |
|
| 91 | - $this->conflictConstraintsPerPackage = array(); |
|
| 92 | - $this->packagesToRemove = array(); |
|
| 93 | - $this->aliasesPerPackage = array(); |
|
| 94 | - $this->removedVersionsByPackage = array(); |
|
| 95 | - |
|
| 96 | - return $optimizedPool; |
|
| 97 | - } |
|
| 98 | - |
|
| 99 | - /** |
|
| 100 | - * @return void |
|
| 101 | - */ |
|
| 102 | - private function prepare(Request $request, Pool $pool) |
|
| 103 | - { |
|
| 104 | - $irremovablePackageConstraintGroups = array(); |
|
| 105 | - |
|
| 106 | - // Mark fixed or locked packages as irremovable |
|
| 107 | - foreach ($request->getFixedOrLockedPackages() as $package) { |
|
| 108 | - $irremovablePackageConstraintGroups[$package->getName()][] = new Constraint('==', $package->getVersion()); |
|
| 109 | - } |
|
| 110 | - |
|
| 111 | - // Extract requested package requirements |
|
| 112 | - foreach ($request->getRequires() as $require => $constraint) { |
|
| 113 | - $constraint = Intervals::compactConstraint($constraint); |
|
| 114 | - $this->requireConstraintsPerPackage[$require][(string) $constraint] = $constraint; |
|
| 115 | - } |
|
| 116 | - |
|
| 117 | - // First pass over all packages to extract information and mark package constraints irremovable |
|
| 118 | - foreach ($pool->getPackages() as $package) { |
|
| 119 | - // Extract package requirements |
|
| 120 | - foreach ($package->getRequires() as $link) { |
|
| 121 | - $constraint = Intervals::compactConstraint($link->getConstraint()); |
|
| 122 | - $this->requireConstraintsPerPackage[$link->getTarget()][(string) $constraint] = $constraint; |
|
| 123 | - } |
|
| 124 | - // Extract package conflicts |
|
| 125 | - foreach ($package->getConflicts() as $link) { |
|
| 126 | - $constraint = Intervals::compactConstraint($link->getConstraint()); |
|
| 127 | - $this->conflictConstraintsPerPackage[$link->getTarget()][(string) $constraint] = $constraint; |
|
| 128 | - } |
|
| 129 | - |
|
| 130 | - // Keep track of alias packages for every package so if either the alias or aliased is kept |
|
| 131 | - // we keep the others as they are a unit of packages really |
|
| 132 | - if ($package instanceof AliasPackage) { |
|
| 133 | - $this->aliasesPerPackage[$package->getAliasOf()->id][] = $package; |
|
| 134 | - } |
|
| 135 | - } |
|
| 136 | - |
|
| 137 | - $irremovablePackageConstraints = array(); |
|
| 138 | - foreach ($irremovablePackageConstraintGroups as $packageName => $constraints) { |
|
| 139 | - $irremovablePackageConstraints[$packageName] = 1 === \count($constraints) ? $constraints[0] : new MultiConstraint($constraints, false); |
|
| 140 | - } |
|
| 141 | - unset($irremovablePackageConstraintGroups); |
|
| 142 | - |
|
| 143 | - // Mark the packages as irremovable based on the constraints |
|
| 144 | - foreach ($pool->getPackages() as $package) { |
|
| 145 | - if (!isset($irremovablePackageConstraints[$package->getName()])) { |
|
| 146 | - continue; |
|
| 147 | - } |
|
| 148 | - |
|
| 149 | - if (CompilingMatcher::match($irremovablePackageConstraints[$package->getName()], Constraint::OP_EQ, $package->getVersion())) { |
|
| 150 | - $this->markPackageIrremovable($package); |
|
| 151 | - } |
|
| 152 | - } |
|
| 153 | - } |
|
| 154 | - |
|
| 155 | - /** |
|
| 156 | - * @return void |
|
| 157 | - */ |
|
| 158 | - private function markPackageIrremovable(BasePackage $package) |
|
| 159 | - { |
|
| 160 | - $this->irremovablePackages[$package->id] = true; |
|
| 161 | - if ($package instanceof AliasPackage) { |
|
| 162 | - // recursing here so aliasesPerPackage for the aliasOf can be checked |
|
| 163 | - // and all its aliases marked as irremovable as well |
|
| 164 | - $this->markPackageIrremovable($package->getAliasOf()); |
|
| 165 | - } |
|
| 166 | - if (isset($this->aliasesPerPackage[$package->id])) { |
|
| 167 | - foreach ($this->aliasesPerPackage[$package->id] as $aliasPackage) { |
|
| 168 | - $this->irremovablePackages[$aliasPackage->id] = true; |
|
| 169 | - } |
|
| 170 | - } |
|
| 171 | - } |
|
| 172 | - |
|
| 173 | - /** |
|
| 174 | - * @return Pool Optimized pool |
|
| 175 | - */ |
|
| 176 | - private function applyRemovalsToPool(Pool $pool) |
|
| 177 | - { |
|
| 178 | - $packages = array(); |
|
| 179 | - $removedVersions = array(); |
|
| 180 | - foreach ($pool->getPackages() as $package) { |
|
| 181 | - if (!isset($this->packagesToRemove[$package->id])) { |
|
| 182 | - $packages[] = $package; |
|
| 183 | - } else { |
|
| 184 | - $removedVersions[$package->getName()][$package->getVersion()] = $package->getPrettyVersion(); |
|
| 185 | - } |
|
| 186 | - } |
|
| 187 | - |
|
| 188 | - $optimizedPool = new Pool($packages, $pool->getUnacceptableFixedOrLockedPackages(), $removedVersions, $this->removedVersionsByPackage); |
|
| 189 | - |
|
| 190 | - return $optimizedPool; |
|
| 191 | - } |
|
| 192 | - |
|
| 193 | - /** |
|
| 194 | - * @return void |
|
| 195 | - */ |
|
| 196 | - private function optimizeByIdenticalDependencies(Request $request, Pool $pool) |
|
| 197 | - { |
|
| 198 | - $identicalDefinitionsPerPackage = array(); |
|
| 199 | - $packageIdenticalDefinitionLookup = array(); |
|
| 200 | - |
|
| 201 | - foreach ($pool->getPackages() as $package) { |
|
| 202 | - |
|
| 203 | - // If that package was already marked irremovable, we can skip |
|
| 204 | - // the entire process for it |
|
| 205 | - if (isset($this->irremovablePackages[$package->id])) { |
|
| 206 | - continue; |
|
| 207 | - } |
|
| 208 | - |
|
| 209 | - $this->markPackageForRemoval($package->id); |
|
| 210 | - |
|
| 211 | - $dependencyHash = $this->calculateDependencyHash($package); |
|
| 212 | - |
|
| 213 | - foreach ($package->getNames(false) as $packageName) { |
|
| 214 | - |
|
| 215 | - if (!isset($this->requireConstraintsPerPackage[$packageName])) { |
|
| 216 | - continue; |
|
| 217 | - } |
|
| 218 | - |
|
| 219 | - foreach ($this->requireConstraintsPerPackage[$packageName] as $requireConstraint) { |
|
| 220 | - $groupHashParts = array(); |
|
| 221 | - |
|
| 222 | - if (CompilingMatcher::match($requireConstraint, Constraint::OP_EQ, $package->getVersion())) { |
|
| 223 | - $groupHashParts[] = 'require:' . (string) $requireConstraint; |
|
| 224 | - } |
|
| 225 | - |
|
| 226 | - if ($package->getReplaces()) { |
|
| 227 | - foreach ($package->getReplaces() as $link) { |
|
| 228 | - if (CompilingMatcher::match($link->getConstraint(), Constraint::OP_EQ, $package->getVersion())) { |
|
| 229 | - // Use the same hash part as the regular require hash because that's what the replacement does |
|
| 230 | - $groupHashParts[] = 'require:' . (string) $link->getConstraint(); |
|
| 231 | - } |
|
| 232 | - } |
|
| 233 | - } |
|
| 234 | - |
|
| 235 | - if (isset($this->conflictConstraintsPerPackage[$packageName])) { |
|
| 236 | - foreach ($this->conflictConstraintsPerPackage[$packageName] as $conflictConstraint) { |
|
| 237 | - if (CompilingMatcher::match($conflictConstraint, Constraint::OP_EQ, $package->getVersion())) { |
|
| 238 | - $groupHashParts[] = 'conflict:' . (string) $conflictConstraint; |
|
| 239 | - } |
|
| 240 | - } |
|
| 241 | - } |
|
| 242 | - |
|
| 243 | - if (!$groupHashParts) { |
|
| 244 | - continue; |
|
| 245 | - } |
|
| 246 | - |
|
| 247 | - $groupHash = implode('', $groupHashParts); |
|
| 248 | - $identicalDefinitionsPerPackage[$packageName][$groupHash][$dependencyHash][] = $package; |
|
| 249 | - $packageIdenticalDefinitionLookup[$package->id][$packageName] = array('groupHash' => $groupHash, 'dependencyHash' => $dependencyHash); |
|
| 250 | - } |
|
| 251 | - } |
|
| 252 | - } |
|
| 253 | - |
|
| 254 | - foreach ($identicalDefinitionsPerPackage as $constraintGroups) { |
|
| 255 | - foreach ($constraintGroups as $constraintGroup) { |
|
| 256 | - foreach ($constraintGroup as $packages) { |
|
| 257 | - // Only one package in this constraint group has the same requirements, we're not allowed to remove that package |
|
| 258 | - if (1 === \count($packages)) { |
|
| 259 | - $this->keepPackage($packages[0], $identicalDefinitionsPerPackage, $packageIdenticalDefinitionLookup); |
|
| 260 | - continue; |
|
| 261 | - } |
|
| 262 | - |
|
| 263 | - // Otherwise we find out which one is the preferred package in this constraint group which is |
|
| 264 | - // then not allowed to be removed either |
|
| 265 | - $literals = array(); |
|
| 266 | - |
|
| 267 | - foreach ($packages as $package) { |
|
| 268 | - $literals[] = $package->id; |
|
| 269 | - } |
|
| 270 | - |
|
| 271 | - foreach ($this->policy->selectPreferredPackages($pool, $literals) as $preferredLiteral) { |
|
| 272 | - $this->keepPackage($pool->literalToPackage($preferredLiteral), $identicalDefinitionsPerPackage, $packageIdenticalDefinitionLookup); |
|
| 273 | - } |
|
| 274 | - } |
|
| 275 | - } |
|
| 276 | - } |
|
| 277 | - } |
|
| 278 | - |
|
| 279 | - /** |
|
| 280 | - * @return string |
|
| 281 | - */ |
|
| 282 | - private function calculateDependencyHash(BasePackage $package) |
|
| 283 | - { |
|
| 284 | - $hash = ''; |
|
| 285 | - |
|
| 286 | - $hashRelevantLinks = array( |
|
| 287 | - 'requires' => $package->getRequires(), |
|
| 288 | - 'conflicts' => $package->getConflicts(), |
|
| 289 | - 'replaces' => $package->getReplaces(), |
|
| 290 | - 'provides' => $package->getProvides() |
|
| 291 | - ); |
|
| 292 | - |
|
| 293 | - foreach ($hashRelevantLinks as $key => $links) { |
|
| 294 | - if (0 === \count($links)) { |
|
| 295 | - continue; |
|
| 296 | - } |
|
| 297 | - |
|
| 298 | - // start new hash section |
|
| 299 | - $hash .= $key . ':'; |
|
| 300 | - |
|
| 301 | - $subhash = array(); |
|
| 302 | - |
|
| 303 | - foreach ($links as $link) { |
|
| 304 | - // To get the best dependency hash matches we should use Intervals::compactConstraint() here. |
|
| 305 | - // However, the majority of projects are going to specify their constraints already pretty |
|
| 306 | - // much in the best variant possible. In other words, we'd be wasting time here and it would actually hurt |
|
| 307 | - // performance more than the additional few packages that could be filtered out would benefit the process. |
|
| 308 | - $subhash[$link->getTarget()] = (string) $link->getConstraint(); |
|
| 309 | - } |
|
| 310 | - |
|
| 311 | - // Sort for best result |
|
| 312 | - ksort($subhash); |
|
| 313 | - |
|
| 314 | - foreach ($subhash as $target => $constraint) { |
|
| 315 | - $hash .= $target . '@' . $constraint; |
|
| 316 | - } |
|
| 317 | - } |
|
| 318 | - |
|
| 319 | - return $hash; |
|
| 320 | - } |
|
| 321 | - |
|
| 322 | - /** |
|
| 323 | - * @param int $id |
|
| 324 | - * @return void |
|
| 325 | - */ |
|
| 326 | - private function markPackageForRemoval($id) |
|
| 327 | - { |
|
| 328 | - // We are not allowed to remove packages if they have been marked as irremovable |
|
| 329 | - if (isset($this->irremovablePackages[$id])) { |
|
| 330 | - throw new \LogicException('Attempted removing a package which was previously marked irremovable'); |
|
| 331 | - } |
|
| 332 | - |
|
| 333 | - $this->packagesToRemove[$id] = true; |
|
| 334 | - } |
|
| 335 | - |
|
| 336 | - /** |
|
| 337 | - * @param array<string, array<string, array<string, list<BasePackage>>>> $identicalDefinitionsPerPackage |
|
| 338 | - * @param array<int, array<string, array{groupHash: string, dependencyHash: string}>> $packageIdenticalDefinitionLookup |
|
| 339 | - * @return void |
|
| 340 | - */ |
|
| 341 | - private function keepPackage(BasePackage $package, $identicalDefinitionsPerPackage, $packageIdenticalDefinitionLookup) |
|
| 342 | - { |
|
| 343 | - unset($this->packagesToRemove[$package->id]); |
|
| 344 | - |
|
| 345 | - if ($package instanceof AliasPackage) { |
|
| 346 | - // recursing here so aliasesPerPackage for the aliasOf can be checked |
|
| 347 | - // and all its aliases marked to be kept as well |
|
| 348 | - $this->keepPackage($package->getAliasOf(), $identicalDefinitionsPerPackage, $packageIdenticalDefinitionLookup); |
|
| 349 | - } |
|
| 350 | - |
|
| 351 | - // record all the versions of the package group so we can list them later in Problem output |
|
| 352 | - foreach ($package->getNames(false) as $name) { |
|
| 353 | - if (isset($packageIdenticalDefinitionLookup[$package->id][$name])) { |
|
| 354 | - $packageGroupPointers = $packageIdenticalDefinitionLookup[$package->id][$name]; |
|
| 355 | - $packageGroup = $identicalDefinitionsPerPackage[$name][$packageGroupPointers['groupHash']][$packageGroupPointers['dependencyHash']]; |
|
| 356 | - foreach ($packageGroup as $pkg) { |
|
| 357 | - if ($pkg instanceof AliasPackage && $pkg->getPrettyVersion() === VersionParser::DEFAULT_BRANCH_ALIAS) { |
|
| 358 | - $pkg = $pkg->getAliasOf(); |
|
| 359 | - } |
|
| 360 | - $this->removedVersionsByPackage[spl_object_hash($package)][$pkg->getVersion()] = $pkg->getPrettyVersion(); |
|
| 361 | - } |
|
| 362 | - } |
|
| 363 | - } |
|
| 364 | - |
|
| 365 | - if (isset($this->aliasesPerPackage[$package->id])) { |
|
| 366 | - foreach ($this->aliasesPerPackage[$package->id] as $aliasPackage) { |
|
| 367 | - unset($this->packagesToRemove[$aliasPackage->id]); |
|
| 368 | - |
|
| 369 | - // record all the versions of the package group so we can list them later in Problem output |
|
| 370 | - foreach ($aliasPackage->getNames(false) as $name) { |
|
| 371 | - if (isset($packageIdenticalDefinitionLookup[$aliasPackage->id][$name])) { |
|
| 372 | - $packageGroupPointers = $packageIdenticalDefinitionLookup[$aliasPackage->id][$name]; |
|
| 373 | - $packageGroup = $identicalDefinitionsPerPackage[$name][$packageGroupPointers['groupHash']][$packageGroupPointers['dependencyHash']]; |
|
| 374 | - foreach ($packageGroup as $pkg) { |
|
| 375 | - if ($pkg instanceof AliasPackage && $pkg->getPrettyVersion() === VersionParser::DEFAULT_BRANCH_ALIAS) { |
|
| 376 | - $pkg = $pkg->getAliasOf(); |
|
| 377 | - } |
|
| 378 | - $this->removedVersionsByPackage[spl_object_hash($aliasPackage)][$pkg->getVersion()] = $pkg->getPrettyVersion(); |
|
| 379 | - } |
|
| 380 | - } |
|
| 381 | - } |
|
| 382 | - } |
|
| 383 | - } |
|
| 384 | - } |
|
| 385 | - |
|
| 386 | - /** |
|
| 387 | - * Use the list of locked packages to constrain the loaded packages |
|
| 388 | - * This will reduce packages with significant numbers of historical versions to a smaller number |
|
| 389 | - * and reduce the resulting rule set that is generated |
|
| 390 | - * |
|
| 391 | - * @return void |
|
| 392 | - */ |
|
| 393 | - private function optimizeImpossiblePackagesAway(Request $request, Pool $pool) |
|
| 394 | - { |
|
| 395 | - if (count($request->getLockedPackages()) === 0) { |
|
| 396 | - return; |
|
| 397 | - } |
|
| 398 | - |
|
| 399 | - $packageIndex = array(); |
|
| 400 | - |
|
| 401 | - foreach ($pool->getPackages() as $package) { |
|
| 402 | - $id = $package->id; |
|
| 403 | - |
|
| 404 | - // Do not remove irremovable packages |
|
| 405 | - if (isset($this->irremovablePackages[$id])) { |
|
| 406 | - continue; |
|
| 407 | - } |
|
| 408 | - // Do not remove a package aliased by another package, nor aliases |
|
| 409 | - if (isset($this->aliasesPerPackage[$id]) || $package instanceof AliasPackage) { |
|
| 410 | - continue; |
|
| 411 | - } |
|
| 412 | - // Do not remove locked packages |
|
| 413 | - if ($request->isFixedPackage($package) || $request->isLockedPackage($package)) { |
|
| 414 | - continue; |
|
| 415 | - } |
|
| 416 | - |
|
| 417 | - $packageIndex[$package->getName()][$package->id] = $package; |
|
| 418 | - } |
|
| 419 | - |
|
| 420 | - foreach ($request->getLockedPackages() as $package) { |
|
| 421 | - // If this locked package is no longer required by root or anything in the pool, it may get uninstalled so do not apply its requirements |
|
| 422 | - // In a case where a requirement WERE to appear in the pool by a package that would not be used, it would've been unlocked and so not filtered still |
|
| 423 | - $isUnusedPackage = true; |
|
| 424 | - foreach ($package->getNames(false) as $packageName) { |
|
| 425 | - if (isset($this->requireConstraintsPerPackage[$packageName])) { |
|
| 426 | - $isUnusedPackage = false; |
|
| 427 | - break; |
|
| 428 | - } |
|
| 429 | - } |
|
| 430 | - |
|
| 431 | - if ($isUnusedPackage) { |
|
| 432 | - continue; |
|
| 433 | - } |
|
| 434 | - |
|
| 435 | - foreach ($package->getRequires() as $link) { |
|
| 436 | - $require = $link->getTarget(); |
|
| 437 | - if (!isset($packageIndex[$require])) { |
|
| 438 | - continue; |
|
| 439 | - } |
|
| 440 | - |
|
| 441 | - $linkConstraint = $link->getConstraint(); |
|
| 442 | - foreach ($packageIndex[$require] as $id => $requiredPkg) { |
|
| 443 | - if (false === CompilingMatcher::match($linkConstraint, Constraint::OP_EQ, $requiredPkg->getVersion())) { |
|
| 444 | - $this->markPackageForRemoval($id); |
|
| 445 | - unset($packageIndex[$require][$id]); |
|
| 446 | - } |
|
| 447 | - } |
|
| 448 | - } |
|
| 449 | - } |
|
| 450 | - } |
|
| 31 | + /** |
|
| 32 | + * @var PolicyInterface |
|
| 33 | + */ |
|
| 34 | + private $policy; |
|
| 35 | + |
|
| 36 | + /** |
|
| 37 | + * @var array<int, true> |
|
| 38 | + */ |
|
| 39 | + private $irremovablePackages = array(); |
|
| 40 | + |
|
| 41 | + /** |
|
| 42 | + * @var array<string, array<string, ConstraintInterface>> |
|
| 43 | + */ |
|
| 44 | + private $requireConstraintsPerPackage = array(); |
|
| 45 | + |
|
| 46 | + /** |
|
| 47 | + * @var array<string, array<string, ConstraintInterface>> |
|
| 48 | + */ |
|
| 49 | + private $conflictConstraintsPerPackage = array(); |
|
| 50 | + |
|
| 51 | + /** |
|
| 52 | + * @var array<int, true> |
|
| 53 | + */ |
|
| 54 | + private $packagesToRemove = array(); |
|
| 55 | + |
|
| 56 | + /** |
|
| 57 | + * @var array<int, BasePackage[]> |
|
| 58 | + */ |
|
| 59 | + private $aliasesPerPackage = array(); |
|
| 60 | + |
|
| 61 | + /** |
|
| 62 | + * @var array<string, array<string, string>> |
|
| 63 | + */ |
|
| 64 | + private $removedVersionsByPackage = array(); |
|
| 65 | + |
|
| 66 | + public function __construct(PolicyInterface $policy) |
|
| 67 | + { |
|
| 68 | + $this->policy = $policy; |
|
| 69 | + } |
|
| 70 | + |
|
| 71 | + /** |
|
| 72 | + * @return Pool |
|
| 73 | + */ |
|
| 74 | + public function optimize(Request $request, Pool $pool) |
|
| 75 | + { |
|
| 76 | + $this->prepare($request, $pool); |
|
| 77 | + |
|
| 78 | + $this->optimizeByIdenticalDependencies($request, $pool); |
|
| 79 | + |
|
| 80 | + $this->optimizeImpossiblePackagesAway($request, $pool); |
|
| 81 | + |
|
| 82 | + $optimizedPool = $this->applyRemovalsToPool($pool); |
|
| 83 | + |
|
| 84 | + // No need to run this recursively at the moment |
|
| 85 | + // because the current optimizations cannot provide |
|
| 86 | + // even more gains when ran again. Might change |
|
| 87 | + // in the future with additional optimizations. |
|
| 88 | + |
|
| 89 | + $this->irremovablePackages = array(); |
|
| 90 | + $this->requireConstraintsPerPackage = array(); |
|
| 91 | + $this->conflictConstraintsPerPackage = array(); |
|
| 92 | + $this->packagesToRemove = array(); |
|
| 93 | + $this->aliasesPerPackage = array(); |
|
| 94 | + $this->removedVersionsByPackage = array(); |
|
| 95 | + |
|
| 96 | + return $optimizedPool; |
|
| 97 | + } |
|
| 98 | + |
|
| 99 | + /** |
|
| 100 | + * @return void |
|
| 101 | + */ |
|
| 102 | + private function prepare(Request $request, Pool $pool) |
|
| 103 | + { |
|
| 104 | + $irremovablePackageConstraintGroups = array(); |
|
| 105 | + |
|
| 106 | + // Mark fixed or locked packages as irremovable |
|
| 107 | + foreach ($request->getFixedOrLockedPackages() as $package) { |
|
| 108 | + $irremovablePackageConstraintGroups[$package->getName()][] = new Constraint('==', $package->getVersion()); |
|
| 109 | + } |
|
| 110 | + |
|
| 111 | + // Extract requested package requirements |
|
| 112 | + foreach ($request->getRequires() as $require => $constraint) { |
|
| 113 | + $constraint = Intervals::compactConstraint($constraint); |
|
| 114 | + $this->requireConstraintsPerPackage[$require][(string) $constraint] = $constraint; |
|
| 115 | + } |
|
| 116 | + |
|
| 117 | + // First pass over all packages to extract information and mark package constraints irremovable |
|
| 118 | + foreach ($pool->getPackages() as $package) { |
|
| 119 | + // Extract package requirements |
|
| 120 | + foreach ($package->getRequires() as $link) { |
|
| 121 | + $constraint = Intervals::compactConstraint($link->getConstraint()); |
|
| 122 | + $this->requireConstraintsPerPackage[$link->getTarget()][(string) $constraint] = $constraint; |
|
| 123 | + } |
|
| 124 | + // Extract package conflicts |
|
| 125 | + foreach ($package->getConflicts() as $link) { |
|
| 126 | + $constraint = Intervals::compactConstraint($link->getConstraint()); |
|
| 127 | + $this->conflictConstraintsPerPackage[$link->getTarget()][(string) $constraint] = $constraint; |
|
| 128 | + } |
|
| 129 | + |
|
| 130 | + // Keep track of alias packages for every package so if either the alias or aliased is kept |
|
| 131 | + // we keep the others as they are a unit of packages really |
|
| 132 | + if ($package instanceof AliasPackage) { |
|
| 133 | + $this->aliasesPerPackage[$package->getAliasOf()->id][] = $package; |
|
| 134 | + } |
|
| 135 | + } |
|
| 136 | + |
|
| 137 | + $irremovablePackageConstraints = array(); |
|
| 138 | + foreach ($irremovablePackageConstraintGroups as $packageName => $constraints) { |
|
| 139 | + $irremovablePackageConstraints[$packageName] = 1 === \count($constraints) ? $constraints[0] : new MultiConstraint($constraints, false); |
|
| 140 | + } |
|
| 141 | + unset($irremovablePackageConstraintGroups); |
|
| 142 | + |
|
| 143 | + // Mark the packages as irremovable based on the constraints |
|
| 144 | + foreach ($pool->getPackages() as $package) { |
|
| 145 | + if (!isset($irremovablePackageConstraints[$package->getName()])) { |
|
| 146 | + continue; |
|
| 147 | + } |
|
| 148 | + |
|
| 149 | + if (CompilingMatcher::match($irremovablePackageConstraints[$package->getName()], Constraint::OP_EQ, $package->getVersion())) { |
|
| 150 | + $this->markPackageIrremovable($package); |
|
| 151 | + } |
|
| 152 | + } |
|
| 153 | + } |
|
| 154 | + |
|
| 155 | + /** |
|
| 156 | + * @return void |
|
| 157 | + */ |
|
| 158 | + private function markPackageIrremovable(BasePackage $package) |
|
| 159 | + { |
|
| 160 | + $this->irremovablePackages[$package->id] = true; |
|
| 161 | + if ($package instanceof AliasPackage) { |
|
| 162 | + // recursing here so aliasesPerPackage for the aliasOf can be checked |
|
| 163 | + // and all its aliases marked as irremovable as well |
|
| 164 | + $this->markPackageIrremovable($package->getAliasOf()); |
|
| 165 | + } |
|
| 166 | + if (isset($this->aliasesPerPackage[$package->id])) { |
|
| 167 | + foreach ($this->aliasesPerPackage[$package->id] as $aliasPackage) { |
|
| 168 | + $this->irremovablePackages[$aliasPackage->id] = true; |
|
| 169 | + } |
|
| 170 | + } |
|
| 171 | + } |
|
| 172 | + |
|
| 173 | + /** |
|
| 174 | + * @return Pool Optimized pool |
|
| 175 | + */ |
|
| 176 | + private function applyRemovalsToPool(Pool $pool) |
|
| 177 | + { |
|
| 178 | + $packages = array(); |
|
| 179 | + $removedVersions = array(); |
|
| 180 | + foreach ($pool->getPackages() as $package) { |
|
| 181 | + if (!isset($this->packagesToRemove[$package->id])) { |
|
| 182 | + $packages[] = $package; |
|
| 183 | + } else { |
|
| 184 | + $removedVersions[$package->getName()][$package->getVersion()] = $package->getPrettyVersion(); |
|
| 185 | + } |
|
| 186 | + } |
|
| 187 | + |
|
| 188 | + $optimizedPool = new Pool($packages, $pool->getUnacceptableFixedOrLockedPackages(), $removedVersions, $this->removedVersionsByPackage); |
|
| 189 | + |
|
| 190 | + return $optimizedPool; |
|
| 191 | + } |
|
| 192 | + |
|
| 193 | + /** |
|
| 194 | + * @return void |
|
| 195 | + */ |
|
| 196 | + private function optimizeByIdenticalDependencies(Request $request, Pool $pool) |
|
| 197 | + { |
|
| 198 | + $identicalDefinitionsPerPackage = array(); |
|
| 199 | + $packageIdenticalDefinitionLookup = array(); |
|
| 200 | + |
|
| 201 | + foreach ($pool->getPackages() as $package) { |
|
| 202 | + |
|
| 203 | + // If that package was already marked irremovable, we can skip |
|
| 204 | + // the entire process for it |
|
| 205 | + if (isset($this->irremovablePackages[$package->id])) { |
|
| 206 | + continue; |
|
| 207 | + } |
|
| 208 | + |
|
| 209 | + $this->markPackageForRemoval($package->id); |
|
| 210 | + |
|
| 211 | + $dependencyHash = $this->calculateDependencyHash($package); |
|
| 212 | + |
|
| 213 | + foreach ($package->getNames(false) as $packageName) { |
|
| 214 | + |
|
| 215 | + if (!isset($this->requireConstraintsPerPackage[$packageName])) { |
|
| 216 | + continue; |
|
| 217 | + } |
|
| 218 | + |
|
| 219 | + foreach ($this->requireConstraintsPerPackage[$packageName] as $requireConstraint) { |
|
| 220 | + $groupHashParts = array(); |
|
| 221 | + |
|
| 222 | + if (CompilingMatcher::match($requireConstraint, Constraint::OP_EQ, $package->getVersion())) { |
|
| 223 | + $groupHashParts[] = 'require:' . (string) $requireConstraint; |
|
| 224 | + } |
|
| 225 | + |
|
| 226 | + if ($package->getReplaces()) { |
|
| 227 | + foreach ($package->getReplaces() as $link) { |
|
| 228 | + if (CompilingMatcher::match($link->getConstraint(), Constraint::OP_EQ, $package->getVersion())) { |
|
| 229 | + // Use the same hash part as the regular require hash because that's what the replacement does |
|
| 230 | + $groupHashParts[] = 'require:' . (string) $link->getConstraint(); |
|
| 231 | + } |
|
| 232 | + } |
|
| 233 | + } |
|
| 234 | + |
|
| 235 | + if (isset($this->conflictConstraintsPerPackage[$packageName])) { |
|
| 236 | + foreach ($this->conflictConstraintsPerPackage[$packageName] as $conflictConstraint) { |
|
| 237 | + if (CompilingMatcher::match($conflictConstraint, Constraint::OP_EQ, $package->getVersion())) { |
|
| 238 | + $groupHashParts[] = 'conflict:' . (string) $conflictConstraint; |
|
| 239 | + } |
|
| 240 | + } |
|
| 241 | + } |
|
| 242 | + |
|
| 243 | + if (!$groupHashParts) { |
|
| 244 | + continue; |
|
| 245 | + } |
|
| 246 | + |
|
| 247 | + $groupHash = implode('', $groupHashParts); |
|
| 248 | + $identicalDefinitionsPerPackage[$packageName][$groupHash][$dependencyHash][] = $package; |
|
| 249 | + $packageIdenticalDefinitionLookup[$package->id][$packageName] = array('groupHash' => $groupHash, 'dependencyHash' => $dependencyHash); |
|
| 250 | + } |
|
| 251 | + } |
|
| 252 | + } |
|
| 253 | + |
|
| 254 | + foreach ($identicalDefinitionsPerPackage as $constraintGroups) { |
|
| 255 | + foreach ($constraintGroups as $constraintGroup) { |
|
| 256 | + foreach ($constraintGroup as $packages) { |
|
| 257 | + // Only one package in this constraint group has the same requirements, we're not allowed to remove that package |
|
| 258 | + if (1 === \count($packages)) { |
|
| 259 | + $this->keepPackage($packages[0], $identicalDefinitionsPerPackage, $packageIdenticalDefinitionLookup); |
|
| 260 | + continue; |
|
| 261 | + } |
|
| 262 | + |
|
| 263 | + // Otherwise we find out which one is the preferred package in this constraint group which is |
|
| 264 | + // then not allowed to be removed either |
|
| 265 | + $literals = array(); |
|
| 266 | + |
|
| 267 | + foreach ($packages as $package) { |
|
| 268 | + $literals[] = $package->id; |
|
| 269 | + } |
|
| 270 | + |
|
| 271 | + foreach ($this->policy->selectPreferredPackages($pool, $literals) as $preferredLiteral) { |
|
| 272 | + $this->keepPackage($pool->literalToPackage($preferredLiteral), $identicalDefinitionsPerPackage, $packageIdenticalDefinitionLookup); |
|
| 273 | + } |
|
| 274 | + } |
|
| 275 | + } |
|
| 276 | + } |
|
| 277 | + } |
|
| 278 | + |
|
| 279 | + /** |
|
| 280 | + * @return string |
|
| 281 | + */ |
|
| 282 | + private function calculateDependencyHash(BasePackage $package) |
|
| 283 | + { |
|
| 284 | + $hash = ''; |
|
| 285 | + |
|
| 286 | + $hashRelevantLinks = array( |
|
| 287 | + 'requires' => $package->getRequires(), |
|
| 288 | + 'conflicts' => $package->getConflicts(), |
|
| 289 | + 'replaces' => $package->getReplaces(), |
|
| 290 | + 'provides' => $package->getProvides() |
|
| 291 | + ); |
|
| 292 | + |
|
| 293 | + foreach ($hashRelevantLinks as $key => $links) { |
|
| 294 | + if (0 === \count($links)) { |
|
| 295 | + continue; |
|
| 296 | + } |
|
| 297 | + |
|
| 298 | + // start new hash section |
|
| 299 | + $hash .= $key . ':'; |
|
| 300 | + |
|
| 301 | + $subhash = array(); |
|
| 302 | + |
|
| 303 | + foreach ($links as $link) { |
|
| 304 | + // To get the best dependency hash matches we should use Intervals::compactConstraint() here. |
|
| 305 | + // However, the majority of projects are going to specify their constraints already pretty |
|
| 306 | + // much in the best variant possible. In other words, we'd be wasting time here and it would actually hurt |
|
| 307 | + // performance more than the additional few packages that could be filtered out would benefit the process. |
|
| 308 | + $subhash[$link->getTarget()] = (string) $link->getConstraint(); |
|
| 309 | + } |
|
| 310 | + |
|
| 311 | + // Sort for best result |
|
| 312 | + ksort($subhash); |
|
| 313 | + |
|
| 314 | + foreach ($subhash as $target => $constraint) { |
|
| 315 | + $hash .= $target . '@' . $constraint; |
|
| 316 | + } |
|
| 317 | + } |
|
| 318 | + |
|
| 319 | + return $hash; |
|
| 320 | + } |
|
| 321 | + |
|
| 322 | + /** |
|
| 323 | + * @param int $id |
|
| 324 | + * @return void |
|
| 325 | + */ |
|
| 326 | + private function markPackageForRemoval($id) |
|
| 327 | + { |
|
| 328 | + // We are not allowed to remove packages if they have been marked as irremovable |
|
| 329 | + if (isset($this->irremovablePackages[$id])) { |
|
| 330 | + throw new \LogicException('Attempted removing a package which was previously marked irremovable'); |
|
| 331 | + } |
|
| 332 | + |
|
| 333 | + $this->packagesToRemove[$id] = true; |
|
| 334 | + } |
|
| 335 | + |
|
| 336 | + /** |
|
| 337 | + * @param array<string, array<string, array<string, list<BasePackage>>>> $identicalDefinitionsPerPackage |
|
| 338 | + * @param array<int, array<string, array{groupHash: string, dependencyHash: string}>> $packageIdenticalDefinitionLookup |
|
| 339 | + * @return void |
|
| 340 | + */ |
|
| 341 | + private function keepPackage(BasePackage $package, $identicalDefinitionsPerPackage, $packageIdenticalDefinitionLookup) |
|
| 342 | + { |
|
| 343 | + unset($this->packagesToRemove[$package->id]); |
|
| 344 | + |
|
| 345 | + if ($package instanceof AliasPackage) { |
|
| 346 | + // recursing here so aliasesPerPackage for the aliasOf can be checked |
|
| 347 | + // and all its aliases marked to be kept as well |
|
| 348 | + $this->keepPackage($package->getAliasOf(), $identicalDefinitionsPerPackage, $packageIdenticalDefinitionLookup); |
|
| 349 | + } |
|
| 350 | + |
|
| 351 | + // record all the versions of the package group so we can list them later in Problem output |
|
| 352 | + foreach ($package->getNames(false) as $name) { |
|
| 353 | + if (isset($packageIdenticalDefinitionLookup[$package->id][$name])) { |
|
| 354 | + $packageGroupPointers = $packageIdenticalDefinitionLookup[$package->id][$name]; |
|
| 355 | + $packageGroup = $identicalDefinitionsPerPackage[$name][$packageGroupPointers['groupHash']][$packageGroupPointers['dependencyHash']]; |
|
| 356 | + foreach ($packageGroup as $pkg) { |
|
| 357 | + if ($pkg instanceof AliasPackage && $pkg->getPrettyVersion() === VersionParser::DEFAULT_BRANCH_ALIAS) { |
|
| 358 | + $pkg = $pkg->getAliasOf(); |
|
| 359 | + } |
|
| 360 | + $this->removedVersionsByPackage[spl_object_hash($package)][$pkg->getVersion()] = $pkg->getPrettyVersion(); |
|
| 361 | + } |
|
| 362 | + } |
|
| 363 | + } |
|
| 364 | + |
|
| 365 | + if (isset($this->aliasesPerPackage[$package->id])) { |
|
| 366 | + foreach ($this->aliasesPerPackage[$package->id] as $aliasPackage) { |
|
| 367 | + unset($this->packagesToRemove[$aliasPackage->id]); |
|
| 368 | + |
|
| 369 | + // record all the versions of the package group so we can list them later in Problem output |
|
| 370 | + foreach ($aliasPackage->getNames(false) as $name) { |
|
| 371 | + if (isset($packageIdenticalDefinitionLookup[$aliasPackage->id][$name])) { |
|
| 372 | + $packageGroupPointers = $packageIdenticalDefinitionLookup[$aliasPackage->id][$name]; |
|
| 373 | + $packageGroup = $identicalDefinitionsPerPackage[$name][$packageGroupPointers['groupHash']][$packageGroupPointers['dependencyHash']]; |
|
| 374 | + foreach ($packageGroup as $pkg) { |
|
| 375 | + if ($pkg instanceof AliasPackage && $pkg->getPrettyVersion() === VersionParser::DEFAULT_BRANCH_ALIAS) { |
|
| 376 | + $pkg = $pkg->getAliasOf(); |
|
| 377 | + } |
|
| 378 | + $this->removedVersionsByPackage[spl_object_hash($aliasPackage)][$pkg->getVersion()] = $pkg->getPrettyVersion(); |
|
| 379 | + } |
|
| 380 | + } |
|
| 381 | + } |
|
| 382 | + } |
|
| 383 | + } |
|
| 384 | + } |
|
| 385 | + |
|
| 386 | + /** |
|
| 387 | + * Use the list of locked packages to constrain the loaded packages |
|
| 388 | + * This will reduce packages with significant numbers of historical versions to a smaller number |
|
| 389 | + * and reduce the resulting rule set that is generated |
|
| 390 | + * |
|
| 391 | + * @return void |
|
| 392 | + */ |
|
| 393 | + private function optimizeImpossiblePackagesAway(Request $request, Pool $pool) |
|
| 394 | + { |
|
| 395 | + if (count($request->getLockedPackages()) === 0) { |
|
| 396 | + return; |
|
| 397 | + } |
|
| 398 | + |
|
| 399 | + $packageIndex = array(); |
|
| 400 | + |
|
| 401 | + foreach ($pool->getPackages() as $package) { |
|
| 402 | + $id = $package->id; |
|
| 403 | + |
|
| 404 | + // Do not remove irremovable packages |
|
| 405 | + if (isset($this->irremovablePackages[$id])) { |
|
| 406 | + continue; |
|
| 407 | + } |
|
| 408 | + // Do not remove a package aliased by another package, nor aliases |
|
| 409 | + if (isset($this->aliasesPerPackage[$id]) || $package instanceof AliasPackage) { |
|
| 410 | + continue; |
|
| 411 | + } |
|
| 412 | + // Do not remove locked packages |
|
| 413 | + if ($request->isFixedPackage($package) || $request->isLockedPackage($package)) { |
|
| 414 | + continue; |
|
| 415 | + } |
|
| 416 | + |
|
| 417 | + $packageIndex[$package->getName()][$package->id] = $package; |
|
| 418 | + } |
|
| 419 | + |
|
| 420 | + foreach ($request->getLockedPackages() as $package) { |
|
| 421 | + // If this locked package is no longer required by root or anything in the pool, it may get uninstalled so do not apply its requirements |
|
| 422 | + // In a case where a requirement WERE to appear in the pool by a package that would not be used, it would've been unlocked and so not filtered still |
|
| 423 | + $isUnusedPackage = true; |
|
| 424 | + foreach ($package->getNames(false) as $packageName) { |
|
| 425 | + if (isset($this->requireConstraintsPerPackage[$packageName])) { |
|
| 426 | + $isUnusedPackage = false; |
|
| 427 | + break; |
|
| 428 | + } |
|
| 429 | + } |
|
| 430 | + |
|
| 431 | + if ($isUnusedPackage) { |
|
| 432 | + continue; |
|
| 433 | + } |
|
| 434 | + |
|
| 435 | + foreach ($package->getRequires() as $link) { |
|
| 436 | + $require = $link->getTarget(); |
|
| 437 | + if (!isset($packageIndex[$require])) { |
|
| 438 | + continue; |
|
| 439 | + } |
|
| 440 | + |
|
| 441 | + $linkConstraint = $link->getConstraint(); |
|
| 442 | + foreach ($packageIndex[$require] as $id => $requiredPkg) { |
|
| 443 | + if (false === CompilingMatcher::match($linkConstraint, Constraint::OP_EQ, $requiredPkg->getVersion())) { |
|
| 444 | + $this->markPackageForRemoval($id); |
|
| 445 | + unset($packageIndex[$require][$id]); |
|
| 446 | + } |
|
| 447 | + } |
|
| 448 | + } |
|
| 449 | + } |
|
| 450 | + } |
|
| 451 | 451 | } |
@@ -4,9 +4,9 @@ |
||
| 4 | 4 | |
| 5 | 5 | interface PlatformRequirementFilterInterface |
| 6 | 6 | { |
| 7 | - /** |
|
| 8 | - * @param string $req |
|
| 9 | - * @return bool |
|
| 10 | - */ |
|
| 11 | - public function isIgnored($req); |
|
| 7 | + /** |
|
| 8 | + * @param string $req |
|
| 9 | + * @return bool |
|
| 10 | + */ |
|
| 11 | + public function isIgnored($req); |
|
| 12 | 12 | } |
@@ -4,12 +4,12 @@ |
||
| 4 | 4 | |
| 5 | 5 | final class IgnoreNothingPlatformRequirementFilter implements PlatformRequirementFilterInterface |
| 6 | 6 | { |
| 7 | - /** |
|
| 8 | - * @param string $req |
|
| 9 | - * @return false |
|
| 10 | - */ |
|
| 11 | - public function isIgnored($req) |
|
| 12 | - { |
|
| 13 | - return false; |
|
| 14 | - } |
|
| 7 | + /** |
|
| 8 | + * @param string $req |
|
| 9 | + * @return false |
|
| 10 | + */ |
|
| 11 | + public function isIgnored($req) |
|
| 12 | + { |
|
| 13 | + return false; |
|
| 14 | + } |
|
| 15 | 15 | } |
@@ -6,12 +6,12 @@ |
||
| 6 | 6 | |
| 7 | 7 | final class IgnoreAllPlatformRequirementFilter implements PlatformRequirementFilterInterface |
| 8 | 8 | { |
| 9 | - /** |
|
| 10 | - * @param string $req |
|
| 11 | - * @return bool |
|
| 12 | - */ |
|
| 13 | - public function isIgnored($req) |
|
| 14 | - { |
|
| 15 | - return PlatformRepository::isPlatformPackage($req); |
|
| 16 | - } |
|
| 9 | + /** |
|
| 10 | + * @param string $req |
|
| 11 | + * @return bool |
|
| 12 | + */ |
|
| 13 | + public function isIgnored($req) |
|
| 14 | + { |
|
| 15 | + return PlatformRepository::isPlatformPackage($req); |
|
| 16 | + } |
|
| 17 | 17 | } |
@@ -4,42 +4,42 @@ |
||
| 4 | 4 | |
| 5 | 5 | final class PlatformRequirementFilterFactory |
| 6 | 6 | { |
| 7 | - /** |
|
| 8 | - * @param mixed $boolOrList |
|
| 9 | - * |
|
| 10 | - * @return PlatformRequirementFilterInterface |
|
| 11 | - */ |
|
| 12 | - public static function fromBoolOrList($boolOrList) |
|
| 13 | - { |
|
| 14 | - if (is_bool($boolOrList)) { |
|
| 15 | - return $boolOrList ? self::ignoreAll() : self::ignoreNothing(); |
|
| 16 | - } |
|
| 7 | + /** |
|
| 8 | + * @param mixed $boolOrList |
|
| 9 | + * |
|
| 10 | + * @return PlatformRequirementFilterInterface |
|
| 11 | + */ |
|
| 12 | + public static function fromBoolOrList($boolOrList) |
|
| 13 | + { |
|
| 14 | + if (is_bool($boolOrList)) { |
|
| 15 | + return $boolOrList ? self::ignoreAll() : self::ignoreNothing(); |
|
| 16 | + } |
|
| 17 | 17 | |
| 18 | - if (is_array($boolOrList)) { |
|
| 19 | - return new IgnoreListPlatformRequirementFilter($boolOrList); |
|
| 20 | - } |
|
| 18 | + if (is_array($boolOrList)) { |
|
| 19 | + return new IgnoreListPlatformRequirementFilter($boolOrList); |
|
| 20 | + } |
|
| 21 | 21 | |
| 22 | - throw new \InvalidArgumentException( |
|
| 23 | - sprintf( |
|
| 24 | - 'PlatformRequirementFilter: Unknown $boolOrList parameter %s. Please report at https://github.com/composer/composer/issues/new.', |
|
| 25 | - gettype($boolOrList) |
|
| 26 | - ) |
|
| 27 | - ); |
|
| 28 | - } |
|
| 22 | + throw new \InvalidArgumentException( |
|
| 23 | + sprintf( |
|
| 24 | + 'PlatformRequirementFilter: Unknown $boolOrList parameter %s. Please report at https://github.com/composer/composer/issues/new.', |
|
| 25 | + gettype($boolOrList) |
|
| 26 | + ) |
|
| 27 | + ); |
|
| 28 | + } |
|
| 29 | 29 | |
| 30 | - /** |
|
| 31 | - * @return PlatformRequirementFilterInterface |
|
| 32 | - */ |
|
| 33 | - public static function ignoreAll() |
|
| 34 | - { |
|
| 35 | - return new IgnoreAllPlatformRequirementFilter(); |
|
| 36 | - } |
|
| 30 | + /** |
|
| 31 | + * @return PlatformRequirementFilterInterface |
|
| 32 | + */ |
|
| 33 | + public static function ignoreAll() |
|
| 34 | + { |
|
| 35 | + return new IgnoreAllPlatformRequirementFilter(); |
|
| 36 | + } |
|
| 37 | 37 | |
| 38 | - /** |
|
| 39 | - * @return PlatformRequirementFilterInterface |
|
| 40 | - */ |
|
| 41 | - public static function ignoreNothing() |
|
| 42 | - { |
|
| 43 | - return new IgnoreNothingPlatformRequirementFilter(); |
|
| 44 | - } |
|
| 38 | + /** |
|
| 39 | + * @return PlatformRequirementFilterInterface |
|
| 40 | + */ |
|
| 41 | + public static function ignoreNothing() |
|
| 42 | + { |
|
| 43 | + return new IgnoreNothingPlatformRequirementFilter(); |
|
| 44 | + } |
|
| 45 | 45 | } |
@@ -14,70 +14,70 @@ |
||
| 14 | 14 | |
| 15 | 15 | final class IgnoreListPlatformRequirementFilter implements PlatformRequirementFilterInterface |
| 16 | 16 | { |
| 17 | - /** |
|
| 18 | - * @var string |
|
| 19 | - */ |
|
| 20 | - private $ignoreRegex; |
|
| 17 | + /** |
|
| 18 | + * @var string |
|
| 19 | + */ |
|
| 20 | + private $ignoreRegex; |
|
| 21 | 21 | |
| 22 | - /** |
|
| 23 | - * @var string |
|
| 24 | - */ |
|
| 25 | - private $ignoreUpperBoundRegex; |
|
| 22 | + /** |
|
| 23 | + * @var string |
|
| 24 | + */ |
|
| 25 | + private $ignoreUpperBoundRegex; |
|
| 26 | 26 | |
| 27 | - /** |
|
| 28 | - * @param string[] $reqList |
|
| 29 | - */ |
|
| 30 | - public function __construct(array $reqList) |
|
| 31 | - { |
|
| 32 | - $ignoreAll = $ignoreUpperBound = array(); |
|
| 33 | - foreach ($reqList as $req) { |
|
| 34 | - if (substr($req, -1) === '+') { |
|
| 35 | - $ignoreUpperBound[] = substr($req, 0, -1); |
|
| 36 | - } else { |
|
| 37 | - $ignoreAll[] = $req; |
|
| 38 | - } |
|
| 39 | - } |
|
| 40 | - $this->ignoreRegex = BasePackage::packageNamesToRegexp($ignoreAll); |
|
| 41 | - $this->ignoreUpperBoundRegex = BasePackage::packageNamesToRegexp($ignoreUpperBound); |
|
| 42 | - } |
|
| 27 | + /** |
|
| 28 | + * @param string[] $reqList |
|
| 29 | + */ |
|
| 30 | + public function __construct(array $reqList) |
|
| 31 | + { |
|
| 32 | + $ignoreAll = $ignoreUpperBound = array(); |
|
| 33 | + foreach ($reqList as $req) { |
|
| 34 | + if (substr($req, -1) === '+') { |
|
| 35 | + $ignoreUpperBound[] = substr($req, 0, -1); |
|
| 36 | + } else { |
|
| 37 | + $ignoreAll[] = $req; |
|
| 38 | + } |
|
| 39 | + } |
|
| 40 | + $this->ignoreRegex = BasePackage::packageNamesToRegexp($ignoreAll); |
|
| 41 | + $this->ignoreUpperBoundRegex = BasePackage::packageNamesToRegexp($ignoreUpperBound); |
|
| 42 | + } |
|
| 43 | 43 | |
| 44 | - /** |
|
| 45 | - * @param string $req |
|
| 46 | - * @return bool |
|
| 47 | - */ |
|
| 48 | - public function isIgnored($req) |
|
| 49 | - { |
|
| 50 | - if (!PlatformRepository::isPlatformPackage($req)) { |
|
| 51 | - return false; |
|
| 52 | - } |
|
| 44 | + /** |
|
| 45 | + * @param string $req |
|
| 46 | + * @return bool |
|
| 47 | + */ |
|
| 48 | + public function isIgnored($req) |
|
| 49 | + { |
|
| 50 | + if (!PlatformRepository::isPlatformPackage($req)) { |
|
| 51 | + return false; |
|
| 52 | + } |
|
| 53 | 53 | |
| 54 | - return Preg::isMatch($this->ignoreRegex, $req); |
|
| 55 | - } |
|
| 54 | + return Preg::isMatch($this->ignoreRegex, $req); |
|
| 55 | + } |
|
| 56 | 56 | |
| 57 | - /** |
|
| 58 | - * @param string $req |
|
| 59 | - * @return ConstraintInterface |
|
| 60 | - */ |
|
| 61 | - public function filterConstraint($req, ConstraintInterface $constraint) |
|
| 62 | - { |
|
| 63 | - if (!PlatformRepository::isPlatformPackage($req)) { |
|
| 64 | - return $constraint; |
|
| 65 | - } |
|
| 57 | + /** |
|
| 58 | + * @param string $req |
|
| 59 | + * @return ConstraintInterface |
|
| 60 | + */ |
|
| 61 | + public function filterConstraint($req, ConstraintInterface $constraint) |
|
| 62 | + { |
|
| 63 | + if (!PlatformRepository::isPlatformPackage($req)) { |
|
| 64 | + return $constraint; |
|
| 65 | + } |
|
| 66 | 66 | |
| 67 | - if (!Preg::isMatch($this->ignoreUpperBoundRegex, $req)) { |
|
| 68 | - return $constraint; |
|
| 69 | - } |
|
| 67 | + if (!Preg::isMatch($this->ignoreUpperBoundRegex, $req)) { |
|
| 68 | + return $constraint; |
|
| 69 | + } |
|
| 70 | 70 | |
| 71 | - if (Preg::isMatch($this->ignoreRegex, $req)) { |
|
| 72 | - return new MatchAllConstraint; |
|
| 73 | - } |
|
| 71 | + if (Preg::isMatch($this->ignoreRegex, $req)) { |
|
| 72 | + return new MatchAllConstraint; |
|
| 73 | + } |
|
| 74 | 74 | |
| 75 | - $intervals = Intervals::get($constraint); |
|
| 76 | - $last = end($intervals['numeric']); |
|
| 77 | - if ($last !== false && (string) $last->getEnd() !== (string) Interval::untilPositiveInfinity()) { |
|
| 78 | - $constraint = new MultiConstraint(array($constraint, new Constraint('>=', $last->getEnd()->getVersion())), false); |
|
| 79 | - } |
|
| 75 | + $intervals = Intervals::get($constraint); |
|
| 76 | + $last = end($intervals['numeric']); |
|
| 77 | + if ($last !== false && (string) $last->getEnd() !== (string) Interval::untilPositiveInfinity()) { |
|
| 78 | + $constraint = new MultiConstraint(array($constraint, new Constraint('>=', $last->getEnd()->getVersion())), false); |
|
| 79 | + } |
|
| 80 | 80 | |
| 81 | - return $constraint; |
|
| 82 | - } |
|
| 81 | + return $constraint; |
|
| 82 | + } |
|
| 83 | 83 | } |