| Total Complexity | 98 | 
| Total Lines | 898 | 
| Duplicated Lines | 0 % | 
| Changes | 0 | ||
Complex classes like AssetManager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use AssetManager, and based on these observations, apply Extract Interface, too.
| 1 | <?php  | 
            ||
| 30 | class AssetManager  | 
            ||
| 31 | { | 
            ||
| 32 | /**  | 
            ||
| 33 | * @var callable a PHP callback that is called after a sub-directory or file is successfully copied. This option is  | 
            ||
| 34 | * used only when publishing a directory. The signature of the callback is the same as for  | 
            ||
| 35 |      *               {@see {beforeCopy}. | 
            ||
| 36 |      *               This is passed as a parameter `afterCopy` to {@see \Yiisoft\Files\FileHelper::copyDirectory()}. | 
            ||
| 37 | */  | 
            ||
| 38 | private $afterCopy;  | 
            ||
| 39 | |||
| 40 | /**  | 
            ||
| 41 | * Aliases component.  | 
            ||
| 42 | *  | 
            ||
| 43 | * @var Aliases $aliase  | 
            ||
| 44 | */  | 
            ||
| 45 | private $aliases;  | 
            ||
| 46 | |||
| 47 | /**  | 
            ||
| 48 | * directory path node_modules.  | 
            ||
| 49 | *  | 
            ||
| 50 | * @var array $alternatives  | 
            ||
| 51 | */  | 
            ||
| 52 | private $alternatives = [  | 
            ||
| 53 | '@npm' => '@root/node_modules',  | 
            ||
| 54 | ];  | 
            ||
| 55 | |||
| 56 | /**  | 
            ||
| 57 | * @var bool whether to append a timestamp to the URL of every published asset. When this is true, the URL of a  | 
            ||
| 58 | * published asset may look like `/path/to/asset?v=timestamp`, where `timestamp` is the last modification  | 
            ||
| 59 | * time of the published asset file. You normally would want to set this property to true when you have  | 
            ||
| 60 | * enabled HTTP caching for assets, because it allows you to bust caching when the assets are updated.  | 
            ||
| 61 | */  | 
            ||
| 62 | private $appendTimestamp = false;  | 
            ||
| 63 | |||
| 64 | /**  | 
            ||
| 65 | * @var array mapping from source asset files (keys) to target asset files (values).  | 
            ||
| 66 | *  | 
            ||
| 67 | * This property is provided to support fixing incorrect asset file paths in some asset bundles. When an asset  | 
            ||
| 68 |      * bundle is registered with a view, each relative asset file in its {@see AssetBundle::css|css} and | 
            ||
| 69 |      * {@see AssetBundle::js|js} arrays will be examined against this map. If any of the keys is found to be the last | 
            ||
| 70 |      * part of an asset file (which is prefixed with {@see {AssetBundle::sourcePath} if available), the corresponding | 
            ||
| 71 | * value will replace the asset and be registered with the view. For example, an asset file `my/path/to/jquery.js`  | 
            ||
| 72 | * matches a key `jquery.js`.  | 
            ||
| 73 | *  | 
            ||
| 74 | * Note that the target asset files should be absolute URLs, domain relative URLs (starting from '/') or paths  | 
            ||
| 75 |      * relative to {@see baseUrl} and {@see basePath}. | 
            ||
| 76 | *  | 
            ||
| 77 | * In the following example, any assets ending with `jquery.min.js` will be replaced with `jquery/dist/jquery.js`  | 
            ||
| 78 |      * which is relative to {@see baseUrl} and {@see basePath}. | 
            ||
| 79 | *  | 
            ||
| 80 | * ```php  | 
            ||
| 81 | * [  | 
            ||
| 82 | * 'jquery.min.js' => 'jquery/dist/jquery.js',  | 
            ||
| 83 | * ]  | 
            ||
| 84 | * ```  | 
            ||
| 85 | *  | 
            ||
| 86 | * You may also use aliases while specifying map value, for example:  | 
            ||
| 87 | *  | 
            ||
| 88 | * ```php  | 
            ||
| 89 | * [  | 
            ||
| 90 | * 'jquery.min.js' => '@web/js/jquery/jquery.js',  | 
            ||
| 91 | * ]  | 
            ||
| 92 | * ```  | 
            ||
| 93 | */  | 
            ||
| 94 | private $assetMap = [];  | 
            ||
| 95 | |||
| 96 | /**  | 
            ||
| 97 | * @var string the root directory storing the published asset files.  | 
            ||
| 98 | */  | 
            ||
| 99 | private $basePath = '@public/assets';  | 
            ||
| 100 | |||
| 101 | /**  | 
            ||
| 102 | * @var string the base URL through which the published asset files can be accessed.  | 
            ||
| 103 | */  | 
            ||
| 104 | private $baseUrl = '@web/assets';  | 
            ||
| 105 | |||
| 106 | /**  | 
            ||
| 107 | * @var callable a PHP callback that is called before copying each sub-directory or file. This option is used only  | 
            ||
| 108 | * when publishing a directory. If the callback returns false, the copy operation for the  | 
            ||
| 109 | * sub-directory or file will be cancelled.  | 
            ||
| 110 | *  | 
            ||
| 111 | * The signature of the callback should be: `function ($from, $to)`, where `$from` is the sub-directory or file to  | 
            ||
| 112 | * be copied from, while `$to` is the copy target.  | 
            ||
| 113 | *  | 
            ||
| 114 |      * This is passed as a parameter `beforeCopy` to {@see Yiisoft\Files\FileHelper::copyDirectory()}. | 
            ||
| 115 | */  | 
            ||
| 116 | private $beforeCopy;  | 
            ||
| 117 | |||
| 118 | /**  | 
            ||
| 119 | * @var array|bool list of asset bundle configurations. This property is provided to customize asset bundles.  | 
            ||
| 120 |      *                 When a bundle is being loaded by {@see getBundle()}, if it has a corresponding configuration | 
            ||
| 121 | * specified here, the configuration will be applied to the bundle.  | 
            ||
| 122 | *  | 
            ||
| 123 | * The array keys are the asset bundle names, which typically are asset bundle class names without leading  | 
            ||
| 124 | * backslash. The array values are the corresponding configurations. If a value is false, it means the corresponding  | 
            ||
| 125 |      * asset bundle is disabled and {@see getBundle()} should return null. | 
            ||
| 126 | *  | 
            ||
| 127 |      * If this property is false, it means the whole asset bundle feature is disabled and {@see {getBundle()} will | 
            ||
| 128 | * always return null.  | 
            ||
| 129 | *  | 
            ||
| 130 | * The following example shows how to disable the bootstrap css file used by Bootstrap widgets (because you want to  | 
            ||
| 131 | * use your own styles):  | 
            ||
| 132 | *  | 
            ||
| 133 | * ```php  | 
            ||
| 134 | * [  | 
            ||
| 135 | * \Yiisoft\Bootstrap4\BootstrapAsset::class => [  | 
            ||
| 136 | * 'css' => [],  | 
            ||
| 137 | * ],  | 
            ||
| 138 | * ]  | 
            ||
| 139 | * ```  | 
            ||
| 140 | */  | 
            ||
| 141 | private $bundles = [];  | 
            ||
| 142 | |||
| 143 | /**  | 
            ||
| 144 | * AssetConverter component.  | 
            ||
| 145 | *  | 
            ||
| 146 | * @var AssetConverterInterface $converter  | 
            ||
| 147 | */  | 
            ||
| 148 | private $converter;  | 
            ||
| 149 | |||
| 150 | /**  | 
            ||
| 151 | * @var int the permission to be set for newly generated asset directories. This value will be used by PHP chmod()  | 
            ||
| 152 | * function. No umask will be applied. Defaults to 0775, meaning the directory is read-writable by owner  | 
            ||
| 153 | * and group, but read-only for other users.  | 
            ||
| 154 | */  | 
            ||
| 155 | private $dirMode = 0775;  | 
            ||
| 156 | |||
| 157 | /**  | 
            ||
| 158 | * @var AssetBundle $dummyBundles  | 
            ||
| 159 | */  | 
            ||
| 160 | private $dummyBundles;  | 
            ||
| 161 | |||
| 162 | /**  | 
            ||
| 163 | * @var int the permission to be set for newly published asset files. This value will be used by PHP chmod()  | 
            ||
| 164 | * function. No umask will be applied. If not set, the permission will be determined by the current  | 
            ||
| 165 | * environment.  | 
            ||
| 166 | */  | 
            ||
| 167 | private $fileMode;  | 
            ||
| 168 | |||
| 169 | /**  | 
            ||
| 170 | * @var bool whether the directory being published should be copied even if it is found in the target directory.  | 
            ||
| 171 | * This option is used only when publishing a directory. You may want to set this to be `true` during the  | 
            ||
| 172 | * development stage to make sure the published directory is always up-to-date. Do not set this to true  | 
            ||
| 173 | * on production servers as it will significantly degrade the performance.  | 
            ||
| 174 | */  | 
            ||
| 175 | private $forceCopy = false;  | 
            ||
| 176 | |||
| 177 | /**  | 
            ||
| 178 | * @var callable a callback that will be called to produce hash for asset directory generation. The signature of the  | 
            ||
| 179 | * callback should be as follows:  | 
            ||
| 180 | *  | 
            ||
| 181 | * ```  | 
            ||
| 182 | * function ($path)  | 
            ||
| 183 | * ```  | 
            ||
| 184 | *  | 
            ||
| 185 | * where `$path` is the asset path. Note that the `$path` can be either directory where the asset files reside or a  | 
            ||
| 186 | * single file. For a CSS file that uses relative path in `url()`, the hash implementation should use the directory  | 
            ||
| 187 | * path of the file instead of the file path to include the relative asset files in the copying.  | 
            ||
| 188 | *  | 
            ||
| 189 | * If this is not set, the asset manager will use the default CRC32 and filemtime in the `hash` method.  | 
            ||
| 190 | *  | 
            ||
| 191 | * Example of an implementation using MD4 hash:  | 
            ||
| 192 | *  | 
            ||
| 193 | * ```php  | 
            ||
| 194 |      * function ($path) { | 
            ||
| 195 |      *     return hash('md4', $path); | 
            ||
| 196 | * }  | 
            ||
| 197 | * ```  | 
            ||
| 198 | */  | 
            ||
| 199 | private $hashCallback;  | 
            ||
| 200 | |||
| 201 | /**  | 
            ||
| 202 | * @var bool whether to use symbolic link to publish asset files. Defaults to false, meaning asset files are copied  | 
            ||
| 203 |      *           to {@see basePath}. Using symbolic links has the benefit that the published assets will always be | 
            ||
| 204 | * consistent with the source assets and there is no copy operation required. This is especially useful  | 
            ||
| 205 | * during development.  | 
            ||
| 206 | *  | 
            ||
| 207 | * However, there are special requirements for hosting environments in order to use symbolic links. In particular,  | 
            ||
| 208 | * symbolic links are supported only on Linux/Unix, and Windows Vista/2008 or greater.  | 
            ||
| 209 | *  | 
            ||
| 210 | * Moreover, some Web servers need to be properly configured so that the linked assets are accessible to Web users.  | 
            ||
| 211 | * For example, for Apache Web server, the following configuration directive should be added for the Web folder:  | 
            ||
| 212 | *  | 
            ||
| 213 | * ```apache  | 
            ||
| 214 | * Options FollowSymLinks  | 
            ||
| 215 | * ```  | 
            ||
| 216 | */  | 
            ||
| 217 | private $linkAssets = false;  | 
            ||
| 218 | |||
| 219 | /**  | 
            ||
| 220 | * @var LoggerInterface $logger  | 
            ||
| 221 | */  | 
            ||
| 222 | private $logger;  | 
            ||
| 223 | |||
| 224 | /**  | 
            ||
| 225 | * @var array published assets  | 
            ||
| 226 | */  | 
            ||
| 227 | private $published = [];  | 
            ||
| 228 | |||
| 229 | /**  | 
            ||
| 230 | * @var string $realBasePath  | 
            ||
| 231 | */  | 
            ||
| 232 | private $realBasePath;  | 
            ||
| 233 | |||
| 234 | /**  | 
            ||
| 235 | * AssetManager constructor.  | 
            ||
| 236 | *  | 
            ||
| 237 | * @param Aliases $aliases  | 
            ||
| 238 | */  | 
            ||
| 239 | public function __construct(Aliases $aliases, LoggerInterface $logger)  | 
            ||
| 240 |     { | 
            ||
| 241 | $this->aliases = $aliases;  | 
            ||
| 242 | $this->logger = $logger;  | 
            ||
| 243 | $this->setDefaultPaths();  | 
            ||
| 244 | }  | 
            ||
| 245 | |||
| 246 | /**  | 
            ||
| 247 | * Returns the actual URL for the specified asset.  | 
            ||
| 248 |      * The actual URL is obtained by prepending either {@see AssetBundle::$baseUrl} or {@see AssetManager::$baseUrl} to | 
            ||
| 249 | * the given asset path.  | 
            ||
| 250 | *  | 
            ||
| 251 | * @param AssetBundle $bundle the asset bundle which the asset file belongs to  | 
            ||
| 252 |      * @param string      $asset the asset path. This should be one of the assets listed in {@see AssetBundle::$js} or | 
            ||
| 253 |      *                    {@see AssetBundle::$css}. | 
            ||
| 254 | *  | 
            ||
| 255 | * @return string the actual URL for the specified asset.  | 
            ||
| 256 | */  | 
            ||
| 257 | public function getAssetUrl(AssetBundle $bundle, string $asset): string  | 
            ||
| 258 |     { | 
            ||
| 259 |         if (($actualAsset = $this->resolveAsset($bundle, $asset)) !== false) { | 
            ||
| 260 |             if (strncmp((string) $actualAsset, '@web/', 5) === 0) { | 
            ||
| 261 | $asset = substr((string) $actualAsset, 5);  | 
            ||
| 262 |                 $basePath = $this->aliases->get('@public'); | 
            ||
| 263 |                 $baseUrl = $this->aliases->get('@web'); | 
            ||
| 264 |             } else { | 
            ||
| 265 | $asset = $this->aliases->get($actualAsset);  | 
            ||
| 
                                                                                                    
                        
                         | 
                |||
| 266 | $basePath = $this->getRealBasePath();  | 
            ||
| 267 | $baseUrl = $this->baseUrl;  | 
            ||
| 268 | }  | 
            ||
| 269 |         } else { | 
            ||
| 270 | $basePath = $this->aliases->get($bundle->basePath);  | 
            ||
| 271 | $baseUrl = $this->aliases->get($bundle->baseUrl);  | 
            ||
| 272 | }  | 
            ||
| 273 | |||
| 274 |         if (!Url::isRelative($asset) || strncmp($asset, '/', 1) === 0) { | 
            ||
| 275 | return $asset;  | 
            ||
| 276 | }  | 
            ||
| 277 | |||
| 278 |         if ($this->appendTimestamp && ($timestamp = @filemtime("$basePath/$asset")) > 0) { | 
            ||
| 279 | return "$baseUrl/$asset?v=$timestamp";  | 
            ||
| 280 | }  | 
            ||
| 281 | |||
| 282 | return "$baseUrl/$asset";  | 
            ||
| 283 | }  | 
            ||
| 284 | |||
| 285 | /**  | 
            ||
| 286 | * Returns the actual file path for the specified asset.  | 
            ||
| 287 | *  | 
            ||
| 288 | * @param AssetBundle $bundle the asset bundle which the asset file belongs to  | 
            ||
| 289 |      * @param string      $asset  the asset path. This should be one of the assets listed in {@see AssetBundle::$js} or | 
            ||
| 290 |      *                    {@see AssetBundle::$css}. | 
            ||
| 291 | *  | 
            ||
| 292 | * @return false|string the actual file path, or `false` if the asset is specified as an absolute URL  | 
            ||
| 293 | */  | 
            ||
| 294 | public function getAssetPath(AssetBundle $bundle, string $asset)  | 
            ||
| 295 |     { | 
            ||
| 296 |         if (($actualAsset = $this->resolveAsset($bundle, $asset)) !== false) { | 
            ||
| 297 | return Url::isRelative((string) $actualAsset) ? $this->getRealBasePath() . '/' . $actualAsset : false;  | 
            ||
| 298 | }  | 
            ||
| 299 | |||
| 300 | return Url::isRelative($asset) ? $bundle->basePath . '/' .$asset : false;  | 
            ||
| 301 | }  | 
            ||
| 302 | |||
| 303 | /**  | 
            ||
| 304 | * Returns the named asset bundle.  | 
            ||
| 305 | *  | 
            ||
| 306 |      * This method will first look for the bundle in {@see bundles()}. If not found, it will treat `$name` as the class | 
            ||
| 307 | * of the asset bundle and create a new instance of it.  | 
            ||
| 308 | *  | 
            ||
| 309 | * @param string $name the class name of the asset bundle (without the leading backslash)  | 
            ||
| 310 | * @param bool $publish whether to publish the asset files in the asset bundle before it is returned. If you set  | 
            ||
| 311 | * this false, you must manually call `AssetBundle::publish()` to publish the asset files.  | 
            ||
| 312 | *  | 
            ||
| 313 | * @return AssetBundle the asset bundle instance  | 
            ||
| 314 | *  | 
            ||
| 315 | * @throws \InvalidArgumentException  | 
            ||
| 316 | */  | 
            ||
| 317 | public function getBundle(string $name, bool $publish = true): AssetBundle  | 
            ||
| 340 | }  | 
            ||
