Browse Source

Merge branch 'master' of git://github.com/yiisoft/yii2 into view-begin-content-docs

tags/2.0.0-beta
Rinat Silnov 12 years ago
parent
commit
2b091e7ba5
  1. 3
      .gitignore
  2. 29
      apps/bootstrap/protected/commands/HelloController.php
  3. 26
      apps/bootstrap/protected/config/console.php
  4. 18
      apps/bootstrap/protected/config/main.php
  5. 5
      apps/bootstrap/protected/config/params.php
  6. 2
      apps/bootstrap/protected/controllers/SiteController.php
  7. 8
      apps/bootstrap/protected/views/layouts/main.php
  8. 16
      apps/bootstrap/protected/views/site/contact.php
  9. 4
      apps/bootstrap/protected/views/site/login.php
  10. 23
      apps/bootstrap/protected/yii
  11. 2
      apps/bootstrap/protected/yii.bat
  12. 2
      build/build
  13. 2
      build/controllers/LocaleController.php
  14. 21
      composer.json
  15. 18
      composer.lock
  16. 4
      docs/api/db/ActiveRecord.md
  17. 28
      docs/guide/upgrade-from-v1.md
  18. 25
      docs/internals/ar.md
  19. 3
      tests/unit/framework/YiiBaseTest.php
  20. 209
      tests/unit/framework/base/DictionaryTest.php
  21. 2
      tests/unit/framework/base/ModelTest.php
  22. 230
      tests/unit/framework/base/VectorTest.php
  23. 7
      tests/unit/framework/caching/ApcCacheTest.php
  24. 41
      tests/unit/framework/helpers/ArrayHelperTest.php
  25. 86
      tests/unit/framework/web/ResponseTest.php
  26. 3
      upgrade.md
  27. 19
      yii/YiiBase.php
  28. 8
      yii/assets.php
  29. 4
      yii/assets/yii.captcha.js
  30. 2
      yii/assets/yii.debug.js
  31. 28
      yii/assets/yii.validation.js
  32. 3
      yii/base/Controller.php
  33. 297
      yii/base/Dictionary.php
  34. 92
      yii/base/DictionaryIterator.php
  35. 28
      yii/base/ErrorException.php
  36. 7
      yii/base/ErrorHandler.php
  37. 2
      yii/base/Exception.php
  38. 2
      yii/base/HttpException.php
  39. 2
      yii/base/InvalidCallException.php
  40. 2
      yii/base/InvalidConfigException.php
  41. 2
      yii/base/InvalidParamException.php
  42. 2
      yii/base/InvalidRequestException.php
  43. 2
      yii/base/InvalidRouteException.php
  44. 49
      yii/base/Model.php
  45. 2
      yii/base/Module.php
  46. 2
      yii/base/NotSupportedException.php
  47. 17
      yii/base/Theme.php
  48. 2
      yii/base/UnknownClassException.php
  49. 2
      yii/base/UnknownMethodException.php
  50. 2
      yii/base/UnknownPropertyException.php
  51. 341
      yii/base/Vector.php
  52. 92
      yii/base/VectorIterator.php
  53. 114
      yii/base/View.php
  54. 101
      yii/base/Widget.php
  55. 105
      yii/behaviors/AutoTimestamp.php
  56. 8
      yii/console/Application.php
  57. 8
      yii/console/Controller.php
  58. 2
      yii/console/Exception.php
  59. 2
      yii/console/Request.php
  60. 2
      yii/console/controllers/AssetController.php
  61. 24
      yii/console/controllers/CacheController.php
  62. 18
      yii/console/controllers/HelpController.php
  63. 2
      yii/console/controllers/MessageController.php
  64. 40
      yii/console/controllers/MigrateController.php
  65. 131
      yii/db/ActiveRecord.php
  66. 28
      yii/db/Connection.php
  67. 2
      yii/db/Exception.php
  68. 4
      yii/db/Migration.php
  69. 2
      yii/db/StaleObjectException.php
  70. 10
      yii/debug/Toolbar.php
  71. 12
      yii/debug/controllers/DefaultController.php
  72. 39
      yii/debug/views/default/toolbar.php
  73. 18
      yii/helpers/base/ArrayHelper.php
  74. 11
      yii/i18n/I18N.php
  75. 2
      yii/i18n/data/plurals.php
  76. 91
      yii/logging/DebugTarget.php
  77. 2
      yii/logging/EmailTarget.php
  78. 28
      yii/logging/Logger.php
  79. 6
      yii/logging/ProfileTarget.php
  80. 21
      yii/logging/Target.php
  81. 61
      yii/logging/WebTarget.php
  82. 83
      yii/rbac/Assignment.php
  83. 92
      yii/rbac/DbManager.php
  84. 144
      yii/rbac/Item.php
  85. 2
      yii/rbac/Manager.php
  86. 58
      yii/rbac/PhpManager.php
  87. 7
      yii/rbac/schema-mssql.sql
  88. 7
      yii/rbac/schema-mysql.sql
  89. 7
      yii/rbac/schema-oci.sql
  90. 7
      yii/rbac/schema-pgsql.sql
  91. 7
      yii/rbac/schema-sqlite.sql
  92. 4
      yii/requirements/requirements.php
  93. 6188
      yii/requirements/views/web/css.php
  94. 62
      yii/requirements/views/web/index.php
  95. 7
      yii/validators/BooleanValidator.php
  96. 7
      yii/validators/CaptchaValidator.php
  97. 23
      yii/validators/CompareValidator.php
  98. 2
      yii/validators/DateValidator.php
  99. 31
      yii/validators/EmailValidator.php
  100. 2
      yii/validators/ExistValidator.php
  101. Some files were not shown because too many files have changed in this diff Show More

3
.gitignore vendored

@ -14,3 +14,6 @@ Thumbs.db
# composer vendor dir
/yii/vendor
# composer itself is not needed
composer.phar

29
apps/bootstrap/protected/commands/HelloController.php

@ -0,0 +1,29 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace app\commands;
use yii\console\Controller;
/**
* This command echos what the first argument that you have entered.
*
* This command is provided as an example for you to learn how to create console commands.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class HelloController extends Controller
{
/**
* This command echos what you have entered as the message.
* @param string $message the message to be echoed.
*/
public function actionIndex($message = 'hello world')
{
echo $message;
}
}

26
apps/bootstrap/protected/config/console.php

@ -0,0 +1,26 @@
<?php
return array(
'id' => 'bootstrap-console',
'basePath' => dirname(__DIR__),
'preload' => array('log'),
'controllerPath' => dirname(__DIR__) . '/commands',
'controllerNamespace' => 'app\commands',
'modules' => array(
),
'components' => array(
'cache' => array(
'class' => 'yii\caching\FileCache',
),
'log' => array(
'class' => 'yii\logging\Router',
'targets' => array(
array(
'class' => 'yii\logging\FileTarget',
'levels' => array('error', 'warning'),
),
),
),
),
'params' => require(__DIR__ . '/params.php'),
);

18
apps/bootstrap/protected/config/main.php

