Browse Source

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

Conflicts:
	.travis.yml
	build/controllers/PhpDocController.php
	docs/guide-es/caching-data.md
	docs/guide-es/concept-events.md
	docs/guide-fr/concept-events.md
	docs/guide-ja/caching-data.md
	docs/guide-ja/concept-events.md
	docs/guide-pt-BR/caching-data.md
	docs/guide-pt-BR/concept-events.md
	docs/guide-ru/caching-data.md
	docs/guide-ru/concept-events.md
	docs/guide-zh-CN/caching-data.md
	docs/guide-zh-CN/concept-events.md
	docs/guide/caching-data.md
	docs/guide/concept-events.md
	docs/guide/runtime-logging.md
	framework/BaseYii.php
	framework/base/BaseObject.php
	framework/base/Security.php
	framework/caching/DbCache.php
	framework/caching/XCache.php
	framework/classes.php
	framework/db/ActiveQueryTrait.php
	framework/db/ActiveRelationTrait.php
	framework/db/BaseActiveRecord.php
	framework/db/Query.php
	framework/db/QueryBuilder.php
	framework/db/TableSchema.php
	framework/db/mssql/QueryBuilder.php
	framework/db/mysql/QueryBuilder.php
	framework/db/oci/QueryBuilder.php
	framework/db/pgsql/QueryBuilder.php
	framework/db/sqlite/QueryBuilder.php
	framework/filters/PageCache.php
	framework/grid/DataColumn.php
	framework/helpers/BaseFileHelper.php
	framework/helpers/BaseJson.php
	framework/helpers/BaseMarkdown.php
	framework/helpers/BaseUrl.php
	framework/i18n/Formatter.php
	framework/log/DbTarget.php
	framework/log/SyslogTarget.php
	framework/messages/sk/yii.php
	framework/mutex/PgsqlMutex.php
	framework/rbac/BaseManager.php
	framework/rbac/DbManager.php
	framework/rbac/PhpManager.php
	framework/web/Request.php
	framework/web/Response.php
	framework/web/Session.php
	framework/web/View.php
	framework/widgets/ActiveForm.php
	framework/widgets/FragmentCache.php
	tests/framework/behaviors/TimestampBehaviorTest.php
	tests/framework/db/CommandTest.php
	tests/framework/db/mysql/connection/DeadLockTest.php
	tests/framework/db/pgsql/CommandTest.php
	tests/framework/filters/auth/BasicAuthTest.php
	tests/framework/log/FileTargetTest.php
	tests/framework/log/SyslogTargetTest.php
	tests/framework/mail/BaseMailerTest.php
	tests/framework/rbac/ManagerTestCase.php
tags/3.0.0-alpha1
Klimov Paul 7 years ago
parent
commit
c2694e7edc
  1. 1
      .gitlab-ci.yml
  2. 10
      .travis.yml
  3. 3
      Dockerfile
  4. 4
      README.md
  5. 2
      build/controllers/ClassmapController.php
  6. 26
      build/controllers/DevController.php
  7. 105
      build/controllers/PhpDocController.php
  8. 40
      build/controllers/ReleaseController.php
  9. 18
      build/controllers/Utf8Controller.php
  10. 2
      docs/guide-es/concept-events.md
  11. 20
      docs/guide-es/db-migrations.md
  12. 10
      docs/guide-es/runtime-logging.md
  13. 2
      docs/guide-es/structure-filters.md
  14. 4
      docs/guide-fr/caching-data.md
  15. 2
      docs/guide-fr/concept-events.md
  16. 20
      docs/guide-fr/db-migrations.md
  17. 8
      docs/guide-fr/output-data-providers.md
  18. 6
      docs/guide-fr/output-data-widgets.md
  19. 10
      docs/guide-fr/runtime-logging.md
  20. 2
      docs/guide-fr/structure-filters.md
  21. 4
      docs/guide-ja/concept-events.md
  22. 20
      docs/guide-ja/db-migrations.md
  23. 8
      docs/guide-ja/output-data-providers.md
  24. 6
      docs/guide-ja/output-data-widgets.md
  25. 10
      docs/guide-ja/runtime-logging.md
  26. 2
      docs/guide-ja/structure-filters.md
  27. 2
      docs/guide-pt-BR/concept-events.md
  28. 8
      docs/guide-pt-BR/output-data-providers.md
  29. 10
      docs/guide-pt-BR/runtime-logging.md
  30. 2
      docs/guide-pt-BR/structure-filters.md
  31. 2
      docs/guide-ru/caching-data.md
  32. 4
      docs/guide-ru/concept-events.md
  33. 12
      docs/guide-ru/input-validation.md
  34. 10
      docs/guide-ru/runtime-logging.md
  35. 2
      docs/guide-ru/structure-filters.md
  36. 2
      docs/guide-zh-CN/concept-events.md
  37. 2
      docs/guide-zh-CN/db-active-record.md
  38. 2
      docs/guide-zh-CN/structure-filters.md
  39. 7
      docs/guide/caching-data.md
  40. 3
      docs/guide/caching-fragment.md
  41. 6
      docs/guide/concept-events.md
  42. 28
      docs/guide/db-active-record.md
  43. 15
      docs/guide/db-dao.md
  44. 209
      docs/guide/db-query-builder.md
  45. 11
      docs/guide/input-validation.md
  46. 10
      docs/guide/rest-resources.md
  47. 24
      docs/guide/start-installation.md
  48. 8
      docs/internals/release.md
  49. 4
      framework/BaseYii.php
  50. 109
      framework/CHANGELOG.md
  51. 34
      framework/UPGRADE.md
  52. 2
      framework/Yii.php
  53. 1
      framework/base/ActionFilter.php
  54. 9
      framework/base/ArrayableTrait.php
  55. 5
      framework/base/Component.php
  56. 40
      framework/base/DynamicContentAwareInterface.php
  57. 82
      framework/base/DynamicContentAwareTrait.php
  58. 1
      framework/base/Event.php
  59. 1
      framework/base/InvalidArgumentException.php
  60. 2
      framework/base/Module.php
  61. 71
      framework/base/View.php
  62. 25
      framework/behaviors/AttributeTypecastBehavior.php
  63. 2
      framework/behaviors/BlameableBehavior.php
  64. 199
      framework/behaviors/CacheableWidgetBehavior.php
  65. 7
      framework/caching/DbCache.php
  66. 1
      framework/caching/ZendDataCache.php
  67. 6
      framework/console/Controller.php
  68. 1
      framework/console/widgets/Table.php
  69. 61
      framework/data/DataFilter.php
  70. 15
      framework/db/ActiveQuery.php
  71. 24
      framework/db/ActiveQueryTrait.php
  72. 8
      framework/db/ActiveRecord.php
  73. 5
      framework/db/ActiveRelationTrait.php
  74. 170
      framework/db/ArrayExpression.php
  75. 63
      framework/db/BaseActiveRecord.php
  76. 38
      framework/db/ColumnSchema.php
  77. 1
      framework/db/ColumnSchemaBuilder.php
  78. 52
      framework/db/Command.php
  79. 28
      framework/db/Connection.php
  80. 125
      framework/db/ConstraintFinderInterface.php
  81. 4
      framework/db/Expression.php
  82. 30
      framework/db/ExpressionBuilder.php
  83. 28
      framework/db/ExpressionBuilderInterface.php
  84. 33
      framework/db/ExpressionBuilderTrait.php
  85. 24
      framework/db/ExpressionInterface.php
  86. 73
      framework/db/JsonExpression.php
  87. 24
      framework/db/Migration.php
  88. 65
      framework/db/PdoValue.php
  89. 31
      framework/db/PdoValueBuilder.php
  90. 187
      framework/db/Query.php
  91. 837
      framework/db/QueryBuilder.php
  92. 36
      framework/db/QueryExpressionBuilder.php
  93. 24
      framework/db/QueryTrait.php
  94. 100
      framework/db/Schema.php
  95. 33
      framework/db/SchemaBuilderTrait.php
  96. 2
      framework/db/TableSchema.php
  97. 27
      framework/db/conditions/AndCondition.php
  98. 121
      framework/db/conditions/BetweenColumnsCondition.php
  99. 81
      framework/db/conditions/BetweenColumnsConditionBuilder.php
  100. 98
      framework/db/conditions/BetweenCondition.php
  101. Some files were not shown because too many files have changed in this diff Show More

1
.gitlab-ci.yml

@ -4,6 +4,7 @@ services:
- docker:dind
variables:
DOCKER_YII2_PHP_IMAGE: yiisoftware/yii2-php:7.1-apache
DOCKER_MYSQL_IMAGE: percona:5.7
DOCKER_POSTGRES_IMAGE: postgres:9.3

10
.travis.yml

@ -46,9 +46,14 @@ cache:
- $HOME/.composer/cache
- $HOME/.npm
# try running against postgres 9.3
# try running against postgres 9.6
addons:
postgresql: "9.3"
postgresql: "9.6"
apt:
sources:
- mysql-5.7-trusty
packages:
- mysql-server
code_climate:
repo_token: 2935307212620b0e2228ab67eadd92c9f5501ddb60549d0d86007a354d56915b
@ -119,6 +124,7 @@ before_script:
- |
if [ $TASK_TESTS_PHP == 1 ]; then
travis_retry mysql -e 'CREATE DATABASE `yiitest`;';
mysql -e "SET GLOBAL sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';";
mysql -e "CREATE USER 'travis'@'localhost' IDENTIFIED WITH mysql_native_password;";
mysql -e "GRANT ALL PRIVILEGES ON *.* TO 'travis'@'localhost' WITH GRANT OPTION;";
psql -U postgres -c 'CREATE DATABASE yiitest;';

3
Dockerfile

@ -1,4 +1,5 @@
FROM yiisoftware/yii2-php:7.1-apache
ARG DOCKER_YII2_PHP_IMAGE
FROM ${DOCKER_YII2_PHP_IMAGE}
# Project source-code
WORKDIR /project

4
README.md

