Browse Source

Merge branch 'master' into 2.1

tags/3.0.0-alpha1
Alexander Makarov 9 years ago
parent
commit
64e134c29c
  1. 67
      build/controllers/PhpDocController.php
  2. 385
      build/controllers/ReleaseController.php
  3. 246
      composer.lock
  4. 24
      docs/guide-id/README.md
  5. 40
      docs/guide-id/intro-upgrade-from-v1.md
  6. 39
      docs/guide-id/intro-yii.md
  7. 5
      docs/guide-ja/db-active-record.md
  8. 2
      docs/guide-ja/db-migrations.md
  9. BIN
      docs/guide-ja/images/tutorial-console-help.png
  10. 2
      docs/guide-ja/rest-authentication.md
  11. 2
      docs/guide-ja/runtime-routing.md
  12. 3
      docs/guide-ja/security-authorization.md
  13. 2
      docs/guide-ja/structure-applications.md
  14. 2
      docs/guide-ja/tutorial-console.md
  15. BIN
      docs/guide-ru/images/tutorial-console-help.png
  16. 4
      docs/guide-ru/structure-modules.md
  17. 4
      docs/guide-ru/tutorial-mailing.md
  18. BIN
      docs/guide-uk/images/tutorial-console-help.png
  19. 5
      docs/guide/db-active-record.md
  20. 8
      docs/guide/db-migrations.md
  21. BIN
      docs/guide/images/tutorial-console-help.png
  22. 4
      docs/guide/input-forms.md
  23. 2
      docs/guide/rest-authentication.md
  24. 2
      docs/guide/runtime-routing.md
  25. 1
      docs/guide/structure-applications.md
  26. 2
      docs/guide/tutorial-console.md
  27. 8
      docs/internals-ja/core-code-style.md
  28. 477
      docs/internals-ru/core-code-style.md
  29. 8
      docs/internals/core-code-style.md
  30. 2
      docs/internals/git-workflow.md
  31. 4
      framework/BaseYii.php
  32. 65
      framework/CHANGELOG.md
  33. 16
      framework/UPGRADE.md
  34. 10
      framework/assets/yii.activeForm.js
  35. 12
      framework/assets/yii.gridView.js
  36. 17
      framework/base/Security.php
  37. 3
      framework/behaviors/AttributeBehavior.php
  38. 2
      framework/behaviors/TimestampBehavior.php
  39. 6
      framework/classes.php
  40. 6
      framework/console/Application.php
  41. 5
      framework/console/controllers/BaseMigrateController.php
  42. 4
      framework/console/controllers/FixtureController.php
  43. 1
      framework/console/controllers/MessageController.php
  44. 14
      framework/console/controllers/MigrateController.php
  45. 1
      framework/console/controllers/ServeController.php
  46. 4
      framework/data/BaseDataProvider.php
  47. 9
      framework/db/ActiveRecordInterface.php
  48. 8
      framework/db/ActiveRelationTrait.php
  49. 22
      framework/db/BaseActiveRecord.php
  50. 34
      framework/db/ColumnSchemaBuilder.php
  51. 60
      framework/db/Command.php
  52. 77
      framework/db/Migration.php
  53. 45
      framework/db/Query.php
  54. 108
      framework/db/QueryBuilder.php
  55. 6
      framework/db/Schema.php
  56. 1
      framework/db/Transaction.php
  57. 16
      framework/db/cubrid/ColumnSchemaBuilder.php
  58. 87
      framework/db/cubrid/QueryBuilder.php
  59. 45
      framework/db/mssql/QueryBuilder.php
  60. 16
      framework/db/mysql/ColumnSchemaBuilder.php
  61. 76
      framework/db/mysql/QueryBuilder.php
  62. 1
      framework/db/mysql/Schema.php
  63. 3
      framework/db/oci/ColumnSchemaBuilder.php
  64. 28
      framework/db/oci/QueryBuilder.php
  65. 1
      framework/db/sqlite/ColumnSchemaBuilder.php
  66. 41
      framework/db/sqlite/QueryBuilder.php
  67. 17
      framework/db/sqlite/Schema.php
  68. 2
      framework/di/Container.php
  69. 60
      framework/grid/CheckboxColumn.php
  70. 13
      framework/grid/Column.php
  71. 26
      framework/grid/DataColumn.php
  72. 2
      framework/helpers/BaseArrayHelper.php
  73. 8
      framework/helpers/BaseConsole.php
  74. 3
      framework/helpers/BaseFileHelper.php
  75. 8
      framework/helpers/BaseMarkdown.php
  76. 9
      framework/log/Dispatcher.php
  77. 3
      framework/messages/de/yii.php
  78. 12
      framework/messages/fa/yii.php
  79. 1
      framework/rbac/DbManager.php
  80. 1
      framework/rbac/PhpManager.php
  81. 61
      framework/validators/DateValidator.php
  82. 67
      framework/views/errorHandler/exception.php
  83. 8
      framework/web/AssetBundle.php
  84. 2
      framework/web/ErrorHandler.php
  85. 3
      framework/web/JsonParser.php
  86. 25
      framework/web/Request.php
  87. 62
      framework/web/UrlManager.php
  88. 1
      framework/web/User.php
  89. 3
      phpunit.xml.dist
  90. 16
      tests/data/ar/Order.php
  91. 15
      tests/data/ar/OrderItem.php
  92. 75
      tests/data/base/TraversableObject.php
  93. 9
      tests/data/mysql.sql
  94. 7
      tests/data/postgres.sql
  95. 24
      tests/framework/BaseYiiTest.php
  96. 21
      tests/framework/ar/ActiveRecordTestTrait.php
  97. 192
      tests/framework/base/SecurityTest.php
  98. 36
      tests/framework/console/ControllerTest.php
  99. 13
      tests/framework/console/FakeController.php
  100. 76
      tests/framework/db/ActiveRecordTest.php
  101. Some files were not shown because too many files have changed in this diff Show More

67
build/controllers/PhpDocController.php

