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. 21
      yii/YiiBase.php
  28. 8
      yii/assets.php
  29. 2
      yii/assets/yii.activeForm.js
  30. 4
      yii/assets/yii.captcha.js
  31. 2
      yii/assets/yii.debug.js
  32. 28
      yii/assets/yii.validation.js
  33. 3
      yii/base/Controller.php
  34. 297
      yii/base/Dictionary.php
  35. 92
      yii/base/DictionaryIterator.php
  36. 28
      yii/base/ErrorException.php
  37. 7
      yii/base/ErrorHandler.php
  38. 2
      yii/base/Exception.php
  39. 2
      yii/base/HttpException.php
  40. 2
      yii/base/InvalidCallException.php
  41. 2
      yii/base/InvalidConfigException.php
  42. 2
      yii/base/InvalidParamException.php
  43. 2
      yii/base/InvalidRequestException.php
  44. 2
      yii/base/InvalidRouteException.php
  45. 63
      yii/base/Model.php
  46. 2
      yii/base/Module.php
  47. 2
      yii/base/NotSupportedException.php
  48. 17
      yii/base/Theme.php
  49. 2
      yii/base/UnknownClassException.php
  50. 2
      yii/base/UnknownMethodException.php
  51. 2
      yii/base/UnknownPropertyException.php
  52. 341
      yii/base/Vector.php
  53. 92
      yii/base/VectorIterator.php
  54. 114
      yii/base/View.php
  55. 101
      yii/base/Widget.php
  56. 105
      yii/behaviors/AutoTimestamp.php
  57. 8
      yii/console/Application.php
  58. 8
      yii/console/Controller.php
  59. 2
      yii/console/Exception.php
  60. 2
      yii/console/Request.php
  61. 2
      yii/console/controllers/AssetController.php
  62. 24
      yii/console/controllers/CacheController.php
  63. 18
      yii/console/controllers/HelpController.php
  64. 2
      yii/console/controllers/MessageController.php
  65. 40
      yii/console/controllers/MigrateController.php
  66. 205
      yii/db/ActiveRecord.php
  67. 28
      yii/db/Connection.php
  68. 2
      yii/db/Exception.php
  69. 4
      yii/db/Migration.php
  70. 2
      yii/db/StaleObjectException.php
  71. 10
      yii/debug/Toolbar.php
  72. 12
      yii/debug/controllers/DefaultController.php
  73. 39
      yii/debug/views/default/toolbar.php
  74. 18
      yii/helpers/base/ArrayHelper.php
  75. 11
      yii/i18n/I18N.php
  76. 2
      yii/i18n/data/plurals.php
  77. 91
      yii/logging/DebugTarget.php
  78. 2
      yii/logging/EmailTarget.php
  79. 28
      yii/logging/Logger.php
  80. 6
      yii/logging/ProfileTarget.php
  81. 21
      yii/logging/Target.php
  82. 61
      yii/logging/WebTarget.php
  83. 83
      yii/rbac/Assignment.php
  84. 92
      yii/rbac/DbManager.php
  85. 144
      yii/rbac/Item.php
  86. 2
      yii/rbac/Manager.php
  87. 60
      yii/rbac/PhpManager.php
  88. 7
      yii/rbac/schema-mssql.sql
  89. 7
      yii/rbac/schema-mysql.sql
  90. 7
      yii/rbac/schema-oci.sql
  91. 7
      yii/rbac/schema-pgsql.sql
  92. 7
      yii/rbac/schema-sqlite.sql
  93. 4
      yii/requirements/requirements.php
  94. 6196
      yii/requirements/views/web/css.php
  95. 118
      yii/requirements/views/web/index.php
  96. 7
      yii/validators/BooleanValidator.php
  97. 7
      yii/validators/CaptchaValidator.php
  98. 23
      yii/validators/CompareValidator.php
  99. 2
      yii/validators/DateValidator.php
  100. 31
      yii/validators/EmailValidator.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.

21
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])) {
self::$aliases[$root] = $path;
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',
),
),
);

2
yii/assets/yii.activeForm.js

@ -57,7 +57,7 @@
// whether to perform validation when a change is detected on the input
validateOnChange: false,
// whether to perform validation when the user is typing.
validateOnType: false,
validateOnType: false,
// number of milliseconds that the validation should be delayed when a user is typing in the input field.
validationDelay: 200,
// whether to enable AJAX-based validation.

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 (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) {
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');
}
}

