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

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

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

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

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

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

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

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

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

3
apps/advanced/composer.json

@ -23,8 +23,7 @@
}, },
"scripts": { "scripts": {
"post-create-project-cmd": [ "post-create-project-cmd": [
"yii\\composer\\Installer::setPermission", "yii\\composer\\Installer::setPermission"
"./init"
] ]
}, },
"extra": { "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/ * @license http://www.yiiframework.com/license/
*/ */
namespace frontend\config; namespace frontend\assets;
use yii\web\AssetBundle; use yii\web\AssetBundle;

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

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

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

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

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

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

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

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

1
apps/basic/codeception.yml

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

2
apps/basic/config/console.php

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

2
apps/basic/config/web.php

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

2
apps/basic/models/ContactForm.php

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

2
apps/basic/models/LoginForm.php

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

1365
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 <?php
// This class was automatically generated by build task // 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 // @codingStandardsIgnoreFile
use Codeception\Maybe;
use \Codeception\Maybe;
use Codeception\Module\CodeHelper; use Codeception\Module\CodeHelper;
/** /**
* Inherited methods * Inherited methods
* @method void execute($callable)
* @method void wantToTest($text) * @method void wantToTest($text)
* @method void wantTo($text) * @method void wantTo($text)
* @method void amTesting($method)
* @method void amTestingMethod($method)
* @method void testMethod($signature)
* @method void expectTo($prediction) * @method void expectTo($prediction)
* @method void expect($prediction) * @method void expect($prediction)
* @method void amGoingTo($argumentation) * @method void amGoingTo($argumentation)
* @method void am($role) * @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 class CodeGuy extends \Codeception\AbstractGuy

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

@ -3,12 +3,13 @@ use yii\helpers\Html;
use yii\bootstrap\Nav; use yii\bootstrap\Nav;
use yii\bootstrap\NavBar; use yii\bootstrap\NavBar;
use yii\widgets\Breadcrumbs; use yii\widgets\Breadcrumbs;
use app\assets\AppAsset;
/** /**
* @var $this \yii\web\View * @var \yii\web\View $this
* @var $content string * @var string $content
*/ */
app\config\AppAsset::register($this); AppAsset::register($this);
?> ?>
<?php $this->beginPage(); ?> <?php $this->beginPage(); ?>
<!DOCTYPE html> <!DOCTYPE html>
@ -29,7 +30,7 @@ app\config\AppAsset::register($this);
], ],
]); ]);
echo Nav::widget([ echo Nav::widget([
'options' => ['class' => 'navbar-nav pull-right'], 'options' => ['class' => 'navbar-nav navbar-right'],
'items' => [ 'items' => [
['label' => 'Home', 'url' => ['/site/index']], ['label' => 'Home', 'url' => ['/site/index']],
['label' => 'About', 'url' => ['/site/about']], ['label' => 'About', 'url' => ['/site/about']],
@ -47,7 +48,7 @@ app\config\AppAsset::register($this);
<div class="container"> <div class="container">
<?= Breadcrumbs::widget([ <?= Breadcrumbs::widget([
'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [], 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
]); ?> ]) ?>
<?= $content ?> <?= $content ?>
</div> </div>

7
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'); $config = require(__DIR__ . '/../config/web-test.php');
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 = new yii\web\Application($config);
$application->run(); $application->run();
}

24
build/controllers/PhpDocController.php

@ -278,11 +278,35 @@ class PhpDocController extends Controller
. ' See [[get'.ucfirst($propName).'()]] and [[set'.ucfirst($propName).'()]] for details.'; . ' See [[get'.ucfirst($propName).'()]] and [[set'.ucfirst($propName).'()]] for details.';
} }
} elseif (isset($prop['get'])) { } elseif (isset($prop['get'])) {
// 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.'; $note = ' This property is read-only.';
// $docline .= '-read'; // $docline .= '-read';
}
} elseif (isset($prop['set'])) { } elseif (isset($prop['set'])) {
// 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.'; $note = ' This property is write-only.';
// $docline .= '-write'; // $docline .= '-write';
}
} else { } else {
continue; continue;
} }

4
docs/guide/query-builder.md

