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 diff --git a/apps/bootstrap/protected/requirements.php b/apps/bootstrap/protected/requirements.php new file mode 100644 index 0000000..ba1f3ff --- /dev/null +++ b/apps/bootstrap/protected/requirements.php @@ -0,0 +1,96 @@ + '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' => false, + '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/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/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__; 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/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 diff --git a/tests/unit/framework/caching/ApcCacheTest.php b/tests/unit/framework/caching/ApcCacheTest.php index 6018ce7..c059554 100644 --- a/tests/unit/framework/caching/ApcCacheTest.php +++ b/tests/unit/framework/caching/ApcCacheTest.php @@ -21,9 +21,18 @@ 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; } + + public function testExpire() + { + $this->markTestSkipped("APC keys are expiring only on the next request."); + } } diff --git a/tests/unit/framework/caching/CacheTest.php b/tests/unit/framework/caching/CacheTest.php index a17f126..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 + */ + 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 +88,83 @@ 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')); + // wait a bit more than 2 sec to avoid random test failure + usleep(2500000); + $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/DbCacheTest.php b/tests/unit/framework/caching/DbCacheTest.php index 36174b9..f5bbba5 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(); + + static::$time = \time(); + $this->assertTrue($cache->set('expire_test', 'expire_test', 2)); + static::$time++; + $this->assertEquals('expire_test', $cache->get('expire_test')); + 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 99e3cbc..b3ac8b7 100644 --- a/tests/unit/framework/caching/FileCacheTest.php +++ b/tests/unit/framework/caching/FileCacheTest.php @@ -22,4 +22,16 @@ class FileCacheTest extends CacheTest } return $this->_cacheInstance; } + + public function testExpire() + { + $cache = $this->getCacheInstance(); + + static::$time = \time(); + $this->assertTrue($cache->set('expire_test', 'expire_test', 2)); + static::$time++; + $this->assertEquals('expire_test', $cache->get('expire_test')); + static::$time++; + $this->assertFalse($cache->get('expire_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 { diff --git a/tests/unit/framework/rbac/ManagerTestBase.php b/tests/unit/framework/rbac/ManagerTestBase.php index dc426a1..8efa3cf 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/tests/unit/framework/requirements/YiiRequirementCheckerTest.php b/tests/unit/framework/requirements/YiiRequirementCheckerTest.php new file mode 100644 index 0000000..484fb1c --- /dev/null +++ b/tests/unit/framework/requirements/YiiRequirementCheckerTest.php @@ -0,0 +1,195 @@ + 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)->getResult(); + $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']; + $requirementsKeys = array_flip(array_keys($requirements)); + + $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[$requirementsKeys['requirementError']]['error'], 'Error requirement has no error!'); + + $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(); + + $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']; + $requirementsKeys = array_flip(array_keys($requirements)); + + $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[$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!'); + } + } + + 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 [[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 [[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/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 diff --git a/yii/base/View.php b/yii/base/View.php index f1a9a0f..2a6f71f 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 @@ -174,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); } } @@ -364,91 +364,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 +384,7 @@ class View extends Component */ public function endBlock() { - $this->endWidget(); + Block::end(); } /** @@ -476,12 +401,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 +417,7 @@ class View extends Component */ public function endContent() { - $this->endWidget(); + ContentDecorator::end(); } /** @@ -510,15 +435,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 +457,7 @@ class View extends Component */ public function endCache() { - $this->endWidget(); + FragmentCache::end(); } @@ -771,7 +696,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 +713,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 +730,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"; } } 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/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) { diff --git a/yii/rbac/Assignment.php b/yii/rbac/Assignment.php index 5b6a607..065aaa5 100644 --- a/yii/rbac/Assignment.php +++ b/yii/rbac/Assignment.php @@ -16,91 +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 + * @var Manager the auth manager of this item */ - 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; - } - + public $manager; /** - * @return mixed user ID (see [[User::id]]) + * @var string the business rule associated with this assignment */ - public function getUserId() - { - return $this->_userId; - } - + public $bizRule; /** - * @return string the authorization item name + * @var mixed additional data for this assignment */ - public function getItemName() - { - return $this->_itemName; - } - + public $data; /** - * @return string the business rule associated with 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 getBizRule() - { - return $this->_bizRule; - } - - /** - * @param string $value the business rule associated with this assignment - */ - public function setBizRule($value) - { - if ($this->_bizRule !== $value) { - $this->_bizRule = $value; - $this->_auth->saveAssignment($this); - } - } - + public $userId; /** - * @return mixed 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 getData() - { - return $this->_data; - } + public $itemName; /** - * @param mixed $value additional data for this assignment + * Saves the changes to an authorization assignment. */ - public function setData($value) + public function save() { - if ($this->_data !== $value) { - $this->_data = $value; - $this->_auth->saveAssignment($this); - } + $this->manager->saveAssignment($this); } } diff --git a/yii/rbac/DbManager.php b/yii/rbac/DbManager.php index f3658b2..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; } } @@ -146,10 +144,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 +160,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 +177,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 +191,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; } @@ -219,11 +205,8 @@ class DbManager extends Manager public function getItemChildren($names) { $query = new Query; - $rows = $query->select(array('name', 'type', 'description', 'bizrule', 'data')) - ->from(array( - $this->itemTable, - $this->itemChildTable - )) + $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) ->queryAll(); @@ -232,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; } @@ -256,10 +246,16 @@ class DbManager extends Manager ->insert($this->assignmentTable, array( 'user_id' => $userId, 'item_name' => $itemName, - 'bizrule' => $bizRule, - 'data' => serialize($data) + '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, + )); } /** @@ -271,10 +267,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 +281,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,17 +297,20 @@ 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) { 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; } @@ -341,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; } @@ -354,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, )); } @@ -381,24 +380,14 @@ 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')) - ->from(array( - $this->itemTable . ' t1', - $this->assignmentTable . ' t2' - )) + $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') - ->from(array( - $this->itemTable . ' t1', - $this->assignmentTable . ' t2' - )) - ->where(array( - 'user_id' => $userId, - 'type' => $type, - 'name' => new Expression('item_name'), - )) + $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); } $items = array(); @@ -406,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; } @@ -433,10 +429,17 @@ class DbManager extends Manager 'name' => $name, 'type' => $type, 'description' => $description, - 'bizrule' => $bizRule, - 'data' => serialize($data) + '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, + )); } /** @@ -448,13 +451,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; } /** @@ -474,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; } @@ -488,32 +497,20 @@ 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() ->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 ef56a53..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,31 +28,30 @@ class Item extends Object const TYPE_TASK = 1; const TYPE_ROLE = 2; - private $_auth; - private $_type; - private $_name; - 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 + * @var Manager the auth manager of 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; - } + 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; + /** * Checks to see if the specified item is within the hierarchy starting from this item. @@ -73,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; } @@ -87,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() @@ -116,66 +91,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); - } - } - - /** - * @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; - $this->_auth->saveItem($this); - } - } - - /** - * @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; - $this->_auth->saveItem($this); - } - } - - /** - * @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; - $this->_auth->saveItem($this); } } @@ -188,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); } /** @@ -200,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); } /** @@ -211,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); } /** @@ -221,7 +138,7 @@ class Item extends Object */ public function getChildren() { - return $this->_auth->getItemChildren($this->_name); + return $this->manager->getItemChildren($this->_name); } /** @@ -236,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); } /** @@ -247,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); } /** @@ -258,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); } /** @@ -270,6 +187,15 @@ class Item extends Object */ public function getAssignment($userId) { - return $this->_auth->getAssignment($userId, $this->_name); + return $this->manager->getAssignment($userId, $this->_name); + } + + /** + * Saves an authorization item to persistent storage. + */ + public function save() + { + $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 diff --git a/yii/requirements/YiiRequirementChecker.php b/yii/requirements/YiiRequirementChecker.php new file mode 100644 index 0000000..03413d1 --- /dev/null +++ b/yii/requirements/YiiRequirementChecker.php @@ -0,0 +1,392 @@ + '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 + */ +class YiiRequirementChecker +{ + /** + * Check the given requirements, collecting results into internal field. + * This method can be invoked several times checking different requirement sets. + * 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; + * @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, "' . gettype($requirements) . '" has been given!'); + } + if (!isset($this->result) || !is_array($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); + $this->result['summary']['total']++; + if (!$requirement['condition']) { + if ($requirement['mandatory']) { + $requirement['error'] = true; + $requirement['warning'] = true; + $this->result['summary']['errors']++; + } else { + $requirement['error'] = false; + $requirement['warning'] = true; + $this->result['summary']['warnings']++; + } + } else { + $requirement['error'] = false; + $requirement['warning'] = false; + } + $this->result['requirements'][] = $requirement; + } + return $this; + } + + /** + * Performs the check for the Yii core requirements. + * @return YiiRequirementChecker self instance. + */ + public function checkYii() + { + return $this->check(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'requirements.php'); + } + + /** + * Return the 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() + { + 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!'); + } + $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); + } + + /** + * 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. + * @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_); + } + } + + /** + * 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 . ';'); + } + + /** + * 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/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/views/console/index.php b/yii/requirements/views/console/index.php new file mode 100644 index 0000000..1b9c61b --- /dev/null +++ b/yii/requirements/views/console/index.php @@ -0,0 +1,36 @@ + $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"; + } +} + +$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 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 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(