| 341 | |||
| 342 | /**  | 
            ||
| 343 | * Returns the asset converter.  | 
            ||
| 344 | *  | 
            ||
| 345 | * @return AssetConverterInterface the asset converter.  | 
            ||
| 346 | */  | 
            ||
| 347 | public function getConverter(): AssetConverterInterface  | 
            ||
| 348 |     { | 
            ||
| 349 |         if ($this->converter === null) { | 
            ||
| 350 | $this->converter = new AssetConverter($this->aliases, $this->logger);  | 
            ||
| 351 |         } elseif (is_array($this->converter) || is_string($this->converter)) { | 
            ||
| 352 |             if (is_array($this->converter) && !isset($this->converter['__class'])) { | 
            ||
| 353 | $this->converter['__class'] = AssetConverter::class;  | 
            ||
| 354 | }  | 
            ||
| 355 | $this->converter = new $this->converter($this->aliases, $this->logger);  | 
            ||
| 356 | }  | 
            ||
| 357 | |||
| 358 | return $this->converter;  | 
            ||
| 359 | }  | 
            ||
| 360 | |||
| 361 | /**  | 
            ||
| 362 | * Returns the published path of a file path.  | 
            ||
| 363 | *  | 
            ||
| 364 | * This method does not perform any publishing. It merely tells you if the file or directory is published, where it  | 
            ||
| 365 | * will go.  | 
            ||
| 366 | *  | 
            ||
| 367 | * @param string $path directory or file path being published  | 
            ||
| 368 | *  | 
            ||
| 369 | * @return string|bool string the published file path. False if the file or directory does not exist  | 
            ||
| 370 | */  | 
            ||
