diff --git a/docs/guide-ru/test-environment-setup.md b/docs/guide-ru/test-environment-setup.md index 84311969c4..bc537476cf 100644 --- a/docs/guide-ru/test-environment-setup.md +++ b/docs/guide-ru/test-environment-setup.md @@ -47,3 +47,25 @@ Changed current directory to путём запуска команды `codecept` без указания пути. Тем не менее, данный подход может не подойти. К примеру, в двух разных проектах может потребоваться установить разные версии Codeception. Для простоты все команды в разделах про тестирование используются так, будто Codeception установлен глобально. + +### Настройка веб-сервера Apache + +Если вы используете Apache и настроили его как описано в разделе «[Установка Yii](start-installation.md)», то для тестов вам необходимо создать отдельный виртуальный хост который будет работать с той же папкой, но использовать входной скрипт `index-test.php`: +``` + + DocumentRoot "path/to/basic/webb" + ServerName mysite-test + + Order Allow,Deny + Allow from all + AddDefaultCharset utf-8 + DirectoryIndex index-test.php + RewriteEngine on + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule . index-test.php + + +``` +Так мы укажем веб серверу перенаправлять все запросы на скрипт `index-test.php`. +> Note: Обратите внимание, что здесь мы указываем параметр `DirectoryIndex`, помимо тех параметров, которые были указаны для первого хоста. Это сделано с той целью, чтобы при обращении к главной странице по адресу `mysite-test` также использовался бы скрипт `index-test.php`. diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index a8563baa4b..2bc113f1de 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -4,6 +4,14 @@ Yii Framework 2 Change Log 2.0.12 under development -------------------------- +- Bug #14052: Fixed processing parse errors on PHP 7 since these are instances of `\ParseError` (samdark) +- Bug #13790: Fixed error in `\yii\widgets\MaskedInput` JavaScript by raising version required (samdark) +- Bug #14033: Fixed `yii\filters\AccessRule::matchIp()` erroring in case IP is not defined under HHVM (Kolyunya) +- Bug #13848: `yii\di\Instance::ensure()` wasn't throwing an exception when `$type` is specified and `$reference` object isn't instance of `$type` (c-jonua) +- Enh #13226: `yii cache` command now warns about the fact that it's not able to flush APC cache from console (samdark) +- Bug #13689: Fixed handling of errors in closures (mikehaertl) +- Bug #11719: Fixed `yii\db\Connection::$enableQueryCache` caused infinite loop when the same connection was used for `yii\caching\DbCache` (michaelarnauts) +- Bug #10346: Fixed "DOMException: Invalid Character Error" in `yii\web\XmlResponseFormatter::buildXml()` (sasha-ch) - Bug #13694: `yii\widgets\Pjax` now sends `X-Pjax-Url` header with response to fix redirect (wleona3, Faryshta) - Bug #14012: `yii\db\pgsql\Schema::findViewNames()` was skipping materialized views (insolita) - Bug #13362: Fixed return value of `yii\caching\MemCache::setValues()` (masterklavi) @@ -13,7 +21,7 @@ Yii Framework 2 Change Log - Bug #13671: Fixed error handler trace to work correctly with XDebug (samdark) - Bug #13657: Fixed `yii\helpers\StringHelper::truncateHtml()` skip extra tags at the end (sam002) - Bug #7946: Fixed a bug when the `form` attribute was not propagated to the hidden input of the checkbox (Kolyunya) -- Bug #13087: Fixed getting active validators for safe attribute (developeruz) +- Bug #13087: Fixed getting active validators for safe attribute (developeruz, klimov-paul) - Bug #13571: Fix `yii\db\mssql\QueryBuilder::checkIntegrity` for all tables (boboldehampsink) - Bug #11230: Include `defaultRoles` in `yii\rbac\DbManager->getRolesByUser()` results (developeruz) - Enh #13243: Added support for unicode attribute names in `yii\widgets\DetailView` (arogachev) @@ -33,6 +41,7 @@ Yii Framework 2 Change Log - Bug #8120: Fixes LIKE special characters escaping for Cubrid/MSSQL/Oracle/SQLite in `yii\db\QueryBuilder` (sergeymakinen) - Bug #12715: Exception `SAVEPOINT LEVEL1 does not exist` instead of deadlock exception (Vovan-VE) - Enh #8641: Enhanced `yii\console\Request::resolve()` to prevent passing parameters, that begin from digits (silverfire) +- Enh #13179: Added `yii\data\Sort::parseSortParam` allowing to customize sort param in descendant class (leandrogehlen) - Enh #13278: `yii\caching\DbQueryDependency` created allowing specification of the cache dependency via `yii\db\QueryInterface` (klimov-paul) - Enh #13467: `yii\data\ActiveDataProvider` no longer queries models if models count is zero (kLkA, Kolyunya) - Enh #13523: Plural rule for pasta (developeruz) @@ -75,6 +84,7 @@ Yii Framework 2 Change Log - Enh #13981: `yii\caching\Cache::getOrSet()` now supports both `Closure` and `callable` (silverfire) - Enh #13911: Significantly enhanced MSSQL schema reading performance (paulzi, WebdevMerlion) - Bug #14072: Fixed a bug where `\yii\db\Command::createTable()`, `addForeignKey()`, `dropForeignKey()`, `addCommentOnColumn()`, and `dropCommentFromColumn()` weren't refreshing the table cache on `yii\db\Schema` (brandonkelly) +- Enh #14059: Removed unused AR instantiating for calling of static methods (ElisDN) 2.0.11.2 February 08, 2017 -------------------------- diff --git a/framework/caching/DbCache.php b/framework/caching/DbCache.php index cecc8d6360..0bae1abfcc 100644 --- a/framework/caching/DbCache.php +++ b/framework/caching/DbCache.php @@ -136,9 +136,8 @@ class DbCache extends Cache $this->db->enableQueryCache = true; return $result; - } else { - return $query->createCommand($this->db)->queryScalar(); } + return $query->createCommand($this->db)->queryScalar(); } /** @@ -187,19 +186,21 @@ class DbCache extends Cache */ protected function setValue($key, $value, $duration) { - $command = $this->db->createCommand() - ->update($this->cacheTable, [ - 'expire' => $duration > 0 ? $duration + time() : 0, - 'data' => [$value, \PDO::PARAM_LOB], - ], ['id' => $key]); + $result = $this->db->noCache(function (Connection $db) use ($key, $value, $duration) { + $command = $db->createCommand() + ->update($this->cacheTable, [ + 'expire' => $duration > 0 ? $duration + time() : 0, + 'data' => [$value, \PDO::PARAM_LOB], + ], ['id' => $key]); + return $command->execute(); + }); - if ($command->execute()) { + if ($result) { $this->gc(); return true; - } else { - return $this->addValue($key, $value, $duration); } + return $this->addValue($key, $value, $duration); } /** @@ -216,12 +217,14 @@ class DbCache extends Cache $this->gc(); try { - $this->db->createCommand() - ->insert($this->cacheTable, [ - 'id' => $key, - 'expire' => $duration > 0 ? $duration + time() : 0, - 'data' => [$value, \PDO::PARAM_LOB], - ])->execute(); + $this->db->noCache(function (Connection $db) use ($key, $value, $duration) { + $db->createCommand() + ->insert($this->cacheTable, [ + 'id' => $key, + 'expire' => $duration > 0 ? $duration + time() : 0, + 'data' => [$value, \PDO::PARAM_LOB], + ])->execute(); + }); return true; } catch (\Exception $e) { @@ -237,9 +240,11 @@ class DbCache extends Cache */ protected function deleteValue($key) { - $this->db->createCommand() - ->delete($this->cacheTable, ['id' => $key]) - ->execute(); + $this->db->noCache(function (Connection $db) use ($key) { + $db->createCommand() + ->delete($this->cacheTable, ['id' => $key]) + ->execute(); + }); return true; } diff --git a/framework/composer.json b/framework/composer.json index 2d00889c11..8bbb8c7768 100644 --- a/framework/composer.json +++ b/framework/composer.json @@ -71,7 +71,7 @@ "ezyang/htmlpurifier": "~4.6", "cebe/markdown": "~1.0.0 | ~1.1.0", "bower-asset/jquery": "2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable", - "bower-asset/jquery.inputmask": "~3.2.2 | ~3.3.3", + "bower-asset/jquery.inputmask": "~3.2.2 | ~3.3.5", "bower-asset/punycode": "1.3.*", "bower-asset/yii2-pjax": "~2.0.1" }, diff --git a/framework/console/controllers/CacheController.php b/framework/console/controllers/CacheController.php index 1f393103d7..9d65f8d381 100644 --- a/framework/console/controllers/CacheController.php +++ b/framework/console/controllers/CacheController.php @@ -8,6 +8,7 @@ namespace yii\console\controllers; use Yii; +use yii\caching\ApcCache; use yii\console\Controller; use yii\caching\Cache; use yii\helpers\Console; @@ -32,7 +33,7 @@ use yii\console\Exception; * configured are different from web application, web application cache won't be cleared. In order to fix it please * duplicate web application cache components in console config. You can use any component names. * - * Both APC and OpCache aren't shared between PHP processes so flushing cache from command line has no effect on web. + * APC is not shared between PHP processes so flushing cache from command line has no effect on web. * Flushing web cache could be either done by: * * - Putting a php file under web root and calling it via HTTP @@ -99,7 +100,7 @@ class CacheController extends Controller $cachesInfo[] = [ 'name' => $name, 'class' => $class, - 'is_flushed' => Yii::$app->get($name)->flush(), + 'is_flushed' => $this->canBeFlushed($class) ? Yii::$app->get($name)->flush() : false, ]; } @@ -123,7 +124,7 @@ class CacheController extends Controller $cachesInfo[] = [ 'name' => $name, 'class' => $class, - 'is_flushed' => Yii::$app->get($name)->flush(), + 'is_flushed' => $this->canBeFlushed($class) ? Yii::$app->get($name)->flush() : false, ]; } @@ -178,7 +179,11 @@ class CacheController extends Controller $this->stdout("The following caches were found in the system:\n\n", Console::FG_YELLOW); foreach ($caches as $name => $class) { - $this->stdout("\t* $name ($class)\n", Console::FG_GREEN); + if ($this->canBeFlushed($class)) { + $this->stdout("\t* $name ($class)\n", Console::FG_GREEN); + } else { + $this->stdout("\t* $name ($class) - can not be flushed via console\n", Console::FG_YELLOW); + } } $this->stdout("\n"); @@ -256,7 +261,7 @@ class CacheController extends Controller $findAll = ($cachesNames === []); foreach ($components as $name => $component) { - if (!$findAll && !in_array($name, $cachesNames)) { + if (!$findAll && !in_array($name, $cachesNames, true)) { continue; } @@ -281,4 +286,14 @@ class CacheController extends Controller { return is_subclass_of($className, Cache::className()); } + + /** + * Checks if cache of a certain class can be flushed + * @param string $className class name. + * @return bool + */ + private function canBeFlushed($className) + { + return !is_a($className, ApcCache::className(), true) || php_sapi_name() !== "cli"; + } } diff --git a/framework/data/DataProviderInterface.php b/framework/data/DataProviderInterface.php index 1eed8824c2..71ed1febfb 100644 --- a/framework/data/DataProviderInterface.php +++ b/framework/data/DataProviderInterface.php @@ -66,7 +66,7 @@ interface DataProviderInterface public function getSort(); /** - * @return Pagination the pagination object. If this is false, it means the pagination is disabled. + * @return Pagination|false the pagination object. If this is false, it means the pagination is disabled. */ public function getPagination(); } diff --git a/framework/data/Sort.php b/framework/data/Sort.php index 6049af4ea4..30185dbe14 100644 --- a/framework/data/Sort.php +++ b/framework/data/Sort.php @@ -243,8 +243,8 @@ class Sort extends Object $request = Yii::$app->getRequest(); $params = $request instanceof Request ? $request->getQueryParams() : []; } - if (isset($params[$this->sortParam]) && is_scalar($params[$this->sortParam])) { - $attributes = explode($this->separator, $params[$this->sortParam]); + if (isset($params[$this->sortParam])) { + $attributes = $this->parseSortParam($params[$this->sortParam]); foreach ($attributes as $attribute) { $descending = false; if (strncmp($attribute, '-', 1) === 0) { @@ -268,6 +268,33 @@ class Sort extends Object return $this->_attributeOrders; } + /** + * Parses the value of [[sortParam]] into an array of sort attributes. + * + * The format must be the attribute name only for ascending + * or the attribute name prefixed with `-` for descending. + * + * For example the following return value will result in ascending sort by + * `category` and descending sort by `created_at`: + * + * ```php + * [ + * 'category', + * '-created_at' + * ] + * ``` + * + * @param string $param the value of the [[sortParam]]. + * @return array the valid sort attributes. + * @since 2.0.12 + * @see $separator for the attribute name separator. + * @see $sortParam + */ + protected function parseSortParam($param) + { + return is_scalar($param) ? explode($this->separator, $param) : []; + } + /** * Sets up the currently sort information. * @param array|null $attributeOrders sort directions indexed by attribute names. diff --git a/framework/data/SqlDataProvider.php b/framework/data/SqlDataProvider.php index e0062ba4df..0b894d355e 100644 --- a/framework/data/SqlDataProvider.php +++ b/framework/data/SqlDataProvider.php @@ -163,6 +163,9 @@ class SqlDataProvider extends BaseDataProvider */ protected function prepareTotalCount() { - return (new Query())->from(['sub' => "({$this->sql})"])->count('*', $this->db); + return (new Query([ + 'from' => ['sub' => "({$this->sql})"], + 'params' => $this->params + ]))->count('*', $this->db); } } diff --git a/framework/db/mssql/QueryBuilder.php b/framework/db/mssql/QueryBuilder.php index 6cccd40d8d..f8cfa33ea5 100644 --- a/framework/db/mssql/QueryBuilder.php +++ b/framework/db/mssql/QueryBuilder.php @@ -276,9 +276,8 @@ class QueryBuilder extends \yii\db\QueryBuilder if (!$modelClass) { return null; } - /* @var $model \yii\db\ActiveRecord */ - $model = new $modelClass; - $schema = $model->getTableSchema(); + /* @var $modelClass \yii\db\ActiveRecord */ + $schema = $modelClass::getTableSchema(); return array_keys($schema->columns); } diff --git a/framework/di/Instance.php b/framework/di/Instance.php index b4d6dea278..2300cc4e94 100644 --- a/framework/di/Instance.php +++ b/framework/di/Instance.php @@ -116,7 +116,12 @@ class Instance $container = Yii::$container; } unset($reference['class']); - return $container->get($class, [], $reference); + $component = $container->get($class, [], $reference); + if ($type === null || $component instanceof $type) { + return $component; + } else { + throw new InvalidConfigException('Invalid data type: ' . $class .'. ' . $type . ' is expected.'); + } } elseif (empty($reference)) { throw new InvalidConfigException('The required component is not specified.'); } diff --git a/framework/filters/AccessRule.php b/framework/filters/AccessRule.php index 4eacf57942..0d859e9091 100644 --- a/framework/filters/AccessRule.php +++ b/framework/filters/AccessRule.php @@ -115,9 +115,9 @@ class AccessRule extends Component && $this->matchCustom($action) ) { return $this->allow ? true : false; - } else { - return null; } + + return null; } /** @@ -165,7 +165,7 @@ class AccessRule extends Component } /** - * @param string $ip the IP address + * @param string|null $ip the IP address * @return bool whether the rule applies to the IP address */ protected function matchIP($ip) @@ -174,7 +174,14 @@ class AccessRule extends Component return true; } foreach ($this->ips as $rule) { - if ($rule === '*' || $rule === $ip || (($pos = strpos($rule, '*')) !== false && !strncmp($ip, $rule, $pos))) { + if ($rule === '*' || + $rule === $ip || + ( + $ip !== null && + ($pos = strpos($rule, '*')) !== false && + strncmp($ip, $rule, $pos) === 0 + ) + ) { return true; } } diff --git a/framework/messages/sk/yii.php b/framework/messages/sk/yii.php index ac2269b530..ef89a0f5db 100644 --- a/framework/messages/sk/yii.php +++ b/framework/messages/sk/yii.php @@ -79,7 +79,7 @@ return [ '{attribute} must be a string.' => '{attribute} musí byť reťazec.', '{attribute} must be a valid IP address.' => '{attribute} musí byť platná IP adresa.', '{attribute} must be an IP address with specified subnet.' => '{attribute} musí byť IP adresa so špecifikovanou podsieťou.', - '{attribute} must be an integer.' => '{attribute} musí byť integer.', + '{attribute} must be an integer.' => '{attribute} musí byť celé číslo.', '{attribute} must be either "{true}" or "{false}".' => '{attribute} musí byť "{true}" alebo "{false}".', '{attribute} must be equal to "{compareValueOrAttribute}".' => '{attribute} musí byť "{compareValueOrAttribute}".', '{attribute} must be greater than "{compareValueOrAttribute}".' => '{attribute} musí byť väčšie ako "{compareValueOrAttribute}".', diff --git a/framework/rbac/BaseManager.php b/framework/rbac/BaseManager.php index 879215f831..27e9c874cb 100644 --- a/framework/rbac/BaseManager.php +++ b/framework/rbac/BaseManager.php @@ -194,7 +194,7 @@ abstract class BaseManager extends Component implements ManagerInterface * @since 2.0.12 * @return Role[] default roles. The array is indexed by the role names */ - public function getDefaultRoles() + public function getDefaultRoleInstances() { $result = []; foreach ($this->defaultRoles as $roleName) { diff --git a/framework/rbac/DbManager.php b/framework/rbac/DbManager.php index 3f7becb073..473cbb147d 100644 --- a/framework/rbac/DbManager.php +++ b/framework/rbac/DbManager.php @@ -466,7 +466,7 @@ class DbManager extends BaseManager ->andWhere(['a.user_id' => (string) $userId]) ->andWhere(['b.type' => Item::TYPE_ROLE]); - $roles = $this->getDefaultRoles(); + $roles = $this->getDefaultRoleInstances(); foreach ($query->all($this->db) as $row) { $roles[$row['name']] = $this->populateItem($row); } diff --git a/framework/rbac/PhpManager.php b/framework/rbac/PhpManager.php index d489a751fd..26eccaa677 100644 --- a/framework/rbac/PhpManager.php +++ b/framework/rbac/PhpManager.php @@ -392,7 +392,7 @@ class PhpManager extends BaseManager */ public function getRolesByUser($userId) { - $roles = $this->getDefaultRoles(); + $roles = $this->getDefaultRoleInstances(); foreach ($this->getAssignments($userId) as $name => $assignment) { $role = $this->items[$assignment->roleName]; if ($role->type === Item::TYPE_ROLE) { diff --git a/framework/test/BaseActiveFixture.php b/framework/test/BaseActiveFixture.php index 7213876254..e3b60920ee 100644 --- a/framework/test/BaseActiveFixture.php +++ b/framework/test/BaseActiveFixture.php @@ -65,10 +65,8 @@ abstract class BaseActiveFixture extends DbFixture implements \IteratorAggregate $row = $this->data[$name]; /* @var $modelClass \yii\db\ActiveRecord */ $modelClass = $this->modelClass; - /* @var $model \yii\db\ActiveRecord */ - $model = new $modelClass; $keys = []; - foreach ($model->primaryKey() as $key) { + foreach ($modelClass::primaryKey() as $key) { $keys[$key] = isset($row[$key]) ? $row[$key] : null; } diff --git a/framework/validators/Validator.php b/framework/validators/Validator.php index f6543807f9..2915d310c4 100644 --- a/framework/validators/Validator.php +++ b/framework/validators/Validator.php @@ -47,6 +47,8 @@ use yii\base\NotSupportedException; * - `ip`: [[IpValidator]] * * For more details and usage information on Validator, see the [guide article on validators](guide:input-validation). + * + * @property array $attributeNames cleaned attribute names without the `!` character at the beginning. This property is read-only. * * @author Qiang Xue * @since 2.0 @@ -101,11 +103,6 @@ class Validator extends Component * please specify them as an array; for single attribute, you may use either a string or an array. */ public $attributes = []; - /** - * @var array cleaned attribute names. Contains attribute names without `!` character at the beginning - * @since 2.0.12 - */ - private $_attributeNames = []; /** * @var string the user-defined error message. It may contain the following placeholders which * will be replaced accordingly by the validator: @@ -238,7 +235,6 @@ class Validator extends Component $this->attributes = (array) $this->attributes; $this->on = (array) $this->on; $this->except = (array) $this->except; - $this->setAttributeNames((array)$this->attributes); } /** @@ -459,23 +455,13 @@ class Validator extends Component /** * Returns cleaned attribute names without the `!` character at the beginning - * @return array + * @return array attribute names. * @since 2.0.12 */ public function getAttributeNames() { - return $this->_attributeNames; - } - - /** - * Saves attribute names without `!` character at the beginning - * @param array $attributeNames - * @since 2.0.12 - */ - private function setAttributeNames($attributeNames) - { - $this->_attributeNames = array_map(function($attribute) { + return array_map(function($attribute) { return ltrim($attribute, '!'); - }, $attributeNames); + }, $this->attributes); } } diff --git a/framework/web/ErrorHandler.php b/framework/web/ErrorHandler.php index bf9e66207a..f01d6d083d 100644 --- a/framework/web/ErrorHandler.php +++ b/framework/web/ErrorHandler.php @@ -191,7 +191,7 @@ class ErrorHandler extends \yii\base\ErrorHandler $url = null; $shouldGenerateLink = true; - if ($method !== null) { + if ($method !== null && substr_compare($method, '{closure}', -9) !== 0) { $reflection = new \ReflectionMethod($class, $method); $shouldGenerateLink = $reflection->isPublic() || $reflection->isProtected(); } @@ -306,11 +306,11 @@ class ErrorHandler extends \yii\base\ErrorHandler /** * Renders call stack. - * @param \Exception $exception exception to get call stack from + * @param \Exception|\ParseError $exception exception to get call stack from * @return string HTML content of the rendered call stack. * @since 2.0.12 */ - public function renderCallStack(\Exception $exception) + public function renderCallStack($exception) { $out = '