Browse Source

Merge branch 'master' of github.com:yiisoft/yii2 into sphinx

tags/2.0.0-beta
Paul Klimov 11 years ago
parent
commit
93ca701a10
  1. 7
      apps/advanced/README.md
  2. 1
      apps/advanced/backend/assets/.gitkeep
  3. 2
      apps/advanced/backend/assets/AppAsset.php
  4. 6
      apps/advanced/backend/config/main.php
  5. 2
      apps/advanced/backend/controllers/SiteController.php
  6. 12
      apps/advanced/backend/views/layouts/main.php
  7. 2
      apps/advanced/common/models/LoginForm.php
  8. 3
      apps/advanced/composer.json
  9. 2
      apps/advanced/frontend/assets/.gitkeep
  10. 2
      apps/advanced/frontend/assets/AppAsset.php
  11. 2
      apps/advanced/frontend/controllers/SiteController.php
  12. 2
      apps/advanced/frontend/models/ContactForm.php
  13. 4
      apps/advanced/frontend/views/emails/passwordResetToken.php
  14. 14
      apps/advanced/frontend/views/layouts/main.php
  15. 2
      apps/advanced/frontend/views/site/login.php
  16. 2
      apps/basic/assets/AppAsset.php
  17. 1
      apps/basic/codeception.yml
  18. 2
      apps/basic/config/console.php
  19. 2
      apps/basic/config/web.php
  20. 2
      apps/basic/models/ContactForm.php
  21. 2
      apps/basic/models/LoginForm.php
  22. 20
      apps/basic/tests/README.md
  23. 1367
      apps/basic/tests/acceptance/WebGuy.php
  24. 1367
      apps/basic/tests/functional/TestGuy.php
  25. 15
      apps/basic/tests/unit/CodeGuy.php
  26. 15
      apps/basic/views/layouts/main.php
  27. 11
      apps/basic/web/index-test.php
  28. 32
      build/controllers/PhpDocController.php
  29. 4
      docs/guide/query-builder.md
  30. 4
      docs/guide/upgrade-from-v1.md
  31. 2
      docs/guide/view.md
  32. 7
      extensions/bootstrap/composer.json
  33. 1
      extensions/composer/composer.json
  34. 7
      extensions/debug/composer.json
  35. 14
      extensions/debug/panels/ConfigPanel.php
  36. 10
      extensions/gii/Generator.php
  37. 7
      extensions/gii/composer.json
  38. 12
      extensions/gii/generators/controller/Generator.php
  39. 36
      extensions/gii/generators/crud/Generator.php
  40. 1
      extensions/gii/generators/crud/templates/controller.php
  41. 16
      extensions/gii/generators/form/Generator.php
  42. 28
      extensions/gii/generators/model/Generator.php
  43. 12
      extensions/gii/generators/module/Generator.php
  44. 8
      extensions/gii/views/default/index.php
  45. 4
      extensions/gii/views/default/view/files.php
  46. 8
      extensions/gii/views/layouts/generator.php
  47. 6
      extensions/gii/views/layouts/main.php
  48. 7
      extensions/jui/composer.json
  49. 3
      extensions/smarty/composer.json
  50. 8
      extensions/swiftmailer/Mailer.php
  51. 16
      extensions/swiftmailer/Message.php
  52. 3
      extensions/swiftmailer/composer.json
  53. 3
      extensions/twig/composer.json
  54. 12
      framework/composer.json
  55. 19
      framework/yii/BaseYii.php
  56. 5
      framework/yii/base/ActionFilter.php
  57. 8
      framework/yii/base/Application.php
  58. 18
      framework/yii/base/Component.php
  59. 5
      framework/yii/base/Controller.php
  60. 3
      framework/yii/base/ErrorHandler.php
  61. 3
      framework/yii/base/Formatter.php
  62. 38
      framework/yii/base/Model.php
  63. 2
      framework/yii/base/Module.php
  64. 15
      framework/yii/base/Object.php
  65. 1
      framework/yii/base/Request.php
  66. 2
      framework/yii/base/Response.php
  67. 4
      framework/yii/base/View.php
  68. 2
      framework/yii/base/ViewEvent.php
  69. 14
      framework/yii/caching/ChainedDependency.php
  70. 18
      framework/yii/caching/DbDependency.php
  71. 15
      framework/yii/caching/ExpressionDependency.php
  72. 16
      framework/yii/caching/FileDependency.php
  73. 22
      framework/yii/caching/GroupDependency.php
  74. 2
      framework/yii/caching/MemCache.php
  75. 2
      framework/yii/captcha/CaptchaAsset.php
  76. 4
      framework/yii/console/Request.php
  77. 2
      framework/yii/console/Response.php
  78. 2
      framework/yii/console/controllers/CacheController.php
  79. 20
      framework/yii/data/ActiveDataProvider.php
  80. 205
      framework/yii/db/ActiveQuery.php
  81. 77
      framework/yii/db/ActiveQueryInterface.php
  82. 199
      framework/yii/db/ActiveQueryTrait.php
  83. 47
      framework/yii/db/ActiveRecord.php
  84. 236
      framework/yii/db/ActiveRelation.php
  85. 29
      framework/yii/db/ActiveRelationInterface.php
  86. 246
      framework/yii/db/ActiveRelationTrait.php
  87. 6
      framework/yii/db/Command.php
  88. 142
      framework/yii/db/Query.php
  89. 2
      framework/yii/db/QueryBuilder.php
  90. 206
      framework/yii/db/QueryInterface.php
  91. 208
      framework/yii/db/QueryTrait.php
  92. 12
      framework/yii/db/Schema.php
  93. 2
      framework/yii/grid/ActionColumn.php
  94. 4
      framework/yii/grid/DataColumn.php
  95. 17
      framework/yii/grid/GridView.php
  96. 1
      framework/yii/grid/GridViewAsset.php
  97. 26
      framework/yii/helpers/BaseHtml.php
  98. 2
      framework/yii/helpers/BaseInflector.php
  99. 8
      framework/yii/helpers/BaseSecurity.php
  100. 9
      framework/yii/i18n/Formatter.php
  101. Some files were not shown because too many files have changed in this diff Show More

7
apps/advanced/README.md

@ -76,6 +76,13 @@ php composer.phar create-project --stability=dev yiisoft/yii2-app-advanced yii-a
Note that in order to install some dependencies you must have `php_openssl` extension enabled.
After the application is installed, switch to the project folder and run the following command
to initialize the application:
~~~
./init (init on Windows)
~~~
### Install from an Archive File

1
apps/advanced/backend/assets/.gitkeep

@ -1 +0,0 @@
*

2
apps/advanced/backend/config/AppAsset.php → apps/advanced/backend/assets/AppAsset.php

@ -5,7 +5,7 @@
* @license http://www.yiiframework.com/license/
*/
namespace backend\config;
namespace backend\assets;
use yii\web\AssetBundle;

6
apps/advanced/backend/config/main.php