| 371 | public function getPublishedPath(string $path)  | 
            ||
| 372 |     { | 
            ||
| 373 | $path = $this->aliases->get($path);  | 
            ||
| 374 | |||
| 375 |         if (isset($this->published[$path])) { | 
            ||
| 376 | return $this->published[$path][0];  | 
            ||
| 377 | }  | 
            ||
| 378 |         if (is_string($path) && ($path = realpath($path)) !== false) { | 
            ||
| 379 | return $this->getRealBasePath() . DIRECTORY_SEPARATOR . $this->hash($path) . (is_file($path) ?  | 
            ||
| 380 | DIRECTORY_SEPARATOR . basename($path) : '');  | 
            ||
| 381 | }  | 
            ||
| 382 | |||
| 383 | return false;  | 
            ||
| 384 | }  | 
            ||
| 385 | |||
| 386 | /**  | 
            ||
| 387 | * Returns the URL of a published file path.  | 
            ||
| 388 | *  | 
            ||
| 389 | * This method does not perform any publishing. It merely tells you if the file path is published, what the URL will  | 
            ||
| 390 | * be to access it.  | 
            ||
| 391 | *  | 
            ||
| 392 | * @param string $path directory or file path being published  | 
            ||
| 393 | *  | 
            ||
| 394 | * @return string|bool string the published URL for the file or directory. False if the file or directory does not  | 
            ||
| 395 | * exist.  | 
            ||
| 396 | */  | 
            ||