@ -1,13 +1,14 @@
<?php
return array(
'id' => 'hello',
'id' => 'bootstrap',
'basePath' => dirname(__DIR__),
'preload' => array('log'),
'controllerNamespace' => 'app\controllers',
'modules' => array(
'debug' => array(
'class' => 'yii\debug\Module',
)
// 'debug' => array(
// 'class' => 'yii\debug\Module',
// )
),
'components' => array(
'cache' => array(
@ -23,14 +24,15 @@ return array(
'log' => array(
'class' => 'yii\logging\Router',
'targets' => array(
'file' => array(
array(
'class' => 'yii\logging\FileTarget',
'levels' => array('error', 'warning'),
),
// array(
// 'class' => 'yii\logging\DebugTarget',
// )
),
),
),
'params' => array(
'adminEmail' => 'admin@example.com',
),
'params' => require(__DIR__ . '/params.php'),
);

5
apps/bootstrap/protected/config/params.php

@ -0,0 +1,5 @@
<?php
return array(
'adminEmail' => 'admin@example.com',
);

2
apps/bootstrap/protected/controllers/SiteController.php

@ -1,5 +1,7 @@
<?php
namespace app\controllers;
use yii\web\Controller;
use app\models\LoginForm;
use app\models\ContactForm;

8
apps/bootstrap/protected/views/layouts/main.php

@ -1,6 +1,8 @@
<?php
use yii\helpers\Html;
use yii\widgets\Menu;
use yii\widgets\Breadcrumbs;
use yii\debug\Toolbar;
/**
* @var $this \yii\base\View
@ -25,7 +27,7 @@ $this->registerAssetBundle('app');
<div class="navbar">
<div class="navbar-inner">
<div class="container">
<?php $this->widget(Menu::className(), array(
<?php echo Menu::widget(array(
'options' => array('class' => 'nav'),
'items' => array(
array('label' => 'Home', 'url' => array('/site/index')),
@ -42,7 +44,7 @@ $this->registerAssetBundle('app');
<!-- /.navbar -->
</div>
<?php $this->widget('yii\widgets\Breadcrumbs', array(
<?php echo Breadcrumbs::widget(array(
'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : array(),
)); ?>
<?php echo $content; ?>
@ -58,7 +60,7 @@ $this->registerAssetBundle('app');
</div>
<?php $this->endBody(); ?>
</div>
<?php $this->widget('yii\debug\Toolbar'); ?>
<?php echo Toolbar::widget(); ?>
</body>
</html>
<?php $this->endPage(); ?>

16
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.
</p>
<?php $form = $this->beginWidget(ActiveForm::className(), array(
<?php $form = ActiveForm::begin(array(
'options' => array('class' => 'form-horizontal'),
'fieldConfig' => array('inputOptions' => array('class' => 'input-xlarge')),
)); ?>
@ -33,14 +33,14 @@ $this->params['breadcrumbs'][] = $this->title;
<?php echo $form->field($model, 'body')->textArea(array('rows' => 6)); ?>
<?php
$field = $form->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()
. Html::activeTextInput($model, 'verifyCode', array('class' => 'input-medium'))
. $field->error()
. $field->end();
?>
<div class="form-actions">
<?php echo Html::submitButton('Submit', null, null, array('class' => 'btn btn-primary')); ?>
</div>
<?php $this->endWidget(); ?>
<?php ActiveForm::end(); ?>

4
apps/bootstrap/protected/views/site/login.php

@ -14,11 +14,11 @@ $this->params['breadcrumbs'][] = $this->title;
<p>Please fill out the following fields to login:</p>
<?php $form = $this->beginWidget(ActiveForm::className(), array('options' => array('class' => 'form-horizontal'))); ?>
<?php $form = ActiveForm::begin(array('options' => array('class' => 'form-horizontal'))); ?>
<?php echo $form->field($model, 'username')->textInput(); ?>
<?php echo $form->field($model, 'password')->passwordInput(); ?>
<?php echo $form->field($model, 'rememberMe')->checkbox(); ?>
<div class="form-actions">
<?php echo Html::submitButton('Login', null, null, array('class' => 'btn btn-primary')); ?>
</div>
<?php $this->endWidget(); ?>
<?php ActiveForm::end(); ?>

23
apps/bootstrap/protected/yii

@ -0,0 +1,23 @@
#!/usr/bin/env php
<?php
/**
* Yii console bootstrap file.
*
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
defined('YII_DEBUG') or define('YII_DEBUG', true);
// fcgi doesn't have STDIN defined by default
defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
$frameworkPath = __DIR__ . '/../../../yii';
require($frameworkPath . '/Yii.php');
$config = require(__DIR__ . '/config/console.php');
$application = new yii\console\Application($config);
$application->run();

2
yii/yiic.bat → apps/bootstrap/protected/yii.bat

@ -17,6 +17,6 @@ set YII_PATH=%~dp0
if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
"%PHP_COMMAND%" "%YII_PATH%yiic" %*
"%PHP_COMMAND%" "%YII_PATH%yii" %*
@endlocal

2
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__;

2
build/controllers/LocaleController.php

@ -93,7 +93,7 @@ class LocaleController extends Controller
/**
* Plural rules.
*
* This file is automatically generated by the "yiic locale/plural" command under the "build" folder.
* This file is automatically generated by the "yii locale/plural" command under the "build" folder.
* Do not modify it directly.
*
* The original plural rule data used for generating this file has the following copyright terms:

21
composer.json

@ -69,11 +69,30 @@
"bin": [
"yii/yiic"
],
"repositories": [
{
"type": "package",
"package": {
"name": "bestiejs/punycode.js",
"version": "1.2.1",
"dist": {
"url": "https://github.com/bestiejs/punycode.js/archive/1.2.1.zip",
"type": "zip"
},
"source": {
"url": "https://github.com/bestiejs/punycode.js.git",
"type": "git",
"reference": "tags/1.2.1"
}
}
}
],
"require": {
"php": ">=5.3.0",
"michelf/php-markdown": "1.3",
"twig/twig": "1.12.*",
"smarty/smarty": "3.1.*",
"ezyang/htmlpurifier": "v4.5.0"
"ezyang/htmlpurifier": "v4.5.0",
"bestiejs/punycode.js": "1.2.1"
}
}

18
composer.lock generated

@ -3,9 +3,25 @@
"This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
],
"hash": "7d46ce9c4d8d5f4ecae1611ea8f0b49c",
"hash": "a8f949e337a229a4cfb41496a0071ef6",
"packages": [
{
"name": "bestiejs/punycode.js",
"version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/bestiejs/punycode.js.git",
"reference": "tags/1.2.1"
},
"dist": {
"type": "zip",
"url": "https://github.com/bestiejs/punycode.js/archive/master.zip",
"reference": null,
"shasum": null
},
"type": "library"
},
{
"name": "ezyang/htmlpurifier",
"version": "v4.5.0",
"source": {

4
docs/api/db/ActiveRecord.md

@ -446,3 +446,7 @@ $customers = Customer::find()->olderThan(50)->all();
The parameters should follow after the `$query` parameter when defining the scope method, and they
can take default values like shown above.
### Atomic operations and scenarios
TBD

28
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(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
------
@ -249,10 +269,6 @@ Message translation is still supported, but managed via the "i18n" application c
The component manages a set of message sources, which allows you to use different message
sources based on message categories. For more information, see the class documentation for `I18N`.
The message translation method is changed by merging the message category into the message being
translated. For example, `Yii::t('yii|message to be translated')`.
Action Filters
--------------
@ -309,13 +325,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
<?php $form = $this->beginWidget('yii\widgets\ActiveForm'); ?>
<?php $form = yii\widgets\ActiveForm::begin(); ?>
<?php echo $form->field($model, 'username')->textInput(); ?>
<?php echo $form->field($model, 'password')->passwordInput(); ?>
<div class="form-actions">
<?php echo Html::submitButton('Login'); ?>
</div>
<?php $this->endWidget(); ?>
<?php yii\widgets\ActiveForm::end(); ?>
```

25
docs/internals/ar.md

@ -1,15 +1,32 @@
ActiveRecord
============
Scenarios
---------
Possible scenario formats supported by ActiveRecord:
```php
public function scenarios()
{
return array(
// attributes array, all operations won't be wrapped with transaction
'scenario1' => array('attribute1', 'attribute2'),
// insert and update operations will be wrapped with transaction, delete won't be wrapped
'scenario2' => array(
'attributes' => array('attribute1', 'attribute2'),
'atomic' => array(self::OP_INSERT, self::OP_UPDATE),
),
);
}
```
Query
-----
### Basic Queries
### Relational Queries
### Scopes

3
tests/unit/framework/YiiBaseTest.php

@ -45,6 +45,9 @@ class YiiBaseTest extends TestCase
Yii::setAlias('@yii', null);
$this->assertFalse(Yii::getAlias('@yii', false));
$this->assertEquals('/yii/gii/file', Yii::getAlias('@yii/gii/file'));
Yii::setAlias('@some/alias', '/www');
$this->assertEquals('/www', Yii::getAlias('@some/alias'));
}
public function testGetVersion()

209
tests/unit/framework/base/DictionaryTest.php

@ -1,209 +0,0 @@
<?php
namespace yiiunit\framework\base;
use yii\base\Dictionary;
class MapItem
{
public $data='data';
}
class DictionaryTest extends \yiiunit\TestCase
{
/**
* @var \yii\base\Dictionary
*/
protected $dictionary;
protected $item1;
protected $item2;
protected $item3;
protected function setUp()
{
parent::setUp();
$this->dictionary = new Dictionary;
$this->item1 = new MapItem;
$this->item2 = new MapItem;
$this->item3 = new MapItem;
$this->dictionary->add('key1', $this->item1);
$this->dictionary->add('key2', $this->item2);
}
protected function tearDown()
{
parent::tearDown();
$this->dictionary = null;
$this->item1 = null;
$this->item2 = null;
$this->item3 = null;
}
public function testConstruct()
{
$a = array(1, 2, 'key3' => 3);
$dictionary = new Dictionary($a);
$this->assertEquals(3, $dictionary->getCount());
$dictionary2=new Dictionary($this->dictionary);
$this->assertEquals(2, $dictionary2->getCount());
}
public function testGetCount()
{
$this->assertEquals(2, $this->dictionary->getCount());
}
public function testGetKeys()
{
$keys = $this->dictionary->getKeys();
$this->assertEquals(2, count($keys));
$this->assertEquals('key1', $keys[0]);
$this->assertEquals('key2', $keys[1]);
}
public function testAdd()
{
$this->dictionary->add('key3', $this->item3);
$this->assertEquals(3, $this->dictionary->getCount());
$this->assertTrue($this->dictionary->has('key3'));
$this->dictionary[] = 'test';
}
public function testRemove()
{
$this->dictionary->remove('key1');
$this->assertEquals(1, $this->dictionary->getCount());
$this->assertTrue(!$this->dictionary->has('key1'));
$this->assertTrue($this->dictionary->remove('unknown key') === null);
}
public function testRemoveAll()
{
$this->dictionary->add('key3', $this->item3);
$this->dictionary->removeAll();
$this->assertEquals(0, $this->dictionary->getCount());
$this->assertTrue(!$this->dictionary->has('key1') && !$this->dictionary->has('key2'));
$this->dictionary->add('key3', $this->item3);
$this->dictionary->removeAll(true);
$this->assertEquals(0, $this->dictionary->getCount());
$this->assertTrue(!$this->dictionary->has('key1') && !$this->dictionary->has('key2'));
}
public function testHas()
{
$this->assertTrue($this->dictionary->has('key1'));
$this->assertTrue($this->dictionary->has('key2'));
$this->assertFalse($this->dictionary->has('key3'));
}
public function testFromArray()
{
$array = array('key3' => $this->item3, 'key4' => $this->item1);
$this->dictionary->copyFrom($array);
$this->assertEquals(2, $this->dictionary->getCount());
$this->assertEquals($this->item3, $this->dictionary['key3']);
$this->assertEquals($this->item1, $this->dictionary['key4']);
$this->setExpectedException('yii\base\InvalidParamException');
$this->dictionary->copyFrom($this);
}
public function testMergeWith()
{
$a = array('a' => 'v1', 'v2', array('2'), 'c' => array('3', 'c' => 'a'));
$b = array('v22', 'a' => 'v11', array('2'), 'c' => array('c' => '3', 'a'));
$c = array('a' => 'v11', 'v2', array('2'), 'c' => array('3', 'c' => '3', 'a'), 'v22', array('2'));
$dictionary = new Dictionary($a);
$dictionary2 = new Dictionary($b);
$dictionary->mergeWith($dictionary2);
$this->assertTrue($dictionary->toArray() === $c);
$array = array('key2' => $this->item1, 'key3' => $this->item3);
$this->dictionary->mergeWith($array, false);
$this->assertEquals(3, $this->dictionary->getCount());
$this->assertEquals($this->item1, $this->dictionary['key2']);
$this->assertEquals($this->item3, $this->dictionary['key3']);
$this->setExpectedException('yii\base\InvalidParamException');
$this->dictionary->mergeWith($this, false);
}
public function testRecursiveMergeWithTraversable(){
$dictionary = new Dictionary();
$obj = new \ArrayObject(array(
'k1' => $this->item1,
'k2' => $this->item2,
'k3' => new \ArrayObject(array(
'k4' => $this->item3,
))
));
$dictionary->mergeWith($obj, true);
$this->assertEquals(3, $dictionary->getCount());
$this->assertEquals($this->item1, $dictionary['k1']);
$this->assertEquals($this->item2, $dictionary['k2']);
$this->assertEquals($this->item3, $dictionary['k3']['k4']);
}
public function testArrayRead()
{
$this->assertEquals($this->item1, $this->dictionary['key1']);
$this->assertEquals($this->item2, $this->dictionary['key2']);
$this->assertEquals(null, $this->dictionary['key3']);
}
public function testArrayWrite()
{
$this->dictionary['key3'] = $this->item3;
$this->assertEquals(3, $this->dictionary->getCount());
$this->assertEquals($this->item3, $this->dictionary['key3']);
$this->dictionary['key1'] = $this->item3;
$this->assertEquals(3, $this->dictionary->getCount());
$this->assertEquals($this->item3, $this->dictionary['key1']);
unset($this->dictionary['key2']);
$this->assertEquals(2, $this->dictionary->getCount());
$this->assertTrue(!$this->dictionary->has('key2'));
unset($this->dictionary['unknown key']);
}
public function testArrayForeach()
{
$n = 0;
$found = 0;
foreach ($this->dictionary as $index => $item) {
$n++;
if ($index === 'key1' && $item === $this->item1) {
$found++;
}
if ($index === 'key2' && $item === $this->item2) {
$found++;
}
}
$this->assertTrue($n == 2 && $found == 2);
}
public function testArrayMisc()
{
$this->assertEquals($this->dictionary->Count, count($this->dictionary));
$this->assertTrue(isset($this->dictionary['key1']));
$this->assertFalse(isset($this->dictionary['unknown key']));
}
public function testToArray()
{
$dictionary = new Dictionary(array('key' => 'value'));
$this->assertEquals(array('key' => 'value'), $dictionary->toArray());
}
public function testIteratorCurrent()
{
$dictionary = new Dictionary(array('key1' => 'value1', 'key2' => 'value2'));
$val = $dictionary->getIterator()->current();
$this->assertEquals('value1', $val);
}
}

2
tests/unit/framework/base/ModelTest.php

@ -12,7 +12,7 @@ use yiiunit\data\base\InvalidRulesModel;
*/
class ModelTest extends TestCase
{
public function testGetAttributeLalel()
public function testGetAttributeLabel()
{
$speaker = new Speaker();
$this->assertEquals('First Name', $speaker->getAttributeLabel('firstName'));

230
tests/unit/framework/base/VectorTest.php

@ -1,230 +0,0 @@
<?php
namespace yiiunit\framework\base;
use yii\base\Vector;
class ListItem
{
public $data='data';
}
class VectorTest extends \yiiunit\TestCase
{
/**
* @var Vector
*/
protected $vector;
protected $item1;
protected $item2;
protected $item3;
protected function setUp()
{
parent::setUp();
$this->vector = new Vector;
$this->item1 = new ListItem;
$this->item2 = new ListItem;
$this->item3 = new ListItem;
$this->vector->add($this->item1);
$this->vector->add($this->item2);
}
protected function tearDown()
{
parent::tearDown();
$this->vector = null;
$this->item1 = null;
$this->item2 = null;
$this->item3 = null;
}
public function testConstruct()
{
$a = array(1, 2, 3);
$vector = new Vector($a);
$this->assertEquals(3, $vector->getCount());
$vector2 = new Vector($this->vector);
$this->assertEquals(2, $vector2->getCount());
}
public function testItemAt()
{
$a = array(1, 2, null, 4);
$vector = new Vector($a);
$this->assertEquals(1, $vector->itemAt(0));
$this->assertEquals(2, $vector->itemAt(1));
$this->assertNull($vector->itemAt(2));
$this->assertEquals(4, $vector->itemAt(3));
}
public function testGetCount()
{
$this->assertEquals(2, $this->vector->getCount());
$this->assertEquals(2, $this->vector->Count);
}
public function testAdd()
{
$this->vector->add(null);
$this->vector->add($this->item3);
$this->assertEquals(4, $this->vector->getCount());
$this->assertEquals(3, $this->vector->indexOf($this->item3));
}
public function testInsertAt()
{
$this->vector->insertAt(0, $this->item3);
$this->assertEquals(3, $this->vector->getCount());
$this->assertEquals(2, $this->vector->indexOf($this->item2));
$this->assertEquals(0, $this->vector->indexOf($this->item3));
$this->assertEquals(1, $this->vector->indexOf($this->item1));
$this->setExpectedException('yii\base\InvalidParamException');
$this->vector->insertAt(4, $this->item3);
}
public function testRemove()
{
$this->vector->remove($this->item1);
$this->assertEquals(1, $this->vector->getCount());
$this->assertEquals(-1, $this->vector->indexOf($this->item1));
$this->assertEquals(0, $this->vector->indexOf($this->item2));
$this->assertEquals(false, $this->vector->remove($this->item1));
}
public function testRemoveAt()
{
$this->vector->add($this->item3);
$this->vector->removeAt(1);
$this->assertEquals(-1, $this->vector->indexOf($this->item2));
$this->assertEquals(1, $this->vector->indexOf($this->item3));
$this->assertEquals(0, $this->vector->indexOf($this->item1));
$this->setExpectedException('yii\base\InvalidParamException');
$this->vector->removeAt(2);
}
public function testRemoveAll()
{
$this->vector->add($this->item3);
$this->vector->removeAll();
$this->assertEquals(0, $this->vector->getCount());
$this->assertEquals(-1, $this->vector->indexOf($this->item1));
$this->assertEquals(-1, $this->vector->indexOf($this->item2));
$this->vector->add($this->item3);
$this->vector->removeAll(true);
$this->assertEquals(0, $this->vector->getCount());
$this->assertEquals(-1, $this->vector->indexOf($this->item1));
$this->assertEquals(-1, $this->vector->indexOf($this->item2));
}
public function testHas()
{
$this->assertTrue($this->vector->has($this->item1));
$this->assertTrue($this->vector->has($this->item2));
$this->assertFalse($this->vector->has($this->item3));
}
public function testIndexOf()
{
$this->assertEquals(0, $this->vector->indexOf($this->item1));
$this->assertEquals(1, $this->vector->indexOf($this->item2));
$this->assertEquals(-1, $this->vector->indexOf($this->item3));
}
public function testFromArray()
{
$array = array($this->item3, $this->item1);
$this->vector->copyFrom($array);
$this->assertTrue(count($array) == 2 && $this->vector[0] === $this->item3 && $this->vector[1] === $this->item1);
$this->setExpectedException('yii\base\InvalidParamException');
$this->vector->copyFrom($this);
}
public function testMergeWith()
{
$array = array($this->item3, $this->item1);
$this->vector->mergeWith($array);
$this->assertTrue($this->vector->getCount() == 4 && $this->vector[0] === $this->item1 &&
$this->vector[3] === $this->item1);
$a = array(1);
$vector = new Vector($a);
$this->vector->mergeWith($vector);
$this->assertTrue($this->vector->getCount() == 5 && $this->vector[0] === $this->item1 &&
$this->vector[3] === $this->item1 && $this->vector[4] === 1);
$this->setExpectedException('yii\base\InvalidParamException');
$this->vector->mergeWith($this);
}
public function testToArray()
{
$array = $this->vector->toArray();
$this->assertTrue(count($array) == 2 && $array[0] === $this->item1 && $array[1] === $this->item2);
}
public function testArrayRead()
{
$this->assertTrue($this->vector[0] === $this->item1);
$this->assertTrue($this->vector[1] === $this->item2);
$this->setExpectedException('yii\base\InvalidParamException');
$a = $this->vector[2];
}
public function testGetIterator()
{
$n = 0;
$found = 0;
foreach ($this->vector as $index => $item) {
foreach ($this->vector as $a => $b) {
// test of iterator
}
$n++;
if ($index === 0 && $item === $this->item1) {
$found++;
}
if ($index === 1 && $item === $this->item2) {
$found++;
}
}
$this->assertTrue($n == 2 && $found == 2);
}
public function testArrayMisc()
{
$this->assertEquals($this->vector->Count, count($this->vector));
$this->assertTrue(isset($this->vector[1]));
$this->assertFalse(isset($this->vector[2]));
}
public function testOffsetSetAdd()
{
$vector = new Vector(array(1, 2, 3));
$vector->offsetSet(null, 4);
$this->assertEquals(array(1, 2, 3, 4), $vector->toArray());
}
public function testOffsetSetReplace()
{
$vector = new Vector(array(1, 2, 3));
$vector->offsetSet(1, 4);
$this->assertEquals(array(1, 4, 3), $vector->toArray());
}
public function testOffsetUnset()
{
$vector = new Vector(array(1, 2, 3));
$vector->offsetUnset(1);
$this->assertEquals(array(1, 3), $vector->toArray());
}
public function testIteratorCurrent()
{
$vector = new Vector(array('value1', 'value2'));
$val = $vector->getIterator()->current();
$this->assertEquals('value1', $val);
}
}

7
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.");
}
}

41
tests/unit/framework/helpers/ArrayHelperTest.php

@ -3,6 +3,8 @@
namespace yiiunit\framework\helpers;
use yii\helpers\ArrayHelper;
use yii\helpers\VarDumper;
use yii\web\Sort;
class ArrayHelperTest extends \yii\test\TestCase
{
@ -54,16 +56,51 @@ class ArrayHelperTest extends \yii\test\TestCase
array('name' => 'A', 'age' => 1),
);
ArrayHelper::multisort($array, array('name', 'age'), SORT_ASC, array(SORT_STRING, SORT_REGULAR));
ArrayHelper::multisort($array, array('name', 'age'), false, array(SORT_STRING, SORT_REGULAR));
$this->assertEquals(array('name' => 'A', 'age' => 1), $array[0]);
$this->assertEquals(array('name' => 'B', 'age' => 4), $array[1]);
$this->assertEquals(array('name' => 'a', 'age' => 3), $array[2]);
$this->assertEquals(array('name' => 'b', 'age' => 2), $array[3]);
ArrayHelper::multisort($array, array('name', 'age'), SORT_ASC, array(SORT_STRING, SORT_REGULAR), false);
ArrayHelper::multisort($array, array('name', 'age'), false, array(SORT_STRING, SORT_REGULAR), false);
$this->assertEquals(array('name' => 'A', 'age' => 1), $array[0]);
$this->assertEquals(array('name' => 'a', 'age' => 3), $array[1]);
$this->assertEquals(array('name' => 'b', 'age' => 2), $array[2]);
$this->assertEquals(array('name' => 'B', 'age' => 4), $array[3]);
}
public function testMultisortUseSort()
{
// single key
$sort = new Sort();
$sort->attributes = array('name', 'age');
$sort->defaults = array('name' => Sort::ASC);
$orders = $sort->getOrders();
$array = array(
array('name' => 'b', 'age' => 3),
array('name' => 'a', 'age' => 1),
array('name' => 'c', 'age' => 2),
);
ArrayHelper::multisort($array, array_keys($orders), array_values($orders));
$this->assertEquals(array('name' => 'a', 'age' => 1), $array[0]);
$this->assertEquals(array('name' => 'b', 'age' => 3), $array[1]);
$this->assertEquals(array('name' => 'c', 'age' => 2), $array[2]);
// multiple keys
$sort = new Sort();
$sort->attributes = array('name', 'age');
$sort->defaults = array('name' => Sort::ASC, 'age' => Sort::DESC);
$orders = $sort->getOrders();
$array = array(
array('name' => 'b', 'age' => 3),
array('name' => 'a', 'age' => 2),
array('name' => 'a', 'age' => 1),
);
ArrayHelper::multisort($array, array_keys($orders), array_values($orders));
$this->assertEquals(array('name' => 'a', 'age' => 2), $array[0]);
$this->assertEquals(array('name' => 'a', 'age' => 1), $array[1]);
$this->assertEquals(array('name' => 'b', 'age' => 3), $array[2]);
}
}

