From af3f5af3be9ee8afd4ccab4ddaefba57f6bc1ca7 Mon Sep 17 00:00:00 2001 From: Alexander Kochetov Date: Sat, 11 May 2013 09:24:02 +0400 Subject: [PATCH 01/33] RBAC: Item and Assignment optimized save approach --- tests/unit/framework/rbac/ManagerTestBase.php | 1 + yii/rbac/Assignment.php | 10 ++++++++-- yii/rbac/Item.php | 16 +++++++++++----- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/tests/unit/framework/rbac/ManagerTestBase.php b/tests/unit/framework/rbac/ManagerTestBase.php index 03d5354..542c1bb 100644 --- a/tests/unit/framework/rbac/ManagerTestBase.php +++ b/tests/unit/framework/rbac/ManagerTestBase.php @@ -57,6 +57,7 @@ abstract class ManagerTestBase extends TestCase $this->assertTrue($item instanceof Item); $this->assertTrue($this->auth->hasItemChild('reader', 'readPost')); $item->name = 'readPost2'; + $item->save(); $this->assertNull($this->auth->getItem('readPost')); $this->assertEquals($this->auth->getItem('readPost2'), $item); $this->assertFalse($this->auth->hasItemChild('reader', 'readPost')); diff --git a/yii/rbac/Assignment.php b/yii/rbac/Assignment.php index 5b6a607..b3aa74f 100644 --- a/yii/rbac/Assignment.php +++ b/yii/rbac/Assignment.php @@ -81,7 +81,6 @@ class Assignment extends Object { if ($this->_bizRule !== $value) { $this->_bizRule = $value; - $this->_auth->saveAssignment($this); } } @@ -100,7 +99,14 @@ class Assignment extends Object { if ($this->_data !== $value) { $this->_data = $value; - $this->_auth->saveAssignment($this); } } + + /** + * Saves the changes to an authorization assignment. + */ + public function save() + { + $this->_auth->saveAssignment($this); + } } diff --git a/yii/rbac/Item.php b/yii/rbac/Item.php index ef56a53..57a7f7e 100644 --- a/yii/rbac/Item.php +++ b/yii/rbac/Item.php @@ -39,6 +39,7 @@ class Item extends Object private $_auth; private $_type; private $_name; + private $_oldName; private $_description; private $_bizRule; private $_data; @@ -116,9 +117,8 @@ class Item extends Object public function setName($value) { if ($this->_name !== $value) { - $oldName = $this->_name; + $this->_oldName = $this->_name; $this->_name = $value; - $this->_auth->saveItem($this, $oldName); } } @@ -137,7 +137,6 @@ class Item extends Object { if ($this->_description !== $value) { $this->_description = $value; - $this->_auth->saveItem($this); } } @@ -156,7 +155,6 @@ class Item extends Object { if ($this->_bizRule !== $value) { $this->_bizRule = $value; - $this->_auth->saveItem($this); } } @@ -175,7 +173,6 @@ class Item extends Object { if ($this->_data !== $value) { $this->_data = $value; - $this->_auth->saveItem($this); } } @@ -272,4 +269,13 @@ class Item extends Object { return $this->_auth->getAssignment($userId, $this->_name); } + + /** + * Saves an authorization item to persistent storage. + */ + public function save() + { + $this->_auth->saveItem($this, $this->_oldName); + unset($this->_oldName); + } } From 70265587fbcc2fe156b9642bca5463f78325c097 Mon Sep 17 00:00:00 2001 From: Alexander Kochetov Date: Sat, 11 May 2013 09:43:45 +0400 Subject: [PATCH 02/33] RBAC: DbManager code style fixes --- yii/rbac/DbManager.php | 82 +++++++++++--------------------------------------- 1 file changed, 18 insertions(+), 64 deletions(-) diff --git a/yii/rbac/DbManager.php b/yii/rbac/DbManager.php index f3658b2..4618f54 100644 --- a/yii/rbac/DbManager.php +++ b/yii/rbac/DbManager.php @@ -146,10 +146,7 @@ class DbManager extends Manager } $query = new Query; $rows = $query->from($this->itemTable) - ->where(array('or', 'name=:name1', 'name=:name2'), array( - ':name1' => $itemName, - ':name2' => $childName - )) + ->where(array('or', 'name=:name1', 'name=:name2'), array(':name1' => $itemName, ':name2' => $childName)) ->createCommand($this->db) ->queryAll(); if (count($rows) == 2) { @@ -165,10 +162,7 @@ class DbManager extends Manager throw new InvalidCallException("Cannot add '$childName' as a child of '$itemName'. A loop has been detected."); } $this->db->createCommand() - ->insert($this->itemChildTable, array( - 'parent' => $itemName, - 'child' => $childName, - )); + ->insert($this->itemChildTable, array('parent' => $itemName, 'child' => $childName)); return true; } else { throw new Exception("Either '$itemName' or '$childName' does not exist."); @@ -185,10 +179,7 @@ class DbManager extends Manager public function removeItemChild($itemName, $childName) { return $this->db->createCommand() - ->delete($this->itemChildTable, array( - 'parent' => $itemName, - 'child' => $childName - )) > 0; + ->delete($this->itemChildTable, array('parent' => $itemName, 'child' => $childName)) > 0; } /** @@ -202,10 +193,7 @@ class DbManager extends Manager $query = new Query; return $query->select(array('parent')) ->from($this->itemChildTable) - ->where(array( - 'parent' => $itemName, - 'child' => $childName - )) + ->where(array('parent' => $itemName, 'child' => $childName)) ->createCommand($this->db) ->queryScalar() !== false; } @@ -220,10 +208,7 @@ class DbManager extends Manager { $query = new Query; $rows = $query->select(array('name', 'type', 'description', 'bizrule', 'data')) - ->from(array( - $this->itemTable, - $this->itemChildTable - )) + ->from(array($this->itemTable, $this->itemChildTable)) ->where(array('parent' => $names, 'name' => new Expression('child'))) ->createCommand($this->db) ->queryAll(); @@ -257,7 +242,7 @@ class DbManager extends Manager 'user_id' => $userId, 'item_name' => $itemName, 'bizrule' => $bizRule, - 'data' => serialize($data) + 'data' => serialize($data), )); return new Assignment($this, $userId, $itemName, $bizRule, $data); } @@ -271,10 +256,7 @@ class DbManager extends Manager public function revoke($userId, $itemName) { return $this->db->createCommand() - ->delete($this->assignmentTable, array( - 'user_id' => $userId, - 'item_name' => $itemName - )) > 0; + ->delete($this->assignmentTable, array('user_id' => $userId, 'item_name' => $itemName)) > 0; } /** @@ -288,10 +270,7 @@ class DbManager extends Manager $query = new Query; return $query->select(array('item_name')) ->from($this->assignmentTable) - ->where(array( - 'user_id' => $userId, - 'item_name' => $itemName - )) + ->where(array('user_id' => $userId, 'item_name' => $itemName)) ->createCommand($this->db) ->queryScalar() !== false; } @@ -307,10 +286,7 @@ class DbManager extends Manager { $query = new Query; $row = $query->from($this->assignmentTable) - ->where(array( - 'user_id' => $userId, - 'item_name' => $itemName - )) + ->where(array('user_id' => $userId, 'item_name' => $itemName)) ->createCommand($this->db) ->queryRow(); if ($row !== false) { @@ -358,7 +334,7 @@ class DbManager extends Manager 'data' => serialize($assignment->getData()), ), array( 'user_id' => $assignment->getUserId(), - 'item_name' => $assignment->getItemName() + 'item_name' => $assignment->getItemName(), )); } @@ -382,23 +358,13 @@ class DbManager extends Manager ->createCommand($this->db); } elseif ($type === null) { $command = $query->select(array('name', 'type', 'description', 't1.bizrule', 't1.data')) - ->from(array( - $this->itemTable . ' t1', - $this->assignmentTable . ' t2' - )) + ->from(array($this->itemTable . ' t1', $this->assignmentTable . ' t2')) ->where(array('user_id' => $userId, 'name' => new Expression('item_name'))) ->createCommand($this->db); } else { $command = $query->select('name', 'type', 'description', 't1.bizrule', 't1.data') - ->from(array( - $this->itemTable . ' t1', - $this->assignmentTable . ' t2' - )) - ->where(array( - 'user_id' => $userId, - 'type' => $type, - 'name' => new Expression('item_name'), - )) + ->from(array($this->itemTable . ' t1', $this->assignmentTable . ' t2')) + ->where(array('user_id' => $userId, 'type' => $type, 'name' => new Expression('item_name'))) ->createCommand($this->db); } $items = array(); @@ -434,7 +400,7 @@ class DbManager extends Manager 'type' => $type, 'description' => $description, 'bizrule' => $bizRule, - 'data' => serialize($data) + 'data' => serialize($data), )); return new Item($this, $name, $type, $description, $bizRule, $data); } @@ -450,7 +416,7 @@ class DbManager extends Manager $this->db->createCommand() ->delete($this->itemChildTable, array('or', 'parent=:name1', 'child=:name2'), array( ':name1' => $name, - ':name2' => $name + ':name2' => $name, )); $this->db->createCommand()->delete($this->assignmentTable, array('item_name' => $name)); } @@ -488,23 +454,11 @@ class DbManager extends Manager { if ($this->usingSqlite() && $oldName !== null && $item->getName() !== $oldName) { $this->db->createCommand() - ->update($this->itemChildTable, array( - 'parent' => $item->getName(), - ), array( - 'parent' => $oldName, - )); + ->update($this->itemChildTable, array('parent' => $item->getName()), array('parent' => $oldName)); $this->db->createCommand() - ->update($this->itemChildTable, array( - 'child' => $item->getName(), - ), array( - 'child' => $oldName, - )); + ->update($this->itemChildTable, array('child' => $item->getName()), array('child' => $oldName)); $this->db->createCommand() - ->update($this->assignmentTable, array( - 'item_name' => $item->getName(), - ), array( - 'item_name' => $oldName, - )); + ->update($this->assignmentTable, array('item_name' => $item->getName()), array('item_name' => $oldName)); } $this->db->createCommand() From a24e4d30f634e126093095a5e62fbaf7d0b02ca4 Mon Sep 17 00:00:00 2001 From: Alexander Kochetov Date: Sat, 11 May 2013 09:48:04 +0400 Subject: [PATCH 03/33] RBAC: DbManager code style fixes --- yii/rbac/DbManager.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/yii/rbac/DbManager.php b/yii/rbac/DbManager.php index 4618f54..28b14d9 100644 --- a/yii/rbac/DbManager.php +++ b/yii/rbac/DbManager.php @@ -414,13 +414,12 @@ class DbManager extends Manager { if ($this->usingSqlite()) { $this->db->createCommand() - ->delete($this->itemChildTable, array('or', 'parent=:name1', 'child=:name2'), array( - ':name1' => $name, - ':name2' => $name, - )); - $this->db->createCommand()->delete($this->assignmentTable, array('item_name' => $name)); + ->delete($this->itemChildTable, array('or', 'parent=:name', 'child=:name'), array(':name' => $name)); + $this->db->createCommand() + ->delete($this->assignmentTable, array('item_name' => $name)); } - return $this->db->createCommand()->delete($this->itemTable, array('name' => $name)) > 0; + return $this->db->createCommand() + ->delete($this->itemTable, array('name' => $name)) > 0; } /** From 6a7529505eaf25c42041e2d4d002ae5b1b9c212a Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 11 May 2013 13:16:47 +0200 Subject: [PATCH 04/33] cache tests refactoring cherry picked from redis branch --- tests/unit/framework/caching/ApcCacheTest.php | 8 +++ tests/unit/framework/caching/CacheTest.php | 80 ++++++++++++++++++++++---- tests/unit/framework/caching/MemCachedTest.php | 2 +- 3 files changed, 78 insertions(+), 12 deletions(-) diff --git a/tests/unit/framework/caching/ApcCacheTest.php b/tests/unit/framework/caching/ApcCacheTest.php index 6018ce7..20a1cc8 100644 --- a/tests/unit/framework/caching/ApcCacheTest.php +++ b/tests/unit/framework/caching/ApcCacheTest.php @@ -21,9 +21,17 @@ class ApcCacheTest extends CacheTest $this->markTestSkipped("APC cli is not enabled. Skipping."); } + if(!ini_get("apc.enabled") || !ini_get("apc.enable_cli")) { + $this->markTestSkipped("APC is installed but not enabled. Skipping."); + } + if ($this->_cacheInstance === null) { $this->_cacheInstance = new ApcCache(); } return $this->_cacheInstance; } + + // TODO there seems to be a problem with APC returning cached value even if it is expired. + // TODO makes test fail on PHP 5.3.10-1ubuntu3.6 with Suhosin-Patch (cli) -- cebe + // TODO http://drupal.org/node/1278292 } diff --git a/tests/unit/framework/caching/CacheTest.php b/tests/unit/framework/caching/CacheTest.php index a17f126..6a9632c 100644 --- a/tests/unit/framework/caching/CacheTest.php +++ b/tests/unit/framework/caching/CacheTest.php @@ -18,19 +18,46 @@ abstract class CacheTest extends TestCase parent::setUp(); $this->mockApplication(); } - + + /** + * @return Cache + */ + public function prepare() + { + $cache = $this->getCacheInstance(); + + $cache->flush(); + $cache->set('string_test', 'string_test'); + $cache->set('number_test', 42); + $cache->set('array_test', array('array_test' => 'array_test')); + $cache['arrayaccess_test'] = new \stdClass(); + + return $cache; + } + + /** + * default value of cache prefix is application id + */ + public function testKeyPrefix() + { + $cache = $this->getCacheInstance(); + $this->assertNotNull(\Yii::$app->id); + $this->assertEquals(\Yii::$app->id, $cache->keyPrefix); + } + public function testSet() { $cache = $this->getCacheInstance(); + $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(); } public function testGet() { - $cache = $this->getCacheInstance(); + $cache = $this->prepare(); + $this->assertEquals('string_test', $cache->get('string_test')); $this->assertEquals(42, $cache->get('number_test')); @@ -38,51 +65,82 @@ abstract class CacheTest extends TestCase $array = $cache->get('array_test'); $this->assertArrayHasKey('array_test', $array); $this->assertEquals('array_test', $array['array_test']); + } + public function testArrayAccess() + { + $cache = $this->getCacheInstance(); + + $cache['arrayaccess_test'] = new \stdClass(); $this->assertInstanceOf('stdClass', $cache['arrayaccess_test']); } - public function testMget() + public function testGetNonExistent() { $cache = $this->getCacheInstance(); + + $this->assertFalse($cache->get('non_existent_key')); + } + + public function testStoreSpecialValues() + { + $cache = $this->getCacheInstance(); + + $this->assertTrue($cache->set('null_value', null)); + $this->assertNull($cache->get('null_value')); + + $this->assertTrue($cache->set('bool_value', true)); + $this->assertTrue($cache->get('bool_value')); + } + + public function testMget() + { + $cache = $this->prepare(); + $this->assertEquals(array('string_test' => 'string_test', 'number_test' => 42), $cache->mget(array('string_test', 'number_test'))); + // ensure that order does not matter + $this->assertEquals(array('number_test' => 42, 'string_test' => 'string_test'), $cache->mget(array('number_test', 'string_test'))); + $this->assertEquals(array('number_test' => 42, 'non_existent_key' => null), $cache->mget(array('number_test', 'non_existent_key'))); } public function testExpire() { $cache = $this->getCacheInstance(); + $this->assertTrue($cache->set('expire_test', 'expire_test', 2)); sleep(1); $this->assertEquals('expire_test', $cache->get('expire_test')); sleep(2); - $this->assertEquals(false, $cache->get('expire_test')); + $this->assertFalse($cache->get('expire_test')); } public function testAdd() { - $cache = $this->getCacheInstance(); + $cache = $this->prepare(); // should not change existing keys $this->assertFalse($cache->add('number_test', 13)); $this->assertEquals(42, $cache->get('number_test')); - // should store data is it's not there yet + // should store data if it's not there yet + $this->assertFalse($cache->get('add_test')); $this->assertTrue($cache->add('add_test', 13)); $this->assertEquals(13, $cache->get('add_test')); } public function testDelete() { - $cache = $this->getCacheInstance(); + $cache = $this->prepare(); + $this->assertNotNull($cache->get('number_test')); $this->assertTrue($cache->delete('number_test')); - $this->assertEquals(null, $cache->get('number_test')); + $this->assertFalse($cache->get('number_test')); } public function testFlush() { - $cache = $this->getCacheInstance(); + $cache = $this->prepare(); $this->assertTrue($cache->flush()); - $this->assertEquals(null, $cache->get('add_test')); + $this->assertFalse($cache->get('number_test')); } } diff --git a/tests/unit/framework/caching/MemCachedTest.php b/tests/unit/framework/caching/MemCachedTest.php index 66dfb50..bce23ba 100644 --- a/tests/unit/framework/caching/MemCachedTest.php +++ b/tests/unit/framework/caching/MemCachedTest.php @@ -4,7 +4,7 @@ use yii\caching\MemCache; use yiiunit\TestCase; /** - * Class for testing memcache cache backend + * Class for testing memcached cache backend */ class MemCachedTest extends CacheTest { From 9c2eb4df0a24aa357a9b9070b571cc9131d7ea8a Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 11 May 2013 13:20:19 +0200 Subject: [PATCH 05/33] doc fix in caching classes --- yii/caching/ApcCache.php | 2 +- yii/caching/Cache.php | 2 +- yii/caching/DbCache.php | 2 +- yii/caching/DummyCache.php | 2 +- yii/caching/FileCache.php | 2 +- yii/caching/MemCache.php | 7 +++---- yii/caching/WinCache.php | 2 +- yii/caching/XCache.php | 2 +- yii/caching/ZendDataCache.php | 2 +- 9 files changed, 11 insertions(+), 12 deletions(-) diff --git a/yii/caching/ApcCache.php b/yii/caching/ApcCache.php index 391851d..ff3acf5 100644 --- a/yii/caching/ApcCache.php +++ b/yii/caching/ApcCache.php @@ -24,7 +24,7 @@ class ApcCache extends Cache * Retrieves a value from cache with a specified key. * This is the implementation of the method declared in the parent class. * @param string $key a unique key identifying the cached value - * @return string the value stored in cache, false if the value is not in the cache or expired. + * @return string|boolean the value stored in cache, false if the value is not in the cache or expired. */ protected function getValue($key) { diff --git a/yii/caching/Cache.php b/yii/caching/Cache.php index ffc7081..efef048 100644 --- a/yii/caching/Cache.php +++ b/yii/caching/Cache.php @@ -247,7 +247,7 @@ abstract class Cache extends Component implements \ArrayAccess * This method should be implemented by child classes to retrieve the data * from specific cache storage. * @param string $key a unique key identifying the cached value - * @return string the value stored in cache, false if the value is not in the cache or expired. + * @return string|boolean the value stored in cache, false if the value is not in the cache or expired. */ abstract protected function getValue($key); diff --git a/yii/caching/DbCache.php b/yii/caching/DbCache.php index dee8c7a..7e5f12d 100644 --- a/yii/caching/DbCache.php +++ b/yii/caching/DbCache.php @@ -92,7 +92,7 @@ class DbCache extends Cache * Retrieves a value from cache with a specified key. * This is the implementation of the method declared in the parent class. * @param string $key a unique key identifying the cached value - * @return string the value stored in cache, false if the value is not in the cache or expired. + * @return string|boolean the value stored in cache, false if the value is not in the cache or expired. */ protected function getValue($key) { diff --git a/yii/caching/DummyCache.php b/yii/caching/DummyCache.php index 359fa7c..8d900df 100644 --- a/yii/caching/DummyCache.php +++ b/yii/caching/DummyCache.php @@ -24,7 +24,7 @@ class DummyCache extends Cache * Retrieves a value from cache with a specified key. * This is the implementation of the method declared in the parent class. * @param string $key a unique key identifying the cached value - * @return string the value stored in cache, false if the value is not in the cache or expired. + * @return string|boolean the value stored in cache, false if the value is not in the cache or expired. */ protected function getValue($key) { diff --git a/yii/caching/FileCache.php b/yii/caching/FileCache.php index cc1a871..0c6d119 100644 --- a/yii/caching/FileCache.php +++ b/yii/caching/FileCache.php @@ -61,7 +61,7 @@ class FileCache extends Cache * Retrieves a value from cache with a specified key. * This is the implementation of the method declared in the parent class. * @param string $key a unique key identifying the cached value - * @return string the value stored in cache, false if the value is not in the cache or expired. + * @return string|boolean the value stored in cache, false if the value is not in the cache or expired. */ protected function getValue($key) { diff --git a/yii/caching/MemCache.php b/yii/caching/MemCache.php index e6b9b53..34c10d0 100644 --- a/yii/caching/MemCache.php +++ b/yii/caching/MemCache.php @@ -7,7 +7,6 @@ namespace yii\caching; -use yii\base\Exception; use yii\base\InvalidConfigException; /** @@ -21,7 +20,7 @@ use yii\base\InvalidConfigException; * MemCache can be configured with a list of memcache servers by settings its [[servers]] property. * By default, MemCache assumes there is a memcache server running on localhost at port 11211. * - * See [[Cache]] for common cache operations that ApcCache supports. + * See [[Cache]] for common cache operations that MemCache supports. * * Note, there is no security measure to protected data in memcache. * All data in memcache can be accessed by any process running in the system. @@ -89,7 +88,7 @@ class MemCache extends Cache if (count($servers)) { foreach ($servers as $server) { if ($server->host === null) { - throw new Exception("The 'host' property must be specified for every memcache server."); + throw new InvalidConfigException("The 'host' property must be specified for every memcache server."); } if ($this->useMemcached) { $cache->addServer($server->host, $server->port, $server->weight); @@ -145,7 +144,7 @@ class MemCache extends Cache * Retrieves a value from cache with a specified key. * This is the implementation of the method declared in the parent class. * @param string $key a unique key identifying the cached value - * @return string the value stored in cache, false if the value is not in the cache or expired. + * @return string|boolean the value stored in cache, false if the value is not in the cache or expired. */ protected function getValue($key) { diff --git a/yii/caching/WinCache.php b/yii/caching/WinCache.php index 4e07c7f..eed580d 100644 --- a/yii/caching/WinCache.php +++ b/yii/caching/WinCache.php @@ -24,7 +24,7 @@ class WinCache extends Cache * Retrieves a value from cache with a specified key. * This is the implementation of the method declared in the parent class. * @param string $key a unique key identifying the cached value - * @return string the value stored in cache, false if the value is not in the cache or expired. + * @return string|boolean the value stored in cache, false if the value is not in the cache or expired. */ protected function getValue($key) { diff --git a/yii/caching/XCache.php b/yii/caching/XCache.php index 2108c4f..91f483b 100644 --- a/yii/caching/XCache.php +++ b/yii/caching/XCache.php @@ -25,7 +25,7 @@ class XCache extends Cache * Retrieves a value from cache with a specified key. * This is the implementation of the method declared in the parent class. * @param string $key a unique key identifying the cached value - * @return string the value stored in cache, false if the value is not in the cache or expired. + * @return string|boolean the value stored in cache, false if the value is not in the cache or expired. */ protected function getValue($key) { diff --git a/yii/caching/ZendDataCache.php b/yii/caching/ZendDataCache.php index 5b41a8d..9ff2fd0 100644 --- a/yii/caching/ZendDataCache.php +++ b/yii/caching/ZendDataCache.php @@ -24,7 +24,7 @@ class ZendDataCache extends Cache * Retrieves a value from cache with a specified key. * This is the implementation of the method declared in the parent class. * @param string $key a unique key identifying the cached value - * @return string the value stored in cache, false if the value is not in the cache or expired. + * @return string|boolean the value stored in cache, false if the value is not in the cache or expired. */ protected function getValue($key) { From 71a9efdd554a3f23e0848b7a82e4ef355ccbac46 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 11 May 2013 13:33:05 +0200 Subject: [PATCH 06/33] changed cache test to wait more than expirytime change is to avoid random test failure on race condition. fixes #203 #231 --- tests/unit/framework/caching/CacheTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit/framework/caching/CacheTest.php b/tests/unit/framework/caching/CacheTest.php index 6a9632c..c524956 100644 --- a/tests/unit/framework/caching/CacheTest.php +++ b/tests/unit/framework/caching/CacheTest.php @@ -110,7 +110,8 @@ abstract class CacheTest extends TestCase $this->assertTrue($cache->set('expire_test', 'expire_test', 2)); sleep(1); $this->assertEquals('expire_test', $cache->get('expire_test')); - sleep(2); + // wait a bit more than 2 sec to avoid random test failure + usleep(2500000); $this->assertFalse($cache->get('expire_test')); } From 3374c782331b47bd5b3f080ba7e7a1b30130e253 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Sat, 11 May 2013 16:06:34 +0300 Subject: [PATCH 07/33] "YiiRequirementChecker" has been created as blank. --- .../requirements/YiiRequirementCheckerTest.php | 17 +++++++++++++++++ yii/requirements/YiiRequirementChecker.php | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 tests/unit/framework/requirements/YiiRequirementCheckerTest.php create mode 100644 yii/requirements/YiiRequirementChecker.php diff --git a/tests/unit/framework/requirements/YiiRequirementCheckerTest.php b/tests/unit/framework/requirements/YiiRequirementCheckerTest.php new file mode 100644 index 0000000..603308c --- /dev/null +++ b/tests/unit/framework/requirements/YiiRequirementCheckerTest.php @@ -0,0 +1,17 @@ +assertTrue(is_object($requirementChecker)); + } +} diff --git a/yii/requirements/YiiRequirementChecker.php b/yii/requirements/YiiRequirementChecker.php new file mode 100644 index 0000000..e604f4e --- /dev/null +++ b/yii/requirements/YiiRequirementChecker.php @@ -0,0 +1,17 @@ + + * @since 2.0 + */ +class YiiRequirementChecker +{ + +} From 07fabfd6fcf37f9ef1f077a49f6e9f5b30379536 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Sat, 11 May 2013 16:18:14 +0300 Subject: [PATCH 08/33] Method "YiiRequirementChecker::check()" has been implemented. --- .../requirements/YiiRequirementCheckerTest.php | 47 ++++++++++- yii/requirements/YiiRequirementChecker.php | 97 ++++++++++++++++++++++ 2 files changed, 141 insertions(+), 3 deletions(-) diff --git a/tests/unit/framework/requirements/YiiRequirementCheckerTest.php b/tests/unit/framework/requirements/YiiRequirementCheckerTest.php index 603308c..79eafb7 100644 --- a/tests/unit/framework/requirements/YiiRequirementCheckerTest.php +++ b/tests/unit/framework/requirements/YiiRequirementCheckerTest.php @@ -10,8 +10,49 @@ use yiiunit\TestCase; */ class YiiRequirementCheckerTest extends TestCase { - public function testCreate() { - $requirementChecker = new YiiRequirementChecker(); - $this->assertTrue(is_object($requirementChecker)); + public function testCheck() + { + $requirementsChecker = new YiiRequirementChecker(); + + $requirements = array( + 'requirementPass' => array( + 'name' => 'Requirement 1', + 'mandatory' => true, + 'condition' => true, + 'by' => 'Requirement 1', + 'memo' => 'Requirement 1', + ), + 'requirementError' => array( + 'name' => 'Requirement 2', + 'mandatory' => true, + 'condition' => false, + 'by' => 'Requirement 2', + 'memo' => 'Requirement 2', + ), + 'requirementWarning' => array( + 'name' => 'Requirement 3', + 'mandatory' => false, + 'condition' => false, + 'by' => 'Requirement 3', + 'memo' => 'Requirement 3', + ), + ); + + $checkResult = $requirementsChecker->check($requirements); + $summary = $checkResult['summary']; + + $this->assertEquals(count($requirements), $summary['total'], 'Wrong summary total!'); + $this->assertEquals(1, $summary['errors'], 'Wrong summary errors!'); + $this->assertEquals(1, $summary['warnings'], 'Wrong summary warnings!'); + + $checkedRequirements = $checkResult['requirements']; + + $this->assertEquals(false, $checkedRequirements['requirementPass']['error'], 'Passed requirement has an error!'); + $this->assertEquals(false, $checkedRequirements['requirementPass']['warning'], 'Passed requirement has a warning!'); + + $this->assertEquals(true, $checkedRequirements['requirementError']['error'], 'Error requirement has no error!'); + + $this->assertEquals(false, $checkedRequirements['requirementWarning']['error'], 'Error requirement has an error!'); + $this->assertEquals(true, $checkedRequirements['requirementWarning']['warning'], 'Error requirement has no warning!'); } } diff --git a/yii/requirements/YiiRequirementChecker.php b/yii/requirements/YiiRequirementChecker.php index e604f4e..fb4e42e 100644 --- a/yii/requirements/YiiRequirementChecker.php +++ b/yii/requirements/YiiRequirementChecker.php @@ -8,10 +8,107 @@ /** * YiiRequirementChecker allows checking, if current system meets the requirements for running the application. * + * @property array|null $result the check results. + * * @author Paul Klimov * @since 2.0 */ class YiiRequirementChecker { + function check($requirements) + { + if (!is_array($requirements)) { + $this->usageError("Requirements must be an array!"); + } + $summary = array( + 'total' => 0, + 'errors' => 0, + 'warnings' => 0, + ); + foreach ($requirements as $key => $rawRequirement) { + $requirement = $this->normalizeRequirement($rawRequirement, $key); + + $summary['total']++; + if (!$requirement['condition']) { + if ($requirement['mandatory']) { + $requirement['error'] = true; + $requirement['warning'] = true; + $summary['errors']++; + } else { + $requirement['error'] = false; + $requirement['warning'] = true; + $summary['warnings']++; + } + } else { + $requirement['error'] = false; + $requirement['warning'] = false; + } + $requirements[$key] = $requirement; + } + $result = array( + 'summary' => $summary, + 'requirements' => $requirements, + ); + return $result; + } + + /** + * Normalizes requirement ensuring it has correct format. + * @param array $requirement raw requirement. + * @param int $requirementKey requirement key in the list. + * @return array normalized requirement. + */ + function normalizeRequirement($requirement, $requirementKey=0) + { + if (!is_array($requirement)) { + $this->usageError('Requirement must be an array!'); + } + if (!array_key_exists('condition', $requirement)) { + $this->usageError("Requirement '{$requirementKey}' has no condition!"); + } else { + $evalPrefix = 'eval:'; + if (is_string($requirement['condition']) && strpos($requirement['condition'], $evalPrefix)===0) { + $expression = substr($requirement['condition'], strlen($evalPrefix)); + $requirement['condition'] = $this->evaluateExpression($expression); + } + } + if (!array_key_exists('name', $requirement)) { + $requirement['name'] = is_numeric($requirementKey) ? 'Requirement #'.$requirementKey : $requirementKey; + } + if (!array_key_exists('mandatory', $requirement)) { + if (array_key_exists('required', $requirement)) { + $requirement['mandatory'] = $requirement['required']; + } else { + $requirement['mandatory'] = false; + } + } + if (!array_key_exists('by', $requirement)) { + $requirement['by'] = 'Unknown'; + } + if (!array_key_exists('memo', $requirement)) { + $requirement['memo'] = ''; + } + return $requirement; + } + + /** + * Displays a usage error. + * This method will then terminate the execution of the current application. + * @param string $message the error message + */ + function usageError($message) + { + echo "Error: $message\n\n"; + exit(1); + } + /** + * Evaluates a PHP expression under the context of this class. + * @param string $expression a PHP expression to be evaluated. + * @return mixed the expression result. + */ + function evaluateExpression($expression) + { + return eval('return '.$expression.';'); + } } From 00d04a1c30e581453e414702cf521d34408d1e1a Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Sat, 11 May 2013 16:26:16 +0300 Subject: [PATCH 09/33] Method "YiiRequirementChecker::check()" has been refactored to be used in method chain. Methods "YiiRequirementChecker::getResults()" and "YiiRequirementChecker::render()" have been added. --- .../requirements/YiiRequirementCheckerTest.php | 2 +- yii/requirements/YiiRequirementChecker.php | 28 +++++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/tests/unit/framework/requirements/YiiRequirementCheckerTest.php b/tests/unit/framework/requirements/YiiRequirementCheckerTest.php index 79eafb7..9661738 100644 --- a/tests/unit/framework/requirements/YiiRequirementCheckerTest.php +++ b/tests/unit/framework/requirements/YiiRequirementCheckerTest.php @@ -38,7 +38,7 @@ class YiiRequirementCheckerTest extends TestCase ), ); - $checkResult = $requirementsChecker->check($requirements); + $checkResult = $requirementsChecker->check($requirements)->getResult(); $summary = $checkResult['summary']; $this->assertEquals(count($requirements), $summary['total'], 'Wrong summary total!'); diff --git a/yii/requirements/YiiRequirementChecker.php b/yii/requirements/YiiRequirementChecker.php index fb4e42e..46fe106 100644 --- a/yii/requirements/YiiRequirementChecker.php +++ b/yii/requirements/YiiRequirementChecker.php @@ -49,7 +49,33 @@ class YiiRequirementChecker 'summary' => $summary, 'requirements' => $requirements, ); - return $result; + $this->result = $result; + return $this; + } + + /** + * Return the check results. + * @return array|null check results. + */ + function getResult() + { + if (isset($this->result)) { + return $this->result; + } else { + return null; + } + } + + /** + * Renders the requirements check result. + * The output will vary depending is a script running from web or from console. + */ + function render() + { + if (isset($this->result)) { + $this->usageError('Nothing to render!'); + } + // @todo render } /** From 3b7ac70c6993eed6204f1440695b0af180efcf21 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Sat, 11 May 2013 16:27:58 +0300 Subject: [PATCH 10/33] Test case "YiiRequirementCheckerTest::testCheckEval()" has been added. --- .../requirements/YiiRequirementCheckerTest.php | 29 ++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/unit/framework/requirements/YiiRequirementCheckerTest.php b/tests/unit/framework/requirements/YiiRequirementCheckerTest.php index 9661738..66a81b7 100644 --- a/tests/unit/framework/requirements/YiiRequirementCheckerTest.php +++ b/tests/unit/framework/requirements/YiiRequirementCheckerTest.php @@ -55,4 +55,33 @@ class YiiRequirementCheckerTest extends TestCase $this->assertEquals(false, $checkedRequirements['requirementWarning']['error'], 'Error requirement has an error!'); $this->assertEquals(true, $checkedRequirements['requirementWarning']['warning'], 'Error requirement has no warning!'); } + + public function testCheckEval() { + $requirementsChecker = new YiiRequirementChecker(); + + $requirements = array( + 'requirementPass' => array( + 'name' => 'Requirement 1', + 'mandatory' => true, + 'condition' => 'eval:2>1', + 'by' => 'Requirement 1', + 'memo' => 'Requirement 1', + ), + 'requirementError' => array( + 'name' => 'Requirement 2', + 'mandatory' => true, + 'condition' => 'eval:2<1', + 'by' => 'Requirement 2', + 'memo' => 'Requirement 2', + ), + ); + + $checkResult = $requirementsChecker->check($requirements)->getResult(); + $checkedRequirements = $checkResult['requirements']; + + $this->assertEquals(false, $checkedRequirements['requirementPass']['error'], 'Passed requirement has an error!'); + $this->assertEquals(false, $checkedRequirements['requirementPass']['warning'], 'Passed requirement has a warning!'); + + $this->assertEquals(true, $checkedRequirements['requirementError']['error'], 'Error requirement has no error!'); + } } From e2513de6c6207fa37894a944e10b8cd420ac6804 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 11 May 2013 15:39:19 +0200 Subject: [PATCH 11/33] better testing: time()-function-mock as discussed in #203 https://github.com/yiisoft/yii2/issues/203#issuecomment-17759631 --- tests/unit/framework/caching/CacheTest.php | 23 +++++++++++++++++++++++ tests/unit/framework/caching/DbCacheTest.php | 13 +++++++++++++ tests/unit/framework/caching/FileCacheTest.php | 11 +++++++++++ 3 files changed, 47 insertions(+) diff --git a/tests/unit/framework/caching/CacheTest.php b/tests/unit/framework/caching/CacheTest.php index c524956..4b34de0 100644 --- a/tests/unit/framework/caching/CacheTest.php +++ b/tests/unit/framework/caching/CacheTest.php @@ -1,5 +1,17 @@ mockApplication(); } + protected function tearDown() + { + static::$time = null; + } + /** * @return Cache */ diff --git a/tests/unit/framework/caching/DbCacheTest.php b/tests/unit/framework/caching/DbCacheTest.php index 36174b9..a2ff9e3 100644 --- a/tests/unit/framework/caching/DbCacheTest.php +++ b/tests/unit/framework/caching/DbCacheTest.php @@ -1,5 +1,7 @@ _cacheInstance; } + + public function testExpire() + { + $cache = $this->getCacheInstance(); + + $this->assertTrue($cache->set('expire_test', 'expire_test', 2)); + static::$time = time() + 1; + $this->assertEquals('expire_test', $cache->get('expire_test')); + static::$time = time() + 2; + $this->assertFalse($cache->get('expire_test')); + } } diff --git a/tests/unit/framework/caching/FileCacheTest.php b/tests/unit/framework/caching/FileCacheTest.php index 99e3cbc..4499d9c 100644 --- a/tests/unit/framework/caching/FileCacheTest.php +++ b/tests/unit/framework/caching/FileCacheTest.php @@ -22,4 +22,15 @@ class FileCacheTest extends CacheTest } return $this->_cacheInstance; } + + public function testExpire() + { + $cache = $this->getCacheInstance(); + + $this->assertTrue($cache->set('expire_test', 'expire_test', 2)); + static::$time = time() + 1; + $this->assertEquals('expire_test', $cache->get('expire_test')); + static::$time = time() + 2; + $this->assertFalse($cache->get('expire_test')); + } } From f82a01e4dd212e88af10a2ef7baa7bca07b569d9 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Sat, 11 May 2013 17:01:54 +0300 Subject: [PATCH 12/33] "YiiRequirementChecker::check()" has been updated to be able to collect different requirements set in the chained call. --- .../requirements/YiiRequirementCheckerTest.php | 56 ++++++++++++++++++---- yii/requirements/YiiRequirementChecker.php | 36 ++++++++------ 2 files changed, 69 insertions(+), 23 deletions(-) diff --git a/tests/unit/framework/requirements/YiiRequirementCheckerTest.php b/tests/unit/framework/requirements/YiiRequirementCheckerTest.php index 66a81b7..3dc8265 100644 --- a/tests/unit/framework/requirements/YiiRequirementCheckerTest.php +++ b/tests/unit/framework/requirements/YiiRequirementCheckerTest.php @@ -46,16 +46,20 @@ class YiiRequirementCheckerTest extends TestCase $this->assertEquals(1, $summary['warnings'], 'Wrong summary warnings!'); $checkedRequirements = $checkResult['requirements']; + $requirementsKeys = array_flip(array_keys($requirements)); - $this->assertEquals(false, $checkedRequirements['requirementPass']['error'], 'Passed requirement has an error!'); - $this->assertEquals(false, $checkedRequirements['requirementPass']['warning'], 'Passed requirement has a warning!'); + $this->assertEquals(false, $checkedRequirements[$requirementsKeys['requirementPass']]['error'], 'Passed requirement has an error!'); + $this->assertEquals(false, $checkedRequirements[$requirementsKeys['requirementPass']]['warning'], 'Passed requirement has a warning!'); - $this->assertEquals(true, $checkedRequirements['requirementError']['error'], 'Error requirement has no error!'); + $this->assertEquals(true, $checkedRequirements[$requirementsKeys['requirementError']]['error'], 'Error requirement has no error!'); - $this->assertEquals(false, $checkedRequirements['requirementWarning']['error'], 'Error requirement has an error!'); - $this->assertEquals(true, $checkedRequirements['requirementWarning']['warning'], 'Error requirement has no warning!'); + $this->assertEquals(false, $checkedRequirements[$requirementsKeys['requirementWarning']]['error'], 'Error requirement has an error!'); + $this->assertEquals(true, $checkedRequirements[$requirementsKeys['requirementWarning']]['warning'], 'Error requirement has no warning!'); } + /** + * @depends testCheck + */ public function testCheckEval() { $requirementsChecker = new YiiRequirementChecker(); @@ -78,10 +82,46 @@ class YiiRequirementCheckerTest extends TestCase $checkResult = $requirementsChecker->check($requirements)->getResult(); $checkedRequirements = $checkResult['requirements']; + $requirementsKeys = array_flip(array_keys($requirements)); - $this->assertEquals(false, $checkedRequirements['requirementPass']['error'], 'Passed requirement has an error!'); - $this->assertEquals(false, $checkedRequirements['requirementPass']['warning'], 'Passed requirement has a warning!'); + $this->assertEquals(false, $checkedRequirements[$requirementsKeys['requirementPass']]['error'], 'Passed requirement has an error!'); + $this->assertEquals(false, $checkedRequirements[$requirementsKeys['requirementPass']]['warning'], 'Passed requirement has a warning!'); - $this->assertEquals(true, $checkedRequirements['requirementError']['error'], 'Error requirement has no error!'); + $this->assertEquals(true, $checkedRequirements[$requirementsKeys['requirementError']]['error'], 'Error requirement has no error!'); + } + + /** + * @depends testCheck + */ + public function testCheckChained() + { + $requirementsChecker = new YiiRequirementChecker(); + + $requirements1 = array( + array( + 'name' => 'Requirement 1', + 'mandatory' => true, + 'condition' => true, + 'by' => 'Requirement 1', + 'memo' => 'Requirement 1', + ), + ); + $requirements2 = array( + array( + 'name' => 'Requirement 2', + 'mandatory' => true, + 'condition' => true, + 'by' => 'Requirement 2', + 'memo' => 'Requirement 2', + ), + ); + $checkResult = $requirementsChecker->check($requirements1)->check($requirements2)->getResult(); + + $mergedRequirements = array_merge($requirements1, $requirements2); + + $this->assertEquals(count($mergedRequirements), $checkResult['summary']['total'], 'Wrong total checks count!'); + foreach ($mergedRequirements as $key => $mergedRequirement) { + $this->assertEquals($mergedRequirement['name'], $checkResult['requirements'][$key]['name'], 'Wrong requirements list!'); + } } } diff --git a/yii/requirements/YiiRequirementChecker.php b/yii/requirements/YiiRequirementChecker.php index 46fe106..26cb041 100644 --- a/yii/requirements/YiiRequirementChecker.php +++ b/yii/requirements/YiiRequirementChecker.php @@ -15,41 +15,47 @@ */ class YiiRequirementChecker { + /** + * Check the given requirements, collecting results into internal field. + * This method can be invoked several times checking different requirement sets. + * Use {@link getResult()} or {@link render()} to get the results. + * @param array $requirements requirements to be checked. + * @return YiiRequirementChecker self instance. + */ function check($requirements) { if (!is_array($requirements)) { $this->usageError("Requirements must be an array!"); } - $summary = array( - 'total' => 0, - 'errors' => 0, - 'warnings' => 0, - ); + if (!isset($this->result)) { + $this->result = array( + 'summary' => array( + 'total' => 0, + 'errors' => 0, + 'warnings' => 0, + ), + 'requirements' => array(), + ); + } foreach ($requirements as $key => $rawRequirement) { $requirement = $this->normalizeRequirement($rawRequirement, $key); - - $summary['total']++; + $this->result['summary']['total']++; if (!$requirement['condition']) { if ($requirement['mandatory']) { $requirement['error'] = true; $requirement['warning'] = true; - $summary['errors']++; + $this->result['summary']['errors']++; } else { $requirement['error'] = false; $requirement['warning'] = true; - $summary['warnings']++; + $this->result['summary']['warnings']++; } } else { $requirement['error'] = false; $requirement['warning'] = false; } - $requirements[$key] = $requirement; + $this->result['requirements'][] = $requirement; } - $result = array( - 'summary' => $summary, - 'requirements' => $requirements, - ); - $this->result = $result; return $this; } From 48f388442f0ed19e1d35db35d539221486d5db97 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 11 May 2013 16:08:14 +0200 Subject: [PATCH 13/33] mocking time in test only usefull when using time() once https://github.com/yiisoft/yii2/commit/e2513de6c6207fa37894a944e10b8cd420ac6804#commitcomment-3192244 --- tests/unit/framework/caching/DbCacheTest.php | 5 +++-- tests/unit/framework/caching/FileCacheTest.php | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/unit/framework/caching/DbCacheTest.php b/tests/unit/framework/caching/DbCacheTest.php index a2ff9e3..f5bbba5 100644 --- a/tests/unit/framework/caching/DbCacheTest.php +++ b/tests/unit/framework/caching/DbCacheTest.php @@ -77,10 +77,11 @@ class DbCacheTest extends CacheTest { $cache = $this->getCacheInstance(); + static::$time = \time(); $this->assertTrue($cache->set('expire_test', 'expire_test', 2)); - static::$time = time() + 1; + static::$time++; $this->assertEquals('expire_test', $cache->get('expire_test')); - static::$time = time() + 2; + static::$time++; $this->assertFalse($cache->get('expire_test')); } } diff --git a/tests/unit/framework/caching/FileCacheTest.php b/tests/unit/framework/caching/FileCacheTest.php index 4499d9c..b3ac8b7 100644 --- a/tests/unit/framework/caching/FileCacheTest.php +++ b/tests/unit/framework/caching/FileCacheTest.php @@ -27,10 +27,11 @@ class FileCacheTest extends CacheTest { $cache = $this->getCacheInstance(); + static::$time = \time(); $this->assertTrue($cache->set('expire_test', 'expire_test', 2)); - static::$time = time() + 1; + static::$time++; $this->assertEquals('expire_test', $cache->get('expire_test')); - static::$time = time() + 2; + static::$time++; $this->assertFalse($cache->get('expire_test')); } } From c5d382c68a41641ab7c9c7651495e5991788a489 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 11 May 2013 16:24:44 +0200 Subject: [PATCH 14/33] added newline to end of header and body blocks in View issue #237 --- yii/base/View.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yii/base/View.php b/yii/base/View.php index f1a9a0f..84a6f11 100644 --- a/yii/base/View.php +++ b/yii/base/View.php @@ -771,7 +771,7 @@ class View extends Component if (!empty($this->js[self::POS_HEAD])) { $lines[] = implode("\n", $this->js[self::POS_HEAD]); } - return implode("\n", $lines); + return empty($lines) ? '' : implode("\n", $lines) . "\n"; } /** @@ -788,7 +788,7 @@ class View extends Component if (!empty($this->js[self::POS_BEGIN])) { $lines[] = implode("\n", $this->js[self::POS_BEGIN]); } - return implode("\n", $lines); + return empty($lines) ? '' : implode("\n", $lines) . "\n"; } /** @@ -805,6 +805,6 @@ class View extends Component if (!empty($this->js[self::POS_END])) { $lines[] = implode("\n", $this->js[self::POS_END]); } - return implode("\n", $lines); + return empty($lines) ? '' : implode("\n", $lines) . "\n"; } } From 2bdb5f803a977fb7ed053cb7db06c94a467a4f65 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Sat, 11 May 2013 18:43:48 +0300 Subject: [PATCH 15/33] "YiiRequirementChecker::render()" has been implemented, console view has been created. --- yii/requirements/YiiRequirementChecker.php | 37 ++++++++++++++++++-- yii/requirements/views/console/index.php | 56 ++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 yii/requirements/views/console/index.php diff --git a/yii/requirements/YiiRequirementChecker.php b/yii/requirements/YiiRequirementChecker.php index 26cb041..9b931d6 100644 --- a/yii/requirements/YiiRequirementChecker.php +++ b/yii/requirements/YiiRequirementChecker.php @@ -78,10 +78,43 @@ class YiiRequirementChecker */ function render() { - if (isset($this->result)) { + if (!isset($this->result)) { $this->usageError('Nothing to render!'); } - // @todo render + $baseViewFilePath = dirname(__FILE__).DIRECTORY_SEPARATOR.'views'; + if (array_key_exists('argv', $_SERVER)) { + $viewFileName = $baseViewFilePath.DIRECTORY_SEPARATOR.'console'.DIRECTORY_SEPARATOR.'index.php'; + } else { + $viewFileName = $baseViewFilePath.DIRECTORY_SEPARATOR.'web'.DIRECTORY_SEPARATOR.'index.php'; + } + $this->renderViewFile($viewFileName, $this->result); + } + + /** + * Renders a view file. + * This method includes the view file as a PHP script + * and captures the display result if required. + * @param string $_viewFile_ view file + * @param array $_data_ data to be extracted and made available to the view file + * @param boolean $_return_ whether the rendering result should be returned as a string + * @return string the rendering result. Null if the rendering result is not required. + */ + function renderViewFile($_viewFile_, $_data_=null, $_return_=false) + { + // we use special variable names here to avoid conflict when extracting data + if (is_array($_data_)) { + extract($_data_, EXTR_PREFIX_SAME, 'data'); + } else { + $data = $_data_; + } + if ($_return_) { + ob_start(); + ob_implicit_flush(false); + require($_viewFile_); + return ob_get_clean(); + } else { + require($_viewFile_); + } } /** diff --git a/yii/requirements/views/console/index.php b/yii/requirements/views/console/index.php new file mode 100644 index 0000000..b6122fc --- /dev/null +++ b/yii/requirements/views/console/index.php @@ -0,0 +1,56 @@ + 25, + 'condition' => 10, + 'by' => 30, + 'memo' => 50, +); + +// Headers: +$tableLength = count($columnSizes)+1; +foreach ($columnSizes as $columnSize) { + $tableLength += $columnSize; +} +echo str_pad('', $tableLength, '-'); +echo "\n"; +echo '|'.str_pad('Name', $columnSizes['name'], ' ', STR_PAD_BOTH).'|'; +echo str_pad('Result', $columnSizes['condition'], ' ', STR_PAD_BOTH).'|'; +echo str_pad('Required By', $columnSizes['by'], ' ', STR_PAD_BOTH).'|'; +echo str_pad('Memo', $columnSizes['memo'], ' ', STR_PAD_BOTH).'|'; +echo "\n"; +echo str_pad('', $tableLength, '-'); +echo "\n"; + +// Rows: +foreach ($requirements as $requirement) { + $name = $requirement['name']; + echo '|'.str_pad(' '.$name, $columnSizes['name'], ' ', STR_PAD_RIGHT).'|'; + $condition = $requirement['condition'] ? 'Passed' : ($requirement['mandatory'] ? 'FAILED' : 'WARNING'); + echo str_pad($condition, $columnSizes['condition'], ' ', STR_PAD_BOTH).'|'; + $by = strip_tags($requirement['by']); + echo str_pad($by, $columnSizes['by'], ' ', STR_PAD_BOTH).'|'; + $memo = strip_tags($requirement['memo']); + echo str_pad(' '.$memo, $columnSizes['memo'], ' ', STR_PAD_RIGHT).'|'; + echo "\n"; +} +echo str_pad('', $tableLength, '-'); +echo "\n"; + +// Summary +$summaryString = 'Errors: '.$summary['errors'].' Warnings: '.$summary['warnings'].' Total checks: '.$summary['total']; +echo $summaryString; + +echo "\n\n"; \ No newline at end of file From fa724fc0cf17468391323907584a20855f8cba18 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Sat, 11 May 2013 18:56:37 +0300 Subject: [PATCH 16/33] Web view for "YiiRequirementChecker" has been created. --- yii/requirements/YiiRequirementChecker.php | 20 +++++++ yii/requirements/views/web/css.php | 93 ++++++++++++++++++++++++++++++ yii/requirements/views/web/index.php | 82 ++++++++++++++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 yii/requirements/views/web/css.php create mode 100644 yii/requirements/views/web/index.php diff --git a/yii/requirements/YiiRequirementChecker.php b/yii/requirements/YiiRequirementChecker.php index 9b931d6..f9fab20 100644 --- a/yii/requirements/YiiRequirementChecker.php +++ b/yii/requirements/YiiRequirementChecker.php @@ -176,4 +176,24 @@ class YiiRequirementChecker { return eval('return '.$expression.';'); } + + /** + * Returns the server information. + * @return string server information. + */ + function getServerInfo() + { + $info = isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : ''; + return $info; + } + + /** + * Returns the now date if possible in string representation. + * @return string now date. + */ + function getNowDate() + { + $nowDate = @strftime('%Y-%m-%d %H:%M', time()); + return $nowDate; + } } diff --git a/yii/requirements/views/web/css.php b/yii/requirements/views/web/css.php new file mode 100644 index 0000000..bff20d1 --- /dev/null +++ b/yii/requirements/views/web/css.php @@ -0,0 +1,93 @@ +body +{ + background: white; + font-family:'Lucida Grande',Verdana,Geneva,Lucida,Helvetica,Arial,sans-serif; + font-size:10pt; + font-weight:normal; +} + +#page +{ + width: 800px; + margin: 0 auto; +} + +#header +{ +} + +#content +{ +} + +#footer +{ + color: gray; + font-size:8pt; + border-top:1px solid #aaa; + margin-top:10px; +} + +h1 +{ + color:black; + font-size:1.6em; + font-weight:bold; + margin:0.5em 0pt; +} + +h2 +{ + color:black; + font-size:1.25em; + font-weight:bold; + margin:0.3em 0pt; +} + +h3 +{ + color:black; + font-size:1.1em; + font-weight:bold; + margin:0.2em 0pt; +} + +table.result +{ + background:#E6ECFF none repeat scroll 0% 0%; + border-collapse:collapse; + width:100%; +} + +table.result th +{ + background:#CCD9FF none repeat scroll 0% 0%; + text-align:left; +} + +table.result th, table.result td +{ + border:1px solid #BFCFFF; + padding:0.2em; +} + +td.passed +{ + background-color: #60BF60; + border: 1px solid silver; + padding: 2px; +} + +td.warning +{ + background-color: #FFFFBF; + border: 1px solid silver; + padding: 2px; +} + +td.failed +{ + background-color: #FF8080; + border: 1px solid silver; + padding: 2px; +} diff --git a/yii/requirements/views/web/index.php b/yii/requirements/views/web/index.php new file mode 100644 index 0000000..f5196c8 --- /dev/null +++ b/yii/requirements/views/web/index.php @@ -0,0 +1,82 @@ + + + + + + + +Yii Application Requirement Checker + + + +
+ + + +
+

Description

+

+This script checks if your server configuration meets the requirements +for running Yii application. +It checks if the server is running the right version of PHP, +if appropriate PHP extensions have been loaded, and if php.ini file settings are correct. +

+ +

Conclusion

+

+0): ?> +Unfortunately your server configuration does not satisfy the requirements by this application. +0): ?> +Your server configuration satisfies the minimum requirements by this application. Please pay attention to the warnings listed below if your application will use the corresponding features. + +Congratulations! Your server configuration satisfies all requirements. + +

