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 14use Composer\Config; 15use Composer\IO\NullIO; 16use Composer\Repository\Vcs\VcsDriverInterface; 17use Fxp\Composer\AssetPlugin\Assets; 18use Fxp\Composer\AssetPlugin\Exception\InvalidArgumentException; 19use Fxp\Composer\AssetPlugin\Type\AssetTypeInterface; 20use Fxp\Composer\AssetPlugin\Util\Validator; 21 22/** 23 * Utils for package converter. 24 * 25 * @author François Pluchino <francois.pluchino@gmail.com> 26 */ 27abstract class PackageUtil 28{ 29 /** 30 * @var string[] 31 */ 32 private static $extensions = array( 33 '.zip', 34 '.tar', 35 '.tar.gz', 36 '.tar.bz2', 37 '.tar.Z', 38 '.tar.xz', 39 '.bz2', 40 '.gz', 41 ); 42 43 /** 44 * Checks if the version is a URL version. 45 * 46 * @param AssetTypeInterface $assetType The asset type 47 * @param string $dependency The dependency 48 * @param string $version The version 49 * @param array $vcsRepos The list of new vcs configs 50 * @param array $composer The partial composer data 51 * 52 * @return string[] The new dependency and the new version 53 */ 54 public static function checkUrlVersion(AssetTypeInterface $assetType, $dependency, $version, array &$vcsRepos, array $composer) 55 { 56 if (preg_match('/(\:\/\/)|\@/', $version)) { 57 list($url, $version) = static::splitUrlVersion($version); 58 59 if (!static::isUrlArchive($url) && static::hasUrlDependencySupported($url)) { 60 $vcsRepos[] = array( 61 'type' => sprintf('%s-vcs', $assetType->getName()), 62 'url' => $url, 63 'name' => $assetType->formatComposerName($dependency), 64 ); 65 } else { 66 $dependency = static::getUrlFileDependencyName($assetType, $composer, $dependency); 67 $vcsRepos[] = array( 68 'type' => 'package', 69 'package' => array( 70 'name' => $assetType->formatComposerName($dependency), 71 'type' => $assetType->getComposerType(), 72 'version' => static::getUrlFileDependencyVersion($assetType, $url, $version), 73 'dist' => array( 74 'url' => $url, 75 'type' => 'file', 76 ), 77 ), 78 ); 79 } 80 } 81 82 return array($dependency, $version); 83 } 84 85 /** 86 * Check if the url is a url of a archive file. 87 * 88 * @param string $url The url 89 * 90 * @return bool 91 */ 92 public static function isUrlArchive($url) 93 { 94 if (0 === strpos($url, 'http')) { 95 foreach (self::$extensions as $extension) { 96 if (substr($url, -\strlen($extension)) === $extension) { 97 return true; 98 } 99 } 100 } 101 102 return false; 103 } 104 105 /** 106 * Checks if the version is a alias version. 107 * 108 * @param AssetTypeInterface $assetType The asset type 109 * @param string $dependency The dependency 110 * @param string $version The version 111 * 112 * @return string[] The new dependency and the new version 113 */ 114 public static function checkAliasVersion(AssetTypeInterface $assetType, $dependency, $version) 115 { 116 $pos = strpos($version, '#'); 117 118 if ($pos > 0 && !preg_match('{[0-9a-f]{40}$}', $version)) { 119 $dependency = substr($version, 0, $pos); 120 $version = substr($version, $pos); 121 $searchVerion = substr($version, 1); 122 123 if (false === strpos($version, '*') && Validator::validateTag($searchVerion, $assetType)) { 124 $dependency .= '-'.str_replace('#', '', $version); 125 } 126 } 127 128 return array($dependency, $version); 129 } 130 131 /** 132 * Convert the dependency version. 133 * 134 * @param AssetTypeInterface $assetType The asset type 135 * @param string $dependency The dependency 136 * @param string $version The version 137 * 138 * @return string[] The new dependency and the new version 139 */ 140 public static function convertDependencyVersion(AssetTypeInterface $assetType, $dependency, $version) 141 { 142 $containsHash = false !== strpos($version, '#'); 143 $version = str_replace('#', '', $version); 144 $version = empty($version) ? '*' : trim($version); 145 $searchVersion = str_replace(array(' ', '<', '>', '=', '^', '~'), '', $version); 146 147 // sha version or branch version 148 // sha size: 4-40. See https://git-scm.com/book/tr/v2/Git-Tools-Revision-Selection#_short_sha_1 149 if ($containsHash && preg_match('{^[0-9a-f]{4,40}$}', $version)) { 150 $version = 'dev-default#'.$version; 151 } elseif ('*' !== $version && !Validator::validateTag($searchVersion, $assetType) && !static::depIsRange($version)) { 152 $version = static::convertBrachVersion($assetType, $version); 153 } 154 155 return array($dependency, $version); 156 } 157 158 /** 159 * Converts the simple key of package. 160 * 161 * @param array $asset The asset data 162 * @param string $assetKey The asset key 163 * @param array $composer The composer data 164 * @param string $composerKey The composer key 165 */ 166 public static function convertStringKey(array $asset, $assetKey, array &$composer, $composerKey) 167 { 168 if (isset($asset[$assetKey])) { 169 $composer[$composerKey] = $asset[$assetKey]; 170 } 171 } 172 173 /** 174 * Converts the simple key of package. 175 * 176 * @param array $asset The asset data 177 * @param string $assetKey The asset key 178 * @param array $composer The composer data 179 * @param array $composerKey The array with composer key name and closure 180 * 181 * @throws InvalidArgumentException When the 'composerKey' argument of asset packager converter is not an string or an array with the composer key and closure 182 */ 183 public static function convertArrayKey(array $asset, $assetKey, array &$composer, $composerKey) 184 { 185 if (2 !== \count($composerKey) 186 || !\is_string($composerKey[0]) || !$composerKey[1] instanceof \Closure) { 187 throw new InvalidArgumentException('The "composerKey" argument of asset packager converter must be an string or an array with the composer key and closure'); 188 } 189 190 $closure = $composerKey[1]; 191 $composerKey = $composerKey[0]; 192 $data = isset($asset[$assetKey]) ? $asset[$assetKey] : null; 193 $previousData = isset($composer[$composerKey]) ? $composer[$composerKey] : null; 194 $data = $closure($data, $previousData); 195 196 if (null !== $data) { 197 $composer[$composerKey] = $data; 198 } 199 } 200 201 /** 202 * Split the URL and version. 203 * 204 * @param string $version The url and version (in the same string) 205 * 206 * @return string[] The url and version 207 */ 208 protected static function splitUrlVersion($version) 209 { 210 $pos = strpos($version, '#'); 211 212 // number version or empty version 213 if (false !== $pos) { 214 $url = substr($version, 0, $pos); 215 $version = substr($version, $pos); 216 } else { 217 $url = $version; 218 $version = '#'; 219 } 220 221 return array($url, $version); 222 } 223 224 /** 225 * Get the name of url file dependency. 226 * 227 * @param AssetTypeInterface $assetType The asset type 228 * @param array $composer The partial composer 229 * @param string $dependency The dependency name 230 * 231 * @return string The dependency name 232 */ 233 protected static function getUrlFileDependencyName(AssetTypeInterface $assetType, array $composer, $dependency) 234 { 235 $prefix = isset($composer['name']) 236 ? substr($composer['name'], \strlen($assetType->getComposerVendorName()) + 1).'-' 237 : ''; 238 239 return $prefix.$dependency.'-file'; 240 } 241 242 /** 243 * Get the version of url file dependency. 244 * 245 * @param AssetTypeInterface $assetType The asset type 246 * @param string $url The url 247 * @param string $version The version 248 * 249 * @return string The version 250 */ 251 protected static function getUrlFileDependencyVersion(AssetTypeInterface $assetType, $url, $version) 252 { 253 if ('#' !== $version) { 254 return substr($version, 1); 255 } 256 257 if (preg_match('/(\d+)(\.\d+)(\.\d+)?(\.\d+)?/', $url, $match)) { 258 return $assetType->getVersionConverter()->convertVersion($match[0]); 259 } 260 261 return '0.0.0.0'; 262 } 263 264 /** 265 * Check if url is supported by vcs drivers. 266 * 267 * @param string $url The url 268 * 269 * @return bool 270 */ 271 protected static function hasUrlDependencySupported($url) 272 { 273 $io = new NullIO(); 274 $config = new Config(); 275 276 /** @var VcsDriverInterface $driver */ 277 foreach (Assets::getVcsDrivers() as $driver) { 278 $supported = $driver::supports($io, $config, $url); 279 280 if ($supported) { 281 return true; 282 } 283 } 284 285 return false; 286 } 287 288 /** 289 * Check if the version of dependency is a range version. 290 * 291 * @param string $version 292 * 293 * @return bool 294 */ 295 protected static function depIsRange($version) 296 { 297 $version = trim($version); 298 299 return (bool) preg_match('/[\<\>\=\^\~\ ]/', $version); 300 } 301 302 /** 303 * Convert the dependency branch version. 304 * 305 * @param AssetTypeInterface $assetType The asset type 306 * @param string $version The version 307 * 308 * @return string 309 */ 310 protected static function convertBrachVersion(AssetTypeInterface $assetType, $version) 311 { 312 $oldVersion = $version; 313 $version = 'dev-'.$assetType->getVersionConverter()->convertVersion($version); 314 315 if (!Validator::validateBranch($oldVersion)) { 316 $version .= ' || '.$oldVersion; 317 } 318 319 return $version; 320 } 321} 322