@ -9,8 +9,8 @@ Yii 2 is a modern framework designed to be a solid foundation for your PHP appli
It is fast, secure and efficient and works right out of the box pre-configured with reasonable defaults.
The framework is easy to adjust to meet your needs, because Yii has been designed to be flexible.
[![Latest Stable Version](https://poser.pugx.org/yiisoft/yii2/v/stable.png)](https://packagist.org/packages/yiisoft/yii2)
[![Total Downloads](https://poser.pugx.org/yiisoft/yii2/downloads.png)](https://packagist.org/packages/yiisoft/yii2)
[![Latest Stable Version](https://img.shields.io/packagist/v/yiisoft/yii2.svg)](https://packagist.org/packages/yiisoft/yii2)
[![Total Downloads](https://img.shields.io/packagist/dt/yiisoft/yii2.svg)](https://packagist.org/packages/yiisoft/yii2)
[![Build Status](https://img.shields.io/travis/yiisoft/yii2.svg)](http://travis-ci.org/yiisoft/yii2)
[![Code Coverage](https://scrutinizer-ci.com/g/yiisoft/yii2/badges/coverage.png?s=31d80f1036099e9d6a3e4d7738f6b000b3c3d10e)](https://scrutinizer-ci.com/g/yiisoft/yii2/)
[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/yiisoft/yii2/badges/quality-score.png?s=b1074a1ff6d0b214d54fa5ab7abbb90fc092471d)](https://scrutinizer-ci.com/g/yiisoft/yii2/)

2
build/controllers/ClassmapController.php

@ -60,7 +60,7 @@ class ClassmapController extends Controller
if (strpos($file, $root) !== 0) {
throw new Exception("Something wrong: $file\n");
}
$path = str_replace('\\', '/', substr($file, strlen($root)));
$path = str_replace('\\', '/', substr($file, \strlen($root)));
$map[$path] = " 'yii" . substr(str_replace('/', '\\', $path), 0, -4) . "' => YII2_PATH . '$path',";
}
ksort($map);

26
build/controllers/DevController.php

@ -24,19 +24,25 @@ use yii\helpers\FileHelper;
*/
class DevController extends Controller
{
/**
* {@inheritdoc}
*/
public $defaultAction = 'all';
/**
* @var bool whether to use HTTP when cloning github repositories
*/
public $useHttp = false;
/**
* @var array
*/
public $apps = [
'basic' => 'git@github.com:yiisoft/yii2-app-basic.git',
'advanced' => 'git@github.com:yiisoft/yii2-app-advanced.git',
'benchmark' => 'git@github.com:yiisoft/yii2-app-benchmark.git',
];
/**
* @var array
*/
public $extensions = [
'apidoc' => 'git@github.com:yiisoft/yii2-apidoc.git',
'authclient' => 'git@github.com:yiisoft/yii2-authclient.git',
@ -98,17 +104,17 @@ class DevController extends Controller
*/
public function actionRun($command)
{
$command = implode(' ', func_get_args());
$command = implode(' ', \func_get_args());
// root of the dev repo
$base = dirname(dirname(__DIR__));
$base = \dirname(\dirname(__DIR__));
$dirs = $this->listSubDirs("$base/extensions");
$dirs = array_merge($dirs, $this->listSubDirs("$base/apps"));
asort($dirs);
$oldcwd = getcwd();
foreach ($dirs as $dir) {
$displayDir = substr($dir, strlen($base));
$displayDir = substr($dir, \strlen($base));
$this->stdout("Running '$command' in $displayDir...\n", Console::BOLD);
chdir($dir);
passthru($command);
@ -137,7 +143,7 @@ class DevController extends Controller
public function actionApp($app, $repo = null)
{
// root of the dev repo
$base = dirname(dirname(__DIR__));
$base = \dirname(\dirname(__DIR__));
$appDir = "$base/apps/$app";
if (!file_exists($appDir)) {
@ -188,7 +194,7 @@ class DevController extends Controller
public function actionExt($extension, $repo = null)
{
// root of the dev repo
$base = dirname(dirname(__DIR__));
$base = \dirname(\dirname(__DIR__));
$extensionDir = "$base/extensions/$extension";
if (!file_exists($extensionDir)) {
@ -229,12 +235,12 @@ class DevController extends Controller
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function options($actionID)
{
$options = parent::options($actionID);
if (in_array($actionID, ['ext', 'app', 'all'], true)) {
if (\in_array($actionID, ['ext', 'app', 'all'], true)) {
$options[] = 'useHttp';
}

105
build/controllers/PhpDocController.php

@ -11,6 +11,7 @@ use Yii;
use yii\console\Controller;
use yii\helpers\Console;
use yii\helpers\FileHelper;
use yii\helpers\Json;
/**
* PhpDocController is there to help maintaining PHPDoc annotation in class files.
@ -21,6 +22,9 @@ use yii\helpers\FileHelper;
*/
class PhpDocController extends Controller
{
/**
* {@inheritdoc}
*/
public $defaultAction = 'property';
/**
* @var bool whether to update class docs directly. Setting this to false will just output docs
@ -82,7 +86,7 @@ class PhpDocController extends Controller
$nFilesUpdated = 0;
foreach ($files as $file) {
$contents = file_get_contents($file);
$sha = sha1($contents);
$hash = $this->hash($contents);
// fix line endings
$lines = preg_split('/(\r\n|\n|\r)/', $contents);
@ -94,10 +98,10 @@ class PhpDocController extends Controller
$lines = array_values($this->fixLineSpacing($lines));
$newContent = implode("\n", $lines);
if ($sha !== sha1($newContent)) {
if ($hash !== $this->hash($newContent)) {
file_put_contents($file, $newContent);
$nFilesUpdated++;
}
file_put_contents($file, $newContent);
$nFilesTotal++;
}
@ -106,13 +110,18 @@ class PhpDocController extends Controller
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function options($actionID)
{
return array_merge(parent::options($actionID), ['updateFiles', 'skipFrameworkRequirements']);
}
/**
* @param string $root
* @param bool $needsInclude
* @return array list of files.
*/
protected function findFiles($root, $needsInclude = true)
{
$except = [];
@ -132,7 +141,7 @@ class PhpDocController extends Controller
'/generators/extension/default/AutoloadExample.php',
],
'swiftmailer' => [
'/Logger.php',
'src/Logger.php',
],
'twig' => [
'/Extension.php',
@ -147,13 +156,9 @@ class PhpDocController extends Controller
}
if ($root === null) {
$root = dirname(YII2_PATH);
$root = \dirname(YII2_PATH);
$extensionPath = "$root/extensions";
foreach (scandir($extensionPath) as $extension) {
if (ctype_alpha($extension) && is_dir($extensionPath . '/' . $extension)) {
Yii::setAlias("@yii/$extension", "$extensionPath/$extension");
}
}
$this->setUpExtensionAliases($extensionPath);
$except = [
'/apps/',
@ -172,11 +177,11 @@ class PhpDocController extends Controller
}
}
} elseif (preg_match('~extensions/([\w-]+)[\\\\/]?$~', $root, $matches)) {
$extensionPath = dirname(rtrim($root, '\\/'));
$extensionPath = \dirname(rtrim($root, '\\/'));
$this->setUpExtensionAliases($extensionPath);
$extension = $matches[1];
Yii::setAlias("@yii/$extension", "$root");
[, $extension] = $matches;
Yii::setAlias("@yii/$extension", (string)$root);
if (is_file($autoloadFile = Yii::getAlias("@yii/$extension/vendor/autoload.php"))) {
include $autoloadFile;
}
@ -195,11 +200,11 @@ class PhpDocController extends Controller
// return [];
// }
} elseif (preg_match('~apps/([\w-]+)[\\\\/]?$~', $root, $matches)) {
$extensionPath = dirname(dirname(rtrim($root, '\\/'))) . '/extensions';
$extensionPath = \dirname(\dirname(rtrim($root, '\\/'))) . '/extensions';
$this->setUpExtensionAliases($extensionPath);
$appName = $matches[1];
Yii::setAlias("@app-$appName", "$root");
[, $appName] = $matches;
Yii::setAlias("@app-$appName", (string)$root);
if (is_file($autoloadFile = Yii::getAlias("@app-$appName/vendor/autoload.php"))) {
include $autoloadFile;
}
@ -230,14 +235,30 @@ class PhpDocController extends Controller
'vendor/',
]),
];
return FileHelper::findFiles($root, $options);
}
/**
* @param string $extensionPath root path containing extension repositories.
*/
private function setUpExtensionAliases($extensionPath)
{
foreach (scandir($extensionPath) as $extension) {
if (ctype_alpha($extension) && is_dir($extensionPath . '/' . $extension)) {
Yii::setAlias("@yii/$extension", "$extensionPath/$extension");
$composerConfigFile = $extensionPath . '/' . $extension . '/composer.json';
if (file_exists($composerConfigFile)) {
$composerConfig = Json::decode(file_get_contents($composerConfigFile));
if (isset($composerConfig['autoload']['psr-4'])) {
foreach ($composerConfig['autoload']['psr-4'] as $namespace => $subPath) {
$alias = '@' . str_replace('\\', '/', $namespace);
$path = rtrim("$extensionPath/$extension/$subPath", '/');
Yii::setAlias($alias, $path);
}
}
}
}
}
}
@ -285,6 +306,7 @@ class PhpDocController extends Controller
/**
* Markdown aware fix of whitespace issues in doc comments.
* @param array $lines
*/
protected function fixDocBlockIndentation(&$lines)
{
@ -326,7 +348,7 @@ class PhpDocController extends Controller
$codeBlock = !$codeBlock;
$listIndent = '';
} elseif (preg_match('/^(\s*)([0-9]+\.|-|\*|\+) /', $docLine, $matches)) {
$listIndent = str_repeat(' ', strlen($matches[0]));
$listIndent = str_repeat(' ', \strlen($matches[0]));
$tag = false;
$lines[$i] = $indent . ' * ' . $docLine;
continue;
@ -340,6 +362,10 @@ class PhpDocController extends Controller
}
}
/**
* @param string $line
* @return string
*/
protected function fixParamTypes($line)
{
return preg_replace_callback('~@(param|return) ([\w\\|]+)~i', function ($matches) {
@ -532,10 +558,10 @@ class PhpDocController extends Controller
if (trim($oldDoc) != trim($newDoc)) {
$fileContent = explode("\n", file_get_contents($file));
$start = $ref->getStartLine() - 2;
$docStart = $start - count(explode("\n", $oldDoc)) + 1;
$docStart = $start - \count(explode("\n", $oldDoc)) + 1;
$newFileContent = [];
$n = count($fileContent);
$n = \count($fileContent);
for ($i = 0; $i < $n; $i++) {
if ($i > $start || $i < $docStart) {
$newFileContent[] = $fileContent[$i];
@ -562,7 +588,7 @@ class PhpDocController extends Controller
protected function cleanDocComment($doc)
{
$lines = explode("\n", $doc);
$n = count($lines);
$n = \count($lines);
for ($i = 0; $i < $n; $i++) {
$lines[$i] = rtrim($lines[$i]);
if (trim($lines[$i]) == '*' && trim($lines[$i + 1]) == '*') {
@ -603,7 +629,7 @@ class PhpDocController extends Controller
// if no properties or other tags where present add properties at the end
if ($propertyPosition === false) {
$propertyPosition = count($lines) - 2;
$propertyPosition = \count($lines) - 2;
}
$finalDoc = '';
@ -626,22 +652,22 @@ class PhpDocController extends Controller
$namespace = $namespace['name'];
$classes = $this->match('#\n(?:abstract )?class (?<name>\w+)( extends .+)?( implements .+)?\n\{(?<content>.*)\n\}(\n|$)#', $file);
if (count($classes) > 1) {
if (\count($classes) > 1) {
$this->stderr("[ERR] There should be only one class in a file: $fileName\n", Console::FG_RED);
return false;
}
if (count($classes) < 1) {
if (\count($classes) < 1) {
$interfaces = $this->match('#\ninterface (?<name>\w+)( extends .+)?\n\{(?<content>.*)\n\}(\n|$)#', $file);
if (count($interfaces) == 1) {
if (\count($interfaces) == 1) {
return false;
} elseif (count($interfaces) > 1) {
} 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);
if (count($traits) == 1) {
if (\count($traits) == 1) {
return false;
} elseif (count($traits) > 1) {
} elseif (\count($traits) > 1) {
$this->stderr("[ERR] There should be only one class/trait/interface in a file: $fileName\n", Console::FG_RED);
} else {
$this->stderr("[ERR] No class in file: $fileName\n", Console::FG_RED);
@ -682,13 +708,13 @@ class PhpDocController extends Controller
ksort($props);
if (count($props) > 0) {
if (\count($props) > 0) {
$phpdoc .= " *\n";
foreach ($props as $propName => &$prop) {
$docline = ' * @';
$docline .= 'property'; // Do not use property-read and property-write as few IDEs support complex syntax.
$note = '';
if (isset($prop['get']) && isset($prop['set'])) {
if (isset($prop['get'], $prop['set'])) {
if ($prop['get']['type'] != $prop['set']['type']) {
$note = ' Note that the type of this property differs in getter and setter.'
. ' See [[get' . ucfirst($propName) . '()]] and [[set' . ucfirst($propName) . '()]] for details.';
@ -706,7 +732,7 @@ class PhpDocController extends Controller
}
if (!$parentSetter) {
$note = ' This property is read-only.';
// $docline .= '-read';
//$docline .= '-read';
}
} elseif (isset($prop['set'])) {
// check if parent class has getter defined
@ -721,7 +747,7 @@ class PhpDocController extends Controller
}
if (!$parentGetter) {
$note = ' This property is write-only.';
// $docline .= '-write';
//$docline .= '-write';
}
} else {
continue;
@ -773,11 +799,24 @@ class PhpDocController extends Controller
return '';
}
return strtoupper(substr($str, 0, 1)) . substr($str, 1) . ($str[strlen($str) - 1] != '.' ? '.' : '');
return strtoupper(substr($str, 0, 1)) . substr($str, 1) . ($str[\strlen($str) - 1] != '.' ? '.' : '');
}
protected function getPropParam($prop, $param)
{
return isset($prop['property']) ? $prop['property'][$param] : (isset($prop['get']) ? $prop['get'][$param] : $prop['set'][$param]);
}
/**
* Generate a hash value (message digest)
* @param string $string message to be hashed.
* @return string calculated message digest.
*/
private function hash($string)
{
if (!function_exists('hash')) {
return sha1($string);
}
return hash('sha256', $string);
}
}

40
build/controllers/ReleaseController.php

@ -82,7 +82,7 @@ class ReleaseController extends Controller
throw new Exception('Sorry, but releases should be run interactively to ensure you actually verify what you are doing ;)');
}
if ($this->basePath === null) {
$this->basePath = dirname(dirname(__DIR__));
$this->basePath = \dirname(\dirname(__DIR__));
}
$this->basePath = rtrim($this->basePath, '\\/');
return parent::beforeAction($action);
@ -109,7 +109,7 @@ class ReleaseController extends Controller
foreach ($items as $item) {
$this->stdout("fetching tags for $item...");
if ($item === 'framework') {
$this->gitFetchTags("{$this->basePath}");
$this->gitFetchTags((string)$this->basePath);
} elseif (strncmp('app-', $item, 4) === 0) {
$this->gitFetchTags("{$this->basePath}/apps/" . substr($item, 4));
} else {
@ -183,7 +183,7 @@ class ReleaseController extends Controller
*/
public function actionRelease(array $what)
{
if (count($what) > 1) {
if (\count($what) > 1) {
$this->stdout("Currently only one simultaneous release is supported.\n");
return 1;
}
@ -315,7 +315,7 @@ class ReleaseController extends Controller
*/
public function actionSortChangelog(array $what)
{
if (count($what) > 1) {
if (\count($what) > 1) {
$this->stdout("Currently only one simultaneous release is supported.\n");
return 1;
}
@ -376,7 +376,7 @@ class ReleaseController extends Controller
{
foreach ($what as $w) {
if (strncmp('app-', $w, 4) === 0) {
if (!empty($limit) && !in_array('app', $limit)) {
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))) {
@ -386,7 +386,7 @@ class ReleaseController extends Controller
$this->ensureGitClean($appPath);
}
} elseif ($w === 'framework') {
if (!empty($limit) && !in_array('framework', $limit)) {
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")) {
@ -396,7 +396,7 @@ class ReleaseController extends Controller
$this->ensureGitClean($fwPath);
}
} else {
if (!empty($limit) && !in_array('ext', $limit)) {
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")) {
@ -414,7 +414,7 @@ class ReleaseController extends Controller
{
$this->stdout("\n");
$this->stdout($h = "Preparing framework release version $version", Console::BOLD);
$this->stdout("\n" . str_repeat('-', strlen($h)) . "\n\n", Console::BOLD);
$this->stdout("\n" . str_repeat('-', \strlen($h)) . "\n\n", Console::BOLD);
if (!$this->confirm('Make sure you are on the right branch for this release and that it tracks the correct remote branch! Continue?')) {
exit(1);
@ -552,7 +552,7 @@ class ReleaseController extends Controller
{
$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->stdout("\n" . str_repeat('-', \strlen($h)) . "\n\n", Console::BOLD);
if (!$this->confirm('Make sure you are on the right branch for this release and that it tracks the correct remote branch! Continue?')) {
exit(1);
@ -670,7 +670,7 @@ class ReleaseController extends Controller
{
$this->stdout("\n");
$this->stdout($h = "Preparing release for extension $name version $version", Console::BOLD);
$this->stdout("\n" . str_repeat('-', strlen($h)) . "\n\n", Console::BOLD);
$this->stdout("\n" . str_repeat('-', \strlen($h)) . "\n\n", Console::BOLD);
if (!$this->confirm('Make sure you are on the right branch for this release and that it tracks the correct remote branch! Continue?')) {
exit(1);
@ -795,7 +795,11 @@ class ReleaseController extends Controller
protected function gitFetchTags($path)
{
chdir($path);
try {
chdir($path);
} catch (\yii\base\ErrorException $e) {
throw new Exception('Failed to getch git tags in ' . $path . ': ' . $e->getMessage());
}
exec('git fetch --tags', $output, $ret);
if ($ret != 0) {
throw new Exception('Command "git fetch --tags" failed with code ' . $ret);
@ -817,7 +821,7 @@ class ReleaseController extends Controller
$headline = $version . ' ' . date('F d, Y');
$this->sed(
'/' . $v . ' under development\n(-+?)\n/',
$headline . "\n" . str_repeat('-', strlen($headline)) . "\n",
$headline . "\n" . str_repeat('-', \strlen($headline)) . "\n",
$this->getChangelogs($what)
);
}
@ -825,7 +829,7 @@ class ReleaseController extends Controller
protected function openChangelogs($what, $version)
{
$headline = "\n$version under development\n";
$headline .= str_repeat('-', strlen($headline) - 2) . "\n\n- no changes in this release.\n";
$headline .= str_repeat('-', \strlen($headline) - 2) . "\n\n- no changes in this release.\n";
foreach ($this->getChangelogs($what) as $file) {
$lines = explode("\n", file_get_contents($file));
$hl = [
@ -874,7 +878,7 @@ class ReleaseController extends Controller
$state = 'end';
}
// add continued lines to the last item to keep them together
if (!empty(${$state}) && trim($line !== '') && strpos($line, '- ') !== 0) {
if (!empty(${$state}) && trim($line) !== '' && strncmp($line, '- ', 2) !== 0) {
end(${$state});
${$state}[key(${$state})] .= "\n" . $line;
} else {
@ -919,7 +923,7 @@ class ReleaseController extends Controller
protected function getChangelogs($what)
{
$changelogs = [];
if (in_array('framework', $what)) {
if (\in_array('framework', $what)) {
$changelogs[] = $this->getFrameworkChangelog();
}
@ -947,13 +951,13 @@ class ReleaseController extends Controller
protected function composerSetStability($what, $version)
{
$apps = [];
if (in_array('app-advanced', $what)) {
if (\in_array('app-advanced', $what)) {
$apps[] = $this->basePath . '/apps/advanced/composer.json';
}
if (in_array('app-basic', $what)) {
if (\in_array('app-basic', $what)) {
$apps[] = $this->basePath . '/apps/basic/composer.json';
}
if (in_array('app-benchmark', $what)) {
if (\in_array('app-benchmark', $what)) {
$apps[] = $this->basePath . '/apps/benchmark/composer.json';
}
if (empty($apps)) {

18
build/controllers/Utf8Controller.php

@ -29,7 +29,7 @@ class Utf8Controller extends Controller
public function actionCheckGuide($directory = null)
{
if ($directory === null) {
$directory = dirname(dirname(__DIR__)) . '/docs';
$directory = \dirname(\dirname(__DIR__)) . '/docs';
}
if (is_file($directory)) {
$files = [$directory];
@ -90,7 +90,7 @@ class Utf8Controller extends Controller
}
$hexcode = dechex($this->unicodeOrd($char));
$hexcode = str_repeat('0', max(4 - strlen($hexcode), 0)) . $hexcode;
$hexcode = str_repeat('0', max(4 - \strlen($hexcode), 0)) . $hexcode;
$this->stdout(" at $line:$pos FOUND $what: 0x$hexcode '$char' http://unicode-table.com/en/$hexcode/\n");
}
@ -105,20 +105,20 @@ class Utf8Controller extends Controller
*/
private function unicodeOrd($c)
{
$h = ord($c[0]);
$h = \ord($c[0]);
if ($h <= 0x7F) {
return $h;
} elseif ($h < 0xC2) {
return false;
} elseif ($h <= 0xDF) {
return ($h & 0x1F) << 6 | (ord($c[1]) & 0x3F);
return ($h & 0x1F) << 6 | (\ord($c[1]) & 0x3F);
} elseif ($h <= 0xEF) {
return ($h & 0x0F) << 12 | (ord($c[1]) & 0x3F) << 6
| (ord($c[2]) & 0x3F);
return ($h & 0x0F) << 12 | (\ord($c[1]) & 0x3F) << 6
| (\ord($c[2]) & 0x3F);
} elseif ($h <= 0xF4) {
return ($h & 0x0F) << 18 | (ord($c[1]) & 0x3F) << 12
| (ord($c[2]) & 0x3F) << 6
| (ord($c[3]) & 0x3F);
return ($h & 0x0F) << 18 | (\ord($c[1]) & 0x3F) << 12
| (\ord($c[2]) & 0x3F) << 6
| (\ord($c[3]) & 0x3F);
}
return false;

2
docs/guide-es/concept-events.md

@ -218,7 +218,7 @@ use yii\base\Event;
use yii\db\ActiveRecord;
Event::on(ActiveRecord::class, ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
Yii::trace(get_class($event->sender) . ' is inserted');
Yii::debug(get_class($event->sender) . ' is inserted');
});
```

20
docs/guide-es/db-migrations.md

@ -204,7 +204,7 @@ esto genera
class m150811_220037_create_post_table extends Migration
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public function up()
{
@ -214,7 +214,7 @@ class m150811_220037_create_post_table extends Migration
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function down()
{
@ -238,7 +238,7 @@ genera
class m150811_220037_create_post_table extends Migration
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public function up()
{
@ -250,7 +250,7 @@ class m150811_220037_create_post_table extends Migration
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function down()
{
@ -275,7 +275,7 @@ genera
class m150811_220037_create_post_table extends Migration
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public function up()
{
@ -287,7 +287,7 @@ class m150811_220037_create_post_table extends Migration
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function down()
{
@ -320,7 +320,7 @@ genera
class m160328_040430_create_post_table extends Migration
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public function up()
{
@ -368,7 +368,7 @@ class m160328_040430_create_post_table extends Migration
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function down()
{
@ -522,7 +522,7 @@ genera
class m160328_041642_create_junction_table_for_post_and_tag_tables extends Migration
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public function up()
{
@ -569,7 +569,7 @@ class m160328_041642_create_junction_table_for_post_and_tag_tables extends Migra
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function down()
{

10
docs/guide-es/runtime-logging.md

@ -20,7 +20,7 @@ En esta sección, se describirán principalmente los dos primeros pasos.
Registrar mensajes de anotación es tan simple como llamar a uno de los siguientes métodos de registro de anotaciones.
* [[Yii::trace()]]: registra un mensaje para trazar el funcionamiento de una sección de código. Se usa principalmente
* [[Yii::debug()]]: registra un mensaje para trazar el funcionamiento de una sección de código. Se usa principalmente
para tareas de desarrollo.
* [[Yii::info()]]: registra un mensaje que transmite información útil.
* [[Yii::warning()]]: registra un mensaje de advertencia que indica que ha sucedido algo inesperado.
@ -32,7 +32,7 @@ tiene que ser registrado, mientras que `$category` es la categoría del registro
ejemplo registra la huella del mensaje para la categoría `application`:
```php
Yii::trace('start calculating average revenue');
Yii::debug('start calculating average revenue');
```
> Info: Los mensajes de registro pueden ser tanto cadenas de texto como datos complejos, como arrays u objetos.
@ -47,7 +47,7 @@ efectiva de organizarlos es usar la constante predefinida (magic constant) de PH
categoría. Además este es el enfoque que se usa en el código del núcleo (core) del framework Yii. Por ejemplo,
```php
Yii::trace('start calculating average revenue', __METHOD__);
Yii::debug('start calculating average revenue', __METHOD__);
```
La constante `__METHOD__` equivale al nombre del método (con el prefijo del nombre completo del nombre de clase) donde
@ -131,7 +131,7 @@ La propiedad [[yii\log\Target::levels|levels]] es un array que consta de uno o v
* `error`: correspondiente a los mensajes registrados por [[Yii::error()]].
* `warning`: correspondiente a los mensajes registrados por [[Yii::warning()]].
* `info`: correspondiente a los mensajes registrados por [[Yii::info()]].
* `trace`: correspondiente a los mensajes registrados por [[Yii::trace()]].
* `trace`: correspondiente a los mensajes registrados por [[Yii::debug()]].
* `profile`: correspondiente a los mensajes registrados por [[Yii::beginProfile()]] y [[Yii::endProfile()]], que se
explicará más detalladamente en la subsección [Perfiles](#performance-profiling).
@ -290,7 +290,7 @@ número configurando la propiedad [[yii\log\Target::exportInterval|exportInterva
```
Debido al nivel de configuración de la liberación y exportación de mensajes, de forma predeterminada cuando se llama a
`Yii::trace()` o cualquier otro método de registro de mensajes, NO veremos el registro de mensaje inmediatamente en
`Yii::debug()` o cualquier otro método de registro de mensajes, NO veremos el registro de mensaje inmediatamente en
los destinos de registros. Esto podría ser un problema para algunas aplicaciones de consola de ejecución
prolongada (long-running). Para hacer que los mensajes de registro aparezcan inmediatamente en los destinos de
registro se deben establecer [[yii\log\Dispatcher::flushInterval|flushInterval]] y

2
docs/guide-es/structure-filters.md

@ -94,7 +94,7 @@ class ActionTimeFilter extends ActionFilter
public function afterAction($action, $result)
{
$time = microtime(true) - $this->_startTime;
Yii::trace("Action '{$action->uniqueId}' spent $time second.");
Yii::debug("Action '{$action->uniqueId}' spent $time second.");
return parent::afterAction($action, $result);
}
}

4
docs/guide-fr/caching-data.md

@ -100,8 +100,8 @@ Yii prend en charge un large panel de supports de stockage pour cache. Ce qui su
(une version de redis égale ou supérieure à 2.6.12 est nécessaire).
* [[yii\caching\WinCache]]: utilise le [WinCache](http://iis.net/downloads/microsoft/wincache-extension) PHP
([voir aussi l'extension](http://php.net/manual/en/book.wincache.php)).
* [[yii\caching\XCache]]: utilise l'extension PHP [XCache](http://xcache.lighttpd.net/).
* [[yii\caching\ZendDataCache]]: utilise le
* [[yii\caching\XCache]] _(deprecated)_: utilise l'extension PHP [XCache](http://xcache.lighttpd.net/).
* [[yii\caching\ZendDataCache]] _(deprecated)_: utilise le
[cache de données Zend](http://files.zend.com/help/Zend-Server-6/zend-server.htm#data_cache_component.htm)
en tant que médium de cache sous-jacent.

2
docs/guide-fr/concept-events.md

@ -263,7 +263,7 @@ Pour gérer l'évenement `EVENT_DANCE` déclenché par n'importe laquelle de ces
```php
Event::on(DanceEventInterface::class, DanceEventInterface::EVENT_DANCE, function ($event) {
Yii::trace(get_class($event->sender) . ' danse'); // enregistrer le message disant que le chien ou le développeur danse.
Yii::debug(get_class($event->sender) . ' danse'); // enregistrer le message disant que le chien ou le développeur danse.
})
```

20
docs/guide-fr/db-migrations.md

@ -169,7 +169,7 @@ génère
class m150811_220037_create_post_table extends Migration
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public function up()
{
@ -179,7 +179,7 @@ class m150811_220037_create_post_table extends Migration
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function down()
{
@ -203,7 +203,7 @@ génère
class m150811_220037_create_post_table extends Migration
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public function up()
{
@ -215,7 +215,7 @@ class m150811_220037_create_post_table extends Migration
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function down()
{
@ -240,7 +240,7 @@ génère
class m150811_220037_create_post_table extends Migration
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public function up()
{
@ -252,7 +252,7 @@ class m150811_220037_create_post_table extends Migration
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function down()
{
@ -284,7 +284,7 @@ génère
class m160328_040430_create_post_table extends Migration
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public function up()
{
@ -332,7 +332,7 @@ class m160328_040430_create_post_table extends Migration
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function down()
{
@ -477,7 +477,7 @@ génère
class m160328_041642_create_junction_table_for_post_and_tag_tables extends Migration
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public function up()
{
@ -524,7 +524,7 @@ class m160328_041642_create_junction_table_for_post_and_tag_tables extends Migra
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function down()
{

8
docs/guide-fr/output-data-providers.md

@ -220,7 +220,7 @@ class CsvDataProvider extends BaseDataProvider
/**
* @inheritdoc
* {@inheritdoc}
*/
public function init()
{
@ -231,7 +231,7 @@ class CsvDataProvider extends BaseDataProvider
}
/**
* @inheritdoc
* {@inheritdoc}
*/
protected function prepareModels()
{
@ -260,7 +260,7 @@ class CsvDataProvider extends BaseDataProvider
}
/**
* @inheritdoc
* {@inheritdoc}
*/
protected function prepareKeys($models)
{
@ -282,7 +282,7 @@ class CsvDataProvider extends BaseDataProvider
}
/**
* @inheritdoc
* {@inheritdoc}
*/
protected function prepareTotalCount()
{

6
docs/guide-fr/output-data-widgets.md

@ -561,7 +561,7 @@ class UserView extends ActiveRecord
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public static function tableName()
{
@ -574,7 +574,7 @@ class UserView extends ActiveRecord
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function rules()
{
@ -584,7 +584,7 @@ class UserView extends ActiveRecord
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function attributeLabels()
{

10
docs/guide-fr/runtime-logging.md

@ -16,7 +16,7 @@ Dans cette section, nous décrivons principalement les deux premières étapes.
Enregistrer des messages est aussi simple que d'appeler une des méthodes suivantes :
* [[Yii::trace()]]: enregistre un message pour garder une trace de comment un morceau de code fonctionne. Cela est utilisé principalement en développement.
* [[Yii::debug()]]: enregistre un message pour garder une trace de comment un morceau de code fonctionne. Cela est utilisé principalement en développement.
* [[Yii::info()]]: enregistre un message qui contient quelques informations utiles.
* [[Yii::warning()]]: enregistre un message d'avertissement qui indique que quelque chose d'inattendu s'est produit.
* [[Yii::error()]]: enregistre une erreur fatale qui doit être analysée dès que possible.
@ -24,7 +24,7 @@ Enregistrer des messages est aussi simple que d'appeler une des méthodes suivan
Ces méthodes enregistrent les messages à différents niveaux de sévérité et dans différentes catégories. Elles partagent la même signature `function ($message, $category = 'application')`, où `$message` représente le message à enregistrer, tandis que `$category` est la catégorie de ce message. Le code de l'exemple qui suit enregistre un message de trace dans la catégorie `application`:
```php
Yii::trace('start calculating average revenue');
Yii::debug('start calculating average revenue');
```
> Info: les messages enregistrés peuvent être des chaînes de caractères aussi bien que des données complexes telles que des tableaux ou des objets. Il est de la responsabilité des [cibles d'enregistrement](#log-targets) de traiter correctement ces messages. Par défaut, si un message enregistré n'est pas un chaîne de caractères, il est exporté comme une chaîne de caractères en appelant la méthode [[yii\helpers\VarDumper::export()]].
@ -32,7 +32,7 @@ Yii::trace('start calculating average revenue');
Pour mieux organiser et filtrer les messages enregistrés, il est recommandé que vous spécifiiez une catégorie appropriée pour chacun des messages. Vous pouvez choisir une schéma de nommage hiérarchisé pour les catégories, ce qui facilitera le filtrage des messages par les [cibles d'enregistrement](#log-targets) sur la base de ces catégories. Un schéma de nommage simple et efficace est d'utiliser la constante magique `__METHOD__` de PHP dans les noms de catégorie. Par exemple :
```php
Yii::trace('start calculating average revenue', __METHOD__);
Yii::debug('start calculating average revenue', __METHOD__);
```
La constante magique `__METHOD__` est évaluée comme le nom de la méthode (préfixée par le nom pleinement qualifié de la classe), là où la constante apparaît. Par exemple, elle est égale à `'app\controllers\RevenueController::calculate'` si la ligne suivante est utilisée dans cette méthode.
@ -100,7 +100,7 @@ La propriété [[yii\log\Target::levels|levels]] accepte un tableau constitué d
* `error`: correspondant aux messages enregistrés par [[Yii::error()]].
* `warning`: correspondant aux messages enregistrés par [[Yii::warning()]].
* `info`: correspondant aux messages enregistrés par [[Yii::info()]].
* `trace`: correspondant aux messages enregistrés par [[Yii::trace()]].
* `trace`: correspondant aux messages enregistrés par [[Yii::debug()]].
* `profile`: correspondant aux messages enregistrés par [[Yii::beginProfile()]] et [[Yii::endProfile()]], et qui sera expliqué en détails dans la sous-section [Profilage de la performance](#performance-profiling).
Si vous ne spécifiez pas la propriété [[yii\log\Target::levels|levels]], cela signifie que la cible traitera les messages de *n'importe quel* niveau de sévérité.
@ -222,7 +222,7 @@ Lorsque l'[[yii\log\Logger|objet *logger*]] purge les messages enregistrés vers
]
```
À cause des niveaux de purge et d'exportation, par défaut, lorsque vous appelez `Yii::trace()` ou toute autre méthode d'enregistrement, vous ne voyez PAS immédiatement le message enregistré dans la cible. Cela peut représenter un problème pour pour certaines applications de console qui durent longtemps. Pour faire en sorte que les messages apparaissent immédiatement dans les cibles d'enregistrement, vous devriez définir les propriétés [[yii\log\Dispatcher::flushInterval|flushInterval]] et [[yii\log\Target::exportInterval|exportInterval]] toutes deux à 1, comme montré ci-après :
À cause des niveaux de purge et d'exportation, par défaut, lorsque vous appelez `Yii::debug()` ou toute autre méthode d'enregistrement, vous ne voyez PAS immédiatement le message enregistré dans la cible. Cela peut représenter un problème pour pour certaines applications de console qui durent longtemps. Pour faire en sorte que les messages apparaissent immédiatement dans les cibles d'enregistrement, vous devriez définir les propriétés [[yii\log\Dispatcher::flushInterval|flushInterval]] et [[yii\log\Target::exportInterval|exportInterval]] toutes deux à 1, comme montré ci-après :
```php
return [

2
docs/guide-fr/structure-filters.md

@ -72,7 +72,7 @@ class ActionTimeFilter extends ActionFilter
public function afterAction($action, $result)
{
$time = microtime(true) - $this->_startTime;
Yii::trace("Action '{$action->uniqueId}' spent $time second.");
Yii::debug("Action '{$action->uniqueId}' spent $time second.");
return parent::afterAction($action, $result);
}
}

4
docs/guide-ja/concept-events.md

@ -209,7 +209,7 @@ use yii\base\Event;
use yii\db\ActiveRecord;
Event::on(ActiveRecord::class, ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
Yii::trace(get_class($event->sender) . ' が挿入されました');
Yii::debug(get_class($event->sender) . ' が挿入されました');
});
```
@ -292,7 +292,7 @@ class Developer extends Component implements DanceEventInterface
```php
Event::on(DanceEventInterface::class, DanceEventInterface::EVENT_DANCE, function ($event) {
Yii::trace(get_class($event->sender) . ' が躍り上がって喜んだ。'); // 犬または開発者が躍り上がって喜んだことをログに記録。
Yii::debug(get_class($event->sender) . ' が躍り上がって喜んだ。'); // 犬または開発者が躍り上がって喜んだことをログに記録。
})
```

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

@ -196,7 +196,7 @@ yii migrate/create create_post_table
class m150811_220037_create_post_table extends Migration
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public function up()
{
@ -206,7 +206,7 @@ class m150811_220037_create_post_table extends Migration
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function down()
{
@ -230,7 +230,7 @@ yii migrate/create create_post_table --fields="title:string,body:text"
class m150811_220037_create_post_table extends Migration
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public function up()
{
@ -242,7 +242,7 @@ class m150811_220037_create_post_table extends Migration
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function down()
{
@ -267,7 +267,7 @@ yii migrate/create create_post_table --fields="title:string(12):notNull:unique,b
class m150811_220037_create_post_table extends Migration
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public function up()
{
@ -279,7 +279,7 @@ class m150811_220037_create_post_table extends Migration
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function down()
{
@ -313,7 +313,7 @@ yii migrate/create create_post_table --fields="author_id:integer:notNull:foreign
class m160328_040430_create_post_table extends Migration
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public function up()
{
@ -361,7 +361,7 @@ class m160328_040430_create_post_table extends Migration
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function down()
{
@ -521,7 +521,7 @@ yii migrate/create create_junction_table_for_post_and_tag_tables --fields="creat
class m160328_041642_create_junction_table_for_post_and_tag_tables extends Migration
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public function up()
{
@ -568,7 +568,7 @@ class m160328_041642_create_junction_table_for_post_and_tag_tables extends Migra
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function down()
{

8
docs/guide-ja/output-data-providers.md

@ -250,7 +250,7 @@ class CsvDataProvider extends BaseDataProvider
/**
* @inheritdoc
* {@inheritdoc}
*/
public function init()
{
@ -261,7 +261,7 @@ class CsvDataProvider extends BaseDataProvider
}
/**
* @inheritdoc
* {@inheritdoc}
*/
protected function prepareModels()
{
@ -290,7 +290,7 @@ class CsvDataProvider extends BaseDataProvider
}
/**
* @inheritdoc
* {@inheritdoc}
*/
protected function prepareKeys($models)
{
@ -312,7 +312,7 @@ class CsvDataProvider extends BaseDataProvider
}
/**
* @inheritdoc
* {@inheritdoc}
*/
protected function prepareTotalCount()
{

6
docs/guide-ja/output-data-widgets.md

@ -637,7 +637,7 @@ class UserView extends ActiveRecord
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public static function tableName()
{
@ -650,7 +650,7 @@ class UserView extends ActiveRecord
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function rules()
{
@ -660,7 +660,7 @@ class UserView extends ActiveRecord
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function attributeLabels()
{

10
docs/guide-ja/runtime-logging.md

@ -17,7 +17,7 @@ Yii のロギングフレームワークを使うためには、下記のステ
ログメッセージを記録することは、次のログ記録メソッドのどれかを呼び出すだけの簡単なことです。
* [[Yii::trace()]]: コードの断片がどのように走ったかをトレースするメッセージを記録します。主として開発のために使用します。
* [[Yii::debug()]]: コードの断片がどのように走ったかをトレースするメッセージを記録します。主として開発のために使用します。
* [[Yii::info()]]: 何らかの有用な情報を伝えるメッセージを記録します。
* [[Yii::warning()]]: 何か予期しないことが発生したことを示す警告メッセージを記録します。
* [[Yii::error()]]: 出来るだけ早急に調査すべき致命的なエラーを記録します。
@ -27,7 +27,7 @@ Yii のロギングフレームワークを使うためには、下記のステ
次のコードサンプルは、トレースメッセージをデフォルトのカテゴリである `application` の下に記録するものです。
```php
Yii::trace('平均収益の計算を開始');
Yii::debug('平均収益の計算を開始');
```
> Info: ログメッセージは文字列でも、配列やオブジェクトのような複雑なデータでも構いません。
@ -40,7 +40,7 @@ Yii::trace('平均収益の計算を開始');
これは、Yii フレームワークのコアコードでも使われている方法です。例えば、
```php
Yii::trace('平均収益の計算を開始', __METHOD__);
Yii::debug('平均収益の計算を開始', __METHOD__);
```
`__METHOD__` という定数は、それが出現する場所のメソッド名 (完全修飾のクラス名が前置されます) として評価されます。
@ -116,7 +116,7 @@ Yii は下記のログターゲットをあらかじめ内蔵しています。
* `error`: [[Yii::error()]] によって記録されたメッセージに対応。
* `warning`: [[Yii::warning()]] によって記録されたメッセージに対応。
* `info`: [[Yii::info()]] によって記録されたメッセージに対応。
* `trace`: [[Yii::trace()]] によって記録されたメッセージに対応。
* `trace`: [[Yii::debug()]] によって記録されたメッセージに対応。
* `profile`: [[Yii::beginProfile()]] と [[Yii::endProfile()]] によって記録されたメッセージに対応。
これについては、[プロファイリング](#performance-profiling) の項で詳細に説明します。
@ -255,7 +255,7 @@ return [
]
```
デフォルトの状態では、吐き出しとエクスポートの間隔の設定のために、`Yii::trace()` やその他のログ記録メソッドを呼んでも、ただちには、ログメッセージはログターゲットに出現しません。
デフォルトの状態では、吐き出しとエクスポートの間隔の設定のために、`Yii::debug()` やその他のログ記録メソッドを呼んでも、ただちには、ログメッセージはログターゲットに出現しません。
このことは、長時間にわたって走るコンソールアプリケーションでは、問題になる場合もあります。
各ログメッセージがただちにログターゲットに出現するようにするためには、下記のように、[[yii\log\Dispatcher::flushInterval|flushInterval]] と [[yii\log\Target::exportInterval|exportInterval]] の両方を 1 に設定しなければなりません。

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

@ -83,7 +83,7 @@ class ActionTimeFilter extends ActionFilter
public function afterAction($action, $result)
{
$time = microtime(true) - $this->_startTime;
Yii::trace("アクション '{$action->uniqueId}' は $time 秒を消費。");
Yii::debug("アクション '{$action->uniqueId}' は $time 秒を消費。");
return parent::afterAction($action, $result);
}
}

2
docs/guide-pt-BR/concept-events.md

@ -192,7 +192,7 @@ use yii\base\Event;
use yii\db\ActiveRecord;
Event::on(ActiveRecord::class, ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
Yii::trace(get_class($event->sender) . ' is inserted');
Yii::debug(get_class($event->sender) . ' is inserted');
});
```

8
docs/guide-pt-BR/output-data-providers.md

@ -228,7 +228,7 @@ class CsvDataProvider extends BaseDataProvider
protected $fileObject; // SplFileObject é muito conveniente para procurar uma linha específica em um arquivo
/**
* @inheritdoc
* {@inheritdoc}
*/
public function init()
{
@ -239,7 +239,7 @@ class CsvDataProvider extends BaseDataProvider
}
/**
* @inheritdoc
* {@inheritdoc}
*/
protected function prepareModels()
{
@ -265,7 +265,7 @@ class CsvDataProvider extends BaseDataProvider
}
/**
* @inheritdoc
* {@inheritdoc}
*/
protected function prepareKeys($models)
{
@ -285,7 +285,7 @@ class CsvDataProvider extends BaseDataProvider
}
/**
* @inheritdoc
* {@inheritdoc}
*/
protected function prepareTotalCount()
{

10
docs/guide-pt-BR/runtime-logging.md

@ -16,7 +16,7 @@ Nesta seção, vamos descrever principalmente os dois primeiros passos.
Gravar mensagens de log é tão simples como chamar um dos seguintes métodos de registro:
* [[Yii::trace()]]: gravar uma mensagem para rastrear como um determinado trecho de código é executado. Isso é principalmente para o uso de desenvolvimento.
* [[Yii::debug()]]: gravar uma mensagem para rastrear como um determinado trecho de código é executado. Isso é principalmente para o uso de desenvolvimento.
* [[Yii::info()]]: gravar uma mensagem que transmite algumas informações úteis.
* [[Yii::warning()]]: gravar uma mensagem de aviso que indica que algo inesperado aconteceu.
* [[Yii::error()]]: gravar um erro fatal que deve ser investigado o mais rápido possível.
@ -24,7 +24,7 @@ Gravar mensagens de log é tão simples como chamar um dos seguintes métodos de
Estes métodos gravam mensagens de log em vários *níveis* e *categorias*. Eles compartilham a mesma assinatura de função `function ($message, $category = 'application')`, onde `$message` significa a mensagem de log a ser gravada, enquanto `$category` é a categoria da mensagem de log. O código no exemplo a seguir registra uma mensagem de rastreamento sob a categoria padrão `application`:
```php
Yii::trace('start calculating average revenue');
Yii::debug('start calculating average revenue');
```
> Observação: Mensagens de log podem ser strings, bem como dados complexos, tais como arrays ou objetos. É da responsabilidade dos [destinos de log](#log-targets) lidar adequadamente com as mensagens de log. Por padrão, se uma mensagem de log não for uma string, ela será exportada como uma string chamando [[yii\helpers\VarDumper::export()]].
@ -32,7 +32,7 @@ Yii::trace('start calculating average revenue');
Para melhor organizar e filtrar as mensagens de log, é recomendável que você especifique uma categoria apropriada para cada mensagem de log. Você pode escolher um esquema de nomenclatura hierárquica para as categorias, o que tornará mais fácil para os [destinos de log](#log-targets) filtrar mensagens com base em suas categorias. Um esquema de nomes simples, mas eficaz é usar a constante mágica PHP `__METHOD__` para os nomes das categorias. Esta é também a abordagem utilizada no código central do framework Yii. Por exemplo,
```php
Yii::trace('start calculating average revenue', __METHOD__);
Yii::debug('start calculating average revenue', __METHOD__);
```
A constante `__METHOD__` corresponde ao nome do método (prefixado com o caminho completo do nome da classe) onde a constante aparece. Por exemplo, é igual a string `'app\controllers\RevenueController::calculate'` se o código acima for chamado dentro deste método.
@ -100,7 +100,7 @@ A propriedade [[yii\log\Target::levels|levels]] é um array que consiste em um o
* `error`: corresponde a mensagens logadas por [[Yii::error()]].
* `warning`: corresponde a mensagens logadas por [[Yii::warning()]].
* `info`: corresponde a mensagens logadas por [[Yii::info()]].
* `trace`: corresponde a mensagens logadas por [[Yii::trace()]].
* `trace`: corresponde a mensagens logadas por [[Yii::debug()]].
* `profile`: corresponde a mensagens logadas por [[Yii::beginProfile()]] e [[Yii::endProfile()]], que será explicado em mais detalhes na subseção [Perfil de Desempenho](#performance-profiling).
Se você não especificar a propriedade [[yii\log\Target::levels|levels]], significa que o alvo de log processará mensagens de *qualquer* nível.
@ -218,7 +218,7 @@ Quando o [[yii\log\Logger|logger object]] libera mensagens de log para os [alvos
]
```
Devido a configuração de nível, liberação e exportação, por padrão quando você chama `Yii::trace()` ou qualquer outro método de log, você NÃO verá a mensagem de log imediatamente no destino. Isto poderia ser um problema para algumas aplicações console de longa execução. Para fazer cada mensagem de log aparecer imediatamente no destino, você deve configurar ambos [[yii\log\Dispatcher::flushInterval|flushInterval]] e [[yii\log\Target::exportInterval|exportInterval]] para 1, como mostrado a seguir:
Devido a configuração de nível, liberação e exportação, por padrão quando você chama `Yii::debug()` ou qualquer outro método de log, você NÃO verá a mensagem de log imediatamente no destino. Isto poderia ser um problema para algumas aplicações console de longa execução. Para fazer cada mensagem de log aparecer imediatamente no destino, você deve configurar ambos [[yii\log\Dispatcher::flushInterval|flushInterval]] e [[yii\log\Target::exportInterval|exportInterval]] para 1, como mostrado a seguir:
```php
return [

2
docs/guide-pt-BR/structure-filters.md

@ -105,7 +105,7 @@ class ActionTimeFilter extends ActionFilter
public function afterAction($action, $result)
{
$time = microtime(true) - $this->_startTime;
Yii::trace("Action '{$action->uniqueId}' spent $time second.");
Yii::debug("Action '{$action->uniqueId}' spent $time second.");
return parent::afterAction($action, $result);
}
}

2
docs/guide-ru/caching-data.md

@ -252,7 +252,7 @@ $result = Customer::getDb()->cache(function ($db) {
### Очистка кэша <span id="cache-flushing">
Для очистки всего кэша, вы можете вызвать [[yii\caching\Cache::clear()]].
Для очистки всего кэша, вы можете вызвать [[yii\caching\Cache::flush()]].
Также вы можете очистить кэш из консоли, вызвав `yii cache/clear`.
- `yii cache`: отображает список доступных кэширующих компонентов приложения

4
docs/guide-ru/concept-events.md

@ -188,7 +188,7 @@ use yii\base\Event;
use yii\db\ActiveRecord;
Event::on(ActiveRecord::class, ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
Yii::trace(get_class($event->sender) . ' добавлен');
Yii::debug(get_class($event->sender) . ' добавлен');
});
```
@ -266,7 +266,7 @@ class Developer extends Component implements DanceEventInterface
```php
Event::on(DanceEventInterface::class, DanceEventInterface::EVENT_DANCE, function ($event) {
Yii::trace(get_class($event->sender) . ' just danced'); // Оставит запись в журнале о том, что кто-то танцевал
Yii::debug(get_class($event->sender) . ' just danced'); // Оставит запись в журнале о том, что кто-то танцевал
});
```

12
docs/guide-ru/input-validation.md

@ -358,8 +358,8 @@ class MyForm extends Model
public function validateCountry($attribute, $params)
{
if (!in_array($this->$attribute, ['USA', 'Web'])) {
$this->addError($attribute, 'Страна должна быть либо "USA" или "Web".');
if (!in_array($this->$attribute, ['USA', 'Indonesia'])) {
$this->addError($attribute, 'Страна должна быть либо "USA" или "Indonesia".');
}
}
}
@ -384,7 +384,9 @@ class MyForm extends Model
Вы можете реализовать свою логику проверки путем переопределения метода
[[yii\validators\Validator::validateAttribute()]]. Если атрибут не прошел проверку, вызвать
[[yii\base\Model::addError()]],
чтобы сохранить сообщение об ошибке в модели, как это делают [встроенные валидаторы](#inline-validators). Например:
чтобы сохранить сообщение об ошибке в модели, как это делают [встроенные валидаторы](#inline-validators).
Валидация может быть помещена в отдельный класс [[components/validators/CountryValidator]]. В этом случае можно использовать метод [[yii\validators\Validator::addError()]] для того, чтобы добавить своё сообщение об ошибке в модель:
```php
namespace app\components;
@ -395,8 +397,8 @@ class CountryValidator extends Validator
{
public function validateAttribute($model, $attribute)
{
if (!in_array($model->$attribute, ['USA', 'Web'])) {
$this->addError($model, $attribute, 'Страна должна быть либо "USA" или "Web".');
if (!in_array($model->$attribute, ['USA', 'Indonesia'])) {
$this->addError($model, $attribute, 'Страна должна быть либо "{country1}" либо "{country2}".', ['country1' => 'USA', 'country2' => 'Indonesia']);
}
}
}

10
docs/guide-ru/runtime-logging.md

@ -14,7 +14,7 @@ Yii предоставляет мощную, гибко настраиваему
Запись сообщений лога осуществляется вызовом одного из следующих методов:
* [[Yii::trace()]]: записывает сообщения для отслеживания выполнения кода приложения. Используется, в основном, при разработке.
* [[Yii::debug()]]: записывает сообщения для отслеживания выполнения кода приложения. Используется, в основном, при разработке.
* [[Yii::info()]]: записывает сообщение, содержащее какую-либо полезную информацию.
* [[Yii::warning()]]: записывает *тревожное* сообщение при возникновении неожиданного события.
* [[Yii::error()]]: записывает критическую ошибку, на которую нужно, как можно скорее, обратить внимаение.
@ -22,7 +22,7 @@ Yii предоставляет мощную, гибко настраиваему
Эти методы позволяют записывать сообщения разных *уровней важности* и *категорий*. Они имеют одинаковое описание функции `function ($message, $category = 'application')`, где `$message` передает сообщение для записи, а `$category` - категорию сообщения. В следующем примере будет записано *trace* сообщение с категорией по умолчанию `application`:
```php
Yii::trace('start calculating average revenue');
Yii::debug('start calculating average revenue');
```
> Note: Сообщение может быть как строкой так и объектом или массивом. За корректную работу с содержимым сообщения отвечают [цели лога](#log-targets). По умолчанию, если сообщение не является строкой, оно будет приведено к строковому типу при помощи [[yii\helpers\VarDumper::export()]].
@ -30,7 +30,7 @@ Yii::trace('start calculating average revenue');
Для упрощения работы с сообщениями лога и их фильтрации, рекомендуется явно указывать подходящую категорию для каждого сообщения. Возможно использование иерархической системы именования категорий, что значительно упростит [целям лога](#log-targets) фильтрацию сообщений по категориям. Простым и эффективным способом именования категорий является использование магической PHP константы `__METHOD__`. Такой подход используется в ядре фреймворка Yii. Например,
```php
Yii::trace('начало вычисления среднего дохода', __METHOD__);
Yii::debug('начало вычисления среднего дохода', __METHOD__);
```
Константа `__METHOD__` вычисляется как имя метода (включая полное имя класса), в котором она использована. Например, её значение будет вычислено как `'app\controllers\RevenueController::calculate'`, если показанный выше код вызывается в соответствующем методе.
@ -101,7 +101,7 @@ return [
* `error`: соответствует сообщениям, сохраненным методом [[Yii::error()]].
* `warning`: соответствует сообщениям, сохраненным методом [[Yii::warning()]].
* `info`: соответствует сообщениям, сохраненным методом [[Yii::info()]].
* `trace`: соответствует сообщениям, сохраненным методом [[Yii::trace()]].
* `trace`: соответствует сообщениям, сохраненным методом [[Yii::debug()]].
* `profile`: соответствует сообщениям, сохраненным методами [[Yii::beginProfile()]] и [[Yii::endProfile()]], подробнее о которых написано в подразделе [Профилирование производительности](#performance-profiling).
Если свойство [[yii\log\Target::levels|levels]] не задано, цель логов будет обрабатывать сообщения с *любым* уровнем важности.
@ -222,7 +222,7 @@ return [
]
```
Из-за того, что значения максимального количества сообщений для передачи и выгрузки по умолчанию достаточно велико, при вызове метода `Yii::trace()`, или любого другого метода логгирования, сообщение не появится сразу в файле или таблице базы данных. Такое поведение может стать проблемой, например, в консольных приложениях с большим временем исполнения. Для того, чтобы все сообщения логов сразу же попадали в лог, необходимо установить значения свойств [[yii\log\Dispatcher::flushInterval|flushInterval]] и [[yii\log\Target::exportInterval|exportInterval]] равными 1, например так:
Из-за того, что значения максимального количества сообщений для передачи и выгрузки по умолчанию достаточно велико, при вызове метода `Yii::debug()`, или любого другого метода логгирования, сообщение не появится сразу в файле или таблице базы данных. Такое поведение может стать проблемой, например, в консольных приложениях с большим временем исполнения. Для того, чтобы все сообщения логов сразу же попадали в лог, необходимо установить значения свойств [[yii\log\Dispatcher::flushInterval|flushInterval]] и [[yii\log\Target::exportInterval|exportInterval]] равными 1, например так:
```php
return [

2
docs/guide-ru/structure-filters.md

@ -87,7 +87,7 @@ class ActionTimeFilter extends ActionFilter
public function afterAction($action, $result)
{
$time = microtime(true) - $this->_startTime;
Yii::trace("Action '{$action->uniqueId}' spent $time second.");
Yii::debug("Action '{$action->uniqueId}' spent $time second.");
return parent::afterAction($action, $result);
}
}

2
docs/guide-zh-CN/concept-events.md

@ -182,7 +182,7 @@ use yii\base\Event;
use yii\db\ActiveRecord;
Event::on(ActiveRecord::class, ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
Yii::trace(get_class($event->sender) . ' is inserted');
Yii::debug(get_class($event->sender) . ' is inserted');
});
```

2
docs/guide-zh-CN/db-active-record.md

@ -796,7 +796,7 @@ use yii\db\ActiveRecord;
class Comment extends ActiveRecord
{
/**
* @inheritdoc
* {@inheritdoc}
* @return CommentQuery
*/
public static function find()

2
docs/guide-zh-CN/structure-filters.md

@ -84,7 +84,7 @@ class ActionTimeFilter extends ActionFilter
public function afterAction($action, $result)
{
$time = microtime(true) - $this->_startTime;
Yii::trace("Action '{$action->uniqueId}' spent $time second.");
Yii::debug("Action '{$action->uniqueId}' spent $time second.");
return parent::afterAction($action, $result);
}
}

7
docs/guide/caching-data.md

@ -319,6 +319,13 @@ $result = Customer::getDb()->cache(function ($db) {
The query caching described above has the advantage that you may specify flexible cache dependencies
and are potentially more efficient.
Since 2.0.14 you can use the following shortcuts:
```php
(new Query())->cache(7200)->all();
// and
User::find()->cache(7200)->all();
```
### Clearing Cache <span id="clearing-cache">

3
docs/guide/caching-fragment.md

@ -174,3 +174,6 @@ if ($this->beginCache($id1)) {
The [[yii\base\View::renderDynamic()|renderDynamic()]] method takes a piece of PHP code as its parameter.
The return value of the PHP code is treated as the dynamic content. The same PHP code will be executed
for every request, no matter the enclosing fragment is being served from cached or not.
> Note: since version 2.0.14 a dynamic content API is exposed via the [[yii\base\DynamicContentAwareInterface]] interface and its [[yii\base\DynamicContentAwareTrait]] trait.
As an example, you may refer to the [[yii\widgets\FragmentCache]] class.

6
docs/guide/concept-events.md

@ -370,7 +370,7 @@ $foo = new Foo();
$foo->on('foo.event.*', function ($event) {
// triggered for any event, which name starts on 'foo.event.'
Yii::trace('trigger event: ' . $event->name);
Yii::debug('trigger event: ' . $event->name);
});
```
@ -382,7 +382,7 @@ use Yii;
Event::on('app\models\*', 'before*', function ($event) {
// triggered for any class in namespace 'app\models' for any event, which name starts on 'before'
Yii::trace('trigger event: ' . $event->name . ' for class: ' . get_class($event->sender));
Yii::debug('trigger event: ' . $event->name . ' for class: ' . get_class($event->sender));
});
```
@ -394,7 +394,7 @@ use Yii;
Event::on('*', '*', function ($event) {
// triggered for any event at any class
Yii::trace('trigger event: ' . $event->name);
Yii::debug('trigger event: ' . $event->name);
});
```

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

@ -472,7 +472,7 @@ $customer->loadDefaultValues();
### Attributes Typecasting <span id="attributes-typecasting"></span>
Being populated by query results [[yii\db\ActiveRecord]] performs automatic typecast for its attribute values, using
Being populated by query results, [[yii\db\ActiveRecord]] performs automatic typecast for its attribute values, using
information from [database table schema](db-dao.md#database-schema). This allows data retrieved from table column
declared as integer to be populated in ActiveRecord instance with PHP integer, boolean with boolean and so on.
However, typecasting mechanism has several limitations:
@ -490,7 +490,33 @@ converted during saving process.
> Tip: you may use [[yii\behaviors\AttributeTypecastBehavior]] to facilitate attribute values typecasting
on ActiveRecord validation or saving.
Since 2.0.14, Yii ActiveRecord supports complex data types, such as JSON or multidimensional arrays.
#### JSON in MySQL and PostgreSQL
After data population, the value from JSON column will be automatically decoded from JSON according to standard JSON
decoding rules.
To save attribute value to a JSON column, ActiveRecord will automatically create a [[yii\db\JsonExpression|JsonExpression]] object
that will be encoded to a JSON string on [QueryBuilder](db-query-builder.md) level.
#### Arrays in PostgreSQL
After data population, the value from Array column will be automatically decoded from PgSQL notation to an [[yii\db\ArrayExpression|ArrayExpression]]
object. It implements PHP `ArrayAccess` interface, so you can use it as an array, or call `->getValue()` to get the array itself.
To save attribute value to an array column, ActiveRecord will automatically create an [[yii\db\ArrayExpression|ArrayExpression]] object
that will be encoded by [QueryBuilder](db-query-builder.md) to an PgSQL string representation of array.
You can also use conditions for JSON columns:
```php
$query->andWhere(['=', 'json', new ArrayExpression(['foo' => 'bar'])
```
To learn more about expressions building system read the [Query Builder – Adding custom Conditions and Expressions](db-query-builder.md#adding-custom-conditions-and-expressions)
article.
### Updating Multiple Rows <span id="updating-multiple-rows"></span>

15
docs/guide/db-dao.md

@ -257,6 +257,21 @@ Yii::$app->db->createCommand()->batchInsert('user', ['name', 'age'], [
])->execute();
```
Another useful method is [[yii\db\Command::upsert()|upsert()]]. Upsert is an atomic operation that inserts rows into
a database table if they do not already exist (matching unique constraints), or update them if they do:
```php
Yii::$app->db->createCommand()->upsert('pages', [
'name' => 'Front page',
'url' => 'http://example.com/', // url is unique
'visits' => 0,
], [
'visits' => new \yii\db\Expression('visits + 1'),
], $params)->execute();
```
The code above will either insert a new page record or increment its visit counter atomically.
Note that the aforementioned methods only create the query and you always have to call [[yii\db\Command::execute()|execute()]]
to actually run them.

209
docs/guide/db-query-builder.md

@ -160,12 +160,12 @@ are in the ["Quoting Tables" section of the "Database Access Objects" guide](gui
### [[yii\db\Query::where()|where()]] <span id="where"></span>
The [[yii\db\Query::where()|where()]] method specifies the `WHERE` fragment of a SQL query. You can use one of
the three formats to specify a `WHERE` condition:
the four formats to specify a `WHERE` condition:
- string format, e.g., `'status=1'`
- hash format, e.g. `['status' => 1, 'type' => 2]`
- operator format, e.g. `['like', 'name', 'test']`
- object format, e.g. `new LikeCondition('name', 'LIKE', 'test')`
#### String Format <span id="string-format"></span>
@ -255,6 +255,9 @@ the operator can be one of the following:
- `between`: operand 1 should be the column name, and operand 2 and 3 should be the
starting and ending values of the range that the column is in.
For example, `['between', 'id', 1, 10]` will generate `id BETWEEN 1 AND 10`.
In case you need to build a condition where value is between two columns (like `11 BETWEEN min_id AND max_id`),
you should use [[yii\db\conditions\BetweenColumnsCondition|BetweenColumnsCondition]].
See [Conditions – Object Format](#object-format) chapter to learn more about object definition of conditions.
- `not between`: similar to `between` except the `BETWEEN` is replaced with `NOT BETWEEN`
in the generated condition.
@ -306,6 +309,41 @@ the operator can be one of the following:
Using the Operator Format, Yii internally uses parameter binding so in contrast to the [string format](#string-format), here
you do not have to add parameters manually.
#### Object Format <span id="object-format"></span>
Object Form is available since 2.0.14 and is both most powerful and most complex way to define conditions.
You need to follow it either if you want to build your own abstraction over query builder or if you want to implement
your own complex conditions.
Instances of condition classes are immutable. Their only purpose is to store condition data and provide getters
for condition builders. Condition builder is a class that holds the logic that transforms data
stored in condition into the SQL expression.
Internally the formats described above are implicitly converted to object format prior to building raw SQL,
so it is possible to combine formats in a single condition:
```php
$query->andWhere(new OrCondition([
new InCondition('type', 'in', $types),
['like', 'name', '%good%'],
'disabled=false'
]))
```
Conversion from operator format into object format is performed according to
[[yii\db\QueryBuilder::conditionClasses|QueryBuilder::conditionClasses]] property, that maps operators names
to representative class names:
- `AND`, `OR` -> `yii\db\conditions\ConjunctionCondition`
- `NOT` -> `yii\db\conditions\NotCondition`
- `IN`, `NOT IN` -> `yii\db\conditions\InCondition`
- `BETWEEN`, `NOT BETWEEN` -> `yii\db\conditions\BetweenCondition`
And so on.
Using the object format makes it possible to create your own conditions or to change the way default ones are built.
See [Creating Custom Conditions and Expressions](#creating-custom-conditions-and-expressions) chapter to learn more.
#### Appending Conditions <span id="appending-conditions"></span>
@ -758,3 +796,170 @@ $unbufferedDb->close();
```
> Note: unbuffered query uses less memory on the PHP-side, but can increase the load on the MySQL server. It is recommended to design your own code with your production practice for extra massive data, [for example, divide the range for integer keys, loop them with Unbuffered Queries](https://github.com/yiisoft/yii2/issues/8420#issuecomment-296109257).
### Adding custom Conditions and Expressions <span id="adding-custom-conditions-and-expressions"></span>
As it was mentioned in [Conditions – Object Format](#object-format) chapter, is is possible to create custom condition
classes. For example, let's create a condition that will check that specific columns are less than some value.
Using the operator format, it would look like the following:
```php
[
'and',
'>', 'posts', $minLimit,
'>', 'comments', $minLimit,
'>', 'reactions', $minLimit,
'>', 'subscriptions', $minLimit
]
```
When such condition applied once, it is fine. In case it is used multiple times in a single query it can
be optimized a lot. Let's create a custom condition object to demonstrate it.
Yii has a [[yii\db\conditions\ConditionInterface|ConditionInterface]], that must be used to mark classes, that represent
a condition. It requires `fromArrayDefinition()` method implementation, in order to make possible to create condition
from array format. In case you don't need it, you can implement this method with exception throwing.
Since we create our custom condition class, we can build API that suits our task the most.
```php
namespace app\db\conditions;
class AllGreaterCondition implements \yii\db\conditions\ConditionInterface
{
private $columns;
private $value;
/**
* @param string[] $columns Array of columns that must be greater, than $value
* @param mixed $value the value to compare each $column against.
*/
public function __construct(array $columns, $value)
{
$this->columns = $columns;
$this->value = $value;
}
public static function fromArrayDefinition($operator, $operands)
{
throw new InvalidArgumentException('Not implemented yet, but we will do it later');
}
public function getColumns() { return $this->columns; }
public function getValue() { return $this->vaule; }
}
```
So we can create a condition object:
```php
$conditon = new AllGreaterCondition(['col1', 'col2'], 42);
```
But `QueryBuilder` still does not know, to to make an SQL condition out of this object.
Now we need to create a builder for this condition. It must implement [[yii\db\ExpressionBuilderInterface]]
that requires us to implement a `build()` method.
```php
namespace app\db\conditions;
class AllGreaterConditionBuilder implements \yii\db\ExpressionBuilderInterface
{
use \yii\db\Condition\ExpressionBuilderTrait; // Contains constructor and `queryBuilder` property.
/**
* @param AllGreaterCondition $condition the condition to be built
* @param array $params the binding parameters.
*/
public function build(ConditionInterface $condition, &$params)
{
$value = $condition->getValue();
$conditions = [];
foreach ($condition->getColumns() as $column) {
$conditions[] = new SimpleCondition($column, '>', $value);
}
return $this->queryBuider->buildCondition(new AndCondition($conditions), $params);
}
}
```
Then simple let [[yii\db\QueryBuilder|QueryBuilder]] know about our new condition – add a mapping for it to
the `expressionBuilders` array. It could be done right from the application configuration:
```php
'db' => [
'class' => 'yii\db\mysql\Connection',
// ...
'queryBuilder' => [
'expressionBuilders' => [
'app\db\conditions\AllGreaterCondition' => 'app\db\conditions\AllGreaterConditionBuilder',
],
],
],
```
Now we can use our condition in `where()`:
```php
$query->andWhere(new AllGreaterCondition(['posts', 'comments', 'reactions', 'subscriptions'], $minValue));
```
If we want to make it possible to create our custom condition using operator format, we should declare it in
[[yii\db\QueryBuilder::conditionClasses|QueryBuilder::conditionClasses]]:
```php
'db' => [
'class' => 'yii\db\mysql\Connection',
// ...
'queryBuilder' => [
'expressionBuilders' => [
'app\db\conditions\AllGreaterCondition' => 'app\db\conditions\AllGreaterConditionBuilder',
],
'conditionClasses' => [
'ALL>' => 'app\db\conditions\AllGreaterCondition',
],
],
],
```
And create a real implementation of `AllGreaterCondition::fromArrayDefinition()` method
in `app\db\conditions\AllGreaterCondition`:
```php
namespace app\db\conditions;
class AllGreaterCondition implements \yii\db\conditions\ConditionInterface
{
// ... see the implementation above
public static function fromArrayDefinition($operator, $operands)
{
return new static($operands[0], $operands[1]);
}
}
```
After that, we can create our custom condition using shorter operator format:
```php
$query->andWhere(['ALL>', ['posts', 'comments', 'reactions', 'subscriptions'], $minValue]);
```
You might notice, that there was two concepts used: Expressions and Conditions. There is a [[yii\db\ExpressionInterface]]
that should be used to mark objects, that require an Expression Builder class, that implements
[[yii\db\ExpressionBuilderInterface]] to be built. Also there is a [[yii\db\condition\ConditionInterface]], that extends
[[yii\db\ExpressionInterface|ExpressionInterface]] and should be used to objects, that can be created from array definition
as it was shown above, but require builder as well.
To summarise:
- Expression – is a Data Transfer Object (DTO) for a dataset, that can be somehow compiled to some SQL
statement (an operator, string, array, JSON, etc).
- Condition – is an Expression superset, that aggregates multiple Expressions (or scalar values) that can be compiled
to a single SQL condition.
You can create your own classes that implement [[yii\db\ExpressionInterface|ExpressionInterface]] to hide the complexity
of transforming data to SQL statements. You will learn more about other examples of Expressions in the
[next article](db-active-record.md);

11
docs/guide/input-validation.md

@ -387,8 +387,8 @@ class MyForm extends Model
public function validateCountry($attribute, $params, $validator)
{
if (!in_array($this->$attribute, ['USA', 'Web'])) {
$this->addError($attribute, 'The country must be either "USA" or "Web".');
if (!in_array($this->$attribute, ['USA', 'Indonesia'])) {
$this->addError($attribute, 'The country must be either "USA" or "Indonesia".');
}
}
}
@ -422,7 +422,8 @@ fails the validation, call [[yii\base\Model::addError()]] to save the error mess
with [inline validators](#inline-validators).
For example the inline validator above could be moved into new [[components/validators/CountryValidator]] class.
For example, the inline validator above could be moved into new [[components/validators/CountryValidator]] class.
In this case we can use [[yii\validators\Validator::addError()]] to set customized message for the model.
```php
namespace app\components;
@ -433,8 +434,8 @@ class CountryValidator extends Validator
{
public function validateAttribute($model, $attribute)
{
if (!in_array($model->$attribute, ['USA', 'Web'])) {
$this->addError($model, $attribute, 'The country must be either "USA" or "Web".');
if (!in_array($model->$attribute, ['USA', 'Indonesia'])) {
$this->addError($model, $attribute, 'The country must be either "{country1}" or "{country2}".', ['country1' => 'USA', 'country2' => 'Indonesia']);
}
}
}

10
docs/guide/rest-resources.md

@ -38,13 +38,17 @@ in the array if an end user requests for them via the `expand` query parameter.
// returns all fields as declared in fields()
http://localhost/users
// only returns field id and email, provided they are declared in fields()
// only returns "id" and "email" fields, provided they are declared in fields()
http://localhost/users?fields=id,email
// returns all fields in fields() and field profile if it is in extraFields()
// returns all fields in fields() and field "profile" if it is in extraFields()
http://localhost/users?expand=profile
// only returns field id, email and profile, provided they are in fields() and extraFields()
// returns all fields in fields(), "profile" if it is in extraFields() and "author" from profile if
// it is in extraFields() of profile model
http://localhost/comments?expand=post.author
// only returns "id" and "email" provided they are in fields() and "profile" if it is in extraFields()
http://localhost/users?fields=id,email&expand=profile
```

24
docs/guide/start-installation.md

@ -111,6 +111,30 @@ But there are other installation options available:
you may consider installing the [Advanced Project Template](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md).
Installing Assets <span id="installing-assets"></span>
-----------------
Yii relies on [Bower](http://bower.io/) and/or [NPM](https://www.npmjs.org/) packages for the asset (CSS and JavaScript) libraries installation.
It uses Composer to obtain these libraries, allowing PHP and CSS/JavaScript package versions to resolve at the same time.
This can be achieved either by usage of [asset-packagist.org](https://asset-packagist.org) or [composer asset plugin](https://github.com/francoispluchino/composer-asset-plugin/).
Please refer to [Assets documentation](structure-assets.md) for more details.
You may want to either manage your assets via native Bower/NPM client, use CDN or avoid assets installation entirely.
In order to prevent assets installation via Composer, add the following lines to your 'composer.json':
```json
"replace": {
"bower-asset/jquery": ">=1.11.0",
"bower-asset/inputmask": ">=3.2.0",
"bower-asset/punycode": ">=1.3.0",
"bower-asset/yii2-pjax": ">=2.0.0"
},
```
> Note: in case of bypassing asset installation via Composer, you are responsible for the assets installation and resolving
> version collisions. Be prepared for possible inconsistencies among asset files from different extensions.
Verifying the Installation <span id="verifying-installation"></span>
--------------------------

8
docs/internals/release.md

@ -53,13 +53,13 @@ You may run it with `--update` to fetch tags for all repos to get the newest inf
Making a framework release includes the following commands (apps are always released together with the framework):
./build release framework
./build release app-basic
./build release app-advanced
./build/build release framework
./build/build release app-basic
./build/build release app-advanced
Making an extension release includes only one command (e.g. for redis):
./build release redis
./build/build release redis
The default release command will release a new minor version from the currently checked out branch.
To release another version than the default, you have to specify it using the `--version` option, e.g.

4
framework/BaseYii.php

@ -11,6 +11,7 @@ use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use yii\base\InvalidArgumentException;
use yii\base\InvalidConfigException;
use yii\base\UnknownClassException;
use yii\di\Container;
use yii\di\Instance;
use yii\helpers\VarDumper;
@ -90,7 +91,7 @@ class BaseYii
*/
public static function getVersion()
{
return '2.0.14-dev';
return '2.0.15-dev';
}
/**
@ -425,6 +426,7 @@ class BaseYii
* @param string|array $message the message to be logged. This can be a simple string or a more
* complex data structure, such as array.
* @param string $category the category of the message.
* @since 2.0.14
*/
public static function debug($message, $category = 'application')
{

109
framework/CHANGELOG.md

@ -43,31 +43,34 @@ Yii Framework 2 Change Log
- Chg #15481: Removed `yii\BaseYii::powered()` method (Kolyunya, samdark)
2.0.14 under development
2.0.15 under development
------------------------
- no changes in this release.
2.0.14 February 18, 2018
------------------------
- Enh #13996: Added `yii\web\View::registerJsVar()` method that allows registering JavaScript variables (Eseperio, samdark)
- Enh #9771: Assign hidden input with its own set of HTML options via `$hiddenOptions` in activeFileInput `$options` (HanafiAhmat)
- Bug #15536: Fixed `yii\widgets\ActiveForm::init()` for call `parent::init()` (panchenkodv)
- Enh #14806: Added $placeFooterAfterBody option for GridView (terehru)
- Bug #14711: Fixed `yii\web\ErrorHandler` displaying exception message in non-debug mode (samdark)
- Enh #13814: MySQL unique index names can now contain spaces (df2)
- Bug #15300: Fixed "Cannot read property 'style' of undefined" error at the error screen (vitorarantes)
- Bug #15540: Fixed `yii\db\ActiveRecord::with()` unable to use relation defined via attached behavior in case `asArray` is enabled (klimov-paul)
- Enh #15426: Added abilitiy to create and drop database views (igravity, vladis84)
- Enh #10186: Use native `hash_equals` in `yii\base\Security::compareString()` if available, throw exception if non-strings are compared (aotd1, samdark)
- Bug #15122: Fixed `yii\db\Command::getRawSql()` to properly replace expressions (hiscaler, samdark)
- Enh #15496: CSRF token is now regenerated on changing identity (samdark, rhertogh)
- Enh #15417: Added `yii\validators\FileValidator::$minFiles` (vladis84)
- Bug #8983: Only truncate the original log file for rotation (matthewyang, developeruz)
- Bug #9342: Fixed `yii\db\ActiveQueryTrait` to apply `indexBy` after relations population in order to prevent excess queries (sammousa, silverfire)
- Bug #11401: Fixed `yii\web\DbSession` concurrency issues when writing and regenerating IDs (samdark, andreasanta, cebe)
- Bug #13034: Fixed `normalizePath` for windows network shares that start with two backslashes (developeruz)
- Bug #14135: Fixed `yii\web\Request::getBodyParam()` crashes on object type body params (klimov-paul)
- Bug #14157: Add support for loading default value `CURRENT_TIMESTAMP` of MySQL `datetime` field (rossoneri)
- Bug #14276: Fixed I18N format with dotted parameters (developeruz)
- Bug #14296: Fixed log targets to throw exception in case log can not be properly exported (bizley)
- Bug #14484: Fixed `yii\validators\UniqueValidator` for target classes with a default scope (laszlovl, developeruz)
- Bug #14604: Fixed `yii\validators\CompareValidator` `compareAttribute` does not work if `compareAttribute` form ID has been changed (mikk150)
- Bug #14711 (CVE-2018-6010): Fixed `yii\web\ErrorHandler` displaying exception message in non-debug mode (samdark)
- Bug #14811: Fixed `yii\filters\HttpCache` to work with PHP 7.2 (samdark)
- Bug #14859: Fixed OCI DB `defaultSchema` failure when `masterConfig` is used (lovezhl456)
- Bug #14903: Fixed route with extra dashes is executed controller while it should not (developeruz)
- Bug #14916: Fixed `yii\db\Query::each()` iterator key starts from 1 instead of 0 (Vovan-VE)
- Bug #15046: Throw an `yii\web\HeadersAlreadySentException` if headers were sent before web response (dmirogin)
- Bug #14980: Fix looping in `yii\i18n\MessageFormatter` tokenize pattern if pattern is invalid (uaoleg, developeruz)
- Bug #15031: Fixed incorrect string type length detection for OCI DB schema (Murolike)
- Bug #15046: Throw an `yii\web\HeadersAlreadySentException` if headers were sent before web response (dmirogin)
- Bug #15122: Fixed `yii\db\Command::getRawSql()` to properly replace expressions (hiscaler, samdark)
- Bug #15142: Fixed array params replacing in `yii\helpers\BaseUrl::current()` (IceJOKER)
- Bug #15169: Fixed translating a string when NULL parameter is passed (developeruz)
- Bug #15194: Fixed `yii\db\QueryBuilder::insert()` to preserve passed params when building a `INSERT INTO ... SELECT` query for MSSQL, PostgreSQL and SQLite (sergeymakinen)
@ -75,6 +78,7 @@ Yii Framework 2 Change Log
- Bug #15234: Fixed `\yii\widgets\LinkPager` removed `tag` from `disabledListItemSubTagOptions` (SDKiller)
- Bug #15249: Controllers in subdirectories were not visible in commands list (IceJOKER)
- Bug #15270: Resolved potential race conditions when writing generated php-files (kalessil)
- Bug #15300: Fixed "Cannot read property 'style' of undefined" error at the error screen (vitorarantes)
- Bug #15301: Fixed `ArrayHelper::filter()` to work properly with `0` in values (hhniao)
- Bug #15302: Fixed `yii\caching\DbCache` so that `getValues` now behaves the same as `getValue` with regards to streams (edwards-sj)
- Bug #15317: Regenerate CSRF token if an empty value is given (sammousa)
@ -89,39 +93,89 @@ Yii Framework 2 Change Log
- Bug #15440: Fixed `yii\behaviors\AttributeTypecastBehavior::$attributeTypes` auto-detection fails for rule, which specify attribute with '!' prefix (klimov-paul)
- Bug #15462: Fixed `accessChecker` configuration error (developeruz)
- Bug #15494: Fixed missing `WWW-Authenticate` header (developeruz)
- Bug #15522: Fixed `yii\db\ActiveRecord::refresh()` method does not use an alias in the condition (vladis84)
- Bug #15523: `yii\web\Session` settings could now be configured after session is started (StalkAlex, rob006, daniel1302, samdark)
- Bug #15536: Fixed `yii\widgets\ActiveForm::init()` for call `parent::init()` (panchenkodv)
- Bug #15540: Fixed `yii\db\ActiveRecord::with()` unable to use relation defined via attached behavior in case `asArray` is enabled (klimov-paul)
- Bug #15553: Fixed `yii\validators\NumberValidator` incorrectly validate resource (developeruz)
- Bug #15621: Fixed `yii\web\User::getIdentity()` returning `null` if an exception had been thrown when it was called previously (brandonkelly)
- Bug #15628: Fixed `yii\validators\DateValidator` to respect time when the `format` property is set to UNIX Epoch format (silverfire, gayHacker)
- Bug #15644: Avoid wrong default selection on a dropdown, checkbox list, and radio list, when a option has a key equals to zero (berosoboy)
- Bug #15658: Fixed `yii\filters\auth\HttpBasicAuth` not to switch identity, when user is already authenticated and identity does not get changed (silverfire)
- Bug #15662: Fixed `yii\log\FileTarget` not to create log directory during init process (alexeevdv)
- Enh #3087: Added `yii\helpers\BaseHtml::error()` "errorSource" option to be able to customize errors display (yanggs07, developeruz, silverfire)
- Enh #3250: Added support for events partial wildcard matching (klimov-paul)
- Enh #5515: Added default value for `yii\behaviors\BlameableBehavior` for cases when the user is guest (dmirogin)
- Enh #6844: `yii\base\ArrayableTrait::toArray()` now allows recursive `$fields` and `$expand` (bboure)
- Enh #7640: Implemented custom data types support. Added JSON support for MySQL and PostgreSQL, array support for PostgreSQL (silverfire, cebe)
- Enh #7988: Added `\yii\helpers\Console::errorSummary()` and `\yii\helpers\Json::errorSummary()` (developeruz)
- Enh #7996: Short syntax for verb in GroupUrlRule (schojniak, developeruz)
- Enh #8092: ExistValidator for relations (developeruz)
- Enh #8527: Added `yii\i18n\Locale` component having `getCurrencySymbol()` method (amarox, samdark)
- Enh #8752: Allow specify `$attributeNames` as a string for `yii\base\Model` `validate()` method (developeruz)
- Enh #9137: Added `Access-Control-Allow-Method` header for the OPTIONS request (developeruz)
- Enh #9253: Allow `variations` to be a string for `yii\filters\PageCache` and `yii\widgets\FragmentCache` (schojniak, developeruz)
- Enh #9771: Assign hidden input with its own set of HTML options via `$hiddenOptions` in activeFileInput `$options` (HanafiAhmat)
- Enh #10186: Use native `hash_equals` in `yii\base\Security::compareString()` if available, throw exception if non-strings are compared (aotd1, samdark)
- Enh #11611: Added `BetweenColumnsCondition` to build SQL condition like `value BETWEEN col1 and col2` (silverfire)
- Enh #12623: Added `yii\helpers\StringHelper::matchWildcard()` replacing usage of `fnmatch()`, which may be unreliable (klimov-paul)
- Enh #13019: Support JSON in SchemaBuilderTrait (zhukovra, undefinedor)
- Enh #13425: Added caching of dynamically added URL rules with `yii\web\UrlManager::addRules()` (scriptcube, silverfire)
- Enh #13465: Added `yii\helpers\FileHelper::findDirectories()` method (ArsSirek, developeruz)
- Enh #13618: Active Record now resets related models after corresponding attributes updates (Kolyunya, rob006)
- Enh #13679: Added `yii\behaviors\CacheableWidgetBehavior` (Kolyunya)
- Enh #13814: MySQL unique index names can now contain spaces (df2)
- Enh #13879: Added upsert support for `yii\db\QueryBuilder`, `yii\db\Command`, and `yii\db\Migration` (sergeymakinen)
- Enh #13919: Added option to add comment for created table to migration console command (mixartemev, developeruz)
- Enh #13996: Added `yii\web\View::registerJsVar()` method that allows registering JavaScript variables (Eseperio, samdark)
- Enh #14043: Added `yii\helpers\IpHelper` (silverfire, cebe)
- Enh #14254: add an option to specify whether validator is forced to always use master DB for `yii\validators\UniqueValidator` and `yii\validators\ExistValidator` (rossoneri, samdark)
- Enh #14355: Added ability to pass an empty array as a parameter in console command (developeruz)
- Enh #14488: Added support for X-Forwarded-Host to `yii\web\Request`, fixed `getServerPort()` usage (si294r, samdark)
- Enh #14538: Added `yii\behaviors\AttributeTypecastBehavior::typecastAfterSave` property (littlefuntik, silverfire)
- Enh #14546: Added `dataDirectory` property into `BaseActiveFixture` (leandrogehlen)
- Enh #14568: Refactored migration templates to use `safeUp()` and `safeDown()` methods (Kolyunya)
- Enh #14638: Added `yii\db\SchemaBuilderTrait::tinyInteger()` (rob006)
- Enh #14643: Added `yii\web\ErrorAction::$layout` property to conveniently set layout from error action config (swods, cebe, samdark)
- Enh #14662: Added support for custom `Content-Type` specification to `yii\web\JsonResponseFormatter` (Kolyunya)
- Enh #14732, #11218, #14810, #10855: It is now possible to pass `yii\db\Query` anywhere, where `yii\db\Expression` was supported (silverfire)
- Enh #14806: Added $placeFooterAfterBody option for GridView (terehru)
- Enh #15024: `yii\web\Pjax` widget does not prevent CSS files from sending anymore because they are handled by client-side plugin correctly (onmotion)
- Enh #15047: `yii\db\Query::select()` and `yii\db\Query::addSelect()` now check for duplicate column names (wapmorgan)
- Enh #15076: Improve `yii\db\QueryBuilder::buildColumns()` to throw exception on invalid input (hiscaler)
- Enh #15120: Refactored dynamic caching introducing `DynamicContentAwareInterface` and `DynamicContentAwareTrait` (sergeymakinen)
- Enh #15135: Automatic completion for help in bash and zsh (Valkeru)
- Enh #15216: Added `yii\web\ErrorHandler::$traceLine` to allow opening file at line clicked in IDE (vladis84)
- Enh #15219: Added `yii\filters\auth\HttpHeaderAuth` (bboure)
- Enh #15221: Added support for specifying `--camelCase` console options in `--kebab-case` (brandonkelly)
- Enh #15221: Added support for the `--<option> <value>` console option syntax (brandonkelly)
- Enh #15221: Improved the `help/list-action-options` console command output for command options without a description (brandonkelly)
- Enh #15226: Auto generate placeholder from fields (vladis84)
- Enh #15272: Removed type attribute from script tag (aleksbelic)
- Enh #15332: Always check for availability of `openssl_pseudo_random_bytes`, even if LibreSSL is available (sammousa)
- Enh #15335: Added `FileHelper::unlink()` that works well under all OSes (samdark)
- Enh #15340: Test CHANGELOG.md for valid format (sammousa)
- Enh #15347: Add `Instance` support for object property in DI container (kojit2009)
- Enh #15357: Added multi statement support for `yii\db\sqlite\Command` (sergeymakinen)
- Enh #15360: Refactored `BaseConsole::updateProgress()` (developeruz)
- Enh #15398: Added `yii\db\Query::cache()` (hubeiwei, silverfire)
- Enh #15415: Added transaction/retry support for `yii\db\Command` (sergeymakinen)
- Enh #15417: Added `yii\validators\FileValidator::$minFiles` (vladis84)
- Enh #15422: Added default roles dynamic definition support via closure for `yii\rbac\BaseManager` (deltacube)
- Enh #15426: Added abilitiy to create and drop database views (igravity, vladis84)
- Enh #15476: Added `\yii\widgets\ActiveForm::$validationStateOn` to be able to specify where to add class for invalid fields (samdark)
- Enh #15496 (CVE-2018-6009): CSRF token is now regenerated on changing identity (samdark, rhertogh)
- Enh #15595: `yii\data\DataFilter` can now handle `lt`,`gt`,`lte` and `gte` on `yii\validators\DateValidator` (mikk150)
- Enh #15661: Added `yii\db\ExpressionInterface` support to `yii\db\Command::batchInsert()` (silverfire)
- Enh: Added check to `yii\base\Model::formName()` to prevent source path disclosure when form is represented by an anonymous class (silverfire)
- Chg #15420: Handle OPTIONS request in `yii\filter\Cors` so the preflight check isn't passed trough authentication filters (michaelarnauts, leandrogehlen)
- Chg #15625: `yii\grid\DataColumn` boolean filter dropdown list values are now in reversed order (bizley)
- Chg #15633: Deprecated `yii\base\BaseObject::className()` in favor of native PHP syntax `::class`, which does not trigger autoloading (brandonkelly)
- Chg #15633: Deprecated XCache and Zend data cache support as caching backends (brandonkelly)
- Chg #15633: Deprecated `yii\BaseYii::powered()` method (brandonkelly)
- Chg #15633: Added `yii\base\InvalidArgumentException` and deprecated `yii\base\InvalidParamException` (brandonkelly)
- Chg #15633: Added `yii\BaseYii::debug()` and deprecated `yii\BaseYii::trace()` (brandonkelly)
2.0.13.1 November 14, 2017
--------------------------
@ -237,6 +291,7 @@ Yii Framework 2 Change Log
- Chg #14321: `yii\widgets\MaskedInput` is now registering its JavaScript `clientOptions` initialization code in head section (DaveFerger)
- Chg #14487: Changed i18n message error to warning (dmirogin)
2.0.12 June 05, 2017
--------------------
@ -902,13 +957,13 @@ Yii Framework 2 Change Log
- Bug #8684: Formatter ignored explicit decimal number settings when a default value is configured (leandrogehlen, cebe)
- Bug #8772: `yii\db\ActiveQuery` failed removing duplicate records after join when the resultset did not contain the pk values e.g. after grouping (cebe)
- Bug #8844: Added a workaround for an oracle bug when fetching information about table constraints and filtering by `CONSTRAINT_TYPE` (nidgetgod)
- Bug #8900: Fixed determining active menu item with url-alias in route `yii\widgets\Menu::isItemActive()` (demi)
- Bug #8900: Fixed determining active menu item with URL-alias in route `yii\widgets\Menu::isItemActive()` (demi)
- Bug #9006: Fixed bit column always returning true using certain version of PDO MySQL (stratoss, RusAlex, mj4444ru, samdark)
- Bug #9046: Fixed problem with endless error loop when an error occurred after sending a stream or file download response to the user (cebe)
- Bug #9059: Fixed PHP Notice in error handler view (dynasource, andrewnester, samdark)
- Bug #9063: Workaround for MySQL losing table case when adding index (sebathi)
- Bug #9076: Fixed `yii\filters\PageCache` not using the configured duration and dependency when caching the response data (kidol)
- Bug #9091: `yii\web\UrlManager::createUrl()` did not create correct url when defaults were used, internal cache is now skipped in certain situations (cebe)
- Bug #9091: `yii\web\UrlManager::createUrl()` did not create correct URL when defaults were used, internal cache is now skipped in certain situations (cebe)
- Bug #9127, #9128: Fixed MSSQL `yii\helpers\mssql\QueryBuilder::renameColumn()` and `yii\helpers\mssql\QueryBuilder::renameTable()` escaping (sitawit)
- Bug #9161: Fixed `yii\web\Request` ignore `queryParams` when resolve request (zetamen)
- Bug: Fixed string comparison in `yii\db\BaseActiveRecord::unlink()` which may result in wrong comparison result for hash valued primary keys starting with `0e` (cebe)
@ -979,7 +1034,7 @@ Yii Framework 2 Change Log
- Bug #7867: Fixed `findUniqueIndexes()` not to perform any processing on unique index on function for pgsql (nineinchnick)
- Bug #7868: Fixed fetching columns definition and composite foreign keys for oci (nineinchnick)
- Bug #7868: Removed column's autoIncrement detection from oci (nineinchnick)
- Bug #7868: Fixed creating raw sql (for logging) by skipping object and resource params (nineinchnick)
- Bug #7868: Fixed creating raw SQL (for logging) by skipping object and resource params (nineinchnick)
- Bug #7868: Fixed `yii\db\Schema::getLastInsertID()`` by quoting sequence name (nineinchnick)
- Bug #7957: Removed extra `parseFloat()` call for the `compare` js validator (CthulhuDen)
- Bug #8012: Fixed fetching multiple relations between two tables for pgsql (nineinchnick)
@ -1193,7 +1248,7 @@ Yii Framework 2 Change Log
- Bug #6632: `yii\di\Container::get()` did not handle config parameter correctly when it is passed as a constructor parameter (qiangxue)
- Bug #6648: Added explicit type casting to avoid dblib issues on SQL Server 2014 (o-rey)
- Bug #6691: Fixed console help description parsing with UTF8 characters (cebe)
- Bug #6717: Fixed issue with UrlManager not matching a route on url creation when it was prefixed with `/` and pattern was empty (cebe)
- Bug #6717: Fixed issue with UrlManager not matching a route on URL creation when it was prefixed with `/` and pattern was empty (cebe)
- Bug #6736: Removed `Content-Transfer-Encoding` from the list of default download headers (DaSourcerer)
- Enh #4502: Added alias support to URL route when calling `yii\helpers\Url::toRoute()` and `yii\helpers\Url::to()` (qiangxue, lynicidn)
- Enh #5194: `yii\console\controllers\AssetController` now handles bundle files from external resources properly (klimov-paul)
@ -1504,7 +1559,7 @@ Yii Framework 2 Change Log
- Bug #3567: Fixed the bug that smallint was treated as string for PostgreSQL (qiangxue)
- Bug #3568: When the primary query sets `asArray`, it is not respected by the `via` relational query (qiangxue)
- Bug #3578: Fixed postgreSQL column type detection, added missing types (MDMunir, cebe)
- Bug #3583: Added typecast to auto value of primary key on insert of sql active record (cebe)
- Bug #3583: Added typecast to auto value of primary key on insert of SQL Active Record (cebe)
- Bug #3591: Fixed incomplete obsolete filling in i18n `MessageController::saveMessagesToDb()` (advsm)
- Bug #3601: Fixed the bug that the refresh URL was not generated correctly by `Captcha` (qiangxue, klevron)
- Bug #3638: `yii\filters\HttpCache` does not work as expected when session is started before the filter (qiangxue)
@ -1612,7 +1667,7 @@ Yii Framework 2 Change Log
- Enh #3459: Added logging of errors, which may occur at `yii\caching\FileCache::gc()` (klimov-paul)
- Enh #3472: Added configurable option to encode spaces in dropDownLists and listBoxes (kartik-v)
- Enh #3518: `yii\helpers\Html::encode()` now replaces invalid code sequences with "<EFBFBD>" (DaSourcerer)
- Enh #3520: Added `unlinkAll()` method to active record to remove all records of a model relation (NmDimas, samdark, cebe)
- Enh #3520: Added `unlinkAll()` method to Active Record to remove all records of a model relation (NmDimas, samdark, cebe)
- Enh #3521: Added `yii\filters\HttpCache::sessionCacheLimiter` (qiangxue)
- Enh #3542: Removed requirement to specify `extensions` in application config (samdark)
- Enh #3562: Adding rotateByCopy to `yii\log\FileTarget` (pawzar)
@ -1666,7 +1721,7 @@ Yii Framework 2 Change Log
- Enh #4485: Added support for deferred validation in `yii\widgets\ActiveForm` (Alex-Code)
- Enh #4520: Added sasl support to `yii\caching\MemCache` (xjflyttp)
- Enh #4566: Added client validation support for image validator (Skysplit, qiangxue)
- Enh #4581: Added ability to disable url encoding in `yii\web\UrlRule` (tadaszelvys)
- Enh #4581: Added ability to disable URL encoding in `yii\web\UrlRule` (tadaszelvys)
- Enh #4602: Added $key param in `yii\grid\ActionColumn` buttons Closure call (disem)
- Enh #4607: AR model will throw an exception if it does not have a primary key to avoid updating/deleting data massively (qiangxue)
- Enh #4630: Added automatic generating of unique slug value to `yii\behaviors\Sluggable` (klimov-paul)
@ -1779,7 +1834,7 @@ Yii Framework 2 Change Log
- Bug #1500: Log messages exported to files are not separated by newlines (omnilight, qiangxue)
- Bug #1504: Debug toolbar isn't loaded successfully in some environments when xdebug is enabled (qiangxue)
- Bug #1509: The SQL for creating Postgres RBAC tables is incorrect (qiangxue)
- Bug #1545: It was not possible to execute db Query twice, params where missing (cebe)
- Bug #1545: It was not possible to execute DB Query twice, params where missing (cebe)
- Bug #1550: fixed the issue that JUI input widgets did not property input IDs.
- Bug #1654: Fixed the issue that a new message source object is generated for every new message being translated (qiangxue)
- Bug #1582: Error messages shown via client-side validation should not be double encoded (qiangxue)
@ -1796,7 +1851,7 @@ Yii Framework 2 Change Log
- Bug #1812: Hide potential warning message due to race condition occurring to `Session::regenerateID()` call (qiangxue)
- Bug #1827: Debugger toolbar is loaded twice if an action is calling `run()` to execute another action (qiangxue)
- Bug #1868: Added ability to exclude tables from `yii\console\controllers\FixtureController` apply/clear actions. (Ragazzo)
- Bug #1869: Fixed tables clearing. `TRUNCATE` changed to `DELETE` to avoid postgresql tables checks (and truncating all tables) (Ragazzo)
- Bug #1869: Fixed tables clearing. `TRUNCATE` changed to `DELETE` to avoid PostgreSQL tables checks (and truncating all tables) (Ragazzo)
- Bug #1870: Validation errors weren't properly translated when using clientside validation (samdark)
- Bug #1930: Fixed domain based URL matching for website root (samdark)
- Bug #1937: Fixed wrong behavior or advanced app's `init --env` when called without parameter actually specified (samdark)
@ -1869,13 +1924,13 @@ Yii Framework 2 Change Log
- Enh #1572: Added `yii\web\Controller::createAbsoluteUrl()` (samdark)
- Enh #1579: throw exception when the given AR relation name does not match in a case sensitive manner (qiangxue)
- Enh #1581: Added `yii\db\ActiveQuery::joinWith()` and `yii\db\ActiveQuery::innerJoinWith()` to support joining with relations (qiangxue)
- Enh #1585: added schema parameter to `createAbsoluteUrl()`` to force 'http' or 'https' (cebe)
- Enh #1585: added schema parameter to `createAbsoluteUrl()` to force 'http' or 'https' (cebe)
- Enh #1601: Added support for tagName and encodeLabel parameters in ButtonDropdown (omnilight)
- Enh #1611: Added `yii\db\BaseActiveRecord::markAttributeDirty()` (qiangxue)
- Enh #1633: Advanced project template now works with MongoDB by default (samdark)
- Enh #1634: Use masked CSRF tokens to prevent BREACH exploits (qiangxue)
- Enh #1641: Added `BaseActiveRecord::updateAttributes()` (qiangxue)
- Enh #1646: Added postgresql `yii\db\QueryBuilder::checkIntegrity()` and `yii\db\QueryBuilder::resetSequence()` (Ragazzo)
- Enh #1646: Added PostgreSQL `yii\db\QueryBuilder::checkIntegrity()` and `yii\db\QueryBuilder::resetSequence()` (Ragazzo)
- Enh #1645: Added `yii\db\Connection::$pdoClass` property (Ragazzo)
- Enh #1645: Added support for nested DB transactions (qiangxue)
- Enh #1681: Added support for automatically adjusting the "for" attribute of label generated by `yii\widgets\ActiveField::label()` (qiangxue)
@ -1904,7 +1959,7 @@ Yii Framework 2 Change Log
- Enh #2102: `yii\widgets\DetailView` now allow use custom label in string format like `name:format:label` (creocoder)
- Enh #2103: Renamed `yii\web\AccessDeniedHttpException` to `yii\web\ForbiddenHttpException`, added new commonly used HTTP exception classes (danschmidt5189)
- Enh #2124: Added support for UNION ALL queries (Ivan Pomortsev, iworker)
- Enh #2132: Allow url of CSS and JS files registered in yii\web\View to be url alias (cebe)
- Enh #2132: Allow URL of CSS and JS files registered in yii\web\View to be URL alias (cebe)
- Enh #2144: `Html` helper now supports rendering "data" attributes (qiangxue)
- Enh #2156: `yii migrate` now automatically creates `migrations` directory if it does not exist (samdark)
- Enh #2211: Added typecast database types into php types (dizews)
@ -1995,7 +2050,7 @@ Yii Framework 2 Change Log
- Chg #2161: Added ability to use `return` in `Widget::run` (samdark)
- Chg #2173: Removed `yii\helpers\StringHelper::diff()`, Moved `phpspec/php-diff` dependency from `yiisoft/yii2` to `yiisoft/yii2-gii` (samdark)
- Chg #2175: `yii\db\QueryBuilder` will now append UNION statements at the end of the primary SQL (qiangxue)
- Chg #2210: Mysql driver will now treat `tinyint(1)` as integer instead of boolean (qiangxue)
- Chg #2210: MySQL driver will now treat `tinyint(1)` as integer instead of boolean (qiangxue)
- Chg #2248: Renamed `yii\base\Model::DEFAULT_SCENARIO` to `yii\base\Model::SCENARIO_DEFAULT` (samdark)
- Chg #2281: Renamed `yii\db\ActiveRecord::create()` to `populateRecord()` and changed signature. This method will not call instantiate() anymore (cebe)
- Chg #2405: The CSS class of `yii\widgets\MaskedInput` now defaults to `form-control` (qiangxue)

34
framework/UPGRADE.md

@ -167,7 +167,39 @@ Upgrade from Yii 2.0.13
* Constants `IPV6_ADDRESS_LENGTH`, `IPV4_ADDRESS_LENGTH` were moved from `yii\validators\IpValidator` to `yii\helpers\IpHelper`.
If your application relies on these constants, make sure to update your code to follow the changes.
* `yii\base\Security::compareString()` is now throwing `yii\base\InvalidParamException` in case non-strings are compared.
* `yii\base\Security::compareString()` is now throwing `yii\base\InvalidArgumentException` in case non-strings are compared.
* `yii\db\ExpressionInterface` has been introduced to represent a wider range of SQL expressions. In case you check for
`instanceof yii\db\Expression` in your code, you might consider changing that to checking for the interface and use the newly
introduced methods to retrieve the expression content.
* Added JSON support for PostgreSQL and MySQL as well as Arrays support for PostgreSQL in ActiveRecord layer.
In case you already implemented such support yourself, please switch to Yii implementation. Active Record will
return arrays instead of strings after data population and expects arrays to be assigned for further saving them into database.
* `yii\db\PdoValue` class has been introduced to replace a special syntax that was used to declare PDO parameter type
when binding parameters to an SQL command, for example: `['value', \PDO::PARAM_STR]`.
You should use `new PdoValue('value', \PDO::PARAM_STR)` instead. Old syntax will be removed in Yii 2.1.
* `yii\db\QueryBuilder::conditionBuilders` property and method-based condition builders are no longer used.
Class-based conditions and builders are introduced instead to provide more flexibility, extensibility and
space to customization. In case you rely on that property or override any of default condition builders, follow the
special [guide article](http://www.yiiframework.com/doc-2.0/guide-db-query-builder.html#adding-custom-conditions-and-expressions)
to update your code.
* Protected method `yii\db\ActiveQueryTrait::createModels()` does not apply indexes as defined in `indexBy` property anymore.
In case you override default ActiveQuery implementation and relied on that behavior, call `yii\db\Query::populate()`
method instead to index query results according to the `indexBy` parameter.
* Log targets (like `yii\log\EmailTarget`) are now throwing `yii\log\LogRuntimeException` in case log can not be properly exported.
* You can start preparing your application for Yii 2.1 by doing the following:
- Replace `::className()` calls with `::class` (if you’re running PHP 5.5+).
- Replace usages of `yii\base\InvalidParamException` with `yii\base\InvalidArgumentException`.
- Replace calls to `Yii::trace()` with `Yii::debug()`.
- Remove calls to `yii\BaseYii::powered()`.
- If you are using XCache or Zend data cache, those are going away in 2.1 so you might want to start looking for an alternative.
Upgrade from Yii 2.0.12
-----------------------

2
framework/Yii.php

@ -5,6 +5,8 @@
* @license http://www.yiiframework.com/license/
*/
require __DIR__ . '/BaseYii.php';
/**
* Yii is a helper class serving common framework functionalities.
*

1
framework/base/ActionFilter.php

@ -6,6 +6,7 @@
*/
namespace yii\base;
use yii\helpers\StringHelper;
/**

9
framework/base/ArrayableTrait.php

@ -123,7 +123,7 @@ trait ArrayableTrait
{
$data = [];
foreach ($this->resolveFields($fields, $expand) as $field => $definition) {
$attribute = is_string($definition) ? $this->$definition : call_user_func($definition, $this, $field);
$attribute = is_string($definition) ? $this->$definition : $definition($this, $field);
if ($recursive) {
$nestedFields = $this->extractFieldsFor($fields, $field);
@ -135,9 +135,8 @@ trait ArrayableTrait
function ($item) use ($nestedFields, $nestedExpand) {
if ($item instanceof Arrayable) {
return $item->toArray($nestedFields, $nestedExpand);
} else {
return $item;
}
return $item;
},
$attribute
);
@ -167,7 +166,7 @@ trait ArrayableTrait
$result = [];
foreach ($fields as $field) {
$result[] = current(explode(".", $field, 2));
$result[] = current(explode('.', $field, 2));
}
if (in_array('*', $result, true)) {
@ -193,7 +192,7 @@ trait ArrayableTrait
foreach ($fields as $field) {
if (0 === strpos($field, "{$rootField}.")) {
$result[] = preg_replace("/^{$rootField}\./i", '', $field);
$result[] = preg_replace('/^' . preg_quote($rootField, '/') . '\./i', '', $field);
}
}

5
framework/base/Component.php

@ -552,8 +552,7 @@ class Component extends BaseObject
return false;
}
if ($handler === null) {
unset($this->_events[$name]);
unset($this->_eventWildcards[$name]);
unset($this->_events[$name], $this->_eventWildcards[$name]);
return true;
}
@ -582,7 +581,7 @@ class Component extends BaseObject
}
if ($removed) {
$this->_eventWildcards[$name] = array_values($this->_eventWildcards[$name]);
// remove empty wildcards to save future redundant regex checks :
// remove empty wildcards to save future redundant regex checks:
if (empty($this->_eventWildcards[$name])) {
unset($this->_eventWildcards[$name]);
}

40
framework/base/DynamicContentAwareInterface.php

@ -0,0 +1,40 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* DynamicContentAwareInterface is the interface that should be implemented by classes
* which support a [[View]] dynamic content feature.
*
* @author Sergey Makinen <sergey@makinen.ru>
* @since 2.0.14
*/
interface DynamicContentAwareInterface
{
/**
* Returns a list of placeholders for dynamic content. This method
* is used internally to implement the content caching feature.
* @return array a list of placeholders.
*/
public function getDynamicPlaceholders();
/**
* Sets a list of placeholders for dynamic content. This method
* is used internally to implement the content caching feature.
* @param array $placeholders a list of placeholders.
*/
public function setDynamicPlaceholders($placeholders);
/**
* Adds a placeholder for dynamic content.
* This method is used internally to implement the content caching feature.
* @param string $name the placeholder name.
* @param string $statements the PHP statements for generating the dynamic content.
*/
public function addDynamicPlaceholder($name, $statements);
}

82
framework/base/DynamicContentAwareTrait.php

@ -0,0 +1,82 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* DynamicContentAwareTrait implements common methods for classes
* which support a [[View]] dynamic content feature.
*
* @author Sergey Makinen <sergey@makinen.ru>
* @since 2.0.14
*/
trait DynamicContentAwareTrait
{
/**
* @var string[] a list of placeholders for dynamic content
*/
private $_dynamicPlaceholders;
/**
* Returns the view object that can be used to render views or view files using dynamic contents.
* @return View the view object that can be used to render views or view files.
*/
abstract protected function getView();
/**
* {@inheritdoc}
*/
public function getDynamicPlaceholders()
{
return $this->_dynamicPlaceholders;
}
/**
* {@inheritdoc}
*/
public function setDynamicPlaceholders($placeholders)
{
$this->_dynamicPlaceholders = $placeholders;
}
/**
* {@inheritdoc}
*/
public function addDynamicPlaceholder($name, $statements)
{
$this->_dynamicPlaceholders[$name] = $statements;
}
/**
* Replaces placeholders in $content with results of evaluated dynamic statements.
* @param string $content content to be parsed.
* @param string[] $placeholders placeholders and their values.
* @param bool $isRestoredFromCache whether content is going to be restored from cache.
* @return string final content.
*/
protected function updateDynamicContent($content, $placeholders, $isRestoredFromCache = false)
{
if (empty($placeholders) || !is_array($placeholders)) {
return $content;
}
if (count($this->getView()->getDynamicContents()) === 0) {
// outermost cache: replace placeholder with dynamic content
foreach ($placeholders as $name => $statements) {
$placeholders[$name] = $this->getView()->evaluateDynamicContent($statements);
}
$content = strtr($content, $placeholders);
}
if ($isRestoredFromCache) {
foreach ($placeholders as $name => $statements) {
$this->getView()->addDynamicPlaceholder($name, $statements);
}
}
return $content;
}
}

1
framework/base/Event.php

@ -297,6 +297,7 @@ class Event extends BaseObject
foreach ($wildcardEventHandlers as $classWildcard => $handlers) {
if (StringHelper::matchWildcard($classWildcard, $class)) {
$eventHandlers = array_merge($eventHandlers, $handlers);
unset($wildcardEventHandlers[$classWildcard]);
}
}

1
framework/base/InvalidArgumentException.php

@ -12,6 +12,7 @@ namespace yii\base;
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
* @deprecated since 2.0.14. Use [[InvalidArgumentException]] instead.
*/
class InvalidArgumentException extends \BadMethodCallException
{

2
framework/base/Module.php

@ -649,6 +649,8 @@ class Module extends ServiceLocator
}
/**
* Checks if class name or prefix is incorrect
*
* @param string $className
* @param string $prefix
* @return bool

71
framework/base/View.php

@ -26,7 +26,7 @@ use yii\widgets\FragmentCache;
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class View extends Component
class View extends Component implements DynamicContentAwareInterface
{
/**
* @event Event an event that is triggered by [[beginPage()]].
@ -86,15 +86,19 @@ class View extends Component
*/
public $blocks;
/**
* @var array a list of currently active fragment cache widgets. This property
* is used internally to implement the content caching feature. Do not modify it directly.
* @var array|DynamicContentAwareInterface[] a list of currently active dynamic content class instances.
* This property is used internally to implement the dynamic content caching feature. Do not modify it directly.
* @internal
* @deprecated Since 2.0.14. Do not use this property directly. Use methods [[getDynamicContents()]],
* [[pushDynamicContent()]], [[popDynamicContent()]] instead.
*/
public $cacheStack = [];
/**
* @var array a list of placeholders for embedding dynamic contents. This property
* is used internally to implement the content caching feature. Do not modify it directly.
* @internal
* @deprecated Since 2.0.14. Do not use this property directly. Use methods [[getDynamicPlaceholders()]],
* [[setDynamicPlaceholders()]], [[addDynamicPlaceholder()]] instead.
*/
public $dynamicPlaceholders = [];
@ -378,18 +382,36 @@ class View extends Component
}
/**
* Adds a placeholder for dynamic content.
* This method is internally used.
* @param string $placeholder the placeholder name
* @param string $statements the PHP statements for generating the dynamic content
* {@inheritdoc}
*/
public function getDynamicPlaceholders()
{
return $this->dynamicPlaceholders;
}
/**
* {@inheritdoc}
*/
public function setDynamicPlaceholders($placeholders)
{
$this->dynamicPlaceholders = $placeholders;
}
/**
* {@inheritdoc}
*/
public function addDynamicPlaceholder($placeholder, $statements)
{
foreach ($this->cacheStack as $cache) {
$cache->dynamicPlaceholders[$placeholder] = $statements;
if ($cache instanceof DynamicContentAwareInterface) {
$cache->addDynamicPlaceholder($placeholder, $statements);
} else {
// TODO: Remove in 2.1
$cache->dynamicPlaceholders[$placeholder] = $statements;
}
}
$this->dynamicPlaceholders[$placeholder] = $statements;
}
}
/**
* Evaluates the given PHP statements.
@ -403,6 +425,37 @@ class View extends Component
}
/**
* Returns a list of currently active dynamic content class instances.
* @return DynamicContentAwareInterface[] class instances supporting dynamic contents.
* @since 2.0.14
*/
public function getDynamicContents()
{
return $this->cacheStack;
}
/**
* Adds a class instance supporting dynamic contents to the end of a list of currently active
* dynamic content class instances.
* @param DynamicContentAwareInterface $instance class instance supporting dynamic contents.
* @since 2.0.14
*/
public function pushDynamicContent(DynamicContentAwareInterface $instance)
{
$this->cacheStack[] = $instance;
}
/**
* Removes a last class instance supporting dynamic contents from a list of currently active
* dynamic content class instances.
* @since 2.0.14
*/
public function popDynamicContent()
{
array_pop($this->cacheStack);
}
/**
* Begins recording a block.
*
* This method is a shortcut to beginning [[Block]].

25
framework/behaviors/AttributeTypecastBehavior.php

@ -162,6 +162,15 @@ class AttributeTypecastBehavior extends Behavior
*/
public $typecastBeforeSave = false;
/**
* @var bool whether to perform typecasting after saving owner model (insert or update).
* This option may be disabled in order to achieve better performance.
* For example, in case of [[\yii\db\ActiveRecord]] usage, typecasting after save
* will grant no benefit an thus can be disabled.
* Note that changing this option value will have no effect after this behavior has been attached to the model.
* @since 2.0.14
*/
public $typecastAfterSave = false;
/**
* @var bool whether to perform typecasting after retrieving owner model data from
* the database (after find or refresh).
* This option may be disabled in order to achieve better performance.
@ -307,6 +316,10 @@ class AttributeTypecastBehavior extends Behavior
$events[BaseActiveRecord::EVENT_BEFORE_INSERT] = 'beforeSave';
$events[BaseActiveRecord::EVENT_BEFORE_UPDATE] = 'beforeSave';
}
if ($this->typecastAfterSave) {
$events[BaseActiveRecord::EVENT_AFTER_INSERT] = 'afterSave';
$events[BaseActiveRecord::EVENT_AFTER_UPDATE] = 'afterSave';
}
if ($this->typecastAfterFind) {
$events[BaseActiveRecord::EVENT_AFTER_FIND] = 'afterFind';
}
@ -326,13 +339,23 @@ class AttributeTypecastBehavior extends Behavior
}
/**
* Handles owner 'afterInsert' and 'afterUpdate' events, ensuring attribute typecasting.
* Handles owner 'beforeInsert' and 'beforeUpdate' events, ensuring attribute typecasting.
* @param \yii\base\Event $event event instance.
*/
public function beforeSave($event)
{
$this->typecastAttributes();
}
/**
* Handles owner 'afterInsert' and 'afterUpdate' events, ensuring attribute typecasting.
* @param \yii\base\Event $event event instance.
* @since 2.0.14
*/
public function afterSave($event)
{
$this->typecastAttributes();
}
/**
* Handles owner 'afterFind' event, ensuring attribute typecasting.

2
framework/behaviors/BlameableBehavior.php

@ -72,13 +72,13 @@ class BlameableBehavior extends AttributeBehavior
* In case, when the property is `null`, the value of `Yii::$app->user->id` will be used as the value.
*/
public $value;
/**
* @var mixed Default value for cases when the user is guest
* @since 2.0.14
*/
public $defaultValue;
/**
* {@inheritdoc}
*/

199
framework/behaviors/CacheableWidgetBehavior.php

@ -0,0 +1,199 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\behaviors;
use yii\base\Behavior;
use yii\base\InvalidConfigException;
use yii\base\Widget;
use yii\base\WidgetEvent;
use yii\caching\CacheInterface;
use yii\caching\Dependency;
use yii\di\Instance;
/**
* Cacheable widget behavior automatically caches widget contents according to duration and dependencies specified.
*
* The behavior may be used without any configuration if an application has `cache` component configured.
* By default the widget will be cached for one minute.
*
* The following example will cache the posts widget for an indefinite duration until any post is modified.
*
* ```php
* use yii\behaviors\CacheableWidgetBehavior;
*
* public function behaviors()
* {
* return [
* [
* 'class' => CacheableWidgetBehavior::className(),
* 'cacheDuration' => 0,
* 'cacheDependency' => [
* 'class' => 'yii\caching\DbDependency',
* 'sql' => 'SELECT MAX(updated_at) FROM posts',
* ],
* ],
* ];
* }
* ```
*
* @property Widget $owner
*
* @author Nikolay Oleynikov <oleynikovny@mail.ru>
* @since 2.0.14
*/
class CacheableWidgetBehavior extends Behavior
{
/**
* @var CacheInterface|string|array a cache object or a cache component ID
* or a configuration array for creating a cache object.
* Defaults to the `cache` application component.
*/
public $cache = 'cache';
/**
* @var int cache duration in seconds.
* Set to `0` to indicate that the cached data will never expire.
* Defaults to 60 seconds or 1 minute.
*/
public $cacheDuration = 60;
/**
* @var Dependency|array|null a cache dependency or a configuration array
* for creating a cache dependency or `null` meaning no cache dependency.
*
* For example,
*
* ```php
* [
* 'class' => 'yii\caching\DbDependency',
* 'sql' => 'SELECT MAX(updated_at) FROM posts',
* ]
* ```
*
* would make the widget cache depend on the last modified time of all posts.
* If any post has its modification time changed, the cached content would be invalidated.
*/
public $cacheDependency;
/**
* @var string[]|string an array of strings or a single string which would cause
* the variation of the content being cached (e.g. an application language, a GET parameter).
*
* The following variation setting will cause the content to be cached in different versions
* according to the current application language:
*
* ```php
* [
* Yii::$app->language,
* ]
* ```
*/
public $cacheKeyVariations = [];
/**
* @var bool whether to enable caching or not. Allows to turn the widget caching
* on and off according to specific conditions.
* The following configuration will disable caching when a special GET parameter is passed:
*
* ```php
* empty(Yii::$app->request->get('disable-caching'))
* ```
*/
public $cacheEnabled = true;
/**
* {@inheritdoc}
*/
public function attach($owner)
{
parent::attach($owner);
$this->initializeEventHandlers();
}
/**
* Begins fragment caching. Prevents owner widget from execution
* if its contents can be retrieved from the cache.
*
* @param WidgetEvent $event `Widget::EVENT_BEFORE_RUN` event.
*/
public function beforeRun($event)
{
$cacheKey = $this->getCacheKey();
$fragmentCacheConfiguration = $this->getFragmentCacheConfiguration();
if (!$this->owner->view->beginCache($cacheKey, $fragmentCacheConfiguration)) {
$event->isValid = false;
}
}
/**
* Outputs widget contents and ends fragment caching.
*
* @param WidgetEvent $event `Widget::EVENT_AFTER_RUN` event.
*/
public function afterRun($event)
{
echo $event->result;
$event->result = null;
$this->owner->view->endCache();
}
/**
* Initializes widget event handlers.
*/
private function initializeEventHandlers()
{
$this->owner->on(Widget::EVENT_BEFORE_RUN, [$this, 'beforeRun']);
$this->owner->on(Widget::EVENT_AFTER_RUN, [$this, 'afterRun']);
}
/**
* Returns the cache instance.
*
* @return CacheInterface cache instance.
* @throws InvalidConfigException if cache instance instantiation fails.
*/
private function getCacheInstance()
{
$cacheInterface = 'yii\caching\CacheInterface';
return Instance::ensure($this->cache, $cacheInterface);
}
/**
* Returns the widget cache key.
*
* @return string[] an array of strings representing the cache key.
*/
private function getCacheKey()
{
// `$cacheKeyVariations` may be a `string` and needs to be cast to an `array`.
$cacheKey = array_merge(
(array)get_class($this->owner),
(array)$this->cacheKeyVariations
);
return $cacheKey;
}
/**
* Returns a fragment cache widget configuration array.
*
* @return array a fragment cache widget configuration array.
*/
private function getFragmentCacheConfiguration()
{
$cache = $this->getCacheInstance();
$fragmentCacheConfiguration = [
'cache' => $cache,
'duration' => $this->cacheDuration,
'dependency' => $this->cacheDependency,
'enabled' => $this->cacheEnabled,
];
return $fragmentCacheConfiguration;
}
}

7
framework/caching/DbCache.php

@ -10,6 +10,7 @@ namespace yii\caching;
use Yii;
use yii\base\InvalidConfigException;
use yii\db\Connection;
use yii\db\PdoValue;
use yii\db\Query;
use yii\di\Instance;
@ -134,6 +135,7 @@ class DbCache extends SimpleCache
$this->db->enableQueryCache = false;
$result = $query->createCommand($this->db)->queryScalar();
$this->db->enableQueryCache = true;
return $result;
}
@ -183,13 +185,14 @@ class DbCache extends SimpleCache
$command = $db->createCommand()
->update($this->cacheTable, [
'expire' => $ttl > 0 ? $ttl + time() : 0,
'data' => [$value, \PDO::PARAM_LOB],
'data' => new PdoValue($value, \PDO::PARAM_LOB),
], ['id' => $key]);
return $command->execute();
});
if ($result) {
$this->gc();
return true;
}
@ -215,7 +218,7 @@ class DbCache extends SimpleCache
->insert($this->cacheTable, [
'id' => $key,
'expire' => $duration > 0 ? $duration + time() : 0,
'data' => [$value, \PDO::PARAM_LOB],
'data' => new PdoValue($value, \PDO::PARAM_LOB),
])->execute();
});

1
framework/caching/ZendDataCache.php

@ -36,6 +36,7 @@ namespace yii\caching;
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
* @deprecated since 2.0.14. This class will be removed in 2.1.0.
*/
class ZendDataCache extends SimpleCache
{

6
framework/console/Controller.php

@ -113,7 +113,8 @@ class Controller extends \yii\base\Controller
foreach ($params as $name => $value) {
// Allow camelCase options to be entered in kebab-case
if (!in_array($name, $options, true) && strpos($name, '-') !== false) {
$altName = lcfirst(Inflector::id2camel($name));
$kebabName = $name;
$altName = lcfirst(Inflector::id2camel($kebabName));
if (in_array($altName, $options, true)) {
$name = $altName;
}
@ -131,6 +132,9 @@ class Controller extends \yii\base\Controller
}
$this->_passedOptions[] = $name;
unset($params[$name]);
if (isset($kebabName)) {
unset($params[$kebabName]);
}
} elseif (!is_int($name)) {
throw new Exception(Yii::t('yii', 'Unknown option: --{name}', ['name' => $name]));
}

1
framework/console/widgets/Table.php

@ -50,7 +50,6 @@ class Table extends Widget
{
const DEFAULT_CONSOLE_SCREEN_WIDTH = 120;
const CONSOLE_SCROLLBAR_OFFSET = 3;
const CHAR_TOP = 'top';
const CHAR_TOP_MID = 'top-mid';
const CHAR_TOP_LEFT = 'top-left';

61
framework/data/DataFilter.php

@ -15,6 +15,8 @@ use yii\validators\BooleanValidator;
use yii\validators\EachValidator;
use yii\validators\NumberValidator;
use yii\validators\StringValidator;
use yii\validators\DateValidator;
use yii\validators\Validator;
/**
* DataFilter is a special [[Model]] for processing query filtering specification.
@ -128,6 +130,9 @@ class DataFilter extends Model
const TYPE_BOOLEAN = 'boolean';
const TYPE_STRING = 'string';
const TYPE_ARRAY = 'array';
const TYPE_DATETIME = 'datetime';
const TYPE_DATE = 'date';
const TYPE_TIME = 'time';
/**
* @var string name of the attribute that handles filter value.
@ -204,10 +209,10 @@ class DataFilter extends Model
* Any unspecified keyword will not be considered as a valid operator.
*/
public $operatorTypes = [
'<' => [self::TYPE_INTEGER, self::TYPE_FLOAT],
'>' => [self::TYPE_INTEGER, self::TYPE_FLOAT],
'<=' => [self::TYPE_INTEGER, self::TYPE_FLOAT],
'>=' => [self::TYPE_INTEGER, self::TYPE_FLOAT],
'<' => [self::TYPE_INTEGER, self::TYPE_FLOAT, self::TYPE_DATETIME, self::TYPE_DATE, self::TYPE_TIME],
'>' => [self::TYPE_INTEGER, self::TYPE_FLOAT, self::TYPE_DATETIME, self::TYPE_DATE, self::TYPE_TIME],
'<=' => [self::TYPE_INTEGER, self::TYPE_FLOAT, self::TYPE_DATETIME, self::TYPE_DATE, self::TYPE_TIME],
'>=' => [self::TYPE_INTEGER, self::TYPE_FLOAT, self::TYPE_DATETIME, self::TYPE_DATE, self::TYPE_TIME],
'=' => '*',
'!=' => '*',
'IN' => '*',
@ -330,16 +335,7 @@ class DataFilter extends Model
}
foreach ($model->getValidators() as $validator) {
$type = null;
if ($validator instanceof BooleanValidator) {
$type = self::TYPE_BOOLEAN;
} elseif ($validator instanceof NumberValidator) {
$type = $validator->integerOnly ? self::TYPE_INTEGER : self::TYPE_FLOAT;
} elseif ($validator instanceof StringValidator) {
$type = self::TYPE_STRING;
} elseif ($validator instanceof EachValidator) {
$type = self::TYPE_ARRAY;
}
$type = $this->detectSearchAttributeType($validator);
if ($type !== null) {
foreach ((array) $validator->attributes as $attribute) {
@ -352,6 +348,43 @@ class DataFilter extends Model
}
/**
* Detect attribute type from given validator.
*
* @param Validator validator from which to detect attribute type.
* @return string|null detected attribute type.
* @since 2.0.14
*/
protected function detectSearchAttributeType(Validator $validator)
{
if ($validator instanceof BooleanValidator) {
return self::TYPE_BOOLEAN;
}
if ($validator instanceof NumberValidator) {
return $validator->integerOnly ? self::TYPE_INTEGER : self::TYPE_FLOAT;
}
if ($validator instanceof StringValidator) {
return self::TYPE_STRING;
}
if ($validator instanceof EachValidator) {
return self::TYPE_ARRAY;
}
if ($validator instanceof DateValidator) {
if ($validator->type == DateValidator::TYPE_DATETIME) {
return self::TYPE_DATETIME;
}
if ($validator->type == DateValidator::TYPE_TIME) {
return self::TYPE_TIME;
}
return self::TYPE_DATE;
}
}
/**
* @return array error messages in format `[errorKey => message]`.
*/
public function getErrorMessages()

15
framework/db/ActiveQuery.php

@ -222,7 +222,7 @@ class ActiveQuery extends Query implements ActiveQueryInterface
}
}
return $models;
return parent::populate($models);
}
/**
@ -319,7 +319,10 @@ class ActiveQuery extends Query implements ActiveQueryInterface
$params = $this->params;
}
return $db->createCommand($sql, $params);
$command = $db->createCommand($sql, $params);
$this->setCommandCache($command);
return $command;
}
/**
@ -337,11 +340,13 @@ class ActiveQuery extends Query implements ActiveQueryInterface
return parent::queryScalar($selectExpression, $db);
}
return (new Query())->select([$selectExpression])
$command = (new Query())->select([$selectExpression])
->from(['c' => "({$this->sql})"])
->params($this->params)
->createCommand($db)
->queryScalar();
->createCommand($db);
$this->setCommandCache($command);
return $command->queryScalar();
}
/**

24
framework/db/ActiveQueryTrait.php

@ -111,21 +111,11 @@ trait ActiveQueryTrait
*/
protected function createModels($rows)
{
$models = [];
if ($this->asArray) {
if ($this->indexBy === null) {
return $rows;
}
foreach ($rows as $row) {
if (is_string($this->indexBy)) {
$key = $row[$this->indexBy];
} else {
$key = call_user_func($this->indexBy, $row);
}
$models[$key] = $row;
}
return $rows;
} else {
/* @var $class BaseActiveRecord */
$models = [];
/* @var $class ActiveRecord */
$class = $this->modelClass;
if ($this->indexBy === null) {
foreach ($rows as $row) {
@ -148,10 +138,14 @@ trait ActiveQueryTrait
}
$models[$key] = $model;
}
foreach ($rows as $row) {
$model = $class::instantiate($row);
$modelClass = get_class($model);
$modelClass::populateRecord($model, $row);
$models[] = $model;
}
return $models;
}
return $models;
}
/**

8
framework/db/ActiveRecord.php

@ -194,13 +194,17 @@ class ActiveRecord extends BaseActiveRecord
*/
public function refresh()
{
$query = static::find();
$tableName = key($query->getTablesUsedInFrom());
$pk = [];
// disambiguate column names in case ActiveQuery adds a JOIN
foreach ($this->getPrimaryKey(true) as $key => $value) {
$pk[static::tableName() . '.' . $key] = $value;
$pk[$tableName . '.' . $key] = $value;
}
$query->where($pk);
/* @var $record BaseActiveRecord */
$record = static::findOne($pk);
$record = $query->one();
return $this->refreshInternal($record);
}

5
framework/db/ActiveRelationTrait.php

@ -7,8 +7,8 @@
namespace yii\db;
use yii\base\InvalidConfigException;
use yii\base\InvalidArgumentException;
use yii\base\InvalidConfigException;
/**
* ActiveRelationTrait implements the common methods and properties for active record relational queries.
@ -177,8 +177,7 @@ trait ActiveRelationTrait
$method = new \ReflectionMethod($model, 'get' . $name);
$realName = lcfirst(substr($method->getName(), 3));
if ($realName !== $name) {
throw new InvalidArgumentException('Relation names are case sensitive. ' . get_class($model)
. " has a relation named \"$realName\" instead of \"$name\".");
throw new InvalidArgumentException('Relation names are case sensitive. ' . get_class($model) . " has a relation named \"$realName\" instead of \"$name\".");
}
}

170
framework/db/ArrayExpression.php

@ -0,0 +1,170 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db;
/**
* Class ArrayExpression represents an array SQL expression.
*
* Expressions of this type can be used in conditions as well:
*
* ```php
* $query->andWhere(['@>', 'items', new ArrayExpression([1, 2, 3], 'integer')])
* ```
*
* which, depending on DBMS, will result in a well-prepared condition. For example, in
* PostgreSQL it will be compiled to `WHERE "items" @> ARRAY[1, 2, 3]::integer[]`.
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class ArrayExpression implements ExpressionInterface, \ArrayAccess, \Countable
{
/**
* @var null|string the type of the array elements. Defaults to `null` which means the type is
* not explicitly specified.
*
* Note that in case when type is not specified explicitly and DBMS can not guess it from the context,
* SQL error will be raised.
*/
private $type;
/**
* @var array|QueryInterface|mixed the array content. Either represented as an array of values or a [[Query]] that
* returns these values. A single value will be considered as an array containing one element.
*/
private $value;
/**
* @var int the number of indices needed to select an element
*/
private $dimension;
/**
* ArrayExpression constructor.
*
* @param array|QueryInterface|mixed $value the array content. Either represented as an array of values or a Query that
* returns these values. A single value will be considered as an array containing one element.
* @param string|null $type the type of the array elements. Defaults to `null` which means the type is
* not explicitly specified. In case when type is not specified explicitly and DBMS can not guess it from the context,
* SQL error will be raised.
* @param int $dimension the number of indices needed to select an element
*/
public function __construct($value, $type = null, $dimension = 1)
{
$this->value = $value;
$this->type = $type;
$this->dimension = $dimension;
}
/**
* @return null|string
* @see type
*/
public function getType()
{
return $this->type;
}
/**
* @return array|mixed|QueryInterface
* @see value
*/
public function getValue()
{
return $this->value;
}
/**
* @return int the number of indices needed to select an element
* @see dimensions
*/
public function getDimension()
{
return $this->dimension;
}
/**
* Whether a offset exists
*
* @link http://php.net/manual/en/arrayaccess.offsetexists.php
* @param mixed $offset <p>
* An offset to check for.
* </p>
* @return bool true on success or false on failure.
* </p>
* <p>
* The return value will be casted to boolean if non-boolean was returned.
* @since 5.0.0
*/
public function offsetExists($offset)
{
return isset($this->value[$offset]);
}
/**
* Offset to retrieve
*
* @link http://php.net/manual/en/arrayaccess.offsetget.php
* @param mixed $offset <p>
* The offset to retrieve.
* </p>
* @return mixed Can return all value types.
* @since 5.0.0
*/
public function offsetGet($offset)
{
return $this->value[$offset];
}
/**
* Offset to set
*
* @link http://php.net/manual/en/arrayaccess.offsetset.php
* @param mixed $offset <p>
* The offset to assign the value to.
* </p>
* @param mixed $value <p>
* The value to set.
* </p>
* @return void
* @since 5.0.0
*/
public function offsetSet($offset, $value)
{
$this->value[$offset] = $value;
}
/**
* Offset to unset
*
* @link http://php.net/manual/en/arrayaccess.offsetunset.php
* @param mixed $offset <p>
* The offset to unset.
* </p>
* @return void
* @since 5.0.0
*/
public function offsetUnset($offset)
{
unset($this->value[$offset]);
}
/**
* Count elements of an object
*
* @link http://php.net/manual/en/countable.count.php
* @return int The custom count as an integer.
* </p>
* <p>
* The return value is cast to an integer.
* @since 5.1.0
*/
public function count()
{
return count($this->value);
}
}

63
framework/db/BaseActiveRecord.php

@ -8,9 +8,10 @@
namespace yii\db;
use Yii;
use yii\base\InvalidArgumentException;
use yii\base\InvalidCallException;
use yii\base\InvalidConfigException;
use yii\base\InvalidArgumentException;
use yii\base\InvalidParamException;
use yii\base\Model;
use yii\base\ModelEvent;
use yii\base\NotSupportedException;
@ -96,6 +97,10 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
* @var array related models indexed by the relation names
*/
private $_related = [];
/**
* @var array relation names indexed by their link attributes
*/
private $_relationsDependencies = [];
/**
@ -278,7 +283,9 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
{
if (isset($this->_attributes[$name]) || array_key_exists($name, $this->_attributes)) {
return $this->_attributes[$name];
} elseif ($this->hasAttribute($name)) {
}
if ($this->hasAttribute($name)) {
return null;
}
@ -287,6 +294,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
}
$value = parent::__get($name);
if ($value instanceof ActiveQueryInterface) {
$this->setRelationDependencies($name, $value);
return $this->_related[$name] = $value->findFor($name, $this);
}
@ -302,6 +310,12 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
public function __set($name, $value)
{
if ($this->hasAttribute($name)) {
if (
!empty($this->_relationsDependencies[$name])
&& (!array_key_exists($name, $this->_attributes) || $this->_attributes[$name] !== $value)
) {
$this->resetDependentRelations($name);
}
$this->_attributes[$name] = $value;
} else {
parent::__set($name, $value);
@ -333,6 +347,9 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
{
if ($this->hasAttribute($name)) {
unset($this->_attributes[$name]);
if (!empty($this->_relationsDependencies[$name])) {
$this->resetDependentRelations($name);
}
} elseif (array_key_exists($name, $this->_related)) {
unset($this->_related[$name]);
} elseif ($this->getRelation($name, false) === null) {
@ -497,6 +514,12 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
public function setAttribute($name, $value)
{
if ($this->hasAttribute($name)) {
if (
!empty($this->_relationsDependencies[$name])
&& (!array_key_exists($name, $this->_attributes) || $this->_attributes[$name] !== $value)
) {
$this->resetDependentRelations($name);
}
$this->_attributes[$name] = $value;
} else {
throw new InvalidArgumentException(get_class($this) . ' has no attribute named "' . $name . '".');
@ -1041,6 +1064,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
}
$this->_oldAttributes = $record->_oldAttributes;
$this->_related = [];
$this->_relationsDependencies = [];
$this->afterRefresh();
return true;
@ -1160,6 +1184,8 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
}
}
$record->_oldAttributes = $record->_attributes;
$record->_related = [];
$record->_relationsDependencies = [];
}
/**
@ -1209,7 +1235,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
$relation = $this->$getter();
} catch (UnknownMethodException $e) {
if ($throwException) {
throw new InvalidArgumentException(get_class($this) . ' has no relation named "' . $name . '".', 0, $e);
throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".', 0, $e);
}
return null;
@ -1682,4 +1708,35 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
unset($this->$offset);
}
}
/**
* Resets dependent related models checking if their links contain specific attribute.
* @param string $attribute The changed attribute name.
*/
private function resetDependentRelations($attribute)
{
foreach ($this->_relationsDependencies[$attribute] as $relation) {
unset($this->_related[$relation]);
}
unset($this->_relationsDependencies[$attribute]);
}
/**
* Sets relation dependencies for a property
* @param string $name property name
* @param ActiveQueryInterface $relation relation instance
*/
private function setRelationDependencies($name, $relation)
{
if (empty($relation->via)) {
foreach ($relation->link as $attribute) {
$this->_relationsDependencies[$attribute][$name] = $name;
}
} elseif ($relation->via instanceof ActiveQueryInterface) {
$this->setRelationDependencies($name, $relation->via);
} elseif (is_array($relation->via)) {
list(, $viaQuery) = $relation->via;
$this->setRelationDependencies($name, $viaQuery);
}
}
}

38
framework/db/ColumnSchema.php

@ -34,7 +34,7 @@ class ColumnSchema extends BaseObject
public $type;
/**
* @var string the PHP type of this column. Possible PHP types include:
* `string`, `boolean`, `integer`, `double`.
* `string`, `boolean`, `integer`, `double`, `array`.
*/
public $phpType;
/**
@ -114,12 +114,36 @@ class ColumnSchema extends BaseObject
*/
protected function typecast($value)
{
if ($value === '' && $this->type !== Schema::TYPE_TEXT && $this->type !== Schema::TYPE_STRING && $this->type !== Schema::TYPE_BINARY && $this->type !== Schema::TYPE_CHAR) {
if ($value === ''
&& !in_array(
$this->type,
[
Schema::TYPE_TEXT,
Schema::TYPE_STRING,
Schema::TYPE_BINARY,
Schema::TYPE_CHAR
],
true)
) {
return null;
}
if ($value === null || gettype($value) === $this->phpType || $value instanceof Expression || $value instanceof Query) {
if ($value === null
|| gettype($value) === $this->phpType
|| $value instanceof ExpressionInterface
|| $value instanceof Query
) {
return $value;
}
if (is_array($value)
&& count($value) === 2
&& isset($value[1])
&& in_array($value[1], $this->getPdoParamTypes(), true)
) {
return new PdoValue($value[0], $value[1]);
}
switch ($this->phpType) {
case 'resource':
case 'string':
@ -143,4 +167,12 @@ class ColumnSchema extends BaseObject
return $value;
}
/**
* @return int[] array of numbers that represent possible PDO parameter types
*/
private function getPdoParamTypes()
{
return [\PDO::PARAM_BOOL, \PDO::PARAM_INT, \PDO::PARAM_STR, \PDO::PARAM_LOB, \PDO::PARAM_NULL, \PDO::PARAM_STMT];
}
}

1
framework/db/ColumnSchemaBuilder.php

@ -91,6 +91,7 @@ class ColumnSchemaBuilder extends BaseObject
Schema::TYPE_CHAR => self::CATEGORY_STRING,
Schema::TYPE_STRING => self::CATEGORY_STRING,
Schema::TYPE_TEXT => self::CATEGORY_STRING,
Schema::TYPE_TINYINT => self::CATEGORY_NUMERIC,
Schema::TYPE_SMALLINT => self::CATEGORY_NUMERIC,
Schema::TYPE_INTEGER => self::CATEGORY_NUMERIC,
Schema::TYPE_BIGINT => self::CATEGORY_NUMERIC,

52
framework/db/Command.php

@ -347,8 +347,8 @@ class Command extends Component
* @param array $values the values to be bound. This must be given in terms of an associative
* array with array keys being the parameter names, and array values the corresponding parameter values,
* e.g. `[':name' => 'John', ':age' => 25]`. By default, the PDO type of each value is determined
* by its PHP type. You may explicitly specify the PDO type by using an array: `[value, type]`,
* e.g. `[':name' => 'John', ':profile' => [$profile, \PDO::PARAM_LOB]]`.
* by its PHP type. You may explicitly specify the PDO type by using a [[yii\db\PdoValue]] class: `new PdoValue(value, type)`,
* e.g. `[':name' => 'John', ':profile' => new PdoValue($profile, \PDO::PARAM_LOB)]`.
* @return $this the current command being executed
*/
public function bindValues($values)
@ -359,9 +359,9 @@ class Command extends Component
$schema = $this->db->getSchema();
foreach ($values as $name => $value) {
if (is_array($value)) {
$this->_pendingParams[$name] = $value;
$this->params[$name] = $value[0];
if ($value instanceof PdoValue) {
$this->_pendingParams[$name] = [$value->getValue(), $value->getType()];
$this->params[$name] = $value->getValue();
} else {
$type = $schema->getPdoType($value);
$this->_pendingParams[$name] = [$value, $type];
@ -500,14 +500,52 @@ class Command extends Component
return $this->db->quoteSql($column);
}, $columns);
$sql = $this->db->getQueryBuilder()->batchInsert($table, $columns, $rows);
$params = [];
$sql = $this->db->getQueryBuilder()->batchInsert($table, $columns, $rows, $params);
$this->setRawSql($sql);
$this->bindValues($params);
return $this;
}
/**
* Creates a command to insert rows into a database table if
* they do not already exist (matching unique constraints),
* or update them if they do.
*
* For example,
*
* ```php
* $sql = $queryBuilder->upsert('pages', [
* 'name' => 'Front page',
* 'url' => 'http://example.com/', // url is unique
* 'visits' => 0,
* ], [
* 'visits' => new \yii\db\Expression('visits + 1'),
* ], $params);
* ```
*
* The method will properly escape the table and column names.
*
* @param string $table the table that new rows will be inserted into/updated in.
* @param array|Query $insertColumns the column data (name => value) to be inserted into the table or instance
* of [[Query]] to perform `INSERT INTO ... SELECT` SQL statement.
* @param array|bool $updateColumns the column data (name => value) to be updated if they already exist.
* If `true` is passed, the column data will be updated to match the insert column data.
* If `false` is passed, no update will be performed if the column data already exists.
* @param array $params the parameters to be bound to the command.
* @return $this the command object itself.
* @since 2.0.14
*/
public function upsert($table, $insertColumns, $updateColumns = true, $params = [])
{
$sql = $this->db->getQueryBuilder()->upsert($table, $insertColumns, $updateColumns, $params);
return $this->setSql($sql)->bindValues($params);
}
/**
* Creates an UPDATE command.
*
* For example,
@ -995,7 +1033,7 @@ class Command extends Component
return $this->setSql($sql)->requireTableSchemaRefresh($viewName);
}
/**
* Drops a SQL View.
*

28
framework/db/Connection.php

@ -118,10 +118,11 @@ use yii\caching\CacheInterface;
* master available. This property is read-only.
* @property PDO $masterPdo The PDO instance for the currently active master connection. This property is
* read-only.
* @property QueryBuilder $queryBuilder The query builder for the current DB connection. This property is
* read-only.
* @property QueryBuilder $queryBuilder The query builder for the current DB connection. Note that the type of
* this property differs in getter and setter. See [[getQueryBuilder()]] and [[setQueryBuilder()]] for details.
* @property Schema $schema The schema information for the database opened by this connection. This property
* is read-only.
* @property string $serverVersion Server version as a string. This property is read-only.
* @property Connection $slave The currently active slave connection. `null` is returned if there is no slave
* available and `$fallbackToMaster` is false. This property is read-only.
* @property PDO $slavePdo The PDO instance for the currently active slave connection. `null` is returned if
@ -295,7 +296,7 @@ class Connection extends Component
* Since version 2.0.14 [[$commandMap]] is used if this property is set to its default value.
* @see createCommand
* @since 2.0.7
* @deprecated 2.0.14 Use [[$commandMap]] for precise configuration.
* @deprecated since 2.0.14. Use [[$commandMap]] for precise configuration.
*/
public $commandClass = Command::class;
/**
@ -826,6 +827,17 @@ class Connection extends Component
}
/**
* Can be used to set [[QueryBuilder]] configuration via Connection configuration array.
*
* @param array $value the [[QueryBuilder]] properties to be configured.
* @since 2.0.14
*/
public function setQueryBuilder($value)
{
Yii::configure($this->getQueryBuilder(), $value);
}
/**
* Obtains the schema information for the named table.
* @param string $name table name.
* @param bool $refresh whether to reload the table schema even if it is found in the cache.
@ -937,6 +949,16 @@ class Connection extends Component
}
/**
* Returns a server version as a string comparable by [[\version_compare()]].
* @return string server version as a string.
* @since 2.0.14
*/
public function getServerVersion()
{
return $this->getSchema()->getServerVersion();
}
/**
* Returns the PDO instance for the currently active slave connection.
* When [[enableSlaves]] is true, one of the slaves will be used for read queries, and its PDO instance
* will be returned by this method.

125
framework/db/ConstraintFinderInterface.php

@ -0,0 +1,125 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db;
/**
* ConstraintFinderInterface defines methods for getting a table constraint information.
*
* @author Sergey Makinen <sergey@makinen.ru>
* @since 2.0.14
*/
interface ConstraintFinderInterface
{
/**
* Obtains the primary key for the named table.
* @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
* @param bool $refresh whether to reload the information even if it is found in the cache.
* @return Constraint|null table primary key, `null` if the table has no primary key.
*/
public function getTablePrimaryKey($name, $refresh = false);
/**
* Returns primary keys for all tables in the database.
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
* @param bool $refresh whether to fetch the latest available table schemas. If this is `false`,
* cached data may be returned if available.
* @return Constraint[] primary keys for all tables in the database.
* Each array element is an instance of [[Constraint]] or its child class.
*/
public function getSchemaPrimaryKeys($schema = '', $refresh = false);
/**
* Obtains the foreign keys information for the named table.
* @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
* @param bool $refresh whether to reload the information even if it is found in the cache.
* @return ForeignKeyConstraint[] table foreign keys.
*/
public function getTableForeignKeys($name, $refresh = false);
/**
* Returns foreign keys for all tables in the database.
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
* @param bool $refresh whether to fetch the latest available table schemas. If this is false,
* cached data may be returned if available.
* @return ForeignKeyConstraint[][] foreign keys for all tables in the database.
* Each array element is an array of [[ForeignKeyConstraint]] or its child classes.
*/
public function getSchemaForeignKeys($schema = '', $refresh = false);
/**
* Obtains the indexes information for the named table.
* @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
* @param bool $refresh whether to reload the information even if it is found in the cache.
* @return IndexConstraint[] table indexes.
*/
public function getTableIndexes($name, $refresh = false);
/**
* Returns indexes for all tables in the database.
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
* @param bool $refresh whether to fetch the latest available table schemas. If this is false,
* cached data may be returned if available.
* @return IndexConstraint[][] indexes for all tables in the database.
* Each array element is an array of [[IndexConstraint]] or its child classes.
*/
public function getSchemaIndexes($schema = '', $refresh = false);
/**
* Obtains the unique constraints information for the named table.
* @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
* @param bool $refresh whether to reload the information even if it is found in the cache.
* @return Constraint[] table unique constraints.
*/
public function getTableUniques($name, $refresh = false);
/**
* Returns unique constraints for all tables in the database.
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
* @param bool $refresh whether to fetch the latest available table schemas. If this is false,
* cached data may be returned if available.
* @return Constraint[][] unique constraints for all tables in the database.
* Each array element is an array of [[Constraint]] or its child classes.
*/
public function getSchemaUniques($schema = '', $refresh = false);
/**
* Obtains the check constraints information for the named table.
* @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
* @param bool $refresh whether to reload the information even if it is found in the cache.
* @return CheckConstraint[] table check constraints.
*/
public function getTableChecks($name, $refresh = false);
/**
* Returns check constraints for all tables in the database.
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
* @param bool $refresh whether to fetch the latest available table schemas. If this is false,
* cached data may be returned if available.
* @return CheckConstraint[][] check constraints for all tables in the database.
* Each array element is an array of [[CheckConstraint]] or its child classes.
*/
public function getSchemaChecks($schema = '', $refresh = false);
/**
* Obtains the default value constraints information for the named table.
* @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
* @param bool $refresh whether to reload the information even if it is found in the cache.
* @return DefaultValueConstraint[] table default value constraints.
*/
public function getTableDefaultValues($name, $refresh = false);
/**
* Returns default value constraints for all tables in the database.
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
* @param bool $refresh whether to fetch the latest available table schemas. If this is false,
* cached data may be returned if available.
* @return DefaultValueConstraint[] default value constraints for all tables in the database.
* Each array element is an array of [[DefaultValueConstraint]] or its child classes.
*/
public function getSchemaDefaultValues($schema = '', $refresh = false);
}

4
framework/db/Expression.php

@ -28,7 +28,7 @@ namespace yii\db;
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Expression extends \yii\base\BaseObject
class Expression extends \yii\base\BaseObject implements ExpressionInterface
{
/**
* @var string the DB expression
@ -57,7 +57,7 @@ class Expression extends \yii\base\BaseObject
/**
* String magic method.
* @return string the DB expression
* @return string the DB expression.
*/
public function __toString()
{

30
framework/db/ExpressionBuilder.php

@ -0,0 +1,30 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db;
/**
* Class ExpressionBuilder builds objects of [[yii\db\Expression]] class.
*
* @author Dmitry Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class ExpressionBuilder implements ExpressionBuilderInterface
{
use ExpressionBuilderTrait;
/**
* {@inheritdoc}
* @param Expression|ExpressionInterface $expression the expression to be built
*/
public function build(ExpressionInterface $expression, array &$params = [])
{
$params = array_merge($params, $expression->params);
return $expression->__toString();
}
}

28
framework/db/ExpressionBuilderInterface.php

@ -0,0 +1,28 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db;
/**
* Interface ExpressionBuilderInterface is designed to build raw SQL from specific expression
* objects that implement [[ExpressionInterface]].
*
* @author Dmitry Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
interface ExpressionBuilderInterface
{
/**
* Method builds the raw SQL from the $expression that will not be additionally
* escaped or quoted.
*
* @param ExpressionInterface $expression the expression to be built.
* @param array $params the binding parameters.
* @return string the raw SQL that will not be additionally escaped or quoted.
*/
public function build(ExpressionInterface $expression, array &$params = []);
}

33
framework/db/ExpressionBuilderTrait.php

@ -0,0 +1,33 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db;
/**
* Trait ExpressionBuilderTrait provides common constructor for classes that
* should implement [[ExpressionBuilderInterface]]
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
trait ExpressionBuilderTrait
{
/**
* @var QueryBuilder
*/
protected $queryBuilder;
/**
* ExpressionBuilderTrait constructor.
*
* @param QueryBuilder $queryBuilder
*/
public function __construct(QueryBuilder $queryBuilder)
{
$this->queryBuilder = $queryBuilder;
}
}

24
framework/db/ExpressionInterface.php

@ -0,0 +1,24 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db;
/**
* Interface ExpressionInterface should be used to mark classes, that should be built
* in a special way.
*
* The database abstraction layer of Yii framework supports objects that implement this
* interface and will use [[ExpressionBuilderInterface]] to build them.
*
* The default implementation is a class [[Expression]].
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
interface ExpressionInterface
{
}

73
framework/db/JsonExpression.php

@ -0,0 +1,73 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db;
/**
* Class JsonExpression represents data that should be encoded to JSON.
*
* For example:
*
* ```php
* new JsonExpression(['a' => 1, 'b' => 2]); // will be encoded to '{"a": 1, "b": 2}'
* ```
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class JsonExpression implements ExpressionInterface
{
const TYPE_JSON = 'json';
const TYPE_JSONB = 'jsonb';
/**
* @var mixed the value to be encoded to JSON.
* The value must be compatible with [\yii\helpers\Json::encode()|Json::encode()]] input requirements.
*/
protected $value;
/**
* @var string|null Type of JSON, expression should be casted to. Defaults to `null`, meaning
* no explicit casting will be performed.
* This property will be encountered only for DBMSs that support different types of JSON.
* For example, PostgreSQL has `json` and `jsonb` types.
*/
protected $type;
/**
* JsonExpression constructor.
*
* @param mixed $value the value to be encoded to JSON.
* The value must be compatible with [\yii\helpers\Json::encode()|Json::encode()]] requirements.
* @param string|null $type the type of the JSON. See [[JsonExpression::type]]
*
* @see type
*/
public function __construct($value, $type = null)
{
$this->value = $value;
$this->type = $type;
}
/**
* @return mixed
* @see value
*/
public function getValue()
{
return $this->value;
}
/**
* @return null|string the type of JSON
* @see type
*/
public function getType()
{
return $this->type;
}
}

24
framework/db/Migration.php

@ -240,6 +240,30 @@ class Migration extends Component implements MigrationInterface
}
/**
* Creates and executes a command to insert rows into a database table if
* they do not already exist (matching unique constraints),
* or update them if they do.
*
* The method will properly escape the column names, and bind the values to be inserted.
*
* @param string $table the table that new rows will be inserted into/updated in.
* @param array|Query $insertColumns the column data (name => value) to be inserted into the table or instance
* of [[Query]] to perform `INSERT INTO ... SELECT` SQL statement.
* @param array|bool $updateColumns the column data (name => value) to be updated if they already exist.
* If `true` is passed, the column data will be updated to match the insert column data.
* If `false` is passed, no update will be performed if the column data already exists.
* @param array $params the parameters to be bound to the command.
* @return $this the command object itself.
* @since 2.0.14
*/
public function upsert($table, $insertColumns, $updateColumns = true, $params = [])
{
$time = $this->beginCommand("upsert into $table");
$this->db->createCommand()->upsert($table, $insertColumns, $updateColumns, $params)->execute();
$this->endCommand($time);
}
/**
* Creates and executes an UPDATE SQL statement.
* The method will properly escape the column names and bind the values to be updated.
* @param string $table the table to be updated.

65
framework/db/PdoValue.php

@ -0,0 +1,65 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db;
/**
* Class PdoValue represents a $value that should be bound to PDO with exact $type.
*
* For example, it will be useful when you need to bind binary data to BLOB column in DBMS:
*
* ```php
* [':name' => 'John', ':profile' => new PdoValue($profile, \PDO::PARAM_LOB)]`.
* ```
*
* To see possible types, check [PDO::PARAM_* constants](http://php.net/manual/en/pdo.constants.php).
*
* @see http://php.net/manual/en/pdostatement.bindparam.php
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
final class PdoValue implements ExpressionInterface
{
/**
* @var mixed
*/
private $value;
/**
* @var int One of PDO_PARAM_* constants
* @see http://php.net/manual/en/pdo.constants.php
*/
private $type;
/**
* PdoValue constructor.
*
* @param $value
* @param $type
*/
public function __construct($value, $type)
{
$this->value = $value;
$this->type = $type;
}
/**
* @return mixed
*/
public function getValue()
{
return $this->value;
}
/**
* @return int
*/
public function getType()
{
return $this->type;
}
}

31
framework/db/PdoValueBuilder.php

@ -0,0 +1,31 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db;
/**
* Class PdoValueBuilder builds object of the [[PdoValue]] expression class.
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class PdoValueBuilder implements ExpressionBuilderInterface
{
const PARAM_PREFIX = ':pv';
/**
* {@inheritdoc}
*/
public function build(ExpressionInterface $expression, array &$params = [])
{
$placeholder = static::PARAM_PREFIX . count($params);
$params[$placeholder] = $expression;
return $placeholder;
}
}

187
framework/db/Query.php

@ -9,8 +9,9 @@ namespace yii\db;
use Yii;
use yii\base\Component;
use yii\base\InvalidConfigException;
use yii\base\InvalidArgumentException;
use yii\helpers\ArrayHelper;
use yii\base\InvalidConfigException;
/**
* Query represents a SELECT SQL statement in a way that is independent of DBMS.
@ -47,7 +48,7 @@ use yii\base\InvalidArgumentException;
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class Query extends Component implements QueryInterface
class Query extends Component implements QueryInterface, ExpressionInterface
{
use QueryTrait;
@ -97,7 +98,7 @@ class Query extends Component implements QueryInterface
*/
public $join;
/**
* @var string|array|Expression the condition to be applied in the GROUP BY clause.
* @var string|array|ExpressionInterface the condition to be applied in the GROUP BY clause.
* It can be either a string or an array. Please refer to [[where()]] on how to specify the condition.
*/
public $having;
@ -114,6 +115,21 @@ class Query extends Component implements QueryInterface
* For example, `[':name' => 'Dan', ':age' => 31]`.
*/
public $params = [];
/**
* @var int|true the default number of seconds that query results can remain valid in cache.
* Use 0 to indicate that the cached data will never expire.
* Use a negative number to indicate that query cache should not be used.
* Use boolean `true` to indicate that [[Connection::queryCacheDuration]] should be used.
* @see cache()
* @since 2.0.14
*/
public $queryCacheDuration;
/**
* @var \yii\caching\Dependency the dependency to be associated with the cached query result for this query
* @see cache()
* @since 2.0.14
*/
public $queryCacheDependency;
/**
@ -129,7 +145,10 @@ class Query extends Component implements QueryInterface
}
[$sql, $params] = $db->getQueryBuilder()->build($this);
return $db->createCommand($sql, $params);
$command = $db->createCommand($sql, $params);
$this->setCommandCache($command);
return $command;
}
/**
@ -233,12 +252,7 @@ class Query extends Component implements QueryInterface
}
$result = [];
foreach ($rows as $row) {
if (is_string($this->indexBy)) {
$key = $row[$this->indexBy];
} else {
$key = call_user_func($this->indexBy, $row);
}
$result[$key] = $row;
$result[ArrayHelper::getValue($row, $this->indexBy)] = $row;
}
return $result;
@ -414,7 +428,7 @@ class Query extends Component implements QueryInterface
/**
* Queries a scalar value by setting [[select]] first.
* Restores the value of select to make this query reusable.
* @param string|Expression $selectExpression
* @param string|ExpressionInterface $selectExpression
* @param Connection|null $db
* @return bool|string
*/
@ -449,11 +463,13 @@ class Query extends Component implements QueryInterface
return $command->queryScalar();
}
return (new self())
$command = (new self())
->select([$selectExpression])
->from(['c' => $this])
->createCommand($db)
->queryScalar();
->createCommand($db);
$this->setCommandCache($command);
return $command->queryScalar();
}
/**
@ -567,12 +583,12 @@ PATTERN;
/**
* Sets the SELECT part of the query.
* @param string|array|Expression $columns the columns to be selected.
* @param string|array|ExpressionInterface $columns the columns to be selected.
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']).
* Columns can be prefixed with table names (e.g. "user.id") and/or contain column aliases (e.g. "user.id AS user_id").
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression). A DB expression may also be passed in form of
* an [[Expression]] object.
* an [[ExpressionInterface]] object.
*
* Note that if you are selecting an expression like `CONCAT(first_name, ' ', last_name)`, you should
* use an array to specify the columns. Otherwise, the expression may be incorrectly split into several parts.
@ -589,12 +605,13 @@ PATTERN;
*/
public function select($columns, $option = null)
{
if ($columns instanceof Expression) {
if ($columns instanceof ExpressionInterface) {
$columns = [$columns];
} elseif (!is_array($columns)) {
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
}
$this->select = $columns;
$this->select = [];
$this->select = $this->getUniqueColumns($columns);
$this->selectOption = $option;
return $this;
}
@ -609,18 +626,19 @@ PATTERN;
* $query->addSelect(["*", "CONCAT(first_name, ' ', last_name) AS full_name"])->one();
* ```
*
* @param string|array|Expression $columns the columns to add to the select. See [[select()]] for more
* @param string|array|ExpressionInterface $columns the columns to add to the select. See [[select()]] for more
* details about the format of this parameter.
* @return $this the query object itself
* @see select()
*/
public function addSelect($columns)
{
if ($columns instanceof Expression) {
if ($columns instanceof ExpressionInterface) {
$columns = [$columns];
} elseif (!is_array($columns)) {
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
}
$columns = $this->getUniqueColumns($columns);
if ($this->select === null) {
$this->select = $columns;
} else {
@ -631,6 +649,51 @@ PATTERN;
}
/**
* Returns unique column names excluding duplicates.
* Columns to be removed:
* - if column definition already present in SELECT part with same alias
* - if column definition without alias already present in SELECT part without alias too
* @param array $columns the columns to be merged to the select.
* @since 2.0.14
*/
protected function getUniqueColumns($columns)
{
$columns = array_unique($columns);
$unaliasedColumns = $this->getUnaliasedColumnsFromSelect();
foreach ($columns as $columnAlias => $columnDefinition) {
if ($columnDefinition instanceof Query) {
continue;
}
if (
(is_string($columnAlias) && isset($this->select[$columnAlias]) && $this->select[$columnAlias] === $columnDefinition)
|| (is_integer($columnAlias) && in_array($columnDefinition, $unaliasedColumns))
) {
unset($columns[$columnAlias]);
}
}
return $columns;
}
/**
* @return array List of columns without aliases from SELECT statement.
* @since 2.0.14
*/
protected function getUnaliasedColumnsFromSelect()
{
$result = [];
if (is_array($this->select)) {
foreach ($this->select as $name => $value) {
if (is_integer($name)) {
$result[] = $value;
}
}
}
return array_unique($result);
}
/**
* Sets the value indicating whether to SELECT DISTINCT or not.
* @param bool $value whether to SELECT DISTINCT or not.
* @return $this the query object itself
@ -643,7 +706,7 @@ PATTERN;
/**
* Sets the FROM part of the query.
* @param string|array|Expression $tables the table(s) to be selected from. This can be either a string (e.g. `'user'`)
* @param string|array|ExpressionInterface $tables the table(s) to be selected from. This can be either a string (e.g. `'user'`)
* or an array (e.g. `['user', 'profile']`) specifying one or several table names.
* Table names can contain schema prefixes (e.g. `'public.user'`) and/or table aliases (e.g. `'user u'`).
* The method will automatically quote the table names unless it contains some parenthesis
@ -655,7 +718,7 @@ PATTERN;
* Use a Query object to represent a sub-query. In this case, the corresponding array key will be used
* as the alias for the sub-query.
*
* To specify the `FROM` part in plain SQL, you may pass an instance of [[Expression]].
* To specify the `FROM` part in plain SQL, you may pass an instance of [[ExpressionInterface]].
*
* Here are some examples:
*
@ -677,6 +740,9 @@ PATTERN;
*/
public function from($tables)
{
if ($tables instanceof Expression) {
$tables = [$tables];
}
if (is_string($tables)) {
$tables = preg_split('/\s*,\s*/', trim($tables), -1, PREG_SPLIT_NO_EMPTY);
}
@ -694,7 +760,7 @@ PATTERN;
*
* {@inheritdoc}
*
* @param string|array|Expression $condition the conditions that should be put in the WHERE part.
* @param string|array|ExpressionInterface $condition the conditions that should be put in the WHERE part.
* @param array $params the parameters (name => value) to be bound to the query.
* @return $this the query object itself
* @see andWhere()
@ -711,7 +777,7 @@ PATTERN;
/**
* Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the `AND` operator.
* @param string|array|Expression $condition the new WHERE condition. Please refer to [[where()]]
* @param string|array|ExpressionInterface $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query.
* @return $this the query object itself
@ -734,7 +800,7 @@ PATTERN;
/**
* Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the `OR` operator.
* @param string|array|Expression $condition the new WHERE condition. Please refer to [[where()]]
* @param string|array|ExpressionInterface $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query.
* @return $this the query object itself
@ -900,7 +966,7 @@ PATTERN;
/**
* Sets the GROUP BY part of the query.
* @param string|array|Expression $columns the columns to be grouped by.
* @param string|array|ExpressionInterface $columns the columns to be grouped by.
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
@ -909,13 +975,14 @@ PATTERN;
* to represent the group-by information. Otherwise, the method will not be able to correctly determine
* the group-by columns.
*
* Since version 2.0.7, an [[Expression]] object can be passed to specify the GROUP BY part explicitly in plain SQL.
* Since version 2.0.7, an [[ExpressionInterface]] object can be passed to specify the GROUP BY part explicitly in plain SQL.
* Since version 2.0.14, an [[ExpressionInterface]] object can be passed as well.
* @return $this the query object itself
* @see addGroupBy()
*/
public function groupBy($columns)
{
if ($columns instanceof Expression) {
if ($columns instanceof ExpressionInterface) {
$columns = [$columns];
} elseif (!is_array($columns)) {
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
@ -936,12 +1003,13 @@ PATTERN;
* the group-by columns.
*
* Since version 2.0.7, an [[Expression]] object can be passed to specify the GROUP BY part explicitly in plain SQL.
* Since version 2.0.14, an [[ExpressionInterface]] object can be passed as well.
* @return $this the query object itself
* @see groupBy()
*/
public function addGroupBy($columns)
{
if ($columns instanceof Expression) {
if ($columns instanceof ExpressionInterface) {
$columns = [$columns];
} elseif (!is_array($columns)) {
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
@ -957,7 +1025,7 @@ PATTERN;
/**
* Sets the HAVING part of the query.
* @param string|array|Expression $condition the conditions to be put after HAVING.
* @param string|array|ExpressionInterface $condition the conditions to be put after HAVING.
* Please refer to [[where()]] on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query.
* @return $this the query object itself
@ -974,7 +1042,7 @@ PATTERN;
/**
* Adds an additional HAVING condition to the existing one.
* The new condition and the existing one will be joined using the `AND` operator.
* @param string|array|Expression $condition the new HAVING condition. Please refer to [[where()]]
* @param string|array|ExpressionInterface $condition the new HAVING condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query.
* @return $this the query object itself
@ -995,7 +1063,7 @@ PATTERN;
/**
* Adds an additional HAVING condition to the existing one.
* The new condition and the existing one will be joined using the `OR` operator.
* @param string|array|Expression $condition the new HAVING condition. Please refer to [[where()]]
* @param string|array|ExpressionInterface $condition the new HAVING condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query.
* @return $this the query object itself
@ -1153,6 +1221,52 @@ PATTERN;
}
/**
* Enables query cache for this Query.
* @param int|true $duration the number of seconds that query results can remain valid in cache.
* Use 0 to indicate that the cached data will never expire.
* Use a negative number to indicate that query cache should not be used.
* Use boolean `true` to indicate that [[Connection::queryCacheDuration]] should be used.
* Defaults to `true`.
* @param \yii\caching\Dependency $dependency the cache dependency associated with the cached result.
* @return $this the Query object itself
* @since 2.0.14
*/
public function cache($duration = true, $dependency = null)
{
$this->queryCacheDuration = $duration;
$this->queryCacheDependency = $dependency;
return $this;
}
/**
* Disables query cache for this Query.
* @return $this the Query object itself
* @since 2.0.14
*/
public function noCache()
{
$this->queryCacheDuration = -1;
return $this;
}
/**
* Sets $command cache, if this query has enabled caching.
*
* @param Command $command
* @return Command
* @since 2.0.14
*/
protected function setCommandCache($command)
{
if ($this->queryCacheDuration !== null || $this->queryCacheDependency !== null) {
$duration = $this->queryCacheDuration === true ? null : $this->queryCacheDuration;
$command->cache($duration, $this->queryCacheDependency);
}
return $command;
}
/**
* Creates a new Query object and copies its property values from an existing one.
* The properties being copies are the ones to be used by query builders.
* @param Query $from the source query object
@ -1177,4 +1291,13 @@ PATTERN;
'params' => $from->params,
]);
}
/**
* Returns the SQL representation of Query
* @return string
*/
public function __toString()
{
return serialize($this);
}
}

837
framework/db/QueryBuilder.php

File diff suppressed because it is too large Load Diff

36
framework/db/QueryExpressionBuilder.php

@ -0,0 +1,36 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db;
/**
* Class QueryExpressionBuilder is used internally to build [[Query]] object
* using unified [[QueryBuilder]] expression building interface.
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class QueryExpressionBuilder implements ExpressionBuilderInterface
{
use ExpressionBuilderTrait;
/**
* Method builds the raw SQL from the $expression that will not be additionally
* escaped or quoted.
*
* @param ExpressionInterface|Query $expression the expression to be built.
* @param array $params the binding parameters.
* @return string the raw SQL that will not be additionally escaped or quoted.
*/
public function build(ExpressionInterface $expression, array &$params = [])
{
list($sql, $params) = $this->queryBuilder->build($expression, $params);
return "($sql)";
}
}

24
framework/db/QueryTrait.php

@ -27,13 +27,13 @@ trait QueryTrait
*/
public $where;
/**
* @var int|Expression maximum number of records to be returned. May be an instance of [[Expression]].
* @var int|ExpressionInterface maximum number of records to be returned. May be an instance of [[ExpressionInterface]].
* If not set or less than 0, it means no limit.
*/
public $limit;
/**
* @var int|Expression zero-based offset from where the records are to be returned.
* May be an instance of [[Expression]]. If not set or less than 0, it means starting from the beginning.
* @var int|ExpressionInterface zero-based offset from where the records are to be returned.
* May be an instance of [[ExpressionInterface]]. If not set or less than 0, it means starting from the beginning.
*/
public $offset;
/**
@ -41,7 +41,7 @@ trait QueryTrait
* The array keys are the columns to be sorted by, and the array values are the corresponding sort directions which
* can be either [SORT_ASC](http://php.net/manual/en/array.constants.php#constant.sort-asc)
* or [SORT_DESC](http://php.net/manual/en/array.constants.php#constant.sort-desc).
* The array may also contain [[Expression]] objects. If that is the case, the expressions
* The array may also contain [[ExpressionInterface]] objects. If that is the case, the expressions
* will be converted into strings without any change.
*/
public $orderBy;
@ -305,7 +305,7 @@ trait QueryTrait
/**
* Sets the ORDER BY part of the query.
* @param string|array|Expression $columns the columns (and the directions) to be ordered by.
* @param string|array|ExpressionInterface $columns the columns (and the directions) to be ordered by.
* Columns can be specified in either a string (e.g. `"id ASC, name DESC"`) or an array
* (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).
*
@ -316,7 +316,7 @@ trait QueryTrait
* to represent the order-by information. Otherwise, the method will not be able to correctly determine
* the order-by columns.
*
* Since version 2.0.7, an [[Expression]] object can be passed to specify the ORDER BY part explicitly in plain SQL.
* Since version 2.0.7, an [[ExpressionInterface]] object can be passed to specify the ORDER BY part explicitly in plain SQL.
* @return $this the query object itself
* @see addOrderBy()
*/
@ -328,7 +328,7 @@ trait QueryTrait
/**
* Adds additional ORDER BY columns to the query.
* @param string|array|Expression $columns the columns (and the directions) to be ordered by.
* @param string|array|ExpressionInterface $columns the columns (and the directions) to be ordered by.
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
* (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).
*
@ -339,7 +339,7 @@ trait QueryTrait
* to represent the order-by information. Otherwise, the method will not be able to correctly determine
* the order-by columns.
*
* Since version 2.0.7, an [[Expression]] object can be passed to specify the ORDER BY part explicitly in plain SQL.
* Since version 2.0.7, an [[ExpressionInterface]] object can be passed to specify the ORDER BY part explicitly in plain SQL.
* @return $this the query object itself
* @see orderBy()
*/
@ -358,12 +358,12 @@ trait QueryTrait
/**
* Normalizes format of ORDER BY data.
*
* @param array|string|Expression $columns the columns value to normalize. See [[orderBy]] and [[addOrderBy]].
* @param array|string|ExpressionInterface $columns the columns value to normalize. See [[orderBy]] and [[addOrderBy]].
* @return array
*/
protected function normalizeOrderBy($columns)
{
if ($columns instanceof Expression) {
if ($columns instanceof ExpressionInterface) {
return [$columns];
} elseif (is_array($columns)) {
return $columns;
@ -384,7 +384,7 @@ trait QueryTrait
/**
* Sets the LIMIT part of the query.
* @param int|Expression|null $limit the limit. Use null or negative value to disable limit.
* @param int|ExpressionInterface|null $limit the limit. Use null or negative value to disable limit.
* @return $this the query object itself
*/
public function limit($limit)
@ -395,7 +395,7 @@ trait QueryTrait
/**
* Sets the OFFSET part of the query.
* @param int|Expression|null $offset the offset. Use null or negative value to disable offset.
* @param int|ExpressionInterface|null $offset the offset. Use null or negative value to disable offset.
* @return $this the query object itself
*/
public function offset($offset)

100
framework/db/Schema.php

@ -15,6 +15,7 @@ use yii\base\NotSupportedException;
use yii\caching\Cache;
use yii\caching\CacheInterface;
use yii\caching\TagDependency;
use yii\helpers\StringHelper;
/**
* Schema is the base class for concrete DBMS-specific schema classes.
@ -26,6 +27,7 @@ use yii\caching\TagDependency;
* @property QueryBuilder $queryBuilder The query builder for this connection. This property is read-only.
* @property string[] $schemaNames All schema names in the database, except system schemas. This property is
* read-only.
* @property string $serverVersion Server version as a string. This property is read-only.
* @property string[] $tableNames All table names in the database. This property is read-only.
* @property TableSchema[] $tableSchemas The metadata for all tables in the database. Each array element is an
* instance of [[TableSchema]] or its child class. This property is read-only.
@ -48,6 +50,7 @@ abstract class Schema extends BaseObject
const TYPE_CHAR = 'char';
const TYPE_STRING = 'string';
const TYPE_TEXT = 'text';
const TYPE_TINYINT = 'tinyint';
const TYPE_SMALLINT = 'smallint';
const TYPE_INTEGER = 'integer';
const TYPE_BIGINT = 'bigint';
@ -61,6 +64,7 @@ abstract class Schema extends BaseObject
const TYPE_BINARY = 'binary';
const TYPE_BOOLEAN = 'boolean';
const TYPE_MONEY = 'money';
const TYPE_JSON = 'json';
/**
* Schema cache version, to detect incompatibilities in cached values when the
* data format of the cache changes.
@ -89,6 +93,19 @@ abstract class Schema extends BaseObject
public $columnSchemaClass = ColumnSchema::class;
/**
* @var string|string[] character used to quote schema, table, etc. names.
* An array of 2 characters can be used in case starting and ending characters are different.
* @since 2.0.14
*/
protected $tableQuoteCharacter = "'";
/**
* @var string|string[] character used to quote column names.
* An array of 2 characters can be used in case starting and ending characters are different.
* @since 2.0.14
*/
protected $columnQuoteCharacter = '"';
/**
* @var array list of ALL schema names in the database, except system schemas
*/
private $_schemaNames;
@ -104,6 +121,10 @@ abstract class Schema extends BaseObject
* @var QueryBuilder the query builder for this database
*/
private $_builder;
/**
* @var string server version as a string.
*/
private $_serverVersion;
/**
@ -508,7 +529,12 @@ abstract class Schema extends BaseObject
*/
public function quoteSimpleTableName($name)
{
return strpos($name, "'") !== false ? $name : "'" . $name . "'";
if (is_string($this->tableQuoteCharacter)) {
$startingCharacter = $endingCharacter = $this->tableQuoteCharacter;
} else {
list($startingCharacter, $endingCharacter) = $this->tableQuoteCharacter;
}
return strpos($name, $startingCharacter) !== false ? $name : $startingCharacter . $name . $endingCharacter;
}
/**
@ -520,7 +546,48 @@ abstract class Schema extends BaseObject
*/
public function quoteSimpleColumnName($name)
{
return strpos($name, '"') !== false || $name === '*' ? $name : '"' . $name . '"';
if (is_string($this->tableQuoteCharacter)) {
$startingCharacter = $endingCharacter = $this->columnQuoteCharacter;
} else {
list($startingCharacter, $endingCharacter) = $this->columnQuoteCharacter;
}
return $name === '*' || strpos($name, $startingCharacter) !== false ? $name : $startingCharacter . $name . $endingCharacter;
}
/**
* Unquotes a simple table name.
* A simple table name should contain the table name only without any schema prefix.
* If the table name is not quoted, this method will do nothing.
* @param string $name table name.
* @return string unquoted table name.
* @since 2.0.14
*/
public function unquoteSimpleTableName($name)
{
if (is_string($this->tableQuoteCharacter)) {
$startingCharacter = $this->tableQuoteCharacter;
} else {
$startingCharacter = $this->tableQuoteCharacter[0];
}
return strpos($name, $startingCharacter) === false ? $name : substr($name, 1, -1);
}
/**
* Unquotes a simple column name.
* A simple column name should contain the column name only without any prefix.
* If the column name is not quoted or is the asterisk character '*', this method will do nothing.
* @param string $name column name.
* @return string unquoted column name.
* @since 2.0.14
*/
public function unquoteSimpleColumnName($name)
{
if (is_string($this->columnQuoteCharacter)) {
$startingCharacter = $this->columnQuoteCharacter;
} else {
$startingCharacter = $this->columnQuoteCharacter[0];
}
return strpos($name, $startingCharacter) === false ? $name : substr($name, 1, -1);
}
/**
@ -550,13 +617,15 @@ abstract class Schema extends BaseObject
{
static $typeMap = [
// abstract type => php type
'smallint' => 'integer',
'integer' => 'integer',
'bigint' => 'integer',
'boolean' => 'boolean',
'float' => 'double',
'double' => 'double',
'binary' => 'resource',
self::TYPE_TINYINT => 'integer',
self::TYPE_SMALLINT => 'integer',
self::TYPE_INTEGER => 'integer',
self::TYPE_BIGINT => 'integer',
self::TYPE_BOOLEAN => 'boolean',
self::TYPE_FLOAT => 'double',
self::TYPE_DOUBLE => 'double',
self::TYPE_BINARY => 'resource',
self::TYPE_JSON => 'array',
];
if (isset($typeMap[$column->type])) {
if ($column->type === 'bigint') {
@ -607,6 +676,19 @@ abstract class Schema extends BaseObject
}
/**
* Returns a server version as a string comparable by [[\version_compare()]].
* @return string server version as a string.
* @since 2.0.14
*/
public function getServerVersion()
{
if ($this->_serverVersion === null) {
$this->_serverVersion = $this->db->getSlavePdo()->getAttribute(\PDO::ATTR_SERVER_VERSION);
}
return $this->_serverVersion;
}
/**
* Returns the cache key for the specified table name.
* @param string $name the table name.
* @return mixed the cache key.

33
framework/db/SchemaBuilderTrait.php

@ -98,6 +98,18 @@ trait SchemaBuilderTrait
}
/**
* Creates a tinyint column. If tinyint is not supported by the DBMS, smallint will be used.
* @param int $length column size or precision definition.
* This parameter will be ignored if not supported by the DBMS.
* @return ColumnSchemaBuilder the column instance which can be further customized.
* @since 2.0.14
*/
public function tinyInteger($length = null)
{
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_TINYINT, $length);
}
/**
* Creates a smallint column.
* @param int $length column size or precision definition.
* This parameter will be ignored if not supported by the DBMS.
@ -272,4 +284,25 @@ trait SchemaBuilderTrait
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_MONEY, $length);
}
/**
* Creates a JSON column.
* @return ColumnSchemaBuilder the column instance which can be further customized.
* @since 2.0.14
* @throws \yii\base\Exception
*/
public function json()
{
/*
* TODO Remove in Yii 2.1
*
* Disabled due to bug in MySQL extension
* @link https://bugs.php.net/bug.php?id=70384
*/
if (version_compare(PHP_VERSION, '5.6', '<') && $this->getDb()->getDriverName() === 'mysql') {
throw new \yii\base\Exception('JSON column type is not supported in PHP < 5.6');
}
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_JSON);
}
}

2
framework/db/TableSchema.php

@ -7,8 +7,8 @@
namespace yii\db;
use yii\base\InvalidArgumentException;
use yii\base\BaseObject;
use yii\base\InvalidArgumentException;
/**
* TableSchema represents the metadata of a database table.

27
framework/db/conditions/AndCondition.php

@ -0,0 +1,27 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
/**
* Condition that connects two or more SQL expressions with the `AND` operator.
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class AndCondition extends ConjunctionCondition
{
/**
* Returns the operator that is represented by this condition class, e.g. `AND`, `OR`.
*
* @return string
*/
public function getOperator()
{
return 'AND';
}
}

121
framework/db/conditions/BetweenColumnsCondition.php

@ -0,0 +1,121 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
use yii\base\InvalidArgumentException;
use yii\db\ExpressionInterface;
use yii\db\Query;
/**
* Class BetweenColumnCondition represents a `BETWEEN` condition where
* values is between two columns. For example:
*
* ```php
* new BetweenColumnsCondition(42, 'BETWEEN', 'min_value', 'max_value')
* // Will be build to:
* // 42 BETWEEN min_value AND max_value
* ```
*
* And a more complex example:
*
* ```php
* new BetweenColumnsCondition(
* new Expression('NOW()'),
* 'NOT BETWEEN',
* (new Query)->select('time')->from('log')->orderBy('id ASC')->limit(1),
* 'update_time'
* );
*
* // Will be built to:
* // NOW() BETWEEN (SELECT time FROM log ORDER BY id ASC LIMIT 1) AND update_time
* ```
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class BetweenColumnsCondition implements ConditionInterface
{
/**
* @var string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)
*/
private $operator;
/**
* @var mixed the value to compare against
*/
private $value;
/**
* @var string|ExpressionInterface|Query the column name or expression that is a beginning of the interval
*/
private $intervalStartColumn;
/**
* @var string|ExpressionInterface|Query the column name or expression that is an end of the interval
*/
private $intervalEndColumn;
/**
* Creates a condition with the `BETWEEN` operator.
*
* @param mixed the value to compare against
* @param string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)
* @param string|ExpressionInterface $intervalStartColumn the column name or expression that is a beginning of the interval
* @param string|ExpressionInterface $intervalEndColumn the column name or expression that is an end of the interval
*/
public function __construct($value, $operator, $intervalStartColumn, $intervalEndColumn)
{
$this->value = $value;
$this->operator = $operator;
$this->intervalStartColumn = $intervalStartColumn;
$this->intervalEndColumn = $intervalEndColumn;
}
/**
* @return string
*/
public function getOperator()
{
return $this->operator;
}
/**
* @return mixed
*/
public function getValue()
{
return $this->value;
}
/**
* @return string|ExpressionInterface|Query
*/
public function getIntervalStartColumn()
{
return $this->intervalStartColumn;
}
/**
* @return string|ExpressionInterface|Query
*/
public function getIntervalEndColumn()
{
return $this->intervalEndColumn;
}
/**
* {@inheritdoc}
* @throws InvalidArgumentException if wrong number of operands have been given.
*/
public static function fromArrayDefinition($operator, $operands)
{
if (!isset($operands[0], $operands[1], $operands[2])) {
throw new InvalidArgumentException("Operator '$operator' requires three operands.");
}
return new static($operands[0], $operator, $operands[1], $operands[2]);
}
}

81
framework/db/conditions/BetweenColumnsConditionBuilder.php

@ -0,0 +1,81 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
use yii\db\ExpressionBuilderInterface;
use yii\db\ExpressionBuilderTrait;
use yii\db\ExpressionInterface;
use yii\db\Query;
/**
* Class BetweenColumnsConditionBuilder builds objects of [[BetweenColumnsCondition]]
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class BetweenColumnsConditionBuilder implements ExpressionBuilderInterface
{
use ExpressionBuilderTrait;
/**
* Method builds the raw SQL from the $expression that will not be additionally
* escaped or quoted.
*
* @param ExpressionInterface|BetweenColumnsCondition $expression the expression to be built.
* @param array $params the binding parameters.
* @return string the raw SQL that will not be additionally escaped or quoted.
*/
public function build(ExpressionInterface $expression, array &$params = [])
{
$operator = $expression->getOperator();
$startColumn = $this->escapeColumnName($expression->getIntervalStartColumn(), $params);
$endColumn = $this->escapeColumnName($expression->getIntervalEndColumn(), $params);
$value = $this->createPlaceholder($expression->getValue(), $params);
return "$value $operator $startColumn AND $endColumn";
}
/**
* Prepares column name to be used in SQL statement.
*
* @param Query|ExpressionInterface|string $columnName
* @param array $params the binding parameters.
* @return string
*/
protected function escapeColumnName($columnName, &$params = [])
{
if ($columnName instanceof Query) {
list($sql, $params) = $this->queryBuilder->build($columnName, $params);
return "($sql)";
} elseif ($columnName instanceof ExpressionInterface) {
return $this->queryBuilder->buildExpression($columnName, $params);
} elseif (strpos($columnName, '(') === false) {
return $this->queryBuilder->db->quoteColumnName($columnName);
}
return $columnName;
}
/**
* Attaches $value to $params array and returns placeholder.
*
* @param mixed $value
* @param array $params passed by reference
* @return string
*/
protected function createPlaceholder($value, &$params)
{
if ($value instanceof ExpressionInterface) {
return $this->queryBuilder->buildExpression($value, $params);
}
return $this->queryBuilder->bindParam($value, $params);
}
}

98
framework/db/conditions/BetweenCondition.php

@ -0,0 +1,98 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
use yii\base\InvalidArgumentException;
/**
* Class BetweenCondition represents a `BETWEEN` condition.
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class BetweenCondition implements ConditionInterface
{
/**
* @var string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)
*/
private $operator;
/**
* @var mixed the column name to the left of [[operator]]
*/
private $column;
/**
* @var mixed beginning of the interval
*/
private $intervalStart;
/**
* @var mixed end of the interval
*/
private $intervalEnd;
/**
* Creates a condition with the `BETWEEN` operator.
*
* @param mixed $column the literal to the left of $operator
* @param string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)
* @param mixed $intervalStart beginning of the interval
* @param mixed $intervalEnd end of the interval
*/
public function __construct($column, $operator, $intervalStart, $intervalEnd)
{
$this->column = $column;
$this->operator = $operator;
$this->intervalStart = $intervalStart;
$this->intervalEnd = $intervalEnd;
}
/**
* @return string
*/
public function getOperator()
{
return $this->operator;
}
/**
* @return mixed
*/
public function getColumn()
{
return $this->column;
}
/**
* @return mixed
*/
public function getIntervalStart()
{
return $this->intervalStart;
}
/**
* @return mixed
*/
public function getIntervalEnd()
{
return $this->intervalEnd;
}
/**
* {@inheritdoc}
* @throws InvalidArgumentException if wrong number of operands have been given.
*/
public static function fromArrayDefinition($operator, $operands)
{
if (!isset($operands[0], $operands[1], $operands[2])) {
throw new InvalidArgumentException("Operator '$operator' requires three operands.");
}
return new static($operands[0], $operator, $operands[1], $operands[2]);
}
}

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

Loading…
Cancel
Save