@ -1,5 +1,5 @@
<?php
$rootDir = __DIR__ . '/../..';
$rootDir = dirname(dirname(__DIR__));
$params = array_merge(
require($rootDir . '/common/config/params.php'),
@ -11,11 +11,11 @@ $params = array_merge(
return [
'id' => 'app-backend',
'basePath' => dirname(__DIR__),
'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',
'vendorPath' => $rootDir . '/vendor',
'preload' => ['log'],
'controllerNamespace' => 'backend\controllers',
'modules' => [],
'extensions' => require(__DIR__ . '/../../vendor/yiisoft/extensions.php'),
'extensions' => require($rootDir . '/vendor/yiisoft/extensions.php'),
'components' => [
'request' => [
'enableCsrfValidation' => true,

2
apps/advanced/backend/controllers/SiteController.php

@ -50,7 +50,7 @@ class SiteController extends Controller
$model = new LoginForm();
if ($model->load($_POST) && $model->login()) {
return $this->goHome();
return $this->goBack();
} else {
return $this->render('login', [
'model' => $model,

12
apps/advanced/backend/views/layouts/main.php

@ -1,13 +1,13 @@
<?php
use backend\config\AppAsset;
use backend\assets\AppAsset;
use yii\helpers\Html;
use yii\bootstrap\Nav;
use yii\bootstrap\NavBar;
use yii\widgets\Breadcrumbs;
/**
* @var $this \yii\web\View
* @var $content string
* @var \yii\web\View $this
* @var string $content
*/
AppAsset::register($this);
?>
@ -38,16 +38,16 @@ AppAsset::register($this);
$menuItems[] = ['label' => 'Logout (' . Yii::$app->user->identity->username .')' , 'url' => ['/site/logout']];
}
echo Nav::widget([
'options' => ['class' => 'navbar-nav pull-right'],
'options' => ['class' => 'navbar-nav navbar-right'],
'items' => $menuItems,
]);
NavBar::end();
?>
<div class="container">
<?=Breadcrumbs::widget([
<?= Breadcrumbs::widget([
'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
]); ?>
]) ?>
<?= $content ?>
</div>

2
apps/advanced/common/models/LoginForm.php

@ -21,7 +21,7 @@ class LoginForm extends Model
{
return [
// username and password are both required
['username, password', 'required'],
[['username', 'password'], 'required'],
// password is validated by validatePassword()
['password', 'validatePassword'],
// rememberMe must be a boolean value

3
apps/advanced/composer.json

@ -23,8 +23,7 @@
},
"scripts": {
"post-create-project-cmd": [
"yii\\composer\\Installer::setPermission",
"./init"
"yii\\composer\\Installer::setPermission"
]
},
"extra": {

2
apps/advanced/frontend/assets/.gitkeep

@ -1,2 +0,0 @@
*
!.gitignore

2
apps/advanced/frontend/config/AppAsset.php → apps/advanced/frontend/assets/AppAsset.php

@ -5,7 +5,7 @@
* @license http://www.yiiframework.com/license/
*/
namespace frontend\config;
namespace frontend\assets;
use yii\web\AssetBundle;

2
apps/advanced/frontend/controllers/SiteController.php

@ -60,7 +60,7 @@ class SiteController extends Controller
$model = new LoginForm();
if ($model->load($_POST) && $model->login()) {
return $this->goHome();
return $this->goBack();
} else {
return $this->render('login', [
'model' => $model,

2
apps/advanced/frontend/models/ContactForm.php

@ -23,7 +23,7 @@ class ContactForm extends Model
{
return [
// name, email, subject and body are required
['name, email, subject, body', 'required'],
[['name', 'email', 'subject', 'body'], 'required'],
// email has to be a valid email address
['email', 'email'],
// verifyCode needs to be entered correctly

4
apps/advanced/frontend/views/emails/passwordResetToken.php

@ -9,8 +9,8 @@ use yii\helpers\Html;
$resetLink = Yii::$app->urlManager->createAbsoluteUrl('site/reset-password', ['token' => $user->password_reset_token]);
?>
Hello <?=Html::encode($user->username)?>,
Hello <?= Html::encode($user->username) ?>,
Follow the link below to reset your password:
<?=Html::a(Html::encode($resetLink), $resetLink)?>
<?= Html::a(Html::encode($resetLink), $resetLink) ?>

14
apps/advanced/frontend/views/layouts/main.php

@ -1,14 +1,14 @@
<?php
use frontend\config\AppAsset;
use yii\helpers\Html;
use yii\bootstrap\Nav;
use yii\bootstrap\NavBar;
use yii\widgets\Breadcrumbs;
use frontend\assets\AppAsset;
use frontend\widgets\Alert;
/**
* @var $this \yii\web\View
* @var $content string
* @var \yii\web\View $this
* @var string $content
*/
AppAsset::register($this);
?>
@ -42,17 +42,17 @@ AppAsset::register($this);
$menuItems[] = ['label' => 'Logout (' . Yii::$app->user->identity->username .')' , 'url' => ['/site/logout']];
}
echo Nav::widget([
'options' => ['class' => 'navbar-nav pull-right'],
'options' => ['class' => 'navbar-nav navbar-right'],
'items' => $menuItems,
]);
NavBar::end();
?>
<div class="container">
<?=Breadcrumbs::widget([
<?= Breadcrumbs::widget([
'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
]); ?>
<?=Alert::widget()?>
]) ?>
<?= Alert::widget() ?>
<?= $content ?>
</div>

2
apps/advanced/frontend/views/site/login.php

@ -22,7 +22,7 @@ $this->params['breadcrumbs'][] = $this->title;
<?= $form->field($model, 'password')->passwordInput() ?>
<?= $form->field($model, 'rememberMe')->checkbox() ?>
<div style="color:#999;margin:1em 0">
If you forgot your password you can <?=Html::a('reset it', ['site/request-password-reset'])?>.
If you forgot your password you can <?= Html::a('reset it', ['site/request-password-reset']) ?>.
</div>
<div class="form-group">
<?= Html::submitButton('Login', ['class' => 'btn btn-primary']) ?>

2
apps/basic/config/AppAsset.php → apps/basic/assets/AppAsset.php

@ -5,7 +5,7 @@
* @license http://www.yiiframework.com/license/
*/
namespace app\config;
namespace app\assets;
use yii\web\AssetBundle;

1
apps/basic/codeception.yml

@ -6,7 +6,6 @@ paths:
settings:
bootstrap: _bootstrap.php
suite_class: \PHPUnit_Framework_TestSuite
colors: true
memory_limit: 1024M
log: true
modules:

2
apps/basic/config/console.php

@ -1,7 +1,7 @@
<?php
$params = require(__DIR__ . '/params.php');
return [
'id' => 'bootstrap-console',
'id' => 'basic-console',
'basePath' => dirname(__DIR__),
'preload' => ['log'],
'controllerPath' => dirname(__DIR__) . '/commands',

2
apps/basic/config/web.php

@ -1,7 +1,7 @@
<?php
$params = require(__DIR__ . '/params.php');
$config = [
'id' => 'bootstrap',
'id' => 'basic',
'basePath' => dirname(__DIR__),
'extensions' => require(__DIR__ . '/../vendor/yiisoft/extensions.php'),
'components' => [

2
apps/basic/models/ContactForm.php

@ -23,7 +23,7 @@ class ContactForm extends Model
{
return [
// name, email, subject and body are required
['name, email, subject, body', 'required'],
[['name', 'email', 'subject', 'body'], 'required'],
// email has to be a valid email address
['email', 'email'],
// verifyCode needs to be entered correctly

2
apps/basic/models/LoginForm.php

@ -21,7 +21,7 @@ class LoginForm extends Model
{
return [
// username and password are both required
['username, password', 'required'],
[['username', 'password'], 'required'],
// password is validated by validatePassword()
['password', 'validatePassword'],
// rememberMe must be a boolean value

20
apps/basic/tests/README.md

@ -0,0 +1,20 @@
This folder contains various tests for the basic application.
These tests are developed with [Codeception PHP Testing Framework](http://codeception.com/).
To run the tests, follow these steps:
1. [Install Codeception](http://codeception.com/quickstart) if you do not have it yet.
2. Create test configuration files based on your environment:
- Copy `acceptance.suite.dist.yml` to `acceptance.suite.yml` and customize it;
- Copy `functional.suite.dist.yml` to `functional.suite.yml` and customize it;
- Copy `unit.suite.dist.yml` to `unit.suite.yml` and customize it.
3. Switch to the parent folder and run tests:
```
cd ..
php codecept.phar build // rebuild test scripts, only need to be run once
php codecept.phar run // run all available tests
```
Please refer to [Codeception tutorial](http://codeception.com/docs/01-Introduction) for
more details about writing acceptance, functional and unit tests.

1367
apps/basic/tests/acceptance/WebGuy.php

File diff suppressed because it is too large Load Diff

1367
apps/basic/tests/functional/TestGuy.php

File diff suppressed because it is too large Load Diff

15
apps/basic/tests/unit/CodeGuy.php

@ -1,23 +1,26 @@
<?php
// This class was automatically generated by build task
// You can change it manually, but it will be overwritten on next build
// You should not change it manually as it will be overwritten on next build
// @codingStandardsIgnoreFile
use Codeception\Maybe;
use \Codeception\Maybe;
use Codeception\Module\CodeHelper;
/**
* Inherited methods
* @method void execute($callable)
* @method void wantToTest($text)
* @method void wantTo($text)
* @method void amTesting($method)
* @method void amTestingMethod($method)
* @method void testMethod($signature)
* @method void expectTo($prediction)
* @method void expect($prediction)
* @method void amGoingTo($argumentation)
* @method void am($role)
* @method void lookForwardTo($role)
* @method void lookForwardTo($achieveValue)
* @method void offsetGet($offset)
* @method void offsetSet($offset, $value)
* @method void offsetExists($offset)
* @method void offsetUnset($offset)
*/
class CodeGuy extends \Codeception\AbstractGuy

15
apps/basic/views/layouts/main.php

@ -3,12 +3,13 @@ use yii\helpers\Html;
use yii\bootstrap\Nav;
use yii\bootstrap\NavBar;
use yii\widgets\Breadcrumbs;
use app\assets\AppAsset;
/**
* @var $this \yii\web\View
* @var $content string
* @var \yii\web\View $this
* @var string $content
*/
app\config\AppAsset::register($this);
AppAsset::register($this);
?>
<?php $this->beginPage(); ?>
<!DOCTYPE html>
@ -29,14 +30,14 @@ app\config\AppAsset::register($this);
],
]);
echo Nav::widget([
'options' => ['class' => 'navbar-nav pull-right'],
'options' => ['class' => 'navbar-nav navbar-right'],
'items' => [
['label' => 'Home', 'url' => ['/site/index']],
['label' => 'About', 'url' => ['/site/about']],
['label' => 'Contact', 'url' => ['/site/contact']],
Yii::$app->user->isGuest ?
['label' => 'Login', 'url' => ['/site/login']] :
['label' => 'Logout (' . Yii::$app->user->identity->username .')' ,
['label' => 'Logout (' . Yii::$app->user->identity->username . ')' ,
'url' => ['/site/logout'],
'linkOptions' => ['data-method' => 'post']],
],
@ -45,9 +46,9 @@ app\config\AppAsset::register($this);
?>
<div class="container">
<?=Breadcrumbs::widget([
<?= Breadcrumbs::widget([
'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
]); ?>
]) ?>
<?= $content ?>
</div>

11
apps/basic/web/index-test.php

@ -13,5 +13,12 @@ require_once(__DIR__ . '/../vendor/yiisoft/yii2/yii/Yii.php');
$config = require(__DIR__ . '/../config/web-test.php');
$application = new yii\web\Application($config);
$application->run();
if (isset($this)) {
// run in functional tests
$config['class'] = 'yii\web\Application';
return $config;
} else {
// run in acceptance tests
$application = new yii\web\Application($config);
$application->run();
}

32
build/controllers/PhpDocController.php

@ -278,11 +278,35 @@ class PhpDocController extends Controller
. ' See [[get'.ucfirst($propName).'()]] and [[set'.ucfirst($propName).'()]] for details.';
}
} elseif (isset($prop['get'])) {
$note = ' This property is read-only.';
// $docline .= '-read';
// check if parent class has setter defined
$c = $className;
$parentSetter = false;
while($parent = get_parent_class($c)) {
if (method_exists($parent, 'set' . ucfirst($propName))) {
$parentSetter = true;
break;
}
$c = $parent;
}
if (!$parentSetter) {
$note = ' This property is read-only.';
// $docline .= '-read';
}
} elseif (isset($prop['set'])) {
$note = ' This property is write-only.';
// $docline .= '-write';
// check if parent class has getter defined
$c = $className;
$parentGetter = false;
while($parent = get_parent_class($c)) {
if (method_exists($parent, 'set' . ucfirst($propName))) {
$parentGetter = true;
break;
}
$c = $parent;
}
if (!$parentGetter) {
$note = ' This property is write-only.';
// $docline .= '-write';
}
} else {
continue;
}

4
docs/guide/query-builder.md

@ -167,8 +167,8 @@ For ordering results `orderBy` and `addOrderBy` could be used:
```php
$query->orderBy([
'id' => Query::SORT_ASC,
'name' => Query::SORT_DESC,
'id' => SORT_ASC,
'name' => SORT_DESC,
]);
```

4
docs/guide/upgrade-from-v1.md

@ -38,7 +38,7 @@ of `Object` should declare its constructor (if needed) in the following way so t
it can be properly configured:
```php
class MyClass extends \yii\Object
class MyClass extends \yii\base\Object
{
public function __construct($param1, $param2, $config = [])
{
@ -109,7 +109,7 @@ Yii::$app->trigger($eventName);
If you need to handle all instances of a class instead of the object you can attach a handler like the following:
```php
Event::on([ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT], function ($event) {
Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
Yii::trace(get_class($event->sender) . ' is inserted.');
});
```

2
docs/guide/view.md

@ -240,7 +240,7 @@ details on how to define asset bundles in [asset manager](assets.md) section of
asset bundle, it's very straightforward:
```php
frontend\config\AppAsset::register($this);
frontend\assets\AppAsset::register($this);
```
### Layout

7
extensions/bootstrap/composer.json

@ -11,7 +11,12 @@
"irc": "irc://irc.freenode.net/yii",
"source": "https://github.com/yiisoft/yii2"
},
"minimum-stability": "dev",
"authors": [
{
"name": "Qiang Xue",
"email": "qiang.xue@gmail.com"
}
],
"require": {
"yiisoft/yii2": "*",
"twbs/bootstrap": "3.0.*"

1
extensions/composer/composer.json

@ -17,7 +17,6 @@
"email": "qiang.xue@gmail.com"
}
],
"minimum-stability": "dev",
"autoload": {
"psr-0": { "yii\\composer\\": "" }
},

7
extensions/debug/composer.json

@ -11,7 +11,12 @@
"irc": "irc://irc.freenode.net/yii",
"source": "https://github.com/yiisoft/yii2"
},
"minimum-stability": "dev",
"authors": [
{
"name": "Qiang Xue",
"email": "qiang.xue@gmail.com"
}
],
"require": {
"yiisoft/yii2": "*",
"yiisoft/yii2-bootstrap": "*"

14
extensions/debug/panels/ConfigPanel.php

@ -63,6 +63,7 @@ EOD;
];
return "<h1>Configuration</h1>\n"
. $this->renderData('Application Configuration', $app) . "\n"
. $this->renderExtensions()
. $this->renderData('PHP Configuration', $php) . "\n"
. $this->getPhpInfo();
}
@ -93,6 +94,18 @@ $rows
EOD;
}
protected function renderExtensions()
{
if (empty($this->data['extensions'])) {
return '';
}
$data = [];
foreach ($this->data['extensions'] as $extension) {
$data[$extension['name']] = $extension['version'];
}
return $this->renderData('Installed Extensions', $data) . "\n";
}
public function save()
{
return [
@ -110,6 +123,7 @@ EOD;
'apc' => extension_loaded('apc'),
'memcache' => extension_loaded('memcache'),
],
'extensions' => Yii::$app->extensions,
];
}
}

10
extensions/gii/Generator.php

@ -178,8 +178,8 @@ abstract class Generator extends Model
public function rules()
{
return [
['template', 'required', 'message' => 'A code template must be selected.'],
['template', 'validateTemplate'],
[['template'], 'required', 'message' => 'A code template must be selected.'],
[['template'], 'validateTemplate'],
];
}
@ -193,7 +193,7 @@ abstract class Generator extends Model
$attributes[] = 'template';
$path = $this->getStickyDataFile();
if (is_file($path)) {
$result = @include($path);
$result = json_decode(file_get_contents($path), true);
if (is_array($result)) {
foreach ($stickyAttributes as $name) {
if (isset($result[$name])) {
@ -218,7 +218,7 @@ abstract class Generator extends Model
}
$path = $this->getStickyDataFile();
@mkdir(dirname($path), 0755, true);
file_put_contents($path, "<?php\nreturn " . var_export($values, true) . ";\n");
file_put_contents($path, json_encode($values));
}
/**
@ -227,7 +227,7 @@ abstract class Generator extends Model
*/
public function getStickyDataFile()
{
return Yii::$app->getRuntimePath() . '/gii-' . Yii::getVersion() . '/' . str_replace('\\', '-', get_class($this)) . '.php';
return Yii::$app->getRuntimePath() . '/gii-' . Yii::getVersion() . '/' . str_replace('\\', '-', get_class($this)) . '.json';
}
/**

7
extensions/gii/composer.json

@ -11,7 +11,12 @@
"irc": "irc://irc.freenode.net/yii",
"source": "https://github.com/yiisoft/yii2"
},
"minimum-stability": "dev",
"authors": [
{
"name": "Qiang Xue",
"email": "qiang.xue@gmail.com"
}
],
"require": {
"yiisoft/yii2": "*",
"yiisoft/yii2-bootstrap": "*"

12
extensions/gii/generators/controller/Generator.php

@ -69,12 +69,12 @@ class Generator extends \yii\gii\Generator
public function rules()
{
return array_merge(parent::rules(), [
['controller, actions, baseClass, ns', 'filter', 'filter' => 'trim'],
['controller, baseClass', 'required'],
['controller', 'match', 'pattern' => '/^[a-z\\-\\/]*$/', 'message' => 'Only a-z, dashes (-) and slashes (/) are allowed.'],
['actions', 'match', 'pattern' => '/^[a-z\\-,\\s]*$/', 'message' => 'Only a-z, dashes (-), spaces and commas are allowed.'],
['baseClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'],
['ns', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'],
[['controller', 'actions', 'baseClass', 'ns'], 'filter', 'filter' => 'trim'],
[['controller', 'baseClass'], 'required'],
[['controller'], 'match', 'pattern' => '/^[a-z\\-\\/]*$/', 'message' => 'Only a-z, dashes (-) and slashes (/) are allowed.'],
[['actions'], 'match', 'pattern' => '/^[a-z\\-,\\s]*$/', 'message' => 'Only a-z, dashes (-), spaces and commas are allowed.'],
[['baseClass'], 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'],
[['ns'], 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'],
]);
}

36
extensions/gii/generators/crud/Generator.php

@ -42,17 +42,17 @@ class Generator extends \yii\gii\Generator
public function rules()
{
return array_merge(parent::rules(), [
['moduleID, controllerClass, modelClass, searchModelClass, baseControllerClass', 'filter', 'filter' => 'trim'],
['modelClass, searchModelClass, controllerClass, baseControllerClass, indexWidgetType', 'required'],
['searchModelClass', 'compare', 'compareAttribute' => 'modelClass', 'operator' => '!==', 'message' => 'Search Model Class must not be equal to Model Class.'],
['modelClass, controllerClass, baseControllerClass, searchModelClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'],
['modelClass', 'validateClass', 'params' => ['extends' => ActiveRecord::className()]],
['baseControllerClass', 'validateClass', 'params' => ['extends' => Controller::className()]],
['controllerClass', 'match', 'pattern' => '/Controller$/', 'message' => 'Controller class name must be suffixed with "Controller".'],
['controllerClass, searchModelClass', 'validateNewClass'],
['indexWidgetType', 'in', 'range' => ['grid', 'list']],
['modelClass', 'validateModelClass'],
['moduleID', 'validateModuleID'],
[['moduleID', 'controllerClass', 'modelClass', 'searchModelClass', 'baseControllerClass'], 'filter', 'filter' => 'trim'],
[['modelClass', 'searchModelClass', 'controllerClass', 'baseControllerClass', 'indexWidgetType'], 'required'],
[['searchModelClass'], 'compare', 'compareAttribute' => 'modelClass', 'operator' => '!==', 'message' => 'Search Model Class must not be equal to Model Class.'],
[['modelClass', 'controllerClass', 'baseControllerClass', 'searchModelClass'], 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'],
[['modelClass'], 'validateClass', 'params' => ['extends' => ActiveRecord::className()]],
[['baseControllerClass'], 'validateClass', 'params' => ['extends' => Controller::className()]],
[['controllerClass'], 'match', 'pattern' => '/Controller$/', 'message' => 'Controller class name must be suffixed with "Controller".'],
[['controllerClass', 'searchModelClass'], 'validateNewClass'],
[['indexWidgetType'], 'in', 'range' => ['grid', 'list']],
[['modelClass'], 'validateModelClass'],
[['moduleID'], 'validateModuleID'],
]);
}
@ -278,7 +278,7 @@ class Generator extends \yii\gii\Generator
$rules = [];
foreach ($types as $type => $columns) {
$rules[] = "['" . implode(', ', $columns) . "', '$type']";
$rules[] = "[['" . implode("', '", $columns) . "'], '$type']";
}
return $rules;
@ -341,7 +341,9 @@ class Generator extends \yii\gii\Generator
public function generateUrlParams()
{
$pks = $this->getTableSchema()->primaryKey;
/** @var ActiveRecord $class */
$class = $this->modelClass;
$pks = $class::primaryKey();
if (count($pks) === 1) {
return "'id' => \$model->{$pks[0]}";
} else {
@ -355,7 +357,9 @@ class Generator extends \yii\gii\Generator
public function generateActionParams()
{
$pks = $this->getTableSchema()->primaryKey;
/** @var ActiveRecord $class */
$class = $this->modelClass;
$pks = $class::primaryKey();
if (count($pks) === 1) {
return '$id';
} else {
@ -366,7 +370,9 @@ class Generator extends \yii\gii\Generator
public function generateActionParamComments()
{
$table = $this->getTableSchema();
$pks = $table->primaryKey;
/** @var ActiveRecord $class */
$class = $this->modelClass;
$pks = $class::primaryKey();
if (count($pks) === 1) {
return ['@param ' . $table->columns[$pks[0]]->phpType . ' $id'];
} else {

1
extensions/gii/generators/crud/templates/controller.php

@ -28,7 +28,6 @@ namespace <?= StringHelper::dirname(ltrim($generator->controllerClass, '\\')) ?>
use <?= ltrim($generator->modelClass, '\\') ?>;
use <?= ltrim($generator->searchModelClass, '\\') ?><?php if (isset($searchModelAlias)):?> as <?= $searchModelAlias ?><?php endif ?>;
use yii\data\ActiveDataProvider;
use <?= ltrim($generator->baseControllerClass, '\\') ?>;
use yii\web\HttpException;
use yii\web\VerbFilter;

16
extensions/gii/generators/form/Generator.php

@ -60,14 +60,14 @@ class Generator extends \yii\gii\Generator
public function rules()
{
return array_merge(parent::rules(), [
['modelClass, viewName, scenarioName, viewPath', 'filter', 'filter' => 'trim'],
['modelClass, viewName, viewPath', 'required'],
['modelClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'],
['modelClass', 'validateClass', 'params' => ['extends' => Model::className()]],
['viewName', 'match', 'pattern' => '/^\w+[\\-\\/\w]*$/', 'message' => 'Only word characters, dashes and slashes are allowed.'],
['viewPath', 'match', 'pattern' => '/^@?\w+[\\-\\/\w]*$/', 'message' => 'Only word characters, dashes, slashes and @ are allowed.'],
['viewPath', 'validateViewPath'],
['scenarioName', 'match', 'pattern' => '/^[\w\\-]+$/', 'message' => 'Only word characters and dashes are allowed.'],
[['modelClass', 'viewName', 'scenarioName', 'viewPath'], 'filter', 'filter' => 'trim'],
[['modelClass', 'viewName', 'viewPath'], 'required'],
[['modelClass'], 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'],
[['modelClass'], 'validateClass', 'params' => ['extends' => Model::className()]],
[['viewName'], 'match', 'pattern' => '/^\w+[\\-\\/\w]*$/', 'message' => 'Only word characters, dashes and slashes are allowed.'],
[['viewPath'], 'match', 'pattern' => '/^@?\w+[\\-\\/\w]*$/', 'message' => 'Only word characters, dashes, slashes and @ are allowed.'],
[['viewPath'], 'validateViewPath'],
[['scenarioName'], 'match', 'pattern' => '/^[\w\\-]+$/', 'message' => 'Only word characters and dashes are allowed.'],
]);
}

28
extensions/gii/generators/model/Generator.php

@ -53,17 +53,17 @@ class Generator extends \yii\gii\Generator
public function rules()
{
return array_merge(parent::rules(), [
['db, ns, tableName, modelClass, baseClass', 'filter', 'filter' => 'trim'],
['db, ns, tableName, baseClass', 'required'],
['db, modelClass', 'match', 'pattern' => '/^\w+$/', 'message' => 'Only word characters are allowed.'],
['ns, baseClass', 'match', 'pattern' => '/^[\w\\\\]+$/', 'message' => 'Only word characters and backslashes are allowed.'],
['tableName', 'match', 'pattern' => '/^(\w+\.)?([\w\*]+)$/', 'message' => 'Only word characters, and optionally an asterisk and/or a dot are allowed.'],
['db', 'validateDb'],
['ns', 'validateNamespace'],
['tableName', 'validateTableName'],
['modelClass', 'validateModelClass', 'skipOnEmpty' => false],
['baseClass', 'validateClass', 'params' => ['extends' => ActiveRecord::className()]],
['generateRelations, generateLabelsFromComments', 'boolean'],
[['db', 'ns', 'tableName', 'modelClass', 'baseClass'], 'filter', 'filter' => 'trim'],
[['db', 'ns', 'tableName', 'baseClass'], 'required'],
[['db', 'modelClass'], 'match', 'pattern' => '/^\w+$/', 'message' => 'Only word characters are allowed.'],
[['ns', 'baseClass'], 'match', 'pattern' => '/^[\w\\\\]+$/', 'message' => 'Only word characters and backslashes are allowed.'],
[['tableName'], 'match', 'pattern' => '/^(\w+\.)?([\w\*]+)$/', 'message' => 'Only word characters, and optionally an asterisk and/or a dot are allowed.'],
[['db'], 'validateDb'],
[['ns'], 'validateNamespace'],
[['tableName'], 'validateTableName'],
[['modelClass'], 'validateModelClass', 'skipOnEmpty' => false],
[['baseClass'], 'validateClass', 'params' => ['extends' => ActiveRecord::className()]],
[['generateRelations', 'generateLabelsFromComments'], 'boolean'],
]);
}
@ -104,7 +104,7 @@ class Generator extends \yii\gii\Generator
'baseClass' => 'This is the base class of the new ActiveRecord class. It should be a fully qualified namespaced class name.',
'generateRelations' => 'This indicates whether the generator should generate relations based on
foreign key constraints it detects in the database. Note that if your database contains too many tables,
you may want to uncheck this option to accelerate the code generation proc ess.',
you may want to uncheck this option to accelerate the code generation process.',
'generateLabelsFromComments' => 'This indicates whether the generator should generate attribute labels
by using the comments of the corresponding DB columns.',
];
@ -237,10 +237,10 @@ class Generator extends \yii\gii\Generator
$rules = [];
foreach ($types as $type => $columns) {
$rules[] = "['" . implode(', ', $columns) . "', '$type']";
$rules[] = "[['" . implode("', '", $columns) . "'], '$type']";
}
foreach ($lengths as $length => $columns) {
$rules[] = "['" . implode(', ', $columns) . "', 'string', 'max' => $length]";
$rules[] = "[['" . implode("', '", $columns) . "'], 'string', 'max' => $length]";
}
return $rules;

12
extensions/gii/generators/module/Generator.php

@ -45,11 +45,11 @@ class Generator extends \yii\gii\Generator
public function rules()
{
return array_merge(parent::rules(), [
['moduleID, moduleClass', 'filter', 'filter' => 'trim'],
['moduleID, moduleClass', 'required'],
['moduleID', 'match', 'pattern' => '/^[\w\\-]+$/', 'message' => 'Only word characters and dashes are allowed.'],
['moduleClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'],
['moduleClass', 'validateModuleClass'],
[['moduleID', 'moduleClass'], 'filter', 'filter' => 'trim'],
[['moduleID', 'moduleClass'], 'required'],
[['moduleID'], 'match', 'pattern' => '/^[\w\\-]+$/', 'message' => 'Only word characters and dashes are allowed.'],
[['moduleClass'], 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'],
[['moduleClass'], 'validateModuleClass'],
]);
}
@ -139,7 +139,7 @@ EOD;
*/
public function validateModuleClass()
{
if (strpos($this->moduleClass, '\\') === false || Yii::getAlias('@' . str_replace('\\', '/', $this->moduleClass)) === false) {
if (strpos($this->moduleClass, '\\') === false || Yii::getAlias('@' . str_replace('\\', '/', $this->moduleClass), false) === false) {
$this->addError('moduleClass', 'Module class must be properly namespaced.');
}
if (substr($this->moduleClass, -1, 1) == '\\') {

8
extensions/gii/views/default/index.php

@ -2,10 +2,10 @@
use yii\helpers\Html;
/**
* @var $this \yii\web\View
* @var $content string
* @var yii\gii\Generator[] $generators
* @var yii\gii\Generator $activeGenerator
* @var \yii\web\View $this
* @var \yii\gii\Generator[] $generators
* @var \yii\gii\Generator $activeGenerator
* @var string $content
*/
$generators = Yii::$app->controller->module->generators;
$activeGenerator = Yii::$app->controller->generator;

4
extensions/gii/views/default/view/files.php

@ -4,8 +4,8 @@ use yii\helpers\Html;
use yii\gii\CodeFile;
/**
* @var $this \yii\web\View
* @var $generator \yii\gii\Generator
* @var \yii\web\View $this
* @var \yii\gii\Generator $generator
* @var CodeFile[] $files
* @var array $answers
*/

8
extensions/gii/views/layouts/generator.php

@ -2,10 +2,10 @@
use yii\helpers\Html;
/**
* @var $this \yii\web\View
* @var $content string
* @var yii\gii\Generator[] $generators
* @var yii\gii\Generator $activeGenerator
* @var \yii\web\View $this
* @var \yii\gii\Generator[] $generators
* @var \yii\gii\Generator $activeGenerator
* @var string $content
*/
$generators = Yii::$app->controller->module->generators;
$activeGenerator = Yii::$app->controller->generator;

6
extensions/gii/views/layouts/main.php

@ -4,8 +4,8 @@ use yii\bootstrap\Nav;
use yii\helpers\Html;
/**
* @var $this \yii\web\View
* @var $content string
* @var \yii\web\View $this
* @var string $content
*/
$asset = yii\gii\GiiAsset::register($this);
?>
@ -26,7 +26,7 @@ NavBar::begin([
'options' => ['class' => 'navbar-inverse navbar-fixed-top'],
]);
echo Nav::widget([
'options' => ['class' => 'nav navbar-nav pull-right'],
'options' => ['class' => 'nav navbar-nav navbar-right'],
'items' => [
['label' => 'Home', 'url' => ['default/index']],
['label' => 'Help', 'url' => 'http://www.yiiframework.com/doc/guide/topics.gii'],

7
extensions/jui/composer.json

@ -11,7 +11,12 @@
"irc": "irc://irc.freenode.net/yii",
"source": "https://github.com/yiisoft/yii2"
},
"minimum-stability": "dev",
"authors": [
{
"name": "Qiang Xue",
"email": "qiang.xue@gmail.com"
}
],
"require": {
"yiisoft/yii2": "*"
},

3
extensions/smarty/composer.json

@ -17,10 +17,9 @@
"email": "sam@rmcreative.ru"
}
],
"minimum-stability": "dev",
"require": {
"yiisoft/yii2": "*",
"smarty/smarty": ">=v3.1.13"
"smarty/smarty": "*"
},
"autoload": {
"psr-0": { "yii\\smarty\\": "" }

8
extensions/swiftmailer/Mailer.php

@ -104,13 +104,13 @@ class Mailer extends BaseMailer
/**
* @inheritdoc
*/
public function send($message)
protected function sendMessage($message)
{
$address = $message->getTo();
if (is_array($address)) {
$address = implode(', ', $address);
$address = implode(', ', array_keys($address));
}
Yii::trace('Sending email "' . $message->getSubject() . '" to "' . $address . '"', __METHOD__);
Yii::info('Sending email "' . $message->getSubject() . '" to "' . $address . '"', __METHOD__);
return $this->getSwiftMailer()->send($message->getSwiftMessage()) > 0;
}
@ -145,7 +145,7 @@ class Mailer extends BaseMailer
$transport->$name = $value;
} else {
$setter = 'set' . $name;
if (method_exists($transport, $setter)) {
if (method_exists($transport, $setter) || method_exists($transport, '__call')) {
$transport->$setter($value);
} else {
throw new InvalidConfigException('Setting unknown property: ' . get_class($transport) . '::' . $name);

16
extensions/swiftmailer/Message.php

@ -44,7 +44,7 @@ class Message extends BaseMessage
*/
public function getCharset()
{
$this->getSwiftMessage()->getCharset();
return $this->getSwiftMessage()->getCharset();
}
/**
@ -61,7 +61,7 @@ class Message extends BaseMessage
*/
public function getFrom()
{
$this->getSwiftMessage()->getFrom();
return $this->getSwiftMessage()->getFrom();
}
/**
@ -78,7 +78,7 @@ class Message extends BaseMessage
*/
public function getReplyTo()
{
$this->getSwiftMessage()->getReplyTo();
return $this->getSwiftMessage()->getReplyTo();
}
/**
@ -95,7 +95,7 @@ class Message extends BaseMessage
*/
public function getTo()
{
$this->getSwiftMessage()->getTo();
return $this->getSwiftMessage()->getTo();
}
/**
@ -112,7 +112,7 @@ class Message extends BaseMessage
*/
public function getCc()
{
$this->getSwiftMessage()->getCc();
return $this->getSwiftMessage()->getCc();
}
/**
@ -129,7 +129,7 @@ class Message extends BaseMessage
*/
public function getBcc()
{
$this->getSwiftMessage()->getBcc();
return $this->getSwiftMessage()->getBcc();
}
/**
@ -146,7 +146,7 @@ class Message extends BaseMessage
*/
public function getSubject()
{
$this->getSwiftMessage()->getSubject();
return $this->getSwiftMessage()->getSubject();
}
/**
@ -192,7 +192,7 @@ class Message extends BaseMessage
$partFound = false;
foreach ($parts as $key => $part) {
if (!($part instanceof \Swift_Mime_Attachment)) {
/* @var $part \Swift_Mime_MimePart */
/* @var \Swift_Mime_MimePart $part */
if ($part->getContentType() == $contentType) {
unset($parts[$key]);
$partFound = true;

3
extensions/swiftmailer/composer.json

@ -17,10 +17,9 @@
"email": "klimov.paul@gmail.com"
}
],
"minimum-stability": "dev",
"require": {
"yiisoft/yii2": "*",
"swiftmailer/swiftmailer": "@stable"
"swiftmailer/swiftmailer": "*"
},
"autoload": {
"psr-0": { "yii\\swiftmailer\\": "" }

3
extensions/twig/composer.json

@ -17,10 +17,9 @@
"email": "sam@rmcreative.ru"
}
],
"minimum-stability": "dev",
"require": {
"yiisoft/yii2": "*",
"twig/twig": "1.13.*"
"twig/twig": "*"
},
"autoload": {
"psr-0": { "yii\\twig\\": "" }

12
framework/composer.json

@ -65,17 +65,15 @@
},
"require": {
"php": ">=5.4.0",
"yiisoft/yii2-composer": "*",
"yiisoft/jquery": "1.10.*",
"ext-mbstring": "*",
"lib-pcre": "*",
"phpspec/php-diff": "dev-master",
"ezyang/htmlpurifier": "4.5.*"
"yiisoft/yii2-composer": "*",
"yiisoft/jquery": "1.10.*",
"phpspec/php-diff": ">=1.0.2",
"ezyang/htmlpurifier": "4.5.*",
"michelf/php-markdown": "1.3.*"
},
"autoload": {
"psr-0": { "yii\\": "/" }
},
"suggest": {
"michelf/php-markdown": "Required by Markdown."
}
}

19
framework/yii/BaseYii.php

@ -63,8 +63,7 @@ class BaseYii
* The array keys are the class names (without leading backslashes), and the array values
* are the corresponding class file paths (or path aliases). This property mainly affects
* how [[autoload()]] works.
* @see import
* @see autoload
* @see autoload()
*/
public static $classMap = [];
/**
@ -73,8 +72,8 @@ class BaseYii
public static $app;
/**
* @var array registered path aliases
* @see getAlias
* @see setAlias
* @see getAlias()
* @see setAlias()
*/
public static $aliases = ['@yii' => __DIR__];
/**
@ -95,7 +94,7 @@ class BaseYii
* ]
* ~~~
*
* @see createObject
* @see createObject()
*/
public static $objectConfig = [];
@ -136,7 +135,7 @@ class BaseYii
* If this is false and an invalid alias is given, false will be returned by this method.
* @return string|boolean the path corresponding to the alias, false if the root alias is not previously registered.
* @throws InvalidParamException if the alias is invalid while $throwException is true.
* @see setAlias
* @see setAlias()
*/
public static function getAlias($alias, $throwException = true)
{
@ -219,7 +218,7 @@ class BaseYii
* actual path first by calling [[getAlias()]].
*
* @throws InvalidParamException if $path is an invalid alias.
* @see getAlias
* @see getAlias()
*/
public static function setAlias($alias, $path)
{
@ -368,7 +367,7 @@ class BaseYii
}
if (($n = func_num_args()) > 1) {
/** @var $reflection \ReflectionClass */
/** @var \ReflectionClass $reflection */
if (isset($reflections[$class])) {
$reflection = $reflections[$class];
} else {
@ -450,7 +449,7 @@ class BaseYii
* ~~~
* @param string $token token for the code block
* @param string $category the category of this log message
* @see endProfile
* @see endProfile()
*/
public static function beginProfile($token, $category = 'application')
{
@ -462,7 +461,7 @@ class BaseYii
* This has to be matched with a previous call to [[beginProfile]] with the same category name.
* @param string $token token for the code block
* @param string $category the category of this log message
* @see beginProfile
* @see beginProfile()
*/
public static function endProfile($token, $category = 'application')
{

5
framework/yii/base/ActionFilter.php

@ -8,6 +8,11 @@
namespace yii\base;
/**
* ActionFilter provides a base implementation for action filters that can be added to a controller
* to handle the `beforeAction` event.
*
* Check implementation of [[AccessControl]], [[PageCache]] and [[HttpCache]] as examples on how to use it.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/

8
framework/yii/base/Application.php

@ -15,6 +15,7 @@ use yii\web\HttpException;
* Application is the base class for all application classes.
*
* @property \yii\rbac\Manager $authManager The auth manager for this application. This property is read-only.
* @property string $basePath The root directory of the application.
* @property \yii\caching\Cache $cache The cache application component. Null if the component is not enabled.
* This property is read-only.
* @property \yii\db\Connection $db The database connection. This property is read-only.
@ -258,9 +259,10 @@ abstract class Application extends Module
}
/**
* Sets the root directory of the applicaition and the @app alias.
* Sets the root directory of the application and the @app alias.
* This method can only be invoked at the beginning of the constructor.
* @param string $path the root directory of the application.
* @property string the root directory of the application.
* @throws InvalidParamException if the directory does not exist.
*/
public function setBasePath($path)
@ -425,7 +427,7 @@ abstract class Application extends Module
/**
* Returns the view object.
* @return View the view object that is used to render various view files.
* @return View|\yii\web\View the view object that is used to render various view files.
*/
public function getView()
{
@ -614,10 +616,8 @@ abstract class Application extends Module
{
$category = get_class($exception);
if ($exception instanceof HttpException) {
/** @var $exception HttpException */
$category .= '\\' . $exception->statusCode;
} elseif ($exception instanceof \ErrorException) {
/** @var $exception \ErrorException */
$category .= '\\' . $exception->getSeverity();
}
Yii::error((string)$exception, $category);

18
framework/yii/base/Component.php

@ -10,6 +10,8 @@ namespace yii\base;
use Yii;
/**
* Component is the base class that implements the *property*, *event* and *behavior* features.
*
* @include @yii/base/Component.md
*
* @property Behavior[] $behaviors List of behaviors attached to this component. This property is read-only.
@ -41,7 +43,7 @@ class Component extends Object
* @return mixed the property value or the value of a behavior's property
* @throws UnknownPropertyException if the property is not defined
* @throws InvalidCallException if the property is write-only.
* @see __set
* @see __set()
*/
public function __get($name)
{
@ -80,7 +82,7 @@ class Component extends Object
* @param mixed $value the property value
* @throws UnknownPropertyException if the property is not defined
* @throws InvalidCallException if the property is read-only.
* @see __get
* @see __get()
*/
public function __set($name, $value)
{
@ -225,8 +227,8 @@ class Component extends Object
* @param boolean $checkVars whether to treat member variables as properties
* @param boolean $checkBehaviors whether to treat behaviors' properties as properties of this component
* @return boolean whether the property is defined
* @see canGetProperty
* @see canSetProperty
* @see canGetProperty()
* @see canSetProperty()
*/
public function hasProperty($name, $checkVars = true, $checkBehaviors = true)
{
@ -246,7 +248,7 @@ class Component extends Object
* @param boolean $checkVars whether to treat member variables as properties
* @param boolean $checkBehaviors whether to treat behaviors' properties as properties of this component
* @return boolean whether the property can be read
* @see canSetProperty
* @see canSetProperty()
*/
public function canGetProperty($name, $checkVars = true, $checkBehaviors = true)
{
@ -276,7 +278,7 @@ class Component extends Object
* @param boolean $checkVars whether to treat member variables as properties
* @param boolean $checkBehaviors whether to treat behaviors' properties as properties of this component
* @return boolean whether the property can be written
* @see canGetProperty
* @see canGetProperty()
*/
public function canSetProperty($name, $checkVars = true, $checkBehaviors = true)
{
@ -492,7 +494,7 @@ class Component extends Object
* - an object configuration array that will be passed to [[Yii::createObject()]] to create the behavior object.
*
* @return Behavior the behavior object
* @see detachBehavior
* @see detachBehavior()
*/
public function attachBehavior($name, $behavior)
{
@ -505,7 +507,7 @@ class Component extends Object
* Each behavior is indexed by its name and should be a [[Behavior]] object,
* a string specifying the behavior class, or an configuration array for creating the behavior.
* @param array $behaviors list of behaviors to be attached to the component
* @see attachBehavior
* @see attachBehavior()
*/
public function attachBehaviors($behaviors)
{

5
framework/yii/base/Controller.php

@ -111,7 +111,7 @@ class Controller extends Component implements ViewContextInterface
* @param array $params the parameters (name-value pairs) to be passed to the action.
* @return mixed the result of the action
* @throws InvalidRouteException if the requested action ID cannot be resolved into an action successfully.
* @see createAction
* @see createAction()
*/
public function runAction($id, $params = [])
{
@ -149,8 +149,7 @@ class Controller extends Component implements ViewContextInterface
* @param string $route the route to be handled, e.g., 'view', 'comment/view', '/admin/comment/view'.
* @param array $params the parameters to be passed to the action.
* @return mixed the result of the action
* @see runAction
* @see forward
* @see runAction()
*/
public function run($route, $params = [])
{

3
framework/yii/base/ErrorHandler.php

@ -16,6 +16,9 @@ use yii\web\HttpException;
* ErrorHandler displays these errors using appropriate views based on the
* nature of the errors and the mode the application runs at.
*
* ErrorHandler is configured as an application component in [[yii\base\Application]] by default.
* You can access that instance via `Yii::$app->errorHandler`.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Timur Ruziev <resurtm@gmail.com>
* @since 2.0

3
framework/yii/base/Formatter.php

@ -19,6 +19,9 @@ use yii\helpers\Html;
* The behavior of some of them may be configured via the properties of Formatter. For example,
* by configuring [[dateFormat]], one may control how [[asDate()]] formats the value into a date string.
*
* Formatter is configured as an application component in [[yii\base\Application]] by default.
* You can access that instance via `Yii::$app->formatter`.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/

38
framework/yii/base/Model.php

@ -46,7 +46,8 @@ use yii\validators\Validator;
* @property ArrayIterator $iterator An iterator for traversing the items in the list. This property is
* read-only.
* @property string $scenario The scenario that this model is in. Defaults to [[DEFAULT_SCENARIO]].
* @property ArrayObject $validators All the validators declared in the model. This property is read-only.
* @property ArrayObject|\yii\validators\Validator[] $validators All the validators declared in the model.
* This property is read-only.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
@ -91,19 +92,19 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
*
* ~~~
* [
* 'attribute list',
* ['attribute1', 'attribute2'],
* 'validator type',
* 'on' => 'scenario name',
* 'on' => ['scenario1', 'scenario2'],
* ...other parameters...
* ]
* ~~~
*
* where
*
* - attribute list: required, specifies the attributes (separated by commas) to be validated;
* - attribute list: required, specifies the attributes array to be validated, for single attribute you can pass string;
* - validator type: required, specifies the validator to be used. It can be the name of a model
* class method, the name of a built-in validator, or a validator class name (or its path alias).
* - on: optional, specifies the [[scenario|scenarios]] (separated by commas) when the validation
* - on: optional, specifies the [[scenario|scenarios]] array when the validation
* rule can be applied. If this option is not set, the rule will apply to all scenarios.
* - additional name-value pairs can be specified to initialize the corresponding validator properties.
* Please refer to individual validator class API for possible properties.
@ -128,7 +129,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
* ~~~
* [
* // built-in "required" validator
* ['username', 'required'],
* [['username', 'password'], 'required'],
* // built-in "string" validator customized with "min" and "max" properties
* ['username', 'string', 'min' => 3, 'max' => 12],
* // built-in "compare" validator that is used in "register" scenario only
@ -144,7 +145,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
* merge the parent rules with child rules using functions such as `array_merge()`.
*
* @return array validation rules
* @see scenarios
* @see scenarios()
*/
public function rules()
{
@ -255,7 +256,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
* merge the parent labels with child labels using functions such as `array_merge()`.
*
* @return array attribute labels (name => label)
* @see generateAttributeLabel
* @see generateAttributeLabel()
*/
public function attributeLabels()
{
@ -349,7 +350,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
* $model->validators[] = $newValidator;
* ~~~
*
* @return ArrayObject all the validators declared in the model.
* @return ArrayObject|\yii\validators\Validator[] all the validators declared in the model.
*/
public function getValidators()
{
@ -369,7 +370,6 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
{
$validators = [];
$scenario = $this->getScenario();
/** @var $validator Validator */
foreach ($this->getValidators() as $validator) {
if ($validator->isActive($scenario) && ($attribute === null || in_array($attribute, $validator->attributes, true))) {
$validators[] = $validator;
@ -391,7 +391,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
if ($rule instanceof Validator) {
$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));
$validator = Validator::createValidator($rule[1], $this, (array) $rule[0], array_slice($rule, 2));
$validators->append($validator);
} else {
throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.');
@ -444,8 +444,8 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
* Returns the text label for the specified attribute.
* @param string $attribute the attribute name
* @return string the attribute label
* @see generateAttributeLabel
* @see attributeLabels
* @see generateAttributeLabel()
* @see attributeLabels()
*/
public function getAttributeLabel($attribute)
{
@ -483,8 +483,8 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
* ]
* ~~~
*
* @see getFirstErrors
* @see getFirstError
* @see getFirstErrors()
* @see getFirstError()
*/
public function getErrors($attribute = null)
{
@ -498,8 +498,8 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
/**
* Returns the first error of every attribute in the model.
* @return array the first errors. An empty array will be returned if there is no error.
* @see getErrors
* @see getFirstError
* @see getErrors()
* @see getFirstError()
*/
public function getFirstErrors()
{
@ -520,8 +520,8 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
* Returns the first error of the specified attribute.
* @param string $attribute attribute name.
* @return string the error message. Null is returned if no error.
* @see getErrors
* @see getFirstErrors
* @see getErrors()
* @see getFirstErrors()
*/
public function getFirstError($attribute)
{

2
framework/yii/base/Module.php

@ -578,7 +578,7 @@ abstract class Module extends Component
{
$parts = $this->createController($route);
if (is_array($parts)) {
/** @var $controller Controller */
/** @var Controller $controller */
list($controller, $actionID) = $parts;
$oldController = Yii::$app->controller;
Yii::$app->controller = $controller;

15
framework/yii/base/Object.php

@ -10,7 +10,10 @@ namespace yii\base;
use Yii;
/**
* Object is the base class that implements the *property* feature.
*
* @include @yii/base/Object.md
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
@ -64,7 +67,7 @@ class Object implements Arrayable
* @return mixed the property value
* @throws UnknownPropertyException if the property is not defined
* @throws InvalidCallException if the property is write-only
* @see __set
* @see __set()
*/
public function __get($name)
{
@ -87,7 +90,7 @@ class Object implements Arrayable
* @param mixed $value the property value
* @throws UnknownPropertyException if the property is not defined
* @throws InvalidCallException if the property is read-only
* @see __get
* @see __get()
*/
public function __set($name, $value)
{
@ -168,8 +171,8 @@ class Object implements Arrayable
* @param string $name the property name
* @param boolean $checkVars whether to treat member variables as properties
* @return boolean whether the property is defined
* @see canGetProperty
* @see canSetProperty
* @see canGetProperty()
* @see canSetProperty()
*/
public function hasProperty($name, $checkVars = true)
{
@ -187,7 +190,7 @@ class Object implements Arrayable
* @param string $name the property name
* @param boolean $checkVars whether to treat member variables as properties
* @return boolean whether the property can be read
* @see canSetProperty
* @see canSetProperty()
*/
public function canGetProperty($name, $checkVars = true)
{
@ -205,7 +208,7 @@ class Object implements Arrayable
* @param string $name the property name
* @param boolean $checkVars whether to treat member variables as properties
* @return boolean whether the property can be written
* @see canGetProperty
* @see canGetProperty()
*/
public function canSetProperty($name, $checkVars = true)
{

1
framework/yii/base/Request.php

@ -8,6 +8,7 @@
namespace yii\base;
/**
* Request represents a request that is handled by an [[Application]].
*
* @property boolean $isConsoleRequest The value indicating whether the current request is made via console.
* @property string $scriptFile Entry script file path (processed w/ realpath()).

2
framework/yii/base/Response.php

@ -8,6 +8,8 @@
namespace yii\base;
/**
* Response represents the response of an [[Application]] to a [[Request]].
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/

4
framework/yii/base/View.php

@ -123,7 +123,7 @@ class View extends Component
* existing [[context]] will be used.
* @return string the rendering result
* @throws InvalidParamException if the view cannot be resolved or the view file does not exist.
* @see renderFile
* @see renderFile()
*/
public function render($view, $params = [], $context = null)
{
@ -410,7 +410,7 @@ class View extends Component
{
$properties['id'] = $id;
$properties['view'] = $this;
/** @var $cache FragmentCache */
/** @var FragmentCache $cache */
$cache = FragmentCache::begin($properties);
if ($cache->getCachedContent() !== false) {
$this->endCache();

2
framework/yii/base/ViewEvent.php

@ -8,6 +8,8 @@
namespace yii\base;
/**
* ViewEvent represents events triggered by the [[View]] component.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/

14
framework/yii/caching/ChainedDependency.php

@ -23,7 +23,7 @@ class ChainedDependency extends Dependency
* @var Dependency[] list of dependencies that this dependency is composed of.
* Each array element must be a dependency object.
*/
public $dependencies;
public $dependencies = [];
/**
* @var boolean whether this dependency is depending on every dependency in [[dependencies]].
* Defaults to true, meaning if any of the dependencies has changed, this dependency is considered changed.
@ -33,18 +33,6 @@ class ChainedDependency extends Dependency
public $dependOnAll = true;
/**
* Constructor.
* @param Dependency[] $dependencies list of dependencies that this dependency is composed of.
* Each array element should be a dependency object.
* @param array $config name-value pairs that will be used to initialize the object properties
*/
public function __construct($dependencies = [], $config = [])
{
$this->dependencies = $dependencies;
parent::__construct($config);
}
/**
* Evaluates the dependency by generating and saving the data related with dependency.
* @param Cache $cache the cache component that is currently evaluating this dependency
*/

18
framework/yii/caching/DbDependency.php

@ -34,20 +34,7 @@ class DbDependency extends Dependency
/**
* @var array the parameters (name => value) to be bound to the SQL statement specified by [[sql]].
*/
public $params;
/**
* Constructor.
* @param string $sql the SQL query whose result is used to determine if the dependency has been changed.
* @param array $params the parameters (name => value) to be bound to the SQL statement specified by [[sql]].
* @param array $config name-value pairs that will be used to initialize the object properties
*/
public function __construct($sql, $params = [], $config = [])
{
$this->sql = $sql;
$this->params = $params;
parent::__construct($config);
}
public $params = [];
/**
* Generates the data needed to determine if dependency has been changed.
@ -62,6 +49,9 @@ class DbDependency extends Dependency
if (!$db instanceof Connection) {
throw new InvalidConfigException("DbDependency::db must be the application component ID of a DB connection.");
}
if ($this->sql === null) {
throw new InvalidConfigException("DbDependency::sql must be set.");
}
if ($db->enableQueryCache) {
// temporarily disable and re-enable query caching

15
framework/yii/caching/ExpressionDependency.php

@ -27,7 +27,7 @@ class ExpressionDependency extends Dependency
* A PHP expression can be any PHP code that evaluates to a value. To learn more about what an expression is,
* please refer to the [php manual](http://www.php.net/manual/en/language.expressions.php).
*/
public $expression;
public $expression = 'true';
/**
* @var mixed custom parameters associated with this dependency. You may get the value
* of this property in [[expression]] using `$this->params`.
@ -35,19 +35,6 @@ class ExpressionDependency extends Dependency
public $params;
/**
* Constructor.
* @param string $expression the PHP expression whose result is used to determine the dependency.
* @param mixed $params the custom parameters associated with this dependency
* @param array $config name-value pairs that will be used to initialize the object properties
*/
public function __construct($expression = 'true', $params = null, $config = [])
{
$this->expression = $expression;
$this->params = $params;
parent::__construct($config);
}
/**
* Generates the data needed to determine if dependency has been changed.
* This method returns the result of the PHP expression.
* @param Cache $cache the cache component that is currently evaluating this dependency

16
framework/yii/caching/FileDependency.php

@ -6,6 +6,7 @@
*/
namespace yii\caching;
use yii\base\InvalidConfigException;
/**
* FileDependency represents a dependency based on a file's last modification time.
@ -25,24 +26,17 @@ class FileDependency extends Dependency
public $fileName;
/**
* Constructor.
* @param string $fileName name of the file whose change is to be checked.
* @param array $config name-value pairs that will be used to initialize the object properties
*/
public function __construct($fileName = null, $config = [])
{
$this->fileName = $fileName;
parent::__construct($config);
}
/**
* Generates the data needed to determine if dependency has been changed.
* This method returns the file's last modification time.
* @param Cache $cache the cache component that is currently evaluating this dependency
* @return mixed the data needed to determine if dependency has been changed.
* @throws InvalidConfigException if [[fileName]] is not set
*/
protected function generateDependencyData($cache)
{
if ($this->fileName === null) {
throw new InvalidConfigException('FileDependency::fileName must be set');
}
return @filemtime($this->fileName);
}
}

22
framework/yii/caching/GroupDependency.php

@ -6,6 +6,7 @@
*/
namespace yii\caching;
use yii\base\InvalidConfigException;
/**
* GroupDependency marks a cached data item with a group name.
@ -19,29 +20,22 @@ namespace yii\caching;
class GroupDependency extends Dependency
{
/**
* @var string the group name
* @var string the group name. This property must be set.
*/
public $group;
/**
* Constructor.
* @param string $group the group name
* @param array $config name-value pairs that will be used to initialize the object properties
*/
public function __construct($group, $config = [])
{
$this->group = $group;
parent::__construct($config);
}
/**
* Generates the data needed to determine if dependency has been changed.
* This method does nothing in this class.
* @param Cache $cache the cache component that is currently evaluating this dependency
* @return mixed the data needed to determine if dependency has been changed.
* @throws InvalidConfigException if [[group]] is not set.
*/
protected function generateDependencyData($cache)
{
if ($this->group === null) {
throw new InvalidConfigException('GroupDependency::group must be set');
}
$version = $cache->get([__CLASS__, $this->group]);
if ($version === false) {
$version = $this->invalidate($cache, $this->group);
@ -53,9 +47,13 @@ class GroupDependency extends Dependency
* Performs the actual dependency checking.
* @param Cache $cache the cache component that is currently evaluating this dependency
* @return boolean whether the dependency is changed or not.
* @throws InvalidConfigException if [[group]] is not set.
*/
public function getHasChanged($cache)
{
if ($this->group === null) {
throw new InvalidConfigException('GroupDependency::group must be set');
}
$version = $cache->get([__CLASS__, $this->group]);
return $version === false || $version !== $this->data;
}

2
framework/yii/caching/MemCache.php

@ -31,7 +31,7 @@ use yii\base\InvalidConfigException;
* [
* 'components' => [
* 'cache' => [
* 'class' => 'MemCache',
* 'class' => 'yii\caching\MemCache',
* 'servers' => [
* [
* 'host' => 'server1',

2
framework/yii/captcha/CaptchaAsset.php

@ -10,6 +10,8 @@ namespace yii\captcha;
use yii\web\AssetBundle;
/**
* This asset bundle provides the javascript files needed for the [[Captcha]] widget.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/

4
framework/yii/console/Request.php

@ -8,6 +8,10 @@
namespace yii\console;
/**
* The console Request represents the environment information for a console application.
*
* It is a wrapper for the PHP `$_SERVER` variable which holds information about the
* currently running PHP script and the command line arguments given to it.
*
* @property array $params The command line arguments. It does not include the entry script name.
*

2
framework/yii/console/Response.php

@ -8,6 +8,8 @@
namespace yii\console;
/**
* The console Response represents the result of a console application by holding the [[exitCode]].
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/

2
framework/yii/console/controllers/CacheController.php

@ -52,7 +52,7 @@ class CacheController extends Controller
*/
public function actionFlush($component = 'cache')
{
/** @var $cache Cache */
/** @var Cache $cache */
$cache = Yii::$app->getComponent($component);
if (!$cache || !$cache instanceof Cache) {
throw new Exception('Application component "'.$component.'" is not defined or not a cache.');

20
framework/yii/data/ActiveDataProvider.php

@ -8,11 +8,11 @@
namespace yii\data;
use Yii;
use yii\db\ActiveQueryInterface;
use yii\base\InvalidConfigException;
use yii\base\Model;
use yii\db\Query;
use yii\db\ActiveQuery;
use yii\db\Connection;
use yii\db\QueryInterface;
/**
* ActiveDataProvider implements a data provider based on [[Query]] and [[ActiveQuery]].
@ -54,7 +54,7 @@ use yii\db\Connection;
class ActiveDataProvider extends BaseDataProvider
{
/**
* @var Query the query that is used to fetch data models and [[totalCount]]
* @var QueryInterface the query that is used to fetch data models and [[totalCount]]
* if it is not explicitly set.
*/
public $query;
@ -97,8 +97,8 @@ class ActiveDataProvider extends BaseDataProvider
*/
protected function prepareModels()
{
if (!$this->query instanceof Query) {
throw new InvalidConfigException('The "query" property must be an instance of Query or its subclass.');
if (!$this->query instanceof QueryInterface) {
throw new InvalidConfigException('The "query" property must be an instance of a class that implements the QueryInterface e.g. yii\db\Query or its subclasses.');
}
if (($pagination = $this->getPagination()) !== false) {
$pagination->totalCount = $this->getTotalCount();
@ -125,7 +125,7 @@ class ActiveDataProvider extends BaseDataProvider
}
}
return $keys;
} elseif ($this->query instanceof ActiveQuery) {
} elseif ($this->query instanceof ActiveQueryInterface) {
/** @var \yii\db\ActiveRecord $class */
$class = $this->query->modelClass;
$pks = $class::primaryKey();
@ -154,11 +154,11 @@ class ActiveDataProvider extends BaseDataProvider
*/
protected function prepareTotalCount()
{
if (!$this->query instanceof Query) {
throw new InvalidConfigException('The "query" property must be an instance of Query or its subclass.');
if (!$this->query instanceof QueryInterface) {
throw new InvalidConfigException('The "query" property must be an instance of a class that implements the QueryInterface e.g. yii\db\Query or its subclasses.');
}
$query = clone $this->query;
return (int) $query->limit(-1)->offset(-1)->count('*', $this->db);
return (int) $query->limit(-1)->offset(-1)->orderBy([])->count('*', $this->db);
}
/**
@ -167,7 +167,7 @@ class ActiveDataProvider extends BaseDataProvider
public function setSort($value)
{
parent::setSort($value);
if (($sort = $this->getSort()) !== false && empty($sort->attributes) && $this->query instanceof ActiveQuery) {
if (($sort = $this->getSort()) !== false && empty($sort->attributes) && $this->query instanceof ActiveQueryInterface) {
/** @var Model $model */
$model = new $this->query->modelClass;
foreach ($model->attributes() as $attribute) {

205
framework/yii/db/ActiveQuery.php

@ -11,8 +11,7 @@ namespace yii\db;
/**
* ActiveQuery represents a DB query associated with an Active Record class.
*
* ActiveQuery instances are usually created by [[ActiveRecord::find()]], [[ActiveRecord::findBySql()]]
* and [[ActiveRecord::count()]].
* ActiveQuery instances are usually created by [[ActiveRecord::find()]] and [[ActiveRecord::findBySql()]].
*
* ActiveQuery mainly provides the following methods to retrieve the query results:
*
@ -42,23 +41,13 @@ namespace yii\db;
* ~~~
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class ActiveQuery extends Query
class ActiveQuery extends Query implements ActiveQueryInterface
{
/**
* @var string the name of the ActiveRecord class.
*/
public $modelClass;
/**
* @var array list of relations that this query should be performed with
*/
public $with;
/**
* @var boolean whether to return each record as an array. If false (default), an object
* of [[modelClass]] will be created to represent each record.
*/
public $asArray;
use ActiveQueryTrait;
/**
* @var string the SQL statement to be executed for retrieving AR records.
* This is set by [[ActiveRecord::findBySql()]].
@ -67,25 +56,6 @@ class ActiveQuery extends Query
/**
* PHP magic method.
* This method allows calling static method defined in [[modelClass]] via this query object.
* It is mainly implemented for supporting the feature of scope.
* @param string $name the method name to be called
* @param array $params the parameters passed to the method
* @return mixed the method return result
*/
public function __call($name, $params)
{
if (method_exists($this->modelClass, $name)) {
array_unshift($params, $this);
call_user_func_array([$this->modelClass, $name], $params);
return $this;
} else {
return parent::__call($name, $params);
}
}
/**
* Executes query and returns all results as an array.
* @param Connection $db the DB connection used to create the DB command.
* If null, the DB connection returned by [[modelClass]] will be used.
@ -122,7 +92,7 @@ class ActiveQuery extends Query
if ($this->asArray) {
$model = $row;
} else {
/** @var $class ActiveRecord */
/** @var ActiveRecord $class */
$class = $this->modelClass;
$model = $class::create($row);
}
@ -145,7 +115,7 @@ class ActiveQuery extends Query
*/
public function createCommand($db = null)
{
/** @var $modelClass ActiveRecord */
/** @var ActiveRecord $modelClass */
$modelClass = $this->modelClass;
if ($db === null) {
$db = $modelClass::getDb();
@ -164,165 +134,4 @@ class ActiveQuery extends Query
}
return $db->createCommand($this->sql, $params);
}
/**
* Sets the [[asArray]] property.
* @param boolean $value whether to return the query results in terms of arrays instead of Active Records.
* @return static the query object itself
*/
public function asArray($value = true)
{
$this->asArray = $value;
return $this;
}
/**
* Specifies the relations with which this query should be performed.
*
* The parameters to this method can be either one or multiple strings, or a single array
* of relation names and the optional callbacks to customize the relations.
*
* A relation name can refer to a relation defined in [[modelClass]]
* or a sub-relation that stands for a relation of a related record.
* For example, `orders.address` means the `address` relation defined
* in the model class corresponding to the `orders` relation.
*
* The followings are some usage examples:
*
* ~~~
* // find customers together with their orders and country
* Customer::find()->with('orders', 'country')->all();
* // find customers together with their orders and the orders' shipping address
* Customer::find()->with('orders.address')->all();
* // find customers together with their country and orders of status 1
* Customer::find()->with([
* 'orders' => function($query) {
* $query->andWhere('status = 1');
* },
* 'country',
* ])->all();
* ~~~
*
* @return static the query object itself
*/
public function with()
{
$this->with = func_get_args();
if (isset($this->with[0]) && is_array($this->with[0])) {
// the parameter is given as an array
$this->with = $this->with[0];
}
return $this;
}
/**
* Sets the [[indexBy]] property.
* @param string|callable $column the name of the column by which the query results should be indexed by.
* This can also be a callable (e.g. anonymous function) that returns the index value based on the given
* row or model data. The signature of the callable should be:
*
* ~~~
* // $model is an AR instance when `asArray` is false,
* // or an array of column values when `asArray` is true.
* function ($model)
* {
* // return the index value corresponding to $model
* }
* ~~~
*
* @return static the query object itself
*/
public function indexBy($column)
{
return parent::indexBy($column);
}
private function createModels($rows)
{
$models = [];
if ($this->asArray) {
if ($this->indexBy === null) {
return $rows;
}
foreach ($rows as $row) {
if (is_string($this->indexBy)) {
$key = $row[$this->indexBy];
} else {
$key = call_user_func($this->indexBy, $row);
}
$models[$key] = $row;
}
} else {
/** @var $class ActiveRecord */
$class = $this->modelClass;
if ($this->indexBy === null) {
foreach ($rows as $row) {
$models[] = $class::create($row);
}
} else {
foreach ($rows as $row) {
$model = $class::create($row);
if (is_string($this->indexBy)) {
$key = $model->{$this->indexBy};
} else {
$key = call_user_func($this->indexBy, $model);
}
$models[$key] = $model;
}
}
}
return $models;
}
private function populateRelations(&$models, $with)
{
$primaryModel = new $this->modelClass;
$relations = $this->normalizeRelations($primaryModel, $with);
foreach ($relations as $name => $relation) {
if ($relation->asArray === null) {
// inherit asArray from primary query
$relation->asArray = $this->asArray;
}
$relation->findWith($name, $models);
}
}
/**
* @param ActiveRecord $model
* @param array $with
* @return ActiveRelation[]
*/
private function normalizeRelations($model, $with)
{
$relations = [];
foreach ($with as $name => $callback) {
if (is_integer($name)) {
$name = $callback;
$callback = null;
}
if (($pos = strpos($name, '.')) !== false) {
// with sub-relations
$childName = substr($name, $pos + 1);
$name = substr($name, 0, $pos);
} else {
$childName = null;
}
$t = strtolower($name);
if (!isset($relations[$t])) {
$relation = $model->getRelation($name);
$relation->primaryModel = null;
$relations[$t] = $relation;
} else {
$relation = $relations[$t];
}
if (isset($childName)) {
$relation->with[$childName] = $callback;
} elseif ($callback !== null) {
call_user_func($callback, $relation);
}
}
return $relations;
}
}

77
framework/yii/db/ActiveQueryInterface.php

@ -0,0 +1,77 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db;
/**
* ActiveQueryInterface defines the common interface to be implemented by active record query classes.
*
* A class implementing this interface should also use [[ActiveQueryTrait]].
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
interface ActiveQueryInterface extends QueryInterface
{
/**
* Sets the [[asArray]] property.
* @param boolean $value whether to return the query results in terms of arrays instead of Active Records.
* @return static the query object itself
*/
public function asArray($value = true);
/**
* Sets the [[indexBy]] property.
* @param string|callable $column the name of the column by which the query results should be indexed by.
* This can also be a callable (e.g. anonymous function) that returns the index value based on the given
* row or model data. The signature of the callable should be:
*
* ~~~
* // $model is an AR instance when `asArray` is false,
* // or an array of column values when `asArray` is true.
* function ($model)
* {
* // return the index value corresponding to $model
* }
* ~~~
*
* @return static the query object itself
*/
public function indexBy($column);
/**
* Specifies the relations with which this query should be performed.
*
* The parameters to this method can be either one or multiple strings, or a single array
* of relation names and the optional callbacks to customize the relations.
*
* A relation name can refer to a relation defined in [[modelClass]]
* or a sub-relation that stands for a relation of a related record.
* For example, `orders.address` means the `address` relation defined
* in the model class corresponding to the `orders` relation.
*
* The followings are some usage examples:
*
* ~~~
* // find customers together with their orders and country
* Customer::find()->with('orders', 'country')->all();
* // find customers together with their orders and the orders' shipping address
* Customer::find()->with('orders.address')->all();
* // find customers together with their country and orders of status 1
* Customer::find()->with([
* 'orders' => function($query) {
* $query->andWhere('status = 1');
* },
* 'country',
* ])->all();
* ~~~
*
* @return static the query object itself
*/
public function with();
}

199
framework/yii/db/ActiveQueryTrait.php

@ -0,0 +1,199 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db;
/**
* ActiveQueryTrait implements the common methods and properties for active record query classes.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
trait ActiveQueryTrait
{
/**
* @var string the name of the ActiveRecord class.
*/
public $modelClass;
/**
* @var array list of relations that this query should be performed with
*/
public $with;
/**
* @var boolean whether to return each record as an array. If false (default), an object
* of [[modelClass]] will be created to represent each record.
*/
public $asArray;
/**
* PHP magic method.
* This method allows calling static method defined in [[modelClass]] via this query object.
* It is mainly implemented for supporting the feature of scope.
* @param string $name the method name to be called
* @param array $params the parameters passed to the method
* @return mixed the method return result
*/
public function __call($name, $params)
{
if (method_exists($this->modelClass, $name)) {
array_unshift($params, $this);
call_user_func_array([$this->modelClass, $name], $params);
return $this;
} else {
return parent::__call($name, $params);
}
}
/**
* Sets the [[asArray]] property.
* @param boolean $value whether to return the query results in terms of arrays instead of Active Records.
* @return static the query object itself
*/
public function asArray($value = true)
{
$this->asArray = $value;
return $this;
}
/**
* Specifies the relations with which this query should be performed.
*
* The parameters to this method can be either one or multiple strings, or a single array
* of relation names and the optional callbacks to customize the relations.
*
* A relation name can refer to a relation defined in [[modelClass]]
* or a sub-relation that stands for a relation of a related record.
* For example, `orders.address` means the `address` relation defined
* in the model class corresponding to the `orders` relation.
*
* The followings are some usage examples:
*
* ~~~
* // find customers together with their orders and country
* Customer::find()->with('orders', 'country')->all();
* // find customers together with their orders and the orders' shipping address
* Customer::find()->with('orders.address')->all();
* // find customers together with their country and orders of status 1
* Customer::find()->with([
* 'orders' => function($query) {
* $query->andWhere('status = 1');
* },
* 'country',
* ])->all();
* ~~~
*
* @return static the query object itself
*/
public function with()
{
$this->with = func_get_args();
if (isset($this->with[0]) && is_array($this->with[0])) {
// the parameter is given as an array
$this->with = $this->with[0];
}
return $this;
}
/**
* Converts found rows into model instances
* @param array $rows
* @return array|ActiveRecord[]
*/
private function createModels($rows)
{
$models = [];
if ($this->asArray) {
if ($this->indexBy === null) {
return $rows;
}
foreach ($rows as $row) {
if (is_string($this->indexBy)) {
$key = $row[$this->indexBy];
} else {
$key = call_user_func($this->indexBy, $row);
}
$models[$key] = $row;
}
} else {
/** @var ActiveRecord $class */
$class = $this->modelClass;
if ($this->indexBy === null) {
foreach ($rows as $row) {
$models[] = $class::create($row);
}
} else {
foreach ($rows as $row) {
$model = $class::create($row);
if (is_string($this->indexBy)) {
$key = $model->{$this->indexBy};
} else {
$key = call_user_func($this->indexBy, $model);
}
$models[$key] = $model;
}
}
}
return $models;
}
/**
* @param ActiveRecord[] $models
* @param array $with
*/
private function populateRelations(&$models, $with)
{
$primaryModel = new $this->modelClass;
$relations = $this->normalizeRelations($primaryModel, $with);
foreach ($relations as $name => $relation) {
if ($relation->asArray === null) {
// inherit asArray from primary query
$relation->asArray = $this->asArray;
}
$relation->findWith($name, $models);
}
}
/**
* @param ActiveRecord $model
* @param array $with
* @return ActiveRelationInterface[]
*/
private function normalizeRelations($model, $with)
{
$relations = [];
foreach ($with as $name => $callback) {
if (is_integer($name)) {
$name = $callback;
$callback = null;
}
if (($pos = strpos($name, '.')) !== false) {
// with sub-relations
$childName = substr($name, $pos + 1);
$name = substr($name, 0, $pos);
} else {
$childName = null;
}
if (!isset($relations[$name])) {
$relation = $model->getRelation($name);
$relation->primaryModel = null;
$relations[$name] = $relation;
} else {
$relation = $relations[$name];
}
if (isset($childName)) {
$relation->with[$childName] = $callback;
} elseif ($callback !== null) {
call_user_func($callback, $relation);
}
}
return $relations;
}
}

47
framework/yii/db/ActiveRecord.php

@ -29,11 +29,14 @@ use yii\helpers\Inflector;
* @property mixed $oldPrimaryKey The old primary key value. An array (column name => column value) is
* returned if the primary key is composite or `$asArray` is true. A string is returned otherwise (null will be
* returned if the key value is null). This property is read-only.
* @property array $populatedRelations An array of relation data indexed by relation names. This property is
* read-only.
* @property mixed $primaryKey The primary key value. An array (column name => column value) is returned if
* the primary key is composite or `$asArray` is true. A string is returned otherwise (null will be returned if
* the key value is null). This property is read-only.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class ActiveRecord extends Model
@ -250,7 +253,7 @@ class ActiveRecord extends Model
/**
* Creates an [[ActiveQuery]] instance.
* This method is called by [[find()]], [[findBySql()]] and [[count()]] to start a SELECT query.
* This method is called by [[find()]], [[findBySql()]] to start a SELECT query.
* You may override this method to return a customized query (e.g. `CustomerQuery` specified
* written for querying `Customer` purpose.)
* @return ActiveQuery the newly created [[ActiveQuery]] instance.
@ -370,7 +373,7 @@ class ActiveRecord extends Model
* This method is overridden so that attributes and related objects can be accessed like properties.
* @param string $name property name
* @return mixed property value
* @see getAttribute
* @see getAttribute()
*/
public function __get($name)
{
@ -383,7 +386,7 @@ class ActiveRecord extends Model
return $this->_related[$name];
}
$value = parent::__get($name);
if ($value instanceof ActiveRelation) {
if ($value instanceof ActiveRelationInterface) {
return $this->_related[$name] = $value->multiple ? $value->all() : $value->one();
} else {
return $value;
@ -472,7 +475,7 @@ class ActiveRecord extends Model
*/
public function hasOne($class, $link)
{
return new ActiveRelation([
return $this->createActiveRelation([
'modelClass' => $class,
'primaryModel' => $this,
'link' => $link,
@ -510,7 +513,7 @@ class ActiveRecord extends Model
*/
public function hasMany($class, $link)
{
return new ActiveRelation([
return $this->createActiveRelation([
'modelClass' => $class,
'primaryModel' => $this,
'link' => $link,
@ -519,6 +522,18 @@ class ActiveRecord extends Model
}
/**
* Creates an [[ActiveRelation]] instance.
* This method is called by [[hasOne()]] and [[hasMany()]] to create a relation instance.
* You may override this method to return a customized relation.
* @param array $config the configuration passed to the ActiveRelation class.
* @return ActiveRelation the newly created [[ActiveRelation]] instance.
*/
protected function createActiveRelation($config = [])
{
return new ActiveRelation($config);
}
/**
* Populates the named relation with the related records.
* Note that this method does not check if the relation exists or not.
* @param string $name the relation name (case-sensitive)
@ -574,7 +589,7 @@ class ActiveRecord extends Model
* null will be returned.
* @param string $name the attribute name
* @return mixed the attribute value. Null if the attribute is not set or does not exist.
* @see hasAttribute
* @see hasAttribute()
*/
public function getAttribute($name)
{
@ -586,7 +601,7 @@ class ActiveRecord extends Model
* @param string $name the attribute name
* @param mixed $value the attribute value.
* @throws InvalidParamException if the named attribute does not exist.
* @see hasAttribute
* @see hasAttribute()
*/
public function setAttribute($name, $value)
{
@ -623,7 +638,7 @@ class ActiveRecord extends Model
* @param string $name the attribute name
* @return mixed the old attribute value. Null if the attribute is not loaded before
* or does not exist.
* @see hasAttribute
* @see hasAttribute()
*/
public function getOldAttribute($name)
{
@ -635,7 +650,7 @@ class ActiveRecord extends Model
* @param string $name the attribute name
* @param mixed $value the old attribute value.
* @throws InvalidParamException if the named attribute does not exist.
* @see hasAttribute
* @see hasAttribute()
*/
public function setOldAttribute($name, $value)
{
@ -1028,7 +1043,7 @@ class ActiveRecord extends Model
/**
* Sets the value indicating whether the record is new.
* @param boolean $value whether the record is new and should be inserted when calling [[save()]].
* @see getIsNewRecord
* @see getIsNewRecord()
*/
public function setIsNewRecord($value)
{
@ -1281,7 +1296,7 @@ class ActiveRecord extends Model
$getter = 'get' . $name;
try {
$relation = $this->$getter();
if ($relation instanceof ActiveRelation) {
if ($relation instanceof ActiveRelationInterface) {
return $relation;
} else {
return null;
@ -1319,9 +1334,9 @@ class ActiveRecord extends Model
throw new InvalidCallException('Unable to link models: both models must NOT be newly created.');
}
if (is_array($relation->via)) {
/** @var $viaRelation ActiveRelation */
/** @var ActiveRelation $viaRelation */
list($viaName, $viaRelation) = $relation->via;
/** @var $viaClass ActiveRecord */
/** @var ActiveRecord $viaClass */
$viaClass = $viaRelation->modelClass;
$viaTable = $viaClass::tableName();
// unset $viaName so that it can be reloaded to reflect the change
@ -1394,9 +1409,9 @@ class ActiveRecord extends Model
if ($relation->via !== null) {
if (is_array($relation->via)) {
/** @var $viaRelation ActiveRelation */
/** @var ActiveRelation $viaRelation */
list($viaName, $viaRelation) = $relation->via;
/** @var $viaClass ActiveRecord */
/** @var ActiveRecord $viaClass */
$viaClass = $viaRelation->modelClass;
$viaTable = $viaClass::tableName();
unset($this->_related[$viaName]);
@ -1442,7 +1457,7 @@ class ActiveRecord extends Model
if (!$relation->multiple) {
unset($this->_related[$name]);
} elseif (isset($this->_related[$name])) {
/** @var $b ActiveRecord */
/** @var ActiveRecord $b */
foreach ($this->_related[$name] as $a => $b) {
if ($model->getPrimaryKey() == $b->getPrimaryKey()) {
unset($this->_related[$name][$a]);

236
framework/yii/db/ActiveRelation.php

@ -8,8 +8,6 @@
namespace yii\db;
use yii\base\InvalidConfigException;
/**
* ActiveRelation represents a relation between two Active Record classes.
*
@ -22,61 +20,16 @@ use yii\base\InvalidConfigException;
*
* If a relation involves a pivot table, it may be specified by [[via()]] or [[viaTable()]] method.
*
* @property array|ActiveRelation $via the query associated with the pivot table. Please call [[via()]]
* or [[viaTable()]] to set this property instead of directly setting it.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class ActiveRelation extends ActiveQuery
class ActiveRelation extends ActiveQuery implements ActiveRelationInterface
{
/**
* @var boolean whether this relation should populate all query results into AR instances.
* If false, only the first row of the results will be retrieved.
*/
public $multiple;
/**
* @var ActiveRecord the primary model that this relation is associated with.
* This is used only in lazy loading with dynamic query options.
*/
public $primaryModel;
/**
* @var array the columns of the primary and foreign tables that establish the relation.
* The array keys must be columns of the table for this relation, and the array values
* must be the corresponding columns from the primary table.
* Do not prefix or quote the column names as they will be done automatically by Yii.
*/
public $link;
/**
* @var array|ActiveRelation the query associated with the pivot table. Please call [[via()]]
* or [[viaTable()]] to set this property instead of directly setting it.
*/
public $via;
/**
* Clones internal objects.
*/
public function __clone()
{
if (is_object($this->via)) {
// make a clone of "via" object so that the same query object can be reused multiple times
$this->via = clone $this->via;
}
}
/**
* Specifies the relation associated with the pivot table.
* @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]].
* @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
* Its signature should be `function($query)`, where `$query` is the query to be customized.
* @return static the relation object itself.
*/
public function via($relationName, $callable = null)
{
$relation = $this->primaryModel->getRelation($relationName);
$this->via = [$relationName, $relation];
if ($callable !== null) {
call_user_func($callable, $relation);
}
return $this;
}
use ActiveRelationTrait;
/**
* Specifies the pivot table.
@ -120,7 +73,7 @@ class ActiveRelation extends ActiveQuery
$this->filterByModels($viaModels);
} elseif (is_array($this->via)) {
// via relation
/** @var $viaQuery ActiveRelation */
/** @var ActiveRelation $viaQuery */
list($viaName, $viaQuery) = $this->via;
if ($viaQuery->multiple) {
$viaModels = $viaQuery->all();
@ -137,179 +90,4 @@ class ActiveRelation extends ActiveQuery
}
return parent::createCommand($db);
}
/**
* Finds the related records and populates them into the primary models.
* This method is internally used by [[ActiveQuery]]. Do not call it directly.
* @param string $name the relation name
* @param array $primaryModels primary models
* @return array the related models
* @throws InvalidConfigException
*/
public function findWith($name, &$primaryModels)
{
if (!is_array($this->link)) {
throw new InvalidConfigException('Invalid link: it must be an array of key-value pairs.');
}
if ($this->via instanceof self) {
// via pivot table
/** @var $viaQuery ActiveRelation */
$viaQuery = $this->via;
$viaModels = $viaQuery->findPivotRows($primaryModels);
$this->filterByModels($viaModels);
} elseif (is_array($this->via)) {
// via relation
/** @var $viaQuery ActiveRelation */
list($viaName, $viaQuery) = $this->via;
$viaQuery->primaryModel = null;
$viaModels = $viaQuery->findWith($viaName, $primaryModels);
$this->filterByModels($viaModels);
} else {
$this->filterByModels($primaryModels);
}
if (count($primaryModels) === 1 && !$this->multiple) {
$model = $this->one();
foreach ($primaryModels as $i => $primaryModel) {
if ($primaryModel instanceof ActiveRecord) {
$primaryModel->populateRelation($name, $model);
} else {
$primaryModels[$i][$name] = $model;
}
}
return [$model];
} else {
$models = $this->all();
if (isset($viaModels, $viaQuery)) {
$buckets = $this->buildBuckets($models, $this->link, $viaModels, $viaQuery->link);
} else {
$buckets = $this->buildBuckets($models, $this->link);
}
$link = array_values(isset($viaQuery) ? $viaQuery->link : $this->link);
foreach ($primaryModels as $i => $primaryModel) {
$key = $this->getModelKey($primaryModel, $link);
$value = isset($buckets[$key]) ? $buckets[$key] : ($this->multiple ? [] : null);
if ($primaryModel instanceof ActiveRecord) {
$primaryModel->populateRelation($name, $value);
} else {
$primaryModels[$i][$name] = $value;
}
}
return $models;
}
}
/**
* @param array $models
* @param array $link
* @param array $viaModels
* @param array $viaLink
* @return array
*/
private function buildBuckets($models, $link, $viaModels = null, $viaLink = null)
{
$buckets = [];
$linkKeys = array_keys($link);
foreach ($models as $i => $model) {
$key = $this->getModelKey($model, $linkKeys);
if ($this->indexBy !== null) {
$buckets[$key][$i] = $model;
} else {
$buckets[$key][] = $model;
}
}
if ($viaModels !== null) {
$viaBuckets = [];
$viaLinkKeys = array_keys($viaLink);
$linkValues = array_values($link);
foreach ($viaModels as $viaModel) {
$key1 = $this->getModelKey($viaModel, $viaLinkKeys);
$key2 = $this->getModelKey($viaModel, $linkValues);
if (isset($buckets[$key2])) {
foreach ($buckets[$key2] as $i => $bucket) {
if ($this->indexBy !== null) {
$viaBuckets[$key1][$i] = $bucket;
} else {
$viaBuckets[$key1][] = $bucket;
}
}
}
}
$buckets = $viaBuckets;
}
if (!$this->multiple) {
foreach ($buckets as $i => $bucket) {
$buckets[$i] = reset($bucket);
}
}
return $buckets;
}
/**
* @param ActiveRecord|array $model
* @param array $attributes
* @return string
*/
private function getModelKey($model, $attributes)
{
if (count($attributes) > 1) {
$key = [];
foreach ($attributes as $attribute) {
$key[] = $model[$attribute];
}
return serialize($key);
} else {
$attribute = reset($attributes);
return $model[$attribute];
}
}
/**
* @param array $models
*/
private function filterByModels($models)
{
$attributes = array_keys($this->link);
$values = [];
if (count($attributes) === 1) {
// single key
$attribute = reset($this->link);
foreach ($models as $model) {
if (($value = $model[$attribute]) !== null) {
$values[] = $value;
}
}
} else {
// composite keys
foreach ($models as $model) {
$v = [];
foreach ($this->link as $attribute => $link) {
$v[$attribute] = $model[$link];
}
$values[] = $v;
}
}
$this->andWhere(['in', $attributes, array_unique($values, SORT_REGULAR)]);
}
/**
* @param ActiveRecord[] $primaryModels
* @return array
*/
private function findPivotRows($primaryModels)
{
if (empty($primaryModels)) {
return [];
}
$this->filterByModels($primaryModels);
/** @var $primaryModel ActiveRecord */
$primaryModel = reset($primaryModels);
$db = $primaryModel->getDb();
list ($sql, $params) = $db->getQueryBuilder()->build($this);
return $db->createCommand($sql, $params)->queryAll();
}
}

29
framework/yii/db/ActiveRelationInterface.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 yii\db;
/**
* ActiveRelationInterface defines the common interface to be implemented by active record relation classes.
*
* A class implementing this interface should also use [[ActiveRelationTrait]].
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
interface ActiveRelationInterface extends ActiveQueryInterface
{
/**
* Specifies the relation associated with the pivot table.
* @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]].
* @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
* Its signature should be `function($query)`, where `$query` is the query to be customized.
* @return static the relation object itself.
*/
public function via($relationName, $callable = null);
}

246
framework/yii/db/ActiveRelationTrait.php

@ -0,0 +1,246 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db;
use yii\base\InvalidConfigException;
/**
* ActiveRelationTrait implements the common methods and properties for active record relation classes.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
trait ActiveRelationTrait
{
/**
* @var boolean whether this relation should populate all query results into AR instances.
* If false, only the first row of the results will be retrieved.
*/
public $multiple;
/**
* @var ActiveRecord the primary model that this relation is associated with.
* This is used only in lazy loading with dynamic query options.
*/
public $primaryModel;
/**
* @var array the columns of the primary and foreign tables that establish the relation.
* The array keys must be columns of the table for this relation, and the array values
* must be the corresponding columns from the primary table.
* Do not prefix or quote the column names as this will be done automatically by Yii.
*/
public $link;
/**
* @var array the query associated with the pivot table. Please call [[via()]]
* to set this property instead of directly setting it.
*/
public $via;
/**
* Clones internal objects.
*/
public function __clone()
{
// make a clone of "via" object so that the same query object can be reused multiple times
if (is_object($this->via)) {
$this->via = clone $this->via;
} elseif (is_array($this->via)) {
$this->via = [$this->via[0], clone $this->via[1]];
}
}
/**
* Specifies the relation associated with the pivot table.
* @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]].
* @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
* Its signature should be `function($query)`, where `$query` is the query to be customized.
* @return static the relation object itself.
*/
public function via($relationName, $callable = null)
{
$relation = $this->primaryModel->getRelation($relationName);
$this->via = [$relationName, $relation];
if ($callable !== null) {
call_user_func($callable, $relation);
}
return $this;
}
/**
* Finds the related records and populates them into the primary models.
* This method is internally used by [[ActiveQuery]]. Do not call it directly.
* @param string $name the relation name
* @param array $primaryModels primary models
* @return array the related models
* @throws InvalidConfigException
*/
public function findWith($name, &$primaryModels)
{
if (!is_array($this->link)) {
throw new InvalidConfigException('Invalid link: it must be an array of key-value pairs.');
}
if ($this->via instanceof self) {
// via pivot table
/** @var ActiveRelationTrait $viaQuery */
$viaQuery = $this->via;
$viaModels = $viaQuery->findPivotRows($primaryModels);
$this->filterByModels($viaModels);
} elseif (is_array($this->via)) {
// via relation
/** @var ActiveRelationTrait $viaQuery */
list($viaName, $viaQuery) = $this->via;
$viaQuery->primaryModel = null;
$viaModels = $viaQuery->findWith($viaName, $primaryModels);
$this->filterByModels($viaModels);
} else {
$this->filterByModels($primaryModels);
}
if (count($primaryModels) === 1 && !$this->multiple) {
$model = $this->one();
foreach ($primaryModels as $i => $primaryModel) {
if ($primaryModel instanceof ActiveRecord) {
$primaryModel->populateRelation($name, $model);
} else {
$primaryModels[$i][$name] = $model;
}
}
return [$model];
} else {
$models = $this->all();
if (isset($viaModels, $viaQuery)) {
$buckets = $this->buildBuckets($models, $this->link, $viaModels, $viaQuery->link);
} else {
$buckets = $this->buildBuckets($models, $this->link);
}
$link = array_values(isset($viaQuery) ? $viaQuery->link : $this->link);
foreach ($primaryModels as $i => $primaryModel) {
$key = $this->getModelKey($primaryModel, $link);
$value = isset($buckets[$key]) ? $buckets[$key] : ($this->multiple ? [] : null);
if ($primaryModel instanceof ActiveRecord) {
$primaryModel->populateRelation($name, $value);
} else {
$primaryModels[$i][$name] = $value;
}
}
return $models;
}
}
/**
* @param array $models
* @param array $link
* @param array $viaModels
* @param array $viaLink
* @return array
*/
private function buildBuckets($models, $link, $viaModels = null, $viaLink = null)
{
$buckets = [];
$linkKeys = array_keys($link);
foreach ($models as $i => $model) {
$key = $this->getModelKey($model, $linkKeys);
if ($this->indexBy !== null) {
$buckets[$key][$i] = $model;
} else {
$buckets[$key][] = $model;
}
}
if ($viaModels !== null) {
$viaBuckets = [];
$viaLinkKeys = array_keys($viaLink);
$linkValues = array_values($link);
foreach ($viaModels as $viaModel) {
$key1 = $this->getModelKey($viaModel, $viaLinkKeys);
$key2 = $this->getModelKey($viaModel, $linkValues);
if (isset($buckets[$key2])) {
foreach ($buckets[$key2] as $i => $bucket) {
if ($this->indexBy !== null) {
$viaBuckets[$key1][$i] = $bucket;
} else {
$viaBuckets[$key1][] = $bucket;
}
}
}
}
$buckets = $viaBuckets;
}
if (!$this->multiple) {
foreach ($buckets as $i => $bucket) {
$buckets[$i] = reset($bucket);
}
}
return $buckets;
}
/**
* @param ActiveRecord|array $model
* @param array $attributes
* @return string
*/
private function getModelKey($model, $attributes)
{
if (count($attributes) > 1) {
$key = [];
foreach ($attributes as $attribute) {
$key[] = $model[$attribute];
}
return serialize($key);
} else {
$attribute = reset($attributes);
return $model[$attribute];
}
}
/**
* @param array $models
*/
private function filterByModels($models)
{
$attributes = array_keys($this->link);
$values = [];
if (count($attributes) === 1) {
// single key
$attribute = reset($this->link);
foreach ($models as $model) {
if (($value = $model[$attribute]) !== null) {
$values[] = $value;
}
}
} else {
// composite keys
foreach ($models as $model) {
$v = [];
foreach ($this->link as $attribute => $link) {
$v[$attribute] = $model[$link];
}
$values[] = $v;
}
}
$this->andWhere(['in', $attributes, array_unique($values, SORT_REGULAR)]);
}
/**
* @param ActiveRecord[] $primaryModels
* @return array
*/
private function findPivotRows($primaryModels)
{
if (empty($primaryModels)) {
return [];
}
$this->filterByModels($primaryModels);
/** @var ActiveRecord $primaryModel */
$primaryModel = reset($primaryModels);
return $this->asArray()->all($primaryModel->getDb());
}
}

6
framework/yii/db/Command.php

@ -260,7 +260,7 @@ class Command extends \yii\base\Component
$rawSql = $this->getRawSql();
Yii::trace($rawSql, __METHOD__);
Yii::info($rawSql, __METHOD__);
if ($sql == '') {
return 0;
@ -364,9 +364,9 @@ class Command extends \yii\base\Component
$db = $this->db;
$rawSql = $this->getRawSql();
Yii::trace($rawSql, __METHOD__);
Yii::info($rawSql, __METHOD__);
/** @var $cache \yii\caching\Cache */
/** @var \yii\caching\Cache $cache */
if ($db->enableQueryCache && $method !== '') {
$cache = is_string($db->queryCache) ? Yii::$app->getComponent($db->queryCache) : $db->queryCache;
}

142
framework/yii/db/Query.php

@ -33,20 +33,12 @@ use yii\base\Component;
* ~~~
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class Query extends Component
class Query extends Component implements QueryInterface
{
/**
* Sort ascending
* @see orderBy
*/
const SORT_ASC = false;
/**
* Sort descending
* @see orderBy
*/
const SORT_DESC = true;
use QueryTrait;
/**
* @var array the columns being selected. For example, `['id', 'name']`.
@ -71,28 +63,6 @@ class Query extends Component
*/
public $from;
/**
* @var string|array query condition. This refers to the WHERE clause in a SQL statement.
* For example, `age > 31 AND team = 1`.
* @see where()
*/
public $where;
/**
* @var integer maximum number of records to be returned. If not set or less than 0, it means no limit.
*/
public $limit;
/**
* @var integer zero-based offset from where the records are to be returned. If not set or
* less than 0, it means starting from the beginning.
*/
public $offset;
/**
* @var array how to sort the query results. This is used to construct the ORDER BY clause in a SQL statement.
* The array keys are the columns to be sorted by, and the array values are the corresponding sort directions which
* can be either [[Query::SORT_ASC]] or [[Query::SORT_DESC]]. The array may also contain [[Expression]] objects.
* If that is the case, the expressions will be converted into strings without any change.
*/
public $orderBy;
/**
* @var array how to group the query results. For example, `['company', 'department']`.
* This is used to construct the GROUP BY clause in a SQL statement.
*/
@ -130,12 +100,6 @@ class Query extends Component
* For example, `[':name' => 'Dan', ':age' => 31]`.
*/
public $params;
/**
* @var string|callable $column the name of the column by which the query results should be indexed by.
* This can also be a callable (e.g. anonymous function) that returns the index value based on the given
* row data. For more details, see [[indexBy()]]. This property is only used by [[all()]].
*/
public $indexBy;
/**
@ -154,27 +118,6 @@ class Query extends Component
}
/**
* Sets the [[indexBy]] property.
* @param string|callable $column the name of the column by which the query results should be indexed by.
* This can also be a callable (e.g. anonymous function) that returns the index value based on the given
* row data. The signature of the callable should be:
*
* ~~~
* function ($row)
* {
* // return the index value corresponding to $row
* }
* ~~~
*
* @return static the query object itself
*/
public function indexBy($column)
{
$this->indexBy = $column;
return $this;
}
/**
* Executes the query and returns all results as an array.
* @param Connection $db the database connection used to generate the SQL statement.
* If this parameter is not given, the `db` application component will be used.
@ -239,7 +182,7 @@ class Query extends Component
* @param string $q the COUNT expression. Defaults to '*'.
* Make sure you properly quote column names in the expression.
* @param Connection $db the database connection used to generate the SQL statement.
* If this parameter is not given, the `db` application component will be used.
* If this parameter is not given (or null), the `db` application component will be used.
* @return integer number of records
*/
public function count($q = '*', $db = null)
@ -653,83 +596,6 @@ class Query extends Component
}
/**
* Sets the ORDER BY part of the query.
* @param string|array $columns the columns (and the directions) to be ordered by.
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
* (e.g. `['id' => Query::SORT_ASC, 'name' => Query::SORT_DESC]`).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return static the query object itself
* @see addOrderBy()
*/
public function orderBy($columns)
{
$this->orderBy = $this->normalizeOrderBy($columns);
return $this;
}
/**
* Adds additional ORDER BY columns to the query.
* @param string|array $columns the columns (and the directions) to be ordered by.
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
* (e.g. `['id' => Query::SORT_ASC, 'name' => Query::SORT_DESC]`).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return static the query object itself
* @see orderBy()
*/
public function addOrderBy($columns)
{
$columns = $this->normalizeOrderBy($columns);
if ($this->orderBy === null) {
$this->orderBy = $columns;
} else {
$this->orderBy = array_merge($this->orderBy, $columns);
}
return $this;
}
protected function normalizeOrderBy($columns)
{
if (is_array($columns)) {
return $columns;
} else {
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
$result = [];
foreach ($columns as $column) {
if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) {
$result[$matches[1]] = strcasecmp($matches[2], 'desc') ? self::SORT_ASC : self::SORT_DESC;
} else {
$result[$column] = self::SORT_ASC;
}
}
return $result;
}
}
/**
* Sets the LIMIT part of the query.
* @param integer $limit the limit. Use null or negative value to disable limit.
* @return static the query object itself
*/
public function limit($limit)
{
$this->limit = $limit;
return $this;
}
/**
* Sets the OFFSET part of the query.
* @param integer $offset the offset. Use null or negative value to disable offset.
* @return static the query object itself
*/
public function offset($offset)
{
$this->offset = $offset;
return $this;
}
/**
* Appends a SQL statement using UNION operator.
* @param string|Query $sql the SQL statement to be appended using UNION
* @return static the query object itself

2
framework/yii/db/QueryBuilder.php

@ -683,7 +683,7 @@ class QueryBuilder extends \yii\base\Object
if (is_object($direction)) {
$orders[] = (string)$direction;
} else {
$orders[] = $this->db->quoteColumnName($name) . ($direction === Query::SORT_DESC ? ' DESC' : '');
$orders[] = $this->db->quoteColumnName($name) . ($direction === SORT_DESC ? ' DESC' : '');
}
}

206
framework/yii/db/QueryInterface.php

@ -0,0 +1,206 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db;
/**
* The QueryInterface defines the minimum set of methods to be implemented by a database query.
*
* The default implementation of this interface is provided by [[QueryTrait]].
*
* It has support for getting [[one]] instance or [[all]].
* Allows pagination via [[limit]] and [[offset]].
* Sorting is supported via [[orderBy]] and items can be limited to match some conditions using [[where]].
*
* By calling [[createCommand()]], we can get a [[Command]] instance which can be further
* used to perform/execute the DB query against a database.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
interface QueryInterface
{
/**
* Executes the query and returns all results as an array.
* @param Connection $db the database connection used to execute the query.
* If this parameter is not given, the `db` application component will be used.
* @return array the query results. If the query results in nothing, an empty array will be returned.
*/
public function all($db = null);
/**
* Executes the query and returns a single row of result.
* @param Connection $db the database connection used to execute the query.
* If this parameter is not given, the `db` application component will be used.
* @return array|boolean the first row (in terms of an array) of the query result. False is returned if the query
* results in nothing.
*/
public function one($db = null);
/**
* Returns the number of records.
* @param string $q the COUNT expression. Defaults to '*'.
* @param Connection $db the database connection used to execute the query.
* If this parameter is not given, the `db` application component will be used.
* @return integer number of records
*/
public function count($q = '*', $db = null);
/**
* Returns a value indicating whether the query result contains any row of data.
* @param Connection $db the database connection used to execute the query.
* If this parameter is not given, the `db` application component will be used.
* @return boolean whether the query result contains any row of data.
*/
public function exists($db = null);
/**
* Sets the [[indexBy]] property.
* @param string|callable $column the name of the column by which the query results should be indexed by.
* This can also be a callable (e.g. anonymous function) that returns the index value based on the given
* row data. The signature of the callable should be:
*
* ~~~
* function ($row)
* {
* // return the index value corresponding to $row
* }
* ~~~
*
* @return static the query object itself
*/
public function indexBy($column);
/**
* Sets the WHERE part of the query.
*
* The method requires a $condition parameter.
*
* The $condition parameter should be an array in one of the following two formats:
*
* - hash format: `['column1' => value1, 'column2' => value2, ...]`
* - operator format: `[operator, operand1, operand2, ...]`
*
* A condition in hash format represents the following SQL expression in general:
* `column1=value1 AND column2=value2 AND ...`. In case when a value is an array,
* an `IN` expression will be generated. And if a value is null, `IS NULL` will be used
* in the generated expression. Below are some examples:
*
* - `['type' => 1, 'status' => 2]` generates `(type = 1) AND (status = 2)`.
* - `['id' => [1, 2, 3], 'status' => 2]` generates `(id IN (1, 2, 3)) AND (status = 2)`.
* - `['status' => null] generates `status IS NULL`.
*
* A condition in operator format generates the SQL expression according to the specified operator, which
* can be one of the followings:
*
* - `and`: the operands should be concatenated together using `AND`. For example,
* `['and', 'id=1', 'id=2']` will generate `id=1 AND id=2`. If an operand is an array,
* it will be converted into a string using the rules described here. For example,
* `['and', 'type=1', ['or', 'id=1', 'id=2']]` will generate `type=1 AND (id=1 OR id=2)`.
* The method will NOT do any quoting or escaping.
*
* - `or`: similar to the `and` operator except that the operands are concatenated using `OR`.
*
* - `between`: operand 1 should be the column name, and operand 2 and 3 should be the
* starting and ending values of the range that the column is in.
* For example, `['between', 'id', 1, 10]` will generate `id BETWEEN 1 AND 10`.
*
* - `not between`: similar to `between` except the `BETWEEN` is replaced with `NOT BETWEEN`
* in the generated condition.
*
* - `in`: operand 1 should be a column or DB expression, and operand 2 be an array representing
* the range of the values that the column or DB expression should be in. For example,
* `['in', 'id', [1, 2, 3]]` will generate `id IN (1, 2, 3)`.
* The method will properly quote the column name and escape values in the range.
*
* - `not in`: similar to the `in` operator except that `IN` is replaced with `NOT IN` in the generated condition.
*
* - `like`: operand 1 should be a column or DB expression, and operand 2 be a string or an array representing
* the values that the column or DB expression should be like.
* For example, `['like', 'name', '%tester%']` will generate `name LIKE '%tester%'`.
* When the value range is given as an array, multiple `LIKE` predicates will be generated and concatenated
* using `AND`. For example, `['like', 'name', ['%test%', '%sample%']]` will generate
* `name LIKE '%test%' AND name LIKE '%sample%'`.
* The method will properly quote the column name and escape values in the range.
*
* - `or like`: similar to the `like` operator except that `OR` is used to concatenate the `LIKE`
* predicates when operand 2 is an array.
*
* - `not like`: similar to the `like` operator except that `LIKE` is replaced with `NOT LIKE`
* in the generated condition.
*
* - `or not like`: similar to the `not like` operator except that `OR` is used to concatenate
* the `NOT LIKE` predicates.
*
* @param array $condition the conditions that should be put in the WHERE part.
* @return static the query object itself
* @see andWhere()
* @see orWhere()
*/
public function where($condition);
/**
* Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'AND' operator.
* @param string|array $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @return static the query object itself
* @see where()
* @see orWhere()
*/
public function andWhere($condition);
/**
* Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'OR' operator.
* @param string|array $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @return static the query object itself
* @see where()
* @see andWhere()
*/
public function orWhere($condition);
/**
* Sets the ORDER BY part of the query.
* @param string|array $columns the columns (and the directions) to be ordered by.
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
* (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return static the query object itself
* @see addOrderBy()
*/
public function orderBy($columns);
/**
* Adds additional ORDER BY columns to the query.
* @param string|array $columns the columns (and the directions) to be ordered by.
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
* (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return static the query object itself
* @see orderBy()
*/
public function addOrderBy($columns);
/**
* Sets the LIMIT part of the query.
* @param integer $limit the limit. Use null or negative value to disable limit.
* @return static the query object itself
*/
public function limit($limit);
/**
* Sets the OFFSET part of the query.
* @param integer $offset the offset. Use null or negative value to disable offset.
* @return static the query object itself
*/
public function offset($offset);
}

208
framework/yii/db/QueryTrait.php

@ -0,0 +1,208 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db;
/**
* The BaseQuery trait represents the minimum method set of a database Query.
*
* It has support for getting [[one]] instance or [[all]].
* Allows pagination via [[limit]] and [[offset]].
* Sorting is supported via [[orderBy]] and items can be limited to match some conditions unsing [[where]].
*
* By calling [[createCommand()]], we can get a [[Command]] instance which can be further
* used to perform/execute the DB query against a database.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
trait QueryTrait
{
/**
* @var string|array query condition. This refers to the WHERE clause in a SQL statement.
* For example, `age > 31 AND team = 1`.
* @see where()
*/
public $where;
/**
* @var integer maximum number of records to be returned. If not set or less than 0, it means no limit.
*/
public $limit;
/**
* @var integer zero-based offset from where the records are to be returned. If not set or
* less than 0, it means starting from the beginning.
*/
public $offset;
/**
* @var array how to sort the query results. This is used to construct the ORDER BY clause in a SQL statement.
* The array keys are the columns to be sorted by, and the array values are the corresponding sort directions which
* can be either [SORT_ASC](http://php.net/manual/en/array.constants.php#constant.sort-asc)
* or [SORT_DESC](http://php.net/manual/en/array.constants.php#constant.sort-desc).
* The array may also contain [[Expression]] objects. If that is the case, the expressions
* will be converted into strings without any change.
*/
public $orderBy;
/**
* @var string|callable $column the name of the column by which the query results should be indexed by.
* This can also be a callable (e.g. anonymous function) that returns the index value based on the given
* row data. For more details, see [[indexBy()]]. This property is only used by [[all()]].
*/
public $indexBy;
/**
* Sets the [[indexBy]] property.
* @param string|callable $column the name of the column by which the query results should be indexed by.
* This can also be a callable (e.g. anonymous function) that returns the index value based on the given
* row data. The signature of the callable should be:
*
* ~~~
* function ($row)
* {
* // return the index value corresponding to $row
* }
* ~~~
*
* @return static the query object itself
*/
public function indexBy($column)
{
$this->indexBy = $column;
return $this;
}
/**
* Sets the WHERE part of the query.
*
* See [[QueryInterface::where()]] for detailed documentation.
*
* @param array $condition the conditions that should be put in the WHERE part.
* @return static the query object itself
* @see andWhere()
* @see orWhere()
*/
public function where($condition)
{
$this->where = $condition;
return $this;
}
/**
* Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'AND' operator.
* @param string|array $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @return static the query object itself
* @see where()
* @see orWhere()
*/
public function andWhere($condition)
{
if ($this->where === null) {
$this->where = $condition;
} else {
$this->where = ['and', $this->where, $condition];
}
return $this;
}
/**
* Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'OR' operator.
* @param string|array $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @return static the query object itself
* @see where()
* @see andWhere()
*/
public function orWhere($condition)
{
if ($this->where === null) {
$this->where = $condition;
} else {
$this->where = ['or', $this->where, $condition];
}
return $this;
}
/**
* Sets the ORDER BY part of the query.
* @param string|array $columns the columns (and the directions) to be ordered by.
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
* (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return static the query object itself
* @see addOrderBy()
*/
public function orderBy($columns)
{
$this->orderBy = $this->normalizeOrderBy($columns);
return $this;
}
/**
* Adds additional ORDER BY columns to the query.
* @param string|array $columns the columns (and the directions) to be ordered by.
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
* (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return static the query object itself
* @see orderBy()
*/
public function addOrderBy($columns)
{
$columns = $this->normalizeOrderBy($columns);
if ($this->orderBy === null) {
$this->orderBy = $columns;
} else {
$this->orderBy = array_merge($this->orderBy, $columns);
}
return $this;
}
protected function normalizeOrderBy($columns)
{
if (is_array($columns)) {
return $columns;
} else {
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
$result = [];
foreach ($columns as $column) {
if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) {
$result[$matches[1]] = strcasecmp($matches[2], 'desc') ? SORT_ASC : SORT_DESC;
} else {
$result[$column] = SORT_ASC;
}
}
return $result;
}
}
/**
* Sets the LIMIT part of the query.
* @param integer $limit the limit. Use null or negative value to disable limit.
* @return static the query object itself
*/
public function limit($limit)
{
$this->limit = $limit;
return $this;
}
/**
* Sets the OFFSET part of the query.
* @param integer $offset the offset. Use null or negative value to disable offset.
* @return static the query object itself
*/
public function offset($offset)
{
$this->offset = $offset;
return $this;
}
}

12
framework/yii/db/Schema.php

@ -92,14 +92,16 @@ abstract class Schema extends Object
$realName = $this->getRawTableName($name);
if ($db->enableSchemaCache && !in_array($name, $db->schemaCacheExclude, true)) {
/** @var $cache Cache */
/** @var Cache $cache */
$cache = is_string($db->schemaCache) ? Yii::$app->getComponent($db->schemaCache) : $db->schemaCache;
if ($cache instanceof Cache) {
$key = $this->getCacheKey($name);
if ($refresh || ($table = $cache->get($key)) === false) {
$table = $this->loadTableSchema($realName);
if ($table !== null) {
$cache->set($key, $table, $db->schemaCacheDuration, new GroupDependency($this->getCacheGroup()));
$cache->set($key, $table, $db->schemaCacheDuration, new GroupDependency([
'group' => $this->getCacheGroup(),
]));
}
}
return $this->_tables[$name] = $table;
@ -213,7 +215,7 @@ abstract class Schema extends Object
*/
public function refresh()
{
/** @var $cache Cache */
/** @var Cache $cache */
$cache = is_string($this->db->schemaCache) ? Yii::$app->getComponent($this->db->schemaCache) : $this->db->schemaCache;
if ($this->db->enableSchemaCache && $cache instanceof Cache) {
GroupDependency::invalidate($cache, $this->getCacheGroup());
@ -289,7 +291,7 @@ abstract class Schema extends Object
* then this method will do nothing.
* @param string $name table name
* @return string the properly quoted table name
* @see quoteSimpleTableName
* @see quoteSimpleTableName()
*/
public function quoteTableName($name)
{
@ -314,7 +316,7 @@ abstract class Schema extends Object
* then this method will do nothing.
* @param string $name column name
* @return string the properly quoted column name
* @see quoteSimpleColumnName
* @see quoteSimpleColumnName()
*/
public function quoteColumnName($name)
{

2
framework/yii/grid/ActionColumn.php

@ -12,6 +12,8 @@ use Closure;
use yii\helpers\Html;
/**
* ActionColumn is a column for the [[GridView]] widget that displays buttons for viewing and manipulating the items.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/

4
framework/yii/grid/DataColumn.php

@ -15,6 +15,10 @@ use yii\helpers\Html;
use yii\helpers\Inflector;
/**
* DataColumn is the default column type for the [[GridView]] widget.
*
* It is used to show data columns and allows sorting them.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/

17
framework/yii/grid/GridView.php

@ -16,6 +16,10 @@ use yii\helpers\Json;
use yii\widgets\BaseListView;
/**
* The GridView widget is used to display data in a grid.
*
* It provides features like sorting, paging and also filtering the data.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
@ -89,10 +93,9 @@ class GridView extends BaseListView
*/
public $showFooter = false;
/**
* @var string|boolean the HTML content to be displayed when [[dataProvider]] does not have any data.
* If false, the grid view will still be displayed (without body content though).
* @var boolean whether to show the grid view if [[dataProvider]] returns no data.
*/
public $empty = false;
public $showOnEmpty = true;
/**
* @var array|Formatter the formatter used to format model attribute values into displayable texts.
* This can be either an instance of [[Formatter]] or an configuration array for creating the [[Formatter]]
@ -342,7 +345,13 @@ class GridView extends BaseListView
}
}
}
return "<tbody>\n" . implode("\n", $rows) . "\n</tbody>";
if (empty($rows)) {
$colspan = count($this->columns);
return "<tbody>\n<tr><td colspan=\"$colspan\">" . $this->renderEmpty() . "</td></tr>\n</tbody>";
} else {
return "<tbody>\n" . implode("\n", $rows) . "\n</tbody>";
}
}
/**

1
framework/yii/grid/GridViewAsset.php

@ -10,6 +10,7 @@ namespace yii\grid;
use yii\web\AssetBundle;
/**
* This asset bundle provides the javascript files for the [[GridView]] widget.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0

26
framework/yii/helpers/BaseHtml.php

@ -86,7 +86,7 @@ class BaseHtml
* @param boolean $doubleEncode whether to encode HTML entities in `$content`. If false,
* HTML entities in `$content` will not be further encoded.
* @return string the encoded content
* @see decode
* @see decode()
* @see http://www.php.net/manual/en/function.htmlspecialchars.php
*/
public static function encode($content, $doubleEncode = true)
@ -99,7 +99,7 @@ class BaseHtml
* This is the opposite of [[encode()]].
* @param string $content the content to be decoded
* @return string the decoded content
* @see encode
* @see encode()
* @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php
*/
public static function decode($content)
@ -116,8 +116,8 @@ class BaseHtml
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated HTML tag
* @see beginTag
* @see endTag
* @see beginTag()
* @see endTag()
*/
public static function tag($name, $content = '', $options = [])
{
@ -132,8 +132,8 @@ class BaseHtml
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated start tag
* @see endTag
* @see tag
* @see endTag()
* @see tag()
*/
public static function beginTag($name, $options = [])
{
@ -144,8 +144,8 @@ class BaseHtml
* Generates an end tag.
* @param string $name the tag name
* @return string the generated end tag
* @see beginTag
* @see tag
* @see beginTag()
* @see tag()
*/
public static function endTag($name)
{
@ -187,7 +187,7 @@ class BaseHtml
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated link tag
* @see url
* @see url()
*/
public static function cssFile($url, $options = [])
{
@ -203,7 +203,7 @@ class BaseHtml
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated script tag
* @see url
* @see url()
*/
public static function jsFile($url, $options = [])
{
@ -222,7 +222,7 @@ class BaseHtml
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated form start tag.
* @see endForm
* @see endForm()
*/
public static function beginForm($action = '', $method = 'post', $options = [])
{
@ -271,7 +271,7 @@ class BaseHtml
/**
* Generates a form end tag.
* @return string the generated tag
* @see beginForm
* @see beginForm()
*/
public static function endForm()
{
@ -290,7 +290,7 @@ class BaseHtml
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated hyperlink
* @see url
* @see url()
*/
public static function a($text, $url = null, $options = [])
{

2
framework/yii/helpers/BaseInflector.php

@ -328,7 +328,7 @@ class BaseInflector
* Converts a word like "send_email" to "SendEmail". It
* will remove non alphanumeric character from the word, so
* "who's online" will be converted to "WhoSOnline"
* @see variablize
* @see variablize()
* @param string $word the word to CamelCase
* @return string
*/

8
framework/yii/helpers/BaseSecurity.php

@ -175,7 +175,7 @@ class BaseSecurity
/**
* Returns a secret key associated with the specified name.
* If the secret key does not exist, a random key will be generated
* and saved in the file "keys.php" under the application's runtime directory
* and saved in the file "keys.data" under the application's runtime directory
* so that the same secret key can be returned in future requests.
* @param string $name the name that is associated with the secret key
* @param integer $length the length of the key that should be generated if not exists
@ -184,16 +184,16 @@ class BaseSecurity
public static function getSecretKey($name, $length = 32)
{
static $keys;
$keyFile = Yii::$app->getRuntimePath() . '/keys.php';
$keyFile = Yii::$app->getRuntimePath() . '/keys.json';
if ($keys === null) {
$keys = [];
if (is_file($keyFile)) {
$keys = require($keyFile);
$keys = json_decode(file_get_contents($keyFile), true);
}
}
if (!isset($keys[$name])) {
$keys[$name] = static::generateRandomKey($length);
file_put_contents($keyFile, "<?php\nreturn " . var_export($keys, true) . ";\n");
file_put_contents($keyFile, json_encode($keys));
}
return $keys[$name];
}

9
framework/yii/i18n/Formatter.php

@ -19,6 +19,15 @@ use yii\base\InvalidConfigException;
* Formatter requires the PHP "intl" extension to be installed. Formatter supports localized
* formatting of date, time and numbers, based on the current [[locale]].
*
* This Formatter can replace the `formatter` application component that is configured by default.
* To do so, add the following to your application config under `components`:
*
* ```php
* 'formatter' => [
* 'class' => 'yii\i18n\Formatter',
* ]
* ```
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/

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

Loading…
Cancel
Save