86
tests/unit/framework/web/ResponseTest.php

@ -0,0 +1,86 @@
<?php
namespace yii\web;
use yiiunit\framework\web\ResponseTest;
/**
* Mock PHP header function to check for sent headers
* @param string $string
* @param bool $replace
* @param int $httpResponseCode
*/
function header($string, $replace = true, $httpResponseCode = null) {
ResponseTest::$headers[] = $string;
// TODO implement replace
if ($httpResponseCode !== null) {
ResponseTest::$httpResponseCode = $httpResponseCode;
}
}
namespace yiiunit\framework\web;
use yii\helpers\StringHelper;
use yii\web\Response;
class ResponseTest extends \yiiunit\TestCase
{
public static $headers = array();
public static $httpResponseCode = 200;
protected function setUp()
{
parent::setUp();
$this->reset();
}
protected function reset()
{
static::$headers = array();
static::$httpResponseCode = 200;
}
public function ranges()
{
// TODO test more cases for range requests and check for rfc compatibility
// http://www.w3.org/Protocols/rfc2616/rfc2616.txt
return array(
array('0-5', '0-5', 6, '12ёж'),
array('2-', '2-66', 65, 'ёжик3456798áèabcdefghijklmnopqrstuvwxyz!"§$%&/(ёжик)=?'),
array('-12', '55-66', 12, '(ёжик)=?'),
);
}
/**
* @dataProvider ranges
*/
public function testSendFileRanges($rangeHeader, $expectedHeader, $length, $expectedFile)
{
$content = $this->generateTestFileContent();
$_SERVER['HTTP_RANGE'] = 'bytes=' . $rangeHeader;
$sent = $this->runSendFile('testFile.txt', $content, null);
$this->assertEquals($expectedFile, $sent);
$this->assertTrue(in_array('HTTP/1.1 206 Partial Content', static::$headers));
$this->assertTrue(in_array('Accept-Ranges: bytes', static::$headers));
$this->assertArrayHasKey('Content-Range: bytes ' . $expectedHeader . '/' . StringHelper::strlen($content), array_flip(static::$headers));
$this->assertTrue(in_array('Content-Type: text/plain', static::$headers));
$this->assertTrue(in_array('Content-Length: ' . $length, static::$headers));
}
protected function generateTestFileContent()
{
return '12ёжик3456798áèabcdefghijklmnopqrstuvwxyz!"§$%&/(ёжик)=?';
}
protected function runSendFile($fileName, $content, $mimeType)
{
ob_start();
ob_implicit_flush(false);
$response = new Response();
$response->sendFile($fileName, $content, $mimeType, false);
$file = ob_get_clean();
return $file;
}
}

3
upgrade.md

@ -35,8 +35,7 @@ Upgrading from v1.1.x
from `Object` and supports events and behaviors. Behaviors declared in
`Component::behaviors()` are attached on demand.
- `CList` is renamed to `Vector`, and `CMap` is renamed to `Dictionary`.
Other collection classes are dropped in favor of SPL classes.
- All collection classes are dropped in favor of SPL classes.
- `CFormModel` is removed. Please use `yii\base\Model` instead.

19
yii/YiiBase.php

@ -287,7 +287,11 @@ class YiiBase
if ($path !== null) {
$path = strncmp($path, '@', 1) ? rtrim($path, '\\/') : static::getAlias($path);
if (!isset(self::$aliases[$root])) {
if ($pos === false) {
self::$aliases[$root] = $path;
} else {
self::$aliases[$root] = array($alias => $path);
}
} elseif (is_string(self::$aliases[$root])) {
if ($pos === false) {
self::$aliases[$root] = $path;
@ -579,10 +583,9 @@ class YiiBase
/**
* Translates a message to the specified language.
*
* The translation will be conducted according to the message category and the target language.
* To specify the category of the message, prefix the message with the category name and separate it
* with "|". For example, "app|hello world". If the category is not specified, the default category "app"
* will be used. The actual message translation is done by a [[\yii\i18n\MessageSource|message source]].
* This is a shortcut method of [[\yii\i18n\I18N::translate()]].
*
* The translation will be conducted according to the message category and the target language will be used.
*
* In case when a translated message has different plural forms (separated by "|"), this method
* will also attempt to choose an appropriate one according to a given numeric value which is
@ -595,20 +598,18 @@ class YiiBase
* For more details on how plural rules are applied, please refer to:
* [[http://www.unicode.org/cldr/charts/supplemental/language_plural_rules.html]]
*
* @param string $category the message category.
* @param string $message the message to be translated.
* @param array $params the parameters that will be used to replace the corresponding placeholders in the message.
* @param string $language the language code (e.g. `en_US`, `en`). If this is null, the current
* [[\yii\base\Application::language|application language]] will be used.
* @return string the translated message.
*/
public static function t($message, $params = array(), $language = null)
public static function t($category, $message, $params = array(), $language = null)
{
if (self::$app !== null) {
return self::$app->getI18N()->translate($message, $params, $language);
return self::$app->getI18N()->translate($category, $message, $params, $language);
} else {
if (strpos($message, '|') !== false && preg_match('/^([\w\-\\/\.\\\\]+)\|(.*)/', $message, $matches)) {
$message = $matches[2];
}
return is_array($params) ? strtr($message, $params) : $message;
}
}

8
yii/assets.php

@ -26,7 +26,7 @@ return array(
'js' => array(
'yii.activeForm.js',
),
'depends' => array('yii', 'yii/validation'),
'depends' => array('yii'),
),
'yii/captcha' => array(
'sourcePath' => __DIR__ . '/assets',
@ -42,4 +42,10 @@ return array(
),
'depends' => array('yii'),
),
'punycode' => array(
'sourcePath' => __DIR__ . '/vendor/bestiejs/punycode.js',
'js' => array(
'punycode.min.js',
),
),
);

4
yii/assets/yii.captcha.js

@ -51,8 +51,8 @@
dataType: 'json',
cache: false,
success: function(data) {
$e.attr('src', data['url']);
$('body').data(settings.hashKey, [data['hash1'], data['hash2']]);
$e.attr('src', data.url);
$('body').data(settings.hashKey, [data.hash1, data.hash2]);
}
});
},

2
yii/assets/yii.debug.js

@ -18,7 +18,7 @@ yii.debug = (function ($) {
//dataType: 'json',
success: function(data) {
var $e = $('#' + id);
$e.html(data);
$e.html(data).show();
}
});
}

28
yii/assets/yii.validation.js

@ -110,9 +110,19 @@ yii.validation = (function ($) {
return;
}
var valid = value.match(options.pattern) && (!options.allowName || value.match(options.fullPattern));
var valid = true;
if (!valid) {
if (options.enableIDN) {
var regexp = /^(.*)@(.*)$/,
matches = regexp.exec(value);
if (matches === null) {
valid = false;
} else {
value = punycode.toASCII(matches[1]) + '@' + punycode.toASCII(matches[2]);
}
}
if (!valid || !(value.match(options.pattern) && (!options.allowName || value.match(options.fullPattern)))) {
messages.push(options.message);
}
},
@ -126,7 +136,19 @@ yii.validation = (function ($) {
value = options.defaultScheme + '://' + value;
}
if (!value.match(options.pattern)) {
var valid = true;
if (options.enableIDN) {
var regexp = /^([^:]+):\/\/([^\/]+)(.*)$/,
matches = regexp.exec(value);
if (matches === null) {
valid = false;
} else {
value = matches[1] + '://' + punycode.toASCII(matches[2]) + matches[3];
}
}
if (!valid || !value.match(options.pattern)) {
messages.push(options.message);
}
},

3
yii/base/Controller.php

@ -183,7 +183,7 @@ class Controller extends Component
}
if (!empty($missing)) {
throw new InvalidRequestException(Yii::t('yii|Missing required parameters: {params}', array(
throw new InvalidRequestException(Yii::t('yii', 'Missing required parameters: {params}', array(
'{params}' => implode(', ', $missing),
)));
}
@ -410,6 +410,7 @@ class Controller extends Component
* Returns the view object that can be used to render views or view files.
* The [[render()]], [[renderPartial()]] and [[renderFile()]] methods will use
* this view object to implement the actual view rendering.
* If not set, it will default to the "view" application component.
* @return View the view object that can be used to render views or view files.
*/
public function getView()

297
yii/base/Dictionary.php

@ -1,297 +0,0 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
use yii\helpers\ArrayHelper;
/**
* Dictionary implements a collection that stores key-value pairs.
*
* You can access, add or remove an item with a key by using
* [[itemAt()]], [[add()]], and [[remove()]].
*
* To get the number of the items in the dictionary, use [[getCount()]].
*
* Because Dictionary implements a set of SPL interfaces, it can be used
* like a regular PHP array as follows,
*
* ~~~
* $dictionary[$key] = $value; // add a key-value pair
* unset($dictionary[$key]); // remove the value with the specified key
* if (isset($dictionary[$key])) // if the dictionary contains the key
* foreach ($dictionary as $key => $value) // traverse the items in the dictionary
* $n = count($dictionary); // returns the number of items in the dictionary
* ~~~
*
* @property integer $count the number of items in the dictionary
* @property array $keys The keys in the dictionary
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Dictionary extends Object implements \IteratorAggregate, \ArrayAccess, \Countable
{
/**
* @var array internal data storage
*/
private $_d = array();
/**
* Constructor.
* Initializes the dictionary with an array or an iterable object.
* @param mixed $data the initial data to be populated into the dictionary.
* This can be an array or an iterable object.
* @param array $config name-value pairs that will be used to initialize the object properties
* @throws Exception if data is not well formed (neither an array nor an iterable object)
*/
public function __construct($data = array(), $config = array())
{
if (!empty($data)) {
$this->copyFrom($data);
}
parent::__construct($config);
}
/**
* Returns an iterator for traversing the items in the dictionary.
* This method is required by the SPL interface `IteratorAggregate`.
* It will be implicitly called when you use `foreach` to traverse the dictionary.
* @return DictionaryIterator an iterator for traversing the items in the dictionary.
*/
public function getIterator()
{
return new DictionaryIterator($this->_d);
}
/**
* Returns the number of items in the dictionary.
* This method is required by the SPL `Countable` interface.
* It will be implicitly called when you use `count($dictionary)`.
* @return integer number of items in the dictionary.
*/
public function count()
{
return $this->getCount();
}
/**
* Returns the number of items in the dictionary.
* @return integer the number of items in the dictionary
*/
public function getCount()
{
return count($this->_d);
}
/**
* Returns the keys stored in the dictionary.
* @return array the key list
*/
public function getKeys()
{
return array_keys($this->_d);
}
/**
* Returns the item with the specified key.
* @param mixed $key the key
* @return mixed the element with the specified key.
* Null if the key cannot be found in the dictionary.
*/
public function itemAt($key)
{
return isset($this->_d[$key]) ? $this->_d[$key] : null;
}
/**
* Adds an item into the dictionary.
* Note, if the specified key already exists, the old value will be overwritten.
* @param mixed $key key
* @param mixed $value value
* @throws Exception if the dictionary is read-only
*/
public function add($key, $value)
{
if ($key === null) {
$this->_d[] = $value;
} else {
$this->_d[$key] = $value;
}
}
/**
* Removes an item from the dictionary by its key.
* @param mixed $key the key of the item to be removed
* @return mixed the removed value, null if no such key exists.
* @throws Exception if the dictionary is read-only
*/
public function remove($key)
{
if (isset($this->_d[$key])) {
$value = $this->_d[$key];
unset($this->_d[$key]);
return $value;
} else { // the value is null
unset($this->_d[$key]);
return null;
}
}
/**
* Removes all items from the dictionary.
* @param boolean $safeClear whether to clear every item by calling [[remove]].
* Defaults to false, meaning all items in the dictionary will be cleared directly
* without calling [[remove]].
*/
public function removeAll($safeClear = false)
{
if ($safeClear) {
foreach (array_keys($this->_d) as $key) {
$this->remove($key);
}
} else {
$this->_d = array();
}
}
/**
* Returns a value indicating whether the dictionary contains the specified key.
* @param mixed $key the key
* @return boolean whether the dictionary contains an item with the specified key
*/
public function has($key)
{
return isset($this->_d[$key]) || array_key_exists($key, $this->_d);
}
/**
* Returns the dictionary as a PHP array.
* @return array the list of items in array
*/
public function toArray()
{
return $this->_d;
}
/**
* Copies iterable data into the dictionary.
* Note, existing data in the dictionary will be cleared first.
* @param mixed $data the data to be copied from, must be an array or an object implementing `Traversable`
* @throws InvalidParamException if data is neither an array nor an iterator.
*/
public function copyFrom($data)
{
if (is_array($data) || $data instanceof \Traversable) {
if (!empty($this->_d)) {
$this->removeAll();
}
if ($data instanceof self) {
$data = $data->_d;
}
foreach ($data as $key => $value) {
$this->add($key, $value);
}
} else {
throw new InvalidParamException('Data must be either an array or an object implementing Traversable.');
}
}
/**
* Merges iterable data into the dictionary.
*
* Existing elements in the dictionary will be overwritten if their keys are the same as those in the source.
* If the merge is recursive, the following algorithm is performed:
*
* - the dictionary data is saved as $a, and the source data is saved as $b;
* - if $a and $b both have an array indexed at the same string key, the arrays will be merged using this algorithm;
* - any integer-indexed elements in $b will be appended to $a;
* - any string-indexed elements in $b will overwrite elements in $a with the same index;
*
* @param array|\Traversable $data the data to be merged with. It must be an array or object implementing Traversable
* @param boolean $recursive whether the merging should be recursive.
* @throws InvalidParamException if data is neither an array nor an object implementing `Traversable`.
*/
public function mergeWith($data, $recursive = true)
{
if (is_array($data) || $data instanceof \Traversable) {
if ($data instanceof self) {
$data = $data->_d;
}
if ($recursive) {
if ($data instanceof \Traversable) {
$d = array();
foreach ($data as $key => $value) {
$d[$key] = $value;
}
$this->_d = ArrayHelper::merge($this->_d, $d);
} else {
$this->_d = ArrayHelper::merge($this->_d, $data);
}
} else {
foreach ($data as $key => $value) {
$this->add($key, $value);
}
}
} else {
throw new InvalidParamException('The data to be merged with must be an array or an object implementing Traversable.');
}
}
/**
* Returns whether there is an element at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `isset($dictionary[$offset])`.
* This is equivalent to [[contains]].
* @param mixed $offset the offset to check on
* @return boolean
*/
public function offsetExists($offset)
{
return $this->has($offset);
}
/**
* Returns the element at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `$value = $dictionary[$offset];`.
* This is equivalent to [[itemAt]].
* @param mixed $offset the offset to retrieve element.
* @return mixed the element at the offset, null if no element is found at the offset
*/
public function offsetGet($offset)
{
return $this->itemAt($offset);
}
/**
* Sets the element at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `$dictionary[$offset] = $item;`.
* If the offset is null, the new item will be appended to the dictionary.
* Otherwise, the existing item at the offset will be replaced with the new item.
* This is equivalent to [[add]].
* @param mixed $offset the offset to set element
* @param mixed $item the element value
*/
public function offsetSet($offset, $item)
{
$this->add($offset, $item);
}
/**
* Unsets the element at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `unset($dictionary[$offset])`.
* This is equivalent to [[remove]].
* @param mixed $offset the offset to unset element
*/
public function offsetUnset($offset)
{
$this->remove($offset);
}
}

92
yii/base/DictionaryIterator.php

@ -1,92 +0,0 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* DictionaryIterator implements the SPL `Iterator` interface for [[Dictionary]].
*
* It allows [[Dictionary]] to return a new iterator for data traversing purpose.
* You normally do not use this class directly.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class DictionaryIterator implements \Iterator
{
/**
* @var array the data to be iterated through
*/
private $_d;
/**
* @var array list of keys in the map
*/
private $_keys;
/**
* @var mixed current key
*/
private $_key;
/**
* Constructor.
* @param array $data the data to be iterated through
*/
public function __construct(&$data)
{
$this->_d = &$data;
$this->_keys = array_keys($data);
$this->_key = reset($this->_keys);
}
/**
* Rewinds the index of the current item.
* This method is required by the SPL interface `Iterator`.
*/
public function rewind()
{
$this->_key = reset($this->_keys);
}
/**
* Returns the key of the current array element.
* This method is required by the SPL interface `Iterator`.
* @return mixed the key of the current array element
*/
public function key()
{
return $this->_key;
}
/**
* Returns the current array element.
* This method is required by the SPL interface `Iterator`.
* @return mixed the current array element
*/
public function current()
{
return $this->_d[$this->_key];
}
/**
* Moves the internal pointer to the next element.
* This method is required by the SPL interface `Iterator`.
*/
public function next()
{
$this->_key = next($this->_keys);
}
/**
* Returns whether there is an element at current position.
* This method is required by the SPL interface `Iterator`.
* @return boolean whether there is an item at current position.
*/
public function valid()
{
return $this->_key !== false;
}
}

28
yii/base/ErrorException.php

@ -90,20 +90,20 @@ class ErrorException extends Exception
public function getName()
{
$names = array(
E_ERROR => Yii::t('yii|Fatal Error'),
E_PARSE => Yii::t('yii|Parse Error'),
E_CORE_ERROR => Yii::t('yii|Core Error'),
E_COMPILE_ERROR => Yii::t('yii|Compile Error'),
E_USER_ERROR => Yii::t('yii|User Error'),
E_WARNING => Yii::t('yii|Warning'),
E_CORE_WARNING => Yii::t('yii|Core Warning'),
E_COMPILE_WARNING => Yii::t('yii|Compile Warning'),
E_USER_WARNING => Yii::t('yii|User Warning'),
E_STRICT => Yii::t('yii|Strict'),
E_NOTICE => Yii::t('yii|Notice'),
E_RECOVERABLE_ERROR => Yii::t('yii|Recoverable Error'),
E_DEPRECATED => Yii::t('yii|Deprecated'),
E_ERROR => Yii::t('yii', 'Fatal Error'),
E_PARSE => Yii::t('yii', 'Parse Error'),
E_CORE_ERROR => Yii::t('yii', 'Core Error'),
E_COMPILE_ERROR => Yii::t('yii', 'Compile Error'),
E_USER_ERROR => Yii::t('yii', 'User Error'),
E_WARNING => Yii::t('yii', 'Warning'),
E_CORE_WARNING => Yii::t('yii', 'Core Warning'),
E_COMPILE_WARNING => Yii::t('yii', 'Compile Warning'),
E_USER_WARNING => Yii::t('yii', 'User Warning'),
E_STRICT => Yii::t('yii', 'Strict'),
E_NOTICE => Yii::t('yii', 'Notice'),
E_RECOVERABLE_ERROR => Yii::t('yii', 'Recoverable Error'),
E_DEPRECATED => Yii::t('yii', 'Deprecated'),
);
return isset($names[$this->getCode()]) ? $names[$this->getCode()] : Yii::t('yii|Error');
return isset($names[$this->getCode()]) ? $names[$this->getCode()] : Yii::t('yii', 'Error');
}
}

7
yii/base/ErrorHandler.php

@ -75,8 +75,11 @@ class ErrorHandler extends Component
\Yii::$app->runAction($this->errorAction);
} elseif (\Yii::$app instanceof \yii\web\Application) {
if (!headers_sent()) {
$errorCode = $exception instanceof HttpException ? $exception->statusCode : 500;
header("HTTP/1.0 $errorCode " . get_class($exception));
if ($exception instanceof HttpException) {
header('HTTP/1.0 ' . $exception->statusCode . ' ' . $exception->getName());
} else {
header('HTTP/1.0 500 ' . get_class($exception));
}
}
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') {
\Yii::$app->renderException($exception);

2
yii/base/Exception.php

@ -20,6 +20,6 @@ class Exception extends \Exception
*/
public function getName()
{
return \Yii::t('yii|Exception');
return \Yii::t('yii', 'Exception');
}
}

2
yii/base/HttpException.php

@ -103,7 +103,7 @@ class HttpException extends UserException
if (isset($httpCodes[$this->statusCode])) {
return $httpCodes[$this->statusCode];
} else {
return \Yii::t('yii|Error');
return \Yii::t('yii', 'Error');
}
}
}