| 397 | public function getPublishedUrl(string $path)  | 
            ||
| 398 |     { | 
            ||
| 399 |         if (isset($this->published[$path])) { | 
            ||
| 400 | return $this->published[$path][1];  | 
            ||
| 401 | }  | 
            ||
| 402 |         if (is_string($path) && ($path = realpath($path)) !== false) { | 
            ||
| 403 | return $this->baseUrl.'/'.$this->hash($path).(is_file($path) ? '/'.basename($path) : '');  | 
            ||
| 404 | }  | 
            ||
| 405 | |||
| 406 | return false;  | 
            ||
| 407 | }  | 
            ||
| 408 | |||
| 409 | /**  | 
            ||
| 410 | * Get RealBasePath.  | 
            ||
| 411 | *  | 
            ||
| 412 | * @return bool|string  | 
            ||
| 413 | */  | 
            ||
| 414 | public function getRealBasePath()  | 
            ||
| 415 |     { | 
            ||
| 416 |         if ($this->realBasePath === null) { | 
            ||
| 417 | $this->realBasePath = (string) $this->prepareBasePath($this->basePath);  | 
            ||
| 418 | }  | 
            ||
| 419 | |||
| 420 | return $this->realBasePath;  | 
            ||
| 421 | }  | 
            ||
| 422 | |||
| 423 | /**  | 
            ||
| 424 | * prepareBasePath  | 
            ||
| 425 | *  | 
            ||
| 426 | * @param string $basePath  | 
            ||
| 427 | *  | 
            ||
| 428 | * @throws \InvalidArgumentException  | 
            ||
| 429 | *  | 
            ||
| 430 | * @return string|bool  | 
            ||
| 431 | */  | 
            ||
| 432 | public function prepareBasePath(string $basePath)  | 
            ||
| 433 |     { | 
            ||
| 434 | $basePath = $this->aliases->get($basePath);  | 
            ||
| 435 | |||
| 436 |         if (!is_dir($basePath)) { | 
            ||
| 437 |             throw new \InvalidArgumentException("The directory does not exist: {$basePath}"); | 
            ||
| 438 | }  | 
            ||
| 439 | |||
| 440 |         if (!is_writable($basePath)) { | 
            ||
| 441 |             throw new \InvalidArgumentException("The directory is not writable by the Web process: {$basePath}"); | 
            ||
| 442 | }  | 
            ||
| 443 | |||
| 444 | return realpath($basePath);  | 
            ||
| 445 | }  | 
            ||
