From e132ed8d185a12edf4154a45af67ac34995ea9d0 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Mon, 22 Apr 2013 17:22:38 -0400 Subject: [PATCH 01/11] initial implementation of asset command. --- framework/console/Application.php | 2 +- framework/console/controllers/AssetController.php | 353 +++++++++++++++++++++ framework/console/controllers/ScriptController.php | 256 --------------- 3 files changed, 354 insertions(+), 257 deletions(-) create mode 100644 framework/console/controllers/AssetController.php delete mode 100644 framework/console/controllers/ScriptController.php diff --git a/framework/console/Application.php b/framework/console/Application.php index e185cc5..2f28cac 100644 --- a/framework/console/Application.php +++ b/framework/console/Application.php @@ -129,7 +129,7 @@ class Application extends \yii\base\Application 'migrate' => 'yii\console\controllers\MigrateController', 'app' => 'yii\console\controllers\AppController', 'cache' => 'yii\console\controllers\CacheController', - 'script' => 'yii\console\controllers\ScriptController', + 'asset' => 'yii\console\controllers\AssetController', ); } diff --git a/framework/console/controllers/AssetController.php b/framework/console/controllers/AssetController.php new file mode 100644 index 0000000..71a2cae --- /dev/null +++ b/framework/console/controllers/AssetController.php @@ -0,0 +1,353 @@ + + * @since 2.0 + */ +class AssetController extends Controller +{ + public $defaultAction = 'compress'; + + public $bundles = array(); + public $extensions = array(); + /** + * @var array + * ~~~ + * 'all' => array( + * 'css' => 'all.css', + * 'js' => 'js.css', + * 'depends' => array( ... ), + * ) + * ~~~ + */ + public $targets = array(); + public $assetManager = array(); + public $jsCompressor = 'java -jar compiler.jar --js {from} --js_output_file {to}'; + public $cssCompressor = 'java -jar yuicompressor.jar {from} -o {to}'; + + public function actionCompress($configFile, $bundleFile) + { + $this->loadConfiguration($configFile); + $bundles = $this->loadBundles($this->bundles, $this->extensions); + $targets = $this->loadTargets($this->targets, $bundles); + $this->publishBundles($bundles, $this->publishOptions); + $timestamp = time(); + foreach ($targets as $target) { + if (!empty($target->js)) { + $this->buildTarget($target, 'js', $bundles, $timestamp); + } + if (!empty($target->css)) { + $this->buildTarget($target, 'css', $bundles, $timestamp); + } + } + + $targets = $this->adjustDependency($targets, $bundles); + $this->saveTargets($targets, $bundleFile); + } + + protected function loadConfiguration($configFile) + { + foreach (require($configFile) as $name => $value) { + if (property_exists($this, $name)) { + $this->$name = $value; + } else { + throw new Exception("Unknown configuration option: $name"); + } + } + + if (!isset($this->assetManager['basePath'])) { + throw new Exception("Please specify 'basePath' for the 'assetManager' option."); + } + if (!isset($this->assetManager['baseUrl'])) { + throw new Exception("Please specify 'baseUrl' for the 'assetManager' option."); + } + } + + protected function loadBundles($bundles, $extensions) + { + $result = array(); + foreach ($bundles as $name => $bundle) { + $bundle['class'] = 'yii\\web\\AssetBundle'; + $result[$name] = Yii::createObject($bundle); + } + foreach ($extensions as $path) { + $manifest = $path . '/assets.php'; + if (!is_file($manifest)) { + continue; + } + foreach (require($manifest) as $name => $bundle) { + if (!isset($result[$name])) { + $bundle['class'] = 'yii\\web\\AssetBundle'; + $result[$name] = Yii::createObject($bundle); + } + } + } + return $result; + } + + protected function loadTargets($targets, $bundles) + { + // build the dependency order of bundles + $registered = array(); + foreach ($bundles as $name => $bundle) { + $this->registerBundle($bundles, $name, $registered); + } + $bundleOrders = array_combine(array_keys($registered), range(0, count($bundles) - 1)); + + // 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 + 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; + } + + /** + * @param \yii\web\AssetBundle[] $bundles + * @param array $options + */ + protected function publishBundles($bundles, $options) + { + if (!isset($options['class'])) { + $options['class'] = 'yii\\web\\AssetManager'; + } + $am = Yii::createObject($options); + foreach ($bundles as $bundle) { + $bundle->publish($am); + } + } + + /** + * @param \yii\web\AssetBundle $target + * @param string $type either "js" or "css" + * @param \yii\web\AssetBundle[] $bundles + * @param integer $timestamp + * @throws Exception + */ + protected function buildTarget($target, $type, $bundles, $timestamp) + { + $outputFile = strtr($target->$type, array( + '{ts}' => $timestamp, + )); + $inputFiles = array(); + + foreach ($target->depends as $name) { + if (isset($bundles[$name])) { + foreach ($bundles[$name]->$type as $file) { + $inputFiles[] = $bundles[$name]->basePath . '/' . $file; + } + } else { + throw new Exception("Unknown bundle: $name"); + } + } + if ($type === 'js') { + $this->compressJsFiles($inputFiles, $target->basePath . '/' . $outputFile); + } else { + $this->compressCssFiles($inputFiles, $target->basePath . '/' . $outputFile); + } + $target->$type = array($outputFile); + } + + protected function adjustDependency($targets, $bundles) + { + $map = array(); + foreach ($targets as $name => $target) { + foreach ($target->depends as $bundle) { + $map[$bundle] = $name; + } + } + + 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), + )); + } + return $targets; + } + + 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'."); + } + } + + 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()); + file_put_contents($bundleFile, <<jsCompressor)) { + $tmpFile = $outputFile . '.tmp'; + $this->combineJsFiles($inputFiles, $tmpFile); + $log = shell_exec(strtr($this->jsCompressor, array( + '{from}' => $tmpFile, + '{to}' => $outputFile, + ))); + @unlink($tmpFile); + } else { + $log = call_user_func($this->jsCompressor, $this, $inputFiles, $outputFile); + } + } + + protected function compressCssFiles($inputFiles, $outputFile) + { + if (is_string($this->cssCompressor)) { + $tmpFile = $outputFile . '.tmp'; + $this->combineCssFiles($inputFiles, $tmpFile); + $log = shell_exec(strtr($this->cssCompressor, array( + '{from}' => $inputFiles, + '{to}' => $outputFile, + ))); + } else { + $log = call_user_func($this->cssCompressor, $this, $inputFiles, $outputFile); + } + } + + public function combineJsFiles($files, $tmpFile) + { + $content = ''; + foreach ($files as $file) { + $content .= "/*** BEGIN FILE: $file ***/\n" + . file_get_contents($file) + . "/*** END FILE: $file ***/\n"; + } + file_put_contents($tmpFile, $content); + } + + public function combineCssFiles($files, $tmpFile) + { + // todo: adjust url() references in CSS files + $content = ''; + foreach ($files as $file) { + $content .= "/*** BEGIN FILE: $file ***/\n" + . file_get_contents($file) + . "/*** END FILE: $file ***/\n"; + } + file_put_contents($tmpFile, $content); + } + + public function actionTemplate($configFile) + { + $template = << 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', + ), + ), + + 'assetManager' => array( + 'basePath' => __DIR__, + 'baseUrl' => '/test', + ), +); +EOD; + file_put_contents($configFile, $template); + } +} \ No newline at end of file diff --git a/framework/console/controllers/ScriptController.php b/framework/console/controllers/ScriptController.php deleted file mode 100644 index ab57f92..0000000 --- a/framework/console/controllers/ScriptController.php +++ /dev/null @@ -1,256 +0,0 @@ - - * @since 2.0 - */ -class ScriptController extends Controller -{ - public $defaultAction = 'compress'; - - public $bundles = array(); - public $extensions = array(); - /** - * @var array - * ~~~ - * 'all' => array( - * 'css' => 'all.css', - * 'js' => 'js.css', - * 'depends' => array( ... ), - * ) - * ~~~ - */ - public $targets = array(); - public $assetManager = array(); - - public function actionCompress($configFile, $bundleFile) - { - $this->loadConfiguration($configFile); - $bundles = $this->loadBundles($this->bundles, $this->extensions); - $targets = $this->loadTargets($this->targets, $bundles); -// $this->publishBundles($bundles, $this->publishOptions); - $timestamp = time(); - foreach ($targets as $target) { - if (!empty($target->js)) { - $this->buildTarget($target, 'js', $bundles, $timestamp); - } - if (!empty($target->css)) { - $this->buildTarget($target, 'css', $bundles, $timestamp); - } - } - - $targets = $this->adjustDependency($targets, $bundles); - $this->saveTargets($targets, $bundleFile); - } - - protected function loadConfiguration($configFile) - { - foreach (require($configFile) as $name => $value) { - if (property_exists($this, $name)) { - $this->$name = $value; - } else { - throw new Exception("Unknown configuration option: $name"); - } - } - - if (!isset($this->assetManager['basePath'])) { - throw new Exception("Please specify 'basePath' for the 'assetManager' option."); - } - if (!isset($this->assetManager['baseUrl'])) { - throw new Exception("Please specify 'baseUrl' for the 'assetManager' option."); - } - } - - protected function loadBundles($bundles, $extensions) - { - $result = array(); - foreach ($bundles as $name => $bundle) { - $bundle['class'] = 'yii\\web\\AssetBundle'; - $result[$name] = Yii::createObject($bundle); - } - foreach ($extensions as $path) { - $manifest = $path . '/assets.php'; - if (!is_file($manifest)) { - continue; - } - foreach (require($manifest) as $name => $bundle) { - if (!isset($result[$name])) { - $bundle['class'] = 'yii\\web\\AssetBundle'; - $result[$name] = Yii::createObject($bundle); - } - } - } - return $result; - } - - protected function loadTargets($targets, $bundles) - { - $registered = array(); - foreach ($bundles as $name => $bundle) { - $this->registerBundle($bundles, $name, $registered); - } - $bundleOrders = array_combine(array_keys($registered), range(0, count($bundles) - 1)); - 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; - } - - /** - * @param \yii\web\AssetBundle[] $bundles - * @param array $options - */ - protected function publishBundles($bundles, $options) - { - if (!isset($options['class'])) { - $options['class'] = 'yii\\web\\AssetManager'; - } - $am = Yii::createObject($options); - foreach ($bundles as $bundle) { - $bundle->publish($am); - } - } - - /** - * @param \yii\web\AssetBundle $target - * @param string $type either "js" or "css" - * @param \yii\web\AssetBundle[] $bundles - * @param integer $timestamp - * @throws Exception - */ - protected function buildTarget($target, $type, $bundles, $timestamp) - { - $outputFile = strtr($target->$type, array( - '{ts}' => $timestamp, - )); - $inputFiles = array(); - - foreach ($target->depends as $name) { - if (isset($bundles[$name])) { - foreach ($bundles[$name]->$type as $file) { - $inputFiles[] = $bundles[$name]->basePath . '/' . $file; - } - } else { - throw new Exception("Unknown bundle: $name"); - } - } - if ($type === 'js') { - $this->compressJsFiles($inputFiles, $target->basePath . '/' . $outputFile); - } else { - $this->compressCssFiles($inputFiles, $target->basePath . '/' . $outputFile); - } - $target->$type = array($outputFile); - } - - protected function adjustDependency($targets, $bundles) - { - $map = array(); - foreach ($targets as $name => $target) { - foreach ($target->depends as $bundle) { - if (!isset($map[$bundle])) { - $map[$bundle] = $name; - } else { - throw new Exception("Bundle '$bundle' is found in both target '{$map[$bundle]}' and '$name'."); - } - } - } - - 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), - )); - } - return $targets; - } - - 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'."); - } - } - - 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()); - file_put_contents($bundleFile, << Date: Mon, 22 Apr 2013 19:45:37 -0400 Subject: [PATCH 02/11] Added a basic app. --- app/assets/.gitignore | 1 + app/index.php | 9 ++++++ app/protected/config/main.php | 15 ++++++++++ app/protected/controllers/SiteController.php | 22 ++++++++++++++ app/protected/models/User.php | 43 ++++++++++++++++++++++++++++ app/protected/runtime/.gitignore | 1 + app/protected/views/layouts/main.php | 22 ++++++++++++++ app/protected/views/site/index.php | 17 +++++++++++ framework/web/UrlManager.php | 8 +++--- 9 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 app/assets/.gitignore create mode 100644 app/index.php create mode 100644 app/protected/config/main.php create mode 100644 app/protected/controllers/SiteController.php create mode 100644 app/protected/models/User.php create mode 100644 app/protected/runtime/.gitignore create mode 100644 app/protected/views/layouts/main.php create mode 100644 app/protected/views/site/index.php diff --git a/app/assets/.gitignore b/app/assets/.gitignore new file mode 100644 index 0000000..72e8ffc --- /dev/null +++ b/app/assets/.gitignore @@ -0,0 +1 @@ +* diff --git a/app/index.php b/app/index.php new file mode 100644 index 0000000..8f98090 --- /dev/null +++ b/app/index.php @@ -0,0 +1,9 @@ +run(); diff --git a/app/protected/config/main.php b/app/protected/config/main.php new file mode 100644 index 0000000..e18ead8 --- /dev/null +++ b/app/protected/config/main.php @@ -0,0 +1,15 @@ + 'hello', + 'basePath' => dirname(__DIR__), + 'components' => array( + 'cache' => array( + 'class' => 'yii\caching\FileCache', + ), + 'user' => array( + 'class' => 'yii\web\User', + 'identityClass' => 'app\models\User', + ) + ), +); \ No newline at end of file diff --git a/app/protected/controllers/SiteController.php b/app/protected/controllers/SiteController.php new file mode 100644 index 0000000..58e9568 --- /dev/null +++ b/app/protected/controllers/SiteController.php @@ -0,0 +1,22 @@ +render('index'); + } + + public function actionLogin() + { + $user = app\models\User::findIdentity(100); + Yii::$app->getUser()->login($user); + Yii::$app->getResponse()->redirect(array('site/index')); + } + + public function actionLogout() + { + Yii::$app->getUser()->logout(); + Yii::$app->getResponse()->redirect(array('site/index')); + } +} \ No newline at end of file diff --git a/app/protected/models/User.php b/app/protected/models/User.php new file mode 100644 index 0000000..cebf1da --- /dev/null +++ b/app/protected/models/User.php @@ -0,0 +1,43 @@ + array( + 'id' => '100', + 'authKey' => 'test100key', + 'name' => 'admin', + ), + '101' => array( + 'id' => '101', + 'authKey' => 'test101key', + 'name' => 'demo', + ), + ); + + public static function findIdentity($id) + { + return isset(self::$users[$id]) ? new self(self::$users[$id]) : null; + } + + public function getId() + { + return $this->id; + } + + public function getAuthKey() + { + return $this->authKey; + } + + public function validateAuthKey($authKey) + { + return $this->authKey === $authKey; + } +} \ No newline at end of file diff --git a/app/protected/runtime/.gitignore b/app/protected/runtime/.gitignore new file mode 100644 index 0000000..72e8ffc --- /dev/null +++ b/app/protected/runtime/.gitignore @@ -0,0 +1 @@ +* diff --git a/app/protected/views/layouts/main.php b/app/protected/views/layouts/main.php new file mode 100644 index 0000000..092e665 --- /dev/null +++ b/app/protected/views/layouts/main.php @@ -0,0 +1,22 @@ + + + +beginPage(); ?> + + <?php echo Html::encode($this->title); ?> + head(); ?> + + +

Welcome

+beginBody(); ?> + +endBody(); ?> + +endPage(); ?> + diff --git a/app/protected/views/site/index.php b/app/protected/views/site/index.php new file mode 100644 index 0000000..3b83080 --- /dev/null +++ b/app/protected/views/site/index.php @@ -0,0 +1,17 @@ +title = 'Hello World'; + +$user = Yii::$app->getUser(); +if ($user->isGuest) { + echo Html::a('login', array('login')); +} else { + echo "You are logged in as " . $user->identity->name . "
"; + echo Html::a('logout', array('logout')); +} +?> + + diff --git a/framework/web/UrlManager.php b/framework/web/UrlManager.php index 459e8e8..755d644 100644 --- a/framework/web/UrlManager.php +++ b/framework/web/UrlManager.php @@ -74,9 +74,6 @@ class UrlManager extends Component public function init() { parent::init(); - if (is_string($this->cache)) { - $this->cache = Yii::$app->getComponent($this->cache); - } $this->compileRules(); } @@ -88,6 +85,9 @@ class UrlManager extends Component if (!$this->enablePrettyUrl || $this->rules === array()) { return; } + if (is_string($this->cache)) { + $this->cache = Yii::$app->getComponent($this->cache); + } if ($this->cache instanceof Cache) { $key = $this->cache->buildKey(__CLASS__); $hash = md5(json_encode($this->rules)); @@ -104,7 +104,7 @@ class UrlManager extends Component $this->rules[$i] = Yii::createObject($rule); } - if ($this->cache instanceof Cache) { + if (isset($key, $hash)) { $this->cache->set($key, array($this->rules, $hash)); } } From 173706f5165a7c28717c9203b7ea1868991b7889 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Tue, 23 Apr 2013 14:11:40 +0400 Subject: [PATCH 03/11] updated expected exception message --- tests/unit/framework/base/ModelTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/framework/base/ModelTest.php b/tests/unit/framework/base/ModelTest.php index aa15230..f04e550 100644 --- a/tests/unit/framework/base/ModelTest.php +++ b/tests/unit/framework/base/ModelTest.php @@ -195,7 +195,7 @@ class ModelTest extends TestCase public function testCreateValidators() { - $this->setExpectedException('yii\base\InvalidConfigException', 'Invalid validation rule: a rule must be an array specifying both attribute names and validator type.'); + $this->setExpectedException('yii\base\InvalidConfigException', 'Invalid validation rule: a rule must specify both attribute names and validator type.'); $invalid = new InvalidRulesModel(); $invalid->createValidators(); From a9215ceddd74b9963784612d7c1e1b74fdd19b2d Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Tue, 23 Apr 2013 14:11:55 +0400 Subject: [PATCH 04/11] create a webapp in test bootstrap --- tests/unit/bootstrap.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/unit/bootstrap.php b/tests/unit/bootstrap.php index 4a388c6..8290bbe 100644 --- a/tests/unit/bootstrap.php +++ b/tests/unit/bootstrap.php @@ -9,4 +9,6 @@ require_once(__DIR__ . '/../../framework/yii.php'); Yii::setAlias('@yiiunit', __DIR__); +new \yii\web\Application(array('id' => 'testapp', 'basePath' => __DIR__)); + require_once(__DIR__ . '/TestCase.php'); From 3e2e4afa8560074797fce9cafc27577d13b78e57 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Tue, 23 Apr 2013 14:15:22 +0400 Subject: [PATCH 05/11] fixed DB quoting typo --- framework/db/Connection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/db/Connection.php b/framework/db/Connection.php index 695034a..797508a 100644 --- a/framework/db/Connection.php +++ b/framework/db/Connection.php @@ -522,7 +522,7 @@ class Connection extends Component if (isset($matches[3])) { return $db->quoteColumnName($matches[3]); } else { - return str_replace('%', $this->tablePrefix, $db->quoteTableName($matches[2])); + return str_replace('%', $db->tablePrefix, $db->quoteTableName($matches[2])); } }, $sql); } From 08be696434a2fbdf61f3a309fffc73c5e80785f2 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Tue, 23 Apr 2013 14:32:54 +0400 Subject: [PATCH 06/11] fixed Html test under Windows (line endings) --- tests/unit/framework/helpers/HtmlTest.php | 46 ++++++++++++++++++------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/tests/unit/framework/helpers/HtmlTest.php b/tests/unit/framework/helpers/HtmlTest.php index bf0ca0a..4077043 100644 --- a/tests/unit/framework/helpers/HtmlTest.php +++ b/tests/unit/framework/helpers/HtmlTest.php @@ -22,6 +22,14 @@ class HtmlTest extends \yii\test\TestCase )); } + public function assertEqualsWithoutLE($expected, $actual) + { + $expected = str_replace("\r\n", "\n", $expected); + $actual = str_replace("\r\n", "\n", $actual); + + $this->assertEquals($expected, $actual); + } + public function tearDown() { Yii::$app = null; @@ -240,21 +248,21 @@ class HtmlTest extends \yii\test\TestCase EOD; - $this->assertEquals($expected, Html::dropDownList('test')); + $this->assertEqualsWithoutLE($expected, Html::dropDownList('test')); $expected = << EOD; - $this->assertEquals($expected, Html::dropDownList('test', null, $this->getDataItems())); + $this->assertEqualsWithoutLE($expected, Html::dropDownList('test', null, $this->getDataItems())); $expected = << EOD; - $this->assertEquals($expected, Html::dropDownList('test', 'value2', $this->getDataItems())); + $this->assertEqualsWithoutLE($expected, Html::dropDownList('test', 'value2', $this->getDataItems())); } public function testListBox() @@ -264,48 +272,48 @@ EOD; EOD; - $this->assertEquals($expected, Html::listBox('test')); + $this->assertEqualsWithoutLE($expected, Html::listBox('test')); $expected = << EOD; - $this->assertEquals($expected, Html::listBox('test', null, $this->getDataItems(), array('size' => 5))); + $this->assertEqualsWithoutLE($expected, Html::listBox('test', null, $this->getDataItems(), array('size' => 5))); $expected = << EOD; - $this->assertEquals($expected, Html::listBox('test', null, $this->getDataItems2())); + $this->assertEqualsWithoutLE($expected, Html::listBox('test', null, $this->getDataItems2())); $expected = << EOD; - $this->assertEquals($expected, Html::listBox('test', 'value2', $this->getDataItems())); + $this->assertEqualsWithoutLE($expected, Html::listBox('test', 'value2', $this->getDataItems())); $expected = << EOD; - $this->assertEquals($expected, Html::listBox('test', array('value1', 'value2'), $this->getDataItems())); + $this->assertEqualsWithoutLE($expected, Html::listBox('test', array('value1', 'value2'), $this->getDataItems())); $expected = << EOD; - $this->assertEquals($expected, Html::listBox('test', null, array(), array('multiple' => true))); + $this->assertEqualsWithoutLE($expected, Html::listBox('test', null, array(), array('multiple' => true))); $expected = << EOD; - $this->assertEquals($expected, Html::listBox('test', '', array(), array('unselect' => '0'))); + $this->assertEqualsWithoutLE($expected, Html::listBox('test', '', array(), array('unselect' => '0'))); } public function testCheckboxList() @@ -316,19 +324,19 @@ EOD; EOD; - $this->assertEquals($expected, Html::checkboxList('test', array('value2'), $this->getDataItems())); + $this->assertEqualsWithoutLE($expected, Html::checkboxList('test', array('value2'), $this->getDataItems())); $expected = << text1<> EOD; - $this->assertEquals($expected, Html::checkboxList('test', array('value2'), $this->getDataItems2())); + $this->assertEqualsWithoutLE($expected, Html::checkboxList('test', array('value2'), $this->getDataItems2())); $expected = <<
EOD; - $this->assertEquals($expected, Html::checkboxList('test', array('value2'), $this->getDataItems(), array( + $this->assertEqualsWithoutLE($expected, Html::checkboxList('test', array('value2'), $this->getDataItems(), array( 'separator' => "
\n", 'unselect' => '0', ))); @@ -337,7 +345,7 @@ EOD; 0 1 EOD; - $this->assertEquals($expected, Html::checkboxList('test', array('value2'), $this->getDataItems(), array( + $this->assertEqualsWithoutLE($expected, Html::checkboxList('test', array('value2'), $this->getDataItems(), array( 'item' => function ($index, $label, $name, $checked, $value) { return $index . Html::label($label . ' ' . Html::checkbox($name, $checked, $value)); } @@ -352,19 +360,19 @@ EOD; EOD; - $this->assertEquals($expected, Html::radioList('test', array('value2'), $this->getDataItems())); + $this->assertEqualsWithoutLE($expected, Html::radioList('test', array('value2'), $this->getDataItems())); $expected = << text1<> EOD; - $this->assertEquals($expected, Html::radioList('test', array('value2'), $this->getDataItems2())); + $this->assertEqualsWithoutLE($expected, Html::radioList('test', array('value2'), $this->getDataItems2())); $expected = <<
EOD; - $this->assertEquals($expected, Html::radioList('test', array('value2'), $this->getDataItems(), array( + $this->assertEqualsWithoutLE($expected, Html::radioList('test', array('value2'), $this->getDataItems(), array( 'separator' => "
\n", 'unselect' => '0', ))); @@ -373,7 +381,7 @@ EOD; 0 1 EOD; - $this->assertEquals($expected, Html::radioList('test', array('value2'), $this->getDataItems(), array( + $this->assertEqualsWithoutLE($expected, Html::radioList('test', array('value2'), $this->getDataItems(), array( 'item' => function ($index, $label, $name, $checked, $value) { return $index . Html::label($label . ' ' . Html::radio($name, $checked, $value)); } @@ -420,7 +428,7 @@ EOD; 'group12' => array('class' => 'group'), ), ); - $this->assertEquals($expected, Html::renderSelectOptions(array('value111', 'value1'), $data, $attributes)); + $this->assertEqualsWithoutLE($expected, Html::renderSelectOptions(array('value111', 'value1'), $data, $attributes)); } public function testRenderAttributes() From b0bf8f74068e867e1273d862e286ac7241cfb798 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Tue, 23 Apr 2013 14:40:52 +0400 Subject: [PATCH 07/11] fixed dbcache multiget --- framework/caching/DbCache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/caching/DbCache.php b/framework/caching/DbCache.php index dee8c7a..befb523 100644 --- a/framework/caching/DbCache.php +++ b/framework/caching/DbCache.php @@ -124,7 +124,7 @@ class DbCache extends Cache $query = new Query; $query->select(array('id', 'data')) ->from($this->cacheTable) - ->where(array('id' => $keys)) + ->where(array('in', 'id', (array)$keys)) ->andWhere('([[expire]] = 0 OR [[expire]] > ' . time() . ')'); if ($this->db->enableQueryCache) { From 09dbaeb70094626067578e82c3071c14bdb0e8cb Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Tue, 23 Apr 2013 15:00:41 +0400 Subject: [PATCH 08/11] more assertions for cache test --- tests/unit/framework/caching/CacheTest.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/unit/framework/caching/CacheTest.php b/tests/unit/framework/caching/CacheTest.php index ad2fcf5..f9db4f4 100644 --- a/tests/unit/framework/caching/CacheTest.php +++ b/tests/unit/framework/caching/CacheTest.php @@ -16,9 +16,9 @@ abstract class CacheTest extends TestCase public function testSet() { $cache = $this->getCacheInstance(); - $cache->set('string_test', 'string_test'); - $cache->set('number_test', 42); - $cache->set('array_test', array('array_test' => 'array_test')); + $this->assertTrue($cache->set('string_test', 'string_test')); + $this->assertTrue($cache->set('number_test', 42)); + $this->assertTrue($cache->set('array_test', array('array_test' => 'array_test'))); $cache['arrayaccess_test'] = new \stdClass(); } @@ -45,7 +45,7 @@ abstract class CacheTest extends TestCase public function testExpire() { $cache = $this->getCacheInstance(); - $cache->set('expire_test', 'expire_test', 2); + $this->assertTrue($cache->set('expire_test', 'expire_test', 2)); sleep(1); $this->assertEquals('expire_test', $cache->get('expire_test')); sleep(2); @@ -57,11 +57,11 @@ abstract class CacheTest extends TestCase $cache = $this->getCacheInstance(); // should not change existing keys - $cache->add('number_test', 13); + $this->assertFalse($cache->add('number_test', 13)); $this->assertEquals(42, $cache->get('number_test')); // should store data is it's not there yet - $cache->add('add_test', 13); + $this->assertTrue($cache->add('add_test', 13)); $this->assertEquals(13, $cache->get('add_test')); } @@ -69,14 +69,14 @@ abstract class CacheTest extends TestCase { $cache = $this->getCacheInstance(); - $cache->delete('number_test'); + $this->assertTrue($cache->delete('number_test')); $this->assertEquals(null, $cache->get('number_test')); } public function testFlush() { $cache = $this->getCacheInstance(); - $cache->flush(); + $this->assertTrue($cache->flush()); $this->assertEquals(null, $cache->get('add_test')); } } From c09ace8e96dc3e977e53d8ec105aa05ba0bb8ec3 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Tue, 23 Apr 2013 15:10:18 +0400 Subject: [PATCH 09/11] added note about enabling APC cache for CLI --- framework/caching/ApcCache.php | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/caching/ApcCache.php b/framework/caching/ApcCache.php index dd954cc..391851d 100644 --- a/framework/caching/ApcCache.php +++ b/framework/caching/ApcCache.php @@ -11,6 +11,7 @@ namespace yii\caching; * ApcCache provides APC caching in terms of an application component. * * To use this application component, the [APC PHP extension](http://www.php.net/apc) must be loaded. + * In order to enable APC for CLI you should add "apc.enable_cli = 1" to your php.ini. * * See [[Cache]] for common cache operations that ApcCache supports. * From 15b9f97ca41ce218a13b26fa5bbebaf64708dcdb Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 23 Apr 2013 16:48:14 -0400 Subject: [PATCH 10/11] Fixed typo in query builder and reverted changes to DbCache.php --- framework/caching/DbCache.php | 2 +- framework/db/QueryBuilder.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/caching/DbCache.php b/framework/caching/DbCache.php index befb523..dee8c7a 100644 --- a/framework/caching/DbCache.php +++ b/framework/caching/DbCache.php @@ -124,7 +124,7 @@ class DbCache extends Cache $query = new Query; $query->select(array('id', 'data')) ->from($this->cacheTable) - ->where(array('in', 'id', (array)$keys)) + ->where(array('id' => $keys)) ->andWhere('([[expire]] = 0 OR [[expire]] > ' . time() . ')'); if ($this->db->enableQueryCache) { diff --git a/framework/db/QueryBuilder.php b/framework/db/QueryBuilder.php index da43940..441d287 100644 --- a/framework/db/QueryBuilder.php +++ b/framework/db/QueryBuilder.php @@ -744,7 +744,7 @@ class QueryBuilder extends \yii\base\Object $parts = array(); foreach ($condition as $column => $value) { if (is_array($value)) { // IN condition - $parts[] = $this->buildInCondition('in', array($column, $value), $query); + $parts[] = $this->buildInCondition('in', array($column, $value), $params); } else { if (strpos($column, '(') === false) { $column = $this->db->quoteColumnName($column); From a578938df79f94c19029752a8f1a86167496ee81 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Wed, 24 Apr 2013 18:59:54 -0400 Subject: [PATCH 11/11] Added beforeAction and afterAction to Module. --- framework/base/Controller.php | 22 ++++++++++++++-------- framework/base/Module.php | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/framework/base/Controller.php b/framework/base/Controller.php index d11d19b..0d46235 100644 --- a/framework/base/Controller.php +++ b/framework/base/Controller.php @@ -19,7 +19,14 @@ use yii\helpers\StringHelper; */ class Controller extends Component { + /** + * @event ActionEvent an event raised right before executing a controller action. + * You may set [[ActionEvent::isValid]] to be false to cancel the action execution. + */ const EVENT_BEFORE_ACTION = 'beforeAction'; + /** + * @event ActionEvent an event raised right after executing a controller action. + */ const EVENT_AFTER_ACTION = 'afterAction'; /** @@ -105,16 +112,15 @@ class Controller extends Component if ($action !== null) { $oldAction = $this->action; $this->action = $action; - - if ($this->beforeAction($action)) { - $status = $action->runWithParams($params); - $this->afterAction($action); - } else { - $status = 1; + $status = 1; + if ($this->module->beforeAction($action)) { + if ($this->beforeAction($action)) { + $status = $action->runWithParams($params); + $this->afterAction($action); + } + $this->module->afterAction($action); } - $this->action = $oldAction; - return $status; } else { throw new InvalidRouteException('Unable to resolve the request: ' . $this->getUniqueId() . '/' . $id); diff --git a/framework/base/Module.php b/framework/base/Module.php index d99778d..959bb04 100644 --- a/framework/base/Module.php +++ b/framework/base/Module.php @@ -9,7 +9,6 @@ namespace yii\base; use Yii; use yii\helpers\StringHelper; -use yii\helpers\FileHelper; /** * Module is the base class for module and application classes. @@ -39,6 +38,15 @@ use yii\helpers\FileHelper; abstract class Module extends Component { /** + * @event ActionEvent an event raised before executing a controller action. + * You may set [[ActionEvent::isValid]] to be false to cancel the action execution. + */ + const EVENT_BEFORE_ACTION = 'beforeAction'; + /** + * @event ActionEvent an event raised after executing a controller action. + */ + const EVENT_AFTER_ACTION = 'afterAction'; + /** * @var array custom module parameters (name => value). */ public $params = array(); @@ -613,4 +621,27 @@ abstract class Module extends Component return isset($controller) ? array($controller, $route) : false; } + + /** + * This method is invoked right before an action is to be executed (after all possible filters.) + * You may override this method to do last-minute preparation for the action. + * @param Action $action the action to be executed. + * @return boolean whether the action should continue to be executed. + */ + public function beforeAction($action) + { + $event = new ActionEvent($action); + $this->trigger(self::EVENT_BEFORE_ACTION, $event); + return $event->isValid; + } + + /** + * This method is invoked right after an action is executed. + * You may override this method to do some postprocessing for the action. + * @param Action $action the action just executed. + */ + public function afterAction($action) + { + $this->trigger(self::EVENT_AFTER_ACTION, new ActionEvent($action)); + } }