63
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,18 +592,22 @@ 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])) {
foreach ($scenarios[$scenario] as $attribute) {
if ($attribute[0] !== '!') {
$attributes[] = $attribute;
}
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;
@ -609,34 +615,37 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess
/**
* 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()];
foreach ($attributes as $i => $attribute) {
if ($attribute[0] === '!') {
$attributes[$i] = substr($attribute, 1);
}
}
return $attributes;
} else {
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;
}
/**
* 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();
/**
* 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
*/
public static function begin($config = array())
{
$config['class'] = get_called_class();
/** @var Widget $widget */
$widget = Yii::createObject($config);
self::$_stack[] = $widget;
return $widget;
}
/**
* @var integer a counter used to generate IDs for widgets.
* 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
*/
private static $_counter = 0;
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

205
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,22 +723,23 @@ class ActiveRecord extends Model
}
$db = static::getDb();
$command = $db->createCommand()->insert($this->tableName(), $values);
if ($command->execute()) {
$table = $this->getTableSchema();
if ($table->sequenceName !== null) {
foreach ($table->primaryKey as $name) {
if (!isset($this->_attributes[$name])) {
$this->_oldAttributes[$name] = $this->_attributes[$name] = $db->getLastInsertID($table->sequenceName);
break;
}
if (!$command->execute()) {
return false;
}
$table = $this->getTableSchema();
if ($table->sequenceName !== null) {
foreach ($table->primaryKey as $name) {
if (!isset($this->_attributes[$name])) {
$this->_oldAttributes[$name] = $this->_attributes[$name] = $db->getLastInsertID($table->sequenceName);
break;
}
}
foreach ($values as $name => $value) {
$this->_oldAttributes[$name] = $value;
}
$this->afterSave(true);
return true;
}
foreach ($values as $name => $value) {
$this->_oldAttributes[$name] = $value;
}
$this->afterSave(true);
return true;
}
/**
@ -744,39 +790,67 @@ 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;
}
$values = $this->getDirtyAttributes($attributes);
if (!empty($values)) {
$condition = $this->getOldPrimaryKey(true);
$lock = $this->optimisticLock();
if ($lock !== null) {
if (!isset($values[$lock])) {
$values[$lock] = $this->$lock + 1;
$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();
}
$condition[$lock] = $this->$lock;
}
// We do not check the return value of updateAll() because it's possible
// that the UPDATE statement doesn't change anything and thus returns 0.
$rows = $this->updateAll($values, $condition);
if ($lock !== null && !$rows) {
throw new StaleObjectException('The object being updated is outdated.');
} catch (\Exception $e) {
if ($transaction !== null) {
$transaction->rollback();
}
throw $e;
}
return $result;
}
foreach ($values as $name => $value) {
$this->_oldAttributes[$name] = $this->_attributes[$name];
/**
* @see CActiveRecord::update()
* @throws StaleObjectException
*/
private function updateInternal($attributes = null)
{
if (!$this->beforeSave(false)) {
return false;
}
$values = $this->getDirtyAttributes($attributes);
if (empty($values)) {
return 0;
}
$condition = $this->getOldPrimaryKey(true);
$lock = $this->optimisticLock();
if ($lock !== null) {
if (!isset($values[$lock])) {
$values[$lock] = $this->$lock + 1;
}
$condition[$lock] = $this->$lock;
}
// We do not check the return value of updateAll() because it's possible
// that the UPDATE statement doesn't change anything and thus returns 0.
$rows = $this->updateAll($values, $condition);
$this->afterSave(false);
return $rows;
} else {
return 0;
if ($lock !== null && !$rows) {
throw new StaleObjectException('The object being updated is outdated.');
}
foreach ($values as $name => $value) {
$this->_oldAttributes[$name] = $this->_attributes[$name];
}
$this->afterSave(false);
return $rows;
}
/**
@ -826,27 +900,43 @@ 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()
{
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
$condition = $this->getOldPrimaryKey(true);
$lock = $this->optimisticLock();
if ($lock !== null) {
$condition[$lock] = $this->$lock;
$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
$condition = $this->getOldPrimaryKey(true);
$lock = $this->optimisticLock();
if ($lock !== null) {
$condition[$lock] = $this->$lock;
}
$result = $this->deleteAll($condition);
if ($lock !== null && !$result) {
throw new StaleObjectException('The object being deleted is outdated.');
}
$this->_oldAttributes = null;
$this->afterDelete();
}
$rows = $this->deleteAll($condition);
if ($lock !== null && !$rows) {
throw new StaleObjectException('The object being deleted is outdated.');
if ($transaction !== null) {
if ($result === false) {
$transaction->rollback();
} else {
$transaction->commit();
}
}
$this->_oldAttributes = null;
$this->afterDelete();
return $rows;
} else {
return false;
} 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]])
* @var Manager the auth manager of this item
*/
public function getUserId()
{
return $this->_userId;
}
/**
* @return string the authorization item name
*/
public function getItemName()
{
return $this->_itemName;
}
public $manager;
/**
* @return string the business rule associated with this assignment
* @var string the business rule associated with this assignment
*/
public function getBizRule()
{
return $this->_bizRule;
}
public $bizRule;
/**
* @param string $value the business rule associated with this assignment
* @var mixed additional data for this assignment
*/
public function setBizRule($value)
{
if ($this->_bizRule !== $value) {
$this->_bizRule = $value;
}
}
public $data;
/**
* @return mixed additional data for this assignment
* @var mixed user ID (see [[User::id]]). Do not modify this property after it is populated.
* To modify the user ID of an assignment, you must remove the assignment and create a new one.
*/
public function getData()
{
return $this->_data;
}
public $userId;
/**
* @param mixed $value additional data for this assignment
* @return string the authorization item name. Do not modify this property after it is populated.
* To modify the item name of an assignment, you must remove the assignment and create a new one.
*/
public function setData($value)
{
if ($this->_data !== $value) {
$this->_data = $value;
}
}
public $itemName;
/**
* Saves the changes to an authorization assignment.
*/
public function save()
{
$this->_auth->saveAssignment($this);
$this->manager->saveAssignment($this);
}
}

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

60
yii/rbac/PhpManager.php

@ -80,14 +80,14 @@ class PhpManager extends Manager
if (!isset($params['userId'])) {
$params['userId'] = $userId;
}
if ($this->executeBizRule($item->getBizRule(), $params, $item->getData())) {
if ($this->executeBizRule($item->bizRule, $params, $item->data)) {
if (in_array($itemName, $this->defaultRoles)) {
return true;
}
if (isset($this->_assignments[$userId][$itemName])) {
/** @var $assignment Assignment */
$assignment = $this->_assignments[$userId][$itemName];
if ($this->executeBizRule($assignment->getBizRule(), $params, $assignment->getData())) {
if ($this->executeBizRule($assignment->bizRule, $params, $assignment->data)) {
return true;
}
}
@ -117,7 +117,7 @@ class PhpManager extends Manager
$child = $this->_items[$childName];
/** @var $item Item */
$item = $this->_items[$itemName];
$this->checkItemChildType($item->getType(), $child->getType());
$this->checkItemChildType($item->type, $child->type);
if ($this->detectLoop($itemName, $childName)) {
throw new InvalidCallException("Cannot add '$childName' as a child of '$itemName'. A loop has been detected.");
}
@ -194,7 +194,13 @@ class PhpManager extends Manager
} elseif (isset($this->_assignments[$userId][$itemName])) {
throw new InvalidParamException("Authorization item '$itemName' has already been assigned to user '$userId'.");
} else {
return $this->_assignments[$userId][$itemName] = new Assignment($this, $userId, $itemName, $bizRule, $data);
return $this->_assignments[$userId][$itemName] = new Assignment(array(
'manager' => $this,
'userId' => $userId,
'itemName' => $itemName,
'bizRule' => $bizRule,
'data' => $data,
));
}
}
@ -265,15 +271,15 @@ class PhpManager extends Manager
if ($userId === null) {
foreach ($this->_items as $name => $item) {
/** @var $item Item */
if ($item->getType() == $type) {
if ($item->type == $type) {
$items[$name] = $item;
}
}
} elseif (isset($this->_assignments[$userId])) {
foreach ($this->_assignments[$userId] as $assignment) {
/** @var $assignment Assignment */
$name = $assignment->getItemName();
if (isset($this->_items[$name]) && ($type === null || $this->_items[$name]->getType() == $type)) {
$name = $assignment->itemName;
if (isset($this->_items[$name]) && ($type === null || $this->_items[$name]->type == $type)) {
$items[$name] = $this->_items[$name];
}
}
@ -301,7 +307,14 @@ class PhpManager extends Manager
if (isset($this->_items[$name])) {
throw new Exception('Unable to add an item whose name is the same as an existing item.');
}
return $this->_items[$name] = new Item($this, $name, $type, $description, $bizRule, $data);
return $this->_items[$name] = new Item(array(
'manager' => $this,
'name' => $name,
'type' => $type,
'description' => $description,
'bizRule' => $bizRule,
'data' => $data,
));
}
/**
@ -390,10 +403,10 @@ class PhpManager extends Manager
foreach ($this->_items as $name => $item) {
/** @var $item Item */
$items[$name] = array(
'type' => $item->getType(),
'description' => $item->getDescription(),
'bizRule' => $item->getBizRule(),
'data' => $item->getData(),
'type' => $item->type,
'description' => $item->description,
'bizRule' => $item->bizRule,
'data' => $item->data,
);
if (isset($this->_children[$name])) {
foreach ($this->_children[$name] as $child) {
@ -408,8 +421,8 @@ class PhpManager extends Manager
/** @var $assignment Assignment */
if (isset($items[$name])) {
$items[$name]['assignments'][$userId] = array(
'bizRule' => $assignment->getBizRule(),
'data' => $assignment->getData(),
'bizRule' => $assignment->bizRule,
'data' => $assignment->data,
);
}
}
@ -428,7 +441,14 @@ class PhpManager extends Manager
$items = $this->loadFromFile($this->authFile);
foreach ($items as $name => $item) {
$this->_items[$name] = new Item($this, $name, $item['type'], $item['description'], $item['bizRule'], $item['data']);
$this->_items[$name] = new Item(array(
'manager' => $this,
'name' => $name,
'type' => $item['type'],
'description' => $item['description'],
'bizRule' => $item['bizRule'],
'data' => $item['data'],
));
}
foreach ($items as $name => $item) {
@ -441,8 +461,14 @@ class PhpManager extends Manager
}
if (isset($item['assignments'])) {
foreach ($item['assignments'] as $userId => $assignment) {
$this->_assignments[$userId][$name] = new Assignment($this, $name, $userId, $assignment['bizRule'], $assignment['data']);
}
$this->_assignments[$userId][$name] = new Assignment(array(
'manager' => $this,
'userId' => $userId,
'itemName' => $name,
'bizRule' => $assignment['bizRule'],
'data' => $assignment['data'],
));
}
}
}
}

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',

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

File diff suppressed because it is too large Load Diff

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

@ -3,80 +3,68 @@
/* @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>
<title>Yii Application Requirement Checker</title>
<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">
<h1>Yii Application Requirement Checker</h1>
</div><!-- header-->
<div id="content">
<h2>Description</h2>
<p>
This script checks if your server configuration meets the requirements
for running Yii application.
It checks if the server is running the right version of PHP,
if appropriate PHP extensions have been loaded, and if php.ini file settings are correct.
</p>
<div class="container">
<div class="header">
<h1>Yii Application Requirement Checker</h1>
</div>
<hr>
<h2>Conclusion</h2>
<p>
<?php if ($summary['errors']>0): ?>
Unfortunately your server configuration does not satisfy the requirements by this application.
<?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.
<?php else: ?>
Congratulations! Your server configuration satisfies all requirements.
<?php endif; ?>
</p>
<div class="content">
<h3>Description</h3>
<p>
This script checks if your server configuration meets the requirements
for running Yii application.
It checks if the server is running the right version of PHP,
if appropriate PHP extensions have been loaded, and if php.ini file settings are correct.
</p>
<h2>Details</h2>
<h3>Conclusion</h3>
<?php if ($summary['errors']>0): ?>
<strong class="text-error">Unfortunately your server configuration does not satisfy the requirements by this application.</strong>
<?php elseif ($summary['warnings']>0): ?>
<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: ?>
<strong class="text-success">Congratulations! Your server configuration satisfies all requirements.</strong>
<?php endif; ?>
<table class="result">
<tr><th>Name</th><th>Result</th><th>Required By</th><th>Memo</th></tr>
<?php foreach($requirements as $requirement): ?>
<tr>
<td>
<?php echo $requirement['name']; ?>
</td>
<td class="<?php echo $requirement['condition'] ? 'passed' : ($requirement['mandatory'] ? 'failed' : 'warning'); ?>">
<?php echo $requirement['condition'] ? 'Passed' : ($requirement['mandatory'] ? 'Failed' : 'Warning'); ?>
</td>
<td>
<?php echo $requirement['by']; ?>
</td>
<td>
<?php echo $requirement['memo']; ?>
</td>
</tr>
<?php endforeach; ?>
</table>
<h3>Details</h3>
<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>
<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 class="<?php echo $requirement['condition'] ? 'success' : ($requirement['mandatory'] ? 'error' : 'warning'); ?>">
<td>
<?php echo $requirement['name']; ?>
</td>
<td >
<?php echo $requirement['condition'] ? 'Passed' : ($requirement['mandatory'] ? 'Failed' : 'Warning'); ?>
</td>
<td>
<?php echo $requirement['by']; ?>
</td>
<td>
<?php echo $requirement['memo']; ?>
</td>
</tr>
<?php endforeach; ?>
</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) . ');';
}
}

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

Loading…
Cancel
Save