1<?php 2 3/* 4 * This file is part of the Fxp Composer Asset Plugin package. 5 * 6 * (c) François Pluchino <francois.pluchino@gmail.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Fxp\Composer\AssetPlugin\Converter; 13 14/** 15 * Converter for Semver syntax version to composer syntax version. 16 * 17 * @author François Pluchino <francois.pluchino@gmail.com> 18 */ 19class SemverConverter implements VersionConverterInterface 20{ 21 /** 22 * {@inheritdoc} 23 */ 24 public function convertVersion($version) 25 { 26 if (\in_array($version, array(null, '', 'latest'), true)) { 27 return ('latest' === $version ? 'default || ' : '').'*'; 28 } 29 30 $version = str_replace('–', '-', $version); 31 $prefix = preg_match('/^[a-z]/', $version) && 0 !== strpos($version, 'dev-') ? substr($version, 0, 1) : ''; 32 $version = substr($version, \strlen($prefix)); 33 $version = SemverUtil::convertVersionMetadata($version); 34 $version = SemverUtil::convertDateVersion($version); 35 36 return $prefix.$version; 37 } 38 39 /** 40 * {@inheritdoc} 41 */ 42 public function convertRange($range) 43 { 44 $range = $this->cleanRange(strtolower($range)); 45 46 return $this->matchRange($range); 47 } 48 49 /** 50 * Clean the raw range. 51 * 52 * @param string $range 53 * 54 * @return string 55 */ 56 protected function cleanRange($range) 57 { 58 foreach (array('<', '>', '=', '~', '^', '||', '&&') as $character) { 59 $range = str_replace($character.' ', $character, $range); 60 } 61 62 $range = preg_replace('/(?:[vV])(\d+)/', '${1}', $range); 63 $range = str_replace(' ||', '||', $range); 64 65 return str_replace(array(' &&', '&&'), ',', $range); 66 } 67 68 /** 69 * Match the range. 70 * 71 * @param string $range The range cleaned 72 * 73 * @return string The range 74 */ 75 protected function matchRange($range) 76 { 77 $pattern = '/(\ -\ )|(<)|(>)|(=)|(\|\|)|(\ )|(,)|(\~)|(\^)/'; 78 $matches = preg_split($pattern, $range, -1, PREG_SPLIT_DELIM_CAPTURE); 79 $special = null; 80 $replace = null; 81 $first = true; 82 83 foreach ($matches as $i => $match) { 84 if ($first && '' !== $match) { 85 $first = false; 86 $match = '=' === $match ? 'EQUAL' : $match; 87 } 88 89 $this->matchRangeToken($i, $match, $matches, $special, $replace); 90 } 91 92 return implode('', $matches); 93 } 94 95 /** 96 * Converts the token of the matched range. 97 * 98 * @param int $i 99 * @param string $match 100 * @param array $matches 101 * @param null|string $special 102 * @param null|string $replace 103 */ 104 protected function matchRangeToken($i, $match, array &$matches, &$special, &$replace) 105 { 106 if (' - ' === $match) { 107 $matches[$i - 1] = '>='.str_replace(array('*', 'x', 'X'), '0', $matches[$i - 1]); 108 109 if (false !== strpos($matches[$i + 1], '.') && false === strpos($matches[$i + 1], '*') 110 && false === strpos($matches[$i + 1], 'x') && false === strpos($matches[$i + 1], 'X')) { 111 $matches[$i] = ',<='; 112 } else { 113 $matches[$i] = ',<'; 114 $special = ',<~'; 115 } 116 } else { 117 $this->matchRangeTokenStep2($i, $match, $matches, $special, $replace); 118 } 119 } 120 121 /** 122 * Step2: Converts the token of the matched range. 123 * 124 * @param int $i 125 * @param string $match 126 * @param array $matches 127 * @param null|string $special 128 * @param null|string $replace 129 */ 130 protected function matchRangeTokenStep2($i, $match, array &$matches, &$special, &$replace) 131 { 132 if (\in_array($match, array('', '<', '>', '=', ','), true)) { 133 $replace = \in_array($match, array('<', '>'), true) ? $match : $replace; 134 $matches[$i] = '~' === $special && \in_array($replace, array('<', '>'), true) ? '' : $matches[$i]; 135 } elseif ('~' === $match) { 136 $special = $match; 137 } elseif (\in_array($match, array('EQUAL', '^'), true)) { 138 $special = $match; 139 $matches[$i] = ''; 140 } else { 141 $this->matchRangeTokenStep3($i, $match, $matches, $special, $replace); 142 } 143 } 144 145 /** 146 * Step3: Converts the token of the matched range. 147 * 148 * @param int $i 149 * @param string $match 150 * @param array $matches 151 * @param null|string $special 152 * @param null|string $replace 153 */ 154 protected function matchRangeTokenStep3($i, $match, array &$matches, &$special, &$replace) 155 { 156 if (' ' === $match) { 157 $matches[$i] = ','; 158 } elseif ('||' === $match) { 159 $matches[$i] = '|'; 160 } elseif (\in_array($special, array('^'), true)) { 161 $matches[$i] = SemverRangeUtil::replaceSpecialRange($this, $match); 162 $special = null; 163 } else { 164 $this->matchRangeTokenStep4($i, $match, $matches, $special, $replace); 165 } 166 } 167 168 /** 169 * Step4: Converts the token of the matched range. 170 * 171 * @param int $i 172 * @param string $match 173 * @param array $matches 174 * @param null|string $special 175 * @param null|string $replace 176 */ 177 protected function matchRangeTokenStep4($i, $match, array &$matches, &$special, &$replace) 178 { 179 if (',<~' === $special) { 180 // Version range contains x in last place. 181 $match .= (false === strpos($match, '.') ? '.x' : ''); 182 $version = explode('.', $match); 183 $change = \count($version) - 2; 184 $version[$change] = (int) ($version[$change]) + 1; 185 $match = str_replace(array('*', 'x', 'X'), '0', implode('.', $version)); 186 } elseif (null === $special && 0 === $i && false === strpos($match, '.') && is_numeric($match)) { 187 $match = isset($matches[$i + 1]) && (' - ' === $matches[$i + 1] || '-' === $matches[$i + 1]) 188 ? $match 189 : '~'.$match; 190 } else { 191 $match = '~' === $special ? str_replace(array('*', 'x', 'X'), '0', $match) : $match; 192 } 193 194 $this->matchRangeTokenStep5($i, $match, $matches, $special, $replace); 195 } 196 197 /** 198 * Step5: Converts the token of the matched range. 199 * 200 * @param int $i 201 * @param string $match 202 * @param array $matches 203 * @param null|string $special 204 * @param null|string $replace 205 */ 206 protected function matchRangeTokenStep5($i, $match, array &$matches, &$special, &$replace) 207 { 208 $matches[$i] = $this->convertVersion($match); 209 $matches[$i] = $replace 210 ? SemverUtil::replaceAlias($matches[$i], $replace) 211 : $matches[$i]; 212 $matches[$i] .= '~' === $special && \in_array($replace, array('<', '>'), true) 213 ? ','.$replace.$matches[$i] 214 : ''; 215 $special = null; 216 $replace = null; 217 } 218} 219