2
yii/base/InvalidCallException.php

@ -20,7 +20,7 @@ class InvalidCallException extends Exception
*/
public function getName()
{
return \Yii::t('yii|Invalid Call');
return \Yii::t('yii', 'Invalid Call');
}
}

2
yii/base/InvalidConfigException.php

@ -20,7 +20,7 @@ class InvalidConfigException extends Exception
*/
public function getName()
{
return \Yii::t('yii|Invalid Configuration');
return \Yii::t('yii', 'Invalid Configuration');
}
}

2
yii/base/InvalidParamException.php

@ -20,7 +20,7 @@ class InvalidParamException extends Exception
*/
public function getName()
{
return \Yii::t('yii|Invalid Parameter');
return \Yii::t('yii', 'Invalid Parameter');
}
}

2
yii/base/InvalidRequestException.php

@ -20,7 +20,7 @@ class InvalidRequestException extends UserException
*/
public function getName()
{
return \Yii::t('yii|Invalid Request');
return \Yii::t('yii', 'Invalid Request');
}
}

2
yii/base/InvalidRouteException.php

@ -20,7 +20,7 @@ class InvalidRouteException extends UserException
*/
public function getName()
{
return \Yii::t('yii|Invalid Route');
return \Yii::t('yii', 'Invalid Route');
}
}

49
yii/base/Model.php

@ -7,6 +7,8 @@
namespace yii\base;
use ArrayObject;
use ArrayIterator;
use yii\helpers\StringHelper;
use yii\validators\RequiredValidator;
use yii\validators\Validator;
@ -30,7 +32,7 @@ use yii\validators\Validator;
* You may directly use Model to store model data, or extend it with customization.
* You may also customize Model by attaching [[ModelBehavior|model behaviors]].
*
* @property Vector $validators All the validators declared in the model.
* @property ArrayObject $validators All the validators declared in the model.
* @property array $activeValidators The validators applicable to the current [[scenario]].
* @property array $errors Errors for all attributes or the specified attribute. Empty array is returned if no error.
* @property array $attributes Attribute values (name => value).
@ -56,7 +58,7 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess
*/
private $_errors;
/**
* @var Vector vector of validators
* @var ArrayObject list of validators
*/
private $_validators;
/**
@ -300,15 +302,15 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess
* This method differs from [[getActiveValidators()]] in that the latter
* only returns the validators applicable to the current [[scenario]].
*
* Because this method returns a [[Vector]] object, you may
* Because this method returns an ArrayObject object, you may
* manipulate it by inserting or removing validators (useful in model behaviors).
* For example,
*
* ~~~
* $model->validators->add($newValidator);
* $model->validators[] = $newValidator;
* ~~~
*
* @return Vector all the validators declared in the model.
* @return ArrayObject all the validators declared in the model.
*/
public function getValidators()
{
@ -340,18 +342,18 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess
/**
* Creates validator objects based on the validation rules specified in [[rules()]].
* Unlike [[getValidators()]], each time this method is called, a new list of validators will be returned.
* @return Vector validators
* @return ArrayObject validators
* @throws InvalidConfigException if any validation rule configuration is invalid
*/
public function createValidators()
{
$validators = new Vector;
$validators = new ArrayObject;
foreach ($this->rules() as $rule) {
if ($rule instanceof Validator) {
$validators->add($rule);
$validators->append($rule);
} elseif (is_array($rule) && isset($rule[0], $rule[1])) { // attributes, validator type
$validator = Validator::createValidator($rule[1], $this, $rule[0], array_slice($rule, 2));
$validators->add($validator);
$validators->append($validator);
} else {
throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.');
}
@ -590,53 +592,60 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess
/**
* Returns the attribute names that are safe to be massively assigned in the current scenario.
* @return array safe attribute names
* @return string[] safe attribute names
*/
public function safeAttributes()
{
$scenario = $this->getScenario();
$scenarios = $this->scenarios();
if (!isset($scenarios[$scenario])) {
return array();
}
$attributes = array();
if (isset($scenarios[$scenario])) {
if (isset($scenarios[$scenario]['attributes']) && is_array($scenarios[$scenario]['attributes'])) {
$scenarios[$scenario] = $scenarios[$scenario]['attributes'];
}
foreach ($scenarios[$scenario] as $attribute) {
if ($attribute[0] !== '!') {
$attributes[] = $attribute;
}
}
}
return $attributes;
}
/**
* Returns the attribute names that are subject to validation in the current scenario.
* @return array safe attribute names
* @return string[] safe attribute names
*/
public function activeAttributes()
{
$scenario = $this->getScenario();
$scenarios = $this->scenarios();
if (isset($scenarios[$scenario])) {
$attributes = $scenarios[$this->getScenario()];
if (!isset($scenarios[$scenario])) {
return array();
}
if (isset($scenarios[$scenario]['attributes']) && is_array($scenarios[$scenario]['attributes'])) {
$attributes = $scenarios[$scenario]['attributes'];
} else {
$attributes = $scenarios[$scenario];
}
foreach ($attributes as $i => $attribute) {
if ($attribute[0] === '!') {
$attributes[$i] = substr($attribute, 1);
}
}
return $attributes;
} else {
return array();
}
}
/**
* Returns an iterator for traversing the attributes in the model.
* This method is required by the interface IteratorAggregate.
* @return DictionaryIterator an iterator for traversing the items in the list.
* @return ArrayIterator an iterator for traversing the items in the list.
*/
public function getIterator()
{
$attributes = $this->getAttributes();
return new DictionaryIterator($attributes);
return new ArrayIterator($attributes);
}
/**

2
yii/base/Module.php

@ -449,7 +449,7 @@ abstract class Module extends Component
public function getComponent($id, $load = true)
{
if (isset($this->_components[$id])) {
if ($this->_components[$id] instanceof Component) {
if ($this->_components[$id] instanceof Object) {
return $this->_components[$id];
} elseif ($load) {
Yii::trace("Loading component: $id", __METHOD__);

2
yii/base/NotSupportedException.php

@ -20,7 +20,7 @@ class NotSupportedException extends Exception
*/
public function getName()
{
return \Yii::t('yii|Not Supported');
return \Yii::t('yii', 'Not Supported');
}
}

17
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 <qiang.xue@gmail.com>
* @since 2.0

2
yii/base/UnknownClassException.php

@ -20,7 +20,7 @@ class UnknownClassException extends Exception
*/
public function getName()
{
return \Yii::t('yii|Unknown Class');
return \Yii::t('yii', 'Unknown Class');
}
}

2
yii/base/UnknownMethodException.php

@ -20,7 +20,7 @@ class UnknownMethodException extends Exception
*/
public function getName()
{
return \Yii::t('yii|Unknown Method');
return \Yii::t('yii', 'Unknown Method');
}
}

2
yii/base/UnknownPropertyException.php

@ -20,7 +20,7 @@ class UnknownPropertyException extends Exception
*/
public function getName()
{
return \Yii::t('yii|Unknown Property');
return \Yii::t('yii', 'Unknown Property');
}
}

341
yii/base/Vector.php