| 446 | |||
| 447 | /**  | 
            ||
| 448 | * Publishes a file or a directory.  | 
            ||
| 449 | *  | 
            ||
| 450 |      * This method will copy the specified file or directory to {@see basePath} so that it can be accessed via the Web | 
            ||
| 451 | * server.  | 
            ||
| 452 | *  | 
            ||
| 453 | * If the asset is a file, its file modification time will be checked to avoid unnecessary file copying.  | 
            ||
| 454 | *  | 
            ||
| 455 | * If the asset is a directory, all files and subdirectories under it will be published recursively. Note, in case  | 
            ||
| 456 | * $forceCopy is false the method only checks the existence of the target directory to avoid repetitive copying  | 
            ||
| 457 | * (which is very expensive).  | 
            ||
| 458 | *  | 
            ||
| 459 | * By default, when publishing a directory, subdirectories and files whose name starts with a dot "." will NOT be  | 
            ||
| 460 | * published. If you want to change this behavior, you may specify the "beforeCopy" option as explained in the  | 
            ||
| 461 | * `$options` parameter.  | 
            ||
| 462 | *  | 
            ||
| 463 | * Note: On rare scenario, a race condition can develop that will lead to a one-time-manifestation of a  | 
            ||
| 464 | * non-critical problem in the creation of the directory that holds the published assets. This problem can be  | 
            ||
| 465 | * avoided altogether by 'requesting' in advance all the resources that are supposed to trigger a 'publish()' call,  | 
            ||
| 466 | * and doing that in the application deployment phase, before system goes live. See more in the following  | 
            ||
| 467 | * discussion: http://code.google.com/p/yii/issues/detail?id=2579  | 
            ||
| 468 | *  | 
            ||
| 469 | * @param string $path the asset (file or directory) to be published  | 
            ||
| 470 | * @param array $options the options to be applied when publishing a directory. The following options are  | 
            ||
| 471 | * supported:  | 
            ||
| 472 | *  | 
            ||
| 473 | * - only: array, list of patterns that the file paths should match if they want to be copied.  | 
            ||
| 474 | * - except: array, list of patterns that the files or directories should match if they want to be excluded from  | 
            ||
| 475 | * being copied.  | 
            ||
| 476 | * - caseSensitive: boolean, whether patterns specified at "only" or "except" should be case sensitive. Defaults to  | 
            ||
| 477 | * true.  | 
            ||
| 478 | * - beforeCopy: callback, a PHP callback that is called before copying each sub-directory or file.  | 
            ||
| 479 |      *   This overrides {@see beforeCopy} if set. | 
            ||
| 480 | * - afterCopy: callback, a PHP callback that is called after a sub-directory or file is successfully copied. This  | 
            ||
| 481 |      *   overrides {@seee afterCopy} if set. | 
            ||
| 482 | * - forceCopy: boolean, whether the directory being published should be copied even if it is found in the target  | 
            ||
| 483 |      *   directory. This option is used only when publishing a directory. This overrides {@see forceCopy} if set. | 
            ||
| 484 | *  | 
            ||
| 485 | * @throws \InvalidArgumentException if the asset to be published does not exist.  | 
            ||
| 486 | *  | 
            ||
| 487 | * @return array the path (directory or file path) and the URL that the asset is published as.  | 
            ||
| 488 | */  | 
            ||
| 489 | public function publish(string $path, array $options = []): array  | 
            ||
| 490 |     { | 
            ||
| 491 | $path = $this->aliases->get($path);  | 
            ||
| 492 | |||
| 493 |         if (isset($this->published[$path])) { | 
            ||
| 494 | return $this->published[$path];  | 
            ||
| 495 | }  | 
            ||
| 496 | |||
| 497 |         if (!is_string($path) || ($src = realpath($path)) === false) { | 
            ||
| 498 |             throw new \InvalidArgumentException("The file or directory to be published does not exist: $path"); | 
            ||
| 499 | }  | 
            ||
| 500 | |||
| 501 |         if (is_file($src)) { | 
            ||
| 502 | return $this->published[$path] = $this->publishFile($src);  | 
            ||
| 503 | }  | 
            ||
| 504 | |||
| 505 | return $this->published[$path] = $this->publishDirectory($src, $options);  | 
            ||
| 506 | }  | 
            ||
| 507 | |||
| 508 | /**  | 
            ||
| 509 | * Set afterCopy.  | 
            ||
| 510 | *  | 
            ||
| 511 | * @param callable $value  | 
            ||
| 512 | *  | 
            ||
| 513 | * @return void  | 
            ||
| 514 | *  | 
            ||
| 515 |      * {@see afterCopy} | 
            ||
| 516 | */  | 
            ||
| 517 | public function setAfterCopy(callable $value): void  | 
            ||
| 518 |     { | 
            ||
| 519 | $this->afterCopy = $value;  | 
            ||
| 520 | }  | 
            ||
| 521 | |||
| 522 | /**  | 
            ||
| 523 | * Set alternatives.  | 
            ||
| 524 | *  | 
            ||
| 525 | * @param array $value  | 
            ||
| 526 | *  | 
            ||
| 527 | * @return void  | 
            ||
| 528 | *  | 
            ||
| 529 |      * {@see alternatives} | 
            ||
| 530 | */  | 
            ||
| 531 | public function setAlternatives(array $value): void  | 
            ||
| 532 |     { | 
            ||
| 533 | $this->alternatives = $value;  | 
            ||
| 534 | $this->setAlternativesAlias();  | 
            ||
| 535 | }  | 
            ||
| 536 | |||
| 537 | /**  | 
            ||
| 538 | * Set appendTimestamp.  | 
            ||
| 539 | *  | 
            ||
| 540 | * @param bool $value  | 
            ||
| 541 | *  | 
            ||
| 542 | * @return void  | 
            ||
| 543 | *  | 
            ||
| 544 |      * {@see appendTimestamp} | 
            ||
| 545 | */  | 
            ||
| 546 | public function setAppendTimestamp(bool $value): void  | 
            ||
| 547 |     { | 
            ||
| 548 | $this->appendTimestamp = $value;  | 
            ||
| 549 | }  | 
            ||