@ -27,6 +27,10 @@ class PhpDocController extends Controller
* for copy and paste.
*/
public $updateFiles = true;
/**
* @var bool whether to add copyright header to php files. This should be skipped in application code.
*/
public $skipFrameworkRequirements = false;
/**
@ -83,7 +87,9 @@ class PhpDocController extends Controller
// fix line endings
$lines = preg_split('/(\r\n|\n|\r)/', $contents);
$this->fixFileDoc($lines);
if (!$this->skipFrameworkRequirements) {
$this->fixFileDoc($lines);
}
$this->fixDocBlockIndentation($lines);
$lines = array_values($this->fixLineSpacing($lines));
@ -105,7 +111,7 @@ class PhpDocController extends Controller
*/
public function options($actionID)
{
return array_merge(parent::options($actionID), ['updateFiles']);
return array_merge(parent::options($actionID), ['updateFiles', 'skipFrameworkRequirements']);
}
protected function findFiles($root, $needsInclude = true)
@ -169,11 +175,7 @@ class PhpDocController extends Controller
} elseif (preg_match('~extensions/([\w\d-]+)[\\\\/]?$~', $root, $matches)) {
$extensionPath = dirname(rtrim($root, '\\/'));
foreach (scandir($extensionPath) as $extension) {
if (ctype_alpha($extension) && is_dir($extensionPath . '/' . $extension)) {
Yii::setAlias("@yii/$extension", "$extensionPath/$extension");
}
}
$this->setUpExtensionAliases($extensionPath);
list(, $extension) = $matches;
Yii::setAlias("@yii/$extension", "$root");
@ -194,6 +196,22 @@ class PhpDocController extends Controller
// if ($extension === 'composer') {
// return [];
// }
} elseif (preg_match('~apps/([\w\d-]+)[\\\\/]?$~', $root, $matches)) {
$extensionPath = dirname(dirname(rtrim($root, '\\/'))) . '/extensions';
$this->setUpExtensionAliases($extensionPath);
list(, $appName) = $matches;
Yii::setAlias("@app-$appName", "$root");
if (is_file($autoloadFile = Yii::getAlias("@app-$appName/vendor/autoload.php"))) {
include($autoloadFile);
}
$except[] = '/runtime/';
$except[] = '/vendor/';
$except[] = '/tests/';
$except[] = '/docs/';
}
$root = FileHelper::normalizePath($root);
$options = [
@ -219,6 +237,15 @@ class PhpDocController extends Controller
return FileHelper::findFiles($root, $options);
}
private function setUpExtensionAliases($extensionPath)
{
foreach (scandir($extensionPath) as $extension) {
if (ctype_alpha($extension) && is_dir($extensionPath . '/' . $extension)) {
Yii::setAlias("@yii/$extension", "$extensionPath/$extension");
}
}
}
/**
* Fix file PHPdoc
*/
@ -459,7 +486,11 @@ class PhpDocController extends Controller
if (!$ref->isSubclassOf('yii\base\Object') && $className != 'yii\base\Object') {
$this->stderr("[INFO] Skipping class $className as it is not a subclass of yii\\base\\Object.\n", Console::FG_BLUE, Console::BOLD);
return false;
}
if ($ref->isSubclassOf('yii\db\BaseActiveRecord')) {
$this->stderr("[INFO] Skipping class $className as it is an ActiveRecord class, property handling is not supported yet.\n", Console::FG_BLUE, Console::BOLD);
return false;
}
@ -484,11 +515,13 @@ class PhpDocController extends Controller
}
}
if (!$seenSince) {
$this->stderr("[ERR] No @since found in class doc in file: $file\n", Console::FG_RED);
}
if (!$seenAuthor) {
$this->stderr("[ERR] No @author found in class doc in file: $file\n", Console::FG_RED);
if (!$this->skipFrameworkRequirements) {
if (!$seenSince) {
$this->stderr("[ERR] No @since found in class doc in file: $file\n", Console::FG_RED);
}
if (!$seenAuthor) {
$this->stderr("[ERR] No @author found in class doc in file: $file\n", Console::FG_RED);
}
}
if (trim($oldDoc) != trim($newDoc)) {
@ -563,6 +596,12 @@ class PhpDocController extends Controller
unset($lines[$i]);
}
}
// if no properties or other tags where present add properties at the end
if ($propertyPosition === false) {
$propertyPosition = count($lines) - 2;
}
$finalDoc = '';
foreach ($lines as $i => $line) {
$finalDoc .= $line . "\n";
@ -589,13 +628,13 @@ class PhpDocController extends Controller
return false;
}
if (count($classes) < 1) {
$interfaces = $this->match('#\ninterface (?<name>\w+)( extends .+)?\n\{(?<content>.+)\n\}(\n|$)#', $file);
$interfaces = $this->match('#\ninterface (?<name>\w+)( extends .+)?\n\{(?<content>.*)\n\}(\n|$)#', $file);
if (count($interfaces) == 1) {
return false;
} elseif (count($interfaces) > 1) {
$this->stderr("[ERR] There should be only one interface in a file: $fileName\n", Console::FG_RED);
} else {
$traits = $this->match('#\ntrait (?<name>\w+)\n\{(?<content>.+)\n\}(\n|$)#', $file);
$traits = $this->match('#\ntrait (?<name>\w+)\n\{(?<content>.*)\n\}(\n|$)#', $file);
if (count($traits) == 1) {
return false;
} elseif (count($traits) > 1) {

385
build/controllers/ReleaseController.php

@ -12,9 +12,30 @@ use yii\base\Exception;
use yii\console\Controller;
use yii\helpers\ArrayHelper;
use yii\helpers\Console;
use yii\helpers\FileHelper;
/**
* ReleaseController is there to help preparing releases
* ReleaseController is there to help preparing releases.
*
* Get a version overview:
*
* ./build release/info
*
* run it with `--update` to fetch tags for all repos:
*
* ./build release/info --update
*
* Make a framework release (apps are always in line with framework):
*
* ./build release framework
* ./build release app-basic
* ./build release app-advanced
*
* Make an extension release (e.g. for redis):
*
* ./build release redis
*
* Be sure to check the help info for individual sub-commands:
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
@ -66,30 +87,35 @@ class ReleaseController extends Controller
*/
public function actionInfo()
{
$extensions = [
$items = [
'framework',
'app-basic',
'app-advanced',
];
$extensionPath = "{$this->basePath}/extensions";
foreach (scandir($extensionPath) as $extension) {
if (ctype_alpha($extension) && is_dir($extensionPath . '/' . $extension)) {
$extensions[] = $extension;
$items[] = $extension;
}
}
if ($this->update) {
foreach($extensions as $extension) {
if ($extension === 'framework') {
continue;
foreach($items as $item) {
$this->stdout("fetching tags for $item...");
if ($item === 'framework') {
$this->gitFetchTags("{$this->basePath}");
} elseif (strncmp('app-', $item, 4) === 0) {
$this->gitFetchTags("{$this->basePath}/apps/" . substr($item, 4));
} else {
$this->gitFetchTags("{$this->basePath}/extensions/$item");
}
$this->stdout("fetching tags for $extension...");
$this->gitFetchTags("{$this->basePath}/extensions/$extension");
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
}
} else {
$this->stdout("\nInformation may be outdated, re-run with `--update` to fetch latest tags.\n\n");
}
$versions = $this->getCurrentVersions($extensions);
$versions = $this->getCurrentVersions($items);
$nextVersions = $this->getNextVersions($versions, self::PATCH);
// print version table
@ -141,8 +167,12 @@ class ReleaseController extends Controller
* committing and pushing them. Each git command must be confirmed and can be skipped individually.
* You may adjust changes in a separate shell or your IDE while the command is waiting for confirmation.
*
* @param array $what what do you want to release? this can either be an extension name such as `redis` or `bootstrap`,
* or `framework` if you want to release a new version of the framework itself.
* @param array $what what do you want to release? this can either be:
*
* - an extension name such as `redis` or `bootstrap`,
* - an application indicated by prefix `app-`, e.g. `app-basic`,
* - or `framework` if you want to release a new version of the framework itself.
*
* @return int
*/
public function actionRelease(array $what)
@ -167,8 +197,10 @@ class ReleaseController extends Controller
$this->stdout("\n");
$this->stdout("Before you make a release briefly go over the changes and check if you spot obvious mistakes:\n\n", Console::BOLD);
$this->stdout("- no accidentally added CHANGELOG lines for other versions than this one?\n");
$this->stdout("- are all new `@since` tags for this relase version?\n");
if (strncmp('app-', reset($what), 4) !== 0) {
$this->stdout("- no accidentally added CHANGELOG lines for other versions than this one?\n");
$this->stdout("- are all new `@since` tags for this relase version?\n");
}
$travisUrl = reset($what) === 'framework' ? '' : '-'.reset($what);
$this->stdout("- are unit tests passing on travis? https://travis-ci.org/yiisoft/yii2$travisUrl/builds\n");
$this->stdout("- other issues with code changes?\n");
@ -184,6 +216,8 @@ class ReleaseController extends Controller
foreach($what as $ext) {
if ($ext === 'framework') {
$this->releaseFramework("{$this->basePath}/framework", $newVersions['framework']);
} elseif (strncmp('app-', $ext, 4) === 0) {
$this->releaseApplication(substr($ext, 4), "{$this->basePath}/apps/" . substr($ext, 4), $newVersions[$ext]);
} else {
$this->releaseExtension($ext, "{$this->basePath}/extensions/$ext", $newVersions[$ext]);
}
@ -192,10 +226,75 @@ class ReleaseController extends Controller
return 0;
}
protected function printWhat(array $what, $newVersions, $versions)
/**
* This will generate application packages for download page.
*
* Usage:
*
* ```
* ./build/build release/package app-basic
* ```
*
* @param array $what what do you want to package? this can either be:
*
* - an application indicated by prefix `app-`, e.g. `app-basic`,
*
* @return int
*/
public function actionPackage(array $what)
{
$this->validateWhat($what, ['app']);
$versions = $this->getCurrentVersions($what);
$this->stdout("You are about to generate packages for the following things:\n\n");
foreach($what as $ext) {
if (strncmp('app-', $ext, 4) === 0) {
$this->stdout(" - ");
$this->stdout(substr($ext, 4), Console::FG_RED);
$this->stdout(" application version ");
} elseif ($ext === 'framework') {
$this->stdout(" - Yii Framework version ");
} else {
$this->stdout(" - ");
$this->stdout($ext, Console::FG_RED);
$this->stdout(" extension version ");
}
$this->stdout($versions[$ext], Console::BOLD);
$this->stdout("\n");
}
$this->stdout("\n");
$packagePath = "{$this->basePath}/packages";
$this->stdout("Packages will be stored in $packagePath\n\n");
if (!$this->confirm('Continue?', false)) {
$this->stdout("Canceled.\n");
return 1;
}
foreach($what as $ext) {
if ($ext === 'framework') {
throw new Exception('Can not package framework.');
} elseif (strncmp('app-', $ext, 4) === 0) {
$this->packageApplication(substr($ext, 4), $versions[$ext], $packagePath);
} else {
throw new Exception('Can not package extension.');
}
}
$this->stdout("\ndone. verify the versions composer installed above and push it to github!\n\n");
return 0;
}
protected function printWhat(array $what, $newVersions, $versions)
{
foreach($what as $ext) {
if (strncmp('app-', $ext, 4) === 0) {
$this->stdout(" - ");
$this->stdout(substr($ext, 4), Console::FG_RED);
$this->stdout(" application version ");
} elseif ($ext === 'framework') {
$this->stdout(" - Yii Framework version ");
} else {
$this->stdout(" - ");
@ -221,15 +320,34 @@ class ReleaseController extends Controller
}
}
protected function validateWhat(array $what)
/**
* @param array $what list of items
* @param array $limit list of things to allow, or empty to allow any, can be `app`, `framework`, `extension`
* @throws \yii\base\Exception
*/
protected function validateWhat(array $what, $limit = [])
{
foreach($what as $w) {
if ($w === 'framework') {
if (strncmp('app-', $w, 4) === 0) {
if (!empty($limit) && !in_array('app', $limit)) {
throw new Exception("Only the following types are allowed: ".implode(', ', $limit)."\n");
}
if (!is_dir($appPath = "{$this->basePath}/apps/" . substr($w, 4))) {
throw new Exception("Application path does not exist: \"{$appPath}\"\n");
}
$this->ensureGitClean($appPath);
} elseif ($w === 'framework') {
if (!empty($limit) && !in_array('framework', $limit)) {
throw new Exception("Only the following types are allowed: ".implode(', ', $limit)."\n");
}
if (!is_dir($fwPath = "{$this->basePath}/framework")) {
throw new Exception("Framework path does not exist: \"{$this->basePath}/framework\"\n");
}
$this->ensureGitClean($fwPath);
} else {
if (!empty($limit) && !in_array('ext', $limit)) {
throw new Exception("Only the following types are allowed: ".implode(', ', $limit)."\n");
}
if (!is_dir($extPath = "{$this->basePath}/extensions/$w")) {
throw new Exception("Extension path for \"$w\" does not exist: \"{$this->basePath}/extensions/$w\"\n");
}
@ -241,27 +359,12 @@ class ReleaseController extends Controller
protected function releaseFramework($frameworkPath, $version)
{
throw new Exception('NOT IMPLEMENTED COMPLETELY YET');
$this->stdout("\n");
$this->stdout($h = "Preparing framework release version $version", Console::BOLD);
$this->stdout("\n" . str_repeat('-', strlen($h)) . "\n\n", Console::BOLD);
if ($this->confirm('Run `git checkout master`?', true)) {
chdir($frameworkPath);
exec('git checkout master', $output, $ret); // TODO add compatibility for other release branches
if ($ret != 0) {
throw new Exception('Command "git checkout master" failed with code ' . $ret);
}
}
if ($this->confirm('Run `git pull`?', true)) {
chdir($frameworkPath);
exec('git pull', $output, $ret);
if ($ret != 0) {
throw new Exception('Command "git pull" failed with code ' . $ret);
}
}
$this->runGit('git checkout master', $frameworkPath); // TODO add compatibility for other release branches
$this->runGit('git pull', $frameworkPath); // TODO add compatibility for other release branches
// checks
@ -271,38 +374,57 @@ class ReleaseController extends Controller
// adjustments
$this->stdout('prepare classmap...');
$this->stdout('prepare classmap...', Console::BOLD);
$this->dryRun || Yii::$app->runAction('classmap', [$frameworkPath]);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout('fixing various PHPdoc style issues...');
$this->dryRun || Yii::$app->runAction('php-doc/fix', [$frameworkPath]);
$this->stdout('updating mimetype magic file...', Console::BOLD);
$this->dryRun || Yii::$app->runAction('mime-type', ["$frameworkPath/helpers/mimeTypes.php"]);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout('updating PHPdoc @property annotations...');
$this->dryRun || Yii::$app->runAction('php-doc/property', [$frameworkPath]);
$this->stdout("fixing various PHPdoc style issues...\n", Console::BOLD);
$this->dryRun || Yii::$app->runAction('php-doc/fix', [$frameworkPath]);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout('updating mimetype magic file...');
$this->dryRun || Yii::$app->runAction('mime-type', ["$frameworkPath/helpers/mimeTypes.php"]);
$this->stdout("updating PHPdoc @property annotations...\n", Console::BOLD);
$this->dryRun || Yii::$app->runAction('php-doc/property', [$frameworkPath]);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout('sorting changelogs...');
$this->stdout('sorting changelogs...', Console::BOLD);
$this->dryRun || $this->resortChangelogs(['framework'], $version);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout('closing changelogs...');
$this->stdout('closing changelogs...', Console::BOLD);
$this->dryRun || $this->closeChangelogs(['framework'], $version);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout('updating Yii version...');
$this->dryRun || $this->updateYiiVersion($frameworkPath, $version);;
$this->dryRun || $this->updateYiiVersion($frameworkPath, $version);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
// TODO Commit and push
$this->stdout("\nIn the following you can check the above changes using git diff.\n\n");
do {
$this->runGit("git diff --color", $frameworkPath);
$this->stdout("\n\n\nCheck whether the above diff is okay, if not you may change things as needed before continuing.\n");
$this->stdout("You may abort the program with Ctrl + C and reset the changes by running `git checkout -- .` in the repo.\n\n");
} while(!$this->confirm("Type `yes` to continue, `no` to view git diff again. Continue?"));
// TODO tag and push
$this->stdout("\n\n");
$this->stdout(" **** RELEASE TIME! ****\n", Console::FG_YELLOW, Console::BOLD);
$this->stdout(" **** Commit, Tag and Push it! ****\n", Console::FG_YELLOW, Console::BOLD);
$this->stdout("\n\nHint: if you decide 'no' for any of the following, the command will not be executed. You may manually run them later if needed. E.g. try the release locally without pushing it.\n\n");
$this->runGit("git commit -a -m \"release version $version\"", $frameworkPath);
$this->runGit("git tag -a $version -m\"version $version\"", $frameworkPath);
$this->runGit("git push origin master", $frameworkPath);
$this->runGit("git push --tags", $frameworkPath);
$this->stdout("\n\n");
$this->stdout("CONGRATULATIONS! You have just released extension ", Console::FG_YELLOW, Console::BOLD);
$this->stdout('framework', Console::FG_RED, Console::BOLD);
$this->stdout(" version ", Console::FG_YELLOW, Console::BOLD);
$this->stdout($version, Console::BOLD);
$this->stdout("!\n\n", Console::FG_YELLOW, Console::BOLD);
// TODO release applications
// $this->composerSetStability($what, $version);
@ -325,6 +447,157 @@ class ReleaseController extends Controller
// $this->updateYiiVersion($devVersion);
// }
// prepare next release
$this->stdout("Time to prepare the next release...\n\n", Console::FG_YELLOW, Console::BOLD);
$this->stdout('opening changelogs...', Console::BOLD);
$nextVersion = $this->getNextVersions(['framework' => $version], self::PATCH); // TODO support other versions
$this->dryRun || $this->openChangelogs(['framework'], $nextVersion['framework']);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout('updating Yii version...');
$this->dryRun || $this->updateYiiVersion($frameworkPath, $nextVersion['framework'] . '-dev');
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout("\n");
$this->runGit("git diff --color", $frameworkPath);
$this->stdout("\n\n");
$this->runGit("git commit -a -m \"prepare for next release\"", $frameworkPath);
$this->runGit("git push origin master", $frameworkPath);
$this->stdout("\n\nDONE!", Console::FG_YELLOW, Console::BOLD);
$this->stdout("\n\nThe following steps are left for you to do manually:\n\n");
$nextVersion2 = $this->getNextVersions($nextVersion, self::PATCH); // TODO support other versions
$this->stdout("- wait for your changes to be propagated to the repo and create a tag $version on https://github.com/yiisoft/yii2-framework\n\n");
$this->stdout("- close the $version milestone on github and open new ones for {$nextVersion['framework']} and {$nextVersion2['framework']}: https://github.com/yiisoft/yii2/milestones\n");
$this->stdout("- create a release on github.\n");
$this->stdout("- release news and announcement.\n");
$this->stdout("- update the website (will be automated soon and is only relevant for the new website).\n");
$this->stdout("\n");
$this->stdout("- release applications: ./build/build release app-basic\n");
$this->stdout("- release applications: ./build/build release app-advanced\n");
$this->stdout("\n");
}
protected function releaseApplication($name, $path, $version)
{
$this->stdout("\n");
$this->stdout($h = "Preparing release for application $name version $version", Console::BOLD);
$this->stdout("\n" . str_repeat('-', strlen($h)) . "\n\n", Console::BOLD);
$this->runGit('git checkout master', $path); // TODO add compatibility for other release branches
$this->runGit('git pull', $path); // TODO add compatibility for other release branches
// adjustments
$this->stdout("fixing various PHPdoc style issues...\n", Console::BOLD);
$this->setAppAliases($name, $path);
$this->dryRun || Yii::$app->runAction('php-doc/fix', [$path, 'skipFrameworkRequirements' => true]);
$this->resetAppAliases();
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout("updating PHPdoc @property annotations...\n", Console::BOLD);
$this->setAppAliases($name, $path);
$this->dryRun || Yii::$app->runAction('php-doc/property', [$path, 'skipFrameworkRequirements' => true]);
$this->resetAppAliases();
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout("updating composer stability...\n", Console::BOLD);
$this->dryRun || $this->composerSetStability(["app-$name"], $version);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout("\nIn the following you can check the above changes using git diff.\n\n");
do {
$this->runGit("git diff --color", $path);
$this->stdout("\n\n\nCheck whether the above diff is okay, if not you may change things as needed before continuing.\n");
$this->stdout("You may abort the program with Ctrl + C and reset the changes by running `git checkout -- .` in the repo.\n\n");
} while(!$this->confirm("Type `yes` to continue, `no` to view git diff again. Continue?"));
$this->stdout("\n\n");
$this->stdout(" **** RELEASE TIME! ****\n", Console::FG_YELLOW, Console::BOLD);
$this->stdout(" **** Commit, Tag and Push it! ****\n", Console::FG_YELLOW, Console::BOLD);
$this->stdout("\n\nHint: if you decide 'no' for any of the following, the command will not be executed. You may manually run them later if needed. E.g. try the release locally without pushing it.\n\n");
$this->runGit("git commit -a -m \"release version $version\"", $path);
$this->runGit("git tag -a $version -m\"version $version\"", $path);
$this->runGit("git push origin master", $path);
$this->runGit("git push --tags", $path);
$this->stdout("\n\n");
$this->stdout("CONGRATULATIONS! You have just released application ", Console::FG_YELLOW, Console::BOLD);
$this->stdout($name, Console::FG_RED, Console::BOLD);
$this->stdout(" version ", Console::FG_YELLOW, Console::BOLD);
$this->stdout($version, Console::BOLD);
$this->stdout("!\n\n", Console::FG_YELLOW, Console::BOLD);
// prepare next release
$this->stdout("Time to prepare the next release...\n\n", Console::FG_YELLOW, Console::BOLD);
$this->stdout("updating composer stability...\n", Console::BOLD);
$this->dryRun || $this->composerSetStability(["app-$name"], 'dev');
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$nextVersion = $this->getNextVersions(["app-$name" => $version], self::PATCH); // TODO support other versions
$this->stdout("\n");
$this->runGit("git diff --color", $path);
$this->stdout("\n\n");
$this->runGit("git commit -a -m \"prepare for next release\"", $path);
$this->runGit("git push origin master", $path);
$this->stdout("\n\nDONE!", Console::FG_YELLOW, Console::BOLD);
$this->stdout("\n\nThe following steps are left for you to do manually:\n\n");
$nextVersion2 = $this->getNextVersions($nextVersion, self::PATCH); // TODO support other versions
$this->stdout("- close the $version milestone on github and open new ones for {$nextVersion["app-$name"]} and {$nextVersion2["app-$name"]}: https://github.com/yiisoft/yii2-app-$name/milestones\n");
$this->stdout("- Create Application packages and upload them to github: ./build release/package app-$name\n");
$this->stdout("\n");
}
private $_oldAlias;
protected function setAppAliases($app, $path)
{
$this->_oldAlias = Yii::getAlias('@app');
switch($app) {
case 'basic':
Yii::setAlias('@app', $path);
break;
case 'advanced':
// setup @frontend, @backend etc...
require("$path/common/config/bootstrap.php");
break;
}
}
protected function resetAppAliases()
{
Yii::setAlias('@app', $this->_oldAlias);
}
protected function packageApplication($name, $version, $packagePath)
{
FileHelper::createDirectory($packagePath);
$this->runCommand("composer create-project yiisoft/yii2-app-$name $name $version", $packagePath);
// clear cookie validation key in basic app
if (is_file($configFile = "$packagePath/$name/config/web.php")) {
$this->sed(
"/'cookieValidationKey' => '.*?',/",
"'cookieValidationKey' => '',",
$configFile
);
}
$this->runCommand("tar zcf yii-$name-app-$version.tgz $name", $packagePath);
}
protected function releaseExtension($name, $path, $version)
@ -405,6 +678,22 @@ class ReleaseController extends Controller
}
protected function runCommand($cmd, $path)
{
$this->stdout("running $cmd ...", Console::BOLD);
if ($this->dryRun) {
$this->stdout("dry run, command `$cmd` not executed.\n");
return;
}
chdir($path);
exec($cmd, $output, $ret);
if ($ret != 0) {
echo implode("\n", $output);
throw new Exception("Command \"$cmd\" failed with code " . $ret);
}
$this->stdout("\ndone.\n", Console::BOLD, Console::FG_GREEN);
}
protected function runGit($cmd, $path)
{
if ($this->confirm("Run `$cmd`?", true)) {
@ -446,7 +735,7 @@ class ReleaseController extends Controller
protected function checkComposer($fwPath)
{
if (!$this->confirm("\nNot yet automated: Please check if composer.json dependencies in framework dir match the on in repo root. Continue?", false)) {
if (!$this->confirm("\nNot yet automated: Please check if composer.json dependencies in framework dir match the one in repo root. Continue?", false)) {
exit;
}
}
@ -626,6 +915,8 @@ class ReleaseController extends Controller
foreach($what as $ext) {
if ($ext === 'framework') {
chdir("{$this->basePath}/framework");
} elseif (strncmp('app-', $ext, 4) === 0) {
chdir("{$this->basePath}/apps/" . substr($ext, 4));
} else {
chdir("{$this->basePath}/extensions/$ext");
}

246
composer.lock generated

@ -4,40 +4,27 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "a1ba02f94426ee64a99dfec5c5c45114",
"hash": "d2005b487c5ff761d806b9a94bfe4cac",
"content-hash": "de99885237d7d9364d74fb5f93389801",
"packages": [
{
"name": "bower-asset/jquery",
"version": "2.1.4",
"version": "2.2.3",
"source": {
"type": "git",
"url": "https://github.com/jquery/jquery.git",
"reference": "7751e69b615c6eca6f783a81e292a55725af6b85"
"url": "https://github.com/jquery/jquery-dist.git",
"reference": "af22a351b2ea5801ffb1695abb3bb34d5bed9198"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jquery/jquery/zipball/7751e69b615c6eca6f783a81e292a55725af6b85",
"reference": "7751e69b615c6eca6f783a81e292a55725af6b85",
"url": "https://api.github.com/repos/jquery/jquery-dist/zipball/af22a351b2ea5801ffb1695abb3bb34d5bed9198",
"reference": "af22a351b2ea5801ffb1695abb3bb34d5bed9198",
"shasum": ""
},
"require-dev": {
"bower-asset/qunit": "1.14.0",
"bower-asset/requirejs": "2.1.10",
"bower-asset/sinon": "1.8.1",
"bower-asset/sizzle": "2.1.1-patch2"
},
"type": "bower-asset-library",
"extra": {
"bower-asset-main": "dist/jquery.js",
"bower-asset-ignore": [
"**/.*",
"build",
"dist/cdn",
"speed",
"test",
"*.md",
"AUTHORS.txt",
"Gruntfile.js",
"package.json"
]
},
@ -45,6 +32,7 @@
"MIT"
],
"keywords": [
"browser",
"javascript",
"jquery",
"library"
@ -52,16 +40,16 @@
},
{
"name": "bower-asset/jquery.inputmask",
"version": "3.1.63",
"version": "3.2.7",
"source": {
"type": "git",
"url": "https://github.com/RobinHerbots/jquery.inputmask.git",
"reference": "c40c7287eadc31e341ebbf0c02352eb55b9cbc48"
"reference": "5a72c563b502b8e05958a524cdfffafe9987be38"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/RobinHerbots/jquery.inputmask/zipball/c40c7287eadc31e341ebbf0c02352eb55b9cbc48",
"reference": "c40c7287eadc31e341ebbf0c02352eb55b9cbc48",
"url": "https://api.github.com/repos/RobinHerbots/jquery.inputmask/zipball/5a72c563b502b8e05958a524cdfffafe9987be38",
"reference": "5a72c563b502b8e05958a524cdfffafe9987be38",
"shasum": ""
},
"require": {
@ -70,23 +58,17 @@
"type": "bower-asset-library",
"extra": {
"bower-asset-main": [
"./dist/inputmask/jquery.inputmask.js",
"./dist/inputmask/jquery.inputmask.extensions.js",
"./dist/inputmask/jquery.inputmask.date.extensions.js",
"./dist/inputmask/jquery.inputmask.numeric.extensions.js",
"./dist/inputmask/jquery.inputmask.phone.extensions.js",
"./dist/inputmask/jquery.inputmask.regex.extensions.js"
"./dist/inputmask/inputmask.js"
],
"bower-asset-ignore": [
"**/.*",
"qunit/",
"nuget/",
"tools/",
"js/",
"*.md",
"build.properties",
"build.xml",
"jquery.inputmask.jquery.json"
"**/*",
"!dist/*",
"!dist/inputmask/*",
"!dist/min/*",
"!dist/min/inputmask/*",
"!extra/bindings/*",
"!extra/dependencyLibs/*",
"!extra/phone-codes/*"
]
},
"license": [
@ -132,16 +114,16 @@
},
{
"name": "bower-asset/yii2-pjax",
"version": "dev-master",
"version": "v2.0.6",
"source": {
"type": "git",
"url": "https://github.com/yiisoft/jquery-pjax.git",
"reference": "3f20897307cca046fca5323b318475ae9dac0ca0"
"reference": "60728da6ade5879e807a49ce59ef9a72039b8978"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/yiisoft/jquery-pjax/zipball/3f20897307cca046fca5323b318475ae9dac0ca0",
"reference": "3f20897307cca046fca5323b318475ae9dac0ca0",
"url": "https://api.github.com/repos/yiisoft/jquery-pjax/zipball/60728da6ade5879e807a49ce59ef9a72039b8978",
"reference": "60728da6ade5879e807a49ce59ef9a72039b8978",
"shasum": ""
},
"require": {
@ -154,18 +136,15 @@
".travis.yml",
"Gemfile",
"Gemfile.lock",
"CONTRIBUTING.md",
"vendor/",
"script/",
"test/"
],
"branch-alias": {
"dev-master": "2.0.3-dev"
}
]
},
"license": [
"MIT"
],
"time": "2015-03-08 21:03:11"
]
},
{
"name": "cebe/markdown",
@ -173,12 +152,12 @@
"source": {
"type": "git",
"url": "https://github.com/cebe/markdown.git",
"reference": "e14d3da8f84eefa3792fd22b5b5ecba9c98d2e18"
"reference": "e2a490ceec590bf5bfd1b43bd424fb9dceceb7c5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cebe/markdown/zipball/e14d3da8f84eefa3792fd22b5b5ecba9c98d2e18",
"reference": "e14d3da8f84eefa3792fd22b5b5ecba9c98d2e18",
"url": "https://api.github.com/repos/cebe/markdown/zipball/e2a490ceec590bf5bfd1b43bd424fb9dceceb7c5",
"reference": "e2a490ceec590bf5bfd1b43bd424fb9dceceb7c5",
"shasum": ""
},
"require": {
@ -225,20 +204,20 @@
"markdown",
"markdown-extra"
],
"time": "2015-03-20 11:07:08"
"time": "2016-03-18 13:28:11"
},
{
"name": "ezyang/htmlpurifier",
"version": "v4.6.0",
"version": "v4.7.0",
"source": {
"type": "git",
"url": "https://github.com/ezyang/htmlpurifier.git",
"reference": "6f389f0f25b90d0b495308efcfa073981177f0fd"
"reference": "ae1828d955112356f7677c465f94f7deb7d27a40"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/6f389f0f25b90d0b495308efcfa073981177f0fd",
"reference": "6f389f0f25b90d0b495308efcfa073981177f0fd",
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/ae1828d955112356f7677c465f94f7deb7d27a40",
"reference": "ae1828d955112356f7677c465f94f7deb7d27a40",
"shasum": ""
},
"require": {
@ -269,7 +248,7 @@
"keywords": [
"html"
],
"time": "2013-11-30 08:25:19"
"time": "2015-08-05 01:03:42"
},
{
"name": "yiisoft/yii2-composer",
@ -277,12 +256,12 @@
"source": {
"type": "git",
"url": "https://github.com/yiisoft/yii2-composer.git",
"reference": "6b2a2b3bb83d4b3dd791f76e64c48973ce01bac6"
"reference": "f5fe6ba58dbc92b37daed5d9bd94cda777852ee4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/yiisoft/yii2-composer/zipball/6b2a2b3bb83d4b3dd791f76e64c48973ce01bac6",
"reference": "6b2a2b3bb83d4b3dd791f76e64c48973ce01bac6",
"url": "https://api.github.com/repos/yiisoft/yii2-composer/zipball/f5fe6ba58dbc92b37daed5d9bd94cda777852ee4",
"reference": "f5fe6ba58dbc92b37daed5d9bd94cda777852ee4",
"shasum": ""
},
"require": {
@ -316,7 +295,7 @@
"extension installer",
"yii2"
],
"time": "2015-06-11 19:55:58"
"time": "2016-04-14 08:46:37"
}
],
"packages-dev": [
@ -326,12 +305,12 @@
"source": {
"type": "git",
"url": "https://github.com/cebe/indent.git",
"reference": "c500ed74d30ed2d7e085f9cf07f8092d32d70776"
"reference": "0f33ba3cb567726a726e7024072232839a0d7cd0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cebe/indent/zipball/c500ed74d30ed2d7e085f9cf07f8092d32d70776",
"reference": "c500ed74d30ed2d7e085f9cf07f8092d32d70776",
"url": "https://api.github.com/repos/cebe/indent/zipball/0f33ba3cb567726a726e7024072232839a0d7cd0",
"reference": "0f33ba3cb567726a726e7024072232839a0d7cd0",
"shasum": ""
},
"bin": [
@ -345,13 +324,11 @@
"authors": [
{
"name": "Carsten Brandt",
"email": "mail@cebe.cc",
"homepage": "http://cebe.cc/",
"role": "Core framework development"
"email": "mail@cebe.cc"
}
],
"description": "a small tool to convert text file indentation",
"time": "2014-05-23 14:40:08"
"time": "2015-11-22 14:46:59"
},
{
"name": "doctrine/instantiator",
@ -359,12 +336,12 @@
"source": {
"type": "git",
"url": "https://github.com/doctrine/instantiator.git",
"reference": "8e884e78f9f0eb1329e445619e04456e64d8051d"
"reference": "416fb8ad1d095a87f1d21bc40711843cd122fd4a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d",
"reference": "8e884e78f9f0eb1329e445619e04456e64d8051d",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/416fb8ad1d095a87f1d21bc40711843cd122fd4a",
"reference": "416fb8ad1d095a87f1d21bc40711843cd122fd4a",
"shasum": ""
},
"require": {
@ -405,7 +382,7 @@
"constructor",
"instantiate"
],
"time": "2015-06-14 21:17:01"
"time": "2016-03-31 10:24:22"
},
{
"name": "phpdocumentor/reflection-docblock",
@ -462,18 +439,20 @@
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
"reference": "5700f75b23b0dd3495c0f495fe33a5e6717ee160"
"reference": "b02221e42163be673f9b44a0bc92a8b4907a7c6d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/5700f75b23b0dd3495c0f495fe33a5e6717ee160",
"reference": "5700f75b23b0dd3495c0f495fe33a5e6717ee160",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/b02221e42163be673f9b44a0bc92a8b4907a7c6d",
"reference": "b02221e42163be673f9b44a0bc92a8b4907a7c6d",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.0.2",
"php": "^5.3|^7.0",
"phpdocumentor/reflection-docblock": "~2.0",
"sebastian/comparator": "~1.1"
"sebastian/comparator": "~1.1",
"sebastian/recursion-context": "~1.0"
},
"require-dev": {
"phpspec/phpspec": "~2.0"
@ -481,7 +460,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4.x-dev"
"dev-master": "1.6.x-dev"
}
},
"autoload": {
@ -514,20 +493,20 @@
"spy",
"stub"
],
"time": "2015-06-30 10:26:46"
"time": "2016-02-21 17:41:21"
},
{
"name": "phpunit/php-code-coverage",
"version": "dev-master",
"version": "2.2.x-dev",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "40103f9b335f1d84daed099f180b9de12ba080e7"
"reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/40103f9b335f1d84daed099f180b9de12ba080e7",
"reference": "40103f9b335f1d84daed099f180b9de12ba080e7",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979",
"reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979",
"shasum": ""
},
"require": {
@ -576,7 +555,7 @@
"testing",
"xunit"
],
"time": "2015-08-04 03:45:55"
"time": "2015-10-06 15:47:00"
},
{
"name": "phpunit/php-file-iterator",
@ -668,7 +647,7 @@
},
{
"name": "phpunit/php-timer",
"version": "dev-master",
"version": "1.0.7",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-timer.git",
@ -713,12 +692,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-token-stream.git",
"reference": "7a9b0969488c3c54fd62b4d504b3ec758fd005d9"
"reference": "cab6c6fefee93d7b7c3a01292a0fe0884ea66644"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/7a9b0969488c3c54fd62b4d504b3ec758fd005d9",
"reference": "7a9b0969488c3c54fd62b4d504b3ec758fd005d9",
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/cab6c6fefee93d7b7c3a01292a0fe0884ea66644",
"reference": "cab6c6fefee93d7b7c3a01292a0fe0884ea66644",
"shasum": ""
},
"require": {
@ -754,7 +733,7 @@
"keywords": [
"tokenizer"
],
"time": "2015-06-19 03:43:16"
"time": "2015-09-23 14:46:55"
},
{
"name": "phpunit/phpunit",
@ -762,12 +741,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "0fe5ed323f0017624a65746153ae3fd69c308c1b"
"reference": "3c4becbce99732549949904c47b76ffe602a7595"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0fe5ed323f0017624a65746153ae3fd69c308c1b",
"reference": "0fe5ed323f0017624a65746153ae3fd69c308c1b",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3c4becbce99732549949904c47b76ffe602a7595",
"reference": "3c4becbce99732549949904c47b76ffe602a7595",
"shasum": ""
},
"require": {
@ -781,7 +760,7 @@
"phpunit/php-code-coverage": "~2.1",
"phpunit/php-file-iterator": "~1.4",
"phpunit/php-text-template": "~1.2",
"phpunit/php-timer": ">=1.0.6",
"phpunit/php-timer": "^1.0.6",
"phpunit/phpunit-mock-objects": "~2.3",
"sebastian/comparator": "~1.1",
"sebastian/diff": "~1.2",
@ -826,7 +805,7 @@
"testing",
"xunit"
],
"time": "2015-08-02 08:24:28"
"time": "2016-04-25 09:17:33"
},
{
"name": "phpunit/phpunit-mock-objects",
@ -834,12 +813,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
"reference": "ca2e42cad8446a05691cc4327b7ce3ed45bfdb11"
"reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ca2e42cad8446a05691cc4327b7ce3ed45bfdb11",
"reference": "ca2e42cad8446a05691cc4327b7ce3ed45bfdb11",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983",
"reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983",
"shasum": ""
},
"require": {
@ -882,7 +861,7 @@
"mock",
"xunit"
],
"time": "2015-08-02 07:29:52"
"time": "2015-10-02 06:51:40"
},
{
"name": "sebastian/comparator",
@ -954,24 +933,24 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "6899b3e33bfbd386d88b5eea5f65f563e8793051"
"reference": "13edfd8706462032c2f52b4b862974dd46b71c9e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/6899b3e33bfbd386d88b5eea5f65f563e8793051",
"reference": "6899b3e33bfbd386d88b5eea5f65f563e8793051",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e",
"reference": "13edfd8706462032c2f52b4b862974dd46b71c9e",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.2"
"phpunit/phpunit": "~4.8"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.3-dev"
"dev-master": "1.4-dev"
}
},
"autoload": {
@ -994,11 +973,11 @@
}
],
"description": "Diff implementation",
"homepage": "http://www.github.com/sebastianbergmann/diff",
"homepage": "https://github.com/sebastianbergmann/diff",
"keywords": [
"diff"
],
"time": "2015-06-22 14:15:55"
"time": "2015-12-08 07:14:41"
},
{
"name": "sebastian/environment",
@ -1006,12 +985,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
"reference": "6324c907ce7a52478eeeaede764f48733ef5ae44"
"reference": "dc7a29032cf72b54f36dac15a1ca5b3a1b6029bf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6324c907ce7a52478eeeaede764f48733ef5ae44",
"reference": "6324c907ce7a52478eeeaede764f48733ef5ae44",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/dc7a29032cf72b54f36dac15a1ca5b3a1b6029bf",
"reference": "dc7a29032cf72b54f36dac15a1ca5b3a1b6029bf",
"shasum": ""
},
"require": {
@ -1048,7 +1027,7 @@
"environment",
"hhvm"
],
"time": "2015-08-03 06:14:51"
"time": "2016-02-26 18:40:46"
},
{
"name": "sebastian/exporter",
@ -1056,12 +1035,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "66fb20c9e1b3617651c3f66be7a1747479d96ba5"
"reference": "f88f8936517d54ae6d589166810877fb2015d0a2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/66fb20c9e1b3617651c3f66be7a1747479d96ba5",
"reference": "66fb20c9e1b3617651c3f66be7a1747479d96ba5",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f88f8936517d54ae6d589166810877fb2015d0a2",
"reference": "f88f8936517d54ae6d589166810877fb2015d0a2",
"shasum": ""
},
"require": {
@ -1115,20 +1094,20 @@
"export",
"exporter"
],
"time": "2015-07-30 09:58:30"
"time": "2015-08-09 04:23:41"
},
{
"name": "sebastian/global-state",
"version": "dev-master",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
"reference": "23af31f402993cfd94e99cbc4b782e9a78eb0e97"
"reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/23af31f402993cfd94e99cbc4b782e9a78eb0e97",
"reference": "23af31f402993cfd94e99cbc4b782e9a78eb0e97",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4",
"reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4",
"shasum": ""
},
"require": {
@ -1166,7 +1145,7 @@
"keywords": [
"global state"
],
"time": "2015-06-21 15:11:22"
"time": "2015-10-12 03:26:01"
},
{
"name": "sebastian/recursion-context",
@ -1174,12 +1153,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git",
"reference": "994d4a811bafe801fb06dccbee797863ba2792ba"
"reference": "7ff5b1b3dcc55b8ab8ae61ef99d4730940856ee7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/994d4a811bafe801fb06dccbee797863ba2792ba",
"reference": "994d4a811bafe801fb06dccbee797863ba2792ba",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/7ff5b1b3dcc55b8ab8ae61ef99d4730940856ee7",
"reference": "7ff5b1b3dcc55b8ab8ae61ef99d4730940856ee7",
"shasum": ""
},
"require": {
@ -1219,7 +1198,7 @@
],
"description": "Provides functionality to recursively process PHP variables",
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
"time": "2015-06-21 08:04:50"
"time": "2016-01-28 05:39:29"
},
{
"name": "sebastian/version",
@ -1261,21 +1240,18 @@
"version": "2.8.x-dev",
"source": {
"type": "git",
"url": "https://github.com/symfony/Yaml.git",
"reference": "6eef1477f3c4451b6024fadb1254009be4602908"
"url": "https://github.com/symfony/yaml.git",
"reference": "e4fbcc65f90909c999ac3b4dfa699ee6563a9940"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/6eef1477f3c4451b6024fadb1254009be4602908",
"reference": "6eef1477f3c4451b6024fadb1254009be4602908",
"url": "https://api.github.com/repos/symfony/yaml/zipball/e4fbcc65f90909c999ac3b4dfa699ee6563a9940",
"reference": "e4fbcc65f90909c999ac3b4dfa699ee6563a9940",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7|~3.0.0"
},
"type": "library",
"extra": {
"branch-alias": {
@ -1285,7 +1261,10 @@
"autoload": {
"psr-4": {
"Symfony\\Component\\Yaml\\": ""
}
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@ -1303,17 +1282,20 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"time": "2015-07-29 07:12:56"
"time": "2016-03-29 19:00:15"
}
],
"aliases": [],
"minimum-stability": "dev",
"stability-flags": [],
"stability-flags": {
"bower-asset/jquery": 0
},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=5.4.0",
"ext-mbstring": "*",
"ext-ctype": "*",
"lib-pcre": "*"
},
"platform-dev": []

24
docs/guide-id/README.md

@ -1,9 +1,9 @@
Panduan Defitif Untuk Yii 2.0
Panduan Definitif Untuk Yii 2.0
===============================
Tutorial ini dirilis di bawah [Persyaratan Dokumentasi Yii] (http://www.yiiframework.com/doc/terms/).
Seluruh hak cipta.
Seluruh hak cipta dilindungi.
2014 (c) Yii Software LLC.
@ -23,7 +23,7 @@ Mulai
* [Mengatakan Hello] (start-hello.md)
* [Bekerja dengan Form] (start-forms.md)
* [Bekerja dengan Database] (start-databases.md)
* [Membangkitkan Kode dengan Gii] (start-gii.md)
* [Membuat Kode Otomatis dengan Gii] (start-gii.md)
* [Menatap ke Depan] (start-looking-ahead.md)
@ -66,7 +66,7 @@ Konsep Pokok
* [Perilaku] (concept-behaviors.md)
* [Konfigurasi] (concept-configurations.md)
* [Alias] (concept-aliases.md)
* [Class autoloading] (concept-autoloading.md)
* [Class Autoloading] (concept-autoloading.md)
* [Layanan Locator] (concept-service-locator.md)
* [Dependency Injection] (concept-di-container.md)
@ -76,7 +76,7 @@ Bekerja dengan Database
* [Data Access Objects] (db-dao.md): Menghubungkan ke database, query dasar, transaksi, dan manipulasi skema
* [Query Builder] (db-query-builder.md): Query database menggunakan lapisan abstraksi sederhana
* [Rekaman Aktif] (db-active-record.md): The Rekaman Aktif ORM, mengambil dan memanipulasi catatan, dan mendefinisikan hubungan
* [Active Record] (db-active-record.md): ORM Active Record, mengambil dan memanipulasi catatan, dan mendefinisikan hubungan
* [Migrasi] (db-migrations.md): Terapkan kontrol versi untuk database Anda dalam lingkungan pengembangan tim
* [Sphinx] (https://github.com/yiisoft/yii2-sphinx/blob/master/docs/guide/README.md)
* [Redis] (https://github.com/yiisoft/yii2-redis/blob/master/docs/guide/README.md)
@ -111,8 +111,8 @@ Keamanan
* [Otentikasi] (security-authentication.md)
* [Otorisasi] (security-authorization.md)
* [Bekerja dengan Sandi] (security-passwords.md)
* [Tupoksi Klien] (https://github.com/yiisoft/yii2-authclient/blob/master/docs/guide/README.md)
* [Bekerja dengan Kata Sandi] (security-passwords.md)
* [Otentikasi Klien] (https://github.com/yiisoft/yii2-authclient/blob/master/docs/guide/README.md)
* [Praktik Terbaik] (security-best-practices.md)
@ -144,8 +144,8 @@ Alat Pengembangan
-----------------
* [Debug Toolbar dan Debugger] (https://github.com/yiisoft/yii2-debug/blob/master/docs/guide/README.md)
* [Kode Membangkitkan menggunakan Gii] (https://github.com/yiisoft/yii2-gii/blob/master/docs/guide/README.md)
* ** TBD ** [Membangkitkan API Documentation] (https://github.com/yiisoft/yii2-apidoc)
* [Membuat Kode Otomatis dengan Gii] (https://github.com/yiisoft/yii2-gii/blob/master/docs/guide/README.md)
* ** TBD ** [Membuat API Documentation] (https://github.com/yiisoft/yii2-apidoc)
Pengujian
@ -169,12 +169,12 @@ Topik Khusus
* [Internasionalisasi] (tutorial-i18n.md)
* [Mailing] (tutorial-mailing.md)
* [Penyetelan Performa] (tutorial-performance-tuning.md)
* [Shared Hosting Lingkungan] (tutorial-shared-hosting.md)
* [Lingkungan Shared Hosting] (tutorial-shared-hosting.md)
* [Template Engine] (tutorial-template-engines.md)
* [Bekerja dengan Kode Pihak Ketiga] (tutorial-yii-integration.md)
widget
Widget
-------
* GridView: ** TBD ** Link ke demo halaman
@ -195,4 +195,4 @@ Alat Bantu
* [Tinjauan] (helper-overview.md)
* [ArrayHelper] (helper-array.md)
* [Html] (helper-html.md)
* [Url] (helper-url.md)
* [Url] (helper-url.md)

40
docs/guide-id/intro-upgrade-from-v1.md

@ -1,21 +1,21 @@
Upgrade dari Versi 1.1
==========================
Ada banyak perbedaan antara versi 1.1 dan 2.0 karena Yii sebagai framework benar-benar ditulis ulang untuk 2.0.
Akibatnya, upgrade dari versi 1.1 tidak mudah seperti upgrade antara versi minor. Dalam panduan ini Anda akan
Ada banyak perbedaan antara versi 1.1 dan 2.0 karena Yii Framework benar-benar ditulis ulang di versi 2.0.
Akibatnya, upgrade dari versi 1.1 tidak mudah seperti upgrade untuk versi minor. Dalam panduan ini Anda akan
menemukan perbedaan utama antara dua versi.
Jika Anda belum pernah menggunakan Yii 1.1 sebelumnya, Anda dapat dengan aman melewati bagian ini dan mengubah langsung ke "[Persiapan](start-installation.md)".
Jika Anda belum pernah menggunakan Yii 1.1 sebelumnya, Anda dapat dengan aman melewati bagian ini dan menuju ke "[Persiapan](start-installation.md)".
Harap dicatat bahwa Yii 2.0 memperkenalkan fitur baru dari tercakup dalam ringkasan ini. Hal ini sangat dianjurkan
Anda membaca melalui panduan definitif keseluruhan untuk belajar tentang mereka semua. Kemungkinannya adalah bahwa
beberapa fitur yang sebelumnya Anda harus kembangkan sendiri sekarang menjadi bagian dari kode inti.
Harap dicatat bahwa Yii 2.0 memperkenalkan lebih banyak fitur baru dari yang tercakup dalam ringkasan ini. Sangat dianjurkan
Anda membaca keseluruhan panduan definitif untuk mempelajari hal tersebut. Kemungkinannya adalah bahwa
beberapa fitur yang sebelumnya harus anda kembangkan sendiri kini menjadi bagian dari kode inti.
Instalasi
------------
Yii 2.0 sepenuhnya merangkul [composer](https://getcomposer.org/), yang adalah paket manager PHP. Instalasi
Yii 2.0 sepenuhnya menggunakan [composer](https://getcomposer.org/), yaitu dependency manager yang sudah diakui oleh PHP. Instalasi
dari kerangka inti serta ekstensi, ditangani melalui Composer. Silakan merujuk ke
bagian [Instalasi Yii](start-installation.md) untuk belajar cara menginstal Yii 2.0. Jika Anda menghendaki
membuat ekstensi baru, atau mengubah ekstensi 1.1 yang sudah ke ekstensi 2.0 yang kompatibel, silakan
@ -25,7 +25,7 @@ merujuk panduan [Membuat Ekstensi](structure-extensions.md#menciptakan-ekstensi)
Persyaratan PHP
----------------
Yii 2.0 membutuhkan PHP 5.4 atau di atas, yang merupakan perbaikan besar atas PHP versi 5.2 yang dibutuhkan oleh Yii 1.1.
Yii 2.0 membutuhkan PHP 5.4 atau versi lebih tinggi, yang merupakan perbaikan besar atas PHP versi 5.2 yang dibutuhkan oleh Yii 1.1.
Akibatnya, ada banyak perbedaan pada tingkat bahasa yang harus Anda perhatikan.
Di bawah ini adalah ringkasan perubahan utama mengenai PHP:
@ -41,7 +41,7 @@ Di bawah ini adalah ringkasan perubahan utama mengenai PHP:
  untuk mendukung fitur internasionalisasi.
namespace
Namespace
---------
Perubahan yang paling jelas dalam Yii 2.0 adalah penggunaan namespace. Hampir setiap kelas inti
@ -49,8 +49,7 @@ menggunakan namespace, misalnya, `yii\web\Request`. Awalan "C" tidak lagi diguna
Skema penamaan sekarang mengikuti struktur direktori. Misalnya, `yii\web\Request`
menunjukkan bahwa file kelas yang sesuai adalah `web/Request.php` bawah folder framework Yii.
(Anda dapat menggunakan setiap kelas inti tanpa menyertakannya secara eksplisit berkat Yii
class loader.)
(Anda dapat menggunakan setiap kelas inti tanpa menyertakannya secara eksplisit berkat Yiiclass loader.)
Komponen dan Object
@ -97,8 +96,7 @@ yang berisi pasangan nama-nilai untuk menginisialisasi properti pada akhir konst
Anda dapat menimpa method [[yii\base\Object::init()|init()]] untuk melakukan pekerjaan inisialisasi yang harus dilakukan setelah
konfigurasi telah diterapkan.
Dengan mengikuti konvensi ini, Anda akan dapat membuat dan mengkonfigurasi objek baru
menggunakan array konfigurasi:
Dengan mengikuti konvensi ini, Anda akan dapat membuat dan mengkonfigurasi objek baru menggunakan array konfigurasi:
```php
$object = Yii::createObject([
@ -138,14 +136,14 @@ Path Alias
Yii 2.0 memperluas penggunaan alias path baik untuk file/direktori maupun URL. Yii 2.0 juga sekarang mensyaratkan
nama alias dimulai dengan karakter `@`.
Misalnya, alias `@yii` mengacu pada direktori instalasi Yii. Alias path
Misalnya, alias `@yii` mengacu pada direktori instalasi Yii. Alias path
didukung di sebagian besar tempat di kode inti Yii. Misalnya, [[yii\caching\FileCache::cachePath]] dapat mengambil
baik alias path maupun direktori normal.
Sebuah alias juga terkait erat dengan namespace kelas. Disarankan alias didefinisikan untuk setiap akar namespace,
sehingga memungkinkan Anda untuk menggunakan autoloader class Yii tanpa konfigurasi lebih lanjut.
Misalnya, karena `@yii` mengacu pada direktori instalasi Yii, class seperti `yii\web\Request` dapat otomatis diambil.
Jika Anda menggunakan librari pihak ketiga seperti Zend Framework. Anda dapat menentukan alias path `@Zend` yang mengacu pada
sehingga memungkinkan Anda untuk menggunakan autoloader class Yii tanpa konfigurasi lebih lanjut.
Misalnya, karena `@yii` mengacu pada direktori instalasi Yii, class seperti `yii\web\Request` dapat otomatis diambil.
Jika Anda menggunakan librari pihak ketiga seperti Zend Framework. Anda dapat menentukan alias path `@Zend` yang mengacu pada
direktori instalasi framework direktori. Setelah Anda selesai melakukannya, Yii akan dapat menload setiap class dalam librari Zend Framework.
Lebih jauh tentang alias path dapat ditemukan di bagian [Alias](concept-aliases.md).
@ -217,7 +215,7 @@ Yii 2.0 menggunakan [[yii\web\Controller]] sebagai kelas dasar controller, yang
Dampak paling nyata dari perubahan ini pada kode Anda adalah bahwa aksi kontroler harus mengembalikan nilai konten
alih-alih menampilkannya:
````php
```php
public function actionView($id)
{
$model = \app\models\Post::findOne($id);
@ -303,10 +301,10 @@ sumber berdasarkan kategori pesan.
Silakan merujuk ke bagian [Internasionalisasi](tutorial-i18n.md) untuk rincian lebih lanjut.
Filter Action
Action Filter
--------------
Filter Action sekarang diimplementasikan melalui behavior. Untuk membuat baru, filter diperluas dari [[yii\base\ActionFilter]].
Action Filter sekarang diimplementasikan melalui behavior. Untuk membuat baru, filter diperluas dari [[yii\base\ActionFilter]].
Untuk menggunakan filter, pasang Kelas filter untuk controller sebagai behavior. Misalnya, untuk menggunakan filter [[yii\filters\AccessControl]],
Anda harus mengikuti kode berikut di kontroler:
@ -539,4 +537,4 @@ Menggunakan Yii 1.1 dan 2.x bersama-sama
------------------------------
Jika Anda memiliki warisan kode Yii 1.1 yang ingin Anda gunakan bersama-sama dengan Yii 2.0, silakan lihat
bagian [Menggunakan Yii 1.1 dan 2.0 Bersama](tutorial-yii-integration.md).
bagian [Menggunakan Yii 1.1 dan 2.0 Bersama](tutorial-yii-integration.md).

39
docs/guide-id/intro-yii.md

@ -2,7 +2,7 @@ Apa Itu Yii
===========
Yii adalah kerangka kerja PHP berkinerja tinggi, berbasis komponen yang digunakan untuk mengembangkan aplikasi web modern dengan cepat.
Nama Yii (diucapkan `Yee` atau` [ji:] `) berarti" sederhana dan evolusi "dalam bahasa Cina. Hal ini dapat juga
Nama Yii (diucapkan `Yee` atau `[ji:]`) yang berarti "sederhana dan berevolusi" dalam bahasa Cina. Hal ini dapat juga
dianggap sebagai singkatan **Yes It Is (Ya, Itu Dia)**!
@ -10,31 +10,24 @@ Yii Terbaik untuk Apa?
---------------------
Yii adalah kerangka kerja pemrograman web umum, yang berarti bahwa hal itu dapat digunakan untuk mengembangkan semua jenis
aplikasi Web yang menggunakan PHP. Karena arsitektur berbasis komponen dan dukungan caching yang canggih,
Yii sangat cocok untuk mengembangkan aplikasi skala besar seperti portal, forum, konten
sistem manajemen (CMS), proyek e-commerce, layanan web REST, dan sebagainya.
aplikasi Web yang menggunakan PHP. Karena arsitektur berbasis komponen dan dukungan caching yang canggih, Yii sangat cocok untuk mengembangkan aplikasi skala besar seperti portal, forum, sistem manajemen konten (CMS), proyek e-commerce, layanan web REST, dan sebagainya.
Bagaimana Yii Dibandingkan dengan Frameworks lain?
Bagaimana jika Yii Dibandingkan dengan Frameworks lain?
-------------------------------------------
Jika Anda sudah akrab dengan framework lain, Anda mungkin menghargai pengetahuan bagaimana Yii dibandingkan:
- Seperti kebanyakan PHP framework, Yii mengimplementasikan MVC (Model-View-Controller) pola arsitektur dan mempromosikan kode
  organisasi berdasarkan pola itu.
- Yii mengambil filosofi kode yang harus ditulis dengan cara sederhana namun elegan. Yii tidak akan pernah mencoba untuk
  mendesain berlebihan terutama untuk secara ketat mengikuti beberapa pola desain.
- Yii adalah framework penuh yang menyediakan banyak fitur teruji dan siap pakai seperti: query builder
  dan ActiveRecord baik untuk relasional maupun NoSQL database; dukungan pengembangan API REST; dukungan
  caching banyak lapis dan masih banyak lagi.
- Yii sangat extensible. Anda dapat menyesuaikan atau mengganti hampir setiap bagian dari kode inti Yii. Anda juga bisa
  mengambil keuntungan dari arsitektur ekstensi Yii yang padat untuk menggunakan atau mengembangkan ekstensi untuk disebarkan kembali.
- Kinerja tinggi selalu tujuan utama dari Yii.
Yii bukan one-man show, Yii didukung oleh [tim pengembang inti yang kuat][about_yii], serta komunitas besar
- Seperti kebanyakan PHP framework, Yii mengimplementasikan pola arsitektur MVC (Model-View-Controller) dan mempromosikan kode organisasi berdasarkan pola itu.
- Yii mengambil filosofi bahwa kode harus ditulis dengan cara sederhana namun elegan. Yii tidak akan pernah mencoba untuk mendesain berlebihan terutama untuk mengikuti beberapa pola desain secara ketat.
- Yii adalah framework penuh yang menyediakan banyak fitur teruji dan siap pakai seperti: query builder dan ActiveRecord baik untuk relasional maupun NoSQL database; dukungan pengembangan API REST; dukungan caching banyak lapis dan masih banyak lagi.
- Yii sangat extensible. Anda dapat menyesuaikan atau mengganti hampir setiap bagian dari kode inti Yii. Anda juga bisa mengambil keuntungan dari arsitektur ekstensi Yii yang solid untuk menggunakan atau mengembangkan ekstensi untuk disebarkan kembali.
- Kinerja tinggi selalu menjadi tujuan utama dari Yii.
Yii tidak dikerjakan oleh satu orang, Yii didukung oleh [tim pengembang inti yang kuat][about_yii], serta komunitas besar
profesional yang terus memberikan kontribusi bagi pengembangan Yii. Tim pengembang Yii
terus mengamati perkembangan tren terbaru Web, pada praktik terbaik serta fitur yang
ditemukan dalam framework dan proyek lain. Praktik terbaik yang paling relevan dan fitur yang ditemukan di tempat lain secara teratur
terus mengamati perkembangan tren terbaru Web, pada penerapan terbaik serta fitur yang
ditemukan dalam framework dan proyek lain. Penerapan terbaik yang paling relevan dan fitur yang ditemukan di tempat lain secara teratur
dimasukkan ke dalam kerangka inti dan menampakkannya melalui antarmuka yang sederhana dan elegan.
[about_yii]: http://www.yiiframework.com/about/
@ -42,8 +35,8 @@ dimasukkan ke dalam kerangka inti dan menampakkannya melalui antarmuka yang sede
Versi Yii
----------
Yii saat ini memiliki dua versi utama yang tersedia: 1.1 dan 2.0. Versi 1.1 adalah generasi tua dan sekarang dalam modus pemeliharaan.
Versi 2.0 adalah penulisan ulang lengkap dari Yii, mengadopsi teknologi dan protokol terbaru, termasuk composer, PSR, namespace, trait, dan sebagainya.
Yii saat ini memiliki dua versi utama yang tersedia: 1.1 dan 2.0. Versi 1.1 adalah generasi lama dan sekarang dalam mode pemeliharaan.
Versi 2.0 adalah penulisan ulang lengkap dari Yii, mengadopsi teknologi dan protokol terbaru, termasuk composer, PSR, namespace, trait, dan sebagainya.
Versi 2.0 merupakan generasi framework yang sekarang dan terus menerima upaya pengembangan selama beberapa tahun ke depan.
Panduan ini terutama tentang versi 2.0.
@ -51,9 +44,9 @@ Panduan ini terutama tentang versi 2.0.
Persyaratan dan Prasyarat
--------------------------
Yii 2.0 memerlukan PHP 5.4.0 atau lebih. Anda dapat menemukan persyaratan yang lebih rinci untuk setiap fitur
Yii 2.0 memerlukan PHP 5.4.0 atau versi lebih tinggi. Anda dapat menemukan persyaratan yang lebih rinci untuk setiap fitur
dengan menjalankan pengecek persyaratan yang diikutsertakan dalam setiap rilis Yii.
Menggunakan Yii memerlukan pengetahuan dasar tentang pemrograman berorientasi objek (OOP), mengingat Yii adalah framework berbasis OOP murni.
Yii 2.0 juga memanfaatkan fitur terbaru dari PHP, seperti [namespace](http://www.php.net/manual/en/language.namespaces.php) dan [traits](http://www.php.net/manual/en/language.oop5.traits.php).
Memahami konsep-konsep ini akan membantu Anda lebih mudah memahami Yii 2.0.
Memahami konsep-konsep ini akan membantu Anda lebih mudah memahami Yii 2.0.

5
docs/guide-ja/db-active-record.md

@ -527,6 +527,11 @@ Customer::deleteAll(['status' => Customer::STATUS_INACTIVE]);
> - [[yii\db\ActiveRecord::updateCounters()]]
> - [[yii\db\ActiveRecord::updateAllCounters()]]
### データをリフレッシュする際のライフサイクル <span id="refreshing-data-life-cycle"></span>
[[yii\db\ActiveRecord::refresh()|refresh()]] を呼んでアクティブレコードインスタンスをリフレッシュする際は、リフレッシュが成功してメソッドが `true` を返すと
[[yii\db\ActiveRecord::EVENT_AFTER_REFRESH|EVENT_AFTER_REFRESH]] イベントがトリガされます。
## トランザクションを扱う <span id="transactional-operations"></span>

2
docs/guide-ja/db-migrations.md

@ -407,7 +407,7 @@ class m160328_040430_create_post extends Migration
パラメータが渡されなかった場合は、テーブル名はカラム名から推測されます。
上記の例で `author_id:integer:notNull:foreignKey(user)` は、`user` テーブルへの外部キーを持つ `author_id` という名前のカラムを生成します。
一方、`category_id:integer:default(1):foreignKey` は、`category` テーブルへの外部キーを持つ `category_id` というカラムを生成します。
一方、`category_id:integer:defaultValue(1):foreignKey` は、`category` テーブルへの外部キーを持つ `category_id` というカラムを生成します。
### テーブルを削除する

BIN
docs/guide-ja/images/tutorial-console-help.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 138 KiB

2
docs/guide-ja/rest-authentication.md

@ -110,5 +110,5 @@ class User extends ActiveRecord implements IdentityInterface
ユーザが認証された後、おそらくは、リクエストされたリソースに対してリクエストされたアクションを実行する許可を彼または彼女が持っているかどうかをチェックしたいでしょう。
*権限付与* と呼ばれるこのプロセスについては、[権限付与](security-authorization.md) のセクションで詳細に説明されています。
あなたのコントローラが [[yii\rest\ActiveController]] から拡張したものである場合は、[[yii\rest\Controller::checkAccess()|checkAccess()]] メソッドをオーバーライドして権限付与のチェックを実行することが出来ます。
あなたのコントローラが [[yii\rest\ActiveController]] から拡張したものである場合は、[[yii\rest\ActiveController::checkAccess()|checkAccess()]] メソッドをオーバーライドして権限付与のチェックを実行することが出来ます。
このメソッドが [[yii\rest\ActiveController]] によって提供されている内蔵のアクションから呼び出されます。

2
docs/guide-ja/runtime-routing.md

@ -109,6 +109,8 @@ $url = Url::to(['post/view', 'id' => 100]);
`catchAll` プロパティは配列を取り、最初の要素はルートを指定し、残りの要素 (「名前-値」のペア) は [アクションのパラメータ](structure-controllers.md#action-parameters) を指定するものでなければなりません。
> Info: このプロパティを有効にすると、開発環境でデバッグパネルが動作しなくなります。
## URL を生成する <span id="creating-urls"></span>

3
docs/guide-ja/security-authorization.md

@ -305,6 +305,9 @@ class RbacController extends Controller
}
```
> Note: アドバンストテンプレートを使おうとするときは、`RbacController` を `console/controllers`
ディレクトリの中に置いて、名前空間を `console/controllers` に変更する必要があります。
`yii rbac/init` によってコマンドを実行した後には、次の権限階層が得られます。
![単純な RBAC 階層](images/rbac-hierarchy-1.png "単純な RBAC 階層")

2
docs/guide-ja/structure-applications.md

@ -182,6 +182,8 @@ if (YII_ENV_DEV) {
]
```
> Info: このプロパティを有効にすると、開発環境でデバッグパネルが動作しなくなります。
#### [[yii\base\Application::components|components]] <span id="components"></span>

2
docs/guide-ja/tutorial-console.md

@ -160,7 +160,7 @@ class HelloController extends Controller
これで、次の構文を使ってコマンドを走らせることが出来るようになります。
```
./yii hello -m=hola
./yii hello -m=hello
```
### 引数

BIN
docs/guide-ru/images/tutorial-console-help.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 138 KiB

4
docs/guide-ru/structure-modules.md

@ -272,6 +272,6 @@ class Module extends \yii\base\Module
которых функции тесно связаны между собой. Каждая группа функций может разрабатываться в виде модуля, над которым работает
один разработчик или одна команда.
Модули - это хороший способ повторно использовать код на уровне групп функций. В виде модулей можно реализовать такая
функциональность как управление пользователями или управление комментариями, а затем использовать эти модули в будущих
Модули - это хороший способ повторно использовать код на уровне групп функций. В виде модулей можно реализовать такую
функциональность, как управление пользователями или управление комментариями, а затем использовать эти модули в будущих
разработках.

4
docs/guide-ru/tutorial-mailing.md

@ -166,7 +166,7 @@ use yii\helpers\Html;
Прикрепление файлов
---------------
Вы можете прикрепить вложения в сообщению с помощью методов `attach()` и `attachContent()`:
Вы можете прикрепить вложения к сообщению с помощью методов `attach()` и `attachContent()`:
```php
$message = Yii::$app->mailer->compose();
@ -183,7 +183,7 @@ $message->attachContent('Attachment content', ['fileName' => 'attach.txt', 'cont
----------------
Вы можете вставить изображения в содержание сообщения через `embed()` метод. Этот метод возвращает id прикрепленной картинки,
которые должны быть дыть доступны в 'img' тегах.
которые должны быть доступны в 'img' тегах.
Этот метод легко использовать, когда сообщение составляется через файлы представления:
```php

BIN
docs/guide-uk/images/tutorial-console-help.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 138 KiB

5
docs/guide/db-active-record.md

@ -570,6 +570,11 @@ life cycle will happen:
> - [[yii\db\ActiveRecord::updateCounters()]]
> - [[yii\db\ActiveRecord::updateAllCounters()]]
### Refreshing Data Life Cycle <span id="refreshing-data-life-cycle"></span>
When calling [[yii\db\ActiveRecord::refresh()|refresh()]] to refresh an Active Record instance, the
[[yii\db\ActiveRecord::EVENT_AFTER_REFRESH|EVENT_AFTER_REFRESH]] event is triggered if refresh is successful and the method returns `true`.
## Working with Transactions <span id="transactional-operations"></span>

8
docs/guide/db-migrations.md

@ -417,8 +417,8 @@ is passed then the table name will be deduced from the column name.
In the
example above `author_id:integer:notNull:foreignKey(user)` will generate a
column named `author_id` with a foreign key to the `user` table while
`category_id:integer:default(1):foreignKey` will generate a column `category_id`
with a foreign key to the `category` table.
`category_id:integer:defaultValue(1):foreignKey` will generate a column
`category_id` with a foreign key to the `category` table.
### Drop Table
@ -685,6 +685,10 @@ Below is the list of all these database accessing methods:
* [[yii\db\Migration::dropForeignKey()|dropForeignKey()]]: removing a foreign key
* [[yii\db\Migration::createIndex()|createIndex()]]: creating an index
* [[yii\db\Migration::dropIndex()|dropIndex()]]: removing an index
* [[yii\db\Migration::addCommentOnColumn()|addCommentOnColumn()]: adding comment to column
* [[yii\db\Migration::dropCommentFromColumn()|dropCommentFromColumn()]: dropping comment from column
* [[yii\db\Migration::addCommentOnTable()|addCommentOnTable()]: adding comment to table
* [[yii\db\Migration::dropCommentFromTable()|dropCommentFromTable()]: dropping comment from table
> Info: [[yii\db\Migration]] does not provide a database query method. This is because you normally do not need
to display extra message about retrieving data from a database. It is also because you can use the powerful

BIN
docs/guide/images/tutorial-console-help.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 138 KiB

4
docs/guide/input-forms.md

@ -78,6 +78,10 @@ The name of the input field is determined automatically from the model's [[yii\b
For example, the name for the input field for the `username` attribute in the above example will be `LoginForm[username]`. This naming rule will result in an array
of all attributes for the login form to be available in `$_POST['LoginForm']` on the server side.
> Tip: If you have only one model in a form and want to simplify the input names you may skip the array part by
> overriding the [[yii\base\Model::formName()|formName()]] method of the model to return an empty string.
> This can be useful for filter models used in the [GridView](output-data-widgets.md#grid-view) to create nicer URLs.
Specifying the attribute of the model can be done in more sophisticated ways. For example when an attribute may
take an array value when uploading multiple files or selecting multiple items you may specify it by appending `[]`
to the attribute name:

2
docs/guide/rest-authentication.md

@ -123,5 +123,5 @@ action for the requested resource. This process is called *authorization* which
the [Authorization section](security-authorization.md).
If your controllers extend from [[yii\rest\ActiveController]], you may override
the [[yii\rest\Controller::checkAccess()|checkAccess()]] method to perform authorization check. The method
the [[yii\rest\ActiveController::checkAccess()|checkAccess()]] method to perform authorization check. The method
will be called by the built-in actions provided by [[yii\rest\ActiveController]].

2
docs/guide/runtime-routing.md

@ -124,6 +124,8 @@ With the above configuration, the `site/offline` action will be used to handle a
The `catchAll` property should take an array whose first element specifies a route, and
the rest of the elements (name-value pairs) specify the parameters to be [bound to the action](structure-controllers.md#action-parameters).
> Info: Debug panel on development environment will not work when this property is enabled
## Creating URLs <span id="creating-urls"></span>

1
docs/guide/structure-applications.md

@ -192,6 +192,7 @@ The rest of the array elements (key-value pairs) specify the parameters to be bo
]
```
> Info: Debug panel on development environment will not work when this property is enabled
#### [[yii\base\Application::components|components]] <span id="components"></span>

2
docs/guide/tutorial-console.md

@ -165,7 +165,7 @@ class HelloController extends Controller
Now, you can use the following syntax to run the command:
```
./yii hello -m=hola
./yii hello -m=hello
```
### Arguments

8
docs/internals-ja/core-code-style.md

@ -126,7 +126,7 @@ class Foo
- クラスのメソッドは常に修飾子 `private`、`protected` または `public` を使って、可視性を宣言すべきです。`var` は許可されません。
- 関数の開始の中括弧は関数宣言の次の行に置くべきです。
```
```php
/**
* ドキュメント
*/
@ -273,9 +273,9 @@ if (!$model && null === $event)
```php
$result = $this->getResult();
if (empty($result)) {
return true;
return true;
} else {
// $result を処理
// $result を処理
}
```
@ -284,7 +284,7 @@ if (empty($result)) {
```php
$result = $this->getResult();
if (empty($result)) {
return true;
return true;
}
// $result を処理

477
docs/internals-ru/core-code-style.md

@ -0,0 +1,477 @@
Стиль кодирования Yii2 framework
==============================
Описанный ниже стиль кодирования используется при разработке ядра Yii 2.x и его официальных расширений. Если вы хотите участвовать в разработке фреймворка, постарайтесь придерживаться данного стиля. Мы не принуждаем вас использовать этот стиль при разработке ваших приложений с использованием Yii 2. В данном случае можете использовать тот стиль, который вам больше подходит.
Пример конфигурационного файла для CodeSniffer вы можете найти здесь: https://github.com/yiisoft/yii2-coding-standards
1. Обзор
-----------
В общем, мы используем совместимый со стандартом [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) стиль, так что все положения [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) вполне применимы к нашему стилю кодирования.
- ДОЛЖНЫ использоваться только открывающие теги `<?php` или `<?=`;
- В конце файла должна быть пустая строка;
- Файлы PHP кодом ДОЛЖНЫ содержать только символы в кодировке UTF-8 без BOM;
- Для выравнивания кода НУЖНО использовать 4 пробела вместо табуляции;
- Имена классов ДОЛЖНЫ быть объявлены используя `StudlyCaps`;
- Константы класса ДОЛЖНЫ быть объявлены в верхнем регистре с подчеркиванием в качестве разделителей;
- Имена методов ДОЛЖНЫ быть объявлены используя `camelCase`;
- Имена свойств ДОЛЖНЫ быть объявлены используя `camelCase`;
- Имена свойств ДОЛЖНЫ начинаться с подчеркивания если они объявлены с использованием модификатора `private`;
- Всегда используйте `elseif` вместо `else if`.
2. Файлы
--------
### 2.1. Теги PHP
- В PHP коде ДОЛЖНЫ использоваться теги `<?php ?>` или `<?=`; НЕ ДОЛЖНЫ использоваться другие теги, такие как `<?`;
- Если файл содержит только PHP код, тогда закрывающий тег `?>` не нужен;
- Не добавляйте лишние пробелы в конец строки;
- Все файлы, содержащие PHP код, должны иметь расширение `.php`.
### 2.2. Кодировка символов
PHP код должен содержать только символы в кодировке UTF-8 без BOM.
3. Имена Классов
--------------
Имена классов ДОЛЖНЫ быть определены используя `StudlyCaps`. Например, `Controller`, `Model`.
4. Классы
----------
В данном случае, под классом подразумеваются все классы и интерфейсы.
- При именовании классов следует использовать `CamelCase`;
- Открывающая фигурная скобка всегда должна быть на следующей строке после имена класса;
- Все классы должны быть документированы в соответствии с PHPDoc;
- Весь код класса должен быть выровнен с использованием 4 пробелов;
- В одном PHP файле должен быть только один класс;
- Все классы должны использовать пространства имен;
- Имя класса должно совпадать с именем файла. Пространство имен класса должно соответствовать структуре каталогов.
```php
/**
* Документация
*/
class MyClass extends \yii\Object implements MyInterface
{
// код
}
```
### 4.1. Константы
Константы класса ДОЛЖНЫ быть объявлены в верхнем регистре с подчеркиванием в качестве разделителей.
Пример:
```php
<?php
class Foo
{
const VERSION = '1.0';
const DATE_APPROVED = '2012-06-01';
}
```
### 4.2. Свойства
- При объявлении общедоступных (public) членов класса нужно использовать ключевое слово `public`;
- Общедоступные и защищенные (protected) переменные должны быть объявлены в начале класса, раньше объявления методов;
Закрытые (private) переменные, так же, должны быть объявлены в начале класса, но могут быть добавлены и непосредственно перед методами, использующими их, в случае, если эти переменные используются только небольшой частью методов класса;
- Порядок объявления свойств класса должен соответствовать их видимости: общедоступные, защищенные и закрытые;
- Ограничений на порядок свойств одной области видимости нет;
- Для улучшения читаемости, следует не оставлять пустых строк между объявлением свойств и оставлять две пустые строки между объявлениями переменных и методов. Между объявлениями свойств разной области видимости нужно должна быть добавлена одна пустая строка;
- Закрытые переменные должны иметь имя вида `$_varName`;
- Общедоступные члены класса и отдельные переменные должны использовать `$camelCase`
с первой буквой в нижнем регистре;
- Используйте понятные имена. Переменные вроде `$i` и `$j` лучше не использовать.
Пример:
```php
<?php
class Foo
{
public $publicProp1;
public $publicProp2;
protected $protectedProp;
private $_privateProp;
public function someMethod()
{
// ...
}
}
```
### 4.3. Методы
- При именовании методов и функций следует использовать `camelCase` с первой буквой в нижнем регистре;
- Имена должны быть понятными и отражать назначение функции;
- Методы классов должны быть объявлены с использованием одного из модификаторов `private`, `protected` или
`public`. Использование `var` недопустимо;
- Открывающая фигурная кавычка должна быть расположена на строке, следующей за строкой объявления функции.
```php
/**
* Документация
*/
class Foo
{
/**
* Документация
*/
public function bar()
{
// код
return $value;
}
}
```
### 4.4 Блоки Документации
`@param`, `@var`, `@property` и `@return` должны описывать типы `boolean`, `integer`, `string`, `array` или `null`. Вы можете использовать и имена классов, например `Model` или `ActiveRecord`. Для типизированных массивов используйте `ClassName[]`.
### 4.5 Конструкторы
- Используйте `__construct` вместо старого стиля, применяемого в PHP 4.
## 5 PHP
### 5.1 Типы Данных
- Все типы данных и значения PHP должны быть в нижнем регистре. Это относится и к `true`, `false`, `null` и `array`.
Изменение типа существующей переменной считается плохой практикой. Постарайтесь не использовать такой подход, за исключением случаев, когда это действительно необходимо.
```php
public function save(Transaction $transaction, $argument2 = 100)
{
$transaction = new Connection; // плохо
$argument2 = 200; // хорошо
}
```
### 5.2 Строки
- Если строка не содержит переменных или одинарных кавычек, используйте одинарные кавычки;
```php
$str = 'Например так.';
```
- Если строка содержит одинарную кавычку, используйте двойные кавычки во избежание излишнего экранирования.
#### Подстановка переменных
```php
$str1 = "Привет, $username!";
$str2 = "Привет, {$username}!";
```
Следующий вариант запрещен:
```php
$str3 = "Привет, ${username}!";
```
#### Конкатенация
При конкатенации строк выделяйте точку пробелами:
```php
$name = 'Yii' . ' Framework';
```
Для длинных строк используйте следующий подход:
```php
$sql = "SELECT *"
. "FROM `post` "
. "WHERE `id` = 121 ";
```
### 5.3 Массивы
Для массивов мы используем краткий синтаксис, появившийся в PHP 5.4.
#### Индексированные массивы
- Не используйте отрицательные числа в качестве индексов массива.
Используйте следующий стиль:
```php
$arr = [3, 14, 15, 'Yii', 'Framework'];
```
При большом количестве элементов:
```php
$arr = [
3, 14, 15,
92, 6, $test,
'Yii', 'Framework',
];
```
#### Ассоциативные массивы
Для ассоциативных массивов используйте следующий стиль:
```php
$config = [
'name' => 'Yii',
'options' => ['usePHP' => true],
];
```
### 5.4 Управляющие конструкции
- Оставляйте один пробел перед открывающей круглой скобкой и после закрывающей круглой скобки в управляющих конструкциях;
- Операторы внутри круглых скобок должны разделяться пробелами;
- Открывающая фигурная скобка должна быть на той же строке;
- Закрывающая фигурная скобка должна быть на новой строке;
- Всегда используйте фигурные скобки для однострочных выражений.
```php
if ($event === null) {
return new Event();
}
if ($event instanceof CoolEvent) {
return $event->instance();
}
return null;
// такой код недопустим:
if (!$model && null === $event)
throw new Exception('test');
```
Старайтесь избегать использования `else` после `return` там, где это возможно.
Используйте [граничные операторы](https://refactoring.guru/ru/replace-nested-conditional-with-guard-clauses).
```php
$result = $this->getResult();
if (empty($result)) {
return true;
} else {
// дальнейшие вычисления
}
```
лучше переписать так:
```php
$result = $this->getResult();
if (empty($result)) {
return true;
}
// дальнейшие вычисления
```
#### switch
Используйте следующий стиль для switch:
```php
switch ($this->phpType) {
case 'string':
$a = (string) $value;
break;
case 'integer':
case 'int':
$a = (int) $value;
break;
case 'boolean':
$a = (bool) $value;
break;
default:
$a = null;
}
```
### 5.5 Вызовы функций
```php
doIt(2, 3);
doIt(['a' => 'b']);
doIt('a', [
'a' => 'b',
'c' => 'd',
]);
```
### 5.6 Анонимные (lambda) функции
Не забывайте про пробелы между ключевыми словами `function`/`use` и открывающими круглыми скобками:
```php
// правильно
$n = 100;
$sum = array_reduce($numbers, function ($r, $x) use ($n) {
$this->doMagic();
$r += $x * $n;
return $r;
});
// неправильно
$n = 100;
$mul = array_reduce($numbers, function($r, $x) use($n) {
$this->doMagic();
$r *= $x * $n;
return $r;
});
```
Документация
-------------
- Для получения информации по синтаксису документации обратитесь к первоисточнику [PHPDoc](http://phpdoc.org/);
- Код без документации недопустим;
- Все файлы классов должны содержать блок документации в начале файла и блок документации непосредственно перед каждым классом;
- Нет необходимости использовать тег `@return` если метод не возвращает значение;
- Все виртуальные свойства классов, наследованных от `yii\base\Object`, документируются тегом `@property` в блоке документации класса;
Аннотации геттеров и сеттеров автоматически генерируются из соответствующих тегов `@return` or `@param`
посредством выполнения команды `./build php-doc` в соответствующем каталоге;
Вы можете добавить дополнительный тег `@property` для геттера или сеттера для пояснения назначения переменной метода, если это необходимо.
Например:
```php
<?php
/**
* Returns the errors for all attribute or a single attribute.
* @param string $attribute attribute name. Use null to retrieve errors for all attributes.
* @property array An array of errors for all attributes. Empty array is returned if no error.
* The result is a two-dimensional array. See [[getErrors()]] for detailed description.
* @return array errors for all attributes or the specified attribute. Empty array is returned if no error.
* Note that when returning errors for all attributes, the result is a two-dimensional array, like the following:
* ...
*/
public function getErrors($attribute = null)
```
#### Файл
```php
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
```
#### Класс
```php
/**
* Component is the base class that provides the *property*, *event* and *behavior* features.
*
* @include @yii/docs/base-Component.md
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Component extends \yii\base\Object
```
#### Функция / метод
```php
/**
* Returns the list of attached event handlers for an event.
* You may manipulate the returned [[Vector]] object by adding or removing handlers.
* For example,
*
* ```
* $component->getEventHandlers($eventName)->insertAt(0, $eventHandler);
* ```
*
* @param string $name the event name
* @return Vector list of attached event handlers for the event
* @throws Exception if the event is not defined
*/
public function getEventHandlers($name)
{
if (!isset($this->_e[$name])) {
$this->_e[$name] = new Vector;
}
$this->ensureBehaviors();
return $this->_e[$name];
}
```
#### Разметка
Как вы можете видеть в примерах выше, мы используем специальную разметку для форматирования комментариев PHPDoc.
Ниже описан дополнительный синтаксис для описания связей между классами, методами и свойствами в документации:
- `'[[canSetProperty]] ` создаст ссылку на метод или свойство `canSetProperty` этого класса;
- `'[[Component::canSetProperty]]` создаст ссылку на метод `canSetProperty` класса `Component` того же пространства имен;
- `'[[yii\base\Component::canSetProperty]]` создаст ссылку на метод `canSetProperty` класса `Component` в пространстве имен `yii\base`;
- `'[[Component]]` создаст ссылку на класс `Component` в том же пространстве имен. Здесь так же возможно явное указание пространства имен.
Для явного указания текста ссылки возможно использование следующего синтаксиса:
```
... as displayed in the [[header|header cell]].
```
Часть до | это имя свойства, метода или класса для ссылки, а часто поле | это текст ссылки.
Так же, возможно создание ссылок на Руководство:
```markdown
[Руководство](guide:file-name.md)
[Раздел руководства](guide:file-name.md#subsection)
```
#### Комментарии
- Однострочные комментарии должны начинаться с `//`, а не с `#`;
- Однострочные комментарии должны располагаться на отдельной строке.
Дополнительные правила
----------------
### `=== []` или `empty()`
Используйте `empty()`, где это возможно.
### Несколько точек возврата
Не допускайте запутанных вложенных условных конструкций, используйте return. Для коротких методов это не актуально.
### `self` или `static`
Всегда используйте `static`, за исключением следующих случаев:
- доступ к константам ДОЛЖЕН осуществляться через `self`: `self::MY_CONSTANT`;
- доступ к защищенным статическим свойствам ДОЛЖЕН осуществляться через `self`: `self::$_events`;
- допустимо использовать `self` для рекурсивного обращения к текущему классу, вместо класса наследника.
### Значение "ничего не делать"
Свойства указывающее компоненту на отсутствие необходимости что-либо делать, должны принимать значение `false`. `null`, `''`, or `[]` не должны использоваться в таких случаях.
### Каталоги/пространства имен
- Используйте нижний регистр;
- используйте множественную форму для существительных, представляющих объекты (например валидаторы);
- используйте единичную форму для имен, представляющих соответствующий функционал (например web).

8
docs/internals/core-code-style.md

@ -125,7 +125,7 @@ class Foo
`public` modifiers. `var` is not allowed.
- Opening brace of a function should be on the line after the function declaration.
```
```php
/**
* Documentation
*/
@ -270,9 +270,9 @@ Use [guard conditions](http://refactoring.com/catalog/replaceNestedConditionalWi
```php
$result = $this->getResult();
if (empty($result)) {
return true;
return true;
} else {
// process result
// process result
}
```
@ -281,7 +281,7 @@ is better as
```php
$result = $this->getResult();
if (empty($result)) {
return true;
return true;
}
// process result

2
docs/internals/git-workflow.md

@ -130,7 +130,7 @@ Failing unit tests as issue description are also accepted.
### 5. Update the CHANGELOG
Edit the CHANGELOG file to include your change, you should insert this at the top of the file under the
"Work in progress" heading, the line in the change log should look like one of the following:
first heading (the version that is currently under development), the line in the change log should look like one of the following:
```
Bug #999: a description of the bug fix (Your Name)

4
framework/BaseYii.php

@ -93,7 +93,7 @@ class BaseYii
*/
public static function getVersion()
{
return '2.0.8-dev';
return '2.0.9-dev';
}
/**
@ -343,7 +343,7 @@ class BaseYii
unset($type['class']);
return static::$container->get($class, $params, $type);
} elseif (is_callable($type, true)) {
return call_user_func($type, $params);
return static::$container->invoke($type, $params);
} elseif (is_array($type)) {
throw new InvalidConfigException('Object configuration must be an array containing a "class" element.');
} else {

65
framework/CHANGELOG.md

@ -7,79 +7,112 @@ Yii Framework 2 Change Log
- Removed methods marked as deprected in 2.0.x (samdark)
2.0.8 under development
2.0.9 under development
-----------------------
- Bug #9935: Fixed `yii\validators\EachValidator` does not invoke `validateAttribute()` method of the embedded validator (klimov-paul)
- Bug #11270: Fixed `BaseActiveRecord::link()` method in order to support closure in `indexBy` for relations declaration (iushev)
- Bug #11333: Avoid serializing PHP 7 errors (zuozp8)
- Bug #11262: Enabled use of yii2 inside of PHAR packaged console applications (hiqsol)
- Bug #11196: Fixed VarDumper throws PHP Fatal when dumping `__PHP_Incomplete_Class` (DamianZ)
- Enh #11414: Files specified as `null` in `yii\web\AssetBundle` won't be registered (Razzwan)
2.0.8 April 28, 2016
--------------------
- Bug #7627: Fixed `yii\widgets\ActiveField` to handle inputs AJAX validation with changed ID properly (dizeee)
- Bug #7717: Fixed the bug that `$properties` parameter in `ArrayHelper::toArray()` was not passed to recursive calls (quantum13)
- Bug #9074: Fixed JS call `$('#grid').yiiGridView('getSelectedRows')` when `GridView::$showHeader` is set to false (NekitoSP, silverfire)
- Bug #9851: Fixed partial commit / rollback in nested transactions (sammousa)
- Bug #9935: Fixed `yii\validators\EachValidator` does not invoke `validateAttribute()` method of the embedded validator (klimov-paul)
- Bug #10201: Fixed bug in ActiveRecord, where relational data could not be fetched in case with composite key and join with IN condition (PaulVanSchayck, airmoi, joe-meyer, cebe)
- Bug #10235: Fixed `yii\console\Application::runAction` to not to corrupt response object (hiqsol)
- Bug #10480: Fixed removing old identity cookie when loggin in as another user without logging out first (maine-mike)
- Bug #10617: Fixed `yii\web\Request::getBodyParams()` returned `null` instead of empty array if request body is empty and content type is application/json (samdark)
- Bug #10784: Fixed `yii\grid\CheckboxColumn` to set correct value when `yii\grid\CheckboxColumn::$checkboxOptions` closure is used (nukkumatti)
- Bug #10850: Fixed unable to use 'definitions' and 'aliases' at `yii\widgets\MaskedInput` (rahimov, klimov-paul)
- Bug #10884: Fixed MessageFormatter for formatting messages when not all parameters are given (laxity7, cebe)
- Enh #10910: Fixed Captcha client side validation after image refresh, when controller is under module (silverfire)
- Bug #10935: Fixed cache key collision in `yii\web\UrlManager::createUrl()` (sammousa)
- Bug #10946: Fixed parameters binding to the SQL query in `yii\db\mysqlSchema::findConstraints()` (silverfire)
- Bug #10969: Fixed generator migration tool with decimal params in column (pana1990)
- Bug #10974: `yii.js` - fixed error in ajaxPrefilter event handler, caused by blocked frame (maximal)
- Bug #11012: Fixed `yii\web\UploadedFile::getBaseName()` to work with UTF-8 file names (hiscaler, silverfire)
- Bug #11026: Fixed `StringHelper::truncateWords()` to count words properly for non-English text (samdark, tol17)
- Bug #11038: Fixed handling of intervals of 0 seconds in `yii\i18n\Formatter::asDuration()` (VirtualRJ)
- Bug #11040: Check parameter 'recursive' and disable recursive copying with option 'recursive' => false in method BaseFileHelper::copyDirectory (Ni-san)
- Bug #11052: Fixed `HtmlPurifier` configuration sequence (samdark)
- Bug #11066: `yii.js` - fixed `getQueryParams()` function to handle URLs with anchors correctly (DrDeath72)
- Bug #11088: Fixed bug with column name not being quoted correctly, when a quoted table name or a table name in prefix syntax was used (cebe, edgardmessias, Ni-san)
- Bug #11093: Fixed `yii\db\QueryBuilder::buildAndCondition()` to add query params passed directly by `yii\db\Expression` (CedricYii, silverfire)
- Bug #11012: Fixed `yii\web\UploadedFile::getBaseName()` to work with UTF-8 file names (hiscaler, silverfire)
- Bug #11026: Fixed `StringHelper::truncateWords()` to count words properly for non-English text (samdark, tol17)
- Bug #11040: Check parameter 'recursive' and disable recursive copying with option 'recursive' => false in method BaseFileHelper::copyDirectory (Ni-san)
- Bug #11125: Fixed `JSON_ERROR_SYNTAX` for `json_decode(null)` in PHP 7 (fps01)
- Bug #11132: Fixed `yii\widgets\FragmentCache` not handling empty content correctly in all cases (kidol)
- Bug #11188: Fixed wrong index usage in `CaptchaAction` when calling `imagefilledrectangle` (alsopub)
- Bug #11196: Fixed VarDumper throws PHP Fatal when dumping `__PHP_Incomplete_Class` (DamianZ)
- Bug #11220: NumberValidator now handles objects properly (samdark)
- Bug #11221: Boolean validator generates incorrect error message (azaikin, githubjeka)
- Bug #11223: Fixed returning an empty array when DbManager::getRolesByUser() was called on a user with user id 0 (VirtualRJ)
- Bug #11228: `yii.activeForm.js` - AJAX validation will not be triggered if client side validation failed (silverfire)
- Bug #11262: Enabled use of yii2 inside of PHAR packaged console applications (hiqsol)
- Bug #11270: Fixed `BaseActiveRecord::link()` method in order to support closure in `indexBy` for relations declaration (iushev)
- Bug #11280: Descendants of `yii\console\controllers\BaseMigrateController`, like the one for MongoDB, unable to create new migration (klimov-paul)
- Bug #11333: Avoid serializing PHP 7 errors (zuozp8)
- Bug #11425: Fixed namespace conflict with Markdown helper and Console helper (cebe, mdmunir)
- Bug: SQlite querybuilder did not create primary key with bigint for `TYPE_BIGPK` (cebe)
- Enh #5469: Add mimetype validation by mask in FileValidator (kirsenn, samdark, silverfire)
- Enh #7177, #10165: Added support for validating datetime and time values using intl short format to `DateValidator` (VirtualRJ, cebe)
- Enh #8145, #8139, #10234 #11153: `yii\validators\Validator::$attributes` property now supports `!attribute` notation to validate attribute, but do not mark it as safe (mdmunir)
- Enh #8148: Implemented ability to add comment on table and column in migration (vaseninm, silverfire)
- Enh #8505: `yii\db\Query` now contains a andFilterCompare() method that allows filtering using operators in the query value (lennartvdd)
- Enh #8602: `yii\validators\DateValidator` skip validation for `timestampAttribute`, if it is already in correct format (klimov-paul)
- Enh #8639: Improve ActiveRecord to not create new instances of classes when objects are available (cebe)
- Enh #8779: Automatically set enctype form option when using file input field (pana1990, arogachev)
- Enh #9340: Adds `after()` and `first()` column schema builder modifiers (df2)
- Enh #9425: `yii\db\Query::exists()` now uses SQL standard `EXISTS()` query via new `yii\db\QueryBuilder::selectExists()` method to improving performance in some cases (PowerGamer1)
- Enh #9562: Adds `char` datatype to framework (df2)
- Enh #9604: `yii\db\BaseActiveRecord` now triggers event `EVENT_AFTER_REFRESH` after a record is refreshed (raoul2000)
- Enh #9893: `yii.js` handleAction enhanced to support for data-form attribute, so links can trigger specific forms (SamMousa)
- Enh #10309: Extracted `yii\web\UrlManager` rule cache key into `$cacheKey` protected property (lordthorzonus)
- Enh #10322: ActiveForm now respects formtarget attribute of submit button (AnatolyRugalev)
- Enh #10451: Check of existence of `$_SERVER` in `\yii\web\Request` before using it (quantum13)
- Enh #10475: Extracted `getUrlFromCache()` and `setRuleToCache()` protected methods from `yii\web\UrlManager::createUrl()` (dmdark)
- Enh #10487: `yii\helpers\BaseArrayHelper::index()` got a third parameter `$groupBy` to group the input array by the key in one or more dimensions (quantum13, silverfire, samdark)
- Enh #10610: Added `BaseUrl::$urlManager` to be able to set URL manager used for creating URLs (samdark)
- Enh #10631: Splitted gettng label and rendering cell in `yii\grid\DataColumn::renderHeaderCellContent()` to make code simpler (t-kanstantsin, samdark)
- Enh #10710: `yii\helpers\FileHelper::copyDirectory()` is now throwing exception when trying to copy a directory to itself or a subdirectory (wallysalami, cebe, samdark)
- Enh #10764: `yii\helpers\Html::tag()` and `::beginTag()` return content without any HTML when the `$tag` attribute is `false` or `null` (pana1990)
- Enh #10840: Added `yii\console\Controller::optionAliases()` method to support aliases for commands (pana1990)
- Enh #10889: Allows unsigned primary key column definitions (df2)
- Enh #10908: Added Dependency Injection for Closure configuration (SamMousa)
- Enh #10910: Fixed Captcha client side validation after image refresh, when controller is under module (silverfire)
- Enh #10921: `__toString()` of column schema builder now adapts to column types (df2)
- Enh #10931: Removed hard dependency of `yii\di\Container` on `Yii::$app` (SamMousa)
- Enh #10937: `yii\web\User` will now confirm the request accepts an HTML response before redirecting to the login page. Added optional `$checkAcceptHeader` to `yii\web\User::loginRequired()` (sammousa)
- Enh #10941: Added `yii\helpers\ArrayHelper::isTraversable`, added support for traversable selections for dropdownList, radioList and checkboxList in `yii\helpers\Html` (sammousa)
- Enh #10941: Added `yii\helpers\ArrayHelper::isTraversable`, added support for traversable selections for dropdownList, radioList and checkboxList in `yii\helpers\Html`.
- Enh #10954: `yii\db\QueryBuilder` now accepts `\Traversable` objects for `in` condition (SamMousa, silverfire)
- Enh #10967: Simplified Javascript on the exception debug page (SamMousa)
- Enh #10976: `Inflector::transliterate()` now uses `strtr` instead of `str_replace` (DrDeath72)
- Enh #11002: `AttributeBehavior::$skipUpdateOnClean` which determines whether to skip a behavior when the behavior owner has not been modified (Faryshta)
- Enh #11056: Allow setting a custom logger configuration for `yii\log\Dispatcher` in configuration (bionoren, cebe)
- Enh #11058: `yii\web\User::loginRequired()` now does not set return URL when request method is not GET (dawei101, silverfire)
- Enh #11110: Added migrations for DB session (mdmunir)
- Enh #11137: Added weak ETag support to `yii\filters\HttpCache`. It could be turned on via setting `$weakEtag` to `true` (particleflux)
- Enh #11139: `yii\validators\EachValidator` injects specific attribute value in error message parameters (silverfire)
- Enh #11166: migrate command new option `useTablePrefix` (Faryshta)
- Enh #11187: migrate command now generates phpdoc for table migrations (Faryshta)
- Enh #11207: migrate command can create foreign keys. (Faryshta)
- Enh #11254: Added ability to attach RBAC rule using class name (mdmunir)
- Enh #11285: `yii\base\Security` enhancements (tom--, samdark)
- Avoid reading more bytes than needed from `/dev/urandom` and `/dev/random`.
- Pefer `/dev/random` to `/dev/urandom` when running on FreeBSD.
- Better RNG performance.
- Enh #11336: Allow resettting `$hostInfo`, `$scriptUrl`, and `$pathInfo` in `yii\web\Request` and `$baseUrl`, and `$hostInfo` in `yii\web\UrlManager` to `null`, to make Yii determine the value again (cebe)
- Enh: Added `StringHelper::countWords()` that given a string returns number of words in it (samdark)
- Enh #11207: migrate command can create foreign keys. (Faryshta)
- Enh #11166: migrate command new option `useTablePrefix` (Faryshta)
- Enh #11002: `AttributeBehavior::$skipUpdateOnClean` which determines whether to skip a behavior when the behavior owner has not been modified (Faryshta)
- Chg #11283: `ActiveRecord::unlink()` is not setting FK to `null` before deleting itself anymore (samdark)
- Eng #10976: `Inflector::transliterate()` now uses `strtr` instead of `str_replace` (DrDeath72)
- Enh #11110: Added migrations for DB session (mdmunir)
- Chg: HTMLPurifier dependency updated to `~4.6` (samdark)
- Chg #9854: Added `ActiveRecordInterface::populateRelation()` to respect the methods called by the implementation (SamMousa)
- Chg #10726: Added `yii\rbac\ManagerInterface::canAddChild()` (dkhlystov, samdark)
- Chg #10921: Inverts responsibility of database specific column schema builder classes (df2)
- Chg #11071: `yii\helpers\BaseArrayHelper::isIn()` and `isTraversable()` since now throw `\yii\base\InvalidParamException` instead of `\InvalidArgumentException` (nukkumatti)
- Chg #11283: `ActiveRecord::unlink()` is not setting FK to `null` before deleting itself anymore (samdark)
- Chg: HTMLPurifier dependency updated to `~4.6` (samdark)
- New #8920: Added `yii\mutex\PgsqlMutex` which implements mutex "lock" mechanism via PgSQL locks (nineinchnick, CSharpRU)
2.0.7 February 14, 2016
-----------------------

16
framework/UPGRADE.md

@ -8,30 +8,40 @@ if you want to upgrade from version A to version C and there is
version B between A and C, you need to follow the instructions
for both A and B.
Make sure you have global install of latest version of composer asset plugin:
Make sure you have global install of latest version of composer asset plugin as well as a stable version of composer:
```
php composer.phar self-update
php composer.phar global require "fxp/composer-asset-plugin:~1.1.1"
```
Upgrade from Yii 2.0.7
______________________
----------------------
* The signature of `yii\helpers\BaseArrayHelper::index()` was changed. The method has got an extra optional parameter
`$groups`.
* `yii\helpers\BaseArrayHelper` methods `isIn()` and `isSubset()` throw `\yii\base\InvalidParamException`
instead of `\InvalidArgumentException`. If you wrap calls of these methods in try/catch block, change expected
exception class.
* `yii\rbac\ManagerInterface::canAddChild()` method was added. If you have custom backend for RBAC you need to implement
it.
* The signature of `yii\web\User::loginRequired()` was changed. The method has got an extra optional parameter
`$checkAcceptHeader`.
* The signature of `yii\db\ColumnSchemaBuilder::__construct()` was changed. The method has got an extra optional
parameter `$db`. In case you are instantiating this class yourself and using the `$config` parameter, you will need to
move it to the right by one.
* String types in the MSSQL column schema map were upgraded to Unicode storage types. This will have no effect on
existing columns, but any new columns you generate via the migrations engine will now store data as Unicode.
* Output buffering was introduced in the pair of `yii\widgets\ActiveForm::init()` and `::run()`. If you override any of
these methods, make sure that output buffer handling is not corrupted. If you call the parent implementation, when
overriding, everything should work fine. You should be doing that anyway.
Upgrade from Yii 2.0.6
----------------------
@ -80,7 +90,6 @@ Upgrade from Yii 2.0.6
setting a lot of configuration options via command line. If you extend from this class, make sure it works as expected after
these changes.
Upgrade from Yii 2.0.5
----------------------
@ -161,7 +170,6 @@ Upgrade from Yii 2.0 RC
Quoting of values is broken in prior versions and Yii has no reliable way to work around this issue.
A workaround that may have worked before has been removed in this release because it was not reliable.
Upgrade from Yii 2.0 Beta
-------------------------

10
framework/assets/yii.activeForm.js

@ -201,7 +201,8 @@
settings: settings,
attributes: attributes,
submitting: false,
validated: false
validated: false,
target: $form.attr('target')
});
/**
@ -575,7 +576,14 @@
data.submitting = false;
} else {
data.validated = true;
var buttonTarget = data.submitObject ? data.submitObject.attr('formtarget') : null;
if (buttonTarget) {
// set target attribute to form tag before submit
$form.attr('target', buttonTarget);
}
$form.submit();
// restore original target attribute value
$form.attr('target', data.target);
}
} else {
$.each(data.attributes, function () {

12
framework/assets/yii.gridView.js

@ -55,7 +55,12 @@
return this.each(function () {
var $e = $(this);
var settings = $.extend({}, defaults, options || {});
gridData[$e.attr('id')] = {settings: settings};
var id = $e.attr('id');
if (gridData[id] === undefined) {
gridData[id] = {};
}
gridData[id] = $.extend(gridData[id], {settings: settings});
var enterPressed = false;
$(document).off('change.yiiGridView keydown.yiiGridView', settings.filterSelector)
@ -142,8 +147,11 @@
setSelectionColumn: function (options) {
var $grid = $(this);
var id = $(this).attr('id');
if (gridData.id === undefined) {
gridData[id] = {};
}
gridData[id].selectionColumn = options.name;
if (!options.multiple) {
if (!options.multiple || !options.checkAll) {
return;
}
var checkAll = "#" + id + " input[name='" + options.checkAll + "']";

17
framework/base/Security.php

@ -428,10 +428,6 @@ class Security extends Component
*/
public function generateRandomKey($length = 32)
{
if (function_exists('random_bytes')) {
return random_bytes($length);
}
if (!is_int($length)) {
throw new InvalidParamException('First parameter ($length) must be an integer');
}
@ -440,6 +436,11 @@ class Security extends Component
throw new InvalidParamException('First parameter ($length) must be greater than 0');
}
// always use random_bytes() if it is available
if (function_exists('random_bytes')) {
return random_bytes($length);
}
// The recent LibreSSL RNGs are faster and likely better than /dev/urandom.
// Parse OPENSSL_VERSION_TEXT because OPENSSL_VERSION_NUMBER is no use for LibreSSL.
// https://bugs.php.net/bug.php?id=71143
@ -489,9 +490,9 @@ class Security extends Component
$this->_randomFile = fopen($device, 'rb') ?: null;
if (is_resource($this->_randomFile)) {
// By default PHP buffer size is 8192 bytes which causes wasting
// more entropy that we're actually using. Therefore setting it to
// lower value.
// Reduce PHP stream buffer from default 8192 bytes to optimize data
// transfer from the random device for smaller values of $length.
// This also helps to keep future randoms out of user memory space.
$bufferSize = 8;
if (function_exists('stream_set_read_buffer')) {
@ -514,7 +515,7 @@ class Security extends Component
break;
}
$buffer .= $someBytes;
$stillNeed -= StringHelper::byteLength($buffer);
$stillNeed -= StringHelper::byteLength($someBytes);
if ($stillNeed === 0) {
// Leaving file pointer open in order to make next generation faster by reusing it.
return $buffer;

3
framework/behaviors/AttributeBehavior.php

@ -62,7 +62,6 @@ class AttributeBehavior extends Behavior
* ```
*/
public $attributes = [];
/**
* @var mixed the value that will be assigned to the current attributes. This can be an anonymous function,
* callable in array format (e.g. `[$this, 'methodName']`), an [[Expression]] object representing a DB expression
@ -78,7 +77,6 @@ class AttributeBehavior extends Behavior
* ```
*/
public $value;
/**
* @var boolean whether to skip this behavior when the `$owner` has not been
* modified
@ -86,6 +84,7 @@ class AttributeBehavior extends Behavior
*/
public $skipUpdateOnClean = true;
/**
* @inheritdoc
*/

2
framework/behaviors/TimestampBehavior.php

@ -29,7 +29,7 @@ use yii\db\BaseActiveRecord;
* By default, TimestampBehavior will fill the `created_at` and `updated_at` attributes with the current timestamp
* when the associated AR object is being inserted; it will fill the `updated_at` attribute
* with the timestamp when the AR object is being updated. The timestamp value is obtained by `time()`.
*
*
* For the above implementation to work with MySQL database, please declare the columns(`created_at`, `updated_at`) as int(11) for being UNIX timestamp.
*
* If your attribute names are different or you want to use a different way of calculating the timestamp,

6
framework/classes.php

@ -113,22 +113,23 @@ return [
'yii\db\StaleObjectException' => YII2_PATH . '/db/StaleObjectException.php',
'yii\db\TableSchema' => YII2_PATH . '/db/TableSchema.php',
'yii\db\Transaction' => YII2_PATH . '/db/Transaction.php',
'yii\db\cubrid\ColumnSchemaBuilder' => YII2_PATH . '/db/cubrid/ColumnSchemaBuilder.php',
'yii\db\cubrid\QueryBuilder' => YII2_PATH . '/db/cubrid/QueryBuilder.php',
'yii\db\cubrid\Schema' => YII2_PATH . '/db/cubrid/Schema.php',
'yii\db\mssql\ColumnSchemaBuilder' => YII2_PATH . '/db/mssql/ColumnSchemaBuilder.php',
'yii\db\mssql\PDO' => YII2_PATH . '/db/mssql/PDO.php',
'yii\db\mssql\QueryBuilder' => YII2_PATH . '/db/mssql/QueryBuilder.php',
'yii\db\mssql\Schema' => YII2_PATH . '/db/mssql/Schema.php',
'yii\db\mssql\SqlsrvPDO' => YII2_PATH . '/db/mssql/SqlsrvPDO.php',
'yii\db\mssql\TableSchema' => YII2_PATH . '/db/mssql/TableSchema.php',
'yii\db\mysql\ColumnSchemaBuilder' => YII2_PATH . '/db/mysql/ColumnSchemaBuilder.php',
'yii\db\mysql\QueryBuilder' => YII2_PATH . '/db/mysql/QueryBuilder.php',
'yii\db\mysql\Schema' => YII2_PATH . '/db/mysql/Schema.php',
'yii\db\oci\ColumnSchemaBuilder' => YII2_PATH . '/db/oci/ColumnSchemaBuilder.php',
'yii\db\oci\QueryBuilder' => YII2_PATH . '/db/oci/QueryBuilder.php',
'yii\db\oci\Schema' => YII2_PATH . '/db/oci/Schema.php',
'yii\db\pgsql\ColumnSchemaBuilder' => YII2_PATH . '/db/pgsql/ColumnSchemaBuilder.php',
'yii\db\pgsql\QueryBuilder' => YII2_PATH . '/db/pgsql/QueryBuilder.php',
'yii\db\pgsql\Schema' => YII2_PATH . '/db/pgsql/Schema.php',
'yii\db\sqlite\ColumnSchemaBuilder' => YII2_PATH . '/db/sqlite/ColumnSchemaBuilder.php',
'yii\db\sqlite\QueryBuilder' => YII2_PATH . '/db/sqlite/QueryBuilder.php',
'yii\db\sqlite\Schema' => YII2_PATH . '/db/sqlite/Schema.php',
'yii\di\Container' => YII2_PATH . '/di/Container.php',
@ -207,6 +208,7 @@ return [
'yii\mutex\FileMutex' => YII2_PATH . '/mutex/FileMutex.php',
'yii\mutex\Mutex' => YII2_PATH . '/mutex/Mutex.php',
'yii\mutex\MysqlMutex' => YII2_PATH . '/mutex/MysqlMutex.php',
'yii\mutex\PgsqlMutex' => YII2_PATH . '/mutex/PgsqlMutex.php',
'yii\rbac\Assignment' => YII2_PATH . '/rbac/Assignment.php',
'yii\rbac\BaseManager' => YII2_PATH . '/rbac/BaseManager.php',
'yii\rbac\DbManager' => YII2_PATH . '/rbac/DbManager.php',

6
framework/console/Application.php

@ -166,13 +166,15 @@ class Application extends \yii\base\Application
*
* @param string $route the route that specifies the action.
* @param array $params the parameters to be passed to the action
* @return integer the status code returned by the action execution. 0 means normal, and other values mean abnormal.
* @return integer|Response the result of the action. This can be either an exit code or Response object.
* Exit code 0 means normal, and other values mean abnormal. Exit code of `null` is treaded as `0` as well.
* @throws Exception if the route is invalid
*/
public function runAction($route, $params = [])
{
try {
return (int)parent::runAction($route, $params);
$res = parent::runAction($route, $params);
return is_object($res) ? $res : (int)$res;
} catch (InvalidRouteException $e) {
throw new Exception("Unknown command \"$route\".", 0, $e);
}

5
framework/console/controllers/BaseMigrateController.php

@ -467,6 +467,11 @@ abstract class BaseMigrateController extends Controller
*
* @param string $name the name of the new migration. This should only contain
* letters, digits and/or underscores.
*
* Note: If the migration name is of a special form, for example create_xxx or
* drop_xxx then the generated migration file will contain extra code,
* in this case for creating/dropping tables.
*
* @throws Exception if the name argument is invalid.
*/
public function actionCreate($name)

4
framework/console/controllers/FixtureController.php

@ -72,6 +72,7 @@ class FixtureController extends Controller
/**
* @inheritdoc
* @since 2.0.8
*/
public function optionAliases()
{
@ -333,6 +334,9 @@ class FixtureController extends Controller
$this->outputList($except);
}
$this->stdout("\nBe aware that:\n", Console::BOLD);
$this->stdout("Applying leads to purging of certain data in the database!\n", Console::FG_RED);
return $this->confirm("\nLoad above fixtures?");
}

1
framework/console/controllers/MessageController.php

@ -162,6 +162,7 @@ class MessageController extends Controller
/**
* @inheritdoc
* @since 2.0.8
*/
public function optionAliases()
{

14
framework/console/controllers/MigrateController.php

@ -89,11 +89,16 @@ class MigrateController extends BaseMigrateController
* @since 2.0.8
*/
public $useTablePrefix = false;
/**
* @var array column definition strings used for creating migration code.
* The format of each definition is `COLUMN_NAME:COLUMN_TYPE:COLUMN_DECORATOR`.
* For example, `--fields=name:string(12):notNull` produces a string column of size 12 which is not null.
*
* The format of each definition is `COLUMN_NAME:COLUMN_TYPE:COLUMN_DECORATOR`. Delimiter is `,`.
* For example, `--fields="name:string(12):notNull:unique"`
* produces a string column of size 12 which is not null and unique values.
*
* Note: primary key is added automatically and is named id by default.
* If you want to use another name you may specify it explicitly like
* `--fields="id_key:primaryKey,name:string(12):notNull:unique"`
* @since 2.0.7
*/
public $fields = [];
@ -121,6 +126,7 @@ class MigrateController extends BaseMigrateController
/**
* @inheritdoc
* @since 2.0.8
*/
public function optionAliases()
{
@ -228,6 +234,7 @@ class MigrateController extends BaseMigrateController
/**
* @inheritdoc
* @since 2.0.8
*/
protected function generateMigrationSourceCode($params)
{
@ -305,6 +312,7 @@ class MigrateController extends BaseMigrateController
*
* @param string $tableName the table name to generate.
* @return string
* @since 2.0.8
*/
protected function generateTableName($tableName)
{

1
framework/console/controllers/ServeController.php

@ -96,6 +96,7 @@ class ServeController extends Controller
/**
* @inheritdoc
* @since 2.0.8
*/
public function optionAliases()
{

4
framework/data/BaseDataProvider.php

@ -18,10 +18,10 @@ use yii\base\InvalidParamException;
* @property array $keys The list of key values corresponding to [[models]]. Each data model in [[models]] is
* uniquely identified by the corresponding key value in this array.
* @property array $models The list of data models in the current page.
* @property Pagination|false $pagination The pagination object. If this is false, it means the pagination
* @property Pagination|boolean $pagination The pagination object. If this is false, it means the pagination
* is disabled. Note that the type of this property differs in getter and setter. See [[getPagination()]] and
* [[setPagination()]] for details.
* @property Sort|false $sort The sorting object. If this is false, it means the sorting is disabled. Note
* @property Sort|boolean $sort The sorting object. If this is false, it means the sorting is disabled. Note
* that the type of this property differs in getter and setter. See [[getSort()]] and [[setSort()]] for details.
* @property integer $totalCount Total number of possible data models.
*

9
framework/db/ActiveRecordInterface.php

@ -364,6 +364,15 @@ interface ActiveRecordInterface
public function getRelation($name, $throwException = true);
/**
* Populates the named relation with the related records.
* Note that this method does not check if the relation exists or not.
* @param string $name the relation name (case-sensitive)
* @param ActiveRecordInterface|array|null $records the related records to be populated into the relation.
* @since 2.0.8
*/
public function populateRelation($name, $records);
/**
* Establishes the relationship between two records.
*
* The relationship is established by setting the foreign key value(s) in one record

8
framework/db/ActiveRelationTrait.php

@ -469,9 +469,15 @@ trait ActiveRelationTrait
}
} else {
// composite keys
// ensure keys of $this->link are prefixed the same way as $attributes
$prefixedLink = array_combine(
$attributes,
array_values($this->link)
);
foreach ($models as $model) {
$v = [];
foreach ($this->link as $attribute => $link) {
foreach ($prefixedLink as $attribute => $link) {
$v[$attribute] = $model[$link];
}
$values[] = $v;

22
framework/db/BaseActiveRecord.php

@ -77,6 +77,11 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
* @event Event an event that is triggered after a record is deleted.
*/
const EVENT_AFTER_DELETE = 'afterDelete';
/**
* @event Event an event that is triggered after a record is refreshed.
* @since 2.0.8
*/
const EVENT_AFTER_REFRESH = 'afterRefresh';
/**
* @var array attribute values indexed by attribute names
@ -951,6 +956,10 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
/**
* Repopulates this active record with the latest data.
*
* If the refresh is successful, an [[EVENT_AFTER_REFRESH]] event will be triggered.
* This event is available since version 2.0.8.
*
* @return boolean whether the row still exists in the database. If true, the latest data
* will be populated to this active record. Otherwise, this record will remain unchanged.
*/
@ -966,11 +975,24 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
}
$this->_oldAttributes = $this->_attributes;
$this->_related = [];
$this->afterRefresh();
return true;
}
/**
* This method is called when the AR object is refreshed.
* The default implementation will trigger an [[EVENT_AFTER_REFRESH]] event.
* When overriding this method, make sure you call the parent implementation to ensure the
* event is triggered.
* @since 2.0.8
*/
public function afterRefresh()
{
$this->trigger(self::EVENT_AFTER_REFRESH);
}
/**
* Returns a value indicating whether the given active record is the same as the current one.
* The comparison is made by comparing the table names and the primary key values of the two active records.
* If one of the records [[isNewRecord|is new]] they are also considered not equal.

34
framework/db/ColumnSchemaBuilder.php

@ -70,6 +70,8 @@ class ColumnSchemaBuilder extends Object
* @since 2.0.8
*/
protected $isFirst;
/**
* @var array mapping of abstract column types (keys) to type categories (values).
* @since 2.0.8
@ -102,6 +104,11 @@ class ColumnSchemaBuilder extends Object
* @since 2.0.8
*/
public $db;
/**
* @var string comment value of the column.
* @since 2.0.8
*/
public $comment;
/**
* Create a column schema builder instance giving the type and value precision.
@ -162,6 +169,18 @@ class ColumnSchemaBuilder extends Object
}
/**
* Specifies the comment for column.
* @param string $comment the comment
* @return $this
* @since 2.0.8
*/
public function comment($comment)
{
$this->comment = $comment;
return $this;
}
/**
* Marks column as unsigned.
* @return $this
* @since 2.0.7
@ -225,10 +244,10 @@ class ColumnSchemaBuilder extends Object
{
switch ($this->getTypeCategory()) {
case self::CATEGORY_PK:
$format = '{type}{check}';
$format = '{type}{check}{comment}';
break;
default:
$format = '{type}{length}{notnull}{unique}{default}{check}';
$format = '{type}{length}{notnull}{unique}{default}{check}{comment}';
}
return $this->buildCompleteString($format);
}
@ -348,6 +367,16 @@ class ColumnSchemaBuilder extends Object
}
/**
* Builds the comment specification for the column.
* @return string a string containing the COMMENT keyword and the comment itself
* @since 2.0.8
*/
protected function buildCommentString()
{
return '';
}
/**
* Returns the complete column definition from input format
* @param string $format the format of the definition.
* @return string a string containing the complete column definition.
@ -363,6 +392,7 @@ class ColumnSchemaBuilder extends Object
'{unique}' => $this->buildUniqueString(),
'{default}' => $this->buildDefaultString(),
'{check}' => $this->buildCheckString(),
'{comment}' => $this->buildCommentString(),
'{pos}' => ($this->isFirst) ?
$this->buildFirstString() :
$this->buildAfterString(),

60
framework/db/Command.php

@ -757,6 +757,66 @@ class Command extends Component
}
/**
* Builds a SQL command for adding comment to column
*
* @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
* @param string $column the name of the column to be commented. The column name will be properly quoted by the method.
* @param string $comment the text of the comment to be added. The comment will be properly quoted by the method.
* @return $this the command object itself
* @since 2.0.8
*/
public function addCommentOnColumn($table, $column, $comment)
{
$sql = $this->db->getQueryBuilder()->addCommentOnColumn($table, $column, $comment);
return $this->setSql($sql);
}
/**
* Builds a SQL command for adding comment to table
*
* @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
* @param string $comment the text of the comment to be added. The comment will be properly quoted by the method.
* @return $this the command object itself
* @since 2.0.8
*/
public function addCommentOnTable($table, $comment)
{
$sql = $this->db->getQueryBuilder()->addCommentOnTable($table, $comment);
return $this->setSql($sql);
}
/**
* Builds a SQL command for dropping comment from column
*
* @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
* @param string $column the name of the column to be commented. The column name will be properly quoted by the method.
* @return $this the command object itself
* @since 2.0.8
*/
public function dropCommentFromColumn($table, $column)
{
$sql = $this->db->getQueryBuilder()->dropCommentFromColumn($table, $column);
return $this->setSql($sql);
}
/**
* Builds a SQL command for dropping comment from table
*
* @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
* @return $this the command object itself
* @since 2.0.8
*/
public function dropCommentFromTable($table)
{
$sql = $this->db->getQueryBuilder()->dropCommentFromTable($table);
return $this->setSql($sql);
}
/**
* Executes the SQL statement.
* This method should only be used for executing non-query SQL statement, such as `INSERT`, `DELETE`, `UPDATE` SQLs.
* No result set will be returned.

77
framework/db/Migration.php

@ -258,7 +258,12 @@ class Migration extends Component implements MigrationInterface
echo " > create table $table ...";
$time = microtime(true);
$this->db->createCommand()->createTable($table, $columns, $options)->execute();
echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . "s)\n";
foreach ($columns as $column => $type) {
if ($type instanceof ColumnSchemaBuilder && $type->comment !== null) {
$this->db->createCommand()->addCommentOnColumn($table, $column, $type->comment)->execute();
}
}
echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
}
/**
@ -311,6 +316,9 @@ class Migration extends Component implements MigrationInterface
echo " > add column $column $type to table $table ...";
$time = microtime(true);
$this->db->createCommand()->addColumn($table, $column, $type)->execute();
if ($type instanceof ColumnSchemaBuilder && $type->comment !== null) {
$this->db->createCommand()->addCommentOnColumn($table, $column, $type->comment)->execute();
}
echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . "s)\n";
}
@ -354,6 +362,9 @@ class Migration extends Component implements MigrationInterface
echo " > alter column $column in table $table to $type ...";
$time = microtime(true);
$this->db->createCommand()->alterColumn($table, $column, $type)->execute();
if ($type instanceof ColumnSchemaBuilder && $type->comment !== null) {
$this->db->createCommand()->addCommentOnColumn($table, $column, $type->comment)->execute();
}
echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . "s)\n";
}
@ -446,4 +457,68 @@ class Migration extends Component implements MigrationInterface
$this->db->createCommand()->dropIndex($name, $table)->execute();
echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . "s)\n";
}
/**
* Builds and execute a SQL statement for adding comment to column
*
* @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
* @param string $column the name of the column to be commented. The column name will be properly quoted by the method.
* @param string $comment the text of the comment to be added. The comment will be properly quoted by the method.
* @return $this the command object itself
* @since 2.0.8
*/
public function addCommentOnColumn($table, $column, $comment)
{
echo " > add comment on column $column ...";
$time = microtime(true);
$this->db->createCommand()->addCommentOnColumn($table, $column, $comment)->execute();
echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . "s)\n";
}
/**
* Builds a SQL statement for adding comment to table
*
* @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
* @param string $comment the text of the comment to be added. The comment will be properly quoted by the method.
* @return $this the command object itself
* @since 2.0.8
*/
public function addCommentOnTable($table, $comment)
{
echo " > add comment on table $table ...";
$time = microtime(true);
$this->db->createCommand()->addCommentOnTable($table, $comment)->execute();
echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . "s)\n";
}
/**
* Builds and execute a SQL statement for dropping comment from column
*
* @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
* @param string $column the name of the column to be commented. The column name will be properly quoted by the method.
* @return $this the command object itself
* @since 2.0.8
*/
public function dropCommentFromColumn($table, $column)
{
echo " > drop comment from column $column ...";
$time = microtime(true);
$this->db->createCommand()->dropCommentFromColumn($table, $column)->execute();
echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . "s)\n";
}
/**
* Builds a SQL statement for dropping comment from table
*
* @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
* @return $this the command object itself
* @since 2.0.8
*/
public function dropCommentFromTable($table)
{
echo " > drop comment from table $table ...";
$time = microtime(true);
$this->db->createCommand()->dropCommentFromTable($table)->execute();
echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . "s)\n";
}
}

45
framework/db/Query.php

@ -360,11 +360,11 @@ class Query extends Component implements QueryInterface
*/
public function exists($db = null)
{
$select = $this->select;
$this->select = [new Expression('1')];
$command = $this->createCommand($db);
$this->select = $select;
return $command->queryScalar() !== false;
$params = $command->params;
$command->setSql($command->db->getQueryBuilder()->selectExists($command->getSql()));
$command->bindValues($params);
return (boolean)$command->queryScalar();
}
/**
@ -582,6 +582,43 @@ class Query extends Component implements QueryInterface
}
/**
* Adds a filtering condition for a specific column and allow the user to choose a filter operator.
*
* It adds an additional WHERE condition for the given field and determines the comparison operator
* based on the first few characters of the given value.
* The condition is added in the same way as in [[andFilterWhere]] so [[isEmpty()|empty values]] are ignored.
* The new condition and the existing one will be joined using the 'AND' operator.
*
* The comparison operator is intelligently determined based on the first few characters in the given value.
* In particular, it recognizes the following operators if they appear as the leading characters in the given value:
*
* - `<`: the column must be less than the given value.
* - `>`: the column must be greater than the given value.
* - `<=`: the column must be less than or equal to the given value.
* - `>=`: the column must be greater than or equal to the given value.
* - `<>`: the column must not be the same as the given value.
* - `=`: the column must be equal to the given value.
* - If none of the above operators is detected, the `$defaultOperator` will be used.
*
* @param string $name the column name.
* @param string $value the column value optionally prepended with the comparison operator.
* @param string $defaultOperator The operator to use, when no operator is given in `$value`.
* Defaults to `=`, performing an exact match.
* @return $this The query object itself
* @since 2.0.8
*/
public function andFilterCompare($name, $value, $defaultOperator = '=')
{
if (preg_match("/^(<>|>=|>|<=|<|=)/", $value, $matches)) {
$operator = $matches[1];
$value = substr($value, strlen($operator));
} else {
$operator = $defaultOperator;
}
return $this->andFilterWhere([$operator, $name, $value]);
}
/**
* Appends a JOIN part to the query.
* The first parameter specifies what type of join it is.
* @param string $type the type of join, such as INNER JOIN, LEFT JOIN.

108
framework/db/QueryBuilder.php

@ -9,6 +9,7 @@ namespace yii\db;
use yii\base\InvalidParamException;
use yii\base\NotSupportedException;
use yii\helpers\ArrayHelper;
/**
* QueryBuilder builds a SELECT SQL statement based on the specification given as a [[Query]] object.
@ -569,6 +570,59 @@ class QueryBuilder extends \yii\base\Object
}
/**
* Builds a SQL command for adding comment to column
*
* @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
* @param string $column the name of the column to be commented. The column name will be properly quoted by the method.
* @param string $comment the text of the comment to be added. The comment will be properly quoted by the method.
* @return string the SQL statement for adding comment on column
* @since 2.0.8
*/
public function addCommentOnColumn($table, $column, $comment)
{
return 'COMMENT ON COLUMN ' . $this->db->quoteTableName($table) . '.' . $this->db->quoteColumnName($column) . ' IS ' . $this->db->quoteValue($comment);
}
/**
* Builds a SQL command for adding comment to table
*
* @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
* @param string $comment the text of the comment to be added. The comment will be properly quoted by the method.
* @return string the SQL statement for adding comment on table
* @since 2.0.8
*/
public function addCommentOnTable($table, $comment)
{
return 'COMMENT ON TABLE ' . $this->db->quoteTableName($table) . ' IS ' . $this->db->quoteValue($comment);
}
/**
* Builds a SQL command for adding comment to column
*
* @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
* @param string $column the name of the column to be commented. The column name will be properly quoted by the method.
* @return string the SQL statement for adding comment on column
* @since 2.0.8
*/
public function dropCommentFromColumn($table, $column)
{
return 'COMMENT ON COLUMN ' . $this->db->quoteTableName($table) . '.' . $this->db->quoteColumnName($column) . ' IS NULL';
}
/**
* Builds a SQL command for adding comment to table
*
* @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
* @return string the SQL statement for adding comment on column
* @since 2.0.8
*/
public function dropCommentFromTable($table)
{
return 'COMMENT ON TABLE ' . $this->db->quoteTableName($table) . ' IS NULL';
}
/**
* Converts an abstract column type into a physical column type.
* The conversion is done using the type map specified in [[typeMap]].
* The following abstract column types are supported (using MySQL as an example to explain the corresponding
@ -970,7 +1024,7 @@ class QueryBuilder extends \yii\base\Object
{
$parts = [];
foreach ($condition as $column => $value) {
if (is_array($value) || $value instanceof Query) {
if (ArrayHelper::isTraversable($value) || $value instanceof Query) {
// IN condition
$parts[] = $this->buildInCondition('IN', [$column, $value], $params);
} else {
@ -1112,7 +1166,7 @@ class QueryBuilder extends \yii\base\Object
list($column, $values) = $operands;
if ($values === [] || $column === []) {
if ($column === []) {
return $operator === 'IN' ? '0=1' : '';
}
@ -1120,41 +1174,46 @@ class QueryBuilder extends \yii\base\Object
return $this->buildSubqueryInCondition($operator, $column, $values, $params);
}
$values = (array) $values;
if (count($column) > 1) {
if ($column instanceof \Traversable || count($column) > 1) {
return $this->buildCompositeInCondition($operator, $column, $values, $params);
}
if (is_array($column)) {
$column = reset($column);
}
$sqlValues = [];
foreach ($values as $i => $value) {
if (is_array($value)) {
if (is_array($value) || $value instanceof \ArrayAccess) {
$value = isset($value[$column]) ? $value[$column] : null;
}
if ($value === null) {
$values[$i] = 'NULL';
$sqlValues[$i] = 'NULL';
} elseif ($value instanceof Expression) {
$values[$i] = $value->expression;
$sqlValues[$i] = $value->expression;
foreach ($value->params as $n => $v) {
$params[$n] = $v;
}
} else {
$phName = self::PARAM_PREFIX . count($params);
$params[$phName] = $value;
$values[$i] = $phName;
$sqlValues[$i] = $phName;
}
}
if (empty($sqlValues)) {
return $operator === 'IN' ? '0=1' : '';
}
if (strpos($column, '(') === false) {
$column = $this->db->quoteColumnName($column);
}
if (count($values) > 1) {
return "$column $operator (" . implode(', ', $values) . ')';
if (count($sqlValues) > 1) {
return "$column $operator (" . implode(', ', $sqlValues) . ')';
} else {
$operator = $operator === 'IN' ? '=' : '<>';
return $column . $operator . reset($values);
return $column . $operator . reset($sqlValues);
}
}
@ -1189,7 +1248,7 @@ class QueryBuilder extends \yii\base\Object
* Builds SQL for IN condition
*
* @param string $operator
* @param array $columns
* @param array|\Traversable $columns
* @param array $values
* @param array $params
* @return string SQL
@ -1210,13 +1269,17 @@ class QueryBuilder extends \yii\base\Object
}
$vss[] = '(' . implode(', ', $vs) . ')';
}
if (empty($vss)) {
return $operator === 'IN' ? '0=1' : '';
};
$sqlColumns = [];
foreach ($columns as $i => $column) {
if (strpos($column, '(') === false) {
$columns[$i] = $this->db->quoteColumnName($column);
}
$sqlColumns[] = strpos($column, '(') === false ? $this->db->quoteColumnName($column) : $column;
}
return '(' . implode(', ', $columns) . ") $operator (" . implode(', ', $vss) . ')';
return '(' . implode(', ', $sqlColumns) . ") $operator (" . implode(', ', $vss) . ')';
}
/**
@ -1340,4 +1403,15 @@ class QueryBuilder extends \yii\base\Object
return "$column $operator $phName";
}
}
/**
* Creates a SELECT EXISTS() SQL statement.
* @param string $rawSql the subquery in a raw form to select from.
* @return string the SELECT EXISTS() SQL statement.
* @since 2.0.8
*/
public function selectExists($rawSql)
{
return 'SELECT EXISTS(' . $rawSql . ')';
}
}

6
framework/db/Schema.php

@ -522,7 +522,7 @@ abstract class Schema extends Object
*/
public function quoteColumnName($name)
{
if (strpos($name, '(') !== false || strpos($name, '[[') !== false || strpos($name, '{{') !== false) {
if (strpos($name, '(') !== false || strpos($name, '[[') !== false) {
return $name;
}
if (($pos = strrpos($name, '.')) !== false) {
@ -531,7 +531,9 @@ abstract class Schema extends Object
} else {
$prefix = '';
}
if (strpos($name, '{{') !== false) {
return $name;
}
return $prefix . $this->quoteSimpleColumnName($name);
}

1
framework/db/Transaction.php

@ -37,6 +37,7 @@ use yii\base\InvalidConfigException;
* one of [[READ_UNCOMMITTED]], [[READ_COMMITTED]], [[REPEATABLE_READ]] and [[SERIALIZABLE]] but also a string
* containing DBMS specific syntax to be used after `SET TRANSACTION ISOLATION LEVEL`. This property is
* write-only.
* @property integer $level The current nesting level of the transaction. This property is read-only.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0

16
framework/db/cubrid/ColumnSchemaBuilder.php

@ -31,7 +31,7 @@ class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder
protected function buildAfterString()
{
return $this->after !== null ?
' AFTER (' . $this->db->quoteColumnName($this->after) . ')' :
' AFTER ' . $this->db->quoteColumnName($this->after) :
'';
}
@ -46,17 +46,25 @@ class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder
/**
* @inheritdoc
*/
protected function buildCommentString()
{
return $this->comment !== null ? " COMMENT " . $this->db->quoteValue($this->comment) : '';
}
/**
* @inheritdoc
*/
public function __toString()
{
switch ($this->getTypeCategory()) {
case self::CATEGORY_PK:
$format = '{type}{check}{pos}';
$format = '{type}{check}{pos}{comment}';
break;
case self::CATEGORY_NUMERIC:
$format = '{type}{length}{unsigned}{notnull}{unique}{default}{check}{pos}';
$format = '{type}{length}{unsigned}{notnull}{unique}{default}{check}{comment}{pos}';
break;
default:
$format = '{type}{length}{notnull}{unique}{default}{check}{pos}';
$format = '{type}{length}{notnull}{unique}{default}{check}{comment}{pos}';
}
return $this->buildCompleteString($format);
}

87
framework/db/cubrid/QueryBuilder.php

@ -8,6 +8,7 @@
namespace yii\db\cubrid;
use yii\base\InvalidParamException;
use yii\db\Exception;
/**
* QueryBuilder is the query builder for CUBRID databases (version 9.3.x and higher).
@ -94,4 +95,90 @@ class QueryBuilder extends \yii\db\QueryBuilder
return $sql;
}
/**
* @inheritdoc
* @since 2.0.8
*/
public function selectExists($rawSql)
{
return 'SELECT CASE WHEN EXISTS(' . $rawSql . ') THEN 1 ELSE 0 END';
}
/**
* @inheritdoc
* @since 2.0.8
*/
public function addCommentOnColumn($table, $column, $comment)
{
$definition = $this->getColumnDefinition($table, $column);
$definition = trim(preg_replace("/COMMENT '(.*?)'/i", '', $definition));
return 'ALTER TABLE ' . $this->db->quoteTableName($table)
. ' CHANGE ' . $this->db->quoteColumnName($column)
. ' ' . $this->db->quoteColumnName($column)
. (empty($definition) ? '' : ' ' . $definition)
. ' COMMENT ' . $this->db->quoteValue($comment);
}
/**
* @inheritdoc
* @since 2.0.8
*/
public function addCommentOnTable($table, $comment)
{
return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' COMMENT ' . $this->db->quoteValue($comment);
}
/**
* @inheritdoc
* @since 2.0.8
*/
public function dropCommentFromColumn($table, $column)
{
return $this->addCommentOnColumn($table, $column, '');
}
/**
* @inheritdoc
* @since 2.0.8
*/
public function dropCommentFromTable($table)
{
return $this->addCommentOnTable($table, '');
}
/**
* Gets column definition.
*
* @param string $table table name
* @param string $column column name
* @return null|string the column definition
* @throws Exception in case when table does not contain column
* @since 2.0.8
*/
private function getColumnDefinition($table, $column)
{
$row = $this->db->createCommand('SHOW CREATE TABLE ' . $this->db->quoteTableName($table))->queryOne();
if ($row === false) {
throw new Exception("Unable to find column '$column' in table '$table'.");
}
if (isset($row['Create Table'])) {
$sql = $row['Create Table'];
} else {
$row = array_values($row);
$sql = $row[1];
}
$sql = preg_replace('/^[^(]+\((.*)\).*$/', '\1', $sql);
$sql = str_replace(', [', ",\n[", $sql);
if (preg_match_all('/^\s*\[(.*?)\]\s+(.*?),?$/m', $sql, $matches)) {
foreach ($matches[1] as $i => $c) {
if ($c === $column) {
return $matches[2][$i];
}
}
}
return null;
}
}

45
framework/db/mssql/QueryBuilder.php

@ -187,6 +187,42 @@ class QueryBuilder extends \yii\db\QueryBuilder
}
/**
* @inheritdoc
* @since 2.0.8
*/
public function addCommentOnColumn($table, $column, $comment)
{
return "sp_updateextendedproperty @name = N'MS_Description', @value = {$this->db->quoteValue($comment)}, @level1type = N'Table', @level1name = {$this->db->quoteTableName($table)}, @level2type = N'Column', @level2name = {$this->db->quoteColumnName($column)}";
}
/**
* @inheritdoc
* @since 2.0.8
*/
public function addCommentOnTable($table, $comment)
{
return "sp_updateextendedproperty @name = N'MS_Description', @value = {$this->db->quoteValue($comment)}, @level1type = N'Table', @level1name = {$this->db->quoteTableName($table)}";
}
/**
* @inheritdoc
* @since 2.0.8
*/
public function dropCommentFromColumn($table, $column)
{
return "sp_dropextendedproperty @name = N'MS_Description', @level1type = N'Table', @level1name = {$this->db->quoteTableName($table)}, @level2type = N'Column', @level2name = {$this->db->quoteColumnName($column)}";
}
/**
* @inheritdoc
* @since 2.0.8
*/
public function dropCommentFromTable($table)
{
return "sp_dropextendedproperty @name = N'MS_Description', @level1type = N'Table', @level1name = {$this->db->quoteTableName($table)}";
}
/**
* Returns an array of column names given model name
*
* @param string $modelClass name of the model class
@ -268,4 +304,13 @@ class QueryBuilder extends \yii\db\QueryBuilder
return '(' . implode($operator === 'IN' ? ' OR ' : ' AND ', $vss) . ')';
}
/**
* @inheritdoc
* @since 2.0.8
*/
public function selectExists($rawSql)
{
return 'SELECT CASE WHEN EXISTS(' . $rawSql . ') THEN 1 ELSE 0 END';
}
}

16
framework/db/mysql/ColumnSchemaBuilder.php

@ -31,7 +31,7 @@ class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder
protected function buildAfterString()
{
return $this->after !== null ?
' AFTER (' . $this->db->quoteColumnName($this->after) . ')' :
' AFTER ' . $this->db->quoteColumnName($this->after) :
'';
}
@ -46,17 +46,25 @@ class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder
/**
* @inheritdoc
*/
protected function buildCommentString()
{
return $this->comment !== null ? " COMMENT " . $this->db->quoteValue($this->comment) : '';
}
/**
* @inheritdoc
*/
public function __toString()
{
switch ($this->getTypeCategory()) {
case self::CATEGORY_PK:
$format = '{type}{length}{check}{pos}';
$format = '{type}{length}{check}{comment}{pos}';
break;
case self::CATEGORY_NUMERIC:
$format = '{type}{length}{unsigned}{notnull}{unique}{default}{check}{pos}';
$format = '{type}{length}{unsigned}{notnull}{unique}{default}{check}{comment}{pos}';
break;
default:
$format = '{type}{length}{notnull}{unique}{default}{check}{pos}';
$format = '{type}{length}{notnull}{unique}{default}{check}{comment}{pos}';
}
return $this->buildCompleteString($format);
}

76
framework/db/mysql/QueryBuilder.php

@ -45,6 +45,7 @@ class QueryBuilder extends \yii\db\QueryBuilder
Schema::TYPE_MONEY => 'decimal(19,4)',
];
/**
* Builds a SQL statement for renaming a column.
* @param string $table the table whose column is to be renamed. The name will be properly quoted by the method.
@ -219,4 +220,79 @@ class QueryBuilder extends \yii\db\QueryBuilder
. (!empty($names) ? ' (' . implode(', ', $names) . ')' : '')
. (!empty($placeholders) ? ' VALUES (' . implode(', ', $placeholders) . ')' : ' DEFAULT VALUES');
}
/**
* @inheritdoc
* @since 2.0.8
*/
public function addCommentOnColumn($table, $column, $comment)
{
$definition = $this->getColumnDefinition($table, $column);
$definition = trim(preg_replace("/COMMENT '(.*?)'/i", '', $definition));
return 'ALTER TABLE ' . $this->db->quoteTableName($table)
. ' CHANGE ' . $this->db->quoteColumnName($column)
. ' ' . $this->db->quoteColumnName($column)
. (empty($definition) ? '' : ' ' . $definition)
. ' COMMENT ' . $this->db->quoteValue($comment);
}
/**
* @inheritdoc
* @since 2.0.8
*/
public function addCommentOnTable($table, $comment)
{
return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' COMMENT ' . $this->db->quoteValue($comment);
}
/**
* @inheritdoc
* @since 2.0.8
*/
public function dropCommentFromColumn($table, $column)
{
return $this->addCommentOnColumn($table, $column, '');
}
/**
* @inheritdoc
* @since 2.0.8
*/
public function dropCommentFromTable($table)
{
return $this->addCommentOnTable($table, '');
}
/**
* Gets column definition.
*
* @param string $table table name
* @param string $column column name
* @return null|string the column definition
* @throws Exception in case when table does not contain column
*/
private function getColumnDefinition($table, $column)
{
$quotedTable = $this->db->quoteTableName($table);
$row = $this->db->createCommand('SHOW CREATE TABLE ' . $quotedTable)->queryOne();
if ($row === false) {
throw new Exception("Unable to find column '$column' in table '$table'.");
}
if (isset($row['Create Table'])) {
$sql = $row['Create Table'];
} else {
$row = array_values($row);
$sql = $row[1];
}
if (preg_match_all('/^\s*`(.*?)`\s+(.*?),?$/m', $sql, $matches)) {
foreach ($matches[1] as $i => $c) {
if ($c === $column) {
return $matches[2][$i];
}
}
}
return null;
}
}

1
framework/db/mysql/Schema.php

@ -52,6 +52,7 @@ class Schema extends \yii\db\Schema
'enum' => self::TYPE_STRING,
];
/**
* Quotes a table name for use in a query.
* A simple table name has no schema prefix.

3
framework/db/oci/ColumnSchemaBuilder.php

@ -13,6 +13,7 @@ use yii\db\ColumnSchemaBuilder as AbstractColumnSchemaBuilder;
* ColumnSchemaBuilder is the schema builder for Oracle databases.
*
* @author Vasenin Matvey <vaseninm@gmail.com>
* @author Chris Harris <chris@buckshotsoftware.com>
* @since 2.0.6
*/
class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder
@ -31,7 +32,7 @@ class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder
protected function buildAfterString()
{
return $this->after !== null ?
' AFTER (' . $this->db->quoteColumnName($this->after) . ')' :
' AFTER ' . $this->db->quoteColumnName($this->after) :
'';
}

28
framework/db/oci/QueryBuilder.php

@ -46,6 +46,7 @@ class QueryBuilder extends \yii\db\QueryBuilder
Schema::TYPE_MONEY => 'NUMBER(19,4)',
];
/**
* @inheritdoc
*/
@ -259,4 +260,31 @@ EOD;
return 'INSERT ALL ' . $tableAndColumns . implode($tableAndColumns, $values) . ' SELECT 1 FROM SYS.DUAL';
}
/**
* @inheritdoc
* @since 2.0.8
*/
public function selectExists($rawSql)
{
return 'SELECT CASE WHEN EXISTS(' . $rawSql . ') THEN 1 ELSE 0 END FROM DUAL';
}
/**
* @inheritdoc
* @since 2.0.8
*/
public function dropCommentFromColumn($table, $column)
{
return 'COMMENT ON COLUMN ' . $this->db->quoteTableName($table) . '.' . $this->db->quoteColumnName($column) . " IS ''";
}
/**
* @inheritdoc
* @since 2.0.8
*/
public function dropCommentFromTable($table)
{
return 'COMMENT ON TABLE ' . $this->db->quoteTableName($table) . " IS ''";
}
}

1
framework/db/sqlite/ColumnSchemaBuilder.php

@ -41,6 +41,7 @@ class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder
default:
$format = '{type}{length}{notnull}{unique}{check}{default}';
}
return $this->buildCompleteString($format);
}
}

41
framework/db/sqlite/QueryBuilder.php

@ -48,6 +48,7 @@ class QueryBuilder extends \yii\db\QueryBuilder
Schema::TYPE_MONEY => 'decimal(19,4)',
];
/**
* Generates a batch INSERT SQL statement.
* For example,
@ -292,6 +293,46 @@ class QueryBuilder extends \yii\db\QueryBuilder
/**
* @inheritdoc
* @throws NotSupportedException
* @since 2.0.8
*/
public function addCommentOnColumn($table, $column, $comment)
{
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
}
/**
* @inheritdoc
* @throws NotSupportedException
* @since 2.0.8
*/
public function addCommentOnTable($table, $comment)
{
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
}
/**
* @inheritdoc
* @throws NotSupportedException
* @since 2.0.8
*/
public function dropCommentFromColumn($table, $column)
{
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
}
/**
* @inheritdoc
* @throws NotSupportedException
* @since 2.0.8
*/
public function dropCommentFromTable($table)
{
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
}
/**
* @inheritdoc
*/
public function buildLimit($limit, $offset)
{

17
framework/db/sqlite/Schema.php

@ -92,6 +92,15 @@ class Schema extends \yii\db\Schema
}
/**
* @inheritdoc
* @return ColumnSchemaBuilder column schema builder instance
*/
public function createColumnSchemaBuilder($type, $length = null)
{
return new ColumnSchemaBuilder($type, $length);
}
/**
* Returns all table names in the database.
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
* @return array all table names in the database. The names have NO schema name prefix.
@ -281,12 +290,4 @@ class Schema extends \yii\db\Schema
throw new NotSupportedException(get_class($this) . ' only supports transaction isolation levels READ UNCOMMITTED and SERIALIZABLE.');
}
}
/**
* @inheritdoc
*/
public function createColumnSchemaBuilder($type, $length = null)
{
return new ColumnSchemaBuilder($type, $length);
}
}

2
framework/di/Container.php

@ -524,7 +524,7 @@ class Container extends Component
unset($params[$name]);
} elseif (!$associative && isset($params[0]) && $params[0] instanceof $className) {
$args[] = array_shift($params);
} elseif (Yii::$app->has($name) && ($obj = Yii::$app->get($name)) instanceof $className) {
} elseif (isset(Yii::$app) && Yii::$app->has($name) && ($obj = Yii::$app->get($name)) instanceof $className) {
$args[] = $obj;
} else {
$args[] = $this->get($className);

60
framework/grid/CheckboxColumn.php

@ -10,6 +10,7 @@ namespace yii\grid;
use Closure;
use yii\base\InvalidConfigException;
use yii\helpers\Html;
use yii\helpers\Json;
/**
* CheckboxColumn displays a column of checkboxes in a grid view.
@ -81,6 +82,8 @@ class CheckboxColumn extends Column
if (substr_compare($this->name, '[]', -2, 2)) {
$this->name .= '[]';
}
$this->registerClientScript();
}
/**
@ -91,28 +94,10 @@ class CheckboxColumn extends Column
*/
protected function renderHeaderCellContent()
{
$name = $this->name;
if (substr_compare($name, '[]', -2, 2) === 0) {
$name = substr($name, 0, -2);
}
if (substr_compare($name, ']', -1, 1) === 0) {
$name = substr($name, 0, -1) . '_all]';
} else {
$name .= '_all';
}
$id = $this->grid->options['id'];
$options = json_encode([
'name' => $this->name,
'multiple' => $this->multiple,
'checkAll' => $name,
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$this->grid->getView()->registerJs("jQuery('#$id').yiiGridView('setSelectionColumn', $options);");
if ($this->header !== null || !$this->multiple) {
return parent::renderHeaderCellContent();
} else {
return Html::checkbox($name, false, ['class' => 'select-on-check-all']);
return Html::checkbox($this->getHeaderCheckBoxName(), false, ['class' => 'select-on-check-all']);
}
}
@ -128,9 +113,44 @@ class CheckboxColumn extends Column
}
if (!isset($options['value'])) {
$options['value'] = is_array($key) ? json_encode($key, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) : $key;
$options['value'] = is_array($key) ? Json::encode($key) : $key;
}
return Html::checkbox($this->name, !empty($options['checked']), $options);
}
/**
* Returns header checkbox name
* @return string header checkbox name
* @since 2.0.8
*/
protected function getHeaderCheckBoxName()
{
$name = $this->name;
if (substr_compare($name, '[]', -2, 2) === 0) {
$name = substr($name, 0, -2);
}
if (substr_compare($name, ']', -1, 1) === 0) {
$name = substr($name, 0, -1) . '_all]';
} else {
$name .= '_all';
}
return $name;
}
/**
* Registers the needed JavaScript
* @since 2.0.8
*/
public function registerClientScript()
{
$id = $this->grid->options['id'];
$options = Json::encode([
'name' => $this->name,
'multiple' => $this->multiple,
'checkAll' => $this->grid->showHeader ? $this->getHeaderCheckBoxName() : null,
]);
$this->grid->getView()->registerJs("jQuery('#$id').yiiGridView('setSelectionColumn', $options);");
}
}

13
framework/grid/Column.php

@ -124,7 +124,18 @@ class Column extends Object
*/
protected function renderHeaderCellContent()
{
return trim($this->header) !== '' ? $this->header : $this->grid->emptyCell;
return trim($this->header) !== '' ? $this->header : $this->getHeaderCellLabel();
}
/**
* Returns header cell label.
* This method may be overridden to customize the label of the header cell.
* @return string label
* @since 2.0.8
*/
protected function getHeaderCellLabel()
{
return $this->grid->emptyCell;
}
/**

26
framework/grid/DataColumn.php

@ -117,6 +117,25 @@ class DataColumn extends Column
return parent::renderHeaderCellContent();
}
$label = $this->getHeaderCellLabel();
if ($this->encodeLabel) {
$label = Html::encode($label);
}
if ($this->attribute !== null && $this->enableSorting &&
($sort = $this->grid->dataProvider->getSort()) !== false && $sort->hasAttribute($this->attribute)) {
return $sort->link($this->attribute, array_merge($this->sortLinkOptions, ['label' => $label]));
} else {
return $label;
}
}
/**
* @inheritdoc
* @since 2.0.8
*/
protected function getHeaderCellLabel()
{
$provider = $this->grid->dataProvider;
if ($this->label === null) {
@ -137,12 +156,7 @@ class DataColumn extends Column
$label = $this->label;
}
if ($this->attribute !== null && $this->enableSorting &&
($sort = $provider->getSort()) !== false && $sort->hasAttribute($this->attribute)) {
return $sort->link($this->attribute, array_merge($this->sortLinkOptions, ['label' => $this->encodeLabel ? Html::encode($label) : $label]));
} else {
return $this->encodeLabel ? Html::encode($label) : $label;
}
return $label;
}
/**

2
framework/helpers/BaseArrayHelper.php

@ -93,7 +93,7 @@ class BaseArrayHelper
}
}
return $recursive ? static::toArray($result) : $result;
return $recursive ? static::toArray($result, $properties) : $result;
} else {
return [$object];
}

8
framework/helpers/BaseConsole.php

@ -7,7 +7,7 @@
namespace yii\helpers;
use yii\console\Markdown;
use yii\console\Markdown as ConsoleMarkdown;
/**
* BaseConsole provides concrete implementation for [[Console]].
@ -455,12 +455,12 @@ class BaseConsole
/**
* Converts Markdown to be better readable in console environments by applying some ANSI format
* @param string $markdown
* @return string
* @param string $markdown the markdown string.
* @return string the parsed result as ANSI formatted string.
*/
public static function markdownToAnsi($markdown)
{
$parser = new Markdown();
$parser = new ConsoleMarkdown();
return $parser->parse($markdown);
}

3
framework/helpers/BaseFileHelper.php

@ -253,6 +253,9 @@ class BaseFileHelper
*/
public static function copyDirectory($src, $dst, $options = [])
{
if ($src === $dst || strpos($dst, $src) === 0) {
throw new InvalidParamException('Trying to copy a directory to itself or a subdirectory.');
}
if (!is_dir($dst)) {
static::createDirectory($dst, isset($options['dirMode']) ? $options['dirMode'] : 0775, true);
}

8
framework/helpers/BaseMarkdown.php

@ -93,13 +93,7 @@ class BaseMarkdown
if (!isset(static::$flavors[$flavor])) {
throw new InvalidParamException("Markdown flavor '$flavor' is not defined.'");
} elseif (!is_object($config = static::$flavors[$flavor])) {
$parser = Yii::createObject($config);
if (is_array($config)) {
foreach ($config as $name => $value) {
$parser->{$name} = $value;
}
}
static::$flavors[$flavor] = $parser;
static::$flavors[$flavor] = Yii::createObject($config);
}
return static::$flavors[$flavor];

9
framework/log/Dispatcher.php

@ -52,7 +52,8 @@ use yii\base\ErrorHandler;
*
* @property integer $flushInterval How many messages should be logged before they are sent to targets. This
* method returns the value of [[Logger::flushInterval]].
* @property Logger $logger The logger. If not set, [[\Yii::getLogger()]] will be used.
* @property Logger $logger The logger. If not set, [[\Yii::getLogger()]] will be used. Note that the type of
* this property differs in getter and setter. See [[getLogger()]] and [[setLogger()]] for details.
* @property integer $traceLevel How many application call stacks should be logged together with each message.
* This method returns the value of [[Logger::traceLevel]]. Defaults to 0.
*
@ -119,10 +120,14 @@ class Dispatcher extends Component
/**
* Sets the connected logger.
* @param Logger $value the logger.
* @param Logger|string|array $value the logger to be used. This can either be a logger instance
* or a configuration that will be used to create one using [[Yii::createObject()]].
*/
public function setLogger($value)
{
if (is_string($value) || is_array($value)) {
$value = Yii::createObject($value);
}
$this->_logger = $value;
$this->_logger->dispatcher = $this;
}

3
framework/messages/de/yii.php

@ -2,7 +2,7 @@
/**
* Message translations.
*
* This file is automatically generated by 'yii message' command.
* This file is automatically generated by 'yii message/extract' command.
* It contains the localizable messages extracted from source code.
* You may modify this file by translating the extracted messages.
*
@ -17,6 +17,7 @@
* NOTE: this file must be saved in UTF-8 encoding.
*/
return [
'Unknown alias: -{name}' => 'Unbekannter Alias: -{name}',
'(not set)' => '(nicht gesetzt)',
'An internal server error occurred.' => 'Es ist ein interner Serverfehler aufgetreten.',
'Are you sure you want to delete this item?' => 'Wollen Sie diesen Eintrag wirklich löschen?',

12
framework/messages/fa/yii.php

@ -95,12 +95,12 @@ return [
'{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} حداقل باید شامل {min, number} کارکتر باشد.',
'{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} حداکثر باید شامل {max, number} کارکتر باشد.',
'{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} باید شامل {length, number} کارکتر باشد.',
'{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 روز} other{# چندین روز}}',
'{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 ساعت} other{# ساعات}}',
'{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 دقیقه} other{# دقایق}}',
'{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, =1{1 ماه} other{# ماه ها}}',
'{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, =1{1 ثانیه} other{# ثانیه ها}}',
'{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, =1{1 سال} other{# سالیان}}',
'{delta, plural, =1{1 day} other{# days}}' => '{delta} روز',
'{delta, plural, =1{1 hour} other{# hours}}' => '{delta} ساعت',
'{delta, plural, =1{1 minute} other{# minutes}}' => '{delta} دقیقه',
'{delta, plural, =1{1 month} other{# months}}' => '{delta} ماه',
'{delta, plural, =1{1 second} other{# seconds}}' => '{delta} ثانیه',
'{delta, plural, =1{1 year} other{# years}}' => '{delta} سال',
'{delta, plural, =1{a day} other{# days}} ago' => '{delta} روز قبل',
'{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta} دقیقه قبل',
'{delta, plural, =1{a month} other{# months}} ago' => '{delta} ماه قبل',

1
framework/rbac/DbManager.php

@ -678,6 +678,7 @@ class DbManager extends BaseManager
/**
* @inheritdoc
* @since 2.0.8
*/
public function canAddChild($parent, $child)
{

1
framework/rbac/PhpManager.php

@ -150,6 +150,7 @@ class PhpManager extends BaseManager
/**
* @inheritdoc
* @since 2.0.8
*/
public function canAddChild($parent, $child)
{

61
framework/validators/DateValidator.php

@ -31,6 +31,41 @@ use yii\helpers\FormatConverter;
class DateValidator extends Validator
{
/**
* Constant for specifying the validation [[type]] as a date value, used for validation with intl short format.
* @since 2.0.8
* @see type
*/
const TYPE_DATE = 'date';
/**
* Constant for specifying the validation [[type]] as a datetime value, used for validation with intl short format.
* @since 2.0.8
* @see type
*/
const TYPE_DATETIME = 'datetime';
/**
* Constant for specifying the validation [[type]] as a time value, used for validation with intl short format.
* @since 2.0.8
* @see type
*/
const TYPE_TIME = 'time';
/**
* @var string the type of the validator. Indicates, whether a date, time or datetime value should be validated.
* This property influences the default value of [[format]] and also sets the correct behavior when [[format]] is one of the intl
* short formats, `short`, `medium`, `long`, or `full`.
*
* This is only effective when the [PHP intl extension](http://php.net/manual/en/book.intl.php) is installed.
*
* This property can be set to the following values:
*
* - [[TYPE_DATE]] - (default) for validating date values only, that means only values that do not include a time range are valid.
* - [[TYPE_DATETIME]] - for validating datetime values, that contain a date part as well as a time part.
* - [[TYPE_TIME]] - for validating time values, that contain no date information.
*
* @since 2.0.8
*/
public $type = self::TYPE_DATE;
/**
* @var string the date format that the value being validated should follow.
* This can be a date time pattern as described in the [ICU manual](http://userguide.icu-project.org/formatparse/datetime#TOC-Date-Time-Format-Syntax).
*
@ -38,6 +73,12 @@ class DateValidator extends Validator
* Please refer to <http://php.net/manual/en/datetime.createfromformat.php> on supported formats.
*
* If this property is not set, the default value will be obtained from `Yii::$app->formatter->dateFormat`, see [[\yii\i18n\Formatter::dateFormat]] for details.
* Since version 2.0.8 the default value will be determined from different formats of the formatter class,
* dependent on the value of [[type]]:
*
* - if type is [[TYPE_DATE]], the default value will be taken from [[\yii\i18n\Formatter::dateFormat]],
* - if type is [[TYPE_DATETIME]], it will be taken from [[\yii\i18n\Formatter::datetimeFormat]],
* - and if type is [[TYPE_TIME]], it will be [[\yii\i18n\Formatter::timeFormat]].
*
* Here are some example values:
*
@ -165,7 +206,15 @@ class DateValidator extends Validator
$this->message = Yii::t('yii', 'The format of {attribute} is invalid.');
}
if ($this->format === null) {
$this->format = Yii::$app->formatter->dateFormat;
if ($this->type === self::TYPE_DATE) {
$this->format = Yii::$app->formatter->dateFormat;
} elseif ($this->type === self::TYPE_DATETIME) {
$this->format = Yii::$app->formatter->datetimeFormat;
} elseif ($this->type === self::TYPE_TIME) {
$this->format = Yii::$app->formatter->timeFormat;
} else {
throw new InvalidConfigException('Unknown validation type set for DateValidator::$type: ' . $this->type);
}
}
if ($this->locale === null) {
$this->locale = Yii::$app->language;
@ -297,7 +346,15 @@ class DateValidator extends Validator
private function parseDateValueIntl($value, $format)
{
if (isset($this->_dateFormats[$format])) {
$formatter = new IntlDateFormatter($this->locale, $this->_dateFormats[$format], IntlDateFormatter::NONE, 'UTC');
if ($this->type === self::TYPE_DATE) {
$formatter = new IntlDateFormatter($this->locale, $this->_dateFormats[$format], IntlDateFormatter::NONE, 'UTC');
} elseif ($this->type === self::TYPE_DATETIME) {
$formatter = new IntlDateFormatter($this->locale, $this->_dateFormats[$format], $this->_dateFormats[$format], $this->timeZone);
} elseif ($this->type === self::TYPE_TIME) {
$formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, $this->_dateFormats[$format], $this->timeZone);
} else {
throw new InvalidConfigException('Unknown validation type set for DateValidator::$type: ' . $this->type);
}
} else {
// if no time was provided in the format string set time to 0 to get a simple date timestamp
$hasTimeInfo = (strpbrk($format, 'ahHkKmsSA') !== false);

67
framework/views/errorHandler/exception.php

File diff suppressed because one or more lines are too long

8
framework/web/AssetBundle.php

@ -153,7 +153,9 @@ class AssetBundle extends Object
$options = ArrayHelper::merge($this->jsOptions, $js);
$view->registerJsFile($manager->getAssetUrl($this, $file), $options);
} else {
$view->registerJsFile($manager->getAssetUrl($this, $js), $this->jsOptions);
if ($js !== null) {
$view->registerJsFile($manager->getAssetUrl($this, $js), $this->jsOptions);
}
}
}
foreach ($this->css as $css) {
@ -162,7 +164,9 @@ class AssetBundle extends Object
$options = ArrayHelper::merge($this->cssOptions, $css);
$view->registerCssFile($manager->getAssetUrl($this, $file), $options);
} else {
$view->registerCssFile($manager->getAssetUrl($this, $css), $this->cssOptions);
if ($css !== null) {
$view->registerCssFile($manager->getAssetUrl($this, $css), $this->cssOptions);
}
}
}
}

2
framework/web/ErrorHandler.php

@ -134,7 +134,7 @@ class ErrorHandler extends \yii\base\ErrorHandler
protected function convertExceptionToArray($exception)
{
if (!YII_DEBUG && !$exception instanceof UserException && !$exception instanceof HttpException) {
$exception = new HttpException(500, Yii::t('yii', 'There was an error at the server.'));
$exception = new HttpException(500, Yii::t('yii', 'An internal server error occurred.'));
}
$array = [

3
framework/web/JsonParser.php

@ -48,7 +48,8 @@ class JsonParser implements RequestParserInterface
public function parse($rawBody, $contentType)
{
try {
return Json::decode($rawBody, $this->asArray);
$parameters = Json::decode($rawBody, $this->asArray);
return $parameters === null ? [] : $parameters;
} catch (InvalidParamException $e) {
if ($this->throwException) {
throw new BadRequestHttpException('Invalid JSON data in request body: ' . $e->getMessage());

25
framework/web/Request.php

@ -27,10 +27,10 @@ use yii\helpers\StringHelper;
* corresponding quality score and other parameters as given in the header.
* @property array $acceptableLanguages The languages ordered by the preference level. The first element
* represents the most preferred language.
* @property string $authPassword The password sent via HTTP authentication, null if the password is not
* @property string|null $authPassword The password sent via HTTP authentication, null if the password is not
* given. This property is read-only.
* @property string|null $authUser The username sent via HTTP authentication, null if the username is not
* given. This property is read-only.
* @property string $authUser The username sent via HTTP authentication, null if the username is not given.
* This property is read-only.
* @property string $baseUrl The relative URL for the application.
* @property array $bodyParams The request parameters given in the request body.
* @property string $contentType Request content-type. Null is returned if this information is not available.
@ -42,7 +42,7 @@ use yii\helpers\StringHelper;
* @property array $eTags The entity tags. This property is read-only.
* @property HeaderCollection $headers The header collection. This property is read-only.
* @property string $hostInfo Schema and hostname part (with port number if needed) of the request URL (e.g.
* `http://www.yiiframework.com`), null in case it can't be obtained from `$_SERVER` and wasn't set.
* `http://www.yiiframework.com`), null if can't be obtained from `$_SERVER` and wasn't set.
* @property boolean $isAjax Whether this is an AJAX (XMLHttpRequest) request. This property is read-only.
* @property boolean $isDelete Whether this is a DELETE request. This property is read-only.
* @property boolean $isFlash Whether this is an Adobe Flash or Adobe Flex request. This property is
@ -65,17 +65,16 @@ use yii\helpers\StringHelper;
* @property string $queryString Part of the request URL that is after the question mark. This property is
* read-only.
* @property string $rawBody The request body.
* @property string $referrer URL referrer, null if not present. This property is read-only.
* @property string|null $referrer URL referrer, null if not available. This property is read-only.
* @property string $scriptFile The entry script file path.
* @property string $scriptUrl The relative URL of the entry script.
* @property integer $securePort Port number for secure requests.
* @property string $serverName Server name, null if not available. This property is read-only.
* @property integer $serverPort Server port number, null if not available. This property is read-only.
* @property integer|null $serverPort Server port number, null if not available. This property is read-only.
* @property string $url The currently requested relative URL. Note that the URI returned is URL-encoded.
* @property string $userAgent User agent, null if not present. This property is read-only.
* @property string $userHost User host name, null if cannot be determined. This property is read-only.
* @property string $userIP User IP address. Null is returned if the user IP address cannot be detected. This
* property is read-only.
* @property string|null $userAgent User agent, null if not available. This property is read-only.
* @property string|null $userHost User host name, null if not available. This property is read-only.
* @property string|null $userIP User IP address, null if not available. This property is read-only.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
@ -557,7 +556,7 @@ class Request extends \yii\base\Request
*/
public function setHostInfo($value)
{
$this->_hostInfo = rtrim($value, '/');
$this->_hostInfo = $value === null ? null : rtrim($value, '/');
}
private $_baseUrl;
@ -628,7 +627,7 @@ class Request extends \yii\base\Request
*/
public function setScriptUrl($value)
{
$this->_scriptUrl = '/' . trim($value, '/');
$this->_scriptUrl = $value === null ? null : '/' . trim($value, '/');
}
private $_scriptFile;
@ -688,7 +687,7 @@ class Request extends \yii\base\Request
*/
public function setPathInfo($value)
{
$this->_pathInfo = ltrim($value, '/');
$this->_pathInfo = $value === null ? null : ltrim($value, '/');
}
/**

62
framework/web/UrlManager.php

@ -126,6 +126,12 @@ class UrlManager extends Component
*/
public $ruleConfig = ['class' => 'yii\web\UrlRule'];
/**
* @var string the cache key for cached rules
* @since 2.0.8
*/
protected $cacheKey = __CLASS__;
private $_baseUrl;
private $_scriptUrl;
private $_hostInfo;
@ -146,7 +152,7 @@ class UrlManager extends Component
$this->cache = Yii::$app->get($this->cache, false);
}
if ($this->cache instanceof Cache) {
$cacheKey = __CLASS__;
$cacheKey = $this->cacheKey;
$hash = md5(json_encode($this->rules));
if (($data = $this->cache->get($cacheKey)) !== false && isset($data[1]) && $data[1] === $hash) {
$this->rules = $data[0];
@ -322,28 +328,19 @@ class UrlManager extends Component
}
}
/* @var $rule UrlRule */
$url = false;
if (isset($this->_ruleCache[$cacheKey])) {
foreach ($this->_ruleCache[$cacheKey] as $rule) {
if (($url = $rule->createUrl($this, $route, $params)) !== false) {
break;
}
}
} else {
$this->_ruleCache[$cacheKey] = [];
}
$url = $this->getUrlFromCache($cacheKey, $route, $params);
if ($url === false) {
$cacheable = true;
foreach ($this->rules as $rule) {
/* @var $rule UrlRule */
if (!empty($rule->defaults) && $rule->mode !== UrlRule::PARSING_ONLY) {
// if there is a rule with default values involved, the matching result may not be cached
$cacheable = false;
}
if (($url = $rule->createUrl($this, $route, $params)) !== false) {
if ($cacheable) {
$this->_ruleCache[$cacheKey][] = $rule;
$this->setRuleToCache($cacheKey, $rule);
}
break;
}
@ -381,6 +378,41 @@ class UrlManager extends Component
}
/**
* Get URL from internal cache if exists
* @param string $cacheKey generated cache key to store data.
* @param string $route the route (e.g. `site/index`).
* @param array $params rule params.
* @return boolean|string the created URL
* @see createUrl()
* @since 2.0.8
*/
protected function getUrlFromCache($cacheKey, $route, $params)
{
if (!empty($this->_ruleCache[$cacheKey])) {
foreach ($this->_ruleCache[$cacheKey] as $rule) {
/* @var $rule UrlRule */
if (($url = $rule->createUrl($this, $route, $params)) !== false) {
return $url;
}
}
} else {
$this->_ruleCache[$cacheKey] = [];
}
return false;
}
/**
* Store rule (e.g. [[UrlRule]]) to internal cache
* @param $cacheKey
* @param UrlRuleInterface $rule
* @since 2.0.8
*/
protected function setRuleToCache($cacheKey, UrlRuleInterface $rule)
{
$this->_ruleCache[$cacheKey][] = $rule;
}
/**
* Creates an absolute URL using the given route and query parameters.
*
* This method prepends the URL created by [[createUrl()]] with the [[hostInfo]].
@ -437,7 +469,7 @@ class UrlManager extends Component
*/
public function setBaseUrl($value)
{
$this->_baseUrl = rtrim($value, '/');
$this->_baseUrl = $value === null ? null : rtrim($value, '/');
}
/**
@ -496,6 +528,6 @@ class UrlManager extends Component
*/
public function setHostInfo($value)
{
$this->_hostInfo = rtrim($value, '/');
$this->_hostInfo = $value === null ? null : rtrim($value, '/');
}
}

1
framework/web/User.php

@ -431,6 +431,7 @@ class User extends Component
$request = Yii::$app->getRequest();
$canRedirect = !$checkAcceptHeader || $this->checkRedirectAcceptable();
if ($this->enableSession
&& $request->getIsGet()
&& (!$checkAjax || !$request->getIsAjax())
&& $canRedirect
) {

3
phpunit.xml.dist

@ -11,6 +11,9 @@
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">framework/</directory>
</whitelist>
<blacklist>
<file>framework/helpers/Json.php</file>
<file>framework/helpers/StringHelper.php</file>

16
tests/data/ar/Order.php

@ -12,9 +12,11 @@ namespace yiiunit\data\ar;
*/
class Order extends ActiveRecord
{
public static $tableName;
public static function tableName()
{
return 'order';
return static::$tableName ?: 'order';
}
public function getCustomer()
@ -22,6 +24,18 @@ class Order extends ActiveRecord
return $this->hasOne(Customer::className(), ['id' => 'customer_id']);
}
public function getCustomerJoinedWithProfile()
{
return $this->hasOne(Customer::className(), ['id' => 'customer_id'])
->joinWith('profile');
}
public function getCustomerJoinedWithProfileIndexOrdered()
{
return $this->hasMany(Customer::className(), ['id' => 'customer_id'])
->joinWith('profile')->orderBy(['profile.description' => SORT_ASC])->indexBy('name');
}
public function getCustomer2()
{
return $this->hasOne(Customer::className(), ['id' => 'customer_id'])->inverseOf('orders2');

15
tests/data/ar/OrderItem.php

@ -12,9 +12,11 @@ namespace yiiunit\data\ar;
*/
class OrderItem extends ActiveRecord
{
public static $tableName;
public static function tableName()
{
return 'order_item';
return static::$tableName ?: 'order_item';
}
public function getOrder()
@ -26,4 +28,15 @@ class OrderItem extends ActiveRecord
{
return $this->hasOne(Item::className(), ['id' => 'item_id']);
}
// relations used by ::testFindCompositeWithJoin()
public function getOrderItemCompositeWithJoin()
{
return $this->hasOne(OrderItem::className(), ['item_id' => 'item_id', 'order_id' => 'order_id' ])
->joinWith('item');
}
public function getOrderItemCompositeNoJoin()
{
return $this->hasOne(OrderItem::className(), ['item_id' => 'item_id', 'order_id' => 'order_id' ]);
}
}

75
tests/data/base/TraversableObject.php

@ -0,0 +1,75 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yiiunit\data\base;
/**
* TraversableObject
* Object that implements `\Traversable` and `\Countable`, but counting throws an exception;
* Used for testing support for traversable objects instead of arrays.
* @author Sam Mousa <sam@mousa.nl>
* @since 2.0.8
*/
class TraversableObject implements \Iterator, \Countable
{
private $data;
private $position = 0;
public function __construct(array $array)
{
$this->data = $array;
}
/**
* @throws \Exception
* @since 5.1.0
*/
public function count()
{
throw new \Exception('Count called on object that should only be traversed.');
}
/**
* @inheritdoc
*/
public function current()
{
return $this->data[$this->position];
}
/**
* @inheritdoc
*/
public function next()
{
$this->position++;
}
/**
* @inheritdoc
*/
public function key()
{
return $this->position;
}
/**
* @inheritdoc
*/
public function valid()
{
return array_key_exists($this->position, $this->data);
}
/**
* @inheritdoc
*/
public function rewind()
{
$this->position = 0;
}
}

9
tests/data/mysql.sql

@ -18,6 +18,7 @@ DROP TABLE IF EXISTS `constraints` CASCADE;
DROP TABLE IF EXISTS `animal` CASCADE;
DROP TABLE IF EXISTS `default_pk` CASCADE;
DROP TABLE IF EXISTS `document` CASCADE;
DROP TABLE IF EXISTS `comment` CASCADE;
DROP VIEW IF EXISTS `animal_view`;
CREATE TABLE `constraints`
@ -150,6 +151,14 @@ CREATE TABLE `document` (
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `comment` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`add_comment` VARCHAR(255) NOT NULL,
`replace_comment` VARCHAR(255) COMMENT 'comment',
`delete_comment` VARCHAR(128) NOT NULL COMMENT 'comment',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE VIEW `animal_view` AS SELECT * FROM `animal`;
INSERT INTO `animal` (`type`) VALUES ('yiiunit\data\ar\Cat');

7
tests/data/postgres.sql

@ -20,6 +20,7 @@ DROP TABLE IF EXISTS "bool_values" CASCADE;
DROP TABLE IF EXISTS "animal" CASCADE;
DROP TABLE IF EXISTS "default_pk" CASCADE;
DROP TABLE IF EXISTS "document" CASCADE;
DROP TABLE IF EXISTS "comment" CASCADE;
DROP VIEW IF EXISTS "animal_view";
DROP SCHEMA IF EXISTS "schema1" CASCADE;
DROP SCHEMA IF EXISTS "schema2" CASCADE;
@ -154,6 +155,12 @@ CREATE TABLE "document" (
version integer not null default 0
);
CREATE TABLE "comment" (
id serial primary key,
name varchar(255) not null,
message text not null
);
CREATE VIEW "animal_view" AS SELECT * FROM "animal";
INSERT INTO "animal" (type) VALUES ('yiiunit\data\ar\Cat');

24
tests/framework/BaseYiiTest.php

@ -2,6 +2,8 @@
namespace yiiunit\framework;
use Yii;
use yii\di\Container;
use yiiunit\data\base\Singer;
use yiiunit\TestCase;
/**
@ -60,4 +62,26 @@ class BaseYiiTest extends TestCase
{
$this->assertTrue(is_string(Yii::powered()));
}
public function testCreateObjectCallable()
{
Yii::$container = new Container();
// Test passing in of normal params combined with DI params.
$this->assertTrue(Yii::createObject(function(Singer $singer, $a) {
return $a === 'a';
}, ['a']));
$singer = new Singer();
$singer->firstName = 'Bob';
$this->assertTrue(Yii::createObject(function(Singer $singer, $a) {
return $singer->firstName === 'Bob';
}, [$singer, 'a']));
$this->assertTrue(Yii::createObject(function(Singer $singer, $a = 3) {
return true;
}));
}
}

21
tests/framework/ar/ActiveRecordTestTrait.php

@ -1126,6 +1126,27 @@ trait ActiveRecordTestTrait
Event::off(BaseActiveRecord::className(), BaseActiveRecord::EVENT_AFTER_FIND);
}
public function testAfterRefresh()
{
/* @var $customerClass \yii\db\ActiveRecordInterface */
$customerClass = $this->getCustomerClass();
/* @var $this TestCase|ActiveRecordTestTrait */
$afterRefreshCalls = [];
Event::on(BaseActiveRecord::className(), BaseActiveRecord::EVENT_AFTER_REFRESH, function ($event) use (&$afterRefreshCalls) {
/* @var $ar BaseActiveRecord */
$ar = $event->sender;
$afterRefreshCalls[] = [get_class($ar), $ar->getIsNewRecord(), $ar->getPrimaryKey(), $ar->isRelationPopulated('orders')];
});
$customer = $customerClass::findOne(1);
$this->assertNotNull($customer);
$customer->refresh();
$this->assertEquals([[$customerClass, false, 1, false]], $afterRefreshCalls);
$afterRefreshCalls = [];
Event::off(BaseActiveRecord::className(), BaseActiveRecord::EVENT_AFTER_REFRESH);
}
public function testFindEmptyInCondition()
{
/* @var $customerClass \yii\db\ActiveRecordInterface */

192
tests/framework/base/SecurityTest.php

@ -5,7 +5,46 @@
* @license http://www.yiiframework.com/license/
*/
namespace yiiunit\framework\base;
namespace yii\base {
/**
* emulate availability of functions, to test different branches of Security class
* where different execution paths are chosen based on calling function_exists.
*
* This function overrides function_exists from the root namespace in yii\base.
*/
function function_exists($name) {
if (isset(\yiiunit\framework\base\SecurityTest::$functions[$name])) {
return \yiiunit\framework\base\SecurityTest::$functions[$name];
}
return \function_exists($name);
}
/**
* emulate chunked reading of fread(), to test different branches of Security class
* where different execution paths are chosen based on the return value of fopen/fread
*
* This function overrides fopen and fread from the root namespace in yii\base.
*/
function fopen($filename, $mode) {
if (\yiiunit\framework\base\SecurityTest::$fopen !== null) {
return \yiiunit\framework\base\SecurityTest::$fopen;
}
return \fopen($filename, $mode);
}
function fread($handle, $length) {
if (\yiiunit\framework\base\SecurityTest::$fread !== null) {
return \yiiunit\framework\base\SecurityTest::$fread;
}
if (\yiiunit\framework\base\SecurityTest::$fopen !== null) {
return $length < 8 ? \str_repeat('s', $length) : 'test1234';
}
return \fread($handle, $length);
}
} // closing namespace yii\base;
namespace yiiunit\framework\base {
use yii\base\Security;
use yiiunit\TestCase;
@ -18,17 +57,38 @@ class SecurityTest extends TestCase
const CRYPT_VECTORS = 'old';
/**
* @var array set of functions for which a fake return value for `function_exists()` is provided.
*/
public static $functions = [];
/**
* @var resource|false|null fake return value for fopen() in \yii\base namespace. Normal behavior if this is null.
*/
public static $fopen;
public static $fread;
/**
* @var ExposedSecurity
*/
protected $security;
protected function setUp()
{
static::$functions = [];
static::$fopen = null;
static::$fread = null;
parent::setUp();
$this->security = new ExposedSecurity();
$this->security->derivationIterations = 1000; // speed up test running
}
protected function tearDown()
{
static::$functions = [];
static::$fopen = null;
static::$fread = null;
parent::tearDown();
}
// Tests :
public function testHashData()
@ -799,9 +859,99 @@ TEXT;
$this->assertEquals($data, $this->security->decryptByPassword($encrypted, $password));
}
public function testGenerateRandomKey()
public function randomKeyInvalidInputs()
{
$length = 21;
return [
[ 0 ],
[ -1 ],
[ '0' ],
[ '34' ],
[ [] ],
];
}
/**
* @dataProvider randomKeyInvalidInputs
* @expectedException \yii\base\InvalidParamException
*/
public function testRandomKeyInvalidInput($input)
{
$key1 = $this->security->generateRandomKey($input);
}
/**
* Test the case where opening /dev/urandom fails
*/
public function testRandomKeyNoOptions()
{
static::$functions = ['random_bytes' => false, 'openssl_random_pseudo_bytes' => false, 'mcrypt_create_iv' => false ];
static::$fopen = false;
$this->setExpectedException('yii\base\Exception', 'Unable to generate a random key');
$this->security->generateRandomKey(42);
}
/**
* Test the case where reading from /dev/urandom fails
*/
public function testRandomKeyFreadFailure()
{
static::$functions = ['random_bytes' => false, 'openssl_random_pseudo_bytes' => false, 'mcrypt_create_iv' => false ];
static::$fread = false;
$this->setExpectedException('yii\base\Exception', 'Unable to generate a random key');
$this->security->generateRandomKey(42);
}
/**
* returns a set of different combinations of functions available.
*/
public function randomKeyVariants()
{
return [
[ ['random_bytes' => true, 'openssl_random_pseudo_bytes' => true, 'mcrypt_create_iv' => true ] ],
[ ['random_bytes' => true, 'openssl_random_pseudo_bytes' => true, 'mcrypt_create_iv' => false ] ],
[ ['random_bytes' => true, 'openssl_random_pseudo_bytes' => false, 'mcrypt_create_iv' => true ] ],
[ ['random_bytes' => true, 'openssl_random_pseudo_bytes' => false, 'mcrypt_create_iv' => false ] ],
[ ['random_bytes' => false, 'openssl_random_pseudo_bytes' => true, 'mcrypt_create_iv' => true ] ],
[ ['random_bytes' => false, 'openssl_random_pseudo_bytes' => true, 'mcrypt_create_iv' => false ] ],
[ ['random_bytes' => false, 'openssl_random_pseudo_bytes' => false, 'mcrypt_create_iv' => true ] ],
[ ['random_bytes' => false, 'openssl_random_pseudo_bytes' => false, 'mcrypt_create_iv' => false ] ],
];
}
/**
* @dataProvider randomKeyVariants
*/
public function testGenerateRandomKey($functions)
{
foreach ($functions as $fun => $available) {
if ($available && !\function_exists($fun)) {
$this->markTestSkipped("Can not test generateRandomKey() branch that includes $fun, because it is not available on your system.");
}
}
// there is no /dev/urandom on windows so we expect this to fail
if (DIRECTORY_SEPARATOR === '\\' && $functions['random_bytes'] === false && $functions['openssl_random_pseudo_bytes'] === false && $functions['mcrypt_create_iv'] === false ) {
$this->setExpectedException('yii\base\Exception', 'Unable to generate a random key');
}
static::$functions = $functions;
// test various string lengths
for ($length = 1; $length < 64; $length++) {
$key1 = $this->security->generateRandomKey($length);
$this->assertInternalType('string', $key1);
$this->assertEquals($length, strlen($key1));
$key2 = $this->security->generateRandomKey($length);
$this->assertInternalType('string', $key2);
$this->assertEquals($length, strlen($key2));
if ($length >= 7) { // avoid random test failure, short strings are likely to collide
$this->assertTrue($key1 != $key2);
}
}
// test for /dev/urandom, reading larger data to see if loop works properly
$length = 1024 * 1024;
$key1 = $this->security->generateRandomKey($length);
$this->assertInternalType('string', $key1);
$this->assertEquals($length, strlen($key1));
@ -809,6 +959,16 @@ TEXT;
$this->assertInternalType('string', $key2);
$this->assertEquals($length, strlen($key2));
$this->assertTrue($key1 != $key2);
// force /dev/urandom reading loop to deal with chunked data
// the above test may have read everything in one run.
// not sure if this can happen in real life but if it does
// we should be prepared
static::$fopen = fopen('php://memory', 'rwb');
$length = 1024 * 1024;
$key1 = $this->security->generateRandomKey($length);
$this->assertInternalType('string', $key1);
$this->assertEquals($length, strlen($key1));
}
protected function randTime(Security $security, $count, $length, $message)
@ -860,29 +1020,6 @@ TEXT;
$this->randTime($security, 10000, 5000, 'Rate test');
}
public function testGenerateRandomKeyURandom()
{
if (function_exists('random_bytes')) {
$this->markTestSkipped('This test can only work on platforms where random_bytes() function does not exist. You may disable it in php.ini for testing. http://php.net/manual/en/ini.core.php#ini.disable-functions');
}
if (PHP_VERSION_ID >= 50307 && function_exists('mcrypt_create_iv')) {
$this->markTestSkipped('This test can only work on platforms where mcrypt_create_iv() function does not exist. You may disable it in php.ini for testing. http://php.net/manual/en/ini.core.php#ini.disable-functions');
}
if (!@is_readable('/dev/urandom')) {
$this->markTestSkipped('/dev/urandom does not seem to exist on your system.');
}
$length = 1024 * 1024;
$key1 = $this->security->generateRandomKey($length);
$this->assertInternalType('string', $key1);
$this->assertEquals($length, strlen($key1));
$key2 = $this->security->generateRandomKey($length);
$this->assertInternalType('string', $key2);
$this->assertEquals($length, strlen($key2));
$this->assertTrue($key1 != $key2);
}
public function testGenerateRandomString()
{
$length = 21;
@ -1110,3 +1247,6 @@ TEXT;
$this->assertEquals(strcmp($expected, $actual) === 0, $this->security->compareString($expected, $actual));
}
}
} // closing namespace yiiunit\framework\base;

36
tests/framework/console/ControllerTest.php

@ -8,6 +8,7 @@
namespace yiiunit\framework\console;
use Yii;
use yii\console\Request;
use yiiunit\TestCase;
/**
@ -53,4 +54,39 @@ class ControllerTest extends TestCase
$this->setExpectedException('yii\console\Exception', $message);
$result = $controller->runAction('aksi3', $params);
}
public function assertResponseStatus($status, $response)
{
$this->assertInstanceOf('yii\console\Response', $response);
$this->assertSame($status, $response->exitStatus);
}
public function runRequest($route, $args = 0)
{
$request = new Request();
$request->setParams(func_get_args());
return Yii::$app->handleRequest($request);
}
public function testResponse()
{
$this->mockApplication();
Yii::$app->controllerMap = [
'fake' => 'yiiunit\framework\console\FakeController',
];
$status = 123;
$response = $this->runRequest('fake/status');
$this->assertResponseStatus(0, $response);
$response = $this->runRequest('fake/status', (string)$status);
$this->assertResponseStatus($status, $response);
$response = $this->runRequest('fake/response');
$this->assertResponseStatus(0, $response);
$response = $this->runRequest('fake/response', (string)$status);
$this->assertResponseStatus($status, $response);
}
}

13
tests/framework/console/FakeController.php

@ -8,6 +8,7 @@
namespace yiiunit\framework\console;
use yii\console\Controller;
use yii\console\Response;
/**
* @author Misbahul D Munir <misbahuldmunir@gmail.com>
@ -67,4 +68,16 @@ class FakeController extends Controller
{
return $this->testArray;
}
public function actionStatus($status = 0)
{
return $status;
}
public function actionResponse($status = 0)
{
$response = new Response();
$response->exitStatus = (int)$status;
return $response;
}
}

76
tests/framework/db/ActiveRecordTest.php

@ -97,6 +97,14 @@ class ActiveRecordTest extends DatabaseTestCase
$this->assertEquals('user2', $customerName);
}
public function testFindExists()
{
$this->assertTrue(Customer::find()->where(['[[id]]' => 2])->exists());
$this->assertFalse(Customer::find()->where(['[[id]]' => 42])->exists());
$this->assertTrue(Customer::find()->where(['[[id]]' => 2])->select('[[name]]')->exists());
$this->assertFalse(Customer::find()->where(['[[id]]' => 42])->select('[[name]]')->exists());
}
public function testFindColumn()
{
/* @var $this TestCase|ActiveRecordTestTrait */
@ -815,6 +823,74 @@ class ActiveRecordTest extends DatabaseTestCase
$this->assertEquals(0, count($orders[0]->itemsIndexed));
}
/**
* https://github.com/yiisoft/yii2/issues/10201
* https://github.com/yiisoft/yii2/issues/9047
*/
public function testFindCompositeRelationWithJoin()
{
/* @var $orderItem OrderItem */
$orderItem = OrderItem::findOne([1, 1]);
$orderItemNoJoin = $orderItem->orderItemCompositeNoJoin;
$this->assertInstanceOf('yiiunit\data\ar\OrderItem', $orderItemNoJoin);
$orderItemWithJoin = $orderItem->orderItemCompositeWithJoin;
$this->assertInstanceOf('yiiunit\data\ar\OrderItem', $orderItemWithJoin);
}
public function testFindSimpleRelationWithJoin()
{
/* @var $order Order */
$order = Order::findOne(1);
$customerNoJoin = $order->customer;
$this->assertInstanceOf('yiiunit\data\ar\Customer', $customerNoJoin);
$customerWithJoin = $order->customerJoinedWithProfile;
$this->assertInstanceOf('yiiunit\data\ar\Customer', $customerWithJoin);
$customerWithJoinIndexOrdered = $order->customerJoinedWithProfileIndexOrdered;
$this->assertTrue(is_array($customerWithJoinIndexOrdered));
$this->assertArrayHasKey('user1', $customerWithJoinIndexOrdered);
$this->assertInstanceOf('yiiunit\data\ar\Customer', $customerWithJoinIndexOrdered['user1']);
}
public function tableNameProvider()
{
return [
['order', 'order_item'],
['order', '{{%order_item}}'],
['{{%order}}', 'order_item'],
['{{%order}}', '{{%order_item}}'],
];
}
/**
* Test whether conditions are quoted correctly in conditions where joinWith is used.
* @see https://github.com/yiisoft/yii2/issues/11088
* @dataProvider tableNameProvider
*/
public function testRelationWhereParams($orderTableName, $orderItemTableName)
{
Order::$tableName = $orderTableName;
OrderItem::$tableName = $orderItemTableName;
/** @var $order Order */
$order = Order::findOne(1);
$itemsSQL = $order->getOrderitems()->createCommand()->rawSql;
$expectedSQL = $this->replaceQuotes("SELECT * FROM [[order_item]] WHERE [[order_id]]=1");
$this->assertEquals($expectedSQL, $itemsSQL);
$order = Order::findOne(1);
$itemsSQL = $order->getOrderItems()->joinWith('item')->createCommand()->rawSql;
$expectedSQL = $this->replaceQuotes("SELECT [[order_item]].* FROM [[order_item]] LEFT JOIN [[item]] ON [[order_item]].[[item_id]] = [[item]].[[id]] WHERE [[order_item]].[[order_id]]=1");
$this->assertEquals($expectedSQL, $itemsSQL);
Order::$tableName = null;
OrderItem::$tableName = null;
}
public function testAlias()
{
$query = Order::find();

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

Loading…
Cancel
Save