AssetController.php 19.8 KB
Newer Older
Qiang Xue committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14
<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

namespace yii\console\controllers;

use Yii;
use yii\console\Exception;
use yii\console\Controller;

/**
15 16
 * This command allows you to combine and compress your JavaScript and CSS files.
 *
17 18
 * @property array|\yii\web\AssetManager $assetManager asset manager, which will be used for assets processing.
 *
Qiang Xue committed
19 20 21
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 2.0
 */
22
class AssetController extends Controller
Qiang Xue committed
23
{
24 25 26
	/**
	 * @var string controller default action ID.
	 */
Qiang Xue committed
27
	public $defaultAction = 'compress';
28 29 30 31 32
	/**
	 * @var array list of asset bundles to be compressed.
	 * The keys are the bundle names, and the values are the configuration
	 * arrays for creating the [[yii\web\AssetBundle]] objects.
	 */
Qiang Xue committed
33
	public $bundles = array();
34 35 36 37
	/**
	 * @var array list of paths to the extensions, which assets should be also compressed.
	 * Each path should contain asset manifest file named "assets.php".
	 */
Qiang Xue committed
38 39
	public $extensions = array();
	/**
40 41 42
	 * @var array list of asset bundles, which represents output compressed files.
	 * You can specify the name of the output compressed file using 'css' and 'js' keys:
	 * For example:
Qiang Xue committed
43 44 45 46 47 48 49
	 * ~~~
	 * 'all' => array(
	 *     'css' => 'all.css',
	 *     'js' => 'js.css',
	 *     'depends' => array( ... ),
	 * )
	 * ~~~
50 51
	 * File names can contain placeholder "{ts}", which will be filled by current timestamp, while
	 * file creation.
Qiang Xue committed
52 53
	 */
	public $targets = array();
54
	/**
55 56
	 * @var array|\yii\web\AssetManager [[yii\web\AssetManager]] instance or its array configuration, which will be used
	 * for assets processing.
57
	 */
58
	private $_assetManager = array();
59 60 61 62
	/**
	 * @var string|callback Java Script file compressor.
	 * If a string, it is treated as shell command template, which should contain
	 * placeholders {from} - source file name - and {to} - output file name.
63
	 * Otherwise, it is treated as PHP callback, which should perform the compression.
64 65 66 67
	 *
	 * Default value relies on usage of "Closure Compiler"
	 * @see https://developers.google.com/closure/compiler/
	 */
68
	public $jsCompressor = 'java -jar compiler.jar --js {from} --js_output_file {to}';
69 70 71 72
	/**
	 * @var string|callback CSS file compressor.
	 * If a string, it is treated as shell command template, which should contain
	 * placeholders {from} - source file name - and {to} - output file name.
73
	 * Otherwise, it is treated as PHP callback, which should perform the compression.
74 75 76 77
	 *
	 * Default value relies on usage of "YUI Compressor"
	 * @see https://github.com/yui/yuicompressor/
	 */
78
	public $cssCompressor = 'java -jar yuicompressor.jar {from} -o {to}';
Qiang Xue committed
79

80
	/**
81 82
	 * Returns the asset manager instance.
	 * @throws \yii\console\Exception on invalid configuration.
83 84 85 86 87 88 89 90 91
	 * @return \yii\web\AssetManager asset manager instance.
	 */
	public function getAssetManager()
	{
		if (!is_object($this->_assetManager)) {
			$options = $this->_assetManager;
			if (!isset($options['class'])) {
				$options['class'] = 'yii\\web\\AssetManager';
			}
92 93 94 95 96 97
			if (!isset($options['basePath'])) {
				throw new Exception("Please specify 'basePath' for the 'assetManager' option.");
			}
			if (!isset($options['baseUrl'])) {
				throw new Exception("Please specify 'baseUrl' for the 'assetManager' option.");
			}
98 99 100 101 102 103
			$this->_assetManager = Yii::createObject($options);
		}
		return $this->_assetManager;
	}

	/**
104
	 * Sets asset manager instance or configuration.
105 106 107 108 109 110 111 112 113 114 115
	 * @param \yii\web\AssetManager|array $assetManager asset manager instance or its array configuration.
	 * @throws \yii\console\Exception on invalid argument type.
	 */
	public function setAssetManager($assetManager)
	{
		if (is_scalar($assetManager)) {
			throw new Exception('"' . get_class($this) . '::assetManager" should be either object or array - "' . gettype($assetManager) . '" given.');
		}
		$this->_assetManager = $assetManager;
	}

116
	/**
117
	 * Combines and compresses the asset files according to the given configuration.
118 119
	 * During the process new asset bundle configuration file will be created.
	 * You should replace your original asset bundle configuration with this file in order to use compressed files.
120
	 * @param string $configFile configuration file name.
121
	 * @param string $bundleFile output asset bundles configuration file name.
122
	 */
Qiang Xue committed
123 124 125 126
	public function actionCompress($configFile, $bundleFile)
	{
		$this->loadConfiguration($configFile);
		$bundles = $this->loadBundles($this->bundles, $this->extensions);
Qiang Xue committed
127
		$targets = $this->loadTargets($this->targets, $bundles);
128
		$this->publishBundles($bundles, $this->assetManager);
Qiang Xue committed
129
		$timestamp = time();
130 131
		foreach ($targets as $name => $target) {
			echo "Creating output bundle '{$name}':\n";
Qiang Xue committed
132
			if (!empty($target->js)) {
Qiang Xue committed
133 134
				$this->buildTarget($target, 'js', $bundles, $timestamp);
			}
Qiang Xue committed
135
			if (!empty($target->css)) {
Qiang Xue committed
136 137
				$this->buildTarget($target, 'css', $bundles, $timestamp);
			}
138
			echo "\n";
Qiang Xue committed
139 140 141
		}

		$targets = $this->adjustDependency($targets, $bundles);
Qiang Xue committed
142
		$this->saveTargets($targets, $bundleFile);
Qiang Xue committed
143 144
	}

145 146 147 148 149
	/**
	 * Applies configuration from the given file to self instance.
	 * @param string $configFile configuration file name.
	 * @throws \yii\console\Exception on failure.
	 */
Qiang Xue committed
150 151
	protected function loadConfiguration($configFile)
	{
152 153
		echo "Loading configuration from '{$configFile}'...\n";

Qiang Xue committed
154
		foreach (require($configFile) as $name => $value) {
155
			if (property_exists($this, $name) || $this->canSetProperty($name)) {
Qiang Xue committed
156 157
				$this->$name = $value;
			} else {
Qiang Xue committed
158
				throw new Exception("Unknown configuration option: $name");
Qiang Xue committed
159 160 161
			}
		}

162
		$this->getAssetManager(); // check asset manager configuration
Qiang Xue committed
163 164
	}

165
	/**
166
	 * Creates full list of source asset bundles.
167 168
	 * @param array[] $bundles list of asset bundle configurations.
	 * @param array $extensions list of the extension paths.
169
	 * @return \yii\web\AssetBundle[] list of source asset bundles.
170
	 */
Qiang Xue committed
171 172
	protected function loadBundles($bundles, $extensions)
	{
173
		echo "Collecting source bundles information...\n";
174 175

		$assetManager = $this->getAssetManager();
Qiang Xue committed
176
		$result = array();
177 178 179 180

		$assetManager->bundles = $bundles;
		foreach ($assetManager->bundles as $name => $bundle) {
			$result[$name] = $assetManager->getBundle($name);
Qiang Xue committed
181
		}
182

Qiang Xue committed
183 184 185 186 187
		foreach ($extensions as $path) {
			$manifest = $path . '/assets.php';
			if (!is_file($manifest)) {
				continue;
			}
188 189
			$assetManager->bundles = require($manifest);
			foreach ($assetManager->bundles as $name => $bundle) {
Qiang Xue committed
190
				if (!isset($result[$name])) {
191
					$result[$name] = $assetManager->getBundle($name);
Qiang Xue committed
192 193 194
				}
			}
		}
195 196 197 198 199

		foreach ($result as $name => $bundle) {
			$this->loadBundleDependency($name, $bundle, $result);
		}

Qiang Xue committed
200 201 202
		return $result;
	}

203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
	/**
	 * Loads asset bundle dependencies recursively.
	 * @param string $name bundle name
	 * @param \yii\web\AssetBundle $bundle bundle instance
	 * @param array $result already loaded bundles list.
	 * @throws \yii\console\Exception on failure.
	 */
	protected function loadBundleDependency($name, $bundle, &$result) {
		if (!empty($bundle->depends)) {
			$assetManager = $this->getAssetManager();
			foreach ($bundle->depends as $dependencyName) {
				if (!array_key_exists($dependencyName, $result)) {
					$dependencyBundle = $assetManager->getBundle($dependencyName);
					if ($dependencyBundle === null) {
						throw new Exception("Unable to load dependency bundle '{$dependencyName}' for bundle '{$name}'.");
					} else {
219
						$result[$dependencyName] = false;
220
						$this->loadBundleDependency($dependencyName, $dependencyBundle, $result);
221
						$result[$dependencyName] = $dependencyBundle;
222
					}
223 224 225 226
				} else {
					if ($result[$dependencyName] === false) {
						throw new Exception("A circular dependency is detected for target '{$dependencyName}'.");
					}
227 228 229 230 231
				}
			}
		}
	}

232
	/**
233 234 235 236
	 * Creates full list of output asset bundles.
	 * @param array $targets output asset bundles configuration.
	 * @param \yii\web\AssetBundle[] $bundles list of source asset bundles.
	 * @return \yii\web\AssetBundle[] list of output asset bundles.
237 238
	 * @throws \yii\console\Exception on failure.
	 */
Qiang Xue committed
239 240
	protected function loadTargets($targets, $bundles)
	{
241
		// build the dependency order of bundles
Qiang Xue committed
242 243 244 245 246
		$registered = array();
		foreach ($bundles as $name => $bundle) {
			$this->registerBundle($bundles, $name, $registered);
		}
		$bundleOrders = array_combine(array_keys($registered), range(0, count($bundles) - 1));
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272

		// fill up the target which has empty 'depends'.
		$referenced = array();
		foreach ($targets as $name => $target) {
			if (empty($target['depends'])) {
				if (!isset($all)) {
					$all = $name;
				} else {
					throw new Exception("Only one target can have empty 'depends' option. Found two now: $all, $name");
				}
			} else {
				foreach ($target['depends'] as $bundle) {
					if (!isset($referenced[$bundle])) {
						$referenced[$bundle] = $name;
					} else {
						throw new Exception("Target '{$referenced[$bundle]}' and '$name' cannot contain the bundle '$bundle' at the same time.");
					}
				}
			}
		}
		if (isset($all)) {
			$targets[$all]['depends'] = array_diff(array_keys($registered), array_keys($referenced));
		}

		// adjust the 'depends' order for each target according to the dependency order of bundles
		// create an AssetBundle object for each target
Qiang Xue committed
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
		foreach ($targets as $name => $target) {
			if (!isset($target['basePath'])) {
				throw new Exception("Please specify 'basePath' for the '$name' target.");
			}
			if (!isset($target['baseUrl'])) {
				throw new Exception("Please specify 'baseUrl' for the '$name' target.");
			}
			usort($target['depends'], function ($a, $b) use ($bundleOrders) {
				if ($bundleOrders[$a] == $bundleOrders[$b]) {
					return 0;
				} else {
					return $bundleOrders[$a] > $bundleOrders[$b] ? 1 : -1;
				}
			});
			$target['class'] = 'yii\\web\\AssetBundle';
			$targets[$name] = Yii::createObject($target);
		}
		return $targets;
	}

Qiang Xue committed
293
	/**
294 295
	 * Publishes given asset bundles.
	 * @param \yii\web\AssetBundle[] $bundles asset bundles to be published.
Qiang Xue committed
296
	 */
297
	protected function publishBundles($bundles)
Qiang Xue committed
298
	{
299
		echo "\nPublishing bundles:\n";
300
		$assetManager = $this->getAssetManager();
301
		foreach ($bundles as $name => $bundle) {
302
			$bundle->publish($assetManager);
303
			echo "  '".$name."' published.\n";
Qiang Xue committed
304
		}
305
		echo "\n";
Qiang Xue committed
306 307 308
	}

	/**
309 310 311 312 313 314
	 * Builds output asset bundle.
	 * @param \yii\web\AssetBundle $target output asset bundle
	 * @param string $type either "js" or "css".
	 * @param \yii\web\AssetBundle[] $bundles source asset bundles.
	 * @param integer $timestamp current timestamp.
	 * @throws Exception on failure.
Qiang Xue committed
315
	 */
Qiang Xue committed
316
	protected function buildTarget($target, $type, $bundles, $timestamp)
Qiang Xue committed
317
	{
Qiang Xue committed
318
		$outputFile = strtr($target->$type, array(
Qiang Xue committed
319 320 321
			'{ts}' => $timestamp,
		));
		$inputFiles = array();
Qiang Xue committed
322 323

		foreach ($target->depends as $name) {
Qiang Xue committed
324 325
			if (isset($bundles[$name])) {
				foreach ($bundles[$name]->$type as $file) {
326
					$inputFiles[] = $bundles[$name]->basePath . '/' . $file;
Qiang Xue committed
327 328
				}
			} else {
329
				throw new Exception("Unknown bundle: '{$name}'");
Qiang Xue committed
330 331 332
			}
		}
		if ($type === 'js') {
Qiang Xue committed
333
			$this->compressJsFiles($inputFiles, $target->basePath . '/' . $outputFile);
Qiang Xue committed
334
		} else {
Qiang Xue committed
335
			$this->compressCssFiles($inputFiles, $target->basePath . '/' . $outputFile);
Qiang Xue committed
336
		}
Qiang Xue committed
337
		$target->$type = array($outputFile);
Qiang Xue committed
338 339
	}

340
	/**
341 342 343 344
	 * Adjust dependencies between asset bundles in the way source bundles begin to depend on output ones.
	 * @param \yii\web\AssetBundle[] $targets output asset bundles.
	 * @param \yii\web\AssetBundle[] $bundles source asset bundles.
	 * @return \yii\web\AssetBundle[] output asset bundles.
345
	 */
Qiang Xue committed
346 347
	protected function adjustDependency($targets, $bundles)
	{
348 349
		echo "Creating new bundle configuration...\n";

Qiang Xue committed
350 351 352
		$map = array();
		foreach ($targets as $name => $target) {
			foreach ($target->depends as $bundle) {
353
				$map[$bundle] = $name;
Qiang Xue committed
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
			}
		}

		foreach ($targets as $name => $target) {
			$depends = array();
			foreach ($target->depends as $bn) {
				foreach ($bundles[$bn]->depends as $bundle) {
					$depends[$map[$bundle]] = true;
				}
			}
			unset($depends[$name]);
			$target->depends = array_keys($depends);
		}

		// detect possible circular dependencies
		foreach ($targets as $name => $target) {
			$registered = array();
			$this->registerBundle($targets, $name, $registered);
		}

		foreach ($map as $bundle => $target) {
			$targets[$bundle] = Yii::createObject(array(
				'class' => 'yii\\web\\AssetBundle',
				'depends' => array($target),
			));
		}
Qiang Xue committed
380 381 382
		return $targets;
	}

383 384 385 386 387 388 389
	/**
	 * Registers asset bundles including their dependencies.
	 * @param \yii\web\AssetBundle[] $bundles asset bundles list.
	 * @param string $name bundle name.
	 * @param array $registered stores already registered names.
	 * @throws \yii\console\Exception if circular dependency is detected.
	 */
Qiang Xue committed
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
	protected function registerBundle($bundles, $name, &$registered)
	{
		if (!isset($registered[$name])) {
			$registered[$name] = false;
			$bundle = $bundles[$name];
			foreach ($bundle->depends as $depend) {
				$this->registerBundle($bundles, $depend, $registered);
			}
			unset($registered[$name]);
			$registered[$name] = true;
		} elseif ($registered[$name] === false) {
			throw new Exception("A circular dependency is detected for target '$name'.");
		}
	}

405 406 407 408
	/**
	 * Saves new asset bundles configuration.
	 * @param \yii\web\AssetBundle[] $targets list of asset bundles to be saved.
	 * @param string $bundleFile output file name.
409
	 * @throws \yii\console\Exception on failure.
410
	 */
Qiang Xue committed
411 412 413 414 415 416 417 418 419 420 421 422
	protected function saveTargets($targets, $bundleFile)
	{
		$array = array();
		foreach ($targets as $name => $target) {
			foreach (array('js', 'css', 'depends', 'basePath', 'baseUrl') as $prop) {
				if (!empty($target->$prop)) {
					$array[$name][$prop] = $target->$prop;
				}
			}
		}
		$array = var_export($array, true);
		$version = date('Y-m-d H:i:s', time());
423
		$bytesWritten = file_put_contents($bundleFile, <<<EOD
Qiang Xue committed
424 425
<?php
/**
426
 * This file is generated by the "yii script" command.
427
 * DO NOT MODIFY THIS FILE DIRECTLY.
Qiang Xue committed
428 429 430 431 432
 * @version $version
 */
return $array;
EOD
		);
433 434 435 436
		if ($bytesWritten <= 0) {
			throw new Exception("Unable to write output bundle configuration at '{$bundleFile}'.");
		}
		echo "Output bundle configuration created at '{$bundleFile}'.\n";
Qiang Xue committed
437 438
	}

439 440 441 442
	/**
	 * Compresses given Java Script files and combines them into the single one.
	 * @param array $inputFiles list of source file names.
	 * @param string $outputFile output file name.
443
	 * @throws \yii\console\Exception on failure
444
	 */
Qiang Xue committed
445 446
	protected function compressJsFiles($inputFiles, $outputFile)
	{
447 448 449 450
		if (empty($inputFiles)) {
			return;
		}
		echo "  Compressing JavaScript files...\n";
451 452 453
		if (is_string($this->jsCompressor)) {
			$tmpFile = $outputFile . '.tmp';
			$this->combineJsFiles($inputFiles, $tmpFile);
454
			echo shell_exec(strtr($this->jsCompressor, array(
455 456
				'{from}' => escapeshellarg($tmpFile),
				'{to}' => escapeshellarg($outputFile),
457 458 459
			)));
			@unlink($tmpFile);
		} else {
460
			call_user_func($this->jsCompressor, $this, $inputFiles, $outputFile);
461
		}
462 463 464 465
		if (!file_exists($outputFile)) {
			throw new Exception("Unable to compress JavaScript files into '{$outputFile}'.");
		}
		echo "  JavaScript files compressed into '{$outputFile}'.\n";
Qiang Xue committed
466 467
	}

468 469 470 471
	/**
	 * Compresses given CSS files and combines them into the single one.
	 * @param array $inputFiles list of source file names.
	 * @param string $outputFile output file name.
472
	 * @throws \yii\console\Exception on failure
473
	 */
Qiang Xue committed
474 475
	protected function compressCssFiles($inputFiles, $outputFile)
	{
476 477 478 479
		if (empty($inputFiles)) {
			return;
		}
		echo "  Compressing CSS files...\n";
480 481 482
		if (is_string($this->cssCompressor)) {
			$tmpFile = $outputFile . '.tmp';
			$this->combineCssFiles($inputFiles, $tmpFile);
483
			echo shell_exec(strtr($this->cssCompressor, array(
484 485
				'{from}' => escapeshellarg($tmpFile),
				'{to}' => escapeshellarg($outputFile),
486
			)));
487
			@unlink($tmpFile);
488
		} else {
489
			call_user_func($this->cssCompressor, $this, $inputFiles, $outputFile);
490
		}
491 492 493 494
		if (!file_exists($outputFile)) {
			throw new Exception("Unable to compress CSS files into '{$outputFile}'.");
		}
		echo "  CSS files compressed into '{$outputFile}'.\n";
495 496
	}

497 498 499 500 501 502
	/**
	 * Combines Java Script files into a single one.
	 * @param array $inputFiles source file names.
	 * @param string $outputFile output file name.
	 */
	public function combineJsFiles($inputFiles, $outputFile)
503 504
	{
		$content = '';
505
		foreach ($inputFiles as $file) {
506 507 508 509
			$content .= "/*** BEGIN FILE: $file ***/\n"
				. file_get_contents($file)
				. "/*** END FILE: $file ***/\n";
		}
510
		file_put_contents($outputFile, $content);
511 512
	}

513 514 515 516 517 518
	/**
	 * Combines CSS files into a single one.
	 * @param array $inputFiles source file names.
	 * @param string $outputFile output file name.
	 */
	public function combineCssFiles($inputFiles, $outputFile)
519 520
	{
		$content = '';
521
		foreach ($inputFiles as $file) {
522
			$content .= "/*** BEGIN FILE: $file ***/\n"
523
				. $this->adjustCssUrl(file_get_contents($file), dirname($file), dirname($outputFile))
524 525
				. "/*** END FILE: $file ***/\n";
		}
526
		file_put_contents($outputFile, $content);
527 528
	}

529 530 531 532 533 534 535 536 537
	/**
	 * Adjusts CSS content allowing URL references pointing to the original resources.
	 * @param string $cssContent source CSS content.
	 * @param string $inputFilePath input CSS file name.
	 * @param string $outputFilePath output CSS file name.
	 * @return string adjusted CSS content.
	 */
	protected function adjustCssUrl($cssContent, $inputFilePath, $outputFilePath)
	{
538 539 540 541 542 543 544 545 546
		$sharedPathParts = array();
		$inputFilePathParts = explode('/', $inputFilePath);
		$inputFilePathPartsCount = count($inputFilePathParts);
		$outputFilePathParts = explode('/', $outputFilePath);
		$outputFilePathPartsCount = count($outputFilePathParts);
		for ($i =0; $i < $inputFilePathPartsCount && $i < $outputFilePathPartsCount; $i++) {
			if ($inputFilePathParts[$i] == $outputFilePathParts[$i]) {
				$sharedPathParts[] = $inputFilePathParts[$i];
			} else {
547 548 549
				break;
			}
		}
550 551
		$sharedPath = implode('/', $sharedPathParts);

552 553 554 555 556 557 558 559 560
		$inputFileRelativePath = trim(str_replace($sharedPath, '', $inputFilePath), '/');
		$outputFileRelativePath = trim(str_replace($sharedPath, '', $outputFilePath), '/');
		$inputFileRelativePathParts = explode('/', $inputFileRelativePath);
		$outputFileRelativePathParts = explode('/', $outputFileRelativePath);

		$callback = function($matches) use ($inputFileRelativePathParts, $outputFileRelativePathParts) {
			$fullMatch = $matches[0];
			$inputUrl = $matches[1];

561 562 563 564
			if (preg_match('/https?:\/\//is', $inputUrl)) {
				return $fullMatch;
			}

565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
			$outputUrlParts = array_fill(0, count($outputFileRelativePathParts), '..');
			$outputUrlParts = array_merge($outputUrlParts, $inputFileRelativePathParts);

			if (strpos($inputUrl, '/') !== false) {
				$inputUrlParts = explode('/', $inputUrl);
				foreach ($inputUrlParts as $key => $inputUrlPart) {
					if ($inputUrlPart == '..') {
						array_pop($outputUrlParts);
						unset($inputUrlParts[$key]);
					}
				}
				$outputUrlParts[] = implode('/', $inputUrlParts);
			} else {
				$outputUrlParts[] = $inputUrl;
			}
			$outputUrl = implode('/', $outputUrlParts);
581 582

			return str_replace($inputUrl, $outputUrl, $fullMatch);
583
		};
584

585
		$cssContent = preg_replace_callback('/url\(["\']?([^"]*)["\']?\)/is', $callback, $cssContent);
586 587 588 589

		return $cssContent;
	}

590 591 592 593
	/**
	 * Creates template of configuration file for [[actionCompress]].
	 * @param string $configFile output file name.
	 */
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612
	public function actionTemplate($configFile)
	{
		$template = <<<EOD
<?php

return array(
	//
	'bundles' => require('path/to/bundles.php'),
	//
	'extensions' => require('path/to/namespaces.php'),
	//
	'targets' => array(
		'all' => array(
			'basePath' => __DIR__,
			'baseUrl' => '/test',
			'js' => 'all-{ts}.js',
			'css' => 'all-{ts}.css',
		),
	),
Qiang Xue committed
613

614 615 616 617 618 619
	'assetManager' => array(
		'basePath' => __DIR__,
		'baseUrl' => '/test',
	),
);
EOD;
620 621 622 623 624 625 626 627 628 629 630
		if (file_exists($configFile)) {
			if (!$this->confirm("File '{$configFile}' already exists. Do you wish to overwrite it?")) {
				return;
			}
		}
		$bytesWritten = file_put_contents($configFile, $template);
		if ($bytesWritten<=0) {
			echo "Error: unable to write file '{$configFile}'!\n\n";
		} else {
			echo "Configuration file template created at '{$configFile}'.\n\n";
		}
Qiang Xue committed
631
	}
Zander Baldwin committed
632
}