Browse Source

Merge branch 'master' into 2.1

# Conflicts:
#	framework/db/mssql/QueryBuilder.php
#	framework/helpers/BaseHtml.php
#	framework/rbac/DbManager.php
#	framework/rbac/PhpManager.php
#	framework/validators/UniqueValidator.php
#	framework/web/Response.php
#	tests/framework/db/ActiveQueryTest.php
#	tests/framework/db/ActiveRecordTest.php
#	tests/framework/filters/AccessRuleTest.php
#	tests/framework/rbac/ManagerTestCase.php
#	tests/framework/test/ActiveFixtureTest.php
#	tests/framework/web/ErrorActionTest.php
tags/3.0.0-alpha1
Alexander Makarov 8 years ago
parent
commit
68dd3d4567
No known key found for this signature in database
GPG Key ID: 3617B79C6A325E4A
  1. 3
      .dockerignore
  2. 1
      .gitignore
  3. 118
      .gitlab-ci.yml
  4. 10
      .travis.yml
  5. 8
      Dockerfile
  6. 80
      README.md
  7. 12
      build/controllers/DevController.php
  8. 4
      build/controllers/MimeTypeController.php
  9. 34
      build/controllers/PhpDocController.php
  10. 46
      build/controllers/ReleaseController.php
  11. 4
      build/controllers/TranslationController.php
  12. 4
      build/controllers/Utf8Controller.php
  13. 4
      build/controllers/views/translation/report_html.php
  14. 109
      composer.lock
  15. 2
      docs/guide-es/db-dao.md
  16. 6
      docs/guide-fr/blocktypes.json
  17. 345
      docs/guide-fr/caching-data.md
  18. 143
      docs/guide-fr/caching-fragment.md
  19. 108
      docs/guide-fr/caching-http.md
  20. 33
      docs/guide-fr/caching-page.md
  21. 2
      docs/guide-fr/db-dao.md
  22. 404
      docs/guide-fr/helper-array.md
  23. 396
      docs/guide-fr/helper-html.md
  24. 68
      docs/guide-fr/helper-overview.md
  25. 161
      docs/guide-fr/helper-url.md
  26. 2
      docs/guide-fr/start-forms.md
  27. 10
      docs/guide-fr/start-installation.md
  28. 2
      docs/guide-ja/db-dao.md
  29. 2
      docs/guide-pl/db-active-record.md
  30. 2
      docs/guide-pt-BR/db-active-record.md
  31. 2
      docs/guide-ru/db-dao.md
  32. 4
      docs/guide-ru/tutorial-core-validators.md
  33. 2
      docs/guide-zh-CN/db-dao.md
  34. 2
      docs/guide/db-dao.md
  35. 6
      docs/guide/input-forms.md
  36. 3
      docs/guide/runtime-logging.md
  37. 44
      docs/guide/security-best-practices.md
  38. 3
      docs/guide/tutorial-core-validators.md
  39. 5
      docs/guide/tutorial-performance-tuning.md
  40. 55
      framework/CHANGELOG.md
  41. 6
      framework/UPGRADE.md
  42. 2
      framework/assets/yii.activeForm.js
  43. 2
      framework/assets/yii.gridView.js
  44. 6
      framework/assets/yii.js
  45. 2
      framework/base/ErrorHandler.php
  46. 16
      framework/base/Event.php
  47. 2
      framework/base/Model.php
  48. 12
      framework/base/Security.php
  49. 2
      framework/base/Widget.php
  50. 8
      framework/behaviors/SluggableBehavior.php
  51. 2
      framework/caching/Cache.php
  52. 3
      framework/console/Controller.php
  53. 9
      framework/console/Request.php
  54. 40
      framework/console/controllers/MessageController.php
  55. 3
      framework/data/ActiveDataProvider.php
  56. 8
      framework/db/ActiveQuery.php
  57. 4
      framework/db/ActiveRecord.php
  58. 2
      framework/db/Command.php
  59. 28
      framework/db/Connection.php
  60. 28
      framework/db/Query.php
  61. 33
      framework/db/QueryBuilder.php
  62. 11
      framework/db/QueryTrait.php
  63. 2
      framework/db/Schema.php
  64. 47
      framework/db/ViewFinderTrait.php
  65. 12
      framework/db/cubrid/QueryBuilder.php
  66. 66
      framework/db/mssql/QueryBuilder.php
  67. 22
      framework/db/mssql/Schema.php
  68. 19
      framework/db/mysql/QueryBuilder.php
  69. 32
      framework/db/oci/QueryBuilder.php
  70. 40
      framework/db/oci/Schema.php
  71. 22
      framework/db/pgsql/QueryBuilder.php
  72. 31
      framework/db/pgsql/Schema.php
  73. 20
      framework/db/sqlite/QueryBuilder.php
  74. 20
      framework/di/Instance.php
  75. 6
      framework/grid/DataColumn.php
  76. 29
      framework/helpers/BaseHtml.php
  77. 2
      framework/helpers/BaseInflector.php
  78. 55
      framework/helpers/BaseStringHelper.php
  79. 2
      framework/i18n/Formatter.php
  80. 2
      framework/i18n/I18N.php
  81. 4
      framework/messages/bg/yii.php
  82. 4
      framework/messages/da/yii.php
  83. 4
      framework/messages/et/yii.php
  84. 4
      framework/messages/fr/yii.php
  85. 4
      framework/messages/hu/yii.php
  86. 18
      framework/messages/ms/yii.php
  87. 2
      framework/messages/pt-BR/yii.php
  88. 199
      framework/messages/tg/yii.php
  89. 2
      framework/messages/uz/yii.php
  90. 2
      framework/rbac/DbManager.php
  91. 2
      framework/rbac/PhpManager.php
  92. 2
      framework/rbac/migrations/schema-oci.sql
  93. 2
      framework/requirements/YiiRequirementChecker.php
  94. 11
      framework/validators/BooleanValidator.php
  95. 4
      framework/validators/CompareValidator.php
  96. 2
      framework/validators/DateValidator.php
  97. 4
      framework/validators/EmailValidator.php
  98. 28
      framework/validators/FileValidator.php
  99. 20
      framework/validators/ImageValidator.php
  100. 4
      framework/validators/IpValidator.php
  101. Some files were not shown because too many files have changed in this diff Show More

3
.dockerignore

@ -0,0 +1,3 @@
.git
vendor
docs

1
.gitignore vendored

@ -41,3 +41,4 @@ phpunit.phar
# NPM packages
/node_modules
.env

118
.gitlab-ci.yml

@ -0,0 +1,118 @@
before_script:
# set stack isolation
- export ISOLATION=buildpipeline${CI_PIPELINE_ID}${CI_BUILD_NAME}
- export COMPOSE_PROJECT_NAME=${ISOLATION}
- export TUPLE_C=$(expr ${CI_BUILD_ID} % 99)
- echo ${TUPLE_C}
# run docker-compose commands from tests environment
- cd tests
- cp .env-dist .env
- docker-compose config
after_script:
- export ISOLATION=buildpipeline${CI_PIPELINE_ID}${CI_BUILD_NAME}
- export COMPOSE_PROJECT_NAME=${ISOLATION}
# run docker-compose commands from tests environment
- cd tests
- cp .env-dist .env
- docker-compose down -v --remove-orphans
- docker ps -f name=${ISOLATION}
stages:
- travis
- test
- cleanup
test:
stage: test
script:
- docker-compose up --build -d
- docker-compose run --rm php vendor/bin/phpunit -v --exclude caching,db,data --log-junit tests/_junit/test.xml
caching:
stage: test
only:
- tests/caching
- tests/full
script:
- export COMPOSE_FILE=docker-compose.yml:docker-compose.${CI_BUILD_NAME}.yml
- docker-compose up --build -d
- docker-compose run --rm php vendor/bin/phpunit -v --group caching --exclude db
db:
stage: test
only:
- tests/mysql
- tests/full
script:
- docker-compose up --build -d
- docker-compose run --rm php vendor/bin/phpunit -v --group db --exclude caching,mysql,pgsql,mssql,cubrid,oci
mysql:
stage: test
only:
- tests/mysql
- tests/full
script:
- export COMPOSE_FILE=docker-compose.yml:docker-compose.${CI_BUILD_NAME}.yml
- docker-compose up --build -d
# wait for db (retry X times)
- docker-compose run --rm php bash -c "while ! curl mysql:3306; do ((c++)) && ((c==30)) && break; sleep 2; done"
- docker-compose run --rm php vendor/bin/phpunit -v --group mysql
pgsql:
stage: test
only:
- tests/pgsql
- tests/full
script:
- export COMPOSE_FILE=docker-compose.yml:docker-compose.${CI_BUILD_NAME}.yml
- docker-compose up --build -d
# wait for db (retry X times)
- docker-compose run --rm php bash -c 'while [ true ]; do curl postgres:5432; if [ $? == 52 ]; then break; fi; ((c++)) && ((c==25)) && break; sleep 2; done'
- docker-compose run --rm php vendor/bin/phpunit -v --group pgsql
cubrid:
stage: test
only:
- tests/cubrid
- tests/extra
script:
- cd cubrid
- docker-compose up --build -d
# wait for db (retry X times)
- docker-compose run --rm php bash -c 'while [ true ]; do curl cubrid:1523; if [ $? == 56 ]; then break; fi; ((c++)) && ((c==20)) && break; sleep 3; done'
- sleep 5
- docker-compose run --rm php /project/vendor/bin/phpunit -v --group cubrid
mssql:
stage: test
only:
- tests/mssql
- tests/extra
script:
- cd mssql
- docker-compose up --build -d
# wait for db (retry X times)
- docker-compose run --rm php bash -c 'while [ true ]; do curl mssql:1433; if [ $? == 52 ]; then break; fi; ((c++)) && ((c==15)) && break; sleep 5; done'
- sleep 3
# Note: Password has to be the last parameter
- docker-compose run --rm sqlcmd sh -c 'sqlcmd -S mssql -U sa -Q "CREATE DATABASE yii2test" -P Microsoft-12345'
- docker-compose run --rm php vendor/bin/phpunit -v --group mssql
travis:
stage: travis
only:
- travis
script:
- export COMPOSE_FILE=docker-compose.yml:docker-compose.mysql.yml:docker-compose.pgsql.yml
- docker-compose up --build -d
# wait for dbs ...
- sleep 10
- docker-compose run --rm php vendor/bin/phpunit -v --exclude mssql,cubrid,oci,wincache,xcache,zenddata,cubrid

10
.travis.yml

@ -9,10 +9,12 @@ dist: trusty
sudo: false
# build only on master branches
branches:
only:
- master
- 2.1
# commented as this prevents people from running builds on their forks:
# https://github.com/yiisoft/yii2/commit/bd87be990fa238c6d5e326d0a171f38d02dc253a
#branches:
# only:
# - master
# - 2.1
#

8
Dockerfile

@ -0,0 +1,8 @@
FROM dmstr/php-yii2:7.0-fpm-1.9-beta2-alpine-nginx
# Project source-code
WORKDIR /project
ADD composer.* /project/
RUN /usr/local/bin/composer install --prefer-dist
ADD ./ /project
ENV PATH /project/vendor/bin:${PATH}

80
README.md