| 550 | |||
| 551 | /**  | 
            ||
| 552 | * Set assetMap.  | 
            ||
| 553 | *  | 
            ||
| 554 | * @param array $value  | 
            ||
| 555 | *  | 
            ||
| 556 | * @return void  | 
            ||
| 557 | *  | 
            ||
| 558 |      * {@see assetMap} | 
            ||
| 559 | */  | 
            ||
| 560 | public function setAssetMap(array $value): void  | 
            ||
| 561 |     { | 
            ||
| 562 | $this->assetMap = $value;  | 
            ||
| 563 | }  | 
            ||
| 564 | |||
| 565 | /**  | 
            ||
| 566 | * Set basePath.  | 
            ||
| 567 | *  | 
            ||
| 568 | * @param string $value  | 
            ||
| 569 | *  | 
            ||
| 570 | * @return void  | 
            ||
| 571 | *  | 
            ||
| 572 |      * {@see basePath} | 
            ||
| 573 | */  | 
            ||
| 574 | public function setBasePath(string $value): void  | 
            ||
| 575 |     { | 
            ||
| 576 | $this->basePath = $value;  | 
            ||
| 577 | }  | 
            ||
| 578 | |||
| 579 | /**  | 
            ||
| 580 | * Set baseUrl.  | 
            ||
| 581 | *  | 
            ||
| 582 | * @param string $value  | 
            ||
| 583 | *  | 
            ||
| 584 | * @return void  | 
            ||
| 585 | *  | 
            ||
| 586 |      * {@see baseUrl} | 
            ||
| 587 | */  | 
            ||
| 588 | public function setBaseUrl(string $value): void  | 
            ||
| 589 |     { | 
            ||
| 590 | $this->baseUrl = $value;  | 
            ||
| 591 | }  | 
            ||
| 592 | |||
| 593 | /**  | 
            ||
| 594 | * Set beforeCopy.  | 
            ||
| 595 | *  | 
            ||
| 596 | * @param callable $value  | 
            ||
| 597 | *  | 
            ||
| 598 | * @return void  | 
            ||
| 599 | *  | 
            ||
| 600 |      * {@see beforeCopy} | 
            ||
| 601 | */  | 
            ||
| 602 | public function setBeforeCopy(callable $value): void  | 
            ||
| 603 |     { | 
            ||
| 604 | $this->beforeCopy = $value;  | 
            ||
| 605 | }  | 
            ||
| 606 | |||
| 607 | /**  | 
            ||
| 608 | * Set bundles.  | 
            ||
| 609 | *  | 
            ||
| 610 | * @param array $value  | 
            ||
| 611 | *  | 
            ||
| 612 | * @return void  | 
            ||
| 613 | *  | 
            ||
| 614 |      * {@see beforeCopy} | 
            ||
| 615 | */  | 
            ||
| 616 | public function setBundles(array $value): void  | 
            ||
| 617 |     { | 
            ||
| 618 | $this->bundles = $value;  | 
            ||
| 619 | }  | 
            ||
| 620 | |||
| 621 | /**  | 
            ||
| 622 | * Sets the asset converter.  | 
            ||
| 623 | *  | 
            ||
| 624 | * @param AssetConverterInterface $value the asset converter. This can be eitheran object implementing the  | 
            ||
| 625 |      *                                      {@see AssetConverterInterface}, or a configuration array that can be used | 
            ||
| 626 | * to create the asset converter object.  | 
            ||
| 627 | */  | 
            ||
| 628 | public function setConverter(AssetConverterInterface $value): void  | 
            ||
| 629 |     { | 
            ||
| 630 | $this->converter = $value;  | 
            ||
| 631 | }  | 
            ||
| 632 | |||
| 633 | /**  | 
            ||
| 634 | * Set dirMode.  | 
            ||
| 635 | *  | 
            ||
| 636 | * @param int $value  | 
            ||
| 637 | *  | 
            ||
| 638 | * @return void  | 
            ||
| 639 | *  | 
            ||
| 640 |      * {@see dirMode} | 
            ||
| 641 | */  | 
            ||
| 642 | public function setDirMode(int $value): void  | 
            ||
| 643 |     { | 
            ||
| 644 | $this->dirMode = $value;  | 
            ||
| 645 | }  | 
            ||
| 646 | |||
| 647 | /**  | 
            ||
| 648 | * Set fileMode.  | 
            ||
| 649 | *  | 
            ||
| 650 | * @param int $value  | 
            ||
| 651 | *  | 
            ||
| 652 | * @return void  | 
            ||
| 653 | *  | 
            ||
| 654 |      * {@see fileMode} | 
            ||
| 655 | */  | 
            ||
| 656 | public function setFileMode(int $value): void  | 
            ||
| 657 |     { | 
            ||
| 658 | $this->fileMode = $value;  | 
            ||
| 659 | }  | 
            ||
| 660 | |||
| 661 | /**  | 
            ||
| 662 | * Set hashCallback.  | 
            ||
| 663 | *  | 
            ||
| 664 | * @param callable $value  | 
            ||
| 665 | *  | 
            ||
| 666 | * @return void  | 
            ||
| 667 | *  | 
            ||
| 668 |      * {@see hashCallback} | 
            ||
| 669 | */  | 
            ||
| 670 | public function setHashCallback(callable $value): void  | 
            ||
| 671 |     { | 
            ||
| 672 | $this->hashCallback = $value;  | 
            ||
| 673 | }  | 
            ||
| 674 | |||
| 675 | /**  | 
            ||
| 676 | * Set linkAssets.  | 
            ||
| 677 | *  | 
            ||
| 678 | * @param bool $value  | 
            ||
| 679 | *  | 
            ||
| 680 | * @return void  | 
            ||
| 681 | *  | 
            ||
| 682 |      * {@see linkAssets} | 
            ||
| 683 | */  | 
            ||
| 684 | public function setLinkAssets(bool $value): void  | 
            ||
| 685 |     { | 
            ||
| 686 | $this->linkAssets = $value;  | 
            ||
| 687 | }  | 
            ||
