From f4cb1e4a0acd70728b2fb1393be63ca2b50bc8c6 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Thu, 16 May 2013 14:21:49 +0300 Subject: [PATCH 01/12] Unit test for "console\controllers\AssetController" has been created. --- .../console/controllers/AssetControllerTest.php | 90 ++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 tests/unit/framework/console/controllers/AssetControllerTest.php diff --git a/tests/unit/framework/console/controllers/AssetControllerTest.php b/tests/unit/framework/console/controllers/AssetControllerTest.php new file mode 100644 index 0000000..42eb320 --- /dev/null +++ b/tests/unit/framework/console/controllers/AssetControllerTest.php @@ -0,0 +1,90 @@ +testFilePath = Yii::getAlias('@yiiunit/runtime') . DIRECTORY_SEPARATOR . get_class($this); + $this->createDir($this->testFilePath); + } + + public function tearDown() + { + $this->removeDir($this->testFilePath); + } + + /** + * Creates directory. + * @param $dirName directory full name. + */ + protected function createDir($dirName) + { + if (!file_exists($dirName)) { + mkdir($dirName, 0777, true); + } + } + + /** + * Removes directory. + * @param $dirName directory full name + */ + protected function removeDir($dirName) + { + if (!empty($dirName) && file_exists($dirName)) { + exec("rm -rf {$dirName}"); + } + } + + /** + * Creates test asset controller instance. + * @return AssetController + */ + protected function createAssetController() + { + $module = $this->getMock('yii\\base\\Module', array('fake'), array('console')); + $assetController = new AssetController('asset', $module); + $assetController->jsCompressor = 'cp {from} {to}'; + $assetController->cssCompressor = 'cp {from} {to}'; + return $assetController; + } + + /** + * Emulates running of the asset controller action. + * @param string $actionId id of action to be run. + * @param array $args action arguments. + * @return string command output. + */ + protected function runAssetControllerAction($actionId, array $args=array()) + { + $controller = $this->createAssetController(); + ob_start(); + ob_implicit_flush(false); + $params = array( + \yii\console\Request::ANONYMOUS_PARAMS => $args + ); + $controller->run($actionId, $params); + return ob_get_clean(); + } + + // Tests : + + public function testActionTemplate() + { + $configFileName = $this->testFilePath . DIRECTORY_SEPARATOR . 'config.php'; + $this->runAssetControllerAction('template', array($configFileName)); + + $this->assertTrue(file_exists($configFileName), 'Unable to create config file template!'); + } +} From 509261221777fa8122f1bda9b707f6c611ca234d Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Thu, 16 May 2013 14:34:27 +0300 Subject: [PATCH 02/12] Method "console\controllers\AssetController::actionTemplate()" has been updated to be more verbose. --- .../console/controllers/AssetControllerTest.php | 1 + yii/console/controllers/AssetController.php | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/unit/framework/console/controllers/AssetControllerTest.php b/tests/unit/framework/console/controllers/AssetControllerTest.php index 42eb320..b19b426 100644 --- a/tests/unit/framework/console/controllers/AssetControllerTest.php +++ b/tests/unit/framework/console/controllers/AssetControllerTest.php @@ -55,6 +55,7 @@ class AssetControllerTest extends TestCase { $module = $this->getMock('yii\\base\\Module', array('fake'), array('console')); $assetController = new AssetController('asset', $module); + $assetController->interactive = false; $assetController->jsCompressor = 'cp {from} {to}'; $assetController->cssCompressor = 'cp {from} {to}'; return $assetController; diff --git a/yii/console/controllers/AssetController.php b/yii/console/controllers/AssetController.php index aab489b..d90f767 100644 --- a/yii/console/controllers/AssetController.php +++ b/yii/console/controllers/AssetController.php @@ -322,6 +322,10 @@ EOD file_put_contents($tmpFile, $content); } + /** + * Creates template of configuration file for [[actionCompress]]. + * @param string $configFile output file name. + */ public function actionTemplate($configFile) { $template = <<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"; + } } } From 98b3cc07cd4c260e2ae274a01956150040285d32 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Thu, 16 May 2013 15:17:42 +0300 Subject: [PATCH 03/12] Test case "AssetControllerTest::testActionCompress()" has been created as draft. --- .../console/controllers/AssetControllerTest.php | 89 +++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/tests/unit/framework/console/controllers/AssetControllerTest.php b/tests/unit/framework/console/controllers/AssetControllerTest.php index b19b426..7d58a01 100644 --- a/tests/unit/framework/console/controllers/AssetControllerTest.php +++ b/tests/unit/framework/console/controllers/AssetControllerTest.php @@ -79,13 +79,100 @@ class AssetControllerTest extends TestCase return ob_get_clean(); } + /** + * Creates test compress config. + * @return array config array. + */ + protected function createCompressConfig() + { + $baseUrl = '/test'; + + $assetsBasePath = $this->testFilePath.DIRECTORY_SEPARATOR.'assets'; + $this->createDir($assetsBasePath); + + $config = array( + 'bundles' => $this->createBundleConfig(), + 'targets' => array( + 'all' => array( + 'basePath' => $assetsBasePath, + 'baseUrl' => $baseUrl, + 'js' => 'all-{ts}.js', + 'css' => 'all-{ts}.css', + ), + ), + 'assetManager' => array( + 'basePath' => $assetsBasePath, + 'baseUrl' => $baseUrl, + ), + ); + return $config; + } + + /** + * Creates test bundle configuration. + * @return array bundle config. + */ + protected function createBundleConfig() + { + $baseUrl = '/test'; + $bundles = array( + 'app' => array( + 'basePath' => $this->testFilePath, + 'baseUrl' => $baseUrl, + 'css' => array( + 'css/test.css', + ), + 'js' => array( + 'js/test.js', + ), + 'depends' => array( + 'yii', + ), + ), + ); + return $bundles; + } + + /** + * Creates test bundles configuration file. + * @param string $fileName output filename. + * @return boolean success. + */ + protected function createBundleFile($fileName) + { + $content = 'createBundleConfig(), true).';'; + return (file_put_contents($fileName, $content) > 0); + } + + /** + * Creates test compress config file. + * @param string $fileName output file name. + * @return boolean success. + */ + protected function createCompressConfigFile($fileName) + { + $content = 'createCompressConfig(), true).';'; + return (file_put_contents($fileName, $content) > 0); + } + // Tests : public function testActionTemplate() { $configFileName = $this->testFilePath . DIRECTORY_SEPARATOR . 'config.php'; $this->runAssetControllerAction('template', array($configFileName)); - $this->assertTrue(file_exists($configFileName), 'Unable to create config file template!'); } + + public function testActionCompress() + { + $configFile = $this->testFilePath . DIRECTORY_SEPARATOR . 'config.php'; + $this->createCompressConfigFile($configFile); + $bundleFile = $this->testFilePath . DIRECTORY_SEPARATOR . 'bundle.php'; + $this->createBundleFile($bundleFile); + + $this->runAssetControllerAction('compress', array($configFile, $bundleFile)); + + $this->markTestIncomplete(); + } } From f9b06e1e6dd0fafeee13807b0675651f9dc80617 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Thu, 16 May 2013 15:38:46 +0300 Subject: [PATCH 04/12] Unknown property "yii\console\controllers\AssetController::publishOptions" error has been fixed. --- tests/unit/framework/console/controllers/AssetControllerTest.php | 4 ++-- yii/console/controllers/AssetController.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/framework/console/controllers/AssetControllerTest.php b/tests/unit/framework/console/controllers/AssetControllerTest.php index 7d58a01..ff1d291 100644 --- a/tests/unit/framework/console/controllers/AssetControllerTest.php +++ b/tests/unit/framework/console/controllers/AssetControllerTest.php @@ -125,9 +125,9 @@ class AssetControllerTest extends TestCase 'js' => array( 'js/test.js', ), - 'depends' => array( + /*'depends' => array( 'yii', - ), + ),*/ ), ); return $bundles; diff --git a/yii/console/controllers/AssetController.php b/yii/console/controllers/AssetController.php index d90f767..92a9467 100644 --- a/yii/console/controllers/AssetController.php +++ b/yii/console/controllers/AssetController.php @@ -41,7 +41,7 @@ class AssetController extends Controller $this->loadConfiguration($configFile); $bundles = $this->loadBundles($this->bundles, $this->extensions); $targets = $this->loadTargets($this->targets, $bundles); - $this->publishBundles($bundles, $this->publishOptions); + $this->publishBundles($bundles, $this->assetManager); $timestamp = time(); foreach ($targets as $target) { if (!empty($target->js)) { From 0ecdcae54a8cdca14a6a3fcea47dc7ca9a5350ba Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Thu, 16 May 2013 16:31:29 +0300 Subject: [PATCH 05/12] Method "yii\console\controllers\AssetController::buildTarget()" has been fixed to compose input file name correctly. --- .../console/controllers/AssetControllerTest.php | 30 +++++++++++++++++++++- yii/console/controllers/AssetController.php | 2 +- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/tests/unit/framework/console/controllers/AssetControllerTest.php b/tests/unit/framework/console/controllers/AssetControllerTest.php index ff1d291..c798409 100644 --- a/tests/unit/framework/console/controllers/AssetControllerTest.php +++ b/tests/unit/framework/console/controllers/AssetControllerTest.php @@ -114,7 +114,8 @@ class AssetControllerTest extends TestCase */ protected function createBundleConfig() { - $baseUrl = '/test'; + $baseUrl = ''; + //$baseUrl = '/test'; $bundles = array( 'app' => array( 'basePath' => $this->testFilePath, @@ -155,6 +156,19 @@ class AssetControllerTest extends TestCase return (file_put_contents($fileName, $content) > 0); } + /** + * Creates test asset file. + * @param string $fileRelativeName file name relative to [[testFilePath]] + * @param string $content file content + * @return boolean success. + */ + protected function createTestAssetFile($fileRelativeName, $content) + { + $fileFullName = $this->testFilePath.DIRECTORY_SEPARATOR.$fileRelativeName; + $this->createDir(dirname($fileFullName)); + return (file_put_contents($fileFullName, $content) > 0); + } + // Tests : public function testActionTemplate() @@ -166,6 +180,20 @@ class AssetControllerTest extends TestCase public function testActionCompress() { + $this->createTestAssetFile( + 'css/test.css', + 'body { + padding-top: 20px; + padding-bottom: 60px; + }' + ); + $this->createTestAssetFile( + 'js/test.js', + "function() { + alert('Test message'); + }" + ); + $configFile = $this->testFilePath . DIRECTORY_SEPARATOR . 'config.php'; $this->createCompressConfigFile($configFile); $bundleFile = $this->testFilePath . DIRECTORY_SEPARATOR . 'bundle.php'; diff --git a/yii/console/controllers/AssetController.php b/yii/console/controllers/AssetController.php index 92a9467..0dd8181 100644 --- a/yii/console/controllers/AssetController.php +++ b/yii/console/controllers/AssetController.php @@ -182,7 +182,7 @@ class AssetController extends Controller foreach ($target->depends as $name) { if (isset($bundles[$name])) { foreach ($bundles[$name]->$type as $file) { - $inputFiles[] = $bundles[$name]->basePath . '/' . $file; + $inputFiles[] = $bundles[$name]->basePath . $file; } } else { throw new Exception("Unknown bundle: $name"); From 8d17fcccaa1d9df3541786ba879960602b081e9e Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Thu, 16 May 2013 16:42:06 +0300 Subject: [PATCH 06/12] Error "yii\console\controllers\AssetController::combineCssFiles()" has been fixed. --- .../framework/console/controllers/AssetControllerTest.php | 12 +++++++++--- yii/console/controllers/AssetController.php | 3 ++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/unit/framework/console/controllers/AssetControllerTest.php b/tests/unit/framework/console/controllers/AssetControllerTest.php index c798409..1b2fdcc 100644 --- a/tests/unit/framework/console/controllers/AssetControllerTest.php +++ b/tests/unit/framework/console/controllers/AssetControllerTest.php @@ -96,8 +96,10 @@ class AssetControllerTest extends TestCase 'all' => array( 'basePath' => $assetsBasePath, 'baseUrl' => $baseUrl, - 'js' => 'all-{ts}.js', - 'css' => 'all-{ts}.css', + 'js' => 'all.js', + //'js' => 'all-{ts}.js', + 'css' => 'all.css', + //'css' => 'all-{ts}.css', ), ), 'assetManager' => array( @@ -201,6 +203,10 @@ class AssetControllerTest extends TestCase $this->runAssetControllerAction('compress', array($configFile, $bundleFile)); - $this->markTestIncomplete(); + $assetsBasePath = $this->testFilePath.DIRECTORY_SEPARATOR.'assets'; + $compressedCssFileName = $assetsBasePath.DIRECTORY_SEPARATOR.'all.css'; + $this->assertTrue(file_exists($compressedCssFileName), 'Unable to compress CSS files!'); + $compressedJsFileName = $assetsBasePath.DIRECTORY_SEPARATOR.'all.js'; + $this->assertTrue(file_exists($compressedJsFileName), 'Unable to compress JS files!'); } } diff --git a/yii/console/controllers/AssetController.php b/yii/console/controllers/AssetController.php index 0dd8181..cbf160c 100644 --- a/yii/console/controllers/AssetController.php +++ b/yii/console/controllers/AssetController.php @@ -291,9 +291,10 @@ EOD $tmpFile = $outputFile . '.tmp'; $this->combineCssFiles($inputFiles, $tmpFile); $log = shell_exec(strtr($this->cssCompressor, array( - '{from}' => $inputFiles, + '{from}' => $tmpFile, '{to}' => $outputFile, ))); + @unlink($tmpFile); } else { $log = call_user_func($this->cssCompressor, $this, $inputFiles, $outputFile); } From 44eaad4f112fc74c2e37059499ff4350d4e577bb Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Thu, 16 May 2013 17:53:28 +0300 Subject: [PATCH 07/12] Doc comments at "yii\console\controllers\AssetController" has been filled up. --- yii/console/controllers/AssetController.php | 107 +++++++++++++++++++++++++--- 1 file changed, 99 insertions(+), 8 deletions(-) diff --git a/yii/console/controllers/AssetController.php b/yii/console/controllers/AssetController.php index cbf160c..126771b 100644 --- a/yii/console/controllers/AssetController.php +++ b/yii/console/controllers/AssetController.php @@ -17,12 +17,25 @@ use yii\console\Controller; */ class AssetController extends Controller { + /** + * @var string controller default action ID. + */ public $defaultAction = 'compress'; - + /** + * @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. + */ public $bundles = array(); + /** + * @var array list of paths to the extensions, which assets should be also compressed. + * Each path should contain asset manifest file named "assets.php". + */ public $extensions = array(); /** - * @var array + * @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: * ~~~ * 'all' => array( * 'css' => 'all.css', @@ -30,12 +43,41 @@ class AssetController extends Controller * 'depends' => array( ... ), * ) * ~~~ + * File names can contain placeholder "{ts}", which will be filled by current timestamp, while + * file creation. */ public $targets = array(); + /** + * @var array configuration for [[yii\web\AssetManager]] instance, which will be used + * for assets publishing. + */ public $assetManager = array(); + /** + * @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. + * If an array, it is treated as PHP callback, which should perform the compression. + * + * Default value relies on usage of "Closure Compiler" + * @see https://developers.google.com/closure/compiler/ + */ public $jsCompressor = 'java -jar compiler.jar --js {from} --js_output_file {to}'; + /** + * @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. + * If an array, it is treated as PHP callback, which should perform the compression. + * + * Default value relies on usage of "YUI Compressor" + * @see https://github.com/yui/yuicompressor/ + */ public $cssCompressor = 'java -jar yuicompressor.jar {from} -o {to}'; + /** + * Compresses the asset files according to the given configuration. + * @param string $configFile configuration file name. + * @param string $bundleFile + */ public function actionCompress($configFile, $bundleFile) { $this->loadConfiguration($configFile); @@ -56,6 +98,11 @@ class AssetController extends Controller $this->saveTargets($targets, $bundleFile); } + /** + * Applies configuration from the given file to self instance. + * @param string $configFile configuration file name. + * @throws \yii\console\Exception on failure. + */ protected function loadConfiguration($configFile) { foreach (require($configFile) as $name => $value) { @@ -74,6 +121,12 @@ class AssetController extends Controller } } + /** + * Creates full list of asset bundles. + * @param array[] $bundles list of asset bundle configurations. + * @param array $extensions list of the extension paths. + * @return \yii\web\AssetBundle[] list of asset bundles. + */ protected function loadBundles($bundles, $extensions) { $result = array(); @@ -96,6 +149,12 @@ class AssetController extends Controller return $result; } + /** + * @param array $targets + * @param \yii\web\AssetBundle[] $bundles list of asset bundles. + * @return \yii\web\AssetBundle[] + * @throws \yii\console\Exception on failure. + */ protected function loadTargets($targets, $bundles) { // build the dependency order of bundles @@ -196,6 +255,11 @@ class AssetController extends Controller $target->$type = array($outputFile); } + /** + * @param \yii\web\AssetBundle[] $targets + * @param \yii\web\AssetBundle[] $bundles + * @return \yii\web\AssetBundle[] + */ protected function adjustDependency($targets, $bundles) { $map = array(); @@ -231,6 +295,13 @@ class AssetController extends Controller return $targets; } + /** + * 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. + */ protected function registerBundle($bundles, $name, &$registered) { if (!isset($registered[$name])) { @@ -270,6 +341,11 @@ EOD ); } + /** + * 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. + */ protected function compressJsFiles($inputFiles, $outputFile) { if (is_string($this->jsCompressor)) { @@ -285,6 +361,11 @@ EOD } } + /** + * 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. + */ protected function compressCssFiles($inputFiles, $outputFile) { if (is_string($this->cssCompressor)) { @@ -300,27 +381,37 @@ EOD } } - public function combineJsFiles($files, $tmpFile) + /** + * 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) { $content = ''; - foreach ($files as $file) { + foreach ($inputFiles as $file) { $content .= "/*** BEGIN FILE: $file ***/\n" . file_get_contents($file) . "/*** END FILE: $file ***/\n"; } - file_put_contents($tmpFile, $content); + file_put_contents($outputFile, $content); } - public function combineCssFiles($files, $tmpFile) + /** + * Combines CSS files into a single one. + * @param array $inputFiles source file names. + * @param string $outputFile output file name. + */ + public function combineCssFiles($inputFiles, $outputFile) { // todo: adjust url() references in CSS files $content = ''; - foreach ($files as $file) { + foreach ($inputFiles as $file) { $content .= "/*** BEGIN FILE: $file ***/\n" . file_get_contents($file) . "/*** END FILE: $file ***/\n"; } - file_put_contents($tmpFile, $content); + file_put_contents($outputFile, $content); } /** From 563f992bec1d4f5d116a2fe39a20008ae282cae5 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Thu, 16 May 2013 19:49:15 +0300 Subject: [PATCH 08/12] Unit test for "yii\console\controllers\AssetController" has been refactored. --- .../console/controllers/AssetControllerTest.php | 144 +++++++++++++-------- 1 file changed, 92 insertions(+), 52 deletions(-) diff --git a/tests/unit/framework/console/controllers/AssetControllerTest.php b/tests/unit/framework/console/controllers/AssetControllerTest.php index 1b2fdcc..d32cb2d 100644 --- a/tests/unit/framework/console/controllers/AssetControllerTest.php +++ b/tests/unit/framework/console/controllers/AssetControllerTest.php @@ -4,7 +4,7 @@ use yiiunit\TestCase; use yii\console\controllers\AssetController; /** - * Unit test for [[yii\console\controllers\AssetController]]. + * Unit test for [[\yii\console\controllers\AssetController]]. * @see AssetController */ class AssetControllerTest extends TestCase @@ -13,11 +13,17 @@ class AssetControllerTest extends TestCase * @var string path for the test files. */ protected $testFilePath = ''; + /** + * @var string test assets path. + */ + protected $testAssetsBasePath = ''; public function setUp() { $this->testFilePath = Yii::getAlias('@yiiunit/runtime') . DIRECTORY_SEPARATOR . get_class($this); $this->createDir($this->testFilePath); + $this->testAssetsBasePath = $this->testFilePath . DIRECTORY_SEPARATOR . 'assets'; + $this->createDir($this->testAssetsBasePath); } public function tearDown() @@ -81,29 +87,26 @@ class AssetControllerTest extends TestCase /** * Creates test compress config. + * @param array[] $bundles asset bundles config. * @return array config array. */ - protected function createCompressConfig() + protected function createCompressConfig(array $bundles) { $baseUrl = '/test'; - $assetsBasePath = $this->testFilePath.DIRECTORY_SEPARATOR.'assets'; - $this->createDir($assetsBasePath); $config = array( - 'bundles' => $this->createBundleConfig(), + 'bundles' => $this->createBundleConfig($bundles), 'targets' => array( 'all' => array( - 'basePath' => $assetsBasePath, + 'basePath' => $this->testAssetsBasePath, 'baseUrl' => $baseUrl, 'js' => 'all.js', - //'js' => 'all-{ts}.js', 'css' => 'all.css', - //'css' => 'all-{ts}.css', ), ), 'assetManager' => array( - 'basePath' => $assetsBasePath, + 'basePath' => $this->testAssetsBasePath, 'baseUrl' => $baseUrl, ), ); @@ -112,63 +115,74 @@ class AssetControllerTest extends TestCase /** * Creates test bundle configuration. + * @param array[] $bundles asset bundles config. * @return array bundle config. */ - protected function createBundleConfig() + protected function createBundleConfig(array $bundles) { - $baseUrl = ''; - //$baseUrl = '/test'; - $bundles = array( - 'app' => array( - 'basePath' => $this->testFilePath, - 'baseUrl' => $baseUrl, - 'css' => array( - 'css/test.css', - ), - 'js' => array( - 'js/test.js', - ), - /*'depends' => array( - 'yii', - ),*/ - ), - ); + foreach ($bundles as $name => $config) { + if (!array_key_exists('basePath', $config)) { + $bundles[$name]['basePath'] = $this->testFilePath; + } + if (!array_key_exists('baseUrl', $config)) { + $bundles[$name]['baseUrl'] = ''; + } + } return $bundles; } /** * Creates test bundles configuration file. * @param string $fileName output filename. - * @return boolean success. + * @param array[] $bundles asset bundles config. + * @throws Exception on failure. */ - protected function createBundleFile($fileName) + protected function createBundleFile($fileName, array $bundles) { - $content = 'createBundleConfig(), true).';'; - return (file_put_contents($fileName, $content) > 0); + $content = 'createBundleConfig($bundles), true).';'; + if (file_put_contents($fileName, $content) <= 0) { + throw new \Exception("Unable to create file '{$fileName}'!"); + } } /** * Creates test compress config file. * @param string $fileName output file name. - * @return boolean success. + * @param array[] $bundles asset bundles config. + * @throws Exception on failure. */ - protected function createCompressConfigFile($fileName) + protected function createCompressConfigFile($fileName, array $bundles) { - $content = 'createCompressConfig(), true).';'; - return (file_put_contents($fileName, $content) > 0); + $content = 'createCompressConfig($bundles), true).';'; + if (file_put_contents($fileName, $content) <= 0) { + throw new \Exception("Unable to create file '{$fileName}'!"); + } } /** * Creates test asset file. * @param string $fileRelativeName file name relative to [[testFilePath]] * @param string $content file content - * @return boolean success. + * @throws Exception on failure. */ - protected function createTestAssetFile($fileRelativeName, $content) + protected function createAssetSourceFile($fileRelativeName, $content) { $fileFullName = $this->testFilePath.DIRECTORY_SEPARATOR.$fileRelativeName; $this->createDir(dirname($fileFullName)); - return (file_put_contents($fileFullName, $content) > 0); + if (file_put_contents($fileFullName, $content)<=0) { + throw new \Exception("Unable to create file '{$fileFullName}'!"); + } + } + + /** + * Creates a list of asset source files. + * @param array $files assert source files in format: file/relative/name => fileContent + */ + protected function createAssertSourceFiles(array $files) + { + foreach ($files as $name => $content) { + $this->createAssetSourceFile($name, $content); + } } // Tests : @@ -182,31 +196,57 @@ class AssetControllerTest extends TestCase public function testActionCompress() { - $this->createTestAssetFile( - 'css/test.css', - 'body { + // Given : + $cssFiles = array( + 'css/test_body.css' => 'body { padding-top: 20px; padding-bottom: 60px; - }' + }', + 'css/test_footer.css' => '.footer { + margin: 20px; + display: block; + }', ); - $this->createTestAssetFile( - 'js/test.js', - "function() { + $this->createAssertSourceFiles($cssFiles); + + $jsFiles = array( + 'js/test_alert.js' => "function test() { alert('Test message'); - }" + }", + 'js/test_sum_ab.js' => "function sumAB(a, b) { + return a + b; + }", ); + $this->createAssertSourceFiles($jsFiles); - $configFile = $this->testFilePath . DIRECTORY_SEPARATOR . 'config.php'; - $this->createCompressConfigFile($configFile); + $bundles = array( + 'app' => array( + 'css' => array_keys($cssFiles), + 'js' => array_keys($jsFiles), + ), + );; $bundleFile = $this->testFilePath . DIRECTORY_SEPARATOR . 'bundle.php'; - $this->createBundleFile($bundleFile); + $this->createBundleFile($bundleFile, $bundles); + + $configFile = $this->testFilePath . DIRECTORY_SEPARATOR . 'config.php'; + $this->createCompressConfigFile($configFile, $bundles); + // When : $this->runAssetControllerAction('compress', array($configFile, $bundleFile)); - $assetsBasePath = $this->testFilePath.DIRECTORY_SEPARATOR.'assets'; - $compressedCssFileName = $assetsBasePath.DIRECTORY_SEPARATOR.'all.css'; + // Then : + $compressedCssFileName = $this->testAssetsBasePath . DIRECTORY_SEPARATOR . 'all.css'; $this->assertTrue(file_exists($compressedCssFileName), 'Unable to compress CSS files!'); - $compressedJsFileName = $assetsBasePath.DIRECTORY_SEPARATOR.'all.js'; + $compressedJsFileName = $this->testAssetsBasePath . DIRECTORY_SEPARATOR . 'all.js'; $this->assertTrue(file_exists($compressedJsFileName), 'Unable to compress JS files!'); + + $compressedCssFileContent = file_get_contents($compressedCssFileName); + foreach ($cssFiles as $name => $content) { + $this->assertContains($content, $compressedCssFileContent, "Source of '{$name}' is missing in combined file!"); + } + $compressedJsFileContent = file_get_contents($compressedJsFileName); + foreach ($jsFiles as $name => $content) { + $this->assertContains($content, $compressedJsFileContent, "Source of '{$name}' is missing in combined file!"); + } } } From 06752ae9e7d37c16386a4d2bb6d7662f764dd267 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Thu, 16 May 2013 20:13:49 +0300 Subject: [PATCH 09/12] Doc comments for "yii\console\controllers\AssetController" has been updated. --- .../console/controllers/AssetControllerTest.php | 2 -- yii/console/controllers/AssetController.php | 20 ++++++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/unit/framework/console/controllers/AssetControllerTest.php b/tests/unit/framework/console/controllers/AssetControllerTest.php index d32cb2d..b87eb87 100644 --- a/tests/unit/framework/console/controllers/AssetControllerTest.php +++ b/tests/unit/framework/console/controllers/AssetControllerTest.php @@ -93,8 +93,6 @@ class AssetControllerTest extends TestCase protected function createCompressConfig(array $bundles) { $baseUrl = '/test'; - - $config = array( 'bundles' => $this->createBundleConfig($bundles), 'targets' => array( diff --git a/yii/console/controllers/AssetController.php b/yii/console/controllers/AssetController.php index 126771b..cf833b4 100644 --- a/yii/console/controllers/AssetController.php +++ b/yii/console/controllers/AssetController.php @@ -12,6 +12,8 @@ use yii\console\Exception; use yii\console\Controller; /** + * This command allows you to combine and compress your JavaScript and CSS files. + * * @author Qiang Xue * @since 2.0 */ @@ -74,7 +76,7 @@ class AssetController extends Controller public $cssCompressor = 'java -jar yuicompressor.jar {from} -o {to}'; /** - * Compresses the asset files according to the given configuration. + * Combines and compresses the asset files according to the given configuration. * @param string $configFile configuration file name. * @param string $bundleFile */ @@ -210,8 +212,9 @@ class AssetController extends Controller } /** - * @param \yii\web\AssetBundle[] $bundles - * @param array $options + * Publishes given asset bundles. + * @param \yii\web\AssetBundle[] $bundles asset bundles to be published. + * @param array $options assert manager instance configuration. */ protected function publishBundles($bundles, $options) { @@ -225,11 +228,12 @@ class AssetController extends Controller } /** - * @param \yii\web\AssetBundle $target - * @param string $type either "js" or "css" - * @param \yii\web\AssetBundle[] $bundles - * @param integer $timestamp - * @throws Exception + * 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. */ protected function buildTarget($target, $type, $bundles, $timestamp) { From d3beeb7ddcf799b1ac827f1b7998f7e308365f51 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Thu, 16 May 2013 20:38:02 +0300 Subject: [PATCH 10/12] Usage of "yii\console\controllers\AssetController::actionCompress()" has been clarified. --- .../console/controllers/AssetControllerTest.php | 17 ++------------ yii/console/controllers/AssetController.php | 27 ++++++++++++++-------- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/tests/unit/framework/console/controllers/AssetControllerTest.php b/tests/unit/framework/console/controllers/AssetControllerTest.php index b87eb87..9dc7c11 100644 --- a/tests/unit/framework/console/controllers/AssetControllerTest.php +++ b/tests/unit/framework/console/controllers/AssetControllerTest.php @@ -130,20 +130,6 @@ class AssetControllerTest extends TestCase } /** - * Creates test bundles configuration file. - * @param string $fileName output filename. - * @param array[] $bundles asset bundles config. - * @throws Exception on failure. - */ - protected function createBundleFile($fileName, array $bundles) - { - $content = 'createBundleConfig($bundles), true).';'; - if (file_put_contents($fileName, $content) <= 0) { - throw new \Exception("Unable to create file '{$fileName}'!"); - } - } - - /** * Creates test compress config file. * @param string $fileName output file name. * @param array[] $bundles asset bundles config. @@ -224,7 +210,6 @@ class AssetControllerTest extends TestCase ), );; $bundleFile = $this->testFilePath . DIRECTORY_SEPARATOR . 'bundle.php'; - $this->createBundleFile($bundleFile, $bundles); $configFile = $this->testFilePath . DIRECTORY_SEPARATOR . 'config.php'; $this->createCompressConfigFile($configFile, $bundles); @@ -233,6 +218,8 @@ class AssetControllerTest extends TestCase $this->runAssetControllerAction('compress', array($configFile, $bundleFile)); // Then : + $this->assertTrue(file_exists($bundleFile), 'Unable to create output bundle file!'); + $compressedCssFileName = $this->testAssetsBasePath . DIRECTORY_SEPARATOR . 'all.css'; $this->assertTrue(file_exists($compressedCssFileName), 'Unable to compress CSS files!'); $compressedJsFileName = $this->testAssetsBasePath . DIRECTORY_SEPARATOR . 'all.js'; diff --git a/yii/console/controllers/AssetController.php b/yii/console/controllers/AssetController.php index cf833b4..a707cb7 100644 --- a/yii/console/controllers/AssetController.php +++ b/yii/console/controllers/AssetController.php @@ -77,8 +77,10 @@ class AssetController extends Controller /** * Combines and compresses the asset files according to the given configuration. + * 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. * @param string $configFile configuration file name. - * @param string $bundleFile + * @param string $bundleFile output asset bundles configuration file name. */ public function actionCompress($configFile, $bundleFile) { @@ -124,10 +126,10 @@ class AssetController extends Controller } /** - * Creates full list of asset bundles. + * Creates full list of source asset bundles. * @param array[] $bundles list of asset bundle configurations. * @param array $extensions list of the extension paths. - * @return \yii\web\AssetBundle[] list of asset bundles. + * @return \yii\web\AssetBundle[] list of source asset bundles. */ protected function loadBundles($bundles, $extensions) { @@ -152,9 +154,10 @@ class AssetController extends Controller } /** - * @param array $targets - * @param \yii\web\AssetBundle[] $bundles list of asset bundles. - * @return \yii\web\AssetBundle[] + * 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. * @throws \yii\console\Exception on failure. */ protected function loadTargets($targets, $bundles) @@ -260,9 +263,10 @@ class AssetController extends Controller } /** - * @param \yii\web\AssetBundle[] $targets - * @param \yii\web\AssetBundle[] $bundles - * @return \yii\web\AssetBundle[] + * 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. */ protected function adjustDependency($targets, $bundles) { @@ -321,6 +325,11 @@ class AssetController extends Controller } } + /** + * Saves new asset bundles configuration. + * @param \yii\web\AssetBundle[] $targets list of asset bundles to be saved. + * @param string $bundleFile output file name. + */ protected function saveTargets($targets, $bundleFile) { $array = array(); From 2c930ae2cddb959c4d98c48b7fc9e1b28f0ac7b7 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Thu, 16 May 2013 20:51:02 +0300 Subject: [PATCH 11/12] Usage of "escapeshellarg" has been added to "yii\console\controllers\AssetController::actionCompress()". --- yii/console/controllers/AssetController.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yii/console/controllers/AssetController.php b/yii/console/controllers/AssetController.php index a707cb7..b2a5d12 100644 --- a/yii/console/controllers/AssetController.php +++ b/yii/console/controllers/AssetController.php @@ -365,8 +365,8 @@ EOD $tmpFile = $outputFile . '.tmp'; $this->combineJsFiles($inputFiles, $tmpFile); $log = shell_exec(strtr($this->jsCompressor, array( - '{from}' => $tmpFile, - '{to}' => $outputFile, + '{from}' => escapeshellarg($tmpFile), + '{to}' => escapeshellarg($outputFile), ))); @unlink($tmpFile); } else { @@ -385,8 +385,8 @@ EOD $tmpFile = $outputFile . '.tmp'; $this->combineCssFiles($inputFiles, $tmpFile); $log = shell_exec(strtr($this->cssCompressor, array( - '{from}' => $tmpFile, - '{to}' => $outputFile, + '{from}' => escapeshellarg($tmpFile), + '{to}' => escapeshellarg($outputFile), ))); @unlink($tmpFile); } else { From 1c786edbc6f8be215910ca8bf565ae0292f95ce0 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Thu, 16 May 2013 21:59:41 +0300 Subject: [PATCH 12/12] Output of "yii\console\controllers\AssetController::actionCompress()" has been made verbose. --- yii/console/controllers/AssetController.php | 38 ++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/yii/console/controllers/AssetController.php b/yii/console/controllers/AssetController.php index b2a5d12..cee4cc1 100644 --- a/yii/console/controllers/AssetController.php +++ b/yii/console/controllers/AssetController.php @@ -89,13 +89,15 @@ class AssetController extends Controller $targets = $this->loadTargets($this->targets, $bundles); $this->publishBundles($bundles, $this->assetManager); $timestamp = time(); - foreach ($targets as $target) { + foreach ($targets as $name => $target) { + echo "Creating output bundle '{$name}':\n"; if (!empty($target->js)) { $this->buildTarget($target, 'js', $bundles, $timestamp); } if (!empty($target->css)) { $this->buildTarget($target, 'css', $bundles, $timestamp); } + echo "\n"; } $targets = $this->adjustDependency($targets, $bundles); @@ -109,6 +111,8 @@ class AssetController extends Controller */ protected function loadConfiguration($configFile) { + echo "Loading configuration from '{$configFile}'...\n"; + foreach (require($configFile) as $name => $value) { if (property_exists($this, $name)) { $this->$name = $value; @@ -133,6 +137,7 @@ class AssetController extends Controller */ protected function loadBundles($bundles, $extensions) { + echo "Collecting source bundles information...\n"; $result = array(); foreach ($bundles as $name => $bundle) { $bundle['class'] = 'yii\\web\\AssetBundle'; @@ -221,13 +226,16 @@ class AssetController extends Controller */ protected function publishBundles($bundles, $options) { + echo "\nPublishing bundles:\n"; if (!isset($options['class'])) { $options['class'] = 'yii\\web\\AssetManager'; } $am = Yii::createObject($options); - foreach ($bundles as $bundle) { + foreach ($bundles as $name => $bundle) { $bundle->publish($am); + echo " '".$name."' published.\n"; } + echo "\n"; } /** @@ -270,6 +278,8 @@ class AssetController extends Controller */ protected function adjustDependency($targets, $bundles) { + echo "Creating new bundle configuration...\n"; + $map = array(); foreach ($targets as $name => $target) { foreach ($target->depends as $bundle) { @@ -342,7 +352,7 @@ class AssetController extends Controller } $array = var_export($array, true); $version = date('Y-m-d H:i:s', time()); - file_put_contents($bundleFile, <<jsCompressor)) { $tmpFile = $outputFile . '.tmp'; $this->combineJsFiles($inputFiles, $tmpFile); @@ -372,15 +391,24 @@ EOD } else { $log = call_user_func($this->jsCompressor, $this, $inputFiles, $outputFile); } + if (!file_exists($outputFile)) { + throw new Exception("Unable to compress JavaScript files into '{$outputFile}'."); + } + echo " JavaScript files compressed into '{$outputFile}'.\n"; } /** * 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. + * @throws \yii\console\Exception on failure */ protected function compressCssFiles($inputFiles, $outputFile) { + if (empty($inputFiles)) { + return; + } + echo " Compressing CSS files...\n"; if (is_string($this->cssCompressor)) { $tmpFile = $outputFile . '.tmp'; $this->combineCssFiles($inputFiles, $tmpFile); @@ -392,6 +420,10 @@ EOD } else { $log = call_user_func($this->cssCompressor, $this, $inputFiles, $outputFile); } + if (!file_exists($outputFile)) { + throw new Exception("Unable to compress CSS files into '{$outputFile}'."); + } + echo " CSS files compressed into '{$outputFile}'.\n"; } /**