@ -1,12 +1,13 @@
Yii PHP Framework Version 2
===========================
<p align="center">
<a href="http://www.yiiframework.com/" target="_blank">
<img src="http://static.yiiframework.com/files/logo/yii.png" width="400" alt="Yii Framework" />
</a>
</p>
Thank you for choosing Yii 2 - a modern PHP framework designed for professional Web development.
Yii 2 is a complete rewrite of its previous version Yii 1.1 which is one of the most popular PHP frameworks.
Yii 2 inherits the main spirit behind Yii for being simple, fast and highly extensible.
Yii 2 requires PHP 5.4 and embraces the best practices and protocols found in modern Web application development.
Yii 2 is a modern framework designed to be a solid foundation for your PHP application.
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)
@ -18,49 +19,62 @@ Yii 2 requires PHP 5.4 and embraces the best practices and protocols found in mo
[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/yiisoft/yii2/badges/quality-score.png?s=b1074a1ff6d0b214d54fa5ab7abbb90fc092471d)](https://scrutinizer-ci.com/g/yiisoft/yii2/)
[![Code Climate](https://img.shields.io/codeclimate/github/yiisoft/yii2.svg)](https://codeclimate.com/github/yiisoft/yii2)
DIRECTORY STRUCTURE
-------------------
```
build/ internally used build tools
docs/ documentation
framework/ core framework code
tests/ tests of the core framework code
```
REQUIREMENTS
Installation
------------
The minimum requirement by Yii is that your Web server supports PHP 5.4.
- The minimum required PHP version of Yii is PHP 5.4.
- It works best with PHP 7.
- [Follow the Definitive Guide](http://www.yiiframework.com/doc-2.0/guide-start-installation.html)
in order to get step by step instructions.
DOCUMENTATION
Documentation
-------------
Yii 2.0 has a [Definitive Guide](http://www.yiiframework.com/doc-2.0/guide-index.html) and
a [Class Reference](http://www.yiiframework.com/doc-2.0/index.html) which cover every detail of Yii.
There is also a [PDF version](http://stuff.cebe.cc/yii2-guide.en.pdf) of the Definitive Guide
- A [Definitive Guide](http://www.yiiframework.com/doc-2.0/guide-index.html) and
a [Class Reference](http://www.yiiframework.com/doc-2.0/index.html) cover every detail
of the framework.
- There is a [PDF version](http://stuff.cebe.cc/yii2-guide.en.pdf) of the Definitive Guide
and a [Definitive Guide Mirror](http://stuff.cebe.cc/yii2docs/) which is updated every 15 minutes.
- For Yii 1.1 users, there is [Upgrading from Yii 1.1](docs/guide/intro-upgrade-from-v1.md)
to get an idea of what has changed in 2.0.
For 1.1 users, you may refer to [Upgrading from Yii 1.1](docs/guide/intro-upgrade-from-v1.md)
to have a general idea of what has changed in 2.0.
Community
---------
- Participate in [discussions at forums](http://www.yiiframework.com/forum/).
- [Chat in IRC](http://www.yiiframework.com/chat/).
- Follow us on [Facebook](https://www.facebook.com/groups/yiitalk/), [Twitter](https://twitter.com/yiiframework)
and [GitHub](https://github.com/yiisoft/yii2).
HOW TO PARTICIPATE
------------------
Contributing
------------
### Your participation to Yii 2 development is very welcome!
The framework is [Open Source](LICENSE.md) powered by [an excellent community](https://github.com/yiisoft/yii2/graphs/contributors).
You may participate in the following ways:
You may join us and:
- [Report an issue](docs/internals/report-an-issue.md)
- [Translate documentation or messages](docs/internals/translation-workflow.md)
- [Give us feedback or start a design discussion](http://www.yiiframework.com/forum/index.php/forum/42-general-discussions-for-yii-20/)
- [Contribute to the core code or fix bugs](docs/internals/git-workflow.md)
### Acknowledging or citing Yii 2
### Reporting Security issues
Please refer to a [special page at the website](http://www.yiiframework.com/security/)
describing proper workflow for security issue reports.
### Directory Structure
```
build/ internally used build tools
docs/ documentation
framework/ core framework code
tests/ tests of the core framework code
```
### Spreading the Word
Acknowledging or citing Yii 2 is as important as direct contributions.
**In presentations**

12
build/controllers/DevController.php

@ -70,14 +70,14 @@ class DevController extends Controller
return 1;
}
foreach($this->extensions as $ext => $repo) {
foreach ($this->extensions as $ext => $repo) {
$ret = $this->actionExt($ext);
if ($ret !== 0) {
return $ret;
}
}
foreach($this->apps as $app => $repo) {
foreach ($this->apps as $app => $repo) {
$ret = $this->actionApp($app);
if ($ret !== 0) {
return $ret;
@ -107,7 +107,7 @@ class DevController extends Controller
asort($dirs);
$oldcwd = getcwd();
foreach($dirs as $dir) {
foreach ($dirs as $dir) {
$displayDir = substr($dir, strlen($base));
$this->stdout("Running '$command' in $displayDir...\n", Console::BOLD);
chdir($dir);
@ -252,7 +252,7 @@ class DevController extends Controller
$this->unlink($link);
}
$extensions = $this->findDirs("$dir/vendor/yiisoft");
foreach($extensions as $ext) {
foreach ($extensions as $ext) {
if (is_link($link = "$dir/vendor/yiisoft/yii2-$ext")) {
$this->stdout("Removing symlink $link.\n");
$this->unlink($link);
@ -276,7 +276,7 @@ class DevController extends Controller
symlink("$base/framework", $link);
}
$extensions = $this->findDirs("$dir/vendor/yiisoft");
foreach($extensions as $ext) {
foreach ($extensions as $ext) {
if (is_dir($link = "$dir/vendor/yiisoft/yii2-$ext")) {
$this->stdout("Removing dir $link.\n");
FileHelper::removeDirectory($link);
@ -359,7 +359,7 @@ class DevController extends Controller
}
closedir($handle);
foreach($list as $i => $e) {
foreach ($list as $i => $e) {
if ($e === 'composer') { // skip composer to not break composer update
unset($list[$i]);
}

4
build/controllers/MimeTypeController.php

@ -39,14 +39,14 @@ class MimeTypeController extends Controller
$this->stdout("done.\n", Console::FG_GREEN);
$this->stdout("generating file $outFile...");
$mimeMap = [];
foreach(explode("\n", $content) as $line) {
foreach (explode("\n", $content) as $line) {
$line = trim($line);
if (empty($line) || $line[0] == '#') { // skip comments and empty lines
continue;
}
$parts = preg_split('/\s+/', $line);
$mime = array_shift($parts);
foreach($parts as $ext) {
foreach ($parts as $ext) {
if (!empty($ext)) {
$mimeMap[$ext] = $mime;
}

34
build/controllers/PhpDocController.php

@ -167,12 +167,12 @@ class PhpDocController extends Controller
'tests/',
'vendor/',
];
foreach($extensionExcept as $ext => $paths) {
foreach($paths as $path) {
foreach ($extensionExcept as $ext => $paths) {
foreach ($paths as $path) {
$except[] = "/extensions/$ext$path";
}
}
} elseif (preg_match('~extensions/([\w\d-]+)[\\\\/]?$~', $root, $matches)) {
} elseif (preg_match('~extensions/([\w-]+)[\\\\/]?$~', $root, $matches)) {
$extensionPath = dirname(rtrim($root, '\\/'));
$this->setUpExtensionAliases($extensionPath);
@ -184,7 +184,7 @@ class PhpDocController extends Controller
}
if (isset($extensionExcept[$extension])) {
foreach($extensionExcept[$extension] as $path) {
foreach ($extensionExcept[$extension] as $path) {
$except[] = $path;
}
}
@ -196,7 +196,7 @@ class PhpDocController extends Controller
// if ($extension === 'composer') {
// return [];
// }
} elseif (preg_match('~apps/([\w\d-]+)[\\\\/]?$~', $root, $matches)) {
} elseif (preg_match('~apps/([\w-]+)[\\\\/]?$~', $root, $matches)) {
$extensionPath = dirname(dirname(rtrim($root, '\\/'))) . '/extensions';
$this->setUpExtensionAliases($extensionPath);
@ -255,7 +255,7 @@ class PhpDocController extends Controller
$namespace = false;
$namespaceLine = '';
$contentAfterNamespace = false;
foreach($lines as $i => $line) {
foreach ($lines as $i => $line) {
$line = trim($line);
if (!empty($line)) {
if (strncmp($line, 'namespace', 9) === 0) {
@ -269,7 +269,7 @@ class PhpDocController extends Controller
}
if ($namespace !== false && $contentAfterNamespace !== false) {
while($contentAfterNamespace > 0) {
while ($contentAfterNamespace > 0) {
array_shift($lines);
$contentAfterNamespace--;
}
@ -297,7 +297,7 @@ class PhpDocController extends Controller
$listIndent = '';
$tag = false;
$indent = '';
foreach($lines as $i => $line) {
foreach ($lines as $i => $line) {
if (preg_match('~^(\s*)/\*\*$~', $line, $matches)) {
$docBlock = true;
$indent = $matches[1];
@ -348,8 +348,8 @@ class PhpDocController extends Controller
{
return preg_replace_callback('~@(param|return) ([\w\\|]+)~i', function($matches) {
$types = explode('|', $matches[2]);
foreach($types as $i => $type) {
switch($type){
foreach ($types as $i => $type) {
switch ($type) {
case 'integer': $types[$i] = 'int'; break;
case 'boolean': $types[$i] = 'bool'; break;
}
@ -367,7 +367,7 @@ class PhpDocController extends Controller
// remove blank lines between properties
$skip = true;
$level = 0;
foreach($lines as $i => $line) {
foreach ($lines as $i => $line) {
if (strpos($line, 'class ') !== false) {
$skip = false;
}
@ -402,7 +402,7 @@ class PhpDocController extends Controller
$skip = true;
$level = 0; // track array properties
$property = '';
foreach($lines as $i => $line) {
foreach ($lines as $i => $line) {
if (strpos($line, 'class ') !== false) {
$skip = false;
}
@ -428,10 +428,10 @@ class PhpDocController extends Controller
$endofPrivate = $i;
$property = 'Private';
$level = 0;
} elseif (substr($line,0 , 6) === 'const ') {
} elseif (substr($line, 0, 6) === 'const ') {
$endofConst = $i;
$property = false;
} elseif (substr($line,0 , 4) === 'use ') {
} elseif (substr($line, 0, 4) === 'use ') {
$endofUse = $i;
$property = false;
} elseif (!empty($line) && $line[0] === '*') {
@ -447,7 +447,7 @@ class PhpDocController extends Controller
}
$endofAll = false;
foreach(['Private', 'Protected', 'Public', 'Const', 'Use'] as $var) {
foreach (['Private', 'Protected', 'Public', 'Const', 'Use'] as $var) {
if (${'endof'.$var} !== false) {
$endofAll = ${'endof'.$var};
break;
@ -456,7 +456,7 @@ class PhpDocController extends Controller
// $this->checkPropertyOrder($lineInfo);
$result = [];
foreach($lines as $i => $line) {
foreach ($lines as $i => $line) {
$result[] = $line;
if (!($propertiesOnly && $i === $endofAll)) {
if ($i === $endofUse || $i === $endofConst || $i === $endofPublic ||
@ -753,7 +753,7 @@ class PhpDocController extends Controller
// example: yii\di\ServiceLocator setComponents() is not recognized in the whole but in
// a part of the class.
$parts = $split ? explode("\n\n", $subject) : [$subject];
foreach($parts as $part) {
foreach ($parts as $part) {
preg_match_all($pattern . 'suU', $part, $matches, PREG_SET_ORDER);
foreach ($matches as &$set) {
foreach ($set as $i => $match)

46
build/controllers/ReleaseController.php

@ -105,7 +105,7 @@ class ReleaseController extends Controller
}
if ($this->update) {
foreach($items as $item) {
foreach ($items as $item) {
$this->stdout("fetching tags for $item...");
if ($item === 'framework') {
$this->gitFetchTags("{$this->basePath}");
@ -126,7 +126,7 @@ class ReleaseController extends Controller
// print version table
$w = $this->minWidth(array_keys($versions));
$this->stdout(str_repeat(' ', $w + 2) . "Current Version Next Version\n", Console::BOLD);
foreach($versions as $ext => $version) {
foreach ($versions as $ext => $version) {
$this->stdout($ext . str_repeat(' ', $w + 3 - mb_strlen($ext)) . $version . "");
$this->stdout(str_repeat(' ', 17 - mb_strlen($version)) . $nextVersions[$ext] . "\n");
}
@ -136,7 +136,7 @@ class ReleaseController extends Controller
private function minWidth($a)
{
$w = 1;
foreach($a as $s) {
foreach ($a as $s) {
if (($l = mb_strlen($s)) > $w) {
$w = $l;
}
@ -199,7 +199,7 @@ class ReleaseController extends Controller
if ($this->version !== null) {
// if a version is explicitly given
$newVersions = [];
foreach($versions as $k => $v) {
foreach ($versions as $k => $v) {
$newVersions[$k] = $this->version;
}
} else {
@ -230,7 +230,7 @@ class ReleaseController extends Controller
return 1;
}
foreach($what as $ext) {
foreach ($what as $ext) {
if ($ext === 'framework') {
$this->releaseFramework("{$this->basePath}/framework", $newVersions['framework']);
} elseif (strncmp('app-', $ext, 4) === 0) {
@ -264,7 +264,7 @@ class ReleaseController extends Controller
$versions = $this->getCurrentVersions($what);
$this->stdout("You are about to generate packages for the following things:\n\n");
foreach($what as $ext) {
foreach ($what as $ext) {
if (strncmp('app-', $ext, 4) === 0) {
$this->stdout(" - ");
$this->stdout(substr($ext, 4), Console::FG_RED);
@ -289,7 +289,7 @@ class ReleaseController extends Controller
return 1;
}
foreach($what as $ext) {
foreach ($what as $ext) {
if ($ext === 'framework') {
throw new Exception('Can not package framework.');
} elseif (strncmp('app-', $ext, 4) === 0) {
@ -334,7 +334,7 @@ class ReleaseController extends Controller
protected function printWhat(array $what, $newVersions, $versions)
{
foreach($what as $ext) {
foreach ($what as $ext) {
if (strncmp('app-', $ext, 4) === 0) {
$this->stdout(" - ");
$this->stdout(substr($ext, 4), Console::FG_RED);
@ -353,7 +353,7 @@ class ReleaseController extends Controller
protected function printWhatUrls(array $what, $oldVersions)
{
foreach($what as $ext) {
foreach ($what as $ext) {
if ($ext === 'framework') {
$this->stdout("framework: https://github.com/yiisoft/yii2-framework/compare/{$oldVersions[$ext]}...master\n");
$this->stdout("app-basic: https://github.com/yiisoft/yii2-app-basic/compare/{$oldVersions[$ext]}...master\n");
@ -372,7 +372,7 @@ class ReleaseController extends Controller
*/
protected function validateWhat(array $what, $limit = [], $ensureGitClean = true)
{
foreach($what as $w) {
foreach ($what as $w) {
if (strncmp('app-', $w, 4) === 0) {
if (!empty($limit) && !in_array('app', $limit)) {
throw new Exception("Only the following types are allowed: ".implode(', ', $limit)."\n");
@ -460,7 +460,7 @@ class ReleaseController extends Controller
$this->runGit("git diff --color", $frameworkPath);
$this->stdout("\n\n\nCheck whether the above diff is okay, if not you may change things as needed before continuing.\n");
$this->stdout("You may abort the program with Ctrl + C and reset the changes by running `git checkout -- .` in the repo.\n\n");
} while(!$this->confirm("Type `yes` to continue, `no` to view git diff again. Continue?"));
} while (!$this->confirm("Type `yes` to continue, `no` to view git diff again. Continue?"));
$this->stdout("\n\n");
$this->stdout(" **** RELEASE TIME! ****\n", Console::FG_YELLOW, Console::BOLD);
@ -581,7 +581,7 @@ class ReleaseController extends Controller
$this->runGit("git diff --color", $path);
$this->stdout("\n\n\nCheck whether the above diff is okay, if not you may change things as needed before continuing.\n");
$this->stdout("You may abort the program with Ctrl + C and reset the changes by running `git checkout -- .` in the repo.\n\n");
} while(!$this->confirm("Type `yes` to continue, `no` to view git diff again. Continue?"));
} while (!$this->confirm("Type `yes` to continue, `no` to view git diff again. Continue?"));
$this->stdout("\n\n");
$this->stdout(" **** RELEASE TIME! ****\n", Console::FG_YELLOW, Console::BOLD);
@ -633,7 +633,7 @@ class ReleaseController extends Controller
protected function setAppAliases($app, $path)
{
$this->_oldAlias = Yii::getAlias('@app');
switch($app) {
switch ($app) {
case 'basic':
Yii::setAlias('@app', $path);
break;
@ -699,7 +699,7 @@ class ReleaseController extends Controller
$this->runGit("git diff --color", $path);
$this->stdout("\n\n\nCheck whether the above diff is okay, if not you may change things as needed before continuing.\n");
$this->stdout("You may abort the program with Ctrl + C and reset the changes by running `git checkout -- .` in the repo.\n\n");
} while(!$this->confirm("Type `yes` to continue, `no` to view git diff again. Continue?"));
} while (!$this->confirm("Type `yes` to continue, `no` to view git diff again. Continue?"));
$this->stdout("\n\n");
$this->stdout(" **** RELEASE TIME! ****\n", Console::FG_YELLOW, Console::BOLD);
@ -825,7 +825,7 @@ class ReleaseController extends Controller
{
$headline = "\n$version under development\n";
$headline .= str_repeat('-', strlen($headline) - 2) . "\n\n- no changes in this release.\n";
foreach($this->getChangelogs($what) as $file) {
foreach ($this->getChangelogs($what) as $file) {
$lines = explode("\n", file_get_contents($file));
$hl = [
array_shift($lines),
@ -839,7 +839,7 @@ class ReleaseController extends Controller
protected function resortChangelogs($what, $version)
{
foreach($this->getChangelogs($what) as $file) {
foreach ($this->getChangelogs($what) as $file) {
// split the file into relevant parts
list($start, $changelog, $end) = $this->splitChangelog($file, $version);
$changelog = $this->resortChangelog($changelog);
@ -860,7 +860,7 @@ class ReleaseController extends Controller
$end = [];
$state = 'start';
foreach($lines as $l => $line) {
foreach ($lines as $l => $line) {
// starting from the changelogs headline
if (isset($lines[$l-2]) && strpos($lines[$l-2], $version) !== false &&
isset($lines[$l-1]) && strncmp($lines[$l-1], '---', 3) === 0) {
@ -886,7 +886,7 @@ class ReleaseController extends Controller
protected function resortChangelog($changelog)
{
// cleanup whitespace
foreach($changelog as $i => $line) {
foreach ($changelog as $i => $line) {
$changelog[$i] = rtrim($line);
}
$changelog = array_filter($changelog);
@ -926,7 +926,7 @@ class ReleaseController extends Controller
protected function getExtensionChangelogs($what)
{
return array_filter(glob($this->basePath . '/extensions/*/CHANGELOG.md'), function($elem) use ($what) {
foreach($what as $ext) {
foreach ($what as $ext) {
if (strpos($elem, "extensions/$ext/CHANGELOG.md") !== false) {
return true;
}
@ -979,7 +979,7 @@ class ReleaseController extends Controller
protected function sed($pattern, $replace, $files)
{
foreach((array) $files as $file) {
foreach ((array) $files as $file) {
file_put_contents($file, preg_replace($pattern, $replace, file_get_contents($file)));
}
}
@ -987,7 +987,7 @@ class ReleaseController extends Controller
protected function getCurrentVersions(array $what)
{
$versions = [];
foreach($what as $ext) {
foreach ($what as $ext) {
if ($ext === 'framework') {
chdir("{$this->basePath}/framework");
} elseif (strncmp('app-', $ext, 4) === 0) {
@ -1011,13 +1011,13 @@ class ReleaseController extends Controller
protected function getNextVersions(array $versions, $type)
{
foreach($versions as $k => $v) {
foreach ($versions as $k => $v) {
if (empty($v)) {
$versions[$k] = '2.0.0';
continue;
}
$parts = explode('.', $v);
switch($type) {
switch ($type) {
case self::MINOR:
$parts[1]++;
$parts[2] = 0;

4
build/controllers/TranslationController.php

@ -47,7 +47,7 @@ class TranslationController extends Controller
$errors = $this->checkFiles($translatedFilePath);
$diff = empty($errors) ? $this->getDiff($translatedFilePath, $sourceFilePath) : '';
if(!empty($diff)) {
if (!empty($diff)) {
$errors[] = 'Translation outdated.';
}
@ -68,7 +68,7 @@ class TranslationController extends Controller
$translatedFilePath = $translationPath . '/' . $fileinfo->getFilename();
$errors = $this->checkFiles(null, $translatedFilePath);
if(!empty($errors)) {
if (!empty($errors)) {
$results[$fileinfo->getFilename()]['errors'] = $errors;
}
}

4
build/controllers/Utf8Controller.php

@ -39,14 +39,14 @@ class Utf8Controller extends Controller
]);
}
foreach($files as $file) {
foreach ($files as $file) {
$content = file_get_contents($file);
$chars = preg_split('//u', $content, null, PREG_SPLIT_NO_EMPTY);
$line = 1;
$pos = 0;
foreach($chars as $c) {
foreach ($chars as $c) {
$ord = $this->unicodeOrd($c);

4
build/controllers/views/translation/report_html.php

@ -36,9 +36,9 @@ use yii\helpers\Html;
<li><strong>Translation:</strong> <?= Html::encode($translationPath) ?></li>
</ul>
<?php foreach($results as $name => $result): ?>
<?php foreach ($results as $name => $result): ?>
<h2 class="<?= empty($result['errors']) ? 'ok' : 'errors' ?>"><?= $name ?></h2>
<?php foreach($result['errors'] as $error): ?>
<?php foreach ($result['errors'] as $error): ?>
<p><?= Html::encode($error) ?></p>
<?php endforeach ?>
<?php if (!empty($result['diff'])): ?>

109
composer.lock generated

@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "30a97926034335c40273795ce316f36c",
"content-hash": "58ccdf60f6da90c6d1623d9755c0b830",
"hash": "db4e038c0e8ca747784fb195c82bfdad",
"content-hash": "cc4b01a602c948040169ebbc1ac30186",
"packages": [
{
"name": "bower-asset/jquery",
@ -44,12 +44,12 @@
"source": {
"type": "git",
"url": "https://github.com/RobinHerbots/jquery.inputmask.git",
"reference": "5a72c563b502b8e05958a524cdfffafe9987be38"
"reference": "ec7726993217ee7b01023ad4f7f1b6a51446a39d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/RobinHerbots/jquery.inputmask/zipball/5a72c563b502b8e05958a524cdfffafe9987be38",
"reference": "5a72c563b502b8e05958a524cdfffafe9987be38",
"url": "https://api.github.com/repos/RobinHerbots/jquery.inputmask/zipball/ec7726993217ee7b01023ad4f7f1b6a51446a39d",
"reference": "ec7726993217ee7b01023ad4f7f1b6a51446a39d",
"shasum": ""
},
"require": {
@ -114,16 +114,16 @@
},
{
"name": "bower-asset/yii2-pjax",
"version": "dev-master",
"version": "v2.0.6",
"source": {
"type": "git",
"url": "https://github.com/yiisoft/jquery-pjax.git",
"reference": "3f20897307cca046fca5323b318475ae9dac0ca0"
"reference": "60728da6ade5879e807a49ce59ef9a72039b8978"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/yiisoft/jquery-pjax/zipball/3f20897307cca046fca5323b318475ae9dac0ca0",
"reference": "3f20897307cca046fca5323b318475ae9dac0ca0",
"url": "https://api.github.com/repos/yiisoft/jquery-pjax/zipball/60728da6ade5879e807a49ce59ef9a72039b8978",
"reference": "60728da6ade5879e807a49ce59ef9a72039b8978",
"shasum": ""
},
"require": {
@ -136,18 +136,15 @@
".travis.yml",
"Gemfile",
"Gemfile.lock",
"CONTRIBUTING.md",
"vendor/",
"script/",
"test/"
],
"branch-alias": {
"dev-master": "2.0.3-dev"
}
]
},
"license": [
"MIT"
],
"time": "2015-03-08 21:03:11"
]
},
{
"name": "cebe/markdown",
@ -255,21 +252,24 @@
},
{
"name": "yiisoft/yii2-composer",
"version": "2.0.4",
"version": "2.0.5",
"source": {
"type": "git",
"url": "https://github.com/yiisoft/yii2-composer.git",
"reference": "7452fd908a5023b8bb5ea1b123a174ca080de464"
"reference": "3f4923c2bde6caf3f5b88cc22fdd5770f52f8df2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/yiisoft/yii2-composer/zipball/7452fd908a5023b8bb5ea1b123a174ca080de464",
"reference": "7452fd908a5023b8bb5ea1b123a174ca080de464",
"url": "https://api.github.com/repos/yiisoft/yii2-composer/zipball/3f4923c2bde6caf3f5b88cc22fdd5770f52f8df2",
"reference": "3f4923c2bde6caf3f5b88cc22fdd5770f52f8df2",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0"
},
"require-dev": {
"composer/composer": "^1.0"
},
"type": "composer-plugin",
"extra": {
"class": "yii\\composer\\Plugin",
@ -298,7 +298,7 @@
"extension installer",
"yii2"
],
"time": "2016-02-06 00:49:24"
"time": "2016-12-20 13:26:02"
}
],
"packages-dev": [
@ -440,16 +440,16 @@
},
{
"name": "phpspec/prophecy",
"version": "v1.6.1",
"version": "v1.6.2",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
"reference": "58a8137754bc24b25740d4281399a4a3596058e0"
"reference": "6c52c2722f8460122f96f86346600e1077ce22cb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/58a8137754bc24b25740d4281399a4a3596058e0",
"reference": "58a8137754bc24b25740d4281399a4a3596058e0",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/6c52c2722f8460122f96f86346600e1077ce22cb",
"reference": "6c52c2722f8460122f96f86346600e1077ce22cb",
"shasum": ""
},
"require": {
@ -457,10 +457,11 @@
"php": "^5.3|^7.0",
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2",
"sebastian/comparator": "^1.1",
"sebastian/recursion-context": "^1.0"
"sebastian/recursion-context": "^1.0|^2.0"
},
"require-dev": {
"phpspec/phpspec": "^2.0"
"phpspec/phpspec": "^2.0",
"phpunit/phpunit": "^4.8 || ^5.6.5"
},
"type": "library",
"extra": {
@ -498,7 +499,7 @@
"spy",
"stub"
],
"time": "2016-06-07 08:13:47"
"time": "2016-11-21 14:58:47"
},
{
"name": "phpunit/php-code-coverage",
@ -564,16 +565,16 @@
},
{
"name": "phpunit/php-file-iterator",
"version": "1.4.1",
"version": "1.4.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
"reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0"
"reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0",
"reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5",
"reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5",
"shasum": ""
},
"require": {
@ -607,7 +608,7 @@
"filesystem",
"iterator"
],
"time": "2015-06-21 13:08:43"
"time": "2016-10-03 07:40:28"
},
{
"name": "phpunit/php-text-template",
@ -696,16 +697,16 @@
},
{
"name": "phpunit/php-token-stream",
"version": "1.4.8",
"version": "1.4.9",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-token-stream.git",
"reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da"
"reference": "3b402f65a4cc90abf6e1104e388b896ce209631b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da",
"reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da",
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3b402f65a4cc90abf6e1104e388b896ce209631b",
"reference": "3b402f65a4cc90abf6e1104e388b896ce209631b",
"shasum": ""
},
"require": {
@ -741,20 +742,20 @@
"keywords": [
"tokenizer"
],
"time": "2015-09-15 10:49:45"
"time": "2016-11-15 14:06:22"
},
{
"name": "phpunit/phpunit",
"version": "4.8.27",
"version": "4.8.31",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "c062dddcb68e44b563f66ee319ddae2b5a322a90"
"reference": "98b2b39a520766bec663ff5b7ff1b729db9dbfe3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c062dddcb68e44b563f66ee319ddae2b5a322a90",
"reference": "c062dddcb68e44b563f66ee319ddae2b5a322a90",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/98b2b39a520766bec663ff5b7ff1b729db9dbfe3",
"reference": "98b2b39a520766bec663ff5b7ff1b729db9dbfe3",
"shasum": ""
},
"require": {
@ -770,7 +771,7 @@
"phpunit/php-text-template": "~1.2",
"phpunit/php-timer": "^1.0.6",
"phpunit/phpunit-mock-objects": "~2.3",
"sebastian/comparator": "~1.1",
"sebastian/comparator": "~1.2.2",
"sebastian/diff": "~1.2",
"sebastian/environment": "~1.3",
"sebastian/exporter": "~1.2",
@ -813,7 +814,7 @@
"testing",
"xunit"
],
"time": "2016-07-21 06:48:14"
"time": "2016-12-09 02:45:31"
},
{
"name": "phpunit/phpunit-mock-objects",
@ -873,22 +874,22 @@
},
{
"name": "sebastian/comparator",
"version": "1.2.0",
"version": "1.2.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
"reference": "937efb279bd37a375bcadf584dec0726f84dbf22"
"reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22",
"reference": "937efb279bd37a375bcadf584dec0726f84dbf22",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6a1ed12e8b2409076ab22e3897126211ff8b1f7f",
"reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"sebastian/diff": "~1.2",
"sebastian/exporter": "~1.2"
"sebastian/exporter": "~1.2 || ~2.0"
},
"require-dev": {
"phpunit/phpunit": "~4.4"
@ -933,7 +934,7 @@
"compare",
"equality"
],
"time": "2015-07-26 15:48:44"
"time": "2016-11-19 09:18:40"
},
{
"name": "sebastian/diff",
@ -1245,16 +1246,16 @@
},
{
"name": "symfony/yaml",
"version": "v2.8.13",
"version": "v2.8.16",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "396784cd06b91f3db576f248f2402d547a077787"
"reference": "dbe61fed9cd4a44c5b1d14e5e7b1a8640cfb2bf2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/396784cd06b91f3db576f248f2402d547a077787",
"reference": "396784cd06b91f3db576f248f2402d547a077787",
"url": "https://api.github.com/repos/symfony/yaml/zipball/dbe61fed9cd4a44c5b1d14e5e7b1a8640cfb2bf2",
"reference": "dbe61fed9cd4a44c5b1d14e5e7b1a8640cfb2bf2",
"shasum": ""
},
"require": {
@ -1290,7 +1291,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"time": "2016-10-21 20:59:10"
"time": "2017-01-03 13:49:52"
}
],
"aliases": [],

2
docs/guide-es/db-dao.md

@ -14,7 +14,7 @@ Yii DAO soporta las siguientes bases de datos:
- [MySQL](http://www.mysql.com/)
- [MariaDB](https://mariadb.com/)
- [SQLite](http://sqlite.org/)
- [PostgreSQL](http://www.postgresql.org/)
- [PostgreSQL](http://www.postgresql.org/): versión 8.4 o superior.
- [CUBRID](http://www.cubrid.org/): versión 9.3 o superior.
- [Oracle](http://www.oracle.com/us/products/database/overview/index.html)
- [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): versión 2008 o superior.

6
docs/guide-fr/blocktypes.json

@ -0,0 +1,6 @@
{
"Warning:": "Attention :",
"Note:": "Note :",
"Info:": "Info :",
"Tip:": "Conseil :"
}

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

@ -0,0 +1,345 @@
Mise en cache de données
========================
La mise en cache de données consiste à stocker quelques variables PHP dans un cache et à les y retrouver plus tard.
C'est également la base pour des fonctionnalités de mise en cache plus avancées, comme la [mise en cache de requêtes](#query-caching) et la [mise en cache de pages](caching-page.md).
Le code qui suit est un modèle d'utilisation typique de la mise en cache de données, dans lequel `cache` fait référence à un [composant de mise en cache](#cache-components) :
```php
// tente de retrouver la donnée $data dans le cache
$data = $cache->get($key);
if ($data === false) {
// la donnée $data n'a pas été trouvée dans le cache, on la recalcule
$data = $this->calculateSomething();
// stocke la donnée $data dans le cache de façon à la retrouver la prochaine fois
$cache->set($key, $data);
}
// la donnée $data est disponible ici
```
Depuis la version 2.0.11, le [composant de mise en cache](#cache-components) fournit la méthode [[yii\caching\Cache::getOrSet()|getOrSet()]] qui simplifie le code pour l'obtention, le calcul et le stockage des données. Le code qui suit fait exactement la même chose que l'exemple précédent :
```php
$data = $cache->getOrSet($key, function () {
return $this->calculateSomething();
});
```
Lorsque le cache possède une donnée associée à la clé `$key`, la valeur en cache est retournée. Autrement, la fonction anonyme passée est exécutée pour calculer cette valeur qui est mise en cache et retournée.
Si la fonction anonyme a besoin de quelques données en dehors de la portée courante, vous pouvez les passer en utilisant l'instruction `use`. Par exemple :
```php
$user_id = 42;
$data = $cache->getOrSet($key, function () use ($user_id) {
return $this->calculateSomething($user_id);
});
```
> Note : la méthode [[yii\caching\Cache::getOrSet()|getOrSet()]] prend également en charge la durée et les dépendances.
Reportez-vous à [Expiration de la mise en cache](#cache-expiration) et à [Dépendances de mise en cache](#cache-dependencies) pour en savoir plus.
## Composants de mise en cache <span id="cache-components"></span>
La mise en cache s'appuie sur ce qu'on appelle les *composants de mise en cache* qui représentent des supports de mise en cache tels que les mémoires, les fichiers et les bases de données.
Les composants de mise en cache sont généralement enregistrés en tant que [composants d'application](structure-application-components.md) de façon à ce qu'ils puissent être configurables et accessibles globalement. Le code qui suit montre comment configurer le composant d'application `cache` pour qu'il utilise [memcached](http://memcached.org/) avec deux serveurs de cache :
```php
'components' => [
'cache' => [
'class' => 'yii\caching\MemCache',
'servers' => [
[
'host' => 'server1',
'port' => 11211,
'weight' => 100,
],
[
'host' => 'server2',
'port' => 11211,
'weight' => 50,
],
],
],
],
```
Vous pouvez accéder au composant de mise en cache configuré ci-dessus en utilisant l'expression `Yii::$app->cache`.
Comme tous les composants de mise en cache prennent en charge le même jeux d'API, vous pouvez remplacer le composant de mise en cache sous-jacent par un autre en le reconfigurant dans la configuration de l'application, cela sans modifier le code qui utilise le cache. Par exemple, vous pouvez modifier le code ci-dessus pour utiliser [[yii\caching\ApcCache|APC cache]] :
```php
'components' => [
'cache' => [
'class' => 'yii\caching\ApcCache',
],
],
```
> Tip: vous pouvez enregistrer de multiples composants d'application de mise en cache. Le composant nommé `cache` est utilisé par défaut par de nombreuses classes dépendantes d'un cache (p. ex.[[yii\web\UrlManager]]).
### Supports de stockage pour cache pris en charge <span id="supported-cache-storage"></span>
Yii prend en charge un large panel de supports de stockage pour cache. Ce qui suit est un résumé :
* [[yii\caching\ApcCache]]: utilise l'extension PHP [APC](http://php.net/manual/en/book.apc.php). Cette option peut être considérée comme la plus rapide lorsqu'on utilise un cache pour une grosse application centralisée (p. ex. un serveur, pas d'équilibrage de charge dédié, etc.).
* [[yii\caching\DbCache]]: utilise une table de base de données pour stocker les données en cache. Pour utiliser ce cache, vous devez créer une table comme spécifié dans [[yii\caching\DbCache::cacheTable]].
* [[yii\caching\DummyCache]]: tient lieu de cache à remplacer qui n'assure pas de mise en cache réelle. Le but de ce composant est de simplifier le code qui a besoin de vérifier la disponibilité du cache. Par exemple, lors du développement ou si le serveur ne dispose pas de la prise en charge d'un cache, vous pouvez configurer un composant de mise en cache pour qu'il utilise ce cache. Lorsque la prise en charge réelle de la mise en cache est activée, vous pouvez basculer sur le composant de mise en cache correspondant. Dans les deux cas, vous pouvez utiliser le même code `Yii::$app->cache->get($key)` pour essayer de retrouver les données du cache sans vous préoccuper du fait que `Yii::$app->cache` puisse être `null`.
* [[yii\caching\FileCache]]: utilise des fichiers standards pour stocker les données en cache. Cela est particulièrement adapté à la mise en cache de gros blocs de données, comme le contenu d'une page.
* [[yii\caching\MemCache]]: utilise le [memcache](http://php.net/manual/en/book.memcache.php) PHP et l'extension [memcached](http://php.net/manual/en/book.memcached.php). Cette option peut être considérée comme la plus rapide lorsqu'on utilise un cache dans des applications distribuées (p. ex. avec plusieurs serveurs, l'équilibrage de charge, etc.).
* [[yii\redis\Cache]]: met en œuvre un composant de mise en cache basé sur un stockage clé-valeur [Redis](http://redis.io/)
(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
[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.
> Tip: vous pouvez utiliser différents supports de stockage pour cache dans la même application. Une stratégie courante est d'utiliser un support de stockage pour cache basé sur la mémoire pour stocker des données de petite taille mais d'usage constant (p. ex. des données statistiques), et d'utiliser des supports de stockage pour cache basés sur des fichiers ou des bases de données pour stocker des données volumineuses et utilisées moins souvent (p. ex. des contenus de pages).
## Les API Cache <span id="cache-apis"></span>
Tous les composants de mise en cache dérivent de la même classe de base [[yii\caching\Cache]] et par conséquent prennent en charge les API suivantes :
* [[yii\caching\Cache::get()|get()]]: retrouve une donnée dans le cache identifiée par une clé spécifiée. Une valeur `false` (faux) est retournée si la donnée n'est pas trouvée dans le cache ou si elle a expiré ou été invalidée.
* [[yii\caching\Cache::set()|set()]]: stocke une donnée sous une clé dans le cache.
* [[yii\caching\Cache::add()|add()]]: stocke une donnée identifiée par une clé dans le cache si la clé n'existe pas déjà dans le cache.
* [[yii\caching\Cache::getOrSet()|getOrSet()]]: retrouve une donnée dans le cache identifiée par une clé spécifiée ou exécute la fonction de rappel passée, stocke la valeur retournée par cette fonction dans le cache sous cette clé et retourne la donnée.
* [[yii\caching\Cache::multiGet()|multiGet()]]: retrouve de multiples données dans le cache identifiées par les clés spécifiées.
* [[yii\caching\Cache::multiSet()|multiSet()]]: stocke de multiples données dans le cache. Chaque donnée est identifiée par une clé.
* [[yii\caching\Cache::multiAdd()|multiAdd()]]: stocke de multiples données dans le cache. Chaque donnée est identifiée par une clé. Si la clé existe déjà dans le cache, la donnée est ignorée.
* [[yii\caching\Cache::exists()|exists()]]: retourne une valeur indiquant si la clé spécifiée existe dans le cache.
* [[yii\caching\Cache::delete()|delete()]]: retire du cache une donnée identifiée par une clé.
* [[yii\caching\Cache::flush()|flush()]]: retire toutes les données du cache.
> Note : ne mettez pas directement en cache une valeur booléenne `false` parce que la méthode [[yii\caching\Cache::get()|get()]] utilise la valeur `false` pour indiquer que la donnée n'a pas été trouvée dans le cache. Au lieu de cela, vous pouvez placer cette donnée dans un tableau et mettre ce tableau en cache pour éviter le problème.
Quelques supports de cache, tels que MemCache, APC, prennent en charge la récupération de multiples valeurs en cache en mode « batch » (lot), ce qui réduit la surcharge occasionnée par la récupération des données en cache. Les API [[yii\caching\Cache::multiGet()|multiGet()]] et [[yii\caching\Cache::multiAdd()|multiAdd()]] sont fournies pour exploiter cette fonctionnalité. Dans le cas où le support de cache sous-jacent ne prend pas en charge cette fonctionnalité, elle est simulée.
Comme [[yii\caching\Cache]] implémente `ArrayAccess`, un composant de mise en cache peut être utilisé comme un tableau. En voici quelques exemples :
```php
$cache['var1'] = $value1; // équivalent à : $cache->set('var1', $value1);
$value2 = $cache['var2']; // équivalent à : $value2 = $cache->get('var2');
```
### Clés de cache <span id="cache-keys"></span>
Chacune des données stockée dans le cache est identifiée de manière unique par une clé. Lorsque vous stockez une donnée dans le cache, vous devez spécifier une clé qui lui est attribuée. Plus tard, pour récupérer la donnée, vous devez fournir cette clé.
Vous pouvez utiliser une chaîne de caractères ou une valeur arbitraire en tant que clé de cache. Lorsqu'il ne s'agit pas d'une chaîne de caractères, elle est automatiquement sérialisée sous forme de chaîne de caractères.
Une stratégie courante pour définir une clé de cache consiste à inclure tous les facteurs déterminants sous forme de tableau. Par exemple,[[yii\db\Schema]] utilise la clé suivante par mettre en cache les informations de schéma d'une table de base de données :
```php
[
__CLASS__, // schema class name
$this->db->dsn, // DB connection data source name
$this->db->username, // DB connection login user
$name, // table name
];
```
Comme vous le constatez, la clé inclut toutes les informations nécessaires pour spécifier de manière unique une table de base de données.
> Note : les valeurs stockées dans le cache via [[yii\caching\Cache::multiSet()|multiSet()]] ou [[yii\caching\Cache::multiAdd()|multiAdd()]] peuvent n'avoir que des clés sous forme de chaînes de caractères ou de nombres entiers. Si vous avez besoin de définir des clés plus complexes, stockez la valeur séparément via [[yii\caching\Cache::set()|set()]] ou [[yii\caching\Cache::add()|add()]].
Lorsque le même support de cache est utilisé par différentes applications, vous devriez spécifier un préfixe de clé de cache pour chacune des applications afin d'éviter les conflits de clés de cache. Cela peut être fait en configurant la propriété [[yii\caching\Cache::keyPrefix]]. Par exemple, dans la configuration de l'application vous pouvez entrer le code suivant :
```php
'components' => [
'cache' => [
'class' => 'yii\caching\ApcCache',
'keyPrefix' => 'myapp', // a unique cache key prefix
],
],
```
Pour garantir l'interopérabilité, vous ne devez utiliser que des caractères alpha-numériques.
### Expiration de la mise en cache <span id="cache-expiration"></span>
Une donnée stockée dans le cache y restera à jamais sauf si elle en est retirée par l'application d'une quelconque politique de mise en cache (p. ex. l'espace de mise en cache est plein et les données les plus anciennes sont retirées). Pour modifier ce comportement, vous pouvez fournir un paramètre d'expiration lors de l'appel de la fonction [[yii\caching\Cache::set()|set()]] pour stocker une donnée. Le paramètre indique pour combien de secondes la donnée restera valide dans le cache. Lorsque vous appelez la fonction [[yii\caching\Cache::get()|get()]] pour récupérer une donnée, si cette dernière a expiré, la méthode retourne `false`, pour indiquer que la donnée n'a pas été trouvée dans le cache. Par exemple,
```php
// conserve la donnée dans le cache pour un maximum de 45 secondes
$cache->set($key, $data, 45);
sleep(50);
$data = $cache->get($key);
if ($data === false) {
// $data a expiré ou n'a pas été trouvée dans le cache
}
```
Depuis la version 2.0.11, vous pouvez définir une valeur [[yii\caching\Cache::$defaultDuration|defaultDuration]] dans la configuration de votre composant de mise en cache si vous préférez utiliser une durée de mise en cache personnalisée au lieu de la durée illimitée par défaut. Cela vous évitera d'avoir à passer la durée personnalisée à la fonction [[yii\caching\Cache::set()|set()]] à chaque fois.
### Dépendances de mise en cache <span id="cache-dependencies"></span>
En plus de la définition du temps d'expiration, les données mises en cache peuvent également être invalidées par modification de ce qu'on appelle les *dépendances de mise en cache*.
Par exemple, [[yii\caching\FileDependency]] représente la dépendance à la date de modification d'un fichier.
Lorsque cette dépendance est modifiée, cela signifie que le fichier correspondant est modifié. En conséquence, tout contenu de fichier périmé trouvé dans le cache devrait être invalidé et l'appel de la fonction [[yii\caching\Cache::get()|get()]] devrait retourner `false`.
Les dépendances de mise en cache sont représentées sous forme d'objets dérivés de [[yii\caching\Dependency]]. Lorsque vous appelez la fonction [[yii\caching\Cache::set()|set()]] pour stocker une donnée dans le cache, vous pouvez lui passer un objet de dépendance (« Dependency ») associé. Par exemple,
```php
// Crée une dépendance à la date de modification du fichier example.txt
$dependency = new \yii\caching\FileDependency(['fileName' => 'example.txt']);
// La donnée expirera dans 30 secondes.
// Elle sera également invalidée plus tôt si le fichier example.txt est modifié.
$cache->set($key, $data, 30, $dependency);
// Le cache vérifiera si la donnée a expiré.
// Il vérifiera également si la dépendance associée a été modifiée.
// Il retournera `false` si l'une de ces conditions est vérifiée.
$data = $cache->get($key);
```
Ci-dessous nous présentons un résumé des dépendances de mise en cache disponibles :
- [[yii\caching\ChainedDependency]]: la dépendance est modifiée si l'une des dépendances de la chaîne est modifiée.
- [[yii\caching\DbDependency]]: la dépendance est modifiée si le résultat de le requête de l'instruction SQL spécifiée est modifié.
- [[yii\caching\ExpressionDependency]]: la dépendance est modifiée si le résultat de l'expression PHP spécifiée est modifié.
- [[yii\caching\FileDependency]]: la dépendance est modifiée si la date de dernière modification du fichier est modifiée.
- [[yii\caching\TagDependency]]: associe une donnée mise en cache à une ou plusieurs balises. Vous pouvez invalider la donnée mise en cache associée à la balise spécifiée en appelant [[yii\caching\TagDependency::invalidate()]].
> Note : évitez d'utiliser la méthode [[yii\caching\Cache::exists()|exists()]] avec des dépendances. Cela ne vérifie pas si la dépendance associée à la donnée mise en cache, s'il en existe une, a changé. Ainsi, un appel de la fonction [[yii\caching\Cache::get()|get()]] peut retourner `false` alors que l'appel de la fonction [[yii\caching\Cache::exists()|exists()]] retourne `true`.
## Mise en cache de requêtes <span id="query-caching"></span>
La mise en cache de requêtes est une fonctionnalité spéciale de la mise en cache construite sur la base de la mise en cache de données. Elle est fournie pour permettre la mise en cache du résultat de requêtes de base de données.
La mise en cache de requêtes nécessite une [[yii\db\Connection|connexion à une base de données]] et un [composant d'application](#cache-components)`cache` valide.
L'utilisation de base de la mise en cache de requêtes est la suivante, en supposant que `$db` est une instance de [[yii\db\Connection]] :
```php
$result = $db->cache(function ($db) {
// le résultat d'une requête SQL sera servi à partir du cache
// si la mise en cache de requêtes est activée et si le résultat de la requête est trouvé dans le cache
return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne();
});
```
La mise en cache de requêtes peut être utilisée pour des [DAO](db-dao.md) ainsi que pour des [enregistrements actifs](db-active-record.md):
```php
$result = Customer::getDb()->cache(function ($db) {
return Customer::find()->where(['id' => 1])->one();
});
```
> Info : quelques systèmes de gestion de bases de données (DBMS) (p. ex. [MySQL](http://dev.mysql.com/doc/refman/5.1/en/query-cache.html))
prennent également en charge la mise en cache de requêtes du côté serveur de base de données. Vous pouvez choisir d'utiliser l'un ou l'autre des ces mécanismes de mise en cache de requêtes. Les mises en cache de requêtes décrites ci-dessus offrent l'avantage de pouvoir spécifier des dépendances de mise en cache flexibles et sont potentiellement plus efficaces.
### Vidage du cache <span id="cache-flushing">
Lorsque vous avez besoin d'invalider toutes les données stockées dans le cache, vous pouvez appeler [[yii\caching\Cache::flush()]].
Vous pouvez aussi vider le cache depuis la console en appelant `yii cache/flush`.
- `yii cache`: liste les caches disponibles dans une application
- `yii cache/flush cache1 cache2`: vide les composants de mise en cache `cache1`, `cache2` (vous pouvez passer de multiples composants en les séparant par une virgule)
- `yii cache/flush-all`: vide tous les composants de mise en cache de l'application
> Info : les applications en console utilisent un fichier de configuration séparé par défaut. Assurez-vous que vous utilisez le même composant de mise en cache dans les configurations de vos application web et console pour obtenir l'effet correct.
### Configurations <span id="query-caching-configs"></span>
La mise en cache de requêtes dispose de trois options globales configurables via [[yii\db\Connection]] :
* [[yii\db\Connection::enableQueryCache|enableQueryCache]] : pour activer ou désactiver la mise en cache de requêtes.
Valeur par défaut : `true`. Notez que pour activer effectivement la mise en cache de requêtes, vous devez également disposer d'un cache valide, tel que spécifié par [[yii\db\Connection::queryCache|queryCache]].
* [[yii\db\Connection::queryCacheDuration|queryCacheDuration]] : ceci représente le nombre de secondes durant lesquelles le résultat d'une requête reste valide dans le cache. Vous pouvez utiliser 0 pour indiquer que le résultat de la requête doit rester valide indéfiniment dans le cache. Cette propriété est la valeur par défaut utilisée lors de l'appel [[yii\db\Connection::cache()]] sans spécifier de durée.
* [[yii\db\Connection::queryCache|queryCache]] : ceci représente l'identifiant du composant d'application de mise en cache.
Sa valeur par défaut est : `'cache'`. La mise en cache de requêtes est activée seulement s'il existe un composant d'application de mise en cache valide.
### Utilisations <span id="query-caching-usages"></span>
Vous pouvez utiliser [[yii\db\Connection::cache()]] si vous avez de multiples requêtes SQL qui doivent bénéficier de la mise en cache de requêtes. On l'utilise comme suit :
```php
$duration = 60; // mettre le résultat de la requête en cache durant 60 secondes.
$dependency = ...; // dépendance optionnelle
$result = $db->cache(function ($db) {
// ... effectuer les requêtes SQL ici ...
return $result;
}, $duration, $dependency);
```
Toutes les requêtes SQL dans la fonction anonyme sont mises en cache pour la durée spécifiée avec la dépendance spécifiée. Si le résultat d'une requête est trouvé valide dans le cache, la requête est ignorée et, à la place, le résultat est servi à partir du cache. Si vous ne spécifiez pas le paramètre `$duration`, la valeur de [[yii\db\Connection::queryCacheDuration|queryCacheDuration]] est utilisée en remplacement.
Parfois, dans `cache()`, il se peut que vous vouliez désactiver la mise en cache de requêtes pour des requêtes particulières. Dans un tel cas, vous pouvez utiliser [[yii\db\Connection::noCache()]].
```php
$result = $db->cache(function ($db) {
// requêtes SQL qui utilisent la mise en cache de requêtes
$db->noCache(function ($db) {
// requêtes qui n'utilisent pas la mise en cache de requêtes
});
// ...
return $result;
});
```
Si vous voulez seulement utiliser la mise en cache de requêtes pour une requête unique, vous pouvez appeler la fonction [[yii\db\Command::cache()]] lors de la construction de la commande. Par exemple :
```php
// utilise la mise en cache de requêtes et définit la durée de mise en cache de la requête à 60 secondes
$customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->cache(60)->queryOne();
```
Vous pouvez aussi utiliser la fonction [[yii\db\Command::noCache()]] pour désactiver la mise en cache de requêtes pour une commande unique. Par exemple :
```php
$result = $db->cache(function ($db) {
// requêtes SQL qui utilisent la mise en cache de requêtes
// ne pas utiliser la mise en cache de requêtes pour cette commande
$customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->noCache()->queryOne();
// ...
return $result;
});
```
### Limitations <span id="query-caching-limitations"></span>
La mise en cache de requêtes ne fonctionne pas avec des résultats de requêtes qui contiennent des gestionnaires de ressources. Par exemple, lorsque vous utilisez de type de colonne `BLOB` dans certains systèmes de gestion de bases de données (DBMS), la requête retourne un gestionnaire de ressources pour la donnée de la colonne.
Quelques supports de stockage pour cache sont limités en taille. Par exemple, avec memcache, chaque entrée est limitée en taille à 1MO. En conséquence, si le résultat d'une requête dépasse cette taille, la mise en cache échoue.

143
docs/guide-fr/caching-fragment.md

@ -0,0 +1,143 @@
Mise en cache de fragments
==========================
La mise en cache de fragments fait référence à la mise en cache de fragments de pages Web. Par exemple, si une page affiche un résumé des ventes annuelles dans un tableau, vous pouvez stocker ce tableau en cache pour éliminer le temps nécessaire à sa génération à chacune des requêtes. La mise en cache de fragments est construite au-dessus de la [mise en cache de données](caching-data.md).
Pour utiliser la mise en cache de fragments, utilisez la construction qui suit dans une [vue](structure-views.md):
```php
if ($this->beginCache($id)) {
// ... générez le contenu ici ...
$this->endCache();
}
```
C'est à dire, insérez la logique de génération du contenu entre les appels [[yii\base\View::beginCache()|beginCache()]] et
[[yii\base\View::endCache()|endCache()]]. Si le contenu est trouvé dans le cache, [[yii\base\View::beginCache()|beginCache()]]
rendra le contenu en cache et retournera `false` (faux), ignorant la logique de génération de contenu.
Autrement, votre logique de génération de contenu sera appelée, et quand [[yii\base\View::endCache()|endCache()]] sera appelée, le contenu généré sera capturé et stocké dans le cache.
Comme pour la [mise en cache de données](caching-data.md), un `$id` (identifiant) unique est nécessaire pour identifier un cache de contenu.
## Options de mise en cache <span id="caching-options"></span>
Vous pouvez spécifier des options additionnelles sur la mise en cache de fragments en passant le tableau d'options comme second paramètre à la méthode [[yii\base\View::beginCache()|beginCache()]]. En arrière plan, ce tableau d'options est utilisé pour configurer un composant graphique [[yii\widgets\FragmentCache]] qui met en œuvre la fonctionnalité réelle de mise en cache de fragments.
### Durée <span id="duration"></span>
L'option [[yii\widgets\FragmentCache::duration|duration]] (durée) est peut-être l'option de la mise en cache de fragments la plus couramment utilisée. Elle spécifie pour combien de secondes le contenu peut demeurer valide dans le cache. Le code qui suit met le fragment de contenu en cache pour au maximum une heure :
```php
if ($this->beginCache($id, ['duration' => 3600])) {
// ... générez le contenu ici...
$this->endCache();
}
```
Si cette option n'est pas définie, la valeur utilisée par défaut est 60, ce qui veut dire que le contenu mise en cache expirera au bout de 60 secondes.
### Dépendances <span id="dependencies"></span>
Comme pour la [mise en cache de données](caching-data.md#cache-dependencies), le fragment de contenu mis en cache peut aussi avoir des dépendances. Par exemple, le contenu d'un article affiché dépend du fait que l'article a été modifié ou pas.
Pour spécifier une dépendance, définissez l'option [[yii\widgets\FragmentCache::dependency|dependency]], soit sous forme d'objet [[yii\caching\Dependency]], soit sous forme d'un tableau de configuration pour créer un objet [[yii\caching\Dependency]]. Le code qui suit spécifie que le fragment de contenu dépend du changement de la valeur de la colonne `updated_at` (mis à jour le) :
```php
$dependency = [
'class' => 'yii\caching\DbDependency',
'sql' => 'SELECT MAX(updated_at) FROM post',
];
if ($this->beginCache($id, ['dependency' => $dependency])) {
// ... générez le contenu ici ...
$this->endCache();
}
```
### Variations <span id="variations"></span>
Le contenu mise en cache peut connaître quelques variations selon certains paramètres. Par exemple, pour une application Web prenant en charge plusieurs langues, le même morceau de code d'une vue peut générer le contenu dans différentes langues. Par conséquent, vous pouvez souhaitez que le contenu mis en cache varie selon la langue courante de l'application.
Pour spécifier des variations de mise en cache, définissez l'option [[yii\widgets\FragmentCache::variations|variations]], qui doit être un tableau de valeurs scalaires, représentant chacune un facteur de variation particulier. Par exemple, pour que le contenu mis en cache varie selon la langue, vous pouvez utiliser le code suivant :
```php
if ($this->beginCache($id, ['variations' => [Yii::$app->language]])) {
// ... générez le contenu ici ...
$this->endCache();
}
```
### Activation désactivation de la mise en cache <span id="toggling-caching"></span>
Parfois, vous désirez activer la mise en cache de fragments seulement lorsque certaines conditions sont rencontrées. Par exemple, pour une page qui affiche un formulaire, vous désirez seulement mettre le formulaire en cache lorsqu'il est initialement demandé (via une requête GET). Tout affichage subséquent du formulaire (via des requêtes POST) ne devrait pas être mise en cache car il contient des données entrées par l'utilisateur. Pour mettre en œuvre ce mécanisme, vous pouvez définir l'option [[yii\widgets\FragmentCache::enabled|enabled]], comme suit :
```php
if ($this->beginCache($id, ['enabled' => Yii::$app->request->isGet])) {
// ... générez le contenu ici ...
$this->endCache();
}
```
## Mises en cache imbriquées <span id="nested-caching"></span>
La mise en cache de fragments peut être imbriquée. C'est à dire qu'un fragment mis en cache peut être contenu dans un autre fragment lui aussi mis en cache.
Par exemple, les commentaires sont mis en cache dans un cache de fragment interne, et sont mis en cache en même temps et avec le contenu de l'article dans un cache de fragment externe. Le code qui suit montre comment deux caches de fragment peuvent être imbriqués :
```php
if ($this->beginCache($id1)) {
// ...logique de génération du contenu ...
if ($this->beginCache($id2, $options2)) {
// ...logique de génération du contenu...
$this->endCache();
}
// ... logique de génération de contenu ...
$this->endCache();
}
```
Différentes options de mise en cache peuvent être définies pour les caches imbriqués. Par exemple, les caches internes et les caches externes peuvent utiliser des valeurs de durée différentes. Même lorsque les données mises en cache dans le cache externe sont invalidées, le cache interne peut continuer à fournir un fragment interne valide. Néanmoins, le réciproque n'est pas vraie; si le cache externe est évalué comme valide, il continue à fournir la même copie mise en cache après que le contenu du cache interne a été invalidé. Par conséquent, vous devez être prudent lors de la définition des durées ou des dépendances des caches imbriqués, autrement des fragments internes périmés peuvent subsister dans le fragment externe.
## Contenu dynamique <span id="dynamic-content"></span>
Lors de l'utilisation de la mise en cache de fragments, vous pouvez rencontrer une situation dans laquelle un gros fragment de contenu est relativement statique en dehors de quelques endroits particuliers. Par exemple, l'entête d'une page peut afficher le menu principal avec le nom de l'utilisateur courant. Un autre problème se rencontre lorsque le contenu mis en cache, contient du code PHP qui doit être exécuté à chacune des requêtes (p. ex. le code pour enregistrer un paquet de ressources). Ces deux problèmes peuvent être résolus par une fonctionnalité qu'on appelle *contenu dynamique*.
Un contenu dynamique signifie un fragment de sortie qui ne doit jamais être mis en cache même s'il est contenu dans un fragment mis en cache. Pour faire en sorte que le contenu soit dynamique en permanence, il doit être généré en exécutant un code PHP à chaque requête, même si le contenu l'englobant est servi à partir du cache.
Vous pouvez appeler la fonction [[yii\base\View::renderDynamic()]] dans un fragment mis en cache pour y insérer un contenu dynamique à l'endroit désiré, comme ceci :
```php
if ($this->beginCache($id1)) {
// ... logique de génération de contenu ...
echo $this->renderDynamic('return Yii::$app->user->identity->name;');
// ... logique de génération de contenu ...
$this->endCache();
}
```
La méthode [[yii\base\View::renderDynamic()|renderDynamic()]] accepte un morceau de code PHP en paramètre. La valeur retournée est traitée comme un contenu dynamique. Le même code PHP est exécuté à chacune des requêtes, peu importe que le fragment englobant soit servi à partir du cache ou pas.

108
docs/guide-fr/caching-http.md

@ -0,0 +1,108 @@
Mise en cache HTTP
============
En plus de la mise en cache côté serveur que nous avons décrite dans les sections précédentes, les applications Web peuvent aussi exploiter la mise en cache côté client pour économiser le temps de génération et de transfert d'un contenu de page inchangé.
Pour utiliser la mise en cache côté client, vous pouvez configurer [[yii\filters\HttpCache]] comme un filtre pour des actions de contrôleur dont le résultat rendu peut être mis en cache du côté du client. [[yii\filters\HttpCache|HttpCache]]
ne fonctionne que pour les requêtes `GET` et `HEAD`. Il peut gérer trois sortes d'entêtes HTTP relatifs à la mise en cache pour ces requêtes :
* [[yii\filters\HttpCache::lastModified|Last-Modified]]
* [[yii\filters\HttpCache::etagSeed|Etag]]
* [[yii\filters\HttpCache::cacheControlHeader|Cache-Control]]
## Entête `Last-Modified` <span id="last-modified"></span>
L'entête `Last-Modified` (dernière modification) utilise un horodatage pour indiquer si la page a été modifiée depuis sa mise en cache par le client.
Vous pouvez configurer la propriété [[yii\filters\HttpCache::lastModified]] pour activer l'envoi de l'entête `Last-modified`. La propriété doit être une fonction de rappel PHP qui retourne un horodatage UNIX concernant la modification de la page. La signature de la fonction de rappel PHP doit être comme suit :
```php
/**
* @param Action $action l'objet action qui est actuellement géré
* @param array $params la valeur de la propriété "params"
* @return int un horodatage UNIX représentant l'instant de modification de la page
*/
function ($action, $params)
```
Ce qui suit est un exemple d'utilisation de l'entête `Last-Modified` :
```php
public function behaviors()
{
return [
[
'class' => 'yii\filters\HttpCache',
'only' => ['index'],
'lastModified' => function ($action, $params) {
$q = new \yii\db\Query();
return $q->from('post')->max('updated_at');
},
],
];
}
```
Le code précédent établit que la mise en cache HTTP doit être activée pour l'action `index` seulement. Il doit générer un entête HTTP `Last-Modified` basé sur l'instant de la dernière mise à jour d'articles (posts). Lorsque le navigateur visite la page `index` pour la première fois, la page est générée par le serveur et envoyée au navigateur. Si le navigateur visite à nouveau la même page, et qu'aucun article n'a été modifié, le serveur ne régénère par la page, et le navigateur utilise la version mise en cache du côté du client. En conséquence, le rendu côté serveur et la transmission de la page sont tous deux évités.
## Entête `ETag` <span id="etag"></span>
L'entête "Entity Tag" (or `ETag` en raccourci) utilise une valeur de hachage pour représenter le contenu d'une page. Si la page est modifiée, la valeur de hachage change également. En comparant la valeur de hachage conservée sur le client avec la valeur de hachage générée côté serveur, le cache peut déterminer si la page a été modifiée et doit être retransmise.
Vous pouvez configurer la propriété [[yii\filters\HttpCache::etagSeed]] pour activer l'envoi de l'entête `ETag`. La propriété doit être une fonction de rappel PHP qui retourne un nonce (sel) pour la génération de la valeur de hachage Etag. La signature de la fonction de rappel PHP doit être comme suit :
```php
/**
* @param Action $action l'objet action qui est actuellement géré
* @param array $params la valeur de la propriété "params"
* @return string une chaîne de caractères à utiliser comme nonce (sel) pour la génération d'une valeur de hachage ETag
*/
function ($action, $params)
```
Ce qui suit est un exemple d'utilisation de l'entête `ETag` :
```php
public function behaviors()
{
return [
[
'class' => 'yii\filters\HttpCache',
'only' => ['view'],
'etagSeed' => function ($action, $params) {
$post = $this->findModel(\Yii::$app->request->get('id'));
return serialize([$post->title, $post->content]);
},
],
];
}
```
Le code ci-dessus établit que la mise en cache HTTP doit être activée pour l'action `view` seulement. Il doit générer un entête HTTP `ETag` basé sur le titre et le contenu de l'article demandé. Lorsque le navigateur visite la page pour la première fois, la page est générée par le serveur et envoyée au navigateur. Si le navigateur visite à nouveau la même page et que ni le titre, ni le contenu de l'article n'ont changé, le serveur ne régénère pas la page et le navigateur utilise la version mise en cache côté client. En conséquence, le rendu par le serveur et la transmission de la page sont tous deux évités.
ETags vous autorise des stratégies de mises en cache plus complexes et/ou plus précises que l'entête `Last-Modified`. Par exemple, un ETag peut être invalidé si on a commuté le site sur un nouveau thème.
Des génération coûteuses d'ETag peuvent contrecarrer l'objectif poursuivi en utilisant `HttpCache` et introduire une surcharge inutile, car il faut les réévaluer à chacune des requêtes. Essayez de trouver une expression simple qui invalide le cache si le contenu de la page a été modifié.
> Note : en conformité avec la norme [RFC 7232](http://tools.ietf.org/html/rfc7232#section-2.4),
`HttpCache` envoie les entêtes `ETag` et `Last-Modified` à la fois si ils sont tous deux configurés. Et si le client envoie les entêtes `If-None-Match` et `If-Modified-Since` à la fois, seul le premier est respecté.
## Entête `Cache-Control` <span id="cache-control"></span>
L'entête `Cache-Control` spécifie la politique de mise en cache générale pour les pages. Vous pouvez l'envoyer en configurant la propriété [[yii\filters\HttpCache::cacheControlHeader]] avec la valeur de l'entête. Par défaut, l'entête suivant est envoyé :
```
Cache-Control: public, max-age=3600
```
## Propriété "Session Cache Limiter" <span id="session-cache-limiter"></span>
Lorsqu'une page utilise une session, PHP envoie automatiquement quelques entêtes HTTP relatifs à la mise en cache comme spécifié dans la propriété `session.cache_limiter` de PHP INI. Ces entêtes peuvent interférer ou désactiver la mise en cache que vous voulez obtenir de `HttpCache`. Pour éviter ce problème, par défaut, `HttpCache` désactive l'envoi de ces entêtes automatiquement. Si vous désirez modifier ce comportement, vous devez configurer la propriété [[yii\filters\HttpCache::sessionCacheLimiter]]. Cette propriété accepte une chaîne de caractères parmi `public`, `private`, `private_no_expire` et `nocache`. Reportez-vous au manuel de PHP à propos de [session_cache_limiter()](http://www.php.net/manual/en/function.session-cache-limiter.php) pour des explications sur ces valeurs.
## Implications SEO <span id="seo-implications"></span>
Les robots moteurs de recherche ont tendance à respecter les entêtes de mise en cache. Comme certains moteurs d'indexation du Web sont limités quant aux nombre de pages par domaine qu'ils sont à même de traiter dans un certain laps de temps, l'introduction d'entêtes de mise en cache peut aider à l'indexation de votre site car ils limitent le nombre de pages qui ont besoin d'être traitées.

33
docs/guide-fr/caching-page.md

@ -0,0 +1,33 @@
Mise en cache de pages
======================
La mise en cache de pages fait référence à la mise en cache du contenu d'une page entière du côté serveur. Plus tard, lorsque la même page est demandée à nouveau, son contenu est servi à partir du cache plutôt que d'être régénéré entièrement.
La mise en cache de pages est prise en charge par [[yii\filters\PageCache]], un [filtre d'action](structure-filters.md). On peut l'utiliser de la manière suivante dans une classe contrôleur :
```php
public function behaviors()
{
return [
[
'class' => 'yii\filters\PageCache',
'only' => ['index'],
'duration' => 60,
'variations' => [
\Yii::$app->language,
],
'dependency' => [
'class' => 'yii\caching\DbDependency',
'sql' => 'SELECT COUNT(*) FROM post',
],
],
];
}
```
Le code ci-dessus établit que la mise en cache de pages doit être utilisée uniquement pour l'action `index`. Le contenu de la page doit être mis en cache pour au plus 60 secondes et doit varier selon la langue courante de l'application. De plus, le contenu de la page mis en cache doit être invalidé si le nombre total d'articles (post) change.
Comme vous pouvez le constater, la mise en cache de pages est très similaire à la [mise en cache de fragments](caching-fragment.md). Les deux prennent en charge les options telles que `duration`, `dependencies`, `variations` et `enabled`. La différence principale est que la mise en cache de pages est mis en œuvre comme un [filtre d'action](structure-filters.md) alors que la mise en cache de framgents l'est comme un [composant graphique](structure-widgets.md).
Vous pouvez utiliser la [mise en cache de fragments](caching-fragment.md) ainsi que le [contenu dynamique](caching-fragment.md#dynamic-content) en simultanéité avec la mise en cache de pages.

2
docs/guide-fr/db-dao.md

@ -10,7 +10,7 @@ Les objets d'accès aux bases de données de Yii prennent en charge les bases de
- [MySQL](http://www.mysql.com/)
- [MariaDB](https://mariadb.com/)
- [SQLite](http://sqlite.org/)
- [PostgreSQL](http://www.postgresql.org/)
- [PostgreSQL](http://www.postgresql.org/): version 8.4 or higher.
- [CUBRID](http://www.cubrid.org/): version 9.3 or higher.
- [Oracle](http://www.oracle.com/us/products/database/overview/index.html)
- [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): version 2008 or higher.

404
docs/guide-fr/helper-array.md

@ -0,0 +1,404 @@
Classe assistante ArrayHelper
=============================
En plus du jeu riche de [fonctions de tableaux](http://php.net/manual/en/book.array.php) qu'offre PHP, la classe assistante traitant les tableaux dans Yii fournit des méthodes statiques supplémentaires qui vous permettent de traiter les tableaux avec plus d'efficacité.
## Obtention de valeurs <span id="getting-values"></span>
Récupérer des valeurs d'un tableau ou d'un objet ou une structure complexe écrits tous deux en PHP standard est un processus assez répétitif. Vous devez d'abord vérifier que la clé existe avec `isset`, puis si c'est le cas, vous récupérez la valeur associée, sinon il vous faut fournir une valeur par défaut :
```php
class User
{
public $name = 'Alex';
}
$array = [
'foo' => [
'bar' => new User(),
]
];
$value = isset($array['foo']['bar']->name) ? $array['foo']['bar']->name : null;
```
Yii fournit une méthode très pratique pour faire cela :
```php
$value = ArrayHelper::getValue($array, 'foo.bar.name');
```
Le premier argument de la méthode indique de quelle source nous voulons récupérer une valeur. Le deuxième spécifie comment récupérer la donnée. Il peut s'agir d'un des éléments suivants :
- Nom d'une clé de tableau ou de la propriété d'un objet de laquelle récupérer une valeur.
- Un jeu de noms de clé de tableau ou de propriétés d'objet séparées par des points, comme dans l'exemple que nous venons de présenter ci-dessus.
- Une fonction de rappel qui retourne une valeur.
Le fonction de rappel doit être la suivante :
```php
$fullName = ArrayHelper::getValue($user, function ($user, $defaultValue) {
return $user->firstName . ' ' . $user->lastName;
});
```
Le troisième argument facultatif est la valeur par défaut qui est `null` si on ne la spécifie pas. Il peut être utilisé comme ceci :
```php
$username = ArrayHelper::getValue($comment, 'user.username', 'Unknown');
```
Dans le cas où vous voulez récupérer la valeur tout en la retirant immédiatement du tableau, vous pouvez utiliser la méthode `remove` :
```php
$array = ['type' => 'A', 'options' => [1, 2]];
$type = ArrayHelper::remove($array, 'type');
```
Après exécution du code, `$array` contiendra `['options' => [1, 2]]` et `$type` sera `A`. Notez que contrairement à la méthode `getValue`, `remove` accepte seulement les noms de clé.
## Tester l'existence des clés <span id="checking-existence-of-keys"></span>
`ArrayHelper::keyExists` fonctionne comme [array_key_exists](http://php.net/manual/en/function.array-key-exists.php) sauf qu'elle prend également en charge la comparaison de clés insensible à la casse. Par exemple,
```php
$data1 = [
'userName' => 'Alex',
];
$data2 = [
'username' => 'Carsten',
];
if (!ArrayHelper::keyExists('username', $data1, false) || !ArrayHelper::keyExists('username', $data2, false)) {
echo "Veuillez fournir un nom d'utilisateur (username).";
}
```
## Récupération de colonnes <span id="retrieving-columns"></span>
Il arrive souvent que vous ayez à récupérer une colonne de valeurs d'un tableau de lignes de données ou d'objets. Un exemple courant est l'obtention d'une liste d'identifiants.
```php
$array = [
['id' => '123', 'data' => 'abc'],
['id' => '345', 'data' => 'def'],
];
$ids = ArrayHelper::getColumn($array, 'id');
```
Le résultat sera `['123', '345']`.
Si des transformations supplémentaires sont nécessaires ou si la manière de récupérer les valeurs est complexe, le second argument peut être formulé sous forme de fonction anonyme :
```php
$result = ArrayHelper::getColumn($array, function ($element) {
return $element['id'];
});
```
## Réindexation de tableaux <span id="reindexing-arrays"></span>
La méthode `index` peut être utilisées pour indexer un tableau selon une clé spécifiée. L'entrée doit être soit un tableau multidimensionnel, soit un tableau d'objets. `$key` peut être un nom de clé du sous-tableau, un nom de propriété d'objet ou une fonction anonyme qui doit retourner la valeur à utiliser comme clé.
L'attribut `$groups` est un tableau de clés qui est utilisé pour regrouper le tableau d'entrée en un ou plusieurs sous-tableaux basés sur les clés spécifiées.
Si l'argument `$key` ou sa valeur pour l'élément particulier est `null` alors que `$groups` n'est pas défini, l'élément du tableau est écarté. Autrement, si `$groups` est spécifié, l'élément du tableau est ajouté au tableau résultant sans aucune clé.
Par exemple :
```php
$array = [
['id' => '123', 'data' => 'abc', 'device' => 'laptop'],
['id' => '345', 'data' => 'def', 'device' => 'tablet'],
['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'],
];
$result = ArrayHelper::index($array, 'id');
```
Le résultat est un tableau associatif, dans lequel la clé est la valeur de l'attribut `id` :
```php
[
'123' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop'],
'345' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone']
// Le second élément du tableau d'origine est écrasé par le dernier élément parce que les identifiants sont identiques.
]
```
Une fonction anonyme passée en tant que `$key`, conduit au même résultat :
```php
$result = ArrayHelper::index($array, function ($element) {
return $element['id'];
});
```
Passer `id` comme troisième argument regroupe `$array` par `id`:
```php
$result = ArrayHelper::index($array, null, 'id');
```
Le résultat est un tableau multidimensionnel regroupé par `id` au premier niveau et non indexé au deuxième niveau :
```php
[
'123' => [
['id' => '123', 'data' => 'abc', 'device' => 'laptop']
],
'345' => [ // all elements with this index are present in the result array
['id' => '345', 'data' => 'def', 'device' => 'tablet'],
['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'],
]
]
```
Une fonction anonyme peut également être utilisée dans le tableau de regroupement :
```php
$result = ArrayHelper::index($array, 'data', [function ($element) {
return $element['id'];
}, 'device']);
```
Le résultat est un tableau multidimensionnel regroupé par `id` au premier niveau, par `device` au deuxième niveau et par `data` au troisième niveau :
```php
[
'123' => [
'laptop' => [
'abc' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop']
]
],
'345' => [
'tablet' => [
'def' => ['id' => '345', 'data' => 'def', 'device' => 'tablet']
],
'smartphone' => [
'hgi' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone']
]
]
]
```
## Construction de tableaux de mise en correspondance <span id="building-maps"></span>
Afin de construire un tableau de mise en correspondance (paires clé-valeur) sur la base d'un tableau multidimensionnel ou d'un tableau d'objets, vous pouvez utiliser la méthode `map`.
Les paramètres `$from` et `$to` spécifient les noms de clé ou les noms des propriétés pour construire le tableau de mise en correspondance. Le paramètre facultatif `$group` est un nom de clé ou de propriété qui permet de regrouper les éléments du tableau au premier niveau. Par exemple :
```php
$array = [
['id' => '123', 'name' => 'aaa', 'class' => 'x'],
['id' => '124', 'name' => 'bbb', 'class' => 'x'],
['id' => '345', 'name' => 'ccc', 'class' => 'y'],
];
$result = ArrayHelper::map($array, 'id', 'name');
// le résultat est :
// [
// '123' => 'aaa',
// '124' => 'bbb',
// '345' => 'ccc',
// ]
$result = ArrayHelper::map($array, 'id', 'name', 'class');
// le résultat est :
// [
// 'x' => [
// '123' => 'aaa',
// '124' => 'bbb',
// ],
// 'y' => [
// '345' => 'ccc',
// ],
// ]
```
## Tri multidimensionnel <span id="multidimensional-sorting"></span>
La méthode `multisort` facilite le tri d'un tableau d'objets ou de tableaux imbriqués selon une ou plusieurs clés. Par exemple :
```php
$data = [
['age' => 30, 'name' => 'Alexander'],
['age' => 30, 'name' => 'Brian'],
['age' => 19, 'name' => 'Barney'],
];
ArrayHelper::multisort($data, ['age', 'name'], [SORT_ASC, SORT_DESC]);
```
Après le tri, `data` contient ce qui suit :
```php
[
['age' => 19, 'name' => 'Barney'],
['age' => 30, 'name' => 'Brian'],
['age' => 30, 'name' => 'Alexander'],
];
```
Le deuxième argument, qui spécifie les clés de tri peut être une chaîne de caractères si la clé est unique, un tableau dans le cas de clés multiples, ou une fonction anonyme telle que celle qui suit :
```php
ArrayHelper::multisort($data, function($item) {
return isset($item['age']) ? ['age', 'name'] : 'name';
});
```
Le troisième argument précise la direction. Dans le cas d'un tri selon une clé unique, il s'agit soit de `SORT_ASC`, soit de `SORT_DESC`. Si le tri se fait selon des valeurs multiples, vous pouvez préciser des directions de tri différentes pour chacune des clés en présentant ces directions sous forme de tableau.
Le dernier argument est une option de tri de PHP qui peut prendre les mêmes valeurs que celles acceptées par la fonction [sort()](http://php.net/manual/en/function.sort.php) de PHP.
## Détection des types de tableau <span id="detecting-array-types"></span>
Il est pratique de savoir si un tableau est indexé ou associatif. Voici un exemple :
```php
// aucune clé spécifiée
$indexed = ['Qiang', 'Paul'];
echo ArrayHelper::isIndexed($indexed);
// toutes les clés sont des chaînes de caractères
$associative = ['framework' => 'Yii', 'version' => '2.0'];
echo ArrayHelper::isAssociative($associative);
```
## Encodage et décodage de valeurs HTML <span id="html-encoding-values"></span>
Afin d'encoder ou décoder des caractères spéciaux dans un tableau de chaînes de caractères en/depuis des entités HTML, vous pouvez utiliser les fonctions suivantes :
```php
$encoded = ArrayHelper::htmlEncode($data);
$decoded = ArrayHelper::htmlDecode($data);
```
Seules les valeurs sont encodées par défaut. En passant un deuxième argument comme `false` vous pouvez également encoder les clés d'un tableau. L'encodage utilise le jeu de caractères de l'application et on peut le changer via un troisième argument.
## Fusion de tableaux <span id="merging-arrays"></span>
La fonction [[yii\helpers\ArrayHelper::merge()|ArrayHelper::merge()]] vous permet de fusionner deux, ou plus, tableaux en un seul de manière récursive. Si chacun des tableaux possède un élément avec la même chaîne clé valeur, le dernier écrase le premier (ce qui est un fonctionnement différent de [array_merge_recursive()](http://php.net/manual/en/function.array-merge-recursive.php)).
La fusion récursive est entreprise si les deux tableaux possèdent un élément de type tableau avec la même clé. Pour des éléments dont la clé est un entier, les éléments du deuxième tableau sont ajoutés aux éléments du premier tableau. Vous pouvez utiliser l'objet [[yii\helpers\UnsetArrayValue]] pour supprimer la valeur du premier tableau ou [[yii\helpers\ReplaceArrayValue]] pour forcer le remplacement de la première valeur au lieu de la fusion récursive.
Par exemple :
```php
$array1 = [
'name' => 'Yii',
'version' => '1.1',
'ids' => [
1,
],
'validDomains' => [
'example.com',
'www.example.com',
],
'emails' => [
'admin' => 'admin@example.com',
'dev' => 'dev@example.com',
],
];
$array2 = [
'version' => '2.0',
'ids' => [
2,
],
'validDomains' => new \yii\helpers\ReplaceArrayValue([
'yiiframework.com',
'www.yiiframework.com',
]),
'emails' => [
'dev' => new \yii\helpers\UnsetArrayValue(),
],
];
$result = ArrayHelper::merge($array1, $array2);
```
Le résultat est :
```php
[
'name' => 'Yii',
'version' => '2.0',
'ids' => [
1,
2,
],
'validDomains' => [
'yiiframework.com',
'www.yiiframework.com',
],
'emails' => [
'admin' => 'admin@example.com',
],
]
```
## Conversion d'objets en tableaux <span id="converting-objects-to-arrays"></span>
Il arrive souvent que vous ayez besoin de convertir un objet, ou un tableau d'objets, en tableau. Le cas le plus courant est la conversion de modèles d'enregistrements actifs afin de servir des tableaux de données via une API REST ou pour un autre usage. Le code suivant peut alors être utilisé :
```php
$posts = Post::find()->limit(10)->all();
$data = ArrayHelper::toArray($posts, [
'app\models\Post' => [
'id',
'title',
// the key name in array result => property name
'createTime' => 'created_at',
// the key name in array result => anonymous function
'length' => function ($post) {
return strlen($post->content);
},
],
]);
```
Le premier argument contient les données à convertir. Dans notre cas, nous convertissons un modèle d'enregistrements actifs `Post`.
The second argument est un tableau de mise en correspondance de conversions par classe. Nous définissons une mise en correspondance pour le modèle `Post`. Chaque tableau de mise en correspondance contient un jeu de mise en correspondance. Chaque mise en correspondance peut être :
- Un nom de champ à inclure tel quel.
- Une paire clé-valeur dans laquelle la clé est donnée sous forme de chaîne de caractères et la valeur sous forme du nom de la colonne dont on doit prendre la valeur.
- Une paire clé-valeur dans laquelle la clé est donnée sous forme de chaîne de caractères et la valeur sous forme de fonction de rappel qui la retourne.
Le résultat de la conversion ci-dessus pour un modèle unique est :
```php
[
'id' => 123,
'title' => 'test',
'createTime' => '2013-01-01 12:00AM',
'length' => 301,
]
```
Il est possible de fournir une manière par défaut de convertir un objet en tableau pour une classe spécifique en implémentant l'interface [[yii\base\Arrayable|Arrayable]] dans cette classe.
## Test de l'appartenance à un tableau <span id="testing-arrays"></span>
Souvent, vous devez savoir si un élément se trouve dans un tableau ou si un jeu d'éléments est un sous-ensemble d'un autre. Bien que PHP offre la fonction `in_array()`, cette dernière ne prend pas en charge les sous-ensembles ou les objets `\Traversable`.
Pour faciliter ce genre de tests, [[yii\helpers\ArrayHelper]] fournit les méthodes [[yii\helpers\ArrayHelper::isIn()|isIn()]]
et [[yii\helpers\ArrayHelper::isSubset()|isSubset()]] avec la même signature que [in_array()](http://php.net/manual/en/function.in-array.php).
```php
// true
ArrayHelper::isIn('a', ['a']);
// true
ArrayHelper::isIn('a', new ArrayObject(['a']));
// true
ArrayHelper::isSubset(new ArrayObject(['a', 'c']), new ArrayObject(['a', 'b', 'c']));
```

396
docs/guide-fr/helper-html.md

@ -0,0 +1,396 @@
Classe assistante Html
======================
Toutes les applications Web génèrent un grand nombre de balises HTML. Si le code HTML est statique, il peut être créé efficacement sous forme de [mélange de code PHP et de code HTML dans un seul fichier](http://php.net/manual/en/language.basic-syntax.phpmode.php), mais lorsqu'il est généré dynamiquement, cela commence à être compliqué à gérer sans une aide supplémentaire. Yii fournit une telle aide sous la forme de la classe assistante Html, qui offre un jeu de méthodes statiques pour manipuler les balises Html les plus courantes, leurs options et leur contenu.
> Note: si votre code HTML est presque statique, il vaut mieux utiliser HTML directement. Il n'est pas nécessaire d'envelopper tout dans des appels aux méthodes de la classe assistante Html.
## Les bases <span id="basics"></span>
Comme la construction de code HTML dynamique en concaténant des chaînes de caractère peut très vite tourner à la confusion, Yii fournit un jeu de méthodes pour manipuler les options de balises et construire des balises s'appuyant sur ces options.
### Génération de balises <span id="generating-tags"></span>
Le code pour générer une balise ressemble à ceci :
```php
<?= Html::tag('p', Html::encode($user->name), ['class' => 'username']) ?>
```
Le premier argument est le nom de la balise. Le deuxième est le contenu qui apparaît entre l'ouverture de la balise et sa fermeture.
Notez que nous utilisons `Html::encode` – c'est parce que le contenu n'est pas encodé automatiquement pour permetre l'utilisation de HTML quand c'est nécessaire.
Le troisième est un tableau d'options HTML ou, en d'autres mots, les attributs de la balise.
Dans ce tableau, la clé est le nom de l'attribut (comme `class`, `href` ou `target`) et la valeur est sa valeur.
Le code ci-dessus génère le code HTML suivant :
```html
<p class="username">samdark</p>
```
Dans le cas où vous avez simplement besoin d'ouvrir ou de fermer la balise, vous pouvez utiliser les méthodes `Html::beginTag()` et `Html::endTag()`.
Des options sont utilisées dans de nombreuses méthodes de la classe assistante Html et de nombreux composants graphiques (widgets). Dans tous ces cas, il y a quelques manipulations supplémentaires à connaître :
- Si une valeur est `null`, l'attribut correspondant n'est pas rendu.
- Les attributs du type booléen sont traités comme des
[attributs booléens ](http://www.w3.org/TR/html5/infrastructure.html#boolean-attributes).
- Les valeurs des attributs sont encodés HTML à l'aide de la méthode [[yii\helpers\Html::encode()|Html::encode()]].
- Si la valeur d'un attribut est un tableau, il est géré comme suit :
* Si l'attribut est un attribut de donnée tel que listé dans [[yii\helpers\Html::$dataAttributes]], tel que `data` ou `ng`,
une liste d'attributs est rendue, un pour chacun des élément dans le tableau de valeurs. Par exemple,
`'data' => ['id' => 1, 'name' => 'yii']` génère `data-id="1" data-name="yii"`; et
`'data' => ['params' => ['id' => 1, 'name' => 'yii'], 'status' => 'ok']` génère
`data-params='{"id":1,"name":"yii"}' data-status="ok"`. Notez que dans le dernier exemple le format JSON est utilisé pour rendre le sous-tableau.
* Si l'attribut n'est PAS un attribut de donnée, la valeur est encodée JSON. Par exemple,
`['params' => ['id' => 1, 'name' => 'yii']` génère `params='{"id":1,"name":"yii"}'`.
### Formation des classes et des styles CSS <span id="forming-css"></span>
Lors de la construction des options pour des balises HTML, nous démarrons souvent avec des valeurs par défaut qu'il faut modifier. Afin d'ajouter ou de retirer une classe, vous pouvez utiliser ce qui suit :
```php
$options = ['class' => 'btn btn-default'];
if ($type === 'success') {
Html::removeCssClass($options, 'btn-default');
Html::addCssClass($options, 'btn-success');
}
echo Html::tag('div', 'Pwede na', $options);
// si la valeur de $type est 'success' le rendu sera
// <div class="btn btn-success">Pwede na</div>
```
Vous pouvez spécifier de multiples classe CSS en utilisant le tableau de styles également :
```php
$options = ['class' => ['btn', 'btn-default']];
echo Html::tag('div', 'Save', $options);
// rend '<div class="btn btn-default">Save</div>'
```
Vous pouvez aussi utiliser le tableau de styles pour ajouter ou retirer des classes :
```php
$options = ['class' => 'btn'];
if ($type === 'success') {
Html::addCssClass($options, ['btn-success', 'btn-lg']);
}
echo Html::tag('div', 'Save', $options);
// rend '<div class="btn btn-success btn-lg">Save</div>'
```
`Html::addCssClass()` empêche la duplication, vous n'avez donc pas à vous préoccuper de savoir si une classe apparaît deux fois :
```php
$options = ['class' => 'btn btn-default'];
Html::addCssClass($options, 'btn-default'); // class 'btn-default' is already present
echo Html::tag('div', 'Save', $options);
// rend '<div class="btn btn-default">Save</div>'
```
Si l'option classe CSS est spécifiée en utilisant le tableau de styles, vous pouvez utiliser une clé nommée pour indiquer le but logique de la classe. Dans ce cas, une classe utilisant la même clé dans le tableau de styles passé à `Html::addClass()` est ignorée :
```php
$options = [
'class' => [
'btn',
'theme' => 'btn-default',
]
];
Html::addCssClass($options, ['theme' => 'btn-success']); // la clé 'theme' est déjà utilisée
echo Html::tag('div', 'Save', $options);
// rend '<div class="btn btn-default">Save</div>'
```
Les styles CSS peuvent être définis d'une façon similaire en utilisant l'attribut `style` :
```php
$options = ['style' => ['width' => '100px', 'height' => '100px']];
// donne style="width: 100px; height: 200px; position: absolute;"
Html::addCssStyle($options, 'height: 200px; position: absolute;');
// gives style="position: absolute;"
Html::removeCssStyle($options, ['width', 'height']);
```
Lors de l'utilisation de [[yii\helpers\Html::addCssStyle()|addCssStyle()]], vous pouvez spécifier soit un tableau de paires clé-valeur qui correspond aux propriétés CSS noms et valeurs, soit une chaîne de caractères telle que `width: 100px; height: 200px;`. Ces formats peuvent être convertis de l'un en l'autre en utilisant les méthodes [[yii\helpers\Html::cssStyleFromArray()|cssStyleFromArray()]] et
[[yii\helpers\Html::cssStyleToArray()|cssStyleToArray()]]. La méthode [[yii\helpers\Html::removeCssStyle()|removeCssStyle()]]
accepte un tableau de propriétés à retirer. S'il s'agit d'une propriété unique, elle peut être spécifiée sous forme de chaîne de caractères.
### Encodage et décodage du contenu <span id="encoding-and-decoding-content"></span>
Pour que le contenu puisse être affiché en HTML de manière propre et en toute sécurité, les caractères spéciaux du contenu doivent être encodés. En PHP, cela s'obtient avec [htmlspecialchars](http://www.php.net/manual/en/function.htmlspecialchars.php) et
[htmlspecialchars_decode](http://www.php.net/manual/en/function.htmlspecialchars-decode.php). Le problème rencontré en utilisant ces méthodes directement est que vous devez spécifier l'encodage et des options supplémentaires tout le temps. Comme ces options restent toujours les mêmes et que l'encodage doit correspondre à celui de l'application pour éviter les problèmes de sécurité, Yii fournit deux méthodes compactes et faciles à utiliser :
```php
$userName = Html::encode($user->name);
echo $userName;
$decodedUserName = Html::decode($userName);
```
## Formulaires <span id="forms"></span>
Manipuler des formulaires dans le code HTML est tout à fait répétitif et sujet à erreurs. À cause de cela, il existe un groupe de méthodes pour aider à les manipuler.
> Note : envisagez d'utiliser [[yii\widgets\ActiveForm|ActiveForm]] dans le cas où vous avez affaire à des modèles et que ces derniers doivent être validés.
### Création de formulaires <span id="creating-forms"></span>
Les formulaires peut être ouverts avec la méthode [[yii\helpers\Html::beginForm()|beginForm()]] comme ceci :
```php
<?= Html::beginForm(['order/update', 'id' => $id], 'post', ['enctype' => 'multipart/form-data']) ?>
```
Le premier argument est l'URL à laquelle le formulaire sera soumis. Il peut être spécifié sous la forme d'une route Yii et de paramètres acceptés par [[yii\helpers\Url::to()|Url::to()]].
Le deuxième est la méthode à utiliser. `post` est la méthode par défaut. Le troisième est un tableau d'options pour la balise form. Dans ce cas, nous modifions l'encodage des données du formulaire dans la requête POST en `multipart/form-data`, ce qui est requis pour envoyer des fichiers.
La fermeture du formulaire se fait simplement par :
```php
<?= Html::endForm() ?>
```
### Boutons <span id="buttons"></span>
Pour générer des boutons, vous pouvez utiliser le code suivant :
```php
<?= Html::button('Pressez-mo!', ['class' => 'teaser']) ?>
<?= Html::submitButton('Envoyer', ['class' => 'submit']) ?>
<?= Html::resetButton('Ré-initialiser', ['class' => 'reset']) ?>
```
Le premier argument pour les trois méthodes est l'intitulé du bouton, le deuxième est un tableau d'options.
L'intitulé n'est pas encodé, mais si vous affichez des données en provenance de l'utilisateur, encodez les avec [[yii\helpers\Html::encode()|Html::encode()]].
### Champs d'entrée <span id="input-fields"></span>
Il y a deux groupes de méthodes d'entrée de données. Celles qui commencent par `active`, est qui sont appelées entrées actives, et celles qui ne commencent pas par ce mot. Les entrées actives prennent leurs données dans le modèle à partir des attributs spécifiés, tandis que pour les entrées régulières, les données sont spécifiées directement.
Les méthodes les plus génériques sont :
```php
type, nom de l'entrée, valeur de l'entrée, options
<?= Html::input('text', 'username', $user->name, ['class' => $username]) ?>
type, modèle, nom de l'attribut du modèle, options
<?= Html::activeInput('text', $user, 'name', ['class' => $username]) ?>
```
Si vous connaissez le type de l'entrée à l'avance, il est plus commode d'utiliser les méthodes raccourcis :
- [[yii\helpers\Html::buttonInput()]]
- [[yii\helpers\Html::submitInput()]]
- [[yii\helpers\Html::resetInput()]]
- [[yii\helpers\Html::textInput()]], [[yii\helpers\Html::activeTextInput()]]
- [[yii\helpers\Html::hiddenInput()]], [[yii\helpers\Html::activeHiddenInput()]]
- [[yii\helpers\Html::passwordInput()]] / [[yii\helpers\Html::activePasswordInput()]]
- [[yii\helpers\Html::fileInput()]], [[yii\helpers\Html::activeFileInput()]]
- [[yii\helpers\Html::textarea()]], [[yii\helpers\Html::activeTextarea()]]
Les listes radio et les boîtes à cocher sont un peu différentes en matière de signature de méthode :
```php
<?= Html::radio('agree', true, ['label' => 'I agree']);
<?= Html::activeRadio($model, 'agree', ['class' => 'agreement'])
<?= Html::checkbox('agree', true, ['label' => 'I agree']);
<?= Html::activeCheckbox($model, 'agree', ['class' => 'agreement'])
```
Les listes déroulantes et les boîtes listes peuvent être rendues comme suit :
```php
<?= Html::dropDownList('list', $currentUserId, ArrayHelper::map($userModels, 'id', 'name')) ?>
<?= Html::activeDropDownList($users, 'id', ArrayHelper::map($userModels, 'id', 'name')) ?>
<?= Html::listBox('list', $currentUserId, ArrayHelper::map($userModels, 'id', 'name')) ?>
<?= Html::activeListBox($users, 'id', ArrayHelper::map($userModels, 'id', 'name')) ?>
```
Le premier argument est le nom de l'entrée, le deuxième est la valeur sélectionnée actuelle et le troisième est un tableau de paires clé-valeur, dans lequel la clé est la valeur d'entrée dans la liste et la valeur est l'étiquette qui correspond à cette valeur dans la liste.
Si vous désirez que des choix multiples soient sélectionnables, vous pouvez utiliser la liste à sélection multiples (checkbox list) :
```php
<?= Html::checkboxList('roles', [16, 42], ArrayHelper::map($roleModels, 'id', 'name')) ?>
<?= Html::activeCheckboxList($user, 'role', ArrayHelper::map($roleModels, 'id', 'name')) ?>
```
Sinon utilisez la liste radio :
```php
<?= Html::radioList('roles', [16, 42], ArrayHelper::map($roleModels, 'id', 'name')) ?>
<?= Html::activeRadioList($user, 'role', ArrayHelper::map($roleModels, 'id', 'name')) ?>
```
### Étiquettes et erreurs <span id="labels-and-errors"></span>
Comme pour les entrées, il existe deux méthodes pour générer les étiquettes de formulaire. Celles pour les entrées « actives » qui prennent leurs étiquettes dans le modèle, et celles « non actives » qui sont étiquetées directement :
```php
<?= Html::label('User name', 'username', ['class' => 'label username']) ?>
<?= Html::activeLabel($user, 'username', ['class' => 'label username']) ?>
```
Pour afficher les erreurs de formulaire à partir d'un modèle ou sous forme de résumé pour un modèle, vous pouvez utiliser :
```php
<?= Html::errorSummary($posts, ['class' => 'errors']) ?>
```
Pour afficher une erreur individuellement :
```php
<?= Html::error($post, 'title', ['class' => 'error']) ?>
```
### Nom et valeur des entrées <span id="input-names-and-values"></span>
Il existe deux méthodes pour obtenir des noms, des identifiants et des valeurs pour des champs d'entrée basés sur un modèle. Elles sont essentiellement utilisées en interne, mais peuvent être pratiques quelques fois :
```php
// Post[title]
echo Html::getInputName($post, 'title');
// post-title
echo Html::getInputId($post, 'title');
// my first post
echo Html::getAttributeValue($post, 'title');
// $post->authors[0]
echo Html::getAttributeValue($post, '[0]authors[0]');
```
Dans ce qui précède, le premier argument est le modèle, tandis que le deuxième est l'expression d'attribut. Dans sa forme la plus simple, l'expression est juste un nom d'attribut, mais il peut aussi s'agir d'un nom d'attribut préfixé et-ou suffixé par des index de tableau, ce qui est essentiellement le cas pour des entrées tabulaires :
- `[0]content` est utilisé dans des entrées de données tabulaires pour représenter l'attribut `content` pour le premier modèle des entrées tabulaires ;
- `dates[0]` représente le premier élément du tableau de l'attribut `dates` ;
- `[0]dates[0]` représente le premier élément du tableau de l'attribut `dates` pour le premier modèle des entrées tabulaires.
Afin d'obtenir le nom de l'attribut sans suffixe ou préfixe, vous pouvez utiliser ce qui suit :
```php
// dates
echo Html::getAttributeName('dates[0]');
```
## Styles et scripts <span id="styles-and-scripts"></span>
Il existe deux méthodes pour générer les balises enveloppes des styles et des scripts :
```php
<?= Html::style('.danger { color: #f00; }') ?>
Produit
<style>.danger { color: #f00; }</style>
<?= Html::script('alert("Hello!");', ['defer' => true]);
Produit
<script defer>alert("Hello!");</script>
```
Si vous désirez utiliser utiliser un style externe d'un fichier CSS :
```php
<?= Html::cssFile('@web/css/ie5.css', ['condition' => 'IE 5']) ?>
génère
<!--[if IE 5]>
<link href="http://example.com/css/ie5.css" />
<![endif]-->
```
Le premier argument est l'URL. Le deuxième est un tableau d'options. En plus des options normales, vous pouvez spécifier :
- `condition` pour envelopper `<link` dans des commentaires conditionnels avec la condition spécifiée. Nous espérons que vous n'aurez jamais besoin de commentaires conditionnels ;
- `noscript` peut être défini à `true` pour envelopper `<link` dans une balise `<noscript>` de façon à ce qu'elle soit incluse seulement si le navigateur ne prend pas en charge JavaScript ou si l'utilisateur l'a désactivé.
Pour lier un fichier JavaScript :
```php
<?= Html::jsFile('@web/js/main.js') ?>
```
Se passe comme avec CSS, le premier argument spécifie l'URL du fichier à inclure. Les options sont passées via le deuxième argument. Dans les options vous pouvez spécifier `condition` de la même manière que dans les options pour un fichier CSS (méthode `cssFile`).
## Hyperliens <span id="hyperlinks"></span>
Il y a une méthode commode pour générer les hyperliens :
```php
<?= Html::a('Profile', ['user/view', 'id' => $id], ['class' => 'profile-link']) ?>
```
Le premier argument est le titre. Il n'est pas encodé, mais si vous utilisez des données entrées par l'utilisateur, vous devez les encoder avec `Html::encode()`. Le deuxième argument est ce qui se retrouvera dans l'attribut `href` de la balise `<a`.
Voir [Url::to()](helper-url.md) pour les détails sur les valeurs acceptées.
Le troisième argument est un tableau pour les attributs de la balise.
Si vous devez générer des liens `mailto`, vous pouvez utiliser le code suivant :
```php
<?= Html::mailto('Contact us', 'admin@example.com') ?>
```
## Images <span id="images"></span>
Pour générer une balise image, utilisez le code suivant :
```php
<?= Html::img('@web/images/logo.png', ['alt' => 'My logo']) ?>
qui génère
<img src="http://example.com/images/logo.png" alt="My logo" />
```
En plus des [alias](concept-aliases.md), le premier argument accepte les routes, les paramètres et les URL, tout comme [Url::to()](helper-url.md).
## Listes <span id="lists"></span>
Les listes non ordonnées peuvent être générées comme suit :
```php
<?= Html::ul($posts, ['item' => function($item, $index) {
return Html::tag(
'li',
$this->render('post', ['item' => $item]),
['class' => 'post']
);
}]) ?>
```
Pour une liste ordonnée, utilisez plutôt `Html::ol()`.

68
docs/guide-fr/helper-overview.md

@ -0,0 +1,68 @@
Classes assistantes
===================
> Note: cette section est en cours de développement.
Yii procure de nombreuses classes qui vous aident à simplifier le code de tâches courantes, telles que la manipulation de chaînes de caractères ou de tableaux, la génération de code HTML, et ainsi de suite. Ces classes assistantes sont organisées dans l'espace de noms `yii\helpers` et sont toutes des classes statiques (ce qui signifie qu'elles ne contiennent que des propriétés et des méthodes statiques et ne doivent jamais être instanciées).
Vous utilisez une classe assistante en appelant directement une de ses méthodes statiques, comme ceci :
```php
use yii\helpers\Html;
echo Html::encode('Test > test');
```
> Note: pour prendre en charge la [personnalisation des classes assistantes](#customizing-helper-classes), Yii éclate chacune des classes assistantes du noyau en deux classes : une classe de base (p. ex. `BaseArrayHelper`) et une classe concrète (p. ex. `ArrayHelper`). Lorsque vous utilisez une classe assistante, vous devez utiliser la version concrète uniquement et ne jamais utiliser la classe de base.
Classes assistantes du noyau
----------------------------
Les versions de Yii fournissent les classes assistantes du noyau suivantes :
- [ArrayHelper](helper-array.md)
- Console
- FileHelper
- FormatConverter
- [Html](helper-html.md)
- HtmlPurifier
- Imagine (provided by yii2-imagine extension)
- Inflector
- Json
- Markdown
- StringHelper
- [Url](helper-url.md)
- VarDumper
Personnalisation des classes assistantes <span id="customizing-helper-classes"></span>
----------------------------------------
Pour personnaliser une classe assistante du noyau (p. ex. [[yii\helpers\ArrayHelper]]), vous devez créer une nouvelle classe qui étend la classe de base correspondant à la classe assistante (p. ex. [[yii\helpers\ArrayHelper]]), y compris son espace de noms. Cette classe sera ensuite configurée pour remplacer l'implémentation originale de Yii.
L'exemple qui suit montre comment personnaliser la méthode [[yii\helpers\ArrayHelper::merge()|merge()]] de la classe [[yii\helpers\ArrayHelper]] :
```php
<?php
namespace yii\helpers;
class ArrayHelper extends BaseArrayHelper
{
public static function merge($a, $b)
{
// votre implémentation personnalisée
}
}
```
Sauvegardez votre classe dans un fichier nommé `ArrayHelper.php`. Le fichier peut se trouver dans n'importe quel dossier, par exemple, `@app/components`.
Ensuite, dans le [script d'entrée](structure-entry-scripts.md) de votre application, ajoutez la ligne de code suivante, après l'inclusion du fichier `yii.php` pour dire à la [classe autoloader de Yii](concept-autoloading.md) de charger votre classe personnalisée au lieu de la classe assistance originale de Yii.
```php
Yii::$classMap['yii\helpers\ArrayHelper'] = '@app/components/ArrayHelper.php';
```
Notez que la personnalisation d'une classe assistante n'est utile que si vous désirez changer le comportement d'une fonction existante de la classe assistante. Si vous désirez ajouter une fonction additionnelle à utiliser dans votre application, le mieux est de créer une classe assistante séparée pour cela.

161
docs/guide-fr/helper-url.md

@ -0,0 +1,161 @@
Classe assistante Url
=====================
La classe assistante Url fournit un jeu de méthodes statiques pour gérer les URL.
## Obtenir des URL communes <span id="getting-common-urls"></span>
Vous pouvez utiliser deux méthodes pour obtenir des URL communes : l'URL de la page d'accueil et l'URL de base de la requête courante. Pour obtenir l'URL de la page d'accueil, utilisez ce qui suit :
```php
$relativeHomeUrl = Url::home();
$absoluteHomeUrl = Url::home(true);
$httpsAbsoluteHomeUrl = Url::home('https');
```
Si aucun paramètre n'est passé, l'URL générée est relative. Vous pouvez passer `true` pour obtenir une URL absolue pour le schéma courant ou spécifier un schéma explicitement (`https`, `http`).
Pour obtenir l'URL de base de la requête courante utilisez ceci :
```php
$relativeBaseUrl = Url::base();
$absoluteBaseUrl = Url::base(true);
$httpsAbsoluteBaseUrl = Url::base('https');
```
L'unique paramètre de la méthode fonctionne comme pour `Url::home()`.
## Création d'URL <span id="creating-urls"></span>
En vue de créer une URL pour une route donnée, utilisez la méthode `Url::toRoute()`. La méthode utilise [[\yii\web\UrlManager]] pour créer une URL :
```php
$url = Url::toRoute(['product/view', 'id' => 42]);
```
Vous pouvez spécifier la route sous forme de chaîne de caractère, p. ex. `site/index`. Vous pouvez également utiliser un tableau si vous désirez spécifier des paramètres de requête supplémentaires pour l'URL créée. Le format du tableau doit être :
```php
// génère : /index.php?r=site%2Findex&param1=value1&param2=value2
['site/index', 'param1' => 'value1', 'param2' => 'value2']
```
Si vous voulez créer une URL avec une ancre, vous pouvez utiliser le format de tableau avec un paramètre `#`. Par exemple :
```php
// génère: /index.php?r=site%2Findex&param1=value1#name
['site/index', 'param1' => 'value1', '#' => 'name']
```
Une route peut être ,soit absolue, soit relative. Une route absolue commence par une barre oblique de division (p. ex. `/site/index`) tandis que route relative commence sans ce caractère (p. ex. `site/index` ou `index`). Une route relative peut être convertie en une route absolue en utilisant une des règles suivantes :
- Si la route est une chaîne de caractères vide, la [[\yii\web\Controller::route|route]] est utilisée ;
- Si la route ne contient aucune barre oblique de division (p. ex. `index`), elle est considérée être un identifiant d'action dans le contrôleur courant et sera préfixée par l'identifiant du contrôleur ([[\yii\web\Controller::uniqueId]]);
- Si la route ne commence pas par une barre oblique de division (p. ex. `site/index`), elle est considérée être une route relative au module courant et sera préfixée par l'identifiant du module ([[\yii\base\Module::uniqueId|uniqueId]]).
Depuis la version 2.0.2, vous pouvez spécifier une route sous forme d'[alias](concept-aliases.md). Si c'est le cas, l'alias sera d'abord converti en la route réelle puis transformé en une route absolue en respectant les règles ci-dessus.
Voci quelques exemple d'utilisation de cette méthode :
```php
// /index.php?r=site%2Findex
echo Url::toRoute('site/index');
// /index.php?r=site%2Findex&src=ref1#name
echo Url::toRoute(['site/index', 'src' => 'ref1', '#' => 'name']);
// /index.php?r=post%2Fedit&id=100 assume the alias "@postEdit" is defined as "post/edit"
echo Url::toRoute(['@postEdit', 'id' => 100]);
// http://www.example.com/index.php?r=site%2Findex
echo Url::toRoute('site/index', true);
// https://www.example.com/index.php?r=site%2Findex
echo Url::toRoute('site/index', 'https');
```
Il existe une autre méthode `Url::to()` très similaire à [[toRoute()]]. La seule différence est que cette méthode requiert la spécification d'une route sous forme de tableau seulement. Si une chaîne de caractères est données, elle est traitée comme une URL.
Le premier argument peut être :
- un tableau : [[toRoute()]] sera appelée pour générer l'URL. Par exemple :
`['site/index']`, `['post/index', 'page' => 2]`. Reportez-vous à la méthode [[toRoute()]] pour plus de détails sur la manière de spécifier une route.
- une chaîne de caractères commençant par `@`: elle est traitée commme un alias, et la chaine aliasée correspondante est retournée ;
- une chaîne de caractères vide : l'URL couramment requise est retournée ;
- une chaîne de caractères normale : elle est retournée telle que.
Lorsque `$scheme` est spécifié (soit une chaîne de caractères, soit `true`), une URL absolue avec l'information hôte tirée de [[\yii\web\UrlManager::hostInfo]]) est retournée. Si`$url` est déjà une URL absolue, son schéma est remplacé par celui qui est spécifié.
Voici quelques exemples d'utilisation :
```php
// /index.php?r=site%2Findex
echo Url::to(['site/index']);
// /index.php?r=site%2Findex&src=ref1#name
echo Url::to(['site/index', 'src' => 'ref1', '#' => 'name']);
// /index.php?r=post%2Fedit&id=100 assume the alias "@postEdit" is defined as "post/edit"
echo Url::to(['@postEdit', 'id' => 100]);
// l'URL couramment requise
echo Url::to();
// /images/logo.gif
echo Url::to('@web/images/logo.gif');
// images/logo.gif
echo Url::to('images/logo.gif');
// http://www.example.com/images/logo.gif
echo Url::to('@web/images/logo.gif', true);
// https://www.example.com/images/logo.gif
echo Url::to('@web/images/logo.gif', 'https');
```
Depuis la version 2.0.3, vous pouvez utiliser [[yii\helpers\Url::current()]] pour créer une URL basée sur la route couramment requise et sur les paramètres de la méthode GET. Vous pouvez modifier ou retirer quelques uns des paramètres GET et en ajouter d'autres en passant le paramètre `$params` à la méthode. Par exemple :
```php
// suppose que $_GET = ['id' => 123, 'src' => 'google'],et que la route courante est "post/view"
// /index.php?r=post%2Fview&id=123&src=google
echo Url::current();
// /index.php?r=post%2Fview&id=123
echo Url::current(['src' => null]);
// /index.php?r=post%2Fview&id=100&src=google
echo Url::current(['id' => 100]);
```
## Se souvenir d'URL <span id="remember-urls"></span>
Il y a des cas dans lesquels vous avez besoin de mémoriser une URL et ensuite de l'utiliser durant le traitement d'une des requêtes séquentielles. Cela peut être fait comme suit :
```php
// se souvenir de l'URL courante
Url::remember();
// Se souvenir de l'URL spécifiée. Voir Url::to() pour le format des arguments.
Url::remember(['product/view', 'id' => 42]);
// Se souvenir de l'URL spécifiée avec un nom
Url::remember(['product/view', 'id' => 42], 'product');
```
Dans la prochaine requête, vous pouvez récupérer l'URL mémorisée comme ceci :
```php
$url = Url::previous();
$productUrl = Url::previous('product');
```
## Vérification des URL relatives <span id="checking-relative-urls"></span>
Pour savoir si une URL est relative, c.-à-d. n'a pas de partie « hôte », vous pouvez utiliser le code suivant :
```php
$isRelative = Url::isRelative('test/it');
```

2
docs/guide-fr/start-forms.md

@ -224,7 +224,7 @@ des données. Dans le cas où vous auriez désactivé le Javascript sur votre na
effectuée coté serveur, comme montré dans la méthode `actionEntry()`. Cela garantit la validité des données en toutes
circonstances.
> Attention : La validation coté client est un confort qui permet une meilleure expérience utilisateur. La validation coté serveur est toujours nécessaire, que la validation coté client soit ou non en place.
> Warning: La validation coté client est un confort qui permet une meilleure expérience utilisateur. La validation coté serveur est toujours nécessaire, que la validation coté client soit ou non en place.
Les étiquettes des champs de saisie sont générés par la méthode `field()`, en utilisant les noms des propriété du
modèle.

10
docs/guide-fr/start-installation.md

@ -4,7 +4,7 @@ Installer Yii
Vous pouvez installer Yii de deux façons, en utilisant [Composer](https://getcomposer.org/) ou en téléchargeant une archive.
La première méthode est conseillée, étant donné qu'elle permet d'installer de nouvelles [extensions](extend-creating-extensions.md) ou de mettre à jour Yii en éxécutant simplement une commande.
> Remarque : Contrairement à Yii 1, les installations standards de Yii 2 auront pour résultat le téléchargement et l'installation du framework, ainsi que d'un squelette d'application.
> Note: contrairement à Yii 1, les installations standards de Yii 2 auront pour résultat le téléchargement et l'installation du framework, ainsi que d'un squelette d'application.
Installer via Composer <span id="installing-via-composer"></span>
@ -26,7 +26,7 @@ Avec Composer installé, vous pouvez installer Yii en éxécutant la commande su
Cette commande installera Yii dans le dossier `basic`.
> Astuce : Si vous souhaitez installer la dernière version de développement de Yii, vous pouvez utiliser la commande suivante qui ajoutera l'[option stability](https://getcomposer.org/doc/04-schema.md#minimum-stability) :
> Tip: si vous souhaitez installer la dernière version de développement de Yii, vous pouvez utiliser la commande suivante qui ajoutera l'[option stability](https://getcomposer.org/doc/04-schema.md#minimum-stability) :
>
> composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic
>
@ -83,16 +83,16 @@ Vous devez configurer votre installation de PHP afin qu'elle réponde aux exigen
Configuration du serveur Web <span id="configuring-web-servers"></span>
----------------------------
> Remarque : Si vous voulez juste tester Yii sans intention de l'utiliser sur un serveur de production, vous pouvez ignorer ce paragraphe.
> Note: si vous voulez juste tester Yii sans intention de l'utiliser sur un serveur de production, vous pouvez ignorer ce paragraphe.
L'application installée selon les instructions ci-dessus devrait fonctionner *out of the box* avec le [serveur HTTP Apache](http://httpd.apache.org/) ou le [serveur HTTP Nginx](http://nginx.org/), sous Windows, Mac OX X, ou linux.
Sur un serveur de production, vous pouvez configurer votre serveur Web afin que l'application soit accessible via l'URL `http://www.example.com/index.php` au lieu de `http://www.example.com/basic/web/index.php`. Cela implique que le dossier racine de votre serveur Web pointe vers le dossier `basic/web`.
Vous pouvez également cacher `index.php` dans l'URL, comme décrit dans la partie [Génération et traitement des URL](runtime-url-handling.md), vous y apprendrez comment configurer votre serveur Apache ou Nginx pour atteindre ces objectifs.
> Remarque : En utilisant `basic/web` comme dossier racine, vous empêchez également aux utilisateurs finaux d'accéder à votre code d'application privé et fichiers de données sensibles qui sont stockés dans le dossier `basic`. Refuser l'accès à ces ressources est une amélioration de la sécurité.
> Note: en utilisant `basic/web` comme dossier racine, vous empêchez également aux utilisateurs finaux d'accéder à votre code d'application privé et fichiers de données sensibles qui sont stockés dans le dossier `basic`. Refuser l'accès à ces ressources est une amélioration de la sécurité.
> Remarque: Si votre application s'exécute dans un environnement d'hébergement mutualisé où vous n'avez pas la permission de modifier la configuration du serveur Web, vous pouvez ajuster la structure de votre application pour une meilleure sécurité. Merci de lire la partie [Environnement d'hébergement mutualisé](tutorial-shared-hosting.md) pour en savoir plus.
> Note: si votre application s'exécute dans un environnement d'hébergement mutualisé où vous n'avez pas la permission de modifier la configuration du serveur Web, vous pouvez ajuster la structure de votre application pour une meilleure sécurité. Merci de lire la partie [Environnement d'hébergement mutualisé](tutorial-shared-hosting.md) pour en savoir plus.
### Configuration Apache recommandée <span id="recommended-apache-configuration"></span>

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

@ -13,7 +13,7 @@ Yii は下記の DBMS のサポートを内蔵しています。
- [MySQL](http://www.mysql.com/)
- [MariaDB](https://mariadb.com/)
- [SQLite](http://sqlite.org/)
- [PostgreSQL](http://www.postgresql.org/)
- [PostgreSQL](http://www.postgresql.org/): バージョン 8.4 以上。
- [CUBRID](http://www.cubrid.org/): バージョン 9.3 以上。
- [Oracle](http://www.oracle.com/us/products/database/overview/index.html)
- [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): バージョン 2008 以上。

2
docs/guide-pl/db-active-record.md

@ -27,7 +27,7 @@ $db->createCommand('INSERT INTO `customer` (`name`) VALUES (:name)', [
Yii zapewnia wsparcie Active Record dla następujących typów relacyjnych baz danych:
* MySQL 4.1 lub nowszy: poprzez [[yii\db\ActiveRecord]]
* PostgreSQL 7.3 lub nowszy: poprzez [[yii\db\ActiveRecord]]
* PostgreSQL 8.4 lub nowszy: poprzez [[yii\db\ActiveRecord]]
* SQLite 2 i 3: poprzez [[yii\db\ActiveRecord]]
* Microsoft SQL Server 2008 lub nowszy: poprzez [[yii\db\ActiveRecord]]
* Oracle: poprzez [[yii\db\ActiveRecord]]

2
docs/guide-pt-BR/db-active-record.md

@ -22,7 +22,7 @@ $db->createCommand('INSERT INTO `customer` (`name`) VALUES (:name)', [
O Yii fornece suporte Active Record para os seguintes bancos de dados relacionais:
* MySQL 4.1 ou superior: via [[yii\db\ActiveRecord]]
* PostgreSQL 7.3 ou superior: via [[yii\db\ActiveRecord]]
* PostgreSQL 8.4 ou superior: via [[yii\db\ActiveRecord]]
* SQLite 2 e 3: via [[yii\db\ActiveRecord]]
* Microsoft SQL Server 2008 ou superior: via [[yii\db\ActiveRecord]]
* Oracle: via [[yii\db\ActiveRecord]]

2
docs/guide-ru/db-dao.md

@ -15,7 +15,7 @@ Yii DAO из коробки поддерживает следующие базы
- [MySQL](http://www.mysql.com/)
- [MariaDB](https://mariadb.com/)
- [SQLite](http://sqlite.org/)
- [PostgreSQL](http://www.postgresql.org/)
- [PostgreSQL](http://www.postgresql.org/): версии 8.4 или выше.
- [CUBRID](http://www.cubrid.org/): версии 9.3 или выше.
- [Oracle](http://www.oracle.com/us/products/database/overview/index.html)
- [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): версии 2008 или выше.

4
docs/guide-ru/tutorial-core-validators.md

@ -623,3 +623,7 @@ IPv4 адрес `192.168.10.128` также разрешен, так как на
По умолчанию - `false`. Учтите, что для того, чтобы IDN валидация работала корректно, вы должны установить `intl`
PHP расширение, иначе будет выброшено исключение.
> Note: Валидатор проверяет, что протокол и хост в URL являются корректными. Валидатор НЕ проверяет другие части URL
и НЕ предназначен для защиты от XSS или любых других видов атак. Обратитесь к секции
[Лучшие практики безопасности](security-best-practices.md) чтобы узнать больше о том, как предтвращать известные угрозы
при разработке приложений.

2
docs/guide-zh-CN/db-dao.md

@ -7,7 +7,7 @@ Yii 默认支持以下数据库 (DBMS):
- [MySQL](http://www.mysql.com/)
- [MariaDB](https://mariadb.com/)
- [SQLite](http://sqlite.org/)
- [PostgreSQL](http://www.postgresql.org/)
- [PostgreSQL](http://www.postgresql.org/): 版本 >= 8.4
- [CUBRID](http://www.cubrid.org/): 版本 >= 9.3 . (由于PHP PDO 扩展的一个[bug](http://jira.cubrid.org/browse/APIS-658) 引用值会无效,所以你需要在 CUBRID的客户端和服务端都使用 9.3 )
- [Oracle](http://www.oracle.com/us/products/database/overview/index.html)
- [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): 版本>=2005.

2
docs/guide/db-dao.md

@ -14,7 +14,7 @@ Yii DAO supports the following databases out of box:
- [MySQL](http://www.mysql.com/)
- [MariaDB](https://mariadb.com/)
- [SQLite](http://sqlite.org/)
- [PostgreSQL](http://www.postgresql.org/)
- [PostgreSQL](http://www.postgresql.org/): version 8.4 or higher
- [CUBRID](http://www.cubrid.org/): version 9.3 or higher.
- [Oracle](http://www.oracle.com/us/products/database/overview/index.html)
- [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): version 2008 or higher.

6
docs/guide/input-forms.md

@ -12,6 +12,10 @@ to validate its input on the server-side (Check the [Validating Input](input-val
When creating model-based forms, the first step is to define the model itself. The model can be either based upon
an [Active Record](db-active-record.md) class, representing some data from the database, or a generic Model class
(extending from [[yii\base\Model]]) to capture arbitrary input, for example a login form.
> Tip: If the form fields are different from database columns or there are formatting and logic that is specific to that
> form only, prefer creating a separate model extended from [[yii\base\Model]].
In the following example, we show how a generic model can be used for a login form:
```php
@ -144,7 +148,7 @@ or by retrieval from the DB:
```php
$items = Category::find()
->select(['id', 'label'])
->select(['label'])
->indexBy('id')
->column();
```

3
docs/guide/runtime-logging.md

@ -340,6 +340,9 @@ sending the content of the [[yii\log\Target::messages]] array to a designated me
[[yii\log\Target::formatMessage()]] method to format each message. For more details, you may refer to any of the
log target classes included in the Yii release.
> Tip: Instead of creating your own loggers you may try any PSR-3 compatible logger such
as [Monolog](https://github.com/Seldaek/monolog) by using
[PSR log target extension](https://github.com/samdark/yii2-psr-log-target).
## Performance Profiling <span id="performance-profiling"></span>

44
docs/guide/security-best-practices.md

@ -2,6 +2,9 @@ Security best practices
=======================
Below we'll review common security principles and describe how to avoid threats when developing applications using Yii.
Most of these priciples are not unique to Yii alone but apply to website or software development in general,
so we you will also find links for further reading on the general ideas behind these.
Basic principles
----------------
@ -28,6 +31,11 @@ if (!in_array($sortBy, ['title', 'created_at', 'status'])) {
In Yii, most probably you'll use [form validation](input-validation.md) to do alike checks.
Further reading on the topic:
- <https://www.owasp.org/index.php/Data_Validation>
- <https://www.owasp.org/index.php/Input_Validation_Cheat_Sheet>
### Escape output
@ -36,6 +44,13 @@ should escape `<`, `>` and alike special characters. In context of JavaScript or
Since it's error-prone to escape everything manually Yii provides various tools to perform escaping for different
contexts.
Further reading on the topic:
- <https://www.owasp.org/index.php/Command_Injection>
- <https://www.owasp.org/index.php/Code_Injection>
- <https://www.owasp.org/index.php/Cross-site_Scripting_%28XSS%29>
Avoiding SQL injections
-----------------------
@ -100,6 +115,10 @@ $rowCount = $connection->createCommand($sql)->queryScalar();
You can get details about the syntax in [Quoting Table and Column Names](db-dao.md#quoting-table-and-column-names).
Further reading on the topic:
- <https://www.owasp.org/index.php/SQL_Injection>
Avoiding XSS
------------
@ -130,6 +149,11 @@ If it should be HTML we could get some help from HtmlPurifier:
Note that HtmlPurifier processing is quite heavy so consider adding caching.
Further reading on the topic:
- <https://www.owasp.org/index.php/Cross-site_Scripting_%28XSS%29>
Avoiding CSRF
-------------
@ -185,6 +209,10 @@ class SiteController extends Controller
}
```
Further reading on the topic:
- <https://www.owasp.org/index.php/CSRF>
Avoiding file exposure
----------------------
@ -195,33 +223,41 @@ environments it could be impossible to achieve so we'll end up with all the code
If it's the case don't forget to deny access to everything except `web`. If it can't be done consider hosting your
application elsewhere.
Avoiding debug info and tools at production
Avoiding debug info and tools in production
-------------------------------------------
In debug mode Yii shows quite verbose errors which are certainly helpful for development. The thing is that these
verbose errors are handy for attacker as well since these could reveal database structure, configuration values and
parts of your code. Never run production applications with `YII_DEBUG` set to `true` in your `index.php`.
You should never enable Gii at production. It could be used to get information about database structure, code and to
You should never enable Gii or the Debug toolbar in production. It could be used to get information about database structure, code and to
simply rewrite code with what's generated by Gii.
Debug toolbar should be avoided at production unless really necessary. It exposes all the application and config
details possible. If you absolutely need it check twice that access is properly restricted to your IP only.
Further reading on the topic:
- <https://www.owasp.org/index.php/Exception_Handling>
- <https://www.owasp.org/index.php/Top_10_2007-Information_Leakage>
Using secure connection over TLS
--------------------------------
Yii provides features that rely on cookies and/or PHP sessions. These can be vulnerable in case your connection is
compromised. The risk is reduced if the app uses secure connection via TLS.
compromised. The risk is reduced if the app uses secure connection via TLS (often referred to as [SSL](https://en.wikipedia.org/wiki/Transport_Layer_Security)).
Please refer to your webserver documentation for instructions on how to configure it. You may also check example configs
provided by H5BP project:
provided by the H5BP project:
- [Nginx](https://github.com/h5bp/server-configs-nginx)
- [Apache](https://github.com/h5bp/server-configs-apache).
- [IIS](https://github.com/h5bp/server-configs-iis).
- [Lighttpd](https://github.com/h5bp/server-configs-lighttpd).
Secure Server configuration
---------------------------

3
docs/guide/tutorial-core-validators.md

@ -677,3 +677,6 @@ This validator checks if the input value is a valid URL.
Defaults to `false`. Note that in order to use IDN validation you have to install and enable the `intl` PHP
extension, otherwise an exception would be thrown.
> Note: The validator checks that URL scheme and host part is correct. It does NOT check the remaining parts of a URL
and is NOT designed to protect against XSS or any other attacks. See [Security best practices](security-best-practices.md)
article to learn more about threats prevention when developing applications.

5
docs/guide/tutorial-performance-tuning.md

@ -187,6 +187,11 @@ by executing the following command:
composer dumpautoload -o
```
Additionally you may consider using
[authoritative class maps](https://getcomposer.org/doc/articles/autoloader-optimization.md#optimization-level-2-a-authoritative-class-maps)
and [APCu cache](https://getcomposer.org/doc/articles/autoloader-optimization.md#optimization-level-2-b-apcu-cache).
Note that both opmizations may or may not be suitable for your particular case.
## Processing Data Offline <span id="processing-data-offline"></span>

55
framework/CHANGELOG.md

@ -24,19 +24,57 @@ Yii Framework 2 Change Log
2.0.12 under development
--------------------------
- Bug #13306: Wildcard in `reloadableScripts` in `yii.js` allows 0 characters (arogachev)
- Enh #13523: Plural rule for pasta (developeruz)
- Bug #13538: Fixed `yii\db\BaseActiveRecord::deleteAll()` changes method signature declared by `yii\db\ActiveRecordInterface::deleteAll()` (klimov-paul)
- Bug: Fixed `yii\db\Connection::useMaster()` - Exception within callback completely disables slaves (Vovan-VE)
- Enh #13278: `yii\caching\DbQueryDependency` created allowing specification of the cache dependency via `yii\db\QueryInterface` (klimov-paul)
- Enh #13820: Add new HTTP status code 451 (yyxx9988)
- Bug #13671: Fixed error handler trace to work correctly with XDebug (samdark)
- Bug #13657: Fixed `yii\helpers\StringHelper::truncateHtml()` skip extra tags at the end (sam002)
- Bug #7946: Fixed a bug when the `form` attribute was not propagated to the hidden input of the checkbox (Kolyunya)
- Bug #13087: Fixed getting active validators for safe attribute (developeruz)
- Bug #13571: Fix `yii\db\mssql\QueryBuilder::checkIntegrity` for all tables (boboldehampsink)
- Bug #11230: Include `defaultRoles` in `yii\rbac\DbManager->getRolesByUser()` results (developeruz)
- Bug #13343: Fixed `yii\i18n\Formatter::asTime()` to process time-only values without time zone conversion (bizley)
- Enh #13243: Added support for unicode attribute names in `yii\widgets\DetailView` (arogachev)
- Bug #11404: `yii\base\Model::loadMultiple()` returns true even if `yii\base\Model::load()` returns false (zvook)
- Bug #13306: Wildcard in `reloadableScripts` in `yii.js` allows 0 characters (arogachev)
- Bug #13340: Fixed `yii\db\Connection::useMaster()` - Exception within callback completely disables slaves (Vovan-VE)
- Bug #13343: Fixed `yii\i18n\Formatter::asTime()` to process time-only values without time zone conversion (bizley)
- Bug #13418: Fixed `QueryBuilder::batchInsert()` if $rows is `\Generator` (lav45)
- Bug #13494: Fixed `yii\console\controllers\MessageConstroller::saveMessagesToDb()` to work on different DBMS correctly (silverfire)
- Bug #13513: Fixed RBAC migration to work correctly on Oracle DBMS (silverfire)
- Enh #13550: Refactored unset call order in `yii\di\ServiceLocator::set()` (Lanrik)
- Bug #13537: Fixed `yii\web\CacheSession::destroySession()` to work correctly when session is not written yet (silverfire, papalapa)
- Bug #13538: Fixed `yii\db\BaseActiveRecord::deleteAll()` changes method signature declared by `yii\db\ActiveRecordInterface::deleteAll()` (klimov-paul)
- Bug #13577: `yii\db\QueryBuilder::truncateTable` should work consistent over all databases (boboldehampsink)
- Bug #13582: PK column in `yii\db\pgsql\QueryBuilder::resetSequence()` was not quoted properly (boboldehampsink)
- Bug #13592: Fixes Oracle’s `yii\db\oci\Schema::setTransactionIsolationLevel()` (sergeymakinen)
- Bug #13594: Fixes insufficient quoting in `yii\db\QueryBuilder::prepareInsertSelectSubQuery()` (sergeymakinen)
- Bug #8120: Fixes LIKE special characters escaping for Cubrid/MSSQL/Oracle/SQLite in `yii\db\QueryBuilder` (sergeymakinen)
- Bug #12715: Exception `SAVEPOINT LEVEL1 does not exist` instead of deadlock exception (Vovan-VE)
- Enh #8641: Enhanced `yii\console\Request::resolve()` to prevent passing parameters, that begin from digits (silverfire)
- Enh #13278: `yii\caching\DbQueryDependency` created allowing specification of the cache dependency via `yii\db\QueryInterface` (klimov-paul)
- Enh #13467: `yii\data\ActiveDataProvider` no longer queries models if models count is zero (kLkA, Kolyunya)
- Enh #13523: Plural rule for pasta (developeruz)
- Enh #13550: Refactored unset call order in `yii\di\ServiceLocator::set()` (Lanrik)
- Enh #13576: Added support of `srcset` to `yii\helpers\Html::img()` (Kolyunya)
- Enh #13577: Implemented `yii\db\mssql\QueryBuilder::resetSequence()` (boboldehampsink)
- Enh #13582: Added tests for all `yii\db\QueryBuilder::resetSequence` implementations, fixed SQLite implementation (boboldehampsink)
- Enh #13407: Added URL-safe base64 encode/decode methods to `StringHelper` (andrewnester)
- Bug #13649: Fixes issue where `['uncheck' => false]` and `['label' => false]` options for `ActiveRadio` and `ActiveCheckbox` were ignored (Alex-Code)
- Enh #13221: Make `\yii\db\QueryTrait::limit()` and `\yii\db\QueryTrait::offset()` methods work with `\yii\db\Expression` (Ni-san)
- Enh #13144: Refactored `yii\db\Query::queryScalar()` (Alex-Code)
- Enh #13360: Added Dockerized test setup for the framework tests (schmunk42)
- Bug #13379: Fixed `applyFilter` function in `yii.gridView.js` to work correctly when params in `filterUrl` are indexed (SilverFire, arogachev)
- Enh #13650: Improved `yii\base\Security::hkdf()` to take advantage of native `hash_hkdf()` implementation in PHP >= 7.1.2 (charlesportwoodii)
- Bug #13670: Fixed alias option from console when it includes `-` or `_` in option name (pana1990)
- Enh: Added `yii\di\Instance::__set_state()` method to restore object after serialization using `var_export()` function (silvefire)
- Enh #13695: `\yii\web\Response::setStatusCode()` method now returns the Response object itself (kyle-mccarthy)
- Bug #13707: Fixed `\yii\web\ErrorHandler` and `\yii\web\ErrorAction` not setting correct response code to response object before rendering error view (samdark)
- Enh #13698: `yii\grid\DataColumn` filter is automatically generated as dropdown list in case of `format` set to `boolean` (bizley)
- Enh #13254: Core validators no longer require Yii::$app to be set (sammousa)
- Bug #4408: Add support for unicode word characters and `+` character in attribute names (sammousa, kmindi)
- Bug #10372: Fixed console controller including complex typed arguments in help (sammousa)
- Bug #13738: Fixed `getQueryParams()` method in `yii.js` to correctly parse URL with question mark and no query parameters (vladdnepr)
- Bug #13776: Fixed setting precision and scale for decimal columns in MSSQL (arturf)
- Bug #13704: Fixed `yii\validators\UniqueValidator` to prefix attribute name with model's database table name (vladis84)
- Enh #13823: Refactored migrations template (Kolyunya)
- Bug #13822: Fixed `yii\web\User::loginRequired()` to throw an `UnauthorizedHttpException` instead of a `ForbiddenHttpException` (Kolyunya)
2.0.11.2 February 08, 2017
--------------------------
@ -181,7 +219,6 @@ Yii Framework 2 Change Log
- Enh: Added support for field `yii\console\controllers\BaseMigrateController::$migrationNamespaces` setup from CLI (schmunk42)
- Chg #11906: Updated `yii\widgets\MaskedInput` inputmask dependency to `~3.3.3` (samdark)
2.0.10 October 20, 2016
-----------------------

6
framework/UPGRADE.md

@ -64,6 +64,12 @@ Upgrade from Yii 2.0.11
* `yii\i18n\Formatter::normalizeDatetimeValue()` returns now array with additional third boolean element
indicating whether the timestamp has date information or it is just time value.
* `yii\grid\DataColumn` filter is now automatically generated as dropdown list with localized `Yes` and `No` strings
in case of `format` being set to `boolean`.
* `yii\web\User::loginRequired()` now throws an `UnauthorizedHttpException` instead of a `ForbiddenHttpException`.
Check and adjust your try-catch blocks.
Upgrade from Yii 2.0.10
-----------------------

2
framework/assets/yii.activeForm.js

@ -107,7 +107,7 @@
* function (event)
* where
* - event: an Event object.
*/
*/
afterInit: 'afterInit'
};

2
framework/assets/yii.gridView.js

@ -126,7 +126,7 @@
var namesInFilter = Object.keys(data);
$.each(yii.getQueryParams(settings.filterUrl), function (name, value) {
if (namesInFilter.indexOf(name) === -1 && namesInFilter.indexOf(name.replace(/\[\]$/, '')) === -1) {
if (namesInFilter.indexOf(name) === -1 && namesInFilter.indexOf(name.replace(/\[\d*\]$/, '')) === -1) {
if (!$.isArray(value)) {
value = [value];
}

6
framework/assets/yii.js

@ -273,8 +273,10 @@ window.yii = (function ($) {
return {};
}
var pairs = url.substring(pos + 1).split('#')[0].split('&'),
params = {};
var pairs = $.grep(url.substring(pos + 1).split('#')[0].split('&'), function (value) {
return value !== '';
});
var params = {};
for (var i = 0, len = pairs.length; i < len; i++) {
var pair = pairs[i].split('=');

2
framework/base/ErrorHandler.php

@ -316,7 +316,7 @@ abstract class ErrorHandler extends Component
/**
* Converts an exception into a simple string.
* @param \Exception $exception the exception being converted
* @param \Exception|\Error $exception the exception being converted
* @return string the string representation of the exception.
*/
public static function convertExceptionToString($exception)

16
framework/base/Event.php

@ -211,13 +211,15 @@ class Event extends Object
);
foreach ($classes as $class) {
if (!empty(self::$_events[$name][$class])) {
foreach (self::$_events[$name][$class] as $handler) {
$event->data = $handler[1];
call_user_func($handler[0], $event);
if ($event->handled) {
return;
}
if (empty(self::$_events[$name][$class])) {
continue;
}
foreach (self::$_events[$name][$class] as $handler) {
$event->data = $handler[1];
call_user_func($handler[0], $event);
if ($event->handled) {
return;
}
}
}

2
framework/base/Model.php

@ -424,7 +424,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab
$validators = [];
$scenario = $this->getScenario();
foreach ($this->getValidators() as $validator) {
if ($validator->isActive($scenario) && ($attribute === null || in_array($attribute, $validator->attributes, true))) {
if ($validator->isActive($scenario) && ($attribute === null || in_array($attribute, $validator->getAttributeNames(), true))) {
$validators[] = $validator;
}
}

12
framework/base/Security.php

@ -273,6 +273,14 @@ class Security extends Component
*/
public function hkdf($algo, $inputKey, $salt = null, $info = null, $length = 0)
{
if (function_exists('hash_hkdf')) {
$outputKey = hash_hkdf($algo, $inputKey, $length, $info, $salt);
if ($outputKey === false) {
throw new InvalidParamException('Invalid parameters to hash_hkdf()');
}
return $outputKey;
}
$test = @hash_hmac($algo, '', '', true);
if (!$test) {
throw new InvalidArgumentException('Failed to generate HMAC with hash algorithm: ' . $algo);
@ -559,9 +567,7 @@ class Security extends Component
}
$bytes = $this->generateRandomKey($length);
// '=' character(s) returned by base64_encode() are always discarded because
// they are guaranteed to be after position $length in the base64_encode() output.
return strtr(substr(base64_encode($bytes), 0, $length), '+/', '_-');
return substr(StringHelper::base64UrlEncode($bytes), 0, $length);
}
/**

2
framework/base/Widget.php

@ -257,7 +257,7 @@ class Widget extends Component implements ViewContextInterface
return dirname($class->getFileName()) . DIRECTORY_SEPARATOR . 'views';
}
/**
* This method is invoked right before the widget is executed.
*

8
framework/behaviors/SluggableBehavior.php

@ -65,12 +65,14 @@ class SluggableBehavior extends AttributeBehavior
*/
public $slugAttribute = 'slug';
/**
* @var string|array the attribute or list of attributes whose value will be converted into a slug
* @var string|array|null the attribute or list of attributes whose value will be converted into a slug
* or `null` meaning that the `$value` property will be used to generate a slug.
*/
public $attribute;
/**
* @var string|callable the value that will be used as a slug. This can be an anonymous function
* or an arbitrary value. If the former, the return value of the function will be used as a slug.
* @var callable|string|null the value that will be used as a slug. This can be an anonymous function
* or an arbitrary value or null. If the former, the return value of the function will be used as a slug.
* If `null` then the `$attribute` property will be used to generate a slug.
* The signature of the function should be as follows,
*
* ```php

2
framework/caching/Cache.php

@ -522,7 +522,7 @@ abstract class Cache extends Component implements \ArrayAccess
$value = call_user_func($closure, $this);
if (!$this->set($key, $value, $duration, $dependency)) {
Yii::warning('Failed to set cache value for key ' . json_encode($value), __METHOD__);
Yii::warning('Failed to set cache value for key ' . json_encode($key), __METHOD__);
}
return $value;

3
framework/console/Controller.php

@ -467,6 +467,9 @@ class Controller extends \yii\base\Controller
/** @var \ReflectionParameter $reflection */
foreach ($method->getParameters() as $i => $reflection) {
if ($reflection->getClass() !== null) {
continue;
}
$name = $reflection->getName();
$tag = isset($params[$i]) ? $params[$i] : '';
if (preg_match('/^(\S+)\s+(\$\w+\s+)?(.*)/s', $tag, $matches)) {

9
framework/console/Request.php

@ -53,6 +53,7 @@ class Request extends \yii\base\Request
/**
* Resolves the current request into a route and the associated parameters.
* @return array the first element is the route, and the second is the associated parameters.
* @throws Exception when parameter is wrong and can not be resolved
*/
public function resolve()
{
@ -75,12 +76,16 @@ class Request extends \yii\base\Request
$params[] = $param;
} elseif ($param === '--') {
$endOfOptionsFound = true;
} elseif (preg_match('/^--(\w+)(?:=(.*))?$/', $param, $matches)) {
} elseif (preg_match('/^--([\w-]+)(?:=(.*))?$/', $param, $matches)) {
$name = $matches[1];
if (is_numeric(substr($name, 0, 1))) {
throw new Exception('Parameter "' . $name . '" is not valid');
}
if ($name !== Application::OPTION_APPCONFIG) {
$params[$name] = isset($matches[2]) ? $matches[2] : true;
}
} elseif (preg_match('/^-(\w+)(?:=(.*))?$/', $param, $matches)) {
} elseif (preg_match('/^-([\w-]+)(?:=(.*))?$/', $param, $matches)) {
$name = $matches[1];
if (is_numeric($name)) {
$params[] = $param;

40
framework/console/controllers/MessageController.php

@ -443,23 +443,31 @@ EOD;
if (empty($obsolete)) {
$this->stdout("Nothing obsoleted...skipped.\n");
} else {
if ($removeUnused) {
$db->createCommand()
->delete($sourceMessageTable, ['in', 'id', $obsolete])
->execute();
$this->stdout("deleted.\n");
} elseif ($markUnused) {
$db->createCommand()
->update(
$sourceMessageTable,
['message' => new \yii\db\Expression("CONCAT('@@',message,'@@')")],
['in', 'id', $obsolete]
)->execute();
$this->stdout("updated.\n");
} else {
$this->stdout("kept untouched.\n");
return;
}
if ($removeUnused) {
$db->createCommand()
->delete($sourceMessageTable, ['in', 'id', $obsolete])
->execute();
$this->stdout("deleted.\n");
} elseif ($markUnused) {
$rows = (new Query)
->select(['id', 'message'])
->from($sourceMessageTable)
->where(['in', 'id', $obsolete])
->all($db);
foreach ($rows as $row) {
$db->createCommand()->update(
$sourceMessageTable,
['message' => '@@' . $row['message'] . '@@'],
['id' => $row['id']]
)->execute();
}
$this->stdout("updated.\n");
} else {
$this->stdout("kept untouched.\n");
}
}

3
framework/data/ActiveDataProvider.php

@ -104,6 +104,9 @@ class ActiveDataProvider extends BaseDataProvider
$query = clone $this->query;
if (($pagination = $this->getPagination()) !== false) {
$pagination->totalCount = $this->getTotalCount();
if ($pagination->totalCount === 0) {
return [];
}
$query->limit($pagination->getLimit())->offset($pagination->getOffset());
}
if (($sort = $this->getSort()) !== false) {

8
framework/db/ActiveQuery.php

@ -330,14 +330,16 @@ class ActiveQuery extends Query implements ActiveQueryInterface
*/
protected function queryScalar($selectExpression, $db)
{
if ($this->sql === null) {
return parent::queryScalar($selectExpression, $db);
}
/* @var $modelClass ActiveRecord */
$modelClass = $this->modelClass;
if ($db === null) {
$db = $modelClass::getDb();
}
if ($this->sql === null) {
return parent::queryScalar($selectExpression, $db);
}
return (new Query)->select([$selectExpression])
->from(['c' => "({$this->sql})"])
->params($this->params)

4
framework/db/ActiveRecord.php

@ -205,7 +205,7 @@ class ActiveRecord extends BaseActiveRecord
*
* ```php
* $models = Customer::find()->where('status = 2')->all();
* foreach($models as $model) {
* foreach ($models as $model) {
* $model->status = 1;
* $model->update(false); // skipping validation as no user input is involved
* }
@ -276,7 +276,7 @@ class ActiveRecord extends BaseActiveRecord
*
* ```php
* $models = Customer::find()->where('status = 3')->all();
* foreach($models as $model) {
* foreach ($models as $model) {
* $model->delete();
* }
* ```

2
framework/db/Command.php

@ -66,7 +66,7 @@ class Command extends Component
public $pdoStatement;
/**
* @var int the default fetch mode for this command.
* @see http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php
* @see http://www.php.net/manual/en/pdostatement.setfetchmode.php
*/
public $fetchMode = \PDO::FETCH_ASSOC;
/**

28
framework/db/Connection.php

@ -713,14 +713,10 @@ class Connection extends Component
$transaction->commit();
}
} catch (\Exception $e) {
if ($transaction->isActive && $transaction->level === $level) {
$transaction->rollBack();
}
$this->rollbackTransactionOnLevel($transaction, $level);
throw $e;
} catch (\Throwable $e) {
if ($transaction->isActive && $transaction->level === $level) {
$transaction->rollBack();
}
$this->rollbackTransactionOnLevel($transaction, $level);
throw $e;
}
@ -728,6 +724,26 @@ class Connection extends Component
}
/**
* Rolls back given [[Transaction]] object if it's still active and level match.
* In some cases rollback can fail, so this method is fail safe. Exception thrown
* from rollback will be caught and just logged with [[\Yii::error()]].
* @param Transaction $transaction Transaction object given from [[beginTransaction()]].
* @param int $level Transaction level just after [[beginTransaction()]] call.
*/
private function rollbackTransactionOnLevel($transaction, $level)
{
if ($transaction->isActive && $transaction->level === $level) {
// https://github.com/yiisoft/yii2/pull/13347
try {
$transaction->rollBack();
} catch (\Exception $e) {
\Yii::error($e, __METHOD__);
// hide this exception to be able to continue throwing original exception outside
}
}
}
/**
* Returns the schema information for the database opened by this connection.
* @return Schema the schema information for the database opened by this connection.
* @throws NotSupportedException if there is no support for the current driver type

28
framework/db/Query.php

@ -408,19 +408,6 @@ class Query extends Component implements QueryInterface
return null;
}
$select = $this->select;
$limit = $this->limit;
$offset = $this->offset;
$this->select = [$selectExpression];
$this->limit = null;
$this->offset = null;
$command = $this->createCommand($db);
$this->select = $select;
$this->limit = $limit;
$this->offset = $offset;
if (
!$this->distinct
&& empty($this->groupBy)
@ -428,11 +415,24 @@ class Query extends Component implements QueryInterface
&& empty($this->union)
&& empty($this->orderBy)
) {
$select = $this->select;
$limit = $this->limit;
$offset = $this->offset;
$this->select = [$selectExpression];
$this->limit = null;
$this->offset = null;
$command = $this->createCommand($db);
$this->select = $select;
$this->limit = $limit;
$this->offset = $offset;
return $command->queryScalar();
} else {
return (new Query)->select([$selectExpression])
->from(['c' => $this])
->createCommand($command->db)
->createCommand($db)
->queryScalar();
}
}

33
framework/db/QueryBuilder.php

@ -65,7 +65,22 @@ class QueryBuilder extends \yii\base\Object
'EXISTS' => 'buildExistsCondition',
'NOT EXISTS' => 'buildExistsCondition',
];
/**
* @var array map of chars to their replacements in LIKE conditions.
* By default it's configured to escape `%`, `_` and `\` with `\`.
* @since 2.0.12.
*/
protected $likeEscapingReplacements = [
'%' => '\%',
'_' => '\_',
'\\' => '\\\\',
];
/**
* @var string|null character used to escape special characters in LIKE conditions.
* By default it's assumed to be `\`.
* @since 2.0.12
*/
protected $likeEscapeCharacter;
/**
* Constructor.
@ -253,6 +268,9 @@ class QueryBuilder extends \yii\base\Object
}
$values[] = '(' . implode(', ', $vs) . ')';
}
if (empty($values)) {
return '';
}
foreach ($columns as $i => $name) {
$columns[$i] = $schema->quoteColumnName($name);
@ -951,7 +969,7 @@ class QueryBuilder extends \yii\base\Object
*/
protected function hasLimit($limit)
{
return ctype_digit((string) $limit);
return ($limit instanceof Expression) || ctype_digit((string) $limit);
}
/**
@ -961,8 +979,7 @@ class QueryBuilder extends \yii\base\Object
*/
protected function hasOffset($offset)
{
$offset = (string) $offset;
return ctype_digit($offset) && $offset !== '0';
return ($offset instanceof Expression) || ctype_digit((string) $offset) && (string) $offset !== '0';
}
/**
@ -1347,7 +1364,7 @@ class QueryBuilder extends \yii\base\Object
throw new InvalidArgumentException("Operator '$operator' requires two operands.");
}
$escape = isset($operands[2]) ? $operands[2] : ['%' => '\%', '_' => '\_', '\\' => '\\\\'];
$escape = isset($operands[2]) ? $operands[2] : $this->likeEscapingReplacements;
unset($operands[2]);
if (!preg_match('/^(AND |OR |)(((NOT |))I?LIKE)/', $operator, $matches)) {
@ -1382,7 +1399,11 @@ class QueryBuilder extends \yii\base\Object
$phName = self::PARAM_PREFIX . count($params);
$params[$phName] = empty($escape) ? $value : ('%' . strtr($value, $escape) . '%');
}
$parts[] = "$column $operator $phName";
$escapeSql = '';
if ($this->likeEscapeCharacter !== null) {
$escapeSql = " ESCAPE '{$this->likeEscapeCharacter}'";
}
$parts[] = "{$column} {$operator} {$phName}{$escapeSql}";
}
return implode($andor, $parts);

11
framework/db/QueryTrait.php

@ -27,12 +27,13 @@ trait QueryTrait
*/
public $where;
/**
* @var int maximum number of records to be returned. If not set or less than 0, it means no limit.
* @var int|Expression maximum number of records to be returned. May be an instance of [[Expression]].
* If not set or less than 0, it means no limit.
*/
public $limit;
/**
* @var int zero-based offset from where the records are to be returned. If not set or
* less than 0, it means starting from the beginning.
* @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.
*/
public $offset;
/**
@ -375,7 +376,7 @@ trait QueryTrait
/**
* Sets the LIMIT part of the query.
* @param int $limit the limit. Use null or negative value to disable limit.
* @param int|Expression $limit the limit. Use null or negative value to disable limit.
* @return $this the query object itself
*/
public function limit($limit)
@ -386,7 +387,7 @@ trait QueryTrait
/**
* Sets the OFFSET part of the query.
* @param int $offset the offset. Use null or negative value to disable offset.
* @param int|Expression $offset the offset. Use null or negative value to disable offset.
* @return $this the query object itself
*/
public function offset($offset)

2
framework/db/Schema.php

@ -438,7 +438,7 @@ abstract class Schema extends Object
*/
public function setTransactionIsolationLevel($level)
{
$this->db->createCommand("SET TRANSACTION ISOLATION LEVEL $level;")->execute();
$this->db->createCommand("SET TRANSACTION ISOLATION LEVEL $level")->execute();
}
/**

47
framework/db/ViewFinderTrait.php

@ -0,0 +1,47 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db;
/**
* ViewFinderTrait implements the method getViewNames for finding views in a database.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Bob Olde Hampsink <b.oldehampsink@nerds.company>
* @since 2.0.12
*/
trait ViewFinderTrait
{
/**
* @var array list of ALL view names in the database
*/
private $_viewNames = [];
/**
* Returns all views names in the database.
* @param string $schema the schema of the views. Defaults to empty string, meaning the current or default schema.
* @return array all views names in the database. The names have NO schema name prefix.
*/
abstract protected function findViewNames($schema = '');
/**
* Returns all view names in the database.
* @param string $schema the schema of the views. Defaults to empty string, meaning the current or default schema name.
* If not empty, the returned view names will be prefixed with the schema name.
* @param bool $refresh whether to fetch the latest available view names. If this is false,
* view names fetched previously (if available) will be returned.
* @return string[] all view names in the database.
*/
public function getViewNames($schema = '', $refresh = false)
{
if (!isset($this->_viewNames[$schema]) || $refresh) {
$this->_viewNames[$schema] = $this->findViewNames($schema);
}
return $this->_viewNames[$schema];
}
}

12
framework/db/cubrid/QueryBuilder.php

@ -44,6 +44,18 @@ class QueryBuilder extends \yii\db\QueryBuilder
Schema::TYPE_MONEY => 'decimal(19,4)',
];
/**
* @inheritdoc
*/
protected $likeEscapeCharacter = '!';
/**
* @inheritdoc
*/
protected $likeEscapingReplacements = [
'%' => '!%',
'_' => '!_',
'!' => '!!',
];
/**
* Creates a SQL statement for resetting the sequence value of a table's primary key.

66
framework/db/mssql/QueryBuilder.php

@ -35,7 +35,7 @@ class QueryBuilder extends \yii\db\QueryBuilder
Schema::TYPE_BIGINT => 'bigint',
Schema::TYPE_FLOAT => 'float',
Schema::TYPE_DOUBLE => 'float',
Schema::TYPE_DECIMAL => 'decimal',
Schema::TYPE_DECIMAL => 'decimal(18,0)',
Schema::TYPE_DATETIME => 'datetime',
Schema::TYPE_TIMESTAMP => 'datetime',
Schema::TYPE_TIME => 'time',
@ -45,6 +45,16 @@ class QueryBuilder extends \yii\db\QueryBuilder
Schema::TYPE_MONEY => 'decimal(19,4)',
];
/**
* @inheritdoc
*/
protected $likeEscapingReplacements = [
'%' => '[%]',
'_' => '[_]',
'[' => '[[]',
']' => '[]]',
'\\' => '[\\]',
];
/**
* @inheritdoc
@ -168,25 +178,57 @@ class QueryBuilder extends \yii\db\QueryBuilder
}
/**
* Creates a SQL statement for resetting the sequence value of a table's primary key.
* The sequence will be reset such that the primary key of the next new row inserted
* will have the specified value or 1.
* @param string $tableName the name of the table whose primary key sequence will be reset
* @param mixed $value the value for the primary key of the next new row inserted. If this is not set,
* the next new row's primary key will have a value 1.
* @return string the SQL statement for resetting sequence
* @throws InvalidParamException if the table does not exist or there is no sequence associated with the table.
*/
public function resetSequence($tableName, $value = null)
{
$table = $this->db->getTableSchema($tableName);
if ($table !== null && $table->sequenceName !== null) {
$tableName = $this->db->quoteTableName($tableName);
if ($value === null) {
$key = $this->db->quoteColumnName(reset($table->primaryKey));
$value = "(SELECT COALESCE(MAX({$key}),0) FROM {$tableName})+1";
} else {
$value = (int) $value;
}
return "DBCC CHECKIDENT ('{$tableName}', RESEED, {$value})";
} elseif ($table === null) {
throw new InvalidParamException("Table not found: $tableName");
} else {
throw new InvalidParamException("There is not sequence associated with table '$tableName'.");
}
}
/**
* Builds a SQL statement for enabling or disabling integrity check.
* @param bool $check whether to turn on or off the integrity check.
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
* @param string $table the table name. Defaults to empty string, meaning that no table will be changed.
* @param string $schema the schema of the tables.
* @param string $table the table name.
* @return string the SQL statement for checking integrity
* @throws InvalidArgumentException if the table does not exist or there is no sequence associated with the table.
*/
public function checkIntegrity($check = true, $schema = '', $table = '')
{
if ($schema !== '') {
$table = "{$schema}.{$table}";
}
$table = $this->db->quoteTableName($table);
if ($this->db->getTableSchema($table) === null) {
throw new InvalidArgumentException("Table not found: $table");
}
$enable = $check ? 'CHECK' : 'NOCHECK';
$schema = $schema ? $schema : $this->db->getSchema()->defaultSchema;
$tableNames = $this->db->getTableSchema($table) ? [$table] : $this->db->getSchema()->getTableNames($schema);
$viewNames = $this->db->getSchema()->getViewNames($schema);
$tableNames = array_diff($tableNames, $viewNames);
$command = '';
foreach ($tableNames as $tableName) {
$tableName = $this->db->quoteTableName("{$schema}.{$tableName}");
$command .= "ALTER TABLE $tableName $enable CONSTRAINT ALL; ";
}
return "ALTER TABLE {$table} {$enable} CONSTRAINT ALL";
return $command;
}
/**

22
framework/db/mssql/Schema.php

@ -8,6 +8,7 @@
namespace yii\db\mssql;
use yii\db\ColumnSchema;
use yii\db\ViewFinderTrait;
/**
* Schema is the class for retrieving metadata from a MS SQL Server databases (version 2008 and above).
@ -17,6 +18,8 @@ use yii\db\ColumnSchema;
*/
class Schema extends \yii\db\Schema
{
use ViewFinderTrait;
/**
* @var string the default schema used for the current session.
*/
@ -412,6 +415,25 @@ SQL;
}
/**
* @inheritdoc
*/
protected function findViewNames($schema = '')
{
if ($schema === '') {
$schema = $this->defaultSchema;
}
$sql = <<<SQL
SELECT [t].[table_name]
FROM [INFORMATION_SCHEMA].[TABLES] AS [t]
WHERE [t].[table_schema] = :schema AND [t].[table_type] = 'VIEW'
ORDER BY [t].[table_name]
SQL;
return $this->db->createCommand($sql, [':schema' => $schema])->queryColumn();
}
/**
* Returns all unique indexes for the given table.
* Each array element is of the following structure:
*

19
framework/db/mysql/QueryBuilder.php

@ -185,6 +185,25 @@ class QueryBuilder extends \yii\db\QueryBuilder
/**
* @inheritdoc
*/
protected function hasLimit($limit)
{
// In MySQL limit argument must be nonnegative integer constant
return ctype_digit((string) $limit);
}
/**
* @inheritdoc
*/
protected function hasOffset($offset)
{
// In MySQL offset argument must be nonnegative integer constant
$offset = (string) $offset;
return ctype_digit($offset) && $offset !== '0';
}
/**
* @inheritdoc
*/
public function insert($table, $columns, &$params)
{
$schema = $this->db->getSchema();

32
framework/db/oci/QueryBuilder.php

@ -46,6 +46,20 @@ class QueryBuilder extends \yii\db\QueryBuilder
Schema::TYPE_MONEY => 'NUMBER(19,4)',
];
/**
* @inheritdoc
*/
protected $likeEscapeCharacter = '!';
/**
* `\` is initialized in [[buildLikeCondition()]] method since
* we need to choose replacement value based on [[\yii\db\Schema::quoteValue()]].
* @inheritdoc
*/
protected $likeEscapingReplacements = [
'%' => '!%',
'_' => '!_',
'!' => '!!',
];
/**
* @inheritdoc
@ -262,6 +276,9 @@ EOD;
}
$values[] = '(' . implode(', ', $vs) . ')';
}
if (empty($values)) {
return '';
}
foreach ($columns as $i => $name) {
$columns[$i] = $schema->quoteColumnName($name);
@ -299,4 +316,19 @@ EOD;
{
return 'COMMENT ON TABLE ' . $this->db->quoteTableName($table) . " IS ''";
}
/**
* @inheritDoc
*/
public function buildLikeCondition($operator, $operands, &$params)
{
if (!isset($this->likeEscapingReplacements['\\'])) {
/*
* Different pdo_oci8 versions may or may not implement PDO::quote(), so
* yii\db\Schema::quoteValue() may or may not quote \.
*/
$this->likeEscapingReplacements['\\'] = substr($this->db->quoteValue('\\'), 1, -1);
}
return parent::buildLikeCondition($operator, $operands, $params);
}
}

40
framework/db/oci/Schema.php

@ -122,13 +122,13 @@ class Schema extends \yii\db\Schema
protected function findColumns($table)
{
$sql = <<<SQL
SELECT
A.COLUMN_NAME,
A.DATA_TYPE,
A.DATA_PRECISION,
A.DATA_SCALE,
SELECT
A.COLUMN_NAME,
A.DATA_TYPE,
A.DATA_PRECISION,
A.DATA_SCALE,
A.DATA_LENGTH,
A.NULLABLE,
A.NULLABLE,
A.DATA_DEFAULT,
COM.COMMENTS AS COLUMN_COMMENT
FROM ALL_TAB_COLUMNS A
@ -175,11 +175,11 @@ SQL;
{
$sequenceNameSql = <<<SQL
SELECT
SELECT
UD.REFERENCED_NAME AS SEQUENCE_NAME
FROM USER_DEPENDENCIES UD
JOIN USER_TRIGGERS UT ON (UT.TRIGGER_NAME = UD.NAME)
WHERE
WHERE
UT.TABLE_NAME = :tableName
AND UD.TYPE = 'TRIGGER'
AND UD.REFERENCED_TYPE = 'SEQUENCE'
@ -330,7 +330,7 @@ SQL;
SELECT
USERNAME
FROM DBA_USERS U
WHERE
WHERE
EXISTS (SELECT 1 FROM DBA_OBJECTS O WHERE O.OWNER = U.USERNAME)
AND DEFAULT_TABLESPACE NOT IN ('SYSTEM','SYSAUX')
SQL;
@ -344,27 +344,27 @@ SQL;
{
if ($schema === '') {
$sql = <<<SQL
SELECT
TABLE_NAME
SELECT
TABLE_NAME
FROM USER_TABLES
UNION ALL
SELECT
VIEW_NAME AS TABLE_NAME
SELECT
VIEW_NAME AS TABLE_NAME
FROM USER_VIEWS
UNION ALL
SELECT
MVIEW_NAME AS TABLE_NAME
SELECT
MVIEW_NAME AS TABLE_NAME
FROM USER_MVIEWS
ORDER BY TABLE_NAME
SQL;
$command = $this->db->createCommand($sql);
} else {
$sql = <<<SQL
SELECT
SELECT
OBJECT_NAME AS TABLE_NAME
FROM ALL_OBJECTS
WHERE
OBJECT_TYPE IN ('TABLE', 'VIEW', 'MATERIALIZED VIEW')
WHERE
OBJECT_TYPE IN ('TABLE', 'VIEW', 'MATERIALIZED VIEW')
AND OWNER = :schema
ORDER BY OBJECT_NAME
SQL;
@ -400,12 +400,12 @@ SQL;
public function findUniqueIndexes($table)
{
$query = <<<SQL
SELECT
SELECT
DIC.INDEX_NAME,
DIC.COLUMN_NAME
FROM ALL_INDEXES DI
INNER JOIN ALL_IND_COLUMNS DIC ON DI.TABLE_NAME = DIC.TABLE_NAME AND DI.INDEX_NAME = DIC.INDEX_NAME
WHERE
WHERE
DI.UNIQUENESS = 'UNIQUE'
AND DIC.TABLE_OWNER = :schemaName
AND DIC.TABLE_NAME = :tableName

22
framework/db/pgsql/QueryBuilder.php

@ -164,8 +164,8 @@ class QueryBuilder extends \yii\db\QueryBuilder
$sequence = $this->db->quoteTableName($table->sequenceName);
$tableName = $this->db->quoteTableName($tableName);
if ($value === null) {
$key = reset($table->primaryKey);
$value = "(SELECT COALESCE(MAX(\"{$key}\"),0) FROM {$tableName})+1";
$key = $this->db->quoteColumnName(reset($table->primaryKey));
$value = "(SELECT COALESCE(MAX({$key}),0) FROM {$tableName})+1";
} else {
$value = (int) $value;
}
@ -188,14 +188,14 @@ class QueryBuilder extends \yii\db\QueryBuilder
public function checkIntegrity($check = true, $schema = '', $table = '')
{
$enable = $check ? 'ENABLE' : 'DISABLE';
$schema = $schema ? $schema : $this->db->getSchema()->defaultSchema;
$schema = $schema ?: $this->db->getSchema()->defaultSchema;
$tableNames = $table ? [$table] : $this->db->getSchema()->getTableNames($schema);
$viewNames = $this->db->getSchema()->getViewNames($schema);
$tableNames = array_diff($tableNames, $viewNames);
$command = '';
foreach ($tableNames as $tableName) {
$tableName = '"' . $schema . '"."' . $tableName . '"';
$tableName = $this->db->quoteTableName("{$schema}.{$tableName}");
$command .= "ALTER TABLE $tableName $enable TRIGGER ALL; ";
}
@ -206,6 +206,17 @@ class QueryBuilder extends \yii\db\QueryBuilder
}
/**
* Builds a SQL statement for truncating a DB table.
* Explicitly restarts identity for PGSQL to be consistent with other databases which all do this by default.
* @param string $table the table to be truncated. The name will be properly quoted by the method.
* @return string the SQL statement for truncating a DB table.
*/
public function truncateTable($table)
{
return 'TRUNCATE TABLE ' . $this->db->quoteTableName($table) . ' RESTART IDENTITY';
}
/**
* Builds a SQL statement for changing the definition of a column.
* @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.
* @param string $column the name of the column to be changed. The name will be properly quoted by the method.
@ -305,6 +316,9 @@ class QueryBuilder extends \yii\db\QueryBuilder
}
$values[] = '(' . implode(', ', $vs) . ')';
}
if (empty($values)) {
return '';
}
foreach ($columns as $i => $name) {
$columns[$i] = $schema->quoteColumnName($name);

31
framework/db/pgsql/Schema.php

@ -10,6 +10,7 @@ namespace yii\db\pgsql;
use yii\db\Expression;
use yii\db\TableSchema;
use yii\db\ColumnSchema;
use yii\db\ViewFinderTrait;
/**
* Schema is the class for retrieving metadata from a PostgreSQL database
@ -22,6 +23,8 @@ use yii\db\ColumnSchema;
*/
class Schema extends \yii\db\Schema
{
use ViewFinderTrait;
/**
* @var string the default schema used for the current session.
*/
@ -110,11 +113,6 @@ class Schema extends \yii\db\Schema
'xml' => self::TYPE_STRING,
];
/**
* @var array list of ALL view names in the database
*/
private $_viewNames = [];
/**
* Creates a query builder for the PostgreSQL database.
@ -212,10 +210,7 @@ SQL;
}
/**
* Returns all views names in the database.
* @param string $schema the schema of the views. Defaults to empty string, meaning the current or default schema.
* @return array all views names in the database. The names have NO schema name prefix.
* @since 2.0.9
* @inheritdoc
*/
protected function findViewNames($schema = '')
{
@ -233,24 +228,6 @@ SQL;
}
/**
* Returns all view names in the database.
* @param string $schema the schema of the views. Defaults to empty string, meaning the current or default schema name.
* If not empty, the returned view names will be prefixed with the schema name.
* @param bool $refresh whether to fetch the latest available view names. If this is false,
* view names fetched previously (if available) will be returned.
* @return string[] all view names in the database.
* @since 2.0.9
*/
public function getViewNames($schema = '', $refresh = false)
{
if (!isset($this->_viewNames[$schema]) || $refresh) {
$this->_viewNames[$schema] = $this->findViewNames($schema);
}
return $this->_viewNames[$schema];
}
/**
* Collects the foreign key column details for the given table.
* @param TableSchema $table the table metadata
*/

20
framework/db/sqlite/QueryBuilder.php

@ -48,6 +48,10 @@ class QueryBuilder extends \yii\db\QueryBuilder
Schema::TYPE_MONEY => 'decimal(19,4)',
];
/**
* @inheritdoc
*/
protected $likeEscapeCharacter = '\\';
/**
* Generates a batch INSERT SQL statement.
@ -106,6 +110,9 @@ class QueryBuilder extends \yii\db\QueryBuilder
}
$values[] = implode(', ', $vs);
}
if (empty($values)) {
return '';
}
foreach ($columns as $i => $name) {
$columns[$i] = $schema->quoteColumnName($name);
@ -130,20 +137,17 @@ class QueryBuilder extends \yii\db\QueryBuilder
$db = $this->db;
$table = $db->getTableSchema($tableName);
if ($table !== null && $table->sequenceName !== null) {
$tableName = $db->quoteTableName($tableName);
if ($value === null) {
$key = reset($table->primaryKey);
$tableName = $db->quoteTableName($tableName);
$key = $this->db->quoteColumnName(reset($table->primaryKey));
$value = $this->db->useMaster(function (Connection $db) use ($key, $tableName) {
return $db->createCommand("SELECT MAX('$key') FROM $tableName")->queryScalar();
return $db->createCommand("SELECT MAX($key) FROM $tableName")->queryScalar();
});
} else {
$value = (int) $value - 1;
}
try {
$db->createCommand("UPDATE sqlite_sequence SET seq='$value' WHERE name='{$table->name}'")->execute();
} catch (Exception $e) {
// it's possible that sqlite_sequence does not exist
}
return "UPDATE sqlite_sequence SET seq='$value' WHERE name='{$table->name}'";
} elseif ($table === null) {
throw new InvalidArgumentException("Table not found: $tableName");
} else {

20
framework/di/Instance.php

@ -130,7 +130,7 @@ class Instance
if ($reference instanceof self) {
try {
$component = $reference->get($container);
} catch(\ReflectionException $e) {
} catch (\ReflectionException $e) {
throw new InvalidConfigException('Failed to instantiate component or class "' . $reference->id . '".', 0, $e);
}
if ($type === null || $component instanceof $type) {
@ -161,4 +161,22 @@ class Instance
return Yii::$container->get($this->id);
}
}
/**
* Restores class state after using `var_export()`
*
* @param array $state
* @return Instance
* @throws InvalidConfigException when $state property does not contain `id` parameter
* @see var_export()
* @since 2.0.12
*/
public static function __set_state($state)
{
if (!isset($state['id'])) {
throw new InvalidConfigException('Failed to instantiate class "Instance". Required parameter "id" is missing');
}
return new self($state['id']);
}
}

6
framework/grid/DataColumn.php

@ -189,6 +189,12 @@ class DataColumn extends Column
if (is_array($this->filter)) {
$options = array_merge(['prompt' => ''], $this->filterInputOptions);
return Html::activeDropDownList($model, $this->attribute, $this->filter, $options) . $error;
} elseif ($this->format === 'boolean') {
$options = array_merge(['prompt' => ''], $this->filterInputOptions);
return Html::activeDropDownList($model, $this->attribute, [
$this->grid->formatter->booleanFormat[0],
$this->grid->formatter->booleanFormat[1],
], $options) . $error;
} else {
return Html::activeTextInput($model, $this->attribute, $this->filterInputOptions) . $error;
}

29
framework/helpers/BaseHtml.php

@ -24,6 +24,12 @@ use yii\base\Model;
*/
class BaseHtml
{
/**
* @var string Regular expression used for attribute name validation.
* @since 2.0.12
*/
public static $attributeRegex = '/(^|.*\])([\w\.\+]+)(\[.*|$)/u';
/**
* @var array list of void elements (element name => 1)
* @see http://www.w3.org/TR/html-markup/syntax.html#void-element
@ -59,6 +65,8 @@ class BaseHtml
'href',
'src',
'srcset',
'form',
'action',
'method',
@ -430,9 +438,10 @@ class BaseHtml
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* See [[renderTagAttributes()]] for details on how attributes are being rendered.
* @since 2.0.12 It is possible to pass the "srcset" option as an array which keys are
*
* Since version 2.0.12 It is possible to pass the `srcset` option as an array which keys are
* descriptors and values are URLs. All URLs will be processed by [[Url::to()]].
* @return string the generated image tag
* @return string the generated image tag.
*/
public static function img($src, $options = [])
{
@ -741,7 +750,11 @@ class BaseHtml
$value = array_key_exists('value', $options) ? $options['value'] : '1';
if (isset($options['uncheck'])) {
// add a hidden field so that if the checkbox is not selected, it still submits a value
$hidden = static::hiddenInput($name, $options['uncheck']);
$hiddenOptions = [];
if (isset($options['form'])) {
$hiddenOptions['form'] = $options['form'];
}
$hidden = static::hiddenInput($name, $options['uncheck'], $hiddenOptions);
unset($options['uncheck']);
} else {
$hidden = '';
@ -1476,9 +1489,13 @@ class BaseHtml
}
if (!array_key_exists('uncheck', $options)) {
$options['uncheck'] = '0';
} elseif ($options['uncheck'] === false) {
unset($options['uncheck']);
}
if (!array_key_exists('label', $options)) {
$options['label'] = static::encode($model->getAttributeLabel(static::getAttributeName($attribute)));
} elseif ($options['label'] === false) {
unset($options['label']);
}
$checked = "$value" === "{$options['value']}";
@ -2062,7 +2079,7 @@ class BaseHtml
*/
public static function getAttributeName($attribute)
{
if (preg_match('/(^|.*\])([\w\.]+)(\[.*|$)/', $attribute, $matches)) {
if (preg_match(static::$attributeRegex, $attribute, $matches)) {
return $matches[2];
} else {
throw new InvalidArgumentException('Attribute name must contain word characters only.');
@ -2085,7 +2102,7 @@ class BaseHtml
*/
public static function getAttributeValue($model, $attribute)
{
if (!preg_match('/(^|.*\])([\w\.]+)(\[.*|$)/', $attribute, $matches)) {
if (!preg_match(static::$attributeRegex, $attribute, $matches)) {
throw new InvalidArgumentException('Attribute name must contain word characters only.');
}
$attribute = $matches[2];
@ -2135,7 +2152,7 @@ class BaseHtml
public static function getInputName($model, $attribute)
{
$formName = $model->formName();
if (!preg_match('/(^|.*\])([\w\.]+)(\[.*|$)/', $attribute, $matches)) {
if (!preg_match(static::$attributeRegex, $attribute, $matches)) {
throw new InvalidArgumentException('Attribute name must contain word characters only.');
}
$prefix = $matches[1];

2
framework/helpers/BaseInflector.php

@ -51,6 +51,7 @@ class BaseInflector
'/us$/i' => 'uses',
'/(alias)$/i' => '\1es',
'/(ax|cris|test)is$/i' => '\1es',
'/(currenc)y$/' => '\1ies',
'/s$/' => 's',
'/^$/' => '',
'/$/' => 's',
@ -97,6 +98,7 @@ class BaseInflector
'/(n)ews$/i' => '\1\2ews',
'/(n)etherlands$/i' => '\1\2etherlands',
'/eaus$/' => 'eau',
'/(currenc)ies$/' => '\1y',
'/^(.*us)$/' => '\\1',
'/s$/i' => '',
];

55
framework/helpers/BaseStringHelper.php

@ -90,7 +90,7 @@ class BaseStringHelper
return '';
}
}
/**
* Truncates a string to the number of characters specified.
*
@ -107,14 +107,14 @@ class BaseStringHelper
if ($asHtml) {
return static::truncateHtml($string, $length, $suffix, $encoding ?: Yii::$app->charset);
}
if (mb_strlen($string, $encoding ?: Yii::$app->charset) > $length) {
return rtrim(mb_substr($string, 0, $length, $encoding ?: Yii::$app->charset)) . $suffix;
} else {
return $string;
}
}
/**
* Truncates a string to the number of words specified.
*
@ -138,7 +138,7 @@ class BaseStringHelper
return $string;
}
}
/**
* Truncate a string while preserving the HTML.
*
@ -157,13 +157,13 @@ class BaseStringHelper
$tokens = $lexer->tokenizeHTML($string, $config, new \HTMLPurifier_Context());
$openTokens = [];
$totalCount = 0;
$depth = 0;
$truncated = [];
foreach ($tokens as $token) {
if ($token instanceof \HTMLPurifier_Token_Start) { //Tag begins
if ($totalCount < $count) {
$openTokens[$token->name] = isset($openTokens[$token->name]) ? $openTokens[$token->name] + 1 : 1;
$truncated[] = $token;
}
$openTokens[$depth] = $token->name;
$truncated[] = $token;
++$depth;
} elseif ($token instanceof \HTMLPurifier_Token_Text && $totalCount <= $count) { //Text
if (false === $encoding) {
preg_match('/^(\s*)/um', $token->data, $prefixSpace) ?: $prefixSpace = ['',''];
@ -176,14 +176,21 @@ class BaseStringHelper
$totalCount += $currentCount;
$truncated[] = $token;
} elseif ($token instanceof \HTMLPurifier_Token_End) { //Tag ends
if (!empty($openTokens[$token->name])) {
$openTokens[$token->name]--;
if ($token->name === $openTokens[$depth-1]) {
--$depth;
unset($openTokens[$depth]);
$truncated[] = $token;
}
} elseif ($token instanceof \HTMLPurifier_Token_Empty) { //Self contained tags, i.e. <img/> etc.
$truncated[] = $token;
}
if (0 === $openTokens && $totalCount >= $count) {
if ($totalCount >= $count) {
if (0 < count($openTokens)) {
krsort($openTokens);
foreach ($openTokens as $name) {
$truncated[] = new \HTMLPurifier_Token_End($name);
}
}
break;
}
}
@ -305,4 +312,30 @@ class BaseStringHelper
return $value;
}
/**
* Encodes string into "Base 64 Encoding with URL and Filename Safe Alphabet" (RFC 4648)
* @see https://tools.ietf.org/html/rfc4648#page-7
*
* @param string $input
* @return string
* @since 2.0.12
*/
public static function base64UrlEncode($input)
{
return strtr(base64_encode($input), '+/', '-_');
}
/**
* Decodes "Base 64 Encoding with URL and Filename Safe Alphabet" (RFC 4648)
* @see https://tools.ietf.org/html/rfc4648#page-7
*
* @param string $input
* @return string
* @since 2.0.12
*/
public static function base64UrlDecode($input)
{
return base64_decode(strtr($input, '-_', '+/'));
}
}

2
framework/i18n/Formatter.php

@ -659,7 +659,7 @@ class Formatter extends Component
* Since version 2.0.1 this may also return an array if `$checkTimeInfo` is true.
* The first element of the array is the normalized timestamp and the second is a boolean indicating whether
* the timestamp has time information or it is just a date value.
* Since version 2.0.12 the array has third boolean element indicating whether the timestamp has date information
* Since version 2.0.12 the array has third boolean element indicating whether the timestamp has date information
* or it is just a time value.
* @throws InvalidArgumentException if the input value can not be evaluated as a date value.
*/

2
framework/i18n/I18N.php

@ -109,7 +109,7 @@ class I18N extends Component
return $message;
}
if (preg_match('~{\s*[\d\w]+\s*,~u', $message)) {
if (preg_match('~{\s*[\w]+\s*,~u', $message)) {
$formatter = $this->getMessageFormatter();
$result = $formatter->format($message, $params, $language);
if ($result === false) {

4
framework/messages/bg/yii.php

@ -16,7 +16,7 @@
*
* NOTE: this file must be saved in UTF-8 encoding.
*/
return array (
return [
'(not set)' => '(не е попълнено)',
'An internal server error occurred.' => 'Възникна вътрешна грешка в сървъра.',
'Are you sure you want to delete this item?' => 'Сигурни ли сте, че искате да изтриете записа?',
@ -103,4 +103,4 @@ return array (
'{n} MB' => '{n} МБ',
'{n} PB' => '{n} ПБ',
'{n} TB' => '{n} ТБ',
);
];

4
framework/messages/da/yii.php

@ -16,7 +16,7 @@
*
* NOTE: this file must be saved in UTF-8 encoding.
*/
return array (
return [
'(not set)' => '(ikke defineret)',
'An internal server error occurred.' => 'Der opstod en intern server fejl.',
'Are you sure you want to delete this item?' => 'Er du sikker på, at du vil slette dette element?',
@ -103,4 +103,4 @@ return array (
'{n} MB' => '{n} MB',
'{n} PB' => '{n} PB',
'{n} TB' => '{n} TB',
);
];

4
framework/messages/et/yii.php

@ -16,7 +16,7 @@
*
* NOTE: this file must be saved in UTF-8 encoding.
*/
return array (
return [
'(not set)' => '(määramata)',
'An internal server error occurred.' => 'Ilmnes serveri sisemine viga.',
'Are you sure you want to delete this item?' => 'Kas olete kindel, et soovite selle üksuse kustutada?',
@ -103,4 +103,4 @@ return array (
'{n} MB' => '{n} MB',
'{n} PB' => '{n} PB',
'{n} TB' => '{n} TB',
);
];

4
framework/messages/fr/yii.php

@ -17,7 +17,7 @@
* NOTE: this file must be saved in UTF-8 encoding.
*/
return [
' and ' => ' et ',
' and ' => ' et ',
'{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 jour} other{# jours}}',
'{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 heure} other{# heures}}',
'{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 minute} other{# minutes}}',
@ -43,7 +43,7 @@ return [
'Please fix the following errors:' => 'Veuillez vérifier les erreurs suivantes :',
'Please upload a file.' => 'Veuillez télécharger un fichier.',
'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Affichage de <b>{begin, number}-{end, number}</b> sur <b>{totalCount, number}</b> {totalCount, plural, one{élément} other{éléments}}.',
'The combination {values} of {attributes} has already been taken.' => 'La combinaison {values} de {attributes} est déjà utilisée.',
'The combination {values} of {attributes} has already been taken.' => 'La combinaison {values} de {attributes} est déjà utilisée.',
'The file "{file}" is not an image.' => 'Le fichier « {file} » n\'est pas une image.',
'The file "{file}" is too big. Its size cannot exceed {formattedLimit}.' => 'Le fichier « {file} » est trop gros. Sa taille ne peut dépasser {formattedLimit}.',
'The file "{file}" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Le fichier « {file} » est trop petit. Sa taille ne peut être inférieure à {formattedLimit}.',

4
framework/messages/hu/yii.php

@ -16,7 +16,7 @@
*
* NOTE: this file must be saved in UTF-8 encoding.
*/
return array (
return [
'(not set)' => '(nincs beállítva)',
'An internal server error occurred.' => 'Egy belső szerver hiba történt.',
'Are you sure you want to delete this item?' => 'Biztos benne, hogy törli ezt az elemet?',
@ -104,4 +104,4 @@ return array (
'{n} MB' => '{n} MB',
'{n} PB' => '{n} PB',
'{n} TB' => '{n} TB',
);
];

18
framework/messages/ms/yii.php

@ -35,7 +35,7 @@ return [
'Page not found.' => 'Halaman tidak dijumpai.',
'Please fix the following errors:' => 'Sila betulkan ralat berikut:',
'Please upload a file.' => 'Sila muat naik fail',
'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' =>
'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' =>
'Memaparkan <b>{begin, number}-{end, number}</b> daripada <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.',
'The file "{file}" is not an image.' => 'Fail ini "{file}" bukan berjenis gambar.',
'The file "{file}" is too big. Its size cannot exceed {formattedLimit}.' =>
@ -43,13 +43,13 @@ return [
'The file "{file}" is too small. Its size cannot be smaller than {formattedLimit}.' =>
'Fail ini "{file}" terlalu kecil. Saiznya tidak boleh lebih kecil daripada {formattedLimit}.',
'The format of {attribute} is invalid.' => 'Format untuk atribut ini {attribute} tidak sah.',
'The image "{file}" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' =>
'The image "{file}" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' =>
'Gambar "{file}" terlalu panjang. Panjang gambar tidak boleh lebih besar daripada {limit, number} {limit, plural, one{pixel} other{pixels}}.',
'The image "{file}" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' =>
'The image "{file}" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' =>
'Gambar "{file}" terlalu lebar. Gambar tidak boleh lebih lebar daripada {limit, number} {limit, plural, one{pixel} other{pixels}}.',
'The image "{file}" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' =>
'The image "{file}" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' =>
'Gambar "{file}" terlalu singkat. Panjang tidak boleh lebih singkat daripada {limit, number} {limit, plural, one{pixel} other{pixels}}.',
'The image "{file}" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' =>
'The image "{file}" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' =>
'Gambar "{file}" terlalu kecil. Lebar gambar tidak boleh kurang daripada {limit, number} {limit, plural, one{pixel} other{pixels}}.',
'The requested view "{name}" was not found.' => 'Paparan yang diminta "{name}" tidak dijumpai.',
'The verification code is incorrect.' => 'Kod penyesah tidak tepat.',
@ -60,7 +60,7 @@ return [
'View' => 'Paparan',
'Yes' => 'Ya',
'You are not allowed to perform this action.' => 'Anda tidak dibenarkan untuk mengunakan fungsi ini.',
'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' =>
'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' =>
'Anda boleh memuat naik tidak lebih daripada {limit, number} {limit, plural, one{file} other{files}}.',
'in {delta, plural, =1{a day} other{# days}}' => 'dalam {delta, plural, =1{a day} other{# days}}',
'in {delta, plural, =1{a minute} other{# minutes}}' => 'dalam {delta, plural, =1{a minute} other{# minutes}}',
@ -88,11 +88,11 @@ return [
'{attribute} must be no less than {min}.' => '{attribute} tidak boleh kurang daripada {min}.',
'{attribute} must be repeated exactly.' => '{attribute} mestilah diulang dengan tepat.',
'{attribute} must not be equal to "{compareValue}".' => '{attribute} mestilah tidak sama dengan "{compareValue}".',
'{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' =>
'{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' =>
'{attribute} mesti mengandungi sekurang-kurangnya {min, number} {min, plural, one{character} other{characters}}.',
'{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' =>
'{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' =>
'{attribute} mesti mengangungi paling banyak {max, number} {max, plural, one{character} other{characters}}.',
'{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' =>
'{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' =>
'{attribute} mesti mengandungi {length, number} {length, plural, one{character} other{characters}}.',
'{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{a day} other{# days}} lalu',
'{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{a minute} other{# minutes}} lalu',

2
framework/messages/pt-BR/yii.php

@ -91,6 +91,7 @@ return [
'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Você pode fazer o upload de, no máximo, {limit, number} {limit, plural, one{arquivo} other{arquivos}}.',
'the input value' => 'o valor de entrada',
'{attribute} "{value}" has already been taken.' => '{attribute} "{value}" já foi utilizado.',
'The combination {values} of {attributes} has already been taken.' => 'A combinação {values} de {attributes} já foi utilizado.',
'{attribute} cannot be blank.' => '"{attribute}" não pode ficar em branco.',
'{attribute} contains wrong subnet mask.' => '{attribute} contém a máscara de sub-rede errado.',
'{attribute} is invalid.' => '"{attribute}" é inválido.',
@ -118,4 +119,5 @@ return [
'{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '"{attribute}" deve conter no máximo {max, number} {max, plural, one{caractere} other{caracteres}}.',
'{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '"{attribute}" deve conter {length, number} {length, plural, one{caractere} other{caracteres}}.',
'{attribute} must be equal to "{compareValueOrAttribute}".' => '{attribute} deve ser igual a "{compareValueOrAttribute}".',
' and ' => ' e ',
];

199
framework/messages/tg/yii.php

@ -1,114 +1,129 @@
<?php
/**
* Message translations.
*
* This file is automatically generated by 'yii message' command.
* It contains the localizable messages extracted from source code.
* You may modify this file by translating the extracted messages.
*
* Each array element represents the translation (value) of a message (key).
* If the value is empty, the message is considered as not translated.
* Messages that no longer need translation will have their translations
* enclosed between a pair of '@@' marks.
*
* Message string can be used with plural forms format. Check i18n section
* of the guide for details.
*
* NOTE: this file must be saved in UTF-8 encoding.
*/
* Message translations.
*
* This file is automatically generated by 'yii message/extract' command.
* It contains the localizable messages extracted from source code.
* You may modify this file by translating the extracted messages.
*
* Each array element represents the translation (value) of a message (key).
* If the value is empty, the message is considered as not translated.
* Messages that no longer need translation will have their translations
* enclosed between a pair of '@@' marks.
*
* Message string can be used with plural forms format. Check i18n section
* of the guide for details.
*
* NOTE: this file must be saved in UTF-8 encoding.
*/
return [
'Powered by {yii}' => 'Дар {yii} кор мекунад',
'Unknown alias: -{name}' => 'Тахаллуси номаълум: -{name}',
'Yii Framework' => 'Yii Framework',
'(not set)' => '(супориш дода нашуд)',
' and ' => ' ва ',
'An internal server error occurred.' => 'Хатои дохилии сервер рух дод.',
'Are you sure you want to delete this item?' => 'Оё шумо дар ҳақиқат мехоҳед, ки ин элементро нест кунед?',
'Error' => 'Иштибоҳ',
'File upload failed.' => 'Фарокашии файл. имконнопазир гашт.',
'Home' => 'Саҳифаи асосӣ',
'Invalid data received for parameter "{param}".' => 'Маънои нодурусти параметри "{param}".',
'Login Required' => 'Вуруд талаб карда мешавад.',
'Missing required arguments: {params}' => 'Далелҳои лозимӣ вуҷуд надоранд: {params}',
'Missing required parameters: {params}' => 'Параметрҳои лозимӣ вуҷуд надоранд: {params}',
'No' => 'Не',
'No results found.' => 'Ҳеҷ чиз ёфт нашуд.',
'Only files with these MIME types are allowed: {mimeTypes}.' => 'Барои фарокашии файлҳо танҳо бо намудҳои зерини MIME иҷозат аст: {mimeTypes}.',
'Only files with these extensions are allowed: {extensions}.' => 'Барои фарокашии файлҳо танҳо тавассути зиёдкуни зерин иҷозат аст: {extensions}.',
'Page not found.' => 'Саҳифа ёфт нашуд.',
'Please fix the following errors:' => 'Лутфан, хатогиҳои зеринро ислоҳ намоед:',
'Please upload a file.' => 'Лутфан, файлро бор кунед.',
'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Қайдҳо нишон дода шудаанд <b>{begin, number}-{end, number}</b> аз <b>{totalCount, number}</b>.',
'The combination {values} of {attributes} has already been taken.' => 'Комбинатсияи {values} параметрҳо {attributes} аллакай вуҷуд дорад.',
'The file "{file}" is not an image.' => 'Файли "{file}" тасвир нест.',
'The file "{file}" is too big. Its size cannot exceed {formattedLimit}.' => 'Ҳаҷми файли "{file}" азҳад зиёд калон аст. Андозаи он набояд аз {formattedLimit} зиёдтар бошад.',
'The file "{file}" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Ҳаҷми файли "{file}" аз ҳад зиёд хурд аст. Он бояд аз {formattedLimit} калонтар бошад.',
'The format of {attribute} is invalid.' => 'Формати нодурусти маънӣ {attribute}.',
'The image "{file}" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Ҳаҷми файли "{file}" аз ҳад зиёд калон аст. Баландияш набояд аз {limit, number} {limit, plural, one{пиксел} few{пиксел} many{пиксел} other{пиксел}} зиёд бошад.',
'The image "{file}" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Ҳаҷми файл "{file}" аз ҳад зиёд калон аст. Дарозияш набояд аз {limit, number} {limit, plural, one{пиксел} few{пиксел} many{пиксел} other{пиксел}} зиёд бошад.',
'The image "{file}" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Ҳаҷми файл "{file}" аз ҳад зиёд хурд аст. Баландияш бояд аз {limit, number} {limit, plural, one{пиксел} few{пиксел} many{пиксел} other{пиксел}} зиёд бошад.',
'The image "{file}" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Ҳаҷми файл "{file}" аз ҳад зиёд хурд аст. Дарозияш бояд аз {limit, number} {limit, plural, one{пиксел} few{пиксел} many{пиксел} other{пиксел}} зиёд бошад.',
'The requested view "{name}" was not found.' => 'Файл дархостшудаи ҷадвали "{name}" ёфт нашуд.',
'The verification code is incorrect.' => 'Рамзи нодурусти санҷишӣ.',
'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Ҳамаги <b>{count, number}</b> {count, plural, one{қайд} few{қайд} many{қайдҳо} other{қайд}}.',
'Unable to verify your data submission.' => 'Санҷидани маълумоти фиристодаи Шумо муяссар нагардид.',
'Unknown option: --{name}' => 'Гузинаи номаълум: --{name}',
'Yes' => 'Ҳа',
'You are not allowed to perform this action.' => 'Шумо барои анҷом додани амали мазкур иҷозат надоред.',
'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Ҳамаги {limit, number} аплод карда метавонед.',
'in {delta, plural, =1{a day} other{# days}}' => '{delta} рӯзи дигар',
'in {delta, plural, =1{a minute} other{# minutes}}' => '{delta} дақиқаи дигар',
'in {delta, plural, =1{a month} other{# months}}' => '{delta} моҳи дигар',
'in {delta, plural, =1{a second} other{# seconds}}' => '{delta} сонияи дигар',
'in {delta, plural, =1{a year} other{# years}}' => '{delta} соли дигар',
'in {delta, plural, =1{an hour} other{# hours}}' => 'баъд аз {delta, plural, =1{соат} one{# соат} few{# соат} many{# соат} other{# соат}}',
'just now' => 'ҳоло',
'the input value' => 'ҷадвали воридшуда',
'{attribute} "{value}" has already been taken.' => 'Ҷадвали «{value}» барои {attribute} аллакай банд аст.',
'{attribute} cannot be blank.' => 'Ҳошияи «{attribute}» набояд холӣ бошад.',
'{attribute} contains wrong subnet mask.' => 'Маънои "{attribute}" дорои нодурусти ниқоби зершабака мебошад.',
'{attribute} is invalid.' => 'Ҷадвали {attribute} ғалат аст.',
'{attribute} is not a valid URL.' => 'Ҷадвали «{attribute}» URL-и нодуруст мебошад.',
'{attribute} is not a valid email address.' => 'Ҷадвали {attribute} сӯроғаи дурусти E-mail нест.',
'{attribute} is not in the allowed range.' => 'Ҷадвали «{attribute}» ба рӯйхати сӯроғаҳои диапазонҳои иҷозат додашуда дохил намешавад.',
'{attribute} must be "{requiredValue}".' => 'Ҷадвали «{attribute}» бояд ба «{requiredValue}» баробар бошад.',
'{attribute} must be a number.' => 'Ҷадвали {attribute} бояд адад бошад.',
'{attribute} must be a string.' => 'Ҷадвали {attribute} бояд сатр бошад.',
'{attribute} must be a valid IP address.' => 'Ҷадвали «{attribute}» бояд сӯроғаи дурусти IP бошад.',
'{attribute} must be an IP address with specified subnet.' => 'Ҷадвали «{attribute}» бояд сӯроғаи IP бо зершабака бошад.',
'{attribute} must be an integer.' => 'Ҷадвали {attribute} бояд адади бутун бошад.',
'{attribute} must be either "{true}" or "{false}".' => 'Маънои «{attribute}» бояд ба «{true}» ё «{false}» баробар бошад.',
'{attribute} must be equal to "{compareValueOrAttribute}".' => 'Маънои «{attribute}» бояд ба «{compareValueOrAttribute}» баробар бошад.',
'{attribute} must be greater than "{compareValueOrAttribute}".' => 'Маънои «{attribute}» бояд аз маънии «{compareValueOrAttribute}» бузургтар бошад.',
'{attribute} must be greater than or equal to "{compareValueOrAttribute}".' => 'Маънои «{attribute}» бояд аз маънии «{compareValueOrAttribute}» бузургтар ё ба он баробар бошад.',
'{attribute} must be less than "{compareValueOrAttribute}".' => 'Маънои «{attribute}» бояд аз маънии «{compareValueOrAttribute}» хурдтар бошад.',
'{attribute} must be less than or equal to "{compareValueOrAttribute}".' => 'Маънои «{attribute}» бояд аз маънии «{compareValueOrAttribute}» хурдтар ё ба он баробар бошад.',
'{attribute} must be no greater than {max}.' => '{attribute} бояд аз {max} зиёд набошад.',
'{attribute} must be no less than {min}.' => '{attribute} бояд аз {min} кам набошад.',
'{attribute} must not be a subnet.' => 'Маънии «{attribute}» набояд зершабака бошад.',
'{attribute} must not be an IPv4 address.' => 'Маънии «{attribute}» набояд сӯроғаи IPv4 бошад.',
'{attribute} must not be an IPv6 address.' => 'Маънии «{attribute}» набояд сӯроғаи IPv6 бошад.',
'{attribute} must not be equal to "{compareValueOrAttribute}".' => 'Маънои «{attribute}» набояд ба «{compareValueOrAttribute}» баробар бошад.',
'{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} хади ақал {min, number} рамз дошта бошад.',
'{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} хамаги {max, number} рамз дошта бошад.',
'{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} бояд {length, number} рамз дошта бошад.',
'{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, one{# рӯз} few{# рӯз} many{# рӯз} other{# рӯз}}',
'{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, one{# соат} few{# соат} many{# соат} other{# соат}}',
'{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, one{# дақиқа} few{# дақиқа} many{# дақиқа} other{# дақиқа}}',
'{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, one{# моҳ} few{# моҳ} many{# моҳ} other{# моҳ}}',
'{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, one{# сония} few{# сония} many{# сония} other{# сония}}',
'{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, one{# сол} few{# сол} many{# сол} other{# сол}}',
'{delta, plural, =1{a day} other{# days}} ago' => '{delta} рӯзи қабл',
'{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta} дақиқаи қабл',
'{delta, plural, =1{a month} other{# months}} ago' => '{delta} моҳи қабл',
'{delta, plural, =1{a second} other{# seconds}} ago' => '{delta} сонияи қабл',
'{delta, plural, =1{a year} other{# years}} ago' => '{delta} сол пеш',
'{delta, plural, =1{an hour} other{# hours}} ago' => '{delta} соати қабл',
'{nFormatted} B' => '{nFormatted} B',
'{nFormatted} GB' => '{nFormatted} GB',
'{nFormatted} GiB' => '{nFormatted} GiB',
'{nFormatted} KB' => '{nFormatted} KB',
'{nFormatted} KiB' => '{nFormatted} KiB',
'{nFormatted} MB' => '{nFormatted} MB',
'{nFormatted} MiB' => '{nFormatted} MiB',
'{nFormatted} GB' => '{nFormatted} GB',
'{nFormatted} GiB' => '{nFormatted} GiB',
'{nFormatted} PB' => '{nFormatted} PB',
'{nFormatted} PiB' => '{nFormatted} PiB',
'{nFormatted} TB' => '{nFormatted} TB',
'{nFormatted} TiB' => '{nFormatted} TiB',
'{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} байт',
'{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} гибибайт',
'{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} гигабайт',
'{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} кибибайт',
'{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} килобайт',
'{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} мебибайт',
'{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} мегабайт',
'{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} гибибайт',
'{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} гигабайт',
'{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} пебибайт',
'{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} петабайт',
'{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} тебибайт',
'{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} терабайт',
'Are you sure you want to delete this item?' => 'Оё шумо дар ҳақиқат мехоҳед, ки ин нашрро нест кунед?',
'The requested view "{name}" was not found.' => 'Файл "{name}" барои манзур ёфт нашуд',
'(not set)' => '(танзим нашуда)',
'An internal server error occurred.' => 'Хатои дохилии сервер рух дод.',
'Delete' => 'Нест',
'Error' => 'Хато',
'File upload failed.' => 'Аплоди файл шикаст хурд.',
'Home' => 'Асосӣ',
'Invalid data received for parameter "{param}".' => 'Маълумоти номувофиқ барои параметри "{param}" гирифта шуд.',
'Login Required' => 'Вуруд маҷбурист',
'Missing required arguments: {params}' => 'Аргументи лозими вуҷд надорад: {params}',
'Missing required parameters: {params}' => 'Параметри лозими вуҷуд надорад: {params}',
'No' => 'На',
'No results found.' => 'Чизе ёфт нашуд.',
'Page not found.' => 'Саҳифа ёфт нашуд.',
'Please fix the following errors:' => 'Илтимос хатоҳои зеринро ислоҳ кунед:',
'Only files with these extensions are allowed: {extensions}.' => 'Танҳо файлҳои бо ин пасванд иҷоза аст: {extensions}.',
'Only files with these MIME types are allowed: {mimeTypes}.' => 'Фақат ин намуди файлҳо иҷозат аст: {mimeTypes}.',
'The format of {attribute} is invalid.' => 'Формати {attribute} ғалат буд.',
'Please upload a file.' => 'Илтимос файл аплод кунед.',
'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Манзури <b>{begin, number}-{end, number}</b> аз <b>{totalCount, number}</b>.',
'The file "{file}" is not an image.' => 'Файл "{file}" расм набуд.',
'The file "{file}" is too big. Its size cannot exceed {formattedLimit}.' => 'Файл "{file}" калон аст. Аз {formattedLimit} набояд калонтар бошад.',
'The file "{file}" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Файл "{file}" хурд аст. Аз {formattedLimit} набояд хурдтар бошад.',
'The image "{file}" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Расми "{file}" баланд аст. Баландияш набояд аз {limit, number} зиёд бошад.',
'The image "{file}" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Расми "{file}" паҳн аст. Паҳнияш набояд аз {limit, number} зиёд бошад.',
'The image "{file}" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Расми "{file}" хурд аст. Баландияш набояд аз {limit, number} хурд бошад.',
'The image "{file}" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Расми "{file}" хурд аст. Паҳнияш набояд аз {limit, number} хурд бошад.',
'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Ҳамаги {limit, number} аплод карда метавонед.',
'The verification code is incorrect.' => 'Коди санҷиши ғалат аст.',
'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Ҳамаги <b>{count, number}</b> нашр.',
'Unable to verify your data submission.' => 'Маълумоти фиристодаи шуморо санҷиш карда натавонистам.',
'Unknown option: --{name}' => 'Гузинаи номаълум: --{name}',
'Update' => 'Тағир',
'View' => 'Манзур',
'Yes' => 'Ҳа',
'just now' => 'ҳоло',
'the input value' => 'маълумоти вурудбуда',
'You are not allowed to perform this action.' => 'Шумо барои анҷоми ин амал дастнорасед.',
'in {delta, plural, =1{a second} other{# seconds}}' => '{delta} сонияи дигар',
'in {delta, plural, =1{a minute} other{# minutes}}' => '{delta} дақиқаи дигар',
'in {delta, plural, =1{an hour} other{# hours}}' => '{delta} соати дигар',
'in {delta, plural, =1{a day} other{# days}}' => '{delta} рӯзи дигар',
'in {delta, plural, =1{a month} other{# months}}' => '{delta} моҳи дигар',
'in {delta, plural, =1{a year} other{# years}}' => '{delta} соли дигар',
'{delta, plural, =1{a second} other{# seconds}} ago' => '{delta} сонияи қабл',
'{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta} дақиқаи қабл',
'{delta, plural, =1{an hour} other{# hours}} ago' => '{delta} соати қабл',
'{delta, plural, =1{a day} other{# days}} ago' => '{delta} рӯзи қабл',
'{delta, plural, =1{a month} other{# months}} ago' => '{delta} моҳи қабл',
'{delta, plural, =1{a year} other{# years}} ago' => '{delta} сол пеш',
'{attribute} "{value}" has already been taken.' => '{attribute} "{value}" машғул аст.',
'{attribute} cannot be blank.' => '{attribute} набояд холи бошад.',
'{attribute} is invalid.' => '{attribute} ғалат аст.',
'{attribute} is not a valid URL.' => '{attribute} URL ғалат аст.',
'{attribute} is not a valid email address.' => '{attribute} E-mail одреси ғалат аст.',
'{attribute} must be "{requiredValue}".' => '{attribute} бояд "{requiredValue}" бошад.',
'{attribute} must be a number.' => '{attribute} бояд адад бошад.',
'{attribute} must be a string.' => '{attribute} бояд хат бошад.',
'{attribute} must be an integer.' => '{attribute} бояд адади комил бошад.',
'{attribute} must be either "{true}" or "{false}".' => '{attribute} бояд ё "{true}" ё "{false}" бошад.',
'{attribute} must be greater than "{compareValue}".' => '{attribute} бояд аз "{compareValue}" калон бошад.',
'{attribute} must be greater than or equal to "{compareValue}".' => '{attribute} бояд калон ё баробари "{compareValue}" бошад.',
'{attribute} must be less than "{compareValue}".' => '{attribute} бояд аз "{compareValue}" хурд бошад.',
'{attribute} must be less than or equal to "{compareValue}".' => '{attribute} бояд хурд ё баробари "{compareValue}" бошад.',
'{attribute} must be no greater than {max}.' => '{attribute} бояд аз {max} зиёд набошад.',
'{attribute} must be no less than {min}.' => '{attribute} бояд аз {min} кам набошад.',
'{attribute} must be repeated exactly.' => '{attribute} айнан бояд такрор шавад.',
'{attribute} must not be equal to "{compareValue}".' => '{attribute} бояд баробари "{compareValue}" набошад.',
'{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} хади ақал {min, number} рамз дошта бошад.',
'{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} хамаги {max, number} рамз дошта бошад.',
'{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} бояд {length, number} рамз дошта бошад.',
];

2
framework/messages/uz/yii.php

@ -78,7 +78,7 @@ return [
'View' => 'Ko`rish',
'Yes' => 'Ha',
'You are not allowed to perform this action.' => 'Sizga ushbu amalni bajarishga ruhsat berilmagan.',
'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Siz {limit, number} {limit, plural, one{fayldan} few{fayllardan} many{fayllardan} other{fayllardan}} ko`pini yuklab ola olmaysiz.',
'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Siz {limit, number} {limit, plural, one{fayldan} few{fayllardan} many{fayllardan} other{fayllardan}} ko`pini yuklab ola olmaysiz.',
'in {delta, plural, =1{a day} other{# days}}' => '{delta, plural, =1{kundan} one{# kundan} few{# kundan} many{# kunlardan} other{# kundan}} keyin',
'in {delta, plural, =1{a minute} other{# minutes}}' => '{delta, plural, =1{minutdan} one{# minutdan} few{# minutlardan} many{# minutdan} other{# minutlardan}} keyin',
'in {delta, plural, =1{a month} other{# months}}' => '{delta, plural, =1{oydan} one{# oydan} few{# oydan} many{# oylardan} other{# oylardan}} keyin',

2
framework/rbac/DbManager.php

@ -480,7 +480,7 @@ class DbManager extends BaseManager
{
$role = $this->getRole($roleName);
if (is_null($role)) {
if ($role === null) {
throw new InvalidArgumentException("Role \"$roleName\" not found.");
}

2
framework/rbac/PhpManager.php

@ -411,7 +411,7 @@ class PhpManager extends BaseManager
{
$role = $this->getRole($roleName);
if (is_null($role)) {
if ($role === null) {
throw new InvalidArgumentException("Role \"$roleName\" not found.");
}

2
framework/rbac/migrations/schema-oci.sql

@ -36,7 +36,7 @@ create table "auth_item"
foreign key ("rule_name") references "auth_rule"("name") on delete set null,
primary key ("name")
);
-- adds oracle specific index to auth_item
-- adds oracle specific index to auth_item
CREATE INDEX auth_type_index ON "auth_item"("type");
create table "auth_item_child"

2
framework/requirements/YiiRequirementChecker.php

@ -386,7 +386,7 @@ class YiiRequirementChecker
*/
function getServerInfo()
{
return isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : '';;
return isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : '';
}
/**

11
framework/validators/BooleanValidator.php

@ -52,8 +52,11 @@ class BooleanValidator extends Validator
*/
protected function validateValue($value)
{
$valid = !$this->strict && ($value == $this->trueValue || $value == $this->falseValue)
|| $this->strict && ($value === $this->trueValue || $value === $this->falseValue);
if ($this->strict) {
$valid = $value === $this->trueValue || $value === $this->falseValue;
} else {
$valid = $value == $this->trueValue || $value == $this->falseValue;
}
if (!$valid) {
return [$this->message, [
@ -84,11 +87,11 @@ class BooleanValidator extends Validator
$options = [
'trueValue' => $this->trueValue,
'falseValue' => $this->falseValue,
'message' => Yii::$app->getI18n()->format($this->message, [
'message' => $this->formatMessage($this->message, [
'attribute' => $model->getAttributeLabel($attribute),
'true' => $this->trueValue === true ? 'true' : $this->trueValue,
'false' => $this->falseValue === false ? 'false' : $this->falseValue,
], Yii::$app->language),
]),
];
if ($this->skipOnEmpty) {
$options['skipOnEmpty'] = 1;

4
framework/validators/CompareValidator.php

@ -255,12 +255,12 @@ class CompareValidator extends Validator
$options['skipOnEmpty'] = 1;
}
$options['message'] = Yii::$app->getI18n()->format($this->message, [
$options['message'] = $this->formatMessage($this->message, [
'attribute' => $model->getAttributeLabel($attribute),
'compareAttribute' => $compareLabel,
'compareValue' => $compareValue,
'compareValueOrAttribute' => $compareValueOrAttribute,
], Yii::$app->language);
]);
return $options;
}

2
framework/validators/DateValidator.php

@ -139,7 +139,7 @@ class DateValidator extends Validator
*
* If not set, [[timestampAttribute]] will receive a UNIX timestamp.
* If [[timestampAttribute]] is not set, this property will be ignored.
* @see format
* @see formatMessage
* @see timestampAttribute
* @since 2.0.4
*/

4
framework/validators/EmailValidator.php

@ -127,9 +127,9 @@ class EmailValidator extends Validator
'pattern' => new JsExpression($this->pattern),
'fullPattern' => new JsExpression($this->fullPattern),
'allowName' => $this->allowName,
'message' => Yii::$app->getI18n()->format($this->message, [
'message' => $this->formatMessage($this->message, [
'attribute' => $model->getAttributeLabel($attribute),
], Yii::$app->language),
]),
'enableIDN' => (bool)$this->enableIDN,
];
if ($this->skipOnEmpty) {

28
framework/validators/FileValidator.php

@ -392,17 +392,17 @@ class FileValidator extends Validator
$options = [];
if ($this->message !== null) {
$options['message'] = Yii::$app->getI18n()->format($this->message, [
$options['message'] = $this->formatMessage($this->message, [
'attribute' => $label,
], Yii::$app->language);
]);
}
$options['skipOnEmpty'] = $this->skipOnEmpty;
if (!$this->skipOnEmpty) {
$options['uploadRequired'] = Yii::$app->getI18n()->format($this->uploadRequired, [
$options['uploadRequired'] = $this->formatMessage($this->uploadRequired, [
'attribute' => $label,
], Yii::$app->language);
]);
}
if ($this->mimeTypes !== null) {
@ -411,44 +411,44 @@ class FileValidator extends Validator
$mimeTypes[] = new JsExpression(Html::escapeJsRegularExpression($this->buildMimeTypeRegexp($mimeType)));
}
$options['mimeTypes'] = $mimeTypes;
$options['wrongMimeType'] = Yii::$app->getI18n()->format($this->wrongMimeType, [
$options['wrongMimeType'] = $this->formatMessage($this->wrongMimeType, [
'attribute' => $label,
'mimeTypes' => implode(', ', $this->mimeTypes),
], Yii::$app->language);
]);
}
if ($this->extensions !== null) {
$options['extensions'] = $this->extensions;
$options['wrongExtension'] = Yii::$app->getI18n()->format($this->wrongExtension, [
$options['wrongExtension'] = $this->formatMessage($this->wrongExtension, [
'attribute' => $label,
'extensions' => implode(', ', $this->extensions),
], Yii::$app->language);
]);
}
if ($this->minSize !== null) {
$options['minSize'] = $this->minSize;
$options['tooSmall'] = Yii::$app->getI18n()->format($this->tooSmall, [
$options['tooSmall'] = $this->formatMessage($this->tooSmall, [
'attribute' => $label,
'limit' => $this->minSize,
'formattedLimit' => Yii::$app->formatter->asShortSize($this->minSize),
], Yii::$app->language);
]);
}
if ($this->maxSize !== null) {
$options['maxSize'] = $this->maxSize;
$options['tooBig'] = Yii::$app->getI18n()->format($this->tooBig, [
$options['tooBig'] = $this->formatMessage($this->tooBig, [
'attribute' => $label,
'limit' => $this->getSizeLimit(),
'formattedLimit' => Yii::$app->formatter->asShortSize($this->getSizeLimit()),
], Yii::$app->language);
]);
}
if ($this->maxFiles !== null) {
$options['maxFiles'] = $this->maxFiles;
$options['tooMany'] = Yii::$app->getI18n()->format($this->tooMany, [
$options['tooMany'] = $this->formatMessage($this->tooMany, [
'attribute' => $label,
'limit' => $this->maxFiles,
], Yii::$app->language);
]);
}
return $options;

20
framework/validators/ImageValidator.php

@ -179,41 +179,41 @@ class ImageValidator extends FileValidator
$label = $model->getAttributeLabel($attribute);
if ($this->notImage !== null) {
$options['notImage'] = Yii::$app->getI18n()->format($this->notImage, [
$options['notImage'] = $this->formatMessage($this->notImage, [
'attribute' => $label,
], Yii::$app->language);
]);
}
if ($this->minWidth !== null) {
$options['minWidth'] = $this->minWidth;
$options['underWidth'] = Yii::$app->getI18n()->format($this->underWidth, [
$options['underWidth'] = $this->formatMessage($this->underWidth, [
'attribute' => $label,
'limit' => $this->minWidth,
], Yii::$app->language);
]);
}
if ($this->maxWidth !== null) {
$options['maxWidth'] = $this->maxWidth;
$options['overWidth'] = Yii::$app->getI18n()->format($this->overWidth, [
$options['overWidth'] = $this->formatMessage($this->overWidth, [
'attribute' => $label,
'limit' => $this->maxWidth,
], Yii::$app->language);
]);
}
if ($this->minHeight !== null) {
$options['minHeight'] = $this->minHeight;
$options['underHeight'] = Yii::$app->getI18n()->format($this->underHeight, [
$options['underHeight'] = $this->formatMessage($this->underHeight, [
'attribute' => $label,
'limit' => $this->minHeight,
], Yii::$app->language);
]);
}
if ($this->maxHeight !== null) {
$options['maxHeight'] = $this->maxHeight;
$options['overHeight'] = Yii::$app->getI18n()->format($this->overHeight, [
$options['overHeight'] = $this->formatMessage($this->overHeight, [
'attribute' => $label,
'limit' => $this->maxHeight,
], Yii::$app->language);
]);
}
return $options;

4
framework/validators/IpValidator.php

@ -527,7 +527,7 @@ class IpValidator extends Validator
*/
private function getIpParsePattern()
{
return '/^(' . preg_quote(static::NEGATION_CHAR) . '?)(.+?)(\/(\d+))?$/';
return '/^(' . preg_quote(static::NEGATION_CHAR, '/') . '?)(.+?)(\/(\d+))?$/';
}
/**
@ -606,7 +606,7 @@ class IpValidator extends Validator
'hasSubnet' => $this->hasSubnet,
];
foreach ($messages as &$message) {
$message = Yii::$app->getI18n()->format($message, [
$message = $this->formatMessage($message, [
'attribute' => $model->getAttributeLabel($attribute),
], Yii::$app->language);
}

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

Loading…
Cancel
Save