diff --git a/BaseYii.php b/BaseYii.php index e63a75ffa8..7ed84d53a9 100644 --- a/BaseYii.php +++ b/BaseYii.php @@ -7,11 +7,17 @@ namespace yii; +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; +use yii\base\InvalidArgumentException; use yii\base\InvalidConfigException; -use yii\base\InvalidParamException; use yii\base\UnknownClassException; -use yii\log\Logger; use yii\di\Container; +use yii\di\Instance; +use yii\helpers\VarDumper; +use yii\log\Logger; +use yii\profile\Profiler; +use yii\profile\ProfilerInterface; /** * Gets the application start timestamp. @@ -59,14 +65,6 @@ */ class BaseYii { - /** - * @var array class map used by the Yii autoloading mechanism. - * The array keys are the class names (without leading backslashes), and the array values - * are the corresponding class file paths (or path aliases). This property mainly affects - * how [[autoload()]] works. - * @see autoload() - */ - public static $classMap = []; /** * @var \yii\console\Application|\yii\web\Application the application instance */ @@ -93,7 +91,7 @@ class BaseYii */ public static function getVersion() { - return '2.0.5'; + return '3.0.0-dev'; } /** @@ -119,11 +117,13 @@ public static function getVersion() * * Note, this method does not check if the returned path exists or not. * + * See the [guide article on aliases](guide:concept-aliases) for more information. + * * @param string $alias the alias to be translated. - * @param boolean $throwException whether to throw an exception if the given alias is invalid. + * @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|boolean the path corresponding to the alias, false if the root alias is not previously registered. - * @throws InvalidParamException if the alias is invalid while $throwException is true. + * @return string|bool the path corresponding to the alias, false if the root alias is not previously registered. + * @throws InvalidArgumentException if the alias is invalid while $throwException is true. * @see setAlias() */ public static function getAlias($alias, $throwException = true) @@ -133,26 +133,17 @@ public static function getAlias($alias, $throwException = true) return $alias; } - $pos = strpos($alias, '/'); - $root = $pos === false ? $alias : substr($alias, 0, $pos); + $result = static::findAlias($alias); - if (isset(static::$aliases[$root])) { - if (is_string(static::$aliases[$root])) { - return $pos === false ? static::$aliases[$root] : static::$aliases[$root] . substr($alias, $pos); - } else { - foreach (static::$aliases[$root] as $name => $path) { - if (strpos($alias . '/', $name . '/') === 0) { - return $path . substr($alias, strlen($name)); - } - } - } + if (is_array($result)) { + return $result['path']; } if ($throwException) { - throw new InvalidParamException("Invalid path alias: $alias"); - } else { - return false; + throw new InvalidArgumentException("Invalid path alias: $alias"); } + + return false; } /** @@ -160,21 +151,34 @@ public static function getAlias($alias, $throwException = true) * A root alias is an alias that has been registered via [[setAlias()]] previously. * If a given alias matches multiple root aliases, the longest one will be returned. * @param string $alias the alias - * @return string|boolean the root alias, or false if no root alias is found + * @return string|bool the root alias, or false if no root alias is found */ public static function getRootAlias($alias) + { + $result = static::findAlias($alias); + if (is_array($result)) { + $result = $result['root']; + } + return $result; + } + + /** + * @param string $alias + * @return array|bool + */ + protected static function findAlias(string $alias) { $pos = strpos($alias, '/'); $root = $pos === false ? $alias : substr($alias, 0, $pos); if (isset(static::$aliases[$root])) { if (is_string(static::$aliases[$root])) { - return $root; - } else { - foreach (static::$aliases[$root] as $name => $path) { - if (strpos($alias . '/', $name . '/') === 0) { - return $name; - } + return ['root' => $root, 'path' => $pos === false ? static::$aliases[$root] : static::$aliases[$root] . substr($alias, $pos)]; + } + + foreach (static::$aliases[$root] as $name => $path) { + if (strpos($alias . '/', $name . '/') === 0) { + return ['root' => $name, 'path' => $path . substr($alias, strlen($name))]; } } } @@ -196,6 +200,8 @@ public static function getRootAlias($alias) * * Any trailing '/' and '\' characters in the given path will be trimmed. * + * See the [guide article on aliases](guide:concept-aliases) for more information. + * * @param string $alias the alias name (e.g. "@yii"). It must start with a '@' character. * It may contain the forward slash '/' which serves as boundary character when performing * alias translation by [[getAlias()]]. @@ -207,7 +213,7 @@ public static function getRootAlias($alias) * - 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) @@ -247,51 +253,6 @@ public static function setAlias($alias, $path) } } - /** - * Class autoload loader. - * This method is invoked automatically when PHP sees an unknown class. - * The method will attempt to include the class file according to the following procedure: - * - * 1. Search in [[classMap]]; - * 2. If the class is namespaced (e.g. `yii\base\Component`), it will attempt - * to include the file associated with the corresponding path alias - * (e.g. `@yii/base/Component.php`); - * - * This autoloader allows loading classes that follow the [PSR-4 standard](http://www.php-fig.org/psr/psr-4/) - * and have its top-level namespace or sub-namespaces defined as path aliases. - * - * Example: When aliases `@yii` and `@yii/bootstrap` are defined, classes in the `yii\bootstrap` namespace - * will be loaded using the `@yii/bootstrap` alias which points to the directory where bootstrap extension - * files are installed and all classes from other `yii` namespaces will be loaded from the yii framework directory. - * - * Also the [guide section on autoloading](guide:concept-autoloading). - * - * @param string $className the fully qualified class name without a leading backslash "\" - * @throws UnknownClassException if the class does not exist in the class file - */ - public static function autoload($className) - { - if (isset(static::$classMap[$className])) { - $classFile = static::$classMap[$className]; - if ($classFile[0] === '@') { - $classFile = static::getAlias($classFile); - } - } elseif (strpos($className, '\\') !== false) { - $classFile = static::getAlias('@' . str_replace('\\', '/', $className) . '.php', false); - if ($classFile === false || !is_file($classFile)) { - return; - } - } else { - return; - } - - include($classFile); - - if (YII_DEBUG && !class_exists($className, false) && !interface_exists($className, false) && !trait_exists($className, false)) { - throw new UnknownClassException("Unable to find '$className' in file: $classFile. Namespace missing?"); - } - } - /** * Creates a new object using the given configuration. * @@ -303,11 +264,11 @@ public static function autoload($className) * * ```php * // create an object using a class name - * $object = Yii::createObject('yii\db\Connection'); + * $object = Yii::createObject(\yii\db\Connection::class); * * // create an object using a configuration array * $object = Yii::createObject([ - * 'class' => 'yii\db\Connection', + * '__class' => \yii\db\Connection::class, * 'dsn' => 'mysql:host=127.0.0.1;dbname=demo', * 'username' => 'root', * 'password' => '', @@ -338,53 +299,145 @@ public static function createObject($type, array $params = []) { if (is_string($type)) { return static::$container->get($type, $params); - } elseif (is_array($type) && isset($type['class'])) { - $class = $type['class']; - unset($type['class']); + } elseif (is_array($type) && (isset($type['__class']) || isset($type['class']))) { + if (isset($type['__class'])) { + $class = $type['__class']; + unset($type['__class']); + } else { + // @todo remove fallback + $class = $type['class']; + unset($type['class']); + } return static::$container->get($class, $params, $type); } elseif (is_callable($type, true)) { - return call_user_func($type, $params); + return static::$container->invoke($type, $params); } elseif (is_array($type)) { - throw new InvalidConfigException('Object configuration must be an array containing a "class" element.'); - } else { - throw new InvalidConfigException("Unsupported configuration type: " . gettype($type)); + throw new InvalidConfigException('Object configuration must be an array containing a "__class" element.'); } + + throw new InvalidConfigException('Unsupported configuration type: ' . gettype($type)); } + /** + * @var LoggerInterface logger instance. + */ private static $_logger; /** - * @return Logger message logger + * @return LoggerInterface message logger */ public static function getLogger() { if (self::$_logger !== null) { return self::$_logger; - } else { - return self::$_logger = static::createObject('yii\log\Logger'); } + + return self::$_logger = Instance::ensure(['__class' => Logger::class], LoggerInterface::class); } /** * Sets the logger object. - * @param Logger $logger the logger object. + * @param LoggerInterface|\Closure|array|null $logger the logger object or its DI compatible configuration. */ public static function setLogger($logger) { - self::$_logger = $logger; + if ($logger === null) { + self::$_logger = null; + return; + } + + if (is_array($logger)) { + if (!isset($logger['__class']) && is_object(self::$_logger)) { + static::configure(self::$_logger, $logger); + return; + } + $logger = array_merge(['__class' => Logger::class], $logger); + } elseif ($logger instanceof \Closure) { + $logger = call_user_func($logger); + } + + self::$_logger = Instance::ensure($logger, LoggerInterface::class); + } + + /** + * @var ProfilerInterface profiler instance. + * @since 3.0.0 + */ + private static $_profiler; + + /** + * @return ProfilerInterface profiler instance. + * @since 3.0.0 + */ + public static function getProfiler() + { + if (self::$_profiler !== null) { + return self::$_profiler; + } + return self::$_profiler = Instance::ensure(['__class' => Profiler::class], ProfilerInterface::class); + } + + /** + * @param ProfilerInterface|\Closure|array|null $profiler profiler instance or its DI compatible configuration. + * @since 3.0.0 + */ + public static function setProfiler($profiler) + { + if ($profiler === null) { + self::$_profiler = null; + return; + } + + if (is_array($profiler)) { + if (!isset($profiler['__class']) && is_object(self::$_profiler)) { + static::configure(self::$_profiler, $profiler); + return; + } + $profiler = array_merge(['__class' => Profiler::class], $profiler); + } elseif ($profiler instanceof \Closure) { + $profiler = call_user_func($profiler); + } + + self::$_profiler = Instance::ensure($profiler, ProfilerInterface::class); + } + + /** + * Logs a message with category. + * @param string $level log level. + * @param mixed $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 3.0.0 + */ + public static function log($level, $message, $category = 'application') + { + $context = ['category' => $category]; + if (!is_string($message)) { + if ($message instanceof \Throwable) { + // exceptions are string-convertable, thus should be passed as it is to the logger + // if exception instance is given to produce a stack trace, it MUST be in a key named "exception". + $context['exception'] = $message; + } else { + // exceptions may not be serializable if in the call stack somewhere is a Closure + $message = VarDumper::export($message); + } + } + static::getLogger()->log($level, $message, $context); } /** - * 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. - * @param string $message the message to be logged. + * @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); + static::log(LogLevel::DEBUG, $message, $category); } } @@ -392,58 +445,62 @@ public static function trace($message, $category = 'application') * Logs an error message. * An error message is typically logged when an unrecoverable error occurs * during the execution of an application. - * @param string $message the message to be logged. + * @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. */ public static function error($message, $category = 'application') { - static::getLogger()->log($message, Logger::LEVEL_ERROR, $category); + static::log(LogLevel::ERROR, $message, $category); } /** * Logs a warning message. * A warning message is typically logged when an error occurs while the execution * can still continue. - * @param string $message the message to be logged. + * @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. */ public static function warning($message, $category = 'application') { - static::getLogger()->log($message, Logger::LEVEL_WARNING, $category); + static::log(LogLevel::WARNING, $message, $category); } /** * Logs an informative message. * An informative message is typically logged by an application to keep record of * something important (e.g. an administrator logs in). - * @param string $message the message to be logged. + * @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. */ public static function info($message, $category = 'application') { - static::getLogger()->log($message, Logger::LEVEL_INFO, $category); + static::log(LogLevel::INFO, $message, $category); } /** * Marks the beginning of a code block for profiling. + * * This has to be matched with a call to [[endProfile]] with the same category name. * The begin- and end- calls must also be properly nested. For example, * - * ~~~ + * ```php * \Yii::beginProfile('block1'); * // some code to be profiled * \Yii::beginProfile('block2'); * // some other code to be profiled * \Yii::endProfile('block2'); * \Yii::endProfile('block1'); - * ~~~ + * ``` * @param string $token token for the code block * @param string $category the category of this log message * @see endProfile() */ public static function beginProfile($token, $category = 'application') { - static::getLogger()->log($token, Logger::LEVEL_PROFILE_BEGIN, $category); + static::getProfiler()->begin($token, ['category' => $category]); } /** @@ -455,16 +512,7 @@ public static function beginProfile($token, $category = 'application') */ public static function endProfile($token, $category = 'application') { - static::getLogger()->log($token, Logger::LEVEL_PROFILE_END, $category); - } - - /** - * 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 - */ - public static function powered() - { - return 'Powered by Yii Framework'; + static::getProfiler()->end($token, ['category' => $category]); } /** @@ -496,14 +544,14 @@ public static function t($category, $message, $params = [], $language = null) { if (static::$app !== null) { return static::$app->getI18n()->translate($category, $message, $params, $language ?: static::$app->language); - } else { - $p = []; - foreach ((array) $params as $name => $value) { - $p['{' . $name . '}'] = $value; - } + } - return ($p === []) ? $message : strtr($message, $p); + $placeholders = []; + foreach ((array) $params as $name => $value) { + $placeholders['{' . $name . '}'] = $value; } + + return ($placeholders === []) ? $message : strtr($message, $placeholders); } /** diff --git a/CHANGELOG.md b/CHANGELOG.md index 2daef153bd..f352e8c82b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,1110 @@ Yii Framework 2 Change Log ========================== +3.0.0 under development +----------------------- + +- Enh #16285: Modified yii\web\XmlResponseFormatter to accept attributes for XML elements (codespede) +- Bug #16327: Fix getComposer() yii\BaseYii::createObject(null) BaseMailer (cjtterabyte) +- Bug #16065: Remove using `date.timezone` at `yii\base\Application`, use `date_default_timezone_get()` instead (sashsvamir) +- Bug #12539: `yii\filters\ContentNegotiator` now generates 406 'Not Acceptable' instead of 415 'Unsupported Media Type' on content-type negotiation fail (PowerGamer1) +- Bug #14458: Fixed `yii\filters\VerbFilter` uses case-insensitive comparison for the HTTP method name (klimov-paul) +- Enh #879: Caching implementation refactored according to PSR-16 'Simple Cache' specification (klimov-paul) +- Enh #11328: Added support for PSR-7 'HTTP Message' (klimov-paul) +- Enh #14339: Uploaded file retrieve methods have been moved from `yii\http\UploadedFile` to `yii\web\Request` (klimov-paul) +- Enh #4352: Result of `yii\web\Request::getBodyParams()` now includes uploaded files (klimov-paul) +- Enh #14522: `yii\web\Request::getBodyParams()` now generates 415 'Unsupported Media Type' error on invalid or missing 'Content-Type' header (klimov-paul) +- Enh #13799: CAPTCHA rendering logic extracted into `yii\captcha\DriverInterface`, which instance is available via `yii\captcha\CaptchaAction::$driver` field (vladis84, klimov-paul) +- Enh #9137: Added `clearErrors` parameter to `yii\base\Model` `validateMultiple()` method (developeruz) +- Enh #9260: Mail view rendering encapsulated into `yii\mail\Template` class allowing rendering in isolation and access to `yii\mail\MessageInterface` instance via `$this->context->message` inside the view (klimov-paul) +- Enh #11058: Add `$checkAjax` parameter to method `yii\web\Controller::redirect()` which controls redirection in AJAX and PJAX requests (ivanovyordan) +- Enh #12385: Methods `addHeader()`, `setHeader()`, `getHeader()`, `setHeaders()` have been added to `yii\mail\MessageInterface` allowing setup of custom message headers (klimov-paul) +- Enh #12592: Optimized `yii\filters\AccessController` on processing accessrules (dynasource) +- Enh #12938: Allow to pass additional parameters to `yii\base\View::renderDynamic()` (mikehaertl) +- Enh #13006: Added a `/` to the `yii\captcha\Captcha::$captchaAction` string to work correctly in a module also (boehsermoe) +- Enh #13702: Added support for PSR-3 'Logger' (klimov-paul) +- Enh #13706: 'Profiler' layer extracted (klimov-paul) +- Enh #15410: Added serialization abstraction layer under `yii\serialize\*` namespace (klimov-paul) +- Enh #608: Added `yii\web\AssetConverter::$isOutdatedCallback` allowing custom check for outdated asset conversion result (klimov-paul) +- Enh: Objects `yii\helpers\ReplaceArrayValue`, `yii\helpers\UnsetArrayValue` now support restoring after being exported with `var_export()` function (silverfire) +- Chg: Removed methods marked as deprecated in 2.0.x (samdark) +- Chg #8452: Packages 'captcha', 'jquery', 'rest', 'mssql' and 'oracle' have been extracted into extensions (klimov-paul) +- Chg #15383: PJAX support removed (klimov-paul) +- Chg #15383: CUBRID database support removed (klimov-paul) +- Chg #14784: Signature of `yii\web\RequestParserInterface::parse()` changed to accept `yii\web\Request` instance as a sole argument (klimov-paul) +- Chg #10771: Consistent behavior of `run()` method in all framework widgets. All return the result now for better extensibility (pkirill99, cebe) +- Chg #11397: Minimum required version of PHP is 7.1 now (samdark) +- Chg: Removed `yii\base\Object::className()` in favor of native PHP syntax `::class`, which does not trigger autoloading (cebe) +- Chg #12074: Updated `yii\widgets\ActiveField::hint()` method signature to match `label()` (PowerGamer1, samdark) +- Chg #11560: Removed XCache and Zend data cache support as caching backends (samdark) +- Chg #7770: Updated the fallback date formats used when no `intl` extension is installed to match the defaults of the latest ICU version (cebe) +- Chg #13080: Rename `yii\base\InvalidParamException` to `yii\base\InvalidArgumentException` (arogachev) +- Enh #2990: `yii\widgets\ActiveField::hiddenInput()` no longer renders label by default (lennartvdd) +- Chg #9260: Mail message composition extracted into separated class `yii\mail\Composer`, which setup is available via `yii\mail\BaseMailer::$composer` (klimov-paul) +- Chg: Moved masked input field widget into separate extension https://github.com/yiisoft/yii2-maskedinput (samdark) +- Chg #12089: Behavior of `yii\grid\DataColumn::$filterInputOptions` changed when default value is overwritten (bvanleeuwen, cebe) +- Chg #13885: Removed APC support in ApcCache, APCu works as before (samdark) +- Chg #14178: Removed HHVM-specific code (samdark) +- Enh #14671: use `random_int()` instead of `mt_rand()` to generate cryptographically secure pseudo-random integers (yyxx9988) +- Chg #14761: Removed Yii autoloader in favor of Composer's PSR-4 implementation (samdark) +- Chg #15448: Package "ezyang/htmlpurifier" has been made optional and is not installed by default (klimov-paul) +- Chg #15481: Removed `yii\BaseYii::powered()` method (Kolyunya, samdark) +- Chg #15811: Fixed issue with additional parameters on `yii\base\View::renderDynamic()` while parameters contains single quote introduced in #12938 (xicond) +- Enh #16054: Callback execution with mutex synchronization (zhuravljov) +- Enh #16126: Allows to configure `Connection::dsn` by config array (leandrogehlen) +- Chg #11397: `yii\i18n\MessageFormatter` polyfills and `yii\i18n\MessageFormatter::parse()` method were removed resulting in performance boost. See UPGRADE for compatibility notes (samdark) +- Chg #16247: Cloning components will now clone their behaviors as well (brandonkelly) + +2.0.14.2 under development +------------------------ + +- Bug #15801: Fixed `has-error` CSS class assignment in `yii\widgets\ActiveField` when attribute name is prefixed with tabular index (FabrizioCaldarelli) +2.0.16 under development +------------------------ + +- Enh #9133: Added `yii\behaviors\OptimisticLockBehavior` (tunecino) +- Bug #16193: Fixed `yii\filters\Cors` to not reflect origin header value when configured to wildcard origins (Jianjun Chen) +- Bug #16068: Fixed `yii\web\CookieCollection::has` when an expiration param is set to 'until the browser is closed' (OndrejVasicek) +- Bug #16006: Handle case when `X-Forwarded-Host` header have multiple hosts separated with a comma (pgaultier) +- Bug #16010: Fixed `yii\filters\ContentNegotiator` behavior when GET parameters contain an array (rugabarbo) +- Bug #14660: Fixed `yii\caching\DbCache` concurrency issue when set values with the same key (rugabarbo) +- Bug #15988: Fixed bash completion (alekciy) +- Bug #15798: Fixed render `yii\grid\RadioButtonColumn::$content` and `yii\grid\CheckboxColumn::$content` (lesha724) +- Bug #15117: Fixed `yii\db\Schema::getTableMetadata` cache refreshing (boboldehampsink) +- Bug #15875: afterSave for new models flushes unsaved data (shirase) +- Bug #16073: Fixed regression in Oracle `IN` condition builder for more than 1000 items (cebe) +- Bug #16120: FileCache: rebuild cache file before touch when different file owner (Slamdunk) +- Bug #16091: Make `yii\test\InitDbFixture` work with non-SQL DBMS (cebe) +- Bug #16184: Fixed `yii\base\Widget` to access `stack` property with `self` instead of `static` (yanggs07) +- Bug #16039: Fixed implicit conversion from `char` to `varbinnary` in MSSQL (vsivsivsi) +- Bug #16217: Fixed `yii\console\controllers\HelpController` to work well in Windows environment (samdark) +- Bug #14636: Views can now use relative paths even when using themed views (sammousa) +- Bug #16245: Fixed `__isset()` in `BaseActiveRecord` not catching errors (sammousa) +- Bug #16266: Fixed `yii\helpers\BaseStringHelper` where explode would not allow 0 as trim string (Thoulah) +- Enh #16191: Enhanced `yii\helpers\Inflector` to work correctly with UTF-8 (silverfire) +- Bug: Fixed bad instnaceof check in `yii\db\Schema::getTableMetadata()` (samdark) + + +2.0.15.1 March 21, 2018 +----------------------- + +- Bug #15931: `yii\db\ActiveRecord::findOne()` now accepts column names prefixed with table name (cebe) + + +2.0.15 March 20, 2018 +--------------------- + +- Bug #15688: (CVE-2018-7269): Fixed possible SQL injection through `yii\db\ActiveRecord::findOne()`, `::findAll()` (analitic1983, silverfire, cebe) +- Bug #15878: Fixed migration with a comment containing an apostrophe (MarcoMoreno) + + +2.0.14.2 March 13, 2018 +----------------------- + +- Bug #15776: Fixed slow MySQL constraints retrieving (MartijnHols, berosoboy, sergeymakinen) +- Bug #15783: Regenerate CSRF token only when logging in directly (samdark) +- Bug #15792: Added missing `yii\db\QueryBuilder::conditionClasses` setter (silverfire) +- Bug #15801: Fixed `has-error` CSS class assignment in `yii\widgets\ActiveField` when attribute name is prefixed with tabular index (FabrizioCaldarelli) +- Bug #15804: Fixed `null` values handling for PostgresSQL arrays (silverfire) +- Bug #15817: Fixed support of deprecated array format type casting in `yii\db\Command::bindValues()` (silverfire) +- Bug #15822: Fixed `yii\base\Component::off()` not to throw an exception when handler does not exist (silverfire) +- Bug #15829: Fixed JSONB support in PostgreSQL 9.4 (silverfire) +- Bug #15836: Fixed nesting of `yii\db\ArrayExpression`, `yii\db\JsonExpression` (silverfire) +- Bug #15839: Fixed `yii\db\mysql\JsonExpressionBuilder` to cast JSON explicitly (silverfire) +- Bug #15840: Fixed regression on load fixture data file (leandrogehlen) +- Bug #15858: Fixed `Undefined offset` error calling `yii\helpers\Html::errorSummary()` with the same error messages for different model attributes (FabrizioCaldarelli, silverfire) +- Bug #15863: Fixed saving of `null` attribute value for JSON and Array columns in MySQL and PostgreSQL (silverfire) +- Bug: Fixed encoding of empty `yii\db\ArrayExpression` for PostgreSQL (silverfire) +- Bug: Fixed table schema retrieving for PostgreSQL when the table name was wrapped in quotes (silverfire) + + +2.0.14.1 February 24, 2018 +-------------------------- + +- Bug #15318: Fixed `session_name(): Cannot change session name when session is active` errors (bscheshirwork, samdark) +- Bug #15678: Fixed `resetForm()` method in `yii.activeForm.js` which used an undefined variable (Izumi-kun) +- Bug #15692: Fix `yii\validators\ExistValidator` to respect filter when `targetRelation` is used (developeruz) +- Bug #15693: Fixed `yii\filters\auth\HttpHeaderAuth` to work correctly when pattern is set but was not matched (bboure) +- Bug #15696: Fix magic getter for `yii\db\ActiveRecord` (developeruz) +- Bug #15707: Fixed JSON retrieving from MySQL (silverfire) +- Bug #15708: Fixed `yii\db\Command::upsert()` for Cubrid/MSSQL/Oracle (sergeymakinen) +- Bug #15724: Changed shortcut in `yii\console\controllers\BaseMigrateController` for `comment` option from `-c` to `-C` due to conflict (Izumi-kun) +- Bug #15726: Fix ExistValidator is broken for NOSQL (developeruz) +- Bug #15728, #15731: Fixed BC break in `Query::select()` method (silverfire) +- Bug #15742: Updated `yii\helpers\BaseHtml::setActivePlaceholder()` to be consistent with `activeLabel()` (edwards-sj) +- Enh #15716: Added `disableJsonSupport` to MySQL and PgSQL `ColumnSchema`, `disableArraySupport` and `deserializeArrayColumnToArrayExpression` to PgSQL `ColumnSchema` (silverfire) +- Enh #15716: Implemented `\Traversable` in `yii\db\ArrayExpression` (silverfire) +- Enh #15760: Added `ArrayAccess` support as validated value in `yii\validators\EachValidator` (silverfire) + + +2.0.14 February 18, 2018 +------------------------ + +- Bug #8983: Only truncate the original log file for rotation (matthewyang, developeruz) +- Bug #9342: Fixed `yii\db\ActiveQueryTrait` to apply `indexBy` after relations population in order to prevent excess queries (sammousa, silverfire) +- Bug #11401: Fixed `yii\web\DbSession` concurrency issues when writing and regenerating IDs (samdark, andreasanta, cebe) +- Bug #13034: Fixed `normalizePath` for windows network shares that start with two backslashes (developeruz) +- Bug #14135: Fixed `yii\web\Request::getBodyParam()` crashes on object type body params (klimov-paul) +- Bug #14157: Add support for loading default value `CURRENT_TIMESTAMP` of MySQL `datetime` field (rossoneri) +- Bug #14276: Fixed I18N format with dotted parameters (developeruz) +- Bug #14296: Fixed log targets to throw exception in case log can not be properly exported (bizley) +- Bug #14484: Fixed `yii\validators\UniqueValidator` for target classes with a default scope (laszlovl, developeruz) +- Bug #14604: Fixed `yii\validators\CompareValidator` `compareAttribute` does not work if `compareAttribute` form ID has been changed (mikk150) +- Bug #14711: (CVE-2018-6010): Fixed `yii\web\ErrorHandler` displaying exception message in non-debug mode (samdark) +- Bug #14811: Fixed `yii\filters\HttpCache` to work with PHP 7.2 (samdark) +- Bug #14859: Fixed OCI DB `defaultSchema` failure when `masterConfig` is used (lovezhl456) +- Bug #14903: Fixed route with extra dashes is executed controller while it should not (developeruz) +- Bug #14916: Fixed `yii\db\Query::each()` iterator key starts from 1 instead of 0 (Vovan-VE) +- Bug #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) +- Bug #15628: Fixed `yii\validators\DateValidator` to respect time when the `format` property is set to UNIX Epoch format (silverfire, gayHacker) +- Bug #15644: Avoid wrong default selection on a dropdown, checkbox list, and radio list, when a option has a key equals to zero (berosoboy) +- Bug #15658: Fixed `yii\filters\auth\HttpBasicAuth` not to switch identity, when user is already authenticated and identity does not get changed (silverfire) +- Bug #15662: Fixed `yii\log\FileTarget` not to create log directory during init process (alexeevdv) +- Enh #3087: Added `yii\helpers\BaseHtml::error()` "errorSource" option to be able to customize errors display (yanggs07, developeruz, silverfire) +- Enh #3250: Added support for events partial wildcard matching (klimov-paul) +- Enh #5515: Added default value for `yii\behaviors\BlameableBehavior` for cases when the user is guest (dmirogin) +- Enh #6844: `yii\base\ArrayableTrait::toArray()` now allows recursive `$fields` and `$expand` (bboure) +- Enh #7640: Implemented custom data types support. Added JSON support for MySQL and PostgreSQL, array support for PostgreSQL (silverfire, cebe) +- Enh #7988: Added `\yii\helpers\Console::errorSummary()` and `\yii\helpers\Json::errorSummary()` (developeruz) +- Enh #7996: Short syntax for verb in GroupUrlRule (schojniak, developeruz) +- Enh #8092: ExistValidator for relations (developeruz) +- Enh #8527: Added `yii\i18n\Locale` component having `getCurrencySymbol()` method (amarox, samdark) +- Enh #8752: Allow specify `$attributeNames` as a string for `yii\base\Model` `validate()` method (developeruz) +- Enh #9137: Added `Access-Control-Allow-Method` header for the OPTIONS request (developeruz) +- Enh #9253: Allow `variations` to be a string for `yii\filters\PageCache` and `yii\widgets\FragmentCache` (schojniak, developeruz) +- Enh #9771: Assign hidden input with its own set of HTML options via `$hiddenOptions` in activeFileInput `$options` (HanafiAhmat) +- Enh #10186: Use native `hash_equals` in `yii\base\Security::compareString()` if available, throw exception if non-strings are compared (aotd1, samdark) +- Enh #11611: Added `BetweenColumnsCondition` to build SQL condition like `value BETWEEN col1 and col2` (silverfire) +- Enh #12623: Added `yii\helpers\StringHelper::matchWildcard()` replacing usage of `fnmatch()`, which may be unreliable (klimov-paul) +- Enh #13019: Support JSON in SchemaBuilderTrait (zhukovra, undefinedor) +- Enh #13425: Added caching of dynamically added URL rules with `yii\web\UrlManager::addRules()` (scriptcube, silverfire) +- Enh #13465: Added `yii\helpers\FileHelper::findDirectories()` method (ArsSirek, developeruz) +- Enh #13618: Active Record now resets related models after corresponding attributes updates (Kolyunya, rob006) +- Enh #13679: Added `yii\behaviors\CacheableWidgetBehavior` (Kolyunya) +- Enh #13814: MySQL unique index names can now contain spaces (df2) +- Enh #13879: Added upsert support for `yii\db\QueryBuilder`, `yii\db\Command`, and `yii\db\Migration` (sergeymakinen) +- Enh #13919: Added option to add comment for created table to migration console command (mixartemev, developeruz) +- Enh #13996: Added `yii\web\View::registerJsVar()` method that allows registering JavaScript variables (Eseperio, samdark) +- Enh #14043: Added `yii\helpers\IpHelper` (silverfire, cebe) +- Enh #14254: add an option to specify whether validator is forced to always use master DB for `yii\validators\UniqueValidator` and `yii\validators\ExistValidator` (rossoneri, samdark) +- Enh #14355: Added ability to pass an empty array as a parameter in console command (developeruz) +- Enh #14488: Added support for X-Forwarded-Host to `yii\web\Request`, fixed `getServerPort()` usage (si294r, samdark) +- Enh #14538: Added `yii\behaviors\AttributeTypecastBehavior::typecastAfterSave` property (littlefuntik, silverfire) +- Enh #14546: Added `dataDirectory` property into `BaseActiveFixture` (leandrogehlen) +- Enh #14568: Refactored migration templates to use `safeUp()` and `safeDown()` methods (Kolyunya) +- Enh #14638: Added `yii\db\SchemaBuilderTrait::tinyInteger()` (rob006) +- Enh #14643: Added `yii\web\ErrorAction::$layout` property to conveniently set layout from error action config (swods, cebe, samdark) +- Enh #14662: Added support for custom `Content-Type` specification to `yii\web\JsonResponseFormatter` (Kolyunya) +- Enh #14732, #11218, #14810, #10855: It is now possible to pass `yii\db\Query` anywhere, where `yii\db\Expression` was supported (silverfire) +- Enh #14806: Added $placeFooterAfterBody option for GridView (terehru) +- Enh #15024: `yii\web\Pjax` widget does not prevent CSS files from sending anymore because they are handled by client-side plugin correctly (onmotion) +- Enh #15047: `yii\db\Query::select()` and `yii\db\Query::addSelect()` now check for duplicate column names (wapmorgan) +- Enh #15076: Improve `yii\db\QueryBuilder::buildColumns()` to throw exception on invalid input (hiscaler) +- Enh #15120: Refactored dynamic caching introducing `DynamicContentAwareInterface` and `DynamicContentAwareTrait` (sergeymakinen) +- Enh #15135: Automatic completion for help in bash and zsh (Valkeru) +- Enh #15216: Added `yii\web\ErrorHandler::$traceLine` to allow opening file at line clicked in IDE (vladis84) +- Enh #15219: Added `yii\filters\auth\HttpHeaderAuth` (bboure) +- Enh #15221: Added support for specifying `--camelCase` console options in `--kebab-case` (brandonkelly) +- Enh #15221: Added support for the `--