+ +

Details

+ + + + + + + + + + + +
NameResultRequired ByMemo
+ + + + + + + +
+ + + + + + + +
 passed failed warning
+ +
+ + + +
+ + \ No newline at end of file From 9a461edd07a2924bea69c109dc547fdb6911f2f4 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Sat, 11 May 2013 19:07:14 +0300 Subject: [PATCH 17/33] Check helper methods have been added to "YiiRequirementChecker". --- .../requirements/YiiRequirementCheckerTest.php | 68 +++++++++++ yii/requirements/YiiRequirementChecker.php | 126 ++++++++++++++++++++- 2 files changed, 193 insertions(+), 1 deletion(-) diff --git a/tests/unit/framework/requirements/YiiRequirementCheckerTest.php b/tests/unit/framework/requirements/YiiRequirementCheckerTest.php index 3dc8265..691f5c9 100644 --- a/tests/unit/framework/requirements/YiiRequirementCheckerTest.php +++ b/tests/unit/framework/requirements/YiiRequirementCheckerTest.php @@ -124,4 +124,72 @@ class YiiRequirementCheckerTest extends TestCase $this->assertEquals($mergedRequirement['name'], $checkResult['requirements'][$key]['name'], 'Wrong requirements list!'); } } + + public function testCheckPhpExtensionVersion() + { + $requirementsChecker = new YiiRequirementChecker(); + + $this->assertFalse($requirementsChecker->checkPhpExtensionVersion('some_unexisting_php_extension', '0.1'), 'No fail while checking unexisting extension!'); + $this->assertTrue($requirementsChecker->checkPhpExtensionVersion('pdo', '1.0'), 'Unable to check PDO version!'); + } + + /** + * Data provider for {@link testGetByteSize()}. + * @return array + */ + public function dataProviderGetByteSize() + { + return array( + array('456', 456), + array('5K', 5*1024), + array('16KB', 16*1024), + array('4M', 4*1024*1024), + array('14MB', 14*1024*1024), + array('7G', 7*1024*1024*1024), + array('12GB', 12*1024*1024*1024), + ); + } + + /** + * @dataProvider dataProviderGetByteSize + * + * @param string $verboseValue verbose value. + * @param integer $expectedByteSize expected byte size. + */ + public function testGetByteSize($verboseValue, $expectedByteSize) + { + $requirementsChecker = new YiiRequirementChecker(); + + $this->assertEquals($expectedByteSize, $requirementsChecker->getByteSize($verboseValue), "Wrong byte size for '{$verboseValue}'!"); + } + + /** + * Data provider for {@link testCompareByteSize()} + * @return array + */ + public function dataProviderCompareByteSize() + { + return array( + array('2M', '2K', '>', true), + array('2M', '2K', '>=', true), + array('1K', '1024', '==', true), + array('10M', '11M', '<', true), + array('10M', '11M', '<=', true), + ); + } + + /** + * @depends testGetByteSize + * @dataProvider dataProviderCompareByteSize + * + * @param string $a first value. + * @param string $b second value. + * @param string $compare comparison. + * @param boolean $expectedComparisonResult expected comparison result. + */ + public function testCompareByteSize($a, $b, $compare, $expectedComparisonResult) + { + $requirementsChecker = new YiiRequirementChecker(); + $this->assertEquals($expectedComparisonResult, $requirementsChecker->compareByteSize($a, $b, $compare), "Wrong compare '{$a}{$compare}{$b}'"); + } } diff --git a/yii/requirements/YiiRequirementChecker.php b/yii/requirements/YiiRequirementChecker.php index f9fab20..b8997bd 100644 --- a/yii/requirements/YiiRequirementChecker.php +++ b/yii/requirements/YiiRequirementChecker.php @@ -27,7 +27,7 @@ class YiiRequirementChecker if (!is_array($requirements)) { $this->usageError("Requirements must be an array!"); } - if (!isset($this->result)) { + if (!isset($this->result) || !is_array($this->result)) { $this->result = array( 'summary' => array( 'total' => 0, @@ -91,6 +91,130 @@ class YiiRequirementChecker } /** + * Checks if the given PHP extension is available and its version matches the given one. + * @param string $extensionName PHP extension name. + * @param string $version required PHP extension version. + * @param string $compare comparison operator, by default '>=' + * @return boolean if PHP extension version matches. + */ + function checkPhpExtensionVersion($extensionName, $version, $compare='>=') + { + if (!extension_loaded($extensionName)) { + return false; + } + $extensionVersion = phpversion($extensionName); + if (empty($extensionVersion)) { + return false; + } + return version_compare($extensionVersion, $version, $compare); + } + + /** + * Checks if PHP configuration option (from php.ini) is on. + * @param string $name configuration option name. + * @return boolean option is on. + */ + function checkPhpIniOn($name) + { + $value = ini_get($name); + if (empty($value)) { + return false; + } + return ((integer)$value==1 || strtolower($value) == 'on'); + } + + /** + * Checks if PHP configuration option (from php.ini) is off. + * @param string $name configuration option name. + * @return boolean option is off. + */ + function checkPhpIniOff($name) + { + $value = ini_get($name); + if (empty($value)) { + return true; + } + return (strtolower($value) == 'off'); + } + + /** + * Compare byte sizes of values given in the verbose representation, + * like '5M', '15K' etc. + * @param string $a first value. + * @param string $b second value. + * @param string $compare comparison operator, by default '>='. + * @return boolean comparison result. + */ + function compareByteSize($a, $b, $compare='>=') + { + $compareExpression = '('.$this->getByteSize($a).$compare.$this->getByteSize($b).')'; + return $this->evaluateExpression($compareExpression); + } + + /** + * Gets the size in bytes from verbose size representation. + * For example: '5K' => 5*1024 + * @param string $verboseSize verbose size representation. + * @return integer actual size in bytes. + */ + function getByteSize($verboseSize) + { + if (empty($verboseSize)) { + return 0; + } + if (is_numeric($verboseSize)) { + return (integer)$verboseSize; + } + $sizeUnit = trim($verboseSize, '0123456789'); + $size = str_replace($sizeUnit, '', $verboseSize); + $size = trim($size); + if (!is_numeric($size)) { + return 0; + } + switch (strtolower($sizeUnit)) { + case 'kb': + case 'k': { + return $size*1024; + } + case 'mb': + case 'm': { + return $size*1024*1024; + } + case 'gb': + case 'g': { + return $size*1024*1024*1024; + } + default: { + return 0; + } + } + } + + /** + * Checks if upload max file size matches the given range. + * @param string|null $min verbose file size minimum required value, pass null to skip minimum check. + * @param string|null $max verbose file size maximum required value, pass null to skip maximum check. + * @return boolean success. + */ + function checkUploadMaxFileSize($min=null, $max=null) + { + $postMaxSize = ini_get('post_max_size'); + $uploadMaxFileSize = ini_get('upload_max_filesize'); + if ($min!==null) { + $minCheckResult = $this->compareByteSize($postMaxSize, $min, '>=') && $this->compareByteSize($uploadMaxFileSize, $min, '>='); + } else { + $minCheckResult = true; + } + if ($max!==null) { + var_dump($postMaxSize, $uploadMaxFileSize, $max); + $maxCheckResult = $this->compareByteSize($postMaxSize, $max, '<=') && $this->compareByteSize($uploadMaxFileSize, $max, '<='); + } else { + $maxCheckResult = true; + } + return ($minCheckResult && $maxCheckResult); + } + + /** * Renders a view file. * This method includes the view file as a PHP script * and captures the display result if required. From e06822940146e203deed8e46a713dc6072f0a9f0 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Sat, 11 May 2013 19:49:18 +0300 Subject: [PATCH 18/33] "YiiRequirementChecker::check()" has been updated allowing to accept filename for the requirements. Yii core requirements file has been composed. --- yii/requirements/YiiRequirementChecker.php | 16 ++++++++++++- yii/requirements/yiirequirements.php | 38 ++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 yii/requirements/yiirequirements.php diff --git a/yii/requirements/YiiRequirementChecker.php b/yii/requirements/YiiRequirementChecker.php index b8997bd..b63d1aa 100644 --- a/yii/requirements/YiiRequirementChecker.php +++ b/yii/requirements/YiiRequirementChecker.php @@ -19,11 +19,16 @@ class YiiRequirementChecker * Check the given requirements, collecting results into internal field. * This method can be invoked several times checking different requirement sets. * Use {@link getResult()} or {@link render()} to get the results. - * @param array $requirements requirements to be checked. + * @param array|string $requirements requirements to be checked. + * If an array, it is treated as the set of requirements; + * If a string, it is treated as the path of the file, which contains the requirements; * @return YiiRequirementChecker self instance. */ function check($requirements) { + if (is_string($requirements)) { + $requirements = require($requirements); + } if (!is_array($requirements)) { $this->usageError("Requirements must be an array!"); } @@ -60,6 +65,15 @@ class YiiRequirementChecker } /** + * Performs the check for the Yii core requirements. + * @return YiiRequirementChecker self instance. + */ + public function checkYii() + { + return $this->check(dirname(__FILE__).DIRECTORY_SEPARATOR.'yiirequirements.php'); + } + + /** * Return the check results. * @return array|null check results. */ diff --git a/yii/requirements/yiirequirements.php b/yii/requirements/yiirequirements.php new file mode 100644 index 0000000..2c6f4d8 --- /dev/null +++ b/yii/requirements/yiirequirements.php @@ -0,0 +1,38 @@ + 'PHP version', + 'mandatory' => true, + 'condition' => version_compare(PHP_VERSION, '5.3.0', '>='), + 'by' => 'Yii Framework', + 'memo' => 'PHP 5.3.0 or higher is required.', + ), + array( + 'name' => 'Reflection extension', + 'mandatory' => true, + 'condition' => class_exists('Reflection', false), + 'by' => 'Yii Framework', + ), + array( + 'name' => 'PCRE extension', + 'mandatory' => true, + 'condition' => extension_loaded('pcre'), + 'by' => 'Yii Framework', + ), + array( + 'name' => 'SPL extension', + 'mandatory' => true, + 'condition' => extension_loaded('SPL'), + 'by' => 'Yii Framework', + ), + array( + 'name' => 'MBString extension', + 'mandatory' => true, + 'condition' => extension_loaded('mbstring'), + 'by' => 'Multibyte string processing', + 'memo' => 'Required for multibyte encoding string processing.' + ), +); \ No newline at end of file From 653b4d1f333b1f7d007c3fbcd4a2ba73c8fb4349 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Sat, 11 May 2013 19:50:53 +0300 Subject: [PATCH 19/33] PHP version fallback for "YiiRequirementChecker" has been added. --- yii/requirements/YiiRequirementChecker.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/yii/requirements/YiiRequirementChecker.php b/yii/requirements/YiiRequirementChecker.php index b63d1aa..bae282f 100644 --- a/yii/requirements/YiiRequirementChecker.php +++ b/yii/requirements/YiiRequirementChecker.php @@ -5,6 +5,11 @@ * @license http://www.yiiframework.com/license/ */ +if (version_compare(PHP_VERSION, '4.3', '<')) { + echo 'At least PHP 4.3 is required to run this script!'; + exit(1); +} + /** * YiiRequirementChecker allows checking, if current system meets the requirements for running the application. * From 43a04d237a0ce61b85e18ddfa24b4331f31b2039 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Sat, 11 May 2013 20:04:12 +0300 Subject: [PATCH 20/33] Doc comments for "YiiRequirementChecker" have been updated. --- .../requirements/YiiRequirementCheckerTest.php | 6 +-- yii/requirements/YiiRequirementChecker.php | 56 ++++++++++++++++++++-- yii/requirements/yiirequirements.php | 2 +- 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/tests/unit/framework/requirements/YiiRequirementCheckerTest.php b/tests/unit/framework/requirements/YiiRequirementCheckerTest.php index 691f5c9..484fb1c 100644 --- a/tests/unit/framework/requirements/YiiRequirementCheckerTest.php +++ b/tests/unit/framework/requirements/YiiRequirementCheckerTest.php @@ -5,7 +5,7 @@ require_once(realpath(__DIR__.'/../../../../yii/requirements/YiiRequirementCheck use yiiunit\TestCase; /** - * Test case for {@link YiiRequirementChecker}. + * Test case for [[YiiRequirementChecker]]. * @see YiiRequirementChecker */ class YiiRequirementCheckerTest extends TestCase @@ -134,7 +134,7 @@ class YiiRequirementCheckerTest extends TestCase } /** - * Data provider for {@link testGetByteSize()}. + * Data provider for [[testGetByteSize()]]. * @return array */ public function dataProviderGetByteSize() @@ -164,7 +164,7 @@ class YiiRequirementCheckerTest extends TestCase } /** - * Data provider for {@link testCompareByteSize()} + * Data provider for [[testCompareByteSize()]] * @return array */ public function dataProviderCompareByteSize() diff --git a/yii/requirements/YiiRequirementChecker.php b/yii/requirements/YiiRequirementChecker.php index bae282f..43401c9 100644 --- a/yii/requirements/YiiRequirementChecker.php +++ b/yii/requirements/YiiRequirementChecker.php @@ -11,9 +11,40 @@ if (version_compare(PHP_VERSION, '4.3', '<')) { } /** - * YiiRequirementChecker allows checking, if current system meets the requirements for running the application. + * YiiRequirementChecker allows checking, if current system meets the requirements for running the Yii application. + * This class allows rendering of the check report for the web and console application interface. * - * @property array|null $result the check results. + * Example: + * + * require_once('path/to/YiiRequirementChecker.php'); + * $requirementsChecker = YiiRequirementChecker(); + * $requirements = array( + * array( + * 'name' => 'PHP Some Extension', + * 'mandatory' => true, + * 'condition' => extension_loaded('some_extension'), + * 'by' => 'Some application feature', + * 'memo' => 'PHP extension "some_extension" required', + * ), + * ); + * $requirementsChecker->checkYii()->check($requirements)->render(); + * + * + * If you wish to render the report with your own representation, use [[getResult()]] instead of [[render()]] + * + * Requirement condition could be in format "eval:PHP expression". + * In this case specified PHP expression will be evaluated in the context of this class instance. + * For example: + * + * $requirements = array( + * array( + * 'name' => 'Upload max file size', + * 'condition' => 'eval:$this->checkUploadMaxFileSize("5M")', + * ), + * ); + * + * + * @property array|null $result the check results, this property is for internal usage only. * * @author Paul Klimov * @since 2.0 @@ -23,7 +54,7 @@ class YiiRequirementChecker /** * Check the given requirements, collecting results into internal field. * This method can be invoked several times checking different requirement sets. - * Use {@link getResult()} or {@link render()} to get the results. + * Use [[getResult()]] or [[render()]] to get the results. * @param array|string $requirements requirements to be checked. * If an array, it is treated as the set of requirements; * If a string, it is treated as the path of the file, which contains the requirements; @@ -80,7 +111,24 @@ class YiiRequirementChecker /** * Return the check results. - * @return array|null check results. + * @return array|null check results in format: + * + * array( + * 'summary' => array( + * 'total' => total number of checks, + * 'errors' => number of errors, + * 'warnings' => number of warnings, + * ), + * 'requirements' => array( + * array( + * ... + * 'error' => is there an error, + * 'warning' => is there a warning, + * ), + * ... + * ), + * ) + * */ function getResult() { diff --git a/yii/requirements/yiirequirements.php b/yii/requirements/yiirequirements.php index 2c6f4d8..9d840a9 100644 --- a/yii/requirements/yiirequirements.php +++ b/yii/requirements/yiirequirements.php @@ -1,6 +1,6 @@ Date: Sat, 11 May 2013 20:12:58 +0300 Subject: [PATCH 21/33] Doc comments and error messages for "YiiRequirementChecker" have been adjusted. --- yii/requirements/YiiRequirementChecker.php | 2 +- yii/requirements/yiirequirements.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/yii/requirements/YiiRequirementChecker.php b/yii/requirements/YiiRequirementChecker.php index 43401c9..1912ba7 100644 --- a/yii/requirements/YiiRequirementChecker.php +++ b/yii/requirements/YiiRequirementChecker.php @@ -66,7 +66,7 @@ class YiiRequirementChecker $requirements = require($requirements); } if (!is_array($requirements)) { - $this->usageError("Requirements must be an array!"); + $this->usageError('Requirements must be an array, "'.gettype($requirements).'" has been given!'); } if (!isset($this->result) || !is_array($this->result)) { $this->result = array( diff --git a/yii/requirements/yiirequirements.php b/yii/requirements/yiirequirements.php index 9d840a9..96ae285 100644 --- a/yii/requirements/yiirequirements.php +++ b/yii/requirements/yiirequirements.php @@ -1,6 +1,7 @@ Date: Sat, 11 May 2013 20:45:47 +0300 Subject: [PATCH 22/33] "requirements.php" has been added to "bootstrap" application. --- apps/bootstrap/protected/requirements.php | 98 ++++++++++++++++++++++++++++++ yii/requirements/YiiRequirementChecker.php | 2 +- 2 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 apps/bootstrap/protected/requirements.php diff --git a/apps/bootstrap/protected/requirements.php b/apps/bootstrap/protected/requirements.php new file mode 100644 index 0000000..5e1ac61 --- /dev/null +++ b/apps/bootstrap/protected/requirements.php @@ -0,0 +1,98 @@ + 'PDO extension', + 'mandatory' => true, + 'condition' => extension_loaded('pdo'), + 'by' => 'All DB-related classes', + ), + array( + 'name' => 'PDO SQLite extension', + 'mandatory' => false, + 'condition' => extension_loaded('pdo_sqlite'), + 'by' => 'All DB-related classes', + 'memo' => 'Required for SQLite database.', + ), + array( + 'name' => 'PDO MySQL extension', + 'mandatory' => false, + 'condition' => extension_loaded('pdo_mysql'), + 'by' => 'All DB-related classes', + 'memo' => 'Required for MySQL database.', + ), + // Cache : + array( + 'name' => 'Memcache extension', + 'mandatory' => fasle, + 'condition' => extension_loaded('memcache') || extension_loaded('memcached'), + 'by' => 'CMemCache', + 'memo' => extension_loaded('memcached') ? 'To use memcached set CMemCache::useMemcached to true.' : '' + ), + array( + 'name' => 'APC extension', + 'mandatory' => false, + 'condition' => extension_loaded('apc') || extension_loaded('apc'), + 'by' => 'CApcCache', + ), + // Additional PHP extensions : + array( + 'name' => 'Mcrypt extension', + 'mandatory' => false, + 'condition' => extension_loaded('mcrypt'), + 'by' => 'CSecurityManager', + 'memo' => 'Required by encrypt and decrypt methods.' + ), + // PHP ini : + 'phpSafeMode' => array( + 'name' => 'PHP safe mode', + 'mandatory' => false, + 'condition' => $requirementsChecker->checkPhpIniOff("safe_mode"), + 'by' => 'File uploading and console command execution', + 'memo' => '"safe_mode" should be disabled at php.ini', + ), + 'phpExposePhp' => array( + 'name' => 'Expose PHP', + 'mandatory' => false, + 'condition' => $requirementsChecker->checkPhpIniOff("expose_php"), + 'by' => 'Security reasons', + 'memo' => '"expose_php" should be disabled at php.ini', + ), + 'phpAllowUrlInclude' => array( + 'name' => 'PHP allow url include', + 'mandatory' => false, + 'condition' => $requirementsChecker->checkPhpIniOff("allow_url_include"), + 'by' => 'Security reasons', + 'memo' => '"allow_url_include" should be disabled at php.ini', + ), + 'phpSmtp' => array( + 'name' => 'PHP mail SMTP', + 'mandatory' => false, + 'condition' => strlen(ini_get('SMTP'))>0, + 'by' => 'Email sending', + 'memo' => 'PHP mail SMTP server required', + ), +); +$requirementsChecker->checkYii()->check($requirements)->render(); \ No newline at end of file diff --git a/yii/requirements/YiiRequirementChecker.php b/yii/requirements/YiiRequirementChecker.php index 1912ba7..e590e92 100644 --- a/yii/requirements/YiiRequirementChecker.php +++ b/yii/requirements/YiiRequirementChecker.php @@ -17,7 +17,7 @@ if (version_compare(PHP_VERSION, '4.3', '<')) { * Example: * * require_once('path/to/YiiRequirementChecker.php'); - * $requirementsChecker = YiiRequirementChecker(); + * $requirementsChecker = new YiiRequirementChecker(); * $requirements = array( * array( * 'name' => 'PHP Some Extension', From 3e39b8f0247b8ade55c68bea01718036afdd96b6 Mon Sep 17 00:00:00 2001 From: resurtm Date: Sat, 11 May 2013 23:56:38 +0600 Subject: [PATCH 23/33] requirements.php typo fix. --- apps/bootstrap/protected/requirements.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/bootstrap/protected/requirements.php b/apps/bootstrap/protected/requirements.php index 5e1ac61..8a4f733 100644 --- a/apps/bootstrap/protected/requirements.php +++ b/apps/bootstrap/protected/requirements.php @@ -46,7 +46,7 @@ $requirements = array( // Cache : array( 'name' => 'Memcache extension', - 'mandatory' => fasle, + 'mandatory' => false, 'condition' => extension_loaded('memcache') || extension_loaded('memcached'), 'by' => 'CMemCache', 'memo' => extension_loaded('memcached') ? 'To use memcached set CMemCache::useMemcached to true.' : '' From fb185d14e50e0711ae8cc375d21c90fae9fe4bbe Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Sat, 11 May 2013 23:07:23 +0400 Subject: [PATCH 24/33] Changed framework in dir structure to yii --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 71c990e..54bd499 100644 --- a/readme.md +++ b/readme.md @@ -19,7 +19,7 @@ DIRECTORY STRUCTURE bootstrap/ a simple app supporting user login and contact page build/ internally used build tools docs/ documentation - framework/ framework source files + yii/ framework source files tests/ tests of the core framework code From bdee4112b06bf9633fe0430220e7ec29012089ef Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Sat, 11 May 2013 22:33:00 +0300 Subject: [PATCH 25/33] "YiiRequirementChecker" console view has been reworked to display requirements by blocks and in brief for the successful ones. --- yii/requirements/views/console/index.php | 56 ++++++++++---------------------- 1 file changed, 18 insertions(+), 38 deletions(-) diff --git a/yii/requirements/views/console/index.php b/yii/requirements/views/console/index.php index b6122fc..1b9c61b 100644 --- a/yii/requirements/views/console/index.php +++ b/yii/requirements/views/console/index.php @@ -10,47 +10,27 @@ echo "for running Yii application.\n"; echo "It checks if the server is running the right version of PHP,\n"; echo "if appropriate PHP extensions have been loaded, and if php.ini file settings are correct.\n"; -echo "\nConclusion:\n"; - -$columnSizes = array( - 'name' => 25, - 'condition' => 10, - 'by' => 30, - 'memo' => 50, -); - -// Headers: -$tableLength = count($columnSizes)+1; -foreach ($columnSizes as $columnSize) { - $tableLength += $columnSize; -} -echo str_pad('', $tableLength, '-'); -echo "\n"; -echo '|'.str_pad('Name', $columnSizes['name'], ' ', STR_PAD_BOTH).'|'; -echo str_pad('Result', $columnSizes['condition'], ' ', STR_PAD_BOTH).'|'; -echo str_pad('Required By', $columnSizes['by'], ' ', STR_PAD_BOTH).'|'; -echo str_pad('Memo', $columnSizes['memo'], ' ', STR_PAD_BOTH).'|'; -echo "\n"; -echo str_pad('', $tableLength, '-'); -echo "\n"; - -// Rows: -foreach ($requirements as $requirement) { - $name = $requirement['name']; - echo '|'.str_pad(' '.$name, $columnSizes['name'], ' ', STR_PAD_RIGHT).'|'; - $condition = $requirement['condition'] ? 'Passed' : ($requirement['mandatory'] ? 'FAILED' : 'WARNING'); - echo str_pad($condition, $columnSizes['condition'], ' ', STR_PAD_BOTH).'|'; - $by = strip_tags($requirement['by']); - echo str_pad($by, $columnSizes['by'], ' ', STR_PAD_BOTH).'|'; - $memo = strip_tags($requirement['memo']); - echo str_pad(' '.$memo, $columnSizes['memo'], ' ', STR_PAD_RIGHT).'|'; - echo "\n"; +$header = 'Check conclusion:'; +echo "\n{$header}\n"; +echo str_pad('', strlen($header), '-')."\n\n"; + +foreach ($requirements as $key => $requirement) { + if ($requirement['condition']) { + echo $requirement['name'].": OK\n"; + echo "\n"; + } else { + echo $requirement['name'].': '.($requirement['mandatory'] ? 'FAILED!!!' : 'WARNING!!!')."\n"; + echo 'Required by: '.strip_tags($requirement['by'])."\n"; + $memo = strip_tags($requirement['memo']); + if (!empty($memo)) { + echo 'Memo: '.strip_tags($requirement['memo'])."\n"; + } + echo "\n"; + } } -echo str_pad('', $tableLength, '-'); -echo "\n"; -// Summary $summaryString = 'Errors: '.$summary['errors'].' Warnings: '.$summary['warnings'].' Total checks: '.$summary['total']; +echo str_pad('', strlen($summaryString), '-')."\n"; echo $summaryString; echo "\n\n"; \ No newline at end of file From e951939372b505bc36770e1dc5fb2f5d0b84916c Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 11 May 2013 15:49:15 -0400 Subject: [PATCH 26/33] code style fix of YiiRequirementChecker.php Renamed yiirequirements.php to requirements.php --- apps/bootstrap/protected/requirements.php | 10 +++--- yii/requirements/YiiRequirementChecker.php | 50 ++++++++++++++++-------------- yii/requirements/requirements.php | 39 +++++++++++++++++++++++ yii/requirements/yiirequirements.php | 39 ----------------------- 4 files changed, 69 insertions(+), 69 deletions(-) create mode 100644 yii/requirements/requirements.php delete mode 100644 yii/requirements/yiirequirements.php diff --git a/apps/bootstrap/protected/requirements.php b/apps/bootstrap/protected/requirements.php index 8a4f733..ba1f3ff 100644 --- a/apps/bootstrap/protected/requirements.php +++ b/apps/bootstrap/protected/requirements.php @@ -10,12 +10,10 @@ * ln requirements.php ../requirements.php */ -$appRootPath = dirname(__FILE__); -if (basename($appRootPath) == 'protected') { - $appRootPath = dirname($appRootPath); -} -// you may need to adjust this path: -require_once(realpath($appRootPath.'/../../yii/requirements/YiiRequirementChecker.php')); +// you may need to adjust this path to the correct Yii framework path +$frameworkPath = dirname(__FILE__) . '/../../../yii'; + +require_once($frameworkPath . '/requirements/YiiRequirementChecker.php'); $requirementsChecker = new YiiRequirementChecker(); /** diff --git a/yii/requirements/YiiRequirementChecker.php b/yii/requirements/YiiRequirementChecker.php index e590e92..03413d1 100644 --- a/yii/requirements/YiiRequirementChecker.php +++ b/yii/requirements/YiiRequirementChecker.php @@ -15,7 +15,8 @@ if (version_compare(PHP_VERSION, '4.3', '<')) { * This class allows rendering of the check report for the web and console application interface. * * Example: - * + * + * ~~~ * require_once('path/to/YiiRequirementChecker.php'); * $requirementsChecker = new YiiRequirementChecker(); * $requirements = array( @@ -28,21 +29,22 @@ if (version_compare(PHP_VERSION, '4.3', '<')) { * ), * ); * $requirementsChecker->checkYii()->check($requirements)->render(); - * + * ~~~ * * If you wish to render the report with your own representation, use [[getResult()]] instead of [[render()]] * * Requirement condition could be in format "eval:PHP expression". * In this case specified PHP expression will be evaluated in the context of this class instance. * For example: - * + * + * ~~~ * $requirements = array( * array( * 'name' => 'Upload max file size', * 'condition' => 'eval:$this->checkUploadMaxFileSize("5M")', * ), * ); - * + * ~~~ * * @property array|null $result the check results, this property is for internal usage only. * @@ -66,7 +68,7 @@ class YiiRequirementChecker $requirements = require($requirements); } if (!is_array($requirements)) { - $this->usageError('Requirements must be an array, "'.gettype($requirements).'" has been given!'); + $this->usageError('Requirements must be an array, "' . gettype($requirements) . '" has been given!'); } if (!isset($this->result) || !is_array($this->result)) { $this->result = array( @@ -106,7 +108,7 @@ class YiiRequirementChecker */ public function checkYii() { - return $this->check(dirname(__FILE__).DIRECTORY_SEPARATOR.'yiirequirements.php'); + return $this->check(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'requirements.php'); } /** @@ -148,11 +150,11 @@ class YiiRequirementChecker if (!isset($this->result)) { $this->usageError('Nothing to render!'); } - $baseViewFilePath = dirname(__FILE__).DIRECTORY_SEPARATOR.'views'; + $baseViewFilePath = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'views'; if (array_key_exists('argv', $_SERVER)) { - $viewFileName = $baseViewFilePath.DIRECTORY_SEPARATOR.'console'.DIRECTORY_SEPARATOR.'index.php'; + $viewFileName = $baseViewFilePath . DIRECTORY_SEPARATOR . 'console' . DIRECTORY_SEPARATOR . 'index.php'; } else { - $viewFileName = $baseViewFilePath.DIRECTORY_SEPARATOR.'web'.DIRECTORY_SEPARATOR.'index.php'; + $viewFileName = $baseViewFilePath . DIRECTORY_SEPARATOR . 'web' . DIRECTORY_SEPARATOR . 'index.php'; } $this->renderViewFile($viewFileName, $this->result); } @@ -164,7 +166,7 @@ class YiiRequirementChecker * @param string $compare comparison operator, by default '>=' * @return boolean if PHP extension version matches. */ - function checkPhpExtensionVersion($extensionName, $version, $compare='>=') + function checkPhpExtensionVersion($extensionName, $version, $compare = '>=') { if (!extension_loaded($extensionName)) { return false; @@ -187,7 +189,7 @@ class YiiRequirementChecker if (empty($value)) { return false; } - return ((integer)$value==1 || strtolower($value) == 'on'); + return ((integer)$value == 1 || strtolower($value) == 'on'); } /** @@ -212,9 +214,9 @@ class YiiRequirementChecker * @param string $compare comparison operator, by default '>='. * @return boolean comparison result. */ - function compareByteSize($a, $b, $compare='>=') + function compareByteSize($a, $b, $compare = '>=') { - $compareExpression = '('.$this->getByteSize($a).$compare.$this->getByteSize($b).')'; + $compareExpression = '(' . $this->getByteSize($a) . $compare . $this->getByteSize($b) . ')'; return $this->evaluateExpression($compareExpression); } @@ -241,15 +243,15 @@ class YiiRequirementChecker switch (strtolower($sizeUnit)) { case 'kb': case 'k': { - return $size*1024; + return $size * 1024; } case 'mb': case 'm': { - return $size*1024*1024; + return $size * 1024 * 1024; } case 'gb': case 'g': { - return $size*1024*1024*1024; + return $size * 1024 * 1024 * 1024; } default: { return 0; @@ -263,16 +265,16 @@ class YiiRequirementChecker * @param string|null $max verbose file size maximum required value, pass null to skip maximum check. * @return boolean success. */ - function checkUploadMaxFileSize($min=null, $max=null) + function checkUploadMaxFileSize($min = null, $max = null) { $postMaxSize = ini_get('post_max_size'); $uploadMaxFileSize = ini_get('upload_max_filesize'); - if ($min!==null) { + if ($min !== null) { $minCheckResult = $this->compareByteSize($postMaxSize, $min, '>=') && $this->compareByteSize($uploadMaxFileSize, $min, '>='); } else { $minCheckResult = true; } - if ($max!==null) { + if ($max !== null) { var_dump($postMaxSize, $uploadMaxFileSize, $max); $maxCheckResult = $this->compareByteSize($postMaxSize, $max, '<=') && $this->compareByteSize($uploadMaxFileSize, $max, '<='); } else { @@ -290,7 +292,7 @@ class YiiRequirementChecker * @param boolean $_return_ whether the rendering result should be returned as a string * @return string the rendering result. Null if the rendering result is not required. */ - function renderViewFile($_viewFile_, $_data_=null, $_return_=false) + function renderViewFile($_viewFile_, $_data_ = null, $_return_ = false) { // we use special variable names here to avoid conflict when extracting data if (is_array($_data_)) { @@ -314,7 +316,7 @@ class YiiRequirementChecker * @param int $requirementKey requirement key in the list. * @return array normalized requirement. */ - function normalizeRequirement($requirement, $requirementKey=0) + function normalizeRequirement($requirement, $requirementKey = 0) { if (!is_array($requirement)) { $this->usageError('Requirement must be an array!'); @@ -323,13 +325,13 @@ class YiiRequirementChecker $this->usageError("Requirement '{$requirementKey}' has no condition!"); } else { $evalPrefix = 'eval:'; - if (is_string($requirement['condition']) && strpos($requirement['condition'], $evalPrefix)===0) { + if (is_string($requirement['condition']) && strpos($requirement['condition'], $evalPrefix) === 0) { $expression = substr($requirement['condition'], strlen($evalPrefix)); $requirement['condition'] = $this->evaluateExpression($expression); } } if (!array_key_exists('name', $requirement)) { - $requirement['name'] = is_numeric($requirementKey) ? 'Requirement #'.$requirementKey : $requirementKey; + $requirement['name'] = is_numeric($requirementKey) ? 'Requirement #' . $requirementKey : $requirementKey; } if (!array_key_exists('mandatory', $requirement)) { if (array_key_exists('required', $requirement)) { @@ -365,7 +367,7 @@ class YiiRequirementChecker */ function evaluateExpression($expression) { - return eval('return '.$expression.';'); + return eval('return ' . $expression . ';'); } /** diff --git a/yii/requirements/requirements.php b/yii/requirements/requirements.php new file mode 100644 index 0000000..96ae285 --- /dev/null +++ b/yii/requirements/requirements.php @@ -0,0 +1,39 @@ + 'PHP version', + 'mandatory' => true, + 'condition' => version_compare(PHP_VERSION, '5.3.0', '>='), + 'by' => 'Yii Framework', + 'memo' => 'PHP 5.3.0 or higher is required.', + ), + array( + 'name' => 'Reflection extension', + 'mandatory' => true, + 'condition' => class_exists('Reflection', false), + 'by' => 'Yii Framework', + ), + array( + 'name' => 'PCRE extension', + 'mandatory' => true, + 'condition' => extension_loaded('pcre'), + 'by' => 'Yii Framework', + ), + array( + 'name' => 'SPL extension', + 'mandatory' => true, + 'condition' => extension_loaded('SPL'), + 'by' => 'Yii Framework', + ), + array( + 'name' => 'MBString extension', + 'mandatory' => true, + 'condition' => extension_loaded('mbstring'), + 'by' => 'Multibyte string processing', + 'memo' => 'Required for multibyte encoding string processing.' + ), +); \ No newline at end of file diff --git a/yii/requirements/yiirequirements.php b/yii/requirements/yiirequirements.php deleted file mode 100644 index 96ae285..0000000 --- a/yii/requirements/yiirequirements.php +++ /dev/null @@ -1,39 +0,0 @@ - 'PHP version', - 'mandatory' => true, - 'condition' => version_compare(PHP_VERSION, '5.3.0', '>='), - 'by' => 'Yii Framework', - 'memo' => 'PHP 5.3.0 or higher is required.', - ), - array( - 'name' => 'Reflection extension', - 'mandatory' => true, - 'condition' => class_exists('Reflection', false), - 'by' => 'Yii Framework', - ), - array( - 'name' => 'PCRE extension', - 'mandatory' => true, - 'condition' => extension_loaded('pcre'), - 'by' => 'Yii Framework', - ), - array( - 'name' => 'SPL extension', - 'mandatory' => true, - 'condition' => extension_loaded('SPL'), - 'by' => 'Yii Framework', - ), - array( - 'name' => 'MBString extension', - 'mandatory' => true, - 'condition' => extension_loaded('mbstring'), - 'by' => 'Multibyte string processing', - 'memo' => 'Required for multibyte encoding string processing.' - ), -); \ No newline at end of file From 51f4424332dbacf77e565bf1ba749ec3bbfdc09b Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 11 May 2013 20:59:14 -0400 Subject: [PATCH 27/33] Fixes issue #172: Implemented new usage of widgets. --- apps/bootstrap/protected/views/layouts/main.php | 8 +- apps/bootstrap/protected/views/site/contact.php | 16 ++-- apps/bootstrap/protected/views/site/login.php | 4 +- docs/guide/upgrade-from-v1.md | 24 +++++- yii/base/View.php | 108 ++++-------------------- yii/base/Widget.php | 87 +++++++++++++++++-- yii/web/Pagination.php | 2 +- yii/widgets/Breadcrumbs.php | 12 +-- yii/widgets/Menu.php | 5 +- 9 files changed, 144 insertions(+), 122 deletions(-) diff --git a/apps/bootstrap/protected/views/layouts/main.php b/apps/bootstrap/protected/views/layouts/main.php index a81f983..05f7259 100644 --- a/apps/bootstrap/protected/views/layouts/main.php +++ b/apps/bootstrap/protected/views/layouts/main.php @@ -1,6 +1,8 @@ registerAssetBundle('app'); -widget('yii\debug\Toolbar'); ?> + endPage(); ?> diff --git a/apps/bootstrap/protected/views/site/contact.php b/apps/bootstrap/protected/views/site/contact.php index a632345..4fa0db5 100644 --- a/apps/bootstrap/protected/views/site/contact.php +++ b/apps/bootstrap/protected/views/site/contact.php @@ -23,7 +23,7 @@ $this->params['breadcrumbs'][] = $this->title; If you have business inquiries or other questions, please fill out the following form to contact us. Thank you.

-beginWidget(ActiveForm::className(), array( + array('class' => 'form-horizontal'), 'fieldConfig' => array('inputOptions' => array('class' => 'input-xlarge')), )); ?> @@ -33,14 +33,14 @@ $this->params['breadcrumbs'][] = $this->title; field($model, 'body')->textArea(array('rows' => 6)); ?> field($model, 'verifyCode'); - echo $field->begin(); - echo $field->label(); - $this->widget(Captcha::className()); - echo Html::activeTextInput($model, 'verifyCode', array('class' => 'input-medium')); - echo $field->error(); - echo $field->end(); + echo $field->begin() + . $field->label() + . Captcha::widget($this) + . Html::activeTextInput($model, 'verifyCode', array('class' => 'input-medium')) + . $field->error() + . $field->end(); ?>
'btn btn-primary')); ?>
-endWidget(); ?> + diff --git a/apps/bootstrap/protected/views/site/login.php b/apps/bootstrap/protected/views/site/login.php index 65dc7d1..7d2fed2 100644 --- a/apps/bootstrap/protected/views/site/login.php +++ b/apps/bootstrap/protected/views/site/login.php @@ -14,11 +14,11 @@ $this->params['breadcrumbs'][] = $this->title;