| 688 | |||
| 689 | /**  | 
            ||
| 690 | * Returns a string representing the current version of the Yii framework.  | 
            ||
| 691 | * @return string the version of Yii framework  | 
            ||
| 692 | */  | 
            ||
| 693 | protected static function getVersion()  | 
            ||
| 694 |     { | 
            ||
| 695 | return '3.0-dev';  | 
            ||
| 696 | }  | 
            ||
| 697 | |||
| 698 | /**  | 
            ||
| 699 | * Generate a CRC32 hash for the directory path. Collisions are higher than MD5 but generates a much smaller hash  | 
            ||
| 700 | * string.  | 
            ||
| 701 | *  | 
            ||
| 702 | * @param string $path string to be hashed.  | 
            ||
| 703 | *  | 
            ||
| 704 | * @return string hashed string.  | 
            ||
| 705 | */  | 
            ||
| 706 | protected function hash(string $path): string  | 
            ||
| 707 |     { | 
            ||
| 708 |         if (is_callable($this->hashCallback)) { | 
            ||
| 709 | return call_user_func($this->hashCallback, $path);  | 
            ||
| 710 | }  | 
            ||
| 711 | $path = (is_file($path) ? dirname($path) : $path).filemtime($path);  | 
            ||
| 712 | |||
| 713 |         return sprintf('%x', crc32($path . static::getVersion() . '|' . $this->linkAssets)); | 
            ||
| 714 | }  | 
            ||
| 715 | |||
| 716 | /**  | 
            ||
| 717 | * Loads asset bundle class by name.  | 
            ||
| 718 | *  | 
            ||
| 719 | * @param string $name bundle name  | 
            ||
| 720 | * @param array $config bundle object configuration  | 
            ||
| 721 | * @param bool $publish if bundle should be published  | 
            ||
| 722 | *  | 
            ||
| 723 | * @return AssetBundle  | 
            ||
| 724 | */  | 
            ||
| 725 | protected function loadBundle(string $name, array $config = [], bool $publish = true): AssetBundle  | 
            ||
| 726 |     { | 
            ||
| 727 |         if (!isset($config['__class'])) { | 
            ||
| 728 | $config['__class'] = $name;  | 
            ||
| 729 | }  | 
            ||
| 730 | /* @var $bundle AssetBundle */  | 
            ||
| 731 | $bundle = new $config['__class']();  | 
            ||
| 732 | |||
| 733 |         if ($publish) { | 
            ||
| 734 | $bundle->publish($this);  | 
            ||
| 735 | }  | 
            ||
| 736 | |||
| 737 | return $bundle;  | 
            ||
| 738 | }  | 
            ||
| 739 | |||
| 740 | /**  | 
            ||
| 741 | * Loads dummy bundle by name.  | 
            ||
| 742 | *  | 
            ||
| 743 | * @param string $name  | 
            ||
| 744 | *  | 
            ||
| 745 | * @return AssetBundle  | 
            ||
| 746 | */  | 
            ||
| 747 | protected function loadDummyBundle(string $name): AssetBundle  | 
            ||
| 748 |     { | 
            ||
| 749 |         if (!isset($this->dummyBundles[$name])) { | 
            ||
| 750 | $this->dummyBundles[$name] = $this->loadBundle($name, [  | 
            ||
| 751 | 'sourcePath' => null,  | 
            ||
| 752 | 'js' => [],  | 
            ||
| 753 | 'css' => [],  | 
            ||
| 754 | 'depends' => [],  | 
            ||
| 755 | ]);  | 
            ||
| 756 | }  | 
            ||
| 757 | |||
| 758 | return $this->dummyBundles[$name];  | 
            ||
| 759 | }  | 
            ||
| 760 | |||
| 761 | /**  | 
            ||
| 762 | * Publishes a file.  | 
            ||
| 763 | *  | 
            ||
| 764 | * @param string $src the asset file to be published  | 
            ||
| 765 | *  | 
            ||
| 766 | * @throws \Exception if the asset to be published does not exist.  | 
            ||
| 767 | *  | 
            ||
| 768 | * @return array the path and the URL that the asset is published as.  | 
            ||
| 769 | */  | 
            ||
| 770 | protected function publishFile(string $src): array  | 
            ||
| 771 |     { | 
            ||
| 772 | $dir = $this->hash($src);  | 
            ||
| 773 | $fileName = basename($src);  | 
            ||
| 774 | $dstDir = $this->getRealBasePath() .DIRECTORY_SEPARATOR . $dir;  | 
            ||
| 775 | $dstFile = $dstDir . DIRECTORY_SEPARATOR . $fileName;  | 
            ||
| 776 | |||
| 777 |         if (!is_dir($dstDir)) { | 
            ||
| 778 | FileHelper::createDirectory($dstDir, $this->dirMode);  | 
            ||
| 779 | }  | 
            ||
| 780 | |||
| 781 |         if ($this->linkAssets) { | 
            ||
| 782 |             if (!is_file($dstFile)) { | 
            ||
| 783 |                 try { // fix #6226 symlinking multi threaded | 
            ||
| 784 | symlink($src, $dstFile);  | 
            ||
| 785 |                 } catch (\Exception $e) { | 
            ||
| 786 |                     if (!is_file($dstFile)) { | 
            ||
| 787 | throw $e;  | 
            ||
| 788 | }  | 
            ||
| 789 | }  | 
            ||
| 790 | }  | 
            ||
| 791 |         } elseif (@filemtime($dstFile) < @filemtime($src)) { | 
            ||
| 792 | copy($src, $dstFile);  | 
            ||
| 793 |             if ($this->fileMode !== null) { | 
            ||
| 794 | @chmod($dstFile, $this->fileMode);  | 
            ||
| 795 | }  | 
            ||
| 796 | }  | 
            ||
| 797 | |||
| 798 | return [$dstFile, $this->baseUrl . "/$dir/$fileName"];  | 
            ||
| 799 | }  | 
            ||