@ -1,341 +0,0 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* Vector implements an integer-indexed collection class.
*
* You can access, append, insert, remove an item from the vector
* by calling methods such as [[itemAt()]], [[add()]], [[insertAt()]],
* [[remove()]] and [[removeAt()]].
*
* To get the number of the items in the vector, use [[getCount()]].
*
* Because Vector implements a set of SPL interfaces, it can be used
* like a regular PHP array as follows,
*
* ~~~
* $vector[] = $item; // append new item at the end
* $vector[$index] = $item; // set new item at $index
* unset($vector[$index]); // remove the item at $index
* if (isset($vector[$index])) // if the vector has an item at $index
* foreach ($vector as $index => $item) // traverse each item in the vector
* $n = count($vector); // count the number of items
* ~~~
*
* Note that if you plan to extend Vector by performing additional operations
* with each addition or removal of an item (e.g. performing type check),
* please make sure you override [[insertAt()]] and [[removeAt()]].
*
* @property integer $count the number of items in the vector
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Vector extends Object implements \IteratorAggregate, \ArrayAccess, \Countable
{
/**
* @var array internal data storage
*/
private $_d = array();
/**
* @var integer number of items
*/
private $_c = 0;
/**
* Constructor.
* Initializes the vector with an array or an iterable object.
* @param mixed $data the initial data to be populated into the vector.
* This can be an array or an iterable object.
* @param array $config name-value pairs that will be used to initialize the object properties
* @throws Exception if data is not well formed (neither an array nor an iterable object)
*/
public function __construct($data = array(), $config = array())
{
if (!empty($data)) {
$this->copyFrom($data);
}
parent::__construct($config);
}
/**
* Returns an iterator for traversing the items in the vector.
* This method is required by the SPL interface `IteratorAggregate`.
* It will be implicitly called when you use `foreach` to traverse the vector.
* @return VectorIterator an iterator for traversing the items in the vector.
*/
public function getIterator()
{
return new VectorIterator($this->_d);
}
/**
* Returns the number of items in the vector.
* This method is required by the SPL `Countable` interface.
* It will be implicitly called when you use `count($vector)`.
* @return integer number of items in the vector.
*/
public function count()
{
return $this->getCount();
}
/**
* Returns the number of items in the vector.
* @return integer the number of items in the vector
*/
public function getCount()
{
return $this->_c;
}
/**
* Returns the item at the specified index.
* @param integer $index the index of the item
* @return mixed the item at the index
* @throws InvalidParamException if the index is out of range
*/
public function itemAt($index)
{
if (isset($this->_d[$index])) {
return $this->_d[$index];
} elseif ($index >= 0 && $index < $this->_c) { // in case the value is null
return $this->_d[$index];
} else {
throw new InvalidParamException('Index out of range: ' . $index);
}
}
/**
* Appends an item at the end of the vector.
* @param mixed $item new item
* @return integer the zero-based index at which the item is added
* @throws Exception if the vector is read-only.
*/
public function add($item)
{
$this->insertAt($this->_c, $item);
return $this->_c - 1;
}
/**
* Inserts an item at the specified position.
* Original item at the position and the following items will be moved
* one step towards the end.
* @param integer $index the specified position.
* @param mixed $item new item to be inserted into the vector
* @throws InvalidParamException if the index specified is out of range, or the vector is read-only.
*/
public function insertAt($index, $item)
{
if ($index === $this->_c) {
$this->_d[$this->_c++] = $item;
} elseif ($index >= 0 && $index < $this->_c) {
array_splice($this->_d, $index, 0, array($item));
$this->_c++;
} else {
throw new InvalidParamException('Index out of range: ' . $index);
}
}
/**
* Removes an item from the vector.
* The vector will search for the item, and the first item found
* will be removed from the vector.
* @param mixed $item the item to be removed.
* @return mixed the index at which the item is being removed, or false
* if the item cannot be found in the vector.
* @throws Exception if the vector is read only.
*/
public function remove($item)
{
if (($index = $this->indexOf($item)) >= 0) {
$this->removeAt($index);
return $index;
} else {
return false;
}
}
/**
* Removes an item at the specified position.
* @param integer $index the index of the item to be removed.
* @return mixed the removed item.
* @throws InvalidParamException if the index is out of range, or the vector is read only.
*/
public function removeAt($index)
{
if ($index >= 0 && $index < $this->_c) {
$this->_c--;
if ($index === $this->_c) {
return array_pop($this->_d);
} else {
$item = $this->_d[$index];
array_splice($this->_d, $index, 1);
return $item;
}
} else {
throw new InvalidParamException('Index out of range: ' . $index);
}
}
/**
* Removes all items from the vector.
* @param boolean $safeClear whether to clear every item by calling [[removeAt]].
* Defaults to false, meaning all items in the vector will be cleared directly
* without calling [[removeAt]].
*/
public function removeAll($safeClear = false)
{
if ($safeClear) {
for ($i = $this->_c - 1; $i >= 0; --$i) {
$this->removeAt($i);
}
} else {
$this->_d = array();
$this->_c = 0;
}
}
/**
* Returns a value indicating whether the vector contains the specified item.
* Note that the search is based on strict PHP comparison.
* @param mixed $item the item
* @return boolean whether the vector contains the item
*/
public function has($item)
{
return $this->indexOf($item) >= 0;
}
/**
* Returns the index of the specified item in the vector.
* The index is zero-based. If the item is not found in the vector, -1 will be returned.
* Note that the search is based on strict PHP comparison.
* @param mixed $item the item
* @return integer the index of the item in the vector (0 based), -1 if not found.
*/
public function indexOf($item)
{
$index = array_search($item, $this->_d, true);
return $index === false ? -1 : $index;
}
/**
* Returns the vector as a PHP array.
* @return array the items in the vector.
*/
public function toArray()
{
return $this->_d;
}
/**
* Copies iterable data into the vector.
* Note, existing data in the vector will be cleared first.
* @param mixed $data the data to be copied from, must be an array or an object implementing `Traversable`
* @throws InvalidParamException if data is neither an array nor an object implementing `Traversable`.
*/
public function copyFrom($data)
{
if (is_array($data) || $data instanceof \Traversable) {
if ($this->_c > 0) {
$this->removeAll();
}
if ($data instanceof self) {
$data = $data->_d;
}
foreach ($data as $item) {
$this->add($item);
}
} else {
throw new InvalidParamException('Data must be either an array or an object implementing Traversable.');
}
}
/**
* Merges iterable data into the vector.
* New items will be appended to the end of the existing items.
* @param array|\Traversable $data the data to be merged with. It must be an array or object implementing Traversable
* @throws InvalidParamException if data is neither an array nor an object implementing `Traversable`.
*/
public function mergeWith($data)
{
if (is_array($data) || ($data instanceof \Traversable)) {
if ($data instanceof Vector) {
$data = $data->_d;
}
foreach ($data as $item) {
$this->add($item);
}
} else {
throw new InvalidParamException('The data to be merged with must be an array or an object implementing Traversable.');
}
}
/**
* Returns a value indicating whether there is an item at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `isset($vector[$offset])`.
* @param integer $offset the offset to be checked
* @return boolean whether there is an item at the specified offset.
*/
public function offsetExists($offset)
{
return $offset >= 0 && $offset < $this->_c;
}
/**
* Returns the item at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `$value = $vector[$offset];`.
* This is equivalent to [[itemAt]].
* @param integer $offset the offset to retrieve item.
* @return mixed the item at the offset
* @throws Exception if the offset is out of range
*/
public function offsetGet($offset)
{
return $this->itemAt($offset);
}
/**
* Sets the item at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `$vector[$offset] = $item;`.
* If the offset is null or equal to the number of the existing items,
* the new item will be appended to the vector.
* Otherwise, the existing item at the offset will be replaced with the new item.
* @param integer $offset the offset to set item
* @param mixed $item the item value
* @throws Exception if the offset is out of range, or the vector is read only.
*/
public function offsetSet($offset, $item)
{
if ($offset === null || $offset === $this->_c) {
$this->insertAt($this->_c, $item);
} else {
$this->removeAt($offset);
$this->insertAt($offset, $item);
}
}
/**
* Unsets the item at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `unset($vector[$offset])`.
* This is equivalent to [[removeAt]].
* @param integer $offset the offset to unset item
* @throws Exception if the offset is out of range, or the vector is read only.
*/
public function offsetUnset($offset)
{
$this->removeAt($offset);
}
}

92
yii/base/VectorIterator.php

@ -1,92 +0,0 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* VectorIterator implements the SPL `Iterator` interface for [[Vector]].
*
* It allows [[Vector]] to return a new iterator for data traversing purpose.
* You normally do not use this class directly.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class VectorIterator implements \Iterator
{
/**
* @var array the data to be iterated through
*/
private $_d;
/**
* @var integer index of the current item
*/
private $_i;
/**
* @var integer count of the data items
*/
private $_c;
/**
* Constructor.
* @param array $data the data to be iterated through
*/
public function __construct(&$data)
{
$this->_d = &$data;
$this->_i = 0;
$this->_c = count($this->_d);
}
/**
* Rewinds the index of the current item.
* This method is required by the SPL interface `Iterator`.
*/
public function rewind()
{
$this->_i = 0;
}
/**
* Returns the key of the current item.
* This method is required by the SPL interface `Iterator`.
* @return integer the key of the current item
*/
public function key()
{
return $this->_i;
}
/**
* Returns the current item.
* This method is required by the SPL interface `Iterator`.
* @return mixed the current item
*/
public function current()
{
return $this->_d[$this->_i];
}
/**
* Moves the internal pointer to the next item.
* This method is required by the SPL interface `Iterator`.
*/
public function next()
{
$this->_i++;
}
/**
* Returns a value indicating whether there is an item at current position.
* This method is required by the SPL interface `Iterator`.
* @return boolean whether there is an item at current position.
*/
public function valid()
{
return $this->_i < $this->_c;
}
}

114
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,93 +364,19 @@ 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(array(
'id' => $id,
'renderInPlace' => $renderInPlace,
'view' => $this,
));
}
@ -459,7 +385,7 @@ class View extends Component
*/
public function endBlock()
{
$this->endWidget();
Block::end();
}
/**
@ -476,14 +402,15 @@ 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(array(
'viewFile' => $viewFile,
'params' => $params,
'view' => $this,
));
}
@ -492,7 +419,7 @@ class View extends Component
*/
public function endContent()
{
$this->endWidget();
ContentDecorator::end();
}
/**
@ -510,15 +437,16 @@ 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);
$properties['view'] = $this;
/** @var $cache FragmentCache */
$cache = FragmentCache::begin($properties);
if ($cache->getCachedContent() !== false) {
$this->endCache();
return false;
@ -532,7 +460,7 @@ class View extends Component
*/
public function endCache()
{
$this->endWidget();
FragmentCache::end();
}

101
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,19 +18,73 @@ 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 integer a counter used to generate [[id]] for widgets.
* @internal
*/
public $view;
public static $_counter = 0;
/**
* @var string id of the widget.
* @var Widget[] the widgets that are currently being rendered (not ended). This property
* is maintained by [[begin()]] and [[end()]] methods.
* @internal
*/
private $_id;
public static $_stack = array();
/**
* @var integer a counter used to generate IDs for widgets.
* 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 array $config name-value pairs that will be used to initialize the object properties
* @return Widget the newly created widget instance
*/
private static $_counter = 0;
public static function begin($config = array())
{
$config['class'] = get_called_class();
/** @var Widget $widget */
$widget = Yii::createObject($config);
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 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($config = array())
{
ob_start();
ob_implicit_flush(false);
/** @var Widget $widget */
$config['class'] = get_called_class();
$widget = Yii::createObject($config);
$widget->run();
return ob_get_clean();
}
private $_id;
/**
* Returns the ID of the widget.
@ -55,6 +108,32 @@ class Widget extends Component
$this->_id = $value;
}
private $_view;
/**
* Returns the view object that can be used to render views or view files.
* The [[render()]] and [[renderFile()]] methods will use
* this view object to implement the actual view rendering.
* If not set, it will default to the "view" application component.
* @return View the view object that can be used to render views or view files.
*/
public function getView()
{
if ($this->_view === null) {
$this->_view = Yii::$app->getView();
}
return $this->_view;
}
/**
* Sets the view object to be used by this widget.
* @param View $view the view object that can be used to render views or view files.
*/
public function setView($view)
{
$this->_view = $view;
}
/**
* Executes the widget.
*/
@ -84,7 +163,7 @@ class Widget extends Component
public function render($view, $params = array())
{
$viewFile = $this->findViewFile($view);
return $this->view->renderFile($viewFile, $params, $this);
return $this->getView()->renderFile($viewFile, $params, $this);
}
/**
@ -96,7 +175,7 @@ class Widget extends Component
*/
public function renderFile($file, $params = array())
{
return $this->view->renderFile($file, $params, $this);
return $this->getView()->renderFile($file, $params, $this);
}
/**

105
yii/behaviors/AutoTimestamp.php

@ -0,0 +1,105 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\behaviors;
use yii\base\Behavior;
use yii\db\Expression;
use yii\db\ActiveRecord;
/**
* AutoTimestamp will automatically fill the attributes about creation time and updating time.
*
* AutoTimestamp fills the attributes when the associated AR model is being inserted or updated.
* You may specify an AR to use this behavior like the following:
*
* ~~~
* public function behaviors()
* {
* return array(
* 'timestamp' => array(
* 'class' => 'yii\behaviors\AutoTimestamp',
* ),
* );
* }
* ~~~
*
* By default, the attribute for keeping the creation time is named as "create_time", and the attribute
* for updating time is "update_time". You may customize the names via [[createAttribute]] and [[updateAttribute]].
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class AutoTimestamp extends Behavior
{
/**
* @var string The name of the attribute to store the creation time. Set to null to not
* use a timestamp for the creation attribute. Defaults to 'create_time'
*/
public $createAttribute = 'create_time';
/**
* @var string The name of the attribute to store the modification time. Set to null to not
* use a timestamp for the update attribute. Defaults to 'update_time'
*/
public $updateAttribute = 'update_time';
/**
* @var \Closure|Expression The expression that will be used for generating the timestamp.
* This can be either an anonymous function that returns the timestamp value,
* or an [[Expression]] object representing a DB expression (e.g. `new Expression('NOW()')`).
* If not set, it will use the value of `time()` to fill the attributes.
*/
public $timestamp;
/**
* Declares event handlers for the [[owner]]'s events.
* @return array events (array keys) and the corresponding event handler methods (array values).
*/
public function events()
{
return array(
ActiveRecord::EVENT_BEFORE_INSERT => 'beforeInsert',
ActiveRecord::EVENT_BEFORE_UPDATE => 'beforeUpdate',
);
}
/**
* This is the event handler for the "beforeInsert" event of the associated AR object.
*/
public function beforeInsert()
{
if ($this->createAttribute !== null) {
$this->owner->{$this->createAttribute} = $this->evaluateTimestamp($this->createAttribute);
}
}
/**
* This is the event handler for the "beforeUpdate" event of the associated AR object.
*/
public function beforeUpdate()
{
if ($this->updateAttribute !== null) {
$this->owner->{$this->updateAttribute} = $this->evaluateTimestamp($this->updateAttribute);
}
}
/**
* Gets the appropriate timestamp depending on the column type $attribute is
* @param string $attribute attribute name
* @return mixed the timestamp value
*/
protected function evaluateTimestamp($attribute)
{
if ($this->timestamp instanceof Expression) {
return $this->timestamp;
} elseif ($this->timestamp !== null) {
return call_user_func($this->timestamp);
} else {
return time();
}
}
}

8
yii/console/Application.php

@ -30,7 +30,7 @@ use yii\base\InvalidRouteException;
* To run the console application, enter the following on the command line:
*
* ~~~
* yiic <route> [--param1=value1 --param2 ...]
* yii <route> [--param1=value1 --param2 ...]
* ~~~
*
* where `<route>` refers to a controller route in the form of `ModuleID/ControllerID/ActionID`
@ -42,7 +42,7 @@ use yii\base\InvalidRouteException;
* To use this command, simply type:
*
* ~~~
* yiic help
* yii help
* ~~~
*
* @author Qiang Xue <qiang.xue@gmail.com>
@ -94,7 +94,7 @@ class Application extends \yii\base\Application
list ($route, $params) = $request->resolve();
return $this->runAction($route, $params);
} else {
throw new Exception(\Yii::t('yii|This script must be run from the command line.'));
throw new Exception(\Yii::t('yii', 'This script must be run from the command line.'));
}
}
@ -113,7 +113,7 @@ class Application extends \yii\base\Application
try {
return parent::runAction($route, $params);
} catch (InvalidRouteException $e) {
throw new Exception(\Yii::t('yii|Unknown command "{command}".', array('{command}' => $route)));
throw new Exception(\Yii::t('yii', 'Unknown command "{command}".', array('{command}' => $route)));
}
}

8
yii/console/Controller.php