@ -167,8 +167,8 @@ For ordering results `orderBy` and `addOrderBy` could be used:
```php ```php
$query->orderBy([ $query->orderBy([
'id' => Query::SORT_ASC, 'id' => SORT_ASC,
'name' => Query::SORT_DESC, '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: it can be properly configured:
```php ```php
class MyClass extends \yii\Object class MyClass extends \yii\base\Object
{ {
public function __construct($param1, $param2, $config = []) 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: If you need to handle all instances of a class instead of the object you can attach a handler like the following:
```php ```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.'); 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: asset bundle, it's very straightforward:
```php ```php
frontend\config\AppAsset::register($this); frontend\assets\AppAsset::register($this);
``` ```
### Layout ### Layout

7
extensions/bootstrap/composer.json

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

1
extensions/composer/composer.json

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

7
extensions/debug/composer.json

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

14
extensions/debug/panels/ConfigPanel.php

@ -63,6 +63,7 @@ EOD;
]; ];
return "<h1>Configuration</h1>\n" return "<h1>Configuration</h1>\n"
. $this->renderData('Application Configuration', $app) . "\n" . $this->renderData('Application Configuration', $app) . "\n"
. $this->renderExtensions()
. $this->renderData('PHP Configuration', $php) . "\n" . $this->renderData('PHP Configuration', $php) . "\n"
. $this->getPhpInfo(); . $this->getPhpInfo();
} }
@ -93,6 +94,18 @@ $rows
EOD; 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() public function save()
{ {
return [ return [
@ -110,6 +123,7 @@ EOD;
'apc' => extension_loaded('apc'), 'apc' => extension_loaded('apc'),
'memcache' => extension_loaded('memcache'), '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() public function rules()
{ {
return [ return [
['template', 'required', 'message' => 'A code template must be selected.'], [['template'], 'required', 'message' => 'A code template must be selected.'],
['template', 'validateTemplate'], [['template'], 'validateTemplate'],
]; ];
} }
@ -193,7 +193,7 @@ abstract class Generator extends Model
$attributes[] = 'template'; $attributes[] = 'template';
$path = $this->getStickyDataFile(); $path = $this->getStickyDataFile();
if (is_file($path)) { if (is_file($path)) {
$result = @include($path); $result = json_decode(file_get_contents($path), true);
if (is_array($result)) { if (is_array($result)) {
foreach ($stickyAttributes as $name) { foreach ($stickyAttributes as $name) {
if (isset($result[$name])) { if (isset($result[$name])) {
@ -218,7 +218,7 @@ abstract class Generator extends Model
} }
$path = $this->getStickyDataFile(); $path = $this->getStickyDataFile();
@mkdir(dirname($path), 0755, true); @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() 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", "irc": "irc://irc.freenode.net/yii",
"source": "https://github.com/yiisoft/yii2" "source": "https://github.com/yiisoft/yii2"
}, },
"minimum-stability": "dev", "authors": [
{
"name": "Qiang Xue",
"email": "qiang.xue@gmail.com"
}
],
"require": { "require": {
"yiisoft/yii2": "*", "yiisoft/yii2": "*",
"yiisoft/yii2-bootstrap": "*" "yiisoft/yii2-bootstrap": "*"

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

@ -69,12 +69,12 @@ class Generator extends \yii\gii\Generator
public function rules() public function rules()
{ {
return array_merge(parent::rules(), [ return array_merge(parent::rules(), [
['controller, actions, baseClass, ns', 'filter', 'filter' => 'trim'], [['controller', 'actions', 'baseClass', 'ns'], 'filter', 'filter' => 'trim'],
['controller, baseClass', 'required'], [['controller', 'baseClass'], 'required'],
['controller', 'match', 'pattern' => '/^[a-z\\-\\/]*$/', 'message' => 'Only a-z, dashes (-) and slashes (/) are allowed.'], [['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.'], [['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.'], [['baseClass'], 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'],
['ns', '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() public function rules()
{ {
return array_merge(parent::rules(), [ return array_merge(parent::rules(), [
['moduleID, controllerClass, modelClass, searchModelClass, baseControllerClass', 'filter', 'filter' => 'trim'], [['moduleID', 'controllerClass', 'modelClass', 'searchModelClass', 'baseControllerClass'], 'filter', 'filter' => 'trim'],
['modelClass, searchModelClass, controllerClass, baseControllerClass, indexWidgetType', 'required'], [['modelClass', 'searchModelClass', 'controllerClass', 'baseControllerClass', 'indexWidgetType'], 'required'],
['searchModelClass', 'compare', 'compareAttribute' => 'modelClass', 'operator' => '!==', 'message' => 'Search Model Class must not be equal to Model Class.'], [['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', 'controllerClass', 'baseControllerClass', 'searchModelClass'], 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'],
['modelClass', 'validateClass', 'params' => ['extends' => ActiveRecord::className()]], [['modelClass'], 'validateClass', 'params' => ['extends' => ActiveRecord::className()]],
['baseControllerClass', 'validateClass', 'params' => ['extends' => Controller::className()]], [['baseControllerClass'], 'validateClass', 'params' => ['extends' => Controller::className()]],
['controllerClass', 'match', 'pattern' => '/Controller$/', 'message' => 'Controller class name must be suffixed with "Controller".'], [['controllerClass'], 'match', 'pattern' => '/Controller$/', 'message' => 'Controller class name must be suffixed with "Controller".'],
['controllerClass, searchModelClass', 'validateNewClass'], [['controllerClass', 'searchModelClass'], 'validateNewClass'],
['indexWidgetType', 'in', 'range' => ['grid', 'list']], [['indexWidgetType'], 'in', 'range' => ['grid', 'list']],
['modelClass', 'validateModelClass'], [['modelClass'], 'validateModelClass'],
['moduleID', 'validateModuleID'], [['moduleID'], 'validateModuleID'],
]); ]);
} }
@ -278,7 +278,7 @@ class Generator extends \yii\gii\Generator
$rules = []; $rules = [];
foreach ($types as $type => $columns) { foreach ($types as $type => $columns) {
$rules[] = "['" . implode(', ', $columns) . "', '$type']"; $rules[] = "[['" . implode("', '", $columns) . "'], '$type']";
} }
return $rules; return $rules;
@ -341,7 +341,9 @@ class Generator extends \yii\gii\Generator
public function generateUrlParams() public function generateUrlParams()
{ {
$pks = $this->getTableSchema()->primaryKey; /** @var ActiveRecord $class */
$class = $this->modelClass;
$pks = $class::primaryKey();
if (count($pks) === 1) { if (count($pks) === 1) {
return "'id' => \$model->{$pks[0]}"; return "'id' => \$model->{$pks[0]}";
} else { } else {
@ -355,7 +357,9 @@ class Generator extends \yii\gii\Generator
public function generateActionParams() public function generateActionParams()
{ {
$pks = $this->getTableSchema()->primaryKey; /** @var ActiveRecord $class */
$class = $this->modelClass;
$pks = $class::primaryKey();
if (count($pks) === 1) { if (count($pks) === 1) {
return '$id'; return '$id';
} else { } else {
@ -366,7 +370,9 @@ class Generator extends \yii\gii\Generator
public function generateActionParamComments() public function generateActionParamComments()
{ {
$table = $this->getTableSchema(); $table = $this->getTableSchema();
$pks = $table->primaryKey; /** @var ActiveRecord $class */
$class = $this->modelClass;
$pks = $class::primaryKey();
if (count($pks) === 1) { if (count($pks) === 1) {
return ['@param ' . $table->columns[$pks[0]]->phpType . ' $id']; return ['@param ' . $table->columns[$pks[0]]->phpType . ' $id'];
} else { } 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->modelClass, '\\') ?>;
use <?= ltrim($generator->searchModelClass, '\\') ?><?php if (isset($searchModelAlias)):?> as <?= $searchModelAlias ?><?php endif ?>; use <?= ltrim($generator->searchModelClass, '\\') ?><?php if (isset($searchModelAlias)):?> as <?= $searchModelAlias ?><?php endif ?>;
use yii\data\ActiveDataProvider;
use <?= ltrim($generator->baseControllerClass, '\\') ?>; use <?= ltrim($generator->baseControllerClass, '\\') ?>;
use yii\web\HttpException; use yii\web\HttpException;
use yii\web\VerbFilter; use yii\web\VerbFilter;

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

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

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

@ -53,17 +53,17 @@ class Generator extends \yii\gii\Generator
public function rules() public function rules()
{ {
return array_merge(parent::rules(), [ return array_merge(parent::rules(), [
['db, ns, tableName, modelClass, baseClass', 'filter', 'filter' => 'trim'], [['db', 'ns', 'tableName', 'modelClass', 'baseClass'], 'filter', 'filter' => 'trim'],
['db, ns, tableName, baseClass', 'required'], [['db', 'ns', 'tableName', 'baseClass'], 'required'],
['db, modelClass', 'match', 'pattern' => '/^\w+$/', 'message' => 'Only word characters are allowed.'], [['db', 'modelClass'], 'match', 'pattern' => '/^\w+$/', 'message' => 'Only word characters are allowed.'],
['ns, baseClass', 'match', 'pattern' => '/^[\w\\\\]+$/', 'message' => 'Only word characters and backslashes 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.'], [['tableName'], 'match', 'pattern' => '/^(\w+\.)?([\w\*]+)$/', 'message' => 'Only word characters, and optionally an asterisk and/or a dot are allowed.'],
['db', 'validateDb'], [['db'], 'validateDb'],
['ns', 'validateNamespace'], [['ns'], 'validateNamespace'],
['tableName', 'validateTableName'], [['tableName'], 'validateTableName'],
['modelClass', 'validateModelClass', 'skipOnEmpty' => false], [['modelClass'], 'validateModelClass', 'skipOnEmpty' => false],
['baseClass', 'validateClass', 'params' => ['extends' => ActiveRecord::className()]], [['baseClass'], 'validateClass', 'params' => ['extends' => ActiveRecord::className()]],
['generateRelations, generateLabelsFromComments', 'boolean'], [['generateRelations', 'generateLabelsFromComments'], 'boolean'],
]); ]);
} }
@ -237,10 +237,10 @@ class Generator extends \yii\gii\Generator
$rules = []; $rules = [];
foreach ($types as $type => $columns) { foreach ($types as $type => $columns) {
$rules[] = "['" . implode(', ', $columns) . "', '$type']"; $rules[] = "[['" . implode("', '", $columns) . "'], '$type']";
} }
foreach ($lengths as $length => $columns) { foreach ($lengths as $length => $columns) {
$rules[] = "['" . implode(', ', $columns) . "', 'string', 'max' => $length]"; $rules[] = "[['" . implode("', '", $columns) . "'], 'string', 'max' => $length]";
} }
return $rules; return $rules;

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

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

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

@ -2,10 +2,10 @@
use yii\helpers\Html; use yii\helpers\Html;
/** /**
* @var $this \yii\web\View * @var \yii\web\View $this
* @var $content string * @var \yii\gii\Generator[] $generators
* @var yii\gii\Generator[] $generators * @var \yii\gii\Generator $activeGenerator
* @var yii\gii\Generator $activeGenerator * @var string $content
*/ */
$generators = Yii::$app->controller->module->generators; $generators = Yii::$app->controller->module->generators;
$activeGenerator = Yii::$app->controller->generator; $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; use yii\gii\CodeFile;
/** /**
* @var $this \yii\web\View * @var \yii\web\View $this
* @var $generator \yii\gii\Generator * @var \yii\gii\Generator $generator
* @var CodeFile[] $files * @var CodeFile[] $files
* @var array $answers * @var array $answers
*/ */

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

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

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

@ -4,8 +4,8 @@ use yii\bootstrap\Nav;
use yii\helpers\Html; use yii\helpers\Html;
/** /**
* @var $this \yii\web\View * @var \yii\web\View $this
* @var $content string * @var string $content
*/ */
$asset = yii\gii\GiiAsset::register($this); $asset = yii\gii\GiiAsset::register($this);
?> ?>
@ -26,7 +26,7 @@ NavBar::begin([
'options' => ['class' => 'navbar-inverse navbar-fixed-top'], 'options' => ['class' => 'navbar-inverse navbar-fixed-top'],
]); ]);
echo Nav::widget([ echo Nav::widget([
'options' => ['class' => 'nav navbar-nav pull-right'], 'options' => ['class' => 'nav navbar-nav navbar-right'],
'items' => [ 'items' => [
['label' => 'Home', 'url' => ['default/index']], ['label' => 'Home', 'url' => ['default/index']],
['label' => 'Help', 'url' => 'http://www.yiiframework.com/doc/guide/topics.gii'], ['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", "irc": "irc://irc.freenode.net/yii",
"source": "https://github.com/yiisoft/yii2" "source": "https://github.com/yiisoft/yii2"
}, },
"minimum-stability": "dev", "authors": [
{
"name": "Qiang Xue",
"email": "qiang.xue@gmail.com"
}
],
"require": { "require": {
"yiisoft/yii2": "*" "yiisoft/yii2": "*"
}, },

3
extensions/smarty/composer.json

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

8
extensions/swiftmailer/Mailer.php

@ -104,13 +104,13 @@ class Mailer extends BaseMailer
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function send($message) protected function sendMessage($message)
{ {
$address = $message->getTo(); $address = $message->getTo();
if (is_array($address)) { 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; return $this->getSwiftMailer()->send($message->getSwiftMessage()) > 0;
} }
@ -145,7 +145,7 @@ class Mailer extends BaseMailer
$transport->$name = $value; $transport->$name = $value;
} else { } else {
$setter = 'set' . $name; $setter = 'set' . $name;
if (method_exists($transport, $setter)) { if (method_exists($transport, $setter) || method_exists($transport, '__call')) {
$transport->$setter($value); $transport->$setter($value);
} else { } else {
throw new InvalidConfigException('Setting unknown property: ' . get_class($transport) . '::' . $name); 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() public function getCharset()
{ {
$this->getSwiftMessage()->getCharset(); return $this->getSwiftMessage()->getCharset();
} }
/** /**
@ -61,7 +61,7 @@ class Message extends BaseMessage
*/ */
public function getFrom() public function getFrom()
{ {
$this->getSwiftMessage()->getFrom(); return $this->getSwiftMessage()->getFrom();
} }
/** /**
@ -78,7 +78,7 @@ class Message extends BaseMessage
*/ */
public function getReplyTo() public function getReplyTo()
{ {
$this->getSwiftMessage()->getReplyTo(); return $this->getSwiftMessage()->getReplyTo();
} }
/** /**
@ -95,7 +95,7 @@ class Message extends BaseMessage
*/ */
public function getTo() public function getTo()
{ {
$this->getSwiftMessage()->getTo(); return $this->getSwiftMessage()->getTo();
} }
/** /**
@ -112,7 +112,7 @@ class Message extends BaseMessage
*/ */
public function getCc() public function getCc()
{ {
$this->getSwiftMessage()->getCc(); return $this->getSwiftMessage()->getCc();
} }
/** /**
@ -129,7 +129,7 @@ class Message extends BaseMessage
*/ */
public function getBcc() public function getBcc()
{ {
$this->getSwiftMessage()->getBcc(); return $this->getSwiftMessage()->getBcc();
} }
/** /**
@ -146,7 +146,7 @@ class Message extends BaseMessage
*/ */
public function getSubject() public function getSubject()
{ {
$this->getSwiftMessage()->getSubject(); return $this->getSwiftMessage()->getSubject();
} }
/** /**
@ -192,7 +192,7 @@ class Message extends BaseMessage
$partFound = false; $partFound = false;
foreach ($parts as $key => $part) { foreach ($parts as $key => $part) {
if (!($part instanceof \Swift_Mime_Attachment)) { if (!($part instanceof \Swift_Mime_Attachment)) {
/* @var $part \Swift_Mime_MimePart */ /* @var \Swift_Mime_MimePart $part */
if ($part->getContentType() == $contentType) { if ($part->getContentType() == $contentType) {
unset($parts[$key]); unset($parts[$key]);
$partFound = true; $partFound = true;

3
extensions/swiftmailer/composer.json

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

3
extensions/twig/composer.json

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

12
framework/composer.json

@ -65,17 +65,15 @@
}, },
"require": { "require": {
"php": ">=5.4.0", "php": ">=5.4.0",
"yiisoft/yii2-composer": "*",
"yiisoft/jquery": "1.10.*",
"ext-mbstring": "*", "ext-mbstring": "*",
"lib-pcre": "*", "lib-pcre": "*",
"phpspec/php-diff": "dev-master", "yiisoft/yii2-composer": "*",
"ezyang/htmlpurifier": "4.5.*" "yiisoft/jquery": "1.10.*",
"phpspec/php-diff": ">=1.0.2",
"ezyang/htmlpurifier": "4.5.*",
"michelf/php-markdown": "1.3.*"
}, },
"autoload": { "autoload": {
"psr-0": { "yii\\": "/" } "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 * 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 * are the corresponding class file paths (or path aliases). This property mainly affects
* how [[autoload()]] works. * how [[autoload()]] works.
* @see import * @see autoload()
* @see autoload
*/ */
public static $classMap = []; public static $classMap = [];
/** /**
@ -73,8 +72,8 @@ class BaseYii
public static $app; public static $app;
/** /**
* @var array registered path aliases * @var array registered path aliases
* @see getAlias * @see getAlias()
* @see setAlias * @see setAlias()
*/ */
public static $aliases = ['@yii' => __DIR__]; public static $aliases = ['@yii' => __DIR__];
/** /**
@ -95,7 +94,7 @@ class BaseYii
* ] * ]
* ~~~ * ~~~
* *
* @see createObject * @see createObject()
*/ */
public static $objectConfig = []; 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. * 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. * @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. * @throws InvalidParamException if the alias is invalid while $throwException is true.
* @see setAlias * @see setAlias()
*/ */
public static function getAlias($alias, $throwException = true) public static function getAlias($alias, $throwException = true)
{ {
@ -219,7 +218,7 @@ class BaseYii
* actual path first by calling [[getAlias()]]. * actual path first by calling [[getAlias()]].
* *
* @throws InvalidParamException if $path is an invalid alias. * @throws InvalidParamException if $path is an invalid alias.
* @see getAlias * @see getAlias()
*/ */
public static function setAlias($alias, $path) public static function setAlias($alias, $path)
{ {
@ -368,7 +367,7 @@ class BaseYii
} }
if (($n = func_num_args()) > 1) { if (($n = func_num_args()) > 1) {
/** @var $reflection \ReflectionClass */ /** @var \ReflectionClass $reflection */
if (isset($reflections[$class])) { if (isset($reflections[$class])) {
$reflection = $reflections[$class]; $reflection = $reflections[$class];
} else { } else {
@ -450,7 +449,7 @@ class BaseYii
* ~~~ * ~~~
* @param string $token token for the code block * @param string $token token for the code block
* @param string $category the category of this log message * @param string $category the category of this log message
* @see endProfile * @see endProfile()
*/ */
public static function beginProfile($token, $category = 'application') 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. * 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 $token token for the code block
* @param string $category the category of this log message * @param string $category the category of this log message
* @see beginProfile * @see beginProfile()
*/ */
public static function endProfile($token, $category = 'application') public static function endProfile($token, $category = 'application')
{ {

5
framework/yii/base/ActionFilter.php

@ -8,6 +8,11 @@
namespace yii\base; 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> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @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. * 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 \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. * @property \yii\caching\Cache $cache The cache application component. Null if the component is not enabled.
* This property is read-only. * This property is read-only.
* @property \yii\db\Connection $db The database connection. 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. * This method can only be invoked at the beginning of the constructor.
* @param string $path the root directory of the application. * @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. * @throws InvalidParamException if the directory does not exist.
*/ */
public function setBasePath($path) public function setBasePath($path)
@ -425,7 +427,7 @@ abstract class Application extends Module
/** /**
* Returns the view object. * 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() public function getView()
{ {
@ -614,10 +616,8 @@ abstract class Application extends Module
{ {
$category = get_class($exception); $category = get_class($exception);
if ($exception instanceof HttpException) { if ($exception instanceof HttpException) {
/** @var $exception HttpException */
$category .= '\\' . $exception->statusCode; $category .= '\\' . $exception->statusCode;
} elseif ($exception instanceof \ErrorException) { } elseif ($exception instanceof \ErrorException) {
/** @var $exception \ErrorException */
$category .= '\\' . $exception->getSeverity(); $category .= '\\' . $exception->getSeverity();
} }
Yii::error((string)$exception, $category); Yii::error((string)$exception, $category);

18
framework/yii/base/Component.php

@ -10,6 +10,8 @@ namespace yii\base;
use Yii; use Yii;
/** /**
* Component is the base class that implements the *property*, *event* and *behavior* features.
*
* @include @yii/base/Component.md * @include @yii/base/Component.md
* *
* @property Behavior[] $behaviors List of behaviors attached to this component. This property is read-only. * @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 * @return mixed the property value or the value of a behavior's property
* @throws UnknownPropertyException if the property is not defined * @throws UnknownPropertyException if the property is not defined
* @throws InvalidCallException if the property is write-only. * @throws InvalidCallException if the property is write-only.
* @see __set * @see __set()
*/ */
public function __get($name) public function __get($name)
{ {
@ -80,7 +82,7 @@ class Component extends Object
* @param mixed $value the property value * @param mixed $value the property value
* @throws UnknownPropertyException if the property is not defined * @throws UnknownPropertyException if the property is not defined
* @throws InvalidCallException if the property is read-only. * @throws InvalidCallException if the property is read-only.
* @see __get * @see __get()
*/ */
public function __set($name, $value) 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 $checkVars whether to treat member variables as properties
* @param boolean $checkBehaviors whether to treat behaviors' properties as properties of this component * @param boolean $checkBehaviors whether to treat behaviors' properties as properties of this component
* @return boolean whether the property is defined * @return boolean whether the property is defined
* @see canGetProperty * @see canGetProperty()
* @see canSetProperty * @see canSetProperty()
*/ */
public function hasProperty($name, $checkVars = true, $checkBehaviors = true) 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 $checkVars whether to treat member variables as properties
* @param boolean $checkBehaviors whether to treat behaviors' properties as properties of this component * @param boolean $checkBehaviors whether to treat behaviors' properties as properties of this component
* @return boolean whether the property can be read * @return boolean whether the property can be read
* @see canSetProperty * @see canSetProperty()
*/ */
public function canGetProperty($name, $checkVars = true, $checkBehaviors = true) 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 $checkVars whether to treat member variables as properties
* @param boolean $checkBehaviors whether to treat behaviors' properties as properties of this component * @param boolean $checkBehaviors whether to treat behaviors' properties as properties of this component
* @return boolean whether the property can be written * @return boolean whether the property can be written
* @see canGetProperty * @see canGetProperty()
*/ */
public function canSetProperty($name, $checkVars = true, $checkBehaviors = true) 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. * - an object configuration array that will be passed to [[Yii::createObject()]] to create the behavior object.
* *
* @return Behavior the behavior object * @return Behavior the behavior object
* @see detachBehavior * @see detachBehavior()
*/ */
public function attachBehavior($name, $behavior) 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, * 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. * 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 * @param array $behaviors list of behaviors to be attached to the component
* @see attachBehavior * @see attachBehavior()
*/ */
public function attachBehaviors($behaviors) 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. * @param array $params the parameters (name-value pairs) to be passed to the action.
* @return mixed the result of the action * @return mixed the result of the action
* @throws InvalidRouteException if the requested action ID cannot be resolved into an action successfully. * @throws InvalidRouteException if the requested action ID cannot be resolved into an action successfully.
* @see createAction * @see createAction()
*/ */
public function runAction($id, $params = []) 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 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. * @param array $params the parameters to be passed to the action.
* @return mixed the result of the action * @return mixed the result of the action
* @see runAction * @see runAction()
* @see forward
*/ */
public function run($route, $params = []) 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 * ErrorHandler displays these errors using appropriate views based on the
* nature of the errors and the mode the application runs at. * 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 Qiang Xue <qiang.xue@gmail.com>
* @author Timur Ruziev <resurtm@gmail.com> * @author Timur Ruziev <resurtm@gmail.com>
* @since 2.0 * @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, * 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. * 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> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @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 * @property ArrayIterator $iterator An iterator for traversing the items in the list. This property is
* read-only. * read-only.
* @property string $scenario The scenario that this model is in. Defaults to [[DEFAULT_SCENARIO]]. * @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> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
@ -91,19 +92,19 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
* *
* ~~~ * ~~~
* [ * [
* 'attribute list', * ['attribute1', 'attribute2'],
* 'validator type', * 'validator type',
* 'on' => 'scenario name', * 'on' => ['scenario1', 'scenario2'],
* ...other parameters... * ...other parameters...
* ] * ]
* ~~~ * ~~~
* *
* where * 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 * - 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). * 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. * 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. * - additional name-value pairs can be specified to initialize the corresponding validator properties.
* Please refer to individual validator class API for possible 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 * // built-in "required" validator
* ['username', 'required'], * [['username', 'password'], 'required'],
* // built-in "string" validator customized with "min" and "max" properties * // built-in "string" validator customized with "min" and "max" properties
* ['username', 'string', 'min' => 3, 'max' => 12], * ['username', 'string', 'min' => 3, 'max' => 12],
* // built-in "compare" validator that is used in "register" scenario only * // 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()`. * merge the parent rules with child rules using functions such as `array_merge()`.
* *
* @return array validation rules * @return array validation rules
* @see scenarios * @see scenarios()
*/ */
public function rules() 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()`. * merge the parent labels with child labels using functions such as `array_merge()`.
* *
* @return array attribute labels (name => label) * @return array attribute labels (name => label)
* @see generateAttributeLabel * @see generateAttributeLabel()
*/ */
public function attributeLabels() public function attributeLabels()
{ {
@ -349,7 +350,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
* $model->validators[] = $newValidator; * $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() public function getValidators()
{ {
@ -369,7 +370,6 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
{ {
$validators = []; $validators = [];
$scenario = $this->getScenario(); $scenario = $this->getScenario();
/** @var $validator Validator */
foreach ($this->getValidators() as $validator) { foreach ($this->getValidators() as $validator) {
if ($validator->isActive($scenario) && ($attribute === null || in_array($attribute, $validator->attributes, true))) { if ($validator->isActive($scenario) && ($attribute === null || in_array($attribute, $validator->attributes, true))) {
$validators[] = $validator; $validators[] = $validator;
@ -391,7 +391,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
if ($rule instanceof Validator) { if ($rule instanceof Validator) {
$validators->append($rule); $validators->append($rule);
} elseif (is_array($rule) && isset($rule[0], $rule[1])) { // attributes, validator type } 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); $validators->append($validator);
} else { } else {
throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.'); 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. * Returns the text label for the specified attribute.
* @param string $attribute the attribute name * @param string $attribute the attribute name
* @return string the attribute label * @return string the attribute label
* @see generateAttributeLabel * @see generateAttributeLabel()
* @see attributeLabels * @see attributeLabels()
*/ */
public function getAttributeLabel($attribute) public function getAttributeLabel($attribute)
{ {
@ -483,8 +483,8 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
* ] * ]
* ~~~ * ~~~
* *
* @see getFirstErrors * @see getFirstErrors()
* @see getFirstError * @see getFirstError()
*/ */
public function getErrors($attribute = null) 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. * 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. * @return array the first errors. An empty array will be returned if there is no error.
* @see getErrors * @see getErrors()
* @see getFirstError * @see getFirstError()
*/ */
public function getFirstErrors() public function getFirstErrors()
{ {
@ -520,8 +520,8 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
* Returns the first error of the specified attribute. * Returns the first error of the specified attribute.
* @param string $attribute attribute name. * @param string $attribute attribute name.
* @return string the error message. Null is returned if no error. * @return string the error message. Null is returned if no error.
* @see getErrors * @see getErrors()
* @see getFirstErrors * @see getFirstErrors()
*/ */
public function getFirstError($attribute) public function getFirstError($attribute)
{ {

2
framework/yii/base/Module.php

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

15
framework/yii/base/Object.php

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

1
framework/yii/base/Request.php

@ -8,6 +8,7 @@
namespace yii\base; 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 boolean $isConsoleRequest The value indicating whether the current request is made via console.
* @property string $scriptFile Entry script file path (processed w/ realpath()). * @property string $scriptFile Entry script file path (processed w/ realpath()).

2
framework/yii/base/Response.php

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

4
framework/yii/base/View.php

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

2
framework/yii/base/ViewEvent.php

@ -8,6 +8,8 @@
namespace yii\base; namespace yii\base;
/** /**
* ViewEvent represents events triggered by the [[View]] component.
*
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @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. * @var Dependency[] list of dependencies that this dependency is composed of.
* Each array element must be a dependency object. * Each array element must be a dependency object.
*/ */
public $dependencies; public $dependencies = [];
/** /**
* @var boolean whether this dependency is depending on every dependency in [[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. * 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; 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. * Evaluates the dependency by generating and saving the data related with dependency.
* @param Cache $cache the cache component that is currently evaluating this 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]]. * @var array the parameters (name => value) to be bound to the SQL statement specified by [[sql]].
*/ */
public $params; 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);
}
/** /**
* Generates the data needed to determine if dependency has been changed. * Generates the data needed to determine if dependency has been changed.
@ -62,6 +49,9 @@ class DbDependency extends Dependency
if (!$db instanceof Connection) { if (!$db instanceof Connection) {
throw new InvalidConfigException("DbDependency::db must be the application component ID of a DB 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) { if ($db->enableQueryCache) {
// temporarily disable and re-enable query caching // 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, * 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). * 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 * @var mixed custom parameters associated with this dependency. You may get the value
* of this property in [[expression]] using `$this->params`. * of this property in [[expression]] using `$this->params`.
@ -35,19 +35,6 @@ class ExpressionDependency extends Dependency
public $params; 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. * Generates the data needed to determine if dependency has been changed.
* This method returns the result of the PHP expression. * This method returns the result of the PHP expression.
* @param Cache $cache the cache component that is currently evaluating this dependency * @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; namespace yii\caching;
use yii\base\InvalidConfigException;
/** /**
* FileDependency represents a dependency based on a file's last modification time. * FileDependency represents a dependency based on a file's last modification time.
@ -25,24 +26,17 @@ class FileDependency extends Dependency
public $fileName; 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. * Generates the data needed to determine if dependency has been changed.
* This method returns the file's last modification time. * This method returns the file's last modification time.
* @param Cache $cache the cache component that is currently evaluating this dependency * @param Cache $cache the cache component that is currently evaluating this dependency
* @return mixed the data needed to determine if dependency has been changed. * @return mixed the data needed to determine if dependency has been changed.
* @throws InvalidConfigException if [[fileName]] is not set
*/ */
protected function generateDependencyData($cache) protected function generateDependencyData($cache)
{ {
if ($this->fileName === null) {
throw new InvalidConfigException('FileDependency::fileName must be set');
}
return @filemtime($this->fileName); return @filemtime($this->fileName);
} }
} }

22
framework/yii/caching/GroupDependency.php

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

2
framework/yii/caching/MemCache.php

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

2
framework/yii/captcha/CaptchaAsset.php

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

4
framework/yii/console/Request.php

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

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

@ -52,7 +52,7 @@ class CacheController extends Controller
*/ */
public function actionFlush($component = 'cache') public function actionFlush($component = 'cache')
{ {
/** @var $cache Cache */ /** @var Cache $cache */
$cache = Yii::$app->getComponent($component); $cache = Yii::$app->getComponent($component);
if (!$cache || !$cache instanceof Cache) { if (!$cache || !$cache instanceof Cache) {
throw new Exception('Application component "'.$component.'" is not defined or not a 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; namespace yii\data;
use Yii; use Yii;
use yii\db\ActiveQueryInterface;
use yii\base\InvalidConfigException; use yii\base\InvalidConfigException;
use yii\base\Model; use yii\base\Model;
use yii\db\Query;
use yii\db\ActiveQuery;
use yii\db\Connection; use yii\db\Connection;
use yii\db\QueryInterface;
/** /**
* ActiveDataProvider implements a data provider based on [[Query]] and [[ActiveQuery]]. * ActiveDataProvider implements a data provider based on [[Query]] and [[ActiveQuery]].
@ -54,7 +54,7 @@ use yii\db\Connection;
class ActiveDataProvider extends BaseDataProvider 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. * if it is not explicitly set.
*/ */
public $query; public $query;
@ -97,8 +97,8 @@ class ActiveDataProvider extends BaseDataProvider
*/ */
protected function prepareModels() protected function prepareModels()
{ {
if (!$this->query instanceof Query) { if (!$this->query instanceof QueryInterface) {
throw new InvalidConfigException('The "query" property must be an instance of Query or its subclass.'); 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) { if (($pagination = $this->getPagination()) !== false) {
$pagination->totalCount = $this->getTotalCount(); $pagination->totalCount = $this->getTotalCount();
@ -125,7 +125,7 @@ class ActiveDataProvider extends BaseDataProvider
} }
} }
return $keys; return $keys;
} elseif ($this->query instanceof ActiveQuery) { } elseif ($this->query instanceof ActiveQueryInterface) {
/** @var \yii\db\ActiveRecord $class */ /** @var \yii\db\ActiveRecord $class */
$class = $this->query->modelClass; $class = $this->query->modelClass;
$pks = $class::primaryKey(); $pks = $class::primaryKey();
@ -154,11 +154,11 @@ class ActiveDataProvider extends BaseDataProvider
*/ */
protected function prepareTotalCount() protected function prepareTotalCount()
{ {
if (!$this->query instanceof Query) { if (!$this->query instanceof QueryInterface) {
throw new InvalidConfigException('The "query" property must be an instance of Query or its subclass.'); 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; $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) public function setSort($value)
{ {
parent::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 */ /** @var Model $model */
$model = new $this->query->modelClass; $model = new $this->query->modelClass;
foreach ($model->attributes() as $attribute) { 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 represents a DB query associated with an Active Record class.
* *
* ActiveQuery instances are usually created by [[ActiveRecord::find()]], [[ActiveRecord::findBySql()]] * ActiveQuery instances are usually created by [[ActiveRecord::find()]] and [[ActiveRecord::findBySql()]].
* and [[ActiveRecord::count()]].
* *
* ActiveQuery mainly provides the following methods to retrieve the query results: * 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 Qiang Xue <qiang.xue@gmail.com>
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0 * @since 2.0
*/ */
class ActiveQuery extends Query class ActiveQuery extends Query implements ActiveQueryInterface
{ {
/** use 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;
/** /**
* @var string the SQL statement to be executed for retrieving AR records. * @var string the SQL statement to be executed for retrieving AR records.
* This is set by [[ActiveRecord::findBySql()]]. * 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. * Executes query and returns all results as an array.
* @param Connection $db the DB connection used to create the DB command. * @param Connection $db the DB connection used to create the DB command.
* If null, the DB connection returned by [[modelClass]] will be used. * If null, the DB connection returned by [[modelClass]] will be used.
@ -122,7 +92,7 @@ class ActiveQuery extends Query
if ($this->asArray) { if ($this->asArray) {
$model = $row; $model = $row;
} else { } else {
/** @var $class ActiveRecord */ /** @var ActiveRecord $class */
$class = $this->modelClass; $class = $this->modelClass;
$model = $class::create($row); $model = $class::create($row);
} }
@ -145,7 +115,7 @@ class ActiveQuery extends Query
*/ */
public function createCommand($db = null) public function createCommand($db = null)
{ {
/** @var $modelClass ActiveRecord */ /** @var ActiveRecord $modelClass */
$modelClass = $this->modelClass; $modelClass = $this->modelClass;
if ($db === null) { if ($db === null) {
$db = $modelClass::getDb(); $db = $modelClass::getDb();
@ -164,165 +134,4 @@ class ActiveQuery extends Query
} }
return $db->createCommand($this->sql, $params); 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 * @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 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. * 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 * @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 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. * the key value is null). This property is read-only.
* *
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0 * @since 2.0
*/ */
class ActiveRecord extends Model class ActiveRecord extends Model
@ -250,7 +253,7 @@ class ActiveRecord extends Model
/** /**
* Creates an [[ActiveQuery]] instance. * 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 * You may override this method to return a customized query (e.g. `CustomerQuery` specified
* written for querying `Customer` purpose.) * written for querying `Customer` purpose.)
* @return ActiveQuery the newly created [[ActiveQuery]] instance. * @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. * This method is overridden so that attributes and related objects can be accessed like properties.
* @param string $name property name * @param string $name property name
* @return mixed property value * @return mixed property value
* @see getAttribute * @see getAttribute()
*/ */
public function __get($name) public function __get($name)
{ {
@ -383,7 +386,7 @@ class ActiveRecord extends Model
return $this->_related[$name]; return $this->_related[$name];
} }
$value = parent::__get($name); $value = parent::__get($name);
if ($value instanceof ActiveRelation) { if ($value instanceof ActiveRelationInterface) {
return $this->_related[$name] = $value->multiple ? $value->all() : $value->one(); return $this->_related[$name] = $value->multiple ? $value->all() : $value->one();
} else { } else {
return $value; return $value;
@ -472,7 +475,7 @@ class ActiveRecord extends Model
*/ */
public function hasOne($class, $link) public function hasOne($class, $link)
{ {
return new ActiveRelation([ return $this->createActiveRelation([
'modelClass' => $class, 'modelClass' => $class,
'primaryModel' => $this, 'primaryModel' => $this,
'link' => $link, 'link' => $link,
@ -510,7 +513,7 @@ class ActiveRecord extends Model
*/ */
public function hasMany($class, $link) public function hasMany($class, $link)
{ {
return new ActiveRelation([ return $this->createActiveRelation([
'modelClass' => $class, 'modelClass' => $class,
'primaryModel' => $this, 'primaryModel' => $this,
'link' => $link, '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. * Populates the named relation with the related records.
* Note that this method does not check if the relation exists or not. * Note that this method does not check if the relation exists or not.
* @param string $name the relation name (case-sensitive) * @param string $name the relation name (case-sensitive)
@ -574,7 +589,7 @@ class ActiveRecord extends Model
* null will be returned. * null will be returned.
* @param string $name the attribute name * @param string $name the attribute name
* @return mixed the attribute value. Null if the attribute is not set or does not exist. * @return mixed the attribute value. Null if the attribute is not set or does not exist.
* @see hasAttribute * @see hasAttribute()
*/ */
public function getAttribute($name) public function getAttribute($name)
{ {
@ -586,7 +601,7 @@ class ActiveRecord extends Model
* @param string $name the attribute name * @param string $name the attribute name
* @param mixed $value the attribute value. * @param mixed $value the attribute value.
* @throws InvalidParamException if the named attribute does not exist. * @throws InvalidParamException if the named attribute does not exist.
* @see hasAttribute * @see hasAttribute()
*/ */
public function setAttribute($name, $value) public function setAttribute($name, $value)
{ {
@ -623,7 +638,7 @@ class ActiveRecord extends Model
* @param string $name the attribute name * @param string $name the attribute name
* @return mixed the old attribute value. Null if the attribute is not loaded before * @return mixed the old attribute value. Null if the attribute is not loaded before
* or does not exist. * or does not exist.
* @see hasAttribute * @see hasAttribute()
*/ */
public function getOldAttribute($name) public function getOldAttribute($name)
{ {
@ -635,7 +650,7 @@ class ActiveRecord extends Model
* @param string $name the attribute name * @param string $name the attribute name
* @param mixed $value the old attribute value. * @param mixed $value the old attribute value.
* @throws InvalidParamException if the named attribute does not exist. * @throws InvalidParamException if the named attribute does not exist.
* @see hasAttribute * @see hasAttribute()
*/ */
public function setOldAttribute($name, $value) public function setOldAttribute($name, $value)
{ {
@ -1028,7 +1043,7 @@ class ActiveRecord extends Model
/** /**
* Sets the value indicating whether the record is new. * Sets the value indicating whether the record is new.
* @param boolean $value whether the record is new and should be inserted when calling [[save()]]. * @param boolean $value whether the record is new and should be inserted when calling [[save()]].
* @see getIsNewRecord * @see getIsNewRecord()
*/ */
public function setIsNewRecord($value) public function setIsNewRecord($value)
{ {
@ -1281,7 +1296,7 @@ class ActiveRecord extends Model
$getter = 'get' . $name; $getter = 'get' . $name;
try { try {
$relation = $this->$getter(); $relation = $this->$getter();
if ($relation instanceof ActiveRelation) { if ($relation instanceof ActiveRelationInterface) {
return $relation; return $relation;
} else { } else {
return null; return null;
@ -1319,9 +1334,9 @@ class ActiveRecord extends Model
throw new InvalidCallException('Unable to link models: both models must NOT be newly created.'); throw new InvalidCallException('Unable to link models: both models must NOT be newly created.');
} }
if (is_array($relation->via)) { if (is_array($relation->via)) {
/** @var $viaRelation ActiveRelation */ /** @var ActiveRelation $viaRelation */
list($viaName, $viaRelation) = $relation->via; list($viaName, $viaRelation) = $relation->via;
/** @var $viaClass ActiveRecord */ /** @var ActiveRecord $viaClass */
$viaClass = $viaRelation->modelClass; $viaClass = $viaRelation->modelClass;
$viaTable = $viaClass::tableName(); $viaTable = $viaClass::tableName();
// unset $viaName so that it can be reloaded to reflect the change // 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 ($relation->via !== null) {
if (is_array($relation->via)) { if (is_array($relation->via)) {
/** @var $viaRelation ActiveRelation */ /** @var ActiveRelation $viaRelation */
list($viaName, $viaRelation) = $relation->via; list($viaName, $viaRelation) = $relation->via;
/** @var $viaClass ActiveRecord */ /** @var ActiveRecord $viaClass */
$viaClass = $viaRelation->modelClass; $viaClass = $viaRelation->modelClass;
$viaTable = $viaClass::tableName(); $viaTable = $viaClass::tableName();
unset($this->_related[$viaName]); unset($this->_related[$viaName]);
@ -1442,7 +1457,7 @@ class ActiveRecord extends Model
if (!$relation->multiple) { if (!$relation->multiple) {
unset($this->_related[$name]); unset($this->_related[$name]);
} elseif (isset($this->_related[$name])) { } elseif (isset($this->_related[$name])) {
/** @var $b ActiveRecord */ /** @var ActiveRecord $b */
foreach ($this->_related[$name] as $a => $b) { foreach ($this->_related[$name] as $a => $b) {
if ($model->getPrimaryKey() == $b->getPrimaryKey()) { if ($model->getPrimaryKey() == $b->getPrimaryKey()) {
unset($this->_related[$name][$a]); unset($this->_related[$name][$a]);

236
framework/yii/db/ActiveRelation.php

@ -8,8 +8,6 @@
namespace yii\db; namespace yii\db;
use yii\base\InvalidConfigException;
/** /**
* ActiveRelation represents a relation between two Active Record classes. * 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. * 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 Qiang Xue <qiang.xue@gmail.com>
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0 * @since 2.0
*/ */
class ActiveRelation extends ActiveQuery class ActiveRelation extends ActiveQuery implements ActiveRelationInterface
{ {
/** use 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 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;
}
/** /**
* Specifies the pivot table. * Specifies the pivot table.
@ -120,7 +73,7 @@ class ActiveRelation extends ActiveQuery
$this->filterByModels($viaModels); $this->filterByModels($viaModels);
} elseif (is_array($this->via)) { } elseif (is_array($this->via)) {
// via relation // via relation
/** @var $viaQuery ActiveRelation */ /** @var ActiveRelation $viaQuery */
list($viaName, $viaQuery) = $this->via; list($viaName, $viaQuery) = $this->via;
if ($viaQuery->multiple) { if ($viaQuery->multiple) {
$viaModels = $viaQuery->all(); $viaModels = $viaQuery->all();
@ -137,179 +90,4 @@ class ActiveRelation extends ActiveQuery
} }
return parent::createCommand($db); 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(); $rawSql = $this->getRawSql();
Yii::trace($rawSql, __METHOD__); Yii::info($rawSql, __METHOD__);
if ($sql == '') { if ($sql == '') {
return 0; return 0;
@ -364,9 +364,9 @@ class Command extends \yii\base\Component
$db = $this->db; $db = $this->db;
$rawSql = $this->getRawSql(); $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 !== '') { if ($db->enableQueryCache && $method !== '') {
$cache = is_string($db->queryCache) ? Yii::$app->getComponent($db->queryCache) : $db->queryCache; $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 Qiang Xue <qiang.xue@gmail.com>
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0 * @since 2.0
*/ */
class Query extends Component class Query extends Component implements QueryInterface
{ {
/** use QueryTrait;
* Sort ascending
* @see orderBy
*/
const SORT_ASC = false;
/**
* Sort descending
* @see orderBy
*/
const SORT_DESC = true;
/** /**
* @var array the columns being selected. For example, `['id', 'name']`. * @var array the columns being selected. For example, `['id', 'name']`.
@ -71,28 +63,6 @@ class Query extends Component
*/ */
public $from; 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']`. * @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. * 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]`. * For example, `[':name' => 'Dan', ':age' => 31]`.
*/ */
public $params; 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. * Executes the query and returns all results as an array.
* @param Connection $db the database connection used to generate the SQL statement. * @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, the `db` application component will be used.
@ -239,7 +182,7 @@ class Query extends Component
* @param string $q the COUNT expression. Defaults to '*'. * @param string $q the COUNT expression. Defaults to '*'.
* Make sure you properly quote column names in the expression. * Make sure you properly quote column names in the expression.
* @param Connection $db the database connection used to generate the SQL statement. * @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 * @return integer number of records
*/ */
public function count($q = '*', $db = null) 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. * Appends a SQL statement using UNION operator.
* @param string|Query $sql the SQL statement to be appended using UNION * @param string|Query $sql the SQL statement to be appended using UNION
* @return static the query object itself * @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)) { if (is_object($direction)) {
$orders[] = (string)$direction; $orders[] = (string)$direction;
} else { } 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); $realName = $this->getRawTableName($name);
if ($db->enableSchemaCache && !in_array($name, $db->schemaCacheExclude, true)) { 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; $cache = is_string($db->schemaCache) ? Yii::$app->getComponent($db->schemaCache) : $db->schemaCache;
if ($cache instanceof Cache) { if ($cache instanceof Cache) {
$key = $this->getCacheKey($name); $key = $this->getCacheKey($name);
if ($refresh || ($table = $cache->get($key)) === false) { if ($refresh || ($table = $cache->get($key)) === false) {
$table = $this->loadTableSchema($realName); $table = $this->loadTableSchema($realName);
if ($table !== null) { 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; return $this->_tables[$name] = $table;
@ -213,7 +215,7 @@ abstract class Schema extends Object
*/ */
public function refresh() public function refresh()
{ {
/** @var $cache Cache */ /** @var Cache $cache */
$cache = is_string($this->db->schemaCache) ? Yii::$app->getComponent($this->db->schemaCache) : $this->db->schemaCache; $cache = is_string($this->db->schemaCache) ? Yii::$app->getComponent($this->db->schemaCache) : $this->db->schemaCache;
if ($this->db->enableSchemaCache && $cache instanceof Cache) { if ($this->db->enableSchemaCache && $cache instanceof Cache) {
GroupDependency::invalidate($cache, $this->getCacheGroup()); GroupDependency::invalidate($cache, $this->getCacheGroup());
@ -289,7 +291,7 @@ abstract class Schema extends Object
* then this method will do nothing. * then this method will do nothing.
* @param string $name table name * @param string $name table name
* @return string the properly quoted table name * @return string the properly quoted table name
* @see quoteSimpleTableName * @see quoteSimpleTableName()
*/ */
public function quoteTableName($name) public function quoteTableName($name)
{ {
@ -314,7 +316,7 @@ abstract class Schema extends Object
* then this method will do nothing. * then this method will do nothing.
* @param string $name column name * @param string $name column name
* @return string the properly quoted column name * @return string the properly quoted column name
* @see quoteSimpleColumnName * @see quoteSimpleColumnName()
*/ */
public function quoteColumnName($name) public function quoteColumnName($name)
{ {

2
framework/yii/grid/ActionColumn.php

@ -12,6 +12,8 @@ use Closure;
use yii\helpers\Html; 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> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */

4
framework/yii/grid/DataColumn.php

@ -15,6 +15,10 @@ use yii\helpers\Html;
use yii\helpers\Inflector; 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> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */

15
framework/yii/grid/GridView.php

@ -16,6 +16,10 @@ use yii\helpers\Json;
use yii\widgets\BaseListView; 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> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
@ -89,10 +93,9 @@ class GridView extends BaseListView
*/ */
public $showFooter = false; public $showFooter = false;
/** /**
* @var string|boolean the HTML content to be displayed when [[dataProvider]] does not have any data. * @var boolean whether to show the grid view if [[dataProvider]] returns no data.
* If false, the grid view will still be displayed (without body content though).
*/ */
public $empty = false; public $showOnEmpty = true;
/** /**
* @var array|Formatter the formatter used to format model attribute values into displayable texts. * @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]] * This can be either an instance of [[Formatter]] or an configuration array for creating the [[Formatter]]
@ -342,8 +345,14 @@ class GridView extends BaseListView
} }
} }
} }
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>"; return "<tbody>\n" . implode("\n", $rows) . "\n</tbody>";
} }
}
/** /**
* Renders a table row with the given data model and key. * Renders a table row with the given data model and key.

1
framework/yii/grid/GridViewAsset.php

@ -10,6 +10,7 @@ namespace yii\grid;
use yii\web\AssetBundle; use yii\web\AssetBundle;
/** /**
* This asset bundle provides the javascript files for the [[GridView]] widget.
* *
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @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, * @param boolean $doubleEncode whether to encode HTML entities in `$content`. If false,
* HTML entities in `$content` will not be further encoded. * HTML entities in `$content` will not be further encoded.
* @return string the encoded content * @return string the encoded content
* @see decode * @see decode()
* @see http://www.php.net/manual/en/function.htmlspecialchars.php * @see http://www.php.net/manual/en/function.htmlspecialchars.php
*/ */
public static function encode($content, $doubleEncode = true) public static function encode($content, $doubleEncode = true)
@ -99,7 +99,7 @@ class BaseHtml
* This is the opposite of [[encode()]]. * This is the opposite of [[encode()]].
* @param string $content the content to be decoded * @param string $content the content to be decoded
* @return string the decoded content * @return string the decoded content
* @see encode * @see encode()
* @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php * @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php
*/ */
public static function decode($content) 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()]]. * 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. * If a value is null, the corresponding attribute will not be rendered.
* @return string the generated HTML tag * @return string the generated HTML tag
* @see beginTag * @see beginTag()
* @see endTag * @see endTag()
*/ */
public static function tag($name, $content = '', $options = []) 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()]]. * 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. * If a value is null, the corresponding attribute will not be rendered.
* @return string the generated start tag * @return string the generated start tag
* @see endTag * @see endTag()
* @see tag * @see tag()
*/ */
public static function beginTag($name, $options = []) public static function beginTag($name, $options = [])
{ {
@ -144,8 +144,8 @@ class BaseHtml
* Generates an end tag. * Generates an end tag.
* @param string $name the tag name * @param string $name the tag name
* @return string the generated end tag * @return string the generated end tag
* @see beginTag * @see beginTag()
* @see tag * @see tag()
*/ */
public static function endTag($name) 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()]]. * 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. * If a value is null, the corresponding attribute will not be rendered.
* @return string the generated link tag * @return string the generated link tag
* @see url * @see url()
*/ */
public static function cssFile($url, $options = []) 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()]]. * 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. * If a value is null, the corresponding attribute will not be rendered.
* @return string the generated script tag * @return string the generated script tag
* @see url * @see url()
*/ */
public static function jsFile($url, $options = []) 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()]]. * 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. * If a value is null, the corresponding attribute will not be rendered.
* @return string the generated form start tag. * @return string the generated form start tag.
* @see endForm * @see endForm()
*/ */
public static function beginForm($action = '', $method = 'post', $options = []) public static function beginForm($action = '', $method = 'post', $options = [])
{ {
@ -271,7 +271,7 @@ class BaseHtml
/** /**
* Generates a form end tag. * Generates a form end tag.
* @return string the generated tag * @return string the generated tag
* @see beginForm * @see beginForm()
*/ */
public static function endForm() public static function endForm()
{ {
@ -290,7 +290,7 @@ class BaseHtml
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. * 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. * If a value is null, the corresponding attribute will not be rendered.
* @return string the generated hyperlink * @return string the generated hyperlink
* @see url * @see url()
*/ */
public static function a($text, $url = null, $options = []) 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 * Converts a word like "send_email" to "SendEmail". It
* will remove non alphanumeric character from the word, so * will remove non alphanumeric character from the word, so
* "who's online" will be converted to "WhoSOnline" * "who's online" will be converted to "WhoSOnline"
* @see variablize * @see variablize()
* @param string $word the word to CamelCase * @param string $word the word to CamelCase
* @return string * @return string
*/ */

8
framework/yii/helpers/BaseSecurity.php

@ -175,7 +175,7 @@ class BaseSecurity
/** /**
* Returns a secret key associated with the specified name. * Returns a secret key associated with the specified name.
* If the secret key does not exist, a random key will be generated * 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. * 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 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 * @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) public static function getSecretKey($name, $length = 32)
{ {
static $keys; static $keys;
$keyFile = Yii::$app->getRuntimePath() . '/keys.php'; $keyFile = Yii::$app->getRuntimePath() . '/keys.json';
if ($keys === null) { if ($keys === null) {
$keys = []; $keys = [];
if (is_file($keyFile)) { if (is_file($keyFile)) {
$keys = require($keyFile); $keys = json_decode(file_get_contents($keyFile), true);
} }
} }
if (!isset($keys[$name])) { if (!isset($keys[$name])) {
$keys[$name] = static::generateRandomKey($length); $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]; 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 * Formatter requires the PHP "intl" extension to be installed. Formatter supports localized
* formatting of date, time and numbers, based on the current [[locale]]. * 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> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */

3
framework/yii/i18n/I18N.php

@ -14,6 +14,9 @@ use yii\base\InvalidConfigException;
/** /**
* I18N provides features related with internationalization (I18N) and localization (L10N). * I18N provides features related with internationalization (I18N) and localization (L10N).
* *
* I18N is configured as an application component in [[yii\base\Application]] by default.
* You can access that instance via `Yii::$app->i18n`.
*
* @property MessageFormatter $messageFormatter The message formatter to be used to format message via ICU * @property MessageFormatter $messageFormatter The message formatter to be used to format message via ICU
* message format. Note that the type of this property differs in getter and setter. See * message format. Note that the type of this property differs in getter and setter. See
* [[getMessageFormatter()]] and [[setMessageFormatter()]] for details. * [[getMessageFormatter()]] and [[setMessageFormatter()]] for details.

2
framework/yii/log/Target.php

@ -112,7 +112,7 @@ abstract class Target extends Component
{ {
$context = []; $context = [];
if ($this->logUser && ($user = Yii::$app->getComponent('user', false)) !== null) { if ($this->logUser && ($user = Yii::$app->getComponent('user', false)) !== null) {
/** @var $user \yii\web\User */ /** @var \yii\web\User $user */
$context[] = 'User: ' . $user->getId(); $context[] = 'User: ' . $user->getId();
} }

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

Loading…
Cancel
Save