| 800 | |||
| 801 | /**  | 
            ||
| 802 | * Publishes a directory.  | 
            ||
| 803 | *  | 
            ||
| 804 | * @param string $src the asset directory to be published  | 
            ||
| 805 | * @param array $options the options to be applied when publishing a directory. The following options are  | 
            ||
| 806 | * supported:  | 
            ||
| 807 | *  | 
            ||
| 808 | * - only: array, list of patterns that the file paths should match if they want to be copied.  | 
            ||
| 809 | * - except: array, list of patterns that the files or directories should match if they want to be excluded from  | 
            ||
| 810 | * being copied.  | 
            ||
| 811 | * - caseSensitive: boolean, whether patterns specified at "only" or "except" should be case sensitive. Defaults  | 
            ||
| 812 | * to true.  | 
            ||
| 813 | * - beforeCopy: callback, a PHP callback that is called before copying each sub-directory or file. This overrides  | 
            ||
| 814 |      *   {@see beforeCopy} if set. | 
            ||
| 815 | * - afterCopy: callback, a PHP callback that is called after a sub-directory or file is successfully copied. This  | 
            ||
| 816 |      *   overrides {@see afterCopy} if set. | 
            ||
| 817 | * - forceCopy: boolean, whether the directory being published should be copied even if it is found in the target  | 
            ||
| 818 |      *   directory. This option is used only when publishing a directory. This overrides {@see forceCopy} if set. | 
            ||
| 819 | *  | 
            ||
| 820 | * @throws \Exception if the asset to be published does not exist.  | 
            ||
| 821 | *  | 
            ||
| 822 | * @return array the path directory and the URL that the asset is published as.  | 
            ||
| 823 | */  | 
            ||
| 824 | protected function publishDirectory(string $src, array $options): array  | 
            ||
| 825 |     { | 
            ||
| 826 | $dir = $this->hash($src);  | 
            ||
| 827 | $dstDir = $this->getRealBasePath() . DIRECTORY_SEPARATOR . $dir;  | 
            ||
| 828 | |||
| 829 |         if ($this->linkAssets) { | 
            ||
| 830 |             if (!is_dir($dstDir)) { | 
            ||
| 831 | FileHelper::createDirectory(dirname($dstDir), $this->dirMode);  | 
            ||
| 832 | |||
| 833 |                 try { // fix #6226 symlinking multi threaded | 
            ||
| 834 | symlink($src, $dstDir);  | 
            ||
| 835 |                 } catch (\Exception $e) { | 
            ||
| 836 |                     if (!is_dir($dstDir)) { | 
            ||
| 837 | throw $e;  | 
            ||
| 838 | }  | 
            ||
| 839 | }  | 
            ||
| 840 | }  | 
            ||
| 841 |         } elseif (!empty($options['forceCopy']) || ($this->forceCopy && !isset($options['forceCopy'])) || !is_dir($dstDir)) { | 
            ||
| 842 | $opts = array_merge(  | 
            ||
| 843 | $options,  | 
            ||
| 844 | [  | 
            ||
| 845 | 'dirMode' => $this->dirMode,  | 
            ||
| 846 | 'fileMode' => $this->fileMode,  | 
            ||
| 847 | 'copyEmptyDirectories' => false,  | 
            ||
| 848 | ]  | 
            ||
| 849 | );  | 
            ||
| 850 | |||
| 851 |             if (!isset($opts['beforeCopy'])) { | 
            ||
| 852 |                 if ($this->beforeCopy !== null) { | 
            ||
| 853 | $opts['beforeCopy'] = $this->beforeCopy;  | 
            ||
| 854 |                 } else { | 
            ||
| 855 |                     $opts['beforeCopy'] = function ($from, $to) { | 
            ||
| 856 | return strncmp(basename($from), '.', 1) !== 0;  | 
            ||
| 857 | };  | 
            ||
| 858 | }  | 
            ||
| 859 | }  | 
            ||
| 860 | |||
| 861 |             if (!isset($opts['afterCopy']) && $this->afterCopy !== null) { | 
            ||
| 862 | $opts['afterCopy'] = $this->afterCopy;  | 
            ||
| 863 | }  | 
            ||
| 864 | |||
| 865 | FileHelper::copyDirectory($src, $dstDir, $opts);  | 
            ||
| 866 | }  | 
            ||
| 867 | |||
| 868 | |||
| 869 | return [$dstDir, $this->baseUrl.'/'.$dir];  | 
            ||
| 870 | }  | 
            ||
| 871 | |||
| 872 | /**  | 
            ||
| 873 | * @param AssetBundle $bundle  | 
            ||
| 874 | * @param string $asset  | 
            ||
| 875 | *  | 
            ||
| 876 | * @return string|bool  | 
            ||
| 877 | */  | 
            ||
| 878 | protected function resolveAsset(AssetBundle $bundle, string $asset)  | 
            ||
| 898 | }  | 
            ||
| 899 | |||
| 900 | /**  | 
            ||
| 901 | * Set default paths asset manager.  | 
            ||
| 902 | *  | 
            ||
| 903 | * @return void  | 
            ||
| 904 | */  | 
            ||
| 905 | private function setDefaultPaths(): void  | 
            ||
| 906 |     { | 
            ||
| 917 | }  | 
            ||
| 918 | |||
| 919 | /**  | 
            ||
| 920 | * Set alternatives aliases.  | 
            ||
| 921 | *  | 
            ||
| 922 | * @return void  | 
            ||
| 923 | */  | 
            ||
| 924 | protected function setAlternativesAlias(): void  | 
            ||
| 925 |     { | 
            ||
| 926 |         foreach ($this->alternatives as $alias => $path) { | 
            ||
| 927 | $this->aliases->set($alias, $path);  | 
            ||
| 928 | }  | 
            ||
| 929 | }  | 
            ||
| 930 | }  | 
            ||
| 931 |