@ -18,10 +18,10 @@ use yii\helpers\Console;
*
* A controller consists of one or several actions known as sub-commands.
* Users call a console command by specifying the corresponding route which identifies a controller action.
* The `yiic` program is used when calling a console command, like the following:
* The `yii` program is used when calling a console command, like the following:
*
* ~~~
* yiic <route> [--param1=value1 --param2 ...]
* yii <route> [--param1=value1 --param2 ...]
* ~~~
*
* @author Qiang Xue <qiang.xue@gmail.com>
@ -91,7 +91,7 @@ class Controller extends \yii\base\Controller
$args = isset($params[Request::ANONYMOUS_PARAMS]) ? $params[Request::ANONYMOUS_PARAMS] : array();
unset($params[Request::ANONYMOUS_PARAMS]);
if (!empty($params)) {
throw new Exception(Yii::t('yii|Unknown options: {params}', array(
throw new Exception(Yii::t('yii', 'Unknown options: {params}', array(
'{params}' => implode(', ', array_keys($params)),
)));
}
@ -115,7 +115,7 @@ class Controller extends \yii\base\Controller
}
if (!empty($missing)) {
throw new Exception(Yii::t('yii|Missing required arguments: {params}', array(
throw new Exception(Yii::t('yii', 'Missing required arguments: {params}', array(
'{params}' => implode(', ', $missing),
)));
}

2
yii/console/Exception.php

@ -22,7 +22,7 @@ class Exception extends UserException
*/
public function getName()
{
return \Yii::t('yii|Error');
return \Yii::t('yii', 'Error');
}
}

2
yii/console/Request.php

@ -27,7 +27,7 @@ class Request extends \yii\base\Request
public function resolve()
{
$rawParams = $this->getRawParams();
array_shift($rawParams); // the 1st argument is the yiic script name
array_shift($rawParams); // the 1st argument is the yii script name
if (isset($rawParams[0])) {
$route = $rawParams[0];

2
yii/console/controllers/AssetController.php

@ -261,7 +261,7 @@ class AssetController extends Controller
file_put_contents($bundleFile, <<<EOD
<?php
/**
* This file is generated by the "yiic script" command.
* This file is generated by the "yii script" command.
* DO NOT MODIFY THIS FILE DIRECTLY.
* @version $version
*/

24
yii/console/controllers/CacheController.php

@ -7,6 +7,7 @@
namespace yii\console\controllers;
use Yii;
use yii\console\Controller;
use yii\console\Exception;
use yii\caching\Cache;
@ -19,9 +20,28 @@ use yii\caching\Cache;
*/
class CacheController extends Controller
{
/**
* Lists the caches that can be flushed.
*/
public function actionIndex()
{
$this->forward('help/index', array('-args' => array('cache/flush')));
$caches = array();
$components = Yii::$app->getComponents();
foreach ($components as $name => $component) {
if ($component instanceof Cache) {
$caches[$name] = get_class($component);
} elseif (is_array($component) && isset($component['class']) && strpos($component['class'], 'Cache') !== false) {
$caches[$name] = $component['class'];
}
}
if (!empty($caches)) {
echo "The following caches can be flushed:\n\n";
foreach ($caches as $name => $class) {
echo " * $name: $class\n";
}
} else {
echo "No cache is used.\n";
}
}
/**
@ -33,7 +53,7 @@ class CacheController extends Controller
public function actionFlush($component = 'cache')
{
/** @var $cache Cache */
$cache = \Yii::$app->getComponent($component);
$cache = Yii::$app->getComponent($component);
if (!$cache || !$cache instanceof Cache) {
throw new Exception('Application component "'.$component.'" is not defined or not a cache.');
}

18
yii/console/controllers/HelpController.php

@ -25,7 +25,7 @@ use yii\helpers\StringHelper;
* This command can be used as follows on command line:
*
* ~~~
* yiic help [command name]
* yii help [command name]
* ~~~
*
* In the above, if the command name is not provided, all
@ -41,8 +41,8 @@ class HelpController extends Controller
* about a particular command. For example,
*
* ~~~
* yiic help # list available commands
* yiic help message # display help info about "message"
* yii help # list available commands
* yii help message # display help info about "message"
* ~~~
*
* @param string $command The name of the command to show help about.
@ -55,7 +55,7 @@ class HelpController extends Controller
if ($command !== null) {
$result = Yii::$app->createController($command);
if ($result === false) {
throw new Exception(Yii::t('yii|No help for unknown command "{command}".', array(
throw new Exception(Yii::t('yii', 'No help for unknown command "{command}".', array(
'{command}' => $command,
)));
}
@ -148,7 +148,7 @@ class HelpController extends Controller
echo "* $command\n";
}
echo "\nTo see the help of each command, enter:\n";
echo "\n yiic help <command-name>\n\n";
echo "\n yii help <command-name>\n\n";
} else {
echo "\nNo commands are found.\n";
}
@ -188,7 +188,7 @@ class HelpController extends Controller
echo "\n";
}
echo "\n\nTo see the detailed information about individual sub-commands, enter:\n";
echo "\n yiic help <sub-command>\n\n";
echo "\n yii help <sub-command>\n\n";
}
}
@ -239,7 +239,7 @@ class HelpController extends Controller
{
$action = $controller->createAction($actionID);
if ($action === null) {
throw new Exception(Yii::t('yii|No help for unknown sub-command "{command}".', array(
throw new Exception(Yii::t('yii', 'No help for unknown sub-command "{command}".', array(
'{command}' => rtrim($controller->getUniqueId() . '/' . $actionID, '/'),
)));
}
@ -259,9 +259,9 @@ class HelpController extends Controller
echo "\nUSAGE\n\n";
if ($action->id === $controller->defaultAction) {
echo 'yiic ' . $controller->getUniqueId();
echo 'yii ' . $controller->getUniqueId();
} else {
echo "yiic " . $action->getUniqueId();
echo "yii " . $action->getUniqueId();
}
list ($required, $optional) = $this->getArgHelps($method, isset($tags['param']) ? $tags['param'] : array());
if (!empty($required)) {

2
yii/console/controllers/MessageController.php

@ -194,7 +194,7 @@ class MessageController extends Controller
/**
* Message translations.
*
* This file is automatically generated by 'yiic message' command.
* This file is automatically generated by 'yii message' command.
* It contains the localizable messages extracted from source code.
* You may modify this file by translating the extracted messages.
*

40
yii/console/controllers/MigrateController.php

@ -42,13 +42,13 @@ use yii\helpers\ArrayHelper;
*
* ~~~
* # creates a new migration named 'create_user_table'
* yiic migrate/create create_user_table
* yii migrate/create create_user_table
*
* # applies ALL new migrations
* yiic migrate
* yii migrate
*
* # reverts the last applied migration
* yiic migrate/down
* yii migrate/down
* ~~~
*
* @author Qiang Xue <qiang.xue@gmail.com>
@ -135,8 +135,8 @@ class MigrateController extends Controller
* For example,
*
* ~~~
* yiic migrate # apply all new migrations
* yiic migrate 3 # apply the first 3 new migrations
* yii migrate # apply all new migrations
* yii migrate 3 # apply the first 3 new migrations
* ~~~
*
* @param integer $limit the number of new migrations to be applied. If 0, it means
@ -184,8 +184,8 @@ class MigrateController extends Controller
* For example,
*
* ~~~
* yiic migrate/down # revert the last migration
* yiic migrate/down 3 # revert the last 3 migrations
* yii migrate/down # revert the last migration
* yii migrate/down 3 # revert the last 3 migrations
* ~~~
*
* @param integer $limit the number of migrations to be reverted. Defaults to 1,
@ -231,8 +231,8 @@ class MigrateController extends Controller
* them again. For example,
*
* ~~~
* yiic migrate/redo # redo the last applied migration
* yiic migrate/redo 3 # redo the last 3 applied migrations
* yii migrate/redo # redo the last applied migration
* yii migrate/redo 3 # redo the last 3 applied migrations
* ~~~
*
* @param integer $limit the number of migrations to be redone. Defaults to 1,
@ -284,8 +284,8 @@ class MigrateController extends Controller
* them again. For example,
*
* ~~~
* yiic migrate/to 101129_185401 # using timestamp
* yiic migrate/to m101129_185401_create_user_table # using full name
* yii migrate/to 101129_185401 # using timestamp
* yii migrate/to m101129_185401_create_user_table # using full name
* ~~~
*
* @param string $version the version name that the application should be migrated to.
@ -332,8 +332,8 @@ class MigrateController extends Controller
* No actual migration will be performed.
*
* ~~~
* yiic migrate/mark 101129_185401 # using timestamp
* yiic migrate/mark m101129_185401_create_user_table # using full name
* yii migrate/mark 101129_185401 # using timestamp
* yii migrate/mark m101129_185401_create_user_table # using full name
* ~~~
*
* @param string $version the version at which the migration history should be marked.
@ -398,9 +398,9 @@ class MigrateController extends Controller
* so far. For example,
*
* ~~~
* yiic migrate/history # showing the last 10 migrations
* yiic migrate/history 5 # showing the last 5 migrations
* yiic migrate/history 0 # showing the whole history
* yii migrate/history # showing the last 10 migrations
* yii migrate/history 5 # showing the last 5 migrations
* yii migrate/history 0 # showing the whole history
* ~~~
*
* @param integer $limit the maximum number of migrations to be displayed.
@ -432,9 +432,9 @@ class MigrateController extends Controller
* For example,
*
* ~~~
* yiic migrate/new # showing the first 10 new migrations
* yiic migrate/new 5 # showing the first 5 new migrations
* yiic migrate/new 0 # showing all new migrations
* yii migrate/new # showing the first 10 new migrations
* yii migrate/new 5 # showing the first 5 new migrations
* yii migrate/new 0 # showing all new migrations
* ~~~
*
* @param integer $limit the maximum number of new migrations to be displayed.
@ -469,7 +469,7 @@ class MigrateController extends Controller
* skeleton by filling up the actual migration logic.
*
* ~~~
* yiic migrate/create create_user_table
* yii migrate/create create_user_table
* ~~~
*
* @param string $name the name of the new migration. This should only contain

131
yii/db/ActiveRecord.php

@ -74,6 +74,22 @@ class ActiveRecord extends Model
const EVENT_AFTER_DELETE = 'afterDelete';
/**
* Represents insert ActiveRecord operation. This constant is used for specifying set of atomic operations
* for particular scenario in the [[scenarios()]] method.
*/
const OP_INSERT = 'insert';
/**
* Represents update ActiveRecord operation. This constant is used for specifying set of atomic operations
* for particular scenario in the [[scenarios()]] method.
*/
const OP_UPDATE = 'update';
/**
* Represents delete ActiveRecord operation. This constant is used for specifying set of atomic operations
* for particular scenario in the [[scenarios()]] method.
*/
const OP_DELETE = 'delete';
/**
* @var array attribute values indexed by attribute names
*/
private $_attributes = array();
@ -664,10 +680,39 @@ class ActiveRecord extends Model
* @param array $attributes list of attributes that need to be saved. Defaults to null,
* meaning all attributes that are loaded from DB will be saved.
* @return boolean whether the attributes are valid and the record is inserted successfully.
* @throws \Exception in case insert failed.
*/
public function insert($runValidation = true, $attributes = null)
{
if ($runValidation && !$this->validate($attributes) || !$this->beforeSave(true)) {
if ($runValidation && !$this->validate($attributes)) {
return false;
}
$db = static::getDb();
$transaction = $this->isOperationAtomic(self::OP_INSERT) && $db->getTransaction() === null ? $db->beginTransaction() : null;
try {
$result = $this->insertInternal($attributes);
if ($transaction !== null) {
if ($result === false) {
$transaction->rollback();
} else {
$transaction->commit();
}
}
} catch (\Exception $e) {
if ($transaction !== null) {
$transaction->rollback();
}
throw $e;
}
return $result;
}
/**
* @see ActiveRecord::insert()
*/
private function insertInternal($attributes = null)
{
if (!$this->beforeSave(true)) {
return false;
}
$values = $this->getDirtyAttributes($attributes);
@ -678,7 +723,9 @@ class ActiveRecord extends Model
}
$db = static::getDb();
$command = $db->createCommand()->insert($this->tableName(), $values);
if ($command->execute()) {
if (!$command->execute()) {
return false;
}
$table = $this->getTableSchema();
if ($table->sequenceName !== null) {
foreach ($table->primaryKey as $name) {
@ -694,7 +741,6 @@ class ActiveRecord extends Model
$this->afterSave(true);
return true;
}
}
/**
* Saves the changes to this active record into the associated database table.
@ -744,14 +790,46 @@ class ActiveRecord extends Model
* or [[beforeSave()]] stops the updating process.
* @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data
* being updated is outdated.
* @throws \Exception in case update failed.
*/
public function update($runValidation = true, $attributes = null)
{
if ($runValidation && !$this->validate($attributes) || !$this->beforeSave(false)) {
if ($runValidation && !$this->validate($attributes)) {
return false;
}
$db = static::getDb();
$transaction = $this->isOperationAtomic(self::OP_UPDATE) && $db->getTransaction() === null ? $db->beginTransaction() : null;
try {
$result = $this->updateInternal($attributes);
if ($transaction !== null) {
if ($result === false) {
$transaction->rollback();
} else {
$transaction->commit();
}
}
} catch (\Exception $e) {
if ($transaction !== null) {
$transaction->rollback();
}
throw $e;
}
return $result;
}
/**
* @see CActiveRecord::update()
* @throws StaleObjectException
*/
private function updateInternal($attributes = null)
{
if (!$this->beforeSave(false)) {
return false;
}
$values = $this->getDirtyAttributes($attributes);
if (!empty($values)) {
if (empty($values)) {
return 0;
}
$condition = $this->getOldPrimaryKey(true);
$lock = $this->optimisticLock();
if ($lock !== null) {
@ -771,12 +849,8 @@ class ActiveRecord extends Model
foreach ($values as $name => $value) {
$this->_oldAttributes[$name] = $this->_attributes[$name];
}
$this->afterSave(false);
return $rows;
} else {
return 0;
}
}
/**
@ -826,9 +900,14 @@ class ActiveRecord extends Model
* Note that it is possible the number of rows deleted is 0, even though the deletion execution is successful.
* @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data
* being deleted is outdated.
* @throws \Exception in case delete failed.
*/
public function delete()
{
$db = static::getDb();
$transaction = $this->isOperationAtomic(self::OP_DELETE) && $db->getTransaction() === null ? $db->beginTransaction() : null;
try {
$result = false;
if ($this->beforeDelete()) {
// we do not check the return value of deleteAll() because it's possible
// the record is already deleted in the database and thus the method will return 0
@ -837,16 +916,27 @@ class ActiveRecord extends Model
if ($lock !== null) {
$condition[$lock] = $this->$lock;
}
$rows = $this->deleteAll($condition);
if ($lock !== null && !$rows) {
$result = $this->deleteAll($condition);
if ($lock !== null && !$result) {
throw new StaleObjectException('The object being deleted is outdated.');
}
$this->_oldAttributes = null;
$this->afterDelete();
return $rows;
}
if ($transaction !== null) {
if ($result === false) {
$transaction->rollback();
} else {
return false;
$transaction->commit();
}
}
} catch (\Exception $e) {
if ($transaction !== null) {
$transaction->rollback();
}
throw $e;
}
return $result;
}
/**
@ -1336,4 +1426,19 @@ class ActiveRecord extends Model
}
return true;
}
/**
* @param string $operation possible values are ActiveRecord::INSERT, ActiveRecord::UPDATE and ActiveRecord::DELETE.
* @return boolean whether given operation is atomic. Currently active scenario is taken into account.
*/
private function isOperationAtomic($operation)
{
$scenario = $this->getScenario();
$scenarios = $this->scenarios();
if (isset($scenarios[$scenario], $scenario[$scenario]['atomic']) && is_array($scenarios[$scenario]['atomic'])) {
return in_array($operation, $scenarios[$scenario]['atomic']);
} else {
return false;
}
}
}

28
yii/db/Connection.php

@ -7,6 +7,8 @@
namespace yii\db;
use PDO;
use Yii;
use yii\base\Component;
use yii\base\InvalidConfigException;
use yii\base\NotSupportedException;
@ -128,7 +130,7 @@ class Connection extends Component
*/
public $attributes;
/**
* @var \PDO the PHP PDO instance associated with this DB connection.
* @var PDO the PHP PDO instance associated with this DB connection.
* This property is mainly managed by [[open()]] and [[close()]] methods.
* When a DB connection is active, this property will represent a PDO instance;
* otherwise, it will be null.
@ -229,7 +231,7 @@ class Connection extends Component
/**
* @var array mapping between PDO driver names and [[Schema]] classes.
* The keys of the array are PDO driver names while the values the corresponding
* schema class name or configuration. Please refer to [[\Yii::createObject()]] for
* schema class name or configuration. Please refer to [[Yii::createObject()]] for
* details on how to specify a configuration.
*
* This property is mainly used by [[getSchema()]] when fetching the database schema information.
@ -313,12 +315,12 @@ class Connection extends Component
throw new InvalidConfigException('Connection::dsn cannot be empty.');
}
try {
\Yii::trace('Opening DB connection: ' . $this->dsn, __METHOD__);
Yii::trace('Opening DB connection: ' . $this->dsn, __METHOD__);
$this->pdo = $this->createPdoInstance();
$this->initConnection();
}
catch (\PDOException $e) {
\Yii::error("Failed to open DB connection ({$this->dsn}): " . $e->getMessage(), __METHOD__);
Yii::error("Failed to open DB connection ({$this->dsn}): " . $e->getMessage(), __METHOD__);
$message = YII_DEBUG ? 'Failed to open DB connection: ' . $e->getMessage() : 'Failed to open DB connection.';
throw new Exception($message, $e->errorInfo, (int)$e->getCode());
}
@ -332,7 +334,7 @@ class Connection extends Component
public function close()
{
if ($this->pdo !== null) {
\Yii::trace('Closing DB connection: ' . $this->dsn, __METHOD__);
Yii::trace('Closing DB connection: ' . $this->dsn, __METHOD__);
$this->pdo = null;
$this->_schema = null;
$this->_transaction = null;
@ -344,11 +346,11 @@ class Connection extends Component
* This method is called by [[open]] to establish a DB connection.
* The default implementation will create a PHP PDO instance.
* You may override this method if the default PDO needs to be adapted for certain DBMS.
* @return \PDO the pdo instance
* @return PDO the pdo instance
*/
protected function createPdoInstance()
{
$pdoClass = '\PDO';
$pdoClass = 'PDO';
if (($pos = strpos($this->dsn, ':')) !== false) {
$driver = strtolower(substr($this->dsn, 0, $pos));
if ($driver === 'mssql' || $driver === 'dblib' || $driver === 'sqlsrv') {
@ -367,9 +369,9 @@ class Connection extends Component
*/
protected function initConnection()
{
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
if ($this->emulatePrepare !== null && constant('\PDO::ATTR_EMULATE_PREPARES')) {
$this->pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, $this->emulatePrepare);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
if ($this->emulatePrepare !== null && constant('PDO::ATTR_EMULATE_PREPARES')) {
$this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, $this->emulatePrepare);
}
if ($this->charset !== null && in_array($this->getDriverName(), array('pgsql', 'mysql', 'mysqli'))) {
$this->pdo->exec('SET NAMES ' . $this->pdo->quote($this->charset));
@ -428,7 +430,7 @@ class Connection extends Component
} else {
$driver = $this->getDriverName();
if (isset($this->schemaMap[$driver])) {
$this->_schema = \Yii::createObject($this->schemaMap[$driver]);
$this->_schema = Yii::createObject($this->schemaMap[$driver]);
$this->_schema->db = $this;
return $this->_schema;
} else {
@ -536,7 +538,7 @@ class Connection extends Component
if (($pos = strpos($this->dsn, ':')) !== false) {
return strtolower(substr($this->dsn, 0, $pos));
} else {
return strtolower($this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME));
return strtolower($this->pdo->getAttribute(PDO::ATTR_DRIVER_NAME));
}
}
@ -551,7 +553,7 @@ class Connection extends Component
*/
public function getQuerySummary()
{
$logger = \Yii::getLogger();
$logger = Yii::getLogger();
$timings = $logger->getProfiling(array('yii\db\Command::query', 'yii\db\Command::execute'));
$count = count($timings);
$time = 0;

2
yii/db/Exception.php

@ -39,6 +39,6 @@ class Exception extends \yii\base\Exception
*/
public function getName()
{
return \Yii::t('yii|Database Exception');
return \Yii::t('yii', 'Database Exception');
}
}

4
yii/db/Migration.php

@ -10,14 +10,14 @@ namespace yii\db;
/**
* Migration is the base class for representing a database migration.
*
* Migration is designed to be used together with the "yiic migrate" command.
* Migration is designed to be used together with the "yii migrate" command.
*
* Each child class of Migration represents an individual database migration which
* is identified by the child class name.
*
* Within each migration, the [[up()]] method should be overwritten to contain the logic
* for "upgrading" the database; while the [[down()]] method for the "downgrading"
* logic. The "yiic migrate" command manages all available migrations in an application.
* logic. The "yii migrate" command manages all available migrations in an application.
*
* If the database supports transactions, you may also overwrite [[safeUp()]] and
* [[safeDown()]] so that if anything wrong happens during the upgrading or downgrading,

2
yii/db/StaleObjectException.php

@ -18,6 +18,6 @@ class StaleObjectException extends Exception
*/
public function getName()
{
return \Yii::t('yii|Stale Object Exception');
return \Yii::t('yii', 'Stale Object Exception');
}
}

10
yii/debug/Toolbar.php

@ -17,18 +17,18 @@ use yii\helpers\Html;
*/
class Toolbar extends Widget
{
public $debugAction = 'debug';
public $enabled = YII_DEBUG;
public $debugAction = 'debug/default/toolbar';
public function run()
{
if ($this->enabled) {
if (Yii::$app->hasModule('debug')) {
$id = 'yii-debug-toolbar';
$url = Yii::$app->getUrlManager()->createUrl($this->debugAction, array(
'tag' => Yii::getLogger()->tag,
));
$this->view->registerJs("yii.debug.load('$id', '$url');");
$this->view->registerAssetBundle('yii/debug');
$view = $this->getView();
$view->registerJs("yii.debug.load('$id', '$url');");
$view->registerAssetBundle('yii/debug');
echo Html::tag('div', '', array(
'id' => $id,
'style' => 'display: none',

12
yii/debug/controllers/DefaultController.php

@ -7,6 +7,7 @@
namespace yii\debug\controllers;
use Yii;
use yii\web\Controller;
/**
@ -19,4 +20,15 @@ class DefaultController extends Controller
{
echo $tag;
}
public function actionToolbar($tag)
{
$file = Yii::$app->getRuntimePath() . "/debug/$tag.log";
if (preg_match('/^[\w\-]+$/', $tag) && is_file($file)) {
$data = json_decode(file_get_contents($file), true);
echo $this->renderPartial('toolbar', $data);
} else {
echo "Unable to find debug data tagged with '$tag'.";
}
}
}

39
yii/debug/views/default/toolbar.php

@ -0,0 +1,39 @@
<?php use yii\helpers\Html;
echo Html::style("
#yii-debug-toolbar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background-color: #eee;
border-top: 1px solid #ccc;
margin: 0;
padding: 5px 10px;
z-index: 1000000;
font: 11px Verdana, Arial, sans-serif;
text-align: left;
}
.yii-debug-toolbar-block {
float: left;
margin: 0 10px;
");
?>
<div class="yii-debug-toolbar-block">
</div>
<div class="yii-debug-toolbar-block">
Peak memory: <?php echo sprintf('%.2fMB', $memory / 1048576); ?>
</div>
<div class="yii-debug-toolbar-block">
Time spent: <?php echo sprintf('%.3fs', $time); ?>
</div>
<div class="yii-debug-toolbar-block">
</div>
<div class="yii-debug-toolbar-block">
</div>

18
yii/helpers/base/ArrayHelper.php

@ -263,8 +263,8 @@ class ArrayHelper
* elements, a property name of the objects, or an anonymous function returning the values for comparison
* purpose. The anonymous function signature should be: `function($item)`.
* To sort by multiple keys, provide an array of keys here.
* @param boolean|array $ascending whether to sort in ascending or descending order. When
* sorting by multiple keys with different ascending orders, use an array of ascending flags.
* @param boolean|array $descending whether to sort in descending or ascending order. When
* sorting by multiple keys with different descending orders, use an array of descending flags.
* @param integer|array $sortFlag the PHP sort flag. Valid values include
* `SORT_REGULAR`, `SORT_NUMERIC`, `SORT_STRING` and `SORT_LOCALE_STRING`.
* Please refer to [PHP manual](http://php.net/manual/en/function.sort.php)
@ -272,20 +272,20 @@ class ArrayHelper
* @param boolean|array $caseSensitive whether to sort string in case-sensitive manner. This parameter
* is used only when `$sortFlag` is `SORT_STRING`.
* When sorting by multiple keys with different case sensitivities, use an array of boolean values.
* @throws InvalidParamException if the $ascending or $sortFlag parameters do not have
* @throws InvalidParamException if the $descending or $sortFlag parameters do not have
* correct number of elements as that of $key.
*/
public static function multisort(&$array, $key, $ascending = true, $sortFlag = SORT_REGULAR, $caseSensitive = true)
public static function multisort(&$array, $key, $descending = false, $sortFlag = SORT_REGULAR, $caseSensitive = true)
{
$keys = is_array($key) ? $key : array($key);
if (empty($keys) || empty($array)) {
return;
}
$n = count($keys);
if (is_scalar($ascending)) {
$ascending = array_fill(0, $n, $ascending);
} elseif (count($ascending) !== $n) {
throw new InvalidParamException('The length of $ascending parameter must be the same as that of $keys.');
if (is_scalar($descending)) {
$descending = array_fill(0, $n, $descending);
} elseif (count($descending) !== $n) {
throw new InvalidParamException('The length of $descending parameter must be the same as that of $keys.');
}
if (is_scalar($sortFlag)) {
$sortFlag = array_fill(0, $n, $sortFlag);
@ -315,7 +315,7 @@ class ArrayHelper
} else {
$args[] = static::getColumn($array, $key);
}
$args[] = $ascending[$i] ? SORT_ASC : SORT_DESC;
$args[] = $descending[$i] ? SORT_DESC : SORT_ASC;
$args[] = $flag;
}
$args[] = &$array;

11
yii/i18n/I18N.php

@ -75,26 +75,19 @@ class I18N extends Component
* Translates a message to the specified language.
* If the first parameter in `$params` is a number and it is indexed by 0, appropriate plural rules
* will be applied to the translated message.
* @param string $category the message category.
* @param string $message the message to be translated.
* @param array $params the parameters that will be used to replace the corresponding placeholders in the message.
* @param string $language the language code (e.g. `en_US`, `en`). If this is null, the current
* [[\yii\base\Application::language|application language]] will be used.
* @return string the translated message.
*/
public function translate($message, $params = array(), $language = null)
public function translate($category, $message, $params = array(), $language = null)
{
if ($language === null) {
$language = Yii::$app->language;
}
// allow chars for category: word chars, ".", "-", "/", "\"
if (strpos($message, '|') !== false && preg_match('/^([\w\-\\/\.\\\\]+)\|(.*)/', $message, $matches)) {
$category = $matches[1];
$message = $matches[2];
} else {
$category = 'app';
}
$message = $this->getMessageSource($category)->translate($category, $message, $language);
if (!is_array($params)) {

2
yii/i18n/data/plurals.php

@ -2,7 +2,7 @@
/**
* Plural rules.
*
* This file is automatically generated by the "yiic locale/plural" command under the "build" folder.
* This file is automatically generated by the "yii locale/plural" command under the "build" folder.
* Do not modify it directly.
*
* The original plural rule data used for generating this file has the following copyright terms:

91
yii/logging/DebugTarget.php

@ -0,0 +1,91 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\logging;
use Yii;
/**
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class DebugTarget extends Target
{
public $maxLogFiles = 20;
/**
* Exports log messages to a specific destination.
* Child classes must implement this method.
* @param array $messages the messages to be exported. See [[Logger::messages]] for the structure
* of each message.
*/
public function export($messages)
{
$path = Yii::$app->getRuntimePath() . '/debug';
if (!is_dir($path)) {
mkdir($path);
}
$file = $path . '/' . Yii::getLogger()->getTag() . '.log';
$data = array(
'messages' => $messages,
'_SERVER' => $_SERVER,
'_GET' => $_GET,
'_POST' => $_POST,
'_COOKIE' => $_COOKIE,
'_FILES' => empty($_FILES) ? array() : $_FILES,
'_SESSION' => empty($_SESSION) ? array() : $_SESSION,
'memory' => memory_get_peak_usage(),
'time' => microtime(true) - YII_BEGIN_TIME,
);
file_put_contents($file, json_encode($data));
}
/**
* Processes the given log messages.
* This method will filter the given messages with [[levels]] and [[categories]].
* And if requested, it will also export the filtering result to specific medium (e.g. email).
* @param array $messages log messages to be processed. See [[Logger::messages]] for the structure
* of each message.
* @param boolean $final whether this method is called at the end of the current application
*/
public function collect($messages, $final)
{
if (Yii::$app->getModule('debug', false) !== null) {
return;
}
$this->messages = array_merge($this->messages, $this->filterMessages($messages));
if ($final) {
$this->export($this->messages);
$this->gc();
}
}
protected function gc()
{
if (mt_rand(0, 10000) > 100) {
return;
}
$iterator = new \DirectoryIterator(Yii::$app->getRuntimePath() . '/debug');
$files = array();
foreach ($iterator as $file) {
if (preg_match('/^[\d\-]+\.log$/', $file->getFileName()) && $file->isFile()) {
$files[] = $file->getPathname();
}
}
sort($files);
if (count($files) > $this->maxLogFiles) {
$n = count($files) - $this->maxLogFiles;
foreach ($files as $i => $file) {
if ($i < $n) {
unlink($file);
} else {
break;
}
}
}
}
}

2
yii/logging/EmailTarget.php

@ -48,7 +48,7 @@ class EmailTarget extends Target
$body .= $this->formatMessage($message);
}
$body = wordwrap($body, 70);
$subject = $this->subject === null ? \Yii::t('yii|Application Log') : $this->subject;
$subject = $this->subject === null ? \Yii::t('yii', 'Application Log') : $this->subject;
foreach ($this->emails as $email) {
$this->sendEmail($subject, $body, $email, $this->sentFrom, $this->headers);
}

28
yii/logging/Logger.php

@ -82,11 +82,13 @@ class Logger extends Component
* @var Router the log target router registered with this logger.
*/
public $router;
/**
* @var string a tag that uniquely identifies the current request. This can be used
* to differentiate the log messages for different requests.
* @var string
*/
public $tag;
private $_tag;
/**
* Initializes the logger by registering [[flush()]] as a shutdown function.
@ -94,7 +96,6 @@ class Logger extends Component
public function init()
{
parent::init();
$this->tag = date('Ymd-His', microtime(true));
register_shutdown_function(array($this, 'flush'), true);
}
@ -143,6 +144,25 @@ class Logger extends Component
}
/**
* @return string a tag that uniquely identifies the current request.
*/
public function getTag()
{
if ($this->_tag === null) {
$this->_tag = date('Ymd-His', microtime(true));
}
return $this->_tag;
}
/**
* @param string $tag a tag that uniquely identifies the current request.
*/
public function setTag($tag)
{
$this->_tag = $tag;
}
/**
* Returns the total elapsed time since the start of the current request.
* This method calculates the difference between now and the timestamp
* defined by constant `YII_BEGIN_TIME` which is evaluated at the beginning

6
yii/logging/ProfileTarget.php

@ -59,7 +59,7 @@ class CProfileLogRoute extends CWebLogRoute
if ($value === 'summary' || $value === 'callstack')
$this->_report = $value;
else
throw new CException(Yii::t('yii|CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".',
throw new CException(Yii::t('yii', 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".',
array('{report}' => $value)));
}
@ -106,7 +106,7 @@ class CProfileLogRoute extends CWebLogRoute
$results[$last[4]] = array($token, $delta, count($stack));
} else
{
throw new CException(Yii::t('yii|CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.',
throw new CException(Yii::t('yii', 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.',
array('{token}' => $token)));
}
}
@ -149,7 +149,7 @@ class CProfileLogRoute extends CWebLogRoute
else
$results[$token] = array($token, 1, $delta, $delta, $delta);
} else
throw new CException(Yii::t('yii|CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.',
throw new CException(Yii::t('yii', 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.',
array('{token}' => $token)));
}
}

21
yii/logging/Target.php

@ -7,6 +7,8 @@
namespace yii\logging;
use Yii;
use yii\base\Component;
use yii\base\InvalidConfigException;
/**
@ -25,7 +27,7 @@ use yii\base\InvalidConfigException;
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
abstract class Target extends \yii\base\Component
abstract class Target extends Component
{
/**
* @var boolean whether to enable this log target. Defaults to true.
@ -67,7 +69,7 @@ abstract class Target extends \yii\base\Component
/**
* @var array the messages that are retrieved from the logger so far by this log target.
*/
private $_messages = array();
public $messages = array();
private $_levels = 0;
@ -89,14 +91,14 @@ abstract class Target extends \yii\base\Component
*/
public function collect($messages, $final)
{
$this->_messages = array_merge($this->_messages, $this->filterMessages($messages));
$count = count($this->_messages);
$this->messages = array_merge($this->messages, $this->filterMessages($messages));
$count = count($this->messages);
if ($count > 0 && ($final || $this->exportInterval > 0 && $count >= $this->exportInterval)) {
if (($context = $this->getContextMessage()) !== '') {
$this->_messages[] = array($context, Logger::LEVEL_INFO, 'application', YII_BEGIN_TIME);
$this->messages[] = array($context, Logger::LEVEL_INFO, 'application', YII_BEGIN_TIME);
}
$this->export($this->_messages);
$this->_messages = array();
$this->export($this->messages);
$this->messages = array();
}
}
@ -108,8 +110,9 @@ abstract class Target extends \yii\base\Component
protected function getContextMessage()
{
$context = array();
if ($this->logUser && ($user = \Yii::$app->getComponent('user', false)) !== null) {
$context[] = 'User: ' . $user->getName() . ' (ID: ' . $user->getId() . ')';
if ($this->logUser && ($user = Yii::$app->getComponent('user', false)) !== null) {
/** @var $user \yii\web\User */
$context[] = 'User: ' . $user->getId();
}
foreach ($this->logVars as $name) {

61
yii/logging/WebTarget.php

@ -1,61 +0,0 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008-2011 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
/**
* CWebLogRoute shows the log content in Web page.
*
* The log content can appear either at the end of the current Web page
* or in FireBug console window (if {@link showInFireBug} is set true).
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class CWebLogRoute extends CLogRoute
{
/**
* @var boolean whether the log should be displayed in FireBug instead of browser window. Defaults to false.
*/
public $showInFireBug = false;
/**
* @var boolean whether the log should be ignored in FireBug for ajax calls. Defaults to true.
* This option should be used carefully, because an ajax call returns all output as a result data.
* For example if the ajax call expects a json type result any output from the logger will cause ajax call to fail.
*/
public $ignoreAjaxInFireBug = true;
/**
* Displays the log messages.
* @param array $logs list of log messages
*/
public function processLogs($logs)
{
$this->render('log', $logs);
}
/**
* Renders the view.
* @param string $view the view name (file name without extension). The file is assumed to be located under framework/data/views.
* @param array $data data to be passed to the view
*/
protected function render($view, $data)
{
$app = \Yii::$app;
$isAjax = $app->getRequest()->getIsAjaxRequest();
if ($this->showInFireBug)
{
if ($isAjax && $this->ignoreAjaxInFireBug)
return;
$view .= '-firebug';
} elseif (!($app instanceof CWebApplication) || $isAjax)
return;
$viewFile = YII_PATH . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . $view . '.php';
include($app->findLocalizedFile($viewFile, 'en'));
}
}

83
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 <qiang.xue@gmail.com>
* @author Alexander Kochetov <creocoder@gmail.com>
* @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]])
*/
public function getUserId()
{
return $this->_userId;
}
/**
* @return string the authorization item name
* @var Manager the auth manager of this item
*/
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);
}
}

92
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 <qiang.xue@gmail.com>
* @author Alexander Kochetov <creocoder@gmail.com>
* @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,
));

144
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 <qiang.xue@gmail.com>
* @author Alexander Kochetov <creocoder@gmail.com>
* @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);
}
}

2
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]}'.");
}
}

58
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,7 +461,13 @@ 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'],
));
}
}
}

7
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

7
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

7
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

7
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

7
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

4
yii/requirements/requirements.php

@ -7,9 +7,9 @@ return array(
array(
'name' => 'PHP version',
'mandatory' => true,
'condition' => version_compare(PHP_VERSION, '5.3.0', '>='),
'condition' => version_compare(PHP_VERSION, '5.3.3', '>='),
'by' => '<a href="http://www.yiiframework.com">Yii Framework</a>',
'memo' => 'PHP 5.3.0 or higher is required.',
'memo' => 'PHP 5.3.3 or higher is required.',
),
array(
'name' => 'Reflection extension',

6188
yii/requirements/views/web/css.php

File diff suppressed because it is too large Load Diff

62
yii/requirements/views/web/index.php

@ -3,26 +3,22 @@
/* @var $summary array */
/* @var $requirements array[] */
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="content-language" content="en"/>
<style type="text/css">
<?php $this->renderViewFile(dirname(__FILE__).DIRECTORY_SEPARATOR.'css.php'); ?>
</style>
<meta charset="utf-8"/>
<title>Yii Application Requirement Checker</title>
<?php $this->renderViewFile(dirname(__FILE__).DIRECTORY_SEPARATOR.'css.php'); ?>
</head>
<body>
<div id="page">
<div id="header">
<div class="container">
<div class="header">
<h1>Yii Application Requirement Checker</h1>
</div><!-- header-->
</div>
<hr>
<div id="content">
<h2>Description</h2>
<div class="content">
<h3>Description</h3>
<p>
This script checks if your server configuration meets the requirements
for running Yii application.
@ -30,27 +26,25 @@ 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.
</p>
<h2>Conclusion</h2>
<p>
<h3>Conclusion</h3>
<?php if ($summary['errors']>0): ?>
Unfortunately your server configuration does not satisfy the requirements by this application.
<strong class="text-error">Unfortunately your server configuration does not satisfy the requirements by this application.</strong>
<?php elseif ($summary['warnings']>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.
<strong class="text-warning">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.</strong>
<?php else: ?>
Congratulations! Your server configuration satisfies all requirements.
<strong class="text-success">Congratulations! Your server configuration satisfies all requirements.</strong>
<?php endif; ?>
</p>
<h2>Details</h2>
<h3>Details</h3>
<table class="result">
<table class="table table-bordered">
<tr><th>Name</th><th>Result</th><th>Required By</th><th>Memo</th></tr>
<?php foreach($requirements as $requirement): ?>
<tr>
<tr class="<?php echo $requirement['condition'] ? 'success' : ($requirement['mandatory'] ? 'error' : 'warning'); ?>">
<td>
<?php echo $requirement['name']; ?>
</td>
<td class="<?php echo $requirement['condition'] ? 'passed' : ($requirement['mandatory'] ? 'failed' : 'warning'); ?>">
<td >
<?php echo $requirement['condition'] ? 'Passed' : ($requirement['mandatory'] ? 'Failed' : 'Warning'); ?>
</td>
<td>
@ -63,20 +57,14 @@ Congratulations! Your server configuration satisfies all requirements.
<?php endforeach; ?>
</table>
<table>
<tr>
<td class="passed">&nbsp;</td><td>passed</td>
<td class="failed">&nbsp;</td><td>failed</td>
<td class="warning">&nbsp;</td><td>warning</td>
</tr>
</table>
</div><!-- content -->
</div>
<div id="footer">
<?php echo $this->getServerInfo().' '.$this->getNowDate(); ?>
</div><!-- footer -->
<hr>
</div><!-- page -->
<div class="footer">
<p>Server: <?php echo $this->getServerInfo().' '.$this->getNowDate(); ?></p>
<p>Powered by <a href="http://www.yiiframework.com/" rel="external">Yii Framework</a></p>
</div>
</div>
</body>
</html>

7
yii/validators/BooleanValidator.php

@ -43,7 +43,7 @@ class BooleanValidator extends Validator
{
parent::init();
if ($this->message === null) {
$this->message = Yii::t('yii|{attribute} must be either "{true}" or "{false}".');
$this->message = Yii::t('yii', '{attribute} must be either "{true}" or "{false}".');
}
}
@ -79,9 +79,11 @@ class BooleanValidator extends Validator
* Returns the JavaScript needed for performing client-side validation.
* @param \yii\base\Model $object the data object being validated
* @param string $attribute the name of the attribute to be validated.
* @param \yii\base\View $view the view object that is going to be used to render views or view files
* containing a model form with this validator applied.
* @return string the client-side validation script.
*/
public function clientValidateAttribute($object, $attribute)
public function clientValidateAttribute($object, $attribute, $view)
{
$options = array(
'trueValue' => $this->trueValue,
@ -100,6 +102,7 @@ class BooleanValidator extends Validator
$options['strict'] = 1;
}
$view->registerAssetBundle('yii/validation');
return 'yii.validation.boolean(value, messages, ' . json_encode($options) . ');';
}
}

7
yii/validators/CaptchaValidator.php

@ -39,7 +39,7 @@ class CaptchaValidator extends Validator
{
parent::init();
if ($this->message === null) {
$this->message = Yii::t('yii|The verification code is incorrect.');
$this->message = Yii::t('yii', 'The verification code is incorrect.');
}
}
@ -91,9 +91,11 @@ class CaptchaValidator extends Validator
* Returns the JavaScript needed for performing client-side validation.
* @param \yii\base\Model $object the data object being validated
* @param string $attribute the name of the attribute to be validated.
* @param \yii\base\View $view the view object that is going to be used to render views or view files
* containing a model form with this validator applied.
* @return string the client-side validation script.
*/
public function clientValidateAttribute($object, $attribute)
public function clientValidateAttribute($object, $attribute, $view)
{
$captcha = $this->getCaptchaAction();
$code = $captcha->getVerifyCode(false);
@ -111,6 +113,7 @@ class CaptchaValidator extends Validator
$options['skipOnEmpty'] = 1;
}
$view->registerAssetBundle('yii/validation');
return 'yii.validation.captcha(value, messages, ' . json_encode($options) . ');';
}
}

23
yii/validators/CompareValidator.php

@ -79,28 +79,28 @@ class CompareValidator extends Validator
if ($this->message === null) {
switch ($this->operator) {
case '==':
$this->message = Yii::t('yii|{attribute} must be repeated exactly.');
$this->message = Yii::t('yii', '{attribute} must be repeated exactly.');
break;
case '===':
$this->message = Yii::t('yii|{attribute} must be repeated exactly.');
$this->message = Yii::t('yii', '{attribute} must be repeated exactly.');
break;
case '!=':
$this->message = Yii::t('yii|{attribute} must not be equal to "{compareValue}".');
$this->message = Yii::t('yii', '{attribute} must not be equal to "{compareValue}".');
break;
case '!==':
$this->message = Yii::t('yii|{attribute} must not be equal to "{compareValue}".');
$this->message = Yii::t('yii', '{attribute} must not be equal to "{compareValue}".');
break;
case '>':
$this->message = Yii::t('yii|{attribute} must be greater than "{compareValue}".');
$this->message = Yii::t('yii', '{attribute} must be greater than "{compareValue}".');
break;
case '>=':
$this->message = Yii::t('yii|{attribute} must be greater than or equal to "{compareValue}".');
$this->message = Yii::t('yii', '{attribute} must be greater than or equal to "{compareValue}".');
break;
case '<':
$this->message = Yii::t('yii|{attribute} must be less than "{compareValue}".');
$this->message = Yii::t('yii', '{attribute} must be less than "{compareValue}".');
break;
case '<=':
$this->message = Yii::t('yii|{attribute} must be less than or equal to "{compareValue}".');
$this->message = Yii::t('yii', '{attribute} must be less than or equal to "{compareValue}".');
break;
default:
throw new InvalidConfigException("Unknown operator: {$this->operator}");
@ -119,7 +119,7 @@ class CompareValidator extends Validator
{
$value = $object->$attribute;
if (is_array($value)) {
$this->addError($object, $attribute, Yii::t('yii|{attribute} is invalid.'));
$this->addError($object, $attribute, Yii::t('yii', '{attribute} is invalid.'));
return;
}
if ($this->compareValue !== null) {
@ -178,9 +178,11 @@ class CompareValidator extends Validator
* @param \yii\base\Model $object the data object being validated
* @param string $attribute the name of the attribute to be validated
* @return string the client-side validation script
* @param \yii\base\View $view the view object that is going to be used to render views or view files
* containing a model form with this validator applied.
* @throws InvalidConfigException if CompareValidator::operator is invalid
*/
public function clientValidateAttribute($object, $attribute)
public function clientValidateAttribute($object, $attribute, $view)
{
$options = array('operator' => $this->operator);
@ -203,6 +205,7 @@ class CompareValidator extends Validator
'{compareValue}' => $compareValue,
)));
$view->registerAssetBundle('yii/validation');
return 'yii.validation.compare(value, messages, ' . json_encode($options) . ');';
}
}

2
yii/validators/DateValidator.php

@ -38,7 +38,7 @@ class DateValidator extends Validator
{
parent::init();
if ($this->message === null) {
$this->message = Yii::t('yii|The format of {attribute} is invalid.');
$this->message = Yii::t('yii', 'The format of {attribute} is invalid.');
}
}

31
yii/validators/EmailValidator.php

@ -47,6 +47,12 @@ class EmailValidator extends Validator
* Defaults to false.
*/
public $checkPort = false;
/**
* @var boolean whether validation process should take into account IDN (internationalized domain
* names). Defaults to false meaning that validation of emails containing IDN will always fail.
*/
public $enableIDN = false;
/**
* Initializes the validator.
@ -55,7 +61,7 @@ class EmailValidator extends Validator
{
parent::init();
if ($this->message === null) {
$this->message = Yii::t('yii|{attribute} is not a valid email address.');
$this->message = Yii::t('yii', '{attribute} is not a valid email address.');
}
}
@ -81,10 +87,18 @@ class EmailValidator extends Validator
public function validateValue($value)
{
// make sure string length is limited to avoid DOS attacks
$valid = is_string($value) && strlen($value) <= 254
&& (preg_match($this->pattern, $value) || $this->allowName && preg_match($this->fullPattern, $value));
if (!is_string($value) || strlen($value) >= 255) {
return false;
}
if (($atPosition = strpos($value, '@')) === false) {
return false;
}
$domain = rtrim(substr($value, $atPosition + 1), '>');
if ($this->enableIDN) {
$value = idn_to_ascii(ltrim(substr($value, 0, $atPosition), '<')) . '@' . idn_to_ascii($domain);
}
$valid = preg_match($this->pattern, $value) || $this->allowName && preg_match($this->fullPattern, $value);
if ($valid) {
$domain = rtrim(substr($value, strpos($value, '@') + 1), '>');
if ($this->checkMX && function_exists('checkdnsrr')) {
$valid = checkdnsrr($domain, 'MX');
}
@ -99,9 +113,11 @@ class EmailValidator extends Validator
* Returns the JavaScript needed for performing client-side validation.
* @param \yii\base\Model $object the data object being validated
* @param string $attribute the name of the attribute to be validated.
* @param \yii\base\View $view the view object that is going to be used to render views or view files
* containing a model form with this validator applied.
* @return string the client-side validation script.
*/
public function clientValidateAttribute($object, $attribute)
public function clientValidateAttribute($object, $attribute, $view)
{
$options = array(
'pattern' => new JsExpression($this->pattern),
@ -111,11 +127,16 @@ class EmailValidator extends Validator
'{attribute}' => $object->getAttributeLabel($attribute),
'{value}' => $object->$attribute,
))),
'enableIDN' => (boolean)$this->enableIDN,
);
if ($this->skipOnEmpty) {
$options['skipOnEmpty'] = 1;
}
$view->registerAssetBundle('yii/validation');
if ($this->enableIDN) {
$view->registerAssetBundle('punycode');
}
return 'yii.validation.email(value, messages, ' . Json::encode($options) . ');';
}
}

2
yii/validators/ExistValidator.php

@ -45,7 +45,7 @@ class ExistValidator extends Validator
{
parent::init();
if ($this->message === null) {
$this->message = Yii::t('yii|{attribute} is invalid.');
$this->message = Yii::t('yii', '{attribute} is invalid.');
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save