diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 37bc6d3..a0f20c4 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,22 +1,18 @@
-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
+image: docker:latest
+
+services:
+ - docker:dind
-after_script:
- - export ISOLATION=buildpipeline${CI_PIPELINE_ID}${CI_BUILD_NAME}
- - export COMPOSE_PROJECT_NAME=${ISOLATION}
- # run docker-compose commands from tests environment
+variables:
+ DOCKER_YII2_PHP_IMAGE: yiisoftware/yii2-php:7.1-apache
+ DOCKER_MYSQL_IMAGE: percona:5.7
+ DOCKER_POSTGRES_IMAGE: postgres:9.3
+
+before_script:
+ - apk add --no-cache python py2-pip git
+ - pip install --no-cache-dir docker-compose==1.16.0
+ - docker info
- cd tests
- - cp .env-dist .env
- - docker-compose down -v --remove-orphans
- - docker ps -f name=${ISOLATION}
stages:
- travis
diff --git a/.travis.yml b/.travis.yml
index 679f8f3..c53afe7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -46,9 +46,14 @@ cache:
- $HOME/.composer/cache
- $HOME/.npm
-# try running against postgres 9.3
+# try running against postgres 9.6
addons:
- postgresql: "9.3"
+ postgresql: "9.6"
+ apt:
+ sources:
+ - mysql-5.7-trusty
+ packages:
+ - mysql-server
code_climate:
repo_token: 2935307212620b0e2228ab67eadd92c9f5501ddb60549d0d86007a354d56915b
@@ -76,10 +81,10 @@ matrix:
code_climate:
repo_token: 2935307212620b0e2228ab67eadd92c9f5501ddb60549d0d86007a354d56915b
apt:
+ sources:
+ - mysql-5.7-trusty
packages:
- - mysql-server-5.6
- - mysql-client-core-5.6
- - mysql-client-5.6
+ - mysql-server
services:
- mysql
@@ -89,12 +94,12 @@ matrix:
addons:
code_climate:
repo_token: 2935307212620b0e2228ab67eadd92c9f5501ddb60549d0d86007a354d56915b
- postgresql: "9.3"
+ postgresql: "9.6"
apt:
+ sources:
+ - mysql-5.7-trusty
packages:
- - mysql-server-5.6
- - mysql-client-core-5.6
- - mysql-client-5.6
+ - mysql-server
services:
- mysql
- postgresql
@@ -166,6 +171,7 @@ before_script:
- |
if [ $TASK_TESTS_PHP == 1 ]; then
travis_retry mysql -e 'CREATE DATABASE `yiitest`;';
+ mysql -e "SET GLOBAL sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';";
mysql -e "CREATE USER 'travis'@'localhost' IDENTIFIED WITH mysql_native_password;";
mysql -e "GRANT ALL PRIVILEGES ON *.* TO 'travis'@'localhost' WITH GRANT OPTION;";
psql -U postgres -c 'CREATE DATABASE yiitest;';
diff --git a/Dockerfile b/Dockerfile
index b890986..77ffdfd 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,5 @@
-FROM dmstr/php-yii2:7.0-fpm-1.9-beta2-alpine-nginx
+ARG DOCKER_YII2_PHP_IMAGE
+FROM ${DOCKER_YII2_PHP_IMAGE}
# Project source-code
WORKDIR /project
diff --git a/README.md b/README.md
index e0e79e0..fcbb459 100644
--- a/README.md
+++ b/README.md
@@ -39,9 +39,10 @@ Community
---------
- Participate in [discussions at forums](http://www.yiiframework.com/forum/).
-- [Chat in IRC](http://www.yiiframework.com/chat/).
+- [Community Slack](https://join.slack.com/t/yii/shared_invite/MjIxMjMxMTk5MTU1LTE1MDE3MDAwMzMtM2VkMTMyMjY1Ng) and [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).
+- Check [other communities](https://github.com/yiisoft/yii2/wiki/communities).
Contributing
------------
diff --git a/build/controllers/ClassmapController.php b/build/controllers/ClassmapController.php
index a88e6cd..7a4e3b2 100644
--- a/build/controllers/ClassmapController.php
+++ b/build/controllers/ClassmapController.php
@@ -60,7 +60,7 @@ class ClassmapController extends Controller
if (strpos($file, $root) !== 0) {
throw new Exception("Something wrong: $file\n");
}
- $path = str_replace('\\', '/', substr($file, strlen($root)));
+ $path = str_replace('\\', '/', substr($file, \strlen($root)));
$map[$path] = " 'yii" . substr(str_replace('/', '\\', $path), 0, -4) . "' => YII2_PATH . '$path',";
}
ksort($map);
diff --git a/build/controllers/DevController.php b/build/controllers/DevController.php
index bc26295..c86930f 100644
--- a/build/controllers/DevController.php
+++ b/build/controllers/DevController.php
@@ -24,19 +24,25 @@ use yii\helpers\FileHelper;
*/
class DevController extends Controller
{
+ /**
+ * {@inheritdoc}
+ */
public $defaultAction = 'all';
-
/**
* @var bool whether to use HTTP when cloning github repositories
*/
public $useHttp = false;
-
+ /**
+ * @var array
+ */
public $apps = [
'basic' => 'git@github.com:yiisoft/yii2-app-basic.git',
'advanced' => 'git@github.com:yiisoft/yii2-app-advanced.git',
'benchmark' => 'git@github.com:yiisoft/yii2-app-benchmark.git',
];
-
+ /**
+ * @var array
+ */
public $extensions = [
'apidoc' => 'git@github.com:yiisoft/yii2-apidoc.git',
'authclient' => 'git@github.com:yiisoft/yii2-authclient.git',
@@ -98,17 +104,17 @@ class DevController extends Controller
*/
public function actionRun($command)
{
- $command = implode(' ', func_get_args());
+ $command = implode(' ', \func_get_args());
// root of the dev repo
- $base = dirname(dirname(__DIR__));
+ $base = \dirname(\dirname(__DIR__));
$dirs = $this->listSubDirs("$base/extensions");
$dirs = array_merge($dirs, $this->listSubDirs("$base/apps"));
asort($dirs);
$oldcwd = getcwd();
foreach ($dirs as $dir) {
- $displayDir = substr($dir, strlen($base));
+ $displayDir = substr($dir, \strlen($base));
$this->stdout("Running '$command' in $displayDir...\n", Console::BOLD);
chdir($dir);
passthru($command);
@@ -137,7 +143,7 @@ class DevController extends Controller
public function actionApp($app, $repo = null)
{
// root of the dev repo
- $base = dirname(dirname(__DIR__));
+ $base = \dirname(\dirname(__DIR__));
$appDir = "$base/apps/$app";
if (!file_exists($appDir)) {
@@ -188,7 +194,7 @@ class DevController extends Controller
public function actionExt($extension, $repo = null)
{
// root of the dev repo
- $base = dirname(dirname(__DIR__));
+ $base = \dirname(\dirname(__DIR__));
$extensionDir = "$base/extensions/$extension";
if (!file_exists($extensionDir)) {
@@ -229,12 +235,12 @@ class DevController extends Controller
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function options($actionID)
{
$options = parent::options($actionID);
- if (in_array($actionID, ['ext', 'app', 'all'], true)) {
+ if (\in_array($actionID, ['ext', 'app', 'all'], true)) {
$options[] = 'useHttp';
}
@@ -250,13 +256,13 @@ class DevController extends Controller
{
if (is_link($link = "$dir/vendor/yiisoft/yii2")) {
$this->stdout("Removing symlink $link.\n");
- $this->unlink($link);
+ FileHelper::unlink($link);
}
$extensions = $this->findDirs("$dir/vendor/yiisoft");
foreach ($extensions as $ext) {
if (is_link($link = "$dir/vendor/yiisoft/yii2-$ext")) {
$this->stdout("Removing symlink $link.\n");
- $this->unlink($link);
+ FileHelper::unlink($link);
}
}
}
@@ -294,20 +300,6 @@ class DevController extends Controller
}
/**
- * Properly removes symlinked directory under Windows, MacOS and Linux.
- *
- * @param string $file path to symlink
- */
- protected function unlink($file)
- {
- if (is_dir($file) && DIRECTORY_SEPARATOR === '\\') {
- rmdir($file);
- } else {
- unlink($file);
- }
- }
-
- /**
* Get a list of subdirectories for directory specified.
* @param string $dir directory to read
*
diff --git a/build/controllers/MimeTypeController.php b/build/controllers/MimeTypeController.php
index 8faf653..2724451 100644
--- a/build/controllers/MimeTypeController.php
+++ b/build/controllers/MimeTypeController.php
@@ -27,34 +27,60 @@ use yii\helpers\VarDumper;
class MimeTypeController extends Controller
{
/**
- * @param string $outFile the file to update. Defaults to @yii/helpers/mimeTypes.php
+ * @var array MIME type aliases
*/
- public function actionIndex($outFile = null)
+ private $aliases = [
+ 'text/xml' => 'application/xml',
+ ];
+
+ /**
+ * @param string $outFile the mime file to update. Defaults to @yii/helpers/mimeTypes.php
+ * @param string $aliasesOutFile the aliases file to update. Defaults to @yii/helpers/mimeAliases.php
+ */
+ public function actionIndex($outFile = null, $aliasesOutFile = null)
{
if ($outFile === null) {
$outFile = Yii::getAlias('@yii/helpers/mimeTypes.php');
}
+
+ if ($aliasesOutFile === null) {
+ $aliasesOutFile = Yii::getAlias('@yii/helpers/mimeAliases.php');
+ }
+
$this->stdout('downloading mime-type file from apache httpd repository...');
- if ($content = file_get_contents('http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?view=co')) {
+ if ($apacheMimeTypesFileContent = file_get_contents('http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?view=co')) {
$this->stdout("done.\n", Console::FG_GREEN);
- $this->stdout("generating file $outFile...");
- $mimeMap = [];
- 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) {
- if (!empty($ext)) {
- $mimeMap[$ext] = $mime;
- }
+ $this->generateMimeTypesFile($outFile, $apacheMimeTypesFileContent);
+ $this->generateMimeAliasesFile($aliasesOutFile);
+ } else {
+ $this->stderr("Failed to download mime.types file from apache SVN.\n");
+ }
+ }
+
+ /**
+ * @param string $outFile
+ * @param string $content
+ */
+ private function generateMimeTypesFile($outFile, $content)
+ {
+ $this->stdout("generating file $outFile...");
+ $mimeMap = [];
+ 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) {
+ if (!empty($ext)) {
+ $mimeMap[$ext] = $mime;
}
}
- ksort($mimeMap);
- $array = VarDumper::export($mimeMap);
- $content = <<stdout("done.\n", Console::FG_GREEN);
- } else {
- $this->stderr("Failed to download mime.types file from apache SVN.\n");
- }
+ file_put_contents($outFile, $content);
+ $this->stdout("done.\n", Console::FG_GREEN);
+ }
+
+ /**
+ * @param string $outFile
+ */
+ private function generateMimeAliasesFile($outFile)
+ {
+ $this->stdout("generating file $outFile...");
+ $array = VarDumper::export($this->aliases);
+ $content = <<stdout("done.\n", Console::FG_GREEN);
}
}
diff --git a/build/controllers/PhpDocController.php b/build/controllers/PhpDocController.php
index 2757c79..19ae5fb 100644
--- a/build/controllers/PhpDocController.php
+++ b/build/controllers/PhpDocController.php
@@ -11,6 +11,7 @@ use Yii;
use yii\console\Controller;
use yii\helpers\Console;
use yii\helpers\FileHelper;
+use yii\helpers\Json;
/**
* PhpDocController is there to help maintaining PHPDoc annotation in class files.
@@ -21,6 +22,9 @@ use yii\helpers\FileHelper;
*/
class PhpDocController extends Controller
{
+ /**
+ * {@inheritdoc}
+ */
public $defaultAction = 'property';
/**
* @var bool whether to update class docs directly. Setting this to false will just output docs
@@ -82,7 +86,7 @@ class PhpDocController extends Controller
$nFilesUpdated = 0;
foreach ($files as $file) {
$contents = file_get_contents($file);
- $sha = sha1($contents);
+ $hash = $this->hash($contents);
// fix line endings
$lines = preg_split('/(\r\n|\n|\r)/', $contents);
@@ -94,10 +98,10 @@ class PhpDocController extends Controller
$lines = array_values($this->fixLineSpacing($lines));
$newContent = implode("\n", $lines);
- if ($sha !== sha1($newContent)) {
+ if ($hash !== $this->hash($newContent)) {
+ file_put_contents($file, $newContent);
$nFilesUpdated++;
}
- file_put_contents($file, $newContent);
$nFilesTotal++;
}
@@ -106,13 +110,18 @@ class PhpDocController extends Controller
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function options($actionID)
{
return array_merge(parent::options($actionID), ['updateFiles', 'skipFrameworkRequirements']);
}
+ /**
+ * @param string $root
+ * @param bool $needsInclude
+ * @return array list of files.
+ */
protected function findFiles($root, $needsInclude = true)
{
$except = [];
@@ -132,7 +141,7 @@ class PhpDocController extends Controller
'/generators/extension/default/AutoloadExample.php',
],
'swiftmailer' => [
- '/Logger.php',
+ 'src/Logger.php',
],
'twig' => [
'/Extension.php',
@@ -147,13 +156,9 @@ class PhpDocController extends Controller
}
if ($root === null) {
- $root = dirname(YII2_PATH);
+ $root = \dirname(YII2_PATH);
$extensionPath = "$root/extensions";
- foreach (scandir($extensionPath) as $extension) {
- if (ctype_alpha($extension) && is_dir($extensionPath . '/' . $extension)) {
- Yii::setAlias("@yii/$extension", "$extensionPath/$extension");
- }
- }
+ $this->setUpExtensionAliases($extensionPath);
$except = [
'/apps/',
@@ -172,11 +177,11 @@ class PhpDocController extends Controller
}
}
} elseif (preg_match('~extensions/([\w-]+)[\\\\/]?$~', $root, $matches)) {
- $extensionPath = dirname(rtrim($root, '\\/'));
+ $extensionPath = \dirname(rtrim($root, '\\/'));
$this->setUpExtensionAliases($extensionPath);
list(, $extension) = $matches;
- Yii::setAlias("@yii/$extension", "$root");
+ Yii::setAlias("@yii/$extension", (string)$root);
if (is_file($autoloadFile = Yii::getAlias("@yii/$extension/vendor/autoload.php"))) {
include $autoloadFile;
}
@@ -195,11 +200,11 @@ class PhpDocController extends Controller
// return [];
// }
} elseif (preg_match('~apps/([\w-]+)[\\\\/]?$~', $root, $matches)) {
- $extensionPath = dirname(dirname(rtrim($root, '\\/'))) . '/extensions';
+ $extensionPath = \dirname(\dirname(rtrim($root, '\\/'))) . '/extensions';
$this->setUpExtensionAliases($extensionPath);
list(, $appName) = $matches;
- Yii::setAlias("@app-$appName", "$root");
+ Yii::setAlias("@app-$appName", (string)$root);
if (is_file($autoloadFile = Yii::getAlias("@app-$appName/vendor/autoload.php"))) {
include $autoloadFile;
}
@@ -230,14 +235,30 @@ class PhpDocController extends Controller
'vendor/',
]),
];
+
return FileHelper::findFiles($root, $options);
}
+ /**
+ * @param string $extensionPath root path containing extension repositories.
+ */
private function setUpExtensionAliases($extensionPath)
{
foreach (scandir($extensionPath) as $extension) {
if (ctype_alpha($extension) && is_dir($extensionPath . '/' . $extension)) {
Yii::setAlias("@yii/$extension", "$extensionPath/$extension");
+
+ $composerConfigFile = $extensionPath . '/' . $extension . '/composer.json';
+ if (file_exists($composerConfigFile)) {
+ $composerConfig = Json::decode(file_get_contents($composerConfigFile));
+ if (isset($composerConfig['autoload']['psr-4'])) {
+ foreach ($composerConfig['autoload']['psr-4'] as $namespace => $subPath) {
+ $alias = '@' . str_replace('\\', '/', $namespace);
+ $path = rtrim("$extensionPath/$extension/$subPath", '/');
+ Yii::setAlias($alias, $path);
+ }
+ }
+ }
}
}
}
@@ -285,6 +306,7 @@ class PhpDocController extends Controller
/**
* Markdown aware fix of whitespace issues in doc comments.
+ * @param array $lines
*/
protected function fixDocBlockIndentation(&$lines)
{
@@ -326,7 +348,7 @@ class PhpDocController extends Controller
$codeBlock = !$codeBlock;
$listIndent = '';
} elseif (preg_match('/^(\s*)([0-9]+\.|-|\*|\+) /', $docLine, $matches)) {
- $listIndent = str_repeat(' ', strlen($matches[0]));
+ $listIndent = str_repeat(' ', \strlen($matches[0]));
$tag = false;
$lines[$i] = $indent . ' * ' . $docLine;
continue;
@@ -340,6 +362,10 @@ class PhpDocController extends Controller
}
}
+ /**
+ * @param string $line
+ * @return string
+ */
protected function fixParamTypes($line)
{
return preg_replace_callback('~@(param|return) ([\w\\|]+)~i', function ($matches) {
@@ -532,10 +558,10 @@ class PhpDocController extends Controller
if (trim($oldDoc) != trim($newDoc)) {
$fileContent = explode("\n", file_get_contents($file));
$start = $ref->getStartLine() - 2;
- $docStart = $start - count(explode("\n", $oldDoc)) + 1;
+ $docStart = $start - \count(explode("\n", $oldDoc)) + 1;
$newFileContent = [];
- $n = count($fileContent);
+ $n = \count($fileContent);
for ($i = 0; $i < $n; $i++) {
if ($i > $start || $i < $docStart) {
$newFileContent[] = $fileContent[$i];
@@ -562,7 +588,7 @@ class PhpDocController extends Controller
protected function cleanDocComment($doc)
{
$lines = explode("\n", $doc);
- $n = count($lines);
+ $n = \count($lines);
for ($i = 0; $i < $n; $i++) {
$lines[$i] = rtrim($lines[$i]);
if (trim($lines[$i]) == '*' && trim($lines[$i + 1]) == '*') {
@@ -603,7 +629,7 @@ class PhpDocController extends Controller
// if no properties or other tags where present add properties at the end
if ($propertyPosition === false) {
- $propertyPosition = count($lines) - 2;
+ $propertyPosition = \count($lines) - 2;
}
$finalDoc = '';
@@ -626,22 +652,22 @@ class PhpDocController extends Controller
$namespace = $namespace['name'];
$classes = $this->match('#\n(?:abstract )?class (?\w+)( extends .+)?( implements .+)?\n\{(?.*)\n\}(\n|$)#', $file);
- if (count($classes) > 1) {
+ if (\count($classes) > 1) {
$this->stderr("[ERR] There should be only one class in a file: $fileName\n", Console::FG_RED);
return false;
}
- if (count($classes) < 1) {
+ if (\count($classes) < 1) {
$interfaces = $this->match('#\ninterface (?\w+)( extends .+)?\n\{(?.*)\n\}(\n|$)#', $file);
- if (count($interfaces) == 1) {
+ if (\count($interfaces) == 1) {
return false;
- } elseif (count($interfaces) > 1) {
+ } elseif (\count($interfaces) > 1) {
$this->stderr("[ERR] There should be only one interface in a file: $fileName\n", Console::FG_RED);
} else {
$traits = $this->match('#\ntrait (?\w+)\n\{(?.*)\n\}(\n|$)#', $file);
- if (count($traits) == 1) {
+ if (\count($traits) == 1) {
return false;
- } elseif (count($traits) > 1) {
+ } elseif (\count($traits) > 1) {
$this->stderr("[ERR] There should be only one class/trait/interface in a file: $fileName\n", Console::FG_RED);
} else {
$this->stderr("[ERR] No class in file: $fileName\n", Console::FG_RED);
@@ -682,13 +708,13 @@ class PhpDocController extends Controller
ksort($props);
- if (count($props) > 0) {
+ if (\count($props) > 0) {
$phpdoc .= " *\n";
foreach ($props as $propName => &$prop) {
$docline = ' * @';
$docline .= 'property'; // Do not use property-read and property-write as few IDEs support complex syntax.
$note = '';
- if (isset($prop['get']) && isset($prop['set'])) {
+ if (isset($prop['get'], $prop['set'])) {
if ($prop['get']['type'] != $prop['set']['type']) {
$note = ' Note that the type of this property differs in getter and setter.'
. ' See [[get' . ucfirst($propName) . '()]] and [[set' . ucfirst($propName) . '()]] for details.';
@@ -706,7 +732,7 @@ class PhpDocController extends Controller
}
if (!$parentSetter) {
$note = ' This property is read-only.';
-// $docline .= '-read';
+ //$docline .= '-read';
}
} elseif (isset($prop['set'])) {
// check if parent class has getter defined
@@ -721,7 +747,7 @@ class PhpDocController extends Controller
}
if (!$parentGetter) {
$note = ' This property is write-only.';
-// $docline .= '-write';
+ //$docline .= '-write';
}
} else {
continue;
@@ -773,11 +799,24 @@ class PhpDocController extends Controller
return '';
}
- return strtoupper(substr($str, 0, 1)) . substr($str, 1) . ($str[strlen($str) - 1] != '.' ? '.' : '');
+ return strtoupper(substr($str, 0, 1)) . substr($str, 1) . ($str[\strlen($str) - 1] != '.' ? '.' : '');
}
protected function getPropParam($prop, $param)
{
return isset($prop['property']) ? $prop['property'][$param] : (isset($prop['get']) ? $prop['get'][$param] : $prop['set'][$param]);
}
+
+ /**
+ * Generate a hash value (message digest)
+ * @param string $string message to be hashed.
+ * @return string calculated message digest.
+ */
+ private function hash($string)
+ {
+ if (!function_exists('hash')) {
+ return sha1($string);
+ }
+ return hash('sha256', $string);
+ }
}
diff --git a/build/controllers/ReleaseController.php b/build/controllers/ReleaseController.php
index 985f1e7..ff1dfd3 100644
--- a/build/controllers/ReleaseController.php
+++ b/build/controllers/ReleaseController.php
@@ -82,7 +82,7 @@ class ReleaseController extends Controller
throw new Exception('Sorry, but releases should be run interactively to ensure you actually verify what you are doing ;)');
}
if ($this->basePath === null) {
- $this->basePath = dirname(dirname(__DIR__));
+ $this->basePath = \dirname(\dirname(__DIR__));
}
$this->basePath = rtrim($this->basePath, '\\/');
return parent::beforeAction($action);
@@ -109,7 +109,7 @@ class ReleaseController extends Controller
foreach ($items as $item) {
$this->stdout("fetching tags for $item...");
if ($item === 'framework') {
- $this->gitFetchTags("{$this->basePath}");
+ $this->gitFetchTags((string)$this->basePath);
} elseif (strncmp('app-', $item, 4) === 0) {
$this->gitFetchTags("{$this->basePath}/apps/" . substr($item, 4));
} else {
@@ -183,7 +183,7 @@ class ReleaseController extends Controller
*/
public function actionRelease(array $what)
{
- if (count($what) > 1) {
+ if (\count($what) > 1) {
$this->stdout("Currently only one simultaneous release is supported.\n");
return 1;
}
@@ -315,7 +315,7 @@ class ReleaseController extends Controller
*/
public function actionSortChangelog(array $what)
{
- if (count($what) > 1) {
+ if (\count($what) > 1) {
$this->stdout("Currently only one simultaneous release is supported.\n");
return 1;
}
@@ -376,7 +376,7 @@ class ReleaseController extends Controller
{
foreach ($what as $w) {
if (strncmp('app-', $w, 4) === 0) {
- if (!empty($limit) && !in_array('app', $limit)) {
+ if (!empty($limit) && !\in_array('app', $limit)) {
throw new Exception('Only the following types are allowed: ' . implode(', ', $limit) . "\n");
}
if (!is_dir($appPath = "{$this->basePath}/apps/" . substr($w, 4))) {
@@ -386,7 +386,7 @@ class ReleaseController extends Controller
$this->ensureGitClean($appPath);
}
} elseif ($w === 'framework') {
- if (!empty($limit) && !in_array('framework', $limit)) {
+ if (!empty($limit) && !\in_array('framework', $limit)) {
throw new Exception('Only the following types are allowed: ' . implode(', ', $limit) . "\n");
}
if (!is_dir($fwPath = "{$this->basePath}/framework")) {
@@ -396,7 +396,7 @@ class ReleaseController extends Controller
$this->ensureGitClean($fwPath);
}
} else {
- if (!empty($limit) && !in_array('ext', $limit)) {
+ if (!empty($limit) && !\in_array('ext', $limit)) {
throw new Exception('Only the following types are allowed: ' . implode(', ', $limit) . "\n");
}
if (!is_dir($extPath = "{$this->basePath}/extensions/$w")) {
@@ -414,7 +414,7 @@ class ReleaseController extends Controller
{
$this->stdout("\n");
$this->stdout($h = "Preparing framework release version $version", Console::BOLD);
- $this->stdout("\n" . str_repeat('-', strlen($h)) . "\n\n", Console::BOLD);
+ $this->stdout("\n" . str_repeat('-', \strlen($h)) . "\n\n", Console::BOLD);
if (!$this->confirm('Make sure you are on the right branch for this release and that it tracks the correct remote branch! Continue?')) {
exit(1);
@@ -433,8 +433,8 @@ class ReleaseController extends Controller
$this->dryRun || Yii::$app->runAction('classmap', [$frameworkPath]);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
- $this->stdout('updating mimetype magic file...', Console::BOLD);
- $this->dryRun || Yii::$app->runAction('mime-type', ["$frameworkPath/helpers/mimeTypes.php"]);
+ $this->stdout('updating mimetype magic file and mime aliases...', Console::BOLD);
+ $this->dryRun || Yii::$app->runAction('mime-type', ["$frameworkPath/helpers/mimeTypes.php"], ["$frameworkPath/helpers/mimeAliases.php"]);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout("fixing various PHPDoc style issues...\n", Console::BOLD);
@@ -552,7 +552,7 @@ class ReleaseController extends Controller
{
$this->stdout("\n");
$this->stdout($h = "Preparing release for application $name version $version", Console::BOLD);
- $this->stdout("\n" . str_repeat('-', strlen($h)) . "\n\n", Console::BOLD);
+ $this->stdout("\n" . str_repeat('-', \strlen($h)) . "\n\n", Console::BOLD);
if (!$this->confirm('Make sure you are on the right branch for this release and that it tracks the correct remote branch! Continue?')) {
exit(1);
@@ -670,7 +670,7 @@ class ReleaseController extends Controller
{
$this->stdout("\n");
$this->stdout($h = "Preparing release for extension $name version $version", Console::BOLD);
- $this->stdout("\n" . str_repeat('-', strlen($h)) . "\n\n", Console::BOLD);
+ $this->stdout("\n" . str_repeat('-', \strlen($h)) . "\n\n", Console::BOLD);
if (!$this->confirm('Make sure you are on the right branch for this release and that it tracks the correct remote branch! Continue?')) {
exit(1);
@@ -817,7 +817,7 @@ class ReleaseController extends Controller
$headline = $version . ' ' . date('F d, Y');
$this->sed(
'/' . $v . ' under development\n(-+?)\n/',
- $headline . "\n" . str_repeat('-', strlen($headline)) . "\n",
+ $headline . "\n" . str_repeat('-', \strlen($headline)) . "\n",
$this->getChangelogs($what)
);
}
@@ -825,7 +825,7 @@ class ReleaseController extends Controller
protected function openChangelogs($what, $version)
{
$headline = "\n$version under development\n";
- $headline .= str_repeat('-', strlen($headline) - 2) . "\n\n- no changes in this release.\n";
+ $headline .= str_repeat('-', \strlen($headline) - 2) . "\n\n- no changes in this release.\n";
foreach ($this->getChangelogs($what) as $file) {
$lines = explode("\n", file_get_contents($file));
$hl = [
@@ -874,7 +874,7 @@ class ReleaseController extends Controller
$state = 'end';
}
// add continued lines to the last item to keep them together
- if (!empty(${$state}) && trim($line !== '') && strpos($line, '- ') !== 0) {
+ if (!empty(${$state}) && trim($line) !== '' && strncmp($line, '- ', 2) !== 0) {
end(${$state});
${$state}[key(${$state})] .= "\n" . $line;
} else {
@@ -919,7 +919,7 @@ class ReleaseController extends Controller
protected function getChangelogs($what)
{
$changelogs = [];
- if (in_array('framework', $what)) {
+ if (\in_array('framework', $what)) {
$changelogs[] = $this->getFrameworkChangelog();
}
@@ -947,13 +947,13 @@ class ReleaseController extends Controller
protected function composerSetStability($what, $version)
{
$apps = [];
- if (in_array('app-advanced', $what)) {
+ if (\in_array('app-advanced', $what)) {
$apps[] = $this->basePath . '/apps/advanced/composer.json';
}
- if (in_array('app-basic', $what)) {
+ if (\in_array('app-basic', $what)) {
$apps[] = $this->basePath . '/apps/basic/composer.json';
}
- if (in_array('app-benchmark', $what)) {
+ if (\in_array('app-benchmark', $what)) {
$apps[] = $this->basePath . '/apps/benchmark/composer.json';
}
if (empty($apps)) {
diff --git a/build/controllers/Utf8Controller.php b/build/controllers/Utf8Controller.php
index 64e8c0a..ca5175d 100644
--- a/build/controllers/Utf8Controller.php
+++ b/build/controllers/Utf8Controller.php
@@ -29,7 +29,7 @@ class Utf8Controller extends Controller
public function actionCheckGuide($directory = null)
{
if ($directory === null) {
- $directory = dirname(dirname(__DIR__)) . '/docs';
+ $directory = \dirname(\dirname(__DIR__)) . '/docs';
}
if (is_file($directory)) {
$files = [$directory];
@@ -90,7 +90,7 @@ class Utf8Controller extends Controller
}
$hexcode = dechex($this->unicodeOrd($char));
- $hexcode = str_repeat('0', max(4 - strlen($hexcode), 0)) . $hexcode;
+ $hexcode = str_repeat('0', max(4 - \strlen($hexcode), 0)) . $hexcode;
$this->stdout(" at $line:$pos FOUND $what: 0x$hexcode '$char' http://unicode-table.com/en/$hexcode/\n");
}
@@ -105,20 +105,20 @@ class Utf8Controller extends Controller
*/
private function unicodeOrd($c)
{
- $h = ord($c[0]);
+ $h = \ord($c[0]);
if ($h <= 0x7F) {
return $h;
} elseif ($h < 0xC2) {
return false;
} elseif ($h <= 0xDF) {
- return ($h & 0x1F) << 6 | (ord($c[1]) & 0x3F);
+ return ($h & 0x1F) << 6 | (\ord($c[1]) & 0x3F);
} elseif ($h <= 0xEF) {
- return ($h & 0x0F) << 12 | (ord($c[1]) & 0x3F) << 6
- | (ord($c[2]) & 0x3F);
+ return ($h & 0x0F) << 12 | (\ord($c[1]) & 0x3F) << 6
+ | (\ord($c[2]) & 0x3F);
} elseif ($h <= 0xF4) {
- return ($h & 0x0F) << 18 | (ord($c[1]) & 0x3F) << 12
- | (ord($c[2]) & 0x3F) << 6
- | (ord($c[3]) & 0x3F);
+ return ($h & 0x0F) << 18 | (\ord($c[1]) & 0x3F) << 12
+ | (\ord($c[2]) & 0x3F) << 6
+ | (\ord($c[3]) & 0x3F);
}
return false;
diff --git a/composer.lock b/composer.lock
index d9ef2af..ccd7209 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,21 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "4708f40ee8cd75e770ca3207cfada97f",
- "content-hash": "50ec7b3fadeb5044752b5074e84acd74",
+ "content-hash": "c5363e83fd40667959bed938186dbdf2",
"packages": [
{
"name": "bower-asset/inputmask",
- "version": "3.3.7",
+ "version": "3.3.11",
"source": {
"type": "git",
"url": "https://github.com/RobinHerbots/Inputmask.git",
- "reference": "9835731cb78cac749734d94a1cb5bd70da4d3b10"
+ "reference": "5e670ad62f50c738388d4dcec78d2888505ad77b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/RobinHerbots/Inputmask/zipball/9835731cb78cac749734d94a1cb5bd70da4d3b10",
- "reference": "9835731cb78cac749734d94a1cb5bd70da4d3b10",
+ "url": "https://api.github.com/repos/RobinHerbots/Inputmask/zipball/5e670ad62f50c738388d4dcec78d2888505ad77b",
+ "reference": "5e670ad62f50c738388d4dcec78d2888505ad77b",
"shasum": null
},
"require": {
@@ -31,33 +30,21 @@
},
{
"name": "bower-asset/jquery",
- "version": "2.2.4",
+ "version": "3.2.1",
"source": {
"type": "git",
"url": "https://github.com/jquery/jquery-dist.git",
- "reference": "c0185ab7c75aab88762c5aae780b9d83b80eda72"
+ "reference": "77d2a51d0520d2ee44173afdf4e40a9201f5964e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/jquery/jquery-dist/zipball/c0185ab7c75aab88762c5aae780b9d83b80eda72",
- "reference": "c0185ab7c75aab88762c5aae780b9d83b80eda72",
- "shasum": ""
- },
- "type": "bower-asset-library",
- "extra": {
- "bower-asset-main": "dist/jquery.js",
- "bower-asset-ignore": [
- "package.json"
- ]
+ "url": "https://api.github.com/repos/jquery/jquery-dist/zipball/77d2a51d0520d2ee44173afdf4e40a9201f5964e",
+ "reference": "77d2a51d0520d2ee44173afdf4e40a9201f5964e",
+ "shasum": null
},
+ "type": "bower-asset",
"license": [
"MIT"
- ],
- "keywords": [
- "browser",
- "javascript",
- "jquery",
- "library"
]
},
{
@@ -90,34 +77,22 @@
},
{
"name": "bower-asset/yii2-pjax",
- "version": "v2.0.6",
+ "version": "2.0.7.1",
"source": {
"type": "git",
"url": "https://github.com/yiisoft/jquery-pjax.git",
- "reference": "60728da6ade5879e807a49ce59ef9a72039b8978"
+ "reference": "aef7b953107264f00234902a3880eb50dafc48be"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/yiisoft/jquery-pjax/zipball/60728da6ade5879e807a49ce59ef9a72039b8978",
- "reference": "60728da6ade5879e807a49ce59ef9a72039b8978",
- "shasum": ""
+ "url": "https://api.github.com/repos/yiisoft/jquery-pjax/zipball/aef7b953107264f00234902a3880eb50dafc48be",
+ "reference": "aef7b953107264f00234902a3880eb50dafc48be",
+ "shasum": null
},
"require": {
"bower-asset/jquery": ">=1.8"
},
- "type": "bower-asset-library",
- "extra": {
- "bower-asset-main": "./jquery.pjax.js",
- "bower-asset-ignore": [
- ".travis.yml",
- "Gemfile",
- "Gemfile.lock",
- "CONTRIBUTING.md",
- "vendor/",
- "script/",
- "test/"
- ]
- },
+ "type": "bower-asset",
"license": [
"MIT"
]
@@ -180,7 +155,7 @@
"markdown",
"markdown-extra"
],
- "time": "2017-07-16 21:13:23"
+ "time": "2017-07-16T21:13:23+00:00"
},
{
"name": "ezyang/htmlpurifier",
@@ -227,7 +202,7 @@
"keywords": [
"html"
],
- "time": "2017-06-03 02:28:16"
+ "time": "2017-06-03T02:28:16+00:00"
},
{
"name": "yiisoft/yii2-composer",
@@ -277,7 +252,7 @@
"extension installer",
"yii2"
],
- "time": "2016-12-20 13:26:02"
+ "time": "2016-12-20T13:26:02+00:00"
}
],
"packages-dev": [
@@ -312,7 +287,69 @@
}
],
"description": "a small tool to convert text file indentation",
- "time": "2014-05-23 14:40:08"
+ "time": "2014-05-23T14:40:08+00:00"
+ },
+ {
+ "name": "composer/semver",
+ "version": "1.4.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/semver.git",
+ "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/semver/zipball/c7cb9a2095a074d131b65a8a0cd294479d785573",
+ "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3.2 || ^7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.5 || ^5.0.5",
+ "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\Semver\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nils Adermann",
+ "email": "naderman@naderman.de",
+ "homepage": "http://www.naderman.de"
+ },
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ },
+ {
+ "name": "Rob Bast",
+ "email": "rob.bast@gmail.com",
+ "homepage": "http://robbast.nl"
+ }
+ ],
+ "description": "Semver library that offers utilities, version constraint parsing and validation.",
+ "keywords": [
+ "semantic",
+ "semver",
+ "validation",
+ "versioning"
+ ],
+ "time": "2016-08-30T16:08:34+00:00"
},
{
"name": "doctrine/annotations",
@@ -380,7 +417,7 @@
"docblock",
"parser"
],
- "time": "2015-08-31 12:32:49"
+ "time": "2015-08-31T12:32:49+00:00"
},
{
"name": "doctrine/instantiator",
@@ -434,7 +471,7 @@
"constructor",
"instantiate"
],
- "time": "2015-06-14 21:17:01"
+ "time": "2015-06-14T21:17:01+00:00"
},
{
"name": "doctrine/lexer",
@@ -488,40 +525,41 @@
"lexer",
"parser"
],
- "time": "2014-09-09 13:34:57"
+ "time": "2014-09-09T13:34:57+00:00"
},
{
"name": "friendsofphp/php-cs-fixer",
- "version": "v2.2.4",
+ "version": "v2.2.12",
"source": {
"type": "git",
"url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git",
- "reference": "5191e01d0fa0f579eb709350306cd11ad6427ca6"
+ "reference": "362ad63e80d05a238fbe542d75cc1f8e9246797e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/5191e01d0fa0f579eb709350306cd11ad6427ca6",
- "reference": "5191e01d0fa0f579eb709350306cd11ad6427ca6",
+ "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/362ad63e80d05a238fbe542d75cc1f8e9246797e",
+ "reference": "362ad63e80d05a238fbe542d75cc1f8e9246797e",
"shasum": ""
},
"require": {
+ "composer/semver": "^1.4",
"doctrine/annotations": "^1.2",
"ext-json": "*",
"ext-tokenizer": "*",
"gecko-packages/gecko-php-unit": "^2.0",
- "php": "^5.3.6 || >=7.0 <7.2",
+ "php": "^5.3.6 || >=7.0 <7.3",
"sebastian/diff": "^1.4",
- "symfony/console": "^2.4 || ^3.0",
- "symfony/event-dispatcher": "^2.1 || ^3.0",
- "symfony/filesystem": "^2.4 || ^3.0",
- "symfony/finder": "^2.2 || ^3.0",
- "symfony/options-resolver": "^2.6 || ^3.0",
+ "symfony/console": "^2.4 || ^3.0 || ^4.0",
+ "symfony/event-dispatcher": "^2.1 || ^3.0 || ^4.0",
+ "symfony/filesystem": "^2.4 || ^3.0 || ^4.0",
+ "symfony/finder": "^2.2 || ^3.0 || ^4.0",
+ "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0",
"symfony/polyfill-php54": "^1.0",
"symfony/polyfill-php55": "^1.3",
"symfony/polyfill-php70": "^1.0",
- "symfony/polyfill-xml": "^1.3",
- "symfony/process": "^2.3 || ^3.0",
- "symfony/stopwatch": "^2.5 || ^3.0"
+ "symfony/polyfill-php72": "^1.4",
+ "symfony/process": "^2.3 || ^3.0 || ^4.0",
+ "symfony/stopwatch": "^2.5 || ^3.0 || ^4.0"
},
"conflict": {
"hhvm": "<3.18"
@@ -529,13 +567,12 @@
"require-dev": {
"johnkary/phpunit-speedtrap": "^1.0.1",
"justinrainbow/json-schema": "^5.0",
+ "php-coveralls/php-coveralls": "^1.0.2",
"phpunit/phpunit": "^4.8.35 || ^5.4.3",
- "satooshi/php-coveralls": "^1.0",
- "symfony/phpunit-bridge": "^3.2.2"
+ "symfony/phpunit-bridge": "^3.2.2 || ^4.0"
},
"suggest": {
"ext-mbstring": "For handling non-UTF8 characters in cache signature.",
- "ext-xml": "For better performance.",
"symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible."
},
"bin": [
@@ -550,7 +587,13 @@
"autoload": {
"psr-4": {
"PhpCsFixer\\": "src/"
- }
+ },
+ "classmap": [
+ "tests/Test/AbstractFixerTestCase.php",
+ "tests/Test/AbstractIntegrationTestCase.php",
+ "tests/Test/IntegrationCase.php",
+ "tests/Test/IntegrationCaseFactory.php"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -567,20 +610,20 @@
}
],
"description": "A tool to automatically fix PHP code style",
- "time": "2017-05-24 21:55:27"
+ "time": "2017-11-26T20:41:43+00:00"
},
{
"name": "gecko-packages/gecko-php-unit",
- "version": "v2.1",
+ "version": "v2.2",
"source": {
"type": "git",
"url": "https://github.com/GeckoPackages/GeckoPHPUnit.git",
- "reference": "5b9e9622c7efd3b22655270b80c03f9e52878a6e"
+ "reference": "ab525fac9a9ffea219687f261b02008b18ebf2d1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/GeckoPackages/GeckoPHPUnit/zipball/5b9e9622c7efd3b22655270b80c03f9e52878a6e",
- "reference": "5b9e9622c7efd3b22655270b80c03f9e52878a6e",
+ "url": "https://api.github.com/repos/GeckoPackages/GeckoPHPUnit/zipball/ab525fac9a9ffea219687f261b02008b18ebf2d1",
+ "reference": "ab525fac9a9ffea219687f261b02008b18ebf2d1",
"shasum": ""
},
"require": {
@@ -589,24 +632,29 @@
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.4.3"
},
+ "suggest": {
+ "ext-dom": "When testing with xml.",
+ "ext-libxml": "When testing with xml.",
+ "phpunit/phpunit": "This is an extension for it so make sure you have it some way."
+ },
"type": "library",
"autoload": {
"psr-4": {
- "GeckoPackages\\PHPUnit\\": "src\\PHPUnit"
+ "GeckoPackages\\PHPUnit\\": "src/PHPUnit"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
- "description": "Additional PHPUnit tests.",
+ "description": "Additional PHPUnit asserts and constraints.",
"homepage": "https://github.com/GeckoPackages",
"keywords": [
"extension",
"filesystem",
"phpunit"
],
- "time": "2017-06-20 11:22:48"
+ "time": "2017-08-23T07:39:54+00:00"
},
{
"name": "ircmaxell/password-compat",
@@ -648,20 +696,20 @@
"hashing",
"password"
],
- "time": "2014-11-20 16:49:30"
+ "time": "2014-11-20T16:49:30+00:00"
},
{
"name": "paragonie/random_compat",
- "version": "v2.0.10",
+ "version": "v2.0.11",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
- "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d"
+ "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/paragonie/random_compat/zipball/634bae8e911eefa89c1abfbf1b66da679ac8f54d",
- "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d",
+ "url": "https://api.github.com/repos/paragonie/random_compat/zipball/5da4d3c796c275c55f057af5a643ae297d96b4d8",
+ "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8",
"shasum": ""
},
"require": {
@@ -696,7 +744,7 @@
"pseudorandom",
"random"
],
- "time": "2017-03-13 16:27:32"
+ "time": "2017-09-27T21:40:39+00:00"
},
{
"name": "phpdocumentor/reflection-docblock",
@@ -745,37 +793,37 @@
"email": "mike.vanriel@naenius.com"
}
],
- "time": "2016-01-25 08:17:30"
+ "time": "2016-01-25T08:17:30+00:00"
},
{
"name": "phpspec/prophecy",
- "version": "v1.7.0",
+ "version": "1.7.3",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
- "reference": "93d39f1f7f9326d746203c7c056f300f7f126073"
+ "reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073",
- "reference": "93d39f1f7f9326d746203c7c056f300f7f126073",
+ "url": "https://api.github.com/repos/phpspec/prophecy/zipball/e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf",
+ "reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.0.2",
"php": "^5.3|^7.0",
- "phpdocumentor/reflection-docblock": "^2.0|^3.0.2",
+ "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0",
"sebastian/comparator": "^1.1|^2.0",
"sebastian/recursion-context": "^1.0|^2.0|^3.0"
},
"require-dev": {
"phpspec/phpspec": "^2.5|^3.2",
- "phpunit/phpunit": "^4.8 || ^5.6.5"
+ "phpunit/phpunit": "^4.8.35 || ^5.7"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.6.x-dev"
+ "dev-master": "1.7.x-dev"
}
},
"autoload": {
@@ -808,7 +856,7 @@
"spy",
"stub"
],
- "time": "2017-03-02 20:05:34"
+ "time": "2017-11-24T13:59:53+00:00"
},
{
"name": "phpunit/php-code-coverage",
@@ -870,20 +918,20 @@
"testing",
"xunit"
],
- "time": "2015-10-06 15:47:00"
+ "time": "2015-10-06T15:47:00+00:00"
},
{
"name": "phpunit/php-file-iterator",
- "version": "1.4.2",
+ "version": "1.4.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
- "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5"
+ "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5",
- "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4",
+ "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4",
"shasum": ""
},
"require": {
@@ -917,7 +965,7 @@
"filesystem",
"iterator"
],
- "time": "2016-10-03 07:40:28"
+ "time": "2017-11-27T13:52:08+00:00"
},
{
"name": "phpunit/php-text-template",
@@ -958,7 +1006,7 @@
"keywords": [
"template"
],
- "time": "2015-06-21 13:50:34"
+ "time": "2015-06-21T13:50:34+00:00"
},
{
"name": "phpunit/php-timer",
@@ -1007,7 +1055,7 @@
"keywords": [
"timer"
],
- "time": "2017-02-26 11:10:40"
+ "time": "2017-02-26T11:10:40+00:00"
},
{
"name": "phpunit/php-token-stream",
@@ -1056,7 +1104,7 @@
"keywords": [
"tokenizer"
],
- "time": "2017-02-27 10:12:30"
+ "time": "2017-02-27T10:12:30+00:00"
},
{
"name": "phpunit/phpunit",
@@ -1128,7 +1176,7 @@
"testing",
"xunit"
],
- "time": "2017-01-26 16:15:36"
+ "time": "2017-01-26T16:15:36+00:00"
},
{
"name": "phpunit/phpunit-mock-objects",
@@ -1184,7 +1232,7 @@
"mock",
"xunit"
],
- "time": "2015-10-02 06:51:40"
+ "time": "2015-10-02T06:51:40+00:00"
},
{
"name": "psr/log",
@@ -1231,7 +1279,7 @@
"psr",
"psr-3"
],
- "time": "2016-10-10 12:19:37"
+ "time": "2016-10-10T12:19:37+00:00"
},
{
"name": "sebastian/comparator",
@@ -1295,7 +1343,7 @@
"compare",
"equality"
],
- "time": "2017-01-29 09:50:25"
+ "time": "2017-01-29T09:50:25+00:00"
},
{
"name": "sebastian/diff",
@@ -1347,7 +1395,7 @@
"keywords": [
"diff"
],
- "time": "2017-05-22 07:24:03"
+ "time": "2017-05-22T07:24:03+00:00"
},
{
"name": "sebastian/environment",
@@ -1397,7 +1445,7 @@
"environment",
"hhvm"
],
- "time": "2016-08-18 05:49:44"
+ "time": "2016-08-18T05:49:44+00:00"
},
{
"name": "sebastian/exporter",
@@ -1464,7 +1512,7 @@
"export",
"exporter"
],
- "time": "2016-06-17 09:04:28"
+ "time": "2016-06-17T09:04:28+00:00"
},
{
"name": "sebastian/global-state",
@@ -1515,7 +1563,7 @@
"keywords": [
"global state"
],
- "time": "2015-10-12 03:26:01"
+ "time": "2015-10-12T03:26:01+00:00"
},
{
"name": "sebastian/recursion-context",
@@ -1568,7 +1616,7 @@
],
"description": "Provides functionality to recursively process PHP variables",
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
- "time": "2016-10-03 07:41:43"
+ "time": "2016-10-03T07:41:43+00:00"
},
{
"name": "sebastian/version",
@@ -1603,20 +1651,20 @@
],
"description": "Library that helps with managing the version number of Git-hosted PHP projects",
"homepage": "https://github.com/sebastianbergmann/version",
- "time": "2015-06-21 13:59:46"
+ "time": "2015-06-21T13:59:46+00:00"
},
{
"name": "symfony/console",
- "version": "v2.8.25",
+ "version": "v2.8.31",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "46e65f8d98c9ab629bbfcc16a4ff023f61c37fb2"
+ "reference": "7cad097cf081c0ab3d0322cc38d34ee80484d86f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/46e65f8d98c9ab629bbfcc16a4ff023f61c37fb2",
- "reference": "46e65f8d98c9ab629bbfcc16a4ff023f61c37fb2",
+ "url": "https://api.github.com/repos/symfony/console/zipball/7cad097cf081c0ab3d0322cc38d34ee80484d86f",
+ "reference": "7cad097cf081c0ab3d0322cc38d34ee80484d86f",
"shasum": ""
},
"require": {
@@ -1664,20 +1712,20 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
- "time": "2017-07-03 08:04:30"
+ "time": "2017-11-16T15:20:19+00:00"
},
{
"name": "symfony/debug",
- "version": "v2.8.25",
+ "version": "v2.8.31",
"source": {
"type": "git",
"url": "https://github.com/symfony/debug.git",
- "reference": "8470d7701177a88edeb0cec59b44d50ef4477e9b"
+ "reference": "a0a29e9867debabdace779a20a9385c623a23bbd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/debug/zipball/8470d7701177a88edeb0cec59b44d50ef4477e9b",
- "reference": "8470d7701177a88edeb0cec59b44d50ef4477e9b",
+ "url": "https://api.github.com/repos/symfony/debug/zipball/a0a29e9867debabdace779a20a9385c623a23bbd",
+ "reference": "a0a29e9867debabdace779a20a9385c623a23bbd",
"shasum": ""
},
"require": {
@@ -1721,20 +1769,20 @@
],
"description": "Symfony Debug Component",
"homepage": "https://symfony.com",
- "time": "2017-06-01 20:52:29"
+ "time": "2017-10-24T13:48:52+00:00"
},
{
"name": "symfony/event-dispatcher",
- "version": "v2.8.25",
+ "version": "v2.8.31",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "1377400fd641d7d1935981546aaef780ecd5bf6d"
+ "reference": "b59aacf238fadda50d612c9de73b74751872a903"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/1377400fd641d7d1935981546aaef780ecd5bf6d",
- "reference": "1377400fd641d7d1935981546aaef780ecd5bf6d",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b59aacf238fadda50d612c9de73b74751872a903",
+ "reference": "b59aacf238fadda50d612c9de73b74751872a903",
"shasum": ""
},
"require": {
@@ -1781,20 +1829,20 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
- "time": "2017-06-02 07:47:27"
+ "time": "2017-11-05T15:25:56+00:00"
},
{
"name": "symfony/filesystem",
- "version": "v2.8.25",
+ "version": "v2.8.31",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
- "reference": "714b1036010c354ae2b25d7f9ca27e14e265e9f2"
+ "reference": "10507c5f24577b0ad971b0d22097c823b2b45dd3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/filesystem/zipball/714b1036010c354ae2b25d7f9ca27e14e265e9f2",
- "reference": "714b1036010c354ae2b25d7f9ca27e14e265e9f2",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/10507c5f24577b0ad971b0d22097c823b2b45dd3",
+ "reference": "10507c5f24577b0ad971b0d22097c823b2b45dd3",
"shasum": ""
},
"require": {
@@ -1830,20 +1878,20 @@
],
"description": "Symfony Filesystem Component",
"homepage": "https://symfony.com",
- "time": "2017-07-11 07:12:11"
+ "time": "2017-11-07T14:08:47+00:00"
},
{
"name": "symfony/finder",
- "version": "v2.8.25",
+ "version": "v2.8.31",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
- "reference": "4f4e84811004e065a3bb5ceeb1d9aa592630f9ad"
+ "reference": "efeceae6a05a9b2fcb3391333f1d4a828ff44ab8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/finder/zipball/4f4e84811004e065a3bb5ceeb1d9aa592630f9ad",
- "reference": "4f4e84811004e065a3bb5ceeb1d9aa592630f9ad",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/efeceae6a05a9b2fcb3391333f1d4a828ff44ab8",
+ "reference": "efeceae6a05a9b2fcb3391333f1d4a828ff44ab8",
"shasum": ""
},
"require": {
@@ -1879,20 +1927,20 @@
],
"description": "Symfony Finder Component",
"homepage": "https://symfony.com",
- "time": "2017-06-01 20:52:29"
+ "time": "2017-11-05T15:25:56+00:00"
},
{
"name": "symfony/options-resolver",
- "version": "v2.8.25",
+ "version": "v2.8.31",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
- "reference": "55bf349a3395afad81a369d57b3b99c373fbbdf5"
+ "reference": "e4e64cb8e01981425098bfb6000ac397206dfc25"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/options-resolver/zipball/55bf349a3395afad81a369d57b3b99c373fbbdf5",
- "reference": "55bf349a3395afad81a369d57b3b99c373fbbdf5",
+ "url": "https://api.github.com/repos/symfony/options-resolver/zipball/e4e64cb8e01981425098bfb6000ac397206dfc25",
+ "reference": "e4e64cb8e01981425098bfb6000ac397206dfc25",
"shasum": ""
},
"require": {
@@ -1933,20 +1981,20 @@
"configuration",
"options"
],
- "time": "2017-04-12 14:07:15"
+ "time": "2017-11-05T15:25:56+00:00"
},
{
"name": "symfony/polyfill-mbstring",
- "version": "v1.4.0",
+ "version": "v1.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "f29dca382a6485c3cbe6379f0c61230167681937"
+ "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f29dca382a6485c3cbe6379f0c61230167681937",
- "reference": "f29dca382a6485c3cbe6379f0c61230167681937",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296",
+ "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296",
"shasum": ""
},
"require": {
@@ -1958,7 +2006,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.4-dev"
+ "dev-master": "1.6-dev"
}
},
"autoload": {
@@ -1992,20 +2040,20 @@
"portable",
"shim"
],
- "time": "2017-06-09 14:24:12"
+ "time": "2017-10-11T12:05:26+00:00"
},
{
"name": "symfony/polyfill-php54",
- "version": "v1.4.0",
+ "version": "v1.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php54.git",
- "reference": "7dd1a8b9f0442273fdfeb1c4f5eaff6890a82789"
+ "reference": "d7810a14b2c6c1aff415e1bb755f611b3d5327bc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/7dd1a8b9f0442273fdfeb1c4f5eaff6890a82789",
- "reference": "7dd1a8b9f0442273fdfeb1c4f5eaff6890a82789",
+ "url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/d7810a14b2c6c1aff415e1bb755f611b3d5327bc",
+ "reference": "d7810a14b2c6c1aff415e1bb755f611b3d5327bc",
"shasum": ""
},
"require": {
@@ -2014,7 +2062,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.4-dev"
+ "dev-master": "1.6-dev"
}
},
"autoload": {
@@ -2050,20 +2098,20 @@
"portable",
"shim"
],
- "time": "2017-06-09 08:25:21"
+ "time": "2017-10-11T12:05:26+00:00"
},
{
"name": "symfony/polyfill-php55",
- "version": "v1.4.0",
+ "version": "v1.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php55.git",
- "reference": "94566239a7720cde0820f15f0cc348ddb51ba51d"
+ "reference": "b64e7f0c37ecf144ecc16668936eef94e628fbfd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/94566239a7720cde0820f15f0cc348ddb51ba51d",
- "reference": "94566239a7720cde0820f15f0cc348ddb51ba51d",
+ "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/b64e7f0c37ecf144ecc16668936eef94e628fbfd",
+ "reference": "b64e7f0c37ecf144ecc16668936eef94e628fbfd",
"shasum": ""
},
"require": {
@@ -2073,7 +2121,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.4-dev"
+ "dev-master": "1.6-dev"
}
},
"autoload": {
@@ -2106,20 +2154,20 @@
"portable",
"shim"
],
- "time": "2017-06-09 08:25:21"
+ "time": "2017-10-11T12:05:26+00:00"
},
{
"name": "symfony/polyfill-php70",
- "version": "v1.4.0",
+ "version": "v1.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php70.git",
- "reference": "032fd647d5c11a9ceab8ee8747e13b5448e93874"
+ "reference": "0442b9c0596610bd24ae7b5f0a6cdbbc16d9fcff"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/032fd647d5c11a9ceab8ee8747e13b5448e93874",
- "reference": "032fd647d5c11a9ceab8ee8747e13b5448e93874",
+ "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/0442b9c0596610bd24ae7b5f0a6cdbbc16d9fcff",
+ "reference": "0442b9c0596610bd24ae7b5f0a6cdbbc16d9fcff",
"shasum": ""
},
"require": {
@@ -2129,7 +2177,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.4-dev"
+ "dev-master": "1.6-dev"
}
},
"autoload": {
@@ -2165,20 +2213,20 @@
"portable",
"shim"
],
- "time": "2017-06-09 14:24:12"
+ "time": "2017-10-11T12:05:26+00:00"
},
{
"name": "symfony/polyfill-php72",
- "version": "v1.4.0",
+ "version": "v1.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php72.git",
- "reference": "d3a71580c1e2cab33b6d705f0ec40e9015e14d5c"
+ "reference": "6de4f4884b97abbbed9f0a84a95ff2ff77254254"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/d3a71580c1e2cab33b6d705f0ec40e9015e14d5c",
- "reference": "d3a71580c1e2cab33b6d705f0ec40e9015e14d5c",
+ "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/6de4f4884b97abbbed9f0a84a95ff2ff77254254",
+ "reference": "6de4f4884b97abbbed9f0a84a95ff2ff77254254",
"shasum": ""
},
"require": {
@@ -2187,7 +2235,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.4-dev"
+ "dev-master": "1.6-dev"
}
},
"autoload": {
@@ -2220,68 +2268,20 @@
"portable",
"shim"
],
- "time": "2017-06-09 08:25:21"
- },
- {
- "name": "symfony/polyfill-xml",
- "version": "v1.4.0",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/polyfill-xml.git",
- "reference": "89326af9d173053826ae8fe26a6f49597ba4e9f3"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-xml/zipball/89326af9d173053826ae8fe26a6f49597ba4e9f3",
- "reference": "89326af9d173053826ae8fe26a6f49597ba4e9f3",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.3",
- "symfony/polyfill-php72": "~1.4"
- },
- "type": "metapackage",
- "extra": {
- "branch-alias": {
- "dev-master": "1.4-dev"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Symfony polyfill for xml's utf8_encode and utf8_decode functions",
- "homepage": "https://symfony.com",
- "keywords": [
- "compatibility",
- "polyfill",
- "portable",
- "shim"
- ],
- "time": "2017-06-09 08:25:21"
+ "time": "2017-10-11T12:05:26+00:00"
},
{
"name": "symfony/process",
- "version": "v2.8.25",
+ "version": "v2.8.31",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
- "reference": "57e52a0a6a80ea0aec4fc1b785a7920a95cb88a8"
+ "reference": "d25449e031f600807949aab7cadbf267712f4eee"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/process/zipball/57e52a0a6a80ea0aec4fc1b785a7920a95cb88a8",
- "reference": "57e52a0a6a80ea0aec4fc1b785a7920a95cb88a8",
+ "url": "https://api.github.com/repos/symfony/process/zipball/d25449e031f600807949aab7cadbf267712f4eee",
+ "reference": "d25449e031f600807949aab7cadbf267712f4eee",
"shasum": ""
},
"require": {
@@ -2317,20 +2317,20 @@
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
- "time": "2017-07-03 08:04:30"
+ "time": "2017-11-05T15:25:56+00:00"
},
{
"name": "symfony/stopwatch",
- "version": "v2.8.25",
+ "version": "v2.8.31",
"source": {
"type": "git",
"url": "https://github.com/symfony/stopwatch.git",
- "reference": "e02577b841394a78306d7b547701bb7bb705bad5"
+ "reference": "533bb9d7c2da1c6d2da163ecf0f22043ea98f59b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/stopwatch/zipball/e02577b841394a78306d7b547701bb7bb705bad5",
- "reference": "e02577b841394a78306d7b547701bb7bb705bad5",
+ "url": "https://api.github.com/repos/symfony/stopwatch/zipball/533bb9d7c2da1c6d2da163ecf0f22043ea98f59b",
+ "reference": "533bb9d7c2da1c6d2da163ecf0f22043ea98f59b",
"shasum": ""
},
"require": {
@@ -2366,20 +2366,20 @@
],
"description": "Symfony Stopwatch Component",
"homepage": "https://symfony.com",
- "time": "2017-04-12 14:07:15"
+ "time": "2017-11-10T18:59:36+00:00"
},
{
"name": "symfony/yaml",
- "version": "v2.8.25",
+ "version": "v2.8.31",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
- "reference": "4c29dec8d489c4e37cf87ccd7166cd0b0e6a45c5"
+ "reference": "d819bf267e901727141fe828ae888486fd21236e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/yaml/zipball/4c29dec8d489c4e37cf87ccd7166cd0b0e6a45c5",
- "reference": "4c29dec8d489c4e37cf87ccd7166cd0b0e6a45c5",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/d819bf267e901727141fe828ae888486fd21236e",
+ "reference": "d819bf267e901727141fe828ae888486fd21236e",
"shasum": ""
},
"require": {
@@ -2415,7 +2415,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
- "time": "2017-06-01 20:52:29"
+ "time": "2017-11-05T15:25:56+00:00"
}
],
"aliases": [],
diff --git a/contrib/completion/bash/yii b/contrib/completion/bash/yii
index 084325a..bc06b7a 100644
--- a/contrib/completion/bash/yii
+++ b/contrib/completion/bash/yii
@@ -15,6 +15,7 @@ _yii()
local cur opts yii command
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
+ prev="${COMP_WORDS[COMP_CWORD-1]}"
yii="${COMP_WORDS[0]}"
# exit if ./yii does not exist
@@ -31,9 +32,10 @@ _yii()
[[ $cur == $command ]] && state="command"
[[ $cur != $command ]] && state="option"
[[ $cur = *=* ]] && state="value"
+ [[ $prev == "help" ]] && state="help"
case $state in
- command)
+ command|help)
# complete command/route if not given
# fetch available commands from ./yii help/list command
opts=$($yii help/list 2> /dev/null)
diff --git a/contrib/completion/zsh/_yii b/contrib/completion/zsh/_yii
index e85ebc3..01f4765 100644
--- a/contrib/completion/zsh/_yii
+++ b/contrib/completion/zsh/_yii
@@ -3,6 +3,7 @@
_yii() {
local state command lastArgument commands options executive
lastArgument=${words[${#words[@]}]}
+ prevArgument=${words[${#words[@]}-1]}
executive=$words[1]
# lookup for command
@@ -16,9 +17,10 @@ _yii() {
[[ $lastArgument == $command ]] && state="command"
[[ $lastArgument != $command ]] && state="option"
+ [[ $prevArgument == "help" ]] && state="help"
case $state in
- command)
+ command|help)
commands=("${(@f)$(${executive} help/list 2>/dev/null)}")
_describe 'command' commands
;;
diff --git a/docs/guide-es/caching-data.md b/docs/guide-es/caching-data.md
index 6280c4b..2c70061 100644
--- a/docs/guide-es/caching-data.md
+++ b/docs/guide-es/caching-data.md
@@ -79,8 +79,8 @@ se muestra un listado con los componentes de caché disponibles:
* [[yii\caching\MemCache]]: utiliza las extensiones de PHP [memcache](http://php.net/manual/es/book.memcache.php) y [memcached](http://php.net/manual/es/book.memcached.php). Esta opción puede ser considerada como la más rápida cuando la caché es manejada en una aplicación distribuida (ej. con varios servidores, con balance de carga, etc..)
* [[yii\redis\Cache]]: implementa un componente de caché basado en [Redis](http://redis.io/) que almacenan pares clave-valor (requiere la versión 2.6.12 de redis).
* [[yii\caching\WinCache]]: utiliza la extensión de PHP [WinCache](http://iis.net/downloads/microsoft/wincache-extension) ([ver también](http://php.net/manual/es/book.wincache.php)).
-* [[yii\caching\XCache]]: utiliza la extensión de PHP [XCache](http://xcache.lighttpd.net/).
-* [[yii\caching\ZendDataCache]]: utiliza [Zend Data Cache](http://files.zend.com/help/Zend-Server-6/zend-server.htm#data_cache_component.htm) como el medio fundamental de caché.
+* [[yii\caching\XCache]] _(deprecated)_: utiliza la extensión de PHP [XCache](http://xcache.lighttpd.net/).
+* [[yii\caching\ZendDataCache]] _(deprecated)_: utiliza [Zend Data Cache](http://files.zend.com/help/Zend-Server-6/zend-server.htm#data_cache_component.htm) como el medio fundamental de caché.
> Tip: Puedes utilizar diferentes tipos de almacenamiento de caché en la misma aplicación. Una estrategia común es la de usar almacenamiento de caché en memoria para almacenar datos que son pequeños pero que son utilizados constantemente (ej. datos estadísticos), y utilizar el almacenamiento de caché en archivos o en base de datos para guardar datos que son grandes y utilizados con menor frecuencia (ej. contenido de página).
diff --git a/docs/guide-es/concept-di-container.md b/docs/guide-es/concept-di-container.md
index 391de0a..e8d13fc 100644
--- a/docs/guide-es/concept-di-container.md
+++ b/docs/guide-es/concept-di-container.md
@@ -75,7 +75,7 @@ En este caso, el contenedor usará una llamada de retorno PHP registrada para co
clase. La llamada de retorno se responsabiliza de que dependencias debe inyectar al nuevo objeto creado. Por ejemplo,
```php
-$container->set('Foo', function () {
+$container->set('Foo', function ($container, $params, $config) {
return new Foo(new Bar);
});
diff --git a/docs/guide-es/concept-events.md b/docs/guide-es/concept-events.md
index 65ae841..2cce5cc 100644
--- a/docs/guide-es/concept-events.md
+++ b/docs/guide-es/concept-events.md
@@ -218,7 +218,7 @@ use yii\base\Event;
use yii\db\ActiveRecord;
Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
- Yii::trace(get_class($event->sender) . ' is inserted');
+ Yii::debug(get_class($event->sender) . ' is inserted');
});
```
diff --git a/docs/guide-es/runtime-logging.md b/docs/guide-es/runtime-logging.md
index a09ec93..bb01a21 100644
--- a/docs/guide-es/runtime-logging.md
+++ b/docs/guide-es/runtime-logging.md
@@ -20,7 +20,7 @@ En esta sección, se describirán principalmente los dos primeros pasos.
Registrar mensajes de anotación es tan simple como llamar a uno de los siguientes métodos de registro de anotaciones.
-* [[Yii::trace()]]: registra un mensaje para trazar el funcionamiento de una sección de código. Se usa principalmente
+* [[Yii::debug()]]: registra un mensaje para trazar el funcionamiento de una sección de código. Se usa principalmente
para tareas de desarrollo.
* [[Yii::info()]]: registra un mensaje que transmite información útil.
* [[Yii::warning()]]: registra un mensaje de advertencia que indica que ha sucedido algo inesperado.
@@ -32,7 +32,7 @@ tiene que ser registrado, mientras que `$category` es la categoría del registro
ejemplo registra la huella del mensaje para la categoría `application`:
```php
-Yii::trace('start calculating average revenue');
+Yii::debug('start calculating average revenue');
```
> Info: Los mensajes de registro pueden ser tanto cadenas de texto como datos complejos, como arrays u objetos.
@@ -47,7 +47,7 @@ efectiva de organizarlos es usar la constante predefinida (magic constant) de PH
categoría. Además este es el enfoque que se usa en el código del núcleo (core) del framework Yii. Por ejemplo,
```php
-Yii::trace('start calculating average revenue', __METHOD__);
+Yii::debug('start calculating average revenue', __METHOD__);
```
La constante `__METHOD__` equivale al nombre del método (con el prefijo del nombre completo del nombre de clase) donde
@@ -131,7 +131,7 @@ La propiedad [[yii\log\Target::levels|levels]] es un array que consta de uno o v
* `error`: correspondiente a los mensajes registrados por [[Yii::error()]].
* `warning`: correspondiente a los mensajes registrados por [[Yii::warning()]].
* `info`: correspondiente a los mensajes registrados por [[Yii::info()]].
-* `trace`: correspondiente a los mensajes registrados por [[Yii::trace()]].
+* `trace`: correspondiente a los mensajes registrados por [[Yii::debug()]].
* `profile`: correspondiente a los mensajes registrados por [[Yii::beginProfile()]] y [[Yii::endProfile()]], que se
explicará más detalladamente en la subsección [Perfiles](#performance-profiling).
@@ -290,7 +290,7 @@ número configurando la propiedad [[yii\log\Target::exportInterval|exportInterva
```
Debido al nivel de configuración de la liberación y exportación de mensajes, de forma predeterminada cuando se llama a
-`Yii::trace()` o cualquier otro método de registro de mensajes, NO veremos el registro de mensaje inmediatamente en
+`Yii::debug()` o cualquier otro método de registro de mensajes, NO veremos el registro de mensaje inmediatamente en
los destinos de registros. Esto podría ser un problema para algunas aplicaciones de consola de ejecución
prolongada (long-running). Para hacer que los mensajes de registro aparezcan inmediatamente en los destinos de
registro se deben establecer [[yii\log\Dispatcher::flushInterval|flushInterval]] y
diff --git a/docs/guide-es/start-installation.md b/docs/guide-es/start-installation.md
index 62d0e5a..70a57e4 100644
--- a/docs/guide-es/start-installation.md
+++ b/docs/guide-es/start-installation.md
@@ -189,7 +189,7 @@ DocumentRoot "path/to/basic/web"
Para utilizar [Nginx](http://wiki.nginx.org/), debes instalar PHP como un [FPM SAPI](http://php.net/install.fpm).
Utiliza la siguiente configuración de Nginx, reemplazando `path/to/basic/web` con la ruta real a
-`basic/web` y `mysite.local` con el hostname real a servir.
+`basic/web` y `mysite.test` con el hostname real a servir.
```
server {
@@ -199,7 +199,7 @@ server {
listen 80; ## listen for ipv4
#listen [::]:80 default_server ipv6only=on; ## listen for ipv6
- server_name mysite.local;
+ server_name mysite.test;
root /path/to/basic/web;
index index.php;
diff --git a/docs/guide-es/structure-filters.md b/docs/guide-es/structure-filters.md
index 39b9158..a54ddc3 100644
--- a/docs/guide-es/structure-filters.md
+++ b/docs/guide-es/structure-filters.md
@@ -94,7 +94,7 @@ class ActionTimeFilter extends ActionFilter
public function afterAction($action, $result)
{
$time = microtime(true) - $this->_startTime;
- Yii::trace("Action '{$action->uniqueId}' spent $time second.");
+ Yii::debug("Action '{$action->uniqueId}' spent $time second.");
return parent::afterAction($action, $result);
}
}
diff --git a/docs/guide-fr/caching-data.md b/docs/guide-fr/caching-data.md
index 6302394..41b8590 100644
--- a/docs/guide-fr/caching-data.md
+++ b/docs/guide-fr/caching-data.md
@@ -100,8 +100,8 @@ Yii prend en charge un large panel de supports de stockage pour cache. Ce qui su
(une version de redis égale ou supérieure à 2.6.12 est nécessaire).
* [[yii\caching\WinCache]]: utilise le [WinCache](http://iis.net/downloads/microsoft/wincache-extension) PHP
([voir aussi l'extension](http://php.net/manual/en/book.wincache.php)).
-* [[yii\caching\XCache]]: utilise l'extension PHP [XCache](http://xcache.lighttpd.net/).
-* [[yii\caching\ZendDataCache]]: utilise le
+* [[yii\caching\XCache]] _(deprecated)_: utilise l'extension PHP [XCache](http://xcache.lighttpd.net/).
+* [[yii\caching\ZendDataCache]] _(deprecated)_: utilise le
[cache de données Zend](http://files.zend.com/help/Zend-Server-6/zend-server.htm#data_cache_component.htm)
en tant que médium de cache sous-jacent.
diff --git a/docs/guide-fr/concept-di-container.md b/docs/guide-fr/concept-di-container.md
index 06af821..0aeb060 100644
--- a/docs/guide-fr/concept-di-container.md
+++ b/docs/guide-fr/concept-di-container.md
@@ -99,7 +99,7 @@ $container->get('Foo', [], [
Dans ce cas, le conteneur utilise une fonction de rappel PRP enregistrée pour construire de nouvelles instances d'une classe. À chaque fois que [[yii\di\Container::get()]] est appelée, la fonction de rappel correspondante est invoquée. Cette fonction de rappel est chargée de la résolution des dépendances et de leur injection appropriée dans les objets nouvellement créés. Par exemple :
```php
-$container->set('Foo', function () {
+$container->set('Foo', function ($container, $params, $config) {
$foo = new Foo(new Bar);
// ... autres initialisations ...
return $foo;
@@ -113,7 +113,7 @@ Pour cacher la logique complexe de construction des nouveaux objets, vous pouvez
```php
class FooBuilder
{
- public static function build()
+ public static function build($container, $params, $config)
{
$foo = new Foo(new Bar);
// ... autres initialisations ...
diff --git a/docs/guide-fr/concept-events.md b/docs/guide-fr/concept-events.md
index d88d514..3d3afcc 100644
--- a/docs/guide-fr/concept-events.md
+++ b/docs/guide-fr/concept-events.md
@@ -186,7 +186,7 @@ use yii\base\Event;
use yii\db\ActiveRecord;
Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
- Yii::trace(get_class($event->sender) . ' is inserted');
+ Yii::debug(get_class($event->sender) . ' is inserted');
});
```
@@ -263,7 +263,7 @@ Pour gérer l'évenement `EVENT_DANCE` déclenché par n'importe laquelle de ces
```php
Event::on('app\interfaces\DanceEventInterface', DanceEventInterface::EVENT_DANCE, function ($event) {
- Yii::trace(get_class($event->sender) . ' just danced'); // Will log that Dog or Developer danced
+ Yii::debug(get_class($event->sender) . ' just danced'); // Will log that Dog or Developer danced
});
```
diff --git a/docs/guide-fr/runtime-logging.md b/docs/guide-fr/runtime-logging.md
index bb0896a..8cd79ce 100644
--- a/docs/guide-fr/runtime-logging.md
+++ b/docs/guide-fr/runtime-logging.md
@@ -16,7 +16,7 @@ Dans cette section, nous décrivons principalement les deux premières étapes.
Enregistrer des messages est aussi simple que d'appeler une des méthodes suivantes :
-* [[Yii::trace()]]: enregistre un message pour garder une trace de comment un morceau de code fonctionne. Cela est utilisé principalement en développement.
+* [[Yii::debug()]]: enregistre un message pour garder une trace de comment un morceau de code fonctionne. Cela est utilisé principalement en développement.
* [[Yii::info()]]: enregistre un message qui contient quelques informations utiles.
* [[Yii::warning()]]: enregistre un message d'avertissement qui indique que quelque chose d'inattendu s'est produit.
* [[Yii::error()]]: enregistre une erreur fatale qui doit être analysée dès que possible.
@@ -24,7 +24,7 @@ Enregistrer des messages est aussi simple que d'appeler une des méthodes suivan
Ces méthodes enregistrent les messages à différents niveaux de sévérité et dans différentes catégories. Elles partagent la même signature `function ($message, $category = 'application')`, où `$message` représente le message à enregistrer, tandis que `$category` est la catégorie de ce message. Le code de l'exemple qui suit enregistre un message de trace dans la catégorie `application`:
```php
-Yii::trace('start calculating average revenue');
+Yii::debug('start calculating average revenue');
```
> Info: les messages enregistrés peuvent être des chaînes de caractères aussi bien que des données complexes telles que des tableaux ou des objets. Il est de la responsabilité des [cibles d'enregistrement](#log-targets) de traiter correctement ces messages. Par défaut, si un message enregistré n'est pas un chaîne de caractères, il est exporté comme une chaîne de caractères en appelant la méthode [[yii\helpers\VarDumper::export()]].
@@ -32,7 +32,7 @@ Yii::trace('start calculating average revenue');
Pour mieux organiser et filtrer les messages enregistrés, il est recommandé que vous spécifiiez une catégorie appropriée pour chacun des messages. Vous pouvez choisir une schéma de nommage hiérarchisé pour les catégories, ce qui facilitera le filtrage des messages par les [cibles d'enregistrement](#log-targets) sur la base de ces catégories. Un schéma de nommage simple et efficace est d'utiliser la constante magique `__METHOD__` de PHP dans les noms de catégorie. Par exemple :
```php
-Yii::trace('start calculating average revenue', __METHOD__);
+Yii::debug('start calculating average revenue', __METHOD__);
```
La constante magique `__METHOD__` est évaluée comme le nom de la méthode (préfixée par le nom pleinement qualifié de la classe), là où la constante apparaît. Par exemple, elle est égale à `'app\controllers\RevenueController::calculate'` si la ligne suivante est utilisée dans cette méthode.
@@ -100,7 +100,7 @@ La propriété [[yii\log\Target::levels|levels]] accepte un tableau constitué d
* `error`: correspondant aux messages enregistrés par [[Yii::error()]].
* `warning`: correspondant aux messages enregistrés par [[Yii::warning()]].
* `info`: correspondant aux messages enregistrés par [[Yii::info()]].
-* `trace`: correspondant aux messages enregistrés par [[Yii::trace()]].
+* `trace`: correspondant aux messages enregistrés par [[Yii::debug()]].
* `profile`: correspondant aux messages enregistrés par [[Yii::beginProfile()]] et [[Yii::endProfile()]], et qui sera expliqué en détails dans la sous-section [Profilage de la performance](#performance-profiling).
Si vous ne spécifiez pas la propriété [[yii\log\Target::levels|levels]], cela signifie que la cible traitera les messages de *n'importe quel* niveau de sévérité.
@@ -222,7 +222,7 @@ Lorsque l'[[yii\log\Logger|objet *logger*]] purge les messages enregistrés vers
]
```
-À cause des niveaux de purge et d'exportation, par défaut, lorsque vous appelez `Yii::trace()` ou toute autre méthode d'enregistrement, vous ne voyez PAS immédiatement le message enregistré dans la cible. Cela peut représenter un problème pour pour certaines applications de console qui durent longtemps. Pour faire en sorte que les messages apparaissent immédiatement dans les cibles d'enregistrement, vous devriez définir les propriétés [[yii\log\Dispatcher::flushInterval|flushInterval]] et [[yii\log\Target::exportInterval|exportInterval]] toutes deux à 1, comme montré ci-après :
+À cause des niveaux de purge et d'exportation, par défaut, lorsque vous appelez `Yii::debug()` ou toute autre méthode d'enregistrement, vous ne voyez PAS immédiatement le message enregistré dans la cible. Cela peut représenter un problème pour pour certaines applications de console qui durent longtemps. Pour faire en sorte que les messages apparaissent immédiatement dans les cibles d'enregistrement, vous devriez définir les propriétés [[yii\log\Dispatcher::flushInterval|flushInterval]] et [[yii\log\Target::exportInterval|exportInterval]] toutes deux à 1, comme montré ci-après :
```php
return [
diff --git a/docs/guide-fr/start-installation.md b/docs/guide-fr/start-installation.md
index 24a1c49..fa36149 100644
--- a/docs/guide-fr/start-installation.md
+++ b/docs/guide-fr/start-installation.md
@@ -120,7 +120,7 @@ DocumentRoot "path/to/basic/web"
### Configuration Nginx recommandée
Pour utiliser Nginx, vous devez avoir installé PHP en utilisant [FPM SAPI](http://php.net/install.fpm).
-Utilisez la configuration Nginx suivante, en remplaçant `path/to/basic/web` par le chemin vers le dossier `basic/web` et `mysite.local` par le nom d'hôte de votre serveur.
+Utilisez la configuration Nginx suivante, en remplaçant `path/to/basic/web` par le chemin vers le dossier `basic/web` et `mysite.test` par le nom d'hôte de votre serveur.
```
server {
@@ -130,7 +130,7 @@ server {
listen 80; ## port pour ipv4
#listen [::]:80 default_server ipv6only=on; ## port pour ipv6
- server_name mysite.local;
+ server_name mysite.test;
root /path/to/basic/web;
index index.php;
diff --git a/docs/guide-fr/structure-filters.md b/docs/guide-fr/structure-filters.md
index b6b897d..e198d35 100644
--- a/docs/guide-fr/structure-filters.md
+++ b/docs/guide-fr/structure-filters.md
@@ -72,7 +72,7 @@ class ActionTimeFilter extends ActionFilter
public function afterAction($action, $result)
{
$time = microtime(true) - $this->_startTime;
- Yii::trace("Action '{$action->uniqueId}' spent $time second.");
+ Yii::debug("Action '{$action->uniqueId}' spent $time second.");
return parent::afterAction($action, $result);
}
}
diff --git a/docs/guide-fr/tutorial-i18n.md b/docs/guide-fr/tutorial-i18n.md
index 147a93e..6929531 100644
--- a/docs/guide-fr/tutorial-i18n.md
+++ b/docs/guide-fr/tutorial-i18n.md
@@ -327,7 +327,7 @@ tandis que `one` correspond à `21` ou `101`:
```
Ces noms d'arguments spéciaux tels que `other`, `few`, `many` et autres varient en fonction de la langue. Pour savoir lesquels utiliser pour une locale particulière, reportez-vous aux "Plural Rules, Cardinal" à [http://intl.rmcreative.ru/](http://intl.rmcreative.ru/).
-En alternative, vous pouvez vous reporter aux [rules reference at unicode.org](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html).
+En alternative, vous pouvez vous reporter aux [rules reference at unicode.org](http://cldr.unicode.org/index/cldr-spec/plural-rules).
> Note: le message en russe ci-dessus est principalement utilisé comme message traduit, pas comme message source, sauf si vous définissez la [[yii\base\Application::$sourceLanguage|langue source]] de votre application comme étant `ru-RU` et traduisez à partir du russe.
>
diff --git a/docs/guide-id/start-installation.md b/docs/guide-id/start-installation.md
index 6fce5c4..54fb407 100644
--- a/docs/guide-id/start-installation.md
+++ b/docs/guide-id/start-installation.md
@@ -189,7 +189,7 @@ DocumentRoot "path/to/basic/web"
Untuk menggunakan [Nginx](http://wiki.nginx.org/), Anda harus menginstal PHP sebagai [FPM SAPI](http://php.net/install.fpm).
Anda dapat menggunakan konfigurasi Nginx berikut, menggantikan `path/to/basic/web` dengan path yang sebenarnya untuk
-`basic/web` dan `mysite.local` dengan hostname yang sebenarnya untuk server.
+`basic/web` dan `mysite.test` dengan hostname yang sebenarnya untuk server.
```nginx
server {
@@ -199,7 +199,7 @@ server {
listen 80; ## listen for ipv4
#listen [::]:80 default_server ipv6only=on; ## listen for ipv6
- server_name mysite.local;
+ server_name mysite.test;
root /path/to/basic/web;
index index.php;
diff --git a/docs/guide-it/start-installation.md b/docs/guide-it/start-installation.md
index 1bfb50c..7315f29 100644
--- a/docs/guide-it/start-installation.md
+++ b/docs/guide-it/start-installation.md
@@ -161,7 +161,7 @@ DocumentRoot "path/to/basic/web"
### Configurazione consigliata di Nginx
Devi aver installato PHP con il demone [FPM](http://php.net/install.fpm) per usare [Nginx](http://wiki.nginx.org/).
-Usa questa configurazione per Nginx, sostituendo `path/to/basic/web` con il percorso reale di `basic/web` e `mysite.local` con
+Usa questa configurazione per Nginx, sostituendo `path/to/basic/web` con il percorso reale di `basic/web` e `mysite.test` con
il nome reale del server web.
```
@@ -172,7 +172,7 @@ server {
listen 80; ## listen for ipv4
#listen [::]:80 default_server ipv6only=on; ## listen for ipv6
- server_name mysite.local;
+ server_name mysite.test;
root /path/to/basic/web;
index index.php;
diff --git a/docs/guide-ja/caching-data.md b/docs/guide-ja/caching-data.md
index 6149dba..473b468 100644
--- a/docs/guide-ja/caching-data.md
+++ b/docs/guide-ja/caching-data.md
@@ -111,8 +111,8 @@ Yii はさまざまなキャッシュストレージをサポートしていま
分散型のアプリケーションでキャッシュを扱うときには最速の一つとして考えることができます (例えば、複数台のサーバで、ロードバランサがある、などの場合) 。
* [[yii\redis\Cache]]: [Redis](http://redis.io/) の key-value ストアに基づいてキャッシュコンポーネントを実装しています。(Redis の バージョン 2.6.12 以降が必要です) 。
* [[yii\caching\WinCache]]: PHP の [WinCache](http://iis.net/downloads/microsoft/wincache-extension) ([関連リンク](http://php.net/manual/ja/book.wincache.php)) 拡張モジュールを使用します。
-* [[yii\caching\XCache]]: PHP の [XCache](http://xcache.lighttpd.net/) 拡張モジュールを使用します。
-* [[yii\caching\ZendDataCache]]: キャッシュメディアして [Zend Data Cache](http://files.zend.com/help/Zend-Server-6/zend-server.htm#data_cache_component.htm) を使用します。
+* [[yii\caching\XCache]] _(deprecated)_: PHP の [XCache](http://xcache.lighttpd.net/) 拡張モジュールを使用します。
+* [[yii\caching\ZendDataCache]] _(deprecated)_: キャッシュメディアして [Zend Data Cache](http://files.zend.com/help/Zend-Server-6/zend-server.htm#data_cache_component.htm) を使用します。
> Tip: 同じアプリケーション内で異なるキャッシュを使用することもできます。一般的なやり方として、小さくとも常に使用されるデータ (例えば、統計データ) を格納する場合はメモリベースのキャッシュストレージを使用し、大きくて使用頻度の低いデータ (例えば、ページコンテント) を格納する場合はファイルベース、またはデータベースのキャッシュストレージを使用します 。
diff --git a/docs/guide-ja/concept-di-container.md b/docs/guide-ja/concept-di-container.md
index ea85c0a..6a76670 100644
--- a/docs/guide-ja/concept-di-container.md
+++ b/docs/guide-ja/concept-di-container.md
@@ -114,7 +114,7 @@ $container->get('Foo', [], [
たとえば
```php
-$container->set('Foo', function () {
+$container->set('Foo', function ($container, $params, $config) {
$foo = new Foo(new Bar);
// ... その他の初期化 ...
return $foo;
@@ -129,7 +129,7 @@ $foo = $container->get('Foo');
```php
class FooBuilder
{
- public static function build()
+ public static function build($container, $params, $config)
{
$foo = new Foo(new Bar);
// ... その他の初期化 ...
@@ -413,7 +413,7 @@ $container->setDefinitions([
'class' => 'app\components\Response',
'format' => 'json'
],
- 'app\storage\DocumentsReader' => function () {
+ 'app\storage\DocumentsReader' => function ($container, $params, $config) {
$fs = new app\storage\FileStorage('/var/tempfiles');
return new app\storage\DocumentsReader($fs);
}
diff --git a/docs/guide-ja/concept-events.md b/docs/guide-ja/concept-events.md
index 2422d55..1cbe19a 100644
--- a/docs/guide-ja/concept-events.md
+++ b/docs/guide-ja/concept-events.md
@@ -209,7 +209,7 @@ use yii\base\Event;
use yii\db\ActiveRecord;
Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
- Yii::trace(get_class($event->sender) . ' が挿入されました');
+ Yii::debug(get_class($event->sender) . ' が挿入されました');
});
```
@@ -292,7 +292,7 @@ class Developer extends Component implements DanceEventInterface
```php
Event::on('app\interfaces\DanceEventInterface', DanceEventInterface::EVENT_DANCE, function ($event) {
- Yii::trace(get_class($event->sender) . ' が躍り上がって喜んだ。'); // 犬または開発者が躍り上がって喜んだことをログに記録。
+ Yii::debug(get_class($event->sender) . ' が躍り上がって喜んだ。'); // 犬または開発者が躍り上がって喜んだことをログに記録。
});
```
diff --git a/docs/guide-ja/runtime-logging.md b/docs/guide-ja/runtime-logging.md
index f644b63..5a4f308 100644
--- a/docs/guide-ja/runtime-logging.md
+++ b/docs/guide-ja/runtime-logging.md
@@ -17,7 +17,7 @@ Yii のロギングフレームワークを使うためには、下記のステ
ログメッセージを記録することは、次のログ記録メソッドのどれかを呼び出すだけの簡単なことです。
-* [[Yii::trace()]]: コードの断片がどのように走ったかをトレースするメッセージを記録します。主として開発のために使用します。
+* [[Yii::debug()]]: コードの断片がどのように走ったかをトレースするメッセージを記録します。主として開発のために使用します。
* [[Yii::info()]]: 何らかの有用な情報を伝えるメッセージを記録します。
* [[Yii::warning()]]: 何か予期しないことが発生したことを示す警告メッセージを記録します。
* [[Yii::error()]]: 出来るだけ早急に調査すべき致命的なエラーを記録します。
@@ -27,7 +27,7 @@ Yii のロギングフレームワークを使うためには、下記のステ
次のコードサンプルは、トレースメッセージをデフォルトのカテゴリである `application` の下に記録するものです。
```php
-Yii::trace('平均収益の計算を開始');
+Yii::debug('平均収益の計算を開始');
```
> Info: ログメッセージは文字列でも、配列やオブジェクトのような複雑なデータでも構いません。
@@ -40,7 +40,7 @@ Yii::trace('平均収益の計算を開始');
これは、Yii フレームワークのコアコードでも使われている方法です。例えば、
```php
-Yii::trace('平均収益の計算を開始', __METHOD__);
+Yii::debug('平均収益の計算を開始', __METHOD__);
```
`__METHOD__` という定数は、それが出現する場所のメソッド名 (完全修飾のクラス名が前置されます) として評価されます。
@@ -116,7 +116,7 @@ Yii は下記のログターゲットをあらかじめ内蔵しています。
* `error`: [[Yii::error()]] によって記録されたメッセージに対応。
* `warning`: [[Yii::warning()]] によって記録されたメッセージに対応。
* `info`: [[Yii::info()]] によって記録されたメッセージに対応。
-* `trace`: [[Yii::trace()]] によって記録されたメッセージに対応。
+* `trace`: [[Yii::debug()]] によって記録されたメッセージに対応。
* `profile`: [[Yii::beginProfile()]] と [[Yii::endProfile()]] によって記録されたメッセージに対応。
これについては、[プロファイリング](#performance-profiling) の項で詳細に説明します。
@@ -255,7 +255,7 @@ return [
]
```
-デフォルトの状態では、吐き出しとエクスポートの間隔の設定のために、`Yii::trace()` やその他のログ記録メソッドを呼んでも、ただちには、ログメッセージはログターゲットに出現しません。
+デフォルトの状態では、吐き出しとエクスポートの間隔の設定のために、`Yii::debug()` やその他のログ記録メソッドを呼んでも、ただちには、ログメッセージはログターゲットに出現しません。
このことは、長時間にわたって走るコンソールアプリケーションでは、問題になる場合もあります。
各ログメッセージがただちにログターゲットに出現するようにするためには、下記のように、[[yii\log\Dispatcher::flushInterval|flushInterval]] と [[yii\log\Target::exportInterval|exportInterval]] の両方を 1 に設定しなければなりません。
diff --git a/docs/guide-ja/start-installation.md b/docs/guide-ja/start-installation.md
index 917ad5f..f9a07d6 100644
--- a/docs/guide-ja/start-installation.md
+++ b/docs/guide-ja/start-installation.md
@@ -202,7 +202,7 @@ DocumentRoot "path/to/basic/web"
[Nginx](http://wiki.nginx.org/) を使うためには、PHP を [FPM SAPI](http://jp1.php.net/install.fpm) としてインストールしなければなりません。
下記の Nginx の設定を使うことができます。
-`path/to/basic/web` の部分を `basic/web` の実際のパスに置き換え、`mysite.local` を実際のサーバのホスト名に置き換えてください。
+`path/to/basic/web` の部分を `basic/web` の実際のパスに置き換え、`mysite.test` を実際のサーバのホスト名に置き換えてください。
```nginx
server {
@@ -212,7 +212,7 @@ server {
listen 80; ## listen for ipv4
#listen [::]:80 default_server ipv6only=on; ## listen for ipv6
- server_name mysite.local;
+ server_name mysite.test;
root /path/to/basic/web;
index index.php;
diff --git a/docs/guide-ja/structure-filters.md b/docs/guide-ja/structure-filters.md
index 890b1b2..4b69260 100644
--- a/docs/guide-ja/structure-filters.md
+++ b/docs/guide-ja/structure-filters.md
@@ -83,7 +83,7 @@ class ActionTimeFilter extends ActionFilter
public function afterAction($action, $result)
{
$time = microtime(true) - $this->_startTime;
- Yii::trace("アクション '{$action->uniqueId}' は $time 秒を消費。");
+ Yii::debug("アクション '{$action->uniqueId}' は $time 秒を消費。");
return parent::afterAction($action, $result);
}
}
diff --git a/docs/guide-ja/tutorial-i18n.md b/docs/guide-ja/tutorial-i18n.md
index 81a1397..809d46f 100644
--- a/docs/guide-ja/tutorial-i18n.md
+++ b/docs/guide-ja/tutorial-i18n.md
@@ -459,7 +459,7 @@ echo \Yii::t('app', 'There {n,plural,=0{are no cats} =1{is one cat} other{are #
これら `other`、`few`、`many` などの特別な引数の名前は言語によって異なります。
特定のロケールに対してどんな引数を指定すべきかを学ぶためには、[http://intl.rmcreative.ru/](http://intl.rmcreative.ru/) の "Plural Rules, Cardinal" を参照してください。
-あるいは、その代りに、[unicode.org の規則のリファレンス](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html) を参照することも出来ます。
+あるいは、その代りに、[unicode.org の規則のリファレンス](http://cldr.unicode.org/index/cldr-spec/plural-rules) を参照することも出来ます。
> Note: 上記のロシア語のメッセージのサンプルは、主として翻訳メッセージとして使用されるものです。
diff --git a/docs/guide-pl/start-installation.md b/docs/guide-pl/start-installation.md
index bbb122d..c11aac4 100644
--- a/docs/guide-pl/start-installation.md
+++ b/docs/guide-pl/start-installation.md
@@ -203,7 +203,7 @@ DocumentRoot "path/to/basic/web"
Aby użyć [Nginx](http://wiki.nginx.org/) powinienieś zainstalować PHP jako [FPM SAPI](http://php.net/install.fpm).
Możesz użyć przedstawionej poniżej konfiguracji Nginx, zastępując jedynie ścieżkę `path/to/basic/web` aktualną ścieżką do `basic/web` Twojej aplikacji oraz
-`mysite.local` aktualną nazwą hosta.
+`mysite.test` aktualną nazwą hosta.
```nginx
server {
@@ -213,7 +213,7 @@ server {
listen 80; ## nasłuchuj ipv4
#listen [::]:80 default_server ipv6only=on; ## nasłuchuj ipv6
- server_name mysite.local;
+ server_name mysite.test;
root /path/to/basic/web;
index index.php;
diff --git a/docs/guide-pt-BR/caching-data.md b/docs/guide-pt-BR/caching-data.md
index aec00ba..05c7c60 100644
--- a/docs/guide-pt-BR/caching-data.md
+++ b/docs/guide-pt-BR/caching-data.md
@@ -96,8 +96,8 @@ Yii suporta uma ampla gama de sistemas de cache. A seguir um resumo:
[Redis](http://redis.io/) (requer redis versão 2.6.12 ou mais recente).
* [[yii\caching\WinCache]]: usa a extensão PHP [WinCache](http://iis.net/downloads/microsoft/wincache-extension)
([veja também](http://php.net/manual/en/book.wincache.php)).
-* [[yii\caching\XCache]]: usa a extensão PHP [XCache](http://xcache.lighttpd.net/).
-* [[yii\caching\ZendDataCache]]: usa
+* [[yii\caching\XCache]] _(deprecated)_: usa a extensão PHP [XCache](http://xcache.lighttpd.net/).
+* [[yii\caching\ZendDataCache]] _(deprecated)_: usa
[Cache de Dados Zend](http://files.zend.com/help/Zend-Server-6/zend-server.htm#data_cache_component.htm)
como o meio de cache subjacente.
diff --git a/docs/guide-pt-BR/concept-di-container.md b/docs/guide-pt-BR/concept-di-container.md
index f0b0739..cb511ae 100644
--- a/docs/guide-pt-BR/concept-di-container.md
+++ b/docs/guide-pt-BR/concept-di-container.md
@@ -75,7 +75,7 @@ Cada vez que [[yii\di\Container::get()]] for chamado, o callable correspondente
O callable é responsável por resolver as dependências e injetá-las de forma adequada para os objetos recém-criados. Por exemplo:
```php
-$container->set('Foo', function () {
+$container->set('Foo', function ($container, $params, $config) {
$foo = new Foo(new Bar);
// ... Outras inicializações...
return $foo;
@@ -89,7 +89,7 @@ Para ocultar a lógica complexa da construção de um novo objeto você pode usa
```php
class FooBuilder
{
- public static function build()
+ public static function build($container, $params, $config)
{
return function () {
$foo = new Foo(new Bar);
diff --git a/docs/guide-pt-BR/concept-events.md b/docs/guide-pt-BR/concept-events.md
index a9be2a0..e126c98 100644
--- a/docs/guide-pt-BR/concept-events.md
+++ b/docs/guide-pt-BR/concept-events.md
@@ -192,7 +192,7 @@ use yii\base\Event;
use yii\db\ActiveRecord;
Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
- Yii::trace(get_class($event->sender) . ' is inserted');
+ Yii::debug(get_class($event->sender) . ' is inserted');
});
```
diff --git a/docs/guide-pt-BR/runtime-logging.md b/docs/guide-pt-BR/runtime-logging.md
index d487244..555df02 100644
--- a/docs/guide-pt-BR/runtime-logging.md
+++ b/docs/guide-pt-BR/runtime-logging.md
@@ -16,7 +16,7 @@ Nesta seção, vamos descrever principalmente os dois primeiros passos.
Gravar mensagens de log é tão simples como chamar um dos seguintes métodos de registro:
-* [[Yii::trace()]]: gravar uma mensagem para rastrear como um determinado trecho de código é executado. Isso é principalmente para o uso de desenvolvimento.
+* [[Yii::debug()]]: gravar uma mensagem para rastrear como um determinado trecho de código é executado. Isso é principalmente para o uso de desenvolvimento.
* [[Yii::info()]]: gravar uma mensagem que transmite algumas informações úteis.
* [[Yii::warning()]]: gravar uma mensagem de aviso que indica que algo inesperado aconteceu.
* [[Yii::error()]]: gravar um erro fatal que deve ser investigado o mais rápido possível.
@@ -24,7 +24,7 @@ Gravar mensagens de log é tão simples como chamar um dos seguintes métodos de
Estes métodos gravam mensagens de log em vários *níveis* e *categorias*. Eles compartilham a mesma assinatura de função `function ($message, $category = 'application')`, onde `$message` significa a mensagem de log a ser gravada, enquanto `$category` é a categoria da mensagem de log. O código no exemplo a seguir registra uma mensagem de rastreamento sob a categoria padrão `application`:
```php
-Yii::trace('start calculating average revenue');
+Yii::debug('start calculating average revenue');
```
> Observação: Mensagens de log podem ser strings, bem como dados complexos, tais como arrays ou objetos. É da responsabilidade dos [destinos de log](#log-targets) lidar adequadamente com as mensagens de log. Por padrão, se uma mensagem de log não for uma string, ela será exportada como uma string chamando [[yii\helpers\VarDumper::export()]].
@@ -32,7 +32,7 @@ Yii::trace('start calculating average revenue');
Para melhor organizar e filtrar as mensagens de log, é recomendável que você especifique uma categoria apropriada para cada mensagem de log. Você pode escolher um esquema de nomenclatura hierárquica para as categorias, o que tornará mais fácil para os [destinos de log](#log-targets) filtrar mensagens com base em suas categorias. Um esquema de nomes simples, mas eficaz é usar a constante mágica PHP `__METHOD__` para os nomes das categorias. Esta é também a abordagem utilizada no código central do framework Yii. Por exemplo,
```php
-Yii::trace('start calculating average revenue', __METHOD__);
+Yii::debug('start calculating average revenue', __METHOD__);
```
A constante `__METHOD__` corresponde ao nome do método (prefixado com o caminho completo do nome da classe) onde a constante aparece. Por exemplo, é igual a string `'app\controllers\RevenueController::calculate'` se o código acima for chamado dentro deste método.
@@ -100,7 +100,7 @@ A propriedade [[yii\log\Target::levels|levels]] é um array que consiste em um o
* `error`: corresponde a mensagens logadas por [[Yii::error()]].
* `warning`: corresponde a mensagens logadas por [[Yii::warning()]].
* `info`: corresponde a mensagens logadas por [[Yii::info()]].
-* `trace`: corresponde a mensagens logadas por [[Yii::trace()]].
+* `trace`: corresponde a mensagens logadas por [[Yii::debug()]].
* `profile`: corresponde a mensagens logadas por [[Yii::beginProfile()]] e [[Yii::endProfile()]], que será explicado em mais detalhes na subseção [Perfil de Desempenho](#performance-profiling).
Se você não especificar a propriedade [[yii\log\Target::levels|levels]], significa que o alvo de log processará mensagens de *qualquer* nível.
@@ -218,7 +218,7 @@ Quando o [[yii\log\Logger|logger object]] libera mensagens de log para os [alvos
]
```
-Devido a configuração de nível, liberação e exportação, por padrão quando você chama `Yii::trace()` ou qualquer outro método de log, você NÃO verá a mensagem de log imediatamente no destino. Isto poderia ser um problema para algumas aplicações console de longa execução. Para fazer cada mensagem de log aparecer imediatamente no destino, você deve configurar ambos [[yii\log\Dispatcher::flushInterval|flushInterval]] e [[yii\log\Target::exportInterval|exportInterval]] para 1, como mostrado a seguir:
+Devido a configuração de nível, liberação e exportação, por padrão quando você chama `Yii::debug()` ou qualquer outro método de log, você NÃO verá a mensagem de log imediatamente no destino. Isto poderia ser um problema para algumas aplicações console de longa execução. Para fazer cada mensagem de log aparecer imediatamente no destino, você deve configurar ambos [[yii\log\Dispatcher::flushInterval|flushInterval]] e [[yii\log\Target::exportInterval|exportInterval]] para 1, como mostrado a seguir:
```php
return [
diff --git a/docs/guide-pt-BR/start-installation.md b/docs/guide-pt-BR/start-installation.md
index 8023e63..e210981 100644
--- a/docs/guide-pt-BR/start-installation.md
+++ b/docs/guide-pt-BR/start-installation.md
@@ -202,7 +202,7 @@ DocumentRoot "path/to/basic/web"
Você deve ter instalado o PHP como um [FPM SAPI](http://php.net/install.fpm) para
usar o [Nginx](http://wiki.nginx.org/). Use a seguinte configuração do Nginx,
-substituindo `path/to/basic/web` com o caminho real para `basic/web` e `mysite.local`
+substituindo `path/to/basic/web` com o caminho real para `basic/web` e `mysite.test`
com o nome de host real a servidor.
```
@@ -213,7 +213,7 @@ server {
listen 80; ## listen for ipv4
#listen [::]:80 default_server ipv6only=on; ## listen for ipv6
- server_name mysite.local;
+ server_name mysite.test;
root /path/to/basic/web;
index index.php;
diff --git a/docs/guide-pt-BR/structure-filters.md b/docs/guide-pt-BR/structure-filters.md
index 49533a8..409f213 100644
--- a/docs/guide-pt-BR/structure-filters.md
+++ b/docs/guide-pt-BR/structure-filters.md
@@ -105,7 +105,7 @@ class ActionTimeFilter extends ActionFilter
public function afterAction($action, $result)
{
$time = microtime(true) - $this->_startTime;
- Yii::trace("Action '{$action->uniqueId}' spent $time second.");
+ Yii::debug("Action '{$action->uniqueId}' spent $time second.");
return parent::afterAction($action, $result);
}
}
diff --git a/docs/guide-ru/README.md b/docs/guide-ru/README.md
index 61c06c8..99d4ff3 100644
--- a/docs/guide-ru/README.md
+++ b/docs/guide-ru/README.md
@@ -79,7 +79,7 @@ All Rights Reserved.
* [Active Record](db-active-record.md) - Получение объектов AR, работа с ними и определение связей.
* [Миграции](db-migrations.md) - Контроль версий схемы данных при работе в команде.
* [Sphinx](https://github.com/yiisoft/yii2-sphinx/blob/master/docs/guide-ru/README.md)
-* [Redis](https://github.com/yiisoft/yii2-redis/blob/master/docs/guide/README.md)
+* [Redis](https://github.com/yiisoft/yii2-redis/blob/master/docs/guide-ru/README.md)
* [MongoDB](https://github.com/yiisoft/yii2-mongodb/blob/master/docs/guide-ru/README.md)
* [ElasticSearch](https://github.com/yiisoft/yii2-elasticsearch/blob/master/docs/guide/README.md)
@@ -175,6 +175,7 @@ All Rights Reserved.
* [Окружение виртуального хостинга](tutorial-shared-hosting.md)
* [Шаблонизаторы](tutorial-template-engines.md)
* [Работа со сторонним кодом](tutorial-yii-integration.md)
+* [Использование Yii в качестве микро-framework'а](tutorial-yii-as-micro-framework.md)
Виджеты
diff --git a/docs/guide-ru/caching-data.md b/docs/guide-ru/caching-data.md
index f6fb13c..4a65bb1 100644
--- a/docs/guide-ru/caching-data.md
+++ b/docs/guide-ru/caching-data.md
@@ -102,8 +102,8 @@ Yii поддерживает множество хранилищ кэша:
* [[yii\caching\MemCache]]: использует расширения PHP [memcache](http://php.net/manual/en/book.memcache.php) и [memcached](http://php.net/manual/en/book.memcached.php). Этот вариант может рассматриваться как самый быстрый при работе в распределенных приложениях (например, с несколькими серверами, балансировкой нагрузки и так далее);
* [[yii\redis\Cache]]: реализует компонент кэша на основе [Redis](http://redis.io/), хранилища ключ-значение (требуется Redis версии 2.6.12 или выше);
* [[yii\caching\WinCache]]: использует расширение PHP [WinCache](http://iis.net/downloads/microsoft/wincache-extension) ([смотрите также](http://php.net/manual/en/book.wincache.php));
-* [[yii\caching\XCache]]: использует расширение PHP [XCache](http://xcache.lighttpd.net/);
-* [[yii\caching\ZendDataCache]]: использует [Zend Data Cache](http://files.zend.com/help/Zend-Server-6/zend-server.htm#data_cache_component.htm).
+* [[yii\caching\XCache]] _(deprecated)_: использует расширение PHP [XCache](http://xcache.lighttpd.net/);
+* [[yii\caching\ZendDataCache]] _(deprecated)_: использует [Zend Data Cache](http://files.zend.com/help/Zend-Server-6/zend-server.htm#data_cache_component.htm).
> Tip: Вы можете использовать разные способы хранения кэша в одном приложении. Общая стратегия заключается в использовании памяти под хранение небольших часто используемых данных (например, статистические данные). Для больших и реже используемых данных (например, содержимое страницы) лучше использовать файлы или базу данных.
diff --git a/docs/guide-ru/concept-di-container.md b/docs/guide-ru/concept-di-container.md
index aaaa3ad..07d083f 100644
--- a/docs/guide-ru/concept-di-container.md
+++ b/docs/guide-ru/concept-di-container.md
@@ -151,7 +151,7 @@ $container->setDefinitions([
'class' => 'app\components\Response',
'format' => 'json'
],
- 'app\storage\DocumentsReader' => function () {
+ 'app\storage\DocumentsReader' => function ($container, $params, $config) {
$fs = new app\storage\FileStorage('/var/tempfiles');
return new app\storage\DocumentsReader($fs);
}
@@ -243,7 +243,7 @@ $reader = $container->get('app\storage\DocumentsReader');
Callback отвечает за разрешения зависимостей и внедряет их в соответствии с вновь создаваемыми объектами. Например,
```php
-$container->set('Foo', function () {
+$container->set('Foo', function ($container, $params, $config) {
$foo = new Foo(new Bar);
// ... дополнительная инициализация
return $foo;
@@ -258,7 +258,7 @@ callable:
```php
class FooBuilder
{
- public static function build()
+ public static function build($container, $params, $config)
{
$foo = new Foo(new Bar);
// ... дополнительная инициализация ...
diff --git a/docs/guide-ru/concept-events.md b/docs/guide-ru/concept-events.md
index acff9b7..12d01ab 100644
--- a/docs/guide-ru/concept-events.md
+++ b/docs/guide-ru/concept-events.md
@@ -188,7 +188,7 @@ use yii\base\Event;
use yii\db\ActiveRecord;
Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
- Yii::trace(get_class($event->sender) . ' добавлен');
+ Yii::debug(get_class($event->sender) . ' добавлен');
});
```
@@ -266,7 +266,7 @@ class Developer extends Component implements DanceEventInterface
```php
Event::on('app\interfaces\DanceEventInterface', DanceEventInterface::EVENT_DANCE, function ($event) {
- Yii::trace(get_class($event->sender) . ' just danced'); // Оставит запись в журнале о том, что кто-то танцевал
+ Yii::debug(get_class($event->sender) . ' just danced'); // Оставит запись в журнале о том, что кто-то танцевал
});
```
diff --git a/docs/guide-ru/db-migrations.md b/docs/guide-ru/db-migrations.md
index a55e00c..69ec182 100644
--- a/docs/guide-ru/db-migrations.md
+++ b/docs/guide-ru/db-migrations.md
@@ -266,7 +266,7 @@ yii migrate/create create_post_table --fields="author_id:integer:notNull:foreign
class m160328_040430_create_post_table extends Migration
{
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function up()
{
@@ -314,7 +314,7 @@ class m160328_040430_create_post_table extends Migration
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function down()
{
@@ -465,7 +465,7 @@ yii migrate/create create_junction_table_for_post_and_tag_tables
class m160328_041642_create_junction_table_for_post_and_tag_tables extends Migration
{
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function up()
{
@@ -512,7 +512,7 @@ class m160328_041642_create_junction_table_for_post_and_tag_tables extends Migra
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function down()
{
diff --git a/docs/guide-ru/input-validation.md b/docs/guide-ru/input-validation.md
index dacd5f9..6e3cca6 100644
--- a/docs/guide-ru/input-validation.md
+++ b/docs/guide-ru/input-validation.md
@@ -358,8 +358,8 @@ class MyForm extends Model
public function validateCountry($attribute, $params)
{
- if (!in_array($this->$attribute, ['USA', 'Web'])) {
- $this->addError($attribute, 'Страна должна быть либо "USA" или "Web".');
+ if (!in_array($this->$attribute, ['USA', 'Indonesia'])) {
+ $this->addError($attribute, 'Страна должна быть либо "USA" или "Indonesia".');
}
}
}
@@ -384,7 +384,9 @@ class MyForm extends Model
Вы можете реализовать свою логику проверки путем переопределения метода
[[yii\validators\Validator::validateAttribute()]]. Если атрибут не прошел проверку, вызвать
[[yii\base\Model::addError()]],
-чтобы сохранить сообщение об ошибке в модели, как это делают [встроенные валидаторы](#inline-validators). Например:
+чтобы сохранить сообщение об ошибке в модели, как это делают [встроенные валидаторы](#inline-validators).
+
+Валидация может быть помещена в отдельный класс [[components/validators/CountryValidator]]. В этом случае можно использовать метод [[yii\validators\Validator::addError()]] для того, чтобы добавить своё сообщение об ошибке в модель:
```php
namespace app\components;
@@ -395,8 +397,8 @@ class CountryValidator extends Validator
{
public function validateAttribute($model, $attribute)
{
- if (!in_array($model->$attribute, ['USA', 'Web'])) {
- $this->addError($model, $attribute, 'Страна должна быть либо "USA" или "Web".');
+ if (!in_array($model->$attribute, ['USA', 'Indonesia'])) {
+ $this->addError($model, $attribute, 'Страна должна быть либо "{country1}" либо "{country2}".', ['country1' => 'USA', 'country2' => 'Indonesia']);
}
}
}
diff --git a/docs/guide-ru/intro-yii.md b/docs/guide-ru/intro-yii.md
index 0c2fbdd..867a7a4 100644
--- a/docs/guide-ru/intro-yii.md
+++ b/docs/guide-ru/intro-yii.md
@@ -1,16 +1,15 @@
Что такое Yii?
==============
-Yii – это высокопроизводительный компонентный PHP фреймворк, предназначенный для быстрой разработки современных веб
-приложений. Слово Yii (произносится как `Йи` `[ji:]`) в китайском языке означает «простой и эволюционирующий». Также Yii
+Yii – это высокопроизводительный компонентный PHP фреймворк, предназначенный для быстрой разработки современных веб-приложений. Слово Yii (произносится как `Йи` `[ji:]`) в китайском языке означает «простой и эволюционирующий». Также Yii
может расшифровываться как акроним **Yes It Is**!
Для каких задач больше всего подходит Yii?
------------------------------------------
-Yii – это универсальный фреймворк и может быть задействован во всех типах веб приложений. Благодаря его компонентной
-структуре и отличной поддержке кэширования, фреймворк особенно подходит для разработки таких крупных проектов как
+Yii – это универсальный фреймворк и может быть задействован во всех типах веб-приложений. Благодаря его компонентной
+структуре и отличной поддержке кэширования, фреймворк особенно подходит для разработки таких крупных проектов, как
порталы, форумы, CMS, магазины или RESTful-приложения.
@@ -20,15 +19,15 @@ Yii – это универсальный фреймворк и может бы
Если вы уже знакомы с другими фреймворками, вам наверняка будет интересно сравнить их с Yii.
- Как и многие другие PHP фреймворки, для организации кода Yii использует архитектурный паттерн MVC (Model-View-Controller).
-- Yii придерживается философии простого и элегантного кода не пытаясь усложнять дизайн только ради следования каким-либо
+- Yii придерживается философии простого и элегантного кода, не пытаясь усложнять дизайн только ради следования каким-либо
шаблонам проектирования.
- Yii является full-stack фреймворком и включает в себя проверенные и хорошо зарекомендовавшие себя возможности, такие как
ActiveRecord для реляционных и NoSQL баз данных, поддержку REST API, многоуровневое кэширование и другие.
-- Yii отлично расширяем. Вы можете настроить или заменить практически любую часть основного кода. Используя архитектуру расширений легко делиться кодом или использовать код сообщества.
+- Yii отлично расширяем. Вы можете настроить или заменить практически любую часть основного кода. Используя архитектуру расширений, легко делиться кодом или использовать код сообщества.
- Одна из главных целей Yii – производительность.
Yii — не проект одного человека. Он поддерживается и развивается [сильной командой](http://www.yiiframework.com/team/) и большим сообществом разработчиков,
-которые ей помогают. Авторы фреймворка следят за тенденциями веб разработки и развитием других проектов. Наиболее
+которые ей помогают. Авторы фреймворка следят за тенденциями веб-разработки и развитием других проектов. Наиболее
подходящие возможности и лучшие практики регулярно внедряются в фреймворк в виде простых и элегантных интерфейсов.
Версии Yii
diff --git a/docs/guide-ru/output-data-providers.md b/docs/guide-ru/output-data-providers.md
index 28239a9..eb311d7 100644
--- a/docs/guide-ru/output-data-providers.md
+++ b/docs/guide-ru/output-data-providers.md
@@ -258,7 +258,7 @@ class CsvDataProvider extends BaseDataProvider
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -269,7 +269,7 @@ class CsvDataProvider extends BaseDataProvider
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function prepareModels()
{
@@ -298,7 +298,7 @@ class CsvDataProvider extends BaseDataProvider
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function prepareKeys($models)
{
@@ -320,7 +320,7 @@ class CsvDataProvider extends BaseDataProvider
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function prepareTotalCount()
{
diff --git a/docs/guide-ru/output-data-widgets.md b/docs/guide-ru/output-data-widgets.md
index 71f173a..61f3916 100644
--- a/docs/guide-ru/output-data-widgets.md
+++ b/docs/guide-ru/output-data-widgets.md
@@ -222,6 +222,13 @@ echo GridView::widget([
'attribute' => 'birthday',
'format' => ['date', 'php:Y-m-d']
],
+ 'created_at:datetime', // короткий вид записи формата
+ [
+ 'label' => 'Education',
+ 'attribute' => 'education',
+ 'filter' => ['0' => 'Elementary', '1' => 'Secondary', '2' => 'Higher'],
+ 'filterInputOptions' => ['prompt' => 'All educations', 'class' => 'form-control', 'id' => null]
+ ],
],
]);
```
@@ -235,6 +242,13 @@ echo GridView::widget([
Для конфигурации колонок данных также доступен короткий вид записи, который описан в API документации для [[yii\grid\GridView::columns|колонок]].
+Используйте [[yii\grid\DataColumn::filter|filter]] и [[yii\grid\DataColumn::filterInputOptions|filterInputOptions]] для
+настройки HTML кода фильтра.
+
+По умолчанию заголовки колонок генерируются используя [[yii\data\Sort::link]]. Это можно изменить через свойство
+[[yii\grid\Column::header]]. Для изменения заголовка нужно задать [[yii\grid\DataColumn::$label]], как в
+примере выше. По умолчанию текст будет взят из модели данных. Подробное описание ищите в [[yii\grid\DataColumn::getHeaderCellLabel]].
+
#### ActionColumn
[[yii\grid\ActionColumn|ActionColumn]] отображает кнопки действия, такие как изменение или удаление для каждой строки.
@@ -619,7 +633,7 @@ class UserView extends ActiveRecord
{
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public static function tableName()
{
@@ -632,7 +646,7 @@ class UserView extends ActiveRecord
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function rules()
{
@@ -642,7 +656,7 @@ class UserView extends ActiveRecord
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function attributeLabels()
{
diff --git a/docs/guide-ru/output-formatting.md b/docs/guide-ru/output-formatting.md
index 4afb759..395f06f 100644
--- a/docs/guide-ru/output-formatting.md
+++ b/docs/guide-ru/output-formatting.md
@@ -36,9 +36,8 @@ Formatter может быть использован двумя различны
[[yii\i18n\Formatter::locale|locale]]. Если оно не было настроено, то в качестве локали будет использован
[[yii\base\Application::language|язык приложения]]. Подробнее смотрите в разделе «[интернационализация](tutorial-i18n.md)».
Компонент форматирования будет выбирать корректный формат для даты и чисел в соответствии с локалью, включая имена
-месяцев и дней недели, переведённые на текущий язык. Форматирование дат также зависит от
-[[yii\i18n\Formatter::timeZone|часового пояса]], которая
-также будет из свойства [[yii\base\Application::timeZone|timeZone]] приложения, если она не была задана явно.
+месяцев и дней недели, переведённые на текущий язык.
+Форматирование дат также зависит от [[yii\i18n\Formatter::timeZone|часового пояса]], который будет взят из одноимённого свойства [[yii\base\Application::timeZone|timeZone]] приложения, если не был задан явно. В свою очередь [[yii\base\Application::timeZone|timeZone]] устанавливает / читает временную зону PHP.
Например, форматирование даты, вызванное с разной локалью, отобразит разные результаты::
@@ -140,16 +139,14 @@ echo Yii::$app->formatter->asTime('2014-10-06 12:41:00'); // 14:41:00
echo Yii::$app->formatter->asTime('2014-10-06 14:41:00 CEST'); // 14:41:00
```
-Начиная с версии 2.0.1 стало возможно настраивать часовой пояс для предполагаемых timestamp, которые не включают в себя
-часовой пояс, как во втором примере в коде выше. Вы можете задать [[yii\i18n\Formatter::defaultTimeZone]] часовым поясом,
-который вы используете для хранения данных.
-
-> Note: Поскольку часовые пояса являются субъектом ответственности правительств по всему миру и могут часто меняться,
-> это значит, что вы, вероятно, не имеете самую свежую информацию в базе данных часовых поясов, установленной на вашем сервере.
-> Вы можете обратиться к [ICU руководству](http://userguide.icu-project.org/datetime/timezone#TOC-Updating-the-Time-Zone-Data)
-> для получения подробностей об обновлении базы данных часовых поясов.
-> См. также: [Настройка вашего PHP окружения для интернационализации](tutorial-i18n.md#setup-environment).
+Если [[yii\i18n\Formatter::timeZone|часовой пояс форматтера]] не задан явно, используется
+[[yii\base\Application::timeZone|часовой пояс приложения]], то есть тот же, что задан в
+конфигурации PHP.
+> Note: Поскольку правила для часовых поясов принимаются различными правительствами и могут часто меняться,
+> вероятно, информация в базе данных часовых поясов на вашем сервере не самая свежая.
+> Как обновить базу вы можете узнать из [руководства ICU](http://userguide.icu-project.org/datetime/timezone#TOC-Updating-the-Time-Zone-Data).
+> Смотрите также: [Настройка вашего PHP окружения для интернационализации](tutorial-i18n.md#setup-environment).
Форматирование чисел
------------------
diff --git a/docs/guide-ru/rest-resources.md b/docs/guide-ru/rest-resources.md
index ee148e4..9cc2f76 100644
--- a/docs/guide-ru/rest-resources.md
+++ b/docs/guide-ru/rest-resources.md
@@ -50,9 +50,9 @@ http://localhost/users?fields=id,email&expand=profile
[[yii\db\ActiveRecord::fields()]] возвращает только те атрибуты, которые были объявлены в схеме БД.
Вы можете переопределить `fields()` для того, чтобы добавить, удалить, переименовать или переобъявить поля. Значение,
-возвращаемое `fields()`, должно быть массивом. Его ключи это имена полей, и значения могут быть либо именами
-свойств/атрибутов, либо анонимными функциями, которые возвращают значение соответствующих полей. Если имя атрибута такое же,
-как ключ массива вы можете опустить значение:
+возвращаемое `fields()`, должно быть массивом. Его ключи — это названия полей. Значения могут быть либо именами
+свойств/атрибутов, либо анонимными функциями, которые возвращают значение соответствующих свойств. Когда
+название поля совпадает с именем аттрибута вы можете опустить ключ массива:
```php
// явное перечисление всех атрибутов лучше всего использовать когда вы хотите быть уверенным что изменение
@@ -61,11 +61,11 @@ http://localhost/users?fields=id,email&expand=profile
public function fields()
{
return [
- // название поля совпадает с названием атрибута
+ // название поля совпадает с именем атрибута
'id',
- // имя поля "email", атрибут "email_address"
+ // название поля "email", атрибут "email_address"
'email' => 'email_address',
- // имя поля "name", значение определяется callback-ом PHP
+ // название поля "name", значение определяется callback-ом PHP
'name' => function () {
return $this->first_name . ' ' . $this->last_name;
},
@@ -77,7 +77,7 @@ public function fields()
{
$fields = parent::fields();
- // удаляем не безопасные поля
+ // удаляем небезопасные поля
unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);
return $fields;
diff --git a/docs/guide-ru/runtime-logging.md b/docs/guide-ru/runtime-logging.md
index d9a4059..84390da 100644
--- a/docs/guide-ru/runtime-logging.md
+++ b/docs/guide-ru/runtime-logging.md
@@ -14,7 +14,7 @@ Yii предоставляет мощную, гибко настраиваему
Запись сообщений лога осуществляется вызовом одного из следующих методов:
-* [[Yii::trace()]]: записывает сообщения для отслеживания выполнения кода приложения. Используется, в основном, при разработке.
+* [[Yii::debug()]]: записывает сообщения для отслеживания выполнения кода приложения. Используется, в основном, при разработке.
* [[Yii::info()]]: записывает сообщение, содержащее какую-либо полезную информацию.
* [[Yii::warning()]]: записывает *тревожное* сообщение при возникновении неожиданного события.
* [[Yii::error()]]: записывает критическую ошибку, на которую нужно, как можно скорее, обратить внимаение.
@@ -22,7 +22,7 @@ Yii предоставляет мощную, гибко настраиваему
Эти методы позволяют записывать сообщения разных *уровней важности* и *категорий*. Они имеют одинаковое описание функции `function ($message, $category = 'application')`, где `$message` передает сообщение для записи, а `$category` - категорию сообщения. В следующем примере будет записано *trace* сообщение с категорией по умолчанию `application`:
```php
-Yii::trace('start calculating average revenue');
+Yii::debug('start calculating average revenue');
```
> Note: Сообщение может быть как строкой так и объектом или массивом. За корректную работу с содержимым сообщения отвечают [цели лога](#log-targets). По умолчанию, если сообщение не является строкой, оно будет приведено к строковому типу при помощи [[yii\helpers\VarDumper::export()]].
@@ -30,7 +30,7 @@ Yii::trace('start calculating average revenue');
Для упрощения работы с сообщениями лога и их фильтрации, рекомендуется явно указывать подходящую категорию для каждого сообщения. Возможно использование иерархической системы именования категорий, что значительно упростит [целям лога](#log-targets) фильтрацию сообщений по категориям. Простым и эффективным способом именования категорий является использование магической PHP константы `__METHOD__`. Такой подход используется в ядре фреймворка Yii. Например,
```php
-Yii::trace('начало вычисления среднего дохода', __METHOD__);
+Yii::debug('начало вычисления среднего дохода', __METHOD__);
```
Константа `__METHOD__` вычисляется как имя метода (включая полное имя класса), в котором она использована. Например, её значение будет вычислено как `'app\controllers\RevenueController::calculate'`, если показанный выше код вызывается в соответствующем методе.
@@ -49,7 +49,9 @@ Yii::trace('начало вычисления среднего дохода', __
return [
// Компонент "log" должен быть загружен на этапе предзагрузки
'bootstrap' => ['log'],
-
+ // Компонент "log" обрабатывает сообщения с меткой времени timestamp.
+ // Задайте временную зону для создания корректных меток времени.
+ 'timeZone' => 'Europe/Moscow',
'components' => [
'log' => [
'targets' => [
@@ -99,7 +101,7 @@ return [
* `error`: соответствует сообщениям, сохраненным методом [[Yii::error()]].
* `warning`: соответствует сообщениям, сохраненным методом [[Yii::warning()]].
* `info`: соответствует сообщениям, сохраненным методом [[Yii::info()]].
-* `trace`: соответствует сообщениям, сохраненным методом [[Yii::trace()]].
+* `trace`: соответствует сообщениям, сохраненным методом [[Yii::debug()]].
* `profile`: соответствует сообщениям, сохраненным методами [[Yii::beginProfile()]] и [[Yii::endProfile()]], подробнее о которых написано в подразделе [Профилирование производительности](#performance-profiling).
Если свойство [[yii\log\Target::levels|levels]] не задано, цель логов будет обрабатывать сообщения с *любым* уровнем важности.
@@ -220,7 +222,7 @@ return [
]
```
-Из-за того, что значения максимального количества сообщений для передачи и выгрузки по умолчанию достаточно велико, при вызове метода `Yii::trace()`, или любого другого метода логгирования, сообщение не появится сразу в файле или таблице базы данных. Такое поведение может стать проблемой, например, в консольных приложениях с большим временем исполнения. Для того, чтобы все сообщения логов сразу же попадали в лог, необходимо установить значения свойств [[yii\log\Dispatcher::flushInterval|flushInterval]] и [[yii\log\Target::exportInterval|exportInterval]] равными 1, например так:
+Из-за того, что значения максимального количества сообщений для передачи и выгрузки по умолчанию достаточно велико, при вызове метода `Yii::debug()`, или любого другого метода логгирования, сообщение не появится сразу в файле или таблице базы данных. Такое поведение может стать проблемой, например, в консольных приложениях с большим временем исполнения. Для того, чтобы все сообщения логов сразу же попадали в лог, необходимо установить значения свойств [[yii\log\Dispatcher::flushInterval|flushInterval]] и [[yii\log\Target::exportInterval|exportInterval]] равными 1, например так:
```php
return [
diff --git a/docs/guide-ru/security-best-practices.md b/docs/guide-ru/security-best-practices.md
index 191717f..7a84629 100644
--- a/docs/guide-ru/security-best-practices.md
+++ b/docs/guide-ru/security-best-practices.md
@@ -162,16 +162,72 @@ CSRF - это аббревиатура для межсайтинговой по
Если это так, то нужно запретить доступ ко всему, кроме директории `web`. Если на вашем хостинге такое невозможно,
рассмотрите возможность смены хостинга.
-Как избежать отладочной информации и утилит в продуктиве
---------------------------------------------------------
+Как избежать вывода информации отладки и инструментов в рабочем режиме
+----------------------------------------------------------------------
В режиме отладки, Yii отображает довольно подробные ошибки, которые полезны во время разработки. Дело в том, что
подробные ошибки удобны для нападающего, так как могут раскрыть структуру базы данных, параметров конфигурации и части
-вашего кода. Никогда не запускайте продуктивное приложение с `YII_DEBUG` установленным в `true` в вашем `index.php`.
+вашего кода. Никогда не запускайте приложения в рабочем режиме с `YII_DEBUG` установленным в `true` в вашем `index.php`.
-Вы никогда не должны включать Gii на продуктиве. Он может быть использован для получения информации о структуре
-базы данных, кода и может позволить заменить файлы, генерируемые Gii автоматически.
+Вы никогда не должны включать Gii или Debug панель в рабочем режиме. Это может быть использованно для получения информации о структуре базы данных, кода и может позволить заменить файлы, генерируемые Gii автоматически.
-Также следует избегать включения на продуктиве панели отладки, если только в этом нет острой необходимости.
-Она раскрывает всё приложение и детали конфигурации. Если вам все таки нужно запустить панель отладки на продуктиве,
-проверьте дважды что доступ ограничен только вашими IP-адресами.
+Следует избегать включения в рабочем режиме панели отладки, если только в этом нет острой необходимости.
+Она раскрывает всё приложение и детали конфигурации. Если Вам всё-таки нужно запустить панель отладки в рабочем режиме,
+проверьте дважды, что доступ ограничен только вашими IP-адресами.
+
+Далее по теме читайте:
+
+-
+-
+
+
+Использование безопасного подключения через TLS
+-----------------------------------------------
+
+Yii предоставляет функции, которые зависят от куки-файлов и/или сессий PHP. Они могут быть уязвимыми, если Ваше соединение
+скомпрометированно. Риск снижается, если приложение использует безопасное соединение через TLS (часто называемое как [SSL](https://en.wikipedia.org/wiki/Transport_Layer_Security)).
+
+Инструкции по настройке смотрите в документации к Вашему веб-серверу. Вы также можете проверить примеры конфигураций
+предоставленные проектом H5BP:
+
+- [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).
+
+
+Безопасная конфигурация сервера
+-------------------------------
+
+Цель этого раздела - выявить риски, которые необходимо учитывать при создании
+конфигурации сервера для обслуживания веб-сайта на основе Yii. Помимо перечисленных здесь пунктов есть и
+другие параметры, связанные с безопасностью, которые необходимо учитывать, поэтому не рассматривайте этот раздел как завершенный.
+
+### Как избежать атаки типа `Host`-header
+
+Классы типа [[yii\web\UrlManager]] и [[yii\helpers\Url]] могут использовать [[yii\web\Request::getHostInfo()|запрашиваемое имя хоста]]] для генерации ссылок. Если веб-сервер настроен на обслуживание одного и того же сайта независимо от значения заголовка `Host`, эта информация может быть ненадежной и может быть подделана пользователем, отправляющим HTTP-запрос. В таких ситуациях Вы должны либо исправить конфигурацию своего веб-сервера, чтобы обслуживать сайт только для указанных имен узлов, либо явно установить или отфильтровать значение, установив свойство [[yii\web\Request::setHostInfo()|hostInfo]] компонента приложения `request`.
+
+Дополнительные сведения о конфигурации сервера смотрите в документации Вашего веб-сервера:
+
+- Apache 2:
+- Nginx:
+
+Если у Вас нет доступа к конфигурации сервера, Вы можете настроить фильтр [[yii\filters\HostControl]] уровня приложения для защиты от такого рода атак:
+
+```php
+// Файл конфигурации веб-приложения
+return [
+ 'as hostControl' => [
+ 'class' => 'yii\filters\HostControl',
+ 'allowedHosts' => [
+ 'example.com',
+ '*.example.com',
+ ],
+ 'fallbackHostInfo' => 'https://example.com',
+ ],
+ // ...
+];
+```
+
+> Note: Вы всегда должны предпочесть конфигурацию веб-сервера для защиты от `атак заголовков узла` вместо использования фильтра.
+ [[yii\filters\HostControl]] следует использовать, только если настройка конфигурации сервера недоступна.
diff --git a/docs/guide-ru/start-installation.md b/docs/guide-ru/start-installation.md
index db185b7..d9eccdc 100644
--- a/docs/guide-ru/start-installation.md
+++ b/docs/guide-ru/start-installation.md
@@ -176,7 +176,7 @@ DocumentRoot "path/to/basic/web"
PHP должен быть установлен как [FPM SAPI](http://php.net/manual/ru/install.fpm.php) для [Nginx](http://wiki.nginx.org/).
Используйте следующие параметры Nginx и не забудьте заменить `path/to/basic/web` на корректный путь к `basic/web` и
-`mysite.local` на ваше имя хоста.
+`mysite.test` на ваше имя хоста.
```
server {
@@ -186,7 +186,7 @@ server {
listen 80; ## listen for ipv4
#listen [::]:80 default_server ipv6only=on; ## слушаем ipv6
- server_name mysite.local;
+ server_name mysite.test;
root /path/to/basic/web;
index index.php;
diff --git a/docs/guide-ru/structure-filters.md b/docs/guide-ru/structure-filters.md
index bcad52d..94d7e21 100644
--- a/docs/guide-ru/structure-filters.md
+++ b/docs/guide-ru/structure-filters.md
@@ -87,7 +87,7 @@ class ActionTimeFilter extends ActionFilter
public function afterAction($action, $result)
{
$time = microtime(true) - $this->_startTime;
- Yii::trace("Action '{$action->uniqueId}' spent $time second.");
+ Yii::debug("Action '{$action->uniqueId}' spent $time second.");
return parent::afterAction($action, $result);
}
}
diff --git a/docs/guide-ru/structure-modules.md b/docs/guide-ru/structure-modules.md
index 78d3e67..ebd2a95 100644
--- a/docs/guide-ru/structure-modules.md
+++ b/docs/guide-ru/structure-modules.md
@@ -171,6 +171,13 @@ yii //
из идентификатора модуля, то контроллер и действие определяются исходя из свойства [[yii\base\Module::defaultRoute]],
которое по умолчанию равно `default`. Таким образом, маршрут `forum` соответствует контроллеру `default` модуля `forum`.
+Добавление маршрутов модуля в URL manager необходимо производить до начала работы [[yii\web\UrlManager::parseRequest()]], что не
+позволяет размещать код добавления правил модуля в `init()`, так как инициализация происходит уже после обработки маршрутов. Таким образом, добавление маршрутов необходимо осуществить в [предзагрузке
+модуля](structure-extensions.md#bootstrapping-classes). Хорошей практикой, также, будет объединение всех правил модуля
+при помощи [[\yii\web\GroupUrlRule]].
+
+Если же вы используете модуль для [версионирования API](rest-versioning.md), добавление маршрутов необходимо
+производить непосредственно в конфигурации `urlManager` приложения.
### Получение доступа к модулям
diff --git a/docs/guide-ru/tutorial-i18n.md b/docs/guide-ru/tutorial-i18n.md
index 1a2f187..5d70ee8 100644
--- a/docs/guide-ru/tutorial-i18n.md
+++ b/docs/guide-ru/tutorial-i18n.md
@@ -269,7 +269,7 @@ for an argument: U_ARGUMENT_TYPE_MISMATCH":
```
Подробная документация о формах склонений для различных языков доступна на сайте
-[unicode.org](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html).
+[unicode.org](http://cldr.unicode.org/index/cldr-spec/plural-rules).
#### Вариации
diff --git a/docs/guide-ru/tutorial-yii-as-micro-framework.md b/docs/guide-ru/tutorial-yii-as-micro-framework.md
new file mode 100644
index 0000000..193d035
--- /dev/null
+++ b/docs/guide-ru/tutorial-yii-as-micro-framework.md
@@ -0,0 +1,206 @@
+# Использование Yii в качестве микро-framework'а
+
+Yii можно легко использовать без функций включенных в базовый и расширенный шаблоны приложений. Другими словами Yii уже является микро-каркасом. Не требуется иметь структуру каталогов предоставляемую этими шаблонами при работе с Yii.
+
+Это особенно удобно, когда Вам не нужен весь пред-установленный шаблонный код, такой как `Assets` или `Views`. Одним из таких случаев является создание JSON API. В следующих разделах будет показано, как это сделать.
+
+## Установка Yii
+
+Создайте каталог для файлов проекта и смените рабочий каталог на этот путь. В примерах используются команды Unix, но аналогичные команды существуют и в Windows.
+
+```bash
+mkdir micro-app
+cd micro-app
+```
+
+> Note: Для продолжения требуется немного знаний о Composer. Если Вы еще не знаете, как использовать Composer, пожалуйста, найдите время, чтобы прочитать [Руководство Composer](https://getcomposer.org/doc/00-intro.md).
+
+Создайте файл `composer.json` в каталоге `micro-app` с помощью Вашего любимого редактора и добавьте следующее:
+
+```json
+{
+ "require": {
+ "yiisoft/yii2": "~2.0.0"
+ },
+ "repositories": [
+ {
+ "type": "composer",
+ "url": "https://asset-packagist.org"
+ }
+ ]
+}
+```
+
+Сохраните файл и запустите команду `comper install`. Это установит framework со всеми его зависимостями.
+
+## Создание структуры проекта
+
+После того как Вы установили фреймворк, пришло время создать [входную точку](structure-entry-scripts.md) приложения. Точка входа - это самый первый файл, который будет выполнен при попытке открыть приложение. По соображениям безопасности рекомендуется поместить файл точки входа в отдельный каталог и сделать каталог корнем веб директории.
+
+Создайте каталог `web` и поместите в него файл `index.php` со следующим содержимым:
+
+```php
+run();
+```
+
+Также создайте файл с именем `config.php`, который будет содержать всю конфигурацию приложения:
+
+```php
+ 'micro-app',
+ // basePath (базовый путь) приложения будет каталог `micro-app`
+ 'basePath' => __DIR__,
+ // это пространство имен где приложение будет искать все контроллеры
+ 'controllerNamespace' => 'micro\controllers',
+ // установим псевдоним '@micro', чтобы включить автозагрузку классов из пространства имен 'micro'
+ 'aliases' => [
+ '@micro' => __DIR__,
+ ],
+];
+```
+
+> Info: Несмотря на то, что конфигурация приложения может находиться в файле `index.php` рекомендуется
+> содержать её в отдельном файле. Таким образом её можно также использовать и для консольного приложения, как показано ниже.
+
+Теперь Ваш проект готов к наполнению кодом. Вам решать какую структуру каталогов проекта Вы выберите, пока Вы сможете видеть пространства имен.
+
+## Создание первого контроллера
+
+Создайте каталог `controllers` и добавьте туда файл `SiteController.php` который является контроллером по умолчанию, он будет обрабатывать запрос без пути.
+
+```php
+ 'default/index'`.
+
+На данный момент структура проекта должна выглядеть так:
+
+```
+micro-app/
+├── composer.json
+├── web/
+ └── index.php
+└── controllers/
+ └── SiteController.php
+```
+
+Если Вы еще не настроили веб-сервер, Вы можете взглянуть на [примеры конфигурационных файлов веб-серверов](start-installation.md#Настройка-веб-сервера-).
+Другой возможностью является использование команды `yii serve` которая будет использовать встроенный веб-сервер PHP. Вы можете запустить её из каталога `micro-app/` через:
+
+ vendor/bin/yii serve --docroot=./web
+
+При открытии URL приложения в браузере, он теперь должен печатать "Hello World!" который был возвращен из `SiteController::actionIndex()`.
+
+> Info: В нашем примере мы изменили пространство имен по умолчанию приложения с `app` на` micro`, чтобы продемонстрировать
+> что Вы не привязаны к этому имени (в случае, если Вы считали, что это так), а затем скорректировали
+> [[yii\base\Application::$controllerNamespace|controllers namespace]] и установили правильный псевдоним.
+
+## Создание REST API
+
+Чтобы продемонстрировать использование нашей "микроархитектуры" мы создадим простой REST API для сообщений.
+
+Чтобы этот API обслуживал некоторые данные, нам нужна база данных. Добавим конфигурацию подключения базы данных
+к конфигурации приложения:
+
+```php
+'components' => [
+ 'db' => [
+ 'class' => 'yii\db\Connection',
+ 'dsn' => 'sqlite:@micro/database.sqlite',
+ ],
+],
+```
+
+> Info: Для простоты мы используем базу данных sqlite. Дополнительную информацию см. в [Руководство по базам данных](db-dao.md).
+
+Затем мы создаем [миграции базы данных](db-migrations.md) для создания таблицы сообщений.
+Убедитесь, что у Вас есть отдельный файл конфигурации, как описано выше, нам это нужно для того, чтобы запустить консольные команды описанные ниже.
+Запуск следующих команд создаст файл миграции базы данных и применит миграцию к базе данных:
+
+ vendor/bin/yii migrate/create --appconfig=config.php create_post_table --fields="title:string,body:text"
+ vendor/bin/yii migrate/up --appconfig=config.php
+
+Создайте каталог `models` и файл` Post.php` в этом каталоге. Это код модели:
+
+```php
+ Info: Созданная модель представляет собой класс ActiveRecord, который представляет данные из таблицы `posts`.
+> Для получения дополнительной информации обратитесь к [active record руководству](db-active-record.md).
+
+Чтобы обслуживать сообщения в нашем API, добавьте `PostController` в` controllers`:
+
+```php
+ Tip: 你可以在同一个应用程序中使用不同的缓存存储器。一个常见的策略是使用基于内存的缓存存储器存储小而常用的数据(例如:统计数据),使用基于文件或数据库的缓存存储器存储大而不太常用的数据(例如:网页内容)。
diff --git a/docs/guide-zh-CN/concept-di-container.md b/docs/guide-zh-CN/concept-di-container.md
index c46367a..0943fb4 100644
--- a/docs/guide-zh-CN/concept-di-container.md
+++ b/docs/guide-zh-CN/concept-di-container.md
@@ -69,7 +69,7 @@ $container->get('Foo', [], [
这种情况下,容器将使用一个注册过的 PHP 回调创建一个类的新实例。回调负责解决依赖并将其恰当地注入新创建的对象。例如:
```php
-$container->set('Foo', function () {
+$container->set('Foo', function ($container, $params, $config) {
return new Foo(new Bar);
});
diff --git a/docs/guide-zh-CN/concept-events.md b/docs/guide-zh-CN/concept-events.md
index 7a292f0..bac1529 100644
--- a/docs/guide-zh-CN/concept-events.md
+++ b/docs/guide-zh-CN/concept-events.md
@@ -182,7 +182,7 @@ use yii\base\Event;
use yii\db\ActiveRecord;
Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
- Yii::trace(get_class($event->sender) . ' is inserted');
+ Yii::debug(get_class($event->sender) . ' is inserted');
});
```
diff --git a/docs/guide-zh-CN/start-installation.md b/docs/guide-zh-CN/start-installation.md
index d3d366a..9d2044f 100644
--- a/docs/guide-zh-CN/start-installation.md
+++ b/docs/guide-zh-CN/start-installation.md
@@ -132,7 +132,7 @@ DocumentRoot "path/to/basic/web"
### 推荐使用的 Nginx 配置
-为了使用 [Nginx](http://wiki.nginx.org/),你应该已经将 PHP 安装为 [FPM SAPI](http://php.net/install.fpm) 了。使用如下 Nginx 配置,将 `path/to/basic/web` 替换为实际的 `basic/web` 目录,`mysite.local` 替换为实际的主机名以提供服务。
+为了使用 [Nginx](http://wiki.nginx.org/),你应该已经将 PHP 安装为 [FPM SAPI](http://php.net/install.fpm) 了。使用如下 Nginx 配置,将 `path/to/basic/web` 替换为实际的 `basic/web` 目录,`mysite.test` 替换为实际的主机名以提供服务。
```
server {
@@ -142,7 +142,7 @@ server {
listen 80; ## 监听 ipv4 上的 80 端口
#listen [::]:80 default_server ipv6only=on; ## 监听 ipv6 上的 80 端口
- server_name mysite.local;
+ server_name mysite.test;
root /path/to/basic/web;
index index.php;
diff --git a/docs/guide-zh-CN/structure-filters.md b/docs/guide-zh-CN/structure-filters.md
index 91023a0..da18136 100644
--- a/docs/guide-zh-CN/structure-filters.md
+++ b/docs/guide-zh-CN/structure-filters.md
@@ -84,7 +84,7 @@ class ActionTimeFilter extends ActionFilter
public function afterAction($action, $result)
{
$time = microtime(true) - $this->_startTime;
- Yii::trace("Action '{$action->uniqueId}' spent $time second.");
+ Yii::debug("Action '{$action->uniqueId}' spent $time second.");
return parent::afterAction($action, $result);
}
}
diff --git a/docs/guide/README.md b/docs/guide/README.md
index 478718b..d032bdb 100644
--- a/docs/guide/README.md
+++ b/docs/guide/README.md
@@ -18,6 +18,7 @@ Introduction
Getting Started
---------------
+* [What do you need to know](start-prerequisites.md)
* [Installing Yii](start-installation.md)
* [Running Applications](start-workflow.md)
* [Saying Hello](start-hello.md)
diff --git a/docs/guide/caching-data.md b/docs/guide/caching-data.md
index e74696a..d964ca2 100644
--- a/docs/guide/caching-data.md
+++ b/docs/guide/caching-data.md
@@ -125,8 +125,8 @@ Yii supports a wide range of cache storage. The following is a summary:
(redis version 2.6.12 or higher is required).
* [[yii\caching\WinCache]]: uses PHP [WinCache](http://iis.net/downloads/microsoft/wincache-extension)
([see also](http://php.net/manual/en/book.wincache.php)) extension.
-* [[yii\caching\XCache]]: uses PHP [XCache](http://xcache.lighttpd.net/) extension.
-* [[yii\caching\ZendDataCache]]: uses
+* [[yii\caching\XCache]] _(deprecated)_: uses PHP [XCache](http://xcache.lighttpd.net/) extension.
+* [[yii\caching\ZendDataCache]] _(deprecated)_: uses
[Zend Data Cache](http://files.zend.com/help/Zend-Server-6/zend-server.htm#data_cache_component.htm)
as the underlying caching medium.
diff --git a/docs/guide/caching-fragment.md b/docs/guide/caching-fragment.md
index a9fef47..5a5e9a1 100644
--- a/docs/guide/caching-fragment.md
+++ b/docs/guide/caching-fragment.md
@@ -174,3 +174,6 @@ if ($this->beginCache($id1)) {
The [[yii\base\View::renderDynamic()|renderDynamic()]] method takes a piece of PHP code as its parameter.
The return value of the PHP code is treated as the dynamic content. The same PHP code will be executed
for every request, no matter the enclosing fragment is being served from cached or not.
+
+> Note: since version 2.0.14 a dynamic content API is exposed via the [[yii\base\DynamicContentAwareInterface]] interface and its [[yii\base\DynamicContentAwareTrait]] trait.
+ As an example, you may refer to the [[yii\widgets\FragmentCache]] class.
diff --git a/docs/guide/concept-di-container.md b/docs/guide/concept-di-container.md
index 67b95f5..665d049 100644
--- a/docs/guide/concept-di-container.md
+++ b/docs/guide/concept-di-container.md
@@ -117,7 +117,7 @@ The callable is responsible to resolve the dependencies and inject them appropri
created objects. For example,
```php
-$container->set('Foo', function () {
+$container->set('Foo', function ($container, $params, $config) {
$foo = new Foo(new Bar);
// ... other initializations ...
return $foo;
@@ -131,7 +131,7 @@ To hide the complex logic for building a new object, you may use a static class
```php
class FooBuilder
{
- public static function build()
+ public static function build($container, $params, $config)
{
$foo = new Foo(new Bar);
// ... other initializations ...
@@ -421,7 +421,7 @@ $container->setDefinitions([
'class' => 'app\components\Response',
'format' => 'json'
],
- 'app\storage\DocumentsReader' => function () {
+ 'app\storage\DocumentsReader' => function ($container, $params, $config) {
$fs = new app\storage\FileStorage('/var/tempfiles');
return new app\storage\DocumentsReader($fs);
}
diff --git a/docs/guide/concept-events.md b/docs/guide/concept-events.md
index 06d11b9..7d0315d 100644
--- a/docs/guide/concept-events.md
+++ b/docs/guide/concept-events.md
@@ -42,7 +42,7 @@ Attaching Event Handlers
You can attach a handler to an event by calling the [[yii\base\Component::on()]] method. For example:
```php
-$foo = new Foo;
+$foo = new Foo();
// this handler is a global function
$foo->on(Foo::EVENT_HELLO, 'function_name');
@@ -212,7 +212,7 @@ use yii\base\Event;
use yii\db\ActiveRecord;
Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
- Yii::trace(get_class($event->sender) . ' is inserted');
+ Yii::debug(get_class($event->sender) . ' is inserted');
});
```
@@ -296,7 +296,7 @@ pass the interface class name as the first argument:
```php
Event::on('app\interfaces\DanceEventInterface', DanceEventInterface::EVENT_DANCE, function ($event) {
- Yii::trace(get_class($event->sender) . ' just danced'); // Will log that Dog or Developer danced
+ Yii::debug(get_class($event->sender) . ' just danced'); // Will log that Dog or Developer danced
});
```
@@ -355,3 +355,74 @@ done through the Singleton (e.g. the application instance).
However, because the namespace of the global events is shared by all parties, you should name the global events
wisely, such as introducing some sort of namespace (e.g. "frontend.mail.sent", "backend.mail.sent").
+
+
+Wildcard Events
+---------------
+
+Since 2.0.14 you can setup event handler for multiple events matching wildcard pattern.
+For example:
+
+```php
+use Yii;
+
+$foo = new Foo();
+
+$foo->on('foo.event.*', function ($event) {
+ // triggered for any event, which name starts on 'foo.event.'
+ Yii::debug('trigger event: ' . $event->name);
+});
+```
+
+Wildcard patterns can be used for class-level events as well. For example:
+
+```php
+use yii\base\Event;
+use Yii;
+
+Event::on('app\models\*', 'before*', function ($event) {
+ // triggered for any class in namespace 'app\models' for any event, which name starts on 'before'
+ Yii::debug('trigger event: ' . $event->name . ' for class: ' . get_class($event->sender));
+});
+```
+
+This allows you catching all application events by single handler using following code:
+
+```php
+use yii\base\Event;
+use Yii;
+
+Event::on('*', '*', function ($event) {
+ // triggered for any event at any class
+ Yii::debug('trigger event: ' . $event->name);
+});
+```
+
+> Note: usage wildcards for event handlers setup may reduce the application performance.
+ It is better to be avoided if possible.
+
+In order to detach event handler specified by wildcard pattern, you should repeat same pattern at
+[[yii\base\Component::off()]] or [[yii\base\Event::off()]] invocation. Keep in mind that passing wildcard
+during detaching of event handler will detach ony the handler specified for this wildcard, while handlers
+attached for regular event names will remain even if they match the pattern. For example:
+
+```php
+use Yii;
+
+$foo = new Foo();
+
+// attach regular handler
+$foo->on('event.hello', function ($event) {
+ echo 'direct-handler'
+});
+
+// attach wildcard handler
+$foo->on('*', function ($event) {
+ echo 'wildcard-handler'
+});
+
+// detach wildcard handler only!
+$foo->off('*');
+
+$foo->trigger('event.hello'); // outputs: 'direct-handler'
+```
diff --git a/docs/guide/db-active-record.md b/docs/guide/db-active-record.md
index d6a482d..9c4d156 100644
--- a/docs/guide/db-active-record.md
+++ b/docs/guide/db-active-record.md
@@ -472,7 +472,7 @@ $customer->loadDefaultValues();
### Attributes Typecasting
-Being populated by query results [[yii\db\ActiveRecord]] performs automatic typecast for its attribute values, using
+Being populated by query results, [[yii\db\ActiveRecord]] performs automatic typecast for its attribute values, using
information from [database table schema](db-dao.md#database-schema). This allows data retrieved from table column
declared as integer to be populated in ActiveRecord instance with PHP integer, boolean with boolean and so on.
However, typecasting mechanism has several limitations:
@@ -490,7 +490,33 @@ converted during saving process.
> Tip: you may use [[yii\behaviors\AttributeTypecastBehavior]] to facilitate attribute values typecasting
on ActiveRecord validation or saving.
+
+Since 2.0.14, Yii ActiveRecord supports complex data types, such as JSON or multidimensional arrays.
+
+#### JSON in MySQL and PostgreSQL
+
+After data population, the value from JSON column will be automatically decoded from JSON according to standard JSON
+decoding rules.
+
+To save attribute value to a JSON column, ActiveRecord will automatically create a [[yii\db\JsonExpression|JsonExpression]] object
+that will be encoded to a JSON string on [QueryBuilder](db-query-builder.md) level.
+
+#### Arrays in PostgreSQL
+
+After data population, the value from Array column will be automatically decoded from PgSQL notation to an [[yii\db\ArrayExpression|ArrayExpression]]
+object. It implements PHP `ArrayAccess` interface, so you can use it as an array, or call `->getValue()` to get the array itself.
+
+To save attribute value to an array column, ActiveRecord will automatically create an [[yii\db\ArrayExpression|ArrayExpression]] object
+that will be encoded by [QueryBuilder](db-query-builder.md) to an PgSQL string representation of array.
+
+You can also use conditions for JSON columns:
+
+```php
+$query->andWhere(['=', 'json', new ArrayExpression(['foo' => 'bar'])
+```
+To learn more about expressions building system read the [Query Builder – Adding custom Conditions and Expressions](db-query-builder.md#adding-custom-conditions-and-expressions)
+article.
### Updating Multiple Rows
diff --git a/docs/guide/db-migrations.md b/docs/guide/db-migrations.md
index c2e0996..0c536c9 100644
--- a/docs/guide/db-migrations.md
+++ b/docs/guide/db-migrations.md
@@ -38,6 +38,13 @@ command `yii help migrate`.
> Tip: migrations could affect not only database schema but adjust existing data to fit new schema, create RBAC
hierarchy or clean up cache.
+> Note: When manipulating data using a migration you may find that using your [Active Record](db-active-record.md) classes
+> for this might be useful because some of the logic is already implemented there. Keep in mind however, that in contrast
+> to code written in the migrations, who's nature is to stay constant forever, application logic is subject to change.
+> So when using Active Record in migration code, changes to the logic in the Active Record layer may accidentally break
+> existing migrations. For this reason migration code should be kept independent from other application logic such
+> as Active Record classes.
+
## Creating Migrations
@@ -204,7 +211,7 @@ generates
class m150811_220037_create_post_table extends Migration
{
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function up()
{
@@ -214,7 +221,7 @@ class m150811_220037_create_post_table extends Migration
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function down()
{
@@ -238,7 +245,7 @@ generates
class m150811_220037_create_post_table extends Migration
{
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function up()
{
@@ -250,7 +257,7 @@ class m150811_220037_create_post_table extends Migration
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function down()
{
@@ -275,7 +282,7 @@ generates
class m150811_220037_create_post_table extends Migration
{
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function up()
{
@@ -287,7 +294,7 @@ class m150811_220037_create_post_table extends Migration
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function down()
{
@@ -320,7 +327,7 @@ generates
class m160328_040430_create_post_table extends Migration
{
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function up()
{
@@ -368,7 +375,7 @@ class m160328_040430_create_post_table extends Migration
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function down()
{
@@ -533,7 +540,7 @@ generates
class m160328_041642_create_junction_table_for_post_and_tag_tables extends Migration
{
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function up()
{
@@ -580,7 +587,7 @@ class m160328_041642_create_junction_table_for_post_and_tag_tables extends Migra
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function down()
{
@@ -716,14 +723,6 @@ Below is the list of all these database accessing methods:
> ```
-> Note: When manipulating data using a migration you may find that using your [Active Record](db-active-record.md) classes
-> for this might be useful because some of the logic is already implemented there. Keep in mind however, that in contrast
-> to code written in the migrations, who's nature is to stay constant forever, application logic is subject to change.
-> So when using Active Record in migration code, changes to the logic in the Active Record layer may accidentally break
-> existing migrations. For this reason migration code should be kept independent from other application logic such
-> as Active Record classes.
-
-
## Applying Migrations
To upgrade a database to its latest structure, you should apply all available new migrations using the following command:
diff --git a/docs/guide/db-query-builder.md b/docs/guide/db-query-builder.md
index b23d1e7..1086f44 100644
--- a/docs/guide/db-query-builder.md
+++ b/docs/guide/db-query-builder.md
@@ -160,12 +160,12 @@ are in the ["Quoting Tables" section of the "Database Access Objects" guide](gui
### [[yii\db\Query::where()|where()]]
The [[yii\db\Query::where()|where()]] method specifies the `WHERE` fragment of a SQL query. You can use one of
-the three formats to specify a `WHERE` condition:
+the four formats to specify a `WHERE` condition:
- string format, e.g., `'status=1'`
- hash format, e.g. `['status' => 1, 'type' => 2]`
- operator format, e.g. `['like', 'name', 'test']`
-
+- object format, e.g. `new LikeCondition('name', 'LIKE', 'test')`
#### String Format
@@ -255,6 +255,9 @@ the operator can be one of the following:
- `between`: operand 1 should be the column name, and operand 2 and 3 should be the
starting and ending values of the range that the column is in.
For example, `['between', 'id', 1, 10]` will generate `id BETWEEN 1 AND 10`.
+ In case you need to build a condition where value is between two columns (like `11 BETWEEN min_id AND max_id`),
+ you should use [[yii\db\conditions\BetweenColumnsCondition|BetweenColumnsCondition]].
+ See [Conditions – Object Format](#object-format) chapter to learn more about object definition of conditions.
- `not between`: similar to `between` except the `BETWEEN` is replaced with `NOT BETWEEN`
in the generated condition.
@@ -306,6 +309,41 @@ the operator can be one of the following:
Using the Operator Format, Yii internally uses parameter binding so in contrast to the [string format](#string-format), here
you do not have to add parameters manually.
+#### Object Format
+
+Object Form is available since 2.0.14 and is both most powerful and most complex way to define conditions.
+You need to follow it either if you want to build your own abstraction over query builder or if you want to implement
+your own complex conditions.
+
+Instances of condition classes are immutable. Their only purpose is to store condition data and provide getters
+for condition builders. Condition builder is a class that holds the logic that transforms data
+stored in condition into the SQL expression.
+
+Internally the formats described above are implicitly converted to object format prior to building raw SQL,
+so it is possible to combine formats in a single condition:
+
+```php
+$query->andWhere(new OrCondition([
+ new InCondition('type', 'in', $types),
+ ['like', 'name', '%good%'],
+ 'disabled=false'
+]))
+```
+
+Conversion from operator format into object format is performed according to
+[[yii\db\QueryBuilder::conditionClasses|QueryBuilder::conditionClasses]] property, that maps operators names
+to representative class names:
+
+- `AND`, `OR` -> `yii\db\conditions\ConjunctionCondition`
+- `NOT` -> `yii\db\conditions\NotCondition`
+- `IN`, `NOT IN` -> `yii\db\conditions\InCondition`
+- `BETWEEN`, `NOT BETWEEN` -> `yii\db\conditions\BetweenCondition`
+
+And so on.
+
+Using the object format makes it possible to create your own conditions or to change the way default ones are built.
+See [Creating Custom Conditions and Expressions](#creating-custom-conditions-and-expressions) chapter to learn more.
+
#### Appending Conditions
@@ -758,3 +796,170 @@ $unbufferedDb->close();
```
> Note: unbuffered query uses less memory on the PHP-side, but can increase the load on the MySQL server. It is recommended to design your own code with your production practice for extra massive data, [for example, divide the range for integer keys, loop them with Unbuffered Queries](https://github.com/yiisoft/yii2/issues/8420#issuecomment-296109257).
+
+### Adding custom Conditions and Expressions
+
+As it was mentioned in [Conditions – Object Format](#object-format) chapter, is is possible to create custom condition
+classes. For example, let's create a condition that will check that specific columns are less than some value.
+Using the operator format, it would look like the following:
+
+```php
+[
+ 'and',
+ '>', 'posts', $minLimit,
+ '>', 'comments', $minLimit,
+ '>', 'reactions', $minLimit,
+ '>', 'subscriptions', $minLimit
+]
+```
+
+When such condition applied once, it is fine. In case it is used multiple times in a single query it can
+be optimized a lot. Let's create a custom condition object to demonstrate it.
+
+Yii has a [[yii\db\conditions\ConditionInterface|ConditionInterface]], that must be used to mark classes, that represent
+a condition. It requires `fromArrayDefinition()` method implementation, in order to make possible to create condition
+from array format. In case you don't need it, you can implement this method with exception throwing.
+
+Since we create our custom condition class, we can build API that suits our task the most.
+
+```php
+namespace app\db\conditions;
+
+class AllGreaterCondition implements \yii\db\conditions\ConditionInterface
+{
+ private $columns;
+ private $value;
+
+ /**
+ * @param string[] $columns Array of columns that must be greater, than $value
+ * @param mixed $value the value to compare each $column against.
+ */
+ public function __construct(array $columns, $value)
+ {
+ $this->columns = $columns;
+ $this->value = $value;
+ }
+
+ public static function fromArrayDefinition($operator, $operands)
+ {
+ throw new InvalidArgumentException('Not implemented yet, but we will do it later');
+ }
+
+ public function getColumns() { return $this->columns; }
+ public function getValue() { return $this->vaule; }
+}
+```
+
+So we can create a condition object:
+
+```php
+$conditon = new AllGreaterCondition(['col1', 'col2'], 42);
+```
+
+But `QueryBuilder` still does not know, to to make an SQL condition out of this object.
+Now we need to create a builder for this condition. It must implement [[yii\db\ExpressionBuilderInterface]]
+that requires us to implement a `build()` method.
+
+```php
+namespace app\db\conditions;
+
+class AllGreaterConditionBuilder implements \yii\db\ExpressionBuilderInterface
+{
+ use \yii\db\Condition\ExpressionBuilderTrait; // Contains constructor and `queryBuilder` property.
+
+ /**
+ * @param AllGreaterCondition $condition the condition to be built
+ * @param array $params the binding parameters.
+ */
+ public function build(ConditionInterface $condition, &$params)
+ {
+ $value = $condition->getValue();
+
+ $conditions = [];
+ foreach ($condition->getColumns() as $column) {
+ $conditions[] = new SimpleCondition($column, '>', $value);
+ }
+
+ return $this->queryBuider->buildCondition(new AndCondition($conditions), $params);
+ }
+}
+```
+
+Then simple let [[yii\db\QueryBuilder|QueryBuilder]] know about our new condition – add a mapping for it to
+the `expressionBuilders` array. It could be done right from the application configuration:
+
+```php
+'db' => [
+ 'class' => 'yii\db\mysql\Connection',
+ // ...
+ 'queryBuilder' => [
+ 'expressionBuilders' => [
+ 'app\db\conditions\AllGreaterCondition' => 'app\db\conditions\AllGreaterConditionBuilder',
+ ],
+ ],
+],
+```
+
+Now we can use our condition in `where()`:
+
+```php
+$query->andWhere(new AllGreaterCondition(['posts', 'comments', 'reactions', 'subscriptions'], $minValue));
+```
+
+If we want to make it possible to create our custom condition using operator format, we should declare it in
+[[yii\db\QueryBuilder::conditionClasses|QueryBuilder::conditionClasses]]:
+
+```php
+'db' => [
+ 'class' => 'yii\db\mysql\Connection',
+ // ...
+ 'queryBuilder' => [
+ 'expressionBuilders' => [
+ 'app\db\conditions\AllGreaterCondition' => 'app\db\conditions\AllGreaterConditionBuilder',
+ ],
+ 'conditionClasses' => [
+ 'ALL>' => 'app\db\conditions\AllGreaterCondition',
+ ],
+ ],
+],
+```
+
+And create a real implementation of `AllGreaterCondition::fromArrayDefinition()` method
+in `app\db\conditions\AllGreaterCondition`:
+
+```php
+namespace app\db\conditions;
+
+class AllGreaterCondition implements \yii\db\conditions\ConditionInterface
+{
+ // ... see the implementation above
+
+ public static function fromArrayDefinition($operator, $operands)
+ {
+ return new static($operands[0], $operands[1]);
+ }
+}
+```
+
+After that, we can create our custom condition using shorter operator format:
+
+```php
+$query->andWhere(['ALL>', ['posts', 'comments', 'reactions', 'subscriptions'], $minValue]);
+```
+
+You might notice, that there was two concepts used: Expressions and Conditions. There is a [[yii\db\ExpressionInterface]]
+that should be used to mark objects, that require an Expression Builder class, that implements
+[[yii\db\ExpressionBuilderInterface]] to be built. Also there is a [[yii\db\condition\ConditionInterface]], that extends
+[[yii\db\ExpressionInterface|ExpressionInterface]] and should be used to objects, that can be created from array definition
+as it was shown above, but require builder as well.
+
+To summarise:
+
+- Expression – is a Data Transfer Object (DTO) for a dataset, that can be somehow compiled to some SQL
+statement (an operator, string, array, JSON, etc).
+- Condition – is an Expression superset, that aggregates multiple Expressions (or scalar values) that can be compiled
+to a single SQL condition.
+
+You can create your own classes that implement [[yii\db\ExpressionInterface|ExpressionInterface]] to hide the complexity
+of transforming data to SQL statements. You will learn more about other examples of Expressions in the
+[next article](db-active-record.md);
diff --git a/docs/guide/input-validation.md b/docs/guide/input-validation.md
index e274616..fc69434 100644
--- a/docs/guide/input-validation.md
+++ b/docs/guide/input-validation.md
@@ -221,7 +221,7 @@ values are stored in an attribute:
```php
['age', 'trim'],
['age', 'default', 'value' => null],
-['age', 'integer', 'integerOnly' => true, 'min' => 0],
+['age', 'integer', 'min' => 0],
['age', 'filter', 'filter' => 'intval', 'skipOnEmpty' => true],
```
@@ -387,8 +387,8 @@ class MyForm extends Model
public function validateCountry($attribute, $params, $validator)
{
- if (!in_array($this->$attribute, ['USA', 'Web'])) {
- $this->addError($attribute, 'The country must be either "USA" or "Web".');
+ if (!in_array($this->$attribute, ['USA', 'Indonesia'])) {
+ $this->addError($attribute, 'The country must be either "USA" or "Indonesia".');
}
}
}
@@ -422,7 +422,8 @@ fails the validation, call [[yii\base\Model::addError()]] to save the error mess
with [inline validators](#inline-validators).
-For example the inline validator above could be moved into new [[components/validators/CountryValidator]] class.
+For example, the inline validator above could be moved into new [[components/validators/CountryValidator]] class.
+In this case we can use [[yii\validators\Validator::addError()]] to set customized message for the model.
```php
namespace app\components;
@@ -433,8 +434,8 @@ class CountryValidator extends Validator
{
public function validateAttribute($model, $attribute)
{
- if (!in_array($model->$attribute, ['USA', 'Web'])) {
- $this->addError($model, $attribute, 'The country must be either "USA" or "Web".');
+ if (!in_array($model->$attribute, ['USA', 'Indonesia'])) {
+ $this->addError($model, $attribute, 'The country must be either "{country1}" or "{country2}".', ['country1' => 'USA', 'country2' => 'Indonesia']);
}
}
}
diff --git a/docs/guide/output-data-providers.md b/docs/guide/output-data-providers.md
index 919ef26..9de89e1 100644
--- a/docs/guide/output-data-providers.md
+++ b/docs/guide/output-data-providers.md
@@ -262,7 +262,7 @@ class CsvDataProvider extends BaseDataProvider
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -273,7 +273,7 @@ class CsvDataProvider extends BaseDataProvider
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function prepareModels()
{
@@ -302,7 +302,7 @@ class CsvDataProvider extends BaseDataProvider
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function prepareKeys($models)
{
@@ -324,7 +324,7 @@ class CsvDataProvider extends BaseDataProvider
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function prepareTotalCount()
{
diff --git a/docs/guide/output-data-widgets.md b/docs/guide/output-data-widgets.md
index eb54081..e009fea 100644
--- a/docs/guide/output-data-widgets.md
+++ b/docs/guide/output-data-widgets.md
@@ -243,6 +243,13 @@ echo GridView::widget([
'attribute' => 'birthday',
'format' => ['date', 'php:Y-m-d']
],
+ 'created_at:datetime', // shortcut format
+ [
+ 'label' => 'Education',
+ 'attribute' => 'education',
+ 'filter' => ['0' => 'Elementary', '1' => 'Secondary', '2' => 'Higher'],
+ 'filterInputOptions' => ['prompt' => 'All educations', 'class' => 'form-control', 'id' => null]
+ ],
],
]);
```
@@ -256,6 +263,12 @@ For a list of available formatters see the [section about Data Formatting](outpu
For configuring data columns there is also a shortcut format which is described in the
API documentation for [[yii\grid\GridView::columns|columns]].
+Use [[yii\grid\DataColumn::filter|filter]] and [[yii\grid\DataColumn::filterInputOptions|filterInputOptions]] to
+control HTML for the filter input.
+
+By default, column headers are rendered by [[yii\data\Sort::link]]. It could be adjusted using [[yii\grid\Column::header]].
+To change header text you should set [[yii\grid\DataColumn::$label]] like in the example above.
+By default the label will be populated from data model. For more details see [[yii\grid\DataColumn::getHeaderCellLabel]].
#### Action column
@@ -643,7 +656,7 @@ class UserView extends ActiveRecord
{
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public static function tableName()
{
@@ -656,7 +669,7 @@ class UserView extends ActiveRecord
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function rules()
{
@@ -666,7 +679,7 @@ class UserView extends ActiveRecord
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function attributeLabels()
{
diff --git a/docs/guide/output-formatting.md b/docs/guide/output-formatting.md
index 58134d6..2d9291a 100644
--- a/docs/guide/output-formatting.md
+++ b/docs/guide/output-formatting.md
@@ -144,12 +144,15 @@ echo Yii::$app->formatter->asTime('2014-10-06 12:41:00'); // 14:41:00
echo Yii::$app->formatter->asTime('2014-10-06 14:41:00 CEST'); // 14:41:00
```
+If the [[yii\i18n\Formatter::timeZone|time zone]] is not set explicitly on the formatter component, the
+[[yii\base\Application::timeZone|time zone configured in the application]] is used, which is the same time zone
+as set in the PHP configuration.
+
> Note: As time zones are subject to rules made by the governments around the world and may change frequently, it is
> likely that you do not have the latest information in the time zone database installed on your system.
> You may refer to the [ICU manual](http://userguide.icu-project.org/datetime/timezone#TOC-Updating-the-Time-Zone-Data)
> for details on updating the time zone database. Please also read
-> [Setting up your PHP environment for internationalization](tutorial-i18n.md#setup-environment).
-
+> [Setting up your PHP environment for internationalization](tutorial-i18n.md#setup-environment).
## Formatting Numbers
diff --git a/docs/guide/runtime-logging.md b/docs/guide/runtime-logging.md
index b011bd0..bf226c8 100644
--- a/docs/guide/runtime-logging.md
+++ b/docs/guide/runtime-logging.md
@@ -18,7 +18,7 @@ In this section, we will mainly describe the first two steps.
Recording log messages is as simple as calling one of the following logging methods:
-* [[Yii::trace()]]: record a message to trace how a piece of code runs. This is mainly for development use.
+* [[Yii::debug()]]: record a message to trace how a piece of code runs. This is mainly for development use.
* [[Yii::info()]]: record a message that conveys some useful information.
* [[Yii::warning()]]: record a warning message that indicates something unexpected has happened.
* [[Yii::error()]]: record a fatal error that should be investigated as soon as possible.
@@ -29,7 +29,7 @@ the log message to be recorded, while `$category` is the category of the log mes
example records a trace message under the default category `application`:
```php
-Yii::trace('start calculating average revenue');
+Yii::debug('start calculating average revenue');
```
> Info: Log messages can be strings as well as complex data, such as arrays or objects. It is the responsibility
@@ -43,7 +43,7 @@ is to use the PHP magic constant `__METHOD__` for the category names. This is al
Yii framework code. For example,
```php
-Yii::trace('start calculating average revenue', __METHOD__);
+Yii::debug('start calculating average revenue', __METHOD__);
```
The `__METHOD__` constant evaluates as the name of the method (prefixed with the fully qualified class name) where
@@ -70,7 +70,8 @@ in the application configuration, like the following:
return [
// the "log" component must be loaded during bootstrapping time
'bootstrap' => ['log'],
-
+ // the "log" component process messages with timestamp. Set PHP timezone to create correct timestamp
+ 'timeZone' => 'America/Los_Angeles',
'components' => [
'log' => [
'targets' => [
@@ -124,7 +125,7 @@ The [[yii\log\Target::levels|levels]] property takes an array consisting of one
* `error`: corresponding to messages logged by [[Yii::error()]].
* `warning`: corresponding to messages logged by [[Yii::warning()]].
* `info`: corresponding to messages logged by [[Yii::info()]].
-* `trace`: corresponding to messages logged by [[Yii::trace()]].
+* `trace`: corresponding to messages logged by [[Yii::debug()]].
* `profile`: corresponding to messages logged by [[Yii::beginProfile()]] and [[Yii::endProfile()]], which will
be explained in more details in the [Profiling](#performance-profiling) subsection.
@@ -276,7 +277,7 @@ property of individual [log targets](#log-targets), like the following,
]
```
-Because of the flushing and exporting level setting, by default when you call `Yii::trace()` or any other logging
+Because of the flushing and exporting level setting, by default when you call `Yii::debug()` or any other logging
method, you will NOT see the log message immediately in the log targets. This could be a problem for some long-running
console applications. To make each log message appear immediately in the log targets, you should set both
[[yii\log\Dispatcher::flushInterval|flushInterval]] and [[yii\log\Target::exportInterval|exportInterval]] to be 1,
diff --git a/docs/guide/runtime-requests.md b/docs/guide/runtime-requests.md
index c1a137a..ec2703f 100644
--- a/docs/guide/runtime-requests.md
+++ b/docs/guide/runtime-requests.md
@@ -186,7 +186,7 @@ In case your proxies are using different headers you can use the request configu
'X-Forwarded-Proto',
'X-Proxy-User-Ip',
'Front-End-Https',
- ];'
+ ];
'ipHeaders' => [
'X-Proxy-User-Ip',
],
diff --git a/docs/guide/security-authorization.md b/docs/guide/security-authorization.md
index cb8e06b..95b6f54 100644
--- a/docs/guide/security-authorization.md
+++ b/docs/guide/security-authorization.md
@@ -225,6 +225,8 @@ return [
'components' => [
'authManager' => [
'class' => 'yii\rbac\DbManager',
+ // uncomment if you want to cache RBAC items hierarchy
+ // 'cache' => 'cache',
],
// ...
],
@@ -264,7 +266,7 @@ Building authorization data is all about the following tasks:
Depending on authorization flexibility requirements the tasks above could be done in different ways.
If your permissions hierarchy is meant to be changed by developers only, you can use either migrations
or a console command. Migration pro is that it could be executed along with other migrations. Console
-command pro is that you have a good overview of the hierarchy in the code rathe than it being scattered
+command pro is that you have a good overview of the hierarchy in the code rather than it being scattered
among multiple migrations.
Either way in the end you'll get the following RBAC hierarchy:
diff --git a/docs/guide/start-installation.md b/docs/guide/start-installation.md
index b079752..f006f88 100644
--- a/docs/guide/start-installation.md
+++ b/docs/guide/start-installation.md
@@ -111,6 +111,30 @@ But there are other installation options available:
you may consider installing the [Advanced Project Template](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md).
+Installing Assets
+-----------------
+
+Yii relies on [Bower](http://bower.io/) and/or [NPM](https://www.npmjs.org/) packages for the asset (CSS and JavaScript) libraries installation.
+It uses Composer to obtain these libraries, allowing PHP and CSS/JavaScript package versions to resolve at the same time.
+This can be achieved either by usage of [asset-packagist.org](https://asset-packagist.org) or [composer asset plugin](https://github.com/francoispluchino/composer-asset-plugin/).
+Please refer to [Assets documentation](structure-assets.md) for more details.
+
+You may want to either manage your assets via native Bower/NPM client, use CDN or avoid assets installation entirely.
+In order to prevent assets installation via Composer, add the following lines to your 'composer.json':
+
+```json
+"replace": {
+ "bower-asset/jquery": ">=1.11.0",
+ "bower-asset/inputmask": ">=3.2.0",
+ "bower-asset/punycode": ">=1.3.0",
+ "bower-asset/yii2-pjax": ">=2.0.0"
+},
+```
+
+> Note: in case of bypassing asset installation via Composer, you are responsible for the assets installation and resolving
+> version collisions. Be prepared for possible inconsistencies among asset files from different extensions.
+
+
Verifying the Installation
--------------------------
@@ -212,7 +236,7 @@ DocumentRoot "path/to/basic/web"
To use [Nginx](http://wiki.nginx.org/), you should install PHP as an [FPM SAPI](http://php.net/install.fpm).
You may use the following Nginx configuration, replacing `path/to/basic/web` with the actual path for
-`basic/web` and `mysite.local` with the actual hostname to serve.
+`basic/web` and `mysite.test` with the actual hostname to serve.
```nginx
server {
@@ -222,7 +246,7 @@ server {
listen 80; ## listen for ipv4
#listen [::]:80 default_server ipv6only=on; ## listen for ipv6
- server_name mysite.local;
+ server_name mysite.test;
root /path/to/basic/web;
index index.php;
diff --git a/docs/guide/start-prerequisites.md b/docs/guide/start-prerequisites.md
new file mode 100644
index 0000000..6956193
--- /dev/null
+++ b/docs/guide/start-prerequisites.md
@@ -0,0 +1,22 @@
+# What do you need to know
+
+Yii learning curve is not as steep as other PHP frameworks but still it requires some beforehand knowledge.
+
+## PHP
+
+Yii is a PHP framework so make sure you [read and understand language reference](http://php.net/manual/en/langref.php).
+
+## Object oriented programming
+
+Basic understanding of object oriented programming is required. If you're not familiar with it, check one of the many
+tutorials available such as [the one from tuts+](https://code.tutsplus.com/tutorials/object-oriented-php-for-beginners--net-12762).
+
+Note that the more complicated your application is the more advanced OOP concepts your should learn in order to successfully
+manage that complexity.
+
+## Command line and composer
+
+Yii extensively uses de-facto standard PHP package manager, [Composer](https://getcomposer.org/) so make sure you read
+and understand its guide. If you are not familiar with using command line it is time to start trying. Once you'll
+learn the basics you'll never want to work without it.
+
diff --git a/docs/guide/structure-assets.md b/docs/guide/structure-assets.md
index 5e5979c..46f9313 100644
--- a/docs/guide/structure-assets.md
+++ b/docs/guide/structure-assets.md
@@ -194,8 +194,8 @@ class FontAwesomeAsset extends AssetBundle
];
public $publishOptions = [
'only' => [
- 'fonts/',
- 'css/',
+ 'fonts/*',
+ 'css/*',
]
];
}
@@ -250,8 +250,8 @@ Visit [asset-packagist.org](https://asset-packagist.org) to know, how it works.
##### Using fxp/composer-asset-plugin
-Comparing to to using asset-packagist, composer-asset-plugin does not require to change application config. Instead, it
-requires to install a special Composer plugin globally by running the following command:
+Compared to asset-packagist, composer-asset-plugin does not require any changes to application config. Instead, it
+requires global installation of a special Composer plugin by running the following command:
```bash
composer global require "fxp/composer-asset-plugin:^1.4.1"
diff --git a/docs/guide/structure-filters.md b/docs/guide/structure-filters.md
index bf03e37..8590c74 100644
--- a/docs/guide/structure-filters.md
+++ b/docs/guide/structure-filters.md
@@ -90,7 +90,7 @@ class ActionTimeFilter extends ActionFilter
public function afterAction($action, $result)
{
$time = microtime(true) - $this->_startTime;
- Yii::trace("Action '{$action->uniqueId}' spent $time second.");
+ Yii::debug("Action '{$action->uniqueId}' spent $time second.");
return parent::afterAction($action, $result);
}
}
diff --git a/docs/guide/structure-modules.md b/docs/guide/structure-modules.md
index 255b545..245c90c 100644
--- a/docs/guide/structure-modules.md
+++ b/docs/guide/structure-modules.md
@@ -176,6 +176,14 @@ only contains the module ID, then the [[yii\base\Module::defaultRoute]] property
will determine which controller/action should be used. This means a route `forum` would represent the `default`
controller in the `forum` module.
+URL manager routes should be added before [[yii\web\UrlManager::parseRequest()]] is fired. That means doing it
+in module's `init()` won't work because module will be initialized when routes were already processed. Thus, routes
+should be added at [bootstrap stage](structure-extensions.md#bootstrapping-classes). It is a also a good practice
+to wrap module's URL rules with [[\yii\web\GroupUrlRule]].
+
+In case module is used to [version API](rest-versioning.md), routes should be added directly in `urlManager`
+section of the application config.
+
### Accessing Modules
diff --git a/docs/guide/test-fixtures.md b/docs/guide/test-fixtures.md
index 55460be..9000442 100644
--- a/docs/guide/test-fixtures.md
+++ b/docs/guide/test-fixtures.md
@@ -154,7 +154,10 @@ You may also assign an alias to a fixture. In the above example, the `UserProfil
In the test methods, you may then access a fixture object using its alias in `grabFixture()` method. For example,
```php
-$profile = $I->grabFixture('profiles', 'user1');` will return the `UserProfileFixture` object.
+$profile = $I->grabFixture('profiles', 'user1');
+```
+
+will return the `UserProfileFixture` object.
Because `UserProfileFixture` extends from `ActiveFixture`, you may further use the following syntax to access
the data provided by the fixture:
diff --git a/docs/guide/tutorial-i18n.md b/docs/guide/tutorial-i18n.md
index 7962e0f..c470cea 100644
--- a/docs/guide/tutorial-i18n.md
+++ b/docs/guide/tutorial-i18n.md
@@ -416,7 +416,7 @@ while `one` matches `21` or `101`:
These `other`, `few`, `many` and other special argument names vary depending on language. To learn which ones you should
specify for a particular locale, please refer to "Plural Rules, Cardinal" at [http://intl.rmcreative.ru/](http://intl.rmcreative.ru/).
-Alternatively you can refer to [rules reference at unicode.org](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html).
+Alternatively you can refer to [rules reference at unicode.org](http://cldr.unicode.org/index/cldr-spec/plural-rules).
> Note: The above example Russian message is mainly used as a translated message, not an original message, unless you set
> the [[yii\base\Application::$sourceLanguage|source language]] of your application as `ru-RU` and translating from Russian.
diff --git a/docs/guide/tutorial-yii-as-micro-framework.md b/docs/guide/tutorial-yii-as-micro-framework.md
index 896e593..d0d0277 100644
--- a/docs/guide/tutorial-yii-as-micro-framework.md
+++ b/docs/guide/tutorial-yii-as-micro-framework.md
@@ -104,6 +104,7 @@ At this point the project structure should look like this:
```
micro-app/
├── composer.json
+├── config.php
├── web/
└── index.php
└── controllers/
diff --git a/docs/internals-ru/git-workflow.md b/docs/internals-ru/git-workflow.md
index 123dc98..a226549 100644
--- a/docs/internals-ru/git-workflow.md
+++ b/docs/internals-ru/git-workflow.md
@@ -1,29 +1,29 @@
Рабочий процесс Git для разработчиков Yii 2
===========================================
-Итак, вы хотите разрабатывать Yii? Хорошо! Но для того чтоб увеличить шанс принятия ваших изменений,
-пожалуйста следуйте следующим шагам. Если вы новичок в git и github, вы можете сначала проверить
-[github help](http://help.github.com/), [try git](https://try.github.com) или прочитать о
+Итак, вы хотите разрабатывать Yii? Хорошо! Но для того, чтобы увеличить шанс принятия ваших изменений,
+пожалуйста, следуйте следующим шагам. Если вы новичок в git и Github, вы можете сначала проверить
+[Github help](http://help.github.com/), [try git](https://try.github.com) или прочитать о
[внутренней модели данных git](http://nfarina.com/post/9868516270/git-is-simpler).
Подготовка вашего рабочего окружения
------------------------------------
-Следующие шаги будут создавать рабочее окружение для Yii, которое вы можете использовать для работы над основным кодом
+Следующие шаги создадут рабочее окружение для Yii, которое вы можете использовать для работы над основным кодом
фреймворка. *Эти шаги будут нужны только в первый раз*.
-### 1. [Форкаем](http://help.github.com/fork-a-repo/) репозиторий Yii на github и клонируйте ваш форк в ваше рабочее окружение.
+### 1. [Сделайте форк](http://help.github.com/fork-a-repo/) репозитория Yii на Github и клонируйте этот форк в ваше рабочее окружение.
```
git clone git@github.com:YOUR-GITHUB-USERNAME/yii2.git
```
-Если у вас есть проблемы с настройкой GIT для работы с GitHub в Linux, или вы получаете ошибку похожую на "Permission Denied
-(publickey)", тогда вы должны настроить ваш GIT по [этой инструкции](http://help.github.com/linux-set-up-git/)
+Если у вас есть проблемы с настройкой Git для работы с GitHub в Linux, или вы получаете ошибку похожую на "Permission Denied
+(publickey)", тогда вы должны настроить ваш Git по [этой инструкции](http://help.github.com/linux-set-up-git/)
-### 2. Добавляем главный репозиторий Yii как дополнительный внешний git репозиторий называемый "upstream"
+### 2. Добавьте главный репозиторий Yii как дополнительный внешний git репозиторий называемый "upstream"
-Перейдите в каталог, куда вы склонировали Yii, как правило "yii2". Затем введите следующую команду:
+Перейдите в каталог куда вы склонировали Yii, как правило "yii2". Затем введите следующую команду:
```
git remote add upstream git://github.com/yiisoft/yii2.git
@@ -33,11 +33,20 @@ git remote add upstream git://github.com/yiisoft/yii2.git
Следующие шаги не обязательны, если вы хотите работать только с переводом или документацией.
-- выполните `composer update` для установки зависимостей (если [composer у вас установлен глобально](https://getcomposer.org/doc/00-intro.md#globally)).
+- выполните `composer install` для установки зависимостей
+ (если [composer у вас установлен глобально](https://getcomposer.org/doc/00-intro.md#globally)).
-> Note: Если вы видите такие ошибки, как `Problem 1 The requested package bower-asset/jquery could not be found in
+> Note: если вы видите такие ошибки, как `Problem 1 The requested package bower-asset/jquery could not be found in
> any version, there may be a typo in the package name.`, необходимо запустить `composer global require "fxp/composer-asset-plugin:^1.4.1"`
+Если вы планируете работать с Javascript:
+
+- выполните `npm install` для установки зависимостей и программ тестирования Javascript
+ (если [Node.js and NPM установлен](https://nodejs.org/en/download/package-manager/)).
+
+> Note: тесты JavaScript зависят от библиотеки jsdom, которой требуется Node.js 4 или более новый.
+> Предпочтительнее использовать Node.js 6 or 7.
+
- выполните `php build/build dev/app basic` для клонирования базового приложения и установки его зависимостей.
Эта команда установит сторонние пакеты composer обычным образом, но создаст ссылку с репозитория yii2 на только
что загруженный репозиторий. Таким образом у вас будет только один экземпляр кода.
@@ -56,13 +65,16 @@ git remote add upstream git://github.com/yiisoft/yii2.git
### Модульные тесты
Вы можете выполнить модульные тесты с помощью команды `phpunit` в корневой директории приложения. Если у вас phpunit
-не установлен глобально, вы можете запустить `php vendor/bin/phpunit`.
+не установлен глобально, вы можете запустить `php vendor/bin/phpunit` или `vendor/bin/phpunit.bat` в случае
+использования OS Windows.
Некоторые тесты требуют дополнительно установки и настройки баз данных. Вы можете создать `tests/data/config.local.php`
для переопределения настроек, которые определены в `tests/data/config.php`.
-Вы можете ограничить тестирование группой тестов, с которой вы сейчас работаете, например запускать только тесты для
-валидаторов и redis. Вы можете получить список доступных групп выполнив `phpunit --list-groups`.
+Вы можете ограничить тестирование группой тестов, с которой вы сейчас работаете. Например, запускать только тесты для
+валидаторов и redis: `phpunit --group=validators,redis`. Вы можете получить список доступных групп выполнив `phpunit --list-groups`.
+
+Вы можете запустить модульные тесты JavaScript с помощью команды `npm test` в корневой директории приложения.
### Расширения
@@ -70,15 +82,16 @@ git remote add upstream git://github.com/yiisoft/yii2.git
сделать это:
```
-php build/build dev/ext
+php build/build dev/ext
```
-где `` это имя расширения, например `redis`.
+где `` это имя расширения, например `redis` и `` это URL вашего форка расширения, например
+`git@github.com:my_nickname/yii2-redis.git`. Если вы контрибьютор ядра фреймворка, вы можете не указывать ``.
Если вы хотите протестировать расширение в одном из шаблонов приложений, просто добавьте его в `composer.json`
приложения, например добавив `"yiisoft/yii2-redis": "~2.0.0"` в секцию `require` базового приложения.
Запустите `php build/build dev/app basic` для установки расширения и его зависимостей и создания символической
-ссылки на `extensions/redis` так чтоб вы работали не папке вендорных пакетов composer, а напрямую в репозиторий yii2.
+ссылки на `extensions/redis` так чтоб вы работали не папке вендорных пакетов composer, а напрямую в репозитории yii2.
> Note: по умолчанию URL репозиториев git на GitHub работают через SSH. Чтобы использовать HTTPS, добавьте
> флаг `--useHttp` к команде `build`.
@@ -94,21 +107,21 @@ php build/build dev/ext
Потратьте несколько минут на поиск существующей issue, которая соответствует вашим изменениям. Если вы найдёте её в
списке, то пожалуйста оставьте комментарий, что вы начали над ней работать. Если вы не нашли нужной issue пожалуйста
[создайте новую issue](report-an-issue.md) или создайте сразу запрос на изменения если это простые изменения.
-Это позволит команде проверить ваше предложение, и обеспечивать соответствующую обратную связь.
+Это позволит команде проверить ваше предложение, и обеспечить соответствующую обратную связь.
> Для небольших изменений или документации, или простых исправлений, вам нет необходимости создавать issue,
запрос на изменения в этом случае подходит лучше.
-### 2. Вытягивание последнего кода из основного репозитория Yii
+### 2. Получите последний код из основного репозитория Yii
```
-git fetch upstream
+git pull upstream
```
-Вы должны начинать с этого действия работу над каждым новым предложением, убеждайтесь что вы работаете над самой
+Вы должны начинать с этого действия работу над каждым новым предложением. Убедитесь, что вы работаете с самой
последней версией кода.
-### 3. Создание новой ветки для ваших изменений, основанных на текущем мастере Yii
+### 3. Сделайте новую ветку для ваших изменений, основанных на текущей ветке master в Yii
> Это очень важно, так как вы не сможете отправлять более одного запроса на изменения из вашего репозитория, если
будете использовать ветку master.
@@ -123,7 +136,7 @@ git checkout upstream/master
git checkout -b 999-name-of-your-branch-goes-here
```
-### 4. Делайте свою магию, пишите ваш код
+### 4. Делайте свою магию, пишите свой код
Убедитесь, что он работает :)
@@ -145,7 +158,7 @@ Enh #999: a description of the enhancement (Your Name)
Для очень маленьких исправлений, например опечаток и изменений документации, нет необходимости обновлять CHANGELOG.
-### 6. Фиксация ваших изменений
+### 6. Зафиксируйте изменения
Добавляем файлы/изменения которые вы хотите зафиксировать в [staging area](http://gitref.org/basic/#add)
@@ -155,14 +168,14 @@ git add path/to/my/file.php
Вы можете использовать опцию `-p` для того, чтоб выбрать, какие изменения вы хотите добавить в коммит.
-Фиксируйте ваши изменения с описательным сообщением. Убедитесь что в сообщение есть номер `#XXX`, так github
-автоматически свяжет ваш коммит с тикетом:
+Фиксируйте ваши изменения с описательным сообщением. Убедитесь, что в сообщении есть номер `#XXX`, так Github
+автоматически свяжет ваш коммит с issue:
```
git commit -m "A brief description of this change which fixes #999 goes here"
```
-### 7. Получение последнего кода из апстрима Yii в вашу ветку
+### 7. Получите последний код из upstream Yii в вашу ветку
```
git pull upstream master
@@ -172,18 +185,18 @@ git pull upstream master
какие-либо конфликты слияния, вы должны исправить их и зафиксировать изменения ещё раз. Это гарантирует, что команда Yii
сможет слить ваши изменения одним кликом.
-### 8. Разрешив зависимости, отправляем код на github
+### 8. Разрешив зависимости, отправьте код на Github
```
git push -u origin 999-name-of-your-branch-goes-here
```
-Опция `-u` сохранит указание на ветку в github, чтобы при следующем выполнении `git push`, git знал, куда отправлять
+Опция `-u` сохранит указание на ветку в Github, чтобы при следующем выполнении `git push`, git знал, куда отправлять
изменения. Это полезно, если вы хотите позже отправлять ещё коммиты в этот запрос на слияние.
-### 9. Открываем [запрос на слияние](http://help.github.com/send-pull-requests/) в *upstream*.
+### 9. Откройте [запрос на слияние](http://help.github.com/send-pull-requests/) в *upstream*.
-Перейдите в репозиторий на github и нажмите "Pull Request", выберите ветку справа и введите больше информации
+Перейдите в репозиторий на Github и нажмите "Pull Request", выберите ветку справа и введите больше информации
в поле комментариев. Чтобы связать запрос на изменение с issue, добавьте в комментарий `#999` - где 999 это номер issue.
> Обратите внимание, что каждый запрос на слияние должен исправлять единственное изменение. Для множества независимых
@@ -191,10 +204,10 @@ git push -u origin 999-name-of-your-branch-goes-here
### 10. Проверка вашего кода кем-то
-Кто-то будет проверять ваш код, и, возможно, вам будет предложено внести некоторые изменения, если это так, то перейдите
+Кто-то будет проверять ваш код, и, возможно, вам будет предложено внести некоторые изменения. Eсли это так, то перейдите
к шагу #6 (вам не надо открывать новый запрос на слияние, если текущий ещё открыт). Если код будет принят, он будет
-влит в основную ветку и станет частью следующего релиза Yii. Если нет, не унывайте, разным людям необходимы различные
-функции и Yii не может реализовывать всё для всех, ваш код будет ещё доступен на github как ссылка для людей кому он
+влит в основную ветку и станет частью следующего релиза Yii. Если нет - не унывайте, разным людям необходимы различные
+функции и Yii не может реализовывать всё для всех, ваш код будет ещё доступен на Github как ссылка для людей кому он
может пригодится.
### 11. Очистка
@@ -210,16 +223,16 @@ git push origin --delete 999-name-of-your-branch-goes-here
### Примечание
-Для обнаружения регрессии как можно раньше, каждое слияние кодовой базы Yii на github будет подхвачено
+Для обнаружения регрессии как можно раньше, каждое слияние кодовой базы Yii на Github будет подхвачено
[Travis CI](http://travis-ci.org) для автоматического запуска тестов. Люди из *core team* не хотят нагружать
этот сервис, поэтому добавляют текст [`[ci skip]`](http://about.travis-ci.org/docs/user/how-to-skip-a-build/) в описание
запроса на слияние, в следующих ситуациях:
-* затронуты только javascript, css файлы или файлы изображений,
+* затронуты только JavaScript, CSS файлы или файлы изображений,
* обновление документации,
* изменения затрагивают только строки (например обновление переводов).
-Это защитит travis от запуска тестов на изменениях, которые не покрыты тестами.
+Это защитит Travis CI от запуска тестов на изменениях, которые не покрыты тестами.
### Обзор команд (для продвинутых участников)
diff --git a/docs/internals/README.md b/docs/internals/README.md
index e1994a9..d8ac9c1 100644
--- a/docs/internals/README.md
+++ b/docs/internals/README.md
@@ -42,3 +42,7 @@ Misc
### Exception Hierarchy
![Yii Framework Exception Hierarchy](exception_hierarchy.png)
+
+### Database testing
+
+[Here](https://gist.github.com/sergeymakinen/0696a5952f160ea28d7b64c3adfecf6f) is config for test environments for all supported Yii databases.
diff --git a/docs/internals/git-workflow.md b/docs/internals/git-workflow.md
index bdb09e4..a1dd877 100644
--- a/docs/internals/git-workflow.md
+++ b/docs/internals/git-workflow.md
@@ -111,10 +111,10 @@ review your suggestion, and provide appropriate feedback along the way.
> For small changes or documentation issues or straightforward fixes, you don't need to create an issue, a pull request is enough in this case.
-### 2. Fetch the latest code from the main Yii branch
+### 2. Pull the latest code from the main Yii branch
```
-git fetch upstream
+git pull upstream
```
You should start at this point for every new contribution to make sure you are working on the latest code.
diff --git a/docs/internals/release.md b/docs/internals/release.md
index 35d092d..c5c0eb7 100644
--- a/docs/internals/release.md
+++ b/docs/internals/release.md
@@ -53,13 +53,13 @@ You may run it with `--update` to fetch tags for all repos to get the newest inf
Making a framework release includes the following commands (apps are always released together with the framework):
- ./build release framework
- ./build release app-basic
- ./build release app-advanced
+ ./build/build release framework
+ ./build/build release app-basic
+ ./build/build release app-advanced
Making an extension release includes only one command (e.g. for redis):
- ./build release redis
+ ./build/build release redis
The default release command will release a new minor version from the currently checked out branch.
To release another version than the default, you have to specify it using the `--version` option, e.g.
diff --git a/framework/BaseYii.php b/framework/BaseYii.php
index a0b2aef..eb5acd9 100644
--- a/framework/BaseYii.php
+++ b/framework/BaseYii.php
@@ -7,8 +7,8 @@
namespace yii;
+use yii\base\InvalidArgumentException;
use yii\base\InvalidConfigException;
-use yii\base\InvalidParamException;
use yii\base\UnknownClassException;
use yii\di\Container;
use yii\log\Logger;
@@ -125,7 +125,7 @@ class BaseYii
* @param bool $throwException whether to throw an exception if the given alias is invalid.
* If this is false and an invalid alias is given, false will be returned by this method.
* @return string|bool the path corresponding to the alias, false if the root alias is not previously registered.
- * @throws InvalidParamException if the alias is invalid while $throwException is true.
+ * @throws InvalidArgumentException if the alias is invalid while $throwException is true.
* @see setAlias()
*/
public static function getAlias($alias, $throwException = true)
@@ -151,7 +151,7 @@ class BaseYii
}
if ($throwException) {
- throw new InvalidParamException("Invalid path alias: $alias");
+ throw new InvalidArgumentException("Invalid path alias: $alias");
}
return false;
@@ -211,7 +211,7 @@ class BaseYii
* - a path alias (e.g. `@yii/base`). In this case, the path alias will be converted into the
* actual path first by calling [[getAlias()]].
*
- * @throws InvalidParamException if $path is an invalid alias.
+ * @throws InvalidArgumentException if $path is an invalid alias.
* @see getAlias()
*/
public static function setAlias($alias, $path)
@@ -380,15 +380,16 @@ class BaseYii
}
/**
- * Logs a trace message.
+ * Logs a debug message.
* Trace messages are logged mainly for development purpose to see
* the execution work flow of some code. This method will only log
* a message when the application is in debug mode.
* @param string|array $message the message to be logged. This can be a simple string or a more
* complex data structure, such as array.
* @param string $category the category of the message.
+ * @since 2.0.14
*/
- public static function trace($message, $category = 'application')
+ public static function debug($message, $category = 'application')
{
if (YII_DEBUG) {
static::getLogger()->log($message, Logger::LEVEL_TRACE, $category);
@@ -396,6 +397,18 @@ class BaseYii
}
/**
+ * Alias of [[debug()]].
+ * @param string|array $message the message to be logged. This can be a simple string or a more
+ * complex data structure, such as array.
+ * @param string $category the category of the message.
+ * @deprecated 2.0.14 Use [[debug()]]
+ */
+ public static function trace($message, $category = 'application')
+ {
+ static::debug($message, $category);
+ }
+
+ /**
* Logs an error message.
* An error message is typically logged when an unrecoverable error occurs
* during the execution of an application.
@@ -472,6 +485,7 @@ class BaseYii
/**
* Returns an HTML hyperlink that can be displayed on your Web page showing "Powered by Yii Framework" information.
* @return string an HTML hyperlink that can be displayed on your Web page showing "Powered by Yii Framework" information
+ * @deprecated 2.0.14
*/
public static function powered()
{
diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md
index eef4a5d..55d1386 100644
--- a/framework/CHANGELOG.md
+++ b/framework/CHANGELOG.md
@@ -4,7 +4,130 @@ Yii Framework 2 Change Log
2.0.14 under development
------------------------
-- no changes in this release.
+- Bug #15644: Avoid wrong default selection on a dropdown, checkbox list, and radio list, when a option has a key equals to zero (berosoboy)
+- Enh #14538: Added `yii\behaviors\AttributeTypecastBehavior::typecastAfterFind` property (littlefuntik, silverfire)
+- Enh #14254: add an option to specify whether validator is forced to always use master DB for `yii\validators\UniqueValidator` and `yii\validators\ExistValidator` (rossoneri, samdark)
+- Enh #15272: Removed type attribute from script tag (aleksbelic)
+- Enh #15120: Refactored dynamic caching introducing `DynamicContentAwareInterface` and `DynamicContentAwareTrait` (sergeymakinen)
+- Bug #8983: Only truncate the original log file for rotation (matthewyang, developeruz)
+- Bug #11401: Fixed `yii\web\DbSession` concurrency issues when writing and regenerating IDs (samdark, andreasanta, cebe)
+- Bug #13034: Fixed `normalizePath` for windows network shares that start with two backslashes (developeruz)
+- Bug #14135: Fixed `yii\web\Request::getBodyParam()` crashes on object type body params (klimov-paul)
+- Bug #14157: Add support for loading default value `CURRENT_TIMESTAMP` of MySQL `datetime` field (rossoneri)
+- Bug #14276: Fixed I18N format with dotted parameters (developeruz)
+- Bug #14296: Fixed log targets to throw exception in case log can not be properly exported (bizley)
+- Bug #14484: Fixed `yii\validators\UniqueValidator` for target classes with a default scope (laszlovl, developeruz)
+- Bug #14604: Fixed `yii\validators\CompareValidator` `compareAttribute` does not work if `compareAttribute` form ID has been changed (mikk150)
+- Bug #14711: Fixed `yii\web\ErrorHandler` displaying exception message in non-debug mode (samdark)
+- Bug #14811: Fixed `yii\filters\HttpCache` to work with PHP 7.2 (samdark)
+- Bug #14859: Fixed OCI DB `defaultSchema` failure when `masterConfig` is used (lovezhl456)
+- Bug #14903: Fixed route with extra dashes is executed controller while it should not (developeruz)
+- Bug #14916: Fixed `yii\db\Query::each()` iterator key starts from 1 instead of 0 (Vovan-VE)
+- Bug #14980: Fix looping in `yii\i18n\MessageFormatter` tokenize pattern if pattern is invalid (uaoleg, developeruz)
+- Bug #15031: Fixed incorrect string type length detection for OCI DB schema (Murolike)
+- Bug #15046: Throw an `yii\web\HeadersAlreadySentException` if headers were sent before web response (dmirogin)
+- Bug #15122: Fixed `yii\db\Command::getRawSql()` to properly replace expressions (hiscaler, samdark)
+- Bug #15142: Fixed array params replacing in `yii\helpers\BaseUrl::current()` (IceJOKER)
+- Bug #15169: Fixed translating a string when NULL parameter is passed (developeruz)
+- Bug #15194: Fixed `yii\db\QueryBuilder::insert()` to preserve passed params when building a `INSERT INTO ... SELECT` query for MSSQL, PostgreSQL and SQLite (sergeymakinen)
+- Bug #15229: Fixed `yii\console\widgets\Table` default value for `getScreenWidth()`, when `Console::getScreenSize()` can't determine screen size (webleaf)
+- Bug #15234: Fixed `\yii\widgets\LinkPager` removed `tag` from `disabledListItemSubTagOptions` (SDKiller)
+- Bug #15249: Controllers in subdirectories were not visible in commands list (IceJOKER)
+- Bug #15270: Resolved potential race conditions when writing generated php-files (kalessil)
+- Bug #15300: Fixed "Cannot read property 'style' of undefined" error at the error screen (vitorarantes)
+- Bug #15301: Fixed `ArrayHelper::filter()` to work properly with `0` in values (hhniao)
+- Bug #15302: Fixed `yii\caching\DbCache` so that `getValues` now behaves the same as `getValue` with regards to streams (edwards-sj)
+- Bug #15317: Regenerate CSRF token if an empty value is given (sammousa)
+- Bug #15320: Fixed special role checks in `yii\filters\AccessRule::matchRole()` (Izumi-kun)
+- Bug #15322: Fixed PHP 7.2 compatibility of `FileHelper::getExtensionsByMimeType()` (samdark)
+- Bug #15353: Remove side effect of ActiveQuery::getTablesUsedInFrom() introduced in 2.0.13 (terales)
+- Bug #15355: Fixed `yii\db\Query::from()` does not work with `yii\db\Expression` (vladis84, silverfire, samdark)
+- Bug #15356: Fixed multiple bugs in `yii\db\Query::getTablesUsedInFrom()` (vladis84, samdark)
+- Bug #15380: `FormatConverter::convertDateIcuToPhp()` now converts `a` ICU symbols to `A` (brandonkelly)
+- Bug #15407: Fixed rendering rows with associative arrays in `yii\console\widgets\Table` (dmrogin)
+- Bug #15432: Fixed wrong value being set in `yii\filters\RateLimiter::checkRateLimit()` resulting in wrong `X-Rate-Limit-Reset` header value (bizley)
+- Bug #15440: Fixed `yii\behaviors\AttributeTypecastBehavior::$attributeTypes` auto-detection fails for rule, which specify attribute with '!' prefix (klimov-paul)
+- Bug #15462: Fixed `accessChecker` configuration error (developeruz)
+- Bug #15494: Fixed missing `WWW-Authenticate` header (developeruz)
+- Bug #15522: Fixed `yii\db\ActiveRecord::refresh()` method does not use an alias in the condition (vladis84)
+- Bug #15523: `yii\web\Session` settings could now be configured after session is started (StalkAlex, rob006, daniel1302, samdark)
+- Bug #15536: Fixed `yii\widgets\ActiveForm::init()` for call `parent::init()` (panchenkodv)
+- Bug #15540: Fixed `yii\db\ActiveRecord::with()` unable to use relation defined via attached behavior in case `asArray` is enabled (klimov-paul)
+- Bug #15553: Fixed `yii\validators\NumberValidator` incorrectly validate resource (developeruz)
+- Bug #15621: Fixed `yii\web\User::getIdentity()` returning `null` if an exception had been thrown when it was called previously (brandonkelly)
+- Enh #3087: Added `yii\helpers\BaseHtml::error()` "errorSource" option to be able to customize errors display (yanggs07, developeruz, silverfire)
+- Enh #3250: Added support for events partial wildcard matching (klimov-paul)
+- Enh #5515: Added default value for `yii\behaviors\BlameableBehavior` for cases when the user is guest (dmirogin)
+- Enh #6844: `yii\base\ArrayableTrait::toArray()` now allows recursive `$fields` and `$expand` (bboure)
+- Enh #7988: Added `\yii\helpers\Console::errorSummary()` and `\yii\helpers\Json::errorSummary()` (developeruz)
+- Enh #7996: Short syntax for verb in GroupUrlRule (schojniak, developeruz)
+- Enh #8092: ExistValidator for relations (developeruz)
+- Enh #8527: Added `yii\i18n\Locale` component having `getCurrencySymbol()` method (amarox, samdark)
+- Enh #8752: Allow specify `$attributeNames` as a string for `yii\base\Model` `validate()` method (developeruz)
+- Enh #9137: Added `Access-Control-Allow-Method` header for the OPTIONS request (developeruz)
+- Enh #9253: Allow `variations` to be a string for `yii\filters\PageCache` and `yii\widgets\FragmentCache` (schojniak, developeruz)
+- Enh #9771: Assign hidden input with its own set of HTML options via `$hiddenOptions` in activeFileInput `$options` (HanafiAhmat)
+- Enh #10186: Use native `hash_equals` in `yii\base\Security::compareString()` if available, throw exception if non-strings are compared (aotd1, samdark)
+- Enh #11611: Added `BetweenColumnsCondition` to build SQL condition like `value BETWEEN col1 and col2` (silverfire)
+- Enh #12623: Added `yii\helpers\StringHelper::matchWildcard()` replacing usage of `fnmatch()`, which may be unreliable (klimov-paul)
+- Enh #13019: Support JSON in SchemaBuilderTrait (zhukovra, undefinedor)
+- Enh #13465: Added `yii\helpers\FileHelper::findDirectory()` method (ArsSirek, developeruz)
+- Enh #13618: Active record now resets related models after corresponding attributes updates (Kolyunya, rob006)
+- Enh #13679: Added `yii\behaviors\CacheableWidgetBehavior` (Kolyunya)
+- Enh #13814: MySQL unique index names can now contain spaces (df2)
+- Enh #13879: Added upsert support for `yii\db\QueryBuilder` and `yii\db\Command` (sergeymakinen)
+- Enh #13919: Added option to add comment for created table to migration console command (mixartemev, developeruz)
+- Enh #13996: Added `yii\web\View::registerJsVar()` method that allows registering JavaScript variables (Eseperio, samdark)
+- Enh #14043: Added `yii\helpers\IpHelper` (silverfire, cebe)
+- Enh #14355: Added ability to pass an empty array as a parameter in console command (developeruz)
+- Enh #14488: Added support for X-Forwarded-Host to `yii\web\Request`, fixed `getServerPort()` usage (si294r, samdark)
+- Enh #14546: Added `dataDirectory` property into `BaseActiveFixture` (leandrogehlen)
+- Enh #14568: Refactored migration templates to use `safeUp()` and `safeDown()` methods (Kolyunya)
+- Enh #14638: Added `yii\db\SchemaBuilderTrait::tinyInteger()` (rob006)
+- Enh #14643: Added `yii\web\ErrorAction::$layout` property to conveniently set layout from error action config (swods, cebe, samdark)
+- Enh #14662: Added support for custom `Content-Type` specification to `yii\web\JsonResponseFormatter` (Kolyunya)
+- Enh #14732, #11218, #14810, #10855: It is now possible to pass `yii\db\Query` anywhere, where `yii\db\Expression` was supported (silverfire)
+- Enh #14806: Added $placeFooterAfterBody option for GridView (terehru)
+- Enh #15024: `yii\web\Pjax` widget does not prevent CSS files from sending anymore because they are handled by client-side plugin correctly (onmotion)
+- Enh #15047: `yii\db\Query::select()` and `yii\db\Query::addSelect()` now check for duplicate column names (wapmorgan)
+- Enh #15135: Automatic completion for help in bash and zsh (Valkeru)
+- Enh #15216: Added `yii\web\ErrorHandler::$traceLine` to allow opening file at line clicked in IDE (vladis84)
+- Enh #15219: Added `yii\filters\auth\HttpHeaderAuth` (bboure)
+- Enh #15221: Added support for specifying `--camelCase` console options in `--kebab-case` (brandonkelly)
+- Enh #15221: Added support for the `--
+ *
+ * The return value will be casted to boolean if non-boolean was returned.
+ * @since 5.0.0
+ */
+ public function offsetExists($offset)
+ {
+ return isset($this->value[$offset]);
+ }
+
+ /**
+ * Offset to retrieve
+ *
+ * @link http://php.net/manual/en/arrayaccess.offsetget.php
+ * @param mixed $offset
+ * The offset to retrieve.
+ *
+ * @return mixed Can return all value types.
+ * @since 5.0.0
+ */
+ public function offsetGet($offset)
+ {
+ return $this->value[$offset];
+ }
+
+ /**
+ * Offset to set
+ *
+ * @link http://php.net/manual/en/arrayaccess.offsetset.php
+ * @param mixed $offset
';
+ }
+
+ return Html::tag('div', $header . $content . $footer, $options);
+ }
+ /**
+ * Return array of the validation errors
+ * @param Model|Model[] $models the model(s) whose validation errors are to be displayed.
+ * @param $encode boolean, if set to false then the error messages won't be encoded.
+ * @param $showAllErrors boolean, if set to true every error message for each attribute will be shown otherwise
+ * only the first error message for each attribute will be shown.
+ * @return array of the validation errors
+ * @since 2.0.14
+ */
+ private static function collectErrors($models, $encode, $showAllErrors)
+ {
$lines = [];
if (!is_array($models)) {
$models = [$models];
}
+
foreach ($models as $model) {
- /* @var $model Model */
- foreach ($model->getErrors() as $errors) {
- foreach ($errors as $error) {
- $line = $encode ? Html::encode($error) : $error;
- if (!in_array($line, $lines, true)) {
- $lines[] = $line;
- }
- if (!$showAllErrors) {
- break;
- }
- }
- }
+ $lines = array_unique(array_merge($lines, $model->getErrorSummary($showAllErrors)));
}
- if (empty($lines)) {
- // still render the placeholder for client-side validation use
- $content = '
';
+ if ($encode) {
+ for ($i = 0, $linesCount = count($lines); $i < $linesCount; $i++) {
+ $lines[$i] = Html::encode($lines[$i]);
+ }
}
- return Html::tag('div', $header . $content . $footer, $options);
+ return $lines;
}
/**
@@ -1261,6 +1278,9 @@ class BaseHtml
* - tag: this specifies the tag name. If not set, "div" will be used.
* See also [[tag()]].
* - encode: boolean, if set to false then the error message won't be encoded.
+ * - errorSource (since 2.0.14): \Closure|callable, callback that will be called to obtain an error message.
+ * The signature of the callback must be: `function ($model, $attribute)` and return a string.
+ * When not set, the `$model->getFirstError()` method will be called.
*
* See [[renderTagAttributes()]] for details on how attributes are being rendered.
*
@@ -1269,7 +1289,12 @@ class BaseHtml
public static function error($model, $attribute, $options = [])
{
$attribute = static::getAttributeName($attribute);
- $error = $model->getFirstError($attribute);
+ $errorSource = ArrayHelper::remove($options, 'errorSource');
+ if ($errorSource !== null) {
+ $error = call_user_func($errorSource, $model, $attribute);
+ } else {
+ $error = $model->getFirstError($attribute);
+ }
$tag = ArrayHelper::remove($options, 'tag', 'div');
$encode = ArrayHelper::remove($options, 'encode', true);
return Html::tag($tag, $encode ? Html::encode($error) : $error, $options);
@@ -1296,6 +1321,8 @@ class BaseHtml
$options['id'] = static::getInputId($model, $attribute);
}
+ self::setActivePlaceholder($model, $attribute, $options);
+
return static::input($type, $name, $value, $options);
}
@@ -1335,6 +1362,8 @@ class BaseHtml
* - maxlength: integer|boolean, when `maxlength` is set true and the model attribute is validated
* by a string validator, the `maxlength` option will take the value of [[\yii\validators\StringValidator::max]].
* This is available since version 2.0.3.
+ * - placeholder: string|boolean, when `placeholder` equals `true`, the attribute label from the $model will be used
+ * as a placeholder (this behavior is available since version 2.0.14).
*
* @return string the generated input tag
*/
@@ -1345,6 +1374,23 @@ class BaseHtml
}
/**
+ * Generate placeholder from model attribute label.
+ *
+ * @param Model $model the model object
+ * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
+ * about attribute expression.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * @since 2.0.14
+ */
+ protected static function setActivePlaceholder($model, $attribute, &$options = [])
+ {
+ if (isset($options['placeholder']) && $options['placeholder'] === true) {
+ $options['placeholder'] = $model->getAttributeLabel($attribute);
+ }
+ }
+
+ /**
* Generates a hidden input tag for the given model attribute.
* This method will generate the "name" and "value" tag attributes automatically for the model attribute
* unless they are explicitly specified in `$options`.
@@ -1376,6 +1422,8 @@ class BaseHtml
* - maxlength: integer|boolean, when `maxlength` is set true and the model attribute is validated
* by a string validator, the `maxlength` option will take the value of [[\yii\validators\StringValidator::max]].
* This option is available since version 2.0.6.
+ * - placeholder: string|boolean, when `placeholder` equals `true`, the attribute label from the $model will be used
+ * as a placeholder (this behavior is available since version 2.0.14).
*
* @return string the generated input tag
*/
@@ -1389,22 +1437,29 @@ class BaseHtml
* Generates a file input tag for the given model attribute.
* This method will generate the "name" and "value" tag attributes automatically for the model attribute
* unless they are explicitly specified in `$options`.
+ * Additionally, if a separate set of HTML options array is defined inside `$options` with a key named `hiddenOptions`,
+ * it will be passed to the `activeHiddenInput` field as its own `$options` parameter.
* @param Model $model the model object
* @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
* about attribute expression.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* See [[renderTagAttributes()]] for details on how attributes are being rendered.
+ * If `hiddenOptions` parameter which is another set of HTML options array is defined, it will be extracted
+ * from `$options` to be used for the hidden input.
* @return string the generated input tag
*/
public static function activeFileInput($model, $attribute, $options = [])
{
- // add a hidden field so that if a model only has a file field, we can
- // still use isset($_POST[$modelClass]) to detect if the input is submitted
$hiddenOptions = ['id' => null, 'value' => ''];
if (isset($options['name'])) {
$hiddenOptions['name'] = $options['name'];
}
+ $hiddenOptions = ArrayHelper::merge($hiddenOptions, ArrayHelper::remove($options, 'hiddenOptions', []));
+ // add a hidden field so that if a model only has a file field, we can
+ // still use isset($_POST[$modelClass]) to detect if the input is submitted.
+ // The hidden input will be assigned its own set of html options via `$hiddenOptions`.
+ // This provides the possibility to interact with the hidden field via client script.
return static::activeHiddenInput($model, $attribute, $hiddenOptions)
. static::activeInput('file', $model, $attribute, $options);
@@ -1424,6 +1479,8 @@ class BaseHtml
* - maxlength: integer|boolean, when `maxlength` is set true and the model attribute is validated
* by a string validator, the `maxlength` option will take the value of [[\yii\validators\StringValidator::max]].
* This option is available since version 2.0.6.
+ * - placeholder: string|boolean, when `placeholder` equals `true`, the attribute label from the $model will be used
+ * as a placeholder (this behavior is available since version 2.0.14).
*
* @return string the generated textarea tag
*/
@@ -1440,6 +1497,7 @@ class BaseHtml
$options['id'] = static::getInputId($model, $attribute);
}
self::normalizeMaxLength($model, $attribute, $options);
+ self::setActivePlaceholder($model, $attribute, $options);
return static::textarea($name, $value, $options);
}
@@ -1756,6 +1814,10 @@ class BaseHtml
*/
public static function renderSelectOptions($selection, $items, &$tagOptions = [])
{
+ if (ArrayHelper::isTraversable($selection)) {
+ $selection = array_map('strval', (array)$selection);
+ }
+
$lines = [];
$encodeSpaces = ArrayHelper::remove($tagOptions, 'encodeSpaces', false);
$encode = ArrayHelper::remove($tagOptions, 'encode', true);
@@ -1795,7 +1857,7 @@ class BaseHtml
if (!array_key_exists('selected', $attrs)) {
$attrs['selected'] = $selection !== null &&
(!ArrayHelper::isTraversable($selection) && !strcmp($key, $selection)
- || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn($key, $selection));
+ || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn((string)$key, $selection));
}
$text = $encode ? static::encode($value) : $value;
if ($encodeSpaces) {
@@ -1829,6 +1891,7 @@ class BaseHtml
* @return string the rendering result. If the attributes are not empty, they will be rendered
* into a string with a leading white space (so that it can be directly appended to the tag name
* in a tag. If there is no attribute, an empty string will be returned.
+ * @see addCssClass()
*/
public static function renderTagAttributes($attributes)
{
@@ -1893,6 +1956,8 @@ class BaseHtml
*
* @param array $options the options to be modified.
* @param string|array $class the CSS class(es) to be added
+ * @see mergeCssClasses()
+ * @see removeCssClass()
*/
public static function addCssClass(&$options, $class)
{
@@ -1914,6 +1979,7 @@ class BaseHtml
* @param array $existingClasses already existing CSS classes.
* @param array $additionalClasses CSS classes to be added.
* @return array merge result.
+ * @see addCssClass()
*/
private static function mergeCssClasses(array $existingClasses, array $additionalClasses)
{
@@ -1932,6 +1998,7 @@ class BaseHtml
* Removes a CSS class from the specified options.
* @param array $options the options to be modified.
* @param string|array $class the CSS class(es) to be removed
+ * @see addCssClass()
*/
public static function removeCssClass(&$options, $class)
{
@@ -2087,7 +2154,7 @@ class BaseHtml
* If `$attribute` has neither prefix nor suffix, it will be returned back without change.
* @param string $attribute the attribute name or expression
* @return string the attribute name without prefix and suffix.
- * @throws InvalidParamException if the attribute name contains non-word characters.
+ * @throws InvalidArgumentException if the attribute name contains non-word characters.
*/
public static function getAttributeName($attribute)
{
@@ -2095,7 +2162,7 @@ class BaseHtml
return $matches[2];
}
- throw new InvalidParamException('Attribute name must contain word characters only.');
+ throw new InvalidArgumentException('Attribute name must contain word characters only.');
}
/**
@@ -2110,12 +2177,12 @@ class BaseHtml
* @param Model $model the model object
* @param string $attribute the attribute name or expression
* @return string|array the corresponding attribute value
- * @throws InvalidParamException if the attribute name contains non-word characters.
+ * @throws InvalidArgumentException if the attribute name contains non-word characters.
*/
public static function getAttributeValue($model, $attribute)
{
if (!preg_match(static::$attributeRegex, $attribute, $matches)) {
- throw new InvalidParamException('Attribute name must contain word characters only.');
+ throw new InvalidArgumentException('Attribute name must contain word characters only.');
}
$attribute = $matches[2];
$value = $model->$attribute;
@@ -2159,13 +2226,13 @@ class BaseHtml
* @param Model $model the model object
* @param string $attribute the attribute name or expression
* @return string the generated input name
- * @throws InvalidParamException if the attribute name contains non-word characters.
+ * @throws InvalidArgumentException if the attribute name contains non-word characters.
*/
public static function getInputName($model, $attribute)
{
$formName = $model->formName();
if (!preg_match(static::$attributeRegex, $attribute, $matches)) {
- throw new InvalidParamException('Attribute name must contain word characters only.');
+ throw new InvalidArgumentException('Attribute name must contain word characters only.');
}
$prefix = $matches[1];
$attribute = $matches[2];
@@ -2176,7 +2243,7 @@ class BaseHtml
return $formName . $prefix . "[$attribute]" . $suffix;
}
- throw new InvalidParamException(get_class($model) . '::formName() cannot be empty for tabular inputs.');
+ throw new InvalidArgumentException(get_class($model) . '::formName() cannot be empty for tabular inputs.');
}
/**
@@ -2187,7 +2254,7 @@ class BaseHtml
* @param Model $model the model object
* @param string $attribute the attribute name or expression. See [[getAttributeName()]] for explanation of attribute expression.
* @return string the generated input ID
- * @throws InvalidParamException if the attribute name contains non-word characters.
+ * @throws InvalidArgumentException if the attribute name contains non-word characters.
*/
public static function getInputId($model, $attribute)
{
diff --git a/framework/helpers/BaseIpHelper.php b/framework/helpers/BaseIpHelper.php
new file mode 100644
index 0000000..74df866
--- /dev/null
+++ b/framework/helpers/BaseIpHelper.php
@@ -0,0 +1,115 @@
+
+ * @since 2.0.14
+ */
+class BaseIpHelper
+{
+ const IPV4 = 4;
+ const IPV6 = 6;
+ /**
+ * The length of IPv6 address in bits
+ */
+ const IPV6_ADDRESS_LENGTH = 128;
+ /**
+ * The length of IPv4 address in bits
+ */
+ const IPV4_ADDRESS_LENGTH = 32;
+
+ /**
+ * Gets the IP version. Does not perform IP address validation.
+ *
+ * @param string $ip the valid IPv4 or IPv6 address.
+ * @return int [[IPV4]] or [[IPV6]]
+ */
+ public static function getIpVersion($ip)
+ {
+ return strpos($ip, ':') === false ? self::IPV4 : self::IPV6;
+ }
+
+ /**
+ * Checks whether IP address or subnet $subnet is contained by $subnet.
+ *
+ * For example, the following code checks whether subnet `192.168.1.0/24` is in subnet `192.168.0.0/22`:
+ *
+ * ```php
+ * IpHelper::inRange('192.168.1.0/24', '192.168.0.0/22'); // true
+ * ```
+ *
+ * In case you need to check whether a single IP address `192.168.1.21` is in the subnet `192.168.1.0/24`,
+ * you can use any of theses examples:
+ *
+ * ```php
+ * IpHelper::inRange('192.168.1.21', '192.168.1.0/24'); // true
+ * IpHelper::inRange('192.168.1.21/32', '192.168.1.0/24'); // true
+ * ```
+ *
+ * @param string $subnet the valid IPv4 or IPv6 address or CIDR range, e.g.: `10.0.0.0/8` or `2001:af::/64`
+ * @param string $range the valid IPv4 or IPv6 CIDR range, e.g. `10.0.0.0/8` or `2001:af::/64`
+ * @return bool whether $subnet is contained by $range
+ *
+ * @see https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing
+ */
+ public static function inRange($subnet, $range)
+ {
+ list($ip, $mask) = array_pad(explode('/', $subnet), 2, null);
+ list($net, $netMask) = array_pad(explode('/', $range), 2, null);
+
+ $ipVersion = static::getIpVersion($ip);
+ $netVersion = static::getIpVersion($net);
+ if ($ipVersion !== $netVersion) {
+ return false;
+ }
+
+ $maxMask = $ipVersion === self::IPV4 ? self::IPV4_ADDRESS_LENGTH : self::IPV6_ADDRESS_LENGTH;
+ $mask = isset($mask) ? $mask : $maxMask;
+ $netMask = isset($netMask) ? $netMask : $maxMask;
+
+ $binIp = static::ip2bin($ip);
+ $binNet = static::ip2bin($net);
+ return substr($binIp, 0, $netMask) === substr($binNet, 0, $netMask) && $mask >= $netMask;
+ }
+
+ /**
+ * Expands an IPv6 address to it's full notation.
+ *
+ * For example `2001:db8::1` will be expanded to `2001:0db8:0000:0000:0000:0000:0000:0001`
+ *
+ * @param string $ip the original valid IPv6 address
+ * @return string the expanded IPv6 address
+ */
+ public static function expandIPv6($ip)
+ {
+ $hex = unpack('H*hex', inet_pton($ip));
+ return substr(preg_replace('/([a-f0-9]{4})/i', '$1:', $hex['hex']), 0, -1);
+ }
+
+ /**
+ * Converts IP address to bits representation.
+ *
+ * @param string $ip the valid IPv4 or IPv6 address
+ * @return string bits as a string
+ */
+ public static function ip2bin($ip)
+ {
+ if (static::getIpVersion($ip) === self::IPV4) {
+ return str_pad(base_convert(ip2long($ip), 10, 2), self::IPV4_ADDRESS_LENGTH, '0', STR_PAD_LEFT);
+ }
+
+ $unpack = unpack('A16', inet_pton($ip));
+ $binStr = array_shift($unpack);
+ $bytes = self::IPV6_ADDRESS_LENGTH / 8; // 128 bit / 8 = 16 bytes
+ $result = '';
+ while ($bytes-- > 0) {
+ $result = sprintf('%08b', isset($binStr[$bytes]) ? ord($binStr[$bytes]) : '0') . $result;
+ }
+ return $result;
+ }
+}
diff --git a/framework/helpers/BaseJson.php b/framework/helpers/BaseJson.php
index 5ac9394..486882f 100644
--- a/framework/helpers/BaseJson.php
+++ b/framework/helpers/BaseJson.php
@@ -8,8 +8,9 @@
namespace yii\helpers;
use yii\base\Arrayable;
-use yii\base\InvalidParamException;
+use yii\base\InvalidArgumentException;
use yii\web\JsExpression;
+use yii\base\Model;
/**
* BaseJson provides concrete implementation for [[Json]].
@@ -52,7 +53,7 @@ class BaseJson
* @param int $options the encoding options. For more details please refer to
* . Default is `JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE`.
* @return string the encoding result.
- * @throws InvalidParamException if there is any encoding error.
+ * @throws InvalidArgumentException if there is any encoding error.
*/
public static function encode($value, $options = 320)
{
@@ -81,7 +82,7 @@ class BaseJson
* @param mixed $value the data to be encoded
* @return string the encoding result
* @since 2.0.4
- * @throws InvalidParamException if there is any encoding error
+ * @throws InvalidArgumentException if there is any encoding error
*/
public static function htmlEncode($value)
{
@@ -93,12 +94,12 @@ class BaseJson
* @param string $json the JSON string to be decoded
* @param bool $asArray whether to return objects in terms of associative arrays.
* @return mixed the PHP data
- * @throws InvalidParamException if there is any decoding error
+ * @throws InvalidArgumentException if there is any decoding error
*/
public static function decode($json, $asArray = true)
{
if (is_array($json)) {
- throw new InvalidParamException('Invalid JSON data.');
+ throw new InvalidArgumentException('Invalid JSON data.');
} elseif ($json === null || $json === '') {
return null;
}
@@ -112,7 +113,7 @@ class BaseJson
* Handles [[encode()]] and [[decode()]] errors by throwing exceptions with the respective error message.
*
* @param int $lastError error code from [json_last_error()](http://php.net/manual/en/function.json-last-error.php).
- * @throws \yii\base\InvalidParamException if there is any encoding/decoding error.
+ * @throws InvalidArgumentException if there is any encoding/decoding error.
* @since 2.0.6
*/
protected static function handleJsonError($lastError)
@@ -129,10 +130,10 @@ class BaseJson
}
if (isset($availableErrors[$lastError])) {
- throw new InvalidParamException($availableErrors[$lastError], $lastError);
+ throw new InvalidArgumentException($availableErrors[$lastError], $lastError);
}
- throw new InvalidParamException('Unknown JSON encoding/decoding error.');
+ throw new InvalidArgumentException('Unknown JSON encoding/decoding error.');
}
/**
@@ -179,4 +180,45 @@ class BaseJson
return $data;
}
+
+ /**
+ * Generates a summary of the validation errors.
+ * @param Model|Model[] $models the model(s) whose validation errors are to be displayed.
+ * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
+ *
+ * - showAllErrors: boolean, if set to true every error message for each attribute will be shown otherwise
+ * only the first error message for each attribute will be shown. Defaults to `false`.
+ *
+ * @return string the generated error summary
+ * @since 2.0.14
+ */
+ public static function errorSummary($models, $options = [])
+ {
+ $showAllErrors = ArrayHelper::remove($options, 'showAllErrors', false);
+ $lines = self::collectErrors($models, $showAllErrors);
+
+ return json_encode($lines);
+ }
+
+ /**
+ * Return array of the validation errors
+ * @param Model|Model[] $models the model(s) whose validation errors are to be displayed.
+ * @param $showAllErrors boolean, if set to true every error message for each attribute will be shown otherwise
+ * only the first error message for each attribute will be shown.
+ * @return array of the validation errors
+ * @since 2.0.14
+ */
+ private static function collectErrors($models, $showAllErrors)
+ {
+ $lines = [];
+ if (!is_array($models)) {
+ $models = [$models];
+ }
+
+ foreach ($models as $model) {
+ $lines = array_unique(array_merge($lines, $model->getErrorSummary($showAllErrors)));
+ }
+
+ return $lines;
+ }
}
diff --git a/framework/helpers/BaseMarkdown.php b/framework/helpers/BaseMarkdown.php
index 0c50213..08da9c5 100644
--- a/framework/helpers/BaseMarkdown.php
+++ b/framework/helpers/BaseMarkdown.php
@@ -8,7 +8,7 @@
namespace yii\helpers;
use Yii;
-use yii\base\InvalidParamException;
+use yii\base\InvalidArgumentException;
/**
* BaseMarkdown provides concrete implementation for [[Markdown]].
@@ -57,7 +57,7 @@ class BaseMarkdown
* @param string $flavor the markdown flavor to use. See [[$flavors]] for available values.
* Defaults to [[$defaultFlavor]], if not set.
* @return string the parsed HTML output
- * @throws \yii\base\InvalidParamException when an undefined flavor is given.
+ * @throws InvalidArgumentException when an undefined flavor is given.
*/
public static function process($markdown, $flavor = null)
{
@@ -75,7 +75,7 @@ class BaseMarkdown
* @param string $flavor the markdown flavor to use. See [[$flavors]] for available values.
* Defaults to [[$defaultFlavor]], if not set.
* @return string the parsed HTML output
- * @throws \yii\base\InvalidParamException when an undefined flavor is given.
+ * @throws InvalidArgumentException when an undefined flavor is given.
*/
public static function processParagraph($markdown, $flavor = null)
{
@@ -88,7 +88,7 @@ class BaseMarkdown
* @param string $flavor the markdown flavor to use. See [[$flavors]] for available values.
* Defaults to [[$defaultFlavor]], if not set.
* @return \cebe\markdown\Parser
- * @throws \yii\base\InvalidParamException when an undefined flavor is given.
+ * @throws InvalidArgumentException when an undefined flavor is given.
*/
protected static function getParser($flavor)
{
@@ -97,7 +97,7 @@ class BaseMarkdown
}
/* @var $parser \cebe\markdown\Markdown */
if (!isset(static::$flavors[$flavor])) {
- throw new InvalidParamException("Markdown flavor '$flavor' is not defined.'");
+ throw new InvalidArgumentException("Markdown flavor '$flavor' is not defined.'");
} elseif (!is_object($config = static::$flavors[$flavor])) {
static::$flavors[$flavor] = Yii::createObject($config);
}
diff --git a/framework/helpers/BaseStringHelper.php b/framework/helpers/BaseStringHelper.php
index f7b1a40..f7469c1 100644
--- a/framework/helpers/BaseStringHelper.php
+++ b/framework/helpers/BaseStringHelper.php
@@ -365,4 +365,57 @@ class BaseStringHelper
// so its safe to call str_replace here
return str_replace(',', '.', (string) $number);
}
+
+ /**
+ * Checks if the passed string would match the given shell wildcard pattern.
+ * This function emulates [[fnmatch()]], which may be unavailable at certain environment, using PCRE.
+ * @param string $pattern the shell wildcard pattern.
+ * @param string $string the tested string.
+ * @param array $options options for matching. Valid options are:
+ *
+ * - caseSensitive: bool, whether pattern should be case sensitive. Defaults to `true`.
+ * - escape: bool, whether backslash escaping is enabled. Defaults to `true`.
+ * - filePath: bool, whether slashes in string only matches slashes in the given pattern. Defaults to `false`.
+ *
+ * @return bool whether the string matches pattern or not.
+ * @since 2.0.14
+ */
+ public static function matchWildcard($pattern, $string, $options = [])
+ {
+ if ($pattern === '*' && empty($options['filePath'])) {
+ return true;
+ }
+
+ $replacements = [
+ '\\\\\\\\' => '\\\\',
+ '\\\\\\*' => '[*]',
+ '\\\\\\?' => '[?]',
+ '\*' => '.*',
+ '\?' => '.',
+ '\[\!' => '[^',
+ '\[' => '[',
+ '\]' => ']',
+ '\-' => '-',
+ ];
+
+ if (isset($options['escape']) && !$options['escape']) {
+ unset($replacements['\\\\\\\\']);
+ unset($replacements['\\\\\\*']);
+ unset($replacements['\\\\\\?']);
+ }
+
+ if (!empty($options['filePath'])) {
+ $replacements['\*'] = '[^/\\\\]*';
+ $replacements['\?'] = '[^/\\\\]';
+ }
+
+ $pattern = strtr(preg_quote($pattern, '#'), $replacements);
+ $pattern = '#^' . $pattern . '$#us';
+
+ if (isset($options['caseSensitive']) && !$options['caseSensitive']) {
+ $pattern .= 'i';
+ }
+
+ return preg_match($pattern, $string) === 1;
+ }
}
diff --git a/framework/helpers/BaseUrl.php b/framework/helpers/BaseUrl.php
index 13789c3..4c26696 100644
--- a/framework/helpers/BaseUrl.php
+++ b/framework/helpers/BaseUrl.php
@@ -8,7 +8,7 @@
namespace yii\helpers;
use Yii;
-use yii\base\InvalidParamException;
+use yii\base\InvalidArgumentException;
/**
* BaseUrl provides concrete implementation for [[Url]].
@@ -91,7 +91,7 @@ class BaseUrl
* for protocol-relative URL).
*
* @return string the generated URL
- * @throws InvalidParamException a relative route is given while there is no active controller
+ * @throws InvalidArgumentException a relative route is given while there is no active controller
*/
public static function toRoute($route, $scheme = false)
{
@@ -122,7 +122,7 @@ class BaseUrl
*
* @param string $route the route. This can be either an absolute route or a relative route.
* @return string normalized route suitable for UrlManager
- * @throws InvalidParamException a relative route is given while there is no active controller
+ * @throws InvalidArgumentException a relative route is given while there is no active controller
*/
protected static function normalizeRoute($route)
{
@@ -134,7 +134,7 @@ class BaseUrl
// relative route
if (Yii::$app->controller === null) {
- throw new InvalidParamException("Unable to resolve the relative route: $route. No active controller is available.");
+ throw new InvalidArgumentException("Unable to resolve the relative route: $route. No active controller is available.");
}
if (strpos($route, '/') === false) {
@@ -206,7 +206,7 @@ class BaseUrl
* for protocol-relative URL).
*
* @return string the generated URL
- * @throws InvalidParamException a relative route is given while there is no active controller
+ * @throws InvalidArgumentException a relative route is given while there is no active controller
*/
public static function to($url = '', $scheme = false)
{
@@ -429,7 +429,7 @@ class BaseUrl
{
$currentParams = Yii::$app->getRequest()->getQueryParams();
$currentParams[0] = '/' . Yii::$app->controller->getRoute();
- $route = array_replace($currentParams, $params);
+ $route = array_replace_recursive($currentParams, $params);
return static::toRoute($route, $scheme);
}
diff --git a/framework/helpers/IpHelper.php b/framework/helpers/IpHelper.php
new file mode 100644
index 0000000..8aedef4
--- /dev/null
+++ b/framework/helpers/IpHelper.php
@@ -0,0 +1,16 @@
+
+ * @since 2.0.14
+ */
+class IpHelper extends BaseIpHelper
+{
+}
diff --git a/framework/helpers/mimeAliases.php b/framework/helpers/mimeAliases.php
new file mode 100644
index 0000000..89a9797
--- /dev/null
+++ b/framework/helpers/mimeAliases.php
@@ -0,0 +1,9 @@
+ 'application/xml',
+];
diff --git a/framework/i18n/Formatter.php b/framework/i18n/Formatter.php
index f74b7a2..01e6142 100644
--- a/framework/i18n/Formatter.php
+++ b/framework/i18n/Formatter.php
@@ -16,8 +16,8 @@ use IntlDateFormatter;
use NumberFormatter;
use Yii;
use yii\base\Component;
+use yii\base\InvalidArgumentException;
use yii\base\InvalidConfigException;
-use yii\base\InvalidParamException;
use yii\helpers\FormatConverter;
use yii\helpers\Html;
use yii\helpers\HtmlPurifier;
@@ -371,7 +371,7 @@ class Formatter extends Component
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -417,7 +417,7 @@ class Formatter extends Component
* which can be used to call other formatting functions.
* The possibility to use an anonymous function is available since version 2.0.13.
* @return string the formatting result.
- * @throws InvalidParamException if the format type is not supported by this class.
+ * @throws InvalidArgumentException if the format type is not supported by this class.
*/
public function format($value, $format)
{
@@ -425,7 +425,7 @@ class Formatter extends Component
return call_user_func($format, $value, $this);
} elseif (is_array($format)) {
if (!isset($format[0])) {
- throw new InvalidParamException('The $format array must contain at least one element.');
+ throw new InvalidArgumentException('The $format array must contain at least one element.');
}
$f = $format[0];
$format[0] = $value;
@@ -439,7 +439,7 @@ class Formatter extends Component
return call_user_func_array([$this, $method], $params);
}
- throw new InvalidParamException("Unknown format type: $format");
+ throw new InvalidArgumentException("Unknown format type: $format");
}
@@ -616,7 +616,7 @@ class Formatter extends Component
* PHP [date()](http://php.net/manual/en/function.date.php)-function.
*
* @return string the formatted result.
- * @throws InvalidParamException if the input value can not be evaluated as a date value.
+ * @throws InvalidArgumentException if the input value can not be evaluated as a date value.
* @throws InvalidConfigException if the date format is invalid.
* @see dateFormat
*/
@@ -653,7 +653,7 @@ class Formatter extends Component
* PHP [date()](http://php.net/manual/en/function.date.php)-function.
*
* @return string the formatted result.
- * @throws InvalidParamException if the input value can not be evaluated as a date value.
+ * @throws InvalidArgumentException if the input value can not be evaluated as a date value.
* @throws InvalidConfigException if the date format is invalid.
* @see timeFormat
*/
@@ -690,7 +690,7 @@ class Formatter extends Component
* PHP [date()](http://php.net/manual/en/function.date.php)-function.
*
* @return string the formatted result.
- * @throws InvalidParamException if the input value can not be evaluated as a date value.
+ * @throws InvalidArgumentException if the input value can not be evaluated as a date value.
* @throws InvalidConfigException if the date format is invalid.
* @see datetimeFormat
*/
@@ -809,7 +809,7 @@ class Formatter extends Component
* 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
* or it is just a time value.
- * @throws InvalidParamException if the input value can not be evaluated as a date value.
+ * @throws InvalidArgumentException if the input value can not be evaluated as a date value.
*/
protected function normalizeDatetimeValue($value, $checkDateTimeInfo = false)
{
@@ -843,7 +843,7 @@ class Formatter extends Component
return new DateTime($value, new DateTimeZone($this->defaultTimeZone));
} catch (\Exception $e) {
- throw new InvalidParamException("'$value' is not a valid date time value: " . $e->getMessage()
+ throw new InvalidArgumentException("'$value' is not a valid date time value: " . $e->getMessage()
. "\n" . print_r(DateTime::getLastErrors(), true), $e->getCode(), $e);
}
}
@@ -890,7 +890,7 @@ class Formatter extends Component
* @param int|string|DateTime $referenceTime if specified the value is used as a reference time instead of `now`
* when `$value` is not a `DateInterval` object.
* @return string the formatted result.
- * @throws InvalidParamException if the input value can not be evaluated as a date value.
+ * @throws InvalidArgumentException if the input value can not be evaluated as a date value.
*/
public function asRelativeTime($value, $referenceTime = null)
{
@@ -1013,6 +1013,7 @@ class Formatter extends Component
$isNegative = $interval->invert;
}
+ $parts = [];
if ($interval->y > 0) {
$parts[] = Yii::t('yii', '{delta, plural, =1{1 year} other{# years}}', ['delta' => $interval->y], $this->locale);
}
@@ -1050,7 +1051,7 @@ class Formatter extends Component
* @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].
* @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].
* @return string the formatted result.
- * @throws InvalidParamException if the input value is not numeric or the formatting failed.
+ * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.
*/
public function asInteger($value, $options = [], $textOptions = [])
{
@@ -1062,7 +1063,7 @@ class Formatter extends Component
$f = $this->createNumberFormatter(NumberFormatter::DECIMAL, null, $options, $textOptions);
$f->setAttribute(NumberFormatter::FRACTION_DIGITS, 0);
if (($result = $f->format($value, NumberFormatter::TYPE_INT64)) === false) {
- throw new InvalidParamException('Formatting integer value failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());
+ throw new InvalidArgumentException('Formatting integer value failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());
}
return $result;
@@ -1088,7 +1089,7 @@ class Formatter extends Component
* @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].
* @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].
* @return string the formatted result.
- * @throws InvalidParamException if the input value is not numeric or the formatting failed.
+ * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.
* @see decimalSeparator
* @see thousandSeparator
*/
@@ -1102,7 +1103,7 @@ class Formatter extends Component
if ($this->_intlLoaded) {
$f = $this->createNumberFormatter(NumberFormatter::DECIMAL, $decimals, $options, $textOptions);
if (($result = $f->format($value)) === false) {
- throw new InvalidParamException('Formatting decimal value failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());
+ throw new InvalidArgumentException('Formatting decimal value failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());
}
return $result;
@@ -1130,7 +1131,7 @@ class Formatter extends Component
* @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].
* @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].
* @return string the formatted result.
- * @throws InvalidParamException if the input value is not numeric or the formatting failed.
+ * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.
*/
public function asPercent($value, $decimals = null, $options = [], $textOptions = [])
{
@@ -1142,7 +1143,7 @@ class Formatter extends Component
if ($this->_intlLoaded) {
$f = $this->createNumberFormatter(NumberFormatter::PERCENT, $decimals, $options, $textOptions);
if (($result = $f->format($value)) === false) {
- throw new InvalidParamException('Formatting percent value failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());
+ throw new InvalidArgumentException('Formatting percent value failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());
}
return $result;
@@ -1170,7 +1171,7 @@ class Formatter extends Component
* @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].
* @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].
* @return string the formatted result.
- * @throws InvalidParamException if the input value is not numeric or the formatting failed.
+ * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.
*/
public function asScientific($value, $decimals = null, $options = [], $textOptions = [])
{
@@ -1182,7 +1183,7 @@ class Formatter extends Component
if ($this->_intlLoaded) {
$f = $this->createNumberFormatter(NumberFormatter::SCIENTIFIC, $decimals, $options, $textOptions);
if (($result = $f->format($value)) === false) {
- throw new InvalidParamException('Formatting scientific number value failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());
+ throw new InvalidArgumentException('Formatting scientific number value failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());
}
return $result;
@@ -1207,7 +1208,7 @@ class Formatter extends Component
* @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].
* @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].
* @return string the formatted result.
- * @throws InvalidParamException if the input value is not numeric or the formatting failed.
+ * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.
* @throws InvalidConfigException if no currency is given and [[currencyCode]] is not defined.
*/
public function asCurrency($value, $currency = null, $options = [], $textOptions = [])
@@ -1231,7 +1232,7 @@ class Formatter extends Component
$result = $formatter->formatCurrency($value, $currency);
}
if ($result === false) {
- throw new InvalidParamException('Formatting currency value failed: ' . $formatter->getErrorCode() . ' ' . $formatter->getErrorMessage());
+ throw new InvalidArgumentException('Formatting currency value failed: ' . $formatter->getErrorCode() . ' ' . $formatter->getErrorMessage());
}
return $result;
@@ -1254,7 +1255,7 @@ class Formatter extends Component
*
* @param mixed $value the value to be formatted
* @return string the formatted result.
- * @throws InvalidParamException if the input value is not numeric or the formatting failed.
+ * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.
* @throws InvalidConfigException when the [PHP intl extension](http://php.net/manual/en/book.intl.php) is not available.
*/
public function asSpellout($value)
@@ -1266,7 +1267,7 @@ class Formatter extends Component
if ($this->_intlLoaded) {
$f = $this->createNumberFormatter(NumberFormatter::SPELLOUT);
if (($result = $f->format($value)) === false) {
- throw new InvalidParamException('Formatting number as spellout failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());
+ throw new InvalidArgumentException('Formatting number as spellout failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());
}
return $result;
@@ -1282,7 +1283,7 @@ class Formatter extends Component
*
* @param mixed $value the value to be formatted
* @return string the formatted result.
- * @throws InvalidParamException if the input value is not numeric or the formatting failed.
+ * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.
* @throws InvalidConfigException when the [PHP intl extension](http://php.net/manual/en/book.intl.php) is not available.
*/
public function asOrdinal($value)
@@ -1294,7 +1295,7 @@ class Formatter extends Component
if ($this->_intlLoaded) {
$f = $this->createNumberFormatter(NumberFormatter::ORDINAL);
if (($result = $f->format($value)) === false) {
- throw new InvalidParamException('Formatting number as ordinal failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());
+ throw new InvalidArgumentException('Formatting number as ordinal failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());
}
return $result;
@@ -1316,7 +1317,7 @@ class Formatter extends Component
* @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].
* @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].
* @return string the formatted result.
- * @throws InvalidParamException if the input value is not numeric or the formatting failed.
+ * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.
* @see sizeFormatBase
* @see asSize
*/
@@ -1372,7 +1373,7 @@ class Formatter extends Component
* @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].
* @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].
* @return string the formatted result.
- * @throws InvalidParamException if the input value is not numeric or the formatting failed.
+ * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.
* @see sizeFormatBase
* @see asShortSize
*/
@@ -1427,7 +1428,7 @@ class Formatter extends Component
* @param array $numberOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].
* @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].
* @return string the formatted result.
- * @throws InvalidParamException if the input value is not numeric or the formatting failed.
+ * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.
* @throws InvalidConfigException when INTL is not installed or does not contain required information.
* @see asLength
* @since 2.0.13
@@ -1450,7 +1451,7 @@ class Formatter extends Component
* @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].
* @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].
* @return string the formatted result.
- * @throws InvalidParamException if the input value is not numeric or the formatting failed.
+ * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.
* @throws InvalidConfigException when INTL is not installed or does not contain required information.
* @see asLength
* @since 2.0.13
@@ -1471,7 +1472,7 @@ class Formatter extends Component
* @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].
* @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].
* @return string the formatted result.
- * @throws InvalidParamException if the input value is not numeric or the formatting failed.
+ * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.
* @throws InvalidConfigException when INTL is not installed or does not contain required information.
* @since 2.0.13
* @author John Was
@@ -1493,7 +1494,7 @@ class Formatter extends Component
* @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].
* @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].
* @return string the formatted result.
- * @throws InvalidParamException if the input value is not numeric or the formatting failed.
+ * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.
* @throws InvalidConfigException when INTL is not installed or does not contain required information.
* @since 2.0.13
* @author John Was
@@ -1600,7 +1601,7 @@ class Formatter extends Component
* @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].
* @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].
* @return array [parameters for Yii::t containing formatted number, internal position of size unit]
- * @throws InvalidParamException if the input value is not numeric or the formatting failed.
+ * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.
*/
private function formatNumber($value, $decimals, $maxPosition, $formatBase, $options, $textOptions)
{
@@ -1668,7 +1669,7 @@ class Formatter extends Component
*
* @param mixed $value the input value
* @return float|int the normalized number value
- * @throws InvalidParamException if the input value is not numeric.
+ * @throws InvalidArgumentException if the input value is not numeric.
*/
protected function normalizeNumericValue($value)
{
@@ -1679,7 +1680,7 @@ class Formatter extends Component
$value = (float) $value;
}
if (!is_numeric($value)) {
- throw new InvalidParamException("'$value' is not a numeric value.");
+ throw new InvalidArgumentException("'$value' is not a numeric value.");
}
return $value;
diff --git a/framework/i18n/I18N.php b/framework/i18n/I18N.php
index 4b6bd69..49cf93b 100644
--- a/framework/i18n/I18N.php
+++ b/framework/i18n/I18N.php
@@ -110,7 +110,7 @@ class I18N extends Component
return $message;
}
- if (preg_match('~{\s*[\w]+\s*,~u', $message)) {
+ if (preg_match('~{\s*[\w.]+\s*,~u', $message)) {
$formatter = $this->getMessageFormatter();
$result = $formatter->format($message, $params, $language);
if ($result === false) {
diff --git a/framework/i18n/Locale.php b/framework/i18n/Locale.php
new file mode 100644
index 0000000..c45db6e
--- /dev/null
+++ b/framework/i18n/Locale.php
@@ -0,0 +1,60 @@
+locale === null) {
+ $this->locale = Yii::$app->language;
+ }
+ }
+
+ /**
+ * Returns a currency symbol
+ *
+ * @param string $currencyCode the 3-letter ISO 4217 currency code to get symbol for. If null,
+ * method will attempt using currency code from [[locale]].
+ * @return string
+ */
+ public function getCurrencySymbol($currencyCode = null)
+ {
+ $locale = $this->locale;
+
+ if ($currencyCode !== null) {
+ $locale .= '@currency=' . $currencyCode;
+ }
+
+ $formatter = new \NumberFormatter($locale, \NumberFormatter::CURRENCY);
+ return $formatter->getSymbol(\NumberFormatter::CURRENCY_SYMBOL);
+ }
+}
diff --git a/framework/i18n/MessageFormatter.php b/framework/i18n/MessageFormatter.php
index 25de640..f28a348 100644
--- a/framework/i18n/MessageFormatter.php
+++ b/framework/i18n/MessageFormatter.php
@@ -216,7 +216,7 @@ class MessageFormatter extends Component
continue;
}
$param = trim($token[0]);
- if (isset($givenParams[$param])) {
+ if (array_key_exists($param, $givenParams)) {
// if param is given, replace it with a number
if (!isset($map[$param])) {
$map[$param] = count($map);
@@ -318,6 +318,10 @@ class MessageFormatter extends Component
$tokens[] = mb_substr($pattern, $start, $open - $start, $charset);
$start = $open;
}
+
+ if ($depth !== 0 && ($open === false || $close === false)) {
+ break;
+ }
}
if ($depth !== 0) {
return false;
diff --git a/framework/log/DbTarget.php b/framework/log/DbTarget.php
index 9528815..343ade6 100644
--- a/framework/log/DbTarget.php
+++ b/framework/log/DbTarget.php
@@ -10,6 +10,7 @@ namespace yii\log;
use Yii;
use yii\base\InvalidConfigException;
use yii\db\Connection;
+use yii\db\Exception;
use yii\di\Instance;
use yii\helpers\VarDumper;
@@ -57,6 +58,9 @@ class DbTarget extends Target
/**
* Stores log messages to DB.
+ * Starting from version 2.0.14, this method throws LogRuntimeException in case the log can not be exported.
+ * @throws Exception
+ * @throws LogRuntimeException
*/
public function export()
{
@@ -80,13 +84,16 @@ class DbTarget extends Target
$text = VarDumper::export($text);
}
}
- $command->bindValues([
- ':level' => $level,
- ':category' => $category,
- ':log_time' => $timestamp,
- ':prefix' => $this->getMessagePrefix($message),
- ':message' => $text,
- ])->execute();
+ if ($command->bindValues([
+ ':level' => $level,
+ ':category' => $category,
+ ':log_time' => $timestamp,
+ ':prefix' => $this->getMessagePrefix($message),
+ ':message' => $text,
+ ])->execute() > 0) {
+ continue;
+ }
+ throw new LogRuntimeException('Unable to export log through database!');
}
}
}
diff --git a/framework/log/Dispatcher.php b/framework/log/Dispatcher.php
index 80ad9b5..018108c 100644
--- a/framework/log/Dispatcher.php
+++ b/framework/log/Dispatcher.php
@@ -75,7 +75,7 @@ class Dispatcher extends Component
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function __construct($config = [])
{
@@ -91,7 +91,7 @@ class Dispatcher extends Component
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -190,7 +190,7 @@ class Dispatcher extends Component
} catch (\Exception $e) {
$target->enabled = false;
$targetErrors[] = [
- 'Unable to send log via ' . get_class($target) . ': ' . ErrorHandler::convertExceptionToString($e),
+ 'Unable to send log via ' . get_class($target) . ': ' . ErrorHandler::convertExceptionToVerboseString($e),
Logger::LEVEL_WARNING,
__METHOD__,
microtime(true),
diff --git a/framework/log/EmailTarget.php b/framework/log/EmailTarget.php
index 659c645..c64ede0 100644
--- a/framework/log/EmailTarget.php
+++ b/framework/log/EmailTarget.php
@@ -59,7 +59,7 @@ class EmailTarget extends Target
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -72,6 +72,8 @@ class EmailTarget extends Target
/**
* Sends log messages to specified email addresses.
+ * Starting from version 2.0.14, this method throws LogRuntimeException in case the log can not be exported.
+ * @throws LogRuntimeException
*/
public function export()
{
@@ -82,7 +84,10 @@ class EmailTarget extends Target
}
$messages = array_map([$this, 'formatMessage'], $this->messages);
$body = wordwrap(implode("\n", $messages), 70);
- $this->composeMessage($body)->send($this->mailer);
+ $message = $this->composeMessage($body);
+ if (!$message->send($this->mailer)) {
+ throw new LogRuntimeException('Unable to export log through email!');
+ }
}
/**
diff --git a/framework/log/FileTarget.php b/framework/log/FileTarget.php
index fabf50e..35310e3 100644
--- a/framework/log/FileTarget.php
+++ b/framework/log/FileTarget.php
@@ -99,7 +99,9 @@ class FileTarget extends Target
/**
* Writes log messages to a file.
+ * Starting from version 2.0.14, this method throws LogRuntimeException in case the log can not be exported.
* @throws InvalidConfigException if unable to open the log file for writing
+ * @throws LogRuntimeException if unable to write complete log to file
*/
public function export()
{
@@ -117,9 +119,25 @@ class FileTarget extends Target
$this->rotateFiles();
@flock($fp, LOCK_UN);
@fclose($fp);
- @file_put_contents($this->logFile, $text, FILE_APPEND | LOCK_EX);
+ $writeResult = @file_put_contents($this->logFile, $text, FILE_APPEND | LOCK_EX);
+ if ($writeResult === false) {
+ $error = error_get_last();
+ throw new LogRuntimeException("Unable to export log through file!: {$error['message']}");
+ }
+ $textSize = strlen($text);
+ if ($writeResult < $textSize) {
+ throw new LogRuntimeException("Unable to export whole log through file! Wrote $writeResult out of $textSize bytes.");
+ }
} else {
- @fwrite($fp, $text);
+ $writeResult = @fwrite($fp, $text);
+ if ($writeResult === false) {
+ $error = error_get_last();
+ throw new LogRuntimeException("Unable to export log through file!: {$error['message']}");
+ }
+ $textSize = strlen($text);
+ if ($writeResult < $textSize) {
+ throw new LogRuntimeException("Unable to export whole log through file! Wrote $writeResult out of $textSize bytes.");
+ }
@flock($fp, LOCK_UN);
@fclose($fp);
}
@@ -141,21 +159,49 @@ class FileTarget extends Target
// suppress errors because it's possible multiple processes enter into this section
if ($i === $this->maxLogFiles) {
@unlink($rotateFile);
- } else {
- if ($this->rotateByCopy) {
- @copy($rotateFile, $file . '.' . ($i + 1));
- if ($fp = @fopen($rotateFile, 'a')) {
- @ftruncate($fp, 0);
- @fclose($fp);
- }
- if ($this->fileMode !== null) {
- @chmod($file . '.' . ($i + 1), $this->fileMode);
- }
- } else {
- @rename($rotateFile, $file . '.' . ($i + 1));
- }
+ continue;
+ }
+ $newFile = $this->logFile . '.' . ($i + 1);
+ $this->rotateByCopy ? $this->rotateByCopy($rotateFile, $newFile) : $this->rotateByRename($rotateFile, $newFile);
+ if ($i === 0) {
+ $this->clearLogFile($rotateFile);
}
}
}
}
+
+ /***
+ * Clear log file without closing any other process open handles
+ * @param string $rotateFile
+ */
+ private function clearLogFile($rotateFile)
+ {
+ if ($filePointer = @fopen($rotateFile, 'a')) {
+ @ftruncate($filePointer, 0);
+ @fclose($filePointer);
+ }
+ }
+
+ /***
+ * Copy rotated file into new file
+ * @param string $rotateFile
+ * @param string $newFile
+ */
+ private function rotateByCopy($rotateFile, $newFile)
+ {
+ @copy($rotateFile, $newFile);
+ if ($this->fileMode !== null) {
+ @chmod($newFile, $this->fileMode);
+ }
+ }
+
+ /**
+ * Renames rotated file into new file
+ * @param string $rotateFile
+ * @param string $newFile
+ */
+ private function rotateByRename($rotateFile, $newFile)
+ {
+ @rename($rotateFile, $newFile);
+ }
}
diff --git a/framework/log/LogRuntimeException.php b/framework/log/LogRuntimeException.php
new file mode 100644
index 0000000..888e2f5
--- /dev/null
+++ b/framework/log/LogRuntimeException.php
@@ -0,0 +1,25 @@
+
+ * @since 2.0.14
+ */
+class LogRuntimeException extends \yii\base\Exception
+{
+ /**
+ * @return string the user-friendly name of this exception
+ */
+ public function getName()
+ {
+ return 'Log Runtime';
+ }
+}
diff --git a/framework/log/SyslogTarget.php b/framework/log/SyslogTarget.php
index 76007e4..3aca96d 100644
--- a/framework/log/SyslogTarget.php
+++ b/framework/log/SyslogTarget.php
@@ -49,7 +49,7 @@ class SyslogTarget extends Target
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -61,18 +61,22 @@ class SyslogTarget extends Target
/**
* Writes log messages to syslog.
+ * Starting from version 2.0.14, this method throws LogRuntimeException in case the log can not be exported.
+ * @throws LogRuntimeException
*/
public function export()
{
openlog($this->identity, $this->options, $this->facility);
foreach ($this->messages as $message) {
- syslog($this->_syslogLevels[$message[1]], $this->formatMessage($message));
+ if (syslog($this->_syslogLevels[$message[1]], $this->formatMessage($message)) === false) {
+ throw new LogRuntimeException('Unable to export log through system log!');
+ }
}
closelog();
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function formatMessage($message)
{
diff --git a/framework/log/Target.php b/framework/log/Target.php
index c8de295..f49d76a 100644
--- a/framework/log/Target.php
+++ b/framework/log/Target.php
@@ -68,6 +68,9 @@ abstract class Target extends Component
* - `var.key` - only `var[key]` key will be logged.
* - `!var.key` - `var[key]` key will be excluded.
*
+ * Note that if you need $_SESSION to logged regardless if session was used you have to open it right at
+ * the start of your request.
+ *
* @see \yii\helpers\ArrayHelper::filter()
*/
public $logVars = ['_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_SERVER'];
@@ -361,8 +364,8 @@ abstract class Target extends Component
*/
protected function getTime($timestamp)
{
- list($timestamp, $usec) = explode('.', StringHelper::floatToString($timestamp));
+ $parts = explode('.', StringHelper::floatToString($timestamp));
- return date('Y-m-d H:i:s', $timestamp) . ($this->microtime ? ('.' . $usec) : '');
+ return date('Y-m-d H:i:s', $parts[0]) . ($this->microtime && isset($parts[1]) ? ('.' . $parts[1]) : '');
}
}
diff --git a/framework/messages/bg/yii.php b/framework/messages/bg/yii.php
index b4b0e23..ebd6544 100644
--- a/framework/messages/bg/yii.php
+++ b/framework/messages/bg/yii.php
@@ -96,7 +96,7 @@ return [
'{delta, plural, =1{a minute} other{# minutes}} ago' => 'преди {delta, plural, =1{минута} other{# минути}}',
'{delta, plural, =1{a month} other{# months}} ago' => 'преди {delta, plural, =1{месец} other{# месеца}}',
'{delta, plural, =1{a second} other{# seconds}} ago' => 'преди {delta, plural, =1{секунда} other{# секунди}}',
- '{delta, plural, =1{a year} other{# years}} ago' => 'преди{delta, plural, =1{година} other{# години}}',
+ '{delta, plural, =1{a year} other{# years}} ago' => 'преди {delta, plural, =1{година} other{# години}}',
'{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{час} other{# часа}}',
'{n, plural, =1{# byte} other{# bytes}}' => '{n, plural, =1{# байт} other{# байта}}',
'{n, plural, =1{# gigabyte} other{# gigabytes}}' => '{n, plural, =1{# гигабайт} other{# гигабайта}}',
diff --git a/framework/messages/config.php b/framework/messages/config.php
index 2549b0e..81de627 100644
--- a/framework/messages/config.php
+++ b/framework/messages/config.php
@@ -12,7 +12,7 @@ return [
'messagePath' => __DIR__,
// array, required, list of language codes that the extracted messages
// should be translated to. For example, ['zh-CN', 'de'].
- 'languages' => ['ar', 'az', 'bg', 'bs', 'ca', 'cs', 'da', 'de', 'el', 'es', 'et', 'fa', 'fi', 'fr', 'he', 'hr', 'hu', 'hy', 'id', 'it', 'ja', 'ka', 'kk', 'ko', 'lt', 'lv', 'ms', 'nb-NO', 'nl', 'pl', 'pt', 'pt-BR', 'ro', 'ru', 'sk', 'sl', 'sr', 'sr-Latn', 'sv', 'tg', 'th', 'uk', 'vi', 'zh-CN', 'zh-TW'],
+ 'languages' => ['ar', 'az', 'bg', 'bs', 'ca', 'cs', 'da', 'de', 'el', 'es', 'et', 'fa', 'fi', 'fr', 'he', 'hr', 'hu', 'hy', 'id', 'it', 'ja', 'ka', 'kk', 'ko', 'kz', 'lt', 'lv', 'ms', 'nb-NO', 'nl', 'pl', 'pt', 'pt-BR', 'ro', 'ru', 'sk', 'sl', 'sr', 'sr-Latn', 'sv', 'tg', 'th', 'tr', 'uk', 'uz', 'vi', 'zh-CN', 'zh-TW'],
// string, the name of the function for translating messages.
// Defaults to 'Yii::t'. This is used as a mark to find the messages to be
// translated. You may use a string for single function name or an array for
diff --git a/framework/messages/de/yii.php b/framework/messages/de/yii.php
index ca6cf06..98a3445 100644
--- a/framework/messages/de/yii.php
+++ b/framework/messages/de/yii.php
@@ -64,7 +64,7 @@ return [
'Yes' => 'Ja',
'Yii Framework' => 'Yii Framework',
'You are not allowed to perform this action.' => 'Sie dürfen diese Aktion nicht durchführen.',
- 'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Sie können maximal {limit, number} {limit, plural, one{eine Datei} other{# Dateien}} hochladen.',
+ 'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Sie können maximal {limit, plural, one{eine Datei} other{# Dateien}} hochladen.',
'in {delta, plural, =1{a day} other{# days}}' => 'in {delta, plural, =1{einem Tag} other{# Tagen}}',
'in {delta, plural, =1{a minute} other{# minutes}}' => 'in {delta, plural, =1{einer Minute} other{# Minuten}}',
'in {delta, plural, =1{a month} other{# months}}' => 'in {delta, plural, =1{einem Monat} other{# Monaten}}',
diff --git a/framework/messages/kz/yii.php b/framework/messages/kz/yii.php
new file mode 100644
index 0000000..aa60f0d
--- /dev/null
+++ b/framework/messages/kz/yii.php
@@ -0,0 +1,117 @@
+ '{nFormatted} Б',
+ '{nFormatted} GB' => '{nFormatted} ГБ',
+ '{nFormatted} GiB' => '{nFormatted} ГиБ',
+ '{nFormatted} KB' => '{nFormatted} КБ',
+ '{nFormatted} KiB' => '{nFormatted} КиБ',
+ '{nFormatted} MB' => '{nFormatted} МБ',
+ '{nFormatted} MiB' => '{nFormatted} МиБ',
+ '{nFormatted} PB' => '{nFormatted} ПБ',
+ '{nFormatted} PiB' => '{nFormatted} ПиБ',
+ '{nFormatted} TB' => '{nFormatted} ТБ',
+ '{nFormatted} TiB' => '{nFormatted} ТиБ',
+ '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, one{байттар} few{байт} many{байттар} other{байт}}',
+ '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, one{гибибайт} few{гибибайта} many{гибибайтов} other{гибибайта}}',
+ '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, one{гигабайт} few{гигабайта} many{гигабайтов} other{гигабайта}}',
+ '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, one{кибибайт} few{кибибайта} many{кибибайтов} other{кибибайта}}',
+ '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, one{килобайт} few{килобайта} many{килобайтов} other{килобайта}}',
+ '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, one{мебибайт} few{мебибайта} many{мебибайтов} other{мебибайта}}',
+ '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, one{мегабайт} few{мегабайта} many{мегабайтов} other{мегабайта}}',
+ '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, one{пебибайт} few{пебибайта} many{пебибайтов} other{пебибайта}}',
+ '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, one{петабайт} few{петабайта} many{петабайтов} other{петабайта}}',
+ '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, one{тебибайт} few{тебибайта} many{тебибайтов} other{тебибайта}}',
+ '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, one{терабайт} few{терабайта} many{терабайтов} other{терабайта}}',
+ '(not set)' => '(көрсетілмеген)',
+ 'An internal server error occurred.' => 'Ішкі сервер қатесі орын алды.',
+ 'Are you sure you want to delete this item?' => 'Бұл элементті жою керек пе?',
+ '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 help for unknown command "{command}".' => 'Анық емес "{command}" пәрменіне көмек жоқ.',
+ 'No help for unknown sub-command "{command}".' => 'Анық емес субкоманда "{command}" үшін көмек жоқ.',
+ '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 {begin, number}-{end, number} of {totalCount, number} {totalCount, plural, one{item} other{items}}.' => 'Жазбаларды көрсету {begin, number}-{end, number} из {totalCount, number}.',
+ 'The file "{file}" is not an image.' => '"{file}" файлы сурет емес.',
+ 'The file "{file}" is too big. Its size cannot exceed {limit, number} {limit, plural, one{byte} other{bytes}}.' => '«{file}» файлы тым үлкен. Өлшемі {шектеу, сан} {limit, plural, one{байттар} few{байт} many{байттар} other{байта}}.',
+ 'The file "{file}" is too small. Its size cannot be smaller than {limit, number} {limit, plural, one{byte} other{bytes}}.' => '«{file}» файлы тым кішкентай. Өлшем көп болуы керек {limit, number} {limit, plural, one{байттар} few{байта} many{байт} other{байта}}.',
+ '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, 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, 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, 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, other{пиксел}}.',
+ 'The requested view "{name}" was not found.' => 'Сұрау салынған "{name}" қарау файлы табылмады.',
+ 'The verification code is incorrect.' => 'Растау коды дұрыс емес.',
+ 'Total {count, number} {count, plural, one{item} other{items}}.' => 'Барлығы {count, number} {count, plural, one{кіру} few{жазбалар} many{жазбалар} other{жазбалар}}.',
+ 'Unable to verify your data submission.' => 'Жіберілген деректерді тексере алмадық.',
+ 'Unknown command "{command}".' => 'Белгісіз команда "{command}".',
+ 'Unknown option: --{name}' => 'Белгісіз опция: --{name}',
+ 'Update' => 'Өңдеу',
+ 'View' => 'Ішінде қараңыз',
+ 'Yes' => 'Ия',
+ 'You are not allowed to perform this action.' => 'Сіз бұл әрекетті орындауға рұқсатыңыз жоқ.',
+ 'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Көбірек жүктей алмайсыз {limit, number} {limit, plural, one{файл} few{файлдар} many{файлдар} other{файл}}.',
+ 'in {delta, plural, =1{a day} other{# days}}' => 'арқылы {delta, plural, =1{күн} one{# күн} few{# күн} many{# күндер} other{# күн}}',
+ 'in {delta, plural, =1{a minute} other{# minutes}}' => 'арқылы {delta, plural, =1{минутына} one{# минутына} few{# минуттар} many{# минуттар} other{# минуттар}}',
+ 'in {delta, plural, =1{a month} other{# months}}' => 'арқылы {delta, plural, =1{ай} one{# ай} few{# айлар} many{# айлар} other{# айлар}}',
+ 'in {delta, plural, =1{a second} other{# seconds}}' => 'арқылы {delta, plural, other{# екіншіден}}',
+ 'in {delta, plural, =1{a year} other{# years}}' => 'арқылы {delta, plural, =1{жыл} one{# жыл} few{# жыл} many{# жастағы} other{# жыл}}',
+ 'in {delta, plural, =1{an hour} other{# hours}}' => 'арқылы {delta, plural, other{# сағат}}',
+ 'just now' => 'дәл қазір',
+ 'the input value' => 'енгізілген мән',
+ '{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}» мәні жарамды электрондық пошта мекенжайы емес.',
+ '{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} болуы керек {min, number} {min, plural, one{таңба} few{таңба} many{таңбалар} other{таңба}}.',
+ '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '«{attribute}» мәні ең көп {max, number} {max, plural, one{таңба} few{таңба} many{таңбалар} other{таңба}}.',
+ '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '«{attribute}» мәні {length, number} болуы керек {length, plural, one{таңба} few{таңба} many{таңбалар} other{таңба}}.',
+ '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{күн} one{күн} few{# күн} many{# күндер} other{# күн}} артқа қарай',
+ '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{минутына} one{# минутына} few{# минуттар} many{# минуттар} other{# минуттар}} артқа қарай',
+ '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{ай} one{# ай} few{# айлар} many{# айлар} other{# айлар}} артқа қарай',
+ '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, other{# екіншіден}} артқа қарай',
+ '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{жыл} one{# жыл} few{# жыл} many{# жастағы} other{# жыл}} артқа қарай',
+ '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, other{# сағат}} артқа қарай',
+];
\ No newline at end of file
diff --git a/framework/messages/lv/yii.php b/framework/messages/lv/yii.php
index 3cbf9ec..9d84708 100644
--- a/framework/messages/lv/yii.php
+++ b/framework/messages/lv/yii.php
@@ -75,7 +75,7 @@ return [
'The file "{file}" is not an image.' => 'Saņemtā „{file}” datne nav attēls.',
'The file "{file}" is too big. Its size cannot exceed {formattedLimit}.' => 'Saņemtās „{file}” datnes izmērs pārsniedz pieļaujamo ierobežojumu {formattedLimit} apmērā.',
'The file "{file}" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Saņemtās „{file}” datnes izmērs ir pārāk maza, tai ir jābūt vismaz {formattedLimit} apmērā.',
- 'The format of {attribute} is invalid.' => '„{attribute}” vertības formāts ir nepareizs.',
+ 'The format of {attribute} is invalid.' => '„{attribute}” vērtības formāts ir nepareizs.',
'The image "{file}" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Attēls „{file}” ir pārāk liels. Augstumam ir jābūt mazākam par {limit, number} {limit, plural, one{pikseli} other{pikseļiem}}.',
'The image "{file}" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Attēls „{file}” ir pārāk liels. Platumam ir jābūt mazākam par {limit, number} {limit, plural, one{pikseli} other{pikseļiem}}.',
'The image "{file}" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Attēls „{file}” ir pārāk mazs. Augstumam ir jābūt lielākam par {limit, number} {limit, plural, one{pikseli} other{pikseļiem}}.',
@@ -112,8 +112,8 @@ return [
'{attribute} must be greater than or equal to "{compareValueOrAttribute}".' => '„{attribute}” vērtībai ir jābūt lielākai vai vienādai ar „{compareValueOrAttribute}” vērtību.',
'{attribute} must be less than "{compareValueOrAttribute}".' => '„{attribute}” vērtībai ir jābūt mazākai par „{compareValueOrAttribute}” vērtību.',
'{attribute} must be less than or equal to "{compareValueOrAttribute}".' => '„{attribute}” vērtībai ir jābūt mazākai vai vienādai ar „{compareValueOrAttribute}” vērtību.',
- '{attribute} must be no greater than {max}.' => '„{attribute}” vērtībai ir jābūt mazākai par {max}.',
- '{attribute} must be no less than {min}.' => '„{attribute}” vērtībai ir jābūt lielākai par {min}.',
+ '{attribute} must be no greater than {max}.' => '„{attribute}” vērtībai ir jābūt ne lielākai par {max}.',
+ '{attribute} must be no less than {min}.' => '„{attribute}” vērtībai ir jābūt ne mazākai par {min}.',
'{attribute} must be equal to "{compareValueOrAttribute}".' => '„{attribute}” vērtībai ir jābūt vienādai ar „{compareValueOrAttribute}”.',
'{attribute} must not be equal to "{compareValueOrAttribute}".' => '„{attribute}” vērtība nedrīkst būt vienāda ar „{compareValueOrAttribute}” vērtību.',
'{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '„{attribute}” vērtībai ir jābūt garākai par {min, number} {min, plural, one{simbolu} other{simboliem}}.',
diff --git a/framework/messages/ms/yii.php b/framework/messages/ms/yii.php
index 9acba8b..c233b1f 100644
--- a/framework/messages/ms/yii.php
+++ b/framework/messages/ms/yii.php
@@ -60,12 +60,12 @@ return [
'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}}.' => '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}}',
- 'in {delta, plural, =1{a month} other{# months}}' => 'dalam {delta, plural, =1{a month} other{# months}}',
- 'in {delta, plural, =1{a second} other{# seconds}}' => 'dalam {delta, plural, =1{a second} other{# seconds}}',
- 'in {delta, plural, =1{a year} other{# years}}' => 'dalam {delta, plural, =1{a year} other{# years}}',
- 'in {delta, plural, =1{an hour} other{# hours}}' => 'dalam {delta, plural, =1{an hour} other{# hours}}',
+ 'in {delta, plural, =1{a day} other{# days}}' => 'dalam {delta, plural, =1{1 hari} other{# hari}}',
+ 'in {delta, plural, =1{a minute} other{# minutes}}' => 'dalam {delta, plural, =1{1 minit} other{# minit}}',
+ 'in {delta, plural, =1{a month} other{# months}}' => 'dalam {delta, plural, =1{1 bulan} other{# bulan}}',
+ 'in {delta, plural, =1{a second} other{# seconds}}' => 'dalam {delta, plural, =1{1 saat} other{# saat}}',
+ 'in {delta, plural, =1{a year} other{# years}}' => 'dalam {delta, plural, =1{1 tahun} other{# tahun}}',
+ 'in {delta, plural, =1{an hour} other{# hours}}' => 'dalam {delta, plural, =1{1 jam} other{# jam}}',
'just now' => 'baru sahaja',
'the input value' => 'nilai input',
'{attribute} "{value}" has already been taken.' => '{attribute} "{value}" telah digunakan.',
@@ -89,12 +89,12 @@ return [
'{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} mesti mengangungi paling banyak {max, number} {max, 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',
- '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{a month} other{# months}} lalu',
- '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{a second} other{# seconds}} lalu',
- '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{a year} other{# years}} lalu',
- '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{an hour} other{# hours}} lalu',
+ '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{1 hari} other{# hari}} lalu',
+ '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{1 minit} other{# minit}} lalu',
+ '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{1 bulan} other{# bulan}} lalu',
+ '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{1 saat} other{# saat}} lalu',
+ '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{1 tahun} other{# tahun}} lalu',
+ '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{1 jam} other{# jam}} lalu',
'{nFormatted} B' => '',
'{nFormatted} GB' => '',
'{nFormatted} GiB' => '',
diff --git a/framework/messages/sr/yii.php b/framework/messages/sr/yii.php
index 61b461d..2edf790 100644
--- a/framework/messages/sr/yii.php
+++ b/framework/messages/sr/yii.php
@@ -69,7 +69,7 @@ return [
'Page not found.' => 'Страница није пронађена.',
'Please fix the following errors:' => 'Молимо вас исправите следеће грешке:',
'Please upload a file.' => 'Молимо вас поставите фајл.',
- 'Showing {begin, number}-{end, number} of {totalCount, number} {totalCount, plural, one{item} other{items}}.' => 'Приказано {begin, number}-{end, number} од {totalCount, number} {totalCount, plural, =1{# ставке} one{# ставкe} few{# ставке} many{# ставки} other{# ставки}}.',
+ 'Showing {begin, number}-{end, number} of {totalCount, number} {totalCount, plural, one{item} other{items}}.' => 'Приказано {begin, number}-{end, number} од {totalCount, number} {totalCount, plural, =1{ставке} one{ставкe} few{ставке} many{ставки} other{ставки}}.',
'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}.',
diff --git a/framework/messages/uz/yii.php b/framework/messages/uz/yii.php
index cf2e2bc..5d227a3 100644
--- a/framework/messages/uz/yii.php
+++ b/framework/messages/uz/yii.php
@@ -47,7 +47,7 @@ return [
'{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, one{terabayt} few{terabayt} many{terabayt} other{terabayt}}',
'(not set)' => '(qiymatlanmagan)',
'An internal server error occurred.' => 'Serverning ichki xatoligi yuz berdi.',
- 'Are you sure you want to delete this item?' => 'Sizni ushbu elementni o`chirishga ishonchngiz komilmi?',
+ 'Are you sure you want to delete this item?' => 'Siz rostdan ham ushbu elementni o`chirmoqchimisiz?',
'Delete' => 'O`chirish',
'Error' => 'Xato',
'File upload failed.' => 'Faylni yuklab bo`lmadi.',
@@ -115,9 +115,9 @@ return [
'{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '«{attribute}» qiymati maksimum {max, number} {max, plural, one{belgidan} few{belgidan} many{belgidan} other{belgidan}} oshmasligi kerak.',
'{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '«{attribute}» qiymati {length, number} {length, plural, one{belgidan} few{belgidan} many{belgidan} other{belgidan}} tashkil topishi kerak.',
'{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{kun} one{kun} few{# kun} many{# kun} other{# kun}} avval',
- '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{minut} one{# minut} few{# minutlar} many{# minutlar} other{# minutlar}} avval',
- '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{oy} one{# oy} few{# oylar} many{# oylar} other{# oylar}} avval',
- '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{sekund} one{# sekundlar} few{# sekundlar} many{# sekundlar} other{# sekundlar}} avval',
- '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{yil} one{# yil} few{# yillar} many{# yillar} other{# yillar}} avval',
- '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{soat} one{# soat} few{# soatlar} many{# soatlar} other{# soatlar}} avval',
+ '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{daqiqa} one{# daqiqa} few{# daqiqa} many{# daqiqa} other{# daqiqa}} avval',
+ '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{oy} one{# oy} few{# oy} many{# oy} other{# oy}} avval',
+ '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{soniya} one{# soniya} few{# soniya} many{# soniya} other{# soniya}} avval',
+ '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{yil} one{# yil} few{# yil} many{# yil} other{# yil}} avval',
+ '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{soat} one{# soat} few{# soat} many{# soat} other{# soat}} avval',
];
diff --git a/framework/messages/vi/yii.php b/framework/messages/vi/yii.php
index 0552548..d56cf29 100644
--- a/framework/messages/vi/yii.php
+++ b/framework/messages/vi/yii.php
@@ -23,6 +23,7 @@
* NOTE: this file must be saved in UTF-8 encoding.
*/
return [
+ ' and ' => ' và ',
'(not set)' => '(không có)',
'An internal server error occurred.' => 'Máy chủ đã gặp sự cố nội bộ.',
'Are you sure you want to delete this item?' => 'Bạn có chắc là sẽ xóa mục này không?',
@@ -69,6 +70,7 @@ return [
'in {delta, plural, =1{a second} other{# seconds}}' => 'trong {delta} giây',
'in {delta, plural, =1{a year} other{# years}}' => 'trong {delta} năm',
'in {delta, plural, =1{an hour} other{# hours}}' => 'trong {delta} giờ',
+ 'just now' => 'ngay bây giờ',
'the input value' => 'giá trị đã nhập',
'{attribute} "{value}" has already been taken.' => '{attribute} "{value}" đã bị sử dụng.',
'{attribute} cannot be blank.' => '{attribute} không được để trống.',
@@ -88,6 +90,7 @@ return [
'{attribute} must be no less than {min}.' => '{attribute} không được nhỏ hơn {min}',
'{attribute} must be repeated exactly.' => '{attribute} phải lặp lại chính xác.',
'{attribute} must not be equal to "{compareValue}".' => '{attribute} không được phép bằng "{compareValue}"',
+ '{attribute} must be equal to "{compareValue}".' => '{attribute} phải bằng "{compareValue}"',
'{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} phải chứa ít nhất {min, number} ký tự.',
'{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} phải chứa nhiều nhất {max, number} ký tự.',
'{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} phải bao gồm {length, number} ký tự.',
diff --git a/framework/mutex/PgsqlMutex.php b/framework/mutex/PgsqlMutex.php
index 25b9b03..e61972e 100644
--- a/framework/mutex/PgsqlMutex.php
+++ b/framework/mutex/PgsqlMutex.php
@@ -7,8 +7,8 @@
namespace yii\mutex;
+use yii\base\InvalidArgumentException;
use yii\base\InvalidConfigException;
-use yii\base\InvalidParamException;
/**
* PgsqlMutex implements mutex "lock" mechanism via PgSQL locks.
@@ -68,7 +68,7 @@ class PgsqlMutex extends DbMutex
protected function acquireLock($name, $timeout = 0)
{
if ($timeout !== 0) {
- throw new InvalidParamException('PgsqlMutex does not support timeout.');
+ throw new InvalidArgumentException('PgsqlMutex does not support timeout.');
}
list($key1, $key2) = $this->getKeysFromName($name);
return $this->db->useMaster(function ($db) use ($key1, $key2) {
diff --git a/framework/rbac/BaseManager.php b/framework/rbac/BaseManager.php
index a772bc9..3daa0d7 100644
--- a/framework/rbac/BaseManager.php
+++ b/framework/rbac/BaseManager.php
@@ -8,8 +8,8 @@
namespace yii\rbac;
use yii\base\Component;
+use yii\base\InvalidArgumentException;
use yii\base\InvalidConfigException;
-use yii\base\InvalidParamException;
/**
* BaseManager is a base class implementing [[ManagerInterface]] for RBAC management.
@@ -28,7 +28,7 @@ abstract class BaseManager extends Component implements ManagerInterface
* @var array a list of role names that are assigned to every user automatically without calling [[assign()]].
* Note that these roles are applied to users, regardless of their state of authentication.
*/
- public $defaultRoles = [];
+ protected $defaultRoles = [];
/**
@@ -96,7 +96,7 @@ abstract class BaseManager extends Component implements ManagerInterface
abstract protected function updateRule($name, $rule);
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function createRole($name)
{
@@ -106,7 +106,7 @@ abstract class BaseManager extends Component implements ManagerInterface
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function createPermission($name)
{
@@ -116,7 +116,7 @@ abstract class BaseManager extends Component implements ManagerInterface
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function add($object)
{
@@ -132,11 +132,11 @@ abstract class BaseManager extends Component implements ManagerInterface
return $this->addRule($object);
}
- throw new InvalidParamException('Adding unsupported object type.');
+ throw new InvalidArgumentException('Adding unsupported object type.');
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function remove($object)
{
@@ -146,11 +146,11 @@ abstract class BaseManager extends Component implements ManagerInterface
return $this->removeRule($object);
}
- throw new InvalidParamException('Removing unsupported object type.');
+ throw new InvalidArgumentException('Removing unsupported object type.');
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function update($name, $object)
{
@@ -166,11 +166,11 @@ abstract class BaseManager extends Component implements ManagerInterface
return $this->updateRule($name, $object);
}
- throw new InvalidParamException('Updating unsupported object type.');
+ throw new InvalidArgumentException('Updating unsupported object type.');
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getRole($name)
{
@@ -179,7 +179,7 @@ abstract class BaseManager extends Component implements ManagerInterface
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getPermission($name)
{
@@ -188,7 +188,7 @@ abstract class BaseManager extends Component implements ManagerInterface
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getRoles()
{
@@ -196,6 +196,36 @@ abstract class BaseManager extends Component implements ManagerInterface
}
/**
+ * Set default roles
+ * @param array|\Closure $roles either array of roles or a callable returning it
+ * @since 2.0.14
+ */
+ public function setDefaultRoles($roles)
+ {
+ if (is_array($roles)) {
+ $this->defaultRoles = $roles;
+ } elseif (is_callable($roles)) {
+ $roles = $roles();
+ if (!is_array($roles)) {
+ throw new InvalidArgumentException('Default roles closure must return an array');
+ }
+ $this->defaultRoles = $roles;
+ } else {
+ throw new InvalidArgumentException('Default roles must be either an array or a callable');
+ }
+ }
+
+ /**
+ * Get default roles
+ * @return array default roles
+ * @since 2.0.14
+ */
+ public function getDefaultRoles()
+ {
+ return $this->defaultRoles;
+ }
+
+ /**
* Returns defaultRoles as array of Role objects.
* @since 2.0.12
* @return Role[] default roles. The array is indexed by the role names
@@ -211,7 +241,7 @@ abstract class BaseManager extends Component implements ManagerInterface
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getPermissions()
{
diff --git a/framework/rbac/DbManager.php b/framework/rbac/DbManager.php
index c5ddcfe..3c1e84f 100644
--- a/framework/rbac/DbManager.php
+++ b/framework/rbac/DbManager.php
@@ -8,8 +8,8 @@
namespace yii\rbac;
use Yii;
+use yii\base\InvalidArgumentException;
use yii\base\InvalidCallException;
-use yii\base\InvalidParamException;
use yii\caching\CacheInterface;
use yii\db\Connection;
use yii\db\Expression;
@@ -118,7 +118,7 @@ class DbManager extends BaseManager
private $_checkAccessAssignments = [];
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function checkAccess($userId, $permissionName, $params = [])
{
@@ -162,7 +162,7 @@ class DbManager extends BaseManager
$item = $this->items[$itemName];
- Yii::trace($item instanceof Role ? "Checking role: $itemName" : "Checking permission: $itemName", __METHOD__);
+ Yii::debug($item instanceof Role ? "Checking role: $itemName" : "Checking permission: $itemName", __METHOD__);
if (!$this->executeRule($user, $item, $params)) {
return false;
@@ -201,7 +201,7 @@ class DbManager extends BaseManager
return false;
}
- Yii::trace($item instanceof Role ? "Checking role: $itemName" : "Checking permission: $itemName", __METHOD__);
+ Yii::debug($item instanceof Role ? "Checking role: $itemName" : "Checking permission: $itemName", __METHOD__);
if (!$this->executeRule($user, $item, $params)) {
return false;
@@ -226,7 +226,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function getItem($name)
{
@@ -260,7 +260,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function addItem($item)
{
@@ -288,7 +288,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function removeItem($item)
{
@@ -311,7 +311,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function updateItem($name, $item)
{
@@ -346,7 +346,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function addRule($rule)
{
@@ -371,7 +371,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function updateRule($name, $rule)
{
@@ -398,7 +398,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function removeRule($rule)
{
@@ -418,7 +418,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function getItems($type)
{
@@ -459,7 +459,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
* The roles returned by this method include the roles assigned via [[$defaultRoles]].
*/
public function getRolesByUser($userId)
@@ -483,14 +483,14 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getChildRoles($roleName)
{
$role = $this->getRole($roleName);
if ($role === null) {
- throw new InvalidParamException("Role \"$roleName\" not found.");
+ throw new InvalidArgumentException("Role \"$roleName\" not found.");
}
$result = [];
@@ -506,7 +506,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getPermissionsByRole($roleName)
{
@@ -529,7 +529,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getPermissionsByUser($userId)
{
@@ -632,7 +632,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getRule($name)
{
@@ -656,7 +656,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getRules()
{
@@ -679,7 +679,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getAssignment($roleName, $userId)
{
@@ -703,7 +703,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getAssignments($userId)
{
@@ -728,7 +728,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
* @since 2.0.8
*/
public function canAddChild($parent, $child)
@@ -737,16 +737,16 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function addChild($parent, $child)
{
if ($parent->name === $child->name) {
- throw new InvalidParamException("Cannot add '{$parent->name}' as a child of itself.");
+ throw new InvalidArgumentException("Cannot add '{$parent->name}' as a child of itself.");
}
if ($parent instanceof Permission && $child instanceof Role) {
- throw new InvalidParamException('Cannot add a role as a child of a permission.');
+ throw new InvalidArgumentException('Cannot add a role as a child of a permission.');
}
if ($this->detectLoop($parent, $child)) {
@@ -763,7 +763,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function removeChild($parent, $child)
{
@@ -777,7 +777,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function removeChildren($parent)
{
@@ -791,7 +791,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function hasChild($parent, $child)
{
@@ -802,7 +802,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getChildren($name)
{
@@ -840,7 +840,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function assign($role, $userId)
{
@@ -862,7 +862,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function revoke($role, $userId)
{
@@ -877,7 +877,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function revokeAll($userId)
{
@@ -892,7 +892,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function removeAll()
{
@@ -904,7 +904,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function removeAllPermissions()
{
@@ -912,7 +912,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function removeAllRoles()
{
@@ -950,7 +950,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function removeAllRules()
{
@@ -966,7 +966,7 @@ class DbManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function removeAllAssignments()
{
diff --git a/framework/rbac/Permission.php b/framework/rbac/Permission.php
index 84166a0..9471079 100644
--- a/framework/rbac/Permission.php
+++ b/framework/rbac/Permission.php
@@ -16,7 +16,7 @@ namespace yii\rbac;
class Permission extends Item
{
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public $type = self::TYPE_PERMISSION;
}
diff --git a/framework/rbac/PhpManager.php b/framework/rbac/PhpManager.php
index 55f82ef..39fdcf1 100644
--- a/framework/rbac/PhpManager.php
+++ b/framework/rbac/PhpManager.php
@@ -8,8 +8,8 @@
namespace yii\rbac;
use Yii;
+use yii\base\InvalidArgumentException;
use yii\base\InvalidCallException;
-use yii\base\InvalidParamException;
use yii\helpers\VarDumper;
/**
@@ -94,7 +94,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function checkAccess($userId, $permissionName, $params = [])
{
@@ -108,7 +108,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getAssignments($userId)
{
@@ -136,7 +136,7 @@ class PhpManager extends BaseManager
/* @var $item Item */
$item = $this->items[$itemName];
- Yii::trace($item instanceof Role ? "Checking role: $itemName" : "Checking permission : $itemName", __METHOD__);
+ Yii::debug($item instanceof Role ? "Checking role: $itemName" : "Checking permission : $itemName", __METHOD__);
if (!$this->executeRule($user, $item, $params)) {
return false;
@@ -156,7 +156,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
* @since 2.0.8
*/
public function canAddChild($parent, $child)
@@ -165,19 +165,19 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function addChild($parent, $child)
{
if (!isset($this->items[$parent->name], $this->items[$child->name])) {
- throw new InvalidParamException("Either '{$parent->name}' or '{$child->name}' does not exist.");
+ throw new InvalidArgumentException("Either '{$parent->name}' or '{$child->name}' does not exist.");
}
if ($parent->name === $child->name) {
- throw new InvalidParamException("Cannot add '{$parent->name} ' as a child of itself.");
+ throw new InvalidArgumentException("Cannot add '{$parent->name} ' as a child of itself.");
}
if ($parent instanceof Permission && $child instanceof Role) {
- throw new InvalidParamException('Cannot add a role as a child of a permission.');
+ throw new InvalidArgumentException('Cannot add a role as a child of a permission.');
}
if ($this->detectLoop($parent, $child)) {
@@ -218,7 +218,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function removeChild($parent, $child)
{
@@ -232,7 +232,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function removeChildren($parent)
{
@@ -246,7 +246,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function hasChild($parent, $child)
{
@@ -254,14 +254,14 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function assign($role, $userId)
{
if (!isset($this->items[$role->name])) {
- throw new InvalidParamException("Unknown role '{$role->name}'.");
+ throw new InvalidArgumentException("Unknown role '{$role->name}'.");
} elseif (isset($this->assignments[$userId][$role->name])) {
- throw new InvalidParamException("Authorization item '{$role->name}' has already been assigned to user '$userId'.");
+ throw new InvalidArgumentException("Authorization item '{$role->name}' has already been assigned to user '$userId'.");
}
$this->assignments[$userId][$role->name] = new Assignment([
@@ -275,7 +275,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function revoke($role, $userId)
{
@@ -289,7 +289,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function revokeAll($userId)
{
@@ -305,7 +305,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getAssignment($roleName, $userId)
{
@@ -313,7 +313,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getItems($type)
{
@@ -331,7 +331,7 @@ class PhpManager extends BaseManager
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function removeItem($item)
{
@@ -352,7 +352,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getItem($name)
{
@@ -360,7 +360,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function updateRule($name, $rule)
{
@@ -373,7 +373,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getRule($name)
{
@@ -381,7 +381,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getRules()
{
@@ -389,7 +389,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
* The roles returned by this method include the roles assigned via [[$defaultRoles]].
*/
public function getRolesByUser($userId)
@@ -406,14 +406,14 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getChildRoles($roleName)
{
$role = $this->getRole($roleName);
if ($role === null) {
- throw new InvalidParamException("Role \"$roleName\" not found.");
+ throw new InvalidArgumentException("Role \"$roleName\" not found.");
}
$result = [];
@@ -429,7 +429,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getPermissionsByRole($roleName)
{
@@ -465,7 +465,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getPermissionsByUser($userId)
{
@@ -523,7 +523,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getChildren($name)
{
@@ -531,7 +531,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function removeAll()
{
@@ -543,7 +543,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function removeAllPermissions()
{
@@ -551,7 +551,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function removeAllRoles()
{
@@ -599,7 +599,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function removeAllRules()
{
@@ -611,7 +611,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function removeAllAssignments()
{
@@ -620,7 +620,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function removeRule($rule)
{
@@ -639,7 +639,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function addRule($rule)
{
@@ -649,13 +649,13 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function updateItem($name, $item)
{
if ($name !== $item->name) {
if (isset($this->items[$item->name])) {
- throw new InvalidParamException("Unable to change the item name. The name '{$item->name}' is already used by another item.");
+ throw new InvalidArgumentException("Unable to change the item name. The name '{$item->name}' is already used by another item.");
}
// Remove old item in case of renaming
@@ -688,7 +688,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function addItem($item)
{
@@ -869,7 +869,7 @@ class PhpManager extends BaseManager
}
/**
- * @inheritdoc
+ * {@inheritdoc}
* @since 2.0.7
*/
public function getUserIdsByRole($roleName)
diff --git a/framework/rbac/Role.php b/framework/rbac/Role.php
index ed084e9..a7b3216 100644
--- a/framework/rbac/Role.php
+++ b/framework/rbac/Role.php
@@ -16,7 +16,7 @@ namespace yii\rbac;
class Role extends Item
{
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public $type = self::TYPE_ROLE;
}
diff --git a/framework/rbac/migrations/m140506_102106_rbac_init.php b/framework/rbac/migrations/m140506_102106_rbac_init.php
index d5c01a7..72083c3 100644
--- a/framework/rbac/migrations/m140506_102106_rbac_init.php
+++ b/framework/rbac/migrations/m140506_102106_rbac_init.php
@@ -44,7 +44,7 @@ class m140506_102106_rbac_init extends \yii\db\Migration
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function up()
{
@@ -137,7 +137,7 @@ class m140506_102106_rbac_init extends \yii\db\Migration
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function down()
{
diff --git a/framework/rbac/migrations/m170907_052038_rbac_add_index_on_auth_assignment_user_id.php b/framework/rbac/migrations/m170907_052038_rbac_add_index_on_auth_assignment_user_id.php
index 08848d1..26ec41f 100644
--- a/framework/rbac/migrations/m170907_052038_rbac_add_index_on_auth_assignment_user_id.php
+++ b/framework/rbac/migrations/m170907_052038_rbac_add_index_on_auth_assignment_user_id.php
@@ -37,7 +37,7 @@ class m170907_052038_rbac_add_index_on_auth_assignment_user_id extends Migration
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function up()
{
@@ -46,7 +46,7 @@ class m170907_052038_rbac_add_index_on_auth_assignment_user_id extends Migration
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function down()
{
diff --git a/framework/requirements/YiiRequirementChecker.php b/framework/requirements/YiiRequirementChecker.php
index 5c82453..bc214aa 100644
--- a/framework/requirements/YiiRequirementChecker.php
+++ b/framework/requirements/YiiRequirementChecker.php
@@ -247,8 +247,7 @@ class YiiRequirementChecker
return (int) $verboseSize;
}
$sizeUnit = trim($verboseSize, '0123456789');
- $size = str_replace($sizeUnit, '', $verboseSize);
- $size = trim($size);
+ $size = trim(str_replace($sizeUnit, '', $verboseSize));
if (!is_numeric($size)) {
return 0;
}
diff --git a/framework/rest/Action.php b/framework/rest/Action.php
index 08f89d4..466acfc 100644
--- a/framework/rest/Action.php
+++ b/framework/rest/Action.php
@@ -60,7 +60,7 @@ class Action extends \yii\base\Action
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
diff --git a/framework/rest/ActiveController.php b/framework/rest/ActiveController.php
index ed68fec..5e7df58 100644
--- a/framework/rest/ActiveController.php
+++ b/framework/rest/ActiveController.php
@@ -56,7 +56,7 @@ class ActiveController extends Controller
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -67,7 +67,7 @@ class ActiveController extends Controller
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function actions()
{
@@ -106,7 +106,7 @@ class ActiveController extends Controller
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function verbs()
{
diff --git a/framework/rest/Controller.php b/framework/rest/Controller.php
index 734b344..e473bdb 100644
--- a/framework/rest/Controller.php
+++ b/framework/rest/Controller.php
@@ -37,13 +37,13 @@ class Controller extends \yii\web\Controller
*/
public $serializer = 'yii\rest\Serializer';
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public $enableCsrfValidation = false;
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function behaviors()
{
@@ -69,7 +69,7 @@ class Controller extends \yii\web\Controller
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function afterAction($action, $result)
{
diff --git a/framework/rest/OptionsAction.php b/framework/rest/OptionsAction.php
index 9fee8a2..5ce6d8f 100644
--- a/framework/rest/OptionsAction.php
+++ b/framework/rest/OptionsAction.php
@@ -40,5 +40,6 @@ class OptionsAction extends \yii\base\Action
}
$options = $id === null ? $this->collectionOptions : $this->resourceOptions;
Yii::$app->getResponse()->getHeaders()->set('Allow', implode(', ', $options));
+ Yii::$app->getResponse()->getHeaders()->set('Access-Control-Allow-Method', implode(', ', $options));
}
}
diff --git a/framework/rest/Serializer.php b/framework/rest/Serializer.php
index 52f5828..8105eb3 100644
--- a/framework/rest/Serializer.php
+++ b/framework/rest/Serializer.php
@@ -121,7 +121,7 @@ class Serializer extends Component
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
diff --git a/framework/rest/UrlRule.php b/framework/rest/UrlRule.php
index c667512..73c5dc4 100644
--- a/framework/rest/UrlRule.php
+++ b/framework/rest/UrlRule.php
@@ -141,7 +141,7 @@ class UrlRule extends CompositeUrlRule
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -164,7 +164,7 @@ class UrlRule extends CompositeUrlRule
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function createRules()
{
@@ -214,7 +214,7 @@ class UrlRule extends CompositeUrlRule
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function parseRequest($manager, $request)
{
@@ -225,7 +225,7 @@ class UrlRule extends CompositeUrlRule
/* @var $rule WebUrlRule */
$result = $rule->parseRequest($manager, $request);
if (YII_DEBUG) {
- Yii::trace([
+ Yii::debug([
'rule' => method_exists($rule, '__toString') ? $rule->__toString() : get_class($rule),
'match' => $result !== false,
'parent' => self::className(),
@@ -242,7 +242,7 @@ class UrlRule extends CompositeUrlRule
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function createUrl($manager, $route, $params)
{
diff --git a/framework/test/ActiveFixture.php b/framework/test/ActiveFixture.php
index 12a1300..b89e732 100644
--- a/framework/test/ActiveFixture.php
+++ b/framework/test/ActiveFixture.php
@@ -7,7 +7,6 @@
namespace yii\test;
-use Yii;
use yii\base\InvalidConfigException;
use yii\db\TableSchema;
@@ -54,7 +53,7 @@ class ActiveFixture extends BaseActiveFixture
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -95,17 +94,22 @@ class ActiveFixture extends BaseActiveFixture
protected function getData()
{
if ($this->dataFile === null) {
- $class = new \ReflectionClass($this);
- $dataFile = dirname($class->getFileName()) . '/data/' . $this->getTableSchema()->fullName . '.php';
- return is_file($dataFile) ? require $dataFile : [];
+ if ($this->dataDirectory !== null) {
+ $dataFile = $this->getTableSchema()->fullName . '.php';
+ } else {
+ $class = new \ReflectionClass($this);
+ $dataFile = dirname($class->getFileName()) . '/data/' . $this->getTableSchema()->fullName . '.php';
+ }
+
+ return $this->loadData($dataFile);
}
return parent::getData();
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function unload()
{
diff --git a/framework/test/ArrayFixture.php b/framework/test/ArrayFixture.php
index 793538c..ea0c000 100644
--- a/framework/test/ArrayFixture.php
+++ b/framework/test/ArrayFixture.php
@@ -7,7 +7,6 @@
namespace yii\test;
-use Yii;
use yii\base\ArrayAccessTrait;
use yii\base\InvalidConfigException;
@@ -22,16 +21,12 @@ use yii\base\InvalidConfigException;
class ArrayFixture extends Fixture implements \IteratorAggregate, \ArrayAccess, \Countable
{
use ArrayAccessTrait;
+ use FileFixtureTrait;
/**
* @var array the data rows. Each array element represents one row of data (column name => column value).
*/
public $data = [];
- /**
- * @var string|bool the file path or [path alias](guide:concept-aliases) of the data file that contains the fixture data
- * to be returned by [[getData()]]. You can set this property to be false to prevent loading any data.
- */
- public $dataFile;
/**
@@ -56,19 +51,11 @@ class ArrayFixture extends Fixture implements \IteratorAggregate, \ArrayAccess,
*/
protected function getData()
{
- if ($this->dataFile === false || $this->dataFile === null) {
- return [];
- }
- $dataFile = Yii::getAlias($this->dataFile);
- if (is_file($dataFile)) {
- return require $dataFile;
- }
-
- throw new InvalidConfigException("Fixture data file does not exist: {$this->dataFile}");
+ return $this->loadData($this->dataFile);
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function unload()
{
diff --git a/framework/test/BaseActiveFixture.php b/framework/test/BaseActiveFixture.php
index 1e952cc..ccdc7e1 100644
--- a/framework/test/BaseActiveFixture.php
+++ b/framework/test/BaseActiveFixture.php
@@ -7,7 +7,6 @@
namespace yii\test;
-use Yii;
use yii\base\ArrayAccessTrait;
use yii\base\InvalidConfigException;
@@ -22,6 +21,7 @@ use yii\base\InvalidConfigException;
abstract class BaseActiveFixture extends DbFixture implements \IteratorAggregate, \ArrayAccess, \Countable
{
use ArrayAccessTrait;
+ use FileFixtureTrait;
/**
* @var string the AR model class associated with this fixture.
@@ -32,17 +32,10 @@ abstract class BaseActiveFixture extends DbFixture implements \IteratorAggregate
*/
public $data = [];
/**
- * @var string|bool the file path or [path alias](guide:concept-aliases) of the data file that contains the fixture data
- * to be returned by [[getData()]]. You can set this property to be false to prevent loading any data.
- */
- public $dataFile;
-
- /**
* @var \yii\db\ActiveRecord[] the loaded AR models
*/
private $_models = [];
-
/**
* Returns the AR model by the specified model name.
* A model name is the key of the corresponding data row in [[data]].
@@ -87,27 +80,17 @@ abstract class BaseActiveFixture extends DbFixture implements \IteratorAggregate
/**
* Returns the fixture data.
*
- * The default implementation will try to return the fixture data by including the external file specified by [[dataFile]].
- * The file should return the data array that will be stored in [[data]] after inserting into the database.
- *
* @return array the data to be put into the database
* @throws InvalidConfigException if the specified data file does not exist.
+ * @see [[loadDataFile]]
*/
protected function getData()
{
- if ($this->dataFile === false || $this->dataFile === null) {
- return [];
- }
- $dataFile = Yii::getAlias($this->dataFile);
- if (is_file($dataFile)) {
- return require $dataFile;
- }
-
- throw new InvalidConfigException("Fixture data file does not exist: {$this->dataFile}");
+ return $this->loadData($this->dataFile);
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function unload()
{
diff --git a/framework/test/DbFixture.php b/framework/test/DbFixture.php
index 4be21e4..098219e 100644
--- a/framework/test/DbFixture.php
+++ b/framework/test/DbFixture.php
@@ -7,7 +7,6 @@
namespace yii\test;
-use Yii;
use yii\base\BaseObject;
use yii\db\Connection;
use yii\di\Instance;
@@ -34,7 +33,7 @@ abstract class DbFixture extends Fixture
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
diff --git a/framework/test/FileFixtureTrait.php b/framework/test/FileFixtureTrait.php
new file mode 100644
index 0000000..2313acc
--- /dev/null
+++ b/framework/test/FileFixtureTrait.php
@@ -0,0 +1,59 @@
+
+ * @since 2.0.14
+ */
+trait FileFixtureTrait
+{
+ /**
+ * @var string the directory path or [path alias](guide:concept-aliases) that contains the fixture data
+ * @since 2.0.14
+ */
+ public $dataDirectory;
+ /**
+ * @var string|bool the file path or [path alias](guide:concept-aliases) of the data file that contains the fixture data
+ * to be returned by [[getData()]]. You can set this property to be false to prevent loading any data.
+ */
+ public $dataFile;
+
+ /**
+ * Returns the fixture data.
+ *
+ * The default implementation will try to return the fixture data by including the external file specified by [[dataFile]].
+ * The file should return the data array that will be stored in [[data]] after inserting into the database.
+ *
+ * @param string $file the data file path
+ * @return array the data to be put into the database
+ * @throws InvalidConfigException if the specified data file does not exist.
+ */
+ protected function loadData($file)
+ {
+ if ($file === false || $file === null) {
+ return [];
+ }
+
+ if (basename($file) == $file && $this->dataDirectory !== null) {
+ $file = $this->dataDirectory . '/' . $file;
+ }
+
+ $file = Yii::getAlias($file);
+ if (is_file($file)) {
+ return require $file;
+ }
+
+ throw new InvalidConfigException("Fixture data file does not exist: {$file}");
+ }
+
+}
diff --git a/framework/test/InitDbFixture.php b/framework/test/InitDbFixture.php
index ea72f42..1cf3d1b 100644
--- a/framework/test/InitDbFixture.php
+++ b/framework/test/InitDbFixture.php
@@ -44,7 +44,7 @@ class InitDbFixture extends DbFixture
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function beforeLoad()
{
@@ -52,7 +52,7 @@ class InitDbFixture extends DbFixture
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function afterLoad()
{
@@ -60,7 +60,7 @@ class InitDbFixture extends DbFixture
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function load()
{
@@ -71,7 +71,7 @@ class InitDbFixture extends DbFixture
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function beforeUnload()
{
@@ -79,7 +79,7 @@ class InitDbFixture extends DbFixture
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function afterUnload()
{
diff --git a/framework/validators/BooleanValidator.php b/framework/validators/BooleanValidator.php
index 2a6830a..ac8e334 100644
--- a/framework/validators/BooleanValidator.php
+++ b/framework/validators/BooleanValidator.php
@@ -37,7 +37,7 @@ class BooleanValidator extends Validator
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -48,7 +48,7 @@ class BooleanValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function validateValue($value)
{
@@ -69,7 +69,7 @@ class BooleanValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function clientValidateAttribute($model, $attribute, $view)
{
@@ -80,7 +80,7 @@ class BooleanValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getClientOptions($model, $attribute)
{
diff --git a/framework/validators/CompareValidator.php b/framework/validators/CompareValidator.php
index 5eb5fc0..57c358c 100644
--- a/framework/validators/CompareValidator.php
+++ b/framework/validators/CompareValidator.php
@@ -97,7 +97,7 @@ class CompareValidator extends Validator
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -135,7 +135,7 @@ class CompareValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function validateAttribute($model, $attribute)
{
@@ -163,7 +163,7 @@ class CompareValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function validateValue($value)
{
@@ -221,18 +221,18 @@ class CompareValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function clientValidateAttribute($model, $attribute, $view)
{
ValidationAsset::register($view);
$options = $this->getClientOptions($model, $attribute);
- return 'yii.validation.compare(value, messages, ' . json_encode($options, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ');';
+ return 'yii.validation.compare(value, messages, ' . json_encode($options, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ', $form);';
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getClientOptions($model, $attribute)
{
diff --git a/framework/validators/DateValidator.php b/framework/validators/DateValidator.php
index 33e466b..0f50a00 100644
--- a/framework/validators/DateValidator.php
+++ b/framework/validators/DateValidator.php
@@ -207,7 +207,7 @@ class DateValidator extends Validator
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -261,7 +261,7 @@ class DateValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function validateAttribute($model, $attribute)
{
@@ -301,7 +301,7 @@ class DateValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function validateValue($value)
{
diff --git a/framework/validators/DefaultValueValidator.php b/framework/validators/DefaultValueValidator.php
index 1b54e5a..098754d 100644
--- a/framework/validators/DefaultValueValidator.php
+++ b/framework/validators/DefaultValueValidator.php
@@ -39,7 +39,7 @@ class DefaultValueValidator extends Validator
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function validateAttribute($model, $attribute)
{
diff --git a/framework/validators/EachValidator.php b/framework/validators/EachValidator.php
index 4c830b0..d817402 100644
--- a/framework/validators/EachValidator.php
+++ b/framework/validators/EachValidator.php
@@ -77,7 +77,7 @@ class EachValidator extends Validator
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -124,7 +124,7 @@ class EachValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function validateAttribute($model, $attribute)
{
@@ -168,7 +168,7 @@ class EachValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function validateValue($value)
{
diff --git a/framework/validators/EmailValidator.php b/framework/validators/EmailValidator.php
index 2c57f28..246808a 100644
--- a/framework/validators/EmailValidator.php
+++ b/framework/validators/EmailValidator.php
@@ -52,7 +52,7 @@ class EmailValidator extends Validator
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -66,7 +66,7 @@ class EmailValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function validateValue($value)
{
@@ -116,7 +116,7 @@ class EmailValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function clientValidateAttribute($model, $attribute, $view)
{
@@ -130,7 +130,7 @@ class EmailValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getClientOptions($model, $attribute)
{
diff --git a/framework/validators/ExistValidator.php b/framework/validators/ExistValidator.php
index 0ba8022..036c993 100644
--- a/framework/validators/ExistValidator.php
+++ b/framework/validators/ExistValidator.php
@@ -12,6 +12,7 @@ use yii\base\InvalidConfigException;
use yii\base\Model;
use yii\db\ActiveQuery;
use yii\db\ActiveRecord;
+use yii\db\QueryInterface;
/**
* ExistValidator validates that the attribute value exists in a table.
@@ -57,6 +58,12 @@ class ExistValidator extends Validator
*/
public $targetAttribute;
/**
+ * @var string the name of the relation that should be used to validate the existence of the current attribute value
+ * This param overwrites $targetClass and $targetAttribute
+ * @since 2.0.14
+ */
+ public $targetRelation;
+ /**
* @var string|array|\Closure additional filter to be applied to the DB query used to check the existence of the attribute value.
* This can be a string or an array representing the additional query condition (refer to [[\yii\db\Query::where()]]
* on the format of query condition), or an anonymous function with the signature `function ($query)`, where `$query`
@@ -73,9 +80,15 @@ class ExistValidator extends Validator
*/
public $targetAttributeJunction = 'and';
+ /**
+ * @var bool whether this validator is forced to always use master DB
+ * @since 2.0.14
+ */
+ public $forceMasterDb = true;
+
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -86,13 +99,52 @@ class ExistValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function validateAttribute($model, $attribute)
{
+ if (!empty($this->targetRelation)) {
+ $this->checkTargetRelationExistence($model, $attribute);
+ } else {
+ $this->checkTargetAttributeExistence($model, $attribute);
+ }
+ }
+
+ /**
+ * Validates existence of the current attribute based on relation name
+ * @param \yii\db\ActiveRecord $model the data model to be validated
+ * @param string $attribute the name of the attribute to be validated.
+ */
+ private function checkTargetRelationExistence($model, $attribute)
+ {
+ $exists = false;
+ /** @var ActiveQuery $relationQuery */
+ $relationQuery = $model->{'get' . ucfirst($this->targetRelation)}();
+
+ if ($this->forceMasterDb) {
+ $model::getDb()->useMaster(function() use ($relationQuery, &$exists) {
+ $exists = $relationQuery->exists();
+ });
+ } else {
+ $relationQuery->exists();
+ }
+
+
+ if (!$exists) {
+ $this->addError($model, $attribute, $this->message);
+ }
+ }
+
+ /**
+ * Validates existence of the current attribute based on targetAttribute
+ * @param \yii\base\Model $model the data model to be validated
+ * @param string $attribute the name of the attribute to be validated.
+ */
+ private function checkTargetAttributeExistence($model, $attribute)
+ {
$targetAttribute = $this->targetAttribute === null ? $attribute : $this->targetAttribute;
$params = $this->prepareConditions($targetAttribute, $model, $attribute);
- $conditions[] = $this->targetAttributeJunction == 'or' ? 'or' : 'and';
+ $conditions = [$this->targetAttributeJunction == 'or' ? 'or' : 'and'];
if (!$this->allowArray) {
foreach ($params as $key => $value) {
@@ -110,11 +162,7 @@ class ExistValidator extends Validator
$targetClass = $this->targetClass === null ? get_class($model) : $this->targetClass;
$query = $this->createQuery($targetClass, $conditions);
- if (is_array($model->$attribute)) {
- if ($query->count("DISTINCT [[$targetAttribute]]") != count($model->$attribute)) {
- $this->addError($model, $attribute, $this->message);
- }
- } elseif (!$query->exists()) {
+ if (!$this->valueExists($targetClass, $query, $model->$attribute)) {
$this->addError($model, $attribute, $this->message);
}
}
@@ -167,7 +215,7 @@ class ExistValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function validateValue($value)
{
@@ -178,17 +226,53 @@ class ExistValidator extends Validator
throw new InvalidConfigException('The "targetAttribute" property must be configured as a string.');
}
+ if (is_array($value) && !$this->allowArray) {
+ return [$this->message, []];
+ }
+
$query = $this->createQuery($this->targetClass, [$this->targetAttribute => $value]);
- if (is_array($value)) {
- if (!$this->allowArray) {
- return [$this->message, []];
- }
+ return $this->valueExists($this->targetClass, $query, $value) ? null : [$this->message, []];
+ }
- return $query->count("DISTINCT [[$this->targetAttribute]]") == count($value) ? null : [$this->message, []];
+ /**
+ * Check whether value exists in target table
+ *
+ * @param string $targetClass
+ * @param QueryInterface $query
+ * @param mixed $value the value want to be checked
+ * @return boolean
+ */
+ private function valueExists($targetClass, $query, $value)
+ {
+ $db = $targetClass::getDb();
+ $exists = false;
+
+ if ($this->forceMasterDb) {
+ $db->useMaster(function ($db) use ($query, $value, &$exists) {
+ $exists = $this->queryValueExists($query, $value);
+ });
+ } else {
+ $exists = $this->queryValueExists($query, $value);
}
- return $query->exists() ? null : [$this->message, []];
+ return $exists;
+ }
+
+
+ /**
+ * Run query to check if value exists
+ *
+ * @param QueryInterface $query
+ * @param mixed $value the value to be checked
+ * @return bool
+ */
+ private function queryValueExists($query, $value)
+ {
+ if (is_array($value)) {
+ return $query->count("DISTINCT [[$this->targetAttribute]]") == count($value) ;
+ }
+ return $query->exists();
}
/**
diff --git a/framework/validators/FileValidator.php b/framework/validators/FileValidator.php
index 1aced9d..095cb76 100644
--- a/framework/validators/FileValidator.php
+++ b/framework/validators/FileValidator.php
@@ -80,6 +80,15 @@ class FileValidator extends Validator
* @see tooMany for the customized message when too many files are uploaded.
*/
public $maxFiles = 1;
+
+ /**
+ * @var int the minimum file count the given attribute can hold.
+ * Defaults to 0. Higher value means at least that number of files should be uploaded.
+ *
+ * @see tooFew for the customized message when too few files are uploaded.
+ * @since 2.0.14
+ */
+ public $minFiles = 0;
/**
* @var string the error message used when a file is not uploaded correctly.
*/
@@ -120,6 +129,18 @@ class FileValidator extends Validator
* - {limit}: the value of [[maxFiles]]
*/
public $tooMany;
+
+ /**
+ * @var string the error message used if the count of multiple uploads less that minFiles.
+ * You may use the following tokens in the message:
+ *
+ * - {attribute}: the attribute name
+ * - {limit}: the value of [[minFiles]]
+ *
+ * @since 2.0.14
+ */
+ public $tooFew;
+
/**
* @var string the error message used when the uploaded file has an extension name
* that is not listed in [[extensions]]. You may use the following tokens in the message:
@@ -142,7 +163,7 @@ class FileValidator extends Validator
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -156,6 +177,9 @@ class FileValidator extends Validator
if ($this->tooMany === null) {
$this->tooMany = Yii::t('yii', 'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.');
}
+ if ($this->tooFew === null) {
+ $this->tooFew = Yii::t('yii', 'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.');
+ }
if ($this->wrongExtension === null) {
$this->wrongExtension = Yii::t('yii', 'Only files with these extensions are allowed: {extensions}.');
}
@@ -181,34 +205,40 @@ class FileValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function validateAttribute($model, $attribute)
{
- if ($this->maxFiles != 1) {
- $files = $model->$attribute;
- if (!is_array($files)) {
+ if ($this->maxFiles != 1 || $this->minFiles > 1) {
+ $rawFiles = $model->$attribute;
+ if (!is_array($rawFiles)) {
$this->addError($model, $attribute, $this->uploadRequired);
return;
}
- foreach ($files as $i => $file) {
- if (!$file instanceof UploadedFile || $file->error == UPLOAD_ERR_NO_FILE) {
- unset($files[$i]);
- }
- }
+
+ $files = $this->filterFiles($rawFiles);
$model->$attribute = $files;
+
if (empty($files)) {
$this->addError($model, $attribute, $this->uploadRequired);
+
+ return;
}
- if ($this->maxFiles && count($files) > $this->maxFiles) {
+
+ $filesCount = count($files);
+ if ($this->maxFiles && $filesCount > $this->maxFiles) {
$this->addError($model, $attribute, $this->tooMany, ['limit' => $this->maxFiles]);
- } else {
- foreach ($files as $file) {
- $result = $this->validateValue($file);
- if (!empty($result)) {
- $this->addError($model, $attribute, $result[0], $result[1]);
- }
+ }
+
+ if ($this->minFiles && $this->minFiles > $filesCount) {
+ $this->addError($model, $attribute, $this->tooFew, ['limit' => $this->minFiles]);
+ }
+
+ foreach ($files as $file) {
+ $result = $this->validateValue($file);
+ if (!empty($result)) {
+ $this->addError($model, $attribute, $result[0], $result[1]);
}
}
} else {
@@ -220,7 +250,25 @@ class FileValidator extends Validator
}
/**
- * @inheritdoc
+ * Files filter.
+ * @param array $files
+ * @return UploadedFile[]
+ */
+ private function filterFiles(array $files)
+ {
+ $result = [];
+
+ foreach ($files as $fileName => $file) {
+ if ($file instanceof UploadedFile && $file->error !== UPLOAD_ERR_NO_FILE) {
+ $result[$fileName] = $file;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * {@inheritdoc}
*/
protected function validateValue($value)
{
@@ -313,7 +361,7 @@ class FileValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
* @param bool $trim
*/
public function isEmpty($value, $trim = false)
@@ -375,7 +423,7 @@ class FileValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function clientValidateAttribute($model, $attribute, $view)
{
@@ -385,7 +433,7 @@ class FileValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getClientOptions($model, $attribute)
{
diff --git a/framework/validators/FilterValidator.php b/framework/validators/FilterValidator.php
index cedf241..84e820d 100644
--- a/framework/validators/FilterValidator.php
+++ b/framework/validators/FilterValidator.php
@@ -58,7 +58,7 @@ class FilterValidator extends Validator
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -69,7 +69,7 @@ class FilterValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function validateAttribute($model, $attribute)
{
@@ -80,7 +80,7 @@ class FilterValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function clientValidateAttribute($model, $attribute, $view)
{
@@ -95,7 +95,7 @@ class FilterValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getClientOptions($model, $attribute)
{
diff --git a/framework/validators/ImageValidator.php b/framework/validators/ImageValidator.php
index f9067fa..9b03749 100644
--- a/framework/validators/ImageValidator.php
+++ b/framework/validators/ImageValidator.php
@@ -89,7 +89,7 @@ class ImageValidator extends FileValidator
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -113,7 +113,7 @@ class ImageValidator extends FileValidator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function validateValue($value)
{
@@ -160,7 +160,7 @@ class ImageValidator extends FileValidator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function clientValidateAttribute($model, $attribute, $view)
{
@@ -170,7 +170,7 @@ class ImageValidator extends FileValidator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getClientOptions($model, $attribute)
{
diff --git a/framework/validators/InlineValidator.php b/framework/validators/InlineValidator.php
index 3192598..93d41a1 100644
--- a/framework/validators/InlineValidator.php
+++ b/framework/validators/InlineValidator.php
@@ -61,7 +61,7 @@ class InlineValidator extends Validator
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function validateAttribute($model, $attribute)
{
@@ -73,7 +73,7 @@ class InlineValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function clientValidateAttribute($model, $attribute, $view)
{
diff --git a/framework/validators/IpValidator.php b/framework/validators/IpValidator.php
index 12a272e..d34c03b 100644
--- a/framework/validators/IpValidator.php
+++ b/framework/validators/IpValidator.php
@@ -10,6 +10,7 @@ namespace yii\validators;
use Yii;
use yii\base\InvalidConfigException;
use yii\helpers\Html;
+use yii\helpers\IpHelper;
use yii\helpers\Json;
use yii\web\JsExpression;
@@ -40,14 +41,6 @@ use yii\web\JsExpression;
class IpValidator extends Validator
{
/**
- * The length of IPv6 address in bits.
- */
- const IPV6_ADDRESS_LENGTH = 128;
- /**
- * The length of IPv4 address in bits.
- */
- const IPV4_ADDRESS_LENGTH = 32;
- /**
* Negation char.
*
* Used to negate [[ranges]] or [[networks]] or to negate validating value when [[negation]] is set to `true`.
@@ -212,7 +205,7 @@ class IpValidator extends Validator
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -290,7 +283,7 @@ class IpValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function validateValue($value)
{
@@ -304,7 +297,7 @@ class IpValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function validateAttribute($model, $attribute)
{
@@ -354,14 +347,14 @@ class IpValidator extends Validator
return [$this->message, []];
}
- if ($this->getIpVersion($ip) == 6) {
+ if ($this->getIpVersion($ip) === IpHelper::IPV6) {
if ($cidr !== null) {
- if ($cidr > static::IPV6_ADDRESS_LENGTH || $cidr < 0) {
+ if ($cidr > IpHelper::IPV6_ADDRESS_LENGTH || $cidr < 0) {
return [$this->wrongCidr, []];
}
} else {
$isCidrDefault = true;
- $cidr = static::IPV6_ADDRESS_LENGTH;
+ $cidr = IpHelper::IPV6_ADDRESS_LENGTH;
}
if (!$this->validateIPv6($ip)) {
@@ -376,12 +369,12 @@ class IpValidator extends Validator
}
} else {
if ($cidr !== null) {
- if ($cidr > static::IPV4_ADDRESS_LENGTH || $cidr < 0) {
+ if ($cidr > IpHelper::IPV4_ADDRESS_LENGTH || $cidr < 0) {
return [$this->wrongCidr, []];
}
} else {
$isCidrDefault = true;
- $cidr = static::IPV4_ADDRESS_LENGTH;
+ $cidr = IpHelper::IPV4_ADDRESS_LENGTH;
}
if (!$this->validateIPv4($ip)) {
return [$this->message, []];
@@ -414,8 +407,7 @@ class IpValidator extends Validator
*/
private function expandIPv6($ip)
{
- $hex = unpack('H*hex', inet_pton($ip));
- return substr(preg_replace('/([a-f0-9]{4})/i', '$1:', $hex['hex']), 0, -1);
+ return IpHelper::expandIPv6($ip);
}
/**
@@ -515,7 +507,7 @@ class IpValidator extends Validator
*/
private function getIpVersion($ip)
{
- return strpos($ip, ':') === false ? 4 : 6;
+ return IpHelper::getIpVersion($ip);
}
/**
@@ -537,51 +529,11 @@ class IpValidator extends Validator
*/
private function inRange($ip, $cidr, $range)
{
- $ipVersion = $this->getIpVersion($ip);
- $binIp = $this->ip2bin($ip);
-
- $parts = explode('/', $range);
- $net = array_shift($parts);
- $range_cidr = array_shift($parts);
-
-
- $netVersion = $this->getIpVersion($net);
- if ($ipVersion !== $netVersion) {
- return false;
- }
- if ($range_cidr === null) {
- $range_cidr = $netVersion === 4 ? static::IPV4_ADDRESS_LENGTH : static::IPV6_ADDRESS_LENGTH;
- }
-
- $binNet = $this->ip2bin($net);
- return substr($binIp, 0, $range_cidr) === substr($binNet, 0, $range_cidr) && $cidr >= $range_cidr;
- }
-
- /**
- * Converts IP address to bits representation.
- *
- * @param string $ip
- * @return string bits as a string
- */
- private function ip2bin($ip)
- {
- if ($this->getIpVersion($ip) === 4) {
- return str_pad(base_convert(ip2long($ip), 10, 2), static::IPV4_ADDRESS_LENGTH, '0', STR_PAD_LEFT);
- }
-
- $unpack = unpack('A16', inet_pton($ip));
- $binStr = array_shift($unpack);
- $bytes = static::IPV6_ADDRESS_LENGTH / 8; // 128 bit / 8 = 16 bytes
- $result = '';
- while ($bytes-- > 0) {
- $result = sprintf('%08b', isset($binStr[$bytes]) ? ord($binStr[$bytes]) : '0') . $result;
- }
-
- return $result;
+ return IpHelper::inRange($ip . '/' . $cidr, $range);
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function clientValidateAttribute($model, $attribute, $view)
{
@@ -592,7 +544,7 @@ class IpValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getClientOptions($model, $attribute)
{
diff --git a/framework/validators/NumberValidator.php b/framework/validators/NumberValidator.php
index e438c04..88b0d41 100644
--- a/framework/validators/NumberValidator.php
+++ b/framework/validators/NumberValidator.php
@@ -58,7 +58,7 @@ class NumberValidator extends Validator
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -76,12 +76,12 @@ class NumberValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function validateAttribute($model, $attribute)
{
$value = $model->$attribute;
- if (is_array($value) || (is_object($value) && !method_exists($value, '__toString'))) {
+ if ($this->isNotNumber($value)) {
$this->addError($model, $attribute, $this->message);
return;
}
@@ -99,11 +99,11 @@ class NumberValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function validateValue($value)
{
- if (is_array($value) || is_object($value)) {
+ if ($this->isNotNumber($value)) {
return [Yii::t('yii', '{attribute} is invalid.'), []];
}
$pattern = $this->integerOnly ? $this->integerPattern : $this->numberPattern;
@@ -118,8 +118,18 @@ class NumberValidator extends Validator
return null;
}
+ /*
+ * @param mixed $value the data value to be checked.
+ */
+ private function isNotNumber($value)
+ {
+ return is_array($value)
+ || (is_object($value) && !method_exists($value, '__toString'))
+ || (!is_object($value) && !is_scalar($value) && !is_null($value));
+ }
+
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function clientValidateAttribute($model, $attribute, $view)
{
@@ -130,7 +140,7 @@ class NumberValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getClientOptions($model, $attribute)
{
diff --git a/framework/validators/RangeValidator.php b/framework/validators/RangeValidator.php
index bbc344a..bcde4a1 100644
--- a/framework/validators/RangeValidator.php
+++ b/framework/validators/RangeValidator.php
@@ -51,7 +51,7 @@ class RangeValidator extends Validator
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -68,7 +68,7 @@ class RangeValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function validateValue($value)
{
@@ -89,7 +89,7 @@ class RangeValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function validateAttribute($model, $attribute)
{
@@ -100,7 +100,7 @@ class RangeValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function clientValidateAttribute($model, $attribute, $view)
{
@@ -115,7 +115,7 @@ class RangeValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getClientOptions($model, $attribute)
{
diff --git a/framework/validators/RegularExpressionValidator.php b/framework/validators/RegularExpressionValidator.php
index fab5c51..bdc54ed 100644
--- a/framework/validators/RegularExpressionValidator.php
+++ b/framework/validators/RegularExpressionValidator.php
@@ -35,7 +35,7 @@ class RegularExpressionValidator extends Validator
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -49,7 +49,7 @@ class RegularExpressionValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function validateValue($value)
{
@@ -61,7 +61,7 @@ class RegularExpressionValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function clientValidateAttribute($model, $attribute, $view)
{
@@ -72,7 +72,7 @@ class RegularExpressionValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getClientOptions($model, $attribute)
{
diff --git a/framework/validators/RequiredValidator.php b/framework/validators/RequiredValidator.php
index a8c620a..c084804 100644
--- a/framework/validators/RequiredValidator.php
+++ b/framework/validators/RequiredValidator.php
@@ -51,7 +51,7 @@ class RequiredValidator extends Validator
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -63,7 +63,7 @@ class RequiredValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function validateValue($value)
{
@@ -84,7 +84,7 @@ class RequiredValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function clientValidateAttribute($model, $attribute, $view)
{
@@ -95,7 +95,7 @@ class RequiredValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getClientOptions($model, $attribute)
{
diff --git a/framework/validators/SafeValidator.php b/framework/validators/SafeValidator.php
index 5e13fb0..f86f239 100644
--- a/framework/validators/SafeValidator.php
+++ b/framework/validators/SafeValidator.php
@@ -26,14 +26,14 @@ namespace yii\validators;
class SafeValidator extends Validator
{
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function validateAttributes($model, $attributes = null)
{
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function validateAttribute($model, $attribute)
{
diff --git a/framework/validators/StringValidator.php b/framework/validators/StringValidator.php
index e01bfe6..6961ac5 100644
--- a/framework/validators/StringValidator.php
+++ b/framework/validators/StringValidator.php
@@ -67,7 +67,7 @@ class StringValidator extends Validator
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -99,7 +99,7 @@ class StringValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function validateAttribute($model, $attribute)
{
@@ -125,7 +125,7 @@ class StringValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function validateValue($value)
{
@@ -149,7 +149,7 @@ class StringValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function clientValidateAttribute($model, $attribute, $view)
{
@@ -160,7 +160,7 @@ class StringValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getClientOptions($model, $attribute)
{
diff --git a/framework/validators/UniqueValidator.php b/framework/validators/UniqueValidator.php
index 41cb179..f932b01 100644
--- a/framework/validators/UniqueValidator.php
+++ b/framework/validators/UniqueValidator.php
@@ -92,7 +92,14 @@ class UniqueValidator extends Validator
/**
- * @inheritdoc
+ * @var bool whether this validator is forced to always use master DB
+ * @since 2.0.14
+ */
+ public $forceMasterDb = true;
+
+
+ /**
+ * {@inheritdoc}
*/
public function init()
{
@@ -113,7 +120,7 @@ class UniqueValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function validateAttribute($model, $attribute)
{
@@ -121,7 +128,7 @@ class UniqueValidator extends Validator
$targetClass = $this->getTargetClass($model);
$targetAttribute = $this->targetAttribute === null ? $attribute : $this->targetAttribute;
$rawConditions = $this->prepareConditions($targetAttribute, $model, $attribute);
- $conditions[] = $this->targetAttributeJunction === 'or' ? 'or' : 'and';
+ $conditions = [$this->targetAttributeJunction === 'or' ? 'or' : 'and'];
foreach ($rawConditions as $key => $value) {
if (is_array($value)) {
@@ -131,7 +138,19 @@ class UniqueValidator extends Validator
$conditions[] = [$key => $value];
}
- if ($this->modelExists($targetClass, $conditions, $model)) {
+ $db = $targetClass::getDb();
+
+ $modelExists = false;
+
+ if ($this->forceMasterDb) {
+ $db->useMaster(function () use ($targetClass, $conditions, $model, &$modelExists) {
+ $modelExists = $this->modelExists($targetClass, $conditions, $model);
+ });
+ } else {
+ $modelExists = $this->modelExists($targetClass, $conditions, $model);
+ }
+
+ if ($modelExists) {
if (is_array($targetAttribute) && count($targetAttribute) > 1) {
$this->addComboNotUniqueError($model, $attribute);
} else {
@@ -174,6 +193,9 @@ class UniqueValidator extends Validator
// only select primary key to optimize query
$columnsCondition = array_flip($targetClass::primaryKey());
$query->select(array_flip($this->applyTableAlias($query, $columnsCondition)));
+
+ // any with relation can't be loaded because related fields are not selected
+ $query->with = null;
}
$models = $query->limit(2)->asArray()->all();
$n = count($models);
diff --git a/framework/validators/UrlValidator.php b/framework/validators/UrlValidator.php
index 2eeb99f..6a82387 100644
--- a/framework/validators/UrlValidator.php
+++ b/framework/validators/UrlValidator.php
@@ -50,7 +50,7 @@ class UrlValidator extends Validator
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -64,7 +64,7 @@ class UrlValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function validateAttribute($model, $attribute)
{
@@ -78,7 +78,7 @@ class UrlValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
protected function validateValue($value)
{
@@ -119,7 +119,7 @@ class UrlValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function clientValidateAttribute($model, $attribute, $view)
{
@@ -133,7 +133,7 @@ class UrlValidator extends Validator
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getClientOptions($model, $attribute)
{
diff --git a/framework/validators/Validator.php b/framework/validators/Validator.php
index 0a9e14c..706cdfb 100644
--- a/framework/validators/Validator.php
+++ b/framework/validators/Validator.php
@@ -227,7 +227,7 @@ class Validator extends Component
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function init()
{
@@ -316,7 +316,21 @@ class Validator extends Component
* Validates a value.
* A validator class can implement this method to support data validation out of the context of a data model.
* @param mixed $value the data value to be validated.
- * @return array|null the error message and the parameters to be inserted into the error message.
+ * @return array|null the error message and the array of parameters to be inserted into the error message.
+ * ```php
+ * if (!$valid) {
+ * return [$this->message, [
+ * 'param1' => $this->param1,
+ * 'formattedLimit' => Yii::$app->formatter->asShortSize($this->getSizeLimit()),
+ * 'mimeTypes' => implode(', ', $this->mimeTypes),
+ * 'param4' => 'etc...',
+ * ]];
+ * }
+ *
+ * return null;
+ * ```
+ * for this example `message` template can contain `{param1}`, `{formattedLimit}`, `{mimeTypes}`, `{param4}`
+ *
* Null should be returned if the data is valid.
* @throws NotSupportedException if the validator does not supporting data validation without a model
*/
diff --git a/framework/views/_addComments.php b/framework/views/_addComments.php
new file mode 100644
index 0000000..fe59921
--- /dev/null
+++ b/framework/views/_addComments.php
@@ -0,0 +1,7 @@
+ $this->addCommentOnTable('= $table ?>', '= $tableComment ?>');
diff --git a/framework/views/addColumnMigration.php b/framework/views/addColumnMigration.php
index 3c39ddb..c992c7c 100644
--- a/framework/views/addColumnMigration.php
+++ b/framework/views/addColumnMigration.php
@@ -29,9 +29,9 @@ use yii\db\Migration;
class = $className ?> extends Migration
{
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function up()
+ public function safeUp()
{
= $this->render('_addColumns', [
'table' => $table,
@@ -42,9 +42,9 @@ class = $className ?> extends Migration
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function down()
+ public function safeDown()
{
= $this->render('_dropColumns', [
'table' => $table,
diff --git a/framework/views/createJunctionMigration.php b/framework/views/createJunctionMigration.php
index f7ce00f..fa62b25 100644
--- a/framework/views/createJunctionMigration.php
+++ b/framework/views/createJunctionMigration.php
@@ -27,9 +27,9 @@ use yii\db\Migration;
class = $className ?> extends Migration
{
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function up()
+ public function safeUp()
{
$this->createTable('= $table ?>', [
'= $field_first ?>_id' => $this->integer(),
@@ -69,9 +69,9 @@ class = $className ?> extends Migration
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function down()
+ public function safeDown()
{
$this->dropTable('= $table ?>');
}
diff --git a/framework/views/createTableMigration.php b/framework/views/createTableMigration.php
index 763a752..dceb6b2 100644
--- a/framework/views/createTableMigration.php
+++ b/framework/views/createTableMigration.php
@@ -7,6 +7,7 @@
/* @var $className string the new migration class name without namespace */
/* @var $namespace string the new migration class namespace */
/* @var $table string the name table */
+/* @var $tableComment string the comment table */
/* @var $fields array the fields */
/* @var $foreignKeys array the foreign keys */
@@ -27,9 +28,9 @@ use yii\db\Migration;
class = $className ?> extends Migration
{
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function up()
+ public function safeUp()
{
= $this->render('_createTable', [
'table' => $table,
@@ -37,12 +38,19 @@ class = $className ?> extends Migration
'foreignKeys' => $foreignKeys,
])
?>
+render('_addComments', [
+ 'table' => $table,
+ 'tableComment' => $tableComment,
+ ]);
+}
+?>
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function down()
+ public function safeDown()
{
= $this->render('_dropTable', [
'table' => $table,
diff --git a/framework/views/dropColumnMigration.php b/framework/views/dropColumnMigration.php
index 32c2002..be1a4d2 100644
--- a/framework/views/dropColumnMigration.php
+++ b/framework/views/dropColumnMigration.php
@@ -28,9 +28,9 @@ use yii\db\Migration;
class = $className ?> extends Migration
{
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function up()
+ public function safeUp()
{
= $this->render('_dropColumns', [
'table' => $table,
@@ -41,9 +41,9 @@ class = $className ?> extends Migration
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function down()
+ public function safeDown()
{
= $this->render('_addColumns', [
'table' => $table,
diff --git a/framework/views/dropTableMigration.php b/framework/views/dropTableMigration.php
index fca42a1..a215110 100644
--- a/framework/views/dropTableMigration.php
+++ b/framework/views/dropTableMigration.php
@@ -26,9 +26,9 @@ use yii\db\Migration;
class = $className ?> extends Migration
{
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function up()
+ public function safeUp()
{
= $this->render('_dropTable', [
'table' => $table,
@@ -38,9 +38,9 @@ class = $className ?> extends Migration
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function down()
+ public function safeDown()
{
= $this->render('_createTable', [
'table' => $table,
diff --git a/framework/views/errorHandler/callStackItem.php b/framework/views/errorHandler/callStackItem.php
index 04f8c88..512181d 100644
--- a/framework/views/errorHandler/callStackItem.php
+++ b/framework/views/errorHandler/callStackItem.php
@@ -34,12 +34,14 @@