From 5488fc6e289052feca388150485c8493e3baba6e Mon Sep 17 00:00:00 2001 From: "p.chapl" Date: Mon, 19 Dec 2016 16:24:37 +0700 Subject: [PATCH 01/75] fixes #13221 Make \yii\db\QueryTrait::limit and \yii\db\QueryTrait::offset methods works with \yii\db\Expression --- framework/CHANGELOG.md | 1 + framework/db/QueryBuilder.php | 5 ++--- framework/db/QueryTrait.php | 11 ++++++----- framework/db/mysql/QueryBuilder.php | 19 +++++++++++++++++++ tests/framework/db/QueryTest.php | 16 ++++++++++++++++ tests/framework/db/mysql/QueryTest.php | 17 +++++++++++++++++ 6 files changed, 61 insertions(+), 8 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 23ed653c49..e8b30c832f 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -85,6 +85,7 @@ Yii Framework 2 Change Log - Bug: #12969: Improved unique ID generation for `yii\widgets\Pjax` widgets (dynasource, samdark, rob006) - Enh #13122: Optimized query for information about foreign keys in `yii\db\oci` (zlakomanoff) - Enh #13202: Refactor validateAttribute method in UniqueValidator (developeruz) +- Enh #13221: Make \yii\db\QueryTrait::limit and \yii\db\QueryTrait::offset methods works with \yii\db\Expression (Ni-san) - Enh: Added constants for specifying `yii\validators\CompareValidator::$type` (cebe) diff --git a/framework/db/QueryBuilder.php b/framework/db/QueryBuilder.php index 85f9666e6b..6646bc9d77 100644 --- a/framework/db/QueryBuilder.php +++ b/framework/db/QueryBuilder.php @@ -921,7 +921,7 @@ class QueryBuilder extends \yii\base\Object */ protected function hasLimit($limit) { - return ctype_digit((string) $limit); + return ($limit instanceof Expression) || ctype_digit((string) $limit); } /** @@ -931,8 +931,7 @@ class QueryBuilder extends \yii\base\Object */ protected function hasOffset($offset) { - $offset = (string) $offset; - return ctype_digit($offset) && $offset !== '0'; + return ($offset instanceof Expression) || ctype_digit((string) $offset) && (string) $offset !== '0'; } /** diff --git a/framework/db/QueryTrait.php b/framework/db/QueryTrait.php index 67cd2bf215..91a0514103 100644 --- a/framework/db/QueryTrait.php +++ b/framework/db/QueryTrait.php @@ -27,12 +27,13 @@ trait QueryTrait */ public $where; /** - * @var int maximum number of records to be returned. If not set or less than 0, it means no limit. + * @var int|Expression maximum number of records to be returned. May be an instance of [[Expression]]. + * If not set or less than 0, it means no limit. */ public $limit; /** - * @var int zero-based offset from where the records are to be returned. If not set or - * less than 0, it means starting from the beginning. + * @var int|Expression zero-based offset from where the records are to be returned. + * May be an instance of [[Expression]]. If not set or less than 0, it means starting from the beginning. */ public $offset; /** @@ -375,7 +376,7 @@ trait QueryTrait /** * Sets the LIMIT part of the query. - * @param int $limit the limit. Use null or negative value to disable limit. + * @param int|Expression $limit the limit. Use null or negative value to disable limit. * @return $this the query object itself */ public function limit($limit) @@ -386,7 +387,7 @@ trait QueryTrait /** * Sets the OFFSET part of the query. - * @param int $offset the offset. Use null or negative value to disable offset. + * @param int|Expression $offset the offset. Use null or negative value to disable offset. * @return $this the query object itself */ public function offset($offset) diff --git a/framework/db/mysql/QueryBuilder.php b/framework/db/mysql/QueryBuilder.php index dd52adc96d..18a96c0c60 100644 --- a/framework/db/mysql/QueryBuilder.php +++ b/framework/db/mysql/QueryBuilder.php @@ -182,6 +182,25 @@ class QueryBuilder extends \yii\db\QueryBuilder return $sql; } + /** + * @inheritdoc + */ + protected function hasLimit($limit) + { + // In MySQL limit argument must be nonnegative integer constant + return ctype_digit((string) $limit); + } + + /** + * @inheritdoc + */ + protected function hasOffset($offset) + { + // In MySQL offset argument must be nonnegative integer constant + $offset = (string) $offset; + return ctype_digit($offset) && $offset !== '0'; + } + /** * @inheritdoc */ diff --git a/tests/framework/db/QueryTest.php b/tests/framework/db/QueryTest.php index 84538a5201..d895fd18c7 100644 --- a/tests/framework/db/QueryTest.php +++ b/tests/framework/db/QueryTest.php @@ -235,6 +235,22 @@ abstract class QueryTest extends DatabaseTestCase $this->assertEquals(5, $query->offset); } + public function testLimitOffsetWithExpression() + { + $query = (new Query())->from('customer')->select('id')->orderBy('id'); + $query + ->limit(new Expression('1 + 1')) + ->offset(new Expression('1 + 0')); + + $result = $query->column($this->getConnection()); + + $this->assertCount(2, $result); + + $this->assertNotContains(1, $result); + $this->assertContains(2, $result); + $this->assertContains(3, $result); + } + public function testUnion() { $connection = $this->getConnection(); diff --git a/tests/framework/db/mysql/QueryTest.php b/tests/framework/db/mysql/QueryTest.php index 85c1c24851..1e9018ce7b 100644 --- a/tests/framework/db/mysql/QueryTest.php +++ b/tests/framework/db/mysql/QueryTest.php @@ -25,4 +25,21 @@ class QueryTest extends \yiiunit\framework\db\QueryTest $this->assertArrayHasKey('name', $row); $this->assertArrayHasKey('email', $row); } + + public function testLimitOffsetWithExpression() + { + $query = (new Query())->from('customer')->select('id')->orderBy('id'); + // In MySQL limit and offset arguments must both be nonnegative integer constant + $query + ->limit(new Expression('2')) + ->offset(new Expression('1')); + + $result = $query->column($this->getConnection()); + + $this->assertCount(2, $result); + + $this->assertNotContains(1, $result); + $this->assertContains(2, $result); + $this->assertContains(3, $result); + } } From d8566aecc4d04ff46d24dacc80a21099ec02f400 Mon Sep 17 00:00:00 2001 From: Vovan-VE Date: Sat, 7 Jan 2017 18:49:52 +0800 Subject: [PATCH 02/75] Exception within useMaster() completely disables slaves --- framework/db/Connection.php | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/framework/db/Connection.php b/framework/db/Connection.php index 2502e3a354..cbbdaf9298 100644 --- a/framework/db/Connection.php +++ b/framework/db/Connection.php @@ -951,10 +951,19 @@ class Connection extends Component */ public function useMaster(callable $callback) { - $enableSlave = $this->enableSlaves; - $this->enableSlaves = false; - $result = call_user_func($callback, $this); - $this->enableSlaves = $enableSlave; + if ($this->enableSlaves) { + $this->enableSlaves = false; + try { + $result = call_user_func($callback, $this); + } catch (\Exception $e) { + $this->enableSlaves = true; + throw $e; + } + $this->enableSlaves = true; + } else { + $result = call_user_func($callback, $this); + } + return $result; } From b68417b1ad5442ced51746081644822d07ef166f Mon Sep 17 00:00:00 2001 From: Vovan-VE Date: Thu, 9 Feb 2017 22:06:12 +0800 Subject: [PATCH 03/75] Fix: catch Throwable too --- framework/db/Connection.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/framework/db/Connection.php b/framework/db/Connection.php index cbbdaf9298..8c015598ae 100644 --- a/framework/db/Connection.php +++ b/framework/db/Connection.php @@ -948,6 +948,7 @@ class Connection extends Component * @param callable $callback a PHP callable to be executed by this method. Its signature is * `function (Connection $db)`. Its return value will be returned by this method. * @return mixed the return value of the callback + * @throws \Exception|\Throwable if there is any exception thrown from the callback */ public function useMaster(callable $callback) { @@ -958,6 +959,9 @@ class Connection extends Component } catch (\Exception $e) { $this->enableSlaves = true; throw $e; + } catch (\Throwable $e) { + $this->enableSlaves = true; + throw $e; } $this->enableSlaves = true; } else { From e295b28333c4465825289e174a2a1ca773ab6d52 Mon Sep 17 00:00:00 2001 From: Dmitry Naumenko Date: Thu, 9 Feb 2017 16:43:37 +0200 Subject: [PATCH 04/75] Added TODO comment --- framework/db/Connection.php | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/db/Connection.php b/framework/db/Connection.php index 8c015598ae..640de978c4 100644 --- a/framework/db/Connection.php +++ b/framework/db/Connection.php @@ -963,6 +963,7 @@ class Connection extends Component $this->enableSlaves = true; throw $e; } + // TODO: use "finally" keyword when miminum required PHP version is >= 5.5 $this->enableSlaves = true; } else { $result = call_user_func($callback, $this); From 3538bde085f51b1987e437dc8ca07e784a9068be Mon Sep 17 00:00:00 2001 From: Vovan-VE Date: Fri, 10 Feb 2017 10:27:16 +0800 Subject: [PATCH 05/75] Add test case for #13340 and update CHANGELOG --- framework/CHANGELOG.md | 1 + tests/framework/db/sqlite/ConnectionTest.php | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 1e7e2d9b62..19b82339b8 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -5,6 +5,7 @@ Yii Framework 2 Change Log -------------------------- - Bug #13538: Fixed `yii\db\BaseActiveRecord::deleteAll()` changes method signature declared by `yii\db\ActiveRecordInterface::deleteAll()` (klimov-paul) +- Bug: Fixed `yii\db\Connection::useMaster()` - Exception within callback completely disables slaves (Vovan-VE) - Enh #13278: `yii\caching\DbQueryDependency` created allowing specification of the cache dependency via `yii\db\QueryInterface` (klimov-paul) diff --git a/tests/framework/db/sqlite/ConnectionTest.php b/tests/framework/db/sqlite/ConnectionTest.php index 2d7cb0ae74..b4a273ef64 100644 --- a/tests/framework/db/sqlite/ConnectionTest.php +++ b/tests/framework/db/sqlite/ConnectionTest.php @@ -141,6 +141,21 @@ class ConnectionTest extends \yiiunit\framework\db\ConnectionTest $this->assertEquals($slavesCount, count($hit_slaves), 'all slaves hit'); } + public function testUseMasterException() + { + $db = $this->prepareMasterSlave(1, 1); + $this->assertTrue($db->enableSlaves); + try { + $db->useMaster(function (Connection $db) { + throw new \Exception('fail'); + }); + $this->fail('Exception was caught somewhere'); + } catch (\Exception $e) { + // ok + } + $this->assertTrue($db->enableSlaves); + } + /** * @param int $masterCount * @param int $slaveCount From a5ab79746baf7d44024a71b38d10e5e209b7c8c8 Mon Sep 17 00:00:00 2001 From: Sergey Makinen Date: Sun, 12 Feb 2017 21:59:37 +0300 Subject: [PATCH 06/75] =?UTF-8?q?Fixes=20Oracle=E2=80=99s=20test=20fixture?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/data/oci.sql | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/tests/data/oci.sql b/tests/data/oci.sql index 1a3584ba25..5c18cf440e 100644 --- a/tests/data/oci.sql +++ b/tests/data/oci.sql @@ -22,6 +22,7 @@ BEGIN EXECUTE IMMEDIATE 'DROP TABLE "document"'; EXCEPTION WHEN OTHERS THEN IF S BEGIN EXECUTE IMMEDIATE 'DROP VIEW "animal_view"'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;-- BEGIN EXECUTE IMMEDIATE 'DROP TABLE "validator_main"'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;-- BEGIN EXECUTE IMMEDIATE 'DROP TABLE "validator_ref"'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;-- +BEGIN EXECUTE IMMEDIATE 'DROP TABLE "bit_values"'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END; -- BEGIN EXECUTE IMMEDIATE 'DROP SEQUENCE "profile_SEQ"'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -2289 THEN RAISE; END IF; END;-- BEGIN EXECUTE IMMEDIATE 'DROP SEQUENCE "customer_SEQ"'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -2289 THEN RAISE; END IF; END;-- @@ -191,6 +192,13 @@ CREATE SEQUENCE "document_SEQ"; CREATE VIEW "animal_view" AS SELECT * FROM "animal"; +CREATE TABLE "bit_values" ( + "id" integer not null, + "val" char(1) NOT NULL, + CONSTRAINT "bit_values_PK" PRIMARY KEY ("id") ENABLE, + CONSTRAINT "bit_values_val" CHECK ("val" IN ('1','0')) +); + /** * (Postgres-)Database Schema for validator tests */ @@ -255,6 +263,11 @@ CREATE TRIGGER "animal_TRG" BEFORE INSERT ON "animal" FOR EACH ROW BEGIN <> BEGIN + IF INSERTING AND :NEW."id" IS NULL THEN SELECT "document_SEQ".NEXTVAL INTO :NEW."id" FROM SYS.DUAL; END IF; +END COLUMN_SEQUENCES; +END; +/ /* TRIGGERS */ @@ -313,23 +326,6 @@ INSERT INTO "validator_ref" ("id", "a_field", "ref") VALUES (4, 'ref_to_4', 4); INSERT INTO "validator_ref" ("id", "a_field", "ref") VALUES (5, 'ref_to_4', 4); INSERT INTO "validator_ref" ("id", "a_field", "ref") VALUES (6, 'ref_to_5', 5); -/* bit test, see https://github.com/yiisoft/yii2/issues/9006 */ - -BEGIN EXECUTE IMMEDIATE 'DROP TABLE "bit_values"'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;-- - -CREATE TABLE [dbo].[] ( - [id] [int] IDENTITY(1,1) NOT NULL, - [val] [bit] NOT NULL, - CONSTRAINT [PK_bit_values] PRIMARY KEY CLUSTERED ( - [id] ASC - ) ON [PRIMARY] -); - -CREATE TABLE "bit_values" ( - "id" integer not null, - "val" char(1) NOT NULL, - CONSTRAINT "bit_values_PK" PRIMARY KEY ("id") ENABLE, - CONSTRAINT "bit_values_val" CHECK (val IN ('1','0')) -); - -INSERT INTO "bit_values" ("id", "val") VALUES (1, '0'), (2, '1'); +INSERT INTO "bit_values" ("id", "val") + SELECT 1, '0' FROM SYS.DUAL + UNION ALL SELECT 2, '1' FROM SYS.DUAL; From 2e32e97c0263f82b9c025cb3cb38f73526f7edb7 Mon Sep 17 00:00:00 2001 From: Roman Grinyov Date: Thu, 16 Feb 2017 23:30:45 +0300 Subject: [PATCH 07/75] fix anchor http://www.yiiframework.com/doc-2.0/guide-db-migrations.html#creating-migrations --- docs/guide/db-migrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/db-migrations.md b/docs/guide/db-migrations.md index 09fcc4847b..9a283be0a0 100644 --- a/docs/guide/db-migrations.md +++ b/docs/guide/db-migrations.md @@ -136,7 +136,7 @@ class m150101_185401_create_news_table extends Migration The base migration class [[yii\db\Migration]] exposes a database connection via the [[yii\db\Migration::db|db]] property. You can use it to manipulate the database schema using the methods as described in -[Working with Database Schema](db-dao.md#working-with-database-schema-). +[Working with Database Schema](db-dao.md#database-schema). Rather than using physical types, when creating a table or column you should use *abstract types* so that your migrations are independent of specific DBMS. The [[yii\db\Schema]] class defines From f18e55c1f0db0ced5b342bea49aa7a2a0c4c7b75 Mon Sep 17 00:00:00 2001 From: Nobuo Kihara Date: Fri, 17 Feb 2017 14:58:32 +0900 Subject: [PATCH 08/75] docs ja translation updated [ci skip] --- docs/guide-ja/db-dao.md | 3 ++- docs/guide-ja/runtime-routing.md | 4 ++++ docs/internals-ja/git-workflow.md | 5 ----- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/guide-ja/db-dao.md b/docs/guide-ja/db-dao.md index 1b69959016..4794538e69 100644 --- a/docs/guide-ja/db-dao.md +++ b/docs/guide-ja/db-dao.md @@ -19,7 +19,8 @@ Yii は下記の DBMS のサポートを内蔵しています。 - [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): バージョン 2008 以上。 > Note: PHP 7 用の pdo_oci の新しいバージョンは、現在、ソースコードとしてのみ存在します。 - [コミュニティによる説明](https://github.com/yiisoft/yii2/issues/10975#issuecomment-248479268) に従って、コンパイルしてください。 + [コミュニティによる説明](https://github.com/yiisoft/yii2/issues/10975#issuecomment-248479268) に従ってコンパイルするか、 + または、[PDO エミュレーションレイヤ](https://github.com/taq/pdooci) を使って下さい。 ## DB 接続を作成する diff --git a/docs/guide-ja/runtime-routing.md b/docs/guide-ja/runtime-routing.md index f169852f31..489acbf436 100644 --- a/docs/guide-ja/runtime-routing.md +++ b/docs/guide-ja/runtime-routing.md @@ -422,6 +422,10 @@ URL 規則のパターンには、ウェブサーバ名を含むことが出来 ] ``` +バージョン 2.0.11 以降は、`http` と `https` の両方に通用する、プロトコル相対パターンを使うことも出来ます。 +記法は上記と同じです、ただ、`http:` の部分を省略します。 +例えば、`'//www.example.com/login' => 'site/login'`。 + > Note: サーバ名を持つ規則は、そのパターンに、エントリスクリプトのサブフォルダを**含まない**ようにすべきです。 例えば、アプリケーションのエントリスクリプトが `http://www.example.com/sandbox/blog/index.php` である場合は、 `http://www.example.com/sandbox/blog/posts` ではなく、`http://www.example.com/posts` というパターンを使うべきです。 diff --git a/docs/internals-ja/git-workflow.md b/docs/internals-ja/git-workflow.md index 0cb0a55da9..fa4e20c662 100644 --- a/docs/internals-ja/git-workflow.md +++ b/docs/internals-ja/git-workflow.md @@ -75,11 +75,6 @@ phpunit をグローバルにインストールしていない場合は、代り JavaScript の単体テストは、レポジトリのルートディレクトリで `npm test` を走らせることによって実行することが出来ます。 -> Note: タイムアウトエラー、例えば `Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.` になる場合は、 - タイムアウトになる時間を延ばすことが出来ます。 - `npm test -- --timeout 30000` - (`--` を忘れないように。追加の引数を渡すために必要です)。 - ### エクステンション エクステンションに取り組むためには、エクステンションのレポジトリをクローンする必要があります。 From 5e987b0b9b304d9bdf77cc9ff979334717d4463e Mon Sep 17 00:00:00 2001 From: SilverFire - Dmitry Naumenko Date: Fri, 17 Feb 2017 09:15:57 +0200 Subject: [PATCH 09/75] Fixed anchor in docs --- docs/guide-es/db-migrations.md | 2 +- docs/guide-fr/db-migrations.md | 2 +- docs/guide-pt-BR/db-migrations.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/guide-es/db-migrations.md b/docs/guide-es/db-migrations.md index 2f424954c7..c71fcf300f 100644 --- a/docs/guide-es/db-migrations.md +++ b/docs/guide-es/db-migrations.md @@ -136,7 +136,7 @@ class m150101_185401_create_news_table extends Migration La clase de migración de base de datos [[yii\db\Migration]] expone una conexión a la base de datos mediante la propiedad [[yii\db\Migration::db|db]]. Puedes utilizar esto para manipular el esquema de la base de datos utilizando métodos como se describen en -[Trabajando con Esquemas de Base de Datos](db-dao.md#working-with-database-schema-). +[Trabajando con Esquemas de Base de Datos](db-dao.md#database-schema). En vez de utilizar tipos físicos, al crear tablas o columnas deberías utilizar los *tipos abstractos* así las migraciones son independientes de algún DBMS específico. La clase [[yii\db\Schema]] define diff --git a/docs/guide-fr/db-migrations.md b/docs/guide-fr/db-migrations.md index e5c77eefe1..e44a4ac5f9 100644 --- a/docs/guide-fr/db-migrations.md +++ b/docs/guide-fr/db-migrations.md @@ -112,7 +112,7 @@ class m150101_185401_create_news_table extends Migration > Info: toutes les migrations ne sont pas réversibles. Par exemple, si la méthode `up()` supprime une ligne dans une table, il se peut que vous soyez incapable de récupérer cette ligne dans la méthode `down()`. Parfois, vous pouvez simplement être trop paresseux pour implémenter la méthode `down`, parce que défaire une migration de base de données n'est pas chose courante. Dans ce cas, vous devriez retourner `false` dans la méthode `down()` pour indiquer que la migration n'est pas réversible. -La classe de migration de base [[yii\db\Migration]] expose une connexion à une base de données via la propriété [[yii\db\Migration::db|db]]. Vous pouvez utiliser cette connexion pour manipuler le schéma en utilisant les méthodes décrites dans la sous-section [Travail avec le schéma de base de données](db-dao.md#working-with-database-schema-). +La classe de migration de base [[yii\db\Migration]] expose une connexion à une base de données via la propriété [[yii\db\Migration::db|db]]. Vous pouvez utiliser cette connexion pour manipuler le schéma en utilisant les méthodes décrites dans la sous-section [Travail avec le schéma de base de données](db-dao.md#database-schema). Plutôt que d'utiliser des types physiques, lors de la création d'une table ou d'une colonne, vous devez utiliser des *types abstraits* afin que vos migrations soient indépendantes d'un système de gestion de base de données en particulier. La classe [[yii\db\Schema]] définit une jeu de constantes pour représenter les types abstraits pris en charge. Ces constantes sont nommées dans le format `TYPE_`. Par exemple, `TYPE_PK` fait référence au type clé primaire à auto-incrémentation ; `TYPE_STRING` fait référence au type chaîne de caractères. Lorsqu'une migration est appliquée à une base de données particulière, le type abstrait est converti dans le type physique correspondant. Dans le cas de MySQL, `TYPE_PK` est transformé en `int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY`, tandis que `TYPE_STRING` est transformé en `varchar(255)`. diff --git a/docs/guide-pt-BR/db-migrations.md b/docs/guide-pt-BR/db-migrations.md index 7a6db6f482..49601285f5 100644 --- a/docs/guide-pt-BR/db-migrations.md +++ b/docs/guide-pt-BR/db-migrations.md @@ -109,7 +109,7 @@ class m150101_185401_criar_tabela_noticias extends \yii\db\Migration Neste caso, você deve retornar `false` no método `down()` para indicar que a migração não é reversível. A classe base [[yii\db\Migration]] expõe a conexão ao banco através da propriedade [[yii\db\Migration::db|db]]. -Você pode usá-la para manipular o esquema do banco de dados usando os métodos como descritos em [Trabalhando com um Esquema de Banco de Dados](db-dao.md#working-with-database-schema-). +Você pode usá-la para manipular o esquema do banco de dados usando os métodos como descritos em [Trabalhando com um Esquema de Banco de Dados](db-dao.md#database-schema). Ao invés de usar tipos físicos, ao criar uma tabela ou coluna, você deve usar *tipos abstratos* para que suas migrações sejam independentes do SGBD. A classe [[yii\db\Schema]] define uma gama de constantes para From 8e1a5eccde58342d11e54ac6dd7d365f78eb0d1a Mon Sep 17 00:00:00 2001 From: zoolanders49 Date: Fri, 17 Feb 2017 08:38:22 +0100 Subject: [PATCH 10/75] Update french translation : yii.php (#13591) * Update yii.php add "must be equal to" translation for french * Update yii.php add translation for : ' and ', 'The combination {values} of {attributes} has already been taken.' --- framework/messages/fr/yii.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework/messages/fr/yii.php b/framework/messages/fr/yii.php index 99b4be776d..0bc2077e9a 100644 --- a/framework/messages/fr/yii.php +++ b/framework/messages/fr/yii.php @@ -17,6 +17,7 @@ * NOTE: this file must be saved in UTF-8 encoding. */ return [ + ' and ' => ' et ', '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 jour} other{# jours}}', '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 heure} other{# heures}}', '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 minute} other{# minutes}}', @@ -42,6 +43,7 @@ return [ 'Please fix the following errors:' => 'Veuillez vérifier les erreurs suivantes :', 'Please upload a file.' => 'Veuillez télécharger un fichier.', 'Showing {begin, number}-{end, number} of {totalCount, number} {totalCount, plural, one{item} other{items}}.' => 'Affichage de {begin, number}-{end, number} sur {totalCount, number} {totalCount, plural, one{élément} other{éléments}}.', + 'The combination {values} of {attributes} has already been taken.' => 'La combinaison {values} de {attributes} est déjà utilisée.', 'The file "{file}" is not an image.' => 'Le fichier « {file} » n\'est pas une image.', 'The file "{file}" is too big. Its size cannot exceed {formattedLimit}.' => 'Le fichier « {file} » est trop gros. Sa taille ne peut dépasser {formattedLimit}.', 'The file "{file}" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Le fichier « {file} » est trop petit. Sa taille ne peut être inférieure à {formattedLimit}.', @@ -82,6 +84,7 @@ return [ '{attribute} must be an IP address with specified subnet.' => '{attribute} doit être une adresse IP avec un sous-réseau.', '{attribute} must be an integer.' => '{attribute} doit être un entier.', '{attribute} must be either "{true}" or "{false}".' => '{attribute} doit être soit « {true} » soit « {false} ».', + '{attribute} must be equal to "{compareValueOrAttribute}".' => '{attribute} doit être égal à "{compareValueOrAttribute}".', '{attribute} must be greater than "{compareValueOrAttribute}".' => '{attribute} doit être supérieur à « {compareValueOrAttribute} ».', '{attribute} must be greater than or equal to "{compareValueOrAttribute}".' => '{attribute} doit être supérieur ou égal à « {compareValueOrAttribute} ».', '{attribute} must be less than "{compareValueOrAttribute}".' => '{attribute} doit être inférieur à « {compareValueOrAttribute} ».', From a26d28f8e39c6d78bc1364169f17143a2a68a12a Mon Sep 17 00:00:00 2001 From: Nikolay Oleynikov Date: Sat, 18 Feb 2017 02:23:20 +0300 Subject: [PATCH 11/75] Fixes #13576: Added support of `srcset` to `yii\helpers\Html::img()` --- framework/CHANGELOG.md | 1 + framework/helpers/BaseHtml.php | 11 ++++ tests/framework/helpers/HtmlTest.php | 95 ++++++++++++++++++++++++++-- 3 files changed, 103 insertions(+), 4 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index c4824f4f17..57bb48e89e 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -12,6 +12,7 @@ Yii Framework 2 Change Log - Bug #11404: `yii\base\Model::loadMultiple()` returns true even if `yii\base\Model::load()` returns false (zvook) - Bug #13513: Fixed RBAC migration to work correctly on Oracle DBMS (silverfire) - Enh #13550: Refactored unset call order in `yii\di\ServiceLocator::set()` (Lanrik) +- Enh #13576: Added support of `srcset` to `yii\helpers\Html::img()` (Kolyunya) 2.0.11.2 February 08, 2017 diff --git a/framework/helpers/BaseHtml.php b/framework/helpers/BaseHtml.php index a3fbbed6fb..eeabe3d26b 100644 --- a/framework/helpers/BaseHtml.php +++ b/framework/helpers/BaseHtml.php @@ -430,11 +430,22 @@ class BaseHtml * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. * If a value is null, the corresponding attribute will not be rendered. * See [[renderTagAttributes()]] for details on how attributes are being rendered. + * @since 2.0.12 It is possible to pass the "srcset" option as an array which keys are + * descriptors and values are URLs. All URLs will be processed by [[Url::to()]]. * @return string the generated image tag */ public static function img($src, $options = []) { $options['src'] = Url::to($src); + + if (isset($options['srcset']) && is_array($options['srcset'])) { + $srcset = []; + foreach ($options['srcset'] as $descriptor => $url) { + $srcset[] = Url::to($url) . ' ' . $descriptor; + } + $options['srcset'] = implode(',', $srcset); + } + if (!isset($options['alt'])) { $options['alt'] = ''; } diff --git a/tests/framework/helpers/HtmlTest.php b/tests/framework/helpers/HtmlTest.php index a83b4dd867..ea41b6f2c4 100644 --- a/tests/framework/helpers/HtmlTest.php +++ b/tests/framework/helpers/HtmlTest.php @@ -132,11 +132,98 @@ class HtmlTest extends TestCase $this->assertEquals('test<>', Html::mailto('test<>', 'test>')); } - public function testImg() + /** + * @return array + */ + public function imgDataProvider() { - $this->assertEquals('', Html::img('/example')); - $this->assertEquals('', Html::img('')); - $this->assertEquals('something', Html::img('/example', ['alt' => 'something', 'width' => 10])); + return [ + [ + '', + '/example', + [], + ], + [ + '', + '', + [], + ], + [ + 'something', + '/example', + [ + 'alt' => 'something', + 'width' => 10, + ], + ], + [ + '', + '/base-url', + [ + 'srcset' => [ + ], + ], + ], + [ + '', + '/base-url', + [ + 'srcset' => [ + '9001w' => '/example-9001w', + ], + ], + ], + [ + '', + '/base-url', + [ + 'srcset' => [ + '100w' => '/example-100w', + '500w' => '/example-500w', + '1500w' => '/example-1500w', + ], + ], + ], + [ + '', + '/base-url', + [ + 'srcset' => [ + '1x' => '/example-1x', + '2x' => '/example-2x', + '3x' => '/example-3x', + '4x' => '/example-4x', + '5x' => '/example-5x', + ], + ], + ], + [ + '', + '/base-url', + [ + 'srcset' => [ + '1.42x' => '/example-1.42x', + '2.0x' => '/example-2.0x', + '3.99999x' => '/example-3.99999x', + ], + ], + ], + [ + '', + '/base-url', + [ + 'srcset' => '/example-1x 1x,/example-2x 2x,/example-3x 3x', + ], + ], + ]; + } + + /** + * @dataProvider imgDataProvider + */ + public function testImg($expected, $src, $options) + { + $this->assertEquals($expected, Html::img($src, $options)); } public function testLabel() From 45d670c995d9840742fe338dd9b5c2415ec9d83f Mon Sep 17 00:00:00 2001 From: Boudewijn Vahrmeijer Date: Sat, 18 Feb 2017 15:49:24 +0100 Subject: [PATCH 12/75] Update ConnectionTest.php --- tests/framework/db/sqlite/ConnectionTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/framework/db/sqlite/ConnectionTest.php b/tests/framework/db/sqlite/ConnectionTest.php index b4a273ef64..d91ef1b8d0 100644 --- a/tests/framework/db/sqlite/ConnectionTest.php +++ b/tests/framework/db/sqlite/ConnectionTest.php @@ -141,7 +141,7 @@ class ConnectionTest extends \yiiunit\framework\db\ConnectionTest $this->assertEquals($slavesCount, count($hit_slaves), 'all slaves hit'); } - public function testUseMasterException() + public function testRestoreMasterAfterException() { $db = $this->prepareMasterSlave(1, 1); $this->assertTrue($db->enableSlaves); From bb55311068f6c91fd47e4baab475df47905448d3 Mon Sep 17 00:00:00 2001 From: Derek Gifford Date: Sat, 18 Feb 2017 13:45:35 -0500 Subject: [PATCH 13/75] Clarified about calling phpunit under Windows in git-workflow.md [skip ci] --- docs/internals/git-workflow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/internals/git-workflow.md b/docs/internals/git-workflow.md index a8e620cb05..a1a7236359 100644 --- a/docs/internals/git-workflow.md +++ b/docs/internals/git-workflow.md @@ -66,7 +66,7 @@ The following steps are optional. ### Unit tests You can execute unit tests by running `phpunit` in the repo root directory. If you do not have phpunit installed globally -you can run `php vendor/bin/phpunit` instead. +you can run `php vendor/bin/phpunit` or `vendor/bin/phpunit.bat` in case of execution from the Windows OS. Some tests require additional databases to be set up and configured. You can create `tests/data/config.local.php` to override settings that are configured in `tests/data/config.php`. From e8f8ba21290d033e4eda69abbc32bc130cfdffd2 Mon Sep 17 00:00:00 2001 From: Nikolay Oleynikov Date: Sat, 18 Feb 2017 21:56:56 +0300 Subject: [PATCH 14/75] Remove excessive `use` statement (#13604) --- tests/framework/helpers/HtmlTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/framework/helpers/HtmlTest.php b/tests/framework/helpers/HtmlTest.php index ea41b6f2c4..6da39da0e7 100644 --- a/tests/framework/helpers/HtmlTest.php +++ b/tests/framework/helpers/HtmlTest.php @@ -4,7 +4,6 @@ namespace yiiunit\framework\helpers; use Yii; use yii\base\DynamicModel; -use yii\base\Model; use yii\helpers\Html; use yii\helpers\Url; use yiiunit\TestCase; From 6b8cf1709ed0667c9d0238db62d83590a23efe4e Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Sun, 19 Feb 2017 01:01:01 +0600 Subject: [PATCH 15/75] Fixes #13306: Wildcard in `reloadableScripts` in `yii.js` allows 0 characters --- framework/CHANGELOG.md | 1 + framework/assets/yii.js | 2 +- tests/js/tests/yii.test.js | 16 +++++++++------- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index abbfec5074..cabafdd4a0 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -4,6 +4,7 @@ Yii Framework 2 Change Log 2.0.12 under development -------------------------- +- Bug #13306: Wildcard in `reloadableScripts` in `yii.js` allows 0 characters (arogachev) - Enh #13523: Plural rule for pasta (developeruz) - Bug #13538: Fixed `yii\db\BaseActiveRecord::deleteAll()` changes method signature declared by `yii\db\ActiveRecordInterface::deleteAll()` (klimov-paul) - Bug: Fixed `yii\db\Connection::useMaster()` - Exception within callback completely disables slaves (Vovan-VE) diff --git a/framework/assets/yii.js b/framework/assets/yii.js index b37451b856..8873069159 100644 --- a/framework/assets/yii.js +++ b/framework/assets/yii.js @@ -483,7 +483,7 @@ window.yii = (function ($) { function isReloadableAsset(url) { for (var i = 0; i < pub.reloadableScripts.length; i++) { var rule = getAbsoluteUrl(pub.reloadableScripts[i]); - var match = new RegExp("^" + escapeRegExp(rule).split('\\*').join('.*') + "$").test(url); + var match = new RegExp("^" + escapeRegExp(rule).split('\\*').join('.+') + "$").test(url); if (match === true) { return true; } diff --git a/tests/js/tests/yii.test.js b/tests/js/tests/yii.test.js index fb5116970c..a18776b36b 100644 --- a/tests/js/tests/yii.test.js +++ b/tests/js/tests/yii.test.js @@ -1095,7 +1095,7 @@ describe('yii', function () { yii.reloadableScripts = [ '/js/reloadable.js', // https://github.com/yiisoft/yii2/issues/11494 - '/js/reloadable/script*.js' + '/js/reloadable/script*.js?v=*' ]; }); @@ -1106,9 +1106,9 @@ describe('yii', function () { describe('with match', function () { withData({ 'relative url, exact': ['/js/reloadable.js'], - 'relative url, wildcard': ['http://foo.bar/js/reloadable/script1.js'], + 'relative url, wildcard': ['/js/reloadable/script1.js?v=1'], 'absolute url, exact': ['http://foo.bar/js/reloadable.js'], - 'absolute url, wildcard': ['http://foo.bar/js/reloadable/script2.js'] + 'absolute url, wildcard': ['http://foo.bar/js/reloadable/script2.js?v=2'] }, function (url) { it('should load it as many times as it was requested', function () { $.getScript(url); @@ -1127,7 +1127,9 @@ describe('yii', function () { describe('with no match', function () { withData({ 'relative url': ['/js/not_reloadable.js'], - 'absolute url': ['http://foo.bar/js/reloadable/not_reloadable_script.js'] + 'relative url, all wildcards are empty': ['/js/reloadable/script.js?v='], + 'absolute url': ['http://foo.bar/js/reloadable/not_reloadable_script.js'], + 'absolute url, 1 empty wildcard': ['http://foo.bar/js/reloadable/script1.js?v='] }, function (url) { it('should load it only once for both relative and absolute urls', function () { $.getScript(url); @@ -1144,14 +1146,14 @@ describe('yii', function () { describe('with failed load after successful load and making it not reloadable', function () { it('should allow to load it again', function () { - $.getScript('/js/reloadable/script_fail.js'); + $.getScript('/js/reloadable/script_fail.js?v=1'); respondToRequestWithSuccess(0); - $.getScript('/js/reloadable/script_fail.js'); + $.getScript('/js/reloadable/script_fail.js?v=1'); respondToRequestWithError(1); yii.reloadableScripts = []; - $.getScript('/js/reloadable/script_fail.js'); + $.getScript('/js/reloadable/script_fail.js?v=1'); respondToRequestWithError(2); assert.lengthOf(server.requests, 3); From 7eb2763b00b3fb3605ba9d2dacf07f66f4cd638b Mon Sep 17 00:00:00 2001 From: jaaf Date: Sat, 18 Feb 2017 21:50:41 +0100 Subject: [PATCH 16/75] New caching-overview.md fr [skip ci] (#13609) --- docs/guide-fr/caching-overview.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 docs/guide-fr/caching-overview.md diff --git a/docs/guide-fr/caching-overview.md b/docs/guide-fr/caching-overview.md new file mode 100644 index 0000000000..49f9fd4376 --- /dev/null +++ b/docs/guide-fr/caching-overview.md @@ -0,0 +1,15 @@ +Mise en cache +============= + +La mise en cache est un moyen peu coûteux et efficace d'améliorer la performance d'une application Web. En stockant des données relativement statiques en cache et en les servant à partir de ce cache lorsqu'elles sont demandées, l'application économise le temps qu'il aurait fallu pour générer ces données à partir de rien à chaque demande. + +La mise en cache se produit à différents endroits et à différents niveaux dans une application Web. Du côté du serveur, au niveau le plus bas, le cache peut être utilisé pour stocker des données de base, telles qu'une liste des informations sur des articles recherchée dans une base de données ; et à un niveau plus élevé, il peut être utilisé pour stocker des fragments ou l'intégralité de pages Web, telles que le rendu des articles les plus récents. + +Du côté client, la mise en cache HTTP peut être utilisée pour conserver le contenu des pages visitées les plus récentes dans le cache du navigateur. + +Yii prend en charge tous ces mécanismes de mise en cache : + +* [Mise en cache de données](caching-data.md) +* [Mise en cache de fragments](caching-fragment.md) +* [Mise en cache de pages](caching-page.md) +* [Mise en cache HTTP](caching-http.md) From 209fb7d94b05771a5f66e0e153bb1a05f2a4dfa2 Mon Sep 17 00:00:00 2001 From: Sergey Makinen Date: Sat, 18 Feb 2017 23:58:04 +0300 Subject: [PATCH 17/75] Fixes #13594: Fixes insufficient quoting in `yii\db\QueryBuilder::prepareInsertSelectSubQuery()` --- framework/CHANGELOG.md | 1 + framework/db/QueryBuilder.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index cabafdd4a0..19a0ca88af 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -14,6 +14,7 @@ Yii Framework 2 Change Log - Bug #11404: `yii\base\Model::loadMultiple()` returns true even if `yii\base\Model::load()` returns false (zvook) - Bug #13513: Fixed RBAC migration to work correctly on Oracle DBMS (silverfire) - Enh #13550: Refactored unset call order in `yii\di\ServiceLocator::set()` (Lanrik) +- Bug #13594: Fixes insufficient quoting in `yii\db\QueryBuilder::prepareInsertSelectSubQuery()` (sergeymakinen) - Enh #13576: Added support of `srcset` to `yii\helpers\Html::img()` (Kolyunya) diff --git a/framework/db/QueryBuilder.php b/framework/db/QueryBuilder.php index 7b20700163..959bb7c1f9 100644 --- a/framework/db/QueryBuilder.php +++ b/framework/db/QueryBuilder.php @@ -205,7 +205,7 @@ class QueryBuilder extends \yii\base\Object $values = ' ' . $values; foreach ($columns->select as $title => $field) { if (is_string($title)) { - $names[] = $title; + $names[] = $schema->quoteColumnName($title); } else if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)([\w\-_\.]+)$/', $field, $matches)) { $names[] = $schema->quoteColumnName($matches[2]); } else { From ef4dadf4374f893166e02f3b4effa88053930c3e Mon Sep 17 00:00:00 2001 From: Sergey Makinen Date: Sun, 19 Feb 2017 17:32:51 +0300 Subject: [PATCH 18/75] =?UTF-8?q?Fixes=20#13592:=20Fixes=20Oracle=E2=80=99?= =?UTF-8?q?s=20`yii\db\oci\Schema::setTransactionIsolationLevel()`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- framework/CHANGELOG.md | 1 + framework/db/Schema.php | 2 +- tests/framework/db/oci/ConnectionTest.php | 13 +++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 19a0ca88af..2332cf7173 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -14,6 +14,7 @@ Yii Framework 2 Change Log - Bug #11404: `yii\base\Model::loadMultiple()` returns true even if `yii\base\Model::load()` returns false (zvook) - Bug #13513: Fixed RBAC migration to work correctly on Oracle DBMS (silverfire) - Enh #13550: Refactored unset call order in `yii\di\ServiceLocator::set()` (Lanrik) +- Bug #13592: Fixes Oracle’s `yii\db\oci\Schema::setTransactionIsolationLevel()` (sergeymakinen) - Bug #13594: Fixes insufficient quoting in `yii\db\QueryBuilder::prepareInsertSelectSubQuery()` (sergeymakinen) - Enh #13576: Added support of `srcset` to `yii\helpers\Html::img()` (Kolyunya) diff --git a/framework/db/Schema.php b/framework/db/Schema.php index 0d1bdcac61..155723544e 100644 --- a/framework/db/Schema.php +++ b/framework/db/Schema.php @@ -438,7 +438,7 @@ abstract class Schema extends Object */ public function setTransactionIsolationLevel($level) { - $this->db->createCommand("SET TRANSACTION ISOLATION LEVEL $level;")->execute(); + $this->db->createCommand("SET TRANSACTION ISOLATION LEVEL $level")->execute(); } /** diff --git a/tests/framework/db/oci/ConnectionTest.php b/tests/framework/db/oci/ConnectionTest.php index b78c6c1789..185021a858 100644 --- a/tests/framework/db/oci/ConnectionTest.php +++ b/tests/framework/db/oci/ConnectionTest.php @@ -2,6 +2,8 @@ namespace yiiunit\framework\db\oci; +use yii\db\Transaction; + /** * @group db * @group oci @@ -67,4 +69,15 @@ class ConnectionTest extends \yiiunit\framework\db\ConnectionTest $this->assertEquals('"table"."column"', $connection->quoteSql('{{%table}}.[[column]]')); $this->assertEquals('"table"."column"', $connection->quoteSql('{{%table}}."column"')); } + + public function testTransactionIsolation() + { + $connection = $this->getConnection(true); + + $transaction = $connection->beginTransaction(Transaction::READ_COMMITTED); + $transaction->commit(); + + $transaction = $connection->beginTransaction(Transaction::SERIALIZABLE); + $transaction->commit(); + } } From 323568c2e9caf5204fc83f7f537ea35bd6785afd Mon Sep 17 00:00:00 2001 From: Nikolay Oleynikov Date: Sun, 19 Feb 2017 17:34:22 +0300 Subject: [PATCH 19/75] Remove trailing spaces from sources and tests (#13621) [skip ci] --- framework/assets/yii.activeForm.js | 2 +- framework/base/Widget.php | 2 +- framework/db/oci/Schema.php | 40 +++++++++---------- framework/helpers/BaseStringHelper.php | 8 ++-- framework/i18n/Formatter.php | 2 +- framework/messages/fr/yii.php | 4 +- framework/messages/ms/yii.php | 18 ++++----- framework/messages/uz/yii.php | 2 +- framework/rbac/migrations/schema-oci.sql | 2 +- framework/web/AssetConverter.php | 2 +- framework/web/Controller.php | 2 +- framework/web/MultipartFormDataParser.php | 2 +- framework/web/XmlResponseFormatter.php | 2 +- framework/widgets/FragmentCache.php | 2 +- framework/widgets/LinkPager.php | 2 +- tests/data/ar/Animal.php | 2 +- tests/data/ar/Cat.php | 2 +- tests/data/ar/Dog.php | 2 +- tests/data/ar/Type.php | 1 - tests/data/codeclimate/phpmd_ruleset.xml | 6 +-- .../controllers/AssetControllerTest.php | 2 +- .../controllers/DbMessageControllerTest.php | 20 +++++----- tests/framework/db/ActiveRecordTest.php | 12 +++--- tests/framework/db/CommandTest.php | 2 +- tests/framework/db/pgsql/CommandTest.php | 2 +- .../framework/filters/auth/AuthMethodTest.php | 4 +- tests/framework/helpers/StringHelperTest.php | 4 +- tests/framework/helpers/VarDumperTest.php | 2 +- tests/framework/log/SyslogTargetTest.php | 4 +- tests/framework/mutex/MysqlMutexTest.php | 2 +- tests/framework/mutex/PgsqlMutexTest.php | 2 +- tests/framework/web/DbSessionTest.php | 4 +- tests/framework/web/UserTest.php | 6 +-- .../web/XmlResponseFormatterTest.php | 10 ++--- tests/framework/widgets/LinkPagerTest.php | 8 ++-- tests/js/tests/yii.test.js | 4 +- 36 files changed, 96 insertions(+), 97 deletions(-) diff --git a/framework/assets/yii.activeForm.js b/framework/assets/yii.activeForm.js index bb39f85a4c..310d3abc45 100644 --- a/framework/assets/yii.activeForm.js +++ b/framework/assets/yii.activeForm.js @@ -107,7 +107,7 @@ * function (event) * where * - event: an Event object. - */ + */ afterInit: 'afterInit' }; diff --git a/framework/base/Widget.php b/framework/base/Widget.php index 61fc3f7a4d..80af12fa6e 100644 --- a/framework/base/Widget.php +++ b/framework/base/Widget.php @@ -257,7 +257,7 @@ class Widget extends Component implements ViewContextInterface return dirname($class->getFileName()) . DIRECTORY_SEPARATOR . 'views'; } - + /** * This method is invoked right before the widget is executed. * diff --git a/framework/db/oci/Schema.php b/framework/db/oci/Schema.php index 1bcb158de7..28d2b2f96e 100644 --- a/framework/db/oci/Schema.php +++ b/framework/db/oci/Schema.php @@ -121,13 +121,13 @@ class Schema extends \yii\db\Schema protected function findColumns($table) { $sql = <<db->createCommand($sql); } else { $sql = <<charset); } - + if (mb_strlen($string, $encoding ?: Yii::$app->charset) > $length) { return rtrim(mb_substr($string, 0, $length, $encoding ?: Yii::$app->charset)) . $suffix; } else { return $string; } } - + /** * Truncates a string to the number of words specified. * @@ -138,7 +138,7 @@ class BaseStringHelper return $string; } } - + /** * Truncate a string while preserving the HTML. * diff --git a/framework/i18n/Formatter.php b/framework/i18n/Formatter.php index a9440fdd63..83895e12ee 100644 --- a/framework/i18n/Formatter.php +++ b/framework/i18n/Formatter.php @@ -659,7 +659,7 @@ class Formatter extends Component * Since version 2.0.1 this may also return an array if `$checkTimeInfo` is true. * The first element of the array is the normalized timestamp and the second is a boolean indicating whether * the timestamp has time information or it is just a date value. - * Since version 2.0.12 the array has third boolean element indicating whether the timestamp has date information + * Since version 2.0.12 the array has third boolean element indicating whether the timestamp has date information * or it is just a time value. * @throws InvalidParamException if the input value can not be evaluated as a date value. */ diff --git a/framework/messages/fr/yii.php b/framework/messages/fr/yii.php index 0bc2077e9a..bbf090a11d 100644 --- a/framework/messages/fr/yii.php +++ b/framework/messages/fr/yii.php @@ -17,7 +17,7 @@ * NOTE: this file must be saved in UTF-8 encoding. */ return [ - ' and ' => ' et ', + ' and ' => ' et ', '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 jour} other{# jours}}', '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 heure} other{# heures}}', '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 minute} other{# minutes}}', @@ -43,7 +43,7 @@ return [ 'Please fix the following errors:' => 'Veuillez vérifier les erreurs suivantes :', 'Please upload a file.' => 'Veuillez télécharger un fichier.', 'Showing {begin, number}-{end, number} of {totalCount, number} {totalCount, plural, one{item} other{items}}.' => 'Affichage de {begin, number}-{end, number} sur {totalCount, number} {totalCount, plural, one{élément} other{éléments}}.', - 'The combination {values} of {attributes} has already been taken.' => 'La combinaison {values} de {attributes} est déjà utilisée.', + 'The combination {values} of {attributes} has already been taken.' => 'La combinaison {values} de {attributes} est déjà utilisée.', 'The file "{file}" is not an image.' => 'Le fichier « {file} » n\'est pas une image.', 'The file "{file}" is too big. Its size cannot exceed {formattedLimit}.' => 'Le fichier « {file} » est trop gros. Sa taille ne peut dépasser {formattedLimit}.', 'The file "{file}" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Le fichier « {file} » est trop petit. Sa taille ne peut être inférieure à {formattedLimit}.', diff --git a/framework/messages/ms/yii.php b/framework/messages/ms/yii.php index 4e9b198842..7ea2b0f6d4 100644 --- a/framework/messages/ms/yii.php +++ b/framework/messages/ms/yii.php @@ -35,7 +35,7 @@ return [ 'Page not found.' => 'Halaman tidak dijumpai.', 'Please fix the following errors:' => 'Sila betulkan ralat berikut:', 'Please upload a file.' => 'Sila muat naik fail', - 'Showing {begin, number}-{end, number} of {totalCount, number} {totalCount, plural, one{item} other{items}}.' => + 'Showing {begin, number}-{end, number} of {totalCount, number} {totalCount, plural, one{item} other{items}}.' => 'Memaparkan {begin, number}-{end, number} daripada {totalCount, number} {totalCount, plural, one{item} other{items}}.', 'The file "{file}" is not an image.' => 'Fail ini "{file}" bukan berjenis gambar.', 'The file "{file}" is too big. Its size cannot exceed {formattedLimit}.' => @@ -43,13 +43,13 @@ return [ 'The file "{file}" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Fail ini "{file}" terlalu kecil. Saiznya tidak boleh lebih kecil daripada {formattedLimit}.', 'The format of {attribute} is invalid.' => 'Format untuk atribut ini {attribute} tidak sah.', - 'The image "{file}" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => + 'The image "{file}" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Gambar "{file}" terlalu panjang. Panjang gambar tidak boleh lebih besar daripada {limit, number} {limit, plural, one{pixel} other{pixels}}.', - 'The image "{file}" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => + 'The image "{file}" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Gambar "{file}" terlalu lebar. Gambar tidak boleh lebih lebar daripada {limit, number} {limit, plural, one{pixel} other{pixels}}.', - 'The image "{file}" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => + 'The image "{file}" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Gambar "{file}" terlalu singkat. Panjang tidak boleh lebih singkat daripada {limit, number} {limit, plural, one{pixel} other{pixels}}.', - 'The image "{file}" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => + 'The image "{file}" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Gambar "{file}" terlalu kecil. Lebar gambar tidak boleh kurang daripada {limit, number} {limit, plural, one{pixel} other{pixels}}.', 'The requested view "{name}" was not found.' => 'Paparan yang diminta "{name}" tidak dijumpai.', 'The verification code is incorrect.' => 'Kod penyesah tidak tepat.', @@ -60,7 +60,7 @@ return [ 'View' => 'Paparan', 'Yes' => 'Ya', 'You are not allowed to perform this action.' => 'Anda tidak dibenarkan untuk mengunakan fungsi ini.', - 'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => + 'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Anda boleh memuat naik tidak lebih daripada {limit, number} {limit, plural, one{file} other{files}}.', 'in {delta, plural, =1{a day} other{# days}}' => 'dalam {delta, plural, =1{a day} other{# days}}', 'in {delta, plural, =1{a minute} other{# minutes}}' => 'dalam {delta, plural, =1{a minute} other{# minutes}}', @@ -88,11 +88,11 @@ return [ '{attribute} must be no less than {min}.' => '{attribute} tidak boleh kurang daripada {min}.', '{attribute} must be repeated exactly.' => '{attribute} mestilah diulang dengan tepat.', '{attribute} must not be equal to "{compareValue}".' => '{attribute} mestilah tidak sama dengan "{compareValue}".', - '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => + '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} mesti mengandungi sekurang-kurangnya {min, number} {min, plural, one{character} other{characters}}.', - '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => + '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} mesti mengangungi paling banyak {max, number} {max, plural, one{character} other{characters}}.', - '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => + '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} mesti mengandungi {length, number} {length, plural, one{character} other{characters}}.', '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{a day} other{# days}} lalu', '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{a minute} other{# minutes}} lalu', diff --git a/framework/messages/uz/yii.php b/framework/messages/uz/yii.php index 81fde640a0..bcbc7f3ccd 100644 --- a/framework/messages/uz/yii.php +++ b/framework/messages/uz/yii.php @@ -78,7 +78,7 @@ return [ 'View' => 'Ko`rish', 'Yes' => 'Ha', 'You are not allowed to perform this action.' => 'Sizga ushbu amalni bajarishga ruhsat berilmagan.', - 'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Siz {limit, number} {limit, plural, one{fayldan} few{fayllardan} many{fayllardan} other{fayllardan}} ko`pini yuklab ola olmaysiz.', + 'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Siz {limit, number} {limit, plural, one{fayldan} few{fayllardan} many{fayllardan} other{fayllardan}} ko`pini yuklab ola olmaysiz.', 'in {delta, plural, =1{a day} other{# days}}' => '{delta, plural, =1{kundan} one{# kundan} few{# kundan} many{# kunlardan} other{# kundan}} keyin', 'in {delta, plural, =1{a minute} other{# minutes}}' => '{delta, plural, =1{minutdan} one{# minutdan} few{# minutlardan} many{# minutdan} other{# minutlardan}} keyin', 'in {delta, plural, =1{a month} other{# months}}' => '{delta, plural, =1{oydan} one{# oydan} few{# oydan} many{# oylardan} other{# oylardan}} keyin', diff --git a/framework/rbac/migrations/schema-oci.sql b/framework/rbac/migrations/schema-oci.sql index b913e8659d..1fc799298f 100644 --- a/framework/rbac/migrations/schema-oci.sql +++ b/framework/rbac/migrations/schema-oci.sql @@ -36,7 +36,7 @@ create table "auth_item" foreign key ("rule_name") references "auth_rule"("name") on delete set null, primary key ("name") ); --- adds oracle specific index to auth_item +-- adds oracle specific index to auth_item CREATE INDEX auth_type_index ON "auth_item"("type"); create table "auth_item_child" diff --git a/framework/web/AssetConverter.php b/framework/web/AssetConverter.php index e1ab5dc30b..a311a9fcbd 100644 --- a/framework/web/AssetConverter.php +++ b/framework/web/AssetConverter.php @@ -89,7 +89,7 @@ class AssetConverter extends Component implements AssetConverterInterface protected function runCommand($command, $basePath, $asset, $result) { $command = Yii::getAlias($command); - + $command = strtr($command, [ '{from}' => escapeshellarg("$basePath/$asset"), '{to}' => escapeshellarg("$basePath/$result"), diff --git a/framework/web/Controller.php b/framework/web/Controller.php index 386666020c..5b91329d97 100644 --- a/framework/web/Controller.php +++ b/framework/web/Controller.php @@ -167,7 +167,7 @@ class Controller extends \yii\base\Controller } return true; } - + return false; } diff --git a/framework/web/MultipartFormDataParser.php b/framework/web/MultipartFormDataParser.php index 12e569c4ec..7b34f06dd7 100644 --- a/framework/web/MultipartFormDataParser.php +++ b/framework/web/MultipartFormDataParser.php @@ -143,7 +143,7 @@ class MultipartFormDataParser extends Object implements RequestParserInterface } list($headers, $value) = preg_split("/\\R\\R/", $bodyPart, 2); $headers = $this->parseHeaders($headers); - + if (!isset($headers['content-disposition']['name'])) { continue; } diff --git a/framework/web/XmlResponseFormatter.php b/framework/web/XmlResponseFormatter.php index c07a07ca0c..441a522a71 100644 --- a/framework/web/XmlResponseFormatter.php +++ b/framework/web/XmlResponseFormatter.php @@ -106,7 +106,7 @@ class XmlResponseFormatter extends Component implements ResponseFormatterInterfa } elseif (is_object($data)) { if ($this->useObjectTags) { $child = new DOMElement(StringHelper::basename(get_class($data))); - $element->appendChild($child); + $element->appendChild($child); } else { $child = $element; } diff --git a/framework/widgets/FragmentCache.php b/framework/widgets/FragmentCache.php index 5b3d17a13c..a542ffbb5f 100644 --- a/framework/widgets/FragmentCache.php +++ b/framework/widgets/FragmentCache.php @@ -105,7 +105,7 @@ class FragmentCache extends Widget echo $content; } elseif ($this->cache instanceof Cache) { array_pop($this->getView()->cacheStack); - + $content = ob_get_clean(); if ($content === false || $content === '') { return; diff --git a/framework/widgets/LinkPager.php b/framework/widgets/LinkPager.php index 484404ec61..16d6f1149c 100644 --- a/framework/widgets/LinkPager.php +++ b/framework/widgets/LinkPager.php @@ -229,7 +229,7 @@ class LinkPager extends Widget if ($disabled) { Html::addCssClass($options, $this->disabledPageCssClass); $tag = ArrayHelper::remove($this->disabledListItemSubTagOptions, 'tag', 'span'); - + return Html::tag('li', Html::tag($tag, $label, $this->disabledListItemSubTagOptions), $options); } $linkOptions = $this->linkOptions; diff --git a/tests/data/ar/Animal.php b/tests/data/ar/Animal.php index dfb8ba2a0f..9c9137b698 100644 --- a/tests/data/ar/Animal.php +++ b/tests/data/ar/Animal.php @@ -37,7 +37,7 @@ class Animal extends ActiveRecord } /** - * + * * @param type $row * @return \yiiunit\data\ar\Animal */ diff --git a/tests/data/ar/Cat.php b/tests/data/ar/Cat.php index 831cb8d6fb..5acc882c17 100644 --- a/tests/data/ar/Cat.php +++ b/tests/data/ar/Cat.php @@ -18,7 +18,7 @@ class Cat extends Animal { /** - * + * * @param self $record * @param array $row */ diff --git a/tests/data/ar/Dog.php b/tests/data/ar/Dog.php index 9422cc5bed..ff1e227d52 100644 --- a/tests/data/ar/Dog.php +++ b/tests/data/ar/Dog.php @@ -18,7 +18,7 @@ class Dog extends Animal { /** - * + * * @param self $record * @param array $row */ diff --git a/tests/data/ar/Type.php b/tests/data/ar/Type.php index 2f8051bff4..0c5c69fe05 100644 --- a/tests/data/ar/Type.php +++ b/tests/data/ar/Type.php @@ -29,4 +29,3 @@ class Type extends ActiveRecord return 'type'; } } - \ No newline at end of file diff --git a/tests/data/codeclimate/phpmd_ruleset.xml b/tests/data/codeclimate/phpmd_ruleset.xml index 3182294457..fa840090fb 100644 --- a/tests/data/codeclimate/phpmd_ruleset.xml +++ b/tests/data/codeclimate/phpmd_ruleset.xml @@ -4,7 +4,7 @@ xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd" xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd"> Custom PHPMD settings for naming, cleancode and controversial rulesets - + @@ -19,14 +19,14 @@ - + - + diff --git a/tests/framework/console/controllers/AssetControllerTest.php b/tests/framework/console/controllers/AssetControllerTest.php index e252410b81..d3a38e09ce 100644 --- a/tests/framework/console/controllers/AssetControllerTest.php +++ b/tests/framework/console/controllers/AssetControllerTest.php @@ -736,7 +736,7 @@ EOL; $this->runAssetControllerAction('compress', [$configFile, $bundleFile]); $bundlesConfig = require $bundleFile; - + $this->assertEquals($assetBundleOverrideConfig['css'], $bundlesConfig[$assetBundleClassName]['css']); $this->assertEquals($assetBundleOverrideConfig['js'], $bundlesConfig[$assetBundleClassName]['js']); } diff --git a/tests/framework/console/controllers/DbMessageControllerTest.php b/tests/framework/console/controllers/DbMessageControllerTest.php index f426532820..28b9c6d946 100644 --- a/tests/framework/console/controllers/DbMessageControllerTest.php +++ b/tests/framework/console/controllers/DbMessageControllerTest.php @@ -11,12 +11,12 @@ class DbMessageControllerTest extends BaseMessageControllerTest { protected static $driverName = 'mysql'; protected static $database; - + /** * @var Connection */ protected static $db; - + protected static function runConsoleAction($route, $params = []) { if (Yii::$app === null) { @@ -41,7 +41,7 @@ class DbMessageControllerTest extends BaseMessageControllerTest ob_end_clean(); } } - + public static function setUpBeforeClass() { parent::setUpBeforeClass(); @@ -55,7 +55,7 @@ class DbMessageControllerTest extends BaseMessageControllerTest static::runConsoleAction('migrate/up', ['migrationPath' => '@yii/i18n/migrations/', 'interactive' => false]); } - + public static function tearDownAfterClass() { static::runConsoleAction('migrate/down', ['migrationPath' => '@yii/i18n/migrations/', 'interactive' => false]); @@ -65,13 +65,13 @@ class DbMessageControllerTest extends BaseMessageControllerTest Yii::$app = null; parent::tearDownAfterClass(); } - + public function tearDown() { parent::tearDown(); Yii::$app = null; } - + /** * @throws \yii\base\InvalidParamException * @throws \yii\db\Exception @@ -97,7 +97,7 @@ class DbMessageControllerTest extends BaseMessageControllerTest } return static::$db; } - + /** * @inheritdoc */ @@ -148,9 +148,9 @@ class DbMessageControllerTest extends BaseMessageControllerTest 't2.language' => $this->language, ])->all(static::$db), 'message', 'translation'); } - + // DbMessage tests variants: - + /** * Source is marked instead of translation. * @depends testMerge @@ -170,7 +170,7 @@ class DbMessageControllerTest extends BaseMessageControllerTest $out = $this->runMessageControllerAction('extract', [$this->configFileName]); $obsoleteMessage = '@@obsolete message@@'; - + $messages = $this->loadMessages($category); $this->assertArrayHasKey($obsoleteMessage, $messages, "Obsolete message should not be removed. Command output:\n\n" . $out); diff --git a/tests/framework/db/ActiveRecordTest.php b/tests/framework/db/ActiveRecordTest.php index 304bfb6737..ec774115b6 100644 --- a/tests/framework/db/ActiveRecordTest.php +++ b/tests/framework/db/ActiveRecordTest.php @@ -973,25 +973,25 @@ abstract class ActiveRecordTest extends DatabaseTestCase public function testInverseOfDynamic() { $customer = Customer::findOne(1); - + // request the inverseOf relation without explicitly (eagerly) loading it $orders2 = $customer->getOrders2()->all(); $this->assertSame($customer, $orders2[0]->customer2); - + $orders2 = $customer->getOrders2()->one(); $this->assertSame($customer, $orders2->customer2); - + // request the inverseOf relation while also explicitly eager loading it (while possible, this is of course redundant) $orders2 = $customer->getOrders2()->with('customer2')->all(); $this->assertSame($customer, $orders2[0]->customer2); - + $orders2 = $customer->getOrders2()->with('customer2')->one(); $this->assertSame($customer, $orders2->customer2); - + // request the inverseOf relation as array $orders2 = $customer->getOrders2()->asArray()->all(); $this->assertEquals($customer['id'], $orders2[0]['customer2']['id']); - + $orders2 = $customer->getOrders2()->asArray()->one(); $this->assertEquals($customer['id'], $orders2['customer2']['id']); } diff --git a/tests/framework/db/CommandTest.php b/tests/framework/db/CommandTest.php index e7e4cb5f7e..b7ebad2747 100644 --- a/tests/framework/db/CommandTest.php +++ b/tests/framework/db/CommandTest.php @@ -141,7 +141,7 @@ abstract class CommandTest extends DatabaseTestCase if (defined('HHVM_VERSION') && $this->driverName === 'pgsql') { $this->markTestSkipped('HHVMs PgSQL implementation has some specific behavior that breaks some parts of this test.'); } - + $db = $this->getConnection(); // bindParam diff --git a/tests/framework/db/pgsql/CommandTest.php b/tests/framework/db/pgsql/CommandTest.php index 19da792d5c..6d8eff6dc7 100644 --- a/tests/framework/db/pgsql/CommandTest.php +++ b/tests/framework/db/pgsql/CommandTest.php @@ -77,7 +77,7 @@ class CommandTest extends \yiiunit\framework\db\CommandTest if (defined('HHVM_VERSION')) { $this->markTestSkipped('HHVMs PgSQL implementation does not seem to support blob colums in the way they are used here.'); } - + $db = $this->getConnection(); $command = $db->createCommand()->insert('type', [ diff --git a/tests/framework/filters/auth/AuthMethodTest.php b/tests/framework/filters/auth/AuthMethodTest.php index eebc00437d..c02bd448ee 100644 --- a/tests/framework/filters/auth/AuthMethodTest.php +++ b/tests/framework/filters/auth/AuthMethodTest.php @@ -47,9 +47,9 @@ class AuthMethodTest extends TestCase $controller = new Controller('test', Yii::$app); return new Action('index', $controller, $config); } - + // Tests : - + public function testBeforeAction() { $action = $this->createAction(); diff --git a/tests/framework/helpers/StringHelperTest.php b/tests/framework/helpers/StringHelperTest.php index 12689d51a2..b00564115c 100644 --- a/tests/framework/helpers/StringHelperTest.php +++ b/tests/framework/helpers/StringHelperTest.php @@ -111,7 +111,7 @@ class StringHelperTest extends TestCase $this->assertEquals('This is a test...', StringHelper::truncate('This is a test sentance', 14, '...', null, true)); $this->assertEquals('This is a test for...', StringHelper::truncate('This is a test for a sentance', 18, '...', null, true)); $this->assertEquals('This is a test for...', StringHelper::truncate('This is a test for a sentance', 18, '...', null, true)); - + $this->assertEquals('This is a test...', StringHelper::truncate('This is a test sentance', 14, '...', null, true)); $this->assertEquals('This is a test...', StringHelper::truncate('This is a test sentance', 14, '...', null, true)); $this->assertEquals('This is a test for...', StringHelper::truncate('This is a test for a sentance', 18, '...', null, true)); @@ -136,7 +136,7 @@ class StringHelperTest extends TestCase $this->assertEquals('This is a test...', StringHelper::truncateWords('This is a test sentance', 4, '...', true)); $this->assertEquals('This is a test for...', StringHelper::truncateWords('This is a test for a sentance', 5, '...', true)); - $this->assertEquals('This is a test for...', StringHelper::truncateWords('This is a test for a sentance', 5, '...', true)); + $this->assertEquals('This is a test for...', StringHelper::truncateWords('This is a test for a sentance', 5, '...', true)); } /** diff --git a/tests/framework/helpers/VarDumperTest.php b/tests/framework/helpers/VarDumperTest.php index cf559e503f..3e34ad3cd1 100644 --- a/tests/framework/helpers/VarDumperTest.php +++ b/tests/framework/helpers/VarDumperTest.php @@ -26,7 +26,7 @@ class VarDumperTest extends TestCase $exportResult = VarDumper::export($incompleteObj); $this->assertContains("nonExistingClass", $exportResult); } - + public function testDumpObject() { $obj = new \StdClass(); diff --git a/tests/framework/log/SyslogTargetTest.php b/tests/framework/log/SyslogTargetTest.php index 2daa91f901..4a61b82033 100644 --- a/tests/framework/log/SyslogTargetTest.php +++ b/tests/framework/log/SyslogTargetTest.php @@ -27,7 +27,7 @@ namespace yiiunit\framework\log { /** * Class SyslogTargetTest - * + * * @package yiiunit\framework\log * @group log */ @@ -35,7 +35,7 @@ namespace yiiunit\framework\log { { /** * Array of static functions - * + * * @var array */ static $functions = []; diff --git a/tests/framework/mutex/MysqlMutexTest.php b/tests/framework/mutex/MysqlMutexTest.php index a339064321..2431279c1a 100644 --- a/tests/framework/mutex/MysqlMutexTest.php +++ b/tests/framework/mutex/MysqlMutexTest.php @@ -10,7 +10,7 @@ use yiiunit\framework\db\DatabaseTestCase; * * @group mutex * @group mysql - * + * * @package yiiunit\framework\mutex */ class MysqlMutexTest extends DatabaseTestCase diff --git a/tests/framework/mutex/PgsqlMutexTest.php b/tests/framework/mutex/PgsqlMutexTest.php index 3eef4057f2..195dd032bf 100644 --- a/tests/framework/mutex/PgsqlMutexTest.php +++ b/tests/framework/mutex/PgsqlMutexTest.php @@ -10,7 +10,7 @@ use yiiunit\framework\db\DatabaseTestCase; * * @group mutex * @group pgsql - * + * * @package yiiunit\framework\mutex */ class PgsqlMutexTest extends DatabaseTestCase diff --git a/tests/framework/web/DbSessionTest.php b/tests/framework/web/DbSessionTest.php index 57eb066de5..2ab01c9dba 100644 --- a/tests/framework/web/DbSessionTest.php +++ b/tests/framework/web/DbSessionTest.php @@ -40,7 +40,7 @@ class DbSessionTest extends TestCase 'user_id' => 'integer', ])->execute(); } - + // Tests : public function testReadWrite() @@ -117,7 +117,7 @@ class DbSessionTest extends TestCase return substr($version, 15); }, (new Query())->select(['version'])->from('migration')->column()); } - + public function testMigration() { $this->mockWebApplication([ diff --git a/tests/framework/web/UserTest.php b/tests/framework/web/UserTest.php index fdbfaa2dcd..8b2838c9aa 100644 --- a/tests/framework/web/UserTest.php +++ b/tests/framework/web/UserTest.php @@ -97,7 +97,7 @@ class UserTest extends TestCase $this->assertFalse(Yii::$app->user->can('doSomething')); } - + public function testCookieCleanup() { global $cookiesMock; @@ -177,7 +177,7 @@ class UserTest extends TestCase ]; $this->mockWebApplication($appConfig); - + $user = Yii::$app->user; $this->reset(); @@ -354,7 +354,7 @@ class MockResponse extends \yii\web\Response public function getCookies() { global $cookiesMock; - + return $cookiesMock; } } diff --git a/tests/framework/web/XmlResponseFormatterTest.php b/tests/framework/web/XmlResponseFormatterTest.php index b0d530c8ef..519d96cf43 100644 --- a/tests/framework/web/XmlResponseFormatterTest.php +++ b/tests/framework/web/XmlResponseFormatterTest.php @@ -75,21 +75,21 @@ class XmlResponseFormatterTest extends FormatterTest public function formatTraversableObjectDataProvider() { $expectedXmlForStack = ''; - + $postsStack = new \SplStack(); - + $postsStack->push(new Post(915, 'record1')); $expectedXmlForStack = '915record1' . $expectedXmlForStack; - + $postsStack->push(new Post(456, 'record2')); $expectedXmlForStack = '456record2' . $expectedXmlForStack; - + $data = [ [$postsStack, "$expectedXmlForStack\n"] ]; - + return $this->addXmlHead($data); } diff --git a/tests/framework/widgets/LinkPagerTest.php b/tests/framework/widgets/LinkPagerTest.php index 1263c4ffbf..81ad3d3f9c 100644 --- a/tests/framework/widgets/LinkPagerTest.php +++ b/tests/framework/widgets/LinkPagerTest.php @@ -56,7 +56,7 @@ class LinkPagerTest extends \yiiunit\TestCase static::assertNotContains('
  • ', $output); static::assertNotContains('
  • ', $output); } - + public function testDisabledPageElementOptions() { $pagination = new Pagination(); @@ -68,10 +68,10 @@ class LinkPagerTest extends \yiiunit\TestCase 'pagination' => $pagination, 'disabledListItemSubTagOptions' => ['class' => 'foo-bar'], ]); - + static::assertContains('«', $output); } - + public function testDisabledPageElementOptionsWithTagOption() { $pagination = new Pagination(); @@ -83,7 +83,7 @@ class LinkPagerTest extends \yiiunit\TestCase 'pagination' => $pagination, 'disabledListItemSubTagOptions' => ['class' => 'foo-bar', 'tag' => 'div'], ]); - + static::assertContains('
    «
    ', $output); } } diff --git a/tests/js/tests/yii.test.js b/tests/js/tests/yii.test.js index a18776b36b..bf7da4d202 100644 --- a/tests/js/tests/yii.test.js +++ b/tests/js/tests/yii.test.js @@ -245,9 +245,9 @@ describe('yii', function () { it(message, function () { confirmed = confirmChoice; - + var result = yii.confirm('Are you sure?', setOk ? okSpy : undefined, setCancel ? cancelSpy : undefined); - + assert.isUndefined(result); assert.isTrue(windowConfirmStub.calledOnce); assert.deepEqual(windowConfirmStub.getCall(0).args, ['Are you sure?']); From aeb6231d921aa989606c03107d3a9efe71e7b4b5 Mon Sep 17 00:00:00 2001 From: Bob Olde Hampsink Date: Sun, 19 Feb 2017 15:35:59 +0100 Subject: [PATCH 20/75] Fixes #13582: PK column in `yii\db\pgsql\resetSequence()` was not quoted properly --- framework/CHANGELOG.md | 1 + framework/db/pgsql/QueryBuilder.php | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 2332cf7173..be645bfdaf 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -14,6 +14,7 @@ Yii Framework 2 Change Log - Bug #11404: `yii\base\Model::loadMultiple()` returns true even if `yii\base\Model::load()` returns false (zvook) - Bug #13513: Fixed RBAC migration to work correctly on Oracle DBMS (silverfire) - Enh #13550: Refactored unset call order in `yii\di\ServiceLocator::set()` (Lanrik) +- Bug #13582: PK column in `yii\db\pgsql\resetSequence()` was not quoted properly (boboldehampsink) - Bug #13592: Fixes Oracle’s `yii\db\oci\Schema::setTransactionIsolationLevel()` (sergeymakinen) - Bug #13594: Fixes insufficient quoting in `yii\db\QueryBuilder::prepareInsertSelectSubQuery()` (sergeymakinen) - Enh #13576: Added support of `srcset` to `yii\helpers\Html::img()` (Kolyunya) diff --git a/framework/db/pgsql/QueryBuilder.php b/framework/db/pgsql/QueryBuilder.php index b43fc5ace1..46e98f11db 100644 --- a/framework/db/pgsql/QueryBuilder.php +++ b/framework/db/pgsql/QueryBuilder.php @@ -164,8 +164,8 @@ class QueryBuilder extends \yii\db\QueryBuilder $sequence = $this->db->quoteTableName($table->sequenceName); $tableName = $this->db->quoteTableName($tableName); if ($value === null) { - $key = reset($table->primaryKey); - $value = "(SELECT COALESCE(MAX(\"{$key}\"),0) FROM {$tableName})+1"; + $key = $this->db->quoteColumnName(reset($table->primaryKey)); + $value = "(SELECT COALESCE(MAX({$key}),0) FROM {$tableName})+1"; } else { $value = (int) $value; } From d489e994345110e3c59c08167d7b69666e7d0505 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Sun, 19 Feb 2017 17:38:23 +0300 Subject: [PATCH 21/75] Added issue number to changelog [skip ci] --- framework/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index be645bfdaf..6716897d14 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -7,7 +7,7 @@ Yii Framework 2 Change Log - Bug #13306: Wildcard in `reloadableScripts` in `yii.js` allows 0 characters (arogachev) - Enh #13523: Plural rule for pasta (developeruz) - Bug #13538: Fixed `yii\db\BaseActiveRecord::deleteAll()` changes method signature declared by `yii\db\ActiveRecordInterface::deleteAll()` (klimov-paul) -- Bug: Fixed `yii\db\Connection::useMaster()` - Exception within callback completely disables slaves (Vovan-VE) +- Bug #13340: Fixed `yii\db\Connection::useMaster()` - Exception within callback completely disables slaves (Vovan-VE) - Enh #13278: `yii\caching\DbQueryDependency` created allowing specification of the cache dependency via `yii\db\QueryInterface` (klimov-paul) - Bug #11230: Include `defaultRoles` in `yii\rbac\DbManager->getRolesByUser()` results (developeruz) - Bug #13343: Fixed `yii\i18n\Formatter::asTime()` to process time-only values without time zone conversion (bizley) From ecd2dc0d1b32a51ae5cd8214ce569ba210df590d Mon Sep 17 00:00:00 2001 From: Dmitry Klyukin Date: Mon, 20 Feb 2017 00:40:45 +0700 Subject: [PATCH 22/75] Model loadMultiple test (#13585) --- tests/framework/base/ModelTest.php | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/framework/base/ModelTest.php b/tests/framework/base/ModelTest.php index 175b45ff75..726bfc293f 100644 --- a/tests/framework/base/ModelTest.php +++ b/tests/framework/base/ModelTest.php @@ -102,6 +102,41 @@ class ModelTest extends TestCase $this->assertEquals('', $model->firstName); } + public function testLoadMultiple() + { + $data = [ + ['firstName' => 'Thomas', 'lastName' => 'Anderson'], + ['firstName' => 'Agent', 'lastName' => 'Smith'], + ]; + + Speaker::$formName = ''; + $neo = new Speaker(); + $neo->setScenario('test'); + $smith = new Speaker(); + $smith->setScenario('test'); + $this->assertTrue(Speaker::loadMultiple([$neo, $smith], $data)); + $this->assertEquals('Thomas', $neo->firstName); + $this->assertEquals('Smith', $smith->lastName); + + Speaker::$formName = 'Speaker'; + $neo = new Speaker(); + $neo->setScenario('test'); + $smith = new Speaker(); + $smith->setScenario('test'); + $this->assertTrue(Speaker::loadMultiple([$neo, $smith], ['Speaker' => $data], 'Speaker')); + $this->assertEquals('Thomas', $neo->firstName); + $this->assertEquals('Smith', $smith->lastName); + + Speaker::$formName = 'Speaker'; + $neo = new Speaker(); + $neo->setScenario('test'); + $smith = new Speaker(); + $smith->setScenario('test'); + $this->assertFalse(Speaker::loadMultiple([$neo, $smith], ['Speaker' => $data], 'Morpheus')); + $this->assertEquals('', $neo->firstName); + $this->assertEquals('', $smith->lastName); + } + public function testActiveAttributes() { // by default mass assignment doesn't work at all From 7d82bbcd372be7edb8651a653aea430ece38e736 Mon Sep 17 00:00:00 2001 From: Nikolay Oleynikov Date: Mon, 20 Feb 2017 12:00:00 +0300 Subject: [PATCH 23/75] Fixes #13467: `yii\data\ActiveDataProvider` no longer queries models if models count is zero --- framework/CHANGELOG.md | 1 + framework/data/ActiveDataProvider.php | 3 +++ .../framework/data/ActiveDataProviderTest.php | 21 ++++++++++++++++ tests/framework/db/UnqueryableQueryMock.php | 25 +++++++++++++++++++ 4 files changed, 50 insertions(+) create mode 100644 tests/framework/db/UnqueryableQueryMock.php diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 6716897d14..27672f3c2d 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -18,6 +18,7 @@ Yii Framework 2 Change Log - Bug #13592: Fixes Oracle’s `yii\db\oci\Schema::setTransactionIsolationLevel()` (sergeymakinen) - Bug #13594: Fixes insufficient quoting in `yii\db\QueryBuilder::prepareInsertSelectSubQuery()` (sergeymakinen) - Enh #13576: Added support of `srcset` to `yii\helpers\Html::img()` (Kolyunya) +- Enh #13467: `yii\data\ActiveDataProvider` no longer queries models if models count is zero (kLkA, Kolyunya) 2.0.11.2 February 08, 2017 diff --git a/framework/data/ActiveDataProvider.php b/framework/data/ActiveDataProvider.php index 3b4aecd48e..691400b33a 100644 --- a/framework/data/ActiveDataProvider.php +++ b/framework/data/ActiveDataProvider.php @@ -104,6 +104,9 @@ class ActiveDataProvider extends BaseDataProvider $query = clone $this->query; if (($pagination = $this->getPagination()) !== false) { $pagination->totalCount = $this->getTotalCount(); + if ($pagination->totalCount === 0) { + return []; + } $query->limit($pagination->getLimit())->offset($pagination->getOffset()); } if (($sort = $this->getSort()) !== false) { diff --git a/tests/framework/data/ActiveDataProviderTest.php b/tests/framework/data/ActiveDataProviderTest.php index 94f52e80d9..ae74ee54cb 100644 --- a/tests/framework/data/ActiveDataProviderTest.php +++ b/tests/framework/data/ActiveDataProviderTest.php @@ -7,12 +7,14 @@ namespace yiiunit\framework\data; +use yii\base\InvalidCallException; use yii\data\ActiveDataProvider; use yii\db\Query; use yiiunit\data\ar\ActiveRecord; use yiiunit\data\ar\Customer; use yiiunit\data\ar\Item; use yiiunit\framework\db\DatabaseTestCase; +use yiiunit\framework\db\UnqueryableQueryMock; use yiiunit\data\ar\Order; /** @@ -177,4 +179,23 @@ abstract class ActiveDataProviderTest extends DatabaseTestCase $provider->refresh(); $this->assertEquals(2, count($provider->getModels())); } + + public function testDoesNotPerformQueryWhenHasNoModels() + { + $query = new UnqueryableQueryMock; + $provider = new ActiveDataProvider([ + 'db' => $this->getConnection(), + 'query' => $query->from('order')->where('0=1'), + ]); + $pagination = $provider->getPagination(); + $this->assertEquals(0, $pagination->getPageCount()); + + try { + $this->assertCount(0, $provider->getModels()); + } catch (InvalidCallException $exception) { + $this->fail('An excessive models query was executed.'); + } + + $this->assertEquals(0, $pagination->getPageCount()); + } } diff --git a/tests/framework/db/UnqueryableQueryMock.php b/tests/framework/db/UnqueryableQueryMock.php new file mode 100644 index 0000000000..87172be484 --- /dev/null +++ b/tests/framework/db/UnqueryableQueryMock.php @@ -0,0 +1,25 @@ + Date: Mon, 20 Feb 2017 10:33:36 +0100 Subject: [PATCH 24/75] Translated caching-data.md into FR [skip ci] (#13626) --- docs/guide-fr/caching-data.md | 345 ++++++++++++++++++++++++++++++++++ 1 file changed, 345 insertions(+) create mode 100644 docs/guide-fr/caching-data.md diff --git a/docs/guide-fr/caching-data.md b/docs/guide-fr/caching-data.md new file mode 100644 index 0000000000..f1071ba533 --- /dev/null +++ b/docs/guide-fr/caching-data.md @@ -0,0 +1,345 @@ +Mise en cache de données +======================== + +La mise en cache de données consiste à stocker quelques variables PHP dans un cache et à les y retrouver plus tard. +C'est également la base pour des fonctionnalités de mise en cache plus avancées, comme la [mise en cache de requêtes](#query-caching) et la [mise en cache de pages](caching-page.md). + +Le code qui suit est un modèle d'utilisation typique de la mise en cache de données, dans lequel `cache` fait référence à un [composant de mise en cache](#cache-components) : + + +```php +// tente de retrouver la donnée $data dans le cache +$data = $cache->get($key); + +if ($data === false) { + // la donnée $data n'a pas été trouvée dans le cache, on la recalcule + $data = $this->calculateSomething(); + + // stocke la donnée $data dans le cache de façon à la retrouver la prochaine fois + $cache->set($key, $data); +} + +// la donnée $data est disponible ici +``` + +Depuis la version 2.0.11, le [composant de mise en cache](#cache-components) fournit la méthode [[yii\caching\Cache::getOrSet()|getOrSet()]] qui simplifie le code pour l'obtention, le calcul et le stockage des données. Le code qui suit fait exactement la même chose que l'exemple précédent : + +```php +$data = $cache->getOrSet($key, function () { + return $this->calculateSomething(); +}); +``` + +Lorsque le cache possède une donnée associée à la clé `$key`, la valeur en cache est retournée. Autrement, la fonction anonyme passée est exécutée pour calculer cette valeur qui est mise en cache et retournée. + +Si la fonction anonyme a besoin de quelques données en dehors de la portée courante, vous pouvez les passer en utilisant l'instruction `use`. Par exemple : + +```php +$user_id = 42; +$data = $cache->getOrSet($key, function () use ($user_id) { + return $this->calculateSomething($user_id); +}); +``` + +> Note : la méthode [[yii\caching\Cache::getOrSet()|getOrSet()]] prend également en charge la durée et les dépendances. + Reportez-vous à [Expiration de la mise en cache](#cache-expiration) et à [Dépendances de mise en cache](#cache-dependencies) pour en savoir plus. + + +## Composants de mise en cache + +La mise en cache s'appuie sur ce qu'on appelle les *composants de mise en cache* qui représentent des supports de mise en cache tels que les mémoires, les fichiers et les bases de données. + +Les composants de mise en cache sont généralement enregistrés en tant que [composants d'application](structure-application-components.md) de façon à ce qu'ils puissent être configurables et accessibles globalement. Le code qui suit montre comment configurer le composant d'application `cache` pour qu'il utilise [memcached](http://memcached.org/) avec deux serveurs de cache : + +```php +'components' => [ + 'cache' => [ + 'class' => 'yii\caching\MemCache', + 'servers' => [ + [ + 'host' => 'server1', + 'port' => 11211, + 'weight' => 100, + ], + [ + 'host' => 'server2', + 'port' => 11211, + 'weight' => 50, + ], + ], + ], +], +``` + +Vous pouvez accéder au composant de mise en cache configuré ci-dessus en utilisant l'expression `Yii::$app->cache`. + +Comme tous les composants de mise en cache prennent en charge le même jeux d'API, vous pouvez remplacer le composant de mise en cache sous-jacent par un autre en le reconfigurant dans la configuration de l'application, cela sans modifier le code qui utilise le cache. Par exemple, vous pouvez modifier le code ci-dessus pour utiliser [[yii\caching\ApcCache|APC cache]] : + + +```php +'components' => [ + 'cache' => [ + 'class' => 'yii\caching\ApcCache', + ], +], +``` + +> Conseil : vous pouvez enregistrer de multiples composants d'application de mise en cache. Le composant nommé `cache` est utilisé par défaut par de nombreuses classes dépendantes d'un cache (p. ex.[[yii\web\UrlManager]]). + + +### Supports de stockage pour cache pris en charge + +Yii prend en charge un large panel de supports de stockage pour cache. Ce qui suit est un résumé : + +* [[yii\caching\ApcCache]]: utilise l'extension PHP [APC](http://php.net/manual/en/book.apc.php). Cette option peut être considérée comme la plus rapide lorsqu'on utilise un cache pour une grosse application centralisée (p. ex. un serveur, pas d'équilibrage de charge dédié, etc.). +* [[yii\caching\DbCache]]: utilise une table de base de données pour stocker les données en cache. Pour utiliser ce cache, vous devez créer une table comme spécifié dans [[yii\caching\DbCache::cacheTable]]. +* [[yii\caching\DummyCache]]: tient lieu de cache à remplacer qui n'assure pas de mise en cache réelle. Le but de ce composant est de simplifier le code qui a besoin de vérifier la disponibilité du cache. Par exemple, lors du développement ou si le serveur ne dispose pas de la prise en charge d'un cache, vous pouvez configurer un composant de mise en cache pour qu'il utilise ce cache. Lorsque la prise en charge réelle de la mise en cache est activée, vous pouvez basculer sur le composant de mise en cache correspondant. Dans les deux cas, vous pouvez utiliser le même code `Yii::$app->cache->get($key)` pour essayer de retrouver les données du cache sans vous préoccuper du fait que `Yii::$app->cache` puisse être `null`. +* [[yii\caching\FileCache]]: utilise des fichiers standards pour stocker les données en cache. Cela est particulièrement adapté à la mise en cache de gros blocs de données, comme le contenu d'une page. +* [[yii\caching\MemCache]]: utilise le [memcache](http://php.net/manual/en/book.memcache.php) PHP et l'extension [memcached](http://php.net/manual/en/book.memcached.php). Cette option peut être considérée comme la plus rapide lorsqu'on utilise un cache dans des applications distribuées (p. ex. avec plusieurs serveurs, l'équilibrage de charge, etc.). +* [[yii\redis\Cache]]: met en œuvre un composant de mise en cache basé sur un stockage clé-valeur [Redis](http://redis.io/) + (une version de redis égale ou supérieure à 2.6.12 est nécessaire). +* [[yii\caching\WinCache]]: utilise le [WinCache](http://iis.net/downloads/microsoft/wincache-extension) PHP + ([voir aussi l'extension](http://php.net/manual/en/book.wincache.php)). +* [[yii\caching\XCache]]: utilise l'extension PHP [XCache](http://xcache.lighttpd.net/). +* [[yii\caching\ZendDataCache]]: utilise le + [cache de données Zend](http://files.zend.com/help/Zend-Server-6/zend-server.htm#data_cache_component.htm) + en tant que médium de cache sous-jacent. + + +> Conseil : vous pouvez utiliser différents supports de stockage pour cache dans la même application. Une stratégie courante est d'utiliser un support de stockage pour cache basé sur la mémoire pour stocker des données de petite taille mais d'usage constant (p. ex. des données statistiques), et d'utiliser des supports de stockage pour cache basés sur des fichiers ou des bases de données pour stocker des données volumineuses et utilisées moins souvent (p. ex. des contenus de pages). + + +## Les API Cache + +Tous les composants de mise en cache dérivent de la même classe de base [[yii\caching\Cache]] et par conséquent prennent en charge les API suivantes : + +* [[yii\caching\Cache::get()|get()]]: retrouve une donnée dans le cache identifiée par une clé spécifiée. Une valeur `false` (faux) est retournée si la donnée n'est pas trouvée dans le cache ou si elle a expiré ou été invalidée. +* [[yii\caching\Cache::set()|set()]]: stocke une donnée sous une clé dans le cache. +* [[yii\caching\Cache::add()|add()]]: stocke une donnée identifiée par une clé dans le cache si la clé n'existe pas déjà dans le cache. +* [[yii\caching\Cache::getOrSet()|getOrSet()]]: retrouve une donnée dans le cache identifiée par une clé spécifiée ou exécute la fonction de rappel passée, stocke la valeur retournée par cette fonction dans le cache sous cette clé et retourne la donnée. +* [[yii\caching\Cache::multiGet()|multiGet()]]: retrouve de multiples données dans le cache identifiées par les clés spécifiées. +* [[yii\caching\Cache::multiSet()|multiSet()]]: stocke de multiples données dans le cache. Chaque donnée est identifiée par une clé. +* [[yii\caching\Cache::multiAdd()|multiAdd()]]: stocke de multiples données dans le cache. Chaque donnée est identifiée par une clé. Si la clé existe déjà dans le cache, la donnée est ignorée. +* [[yii\caching\Cache::exists()|exists()]]: retourne une valeur indiquant si la clé spécifiée existe dans le cache. +* [[yii\caching\Cache::delete()|delete()]]: retire du cache une donnée identifiée par une clé. +* [[yii\caching\Cache::flush()|flush()]]: retire toutes les données du cache. + +> Note : ne mettez pas directement en cache une valeur booléenne `false` parce que la méthode [[yii\caching\Cache::get()|get()]] utilise la valeur `false` pour indiquer que la donnée n'a pas été trouvée dans le cache. Au lieu de cela, vous pouvez placer cette donnée dans un tableau et mettre ce tableau en cache pour éviter le problème. + +Quelques supports de cache, tels que MemCache, APC, prennent en charge la récupération de multiples valeurs en cache en mode « batch » (lot), ce qui réduit la surcharge occasionnée par la récupération des données en cache. Les API [[yii\caching\Cache::multiGet()|multiGet()]] et [[yii\caching\Cache::multiAdd()|multiAdd()]] sont fournies pour exploiter cette fonctionnalité. Dans le cas où le support de cache sous-jacent ne prend pas en charge cette fonctionnalité, elle est simulée. +Comme [[yii\caching\Cache]] implémente `ArrayAccess`, un composant de mise en cache peut être utilisé comme un tableau. En voici quelques exemples : + +```php +$cache['var1'] = $value1; // équivalent à : $cache->set('var1', $value1); +$value2 = $cache['var2']; // équivalent à : $value2 = $cache->get('var2'); +``` + + +### Clés de cache + +Chacune des données stockée dans le cache est identifiée de manière unique par une clé. Lorsque vous stockez une donnée dans le cache, vous devez spécifier une clé qui lui est attribuée. Plus tard, pour récupérer la donnée, vous devez fournir cette clé. + +Vous pouvez utiliser une chaîne de caractères ou une valeur arbitraire en tant que clé de cache. Lorsqu'il ne s'agit pas d'une chaîne de caractères, elle est automatiquement sérialisée sous forme de chaîne de caractères. + +Une stratégie courante pour définir une clé de cache consiste à inclure tous les facteurs déterminants sous forme de tableau. Par exemple,[[yii\db\Schema]] utilise la clé suivante par mettre en cache les informations de schéma d'une table de base de données : + +```php +[ + __CLASS__, // schema class name + $this->db->dsn, // DB connection data source name + $this->db->username, // DB connection login user + $name, // table name +]; +``` + +Comme vous le constatez, la clé inclut toutes les informations nécessaires pour spécifier de manière unique une table de base de données. + +> Note : les valeurs stockées dans le cache via [[yii\caching\Cache::multiSet()|multiSet()]] ou [[yii\caching\Cache::multiAdd()|multiAdd()]] peuvent n'avoir que des clés sous forme de chaînes de caractères ou de nombres entiers. Si vous avez besoin de définir des clés plus complexes, stockez la valeur séparément via [[yii\caching\Cache::set()|set()]] ou [[yii\caching\Cache::add()|add()]]. + +Lorsque le même support de cache est utilisé par différentes applications, vous devriez spécifier un préfixe de clé de cache pour chacune des applications afin d'éviter les conflits de clés de cache. Cela peut être fait en configurant la propriété [[yii\caching\Cache::keyPrefix]]. Par exemple, dans la configuration de l'application vous pouvez entrer le code suivant : + +```php +'components' => [ + 'cache' => [ + 'class' => 'yii\caching\ApcCache', + 'keyPrefix' => 'myapp', // a unique cache key prefix + ], +], +``` + +Pour garantir l'interopérabilité, vous ne devez utiliser que des caractères alpha-numériques. + + +### Expiration de la mise en cache + +Une donnée stockée dans le cache y restera à jamais sauf si elle en est retirée par l'application d'une quelconque politique de mise en cache (p. ex. l'espace de mise en cache est plein et les données les plus anciennes sont retirées). Pour modifier ce comportement, vous pouvez fournir un paramètre d'expiration lors de l'appel de la fonction [[yii\caching\Cache::set()|set()]] pour stocker une donnée. Le paramètre indique pour combien de secondes la donnée restera valide dans le cache. Lorsque vous appelez la fonction [[yii\caching\Cache::get()|get()]] pour récupérer une donnée, si cette dernière a expiré, la méthode retourne `false`, pour indiquer que la donnée n'a pas été trouvée dans le cache. Par exemple, + +```php +// conserve la donnée dans le cache pour un maximum de 45 secondes +$cache->set($key, $data, 45); + +sleep(50); + +$data = $cache->get($key); +if ($data === false) { + // $data a expiré ou n'a pas été trouvée dans le cache +} +``` + +Depuis la version 2.0.11, vous pouvez définir une valeur [[yii\caching\Cache::$defaultDuration|defaultDuration]] dans la configuration de votre composant de mise en cache si vous préférez utiliser une durée de mise en cache personnalisée au lieu de la durée illimitée par défaut. Cela vous évitera d'avoir à passer la durée personnalisée à la fonction [[yii\caching\Cache::set()|set()]] à chaque fois. + + +### Dépendances de mise en cache + +En plus de la définition du temps d'expiration, les données mises en cache peuvent également être invalidées par modification de ce qu'on appelle les *dépendances de mise en cache*. +Par exemple, [[yii\caching\FileDependency]] représente la dépendance à la date de modification d'un fichier. +Lorsque cette dépendance est modifiée, cela signifie que le fichier correspondant est modifié. En conséquence, tout contenu de fichier périmé trouvé dans le cache devrait être invalidé et l'appel de la fonction [[yii\caching\Cache::get()|get()]] devrait retourner `false`. + + +Les dépendances de mise en cache sont représentées sous forme d'objets dérivés de [[yii\caching\Dependency]]. Lorsque vous appelez la fonction [[yii\caching\Cache::set()|set()]] pour stocker une donnée dans le cache, vous pouvez lui passer un objet de dépendance (« Dependency ») associé. Par exemple, + +```php +// Crée une dépendance à la date de modification du fichier example.txt +$dependency = new \yii\caching\FileDependency(['fileName' => 'example.txt']); + +// La donnée expirera dans 30 secondes. +// Elle sera également invalidée plus tôt si le fichier example.txt est modifié. +$cache->set($key, $data, 30, $dependency); + +// Le cache vérifiera si la donnée a expiré. +// Il vérifiera également si la dépendance associée a été modifiée. +// Il retournera `false` si l'une de ces conditions est vérifiée. +$data = $cache->get($key); +``` + +Ci-dessous nous présentons un résumé des dépendances de mise en cache disponibles : + +- [[yii\caching\ChainedDependency]]: la dépendance est modifiée si l'une des dépendances de la chaîne est modifiée. +- [[yii\caching\DbDependency]]: la dépendance est modifiée si le résultat de le requête de l'instruction SQL spécifiée est modifié. +- [[yii\caching\ExpressionDependency]]: la dépendance est modifiée si le résultat de l'expression PHP spécifiée est modifié. +- [[yii\caching\FileDependency]]: la dépendance est modifiée si la date de dernière modification du fichier est modifiée. +- [[yii\caching\TagDependency]]: associe une donnée mise en cache à une ou plusieurs balises. Vous pouvez invalider la donnée mise en cache associée à la balise spécifiée en appelant [[yii\caching\TagDependency::invalidate()]]. + +> Note : évitez d'utiliser la méthode [[yii\caching\Cache::exists()|exists()]] avec des dépendances. Cela ne vérifie pas si la dépendance associée à la donnée mise en cache, s'il en existe une, a changé. Ainsi, un appel de la fonction [[yii\caching\Cache::get()|get()]] peut retourner `false` alors que l'appel de la fonction [[yii\caching\Cache::exists()|exists()]] retourne `true`. + + +## Mise en cache de requêtes + +La mise en cache de requêtes est une fonctionnalité spéciale de la mise en cache construite sur la base de la mise en cache de données. Elle est fournie pour permettre la mise en cache du résultat de requêtes de base de données. + +La mise en cache de requêtes nécessite une [[yii\db\Connection|connexion à une base de données]] et un [composant d'application](#cache-components)`cache` valide. +L'utilisation de base de la mise en cache de requêtes est la suivante, en supposant que `$db` est une instance de [[yii\db\Connection]] : + +```php +$result = $db->cache(function ($db) { + + // le résultat d'une requête SQL sera servi à partir du cache + // si la mise en cache de requêtes est activée et si le résultat de la requête est trouvé dans le cache + return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne(); + +}); +``` + +La mise en cache de requêtes peut être utilisée pour des [DAO](db-dao.md) ainsi que pour des [enregistrements actifs](db-active-record.md): + +```php +$result = Customer::getDb()->cache(function ($db) { + return Customer::find()->where(['id' => 1])->one(); +}); +``` + +> Info : quelques systèmes de gestion de bases de données (DBMS) (p. ex. [MySQL](http://dev.mysql.com/doc/refman/5.1/en/query-cache.html)) + prennent également en charge la mise en cache de requêtes du côté serveur de base de données. Vous pouvez choisir d'utiliser l'un ou l'autre des ces mécanismes de mise en cache de requêtes. Les mises en cache de requêtes décrites ci-dessus offrent l'avantage de pouvoir spécifier des dépendances de mise en cache flexibles et sont potentiellement plus efficaces. + + +### Vidage du cache + +Lorsque vous avez besoin d'invalider toutes les données stockées dans le cache, vous pouvez appeler [[yii\caching\Cache::flush()]]. + +Vous pouvez aussi vider le cache depuis la console en appelant `yii cache/flush`. + - `yii cache`: liste les caches disponibles dans une application + - `yii cache/flush cache1 cache2`: vide les composants de mise en cache `cache1`, `cache2` (vous pouvez passer de multiples composants en les séparant par une virgule) + - `yii cache/flush-all`: vide tous les composants de mise en cache de l'application + +> Info : les applications en console utilisent un fichier de configuration séparé par défaut. Assurez-vous que vous utilisez le même composant de mise en cache dans les configurations de vos application web et console pour obtenir l'effet correct. + + +### Configurations + +La mise en cache de requêtes dispose de trois options globales configurables via [[yii\db\Connection]] : + +* [[yii\db\Connection::enableQueryCache|enableQueryCache]] : pour activer ou désactiver la mise en cache de requêtes. + Valeur par défaut : `true`. Notez que pour activer effectivement la mise en cache de requêtes, vous devez également disposer d'un cache valide, tel que spécifié par [[yii\db\Connection::queryCache|queryCache]]. +* [[yii\db\Connection::queryCacheDuration|queryCacheDuration]] : ceci représente le nombre de secondes durant lesquelles le résultat d'une requête reste valide dans le cache. Vous pouvez utiliser 0 pour indiquer que le résultat de la requête doit rester valide indéfiniment dans le cache. Cette propriété est la valeur par défaut utilisée lors de l'appel [[yii\db\Connection::cache()]] sans spécifier de durée. +* [[yii\db\Connection::queryCache|queryCache]] : ceci représente l'identifiant du composant d'application de mise en cache. + Sa valeur par défaut est : `'cache'`. La mise en cache de requêtes est activée seulement s'il existe un composant d'application de mise en cache valide. + + +### Utilisations + +Vous pouvez utiliser [[yii\db\Connection::cache()]] si vous avez de multiples requêtes SQL qui doivent bénéficier de la mise en cache de requêtes. On l'utilise comme suit : + +```php +$duration = 60; // mettre le résultat de la requête en cache durant 60 secondes. +$dependency = ...; // dépendance optionnelle + +$result = $db->cache(function ($db) { + + // ... effectuer les requêtes SQL ici ... + + return $result; + +}, $duration, $dependency); +``` + +Toutes les requêtes SQL dans la fonction anonyme sont mises en cache pour la durée spécifiée avec la dépendance spécifiée. Si le résultat d'une requête est trouvé valide dans le cache, la requête est ignorée et, à la place, le résultat est servi à partir du cache. Si vous ne spécifiez pas le paramètre `$duration`, la valeur de [[yii\db\Connection::queryCacheDuration|queryCacheDuration]] est utilisée en remplacement. + +Parfois, dans `cache()`, il se peut que vous vouliez désactiver la mise en cache de requêtes pour des requêtes particulières. Dans un tel cas, vous pouvez utiliser [[yii\db\Connection::noCache()]]. + +```php +$result = $db->cache(function ($db) { + + // requêtes SQL qui utilisent la mise en cache de requêtes + + $db->noCache(function ($db) { + + // requêtes qui n'utilisent pas la mise en cache de requêtes + + }); + + // ... + + return $result; +}); +``` + +Si vous voulez seulement utiliser la mise en cache de requêtes pour une requête unique, vous pouvez appeler la fonction [[yii\db\Command::cache()]] lors de la construction de la commande. Par exemple : + +```php +// utilise la mise en cache de requêtes et définit la durée de mise en cache de la requête à 60 secondes +$customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->cache(60)->queryOne(); +``` + +Vous pouvez aussi utiliser la fonction [[yii\db\Command::noCache()]] pour désactiver la mise en cache de requêtes pour une commande unique. Par exemple : + +```php +$result = $db->cache(function ($db) { + + // requêtes SQL qui utilisent la mise en cache de requêtes + + // ne pas utiliser la mise en cache de requêtes pour cette commande + $customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->noCache()->queryOne(); + + // ... + + return $result; +}); +``` + + +### Limitations + +La mise en cache de requêtes ne fonctionne pas avec des résultats de requêtes qui contiennent des gestionnaires de ressources. Par exemple, lorsque vous utilisez de type de colonne `BLOB` dans certains systèmes de gestion de bases de données (DBMS), la requête retourne un gestionnaire de ressources pour la donnée de la colonne. + +Quelques supports de stockage pour cache sont limités en taille. Par exemple, avec memcache, chaque entrée est limitée en taille à 1 MO. En conséquence, si le résultat d'une requête dépasse cette taille, la mise en cache échoue. + From 56c65f60794e73c039e25ba1889483d09b252db1 Mon Sep 17 00:00:00 2001 From: Bob Olde Hampsink Date: Mon, 20 Feb 2017 12:54:33 +0100 Subject: [PATCH 25/75] Fixes #13577: Implemented `yii\db\mssql\QueryBuilder::resetSequence()` --- framework/CHANGELOG.md | 1 + framework/db/mssql/QueryBuilder.php | 30 +++++++++++++++++++ tests/framework/db/mssql/QueryBuilderTest.php | 13 ++++++++ 3 files changed, 44 insertions(+) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 27672f3c2d..31e7d23876 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -12,6 +12,7 @@ Yii Framework 2 Change Log - Bug #11230: Include `defaultRoles` in `yii\rbac\DbManager->getRolesByUser()` results (developeruz) - Bug #13343: Fixed `yii\i18n\Formatter::asTime()` to process time-only values without time zone conversion (bizley) - Bug #11404: `yii\base\Model::loadMultiple()` returns true even if `yii\base\Model::load()` returns false (zvook) +- Enh #13577: Implemented `yii\db\mssql\QueryBuilder::resetSequence()` (boboldehampsink) - Bug #13513: Fixed RBAC migration to work correctly on Oracle DBMS (silverfire) - Enh #13550: Refactored unset call order in `yii\di\ServiceLocator::set()` (Lanrik) - Bug #13582: PK column in `yii\db\pgsql\resetSequence()` was not quoted properly (boboldehampsink) diff --git a/framework/db/mssql/QueryBuilder.php b/framework/db/mssql/QueryBuilder.php index b86bba165c..d15e0e5e5b 100644 --- a/framework/db/mssql/QueryBuilder.php +++ b/framework/db/mssql/QueryBuilder.php @@ -165,6 +165,36 @@ class QueryBuilder extends \yii\db\QueryBuilder return $sql; } + /** + * Creates a SQL statement for resetting the sequence value of a table's primary key. + * The sequence will be reset such that the primary key of the next new row inserted + * will have the specified value or 1. + * @param string $tableName the name of the table whose primary key sequence will be reset + * @param mixed $value the value for the primary key of the next new row inserted. If this is not set, + * the next new row's primary key will have a value 1. + * @return string the SQL statement for resetting sequence + * @throws InvalidParamException if the table does not exist or there is no sequence associated with the table. + */ + public function resetSequence($tableName, $value = null) + { + $table = $this->db->getTableSchema($tableName); + if ($table !== null && $table->sequenceName !== null) { + $tableName = $this->db->quoteTableName($tableName); + if ($value === null) { + $key = $this->db->quoteColumnName(reset($table->primaryKey)); + $value = "(SELECT COALESCE(MAX({$key}),0) FROM {$tableName})+1"; + } else { + $value = (int) $value; + } + + return "DBCC CHECKIDENT ('{$tableName}', RESEED, {$value})"; + } elseif ($table === null) { + throw new InvalidParamException("Table not found: $tableName"); + } else { + throw new InvalidParamException("There is not sequence associated with table '$tableName'."); + } + } + /** * Builds a SQL statement for enabling or disabling integrity check. * @param bool $check whether to turn on or off the integrity check. diff --git a/tests/framework/db/mssql/QueryBuilderTest.php b/tests/framework/db/mssql/QueryBuilderTest.php index 281f3a6ff1..1329ecc1d0 100644 --- a/tests/framework/db/mssql/QueryBuilderTest.php +++ b/tests/framework/db/mssql/QueryBuilderTest.php @@ -100,4 +100,17 @@ class QueryBuilderTest extends \yiiunit\framework\db\QueryBuilderTest return $data; } + + public function testResetSequence() + { + $qb = $this->getQueryBuilder(); + + $expected = "DBCC CHECKIDENT ('[item]', RESEED, (SELECT COALESCE(MAX([id]),0) FROM [item])+1)"; + $sql = $qb->resetSequence('item'); + $this->assertEquals($expected, $sql); + + $expected = "DBCC CHECKIDENT ('[item], RESEED, 4)"; + $sql = $qb->resetSequence('item', 4); + $this->assertEquals($expected, $sql); + } } From fab53b4a6ee67ecaf8902e5f6536dbbb5cceb0ae Mon Sep 17 00:00:00 2001 From: Bob Olde Hampsink Date: Mon, 20 Feb 2017 21:04:04 +0100 Subject: [PATCH 26/75] Fixes #13582: Added tests for all `yii\db\QueryBuilder::resetSequence` implementations, fixed SQLite implementation --- framework/CHANGELOG.md | 3 ++- framework/db/sqlite/QueryBuilder.php | 4 ++-- tests/framework/db/QueryBuilderTest.php | 4 ++-- tests/framework/db/cubrid/QueryBuilderTest.php | 13 +++++++++++++ tests/framework/db/mysql/QueryBuilderTest.php | 13 +++++++++++++ tests/framework/db/oci/QueryBuilderTest.php | 15 +++++++++++++++ tests/framework/db/pgsql/QueryBuilderTest.php | 13 +++++++++++++ tests/framework/db/sqlite/QueryBuilderTest.php | 13 +++++++++++++ 8 files changed, 73 insertions(+), 5 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 31e7d23876..277a7f4e53 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -15,11 +15,12 @@ Yii Framework 2 Change Log - Enh #13577: Implemented `yii\db\mssql\QueryBuilder::resetSequence()` (boboldehampsink) - Bug #13513: Fixed RBAC migration to work correctly on Oracle DBMS (silverfire) - Enh #13550: Refactored unset call order in `yii\di\ServiceLocator::set()` (Lanrik) -- Bug #13582: PK column in `yii\db\pgsql\resetSequence()` was not quoted properly (boboldehampsink) +- Bug #13582: PK column in `yii\db\pgsql\QueryBuilder::resetSequence()` was not quoted properly (boboldehampsink) - Bug #13592: Fixes Oracle’s `yii\db\oci\Schema::setTransactionIsolationLevel()` (sergeymakinen) - Bug #13594: Fixes insufficient quoting in `yii\db\QueryBuilder::prepareInsertSelectSubQuery()` (sergeymakinen) - Enh #13576: Added support of `srcset` to `yii\helpers\Html::img()` (Kolyunya) - Enh #13467: `yii\data\ActiveDataProvider` no longer queries models if models count is zero (kLkA, Kolyunya) +- Enh #13582: Added tests for all `yii\db\QueryBuilder::resetSequence` implementations, fixed SQLite implementation (boboldehampsink) 2.0.11.2 February 08, 2017 diff --git a/framework/db/sqlite/QueryBuilder.php b/framework/db/sqlite/QueryBuilder.php index 0e361ca7f8..41ec8fb711 100644 --- a/framework/db/sqlite/QueryBuilder.php +++ b/framework/db/sqlite/QueryBuilder.php @@ -134,13 +134,13 @@ class QueryBuilder extends \yii\db\QueryBuilder $key = reset($table->primaryKey); $tableName = $db->quoteTableName($tableName); $value = $this->db->useMaster(function (Connection $db) use ($key, $tableName) { - return $db->createCommand("SELECT MAX('$key') FROM $tableName")->queryScalar(); + return $db->createCommand("SELECT MAX($key) FROM $tableName")->queryScalar(); }); } else { $value = (int) $value - 1; } try { - $db->createCommand("UPDATE sqlite_sequence SET seq='$value' WHERE name='{$table->name}'")->execute(); + return "UPDATE sqlite_sequence SET seq='$value' WHERE name='{$table->name}'"; } catch (Exception $e) { // it's possible that sqlite_sequence does not exist } diff --git a/tests/framework/db/QueryBuilderTest.php b/tests/framework/db/QueryBuilderTest.php index 1db023b4c5..6daf9f504b 100644 --- a/tests/framework/db/QueryBuilderTest.php +++ b/tests/framework/db/QueryBuilderTest.php @@ -28,9 +28,9 @@ abstract class QueryBuilderTest extends DatabaseTestCase * @throws \Exception * @return QueryBuilder */ - protected function getQueryBuilder() + protected function getQueryBuilder($reset = true, $open = false) { - $connection = $this->getConnection(true, false); + $connection = $this->getConnection($reset, $open); \Yii::$container->set('db', $connection); diff --git a/tests/framework/db/cubrid/QueryBuilderTest.php b/tests/framework/db/cubrid/QueryBuilderTest.php index fbd886628c..590a1c5ffb 100644 --- a/tests/framework/db/cubrid/QueryBuilderTest.php +++ b/tests/framework/db/cubrid/QueryBuilderTest.php @@ -20,4 +20,17 @@ class QueryBuilderTest extends \yiiunit\framework\db\QueryBuilderTest { return array_merge(parent::columnTypes(), []); } + + public function testResetSequence() + { + $qb = $this->getQueryBuilder(); + + $expected = 'ALTER TABLE "item" AUTO_INCREMENT=6;'; + $sql = $qb->resetSequence('item'); + $this->assertEquals($expected, $sql); + + $expected = 'ALTER TABLE "item" AUTO_INCREMENT=4;'; + $sql = $qb->resetSequence('item', 4); + $this->assertEquals($expected, $sql); + } } diff --git a/tests/framework/db/mysql/QueryBuilderTest.php b/tests/framework/db/mysql/QueryBuilderTest.php index 16d1eed267..57c4244419 100644 --- a/tests/framework/db/mysql/QueryBuilderTest.php +++ b/tests/framework/db/mysql/QueryBuilderTest.php @@ -56,4 +56,17 @@ class QueryBuilderTest extends \yiiunit\framework\db\QueryBuilderTest ], ]); } + + public function testResetSequence() + { + $qb = $this->getQueryBuilder(); + + $expected = 'ALTER TABLE `item` AUTO_INCREMENT=6'; + $sql = $qb->resetSequence('item'); + $this->assertEquals($expected, $sql); + + $expected = 'ALTER TABLE `item` AUTO_INCREMENT=4'; + $sql = $qb->resetSequence('item', 4); + $this->assertEquals($expected, $sql); + } } diff --git a/tests/framework/db/oci/QueryBuilderTest.php b/tests/framework/db/oci/QueryBuilderTest.php index fe8f932db1..c95cf5ae2d 100644 --- a/tests/framework/db/oci/QueryBuilderTest.php +++ b/tests/framework/db/oci/QueryBuilderTest.php @@ -53,4 +53,19 @@ class QueryBuilderTest extends \yiiunit\framework\db\QueryBuilderTest $sql = $qb->dropCommentFromTable('comment'); $this->assertEquals($this->replaceQuotes($expected), $sql); } + + public function testResetSequence() + { + $qb = $this->getQueryBuilder(); + + $expected = 'DROP SEQUENCE "item_SEQ";' + .'CREATE SEQUENCE "item_SEQ" START WITH 6 INCREMENT BY 1 NOMAXVALUE NOCACHE'; + $sql = $qb->resetSequence('item'); + $this->assertEquals($expected, $sql); + + $expected = 'DROP SEQUENCE "item_SEQ";' + .'CREATE SEQUENCE "item_SEQ" START WITH 4 INCREMENT BY 1 NOMAXVALUE NOCACHE'; + $sql = $qb->resetSequence('item', 4); + $this->assertEquals($expected, $sql); + } } diff --git a/tests/framework/db/pgsql/QueryBuilderTest.php b/tests/framework/db/pgsql/QueryBuilderTest.php index 4ffe5342ba..508734f851 100644 --- a/tests/framework/db/pgsql/QueryBuilderTest.php +++ b/tests/framework/db/pgsql/QueryBuilderTest.php @@ -131,4 +131,17 @@ class QueryBuilderTest extends \yiiunit\framework\db\QueryBuilderTest return $data; } + + public function testResetSequence() + { + $qb = $this->getQueryBuilder(); + + $expected = "SELECT SETVAL('\"item_id_seq\"',(SELECT COALESCE(MAX(\"id\"),0) FROM \"item\")+1,false)"; + $sql = $qb->resetSequence('item'); + $this->assertEquals($expected, $sql); + + $expected = "SELECT SETVAL('\"item_id_seq\"',4,false)"; + $sql = $qb->resetSequence('item', 4); + $this->assertEquals($expected, $sql); + } } diff --git a/tests/framework/db/sqlite/QueryBuilderTest.php b/tests/framework/db/sqlite/QueryBuilderTest.php index 551f787dd1..f6f6db8ede 100644 --- a/tests/framework/db/sqlite/QueryBuilderTest.php +++ b/tests/framework/db/sqlite/QueryBuilderTest.php @@ -108,4 +108,17 @@ class QueryBuilderTest extends \yiiunit\framework\db\QueryBuilderTest $this->assertEquals($expectedQuerySql, $actualQuerySql); $this->assertEquals([], $queryParams); } + + public function testResetSequence() + { + $qb = $this->getQueryBuilder(true, true); + + $expected = "UPDATE sqlite_sequence SET seq='5' WHERE name='item'"; + $sql = $qb->resetSequence('item'); + $this->assertEquals($expected, $sql); + + $expected = "UPDATE sqlite_sequence SET seq='3' WHERE name='item'"; + $sql = $qb->resetSequence('item', 4); + $this->assertEquals($expected, $sql); + } } From ab36cb29e10321b43a41e6d3d3fa7244ba29059d Mon Sep 17 00:00:00 2001 From: SilverFire - Dmitry Naumenko Date: Mon, 20 Feb 2017 22:12:44 +0200 Subject: [PATCH 27/75] Updated PHPDocs: marked session-handling methods as internal --- framework/web/CacheSession.php | 6 +++--- framework/web/DbSession.php | 8 ++++---- framework/web/Session.php | 12 ++++++------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/framework/web/CacheSession.php b/framework/web/CacheSession.php index 4f47322686..ca75ca3127 100644 --- a/framework/web/CacheSession.php +++ b/framework/web/CacheSession.php @@ -71,7 +71,7 @@ class CacheSession extends Session /** * Session read handler. - * Do not call this method directly. + * @internal Do not call this method directly. * @param string $id session ID * @return string the session data */ @@ -84,7 +84,7 @@ class CacheSession extends Session /** * Session write handler. - * Do not call this method directly. + * @internal Do not call this method directly. * @param string $id session ID * @param string $data session data * @return bool whether session write is successful @@ -96,7 +96,7 @@ class CacheSession extends Session /** * Session destroy handler. - * Do not call this method directly. + * @internal Do not call this method directly. * @param string $id session ID * @return bool whether session is destroyed successfully */ diff --git a/framework/web/DbSession.php b/framework/web/DbSession.php index 67c7191f00..072aca3ee5 100644 --- a/framework/web/DbSession.php +++ b/framework/web/DbSession.php @@ -134,7 +134,7 @@ class DbSession extends MultiFieldSession /** * Session read handler. - * Do not call this method directly. + * @internal Do not call this method directly. * @param string $id session ID * @return string the session data */ @@ -155,7 +155,7 @@ class DbSession extends MultiFieldSession /** * Session write handler. - * Do not call this method directly. + * @internal Do not call this method directly. * @param string $id session ID * @param string $data session data * @return bool whether session write is successful @@ -197,7 +197,7 @@ class DbSession extends MultiFieldSession /** * Session destroy handler. - * Do not call this method directly. + * @internal Do not call this method directly. * @param string $id session ID * @return bool whether session is destroyed successfully */ @@ -212,7 +212,7 @@ class DbSession extends MultiFieldSession /** * Session GC (garbage collection) handler. - * Do not call this method directly. + * @internal Do not call this method directly. * @param int $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up. * @return bool whether session is GCed successfully */ diff --git a/framework/web/Session.php b/framework/web/Session.php index 923f97f2fe..d658064efe 100644 --- a/framework/web/Session.php +++ b/framework/web/Session.php @@ -471,7 +471,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co /** * Session open handler. * This method should be overridden if [[useCustomStorage]] returns true. - * Do not call this method directly. + * @internal Do not call this method directly. * @param string $savePath session save path * @param string $sessionName session name * @return bool whether session is opened successfully @@ -484,7 +484,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co /** * Session close handler. * This method should be overridden if [[useCustomStorage]] returns true. - * Do not call this method directly. + * @internal Do not call this method directly. * @return bool whether session is closed successfully */ public function closeSession() @@ -495,7 +495,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co /** * Session read handler. * This method should be overridden if [[useCustomStorage]] returns true. - * Do not call this method directly. + * @internal Do not call this method directly. * @param string $id session ID * @return string the session data */ @@ -507,7 +507,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co /** * Session write handler. * This method should be overridden if [[useCustomStorage]] returns true. - * Do not call this method directly. + * @internal Do not call this method directly. * @param string $id session ID * @param string $data session data * @return bool whether session write is successful @@ -520,7 +520,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co /** * Session destroy handler. * This method should be overridden if [[useCustomStorage]] returns true. - * Do not call this method directly. + * @internal Do not call this method directly. * @param string $id session ID * @return bool whether session is destroyed successfully */ @@ -532,7 +532,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co /** * Session GC (garbage collection) handler. * This method should be overridden if [[useCustomStorage]] returns true. - * Do not call this method directly. + * @internal Do not call this method directly. * @param int $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up. * @return bool whether session is GCed successfully */ From f4965677351a94fd1d4779b84f9d7c5cabc27f9b Mon Sep 17 00:00:00 2001 From: SilverFire - Dmitry Naumenko Date: Mon, 20 Feb 2017 22:34:40 +0200 Subject: [PATCH 28/75] Fixed `yii\web\CacheSession::destroySession()` to work correctly when session is not written yet Fixes #13537 --- framework/CHANGELOG.md | 1 + framework/web/CacheSession.php | 4 ++++ tests/framework/web/CacheSessionTest.php | 13 +++++++++++++ 3 files changed, 18 insertions(+) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 277a7f4e53..4d99483857 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -16,6 +16,7 @@ Yii Framework 2 Change Log - Bug #13513: Fixed RBAC migration to work correctly on Oracle DBMS (silverfire) - Enh #13550: Refactored unset call order in `yii\di\ServiceLocator::set()` (Lanrik) - Bug #13582: PK column in `yii\db\pgsql\QueryBuilder::resetSequence()` was not quoted properly (boboldehampsink) +- Bug #13537: Fixed `yii\web\CacheSession::destroySession()` to work correctly when session is not written yet (silverfire, papalapa) - Bug #13592: Fixes Oracle’s `yii\db\oci\Schema::setTransactionIsolationLevel()` (sergeymakinen) - Bug #13594: Fixes insufficient quoting in `yii\db\QueryBuilder::prepareInsertSelectSubQuery()` (sergeymakinen) - Enh #13576: Added support of `srcset` to `yii\helpers\Html::img()` (Kolyunya) diff --git a/framework/web/CacheSession.php b/framework/web/CacheSession.php index ca75ca3127..3b1fe222d2 100644 --- a/framework/web/CacheSession.php +++ b/framework/web/CacheSession.php @@ -102,6 +102,10 @@ class CacheSession extends Session */ public function destroySession($id) { + if (!$this->cache->exists($id)) { + return true; + } + return $this->cache->delete($this->calculateKey($id)); } diff --git a/tests/framework/web/CacheSessionTest.php b/tests/framework/web/CacheSessionTest.php index c81e01a42e..e61ced98aa 100644 --- a/tests/framework/web/CacheSessionTest.php +++ b/tests/framework/web/CacheSessionTest.php @@ -33,4 +33,17 @@ class CacheSessionTest extends \yiiunit\TestCase $this->setExpectedException('\Exception'); new CacheSession(['cache' => 'invalid']); } + + /** + * @see https://github.com/yiisoft/yii2/issues/13537 + */ + public function testNotWrittenSessionDestroying() + { + $session = new CacheSession(); + + $session->set('foo', 'bar'); + $this->assertEquals('bar', $session->get('foo')); + + $this->assertTrue($session->destroySession($session->getId())); + } } From 30b7fc8dc1bf0efd4a2e19d37953176a007bb692 Mon Sep 17 00:00:00 2001 From: Bob Olde Hampsink Date: Mon, 20 Feb 2017 21:38:50 +0100 Subject: [PATCH 29/75] Fixes #13577: `yii\db\QueryBuilder::truncateTable` should work consistent over all databases --- docs/guide-es/db-dao.md | 2 +- docs/guide-fr/db-dao.md | 2 +- docs/guide-ja/db-dao.md | 2 +- docs/guide-pl/db-active-record.md | 2 +- docs/guide-pt-BR/db-active-record.md | 2 +- docs/guide-ru/db-dao.md | 2 +- docs/guide-zh-CN/db-dao.md | 2 +- docs/guide/db-dao.md | 2 +- framework/CHANGELOG.md | 1 + framework/db/pgsql/QueryBuilder.php | 11 +++++++++++ 10 files changed, 20 insertions(+), 8 deletions(-) diff --git a/docs/guide-es/db-dao.md b/docs/guide-es/db-dao.md index 5d34a1d484..309a796cf9 100644 --- a/docs/guide-es/db-dao.md +++ b/docs/guide-es/db-dao.md @@ -14,7 +14,7 @@ Yii DAO soporta las siguientes bases de datos: - [MySQL](http://www.mysql.com/) - [MariaDB](https://mariadb.com/) - [SQLite](http://sqlite.org/) -- [PostgreSQL](http://www.postgresql.org/) +- [PostgreSQL](http://www.postgresql.org/): versión 8.4 o superior. - [CUBRID](http://www.cubrid.org/): versión 9.3 o superior. - [Oracle](http://www.oracle.com/us/products/database/overview/index.html) - [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): versión 2008 o superior. diff --git a/docs/guide-fr/db-dao.md b/docs/guide-fr/db-dao.md index 1e162b583f..af899f1cd3 100644 --- a/docs/guide-fr/db-dao.md +++ b/docs/guide-fr/db-dao.md @@ -10,7 +10,7 @@ Les objets d'accès aux bases de données de Yii prennent en charge les bases de - [MySQL](http://www.mysql.com/) - [MariaDB](https://mariadb.com/) - [SQLite](http://sqlite.org/) -- [PostgreSQL](http://www.postgresql.org/) +- [PostgreSQL](http://www.postgresql.org/): version 8.4 or higher. - [CUBRID](http://www.cubrid.org/): version 9.3 or higher. - [Oracle](http://www.oracle.com/us/products/database/overview/index.html) - [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): version 2008 or higher. diff --git a/docs/guide-ja/db-dao.md b/docs/guide-ja/db-dao.md index 4794538e69..64c0dd9483 100644 --- a/docs/guide-ja/db-dao.md +++ b/docs/guide-ja/db-dao.md @@ -13,7 +13,7 @@ Yii は下記の DBMS のサポートを内蔵しています。 - [MySQL](http://www.mysql.com/) - [MariaDB](https://mariadb.com/) - [SQLite](http://sqlite.org/) -- [PostgreSQL](http://www.postgresql.org/) +- [PostgreSQL](http://www.postgresql.org/): バージョン 8.4 以上。 - [CUBRID](http://www.cubrid.org/): バージョン 9.3 以上。 - [Oracle](http://www.oracle.com/us/products/database/overview/index.html) - [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): バージョン 2008 以上。 diff --git a/docs/guide-pl/db-active-record.md b/docs/guide-pl/db-active-record.md index 9af6693ff8..72866623cf 100644 --- a/docs/guide-pl/db-active-record.md +++ b/docs/guide-pl/db-active-record.md @@ -27,7 +27,7 @@ $db->createCommand('INSERT INTO `customer` (`name`) VALUES (:name)', [ Yii zapewnia wsparcie Active Record dla następujących typów relacyjnych baz danych: * MySQL 4.1 lub nowszy: poprzez [[yii\db\ActiveRecord]] -* PostgreSQL 7.3 lub nowszy: poprzez [[yii\db\ActiveRecord]] +* PostgreSQL 8.4 lub nowszy: poprzez [[yii\db\ActiveRecord]] * SQLite 2 i 3: poprzez [[yii\db\ActiveRecord]] * Microsoft SQL Server 2008 lub nowszy: poprzez [[yii\db\ActiveRecord]] * Oracle: poprzez [[yii\db\ActiveRecord]] diff --git a/docs/guide-pt-BR/db-active-record.md b/docs/guide-pt-BR/db-active-record.md index 69595217e6..5139cc6864 100644 --- a/docs/guide-pt-BR/db-active-record.md +++ b/docs/guide-pt-BR/db-active-record.md @@ -22,7 +22,7 @@ $db->createCommand('INSERT INTO `customer` (`name`) VALUES (:name)', [ O Yii fornece suporte Active Record para os seguintes bancos de dados relacionais: * MySQL 4.1 ou superior: via [[yii\db\ActiveRecord]] -* PostgreSQL 7.3 ou superior: via [[yii\db\ActiveRecord]] +* PostgreSQL 8.4 ou superior: via [[yii\db\ActiveRecord]] * SQLite 2 e 3: via [[yii\db\ActiveRecord]] * Microsoft SQL Server 2008 ou superior: via [[yii\db\ActiveRecord]] * Oracle: via [[yii\db\ActiveRecord]] diff --git a/docs/guide-ru/db-dao.md b/docs/guide-ru/db-dao.md index 0c710f9d04..15f4b95e26 100644 --- a/docs/guide-ru/db-dao.md +++ b/docs/guide-ru/db-dao.md @@ -15,7 +15,7 @@ Yii DAO из коробки поддерживает следующие базы - [MySQL](http://www.mysql.com/) - [MariaDB](https://mariadb.com/) - [SQLite](http://sqlite.org/) -- [PostgreSQL](http://www.postgresql.org/) +- [PostgreSQL](http://www.postgresql.org/): версии 8.4 или выше. - [CUBRID](http://www.cubrid.org/): версии 9.3 или выше. - [Oracle](http://www.oracle.com/us/products/database/overview/index.html) - [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): версии 2008 или выше. diff --git a/docs/guide-zh-CN/db-dao.md b/docs/guide-zh-CN/db-dao.md index a813e8a4ae..0aa6036639 100644 --- a/docs/guide-zh-CN/db-dao.md +++ b/docs/guide-zh-CN/db-dao.md @@ -7,7 +7,7 @@ Yii 默认支持以下数据库 (DBMS): - [MySQL](http://www.mysql.com/) - [MariaDB](https://mariadb.com/) - [SQLite](http://sqlite.org/) -- [PostgreSQL](http://www.postgresql.org/) +- [PostgreSQL](http://www.postgresql.org/): 版本 >= 8.4 - [CUBRID](http://www.cubrid.org/): 版本 >= 9.3 . (由于PHP PDO 扩展的一个[bug](http://jira.cubrid.org/browse/APIS-658) 引用值会无效,所以你需要在 CUBRID的客户端和服务端都使用 9.3 ) - [Oracle](http://www.oracle.com/us/products/database/overview/index.html) - [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): 版本>=2005. diff --git a/docs/guide/db-dao.md b/docs/guide/db-dao.md index 0464d365f5..931fc8566c 100644 --- a/docs/guide/db-dao.md +++ b/docs/guide/db-dao.md @@ -14,7 +14,7 @@ Yii DAO supports the following databases out of box: - [MySQL](http://www.mysql.com/) - [MariaDB](https://mariadb.com/) - [SQLite](http://sqlite.org/) -- [PostgreSQL](http://www.postgresql.org/) +- [PostgreSQL](http://www.postgresql.org/): version 8.4 or higher - [CUBRID](http://www.cubrid.org/): version 9.3 or higher. - [Oracle](http://www.oracle.com/us/products/database/overview/index.html) - [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): version 2008 or higher. diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 4d99483857..b26f1cac0a 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -12,6 +12,7 @@ Yii Framework 2 Change Log - Bug #11230: Include `defaultRoles` in `yii\rbac\DbManager->getRolesByUser()` results (developeruz) - Bug #13343: Fixed `yii\i18n\Formatter::asTime()` to process time-only values without time zone conversion (bizley) - Bug #11404: `yii\base\Model::loadMultiple()` returns true even if `yii\base\Model::load()` returns false (zvook) +- Bug #13577: `yii\db\QueryBuilder::truncateTable` should work consistent over all databases (boboldehampsink) - Enh #13577: Implemented `yii\db\mssql\QueryBuilder::resetSequence()` (boboldehampsink) - Bug #13513: Fixed RBAC migration to work correctly on Oracle DBMS (silverfire) - Enh #13550: Refactored unset call order in `yii\di\ServiceLocator::set()` (Lanrik) diff --git a/framework/db/pgsql/QueryBuilder.php b/framework/db/pgsql/QueryBuilder.php index 46e98f11db..2f6127f894 100644 --- a/framework/db/pgsql/QueryBuilder.php +++ b/framework/db/pgsql/QueryBuilder.php @@ -205,6 +205,17 @@ class QueryBuilder extends \yii\db\QueryBuilder return $command; } + /** + * Builds a SQL statement for truncating a DB table. + * Explicitly restarts identity for PGSQL to be consistent with other databases which all do this by default. + * @param string $table the table to be truncated. The name will be properly quoted by the method. + * @return string the SQL statement for truncating a DB table. + */ + public function truncateTable($table) + { + return 'TRUNCATE TABLE ' . $this->db->quoteTableName($table) . ' RESTART IDENTITY'; + } + /** * Builds a SQL statement for changing the definition of a column. * @param string $table the table whose column is to be changed. The table name will be properly quoted by the method. From ecefc2bdfb1689055f30fe0e45081e488f6297e3 Mon Sep 17 00:00:00 2001 From: SilverFire - Dmitry Naumenko Date: Mon, 20 Feb 2017 23:03:26 +0200 Subject: [PATCH 30/75] Fixed typo in CacheSession::destroySession() --- framework/web/CacheSession.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/framework/web/CacheSession.php b/framework/web/CacheSession.php index 3b1fe222d2..77d360a581 100644 --- a/framework/web/CacheSession.php +++ b/framework/web/CacheSession.php @@ -102,11 +102,12 @@ class CacheSession extends Session */ public function destroySession($id) { - if (!$this->cache->exists($id)) { + $cacheId = $this->calculateKey($id); + if ($this->cache->exists($cacheId) === false) { return true; } - return $this->cache->delete($this->calculateKey($id)); + return $this->cache->delete($cacheId); } /** From 86b08e29d262c856c77ddca3397095ffd71f698e Mon Sep 17 00:00:00 2001 From: SilverFire - Dmitry Naumenko Date: Mon, 20 Feb 2017 23:17:40 +0200 Subject: [PATCH 31/75] Enhanced `yii\console\Request::resolve()` to prevent passing parameters, that begin from digits Closes #8641 --- framework/CHANGELOG.md | 1 + framework/console/Request.php | 7 +++ tests/framework/console/RequestTest.php | 58 +++++++++++++++++-------- 3 files changed, 48 insertions(+), 18 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index b26f1cac0a..051a2af275 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -21,6 +21,7 @@ Yii Framework 2 Change Log - Bug #13592: Fixes Oracle’s `yii\db\oci\Schema::setTransactionIsolationLevel()` (sergeymakinen) - Bug #13594: Fixes insufficient quoting in `yii\db\QueryBuilder::prepareInsertSelectSubQuery()` (sergeymakinen) - Enh #13576: Added support of `srcset` to `yii\helpers\Html::img()` (Kolyunya) +- Enh #8641: Enhanced `yii\console\Request::resolve()` to prevent passing parameters, that begin from digits (silverfire) - Enh #13467: `yii\data\ActiveDataProvider` no longer queries models if models count is zero (kLkA, Kolyunya) - Enh #13582: Added tests for all `yii\db\QueryBuilder::resetSequence` implementations, fixed SQLite implementation (boboldehampsink) diff --git a/framework/console/Request.php b/framework/console/Request.php index 7b667be64d..637e3b348a 100644 --- a/framework/console/Request.php +++ b/framework/console/Request.php @@ -7,6 +7,8 @@ namespace yii\console; +use yii\base\InvalidParamException; + /** * The console Request represents the environment information for a console application. * @@ -53,6 +55,7 @@ class Request extends \yii\base\Request /** * Resolves the current request into a route and the associated parameters. * @return array the first element is the route, and the second is the associated parameters. + * @throws InvalidParamException when parameter is wrong and can not be resolved */ public function resolve() { @@ -77,6 +80,10 @@ class Request extends \yii\base\Request $endOfOptionsFound = true; } elseif (preg_match('/^--(\w+)(?:=(.*))?$/', $param, $matches)) { $name = $matches[1]; + if (is_numeric(substr($name, 0, 1))) { + throw new InvalidParamException('Parameter "' . $name . '" is not valid'); + } + if ($name !== Application::OPTION_APPCONFIG) { $params[$name] = isset($matches[2]) ? $matches[2] : true; } diff --git a/tests/framework/console/RequestTest.php b/tests/framework/console/RequestTest.php index 130de9bf9d..d23186d811 100644 --- a/tests/framework/console/RequestTest.php +++ b/tests/framework/console/RequestTest.php @@ -18,8 +18,8 @@ class RequestTest extends TestCase 'expected' => [ 'route' => 'controller', 'params' => [ - ] - ] + ], + ], ], [ 'params' => [ @@ -29,7 +29,7 @@ class RequestTest extends TestCase '--option1', '--option2=testValue', '-alias1', - '-alias2=testValue' + '-alias2=testValue', ], 'expected' => [ 'route' => 'controller/route', @@ -40,10 +40,10 @@ class RequestTest extends TestCase 'option2' => 'testValue', '_aliases' => [ 'alias1' => true, - 'alias2' => 'testValue' - ] - ] - ] + 'alias2' => 'testValue', + ], + ], + ], ], [ // Case: Special argument "End of Options" used @@ -62,7 +62,7 @@ class RequestTest extends TestCase '--', // Second `--` argument shouldn't be treated as special '--option4=testValue', '-alias3', - '-alias4=testValue' + '-alias4=testValue', ], 'expected' => [ 'route' => 'controller/route', @@ -73,7 +73,7 @@ class RequestTest extends TestCase 'option2' => 'testValue', '_aliases' => [ 'alias1' => true, - 'alias2' => 'testValue' + 'alias2' => 'testValue', ], 'param2', '-54321', @@ -81,9 +81,9 @@ class RequestTest extends TestCase '--', '--option4=testValue', '-alias3', - '-alias4=testValue' - ] - ] + '-alias4=testValue', + ], + ], ], [ // Case: Special argument "End of Options" placed before route @@ -95,7 +95,7 @@ class RequestTest extends TestCase '--option1', '--option2=testValue', '-alias1', - '-alias2=testValue' + '-alias2=testValue', ], 'expected' => [ 'route' => 'controller/route', @@ -105,18 +105,40 @@ class RequestTest extends TestCase '--option1', '--option2=testValue', '-alias1', - '-alias2=testValue' - ] - ] - ] + '-alias2=testValue', + ], + ], + ], + [ + // PHP does not allow variable name, starting with digit. + // InvalidParamException must be thrown during request resolving: + 'params' => [ + 'controller/route', + '--0=test', + '--1=testing', + ], + 'expected' => [ + 'route' => 'controller/route', + 'params' => [ + ], + ], + 'exception' => [ + '\yii\base\InvalidParamException', + 'Parameter "0" is not valid' + ], + ], ]; } /** * @dataProvider provider */ - public function testResolve($params, $expected) + public function testResolve($params, $expected, $expectedException = null) { + if (isset($expectedException)) { + $this->setExpectedException($expectedException[0], $expectedException[1]); + } + $request = new Request(); $request->setParams($params); From e9a96ee83442ca98f03cf0514ecabc57ce8f6b79 Mon Sep 17 00:00:00 2001 From: SilverFire - Dmitry Naumenko Date: Tue, 21 Feb 2017 08:24:15 +0200 Subject: [PATCH 32/75] Replced InvalidParamException with \yii\console\Exception in Request::resolve() --- framework/console/Request.php | 6 ++---- tests/framework/console/RequestTest.php | 8 ++------ 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/framework/console/Request.php b/framework/console/Request.php index 637e3b348a..bba9fca626 100644 --- a/framework/console/Request.php +++ b/framework/console/Request.php @@ -7,8 +7,6 @@ namespace yii\console; -use yii\base\InvalidParamException; - /** * The console Request represents the environment information for a console application. * @@ -55,7 +53,7 @@ class Request extends \yii\base\Request /** * Resolves the current request into a route and the associated parameters. * @return array the first element is the route, and the second is the associated parameters. - * @throws InvalidParamException when parameter is wrong and can not be resolved + * @throws Exception when parameter is wrong and can not be resolved */ public function resolve() { @@ -81,7 +79,7 @@ class Request extends \yii\base\Request } elseif (preg_match('/^--(\w+)(?:=(.*))?$/', $param, $matches)) { $name = $matches[1]; if (is_numeric(substr($name, 0, 1))) { - throw new InvalidParamException('Parameter "' . $name . '" is not valid'); + throw new Exception('Parameter "' . $name . '" is not valid'); } if ($name !== Application::OPTION_APPCONFIG) { diff --git a/tests/framework/console/RequestTest.php b/tests/framework/console/RequestTest.php index d23186d811..c515b6ac24 100644 --- a/tests/framework/console/RequestTest.php +++ b/tests/framework/console/RequestTest.php @@ -117,13 +117,9 @@ class RequestTest extends TestCase '--0=test', '--1=testing', ], - 'expected' => [ - 'route' => 'controller/route', - 'params' => [ - ], - ], + 'expected' => [], 'exception' => [ - '\yii\base\InvalidParamException', + '\yii\console\Exception', 'Parameter "0" is not valid' ], ], From 677cd75c71efcfa63a579905abf2fbe3f2e9118b Mon Sep 17 00:00:00 2001 From: jaaf Date: Tue, 21 Feb 2017 10:04:46 +0100 Subject: [PATCH 33/75] Translated caching-fragment.md into FR [skip ci] (#13633) --- docs/guide-fr/caching-fragment.md | 143 ++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 docs/guide-fr/caching-fragment.md diff --git a/docs/guide-fr/caching-fragment.md b/docs/guide-fr/caching-fragment.md new file mode 100644 index 0000000000..0194a61e76 --- /dev/null +++ b/docs/guide-fr/caching-fragment.md @@ -0,0 +1,143 @@ +Mise en cache de fragments +========================== + +La mise en cache de fragments fait référence à la mise en cache de fragments de pages Web. Par exemple, si une page affiche un résumé des ventes annuelles dans un tableau, vous pouvez stocker ce tableau en cache pour éliminer le temps nécessaire à sa génération à chacune des requêtes. La mise en cache de fragments est construite au-dessus de la [mise en cache de données](caching-data.md). + +Pour utiliser la mise en cache de fragments, utilisez la construction qui suit dans une [vue](structure-views.md): + +```php +if ($this->beginCache($id)) { + + // ... générez le contenu ici ... + + $this->endCache(); +} +``` + +C'est à dire, insérez la logique de génération du contenu entre les appels [[yii\base\View::beginCache()|beginCache()]] et +[[yii\base\View::endCache()|endCache()]]. Si le contenu est trouvé dans le cache, [[yii\base\View::beginCache()|beginCache()]] +rendra le contenu en cache et retournera `false` (faux), ignorant la logique de génération de contenu. +Autrement, votre logique de génération de contenu sera appelée, et quand [[yii\base\View::endCache()|endCache()]] sera appelée, le contenu généré sera capturé et stocké dans le cache. + +Comme pour la [mise en cache de données](caching-data.md), un `$id` (identifiant) unique est nécessaire pour identifier un cache de contenu. + + +## Options de mise en cache + +Vous pouvez spécifier des options additionnelles sur la mise en cache de fragments en passant le tableau d'options comme second paramètre à la méthode [[yii\base\View::beginCache()|beginCache()]]. En arrière plan, ce tableau d'options est utilisé pour configurer un composant graphique [[yii\widgets\FragmentCache]] qui met en œuvre la fonctionnalité réelle de mise en cache de fragments. + +### Durée + +L'option [[yii\widgets\FragmentCache::duration|duration]] (durée) est peut-être l'option de la mise en cache de fragments la plus couramment utilisée. Elle spécifie pour combien de secondes le contenu peut demeurer valide dans le cache. Le code qui suit met le fragment de contenu en cache pour au maximum une heure : + +```php +if ($this->beginCache($id, ['duration' => 3600])) { + + // ... générez le contenu ici... + + $this->endCache(); +} +``` + +Si cette option n'est pas définie, la valeur utilisée par défaut est 60, ce qui veut dire que le contenu mise en cache expirera au bout de 60 secondes. + + +### Dépendances + +Comme pour la [mise en cache de données](caching-data.md#cache-dependencies), le fragment de contenu mis en cache peut aussi avoir des dépendances. Par exemple, le contenu d'un article affiché dépend du fait que l'article a été modifié ou pas. + +Pour spécifier une dépendance, définissez l'option [[yii\widgets\FragmentCache::dependency|dependency]], soit sous forme d'objet [[yii\caching\Dependency]], soit sous forme d'un tableau de configuration pour créer un objet [[yii\caching\Dependency]]. Le code qui suit spécifie que le fragment de contenu dépend du changement de la valeur de la colonne `updated_at` (mis à jour le) : + +```php +$dependency = [ + 'class' => 'yii\caching\DbDependency', + 'sql' => 'SELECT MAX(updated_at) FROM post', +]; + +if ($this->beginCache($id, ['dependency' => $dependency])) { + + // ... générez le contenu ici ... + + $this->endCache(); +} +``` + + +### Variations + +Le contenu mise en cache peut connaître quelques variations selon certains paramètres. Par exemple, pour une application Web prenant en charge plusieurs langues, le même morceau de code d'une vue peut générer le contenu dans différentes langues. Par conséquent, vous pouvez souhaitez que le contenu mis en cache varie selon la langue courante de l'application. + +Pour spécifier des variations de mise en cache, définissez l'option [[yii\widgets\FragmentCache::variations|variations]], qui doit être un tableau de valeurs scalaires, représentant chacune un facteur de variation particulier. Par exemple, pour que le contenu mis en cache varie selon la langue, vous pouvez utiliser le code suivant : + +```php +if ($this->beginCache($id, ['variations' => [Yii::$app->language]])) { + + // ... générez le contenu ici ... + + $this->endCache(); +} +``` + + +### Activation désactivation de la mise en cache + +Parfois, vous désirez activer la mise en cache de fragments seulement lorsque certaines conditions sont rencontrées. Par exemple, pour une page qui affiche un formulaire, vous désirez seulement mettre le formulaire en cache lorsqu'il est initialement demandé (via une requête GET). Tout affichage subséquent du formulaire (via des requêtes POST) ne devrait pas être mise en cache car il contient des données entrées par l'utilisateur. Pour mettre en œuvre ce mécanisme, vous pouvez définir l'option [[yii\widgets\FragmentCache::enabled|enabled]], comme suit : + +```php +if ($this->beginCache($id, ['enabled' => Yii::$app->request->isGet])) { + + // ... générez le contenu ici ... + + $this->endCache(); +} +``` + + +## Mises en cache imbriquées + +La mise en cache de fragments peut être imbriquée. C'est à dire qu'un fragment mis en cache peut être contenu dans un autre fragment lui aussi mis en cache. +Par exemple, les commentaires sont mis en cache dans un cache de fragment interne, et sont mis en cache en même temps et avec le contenu de l'article dans un cache de fragment externe. Le code qui suit montre comment deux caches de fragment peuvent être imbriqués : + +```php +if ($this->beginCache($id1)) { + + // ...logique de génération du contenu ... + + if ($this->beginCache($id2, $options2)) { + + // ...logique de génération du contenu... + + $this->endCache(); + } + + // ... logique de génération de contenu ... + + $this->endCache(); +} +``` + +Différentes options de mise en cache peuvent être définies pour les caches imbriqués. Par exemple, les caches internes et les caches externes peuvent utiliser des valeurs de durée différentes. Même lorsque les données mises en cache dans le cache externe sont invalidées, le cache interne peut continuer à fournir un fragment interne valide. Néanmoins, le réciproque n'est pas vraie ; si le cache externe est évalué comme valide, il continue à fournir la même copie mise en cache après que le contenu du cache interne a été invalidé. Par conséquent, vous devez être prudent lors de la définition des durées ou des dépendances des caches imbriqués, autrement des fragments internes périmés peuvent subsister dans le fragment externe. + + +## Contenu dynamique + +Lors de l'utilisation de la mise en cache de fragments, vous pouvez rencontrer une situation dans laquelle un gros fragment de contenu est relativement statique en dehors de quelques endroits particuliers. Par exemple, l'entête d'une page peut afficher le menu principal avec le nom de l'utilisateur courant. Un autre problème se rencontre lorsque le contenu mis en cache, contient du code PHP qui doit être exécuté à chacune des requêtes (p. ex. le code pour enregistrer un paquet de ressources). Ces deux problèmes peuvent être résolus par une fonctionnalité qu'on appelle *contenu dynamique*. + +Un contenu dynamique signifie un fragment de sortie qui ne doit jamais être mis en cache même s'il est contenu dans un fragment mis en cache. Pour faire en sorte que le contenu soit dynamique en permanence, il doit être généré en exécutant un code PHP à chaque requête, même si le contenu l'englobant est servi à partir du cache. + +Vous pouvez appeler la fonction [[yii\base\View::renderDynamic()]] dans un fragment mis en cache pour y insérer un contenu dynamique à l'endroit désiré, comme ceci : + +```php +if ($this->beginCache($id1)) { + + // ... logique de génération de contenu ... + + echo $this->renderDynamic('return Yii::$app->user->identity->name;'); + + // ... logique de génération de contenu ... + + $this->endCache(); +} +``` + +La méthode [[yii\base\View::renderDynamic()|renderDynamic()]] accepte un morceau de code PHP en paramètre. La valeur retournée est traitée comme un contenu dynamique. Le même code PHP est exécuté à chacune des requêtes, peu importe que le fragment englobant soit servi à partir du cache ou pas. From 49539d81826a17eeb0ad1e0aedefa3139d1cdd5c Mon Sep 17 00:00:00 2001 From: jaaf Date: Tue, 21 Feb 2017 13:14:59 +0100 Subject: [PATCH 34/75] Translated caching-page.md into FR [skip ci] (#13635) --- docs/guide-fr/caching-page.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/guide-fr/caching-page.md diff --git a/docs/guide-fr/caching-page.md b/docs/guide-fr/caching-page.md new file mode 100644 index 0000000000..47aaef58ea --- /dev/null +++ b/docs/guide-fr/caching-page.md @@ -0,0 +1,33 @@ +Mise en cache de pages +====================== + +La mise en cache de pages fait référence à la mise en cache du contenu d'une page entière du côté serveur. Plus tard, lorsque la même page est demandée à nouveau, son contenu est servi à partir du cache plutôt que d'être régénéré entièrement. + +La mise en cache de pages est prise en charge par [[yii\filters\PageCache]], un [filtre d'action](structure-filters.md). On peut l'utiliser de la manière suivante dans une classe contrôleur : + +```php +public function behaviors() +{ + return [ + [ + 'class' => 'yii\filters\PageCache', + 'only' => ['index'], + 'duration' => 60, + 'variations' => [ + \Yii::$app->language, + ], + 'dependency' => [ + 'class' => 'yii\caching\DbDependency', + 'sql' => 'SELECT COUNT(*) FROM post', + ], + ], + ]; +} +``` + +Le code ci-dessus établit que la mise en cache de pages doit être utilisée uniquement pour l'action `index`. Le contenu de la page doit être mis en cache pour au plus 60 secondes et doit varier selon la langue courante de l'application. De plus, le contenu de la page mis en cache doit être invalidé si le nombre total d'articles (post) change. + +Comme vous pouvez le constater, la mise en cache de pages est très similaire à la [mise en cache de fragments](caching-fragment.md). Les deux prennent en charge les options telles que `duration`, `dependencies`, `variations` et `enabled`. La différence principale est que la mise en cache de pages est mis en œuvre comme un [filtre d'action](structure-filters.md) alors que la mise en cache de framgents l'est comme un [composant graphique](structure-widgets.md). + +Vous pouvez utiliser la [mise en cache de fragments](caching-fragment.md) ainsi que le [contenu dynamique](caching-fragment.md#dynamic-content) en simultanéité avec la mise en cache de pages. + From 1b06898c269796569022f5df420acd52d0090c61 Mon Sep 17 00:00:00 2001 From: jaaf Date: Tue, 21 Feb 2017 17:49:46 +0100 Subject: [PATCH 35/75] adding caching-http.md FR (#13636) --- docs/guide-fr/caching-http.md | 108 ++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 docs/guide-fr/caching-http.md diff --git a/docs/guide-fr/caching-http.md b/docs/guide-fr/caching-http.md new file mode 100644 index 0000000000..f67260a2d4 --- /dev/null +++ b/docs/guide-fr/caching-http.md @@ -0,0 +1,108 @@ +Mise en cache HTTP +============ + +En plus de la mise en cache côté serveur que nous avons décrite dans les sections précédentes, les applications Web peuvent aussi exploiter la mise en cache côté client pour économiser le temps de génération et de transfert d'un contenu de page inchangé. + +Pour utiliser la mise en cache côté client, vous pouvez configurer [[yii\filters\HttpCache]] comme un filtre pour des actions de contrôleur dont le résultat rendu peut être mis en cache du côté du client. [[yii\filters\HttpCache|HttpCache]] +ne fonctionne que pour les requêtes `GET` et `HEAD`. Il peut gérer trois sortes d'entêtes HTTP relatifs à la mise en cache pour ces requêtes : + +* [[yii\filters\HttpCache::lastModified|Last-Modified]] +* [[yii\filters\HttpCache::etagSeed|Etag]] +* [[yii\filters\HttpCache::cacheControlHeader|Cache-Control]] + + +## Entête `Last-Modified` + +L'entête `Last-Modified` (dernière modification) utilise un horodatage pour indiquer si la page a été modifiée depuis sa mise en cache par le client. + +Vous pouvez configurer la propriété [[yii\filters\HttpCache::lastModified]] pour activer l'envoi de l'entête `Last-modified`. La propriété doit être une fonction de rappel PHP qui retourne un horodatage UNIX concernant la modification de la page. La signature de la fonction de rappel PHP doit être comme suit : + +```php +/** + * @param Action $action l'objet action qui est actuellement géré + * @param array $params la valeur de la propriété "params" + * @return int un horodatage UNIX représentant l'instant de modification de la page + */ +function ($action, $params) +``` + +Ce qui suit est un exemple d'utilisation de l'entête `Last-Modified` : + +```php +public function behaviors() +{ + return [ + [ + 'class' => 'yii\filters\HttpCache', + 'only' => ['index'], + 'lastModified' => function ($action, $params) { + $q = new \yii\db\Query(); + return $q->from('post')->max('updated_at'); + }, + ], + ]; +} +``` + +Le code précédent établit que la mise en cache HTTP doit être activée pour l'action `index` seulement. Il doit générer un entête HTTP `Last-Modified` basé sur l'instant de la dernière mise à jour d'articles (posts). Lorsque le navigateur visite la page `index` pour la première fois, la page est générée par le serveur et envoyée au navigateur. Si le navigateur visite à nouveau la même page, et qu'aucun article n'a été modifié, le serveur ne régénère par la page, et le navigateur utilise la version mise en cache du côté du client. En conséquence, le rendu côté serveur et la transmission de la page sont tous deux évités. + + +## Entête `ETag` + +L'entête "Entity Tag" (or `ETag` en raccourci) utilise une valeur de hachage pour représenter le contenu d'une page. Si la page est modifiée, la valeur de hachage change également. En comparant la valeur de hachage conservée sur le client avec la valeur de hachage générée côté serveur, le cache peut déterminer si la page a été modifiée et doit être retransmise. + +Vous pouvez configurer la propriété [[yii\filters\HttpCache::etagSeed]] pour activer l'envoi de l'entête `ETag`. La propriété doit être une fonction de rappel PHP qui retourne un nonce (sel) pour la génération de la valeur de hachage Etag. La signature de la fonction de rappel PHP doit être comme suit : + +```php +/** + * @param Action $action l'objet action qui est actuellement géré + * @param array $params la valeur de la propriété "params" + * @return string une chaîne de caractères à utiliser comme nonce (sel) pour la génération d'une valeur de hachage ETag + */ +function ($action, $params) +``` + +Ce qui suit est un exemple d'utilisation de l'entête `ETag` : + +```php +public function behaviors() +{ + return [ + [ + 'class' => 'yii\filters\HttpCache', + 'only' => ['view'], + 'etagSeed' => function ($action, $params) { + $post = $this->findModel(\Yii::$app->request->get('id')); + return serialize([$post->title, $post->content]); + }, + ], + ]; +} +``` + +Le code ci-dessus établit que la mise en cache HTTP doit être activée pour l'action `view` seulement. Il doit générer un entête HTTP `ETag` basé sur le titre et le contenu de l'article demandé. Lorsque le navigateur visite la page pour la première fois, la page est générée par le serveur et envoyée au navigateur. Si le navigateur visite à nouveau la même page et que ni le titre, ni le contenu de l'article n'ont changé, le serveur ne régénère pas la page et le navigateur utilise la version mise en cache côté client. En conséquence, le rendu par le serveur et la transmission de la page sont tous deux évités. + +ETags vous autorise des stratégies de mises en cache plus complexes et/ou plus précises que l'entête `Last-Modified`. Par exemple, un ETag peut être invalidé si on a commuté le site sur un nouveau thème. + +Des génération coûteuses d'ETag peuvent contrecarrer l'objectif poursuivi en utilisant `HttpCache` et introduire une surcharge inutile, car il faut les réévaluer à chacune des requêtes. Essayez de trouver une expression simple qui invalide le cache si le contenu de la page a été modifié. + +> Note : en conformité avec la norme [RFC 7232](http://tools.ietf.org/html/rfc7232#section-2.4), + `HttpCache` envoie les entêtes `ETag` et `Last-Modified` à la fois si ils sont tous deux configurés. Et si le client envoie les entêtes `If-None-Match` et `If-Modified-Since` à la fois, seul le premier est respecté. + + +## Entête `Cache-Control` + +L'entête `Cache-Control` spécifie la politique de mise en cache générale pour les pages. Vous pouvez l'envoyer en configurant la propriété [[yii\filters\HttpCache::cacheControlHeader]] avec la valeur de l'entête. Par défaut, l'entête suivant est envoyé : + +``` +Cache-Control: public, max-age=3600 +``` + +## Propriété "Session Cache Limiter" + +Lorsqu'une page utilise une session, PHP envoie automatiquement quelques entêtes HTTP relatifs à la mise en cache comme spécifié dans la propriété `session.cache_limiter` de PHP INI. Ces entêtes peuvent interférer ou désactiver la mise en cache que vous voulez obtenir de `HttpCache`. Pour éviter ce problème, par défaut, `HttpCache` désactive l'envoi de ces entêtes automatiquement. Si vous désirez modifier ce comportement, vous devez configurer la propriété [[yii\filters\HttpCache::sessionCacheLimiter]]. Cette propriété accepte une chaîne de caractères parmi `public`, `private`, `private_no_expire` et `nocache`. Reportez-vous au manuel de PHP à propos de [session_cache_limiter()](http://www.php.net/manual/en/function.session-cache-limiter.php) pour des explications sur ces valeurs. + + +## Implications SEO + +Les robots moteurs de recherche ont tendance à respecter les entêtes de mise en cache. Comme certains moteurs d'indexation du Web sont limités quant aux nombre de pages par domaine qu'ils sont à même de traiter dans un certain laps de temps, l'introduction d'entêtes de mise en cache peut aider à l'indexation de votre site car ils limitent le nombre de pages qui ont besoin d'être traitées. From 50927d075169939865fc36201bde2616237e5a91 Mon Sep 17 00:00:00 2001 From: SilverFire - Dmitry Naumenko Date: Tue, 21 Feb 2017 22:36:26 +0200 Subject: [PATCH 36/75] Enhanced core-validators docs Closes #13485 --- docs/guide-ru/tutorial-core-validators.md | 4 ++++ docs/guide/tutorial-core-validators.md | 3 +++ 2 files changed, 7 insertions(+) diff --git a/docs/guide-ru/tutorial-core-validators.md b/docs/guide-ru/tutorial-core-validators.md index 9c583f32c7..8f3dece4a5 100644 --- a/docs/guide-ru/tutorial-core-validators.md +++ b/docs/guide-ru/tutorial-core-validators.md @@ -623,3 +623,7 @@ IPv4 адрес `192.168.10.128` также разрешен, так как на По умолчанию - `false`. Учтите, что для того, чтобы IDN валидация работала корректно, вы должны установить `intl` PHP расширение, иначе будет выброшено исключение. +> Note: Валидатор проверяет, что протокол и хост в URL являются корректными. Валидатор НЕ проверяет другие части URL +и НЕ предназначен для защиты от XSS или любых других видов атак. Обратитесь к секции +[Лучшие практики безопасности](security-best-practices.md) чтобы узнать больше о том, как предтвращать известные угрозы +при разработке приложений. diff --git a/docs/guide/tutorial-core-validators.md b/docs/guide/tutorial-core-validators.md index 178191e3d8..9c22fc3753 100644 --- a/docs/guide/tutorial-core-validators.md +++ b/docs/guide/tutorial-core-validators.md @@ -677,3 +677,6 @@ This validator checks if the input value is a valid URL. Defaults to `false`. Note that in order to use IDN validation you have to install and enable the `intl` PHP extension, otherwise an exception would be thrown. +> Note: The validator checks that URL scheme and host part is correct. It does NOT check the remaining parts of a URL +and is NOT designed to protect against XSS or any other attacks. See [Security best practices](security-best-practices.md) +article to learn more about threats prevention when developing applications. From 1dc4618f3dd196af5e9313664e2b808935f5d3ae Mon Sep 17 00:00:00 2001 From: SilverFire - Dmitry Naumenko Date: Tue, 21 Feb 2017 23:37:17 +0200 Subject: [PATCH 37/75] Fixed `MessageConstroller::saveMessagesToDb()` to work on different DBMS correctly Closes #13494 --- framework/CHANGELOG.md | 1 + .../console/controllers/MessageController.php | 40 +++++++++++-------- .../controllers/DbMessageControllerTest.php | 2 +- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 051a2af275..faf80ff5af 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -21,6 +21,7 @@ Yii Framework 2 Change Log - Bug #13592: Fixes Oracle’s `yii\db\oci\Schema::setTransactionIsolationLevel()` (sergeymakinen) - Bug #13594: Fixes insufficient quoting in `yii\db\QueryBuilder::prepareInsertSelectSubQuery()` (sergeymakinen) - Enh #13576: Added support of `srcset` to `yii\helpers\Html::img()` (Kolyunya) +- Bug #13494: Fixed `yii\console\controllers\MessageConstroller::saveMessagesToDb()` to work on different DBMS correctly (silverfire) - Enh #8641: Enhanced `yii\console\Request::resolve()` to prevent passing parameters, that begin from digits (silverfire) - Enh #13467: `yii\data\ActiveDataProvider` no longer queries models if models count is zero (kLkA, Kolyunya) - Enh #13582: Added tests for all `yii\db\QueryBuilder::resetSequence` implementations, fixed SQLite implementation (boboldehampsink) diff --git a/framework/console/controllers/MessageController.php b/framework/console/controllers/MessageController.php index a58d34af12..5ace795255 100644 --- a/framework/console/controllers/MessageController.php +++ b/framework/console/controllers/MessageController.php @@ -443,23 +443,31 @@ EOD; if (empty($obsolete)) { $this->stdout("Nothing obsoleted...skipped.\n"); - } else { - if ($removeUnused) { - $db->createCommand() - ->delete($sourceMessageTable, ['in', 'id', $obsolete]) - ->execute(); - $this->stdout("deleted.\n"); - } elseif ($markUnused) { - $db->createCommand() - ->update( - $sourceMessageTable, - ['message' => new \yii\db\Expression("CONCAT('@@',message,'@@')")], - ['in', 'id', $obsolete] - )->execute(); - $this->stdout("updated.\n"); - } else { - $this->stdout("kept untouched.\n"); + return; + } + + if ($removeUnused) { + $db->createCommand() + ->delete($sourceMessageTable, ['in', 'id', $obsolete]) + ->execute(); + $this->stdout("deleted.\n"); + } elseif ($markUnused) { + $rows = (new Query) + ->select(['id', 'message']) + ->from($sourceMessageTable) + ->where(['in', 'id', $obsolete]) + ->all($db); + + foreach ($rows as $row) { + $db->createCommand()->update( + $sourceMessageTable, + ['message' => '@@' . $row['message'] . '@@'], + ['id' => $row['id']] + )->execute(); } + $this->stdout("updated.\n"); + } else { + $this->stdout("kept untouched.\n"); } } diff --git a/tests/framework/console/controllers/DbMessageControllerTest.php b/tests/framework/console/controllers/DbMessageControllerTest.php index 28b9c6d946..4904c6e359 100644 --- a/tests/framework/console/controllers/DbMessageControllerTest.php +++ b/tests/framework/console/controllers/DbMessageControllerTest.php @@ -176,4 +176,4 @@ class DbMessageControllerTest extends BaseMessageControllerTest $this->assertArrayHasKey($obsoleteMessage, $messages, "Obsolete message should not be removed. Command output:\n\n" . $out); $this->assertEquals($obsoleteTranslation, $messages[$obsoleteMessage], "Obsolete message was not marked properly. Command output:\n\n" . $out); } -} \ No newline at end of file +} From 643431a2e3b858023db62b9677d131049b0c150f Mon Sep 17 00:00:00 2001 From: SilverFire - Dmitry Naumenko Date: Tue, 21 Feb 2017 23:38:27 +0200 Subject: [PATCH 38/75] Reordered CHANGELOG --- framework/CHANGELOG.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index faf80ff5af..ff06ff5a94 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -4,26 +4,26 @@ Yii Framework 2 Change Log 2.0.12 under development -------------------------- -- Bug #13306: Wildcard in `reloadableScripts` in `yii.js` allows 0 characters (arogachev) -- Enh #13523: Plural rule for pasta (developeruz) -- Bug #13538: Fixed `yii\db\BaseActiveRecord::deleteAll()` changes method signature declared by `yii\db\ActiveRecordInterface::deleteAll()` (klimov-paul) -- Bug #13340: Fixed `yii\db\Connection::useMaster()` - Exception within callback completely disables slaves (Vovan-VE) -- Enh #13278: `yii\caching\DbQueryDependency` created allowing specification of the cache dependency via `yii\db\QueryInterface` (klimov-paul) - Bug #11230: Include `defaultRoles` in `yii\rbac\DbManager->getRolesByUser()` results (developeruz) -- Bug #13343: Fixed `yii\i18n\Formatter::asTime()` to process time-only values without time zone conversion (bizley) - Bug #11404: `yii\base\Model::loadMultiple()` returns true even if `yii\base\Model::load()` returns false (zvook) -- Bug #13577: `yii\db\QueryBuilder::truncateTable` should work consistent over all databases (boboldehampsink) -- Enh #13577: Implemented `yii\db\mssql\QueryBuilder::resetSequence()` (boboldehampsink) +- Bug #13306: Wildcard in `reloadableScripts` in `yii.js` allows 0 characters (arogachev) +- Bug #13340: Fixed `yii\db\Connection::useMaster()` - Exception within callback completely disables slaves (Vovan-VE) +- Bug #13343: Fixed `yii\i18n\Formatter::asTime()` to process time-only values without time zone conversion (bizley) +- Bug #13494: Fixed `yii\console\controllers\MessageConstroller::saveMessagesToDb()` to work on different DBMS correctly (silverfire) - Bug #13513: Fixed RBAC migration to work correctly on Oracle DBMS (silverfire) -- Enh #13550: Refactored unset call order in `yii\di\ServiceLocator::set()` (Lanrik) -- Bug #13582: PK column in `yii\db\pgsql\QueryBuilder::resetSequence()` was not quoted properly (boboldehampsink) - Bug #13537: Fixed `yii\web\CacheSession::destroySession()` to work correctly when session is not written yet (silverfire, papalapa) +- Bug #13538: Fixed `yii\db\BaseActiveRecord::deleteAll()` changes method signature declared by `yii\db\ActiveRecordInterface::deleteAll()` (klimov-paul) +- Bug #13577: `yii\db\QueryBuilder::truncateTable` should work consistent over all databases (boboldehampsink) +- Bug #13582: PK column in `yii\db\pgsql\QueryBuilder::resetSequence()` was not quoted properly (boboldehampsink) - Bug #13592: Fixes Oracle’s `yii\db\oci\Schema::setTransactionIsolationLevel()` (sergeymakinen) - Bug #13594: Fixes insufficient quoting in `yii\db\QueryBuilder::prepareInsertSelectSubQuery()` (sergeymakinen) -- Enh #13576: Added support of `srcset` to `yii\helpers\Html::img()` (Kolyunya) -- Bug #13494: Fixed `yii\console\controllers\MessageConstroller::saveMessagesToDb()` to work on different DBMS correctly (silverfire) - Enh #8641: Enhanced `yii\console\Request::resolve()` to prevent passing parameters, that begin from digits (silverfire) +- Enh #13278: `yii\caching\DbQueryDependency` created allowing specification of the cache dependency via `yii\db\QueryInterface` (klimov-paul) - Enh #13467: `yii\data\ActiveDataProvider` no longer queries models if models count is zero (kLkA, Kolyunya) +- Enh #13523: Plural rule for pasta (developeruz) +- Enh #13550: Refactored unset call order in `yii\di\ServiceLocator::set()` (Lanrik) +- Enh #13576: Added support of `srcset` to `yii\helpers\Html::img()` (Kolyunya) +- Enh #13577: Implemented `yii\db\mssql\QueryBuilder::resetSequence()` (boboldehampsink) - Enh #13582: Added tests for all `yii\db\QueryBuilder::resetSequence` implementations, fixed SQLite implementation (boboldehampsink) From b4c7aaa5e03587752b06c009772792ccb21482cf Mon Sep 17 00:00:00 2001 From: SilverFire - Dmitry Naumenko Date: Tue, 21 Feb 2017 23:57:55 +0200 Subject: [PATCH 39/75] Added more examples to EmailValidatorTest Closes #9201 Closes #9067 --- tests/framework/validators/EmailValidatorTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/framework/validators/EmailValidatorTest.php b/tests/framework/validators/EmailValidatorTest.php index e1ffe3cb41..1b49ed8682 100644 --- a/tests/framework/validators/EmailValidatorTest.php +++ b/tests/framework/validators/EmailValidatorTest.php @@ -41,6 +41,7 @@ class EmailValidatorTest extends TestCase $this->assertTrue($validator->validate('"Carsten Brandt" ')); $this->assertTrue($validator->validate('')); $this->assertFalse($validator->validate('info@örtliches.de')); + $this->assertFalse($validator->validate('üñîçøðé@üñîçøðé.com')); $this->assertFalse($validator->validate('sam@рмкреатиф.ru')); $this->assertFalse($validator->validate('Informtation info@oertliches.de')); $this->assertTrue($validator->validate('test@example.com')); @@ -68,6 +69,7 @@ class EmailValidatorTest extends TestCase $this->assertTrue($validator->validate('sam@рмкреатиф.ru')); $this->assertTrue($validator->validate('sam@rmcreative.ru')); $this->assertTrue($validator->validate('5011@gmail.com')); + $this->assertTrue($validator->validate('üñîçøðé@üñîçøðé.com')); $this->assertFalse($validator->validate('rmcreative.ru')); $this->assertFalse($validator->validate('Carsten Brandt ')); $this->assertFalse($validator->validate('"Carsten Brandt" ')); @@ -84,6 +86,7 @@ class EmailValidatorTest extends TestCase $this->assertFalse($validator->validate('rmcreative.ru')); $this->assertTrue($validator->validate('Carsten Brandt ')); $this->assertTrue($validator->validate('"Carsten Brandt" ')); + $this->assertTrue($validator->validate('üñîçøðé 日本国 <üñîçøðé@üñîçøðé.com>')); $this->assertTrue($validator->validate('')); $this->assertTrue($validator->validate('test@example.com')); $this->assertTrue($validator->validate('John Smith ')); From 953c4a8e5a9f8b393c7baa43b723e55d4acc9862 Mon Sep 17 00:00:00 2001 From: Andrew Nester Date: Wed, 22 Feb 2017 16:54:27 +0300 Subject: [PATCH 40/75] Fixes #13407: Added URL-safe base64 encode/decode methods to `StringHelper` --- framework/CHANGELOG.md | 2 +- framework/base/Security.php | 4 +-- framework/helpers/BaseStringHelper.php | 26 ++++++++++++++++ tests/framework/helpers/StringHelperTest.php | 32 ++++++++++++++++++++ 4 files changed, 60 insertions(+), 4 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index ff06ff5a94..6eb0c3ab2e 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -25,6 +25,7 @@ Yii Framework 2 Change Log - Enh #13576: Added support of `srcset` to `yii\helpers\Html::img()` (Kolyunya) - Enh #13577: Implemented `yii\db\mssql\QueryBuilder::resetSequence()` (boboldehampsink) - Enh #13582: Added tests for all `yii\db\QueryBuilder::resetSequence` implementations, fixed SQLite implementation (boboldehampsink) +- Enh #13407: Added URL-safe base64 encode/decode methods to `StringHelper` (andrewnester) 2.0.11.2 February 08, 2017 @@ -170,7 +171,6 @@ Yii Framework 2 Change Log - Enh: Added support for field `yii\console\controllers\BaseMigrateController::$migrationNamespaces` setup from CLI (schmunk42) - Chg #11906: Updated `yii\widgets\MaskedInput` inputmask dependency to `~3.3.3` (samdark) - 2.0.10 October 20, 2016 ----------------------- diff --git a/framework/base/Security.php b/framework/base/Security.php index ae3635057e..2153cf8e73 100644 --- a/framework/base/Security.php +++ b/framework/base/Security.php @@ -559,9 +559,7 @@ class Security extends Component } $bytes = $this->generateRandomKey($length); - // '=' character(s) returned by base64_encode() are always discarded because - // they are guaranteed to be after position $length in the base64_encode() output. - return strtr(substr(base64_encode($bytes), 0, $length), '+/', '_-'); + return substr(StringHelper::base64UrlEncode($bytes), 0, $length); } /** diff --git a/framework/helpers/BaseStringHelper.php b/framework/helpers/BaseStringHelper.php index 9f572e2e1b..e06da6ed6e 100644 --- a/framework/helpers/BaseStringHelper.php +++ b/framework/helpers/BaseStringHelper.php @@ -305,4 +305,30 @@ class BaseStringHelper return $value; } + + /** + * Encodes string into "Base 64 Encoding with URL and Filename Safe Alphabet" (RFC 4648) + * @see https://tools.ietf.org/html/rfc4648#page-7 + * + * @param string $input + * @return string + * @since 2.0.11 + */ + public static function base64UrlEncode($input) + { + return strtr(base64_encode($input), '+/', '-_'); + } + + /** + * Decodes "Base 64 Encoding with URL and Filename Safe Alphabet" (RFC 4648) + * @see https://tools.ietf.org/html/rfc4648#page-7 + * + * @param string $input + * @return string + * @since 2.0.11 + */ + public static function base64UrlDecode($input) + { + return base64_decode(strtr($input, '-_', '+/')); + } } diff --git a/tests/framework/helpers/StringHelperTest.php b/tests/framework/helpers/StringHelperTest.php index b00564115c..1e505871b1 100644 --- a/tests/framework/helpers/StringHelperTest.php +++ b/tests/framework/helpers/StringHelperTest.php @@ -264,4 +264,36 @@ class StringHelperTest extends TestCase $this->assertEquals(1, StringHelper::countWords('крем-брюле')); $this->assertEquals(1, StringHelper::countWords(' слово ')); } + + /** + * @dataProvider base64UrlEncodedStringsProvider + * @param $input + * @param $base64UrlEncoded + */ + public function testBase64UrlEncode($input, $base64UrlEncoded) + { + $encoded = StringHelper::base64UrlEncode($input); + $this->assertEquals($base64UrlEncoded, $encoded); + } + + /** + * @dataProvider base64UrlEncodedStringsProvider + * @param $output + * @param $base64UrlEncoded + */ + public function testBase64UrlDecode($output, $base64UrlEncoded) + { + $decoded = StringHelper::base64UrlDecode($base64UrlEncoded); + $this->assertEquals($output, $decoded); + } + + public function base64UrlEncodedStringsProvider() + { + return [ + ['This is an encoded string', 'VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw=='], + ['subjects?_d=1', 'c3ViamVjdHM_X2Q9MQ=='], + ['subjects>_d=1', 'c3ViamVjdHM-X2Q9MQ=='], + ['Это закодированная строка', '0K3RgtC-INC30LDQutC-0LTQuNGA0L7QstCw0L3QvdCw0Y8g0YHRgtGA0L7QutCw'], + ]; + } } From 0ea45c78a8fcc926c6fb72eeaa8ed37788e5286d Mon Sep 17 00:00:00 2001 From: Bob Olde Hampsink Date: Wed, 22 Feb 2017 14:57:32 +0100 Subject: [PATCH 41/75] Fixes #13571: Fix `yii\db\mssql\QueryBuilder::checkIntegrity` for all tables --- framework/CHANGELOG.md | 2 ++ framework/db/ViewFinderTrait.php | 47 +++++++++++++++++++++++++++++ framework/db/mssql/QueryBuilder.php | 24 ++++++++------- framework/db/mssql/Schema.php | 22 ++++++++++++++ framework/db/pgsql/QueryBuilder.php | 2 +- framework/db/pgsql/Schema.php | 31 +++---------------- 6 files changed, 89 insertions(+), 39 deletions(-) create mode 100644 framework/db/ViewFinderTrait.php diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 6eb0c3ab2e..49a5f37c2c 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -4,6 +4,8 @@ Yii Framework 2 Change Log 2.0.12 under development -------------------------- + +- Bug #13571: Fix `yii\db\mssql\QueryBuilder::checkIntegrity` for all tables (boboldehampsink) - Bug #11230: Include `defaultRoles` in `yii\rbac\DbManager->getRolesByUser()` results (developeruz) - Bug #11404: `yii\base\Model::loadMultiple()` returns true even if `yii\base\Model::load()` returns false (zvook) - Bug #13306: Wildcard in `reloadableScripts` in `yii.js` allows 0 characters (arogachev) diff --git a/framework/db/ViewFinderTrait.php b/framework/db/ViewFinderTrait.php new file mode 100644 index 0000000000..566d0fc2e1 --- /dev/null +++ b/framework/db/ViewFinderTrait.php @@ -0,0 +1,47 @@ + + * @author Bob Olde Hampsink + * @since 2.0.12 + */ +trait ViewFinderTrait +{ + /** + * @var array list of ALL view names in the database + */ + private $_viewNames = []; + + /** + * Returns all views names in the database. + * @param string $schema the schema of the views. Defaults to empty string, meaning the current or default schema. + * @return array all views names in the database. The names have NO schema name prefix. + */ + abstract protected function findViewNames($schema = ''); + + /** + * Returns all view names in the database. + * @param string $schema the schema of the views. Defaults to empty string, meaning the current or default schema name. + * If not empty, the returned view names will be prefixed with the schema name. + * @param bool $refresh whether to fetch the latest available view names. If this is false, + * view names fetched previously (if available) will be returned. + * @return string[] all view names in the database. + */ + public function getViewNames($schema = '', $refresh = false) + { + if (!isset($this->_viewNames[$schema]) || $refresh) { + $this->_viewNames[$schema] = $this->findViewNames($schema); + } + + return $this->_viewNames[$schema]; + } +} diff --git a/framework/db/mssql/QueryBuilder.php b/framework/db/mssql/QueryBuilder.php index d15e0e5e5b..30dbe64f36 100644 --- a/framework/db/mssql/QueryBuilder.php +++ b/framework/db/mssql/QueryBuilder.php @@ -198,23 +198,25 @@ class QueryBuilder extends \yii\db\QueryBuilder /** * Builds a SQL statement for enabling or disabling integrity check. * @param bool $check whether to turn on or off the integrity check. - * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema. - * @param string $table the table name. Defaults to empty string, meaning that no table will be changed. + * @param string $schema the schema of the tables. + * @param string $table the table name. * @return string the SQL statement for checking integrity - * @throws InvalidParamException if the table does not exist or there is no sequence associated with the table. */ public function checkIntegrity($check = true, $schema = '', $table = '') { - if ($schema !== '') { - $table = "{$schema}.{$table}"; - } - $table = $this->db->quoteTableName($table); - if ($this->db->getTableSchema($table) === null) { - throw new InvalidParamException("Table not found: $table"); - } $enable = $check ? 'CHECK' : 'NOCHECK'; + $schema = $schema ? $schema : $this->db->getSchema()->defaultSchema; + $tableNames = $this->db->getTableSchema($table) ? [$table] : $this->db->getSchema()->getTableNames($schema); + $viewNames = $this->db->getSchema()->getViewNames($schema); + $tableNames = array_diff($tableNames, $viewNames); + $command = ''; - return "ALTER TABLE {$table} {$enable} CONSTRAINT ALL"; + foreach ($tableNames as $tableName) { + $tableName = $this->db->quoteTableName("{$schema}.{$tableName}"); + $command .= "ALTER TABLE $tableName $enable CONSTRAINT ALL; "; + } + + return $command; } /** diff --git a/framework/db/mssql/Schema.php b/framework/db/mssql/Schema.php index 1abcaee59d..045f746cc7 100644 --- a/framework/db/mssql/Schema.php +++ b/framework/db/mssql/Schema.php @@ -8,6 +8,7 @@ namespace yii\db\mssql; use yii\db\ColumnSchema; +use yii\db\ViewFinderTrait; /** * Schema is the class for retrieving metadata from a MS SQL Server databases (version 2008 and above). @@ -17,6 +18,8 @@ use yii\db\ColumnSchema; */ class Schema extends \yii\db\Schema { + use ViewFinderTrait; + /** * @var string the default schema used for the current session. */ @@ -411,6 +414,25 @@ SQL; return $this->db->createCommand($sql, [':schema' => $schema])->queryColumn(); } + /** + * @inheritdoc + */ + protected function findViewNames($schema = '') + { + if ($schema === '') { + $schema = $this->defaultSchema; + } + + $sql = <<db->createCommand($sql, [':schema' => $schema])->queryColumn(); + } + /** * Returns all unique indexes for the given table. * Each array element is of the following structure: diff --git a/framework/db/pgsql/QueryBuilder.php b/framework/db/pgsql/QueryBuilder.php index 2f6127f894..fb3330eae7 100644 --- a/framework/db/pgsql/QueryBuilder.php +++ b/framework/db/pgsql/QueryBuilder.php @@ -195,7 +195,7 @@ class QueryBuilder extends \yii\db\QueryBuilder $command = ''; foreach ($tableNames as $tableName) { - $tableName = '"' . $schema . '"."' . $tableName . '"'; + $tableName = $this->db->quoteTableName("{$schema}.{$tableName}"); $command .= "ALTER TABLE $tableName $enable TRIGGER ALL; "; } diff --git a/framework/db/pgsql/Schema.php b/framework/db/pgsql/Schema.php index b32285799c..0d0fa3455b 100644 --- a/framework/db/pgsql/Schema.php +++ b/framework/db/pgsql/Schema.php @@ -10,6 +10,7 @@ namespace yii\db\pgsql; use yii\db\Expression; use yii\db\TableSchema; use yii\db\ColumnSchema; +use yii\db\ViewFinderTrait; /** * Schema is the class for retrieving metadata from a PostgreSQL database @@ -22,6 +23,8 @@ use yii\db\ColumnSchema; */ class Schema extends \yii\db\Schema { + use ViewFinderTrait; + /** * @var string the default schema used for the current session. */ @@ -110,11 +113,6 @@ class Schema extends \yii\db\Schema 'xml' => self::TYPE_STRING, ]; - /** - * @var array list of ALL view names in the database - */ - private $_viewNames = []; - /** * Creates a query builder for the PostgreSQL database. @@ -212,10 +210,7 @@ SQL; } /** - * Returns all views names in the database. - * @param string $schema the schema of the views. Defaults to empty string, meaning the current or default schema. - * @return array all views names in the database. The names have NO schema name prefix. - * @since 2.0.9 + * @inheritdoc */ protected function findViewNames($schema = '') { @@ -232,24 +227,6 @@ SQL; return $this->db->createCommand($sql, [':schemaName' => $schema])->queryColumn(); } - /** - * Returns all view names in the database. - * @param string $schema the schema of the views. Defaults to empty string, meaning the current or default schema name. - * If not empty, the returned view names will be prefixed with the schema name. - * @param bool $refresh whether to fetch the latest available view names. If this is false, - * view names fetched previously (if available) will be returned. - * @return string[] all view names in the database. - * @since 2.0.9 - */ - public function getViewNames($schema = '', $refresh = false) - { - if (!isset($this->_viewNames[$schema]) || $refresh) { - $this->_viewNames[$schema] = $this->findViewNames($schema); - } - - return $this->_viewNames[$schema]; - } - /** * Collects the foreign key column details for the given table. * @param TableSchema $table the table metadata From 2de18cf9a5fadd773aa64e9658ba1c7107b8f853 Mon Sep 17 00:00:00 2001 From: Elvira Sheina Date: Wed, 22 Feb 2017 16:06:19 +0200 Subject: [PATCH 42/75] Fixes #13087: Fixed getting active validators for safe attribute --- framework/CHANGELOG.md | 2 +- framework/base/Model.php | 2 +- framework/validators/Validator.php | 38 ++++++++++++++++--- .../models/FakedValidationModel.php | 4 +- tests/framework/validators/ValidatorTest.php | 14 +++++++ 5 files changed, 51 insertions(+), 9 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 49a5f37c2c..9b9f233f73 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -4,7 +4,7 @@ Yii Framework 2 Change Log 2.0.12 under development -------------------------- - +- Bug #13087: Fixed getting active validators for safe attribute (developeruz) - Bug #13571: Fix `yii\db\mssql\QueryBuilder::checkIntegrity` for all tables (boboldehampsink) - Bug #11230: Include `defaultRoles` in `yii\rbac\DbManager->getRolesByUser()` results (developeruz) - Bug #11404: `yii\base\Model::loadMultiple()` returns true even if `yii\base\Model::load()` returns false (zvook) diff --git a/framework/base/Model.php b/framework/base/Model.php index b3e87ab6d8..79078d26c3 100644 --- a/framework/base/Model.php +++ b/framework/base/Model.php @@ -424,7 +424,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab $validators = []; $scenario = $this->getScenario(); foreach ($this->getValidators() as $validator) { - if ($validator->isActive($scenario) && ($attribute === null || in_array($attribute, $validator->attributes, true))) { + if ($validator->isActive($scenario) && ($attribute === null || in_array($attribute, $validator->getAttributeNames(), true))) { $validators[] = $validator; } } diff --git a/framework/validators/Validator.php b/framework/validators/Validator.php index e04d6f4994..4b2f6dc093 100644 --- a/framework/validators/Validator.php +++ b/framework/validators/Validator.php @@ -101,6 +101,11 @@ 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: @@ -233,30 +238,29 @@ class Validator extends Component $this->attributes = (array) $this->attributes; $this->on = (array) $this->on; $this->except = (array) $this->except; + $this->setAttributeNames((array)$this->attributes); } /** * Validates the specified object. * @param \yii\base\Model $model the data model being validated * @param array|null $attributes the list of attributes to be validated. - * Note that if an attribute is not associated with the validator, or is is prefixed with `!` char - it will be + * Note that if an attribute is not associated with the validator - it will be * ignored. If this parameter is null, every attribute listed in [[attributes]] will be validated. + * @since 2.0.12 */ public function validateAttributes($model, $attributes = null) { if (is_array($attributes)) { $newAttributes = []; foreach ($attributes as $attribute) { - if (in_array($attribute, $this->attributes) || in_array('!' . $attribute, $this->attributes)) { + if (in_array($attribute, $this->getAttributeNames(), true)) { $newAttributes[] = $attribute; } } $attributes = $newAttributes; } else { - $attributes = []; - foreach ($this->attributes as $attribute) { - $attributes[] = $attribute[0] === '!' ? substr($attribute, 1) : $attribute; - } + $attributes = $this->getAttributeNames(); } foreach ($attributes as $attribute) { @@ -432,4 +436,26 @@ class Validator extends Component return $value === null || $value === [] || $value === ''; } } + + /** + * Returns cleaned attribute names without the `!` character at the beginning + * @return array + * @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 ltrim($attribute, '!'); + }, $attributeNames); + } } diff --git a/tests/data/validators/models/FakedValidationModel.php b/tests/data/validators/models/FakedValidationModel.php index fa08686a2f..2d769641f2 100644 --- a/tests/data/validators/models/FakedValidationModel.php +++ b/tests/data/validators/models/FakedValidationModel.php @@ -10,6 +10,7 @@ class FakedValidationModel extends Model public $val_attr_b; public $val_attr_c; public $val_attr_d; + public $safe_attr; private $attr = []; private $inlineValArgs; @@ -33,7 +34,8 @@ class FakedValidationModel extends Model [['val_attr_a', 'val_attr_b'], 'required', 'on' => 'reqTest'], ['val_attr_c', 'integer'], ['attr_images', 'file', 'maxFiles' => 3, 'extensions' => ['png'], 'on' => 'validateMultipleFiles', 'checkExtensionByMimeType' => false], - ['attr_image', 'file', 'extensions' => ['png'], 'on' => 'validateFile', 'checkExtensionByMimeType' => false] + ['attr_image', 'file', 'extensions' => ['png'], 'on' => 'validateFile', 'checkExtensionByMimeType' => false], + ['!safe_attr', 'integer'] ]; } diff --git a/tests/framework/validators/ValidatorTest.php b/tests/framework/validators/ValidatorTest.php index 2a7d9ff5da..4f648874d4 100644 --- a/tests/framework/validators/ValidatorTest.php +++ b/tests/framework/validators/ValidatorTest.php @@ -241,4 +241,18 @@ class ValidatorTest extends TestCase $errors = $m->getErrors('attr_msg_val'); $this->assertEquals('attr_msg_val::abc::param_value', $errors[0]); } + + public function testGetActiveValidatorsForSafeAttributes() + { + $model = $this->getTestModel(); + $validators = $model->getActiveValidators('safe_attr'); + $is_found = false; + foreach ($validators as $v) { + if ($v instanceof NumberValidator) { + $is_found = true; + break; + } + } + $this->assertTrue($is_found); + } } From a182ce57fc7cfd1eae1c20b3acd7e5d0a0812032 Mon Sep 17 00:00:00 2001 From: Vladimir Reznichenko Date: Wed, 22 Feb 2017 15:07:52 +0100 Subject: [PATCH 43/75] Fixes for issues found with Static Code Analysis with Php Inspections (EA Extended) (#13606) * Php Inspections (EA Extended): language level migration fixes * Php Inspections (EA Extended): instanceof a trait always return false * Php Inspections (EA Extended): fixed preg_quote (/ is not escaped by default) * Php Inspections (EA Extended): fixed a greedy regex * Php Inspections (EA Extended): refereted instanceof self in a trait * Php Inspections (EA Extended): revert language level changes in requirements checker * Php Inspections (EA Extended): revert language level changes in requirements checker * Php Inspections (EA Extended): more greedy regexes fixed --- build/controllers/PhpDocController.php | 4 ++-- framework/db/pgsql/QueryBuilder.php | 2 +- framework/i18n/I18N.php | 2 +- framework/rbac/DbManager.php | 2 +- framework/rbac/PhpManager.php | 2 +- framework/validators/IpValidator.php | 2 +- framework/web/MultipartFormDataParser.php | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build/controllers/PhpDocController.php b/build/controllers/PhpDocController.php index 9aedda1a47..f8cee3f7d3 100644 --- a/build/controllers/PhpDocController.php +++ b/build/controllers/PhpDocController.php @@ -172,7 +172,7 @@ class PhpDocController extends Controller $except[] = "/extensions/$ext$path"; } } - } elseif (preg_match('~extensions/([\w\d-]+)[\\\\/]?$~', $root, $matches)) { + } elseif (preg_match('~extensions/([\w-]+)[\\\\/]?$~', $root, $matches)) { $extensionPath = dirname(rtrim($root, '\\/')); $this->setUpExtensionAliases($extensionPath); @@ -196,7 +196,7 @@ class PhpDocController extends Controller // if ($extension === 'composer') { // return []; // } - } elseif (preg_match('~apps/([\w\d-]+)[\\\\/]?$~', $root, $matches)) { + } elseif (preg_match('~apps/([\w-]+)[\\\\/]?$~', $root, $matches)) { $extensionPath = dirname(dirname(rtrim($root, '\\/'))) . '/extensions'; $this->setUpExtensionAliases($extensionPath); diff --git a/framework/db/pgsql/QueryBuilder.php b/framework/db/pgsql/QueryBuilder.php index fb3330eae7..77b3ae91df 100644 --- a/framework/db/pgsql/QueryBuilder.php +++ b/framework/db/pgsql/QueryBuilder.php @@ -188,7 +188,7 @@ class QueryBuilder extends \yii\db\QueryBuilder public function checkIntegrity($check = true, $schema = '', $table = '') { $enable = $check ? 'ENABLE' : 'DISABLE'; - $schema = $schema ? $schema : $this->db->getSchema()->defaultSchema; + $schema = $schema ?: $this->db->getSchema()->defaultSchema; $tableNames = $table ? [$table] : $this->db->getSchema()->getTableNames($schema); $viewNames = $this->db->getSchema()->getViewNames($schema); $tableNames = array_diff($tableNames, $viewNames); diff --git a/framework/i18n/I18N.php b/framework/i18n/I18N.php index c8e87d1437..36bc1aecad 100644 --- a/framework/i18n/I18N.php +++ b/framework/i18n/I18N.php @@ -109,7 +109,7 @@ class I18N extends Component return $message; } - if (preg_match('~{\s*[\d\w]+\s*,~u', $message)) { + if (preg_match('~{\s*[\w]+\s*,~u', $message)) { $formatter = $this->getMessageFormatter(); $result = $formatter->format($message, $params, $language); if ($result === false) { diff --git a/framework/rbac/DbManager.php b/framework/rbac/DbManager.php index b66f6e4da9..fca49eee35 100644 --- a/framework/rbac/DbManager.php +++ b/framework/rbac/DbManager.php @@ -480,7 +480,7 @@ class DbManager extends BaseManager { $role = $this->getRole($roleName); - if (is_null($role)) { + if ($role === null) { throw new InvalidParamException("Role \"$roleName\" not found."); } diff --git a/framework/rbac/PhpManager.php b/framework/rbac/PhpManager.php index fcb328183c..d489a751fd 100644 --- a/framework/rbac/PhpManager.php +++ b/framework/rbac/PhpManager.php @@ -410,7 +410,7 @@ class PhpManager extends BaseManager { $role = $this->getRole($roleName); - if (is_null($role)) { + if ($role === null) { throw new InvalidParamException("Role \"$roleName\" not found."); } diff --git a/framework/validators/IpValidator.php b/framework/validators/IpValidator.php index 55f762006c..6533e7556f 100644 --- a/framework/validators/IpValidator.php +++ b/framework/validators/IpValidator.php @@ -527,7 +527,7 @@ class IpValidator extends Validator */ private function getIpParsePattern() { - return '/^(' . preg_quote(static::NEGATION_CHAR) . '?)(.+?)(\/(\d+))?$/'; + return '/^(' . preg_quote(static::NEGATION_CHAR, '/') . '?)(.+?)(\/(\d+))?$/'; } /** diff --git a/framework/web/MultipartFormDataParser.php b/framework/web/MultipartFormDataParser.php index 7b34f06dd7..6fae328d9b 100644 --- a/framework/web/MultipartFormDataParser.php +++ b/framework/web/MultipartFormDataParser.php @@ -132,7 +132,7 @@ class MultipartFormDataParser extends Object implements RequestParserInterface } $boundary = $matches[1]; - $bodyParts = preg_split('/\\R?-+' . preg_quote($boundary) . '/s', $rawBody); + $bodyParts = preg_split('/\\R?-+' . preg_quote($boundary, '/') . '/s', $rawBody); array_pop($bodyParts); // last block always has no data, contains boundary ending like `--` $bodyParams = []; From 3536c7c4f9a8781f2e131958e05bdc8e07e9a308 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Wed, 22 Feb 2017 23:15:40 +0300 Subject: [PATCH 44/75] Additional fixes for #13087 --- framework/validators/Validator.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/framework/validators/Validator.php b/framework/validators/Validator.php index 4b2f6dc093..99f83a52b3 100644 --- a/framework/validators/Validator.php +++ b/framework/validators/Validator.php @@ -105,7 +105,7 @@ class Validator extends Component * @var array cleaned attribute names. Contains attribute names without `!` character at the beginning * @since 2.0.12 */ - private $attributeNames = []; + private $_attributeNames = []; /** * @var string the user-defined error message. It may contain the following placeholders which * will be replaced accordingly by the validator: @@ -247,7 +247,6 @@ class Validator extends Component * @param array|null $attributes the list of attributes to be validated. * Note that if an attribute is not associated with the validator - it will be * ignored. If this parameter is null, every attribute listed in [[attributes]] will be validated. - * @since 2.0.12 */ public function validateAttributes($model, $attributes = null) { @@ -444,7 +443,7 @@ class Validator extends Component */ public function getAttributeNames() { - return $this->attributeNames; + return $this->_attributeNames; } /** @@ -454,7 +453,7 @@ class Validator extends Component */ private function setAttributeNames($attributeNames) { - $this->attributeNames = array_map(function($attribute) { + $this->_attributeNames = array_map(function($attribute) { return ltrim($attribute, '!'); }, $attributeNames); } From 13de76714bd5482d0c2b9a68b7cb5d4977fc7bd0 Mon Sep 17 00:00:00 2001 From: Alex-Code Date: Tue, 6 Dec 2016 09:51:17 +0000 Subject: [PATCH 45/75] Refactored `yii\db\Query::queryScalar()` method --- framework/CHANGELOG.md | 1 + framework/db/ActiveQuery.php | 8 +++++--- framework/db/Query.php | 28 ++++++++++++++-------------- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 9b9f233f73..08ea384d31 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -28,6 +28,7 @@ Yii Framework 2 Change Log - Enh #13577: Implemented `yii\db\mssql\QueryBuilder::resetSequence()` (boboldehampsink) - Enh #13582: Added tests for all `yii\db\QueryBuilder::resetSequence` implementations, fixed SQLite implementation (boboldehampsink) - Enh #13407: Added URL-safe base64 encode/decode methods to `StringHelper` (andrewnester) +- Enh #13144: Refactored `yii\db\Query::queryScalar()` (Alex-Code) 2.0.11.2 February 08, 2017 diff --git a/framework/db/ActiveQuery.php b/framework/db/ActiveQuery.php index c25ea8d1d4..7ec4900e77 100644 --- a/framework/db/ActiveQuery.php +++ b/framework/db/ActiveQuery.php @@ -330,14 +330,16 @@ class ActiveQuery extends Query implements ActiveQueryInterface */ protected function queryScalar($selectExpression, $db) { - if ($this->sql === null) { - return parent::queryScalar($selectExpression, $db); - } /* @var $modelClass ActiveRecord */ $modelClass = $this->modelClass; if ($db === null) { $db = $modelClass::getDb(); } + + if ($this->sql === null) { + return parent::queryScalar($selectExpression, $db); + } + return (new Query)->select([$selectExpression]) ->from(['c' => "({$this->sql})"]) ->params($this->params) diff --git a/framework/db/Query.php b/framework/db/Query.php index 2b1fa47cc7..b20a8b4cb0 100644 --- a/framework/db/Query.php +++ b/framework/db/Query.php @@ -408,19 +408,6 @@ class Query extends Component implements QueryInterface return null; } - $select = $this->select; - $limit = $this->limit; - $offset = $this->offset; - - $this->select = [$selectExpression]; - $this->limit = null; - $this->offset = null; - $command = $this->createCommand($db); - - $this->select = $select; - $this->limit = $limit; - $this->offset = $offset; - if ( !$this->distinct && empty($this->groupBy) @@ -428,11 +415,24 @@ class Query extends Component implements QueryInterface && empty($this->union) && empty($this->orderBy) ) { + $select = $this->select; + $limit = $this->limit; + $offset = $this->offset; + + $this->select = [$selectExpression]; + $this->limit = null; + $this->offset = null; + $command = $this->createCommand($db); + + $this->select = $select; + $this->limit = $limit; + $this->offset = $offset; + return $command->queryScalar(); } else { return (new Query)->select([$selectExpression]) ->from(['c' => $this]) - ->createCommand($command->db) + ->createCommand($db) ->queryScalar(); } } From 8582dc93f3edc3da5f683fe2bab33ca577b518f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nagy=20Attila=20G=C3=A1bor?= Date: Thu, 23 Feb 2017 21:55:10 +0100 Subject: [PATCH 46/75] Added unit tests for role based access filter (#13597) --- tests/framework/filters/AccessRuleTest.php | 95 ++++++++++++++++++- .../filters/stubs/MockAuthManager.php | 16 ++++ 2 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 tests/framework/filters/stubs/MockAuthManager.php diff --git a/tests/framework/filters/AccessRuleTest.php b/tests/framework/filters/AccessRuleTest.php index 337013b390..3a8394e6a0 100644 --- a/tests/framework/filters/AccessRuleTest.php +++ b/tests/framework/filters/AccessRuleTest.php @@ -9,6 +9,7 @@ use yii\filters\HttpCache; use yii\web\Controller; use yii\web\Request; use yii\web\User; +use yiiunit\framework\filters\stubs\MockAuthManager; use yiiunit\framework\filters\stubs\UserIdentity; /** @@ -39,14 +40,19 @@ class AccessRuleTest extends \yiiunit\TestCase } /** + * @param string optional user id * @return User */ - protected function mockUser() + protected function mockUser($userid = null) { - return new User([ + $user = new User([ 'identityClass' => UserIdentity::className(), 'enableAutoLogin' => false, ]); + if ($userid !== null) { + $user->setIdentity(UserIdentity::findIdentity($userid)); + } + return $user; } /** @@ -58,6 +64,41 @@ class AccessRuleTest extends \yiiunit\TestCase return new Action('test', $controller); } + /** + * @return BaseManager + */ + protected function mockAuthManager() { + $auth = new MockAuthManager(); + // add "createPost" permission + $createPost = $auth->createPermission('createPost'); + $createPost->description = 'Create a post'; + $auth->add($createPost); + + // add "updatePost" permission + $updatePost = $auth->createPermission('updatePost'); + $updatePost->description = 'Update post'; + $auth->add($updatePost); + + // add "author" role and give this role the "createPost" permission + $author = $auth->createRole('author'); + $auth->add($author); + $auth->addChild($author, $createPost); + + // add "admin" role and give this role the "updatePost" permission + // as well as the permissions of the "author" role + $admin = $auth->createRole('admin'); + $auth->add($admin); + $auth->addChild($admin, $updatePost); + $auth->addChild($admin, $author); + + // Assign roles to users. 1 and 2 are IDs returned by IdentityInterface::getId() + // usually implemented in your User model. + $auth->assign($author, 'user2'); + $auth->assign($admin, 'user1'); + + return $auth; + } + public function testMatchAction() { $action = $this->mockAction(); @@ -88,7 +129,55 @@ class AccessRuleTest extends \yiiunit\TestCase // TODO test match controller - // TODO test match roles + /** + * Data provider for testMatchRole + * + * @return array or arrays + * the id of the action + * should the action allow (true) or disallow (false) + * test user id + * expected match result (true, false, null) + */ + public function matchRoleProvider() { + return [ + ['create', true, 'user1', true], + ['create', true, 'user2', true], + ['create', true, 'user3', null], + ['create', true, 'unknown', null], + ['create', false, 'user1', false], + ['create', false, 'user2', false], + ['create', false, 'user3', null], + ['create', false, 'unknown', null], + ]; + } + + /** + * Test that a user matches certain roles + * + * @dataProvider matchRoleProvider + * @param string $actionid the action id + * @param boolean $allow whether the rule should allow access + * @param string $userid the userid to check + * @param boolean $expected the expected result or null + */ + public function testMatchRole($actionid, $allow, $userid, $expected) { + $action = $this->mockAction(); + $auth = $this->mockAuthManager(); + $request = $this->mockRequest(); + + $rule = new AccessRule([ + 'allow' => $allow, + 'roles' => ['createPost'], + 'actions' => ['create'], + ]); + + $action->id = $actionid; + + $user = $this->mockUser($userid); + $user->accessChecker = $auth; + $this->assertEquals($expected, $rule->allows($action, $user, $request)); + } + public function testMatchVerb() { diff --git a/tests/framework/filters/stubs/MockAuthManager.php b/tests/framework/filters/stubs/MockAuthManager.php new file mode 100644 index 0000000000..956787fb8f --- /dev/null +++ b/tests/framework/filters/stubs/MockAuthManager.php @@ -0,0 +1,16 @@ + Date: Fri, 24 Feb 2017 00:28:27 +0300 Subject: [PATCH 47/75] Fixes #13418: Fixed `QueryBuilder::batchInsert()` if $rows is `\Generator` --- framework/CHANGELOG.md | 1 + framework/db/QueryBuilder.php | 3 +++ framework/db/oci/QueryBuilder.php | 3 +++ framework/db/pgsql/QueryBuilder.php | 3 +++ framework/db/sqlite/QueryBuilder.php | 3 +++ tests/framework/db/CommandTest.php | 10 +++++++++- .../framework/db/testBatchInsertWithYield.php | 18 ++++++++++++++++++ 7 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 tests/framework/db/testBatchInsertWithYield.php diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 08ea384d31..93f7bd2ff3 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -11,6 +11,7 @@ Yii Framework 2 Change Log - Bug #13306: Wildcard in `reloadableScripts` in `yii.js` allows 0 characters (arogachev) - Bug #13340: Fixed `yii\db\Connection::useMaster()` - Exception within callback completely disables slaves (Vovan-VE) - Bug #13343: Fixed `yii\i18n\Formatter::asTime()` to process time-only values without time zone conversion (bizley) +- Bug #13418: Fixed `QueryBuilder::batchInsert()` if $rows is `\Generator` (lav45) - Bug #13494: Fixed `yii\console\controllers\MessageConstroller::saveMessagesToDb()` to work on different DBMS correctly (silverfire) - Bug #13513: Fixed RBAC migration to work correctly on Oracle DBMS (silverfire) - Bug #13537: Fixed `yii\web\CacheSession::destroySession()` to work correctly when session is not written yet (silverfire, papalapa) diff --git a/framework/db/QueryBuilder.php b/framework/db/QueryBuilder.php index 959bb7c1f9..636a3c71f4 100644 --- a/framework/db/QueryBuilder.php +++ b/framework/db/QueryBuilder.php @@ -268,6 +268,9 @@ class QueryBuilder extends \yii\base\Object } $values[] = '(' . implode(', ', $vs) . ')'; } + if (empty($values)) { + return ''; + } foreach ($columns as $i => $name) { $columns[$i] = $schema->quoteColumnName($name); diff --git a/framework/db/oci/QueryBuilder.php b/framework/db/oci/QueryBuilder.php index 4bce7b1cac..829f87b4af 100644 --- a/framework/db/oci/QueryBuilder.php +++ b/framework/db/oci/QueryBuilder.php @@ -262,6 +262,9 @@ EOD; } $values[] = '(' . implode(', ', $vs) . ')'; } + if (empty($values)) { + return ''; + } foreach ($columns as $i => $name) { $columns[$i] = $schema->quoteColumnName($name); diff --git a/framework/db/pgsql/QueryBuilder.php b/framework/db/pgsql/QueryBuilder.php index 77b3ae91df..54fcfb7722 100644 --- a/framework/db/pgsql/QueryBuilder.php +++ b/framework/db/pgsql/QueryBuilder.php @@ -316,6 +316,9 @@ class QueryBuilder extends \yii\db\QueryBuilder } $values[] = '(' . implode(', ', $vs) . ')'; } + if (empty($values)) { + return ''; + } foreach ($columns as $i => $name) { $columns[$i] = $schema->quoteColumnName($name); diff --git a/framework/db/sqlite/QueryBuilder.php b/framework/db/sqlite/QueryBuilder.php index 41ec8fb711..4101f1a8f4 100644 --- a/framework/db/sqlite/QueryBuilder.php +++ b/framework/db/sqlite/QueryBuilder.php @@ -106,6 +106,9 @@ class QueryBuilder extends \yii\db\QueryBuilder } $values[] = implode(', ', $vs); } + if (empty($values)) { + return ''; + } foreach ($columns as $i => $name) { $columns[$i] = $schema->quoteColumnName($name); diff --git a/tests/framework/db/CommandTest.php b/tests/framework/db/CommandTest.php index b7ebad2747..5ce2ae246a 100644 --- a/tests/framework/db/CommandTest.php +++ b/tests/framework/db/CommandTest.php @@ -298,6 +298,15 @@ SQL; $this->assertEquals(0, $command->execute()); } + public function testBatchInsertWithYield() + { + if (version_compare(PHP_VERSION, '5.5', '<')) { + $this->markTestSkipped('The yield function is only supported with php 5.5 =< version'); + } else { + include __DIR__ . '/testBatchInsertWithYield.php'; + } + } + public function testInsert() { $db = $this->getConnection(); @@ -559,7 +568,6 @@ SQL; ], $records); } - public function testDropTable() { $db = $this->getConnection(); diff --git a/tests/framework/db/testBatchInsertWithYield.php b/tests/framework/db/testBatchInsertWithYield.php new file mode 100644 index 0000000000..6616d0fb03 --- /dev/null +++ b/tests/framework/db/testBatchInsertWithYield.php @@ -0,0 +1,18 @@ +getConnection()->createCommand(); +$command->batchInsert( + '{{customer}}', + ['email', 'name', 'address'], + $rows +); +$this->assertEquals(0, $command->execute()); From 17d03977a0ede1439cae7a0af0d646da4a69b8e2 Mon Sep 17 00:00:00 2001 From: Hiren Bhut Date: Fri, 24 Feb 2017 03:14:50 +0530 Subject: [PATCH 48/75] Fixed mistake in guide code for list retrieval from the DB (#13634) --- docs/guide/input-forms.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/input-forms.md b/docs/guide/input-forms.md index 2a93340a5e..22bb67333d 100644 --- a/docs/guide/input-forms.md +++ b/docs/guide/input-forms.md @@ -144,7 +144,7 @@ or by retrieval from the DB: ```php $items = Category::find() - ->select(['id', 'label']) + ->select(['label']) ->indexBy('id') ->column(); ``` From 9a915ba10c34171d1b8a51ab97fd2eda4b90ef64 Mon Sep 17 00:00:00 2001 From: Sergey Makinen Date: Fri, 24 Feb 2017 01:24:06 +0300 Subject: [PATCH 49/75] Fixes #8120: Fixes LIKE special characters escaping for Cubrid/MSSQL/Oracle/SQLite in `yii\db\QueryBuilder` --- framework/CHANGELOG.md | 1 + framework/db/QueryBuilder.php | 25 +++++- framework/db/cubrid/QueryBuilder.php | 4 + framework/db/mssql/QueryBuilder.php | 14 ++++ framework/db/oci/QueryBuilder.php | 4 + framework/db/sqlite/QueryBuilder.php | 4 + tests/framework/db/QueryBuilderTest.php | 84 ++++++++++++++----- .../framework/db/cubrid/QueryBuilderTest.php | 2 + tests/framework/db/mssql/QueryBuilderTest.php | 6 ++ tests/framework/db/oci/QueryBuilderTest.php | 2 + .../framework/db/sqlite/QueryBuilderTest.php | 2 + 11 files changed, 123 insertions(+), 25 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index fa2e8806d5..312e91036d 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -20,6 +20,7 @@ Yii Framework 2 Change Log - Bug #13582: PK column in `yii\db\pgsql\QueryBuilder::resetSequence()` was not quoted properly (boboldehampsink) - Bug #13592: Fixes Oracle’s `yii\db\oci\Schema::setTransactionIsolationLevel()` (sergeymakinen) - Bug #13594: Fixes insufficient quoting in `yii\db\QueryBuilder::prepareInsertSelectSubQuery()` (sergeymakinen) +- Bug #8120: Fixes LIKE special characters escaping for Cubrid/MSSQL/Oracle/SQLite in `yii\db\QueryBuilder` (sergeymakinen) - Enh #8641: Enhanced `yii\console\Request::resolve()` to prevent passing parameters, that begin from digits (silverfire) - Enh #13278: `yii\caching\DbQueryDependency` created allowing specification of the cache dependency via `yii\db\QueryInterface` (klimov-paul) - Enh #13467: `yii\data\ActiveDataProvider` no longer queries models if models count is zero (kLkA, Kolyunya) diff --git a/framework/db/QueryBuilder.php b/framework/db/QueryBuilder.php index c913a433ce..579e56881b 100644 --- a/framework/db/QueryBuilder.php +++ b/framework/db/QueryBuilder.php @@ -65,7 +65,22 @@ class QueryBuilder extends \yii\base\Object 'EXISTS' => 'buildExistsCondition', 'NOT EXISTS' => 'buildExistsCondition', ]; - + /** + * @var array map of chars to their replacements in LIKE conditions. + * By default it's configured to escape `%`, `_` and `\` with `\`. + * @since 2.0.12. + */ + protected $likeEscapingReplacements = [ + '%' => '\%', + '_' => '\_', + '\\' => '\\\\', + ]; + /** + * @var string|null character used to escape special characters in LIKE conditions. + * By default it's assumed to be `\`. + * @since 2.0.12 + */ + protected $likeEscapeCharacter; /** * Constructor. @@ -1359,7 +1374,7 @@ class QueryBuilder extends \yii\base\Object throw new InvalidParamException("Operator '$operator' requires two operands."); } - $escape = isset($operands[2]) ? $operands[2] : ['%' => '\%', '_' => '\_', '\\' => '\\\\']; + $escape = isset($operands[2]) ? $operands[2] : $this->likeEscapingReplacements; unset($operands[2]); if (!preg_match('/^(AND |OR |)(((NOT |))I?LIKE)/', $operator, $matches)) { @@ -1394,7 +1409,11 @@ class QueryBuilder extends \yii\base\Object $phName = self::PARAM_PREFIX . count($params); $params[$phName] = empty($escape) ? $value : ('%' . strtr($value, $escape) . '%'); } - $parts[] = "$column $operator $phName"; + $escapeSql = ''; + if ($this->likeEscapeCharacter !== null) { + $escapeSql = " ESCAPE '{$this->likeEscapeCharacter}'"; + } + $parts[] = "{$column} {$operator} {$phName}{$escapeSql}"; } return implode($andor, $parts); diff --git a/framework/db/cubrid/QueryBuilder.php b/framework/db/cubrid/QueryBuilder.php index 3ccee2443a..c83a27917a 100644 --- a/framework/db/cubrid/QueryBuilder.php +++ b/framework/db/cubrid/QueryBuilder.php @@ -44,6 +44,10 @@ class QueryBuilder extends \yii\db\QueryBuilder Schema::TYPE_MONEY => 'decimal(19,4)', ]; + /** + * @inheritdoc + */ + protected $likeEscapeCharacter = '\\'; /** * Creates a SQL statement for resetting the sequence value of a table's primary key. diff --git a/framework/db/mssql/QueryBuilder.php b/framework/db/mssql/QueryBuilder.php index 30dbe64f36..98db75731f 100644 --- a/framework/db/mssql/QueryBuilder.php +++ b/framework/db/mssql/QueryBuilder.php @@ -45,6 +45,20 @@ class QueryBuilder extends \yii\db\QueryBuilder Schema::TYPE_MONEY => 'decimal(19,4)', ]; + /** + * @inheritdoc + */ + protected $likeEscapingReplacements = [ + '%' => '\%', + '_' => '\_', + '[' => '\[', + ']' => '\]', + '\\' => '\\\\', + ]; + /** + * @inheritdoc + */ + protected $likeEscapeCharacter = '\\'; /** * @inheritdoc diff --git a/framework/db/oci/QueryBuilder.php b/framework/db/oci/QueryBuilder.php index 829f87b4af..e6dfafd749 100644 --- a/framework/db/oci/QueryBuilder.php +++ b/framework/db/oci/QueryBuilder.php @@ -46,6 +46,10 @@ class QueryBuilder extends \yii\db\QueryBuilder Schema::TYPE_MONEY => 'NUMBER(19,4)', ]; + /** + * @inheritdoc + */ + protected $likeEscapeCharacter = '\\'; /** * @inheritdoc diff --git a/framework/db/sqlite/QueryBuilder.php b/framework/db/sqlite/QueryBuilder.php index 4101f1a8f4..0be9bc887c 100644 --- a/framework/db/sqlite/QueryBuilder.php +++ b/framework/db/sqlite/QueryBuilder.php @@ -48,6 +48,10 @@ class QueryBuilder extends \yii\db\QueryBuilder Schema::TYPE_MONEY => 'decimal(19,4)', ]; + /** + * @inheritdoc + */ + protected $likeEscapeCharacter = '\\'; /** * Generates a batch INSERT SQL statement. diff --git a/tests/framework/db/QueryBuilderTest.php b/tests/framework/db/QueryBuilderTest.php index 6daf9f504b..1defde61de 100644 --- a/tests/framework/db/QueryBuilderTest.php +++ b/tests/framework/db/QueryBuilderTest.php @@ -19,6 +19,15 @@ abstract class QueryBuilderTest extends DatabaseTestCase { use SchemaBuilderTrait; + /** + * @var string ` ESCAPE 'char'` part of a LIKE condition SQL. + */ + protected $likeEscapeCharSql = ''; + /** + * @var array map of values to their replacements in LIKE query params. + */ + protected $likeParameterReplacements = []; + public function getDb() { return $this->getConnection(false, false); @@ -1038,28 +1047,6 @@ abstract class QueryBuilderTest extends DatabaseTestCase [ ['or like', 'name', []], '0=1', [] ], [ ['or not like', 'name', []], '', [] ], - // simple like - [ ['like', 'name', 'heyho'], '[[name]] LIKE :qp0', [':qp0' => '%heyho%'] ], - [ ['not like', 'name', 'heyho'], '[[name]] NOT LIKE :qp0', [':qp0' => '%heyho%'] ], - [ ['or like', 'name', 'heyho'], '[[name]] LIKE :qp0', [':qp0' => '%heyho%'] ], - [ ['or not like', 'name', 'heyho'], '[[name]] NOT LIKE :qp0', [':qp0' => '%heyho%'] ], - - // like for many values - [ ['like', 'name', ['heyho', 'abc']], '[[name]] LIKE :qp0 AND [[name]] LIKE :qp1', [':qp0' => '%heyho%', ':qp1' => '%abc%'] ], - [ ['not like', 'name', ['heyho', 'abc']], '[[name]] NOT LIKE :qp0 AND [[name]] NOT LIKE :qp1', [':qp0' => '%heyho%', ':qp1' => '%abc%'] ], - [ ['or like', 'name', ['heyho', 'abc']], '[[name]] LIKE :qp0 OR [[name]] LIKE :qp1', [':qp0' => '%heyho%', ':qp1' => '%abc%'] ], - [ ['or not like', 'name', ['heyho', 'abc']], '[[name]] NOT LIKE :qp0 OR [[name]] NOT LIKE :qp1', [':qp0' => '%heyho%', ':qp1' => '%abc%'] ], - - // like with Expression - [ ['like', 'name', new Expression('CONCAT("test", colname, "%")')], '[[name]] LIKE CONCAT("test", colname, "%")', [] ], - [ ['not like', 'name', new Expression('CONCAT("test", colname, "%")')], '[[name]] NOT LIKE CONCAT("test", colname, "%")', [] ], - [ ['or like', 'name', new Expression('CONCAT("test", colname, "%")')], '[[name]] LIKE CONCAT("test", colname, "%")', [] ], - [ ['or not like', 'name', new Expression('CONCAT("test", colname, "%")')], '[[name]] NOT LIKE CONCAT("test", colname, "%")', [] ], - [ ['like', 'name', [new Expression('CONCAT("test", colname, "%")'), 'abc']], '[[name]] LIKE CONCAT("test", colname, "%") AND [[name]] LIKE :qp0', [':qp0' => '%abc%'] ], - [ ['not like', 'name', [new Expression('CONCAT("test", colname, "%")'), 'abc']], '[[name]] NOT LIKE CONCAT("test", colname, "%") AND [[name]] NOT LIKE :qp0', [':qp0' => '%abc%'] ], - [ ['or like', 'name', [new Expression('CONCAT("test", colname, "%")'), 'abc']], '[[name]] LIKE CONCAT("test", colname, "%") OR [[name]] LIKE :qp0', [':qp0' => '%abc%'] ], - [ ['or not like', 'name', [new Expression('CONCAT("test", colname, "%")'), 'abc']], '[[name]] NOT LIKE CONCAT("test", colname, "%") OR [[name]] NOT LIKE :qp0', [':qp0' => '%abc%'] ], - // not [ ['not', 'name'], 'NOT (name)', [] ], @@ -1682,4 +1669,57 @@ abstract class QueryBuilderTest extends DatabaseTestCase $sql = $qb->dropCommentFromTable('comment'); $this->assertEquals($this->replaceQuotes($expected), $sql); } + + public function likeConditionProvider() + { + $conditions = [ + // simple like + [ ['like', 'name', 'foo%'], '[[name]] LIKE :qp0', [':qp0' => '%foo\%%'] ], + [ ['not like', 'name', 'foo%'], '[[name]] NOT LIKE :qp0', [':qp0' => '%foo\%%'] ], + [ ['or like', 'name', 'foo%'], '[[name]] LIKE :qp0', [':qp0' => '%foo\%%'] ], + [ ['or not like', 'name', 'foo%'], '[[name]] NOT LIKE :qp0', [':qp0' => '%foo\%%'] ], + + // like for many values + [ ['like', 'name', ['foo%', '[abc]']], '[[name]] LIKE :qp0 AND [[name]] LIKE :qp1', [':qp0' => '%foo\%%', ':qp1' => '%[abc]%'] ], + [ ['not like', 'name', ['foo%', '[abc]']], '[[name]] NOT LIKE :qp0 AND [[name]] NOT LIKE :qp1', [':qp0' => '%foo\%%', ':qp1' => '%[abc]%'] ], + [ ['or like', 'name', ['foo%', '[abc]']], '[[name]] LIKE :qp0 OR [[name]] LIKE :qp1', [':qp0' => '%foo\%%', ':qp1' => '%[abc]%'] ], + [ ['or not like', 'name', ['foo%', '[abc]']], '[[name]] NOT LIKE :qp0 OR [[name]] NOT LIKE :qp1', [':qp0' => '%foo\%%', ':qp1' => '%[abc]%'] ], + + // like with Expression + [ ['like', 'name', new Expression('CONCAT("test", name, "%")')], '[[name]] LIKE CONCAT("test", name, "%")', [] ], + [ ['not like', 'name', new Expression('CONCAT("test", name, "%")')], '[[name]] NOT LIKE CONCAT("test", name, "%")', [] ], + [ ['or like', 'name', new Expression('CONCAT("test", name, "%")')], '[[name]] LIKE CONCAT("test", name, "%")', [] ], + [ ['or not like', 'name', new Expression('CONCAT("test", name, "%")')], '[[name]] NOT LIKE CONCAT("test", name, "%")', [] ], + [ ['like', 'name', [new Expression('CONCAT("test", name, "%")'), '\ab_c']], '[[name]] LIKE CONCAT("test", name, "%") AND [[name]] LIKE :qp0', [':qp0' => '%\\\ab\_c%'] ], + [ ['not like', 'name', [new Expression('CONCAT("test", name, "%")'), '\ab_c']], '[[name]] NOT LIKE CONCAT("test", name, "%") AND [[name]] NOT LIKE :qp0', [':qp0' => '%\\\ab\_c%'] ], + [ ['or like', 'name', [new Expression('CONCAT("test", name, "%")'), '\ab_c']], '[[name]] LIKE CONCAT("test", name, "%") OR [[name]] LIKE :qp0', [':qp0' => '%\\\ab\_c%'] ], + [ ['or not like', 'name', [new Expression('CONCAT("test", name, "%")'), '\ab_c']], '[[name]] NOT LIKE CONCAT("test", name, "%") OR [[name]] NOT LIKE :qp0', [':qp0' => '%\\\ab\_c%'] ], + ]; + + // adjust dbms specific escaping + foreach($conditions as $i => $condition) { + $conditions[$i][1] = $this->replaceQuotes($condition[1]); + if (!empty($this->likeEscapeCharSql)) { + preg_match_all('/(?PLIKE.+?)( AND| OR|$)/', $conditions[$i][1], $matches, PREG_SET_ORDER); + foreach ($matches as $match) { + $conditions[$i][1] = str_replace($match['condition'], $match['condition'] . $this->likeEscapeCharSql, $conditions[$i][1]); + } + } + foreach ($conditions[$i][2] as $name => $value) { + $conditions[$i][2][$name] = strtr($conditions[$i][2][$name], $this->likeParameterReplacements); + } + } + return $conditions; + } + + /** + * @dataProvider likeConditionProvider + */ + public function testBuildLikeCondition($condition, $expected, $expectedParams) + { + $query = (new Query())->where($condition); + list($sql, $params) = $this->getQueryBuilder()->build($query); + $this->assertEquals('SELECT *' . (empty($expected) ? '' : ' WHERE ' . $this->replaceQuotes($expected)), $sql); + $this->assertEquals($expectedParams, $params); + } } diff --git a/tests/framework/db/cubrid/QueryBuilderTest.php b/tests/framework/db/cubrid/QueryBuilderTest.php index 590a1c5ffb..d42025fbfe 100644 --- a/tests/framework/db/cubrid/QueryBuilderTest.php +++ b/tests/framework/db/cubrid/QueryBuilderTest.php @@ -12,6 +12,8 @@ class QueryBuilderTest extends \yiiunit\framework\db\QueryBuilderTest { public $driverName = 'cubrid'; + protected $likeEscapeCharSql = " ESCAPE '\\'"; + /** * this is not used as a dataprovider for testGetColumnType to speed up the test * when used as dataprovider every single line will cause a reconnect with the database which is not needed here diff --git a/tests/framework/db/mssql/QueryBuilderTest.php b/tests/framework/db/mssql/QueryBuilderTest.php index 1329ecc1d0..32ac85d6f1 100644 --- a/tests/framework/db/mssql/QueryBuilderTest.php +++ b/tests/framework/db/mssql/QueryBuilderTest.php @@ -2,6 +2,7 @@ namespace yiiunit\framework\db\mssql; +use yii\db\Expression; use yii\db\mssql\Schema; use yii\db\Query; @@ -13,6 +14,11 @@ class QueryBuilderTest extends \yiiunit\framework\db\QueryBuilderTest { public $driverName = 'sqlsrv'; + protected $likeEscapeCharSql = " ESCAPE '\\'"; + protected $likeParameterReplacements = [ + '[abc]' => '\[abc\]', + ]; + public function testOffsetLimit() { $expectedQuerySql = 'SELECT [id] FROM [example] ORDER BY (SELECT NULL) OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY'; diff --git a/tests/framework/db/oci/QueryBuilderTest.php b/tests/framework/db/oci/QueryBuilderTest.php index c95cf5ae2d..ab9f220766 100644 --- a/tests/framework/db/oci/QueryBuilderTest.php +++ b/tests/framework/db/oci/QueryBuilderTest.php @@ -12,6 +12,8 @@ class QueryBuilderTest extends \yiiunit\framework\db\QueryBuilderTest { public $driverName = 'oci'; + protected $likeEscapeCharSql = " ESCAPE '\\'"; + /** * this is not used as a dataprovider for testGetColumnType to speed up the test * when used as dataprovider every single line will cause a reconnect with the database which is not needed here diff --git a/tests/framework/db/sqlite/QueryBuilderTest.php b/tests/framework/db/sqlite/QueryBuilderTest.php index f6f6db8ede..28446b8924 100644 --- a/tests/framework/db/sqlite/QueryBuilderTest.php +++ b/tests/framework/db/sqlite/QueryBuilderTest.php @@ -14,6 +14,8 @@ class QueryBuilderTest extends \yiiunit\framework\db\QueryBuilderTest { protected $driverName = 'sqlite'; + protected $likeEscapeCharSql = " ESCAPE '\\'"; + public function columnTypes() { return array_merge(parent::columnTypes(), [ From 9aacd89b77c52a28feba00f9466a0e2ac58119a0 Mon Sep 17 00:00:00 2001 From: jaaf Date: Fri, 24 Feb 2017 16:15:06 +0100 Subject: [PATCH 50/75] compliance with style-guide, adding blocktypes.json and removing unnecessary translations [skip ci] (#13653) --- docs/guide-fr/blocktypes.json | 6 ++++++ docs/guide-fr/caching-data.md | 4 ++-- docs/guide-fr/start-forms.md | 2 +- docs/guide-fr/start-installation.md | 10 +++++----- 4 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 docs/guide-fr/blocktypes.json diff --git a/docs/guide-fr/blocktypes.json b/docs/guide-fr/blocktypes.json new file mode 100644 index 0000000000..593763558a --- /dev/null +++ b/docs/guide-fr/blocktypes.json @@ -0,0 +1,6 @@ +{ + "Warning:": "Attention :", + "Note:": "Note :", + "Info:": "Info :", + "Tip:": "Conseil :" +} diff --git a/docs/guide-fr/caching-data.md b/docs/guide-fr/caching-data.md index f1071ba533..6302394d04 100644 --- a/docs/guide-fr/caching-data.md +++ b/docs/guide-fr/caching-data.md @@ -84,7 +84,7 @@ Comme tous les composants de mise en cache prennent en charge le même jeux d'AP ], ``` -> Conseil : vous pouvez enregistrer de multiples composants d'application de mise en cache. Le composant nommé `cache` est utilisé par défaut par de nombreuses classes dépendantes d'un cache (p. ex.[[yii\web\UrlManager]]). +> Tip: vous pouvez enregistrer de multiples composants d'application de mise en cache. Le composant nommé `cache` est utilisé par défaut par de nombreuses classes dépendantes d'un cache (p. ex.[[yii\web\UrlManager]]). ### Supports de stockage pour cache pris en charge @@ -106,7 +106,7 @@ Yii prend en charge un large panel de supports de stockage pour cache. Ce qui su en tant que médium de cache sous-jacent. -> Conseil : vous pouvez utiliser différents supports de stockage pour cache dans la même application. Une stratégie courante est d'utiliser un support de stockage pour cache basé sur la mémoire pour stocker des données de petite taille mais d'usage constant (p. ex. des données statistiques), et d'utiliser des supports de stockage pour cache basés sur des fichiers ou des bases de données pour stocker des données volumineuses et utilisées moins souvent (p. ex. des contenus de pages). +> Tip: vous pouvez utiliser différents supports de stockage pour cache dans la même application. Une stratégie courante est d'utiliser un support de stockage pour cache basé sur la mémoire pour stocker des données de petite taille mais d'usage constant (p. ex. des données statistiques), et d'utiliser des supports de stockage pour cache basés sur des fichiers ou des bases de données pour stocker des données volumineuses et utilisées moins souvent (p. ex. des contenus de pages). ## Les API Cache diff --git a/docs/guide-fr/start-forms.md b/docs/guide-fr/start-forms.md index c9dcadc88f..63482d17b8 100644 --- a/docs/guide-fr/start-forms.md +++ b/docs/guide-fr/start-forms.md @@ -224,7 +224,7 @@ des données. Dans le cas où vous auriez désactivé le Javascript sur votre na effectuée coté serveur, comme montré dans la méthode `actionEntry()`. Cela garantit la validité des données en toutes circonstances. -> Attention : La validation coté client est un confort qui permet une meilleure expérience utilisateur. La validation coté serveur est toujours nécessaire, que la validation coté client soit ou non en place. +> Warning: La validation coté client est un confort qui permet une meilleure expérience utilisateur. La validation coté serveur est toujours nécessaire, que la validation coté client soit ou non en place. Les étiquettes des champs de saisie sont générés par la méthode `field()`, en utilisant les noms des propriété du modèle. diff --git a/docs/guide-fr/start-installation.md b/docs/guide-fr/start-installation.md index 72d34a057d..24a1c496b1 100644 --- a/docs/guide-fr/start-installation.md +++ b/docs/guide-fr/start-installation.md @@ -4,7 +4,7 @@ Installer Yii Vous pouvez installer Yii de deux façons, en utilisant [Composer](https://getcomposer.org/) ou en téléchargeant une archive. La première méthode est conseillée, étant donné qu'elle permet d'installer de nouvelles [extensions](extend-creating-extensions.md) ou de mettre à jour Yii en éxécutant simplement une commande. -> Remarque : Contrairement à Yii 1, les installations standards de Yii 2 auront pour résultat le téléchargement et l'installation du framework, ainsi que d'un squelette d'application. +> Note: contrairement à Yii 1, les installations standards de Yii 2 auront pour résultat le téléchargement et l'installation du framework, ainsi que d'un squelette d'application. Installer via Composer @@ -26,7 +26,7 @@ Avec Composer installé, vous pouvez installer Yii en éxécutant la commande su Cette commande installera Yii dans le dossier `basic`. -> Astuce : Si vous souhaitez installer la dernière version de développement de Yii, vous pouvez utiliser la commande suivante qui ajoutera l'[option stability](https://getcomposer.org/doc/04-schema.md#minimum-stability) : +> Tip: si vous souhaitez installer la dernière version de développement de Yii, vous pouvez utiliser la commande suivante qui ajoutera l'[option stability](https://getcomposer.org/doc/04-schema.md#minimum-stability) : > > composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic > @@ -83,16 +83,16 @@ Vous devez configurer votre installation de PHP afin qu'elle réponde aux exigen Configuration du serveur Web ---------------------------- -> Remarque : Si vous voulez juste tester Yii sans intention de l'utiliser sur un serveur de production, vous pouvez ignorer ce paragraphe. +> Note: si vous voulez juste tester Yii sans intention de l'utiliser sur un serveur de production, vous pouvez ignorer ce paragraphe. L'application installée selon les instructions ci-dessus devrait fonctionner *out of the box* avec le [serveur HTTP Apache](http://httpd.apache.org/) ou le [serveur HTTP Nginx](http://nginx.org/), sous Windows, Mac OX X, ou linux. Sur un serveur de production, vous pouvez configurer votre serveur Web afin que l'application soit accessible via l'URL `http://www.example.com/index.php` au lieu de `http://www.example.com/basic/web/index.php`. Cela implique que le dossier racine de votre serveur Web pointe vers le dossier `basic/web`. Vous pouvez également cacher `index.php` dans l'URL, comme décrit dans la partie [Génération et traitement des URL](runtime-url-handling.md), vous y apprendrez comment configurer votre serveur Apache ou Nginx pour atteindre ces objectifs. -> Remarque : En utilisant `basic/web` comme dossier racine, vous empêchez également aux utilisateurs finaux d'accéder à votre code d'application privé et fichiers de données sensibles qui sont stockés dans le dossier `basic`. Refuser l'accès à ces ressources est une amélioration de la sécurité. +> Note: en utilisant `basic/web` comme dossier racine, vous empêchez également aux utilisateurs finaux d'accéder à votre code d'application privé et fichiers de données sensibles qui sont stockés dans le dossier `basic`. Refuser l'accès à ces ressources est une amélioration de la sécurité. -> Remarque: Si votre application s'exécute dans un environnement d'hébergement mutualisé où vous n'avez pas la permission de modifier la configuration du serveur Web, vous pouvez ajuster la structure de votre application pour une meilleure sécurité. Merci de lire la partie [Environnement d'hébergement mutualisé](tutorial-shared-hosting.md) pour en savoir plus. +> Note: si votre application s'exécute dans un environnement d'hébergement mutualisé où vous n'avez pas la permission de modifier la configuration du serveur Web, vous pouvez ajuster la structure de votre application pour une meilleure sécurité. Merci de lire la partie [Environnement d'hébergement mutualisé](tutorial-shared-hosting.md) pour en savoir plus. ### Configuration Apache recommandée From 186420da40f234b1d1acbe5052638f1c879d0d73 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Sat, 25 Feb 2017 03:19:29 +0600 Subject: [PATCH 51/75] Fixes #13243: Added support for unicode attribute names in `yii\widgets\DetailView` --- framework/CHANGELOG.md | 1 + framework/widgets/DetailView.php | 2 +- tests/framework/widgets/DetailViewTest.php | 59 +++++++++++++++++++--- 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 312e91036d..a574da420e 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -7,6 +7,7 @@ Yii Framework 2 Change Log - Bug #13087: Fixed getting active validators for safe attribute (developeruz) - Bug #13571: Fix `yii\db\mssql\QueryBuilder::checkIntegrity` for all tables (boboldehampsink) - Bug #11230: Include `defaultRoles` in `yii\rbac\DbManager->getRolesByUser()` results (developeruz) +- Enh #13243: Added support for unicode attribute names in `yii\widgets\DetailView` (arogachev) - Bug #11404: `yii\base\Model::loadMultiple()` returns true even if `yii\base\Model::load()` returns false (zvook) - Bug #13306: Wildcard in `reloadableScripts` in `yii.js` allows 0 characters (arogachev) - Bug #13340: Fixed `yii\db\Connection::useMaster()` - Exception within callback completely disables slaves (Vovan-VE) diff --git a/framework/widgets/DetailView.php b/framework/widgets/DetailView.php index e2a993681b..4cf8ad1180 100644 --- a/framework/widgets/DetailView.php +++ b/framework/widgets/DetailView.php @@ -205,7 +205,7 @@ class DetailView extends Widget foreach ($this->attributes as $i => $attribute) { if (is_string($attribute)) { - if (!preg_match('/^([\w\.]+)(:(\w*))?(:(.*))?$/', $attribute, $matches)) { + if (!preg_match('/^([^:]+)(:(\w*))?(:(.*))?$/', $attribute, $matches)) { throw new InvalidConfigException('The attribute must be specified in the format of "attribute", "attribute:format" or "attribute:format:label"'); } $attribute = [ diff --git a/tests/framework/widgets/DetailViewTest.php b/tests/framework/widgets/DetailViewTest.php index bbaaee3d6f..0fb2949377 100644 --- a/tests/framework/widgets/DetailViewTest.php +++ b/tests/framework/widgets/DetailViewTest.php @@ -7,7 +7,7 @@ namespace yiiunit\framework\widgets; use yii\base\Arrayable; use yii\base\ArrayableTrait; -use yii\base\Object; +use yii\base\Model; use yii\widgets\DetailView; /** @@ -27,7 +27,7 @@ class DetailViewTest extends \yiiunit\TestCase public function testAttributeValue() { - $model = new ObjectMock(); + $model = new ModelMock(); $model->id = 'id'; $this->detailView = new PublicDetailView([ @@ -64,9 +64,37 @@ class DetailViewTest extends \yiiunit\TestCase $this->assertEquals(2, $model->getDisplayedIdCallCount()); } + /** + * @see https://github.com/yiisoft/yii2/issues/13243 + */ + public function testUnicodeAttributeNames() + { + $model = new UnicodeAttributesModelMock(); + $model->ИдентификаторТовара = 'A00001'; + $model->το_αναγνωριστικό_του = 'A00002'; + + $this->detailView = new PublicDetailView([ + 'model' => $model, + 'template' => '{label}:{value}', + 'attributes' => [ + 'ИдентификаторТовара', + 'το_αναγνωριστικό_του', + ], + ]); + + $this->assertEquals( + 'ИдентификаторТовара:A00001', + $this->detailView->renderAttribute($this->detailView->attributes[0], 0) + ); + $this->assertEquals( + 'το αναγνωριστικό του:A00002', + $this->detailView->renderAttribute($this->detailView->attributes[1], 1) + ); + } + public function testAttributeVisible() { - $model = new ObjectMock(); + $model = new ModelMock(); $model->id = 'id'; $this->detailView = new PublicDetailView([ @@ -143,9 +171,9 @@ class DetailViewTest extends \yiiunit\TestCase public function testRelationAttribute() { - $model = new ObjectMock(); + $model = new ModelMock(); $model->id = 'model'; - $model->related = new ObjectMock(); + $model->related = new ModelMock(); $model->related->id = 'related'; $this->detailView = new PublicDetailView([ @@ -221,7 +249,7 @@ class DetailViewTest extends \yiiunit\TestCase ], ]; - $model = new ObjectMock(); + $model = new ModelMock(); $model->id = 1; $model->text = 'I`m an object'; @@ -301,7 +329,7 @@ class ArrayableMock implements Arrayable /** * Helper Class */ -class ObjectMock extends Object +class ModelMock extends Model { public $id; public $text; @@ -332,6 +360,23 @@ class ObjectMock extends Object } } +/** + * Used for testing attributes containing non-English characters + */ +class UnicodeAttributesModelMock extends Model +{ + /** + * Product's ID (Russian) + * @var mixed + */ + public $ИдентификаторТовара; + /** + * ID (Greek) + * @var mixed + */ + public $το_αναγνωριστικό_του; +} + class PublicDetailView extends DetailView { public function renderAttribute($attribute, $index) From b4dc7a4f5108cf76f46f5031da0fd5983d4dca8c Mon Sep 17 00:00:00 2001 From: "Anderson Scouto da Silva (Dan)" Date: Sat, 25 Feb 2017 04:25:28 -0300 Subject: [PATCH 52/75] Update yii.php (#13658) [skip ci] --- framework/messages/pt-BR/yii.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/messages/pt-BR/yii.php b/framework/messages/pt-BR/yii.php index 91419242ef..5b0cd0f2e2 100644 --- a/framework/messages/pt-BR/yii.php +++ b/framework/messages/pt-BR/yii.php @@ -91,6 +91,7 @@ return [ 'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Você pode fazer o upload de, no máximo, {limit, number} {limit, plural, one{arquivo} other{arquivos}}.', 'the input value' => 'o valor de entrada', '{attribute} "{value}" has already been taken.' => '{attribute} "{value}" já foi utilizado.', + 'The combination {values} of {attributes} has already been taken.' => 'A combinação {values} de {attributes} já foi utilizado.', '{attribute} cannot be blank.' => '"{attribute}" não pode ficar em branco.', '{attribute} contains wrong subnet mask.' => '{attribute} contém a máscara de sub-rede errado.', '{attribute} is invalid.' => '"{attribute}" é inválido.', @@ -118,4 +119,5 @@ return [ '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '"{attribute}" deve conter no máximo {max, number} {max, plural, one{caractere} other{caracteres}}.', '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '"{attribute}" deve conter {length, number} {length, plural, one{caractere} other{caracteres}}.', '{attribute} must be equal to "{compareValueOrAttribute}".' => '{attribute} deve ser igual a "{compareValueOrAttribute}".', + ' and ' => ' e ', ]; From a719293ab01885ca5b31b0e24a47c9c73961de5a Mon Sep 17 00:00:00 2001 From: Alex-Code Date: Sat, 25 Feb 2017 11:41:02 +0000 Subject: [PATCH 53/75] Fixes #13649: Fixes issue where `['uncheck' => false]` and `['label' => false]` options for `ActiveRadio` and `ActiveCheckbox` were ignored --- framework/CHANGELOG.md | 1 + framework/helpers/BaseHtml.php | 4 ++ tests/framework/helpers/HtmlTest.php | 91 +++++++++++++++++++++++++++- 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index a574da420e..b07064d934 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -31,6 +31,7 @@ Yii Framework 2 Change Log - Enh #13577: Implemented `yii\db\mssql\QueryBuilder::resetSequence()` (boboldehampsink) - Enh #13582: Added tests for all `yii\db\QueryBuilder::resetSequence` implementations, fixed SQLite implementation (boboldehampsink) - Enh #13407: Added URL-safe base64 encode/decode methods to `StringHelper` (andrewnester) +- Bug #13649: Fixes issue where `['uncheck' => false]` and `['label' => false]` options for `ActiveRadio` and `ActiveCheckbox` were ignored (Alex-Code) - Enh #13221: Make `\yii\db\QueryTrait::limit()` and `\yii\db\QueryTrait::offset()` methods work with `\yii\db\Expression` (Ni-san) - Enh #13144: Refactored `yii\db\Query::queryScalar()` (Alex-Code) diff --git a/framework/helpers/BaseHtml.php b/framework/helpers/BaseHtml.php index eeabe3d26b..f8f36d8974 100644 --- a/framework/helpers/BaseHtml.php +++ b/framework/helpers/BaseHtml.php @@ -1476,9 +1476,13 @@ class BaseHtml } if (!array_key_exists('uncheck', $options)) { $options['uncheck'] = '0'; + } elseif ($options['uncheck'] === false) { + unset($options['uncheck']); } if (!array_key_exists('label', $options)) { $options['label'] = static::encode($model->getAttributeLabel(static::getAttributeName($attribute))); + } elseif ($options['label'] === false) { + unset($options['label']); } $checked = "$value" === "{$options['value']}"; diff --git a/tests/framework/helpers/HtmlTest.php b/tests/framework/helpers/HtmlTest.php index 6da39da0e7..d48c99f4b3 100644 --- a/tests/framework/helpers/HtmlTest.php +++ b/tests/framework/helpers/HtmlTest.php @@ -1165,6 +1165,94 @@ EOD; $noCsrfForm = Html::beginForm('/index.php', 'post', ['csrf' => false, 'id' => 'myform']); $this->assertEquals('
    ', $noCsrfForm); } + + /** + * Data provider for [[testActiveRadio()]] + * @return array test data + */ + public function dataProviderActiveRadio() + { + return [ + [ + true, + [], + '' + ], + [ + true, + ['uncheck' => false], + '' + ], + [ + true, + ['label' => false], + '' + ], + [ + true, + ['uncheck' => false, 'label' => false], + '' + ], + ]; + } + + /** + * @dataProvider dataProviderActiveRadio + * + * @param string $value + * @param array $options + * @param string $expectedHtml + */ + public function testActiveRadio($value, array $options, $expectedHtml) + { + $model = new HtmlTestModel(); + $model->radio = $value; + $this->assertEquals($expectedHtml, Html::activeRadio($model, 'radio', $options)); + } + + /** + * Data provider for [[testActiveCheckbox()]] + * @return array test data + */ + public function dataProviderActiveCheckbox() + { + return [ + [ + true, + [], + '' + ], + [ + true, + ['uncheck' => false], + '' + ], + [ + true, + ['label' => false], + '' + ], + [ + true, + ['uncheck' => false, 'label' => false], + '' + ], + ]; + } + + /** + * @dataProvider dataProviderActiveCheckbox + * + * @param string $value + * @param array $options + * @param string $expectedHtml + */ + public function testActiveCheckbox($value, array $options, $expectedHtml) + { + $model = new HtmlTestModel(); + $model->checkbox = $value; + $this->assertEquals($expectedHtml, Html::activeCheckbox($model, 'checkbox', $options)); + } } /** @@ -1176,7 +1264,7 @@ class HtmlTestModel extends DynamicModel { public function init() { - foreach (['name', 'types', 'description'] as $attribute) { + foreach (['name', 'types', 'description', 'radio', 'checkbox'] as $attribute) { $this->defineAttribute($attribute); } } @@ -1187,6 +1275,7 @@ class HtmlTestModel extends DynamicModel ['name', 'required'], ['name', 'string', 'max' => 100], ['description', 'string', 'max' => 500], + [['radio', 'checkbox'], 'boolean'], ]; } } From 36960da10150e71ce19c53074e7ccc1debbf1b75 Mon Sep 17 00:00:00 2001 From: SilverFire - Dmitry Naumenko Date: Sun, 26 Feb 2017 19:29:56 +0200 Subject: [PATCH 54/75] Fixed `applyFilter` function in `yii.gridView.js` Closes bug #13379 `applyFilter` function in `yii.gridView.js` fixed to work correctly when params in `filterUrl` are indexed --- framework/CHANGELOG.md | 1 + framework/assets/yii.gridView.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index b07064d934..caf45544e3 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -34,6 +34,7 @@ Yii Framework 2 Change Log - Bug #13649: Fixes issue where `['uncheck' => false]` and `['label' => false]` options for `ActiveRadio` and `ActiveCheckbox` were ignored (Alex-Code) - Enh #13221: Make `\yii\db\QueryTrait::limit()` and `\yii\db\QueryTrait::offset()` methods work with `\yii\db\Expression` (Ni-san) - Enh #13144: Refactored `yii\db\Query::queryScalar()` (Alex-Code) +- Bug #13379: Fixed `applyFilter` function in `yii.gridView.js` to work correctly when params in `filterUrl` are indexed (SilverFire) 2.0.11.2 February 08, 2017 diff --git a/framework/assets/yii.gridView.js b/framework/assets/yii.gridView.js index a08c45f784..2601414401 100644 --- a/framework/assets/yii.gridView.js +++ b/framework/assets/yii.gridView.js @@ -126,7 +126,7 @@ var namesInFilter = Object.keys(data); $.each(yii.getQueryParams(settings.filterUrl), function (name, value) { - if (namesInFilter.indexOf(name) === -1 && namesInFilter.indexOf(name.replace(/\[\]$/, '')) === -1) { + if (namesInFilter.indexOf(name) === -1 && namesInFilter.indexOf(name.replace(/\[\d*\]$/, '')) === -1) { if (!$.isArray(value)) { value = [value]; } From 148007ffd66ef7c3fc8bf2c978daa2da07fa67d1 Mon Sep 17 00:00:00 2001 From: Nikolay Oleynikov Date: Sun, 26 Feb 2017 20:54:44 +0300 Subject: [PATCH 55/75] Refactor HTML helper attribute order (#13665) --- framework/helpers/BaseHtml.php | 1 + tests/framework/helpers/HtmlTest.php | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/framework/helpers/BaseHtml.php b/framework/helpers/BaseHtml.php index f8f36d8974..6e6a9c27df 100644 --- a/framework/helpers/BaseHtml.php +++ b/framework/helpers/BaseHtml.php @@ -59,6 +59,7 @@ class BaseHtml 'href', 'src', + 'srcset', 'action', 'method', diff --git a/tests/framework/helpers/HtmlTest.php b/tests/framework/helpers/HtmlTest.php index d48c99f4b3..be457c6835 100644 --- a/tests/framework/helpers/HtmlTest.php +++ b/tests/framework/helpers/HtmlTest.php @@ -156,7 +156,7 @@ class HtmlTest extends TestCase ], ], [ - '', + '', '/base-url', [ 'srcset' => [ @@ -164,7 +164,7 @@ class HtmlTest extends TestCase ], ], [ - '', + '', '/base-url', [ 'srcset' => [ @@ -173,7 +173,7 @@ class HtmlTest extends TestCase ], ], [ - '', + '', '/base-url', [ 'srcset' => [ @@ -184,7 +184,7 @@ class HtmlTest extends TestCase ], ], [ - '', + '', '/base-url', [ 'srcset' => [ @@ -197,7 +197,7 @@ class HtmlTest extends TestCase ], ], [ - '', + '', '/base-url', [ 'srcset' => [ @@ -208,7 +208,7 @@ class HtmlTest extends TestCase ], ], [ - '', + '', '/base-url', [ 'srcset' => '/example-1x 1x,/example-2x 2x,/example-3x 3x', From 572769944511a4ee404504c78b9b9a9de5ce5e76 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Mon, 27 Feb 2017 09:31:04 +0100 Subject: [PATCH 56/75] Aadded OWASP references to security guide (#13667) [skip ci] Currently security guide is very thrifty with information on what a topic is about. So for beginners it is not clear why something like CSRF or XSS protection is even needed. I added a few reference links to allow reading about more background behind the topics. --- docs/guide/security-best-practices.md | 44 ++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/docs/guide/security-best-practices.md b/docs/guide/security-best-practices.md index f6d3786748..baeba0728c 100644 --- a/docs/guide/security-best-practices.md +++ b/docs/guide/security-best-practices.md @@ -2,6 +2,9 @@ Security best practices ======================= Below we'll review common security principles and describe how to avoid threats when developing applications using Yii. +Most of these priciples are not unique to Yii alone but apply to website or software development in general, +so we you will also find links for further reading on the general ideas behind these. + Basic principles ---------------- @@ -28,6 +31,11 @@ if (!in_array($sortBy, ['title', 'created_at', 'status'])) { In Yii, most probably you'll use [form validation](input-validation.md) to do alike checks. +Further reading on the topic: + +- +- + ### Escape output @@ -36,6 +44,13 @@ should escape `<`, `>` and alike special characters. In context of JavaScript or Since it's error-prone to escape everything manually Yii provides various tools to perform escaping for different contexts. +Further reading on the topic: + +- +- +- + + Avoiding SQL injections ----------------------- @@ -100,6 +115,10 @@ $rowCount = $connection->createCommand($sql)->queryScalar(); You can get details about the syntax in [Quoting Table and Column Names](db-dao.md#quoting-table-and-column-names). +Further reading on the topic: + +- + Avoiding XSS ------------ @@ -130,6 +149,11 @@ If it should be HTML we could get some help from HtmlPurifier: Note that HtmlPurifier processing is quite heavy so consider adding caching. +Further reading on the topic: + +- + + Avoiding CSRF ------------- @@ -185,6 +209,10 @@ class SiteController extends Controller } ``` +Further reading on the topic: + +- + Avoiding file exposure ---------------------- @@ -195,33 +223,41 @@ environments it could be impossible to achieve so we'll end up with all the code If it's the case don't forget to deny access to everything except `web`. If it can't be done consider hosting your application elsewhere. -Avoiding debug info and tools at production + +Avoiding debug info and tools in production ------------------------------------------- In debug mode Yii shows quite verbose errors which are certainly helpful for development. The thing is that these verbose errors are handy for attacker as well since these could reveal database structure, configuration values and parts of your code. Never run production applications with `YII_DEBUG` set to `true` in your `index.php`. -You should never enable Gii at production. It could be used to get information about database structure, code and to +You should never enable Gii or the Debug toolbar in production. It could be used to get information about database structure, code and to simply rewrite code with what's generated by Gii. Debug toolbar should be avoided at production unless really necessary. It exposes all the application and config details possible. If you absolutely need it check twice that access is properly restricted to your IP only. +Further reading on the topic: + +- +- + + Using secure connection over TLS -------------------------------- Yii provides features that rely on cookies and/or PHP sessions. These can be vulnerable in case your connection is -compromised. The risk is reduced if the app uses secure connection via TLS. +compromised. The risk is reduced if the app uses secure connection via TLS (often referred to as [SSL](https://en.wikipedia.org/wiki/Transport_Layer_Security)). Please refer to your webserver documentation for instructions on how to configure it. You may also check example configs -provided by H5BP project: +provided by the H5BP project: - [Nginx](https://github.com/h5bp/server-configs-nginx) - [Apache](https://github.com/h5bp/server-configs-apache). - [IIS](https://github.com/h5bp/server-configs-iis). - [Lighttpd](https://github.com/h5bp/server-configs-lighttpd). + Secure Server configuration --------------------------- From 953a0bba2b0115cb39b5c67b6095b378c7f683d2 Mon Sep 17 00:00:00 2001 From: "Charles R. Portwood II" Date: Mon, 27 Feb 2017 04:32:48 -0600 Subject: [PATCH 57/75] Fixes #13650: Improved `yii\base\Security::hkdf()` to take advantage of native `hash_hkdf()` implementation in PHP >= 7.1.2 --- framework/CHANGELOG.md | 1 + framework/base/Security.php | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index caf45544e3..62fa0bb8e0 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -34,6 +34,7 @@ Yii Framework 2 Change Log - Bug #13649: Fixes issue where `['uncheck' => false]` and `['label' => false]` options for `ActiveRadio` and `ActiveCheckbox` were ignored (Alex-Code) - Enh #13221: Make `\yii\db\QueryTrait::limit()` and `\yii\db\QueryTrait::offset()` methods work with `\yii\db\Expression` (Ni-san) - Enh #13144: Refactored `yii\db\Query::queryScalar()` (Alex-Code) +- Enh #13650: Improved `yii\base\Security::hkdf()` to take advantage of native `hash_hkdf()` implementation in PHP >= 7.1.2 (charlesportwoodii) - Bug #13379: Fixed `applyFilter` function in `yii.gridView.js` to work correctly when params in `filterUrl` are indexed (SilverFire) diff --git a/framework/base/Security.php b/framework/base/Security.php index 2153cf8e73..b946de7a65 100644 --- a/framework/base/Security.php +++ b/framework/base/Security.php @@ -273,6 +273,14 @@ class Security extends Component */ public function hkdf($algo, $inputKey, $salt = null, $info = null, $length = 0) { + if (function_exists('hash_hkdf')) { + $outputKey = hash_hkdf($algo, $inputKey, $length, $info, $salt); + if ($outputKey === false) { + throw new InvalidParamException('Invalid parameters to hash_hkdf()'); + } + return $outputKey; + } + $test = @hash_hmac($algo, '', '', true); if (!$test) { throw new InvalidParamException('Failed to generate HMAC with hash algorithm: ' . $algo); From f47b6c7683171c9a94a741dadc47ddbd38d1228a Mon Sep 17 00:00:00 2001 From: Nikolay Oleynikov Date: Mon, 27 Feb 2017 13:47:37 +0300 Subject: [PATCH 58/75] Fixes #7946 Fixed a bug when the `form` attribute was not propagated to the hidden input of the checkbox --- framework/CHANGELOG.md | 1 + framework/helpers/BaseHtml.php | 7 ++++++- tests/framework/helpers/HtmlTest.php | 7 +++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 62fa0bb8e0..c4807313d9 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -4,6 +4,7 @@ Yii Framework 2 Change Log 2.0.12 under development -------------------------- +- Bug #7946 Fixed a bug when the `form` attribute was not propagated to the hidden input of the checkbox (Kolyunya) - Bug #13087: Fixed getting active validators for safe attribute (developeruz) - Bug #13571: Fix `yii\db\mssql\QueryBuilder::checkIntegrity` for all tables (boboldehampsink) - Bug #11230: Include `defaultRoles` in `yii\rbac\DbManager->getRolesByUser()` results (developeruz) diff --git a/framework/helpers/BaseHtml.php b/framework/helpers/BaseHtml.php index 6e6a9c27df..4702aff4e1 100644 --- a/framework/helpers/BaseHtml.php +++ b/framework/helpers/BaseHtml.php @@ -60,6 +60,7 @@ class BaseHtml 'href', 'src', 'srcset', + 'form', 'action', 'method', @@ -742,7 +743,11 @@ class BaseHtml $value = array_key_exists('value', $options) ? $options['value'] : '1'; if (isset($options['uncheck'])) { // add a hidden field so that if the checkbox is not selected, it still submits a value - $hidden = static::hiddenInput($name, $options['uncheck']); + $hiddenOptions = []; + if (isset($options['form'])) { + $hiddenOptions['form'] = $options['form']; + } + $hidden = static::hiddenInput($name, $options['uncheck'], $hiddenOptions); unset($options['uncheck']); } else { $hidden = ''; diff --git a/tests/framework/helpers/HtmlTest.php b/tests/framework/helpers/HtmlTest.php index be457c6835..55e74ec00b 100644 --- a/tests/framework/helpers/HtmlTest.php +++ b/tests/framework/helpers/HtmlTest.php @@ -378,6 +378,13 @@ class HtmlTest extends TestCase 'label' => 'ccc', 'value' => 2, ])); + $this->assertEquals('', Html::checkbox('test', true, [ + 'class' => 'a', + 'uncheck' => '0', + 'label' => 'ccc', + 'value' => 2, + 'form' => 'test-form', + ])); } public function testDropDownList() From c78b9e470d2012abd8615be02b2dbfec32a1d895 Mon Sep 17 00:00:00 2001 From: sam002 Date: Mon, 27 Feb 2017 15:16:31 +0300 Subject: [PATCH 59/75] Fixes #13657: Fixed `yii\helpers\StringHelper::truncateHtml()` skip extra tags at the end --- framework/CHANGELOG.md | 1 + framework/helpers/BaseStringHelper.php | 19 ++++++++++++++----- tests/framework/helpers/StringHelperTest.php | 2 ++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index c4807313d9..971f451269 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -4,6 +4,7 @@ Yii Framework 2 Change Log 2.0.12 under development -------------------------- +- Bug #13657: Fixed `yii\helpers\StringHelper::truncateHtml()` skip extra tags at the end (sam002) - Bug #7946 Fixed a bug when the `form` attribute was not propagated to the hidden input of the checkbox (Kolyunya) - Bug #13087: Fixed getting active validators for safe attribute (developeruz) - Bug #13571: Fix `yii\db\mssql\QueryBuilder::checkIntegrity` for all tables (boboldehampsink) diff --git a/framework/helpers/BaseStringHelper.php b/framework/helpers/BaseStringHelper.php index e06da6ed6e..3dca93158a 100644 --- a/framework/helpers/BaseStringHelper.php +++ b/framework/helpers/BaseStringHelper.php @@ -160,10 +160,8 @@ class BaseStringHelper $truncated = []; foreach ($tokens as $token) { if ($token instanceof \HTMLPurifier_Token_Start) { //Tag begins - if ($totalCount < $count) { - $openTokens[$token->name] = isset($openTokens[$token->name]) ? $openTokens[$token->name] + 1 : 1; - $truncated[] = $token; - } + $openTokens[$token->name] = isset($openTokens[$token->name]) ? $openTokens[$token->name] + 1 : 1; + $truncated[] = $token; } elseif ($token instanceof \HTMLPurifier_Token_Text && $totalCount <= $count) { //Text if (false === $encoding) { preg_match('/^(\s*)/um', $token->data, $prefixSpace) ?: $prefixSpace = ['','']; @@ -178,12 +176,23 @@ class BaseStringHelper } elseif ($token instanceof \HTMLPurifier_Token_End) { //Tag ends if (!empty($openTokens[$token->name])) { $openTokens[$token->name]--; + if ($openTokens[$token->name] <= 0) { + unset($openTokens[$token->name]); + } $truncated[] = $token; } } elseif ($token instanceof \HTMLPurifier_Token_Empty) { //Self contained tags, i.e. etc. $truncated[] = $token; } - if (0 === $openTokens && $totalCount >= $count) { + if ($totalCount >= $count) { + if (0 < count($openTokens)) { + foreach (array_reverse($openTokens) as $name => $countTag) { + while ($countTag > 0) { + $truncated[] = new \HTMLPurifier_Token_End($name); + $countTag--; + } + } + } break; } } diff --git a/tests/framework/helpers/StringHelperTest.php b/tests/framework/helpers/StringHelperTest.php index 1e505871b1..27a060d207 100644 --- a/tests/framework/helpers/StringHelperTest.php +++ b/tests/framework/helpers/StringHelperTest.php @@ -117,6 +117,8 @@ class StringHelperTest extends TestCase $this->assertEquals('This is a test for...', StringHelper::truncate('This is a test for a sentance', 18, '...', null, true)); $this->assertEquals('

    This is a test

    • bullet1
    • b
    ...', StringHelper::truncate('

    This is a test

    • bullet1
    • bullet2
    • bullet3
    • bullet4
    ', 22, '...', null, true)); + + $this->assertEquals('
    • bullet1
    • b
    ...', StringHelper::truncate('
    • bullet1
    • bullet2
    • bullet3

    ', 8, '...', null, true)); } public function testTruncateWords() From 22dbd25e1a949cd0a486131ceb9345ae1f2ca90c Mon Sep 17 00:00:00 2001 From: Sergey Gonimar Date: Mon, 27 Feb 2017 17:26:30 +0500 Subject: [PATCH 60/75] Updated Tajik message translations [skip ci] --- framework/messages/tg/yii.php | 199 ++++++++++++++++++---------------- 1 file changed, 107 insertions(+), 92 deletions(-) diff --git a/framework/messages/tg/yii.php b/framework/messages/tg/yii.php index 93c408efe3..5721a07295 100644 --- a/framework/messages/tg/yii.php +++ b/framework/messages/tg/yii.php @@ -1,114 +1,129 @@ 'Дар {yii} кор мекунад', + 'Unknown alias: -{name}' => 'Тахаллуси номаълум: -{name}', + 'Yii Framework' => 'Yii Framework', + '(not set)' => '(супориш дода нашуд)', + ' and ' => ' ва ', + 'An internal server error occurred.' => 'Хатои дохилии сервер рух дод.', + 'Are you sure you want to delete this item?' => 'Оё шумо дар ҳақиқат мехоҳед, ки ин элементро нест кунед?', + 'Error' => 'Иштибоҳ', + 'File upload failed.' => 'Фарокашии файл. имконнопазир гашт.', + 'Home' => 'Саҳифаи асосӣ', + 'Invalid data received for parameter "{param}".' => 'Маънои нодурусти параметри "{param}".', + 'Login Required' => 'Вуруд талаб карда мешавад.', + 'Missing required arguments: {params}' => 'Далелҳои лозимӣ вуҷуд надоранд: {params}', + 'Missing required parameters: {params}' => 'Параметрҳои лозимӣ вуҷуд надоранд: {params}', + 'No' => 'Не', + 'No results found.' => 'Ҳеҷ чиз ёфт нашуд.', + 'Only files with these MIME types are allowed: {mimeTypes}.' => 'Барои фарокашии файлҳо танҳо бо намудҳои зерини MIME иҷозат аст: {mimeTypes}.', + 'Only files with these extensions are allowed: {extensions}.' => 'Барои фарокашии файлҳо танҳо тавассути зиёдкуни зерин иҷозат аст: {extensions}.', + 'Page not found.' => 'Саҳифа ёфт нашуд.', + 'Please fix the following errors:' => 'Лутфан, хатогиҳои зеринро ислоҳ намоед:', + 'Please upload a file.' => 'Лутфан, файлро бор кунед.', + 'Showing {begin, number}-{end, number} of {totalCount, number} {totalCount, plural, one{item} other{items}}.' => 'Қайдҳо нишон дода шудаанд {begin, number}-{end, number} аз {totalCount, number}.', + 'The combination {values} of {attributes} has already been taken.' => 'Комбинатсияи {values} параметрҳо {attributes} аллакай вуҷуд дорад.', + 'The file "{file}" is not an image.' => 'Файли "{file}" тасвир нест.', + 'The file "{file}" is too big. Its size cannot exceed {formattedLimit}.' => 'Ҳаҷми файли "{file}" азҳад зиёд калон аст. Андозаи он набояд аз {formattedLimit} зиёдтар бошад.', + 'The file "{file}" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Ҳаҷми файли "{file}" аз ҳад зиёд хурд аст. Он бояд аз {formattedLimit} калонтар бошад.', + 'The format of {attribute} is invalid.' => 'Формати нодурусти маънӣ {attribute}.', + 'The image "{file}" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Ҳаҷми файли "{file}" аз ҳад зиёд калон аст. Баландияш набояд аз {limit, number} {limit, plural, one{пиксел} few{пиксел} many{пиксел} other{пиксел}} зиёд бошад.', + 'The image "{file}" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Ҳаҷми файл "{file}" аз ҳад зиёд калон аст. Дарозияш набояд аз {limit, number} {limit, plural, one{пиксел} few{пиксел} many{пиксел} other{пиксел}} зиёд бошад.', + 'The image "{file}" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Ҳаҷми файл "{file}" аз ҳад зиёд хурд аст. Баландияш бояд аз {limit, number} {limit, plural, one{пиксел} few{пиксел} many{пиксел} other{пиксел}} зиёд бошад.', + 'The image "{file}" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Ҳаҷми файл "{file}" аз ҳад зиёд хурд аст. Дарозияш бояд аз {limit, number} {limit, plural, one{пиксел} few{пиксел} many{пиксел} other{пиксел}} зиёд бошад.', + 'The requested view "{name}" was not found.' => 'Файл дархостшудаи ҷадвали "{name}" ёфт нашуд.', + 'The verification code is incorrect.' => 'Рамзи нодурусти санҷишӣ.', + 'Total {count, number} {count, plural, one{item} other{items}}.' => 'Ҳамаги {count, number} {count, plural, one{қайд} few{қайд} many{қайдҳо} other{қайд}}.', + 'Unable to verify your data submission.' => 'Санҷидани маълумоти фиристодаи Шумо муяссар нагардид.', + 'Unknown option: --{name}' => 'Гузинаи номаълум: --{name}', + 'Yes' => 'Ҳа', + 'You are not allowed to perform this action.' => 'Шумо барои анҷом додани амали мазкур иҷозат надоред.', + 'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Ҳамаги {limit, number} аплод карда метавонед.', + 'in {delta, plural, =1{a day} other{# days}}' => '{delta} рӯзи дигар', + 'in {delta, plural, =1{a minute} other{# minutes}}' => '{delta} дақиқаи дигар', + 'in {delta, plural, =1{a month} other{# months}}' => '{delta} моҳи дигар', + 'in {delta, plural, =1{a second} other{# seconds}}' => '{delta} сонияи дигар', + 'in {delta, plural, =1{a year} other{# years}}' => '{delta} соли дигар', + 'in {delta, plural, =1{an hour} other{# hours}}' => 'баъд аз {delta, plural, =1{соат} one{# соат} few{# соат} many{# соат} other{# соат}}', + 'just now' => 'ҳоло', + 'the input value' => 'ҷадвали воридшуда', + '{attribute} "{value}" has already been taken.' => 'Ҷадвали «{value}» барои {attribute} аллакай банд аст.', + '{attribute} cannot be blank.' => 'Ҳошияи «{attribute}» набояд холӣ бошад.', + '{attribute} contains wrong subnet mask.' => 'Маънои "{attribute}" дорои нодурусти ниқоби зершабака мебошад.', + '{attribute} is invalid.' => 'Ҷадвали {attribute} ғалат аст.', + '{attribute} is not a valid URL.' => 'Ҷадвали «{attribute}» URL-и нодуруст мебошад.', + '{attribute} is not a valid email address.' => 'Ҷадвали {attribute} сӯроғаи дурусти E-mail нест.', + '{attribute} is not in the allowed range.' => 'Ҷадвали «{attribute}» ба рӯйхати сӯроғаҳои диапазонҳои иҷозат додашуда дохил намешавад.', + '{attribute} must be "{requiredValue}".' => 'Ҷадвали «{attribute}» бояд ба «{requiredValue}» баробар бошад.', + '{attribute} must be a number.' => 'Ҷадвали {attribute} бояд адад бошад.', + '{attribute} must be a string.' => 'Ҷадвали {attribute} бояд сатр бошад.', + '{attribute} must be a valid IP address.' => 'Ҷадвали «{attribute}» бояд сӯроғаи дурусти IP бошад.', + '{attribute} must be an IP address with specified subnet.' => 'Ҷадвали «{attribute}» бояд сӯроғаи IP бо зершабака бошад.', + '{attribute} must be an integer.' => 'Ҷадвали {attribute} бояд адади бутун бошад.', + '{attribute} must be either "{true}" or "{false}".' => 'Маънои «{attribute}» бояд ба «{true}» ё «{false}» баробар бошад.', + '{attribute} must be equal to "{compareValueOrAttribute}".' => 'Маънои «{attribute}» бояд ба «{compareValueOrAttribute}» баробар бошад.', + '{attribute} must be greater than "{compareValueOrAttribute}".' => 'Маънои «{attribute}» бояд аз маънии «{compareValueOrAttribute}» бузургтар бошад.', + '{attribute} must be greater than or equal to "{compareValueOrAttribute}".' => 'Маънои «{attribute}» бояд аз маънии «{compareValueOrAttribute}» бузургтар ё ба он баробар бошад.', + '{attribute} must be less than "{compareValueOrAttribute}".' => 'Маънои «{attribute}» бояд аз маънии «{compareValueOrAttribute}» хурдтар бошад.', + '{attribute} must be less than or equal to "{compareValueOrAttribute}".' => 'Маънои «{attribute}» бояд аз маънии «{compareValueOrAttribute}» хурдтар ё ба он баробар бошад.', + '{attribute} must be no greater than {max}.' => '{attribute} бояд аз {max} зиёд набошад.', + '{attribute} must be no less than {min}.' => '{attribute} бояд аз {min} кам набошад.', + '{attribute} must not be a subnet.' => 'Маънии «{attribute}» набояд зершабака бошад.', + '{attribute} must not be an IPv4 address.' => 'Маънии «{attribute}» набояд сӯроғаи IPv4 бошад.', + '{attribute} must not be an IPv6 address.' => 'Маънии «{attribute}» набояд сӯроғаи IPv6 бошад.', + '{attribute} must not be equal to "{compareValueOrAttribute}".' => 'Маънои «{attribute}» набояд ба «{compareValueOrAttribute}» баробар бошад.', + '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} хади ақал {min, number} рамз дошта бошад.', + '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} хамаги {max, number} рамз дошта бошад.', + '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} бояд {length, number} рамз дошта бошад.', + '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, one{# рӯз} few{# рӯз} many{# рӯз} other{# рӯз}}', + '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, one{# соат} few{# соат} many{# соат} other{# соат}}', + '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, one{# дақиқа} few{# дақиқа} many{# дақиқа} other{# дақиқа}}', + '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, one{# моҳ} few{# моҳ} many{# моҳ} other{# моҳ}}', + '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, one{# сония} few{# сония} many{# сония} other{# сония}}', + '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, one{# сол} few{# сол} many{# сол} other{# сол}}', + '{delta, plural, =1{a day} other{# days}} ago' => '{delta} рӯзи қабл', + '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta} дақиқаи қабл', + '{delta, plural, =1{a month} other{# months}} ago' => '{delta} моҳи қабл', + '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta} сонияи қабл', + '{delta, plural, =1{a year} other{# years}} ago' => '{delta} сол пеш', + '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta} соати қабл', '{nFormatted} B' => '{nFormatted} B', + '{nFormatted} GB' => '{nFormatted} GB', + '{nFormatted} GiB' => '{nFormatted} GiB', '{nFormatted} KB' => '{nFormatted} KB', '{nFormatted} KiB' => '{nFormatted} KiB', '{nFormatted} MB' => '{nFormatted} MB', '{nFormatted} MiB' => '{nFormatted} MiB', - '{nFormatted} GB' => '{nFormatted} GB', - '{nFormatted} GiB' => '{nFormatted} GiB', '{nFormatted} PB' => '{nFormatted} PB', '{nFormatted} PiB' => '{nFormatted} PiB', '{nFormatted} TB' => '{nFormatted} TB', '{nFormatted} TiB' => '{nFormatted} TiB', '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} байт', + '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} гибибайт', + '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} гигабайт', '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} кибибайт', '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} килобайт', '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} мебибайт', '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} мегабайт', - '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} гибибайт', - '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} гигабайт', '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} пебибайт', '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} петабайт', '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} тебибайт', '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} терабайт', - 'Are you sure you want to delete this item?' => 'Оё шумо дар ҳақиқат мехоҳед, ки ин нашрро нест кунед?', - 'The requested view "{name}" was not found.' => 'Файл "{name}" барои манзур ёфт нашуд', - '(not set)' => '(танзим нашуда)', - 'An internal server error occurred.' => 'Хатои дохилии сервер рух дод.', - 'Delete' => 'Нест', - 'Error' => 'Хато', - 'File upload failed.' => 'Аплоди файл шикаст хурд.', - 'Home' => 'Асосӣ', - 'Invalid data received for parameter "{param}".' => 'Маълумоти номувофиқ барои параметри "{param}" гирифта шуд.', - 'Login Required' => 'Вуруд маҷбурист', - 'Missing required arguments: {params}' => 'Аргументи лозими вуҷд надорад: {params}', - 'Missing required parameters: {params}' => 'Параметри лозими вуҷуд надорад: {params}', - 'No' => 'На', - 'No results found.' => 'Чизе ёфт нашуд.', - 'Page not found.' => 'Саҳифа ёфт нашуд.', - 'Please fix the following errors:' => 'Илтимос хатоҳои зеринро ислоҳ кунед:', - 'Only files with these extensions are allowed: {extensions}.' => 'Танҳо файлҳои бо ин пасванд иҷоза аст: {extensions}.', - 'Only files with these MIME types are allowed: {mimeTypes}.' => 'Фақат ин намуди файлҳо иҷозат аст: {mimeTypes}.', - 'The format of {attribute} is invalid.' => 'Формати {attribute} ғалат буд.', - 'Please upload a file.' => 'Илтимос файл аплод кунед.', - 'Showing {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 {formattedLimit}.' => 'Файл "{file}" калон аст. Аз {formattedLimit} набояд калонтар бошад.', - 'The file "{file}" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Файл "{file}" хурд аст. Аз {formattedLimit} набояд хурдтар бошад.', - 'The image "{file}" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Расми "{file}" баланд аст. Баландияш набояд аз {limit, number} зиёд бошад.', - 'The image "{file}" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Расми "{file}" паҳн аст. Паҳнияш набояд аз {limit, number} зиёд бошад.', - 'The image "{file}" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Расми "{file}" хурд аст. Баландияш набояд аз {limit, number} хурд бошад.', - 'The image "{file}" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Расми "{file}" хурд аст. Паҳнияш набояд аз {limit, number} хурд бошад.', - 'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Ҳамаги {limit, number} аплод карда метавонед.', - 'The verification code is incorrect.' => 'Коди санҷиши ғалат аст.', - 'Total {count, number} {count, plural, one{item} other{items}}.' => 'Ҳамаги {count, number} нашр.', - 'Unable to verify your data submission.' => 'Маълумоти фиристодаи шуморо санҷиш карда натавонистам.', - 'Unknown option: --{name}' => 'Гузинаи номаълум: --{name}', - 'Update' => 'Тағир', - 'View' => 'Манзур', - 'Yes' => 'Ҳа', - 'just now' => 'ҳоло', - 'the input value' => 'маълумоти вурудбуда', - 'You are not allowed to perform this action.' => 'Шумо барои анҷоми ин амал дастнорасед.', - 'in {delta, plural, =1{a second} other{# seconds}}' => '{delta} сонияи дигар', - 'in {delta, plural, =1{a minute} other{# minutes}}' => '{delta} дақиқаи дигар', - 'in {delta, plural, =1{an hour} other{# hours}}' => '{delta} соати дигар', - 'in {delta, plural, =1{a day} other{# days}}' => '{delta} рӯзи дигар', - 'in {delta, plural, =1{a month} other{# months}}' => '{delta} моҳи дигар', - 'in {delta, plural, =1{a year} other{# years}}' => '{delta} соли дигар', - '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta} сонияи қабл', - '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta} дақиқаи қабл', - '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta} соати қабл', - '{delta, plural, =1{a day} other{# days}} ago' => '{delta} рӯзи қабл', - '{delta, plural, =1{a month} other{# months}} ago' => '{delta} моҳи қабл', - '{delta, plural, =1{a year} other{# years}} ago' => '{delta} сол пеш', - '{attribute} "{value}" has already been taken.' => '{attribute} "{value}" машғул аст.', - '{attribute} cannot be blank.' => '{attribute} набояд холи бошад.', - '{attribute} is invalid.' => '{attribute} ғалат аст.', - '{attribute} is not a valid URL.' => '{attribute} URL ғалат аст.', - '{attribute} is not a valid email address.' => '{attribute} E-mail одреси ғалат аст.', - '{attribute} must be "{requiredValue}".' => '{attribute} бояд "{requiredValue}" бошад.', - '{attribute} must be a number.' => '{attribute} бояд адад бошад.', - '{attribute} must be a string.' => '{attribute} бояд хат бошад.', - '{attribute} must be an integer.' => '{attribute} бояд адади комил бошад.', - '{attribute} must be either "{true}" or "{false}".' => '{attribute} бояд ё "{true}" ё "{false}" бошад.', - '{attribute} must be greater than "{compareValue}".' => '{attribute} бояд аз "{compareValue}" калон бошад.', - '{attribute} must be greater than or equal to "{compareValue}".' => '{attribute} бояд калон ё баробари "{compareValue}" бошад.', - '{attribute} must be less than "{compareValue}".' => '{attribute} бояд аз "{compareValue}" хурд бошад.', - '{attribute} must be less than or equal to "{compareValue}".' => '{attribute} бояд хурд ё баробари "{compareValue}" бошад.', - '{attribute} must be no greater than {max}.' => '{attribute} бояд аз {max} зиёд набошад.', - '{attribute} must be no less than {min}.' => '{attribute} бояд аз {min} кам набошад.', - '{attribute} must be repeated exactly.' => '{attribute} айнан бояд такрор шавад.', - '{attribute} must not be equal to "{compareValue}".' => '{attribute} бояд баробари "{compareValue}" набошад.', - '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} хади ақал {min, number} рамз дошта бошад.', - '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} хамаги {max, number} рамз дошта бошад.', - '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} бояд {length, number} рамз дошта бошад.', ]; From cdb21d7e2c4c5382407f04910d76e473ea65d8b3 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Tue, 28 Feb 2017 00:58:25 +0600 Subject: [PATCH 61/75] Added JS tests for # #13379 --- framework/CHANGELOG.md | 2 +- tests/js/tests/yii.gridView.test.js | 53 +++++++++++++++++++++++------ 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index caf45544e3..8ee216e943 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -34,7 +34,7 @@ Yii Framework 2 Change Log - Bug #13649: Fixes issue where `['uncheck' => false]` and `['label' => false]` options for `ActiveRadio` and `ActiveCheckbox` were ignored (Alex-Code) - Enh #13221: Make `\yii\db\QueryTrait::limit()` and `\yii\db\QueryTrait::offset()` methods work with `\yii\db\Expression` (Ni-san) - Enh #13144: Refactored `yii\db\Query::queryScalar()` (Alex-Code) -- Bug #13379: Fixed `applyFilter` function in `yii.gridView.js` to work correctly when params in `filterUrl` are indexed (SilverFire) +- Bug #13379: Fixed `applyFilter` function in `yii.gridView.js` to work correctly when params in `filterUrl` are indexed (SilverFire, arogachev) 2.0.11.2 February 08, 2017 diff --git a/tests/js/tests/yii.gridView.test.js b/tests/js/tests/yii.gridView.test.js index be91ea7cf6..d166d33f7f 100644 --- a/tests/js/tests/yii.gridView.test.js +++ b/tests/js/tests/yii.gridView.test.js @@ -389,18 +389,12 @@ describe('yii.gridView', function () { }); }); - // https://github.com/yiisoft/yii2/pull/10284 - describe('with list box', function () { - var queryString = 'PostSearch[name]=&PostSearch[tags]=-1&PostSearch[tags][]=1&PostSearch[tags][]=2'; - - beforeEach(function () { - $listBox.find('option[value="1"]').prop('selected', true); - $listBox.find('option[value="2"]').prop('selected', true); - }); - describe('with values selected', function () { it('should send the request to correct url with correct parameters', function () { + $listBox.find('option[value="1"]').prop('selected', true); + $listBox.find('option[value="2"]').prop('selected', true); + $gridView = $('#w2').yiiGridView({ filterUrl: '/posts/index', filterSelector: '#w2-filters input, #w2-filters select' @@ -408,15 +402,25 @@ describe('yii.gridView', function () { $gridView.yiiGridView('applyFilter'); var $form = $gridView.find('.gridview-filter-form'); + var expectedQueryString = 'PostSearch[name]=&PostSearch[tags]=-1&PostSearch[tags][]=1' + + '&PostSearch[tags][]=2'; + assert.equal($form.attr('action'), '/posts/index'); - assert.equal(decodeURIComponent($form.serialize()), queryString); + assert.equal(decodeURIComponent($form.serialize()), expectedQueryString); }); }); + // https://github.com/yiisoft/yii2/pull/10284 + describe('with unselected values after applied filter', function () { it('should send the request to correct url with correct parameters', function () { + $listBox.find('option[value="1"]').prop('selected', true); + $listBox.find('option[value="2"]').prop('selected', true); + + var filterUrl = '/posts/index/?PostSearch[name]=&PostSearch[tags]=-1&PostSearch[tags][]=1' + + '&PostSearch[tags][]=2'; $gridView = $('#w2').yiiGridView({ - filterUrl: '/posts/index/?' + queryString, + filterUrl: filterUrl, filterSelector: '#w2-filters input, #w2-filters select' }); $listBox.find('option:selected').prop('selected', false); @@ -427,6 +431,33 @@ describe('yii.gridView', function () { assert.equal(decodeURIComponent($form.serialize()), 'PostSearch[name]=&PostSearch[tags]=-1'); }); }); + + // https://github.com/yiisoft/yii2/issues/13379 + + describe('with applied pagination', function () { + it("should correctly change multiple select's data", function () { + $listBox.find('option[value="2"]').prop('selected', true); + $listBox.find('option[value="3"]').prop('selected', true); + + var filterUrl = '/posts/index?PostSearch[tags]=-1PostSearch[tags][0]=2&PostSearch[tags][1]=3' + + '&page=2&per-page=2'; + $gridView = $('#w2').yiiGridView({ + filterUrl: filterUrl, + filterSelector: '#w2-filters input, #w2-filters select' + }); + + $listBox.find('option[value="4"]').prop('selected', true); + + $gridView.yiiGridView('applyFilter'); + + var $form = $gridView.find('.gridview-filter-form'); + var expectedQueryString = 'PostSearch[name]=' + + '&PostSearch[tags]=-1&PostSearch[tags][]=2&PostSearch[tags][]=3&PostSearch[tags][]=4' + + '&page=2&per-page=2'; + + assert.equal(decodeURIComponent($form.serialize()), expectedQueryString); + }); + }); }); describe('with repeated method call', function () { From 73ddf2e65eddf426b3cae94c33092a96327a0106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez=20Pan?= Date: Tue, 28 Feb 2017 14:39:03 +0100 Subject: [PATCH 62/75] Fixes #13670: Fixed alias option from console when it includes `-` or `_` in option name --- framework/CHANGELOG.md | 1 + framework/console/Request.php | 4 ++-- tests/framework/console/RequestTest.php | 10 +++++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index df05ff562e..b2de0b3c78 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -39,6 +39,7 @@ Yii Framework 2 Change Log - Bug #13379: Fixed `applyFilter` function in `yii.gridView.js` to work correctly when params in `filterUrl` are indexed (SilverFire, arogachev) - Enh #13650: Improved `yii\base\Security::hkdf()` to take advantage of native `hash_hkdf()` implementation in PHP >= 7.1.2 (charlesportwoodii) - Bug #13379: Fixed `applyFilter` function in `yii.gridView.js` to work correctly when params in `filterUrl` are indexed (SilverFire) +- Bug #13670: Fixed alias option from console when it includes `-` or `_` in option name (pana1990) 2.0.11.2 February 08, 2017 diff --git a/framework/console/Request.php b/framework/console/Request.php index bba9fca626..e7032f4c6a 100644 --- a/framework/console/Request.php +++ b/framework/console/Request.php @@ -76,7 +76,7 @@ class Request extends \yii\base\Request $params[] = $param; } elseif ($param === '--') { $endOfOptionsFound = true; - } elseif (preg_match('/^--(\w+)(?:=(.*))?$/', $param, $matches)) { + } elseif (preg_match('/^--([\w-]+)(?:=(.*))?$/', $param, $matches)) { $name = $matches[1]; if (is_numeric(substr($name, 0, 1))) { throw new Exception('Parameter "' . $name . '" is not valid'); @@ -85,7 +85,7 @@ class Request extends \yii\base\Request if ($name !== Application::OPTION_APPCONFIG) { $params[$name] = isset($matches[2]) ? $matches[2] : true; } - } elseif (preg_match('/^-(\w+)(?:=(.*))?$/', $param, $matches)) { + } elseif (preg_match('/^-([\w-]+)(?:=(.*))?$/', $param, $matches)) { $name = $matches[1]; if (is_numeric($name)) { $params[] = $param; diff --git a/tests/framework/console/RequestTest.php b/tests/framework/console/RequestTest.php index c515b6ac24..c4c1bee188 100644 --- a/tests/framework/console/RequestTest.php +++ b/tests/framework/console/RequestTest.php @@ -28,19 +28,27 @@ class RequestTest extends TestCase '-12345', '--option1', '--option2=testValue', + '--option-3=testValue', + '--option_4=testValue', '-alias1', '-alias2=testValue', + '-alias-3=testValue', + '-alias_4=testValue', ], 'expected' => [ 'route' => 'controller/route', 'params' => [ 'param1', '-12345', - 'option1' => '1', + 'option1' => true, 'option2' => 'testValue', + 'option-3' => 'testValue', + 'option_4' => 'testValue', '_aliases' => [ 'alias1' => true, 'alias2' => 'testValue', + 'alias-3' => 'testValue', + 'alias_4' => 'testValue', ], ], ], From a350b34c1b7476b91010feb028dbf7d94af63158 Mon Sep 17 00:00:00 2001 From: sam002 Date: Tue, 28 Feb 2017 16:52:09 +0300 Subject: [PATCH 63/75] Fixes additional case for #13657 which wasn't covered by tests --- framework/helpers/BaseStringHelper.php | 20 +++++++++----------- tests/framework/helpers/StringHelperTest.php | 2 +- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/framework/helpers/BaseStringHelper.php b/framework/helpers/BaseStringHelper.php index 3dca93158a..ecf228e0bc 100644 --- a/framework/helpers/BaseStringHelper.php +++ b/framework/helpers/BaseStringHelper.php @@ -157,11 +157,13 @@ class BaseStringHelper $tokens = $lexer->tokenizeHTML($string, $config, new \HTMLPurifier_Context()); $openTokens = []; $totalCount = 0; + $depth = 0; $truncated = []; foreach ($tokens as $token) { if ($token instanceof \HTMLPurifier_Token_Start) { //Tag begins - $openTokens[$token->name] = isset($openTokens[$token->name]) ? $openTokens[$token->name] + 1 : 1; + $openTokens[$depth] = $token->name; $truncated[] = $token; + ++$depth; } elseif ($token instanceof \HTMLPurifier_Token_Text && $totalCount <= $count) { //Text if (false === $encoding) { preg_match('/^(\s*)/um', $token->data, $prefixSpace) ?: $prefixSpace = ['','']; @@ -174,11 +176,9 @@ class BaseStringHelper $totalCount += $currentCount; $truncated[] = $token; } elseif ($token instanceof \HTMLPurifier_Token_End) { //Tag ends - if (!empty($openTokens[$token->name])) { - $openTokens[$token->name]--; - if ($openTokens[$token->name] <= 0) { - unset($openTokens[$token->name]); - } + if ($token->name === $openTokens[$depth-1]) { + --$depth; + unset($openTokens[$depth]); $truncated[] = $token; } } elseif ($token instanceof \HTMLPurifier_Token_Empty) { //Self contained tags, i.e. etc. @@ -186,11 +186,9 @@ class BaseStringHelper } if ($totalCount >= $count) { if (0 < count($openTokens)) { - foreach (array_reverse($openTokens) as $name => $countTag) { - while ($countTag > 0) { - $truncated[] = new \HTMLPurifier_Token_End($name); - $countTag--; - } + krsort($openTokens); + foreach ($openTokens as $name) { + $truncated[] = new \HTMLPurifier_Token_End($name); } } break; diff --git a/tests/framework/helpers/StringHelperTest.php b/tests/framework/helpers/StringHelperTest.php index 27a060d207..337a131540 100644 --- a/tests/framework/helpers/StringHelperTest.php +++ b/tests/framework/helpers/StringHelperTest.php @@ -118,7 +118,7 @@ class StringHelperTest extends TestCase $this->assertEquals('

    This is a test

    • bullet1
    • b
    ...', StringHelper::truncate('

    This is a test

    • bullet1
    • bullet2
    • bullet3
    • bullet4
    ', 22, '...', null, true)); - $this->assertEquals('
    • bullet1
    • b
    ...', StringHelper::truncate('
    • bullet1
    • bullet2
    • bullet3

    ', 8, '...', null, true)); + $this->assertEquals('
    • bullet1
    • b
    ...', StringHelper::truncate('
    • bullet1
    • bullet2

    ', 8, '...', null, true)); } public function testTruncateWords() From af612f47d11f76e9674bb26faca1d9fe7f4389ac Mon Sep 17 00:00:00 2001 From: SilverFire - Dmitry Naumenko Date: Tue, 28 Feb 2017 15:58:59 +0200 Subject: [PATCH 64/75] Addded pluralization and singularization exception for `currency` --- framework/helpers/BaseInflector.php | 2 ++ tests/framework/helpers/InflectorTest.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/framework/helpers/BaseInflector.php b/framework/helpers/BaseInflector.php index 039e70d589..d8754be040 100644 --- a/framework/helpers/BaseInflector.php +++ b/framework/helpers/BaseInflector.php @@ -51,6 +51,7 @@ class BaseInflector '/us$/i' => 'uses', '/(alias)$/i' => '\1es', '/(ax|cris|test)is$/i' => '\1es', + '/(currenc)y$/' => '\1ies', '/s$/' => 's', '/^$/' => '', '/$/' => 's', @@ -97,6 +98,7 @@ class BaseInflector '/(n)ews$/i' => '\1\2ews', '/(n)etherlands$/i' => '\1\2etherlands', '/eaus$/' => 'eau', + '/(currenc)ies$/' => '\1y', '/^(.*us)$/' => '\\1', '/s$/i' => '', ]; diff --git a/tests/framework/helpers/InflectorTest.php b/tests/framework/helpers/InflectorTest.php index 4a9a1c6d44..713b3c7c9d 100644 --- a/tests/framework/helpers/InflectorTest.php +++ b/tests/framework/helpers/InflectorTest.php @@ -31,6 +31,7 @@ class InflectorTest extends TestCase 'test' => 'tests', 'car' => 'cars', 'netherlands' => 'netherlands', + 'currency' => 'currencies', ]; foreach ($testData as $testIn => $testOut) { @@ -59,6 +60,7 @@ class InflectorTest extends TestCase 'tests' => 'test', 'cars' => 'car', 'Netherlands' => 'Netherlands', + 'currencies' => 'currency', ]; foreach ($testData as $testIn => $testOut) { $this->assertEquals($testOut, Inflector::singularize($testIn)); From b5558835a11a6161e1692cacea87166479407af6 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Wed, 1 Mar 2017 12:27:03 +0300 Subject: [PATCH 65/75] Readme redesign (#13682) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Redesigned readme [skip ci] * Them → Us * Clarified reasonable defaults part * Update README.md --- README.md | 96 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 55 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 9e8f9c248c..1f46b69f52 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ -Yii PHP Framework Version 2 -=========================== +

    + + Yii Framework + +

    -Thank you for choosing Yii 2 - a modern PHP framework designed for professional Web development. - -Yii 2 is a complete rewrite of its previous version Yii 1.1 which is one of the most popular PHP frameworks. -Yii 2 inherits the main spirit behind Yii for being simple, fast and highly extensible. -Yii 2 requires PHP 5.4 and embraces the best practices and protocols found in modern Web application development. +Yii 2 is a modern framework designed to be a solid foundation for your PHP application. +It is fast, secure and efficient and works right out of the box pre-configured with reasonable defaults. +The framework is easy to adjust to meet your needs, because Yii has been designed to be flexible. [![Latest Stable Version](https://poser.pugx.org/yiisoft/yii2/v/stable.png)](https://packagist.org/packages/yiisoft/yii2) [![Total Downloads](https://poser.pugx.org/yiisoft/yii2/downloads.png)](https://packagist.org/packages/yiisoft/yii2) @@ -18,8 +19,51 @@ Yii 2 requires PHP 5.4 and embraces the best practices and protocols found in mo [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/yiisoft/yii2/badges/quality-score.png?s=b1074a1ff6d0b214d54fa5ab7abbb90fc092471d)](https://scrutinizer-ci.com/g/yiisoft/yii2/) [![Code Climate](https://img.shields.io/codeclimate/github/yiisoft/yii2.svg)](https://codeclimate.com/github/yiisoft/yii2) -DIRECTORY STRUCTURE -------------------- +Installation +------------ + +- The minimum required PHP version of Yii is PHP 5.4. +- It works best with PHP 7. +- [Follow the Definitive Guide](http://www.yiiframework.com/doc-2.0/guide-start-installation.html) +in order to get step by step instructions. + +Documentation +------------- + +- A [Definitive Guide](http://www.yiiframework.com/doc-2.0/guide-index.html) and +a [Class Reference](http://www.yiiframework.com/doc-2.0/index.html) cover every detail +of the framework. +- There is a [PDF version](http://stuff.cebe.cc/yii2-guide.en.pdf) of the Definitive Guide +and a [Definitive Guide Mirror](http://stuff.cebe.cc/yii2docs/) which is updated every 15 minutes. +- For Yii 1.1 users, there is [Upgrading from Yii 1.1](docs/guide/intro-upgrade-from-v1.md) +to get an idea of what has changed in 2.0. + +Community +--------- + +- Participate in [discussions at forums](http://www.yiiframework.com/forum/). +- [Chat in IRC](http://www.yiiframework.com/chat/). +- Follow us on [Facebook](https://www.facebook.com/groups/yiitalk/), [Twitter](https://twitter.com/yiiframework) +and [GitHub](https://github.com/yiisoft/yii2). + +Contributing +------------ + +The framework is Open Source [powered by an excellent community](https://github.com/yiisoft/yii2/graphs/contributors). + +You may join us and: + +- [Report an issue](docs/internals/report-an-issue.md) +- [Translate documentation or messages](docs/internals/translation-workflow.md) +- [Give us feedback or start a design discussion](http://www.yiiframework.com/forum/index.php/forum/42-general-discussions-for-yii-20/) +- [Contribute to the core code or fix bugs](docs/internals/git-workflow.md) + +### Reporting Secirity issues + +Please refer to a [special page at the website](http://www.yiiframework.com/security/) +describing proper workflow for security issue reports. + +### Directory Structure ``` build/ internally used build tools @@ -28,39 +72,9 @@ framework/ core framework code tests/ tests of the core framework code ``` +### Spreading the Word -REQUIREMENTS ------------- - -The minimum requirement by Yii is that your Web server supports PHP 5.4. - - -DOCUMENTATION -------------- - -Yii 2.0 has a [Definitive Guide](http://www.yiiframework.com/doc-2.0/guide-index.html) and -a [Class Reference](http://www.yiiframework.com/doc-2.0/index.html) which cover every detail of Yii. - -There is also a [PDF version](http://stuff.cebe.cc/yii2-guide.en.pdf) of the Definitive Guide -and a [Definitive Guide Mirror](http://stuff.cebe.cc/yii2docs/) which is updated every 15 minutes. - -For 1.1 users, you may refer to [Upgrading from Yii 1.1](docs/guide/intro-upgrade-from-v1.md) -to have a general idea of what has changed in 2.0. - - -HOW TO PARTICIPATE ------------------- - -### Your participation to Yii 2 development is very welcome! - -You may participate in the following ways: - -- [Report an issue](docs/internals/report-an-issue.md) -- [Translate documentation or messages](docs/internals/translation-workflow.md) -- [Give us feedback or start a design discussion](http://www.yiiframework.com/forum/index.php/forum/42-general-discussions-for-yii-20/) -- [Contribute to the core code or fix bugs](docs/internals/git-workflow.md) - -### Acknowledging or citing Yii 2 +Acknowledging or citing Yii 2 is as important as direct contributions. **In presentations** From aafa574a553e74099ed10c21785a6325c2205733 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 1 Mar 2017 10:30:22 +0100 Subject: [PATCH 66/75] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1f46b69f52..4825d3d5dd 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ and [GitHub](https://github.com/yiisoft/yii2). Contributing ------------ -The framework is Open Source [powered by an excellent community](https://github.com/yiisoft/yii2/graphs/contributors). +The framework is [Open Source](LICENSE.md) powered by [an excellent community](https://github.com/yiisoft/yii2/graphs/contributors). You may join us and: From bfa2390d5a57a4319eb656fad9ca8b3d69a9462d Mon Sep 17 00:00:00 2001 From: SilverFire - Dmitry Naumenko Date: Wed, 1 Mar 2017 19:29:30 +0200 Subject: [PATCH 67/75] Added `yii\di\Instance::__set_state()` method --- framework/CHANGELOG.md | 1 + framework/di/Instance.php | 18 ++++++++++++++++++ tests/framework/di/InstanceTest.php | 27 +++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index b2de0b3c78..3db93dca18 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -40,6 +40,7 @@ Yii Framework 2 Change Log - Enh #13650: Improved `yii\base\Security::hkdf()` to take advantage of native `hash_hkdf()` implementation in PHP >= 7.1.2 (charlesportwoodii) - Bug #13379: Fixed `applyFilter` function in `yii.gridView.js` to work correctly when params in `filterUrl` are indexed (SilverFire) - Bug #13670: Fixed alias option from console when it includes `-` or `_` in option name (pana1990) +- Enh: Added `yii\di\Instance::__set_state()` method to restore object after serialization using `var_export()` function (silvefire) 2.0.11.2 February 08, 2017 diff --git a/framework/di/Instance.php b/framework/di/Instance.php index a11491a150..6a5e56761f 100644 --- a/framework/di/Instance.php +++ b/framework/di/Instance.php @@ -161,4 +161,22 @@ class Instance return Yii::$container->get($this->id); } } + + /** + * Restores class state after using `var_export()` + * + * @param array $state + * @return Instance + * @throws InvalidConfigException when $state property does not contain `id` parameter + * @see var_export() + * @since 2.0.12 + */ + public static function __set_state($state) + { + if (!isset($state['id'])) { + throw new InvalidConfigException('Failed to instantiate class "Instance". Required parameter "id" is missing'); + } + + return new self($state['id']); + } } diff --git a/tests/framework/di/InstanceTest.php b/tests/framework/di/InstanceTest.php index 2ac448d447..349b55ab9a 100644 --- a/tests/framework/di/InstanceTest.php +++ b/tests/framework/di/InstanceTest.php @@ -156,4 +156,31 @@ class InstanceTest extends TestCase $this->assertInstanceOf('yii\db\Connection', $db = $cache->db); $this->assertEquals('sqlite:path/to/file.db', $db->dsn); } + + public function testRestoreAfterVarExport() + { + $instance = Instance::of('something'); + $export = var_export($instance, true); + + $this->assertEquals(<<<'PHP' +yii\di\Instance::__set_state(array( + 'id' => 'something', +)) +PHP + , $export); + + $this->assertEquals($instance, Instance::__set_state([ + 'id' => 'something', + ])); + } + + public function testRestoreAfterVarExportRequiresId() + { + $this->setExpectedException( + 'yii\base\InvalidConfigException', + 'Failed to instantiate class "Instance". Required parameter "id" is missing' + ); + + Instance::__set_state([]); + } } From 5e422fd27d4ca1097a316cb56d6fbcbe939f8d91 Mon Sep 17 00:00:00 2001 From: SilverFire - Dmitry Naumenko Date: Thu, 2 Mar 2017 12:58:08 +0200 Subject: [PATCH 68/75] Fixed InstanceTest::testRestoreAfterVarExport() to work on HHVM --- tests/framework/di/InstanceTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/framework/di/InstanceTest.php b/tests/framework/di/InstanceTest.php index 349b55ab9a..4a660a92df 100644 --- a/tests/framework/di/InstanceTest.php +++ b/tests/framework/di/InstanceTest.php @@ -162,10 +162,10 @@ class InstanceTest extends TestCase $instance = Instance::of('something'); $export = var_export($instance, true); - $this->assertEquals(<<<'PHP' -yii\di\Instance::__set_state(array( - 'id' => 'something', -)) + $this->assertRegExp(<<<'PHP' +@yii\\di\\Instance::__set_state\(array\( +\s+'id' => 'something', +\)\)@ PHP , $export); From 1cb1a978f2a4f01060cb918b708e52bb168c9949 Mon Sep 17 00:00:00 2001 From: Nikolay Oleynikov Date: Thu, 2 Mar 2017 20:49:31 +0300 Subject: [PATCH 69/75] Remove imports of inexistent classes --- tests/framework/web/ControllerTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/framework/web/ControllerTest.php b/tests/framework/web/ControllerTest.php index 6dbce18b3f..5f32b614c5 100644 --- a/tests/framework/web/ControllerTest.php +++ b/tests/framework/web/ControllerTest.php @@ -10,8 +10,6 @@ namespace yiiunit\framework\web; use Yii; use yii\base\InlineAction; use yii\web\Response; -use yiiunit\framework\web\stubs\Bar; -use yiiunit\framework\web\stubs\OtherQux; use yiiunit\TestCase; /** From 8f88780104cc59e9af26219b259f653d9c00bc26 Mon Sep 17 00:00:00 2001 From: Dmitry Naumenko Date: Fri, 3 Mar 2017 09:30:03 +0200 Subject: [PATCH 70/75] Fixed typo in warning message in Cache::getOrSet() Fixes #13690 --- framework/caching/Cache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/caching/Cache.php b/framework/caching/Cache.php index 4693d185b7..4efc473d3b 100644 --- a/framework/caching/Cache.php +++ b/framework/caching/Cache.php @@ -574,7 +574,7 @@ abstract class Cache extends Component implements \ArrayAccess $value = call_user_func($closure, $this); if (!$this->set($key, $value, $duration, $dependency)) { - Yii::warning('Failed to set cache value for key ' . json_encode($value), __METHOD__); + Yii::warning('Failed to set cache value for key ' . json_encode($key), __METHOD__); } return $value; From 608013f3e01ff16af4d6b846cf5a9b63219f0625 Mon Sep 17 00:00:00 2001 From: Kyle McCarthy Date: Fri, 3 Mar 2017 11:28:58 -0600 Subject: [PATCH 71/75] fixed spelling error in main README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4825d3d5dd..587555355b 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ You may join us and: - [Give us feedback or start a design discussion](http://www.yiiframework.com/forum/index.php/forum/42-general-discussions-for-yii-20/) - [Contribute to the core code or fix bugs](docs/internals/git-workflow.md) -### Reporting Secirity issues +### Reporting Security issues Please refer to a [special page at the website](http://www.yiiframework.com/security/) describing proper workflow for security issue reports. From 0b3c066bd20b870a8db3e9a0a06a84e64c9d7184 Mon Sep 17 00:00:00 2001 From: Kyle McCarthy Date: Sat, 4 Mar 2017 09:55:17 -0600 Subject: [PATCH 72/75] Fixes #13695: `\yii\web\Response::setStatusCode()` method now returns the Response object itself --- framework/CHANGELOG.md | 1 + framework/web/Response.php | 2 ++ 2 files changed, 3 insertions(+) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 3db93dca18..8afa5a5a53 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -41,6 +41,7 @@ Yii Framework 2 Change Log - Bug #13379: Fixed `applyFilter` function in `yii.gridView.js` to work correctly when params in `filterUrl` are indexed (SilverFire) - Bug #13670: Fixed alias option from console when it includes `-` or `_` in option name (pana1990) - Enh: Added `yii\di\Instance::__set_state()` method to restore object after serialization using `var_export()` function (silvefire) +- Enh #13695: `\yii\web\Response::setStatusCode()` method now returns the Response object itself (kyle-mccarthy) 2.0.11.2 February 08, 2017 diff --git a/framework/web/Response.php b/framework/web/Response.php index dc63fc9ec1..723d8c714b 100644 --- a/framework/web/Response.php +++ b/framework/web/Response.php @@ -271,6 +271,7 @@ class Response extends \yii\base\Response * @param int $value the status code * @param string $text the status text. If not set, it will be set automatically based on the status code. * @throws InvalidParamException if the status code is invalid. + * @return $this the response object itself */ public function setStatusCode($value, $text = null) { @@ -286,6 +287,7 @@ class Response extends \yii\base\Response } else { $this->statusText = $text; } + return $this; } /** From e66722aa4397c33518f6bbafa7953df0efc70505 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 4 Mar 2017 23:44:57 +0100 Subject: [PATCH 73/75] Update .travis.yml allow building on all branches. /cc @Silverfire --- .travis.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 404d5a3a1b..f458b99e82 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,10 +9,12 @@ dist: trusty sudo: false # build only on master branches -branches: - only: - - master - - 2.1 +# commented as this prevents people from running builds on their forks: +# https://github.com/yiisoft/yii2/commit/bd87be990fa238c6d5e326d0a171f38d02dc253a +#branches: +# only: +# - master +# - 2.1 # From f9041646ddaac6760b727a4fdc6d3e37705e69ef Mon Sep 17 00:00:00 2001 From: Kolyunya Date: Sun, 5 Mar 2017 14:27:13 +0300 Subject: [PATCH 74/75] Improve string validator test coverage --- tests/framework/validators/StringValidatorTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/framework/validators/StringValidatorTest.php b/tests/framework/validators/StringValidatorTest.php index 3f6100ffc0..eadf4fa671 100644 --- a/tests/framework/validators/StringValidatorTest.php +++ b/tests/framework/validators/StringValidatorTest.php @@ -22,6 +22,8 @@ class StringValidatorTest extends TestCase $val = new StringValidator(); $this->assertFalse($val->validate(['not a string'])); $this->assertTrue($val->validate('Just some string')); + $this->assertFalse($val->validate(true)); + $this->assertFalse($val->validate(false)); } public function testValidateValueLength() @@ -70,6 +72,12 @@ class StringValidatorTest extends TestCase $model->attr_string = 'a tet string'; $val->validateAttribute($model, 'attr_string'); $this->assertFalse($model->hasErrors()); + $model->attr_string = true; + $val->validateAttribute($model, 'attr_string'); + $this->assertTrue($model->hasErrors()); + $model->attr_string = false; + $val->validateAttribute($model, 'attr_string'); + $this->assertTrue($model->hasErrors()); $val = new StringValidator(['length' => 20]); $model = new FakedValidationModel(); $model->attr_string = str_repeat('x', 20); From a6d266405d53be96e4d189acbbaec1bc6362626d Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Mon, 6 Mar 2017 15:24:18 +0300 Subject: [PATCH 75/75] Fixes #13671: Fixed error handler trace to work correctly with XDebug --- framework/CHANGELOG.md | 1 + framework/views/errorHandler/exception.php | 8 +------- framework/web/ErrorHandler.php | 24 ++++++++++++++++++++++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 8afa5a5a53..13f5f7acbf 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -4,6 +4,7 @@ Yii Framework 2 Change Log 2.0.12 under development -------------------------- +- 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) diff --git a/framework/views/errorHandler/exception.php b/framework/views/errorHandler/exception.php index 03a384cf88..db3e88b053 100644 --- a/framework/views/errorHandler/exception.php +++ b/framework/views/errorHandler/exception.php @@ -378,13 +378,7 @@ body.mousedown pre {
    -
      - renderCallStackItem($exception->getFile(), $exception->getLine(), null, null, [], 1) ?> - getTrace(), $length = count($trace); $i < $length; ++$i): ?> - renderCallStackItem(@$trace[$i]['file'] ?: null, @$trace[$i]['line'] ?: null, - @$trace[$i]['class'] ?: null, @$trace[$i]['function'] ?: null, @$trace[$i]['args'] ?: [], $i + 2) ?> - -
    + renderCallStack($exception) ?>
    diff --git a/framework/web/ErrorHandler.php b/framework/web/ErrorHandler.php index b43d235675..dfeca722da 100644 --- a/framework/web/ErrorHandler.php +++ b/framework/web/ErrorHandler.php @@ -308,6 +308,30 @@ class ErrorHandler extends \yii\base\ErrorHandler ]); } + /** + * Renders call stack. + * @param \Exception $exception exception to get call stack from + * @return string HTML content of the rendered call stack. + */ + public function renderCallStack(\Exception $exception) + { + $out = '
      '; + $out .= $this->renderCallStackItem($exception->getFile(), $exception->getLine(), null, null, [], 1); + for ($i = 0, $trace = $exception->getTrace(), $length = count($trace); $i < $length; ++$i) { + $file = !empty($trace[$i]['file']) ? $trace[$i]['file'] : null; + $line = !empty($trace[$i]['line']) ? $trace[$i]['line'] : null; + $class = !empty($trace[$i]['class']) ? $trace[$i]['class'] : null; + $function = null; + if (!empty($trace[$i]['function']) && $trace[$i]['function'] !== 'unknown') { + $function = $trace[$i]['function']; + } + $args = !empty($trace[$i]['args']) ? $trace[$i]['args'] : []; + $out .= $this->renderCallStackItem($file, $line, $class, $function, $args, $i + 2); + } + $out .= '
    '; + return $out; + } + /** * Renders the global variables of the request. * List of global variables is defined in [[displayVars]].