Please fill out the following fields to login:

-beginWidget(ActiveForm::className(), array('options' => array('class' => 'form-horizontal'))); ?> + array('class' => 'form-horizontal'))); ?> field($model, 'username')->textInput(); ?> field($model, 'password')->passwordInput(); ?> field($model, 'rememberMe')->checkbox(); ?>
'btn btn-primary')); ?>
-endWidget(); ?> + diff --git a/docs/guide/upgrade-from-v1.md b/docs/guide/upgrade-from-v1.md index d35e6e0..f02f825 100644 --- a/docs/guide/upgrade-from-v1.md +++ b/docs/guide/upgrade-from-v1.md @@ -209,6 +209,26 @@ if (isset($_POST['Post'])) { ``` +Widgets +------- + +Using a widget is more straightforward in 2.0. You mainly use the `begin()`, `end()` and `widget()` +methods of the `Widget` class. For example, + +```php +// $this refers to the View object +// Note that you have to "echo" the result to display it +echo \yii\widgets\Menu::widget($this, array('items' => $items)); + +// $this refers to the View object +$form = \yii\widgets\ActiveForm::begin($this); +... form inputs here ... +\yii\widgets\ActiveForm::end(); +``` + +Previously in 1.1, you would have to enter the widget class names as strings via the `beginWidget()`, +`endWidget()` and `widget()` methods of `CBaseController`. The approach above gets better IDE support. + Themes ------ @@ -309,13 +329,13 @@ is a container consisting of a label, an input, and an error message. It is repr as an `ActiveField` object. Using fields, you can build a form more cleanly than before: ```php -beginWidget('yii\widgets\ActiveForm'); ?> + field($model, 'username')->textInput(); ?> field($model, 'password')->passwordInput(); ?>
-endWidget(); ?> + ``` diff --git a/yii/base/View.php b/yii/base/View.php index 84a6f11..8482a68 100644 --- a/yii/base/View.php +++ b/yii/base/View.php @@ -11,6 +11,9 @@ use Yii; use yii\base\Application; use yii\helpers\FileHelper; use yii\helpers\Html; +use yii\widgets\Block; +use yii\widgets\ContentDecorator; +use yii\widgets\FragmentCache; /** * View represents a view object in the MVC pattern. @@ -108,12 +111,6 @@ class View extends Component */ public $blocks; /** - * @var Widget[] the widgets that are currently being rendered (not ended). This property - * is maintained by [[beginWidget()]] and [[endWidget()]] methods. Do not modify it directly. - * @internal - */ - public $widgetStack = array(); - /** * @var array a list of currently active fragment cache widgets. This property * is used internally to implement the content caching feature. Do not modify it directly. * @internal @@ -364,91 +361,16 @@ class View extends Component } /** - * Creates a widget. - * This method will use [[Yii::createObject()]] to create the widget. - * @param string $class the widget class name or path alias - * @param array $properties the initial property values of the widget. - * @return Widget the newly created widget instance - */ - public function createWidget($class, $properties = array()) - { - $properties['class'] = $class; - if (!isset($properties['view'])) { - $properties['view'] = $this; - } - return Yii::createObject($properties); - } - - /** - * Creates and runs a widget. - * Compared with [[createWidget()]], this method does one more thing: it will - * run the widget after it is created. - * @param string $class the widget class name or path alias - * @param array $properties the initial property values of the widget. - * @param boolean $captureOutput whether to capture the output of the widget and return it as a string - * @return string|Widget if $captureOutput is true, the output of the widget will be returned; - * otherwise the widget object will be returned. - */ - public function widget($class, $properties = array(), $captureOutput = false) - { - if ($captureOutput) { - ob_start(); - ob_implicit_flush(false); - $widget = $this->createWidget($class, $properties); - $widget->run(); - return ob_get_clean(); - } else { - $widget = $this->createWidget($class, $properties); - $widget->run(); - return $widget; - } - } - - /** - * Begins a widget. - * This method is similar to [[createWidget()]] except that it will expect a matching - * [[endWidget()]] call after this. - * @param string $class the widget class name or path alias - * @param array $properties the initial property values of the widget. - * @return Widget the widget instance - */ - public function beginWidget($class, $properties = array()) - { - $widget = $this->createWidget($class, $properties); - $this->widgetStack[] = $widget; - return $widget; - } - - /** - * Ends a widget. - * Note that the rendering result of the widget is directly echoed out. - * If you want to capture the rendering result of a widget, you may use - * [[createWidget()]] and [[Widget::run()]]. - * @return Widget the widget instance - * @throws InvalidCallException if [[beginWidget()]] and [[endWidget()]] calls are not properly nested - */ - public function endWidget() - { - $widget = array_pop($this->widgetStack); - if ($widget instanceof Widget) { - $widget->run(); - return $widget; - } else { - throw new InvalidCallException("Unmatched beginWidget() and endWidget() calls."); - } - } - - /** * Begins recording a block. - * This method is a shortcut to beginning [[yii\widgets\Block]] + * This method is a shortcut to beginning [[Block]] * @param string $id the block ID. * @param boolean $renderInPlace whether to render the block content in place. * Defaults to false, meaning the captured block will not be displayed. - * @return \yii\widgets\Block the Block widget instance + * @return Block the Block widget instance */ public function beginBlock($id, $renderInPlace = false) { - return $this->beginWidget('yii\widgets\Block', array( + return Block::begin($this, array( 'id' => $id, 'renderInPlace' => $renderInPlace, )); @@ -459,7 +381,7 @@ class View extends Component */ public function endBlock() { - $this->endWidget(); + Block::end(); } /** @@ -476,12 +398,12 @@ class View extends Component * @param string $viewFile the view file that will be used to decorate the content enclosed by this widget. * This can be specified as either the view file path or path alias. * @param array $params the variables (name => value) to be extracted and made available in the decorative view. - * @return \yii\widgets\ContentDecorator the ContentDecorator widget instance - * @see \yii\widgets\ContentDecorator + * @return ContentDecorator the ContentDecorator widget instance + * @see ContentDecorator */ public function beginContent($viewFile, $params = array()) { - return $this->beginWidget('yii\widgets\ContentDecorator', array( + return ContentDecorator::begin($this, array( 'viewFile' => $viewFile, 'params' => $params, )); @@ -492,7 +414,7 @@ class View extends Component */ public function endContent() { - $this->endWidget(); + ContentDecorator::end(); } /** @@ -510,15 +432,15 @@ class View extends Component * ~~~ * * @param string $id a unique ID identifying the fragment to be cached. - * @param array $properties initial property values for [[\yii\widgets\FragmentCache]] + * @param array $properties initial property values for [[FragmentCache]] * @return boolean whether you should generate the content for caching. * False if the cached version is available. */ public function beginCache($id, $properties = array()) { $properties['id'] = $id; - /** @var $cache \yii\widgets\FragmentCache */ - $cache = $this->beginWidget('yii\widgets\FragmentCache', $properties); + /** @var $cache FragmentCache */ + $cache = FragmentCache::begin($this, $properties); if ($cache->getCachedContent() !== false) { $this->endCache(); return false; @@ -532,7 +454,7 @@ class View extends Component */ public function endCache() { - $this->endWidget(); + FragmentCache::end(); } diff --git a/yii/base/Widget.php b/yii/base/Widget.php index c0c524f..9ec690c 100644 --- a/yii/base/Widget.php +++ b/yii/base/Widget.php @@ -8,7 +8,6 @@ namespace yii\base; use Yii; -use yii\helpers\FileHelper; /** * Widget is the base class for widgets. @@ -19,9 +18,9 @@ use yii\helpers\FileHelper; class Widget extends Component { /** - * @var View the view object that is used to create this widget. - * This property is automatically set by [[View::createWidget()]]. - * This property is required by [[render()]] and [[renderFile()]]. + * @var View the view object that this widget is associated with. + * The widget will use this view object to register any needed assets. + * This property is also required by [[render()]] and [[renderFile()]]. */ public $view; /** @@ -29,9 +28,85 @@ class Widget extends Component */ private $_id; /** - * @var integer a counter used to generate IDs for widgets. + * @var integer a counter used to generate [[id]] for widgets. + * @internal */ - private static $_counter = 0; + public static $_counter = 0; + /** + * @var Widget[] the widgets that are currently being rendered (not ended). This property + * is maintained by [[begin()]] and [[end()]] methods. + * @internal + */ + public static $_stack = array(); + + /** + * Constructor. + * @param View $view the view object that this widget is associated with. + * The widget will use this view object to register any needed assets. + * It is also required by [[render()]] and [[renderFile()]]. + * @param array $config name-value pairs that will be used to initialize the object properties + */ + public function __construct($view, $config = array()) + { + $this->view = $view; + parent::__construct($config); + } + + /** + * Begins a widget. + * This method creates an instance of the calling class. It will apply the configuration + * to the created instance. A matching [[end()]] call should be called later. + * @param View $view the view object that the newly created widget is associated with. + * @param array $config name-value pairs that will be used to initialize the object properties + * @return Widget the newly created widget instance + */ + public static function begin($view, $config = array()) + { + $config['class'] = get_called_class(); + /** @var Widget $widget */ + $widget = Yii::createObject($config, $view); + self::$_stack[] = $widget; + return $widget; + } + + /** + * Ends a widget. + * Note that the rendering result of the widget is directly echoed out. + * @return Widget the widget instance that is ended. + * @throws InvalidCallException if [[begin()]] and [[end()]] calls are not properly nested + */ + public static function end() + { + if (!empty(self::$_stack)) { + $widget = array_pop(self::$_stack); + if (get_class($widget) === get_called_class()) { + $widget->run(); + return $widget; + } else { + throw new InvalidCallException("Expecting end() of " . get_class($widget) . ", found " . get_called_class()); + } + } else { + throw new InvalidCallException("Unexpected " . get_called_class() . '::end() call. A matching begin() is not found.'); + } + } + + /** + * Creates a widget instance and runs it. + * The widget rendering result is returned by this method. + * @param View $view the view object that the newly created widget is associated with. + * @param array $config name-value pairs that will be used to initialize the object properties + * @return string the rendering result of the widget. + */ + public static function widget($view, $config = array()) + { + ob_start(); + ob_implicit_flush(false); + /** @var Widget $widget */ + $config['class'] = get_called_class(); + $widget = Yii::createObject($config, $view); + $widget->run(); + return ob_get_clean(); + } /** * Returns the ID of the widget. diff --git a/yii/web/Pagination.php b/yii/web/Pagination.php index 20241ef..73c0adb 100644 --- a/yii/web/Pagination.php +++ b/yii/web/Pagination.php @@ -47,7 +47,7 @@ use Yii; * } * * // display pagination - * $this->widget('yii\widgets\LinkPager', array( + * LinkPager::widget($this, array( * 'pages' => $pages, * )); * ~~~ diff --git a/yii/widgets/Breadcrumbs.php b/yii/widgets/Breadcrumbs.php index 22d09b3..35772e0 100644 --- a/yii/widgets/Breadcrumbs.php +++ b/yii/widgets/Breadcrumbs.php @@ -19,10 +19,11 @@ use yii\helpers\Html; * for the "Sample Post". He can click on "Sample Post" to view that page, or he can click on "Home" * to return to the homepage. * - * To use Breadcrumbs, you need to configure its [[links]] property, which specifiesthe links to be displayed. For example, + * To use Breadcrumbs, you need to configure its [[links]] property, which specifies the links to be displayed. For example, * * ~~~ - * $this->widget('yii\widgets\Breadcrumbs', array( + * // $this is the view object currently being used + * echo Breadcrumbs::widget($this, array( * 'links' => array( * array('label' => 'Sample Post', 'url' => array('post/edit', 'id' => 1)), * 'Edit', @@ -30,12 +31,13 @@ use yii\helpers\Html; * )); * ~~~ * - * Because breadcrumbs usually appears in nearly every page of a website, you may consider place it in a layout view. - * You can then use a view parameter (e.g. `$this->params['breadcrumbs']`) to configure the links in different + * Because breadcrumbs usually appears in nearly every page of a website, you may consider placing it in a layout view. + * You can use a view parameter (e.g. `$this->params['breadcrumbs']`) to configure the links in different * views. In the layout view, you assign this view parameter to the [[links]] property like the following: * * ~~~ - * $this->widget('yii\widgets\Breadcrumbs', array( + * // $this is the view object currently being used + * echo Breadcrumbs::widget($this, array( * 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : array(), * )); * ~~~ diff --git a/yii/widgets/Menu.php b/yii/widgets/Menu.php index b8f69e1..3da0f31 100644 --- a/yii/widgets/Menu.php +++ b/yii/widgets/Menu.php @@ -26,10 +26,11 @@ use yii\helpers\Html; * The following example shows how to use Menu: * * ~~~ - * $this->widget('yii\widgets\Menu', array( + * // $this is the view object currently being used + * echo Menu::widget($this, array( * 'items' => array( * // Important: you need to specify url as 'controller/action', - * // not just as 'controller' even if default acion is used. + * // not just as 'controller' even if default action is used. * array('label' => 'Home', 'url' => array('site/index')), * // 'Products' menu item will be selected as long as the route is 'product/index' * array('label' => 'Products', 'url' => array('product/index'), 'items' => array( From 4fa377d890e762cdb8d7fa985a7e0bcd99bfda95 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 11 May 2013 21:09:36 -0400 Subject: [PATCH 28/33] Added default class for theme. --- yii/base/View.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/yii/base/View.php b/yii/base/View.php index 8482a68..2a6f71f 100644 --- a/yii/base/View.php +++ b/yii/base/View.php @@ -171,6 +171,9 @@ class View extends Component { parent::init(); if (is_array($this->theme)) { + if (!isset($this->theme['class'])) { + $this->theme['class'] = 'yii\base\Theme'; + } $this->theme = Yii::createObject($this->theme); } } From 667b808f7d264f0441d85808393e86b9803b81f5 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 11 May 2013 21:17:04 -0400 Subject: [PATCH 29/33] Added usage example of Theme. --- yii/base/Theme.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/yii/base/Theme.php b/yii/base/Theme.php index a60d56e..ca1efcd 100644 --- a/yii/base/Theme.php +++ b/yii/base/Theme.php @@ -25,7 +25,22 @@ use yii\helpers\FileHelper; * then the themed version for a view file `/www/views/site/index.php` will be * `/www/themes/basic/site/index.php`. * - * @property string $baseUrl the base URL for this theme. This is mainly used by [[getUrl()]]. + * To use a theme, you should configure the [[View::theme|theme]] property of the "view" application + * component like the following: + * + * ~~~ + * 'view' => array( + * 'theme' => array( + * 'basePath' => '@wwwroot/themes/basic', + * 'baseUrl' => '@www/themes/basic', + * ), + * ), + * ~~~ + * + * The above configuration specifies a theme located under the "themes/basic" directory of the Web folder + * that contains the entry script of the application. If your theme is designed to handle modules, + * you may configure the [[pathMap]] property like described above. + * * * @author Qiang Xue * @since 2.0 From 491e66083a08c3715ad4f8461b80dccac2951a08 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 11 May 2013 23:35:16 -0400 Subject: [PATCH 30/33] refactored RBAC. --- yii/rbac/Assignment.php | 83 ++++---------------------- yii/rbac/DbManager.php | 92 +++++++++++++++++++++-------- yii/rbac/Item.php | 144 ++++++++++----------------------------------- yii/rbac/Manager.php | 2 +- yii/rbac/PhpManager.php | 60 +++++++++++++------ yii/rbac/schema-mssql.sql | 7 ++- yii/rbac/schema-mysql.sql | 7 ++- yii/rbac/schema-oci.sql | 7 ++- yii/rbac/schema-pgsql.sql | 7 ++- yii/rbac/schema-sqlite.sql | 7 ++- 10 files changed, 177 insertions(+), 239 deletions(-) diff --git a/yii/rbac/Assignment.php b/yii/rbac/Assignment.php index b3aa74f..065aaa5 100644 --- a/yii/rbac/Assignment.php +++ b/yii/rbac/Assignment.php @@ -16,97 +16,40 @@ use yii\base\Object; * Do not create a Assignment instance using the 'new' operator. * Instead, call [[Manager::assign()]]. * - * @property mixed $userId User ID (see [[User::id]]). - * @property string $itemName The authorization item name. - * @property string $bizRule The business rule associated with this assignment. - * @property mixed $data Additional data for this assignment. - * * @author Qiang Xue * @author Alexander Kochetov * @since 2.0 */ class Assignment extends Object { - private $_auth; - private $_userId; - private $_itemName; - private $_bizRule; - private $_data; - - /** - * Constructor. - * @param Manager $auth the authorization manager - * @param mixed $userId user ID (see [[User::id]]) - * @param string $itemName authorization item name - * @param string $bizRule the business rule associated with this assignment - * @param mixed $data additional data for this assignment - */ - public function __construct($auth, $userId, $itemName, $bizRule = null, $data = null) - { - $this->_auth = $auth; - $this->_userId = $userId; - $this->_itemName = $itemName; - $this->_bizRule = $bizRule; - $this->_data = $data; - } - /** - * @return mixed user ID (see [[User::id]]) + * @var Manager the auth manager of this item */ - public function getUserId() - { - return $this->_userId; - } - - /** - * @return string the authorization item name - */ - public function getItemName() - { - return $this->_itemName; - } - + public $manager; /** - * @return string the business rule associated with this assignment + * @var string the business rule associated with this assignment */ - public function getBizRule() - { - return $this->_bizRule; - } - + public $bizRule; /** - * @param string $value the business rule associated with this assignment + * @var mixed additional data for this assignment */ - public function setBizRule($value) - { - if ($this->_bizRule !== $value) { - $this->_bizRule = $value; - } - } - + public $data; /** - * @return mixed additional data for this assignment + * @var mixed user ID (see [[User::id]]). Do not modify this property after it is populated. + * To modify the user ID of an assignment, you must remove the assignment and create a new one. */ - public function getData() - { - return $this->_data; - } - + public $userId; /** - * @param mixed $value additional data for this assignment + * @return string the authorization item name. Do not modify this property after it is populated. + * To modify the item name of an assignment, you must remove the assignment and create a new one. */ - public function setData($value) - { - if ($this->_data !== $value) { - $this->_data = $value; - } - } + public $itemName; /** * Saves the changes to an authorization assignment. */ public function save() { - $this->_auth->saveAssignment($this); + $this->manager->saveAssignment($this); } } diff --git a/yii/rbac/DbManager.php b/yii/rbac/DbManager.php index 28b14d9..719ffa8 100644 --- a/yii/rbac/DbManager.php +++ b/yii/rbac/DbManager.php @@ -24,8 +24,6 @@ use yii\base\InvalidParamException; * the three tables used to store the authorization data by setting [[itemTable]], * [[itemChildTable]] and [[assignmentTable]]. * - * @property array $authItems The authorization items of the specific type. - * * @author Qiang Xue * @author Alexander Kochetov * @since 2.0 @@ -106,13 +104,13 @@ class DbManager extends Manager if (!isset($params['userId'])) { $params['userId'] = $userId; } - if ($this->executeBizRule($item->getBizRule(), $params, $item->getData())) { + if ($this->executeBizRule($item->bizRule, $params, $item->data)) { if (in_array($itemName, $this->defaultRoles)) { return true; } if (isset($assignments[$itemName])) { $assignment = $assignments[$itemName]; - if ($this->executeBizRule($assignment->getBizRule(), $params, $assignment->getData())) { + if ($this->executeBizRule($assignment->bizRule, $params, $assignment->data)) { return true; } } @@ -207,7 +205,7 @@ class DbManager extends Manager public function getItemChildren($names) { $query = new Query; - $rows = $query->select(array('name', 'type', 'description', 'bizrule', 'data')) + $rows = $query->select(array('name', 'type', 'description', 'biz_rule', 'data')) ->from(array($this->itemTable, $this->itemChildTable)) ->where(array('parent' => $names, 'name' => new Expression('child'))) ->createCommand($this->db) @@ -217,7 +215,14 @@ class DbManager extends Manager if (($data = @unserialize($row['data'])) === false) { $data = null; } - $children[$row['name']] = new Item($this, $row['name'], $row['type'], $row['description'], $row['bizrule'], $data); + $children[$row['name']] = new Item(array( + 'manager' => $this, + 'name' => $row['name'], + 'type' => $row['type'], + 'description' => $row['description'], + 'bizRule' => $row['biz_rule'], + 'data' => $data, + )); } return $children; } @@ -241,10 +246,16 @@ class DbManager extends Manager ->insert($this->assignmentTable, array( 'user_id' => $userId, 'item_name' => $itemName, - 'bizrule' => $bizRule, + 'biz_rule' => $bizRule, 'data' => serialize($data), )); - return new Assignment($this, $userId, $itemName, $bizRule, $data); + return new Assignment(array( + 'manager' => $this, + 'userId' => $userId, + 'itemName' => $itemName, + 'bizRule' => $bizRule, + 'data' => $data, + )); } /** @@ -293,7 +304,13 @@ class DbManager extends Manager if (($data = @unserialize($row['data'])) === false) { $data = null; } - return new Assignment($this, $row['user_id'], $row['item_name'], $row['bizrule'], $data); + return new Assignment(array( + 'manager' => $this, + 'userId' => $row['user_id'], + 'itemName' => $row['item_name'], + 'bizRule' => $row['biz_rule'], + 'data' => $data, + )); } else { return null; } @@ -317,7 +334,13 @@ class DbManager extends Manager if (($data = @unserialize($row['data'])) === false) { $data = null; } - $assignments[$row['item_name']] = new Assignment($this, $row['user_id'], $row['item_name'], $row['bizrule'], $data); + $assignments[$row['item_name']] = new Assignment(array( + 'manager' => $this, + 'userId' => $row['user_id'], + 'itemName' => $row['item_name'], + 'bizRule' => $row['biz_rule'], + 'data' => $data, + )); } return $assignments; } @@ -330,11 +353,11 @@ class DbManager extends Manager { $this->db->createCommand() ->update($this->assignmentTable, array( - 'bizrule' => $assignment->getBizRule(), - 'data' => serialize($assignment->getData()), + 'biz_rule' => $assignment->bizRule, + 'data' => serialize($assignment->data), ), array( - 'user_id' => $assignment->getUserId(), - 'item_name' => $assignment->getItemName(), + 'user_id' => $assignment->userId, + 'item_name' => $assignment->itemName, )); } @@ -357,12 +380,12 @@ class DbManager extends Manager ->where(array('type' => $type)) ->createCommand($this->db); } elseif ($type === null) { - $command = $query->select(array('name', 'type', 'description', 't1.bizrule', 't1.data')) + $command = $query->select(array('name', 'type', 'description', 't1.biz_rule', 't1.data')) ->from(array($this->itemTable . ' t1', $this->assignmentTable . ' t2')) ->where(array('user_id' => $userId, 'name' => new Expression('item_name'))) ->createCommand($this->db); } else { - $command = $query->select('name', 'type', 'description', 't1.bizrule', 't1.data') + $command = $query->select('name', 'type', 'description', 't1.biz_rule', 't1.data') ->from(array($this->itemTable . ' t1', $this->assignmentTable . ' t2')) ->where(array('user_id' => $userId, 'type' => $type, 'name' => new Expression('item_name'))) ->createCommand($this->db); @@ -372,7 +395,14 @@ class DbManager extends Manager if (($data = @unserialize($row['data'])) === false) { $data = null; } - $items[$row['name']] = new Item($this, $row['name'], $row['type'], $row['description'], $row['bizrule'], $data); + $items[$row['name']] = new Item(array( + 'manager' => $this, + 'name' => $row['name'], + 'type' => $row['type'], + 'description' => $row['description'], + 'bizRule' => $row['biz_rule'], + 'data' => $data, + )); } return $items; } @@ -399,10 +429,17 @@ class DbManager extends Manager 'name' => $name, 'type' => $type, 'description' => $description, - 'bizrule' => $bizRule, + 'biz_rule' => $bizRule, 'data' => serialize($data), )); - return new Item($this, $name, $type, $description, $bizRule, $data); + return new Item(array( + 'manager' => $this, + 'name' => $name, + 'type' => $type, + 'description' => $description, + 'bizRule' => $bizRule, + 'data' => $data, + )); } /** @@ -439,7 +476,14 @@ class DbManager extends Manager if (($data = @unserialize($row['data'])) === false) { $data = null; } - return new Item($this, $row['name'], $row['type'], $row['description'], $row['bizrule'], $data); + return new Item(array( + 'manager' => $this, + 'name' => $row['name'], + 'type' => $row['type'], + 'description' => $row['description'], + 'bizRule' => $row['biz_rule'], + 'data' => $data, + )); } else return null; } @@ -463,10 +507,10 @@ class DbManager extends Manager $this->db->createCommand() ->update($this->itemTable, array( 'name' => $item->getName(), - 'type' => $item->getType(), - 'description' => $item->getDescription(), - 'bizrule' => $item->getBizRule(), - 'data' => serialize($item->getData()), + 'type' => $item->type, + 'description' => $item->description, + 'biz_rule' => $item->bizRule, + 'data' => serialize($item->data), ), array( 'name' => $oldName === null ? $item->getName() : $oldName, )); diff --git a/yii/rbac/Item.php b/yii/rbac/Item.php index 57a7f7e..2504ad5 100644 --- a/yii/rbac/Item.php +++ b/yii/rbac/Item.php @@ -18,14 +18,6 @@ use yii\base\Object; * A user may be assigned one or several authorization items (called [[Assignment]] assignments). * He can perform an operation only when it is among his assigned items. * - * @property Manager $authManager The authorization manager. - * @property integer $type The authorization item type. This could be 0 (operation), 1 (task) or 2 (role). - * @property string $name The item name. - * @property string $description The item description. - * @property string $bizRule The business rule associated with this item. - * @property mixed $data The additional data associated with this item. - * @property array $children All child items of this item. - * * @author Qiang Xue * @author Alexander Kochetov * @since 2.0 @@ -36,32 +28,30 @@ class Item extends Object const TYPE_TASK = 1; const TYPE_ROLE = 2; - private $_auth; - private $_type; + /** + * @var Manager the auth manager of this item + */ + public $manager; + /** + * @var string the item description + */ + public $description; + /** + * @var string the business rule associated with this item + */ + public $bizRule; + /** + * @var mixed the additional data associated with this item + */ + public $data; + /** + * @var integer the authorization item type. This could be 0 (operation), 1 (task) or 2 (role). + */ + public $type; + private $_name; private $_oldName; - private $_description; - private $_bizRule; - private $_data; - /** - * Constructor. - * @param Manager $auth authorization manager - * @param string $name authorization item name - * @param integer $type authorization item type. This can be 0 (operation), 1 (task) or 2 (role). - * @param string $description the description - * @param string $bizRule the business rule associated with this item - * @param mixed $data additional data for this item - */ - public function __construct($auth, $name, $type, $description = '', $bizRule = null, $data = null) - { - $this->_type = (int)$type; - $this->_auth = $auth; - $this->_name = $name; - $this->_description = $description; - $this->_bizRule = $bizRule; - $this->_data = $data; - } /** * Checks to see if the specified item is within the hierarchy starting from this item. @@ -74,11 +64,11 @@ class Item extends Object public function checkAccess($itemName, $params = array()) { Yii::trace('Checking permission: ' . $this->_name, __METHOD__); - if ($this->_auth->executeBizRule($this->_bizRule, $params, $this->_data)) { + if ($this->manager->executeBizRule($this->bizRule, $params, $this->data)) { if ($this->_name == $itemName) { return true; } - foreach ($this->_auth->getItemChildren($this->_name) as $item) { + foreach ($this->manager->getItemChildren($this->_name) as $item) { if ($item->checkAccess($itemName, $params)) { return true; } @@ -88,22 +78,6 @@ class Item extends Object } /** - * @return Manager the authorization manager - */ - public function getManager() - { - return $this->_auth; - } - - /** - * @return integer the authorization item type. This could be 0 (operation), 1 (task) or 2 (role). - */ - public function getType() - { - return $this->_type; - } - - /** * @return string the item name */ public function getName() @@ -123,60 +97,6 @@ class Item extends Object } /** - * @return string the item description - */ - public function getDescription() - { - return $this->_description; - } - - /** - * @param string $value the item description - */ - public function setDescription($value) - { - if ($this->_description !== $value) { - $this->_description = $value; - } - } - - /** - * @return string the business rule associated with this item - */ - public function getBizRule() - { - return $this->_bizRule; - } - - /** - * @param string $value the business rule associated with this item - */ - public function setBizRule($value) - { - if ($this->_bizRule !== $value) { - $this->_bizRule = $value; - } - } - - /** - * @return mixed the additional data associated with this item - */ - public function getData() - { - return $this->_data; - } - - /** - * @param mixed $value the additional data associated with this item - */ - public function setData($value) - { - if ($this->_data !== $value) { - $this->_data = $value; - } - } - - /** * Adds a child item. * @param string $name the name of the child item * @return boolean whether the item is added successfully @@ -185,7 +105,7 @@ class Item extends Object */ public function addChild($name) { - return $this->_auth->addItemChild($this->_name, $name); + return $this->manager->addItemChild($this->_name, $name); } /** @@ -197,7 +117,7 @@ class Item extends Object */ public function removeChild($name) { - return $this->_auth->removeItemChild($this->_name, $name); + return $this->manager->removeItemChild($this->_name, $name); } /** @@ -208,7 +128,7 @@ class Item extends Object */ public function hasChild($name) { - return $this->_auth->hasItemChild($this->_name, $name); + return $this->manager->hasItemChild($this->_name, $name); } /** @@ -218,7 +138,7 @@ class Item extends Object */ public function getChildren() { - return $this->_auth->getItemChildren($this->_name); + return $this->manager->getItemChildren($this->_name); } /** @@ -233,7 +153,7 @@ class Item extends Object */ public function assign($userId, $bizRule = null, $data = null) { - return $this->_auth->assign($userId, $this->_name, $bizRule, $data); + return $this->manager->assign($userId, $this->_name, $bizRule, $data); } /** @@ -244,7 +164,7 @@ class Item extends Object */ public function revoke($userId) { - return $this->_auth->revoke($userId, $this->_name); + return $this->manager->revoke($userId, $this->_name); } /** @@ -255,7 +175,7 @@ class Item extends Object */ public function isAssigned($userId) { - return $this->_auth->isAssigned($userId, $this->_name); + return $this->manager->isAssigned($userId, $this->_name); } /** @@ -267,7 +187,7 @@ class Item extends Object */ public function getAssignment($userId) { - return $this->_auth->getAssignment($userId, $this->_name); + return $this->manager->getAssignment($userId, $this->_name); } /** @@ -275,7 +195,7 @@ class Item extends Object */ public function save() { - $this->_auth->saveItem($this, $this->_oldName); + $this->manager->saveItem($this, $this->_oldName); unset($this->_oldName); } } diff --git a/yii/rbac/Manager.php b/yii/rbac/Manager.php index adc9013..23ac1a8 100644 --- a/yii/rbac/Manager.php +++ b/yii/rbac/Manager.php @@ -161,7 +161,7 @@ abstract class Manager extends Component { static $types = array('operation', 'task', 'role'); if ($parentType < $childType) { - throw new InvalidParamException("Cannot add an item of type '$types[$childType]' to an item of type '$types[$parentType]'."); + throw new InvalidParamException("Cannot add an item of type '{$types[$childType]}' to an item of type '{$types[$parentType]}'."); } } diff --git a/yii/rbac/PhpManager.php b/yii/rbac/PhpManager.php index 8a4dbec..7a476e0 100644 --- a/yii/rbac/PhpManager.php +++ b/yii/rbac/PhpManager.php @@ -80,14 +80,14 @@ class PhpManager extends Manager if (!isset($params['userId'])) { $params['userId'] = $userId; } - if ($this->executeBizRule($item->getBizRule(), $params, $item->getData())) { + if ($this->executeBizRule($item->bizRule, $params, $item->data)) { if (in_array($itemName, $this->defaultRoles)) { return true; } if (isset($this->_assignments[$userId][$itemName])) { /** @var $assignment Assignment */ $assignment = $this->_assignments[$userId][$itemName]; - if ($this->executeBizRule($assignment->getBizRule(), $params, $assignment->getData())) { + if ($this->executeBizRule($assignment->bizRule, $params, $assignment->data)) { return true; } } @@ -117,7 +117,7 @@ class PhpManager extends Manager $child = $this->_items[$childName]; /** @var $item Item */ $item = $this->_items[$itemName]; - $this->checkItemChildType($item->getType(), $child->getType()); + $this->checkItemChildType($item->type, $child->type); if ($this->detectLoop($itemName, $childName)) { throw new InvalidCallException("Cannot add '$childName' as a child of '$itemName'. A loop has been detected."); } @@ -194,7 +194,13 @@ class PhpManager extends Manager } elseif (isset($this->_assignments[$userId][$itemName])) { throw new InvalidParamException("Authorization item '$itemName' has already been assigned to user '$userId'."); } else { - return $this->_assignments[$userId][$itemName] = new Assignment($this, $userId, $itemName, $bizRule, $data); + return $this->_assignments[$userId][$itemName] = new Assignment(array( + 'manager' => $this, + 'userId' => $userId, + 'itemName' => $itemName, + 'bizRule' => $bizRule, + 'data' => $data, + )); } } @@ -265,15 +271,15 @@ class PhpManager extends Manager if ($userId === null) { foreach ($this->_items as $name => $item) { /** @var $item Item */ - if ($item->getType() == $type) { + if ($item->type == $type) { $items[$name] = $item; } } } elseif (isset($this->_assignments[$userId])) { foreach ($this->_assignments[$userId] as $assignment) { /** @var $assignment Assignment */ - $name = $assignment->getItemName(); - if (isset($this->_items[$name]) && ($type === null || $this->_items[$name]->getType() == $type)) { + $name = $assignment->itemName; + if (isset($this->_items[$name]) && ($type === null || $this->_items[$name]->type == $type)) { $items[$name] = $this->_items[$name]; } } @@ -301,7 +307,14 @@ class PhpManager extends Manager if (isset($this->_items[$name])) { throw new Exception('Unable to add an item whose name is the same as an existing item.'); } - return $this->_items[$name] = new Item($this, $name, $type, $description, $bizRule, $data); + return $this->_items[$name] = new Item(array( + 'manager' => $this, + 'name' => $name, + 'type' => $type, + 'description' => $description, + 'bizRule' => $bizRule, + 'data' => $data, + )); } /** @@ -390,10 +403,10 @@ class PhpManager extends Manager foreach ($this->_items as $name => $item) { /** @var $item Item */ $items[$name] = array( - 'type' => $item->getType(), - 'description' => $item->getDescription(), - 'bizRule' => $item->getBizRule(), - 'data' => $item->getData(), + 'type' => $item->type, + 'description' => $item->description, + 'bizRule' => $item->bizRule, + 'data' => $item->data, ); if (isset($this->_children[$name])) { foreach ($this->_children[$name] as $child) { @@ -408,8 +421,8 @@ class PhpManager extends Manager /** @var $assignment Assignment */ if (isset($items[$name])) { $items[$name]['assignments'][$userId] = array( - 'bizRule' => $assignment->getBizRule(), - 'data' => $assignment->getData(), + 'bizRule' => $assignment->bizRule, + 'data' => $assignment->data, ); } } @@ -428,7 +441,14 @@ class PhpManager extends Manager $items = $this->loadFromFile($this->authFile); foreach ($items as $name => $item) { - $this->_items[$name] = new Item($this, $name, $item['type'], $item['description'], $item['bizRule'], $item['data']); + $this->_items[$name] = new Item(array( + 'manager' => $this, + 'name' => $name, + 'type' => $item['type'], + 'description' => $item['description'], + 'bizRule' => $item['bizRule'], + 'data' => $item['data'], + )); } foreach ($items as $name => $item) { @@ -441,8 +461,14 @@ class PhpManager extends Manager } if (isset($item['assignments'])) { foreach ($item['assignments'] as $userId => $assignment) { - $this->_assignments[$userId][$name] = new Assignment($this, $name, $userId, $assignment['bizRule'], $assignment['data']); - } + $this->_assignments[$userId][$name] = new Assignment(array( + 'manager' => $this, + 'userId' => $userId, + 'itemName' => $name, + 'bizRule' => $assignment['bizRule'], + 'data' => $assignment['data'], + )); + } } } } diff --git a/yii/rbac/schema-mssql.sql b/yii/rbac/schema-mssql.sql index a1170b1..923417a 100644 --- a/yii/rbac/schema-mssql.sql +++ b/yii/rbac/schema-mssql.sql @@ -18,9 +18,10 @@ create table [tbl_auth_item] [name] varchar(64) not null, [type] integer not null, [description] text, - [bizrule] text, + [biz_rule] text, [data] text, - primary key ([name]) + primary key ([name]), + key [type] ([type]) ); create table [tbl_auth_item_child] @@ -36,7 +37,7 @@ create table [tbl_auth_assignment] ( [item_name] varchar(64) not null, [user_id] varchar(64) not null, - [bizrule] text, + [biz_rule] text, [data] text, primary key ([item_name],[user_id]), foreign key ([item_name]) references [tbl_auth_item] ([name]) on delete cascade on update cascade diff --git a/yii/rbac/schema-mysql.sql b/yii/rbac/schema-mysql.sql index aa8015b..1530409 100644 --- a/yii/rbac/schema-mysql.sql +++ b/yii/rbac/schema-mysql.sql @@ -18,9 +18,10 @@ create table `tbl_auth_item` `name` varchar(64) not null, `type` integer not null, `description` text, - `bizrule` text, + `biz_rule` text, `data` text, - primary key (`name`) + primary key (`name`), + key `type` (`type`) ) engine InnoDB; create table `tbl_auth_item_child` @@ -36,7 +37,7 @@ create table `tbl_auth_assignment` ( `item_name` varchar(64) not null, `user_id` varchar(64) not null, - `bizrule` text, + `biz_rule` text, `data` text, primary key (`item_name`,`user_id`), foreign key (`item_name`) references `tbl_auth_item` (`name`) on delete cascade on update cascade diff --git a/yii/rbac/schema-oci.sql b/yii/rbac/schema-oci.sql index 16b7964..78dbb3d 100644 --- a/yii/rbac/schema-oci.sql +++ b/yii/rbac/schema-oci.sql @@ -18,9 +18,10 @@ create table "tbl_auth_item" "name" varchar(64) not null, "type" integer not null, "description" text, - "bizrule" text, + "biz_rule" text, "data" text, - primary key ("name") + primary key ("name"), + key "type" ("type") ); create table "tbl_auth_item_child" @@ -36,7 +37,7 @@ create table "tbl_auth_assignment" ( "item_name" varchar(64) not null, "user_id" varchar(64) not null, - "bizrule" text, + "biz_rule" text, "data" text, primary key ("item_name","user_id"), foreign key ("item_name") references "tbl_auth_item" ("name") on delete cascade on update cascade diff --git a/yii/rbac/schema-pgsql.sql b/yii/rbac/schema-pgsql.sql index 16b7964..78dbb3d 100644 --- a/yii/rbac/schema-pgsql.sql +++ b/yii/rbac/schema-pgsql.sql @@ -18,9 +18,10 @@ create table "tbl_auth_item" "name" varchar(64) not null, "type" integer not null, "description" text, - "bizrule" text, + "biz_rule" text, "data" text, - primary key ("name") + primary key ("name"), + key "type" ("type") ); create table "tbl_auth_item_child" @@ -36,7 +37,7 @@ create table "tbl_auth_assignment" ( "item_name" varchar(64) not null, "user_id" varchar(64) not null, - "bizrule" text, + "biz_rule" text, "data" text, primary key ("item_name","user_id"), foreign key ("item_name") references "tbl_auth_item" ("name") on delete cascade on update cascade diff --git a/yii/rbac/schema-sqlite.sql b/yii/rbac/schema-sqlite.sql index 668196e..2b79bbf 100644 --- a/yii/rbac/schema-sqlite.sql +++ b/yii/rbac/schema-sqlite.sql @@ -18,9 +18,10 @@ create table 'tbl_auth_item' "name" varchar(64) not null, "type" integer not null, "description" text, - "bizrule" text, + "biz_rule" text, "data" text, - primary key ("name") + primary key ("name"), + key "type" ("type") ); create table 'tbl_auth_item_child' @@ -36,7 +37,7 @@ create table 'tbl_auth_assignment' ( "item_name" varchar(64) not null, "user_id" varchar(64) not null, - "bizrule" text, + "biz_rule" text, "data" text, primary key ("item_name","user_id"), foreign key ("item_name") references 'tbl_auth_item' ("name") on delete cascade on update cascade From 6162d4b3416c4264865e88f6325397825216fa2a Mon Sep 17 00:00:00 2001 From: resurtm Date: Sun, 12 May 2013 13:52:45 +0600 Subject: [PATCH 31/33] Build command path fix. --- build/build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/build b/build/build index 95b51e4..f8b21a6 100755 --- a/build/build +++ b/build/build @@ -11,7 +11,7 @@ // fcgi doesn't have STDIN defined by default defined('STDIN') or define('STDIN', fopen('php://stdin', 'r')); -require(__DIR__ . '/../framework/Yii.php'); +require(__DIR__ . '/../yii/Yii.php'); $id = 'yiic-build'; $basePath = __DIR__; From a7f2e374d212ccb069183b2e8c9c2fe02360d9eb Mon Sep 17 00:00:00 2001 From: resurtm Date: Sun, 12 May 2013 15:11:50 +0600 Subject: [PATCH 32/33] =?UTF-8?q?MS=20Windows=20EOLs=20=E2=86=92=20UNIX=20?= =?UTF-8?q?EOLs.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/bootstrap/protected/.htaccess | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/bootstrap/protected/.htaccess b/apps/bootstrap/protected/.htaccess index e019832..8d2f256 100644 --- a/apps/bootstrap/protected/.htaccess +++ b/apps/bootstrap/protected/.htaccess @@ -1 +1 @@ -deny from all +deny from all From a021fbe54d352184ddd2287f82a7c83bf0b12532 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Sun, 12 May 2013 18:23:42 +0400 Subject: [PATCH 33/33] Skipped ApcCacheTest expire --- tests/unit/framework/caching/ApcCacheTest.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/unit/framework/caching/ApcCacheTest.php b/tests/unit/framework/caching/ApcCacheTest.php index 20a1cc8..c059554 100644 --- a/tests/unit/framework/caching/ApcCacheTest.php +++ b/tests/unit/framework/caching/ApcCacheTest.php @@ -31,7 +31,8 @@ class ApcCacheTest extends CacheTest return $this->_cacheInstance; } - // TODO there seems to be a problem with APC returning cached value even if it is expired. - // TODO makes test fail on PHP 5.3.10-1ubuntu3.6 with Suhosin-Patch (cli) -- cebe - // TODO http://drupal.org/node/1278292 + public function testExpire() + { + $this->markTestSkipped("APC keys are expiring only on the next request."); + } }