From c9ac82d85db77bc0f8dd92a068be8aa5af1c3c00 Mon Sep 17 00:00:00 2001
From: Nabi KaramAliZadeh
Date: Mon, 23 Oct 2023 21:46:23 +0330
Subject: [PATCH 01/20] Fixed #20023, Added `eol=lf` to `.gitattributes` file.
(#20026)
Signed-off-by: Nabi
---
.gitattributes | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/.gitattributes b/.gitattributes
index 08f809af71..044c0a1615 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -4,16 +4,16 @@
# ...Unless the name matches the following overriding patterns
# Definitively text files
-*.php text
-*.css text
-*.js text
-*.txt text
-*.md text
-*.xml text
-*.json text
-*.bat text
-*.sql text
-*.yml text
+*.php text eol=lf
+*.css text eol=lf
+*.js text eol=lf
+*.txt text eol=lf
+*.md text eol=lf
+*.xml text eol=lf
+*.json text eol=lf
+*.bat text eol=lf
+*.sql text eol=lf
+*.yml text eol=lf
# Ensure those won't be messed up with
*.png binary
From 2141f9abdfbdeffdc0c245f7850bf397b3b9e409 Mon Sep 17 00:00:00 2001
From: Robert Korulczyk
Date: Mon, 23 Oct 2023 20:49:27 +0200
Subject: [PATCH 02/20] Improve performance of handling
`ErrorHandler::$memoryReserveSize`
---
framework/CHANGELOG.md | 1 +
framework/base/ErrorHandler.php | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md
index 469a23773d..69bcec2bd8 100644
--- a/framework/CHANGELOG.md
+++ b/framework/CHANGELOG.md
@@ -10,6 +10,7 @@ Yii Framework 2 Change Log
- Bug #19927: Fixed `console\controllers\MessageController` when saving translations to database: fixed FK error when adding new string and language at the same time, checking/regenerating all missing messages and dropping messages for unused languages (atrandafir)
- Bug #20002: Fixed superfluous query on HEAD request in serializer (xicond)
- Enh #12743: Added new methods `BaseActiveRecord::loadRelations()` and `BaseActiveRecord::loadRelationsFor()` to eager load related models for existing primary model instances (PowerGamer1)
+- Enh #20030: Improve performance of handling `ErrorHandler::$memoryReserveSize` (antonshevelev, rob006)
2.0.49.2 October 12, 2023
-------------------------
diff --git a/framework/base/ErrorHandler.php b/framework/base/ErrorHandler.php
index 81a20f0a3c..393438ede5 100644
--- a/framework/base/ErrorHandler.php
+++ b/framework/base/ErrorHandler.php
@@ -94,7 +94,7 @@ abstract class ErrorHandler extends Component
set_error_handler([$this, 'handleError']);
}
if ($this->memoryReserveSize > 0) {
- $this->_memoryReserve = str_pad('', $this->memoryReserveSize, 'x');
+ $this->_memoryReserve = str_repeat('x', $this->memoryReserveSize);
}
// to restore working directory in shutdown handler
if (PHP_SAPI !== 'cli') {
From 6c19ba1c77e70bceb6e0a617660d1a34df7bd8da Mon Sep 17 00:00:00 2001
From: Nabi KaramAliZadeh
Date: Tue, 24 Oct 2023 11:51:28 +0330
Subject: [PATCH 03/20] Effectiveness eol=lf on all text file extensions, in
.gitattributes file (#20035)
* Fixed #20023, Added `eol=lf` to `.gitattributes` file.
Signed-off-by: Nabi
* Fix #20023: Effectiveness eol=lf on all file extensions, in .gitattributes file.
Signed-off-by: Nabi
---------
Signed-off-by: Nabi
---
.gitattributes | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/.gitattributes b/.gitattributes
index 044c0a1615..1d3d767fd3 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,19 +1,19 @@
# Autodetect text files
-* text=auto
+* text=auto eol=lf
# ...Unless the name matches the following overriding patterns
# Definitively text files
-*.php text eol=lf
-*.css text eol=lf
-*.js text eol=lf
-*.txt text eol=lf
-*.md text eol=lf
-*.xml text eol=lf
-*.json text eol=lf
-*.bat text eol=lf
-*.sql text eol=lf
-*.yml text eol=lf
+*.php text
+*.css text
+*.js text
+*.txt text
+*.md text
+*.xml text
+*.json text
+*.bat text
+*.sql text
+*.yml text
# Ensure those won't be messed up with
*.png binary
From 778d708c4f028c6997ff42ee2e1aead86cce3a64 Mon Sep 17 00:00:00 2001
From: Nabi KaramAliZadeh
Date: Tue, 24 Oct 2023 11:55:08 +0330
Subject: [PATCH 04/20] Fix fa messages (#20037)
* Fix 20016: Updating the Persian translation message file.
Signed-off-by: Nabi
* Fix 20036: Updating the Persian translation message file.
Signed-off-by: Nabi
---------
Signed-off-by: Nabi
---
framework/messages/fa/yii.php | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/framework/messages/fa/yii.php b/framework/messages/fa/yii.php
index 86d08922ca..e9213b3482 100644
--- a/framework/messages/fa/yii.php
+++ b/framework/messages/fa/yii.php
@@ -27,7 +27,7 @@ return [
'"{attribute}" does not support operator "{operator}".' => '"{attribute}" از عملگر "{operator}" پشتیبانی نمیکند.',
'(not set)' => '(تنظیم نشده)',
'Action not found.' => 'عمل یافت نشد.',
- 'Aliases available: {aliases}' => 'نام مستعارهای موجود: {aliases}',
+ 'Aliases available: {aliases}' => 'نامهای مستعار موجود: {aliases}',
'An internal server error occurred.' => 'خطای داخلی سرور رخ داده است.',
'Are you sure you want to delete this item?' => 'آیا اطمینان به حذف این مورد دارید؟',
'Condition for "{attribute}" should be either a value or valid operator specification.' => 'شرط برای "{attribute}" باید یک مقدار یا مشخصهی عملگر معتبر باشد.',
@@ -49,11 +49,11 @@ return [
'Page not found.' => 'صفحهای یافت نشد.',
'Please fix the following errors:' => 'لطفاً خطاهای زیر را رفع نمائید:',
'Please upload a file.' => 'لطفاً یک فایل آپلود کنید.',
- 'Showing {begin, number}-{end, number} of {totalCount, number} {totalCount, plural, one{item} other{items}}.' => 'نمایش {begin, number} تا {end, number} مورد از کل {totalCount, number} مورد.',
+ '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 file "{file}" is too small. Its size cannot be smaller than {formattedLimit}.' => 'حجم فایل "{file}" بسیار کم میباشد. حجم آن نمیتواند از {formattedLimit} کمتر باشد.',
'The format of {attribute} is invalid.' => 'قالب {attribute} نامعتبر است.',
'The format of {filter} is invalid.' => 'قالب {filter} نامعتبر است.',
'The image "{file}" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'تصویر "{file}" خیلی بزرگ است. ارتفاع نمیتواند بزرگتر از {limit, number} پیکسل باشد.',
@@ -87,7 +87,7 @@ return [
'{attribute} is invalid.' => '{attribute} معتبر نیست.',
'{attribute} is not a valid URL.' => '{attribute} یک URL معتبر نیست.',
'{attribute} is not a valid email address.' => '{attribute} یک آدرس ایمیل معتبر نیست.',
- '{attribute} is not in the allowed range.' => '{attribute} در محدوده مجاز نمیباشد.',
+ '{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} باید یک رشته باشد.',
From 4b7669cf08c1ae1e3a32a4cc8c4a6f355b84707b Mon Sep 17 00:00:00 2001
From: Wilmer Arambula
Date: Wed, 25 Oct 2023 08:37:18 -0300
Subject: [PATCH 05/20] Fix boolean type `MYSQL`.
---
framework/db/mysql/Schema.php | 2 +-
tests/framework/db/mysql/type/BooleanTest.php | 249 ++++++++++++++++++
2 files changed, 250 insertions(+), 1 deletion(-)
create mode 100644 tests/framework/db/mysql/type/BooleanTest.php
diff --git a/framework/db/mysql/Schema.php b/framework/db/mysql/Schema.php
index fa2270eb77..666279e3b9 100644
--- a/framework/db/mysql/Schema.php
+++ b/framework/db/mysql/Schema.php
@@ -279,7 +279,7 @@ SQL;
if (isset($values[1])) {
$column->scale = (int) $values[1];
}
- if ($column->size === 1 && $type === 'bit') {
+ if ($column->size === 1 && ($type === 'tinyint' || $type === 'bit')) {
$column->type = 'boolean';
} elseif ($type === 'bit') {
if ($column->size > 32) {
diff --git a/tests/framework/db/mysql/type/BooleanTest.php b/tests/framework/db/mysql/type/BooleanTest.php
new file mode 100644
index 0000000000..5f8326134b
--- /dev/null
+++ b/tests/framework/db/mysql/type/BooleanTest.php
@@ -0,0 +1,249 @@
+getConnection(true);
+ $schema = $db->getSchema();
+ $tableName = '{{%boolean}}';
+
+ if ($db->getTableSchema($tableName)) {
+ $db->createCommand()->dropTable($tableName)->execute();
+ }
+
+ $db->createCommand()->createTable(
+ $tableName,
+ [
+ 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK),
+ 'bool_col_tinyint' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN),
+ 'bool_col_bit' => $schema->createColumnSchemaBuilder('bit', 1),
+ ]
+ )->execute();
+
+ // test type `boolean`
+ $columnBoolColTinyint = $db->getTableSchema($tableName)->getColumn('bool_col_tinyint');
+ $this->assertSame('boolean', $columnBoolColTinyint->phpType);
+
+ $columnBoolColBit = $db->getTableSchema($tableName)->getColumn('bool_col_bit');
+ $this->assertSame('boolean', $columnBoolColBit->phpType);
+
+ // test value `false`
+ $db->createCommand()->insert($tableName, ['bool_col_tinyint' => false, 'bool_col_bit' => false])->execute();
+ $boolValues = $db->createCommand("SELECT * FROM $tableName WHERE id = 1")->queryOne();
+ $this->assertEquals(0, $boolValues['bool_col_tinyint']);
+ $this->assertEquals(0, $boolValues['bool_col_bit']);
+
+ // test php typecast
+ $phpTypeCastBoolColTinyint = $columnBoolColTinyint->phpTypecast($boolValues['bool_col_tinyint']);
+ $this->assertFalse($phpTypeCastBoolColTinyint);
+
+ $phpTypeCastBoolColBit = $columnBoolColBit->phpTypecast($boolValues['bool_col_bit']);
+ $this->assertFalse($phpTypeCastBoolColBit);
+
+ // test value `true`
+ $db->createCommand()->insert($tableName, ['bool_col_tinyint' => true, 'bool_col_bit' => true])->execute();
+ $boolValues = $db->createCommand("SELECT * FROM $tableName WHERE id = 2")->queryOne();
+ $this->assertEquals(1, $boolValues['bool_col_tinyint']);
+ $this->assertEquals(1, $boolValues['bool_col_bit']);
+
+ // test php typecast
+ $phpTypeCastBoolColTinyint = $columnBoolColTinyint->phpTypecast($boolValues['bool_col_tinyint']);
+ $this->assertTrue($phpTypeCastBoolColTinyint);
+
+ $phpTypeCastBoolColBit = $columnBoolColBit->phpTypecast($boolValues['bool_col_bit']);
+ $this->assertTrue($phpTypeCastBoolColBit);
+ }
+
+ public function testBooleanWithValueInteger()
+ {
+ $db = $this->getConnection(true);
+ $schema = $db->getSchema();
+ $tableName = '{{%boolean}}';
+
+ if ($db->getTableSchema($tableName)) {
+ $db->createCommand()->dropTable($tableName)->execute();
+ }
+
+ $db->createCommand()->createTable(
+ $tableName,
+ [
+ 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK),
+ 'bool_col_tinyint' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN),
+ 'bool_col_bit' => $schema->createColumnSchemaBuilder('bit', 1),
+ ]
+ )->execute();
+
+ // test type `boolean`
+ $columnBoolColTinyint = $db->getTableSchema($tableName)->getColumn('bool_col_tinyint');
+ $this->assertSame('boolean', $columnBoolColTinyint->phpType);
+
+ $columnBoolColBit = $db->getTableSchema($tableName)->getColumn('bool_col_bit');
+ $this->assertSame('boolean', $columnBoolColBit->phpType);
+
+ // test value `0`
+ $db->createCommand()->insert($tableName, ['bool_col_tinyint' => 0, 'bool_col_bit' => 0])->execute();
+ $boolValues = $db->createCommand("SELECT * FROM $tableName WHERE id = 1")->queryOne();
+ $this->assertEquals(0, $boolValues['bool_col_tinyint']);
+ $this->assertEquals(0, $boolValues['bool_col_bit']);
+
+ // test php typecast
+ $phpTypeCastBoolColTinyint = $columnBoolColTinyint->phpTypecast($boolValues['bool_col_tinyint']);
+ $this->assertFalse($phpTypeCastBoolColTinyint);
+
+ $phpTypeCastBoolColBit = $columnBoolColBit->phpTypecast($boolValues['bool_col_bit']);
+ $this->assertFalse($phpTypeCastBoolColBit);
+
+ // test value `1`
+ $db->createCommand()->insert($tableName, ['bool_col_tinyint' => 1, 'bool_col_bit' => 1])->execute();
+ $boolValues = $db->createCommand("SELECT * FROM $tableName WHERE id = 2")->queryOne();
+ $this->assertEquals(1, $boolValues['bool_col_tinyint']);
+ $this->assertEquals(1, $boolValues['bool_col_bit']);
+
+ // test php typecast
+ $phpTypeCastBoolColTinyint = $columnBoolColTinyint->phpTypecast($boolValues['bool_col_tinyint']);
+ $this->assertTrue($phpTypeCastBoolColTinyint);
+
+ $phpTypeCastBoolColBit = $columnBoolColBit->phpTypecast($boolValues['bool_col_bit']);
+ $this->assertTrue($phpTypeCastBoolColBit);
+ }
+
+ public function testBooleanWithValueNegative()
+ {
+ $db = $this->getConnection(true);
+ $schema = $db->getSchema();
+ $tableName = '{{%boolean}}';
+
+ if ($db->getTableSchema($tableName)) {
+ $db->createCommand()->dropTable($tableName)->execute();
+ }
+
+ $db->createCommand()->createTable(
+ $tableName,
+ [
+ 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK),
+ 'bool_col_tinyint' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN),
+ 'bool_col_bit' => $schema->createColumnSchemaBuilder('bit', 1),
+ ]
+ )->execute();
+
+ // test type `boolean`
+ $columnBoolColTinyint = $db->getTableSchema($tableName)->getColumn('bool_col_tinyint');
+ $this->assertSame('boolean', $columnBoolColTinyint->phpType);
+
+ $columnBoolColBit = $db->getTableSchema($tableName)->getColumn('bool_col_bit');
+ $this->assertSame('boolean', $columnBoolColBit->phpType);
+
+ // test value `-1`
+ $db->createCommand()->insert($tableName, ['bool_col_tinyint' => -1, 'bool_col_bit' => -1])->execute();
+ $boolValues = $db->createCommand("SELECT * FROM $tableName WHERE id = 1")->queryOne();
+
+ $this->assertEquals(1, $boolValues['bool_col_tinyint']);
+ $this->assertEquals(1, $boolValues['bool_col_bit']);
+
+ // test php typecast
+ $phpTypeCastBoolColTinyint = $columnBoolColTinyint->phpTypecast($boolValues['bool_col_tinyint']);
+ $this->assertTrue($phpTypeCastBoolColTinyint);
+
+ $phpTypeCastBoolColBit = $columnBoolColBit->phpTypecast($boolValues['bool_col_bit']);
+ $this->assertTrue($phpTypeCastBoolColBit);
+ }
+
+ public function testBooleanWithValueNull()
+ {
+ $db = $this->getConnection(true);
+ $schema = $db->getSchema();
+ $tableName = '{{%boolean}}';
+
+ if ($db->getTableSchema($tableName)) {
+ $db->createCommand()->dropTable($tableName)->execute();
+ }
+
+ $db->createCommand()->createTable(
+ $tableName,
+ [
+ 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK),
+ 'bool_col_tinyint' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN),
+ 'bool_col_bit' => $schema->createColumnSchemaBuilder('bit', 1),
+ ]
+ )->execute();
+
+ // test type `boolean`
+ $columnBoolColTinyint = $db->getTableSchema($tableName)->getColumn('bool_col_tinyint');
+ $this->assertSame('boolean', $columnBoolColTinyint->phpType);
+
+ $columnBoolColBit = $db->getTableSchema($tableName)->getColumn('bool_col_bit');
+ $this->assertSame('boolean', $columnBoolColBit->phpType);
+
+ // test value `null`
+ $db->createCommand()->insert($tableName, ['bool_col_tinyint' => null, 'bool_col_bit' => null])->execute();
+ $boolValues = $db->createCommand("SELECT * FROM $tableName WHERE id = 1")->queryOne();
+
+ $this->assertNull($boolValues['bool_col_tinyint']);
+ $this->assertNull($boolValues['bool_col_bit']);
+
+ // test php typecast
+ $phpTypeCastBoolColTinyint = $columnBoolColTinyint->phpTypecast($boolValues['bool_col_tinyint']);
+ $this->assertNull($phpTypeCastBoolColTinyint);
+
+ $phpTypeCastBoolColBit = $columnBoolColBit->phpTypecast($boolValues['bool_col_bit']);
+ $this->assertNull($phpTypeCastBoolColBit);
+ }
+
+ public function testBooleanWithValueOverflow()
+ {
+ $db = $this->getConnection(true);
+ $schema = $db->getSchema();
+ $tableName = '{{%boolean}}';
+
+ if ($db->getTableSchema($tableName)) {
+ $db->createCommand()->dropTable($tableName)->execute();
+ }
+
+ $db->createCommand()->createTable(
+ $tableName,
+ [
+ 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK),
+ 'bool_col_tinyint' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN),
+ 'bool_col_bit' => $schema->createColumnSchemaBuilder('bit', 1),
+ ]
+ )->execute();
+
+ // test type `boolean`
+ $columnBoolColTinyint = $db->getTableSchema($tableName)->getColumn('bool_col_tinyint');
+ $this->assertSame('boolean', $columnBoolColTinyint->phpType);
+
+ $columnBoolColBit = $db->getTableSchema($tableName)->getColumn('bool_col_bit');
+ $this->assertSame('boolean', $columnBoolColBit->phpType);
+
+ // test value `2`
+ $db->createCommand()->insert($tableName, ['bool_col_tinyint' => 2, 'bool_col_bit' => 2])->execute();
+ $boolValues = $db->createCommand("SELECT * FROM $tableName WHERE id = 1")->queryOne();
+
+ $this->assertEquals(1, $boolValues['bool_col_tinyint']);
+ $this->assertEquals(1, $boolValues['bool_col_bit']);
+
+ // test php typecast
+ $phpTypeCastBoolColTinyint = $columnBoolColTinyint->phpTypecast($boolValues['bool_col_tinyint']);
+ $this->assertTrue($phpTypeCastBoolColTinyint);
+
+ $phpTypeCastBoolColBit = $columnBoolColBit->phpTypecast($boolValues['bool_col_bit']);
+ $this->assertTrue($phpTypeCastBoolColBit);
+ }
+}
From 18ed620e7890de315409a28f720be794a15f4434 Mon Sep 17 00:00:00 2001
From: Wilmer Arambula
Date: Wed, 25 Oct 2023 08:57:40 -0300
Subject: [PATCH 06/20] Update tests.
---
tests/framework/db/mysql/SchemaTest.php | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/tests/framework/db/mysql/SchemaTest.php b/tests/framework/db/mysql/SchemaTest.php
index 4e602a317e..82342419a8 100644
--- a/tests/framework/db/mysql/SchemaTest.php
+++ b/tests/framework/db/mysql/SchemaTest.php
@@ -228,9 +228,13 @@ SQL;
]
);
+ $columns['bool_col']['type'] = 'boolean';
+ $columns['bool_col']['phpType'] = 'boolean';
+ $columns['bool_col2']['type'] = 'boolean';
+ $columns['bool_col2']['phpType'] = 'boolean';
+
if (version_compare($version, '5.7', '<')) {
$columns['int_col3']['phpType'] = 'string';
-
$columns['json_col']['type'] = 'text';
$columns['json_col']['dbType'] = 'longtext';
$columns['json_col']['phpType'] = 'string';
From e2773e452491f2664d4ef0d11e2bb557c6d19112 Mon Sep 17 00:00:00 2001
From: Saleh Hashemi <81674631+salehhashemi1992@users.noreply.github.com>
Date: Wed, 25 Oct 2023 20:17:03 +0330
Subject: [PATCH 07/20] Fix #20032: Added `mask` method for string masking with
multibyte support
---
framework/CHANGELOG.md | 2 ++
framework/helpers/BaseStringHelper.php | 30 ++++++++++++++++++
tests/framework/helpers/StringHelperTest.php | 32 ++++++++++++++++++++
3 files changed, 64 insertions(+)
diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md
index 69bcec2bd8..ec2923a1e5 100644
--- a/framework/CHANGELOG.md
+++ b/framework/CHANGELOG.md
@@ -11,6 +11,8 @@ Yii Framework 2 Change Log
- Bug #20002: Fixed superfluous query on HEAD request in serializer (xicond)
- Enh #12743: Added new methods `BaseActiveRecord::loadRelations()` and `BaseActiveRecord::loadRelationsFor()` to eager load related models for existing primary model instances (PowerGamer1)
- Enh #20030: Improve performance of handling `ErrorHandler::$memoryReserveSize` (antonshevelev, rob006)
+- Enh #20032: Added `mask` method for string masking with multibyte support (salehhashemi1992)
+
2.0.49.2 October 12, 2023
-------------------------
diff --git a/framework/helpers/BaseStringHelper.php b/framework/helpers/BaseStringHelper.php
index 60261f9828..e9c5327b03 100644
--- a/framework/helpers/BaseStringHelper.php
+++ b/framework/helpers/BaseStringHelper.php
@@ -497,4 +497,34 @@ class BaseStringHelper
return implode('', $parts);
}
+
+ /**
+ * Masks a portion of a string with a repeated character.
+ * This method is multibyte-safe.
+ *
+ * @param string $string The input string.
+ * @param int $start The starting position from where to begin masking.
+ * This can be a positive or negative integer.
+ * Positive values count from the beginning,
+ * negative values count from the end of the string.
+ * @param int $length The length of the section to be masked.
+ * The masking will start from the $start position
+ * and continue for $length characters.
+ * @param string $mask The character to use for masking. The default is '*'.
+ * @return string The masked string.
+ */
+ public static function mask($string, $start, $length, $mask = '*') {
+ $strLength = mb_strlen($string, 'UTF-8');
+
+ // Return original string if start position is out of bounds
+ if ($start >= $strLength || $start < -$strLength) {
+ return $string;
+ }
+
+ $masked = mb_substr($string, 0, $start, 'UTF-8');
+ $masked .= str_repeat($mask, abs($length));
+ $masked .= mb_substr($string, $start + abs($length), null, 'UTF-8');
+
+ return $masked;
+ }
}
diff --git a/tests/framework/helpers/StringHelperTest.php b/tests/framework/helpers/StringHelperTest.php
index 56acdb0c26..a640e5cdda 100644
--- a/tests/framework/helpers/StringHelperTest.php
+++ b/tests/framework/helpers/StringHelperTest.php
@@ -474,4 +474,36 @@ class StringHelperTest extends TestCase
['', ''],
];
}
+
+ public function testMask()
+ {
+ // Standard masking
+ $this->assertSame('12******90', StringHelper::mask('1234567890', 2, 6));
+ $this->assertSame('a********j', StringHelper::mask('abcdefghij', 1, 8));
+ $this->assertSame('*************', StringHelper::mask('Hello, World!', 0, 13));
+ $this->assertSame('************!', StringHelper::mask('Hello, World!', 0, 12));
+ $this->assertSame('Hello, *orld!', StringHelper::mask('Hello, World!', 7, 1));
+ $this->assertSame('Saleh Hashemi', StringHelper::mask('Saleh Hashemi', 0, 0));
+
+ // Different Mask Character
+ $this->assertSame('12######90', StringHelper::mask('1234567890', 2, 6, '#'));
+
+ // Positions outside the string
+ $this->assertSame('1234567890', StringHelper::mask('1234567890', 20, 6));
+ $this->assertSame('1234567890', StringHelper::mask('1234567890', -20, 6));
+
+ // Negative values for start
+ $this->assertSame('1234****90', StringHelper::mask('1234567890', -6, 4));
+
+ // type-related edge case
+ $this->assertSame('1234****90', StringHelper::mask(1234567890, -6, 4));
+
+ // Multibyte characters
+ $this->assertSame('你**', StringHelper::mask('你好吗', 1, 2));
+ $this->assertSame('你好吗', StringHelper::mask('你好吗', 4, 2));
+
+ // Special characters
+ $this->assertSame('em**l@email.com', StringHelper::mask('email@email.com', 2, 2));
+ $this->assertSame('******email.com', StringHelper::mask('email@email.com', 0, 6));
+ }
}
From 9d3c71d6a712a99f42ae1c5d588312f49514523a Mon Sep 17 00:00:00 2001
From: Wilmer Arambula <42547589+terabytesoftw@users.noreply.github.com>
Date: Wed, 25 Oct 2023 13:47:46 -0300
Subject: [PATCH 08/20] Fix #20040: Fix type `boolean` in `MSSQL`
---
framework/CHANGELOG.md | 2 +-
framework/db/mssql/Schema.php | 41 +++-
tests/framework/db/mssql/type/BooleanTest.php | 198 ++++++++++++++++++
3 files changed, 232 insertions(+), 9 deletions(-)
create mode 100644 tests/framework/db/mssql/type/BooleanTest.php
diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md
index ec2923a1e5..42ce39634c 100644
--- a/framework/CHANGELOG.md
+++ b/framework/CHANGELOG.md
@@ -3,7 +3,7 @@ Yii Framework 2 Change Log
2.0.50 under development
------------------------
-
+- Bug #20040: Fix type `boolean` in `MSSQL` (terabytesoftw)
- Bug #20005: Fix `yii\console\controllers\ServeController` to specify the router script (terabytesoftw)
- Bug #19060: Fix `yii\widgets\Menu` bug when using Closure for active item and adding additional tests in `tests\framework\widgets\MenuTest` (atrandafir)
- Bug #13920: Fixed erroneous validation for specific cases (tim-fischer-maschinensucher)
diff --git a/framework/db/mssql/Schema.php b/framework/db/mssql/Schema.php
index 005b1555f7..db7f07c87c 100644
--- a/framework/db/mssql/Schema.php
+++ b/framework/db/mssql/Schema.php
@@ -375,6 +375,7 @@ SQL;
*/
protected function loadColumnSchema($info)
{
+ $isVersion2017orLater = version_compare($this->db->getSchema()->getServerVersion(), '14', '>=');
$column = $this->createColumnSchema();
$column->name = $info['column_name'];
@@ -393,20 +394,21 @@ SQL;
if (isset($this->typeMap[$type])) {
$column->type = $this->typeMap[$type];
}
+
+ if ($isVersion2017orLater && $type === 'bit') {
+ $column->type = 'boolean';
+ }
+
if (!empty($matches[2])) {
$values = explode(',', $matches[2]);
$column->size = $column->precision = (int) $values[0];
+
if (isset($values[1])) {
$column->scale = (int) $values[1];
}
- if ($column->size === 1 && ($type === 'tinyint' || $type === 'bit')) {
- $column->type = 'boolean';
- } elseif ($type === 'bit') {
- if ($column->size > 32) {
- $column->type = 'bigint';
- } elseif ($column->size === 32) {
- $column->type = 'integer';
- }
+
+ if ($isVersion2017orLater === false) {
+ $column->type = $this->booleanTypeLegacy($column->size, $type);
}
}
}
@@ -813,4 +815,27 @@ SQL;
{
return Yii::createObject(ColumnSchemaBuilder::className(), [$type, $length, $this->db]);
}
+
+ /**
+ * Assigns a type boolean for the column type bit, for legacy versions of MSSQL.
+ *
+ * @param int $size column size.
+ * @param string $type column type.
+ *
+ * @return string column type.
+ */
+ private function booleanTypeLegacy($size, $type)
+ {
+ if ($size === 1 && ($type === 'tinyint' || $type === 'bit')) {
+ return 'boolean';
+ } elseif ($type === 'bit') {
+ if ($size > 32) {
+ return 'bigint';
+ } elseif ($size === 32) {
+ return 'integer';
+ }
+ }
+
+ return $type;
+ }
}
diff --git a/tests/framework/db/mssql/type/BooleanTest.php b/tests/framework/db/mssql/type/BooleanTest.php
new file mode 100644
index 0000000000..97fd712451
--- /dev/null
+++ b/tests/framework/db/mssql/type/BooleanTest.php
@@ -0,0 +1,198 @@
+getConnection(true);
+ $schema = $db->getSchema();
+ $tableName = '{{%boolean}}';
+
+ if ($db->getTableSchema($tableName)) {
+ $db->createCommand()->dropTable($tableName)->execute();
+ }
+
+ $db->createCommand()->createTable(
+ $tableName,
+ [
+ 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK),
+ 'bool_col' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN),
+ ]
+ )->execute();
+
+ // test type
+ $column = $db->getTableSchema($tableName)->getColumn('bool_col');
+ $this->assertSame('boolean', $column->phpType);
+
+ // test value `false`
+ $db->createCommand()->insert($tableName, ['bool_col' => false])->execute();
+ $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 1")->queryScalar();
+ $this->assertEquals(0, $boolValue);
+
+ // test php typecast
+ $phpTypeCast = $column->phpTypecast($boolValue);
+ $this->assertFalse($phpTypeCast);
+
+ // test value `true`
+ $db->createCommand()->insert($tableName, ['bool_col' => true])->execute();
+ $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 2")->queryScalar();
+ $this->assertEquals(1, $boolValue);
+
+ // test php typecast
+ $phpTypeCast = $column->phpTypecast($boolValue);
+ $this->assertTrue($phpTypeCast);
+ }
+
+ public function testBooleanWithValueInteger()
+ {
+ $db = $this->getConnection(true);
+ $schema = $db->getSchema();
+ $tableName = '{{%boolean}}';
+
+ if ($db->getTableSchema($tableName)) {
+ $db->createCommand()->dropTable($tableName)->execute();
+ }
+
+ $db->createCommand()->createTable(
+ $tableName,
+ [
+ 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK),
+ 'bool_col' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN),
+ ]
+ )->execute();
+
+ // test type
+ $column = $db->getTableSchema($tableName)->getColumn('bool_col');
+ $this->assertSame('boolean', $column->phpType);
+
+ // test value 0
+ $db->createCommand()->insert($tableName, ['bool_col' => 0])->execute();
+ $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 1")->queryScalar();
+ $this->assertEquals(0, $boolValue);
+
+ // test php typecast
+ $phpTypeCast = $column->phpTypecast($boolValue);
+ $this->assertFalse($phpTypeCast);
+
+ // test value 1
+ $db->createCommand()->insert($tableName, ['bool_col' => 1])->execute();
+ $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 2")->queryScalar();
+ $this->assertEquals(1, $boolValue);
+
+ // test php typecast
+ $phpTypeCast = $column->phpTypecast($boolValue);
+ $this->assertTrue($phpTypeCast);
+ }
+
+ public function testBooleanValueNegative()
+ {
+ $db = $this->getConnection(true);
+ $schema = $db->getSchema();
+ $tableName = '{{%boolean}}';
+
+ if ($db->getTableSchema($tableName)) {
+ $db->createCommand()->dropTable($tableName)->execute();
+ }
+
+ $db->createCommand()->createTable(
+ $tableName,
+ [
+ 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK),
+ 'bool_col' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN),
+ ]
+ )->execute();
+
+ // test type
+ $column = $db->getTableSchema($tableName)->getColumn('bool_col');
+ $this->assertSame('boolean', $column->phpType);
+
+ // test value 2
+ $db->createCommand()->insert($tableName, ['bool_col' => -1])->execute();
+ $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 1")->queryScalar();
+ $this->assertEquals(1, $boolValue);
+
+ // test php typecast
+ $phpTypeCast = $column->phpTypecast($boolValue);
+ $this->assertTrue($phpTypeCast);
+ }
+
+ public function testBooleanWithValueNull()
+ {
+ $db = $this->getConnection(true);
+ $schema = $db->getSchema();
+ $tableName = '{{%boolean}}';
+
+ if ($db->getTableSchema($tableName)) {
+ $db->createCommand()->dropTable($tableName)->execute();
+ }
+
+ $db->createCommand()->createTable(
+ $tableName,
+ [
+ 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK),
+ 'bool_col' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN),
+ ]
+ )->execute();
+
+ // test type
+ $column = $db->getTableSchema($tableName)->getColumn('bool_col');
+ $this->assertSame('boolean', $column->phpType);
+
+ // test value `null`
+ $db->createCommand()->insert($tableName, ['bool_col' => null])->execute();
+ $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 1")->queryScalar();
+ $this->assertNull($boolValue);
+
+ // test php typecast
+ $phpTypeCast = $column->phpTypecast($boolValue);
+ $this->assertNull($phpTypeCast);
+ }
+
+ public function testBooleanWithValueOverflow()
+ {
+ $db = $this->getConnection(true);
+ $schema = $db->getSchema();
+ $tableName = '{{%boolean}}';
+
+ if ($db->getTableSchema($tableName)) {
+ $db->createCommand()->dropTable($tableName)->execute();
+ }
+
+ $db->createCommand()->createTable(
+ $tableName,
+ [
+ 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK),
+ 'bool_col' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN),
+ ]
+ )->execute();
+
+ // test type
+ $column = $db->getTableSchema($tableName)->getColumn('bool_col');
+ $this->assertSame('boolean', $column->phpType);
+
+ // test value 2
+ $db->createCommand()->insert($tableName, ['bool_col' => 2])->execute();
+ $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 1")->queryScalar();
+ $this->assertEquals(1, $boolValue);
+
+ // test php typecast
+ $phpTypeCast = $column->phpTypecast($boolValue);
+ $this->assertTrue($phpTypeCast);
+ }
+}
From 7005d2775b68003927cbf82efbdba823c1819a30 Mon Sep 17 00:00:00 2001
From: Rene Saare
Date: Wed, 25 Oct 2023 19:53:45 +0300
Subject: [PATCH 09/20] Fix #20042: Add empty array check to
`ActiveQueryTrait::findWith()`
---
framework/CHANGELOG.md | 1 +
framework/db/ActiveQueryTrait.php | 4 ++++
2 files changed, 5 insertions(+)
diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md
index 42ce39634c..13fb6e3d93 100644
--- a/framework/CHANGELOG.md
+++ b/framework/CHANGELOG.md
@@ -11,6 +11,7 @@ Yii Framework 2 Change Log
- Bug #20002: Fixed superfluous query on HEAD request in serializer (xicond)
- Enh #12743: Added new methods `BaseActiveRecord::loadRelations()` and `BaseActiveRecord::loadRelationsFor()` to eager load related models for existing primary model instances (PowerGamer1)
- Enh #20030: Improve performance of handling `ErrorHandler::$memoryReserveSize` (antonshevelev, rob006)
+- Enh #20042: Add empty array check to `ActiveQueryTrait::findWith()` (renkas)
- Enh #20032: Added `mask` method for string masking with multibyte support (salehhashemi1992)
diff --git a/framework/db/ActiveQueryTrait.php b/framework/db/ActiveQueryTrait.php
index e2de1dd12d..d49fa0fba8 100644
--- a/framework/db/ActiveQueryTrait.php
+++ b/framework/db/ActiveQueryTrait.php
@@ -135,6 +135,10 @@ trait ActiveQueryTrait
*/
public function findWith($with, &$models)
{
+ if (empty($models)) {
+ return;
+ }
+
$primaryModel = reset($models);
if (!$primaryModel instanceof ActiveRecordInterface) {
/* @var $modelClass ActiveRecordInterface */
From f0628dfab5e5f78b1fd73f98df7be4bf649eccb3 Mon Sep 17 00:00:00 2001
From: Wilmer Arambula
Date: Wed, 25 Oct 2023 14:02:42 -0300
Subject: [PATCH 10/20] Fix error typo.
---
tests/framework/db/mysql/type/BooleanTest.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/framework/db/mysql/type/BooleanTest.php b/tests/framework/db/mysql/type/BooleanTest.php
index 5f8326134b..bc9d307a78 100644
--- a/tests/framework/db/mysql/type/BooleanTest.php
+++ b/tests/framework/db/mysql/type/BooleanTest.php
@@ -7,7 +7,7 @@
namespace yiiunit\framework\db\mysql\type;
-use yii\db\mssql\Schema;
+use yii\db\mysql\Schema;
use yiiunit\framework\db\DatabaseTestCase;
/**
From 1d35584ec9f407c2ad89c3a15c5d8c362406c021 Mon Sep 17 00:00:00 2001
From: Wilmer Arambula
Date: Wed, 25 Oct 2023 14:39:54 -0300
Subject: [PATCH 11/20] Add changelog line.
---
framework/CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md
index 13fb6e3d93..5518b2bb58 100644
--- a/framework/CHANGELOG.md
+++ b/framework/CHANGELOG.md
@@ -3,6 +3,7 @@ Yii Framework 2 Change Log
2.0.50 under development
------------------------
+- Bug #20045: Fix type `boolean` in `MYSQL` (terabytesoftw)
- Bug #20040: Fix type `boolean` in `MSSQL` (terabytesoftw)
- Bug #20005: Fix `yii\console\controllers\ServeController` to specify the router script (terabytesoftw)
- Bug #19060: Fix `yii\widgets\Menu` bug when using Closure for active item and adding additional tests in `tests\framework\widgets\MenuTest` (atrandafir)
From b40de6a5380819472f5bde6c5d120e9c83146c87 Mon Sep 17 00:00:00 2001
From: Wilmer Arambula <42547589+terabytesoftw@users.noreply.github.com>
Date: Thu, 26 Oct 2023 05:05:34 -0300
Subject: [PATCH 12/20] Update framework/CHANGELOG.md
Co-authored-by: Robert Korulczyk
---
framework/CHANGELOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md
index 5518b2bb58..a80668a452 100644
--- a/framework/CHANGELOG.md
+++ b/framework/CHANGELOG.md
@@ -3,7 +3,7 @@ Yii Framework 2 Change Log
2.0.50 under development
------------------------
-- Bug #20045: Fix type `boolean` in `MYSQL` (terabytesoftw)
+- Bug #20045: Fix type `boolean` in `MySQL` (terabytesoftw)
- Bug #20040: Fix type `boolean` in `MSSQL` (terabytesoftw)
- Bug #20005: Fix `yii\console\controllers\ServeController` to specify the router script (terabytesoftw)
- Bug #19060: Fix `yii\widgets\Menu` bug when using Closure for active item and adding additional tests in `tests\framework\widgets\MenuTest` (atrandafir)
From 6e6174deaac5f1781da3435ab0a707402a95bd38 Mon Sep 17 00:00:00 2001
From: Wilmer Arambula
Date: Thu, 26 Oct 2023 12:59:15 -0300
Subject: [PATCH 13/20] Add test for boolean type `SQLite`.
---
.../framework/db/sqlite/type/BooleanTest.php | 249 ++++++++++++++++++
1 file changed, 249 insertions(+)
create mode 100644 tests/framework/db/sqlite/type/BooleanTest.php
diff --git a/tests/framework/db/sqlite/type/BooleanTest.php b/tests/framework/db/sqlite/type/BooleanTest.php
new file mode 100644
index 0000000000..c5bfd811c1
--- /dev/null
+++ b/tests/framework/db/sqlite/type/BooleanTest.php
@@ -0,0 +1,249 @@
+getConnection(true);
+ $schema = $db->getSchema();
+ $tableName = '{{%boolean}}';
+
+ if ($db->getTableSchema($tableName)) {
+ $db->createCommand()->dropTable($tableName)->execute();
+ }
+
+ $db->createCommand()->createTable(
+ $tableName,
+ [
+ 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK),
+ 'bool_col_tinyint' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN),
+ 'bool_col_bit' => $schema->createColumnSchemaBuilder('bit', 1),
+ ]
+ )->execute();
+
+ // test type `boolean`
+ $columnBoolColTinyint = $db->getTableSchema($tableName)->getColumn('bool_col_tinyint');
+ $this->assertSame('boolean', $columnBoolColTinyint->phpType);
+
+ $columnBoolColBit = $db->getTableSchema($tableName)->getColumn('bool_col_bit');
+ $this->assertSame('boolean', $columnBoolColBit->phpType);
+
+ // test value `false`
+ $db->createCommand()->insert($tableName, ['bool_col_tinyint' => false, 'bool_col_bit' => false])->execute();
+ $boolValues = $db->createCommand("SELECT * FROM $tableName WHERE id = 1")->queryOne();
+ $this->assertEquals(0, $boolValues['bool_col_tinyint']);
+ $this->assertEquals(0, $boolValues['bool_col_bit']);
+
+ // test php typecast
+ $phpTypeCastBoolColTinyint = $columnBoolColTinyint->phpTypecast($boolValues['bool_col_tinyint']);
+ $this->assertFalse($phpTypeCastBoolColTinyint);
+
+ $phpTypeCastBoolColBit = $columnBoolColBit->phpTypecast($boolValues['bool_col_bit']);
+ $this->assertFalse($phpTypeCastBoolColBit);
+
+ // test value `true`
+ $db->createCommand()->insert($tableName, ['bool_col_tinyint' => true, 'bool_col_bit' => true])->execute();
+ $boolValues = $db->createCommand("SELECT * FROM $tableName WHERE id = 2")->queryOne();
+ $this->assertEquals(1, $boolValues['bool_col_tinyint']);
+ $this->assertEquals(1, $boolValues['bool_col_bit']);
+
+ // test php typecast
+ $phpTypeCastBoolColTinyint = $columnBoolColTinyint->phpTypecast($boolValues['bool_col_tinyint']);
+ $this->assertTrue($phpTypeCastBoolColTinyint);
+
+ $phpTypeCastBoolColBit = $columnBoolColBit->phpTypecast($boolValues['bool_col_bit']);
+ $this->assertTrue($phpTypeCastBoolColBit);
+ }
+
+ public function testBooleanWithValueInteger()
+ {
+ $db = $this->getConnection(true);
+ $schema = $db->getSchema();
+ $tableName = '{{%boolean}}';
+
+ if ($db->getTableSchema($tableName)) {
+ $db->createCommand()->dropTable($tableName)->execute();
+ }
+
+ $db->createCommand()->createTable(
+ $tableName,
+ [
+ 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK),
+ 'bool_col_tinyint' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN),
+ 'bool_col_bit' => $schema->createColumnSchemaBuilder('bit', 1),
+ ]
+ )->execute();
+
+ // test type `boolean`
+ $columnBoolColTinyint = $db->getTableSchema($tableName)->getColumn('bool_col_tinyint');
+ $this->assertSame('boolean', $columnBoolColTinyint->phpType);
+
+ $columnBoolColBit = $db->getTableSchema($tableName)->getColumn('bool_col_bit');
+ $this->assertSame('boolean', $columnBoolColBit->phpType);
+
+ // test value `0`
+ $db->createCommand()->insert($tableName, ['bool_col_tinyint' => 0, 'bool_col_bit' => 0])->execute();
+ $boolValues = $db->createCommand("SELECT * FROM $tableName WHERE id = 1")->queryOne();
+ $this->assertEquals(0, $boolValues['bool_col_tinyint']);
+ $this->assertEquals(0, $boolValues['bool_col_bit']);
+
+ // test php typecast
+ $phpTypeCastBoolColTinyint = $columnBoolColTinyint->phpTypecast($boolValues['bool_col_tinyint']);
+ $this->assertFalse($phpTypeCastBoolColTinyint);
+
+ $phpTypeCastBoolColBit = $columnBoolColBit->phpTypecast($boolValues['bool_col_bit']);
+ $this->assertFalse($phpTypeCastBoolColBit);
+
+ // test value `1`
+ $db->createCommand()->insert($tableName, ['bool_col_tinyint' => 1, 'bool_col_bit' => 1])->execute();
+ $boolValues = $db->createCommand("SELECT * FROM $tableName WHERE id = 2")->queryOne();
+ $this->assertEquals(1, $boolValues['bool_col_tinyint']);
+ $this->assertEquals(1, $boolValues['bool_col_bit']);
+
+ // test php typecast
+ $phpTypeCastBoolColTinyint = $columnBoolColTinyint->phpTypecast($boolValues['bool_col_tinyint']);
+ $this->assertTrue($phpTypeCastBoolColTinyint);
+
+ $phpTypeCastBoolColBit = $columnBoolColBit->phpTypecast($boolValues['bool_col_bit']);
+ $this->assertTrue($phpTypeCastBoolColBit);
+ }
+
+ public function testBooleanWithValueNegative()
+ {
+ $db = $this->getConnection(true);
+ $schema = $db->getSchema();
+ $tableName = '{{%boolean}}';
+
+ if ($db->getTableSchema($tableName)) {
+ $db->createCommand()->dropTable($tableName)->execute();
+ }
+
+ $db->createCommand()->createTable(
+ $tableName,
+ [
+ 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK),
+ 'bool_col_tinyint' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN),
+ 'bool_col_bit' => $schema->createColumnSchemaBuilder('bit', 1),
+ ]
+ )->execute();
+
+ // test type `boolean`
+ $columnBoolColTinyint = $db->getTableSchema($tableName)->getColumn('bool_col_tinyint');
+ $this->assertSame('boolean', $columnBoolColTinyint->phpType);
+
+ $columnBoolColBit = $db->getTableSchema($tableName)->getColumn('bool_col_bit');
+ $this->assertSame('boolean', $columnBoolColBit->phpType);
+
+ // test value `-1`
+ $db->createCommand()->insert($tableName, ['bool_col_tinyint' => -1, 'bool_col_bit' => -1])->execute();
+ $boolValues = $db->createCommand("SELECT * FROM $tableName WHERE id = 1")->queryOne();
+
+ $this->assertEquals(1, $boolValues['bool_col_tinyint']);
+ $this->assertEquals(1, $boolValues['bool_col_bit']);
+
+ // test php typecast
+ $phpTypeCastBoolColTinyint = $columnBoolColTinyint->phpTypecast($boolValues['bool_col_tinyint']);
+ $this->assertTrue($phpTypeCastBoolColTinyint);
+
+ $phpTypeCastBoolColBit = $columnBoolColBit->phpTypecast($boolValues['bool_col_bit']);
+ $this->assertTrue($phpTypeCastBoolColBit);
+ }
+
+ public function testBooleanWithValueNull()
+ {
+ $db = $this->getConnection(true);
+ $schema = $db->getSchema();
+ $tableName = '{{%boolean}}';
+
+ if ($db->getTableSchema($tableName)) {
+ $db->createCommand()->dropTable($tableName)->execute();
+ }
+
+ $db->createCommand()->createTable(
+ $tableName,
+ [
+ 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK),
+ 'bool_col_tinyint' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN),
+ 'bool_col_bit' => $schema->createColumnSchemaBuilder('bit', 1),
+ ]
+ )->execute();
+
+ // test type `boolean`
+ $columnBoolColTinyint = $db->getTableSchema($tableName)->getColumn('bool_col_tinyint');
+ $this->assertSame('boolean', $columnBoolColTinyint->phpType);
+
+ $columnBoolColBit = $db->getTableSchema($tableName)->getColumn('bool_col_bit');
+ $this->assertSame('boolean', $columnBoolColBit->phpType);
+
+ // test value `null`
+ $db->createCommand()->insert($tableName, ['bool_col_tinyint' => null, 'bool_col_bit' => null])->execute();
+ $boolValues = $db->createCommand("SELECT * FROM $tableName WHERE id = 1")->queryOne();
+
+ $this->assertNull($boolValues['bool_col_tinyint']);
+ $this->assertNull($boolValues['bool_col_bit']);
+
+ // test php typecast
+ $phpTypeCastBoolColTinyint = $columnBoolColTinyint->phpTypecast($boolValues['bool_col_tinyint']);
+ $this->assertNull($phpTypeCastBoolColTinyint);
+
+ $phpTypeCastBoolColBit = $columnBoolColBit->phpTypecast($boolValues['bool_col_bit']);
+ $this->assertNull($phpTypeCastBoolColBit);
+ }
+
+ public function testBooleanWithValueOverflow()
+ {
+ $db = $this->getConnection(true);
+ $schema = $db->getSchema();
+ $tableName = '{{%boolean}}';
+
+ if ($db->getTableSchema($tableName)) {
+ $db->createCommand()->dropTable($tableName)->execute();
+ }
+
+ $db->createCommand()->createTable(
+ $tableName,
+ [
+ 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK),
+ 'bool_col_tinyint' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN),
+ 'bool_col_bit' => $schema->createColumnSchemaBuilder('bit', 1),
+ ]
+ )->execute();
+
+ // test type `boolean`
+ $columnBoolColTinyint = $db->getTableSchema($tableName)->getColumn('bool_col_tinyint');
+ $this->assertSame('boolean', $columnBoolColTinyint->phpType);
+
+ $columnBoolColBit = $db->getTableSchema($tableName)->getColumn('bool_col_bit');
+ $this->assertSame('boolean', $columnBoolColBit->phpType);
+
+ // test value `2`
+ $db->createCommand()->insert($tableName, ['bool_col_tinyint' => 2, 'bool_col_bit' => 2])->execute();
+ $boolValues = $db->createCommand("SELECT * FROM $tableName WHERE id = 1")->queryOne();
+
+ $this->assertEquals(1, $boolValues['bool_col_tinyint']);
+ $this->assertEquals(1, $boolValues['bool_col_bit']);
+
+ // test php typecast
+ $phpTypeCastBoolColTinyint = $columnBoolColTinyint->phpTypecast($boolValues['bool_col_tinyint']);
+ $this->assertTrue($phpTypeCastBoolColTinyint);
+
+ $phpTypeCastBoolColBit = $columnBoolColBit->phpTypecast($boolValues['bool_col_bit']);
+ $this->assertTrue($phpTypeCastBoolColBit);
+ }
+}
From f3c1d0cba7a83100a97a6728becbd4a9de46313a Mon Sep 17 00:00:00 2001
From: Wilmer Arambula
Date: Thu, 26 Oct 2023 13:07:23 -0300
Subject: [PATCH 14/20] Add test for boolean type `PostgreSQL`.
---
tests/framework/db/pgsql/type/BooleanTest.php | 239 ++++++++++++++++++
1 file changed, 239 insertions(+)
create mode 100644 tests/framework/db/pgsql/type/BooleanTest.php
diff --git a/tests/framework/db/pgsql/type/BooleanTest.php b/tests/framework/db/pgsql/type/BooleanTest.php
new file mode 100644
index 0000000000..4a9b21df24
--- /dev/null
+++ b/tests/framework/db/pgsql/type/BooleanTest.php
@@ -0,0 +1,239 @@
+getConnection(true);
+ $schema = $db->getSchema();
+ $tableName = '{{%boolean}}';
+
+ if ($db->getTableSchema($tableName)) {
+ $db->createCommand()->dropTable($tableName)->execute();
+ }
+
+ $db->createCommand()->createTable(
+ $tableName,
+ [
+ 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK),
+ 'bool_col' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN),
+ ]
+ )->execute();
+
+ // test type `boolean`
+ $column = $db->getTableSchema($tableName)->getColumn('bool_col');
+ $this->assertSame('boolean', $column->phpType);
+
+ // test value `false`
+ $db->createCommand()->insert($tableName, ['bool_col' => false])->execute();
+ $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 1")->queryScalar();
+ $this->assertEquals(0, $boolValue);
+
+ // test php typecast
+ $phpTypeCast = $column->phpTypecast($boolValue);
+ $this->assertFalse($phpTypeCast);
+
+ // test value `true`
+ $db->createCommand()->insert($tableName, ['bool_col' => true])->execute();
+ $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 2")->queryScalar();
+ $this->assertEquals(1, $boolValue);
+
+ // test php typecast
+ $phpTypeCast = $column->phpTypecast($boolValue);
+ $this->assertTrue($phpTypeCast);
+ }
+
+ public function testBooleanWithValueInteger()
+ {
+ $db = $this->getConnection(true);
+ $schema = $db->getSchema();
+ $tableName = '{{%boolean}}';
+
+ if ($db->getTableSchema($tableName)) {
+ $db->createCommand()->dropTable($tableName)->execute();
+ }
+
+ $db->createCommand()->createTable(
+ $tableName,
+ [
+ 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK),
+ 'bool_col' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN),
+ ]
+ )->execute();
+
+ // test type `boolean`
+ $column = $db->getTableSchema($tableName)->getColumn('bool_col');
+ $this->assertSame('boolean', $column->phpType);
+
+ // test value `0`
+ $db->createCommand()->insert($tableName, ['bool_col' => 0])->execute();
+ $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 1")->queryScalar();
+ $this->assertEquals(0, $boolValue);
+
+ // test php typecast
+ $phpTypeCast = $column->phpTypecast($boolValue);
+ $this->assertFalse($phpTypeCast);
+
+ // test value `1`
+ $db->createCommand()->insert($tableName, ['bool_col' => 1])->execute();
+ $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 2")->queryScalar();
+ $this->assertEquals(1, $boolValue);
+
+ // test php typecast
+ $phpTypeCast = $column->phpTypecast($boolValue);
+ $this->assertTrue($phpTypeCast);
+ }
+
+ public function testBooleanWithValueNegative()
+ {
+ $db = $this->getConnection(true);
+ $schema = $db->getSchema();
+ $tableName = '{{%boolean}}';
+
+ if ($db->getTableSchema($tableName)) {
+ $db->createCommand()->dropTable($tableName)->execute();
+ }
+
+ $db->createCommand()->createTable(
+ $tableName,
+ [
+ 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK),
+ 'bool_col' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN),
+ ]
+ )->execute();
+
+ // test type `boolean`
+ $column = $db->getTableSchema($tableName)->getColumn('bool_col');
+ $this->assertSame('boolean', $column->phpType);
+
+ // test value `-1`
+ $db->createCommand()->insert($tableName, ['bool_col' => '-1'])->execute();
+ $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 1")->queryScalar();
+ $this->assertEquals(1, $boolValue);
+
+ // test php typecast
+ $phpTypeCast = $column->phpTypecast($boolValue);
+ $this->assertTrue($phpTypeCast);
+ }
+
+ public function testBooleanWithValueNull()
+ {
+ $db = $this->getConnection(true);
+ $schema = $db->getSchema();
+ $tableName = '{{%boolean}}';
+
+ if ($db->getTableSchema($tableName)) {
+ $db->createCommand()->dropTable($tableName)->execute();
+ }
+
+ $db->createCommand()->createTable(
+ $tableName,
+ [
+ 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK),
+ 'bool_col' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN),
+ ]
+ )->execute();
+
+ // test type `boolean`
+ $column = $db->getTableSchema($tableName)->getColumn('bool_col');
+ $this->assertSame('boolean', $column->phpType);
+
+ // test value `null`
+ $db->createCommand()->insert($tableName, ['bool_col' => null])->execute();
+ $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 1")->queryScalar();
+ $this->assertNull($boolValue);
+
+ // test php typecast
+ $phpTypeCast = $column->phpTypecast($boolValue);
+ $this->assertNull($phpTypeCast);
+ }
+
+ public function testBooleanWithValueOverflow()
+ {
+ $db = $this->getConnection(true);
+ $schema = $db->getSchema();
+ $tableName = '{{%boolean}}';
+
+ if ($db->getTableSchema($tableName)) {
+ $db->createCommand()->dropTable($tableName)->execute();
+ }
+
+ $db->createCommand()->createTable(
+ $tableName,
+ [
+ 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK),
+ 'bool_col' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN),
+ ]
+ )->execute();
+
+ // test type `boolean`
+ $column = $db->getTableSchema($tableName)->getColumn('bool_col');
+ $this->assertSame('boolean', $column->phpType);
+
+ // test value `2`
+ $db->createCommand()->insert($tableName, ['bool_col' => 2])->execute();
+ $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 1")->queryScalar();
+ $this->assertEquals(1, $boolValue);
+
+ // test php typecast
+ $phpTypeCast = $column->phpTypecast($boolValue);
+ $this->assertTrue($phpTypeCast);
+ }
+
+ public function testBooleanWithValueString()
+ {
+ $db = $this->getConnection(true);
+ $schema = $db->getSchema();
+ $tableName = '{{%boolean}}';
+
+ if ($db->getTableSchema($tableName)) {
+ $db->createCommand()->dropTable($tableName)->execute();
+ }
+
+ $db->createCommand()->createTable(
+ $tableName,
+ [
+ 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK),
+ 'bool_col' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN),
+ ]
+ )->execute();
+
+ // test type `boolean`
+ $column = $db->getTableSchema($tableName)->getColumn('bool_col');
+ $this->assertSame('boolean', $column->phpType);
+
+ // test value `0`
+ $db->createCommand()->insert($tableName, ['bool_col' => '0'])->execute();
+ $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 1")->queryScalar();
+ $this->assertEquals(0, $boolValue);
+
+ // test php typecast
+ $phpTypeCast = $column->phpTypecast($boolValue);
+ $this->assertFalse($phpTypeCast);
+
+ // test value `1`
+ $db->createCommand()->insert($tableName, ['bool_col' => '1'])->execute();
+ $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 2")->queryScalar();
+ $this->assertEquals(1, $boolValue);
+
+ // test php typecast
+ $phpTypeCast = $column->phpTypecast($boolValue);
+ $this->assertTrue($phpTypeCast);
+ }
+}
From cb8307fad94d971d2012edf865d04490d10026dd Mon Sep 17 00:00:00 2001
From: Saleh Hashemi <81674631+salehhashemi1992@users.noreply.github.com>
Date: Fri, 27 Oct 2023 22:23:07 +0330
Subject: [PATCH 15/20] Fix various linguistic and stylistic issues in Persian
core-code-style file (#20054)
---
docs/internals-fa/core-code-style.md | 236 ++++++++++++++-------------
1 file changed, 119 insertions(+), 117 deletions(-)
diff --git a/docs/internals-fa/core-code-style.md b/docs/internals-fa/core-code-style.md
index e63e2b7795..3becc5802b 100644
--- a/docs/internals-fa/core-code-style.md
+++ b/docs/internals-fa/core-code-style.md
@@ -1,49 +1,52 @@
رعایت اصول و سبک کدنویسی فریمورک Yii2
===============================
-
-سبک کدنویسی که در نسخه 2 فریمورک و extension های رسمی استفاده میشه دارای اصول، قواعد و قانون های خودش هست. پس اگر تصمیم دارید چیزی به هسته اضافه کنید باید این قواعد رو در نظر بگیرید حتی در غیر این صورت هم رعایت این موارد خالی از لطف نیست و توصیه میکنم این کارُ انجام بدین. در حالی که میتونید راحت باشید، شما مجبور به رعایت این سبک در application خودتون نیستید...
+
+سبک کدنویسی که در نسخه 2 فریمورک و extension های رسمی استفاده میشه دارای اصول، قواعد و قانون های خودش هست. پس اگر تصمیم دارید چیزی به هسته اضافه کنید باید این قواعد رو در نظر بگیرید حتی در غیر این صورت هم رعایت این موارد خالی از لطف نیست و توصیه میکنم این کارو انجام بدین.
+
+البته که نیاز نیست حتما این موارد رو در برنامههای خودتون رعایت کنید و می تونید در این مورد راحت باشید...
-
-میتونید برای دریافت پیکره بندی CodeSniffer اینجا رو مطالعه کنید: https://github.com/yiisoft/yii2-coding-standards
+
+میتونید برای دریافت پیکربندی CodeSniffer اینجا رو مطالعه کنید: https://github.com/yiisoft/yii2-coding-standards
## 1. نگاه کلی
-
-به طور کلی ما از سبک PSR-2 استفاده میکنیم و هر چیزی که در این سبک وجود داره اینجا هم هست.
-(https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)
+
+به طور کلی ما از سبک کدنویسی PSR-2 پیروی میکنیم:
-
در فایل ها باید از برچسب های php?> و =?> استفاده شود.
- در پایان هر فایل باید یک خط جدید(newline) داشته باشید.
- encoding فایل برای کد های php باید UTF-8 without BOM باشد.
- به جای tab از 4 فضای خالی(space) استفاده کنید.
- نام کلاس ها باید به صورت StudlyCaps تعریف شوند.
- ثابت های داخل کلاس تماما باید با حروف بزرگ و گاهی با جداکننده "_" تعریف شوند.
- نام متد ها و پراپرتی ها باید به صورت camelCase تعریف شوند.
- پراپرتی های خصوصی(private) باید با "_" شروع شوند.
- همیشه از elseif جای else if استفاده کنید.
+https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md
-## 2. فایل ها
+ در فایلها باید از برچسبهای php?> و =?> استفاده شود.
+ در پایان هر فایل باید یک خط جدید (newline) داشته باشید.
+ encoding فایل برای کدهای php باید UTF-8 without BOM باشد.
+ به جای tab از 4 فضای خالی (space) استفاده کنید.
+ نام کلاسها باید به صورت StudlyCaps تعریف شوند.
+ ثابتهای داخل کلاس تماما باید با حروف بزرگ و گاهی با جداکننده "_" تعریف شوند.
+ نام متدها و پراپرتیها باید به صورت camelCase تعریف شوند.
+ پراپرتیهای خصوصی (private) باید با "_" شروع شوند.
+ همیشه از elseif جای else if استفاده کنید.
- در فایل ها باید از برچسب های php?> و =?> استفاده کرد نه از ?> .
- در انتهای فایل های php نباید از تگ استفاده کنید.
- در انتهای هر خط نباید space وجود داشته باشد
- پسوند فایل هایی که شامل کد php هستند باید php. باشد.
- encoding فایل برای کد های php باید UTF-8 without BOM باشد.
+## 2. فایلها
+
+ در فایلها باید از برچسب های php?> و =?> استفاده کرد نه از ?> .
+ در انتهای فایلهای php نباید از تگ ?> استفاده کنید.
+ در انتهای هر خط نباید space وجود داشته باشد.
+ پسوند فایلهایی که شامل کد php هستند باید php. باشد.
+ encoding فایل برای کدهای php باید UTF-8 without BOM باشد.
-## 3. نام کلاس ها
-
-نام کلاس ها باید به صورت StudlyCaps تعریف شوند. به عنوان مثال, `Controller`, `Model`.
+## 3. نام کلاسها
+
+نام کلاسها باید به صورت StudlyCaps تعریف شوند. به عنوان مثال، `Controller` و `Model`.
-## 4. کلاس ها
+## 4. کلاسها
- نام کلاس ها باید به صورت CamelCase تعریف شوند.
- آکولاد باز باید در خط بعدی، زیر نام کلاس نوشته شود.
- تمام کلاس ها باید بلاک مستندات مطابق استاندارد PHPDoc داشته باشند.
- برای تمام کد های داخل کلاس باید با 4 space فاصله ایجاد کنید.
- فقط یک کلاس داخل هر فایل php باید موجود باشد.
- تمام کلاس ها باید namespaced داشته باشند.
- نام کلاس باید معال نام فایل و namespace باید مطابق مسیر آن باشد.
+ نام کلاسها باید به صورت CamelCase تعریف شوند.
+ آکولاد باز باید در خط بعدی، زیر نام کلاس نوشته شود.
+ تمام کلاسها باید بلاک مستندات مطابق استاندارد PHPDoc داشته باشند.
+ برای تمام کدهای داخل کلاس باید با 4 space فاصله ایجاد کنید.
+ فقط یک کلاس داخل هر فایل php باید موجود باشد.
+ تمام کلاسها باید namespaced داشته باشند.
+ نام کلاس باید معادل نام فایل و namespace باید مطابق مسیر آن باشد.
```php
/**
@@ -55,9 +58,9 @@ class MyClass extends \yii\base\BaseObject implements MyInterface
}
```
-### 4.1. ثابت ها
-
-ثابت های داخل کلاس تماما باید با حروف بزرگ و گاهی با جداکننده "_" تعریف شوند.
+### 4.1. ثابتها
+
+ثابتهای داخل کلاس تماما باید با حروف بزرگ و گاهی با جداکننده "_" تعریف شوند.
```php
از کلید واژه های public، protected و private استفاده کنید.
- پراپرتی های public و protected باید در بالای کلاس و قبل از متد ها تعریف شوند. private هم همینطور اما ممکن هست کاهی قبل از متدی که با آن مرتبط هست آورده شود.
- ترتیب تعریف پراپرتی ها باید به صورت اول public، دوم protected و سپس private باشد! هیچ قانون سختی برای رعایت این مورد نیست...
- برای خوانایی بهتر میتونید از خط خالی بین گروه های public، protected و private استفاده کنید.
-
متغییر های private باید مثل varName_$ باشند.
- اعضای عمومی داخل کلاس باید به صورت camelCase تعریف شوند.(حرف اول کوچک، با CamelCase فرق میکنه).
- بهتره از نام هایی مثل i$ و j$ استفاده نکنید.
+ از کلید واژه های public ،protected و private استفاده کنید.
+ پراپرتیهای public و protected باید در بالای کلاس و قبل از متدها تعریف شوند. private هم همینطور اما ممکن هست گاهی قبل از متدی که با آن مرتبط هست آورده شود.
+ ترتیب تعریف پراپرتیها باید به صورت اول public، دوم protected و سپس private باشد! کار بسیار سادهایست :)
+ برای خوانایی بهتر میتونید از خط خالی بین گروههای public، protected و private استفاده کنید.
+
متغیر های private باید مثل varName_$ باشند.
+ اعضای عمومی داخل کلاس باید به صورت camelCase تعریف شوند. (حرف اول کوچک، با CamelCase فرق میکنه)
+ بهتره از نامهایی مثل i$ و j$ استفاده نکنید.
```php
توابع و متد ها باید camelCase باشند.
- نام باید هدف رو نشون بده.
- از کلید واژه های public، protected و private استفاده کنید.
- آکولاد باز باید در خط بعدی یعنی زیر نام متد قرار بگیره.
+ توابع و متدها باید camelCase باشند.
+ نام باید هدف رو نشون بده.
+ از کلید واژه های public، protected و private استفاده کنید.
+ آکولاد باز باید در خط بعدی یعنی زیر نام متد قرار بگیره.
```php
/**
@@ -120,14 +123,14 @@ class Foo
}
```
-### 4.4 بلوک های PHPDoc
+### 4.4 بلوکهای PHPDoc
- برای متد ها باید مستندات بنویسید(PHPDoc).
- در PHPDoc نوع param@, var@, property@ و return@ باید مشخص شود(bool, int, string, array یا null).
- برای تایپ آرایه در PHPDoc از []ClassName استفاده کنید.
- خط اول PHPDoc باید هدف یک متد رو شرح بده.
- اگر متد ها چیزی رو بررسی میکنن مثل isActive بخش PHPDoc رو باید با عبارت Checks whether شروع کنید.
- return@ در PHPDoc یاید دقیقا مشخص کنه چی بازگردانده میشود.
+ برای متدها باید مستندات بنویسید (PHPDoc).
+ در PHPDoc نوع param@ ،var@ ،property@ و return@ باید مشخص شود (bool, int, string, array یا null).
+ برای تایپ آرایه در PHPDoc از []ClassName استفاده کنید.
+ خط اول PHPDoc باید هدف یک متد رو شرح بده.
+ اگر متدها چیزی رو بررسی میکنن مثل isActive بخش PHPDoc رو باید با عبارت Checks whether شروع کنید.
+ return@ در PHPDoc یاید دقیقا مشخص کنه چی بازگردانده میشه.
```php
/**
@@ -146,14 +149,14 @@ class Foo
### 4.5 Constructors
- `__construct` باید به جای استایل PHP 4 constructors استفاده شود.
+ `__construct` باید به جای استایل PHP 4 constructors استفاده شود.
## 5 PHP
-### 5.1 نوع ها
+### 5.1 نوعها
- تمام انواع و مقادیر باید با حروف کوچک نوشته شوند مثل true, false, null و array.
- تغییر نوع یک متغییر خیلی بده، به این مثال توجه کنید:
+ تمام انواع و مقادیر باید با حروف کوچک نوشته شوند مثل true ،false ،null و array.
+ تغییر نوع یک متغیر خیلی بده، به این مثال توجه کنید:
```php
@@ -164,22 +167,22 @@ public function save(Transaction $transaction, $argument2 = 100)
}
```
-### 5.2 رشته ها
+### 5.2 رشتهها
- اگر رشته ی شما شامل متغییر های دیگه این نیست از تک کوتیشن جای دابل کوتیشن استفاده کنید.
+ اگر رشتهی شما شامل متغیرهای دیگهای نیست از تککوتیشن جای دابلکوتیشن استفاده کنید.
```php
$str = 'Like this.';
```
- دو روش زیر مناسب برای جایگزینی هستند:
+ دو روش زیر مناسب برای جایگزینی هستند:
```php
$str1 = "Hello $username!";
$str2 = "Hello {$username}!";
```
-
-حالت زی مجاز نیست:
+
+حالت زیر مجاز نیست:
```php
$str3 = "Hello ${username}!";
@@ -187,13 +190,13 @@ $str3 = "Hello ${username}!";
#### الحاق
- برای الحاق قبل و بعد کاراکتر dot فاصله بذارید
+ برای الحاق قبل و بعد کاراکتر dot فاصله بذارید:
```php
$name = 'Yii' . ' Framework';
```
- و اگر رشته ی شما بلند بود میتونید اینطور عمل کنید:
+ و اگر رشته ی شما بلند بود میتونید اینطور عمل کنید:
```php
$sql = "SELECT *"
@@ -201,11 +204,11 @@ $sql = "SELECT *"
. "WHERE `id` = 121 ";
```
-### 5.3 آرایه ها
+### 5.3 آرایهها
- برای تعریف آرایه ها از نحوه ی کوتاه اون یعنی [] استفاده کنید.
- از ایندکس منفی در آرایه ها استفاده نکنید.
- روش های زیر قابل قبول و مناسب هستند:
+ برای تعریف آرایهها از ساختار کوتاه اون یعنی [] استفاده کنید.
+ از ایندکس منفی در آرایهها استفاده نکنید.
+ روشهای زیر قابل قبول و مناسب هستند:
```php
$arr = [3, 14, 15, 'Yii', 'Framework'];
@@ -228,10 +231,10 @@ $config = [
### 5.4 دستورات کنترلی
- در دستورات کنترلی قبل و بعد پرانتز space بذارید.
- آکولاد باز در همان خط دستور قرار میگیرد.
- آکولاد بسته در خط جدید.
- برای دستورات یک خطی همیشه از پرانتز استفاده کنید.
+ در دستورات کنترلی قبل و بعد پرانتز space بذارید.
+ آکولاد باز در همان خط دستور قرار میگیرد.
+ آکولاد بسته در خط جدید.
+ برای دستورات یک خطی همیشه از پرانتز استفاده کنید.
```php
if ($event === null) {
@@ -248,7 +251,7 @@ if (!$model && null === $event)
throw new Exception('test');
```
-بعد از return از else استفاده نکنید
+بعد از return از else استفاده نکنید:
```php
$result = $this->getResult();
@@ -258,7 +261,7 @@ if (empty($result)) {
// process result
}
```
-اینطوری بهتره
+اینطوری بهتره:
```php
@@ -272,8 +275,8 @@ if (empty($result)) {
#### switch
- از فرمت زیر برای switch استفاده کنید
-
+ از فرمت زیر برای switch استفاده کنید:
+
```php
switch ($this->phpType) {
case 'string':
@@ -291,9 +294,9 @@ switch ($this->phpType) {
}
```
-### 5.5 function calls
+### 5.5 صدا زدن فانکشنها
-روش مناسب صدا زدن توابع همراه با پارامتر ها هم اینطور صحیحه
+روش مناسب صدا زدن فانکشنها همراه با پارامتر هم به این صورته:
```php
doIt(2, 3);
@@ -308,7 +311,7 @@ doIt('a', [
### 5.6 تعریف Anonymous functions (lambda)
- در توابع بی نام بین function/use فضای خالی(space) بذارید.
+ در توابع بی نام بین function/use فضای خالی (space) بذارید:
```php
// good
@@ -328,14 +331,14 @@ $mul = array_reduce($numbers, function($r, $x) use($n) {
});
```
-مستند نویسی
+مستند نویسی
-------------
- [phpDoc](https://phpdoc.org/) رو بخونید و موارد اونُ رعایت کنید.
- کد بدون مستندات مجاز نیست.
- تمام کلاس ها باید شامل بلاک مستندات در ابتدای فایل باشند.
- نیازی به نوشتن return@ ندارید اگر متد شما اگر چیزی را برنمیگرداند.
- به مثال های زیر توجه کنید:
+ https://phpdoc.org رو بخونید و موارد اون رو رعایت کنید.
+ کد بدون مستندات مجاز نیست.
+ تمام کلاسها باید شامل بلاک مستندات در ابتدای فایل باشند.
+ نیازی به نوشتن return@ ندارید اگر متد شما چیزی برنمیگرداند.
+ به مثالهای زیر توجه کنید:
```php
getEventHandlers($eventName)->insertAt(0, $eventHandler);
- * ```
- *
- * @param string $name the event name
- * @return Vector list of attached event handlers for the event
- * @throws Exception if the event is not defined
- */
-public function getEventHandlers($name)
-{
- if (!isset($this->_e[$name])) {
- $this->_e[$name] = new Vector;
- }
- $this->ensureBehaviors();
- return $this->_e[$name];
-}
+* $component->getEventHandlers($eventName)->insertAt(0, $eventHandler);
+* ```
+*
+* @param string $name the event name
+* @return Vector list of attached event handlers for the event
+* @throws Exception if the event is not defined
+ */
+ public function getEventHandlers($name)
+ {
+ if (!isset($this->_e[$name])) {
+ $this->_e[$name] = new Vector;
+ }
+ $this->ensureBehaviors();
+ return $this->_e[$name];
+ }
```
#### نظرات
- از // برای کامنت گذاری استفاده کنید نه از #.
- در خطوطی که کامنت گذاشتین نباید کد بنویسید، یعنی اون خط برای اون کامنت باید باشه.
+ از // برای کامنت گذاری استفاده کنید نه از #.
+ در خطوطی که کامنت گذاشتین نباید کد بنویسید، یعنی اون خط برای اون کامنت باید باشه.
قوانین بیشتر
----------------
- تا جایی که میتونید از تابع empty به جای === استفاده کنید.
- اگر شرایط تو در تویی در کد شما وجود نداره return زود هنگام یا ساده تر بگم return وسط متد مشکلی نخواهد داشت.
- همیشه از static جای self به جز موارد زیر استفاده کنید:
-1) دسترسی به ثابت ها باید با self انجام بشه.
-2) دسترسی به پراپرتی های خصوصی باید با self انجام بشه.
-3) مجاز به استفاده از self برای صدا زدن توابع در مواقعی مثل فراخوانی بازگشتی هستید.
+ تا جایی که میتونید از تابع empty به جای === استفاده کنید.
+ اگر شرایط تو در تویی در کد شما وجود نداره return زود هنگام یا ساده تر بگم return وسط متد مشکلی نخواهد داشت.
+ همیشه از static جای self به جز موارد زیر استفاده کنید:
+1) دسترسی به ثابتها باید با self انجام بشه.
+2) دسترسی به پراپرتیهای خصوصی باید با self انجام بشه.
+3) مجاز به استفاده از self برای صدا زدن توابع در مواقعی مثل فراخوانی بازگشتی هستید.
-namespace ها
+نیماسپیسها
----------------
- از حرف کوچک استفاده کنید.
- از فرم جمع اسم ها برای نشان دادن یک شی استفاده کنید مثل validators.
- از فرم منفرد اسم ها برای قابلیت ها و امکانات استفاده کنید مثل web.
- بهتره فضای نام تک کلمه ای باشه در غیر این صورت از camelCase استفاده کنید.
-
+ از حرف کوچک استفاده کنید.
+ از فرم جمع اسمها برای نشان دادن یک شی استفاده کنید مثل validators.
+ از فرم مفرد اسمها برای قابلیتها و امکانات استفاده کنید مثل web.
+ بهتره فضای نام تککلمهای باشه در غیر این صورت از camelCase استفاده کنید.
From bd452da4d2bc6636e3eabeef817b3917ae0b293b Mon Sep 17 00:00:00 2001
From: Saleh Hashemi <81674631+salehhashemi1992@users.noreply.github.com>
Date: Fri, 27 Oct 2023 22:26:06 +0330
Subject: [PATCH 16/20] Add tests for uncovered lines in DbDependency (#20052)
---
tests/framework/caching/DbDependencyTest.php | 41 +++++++++++++++++++-
1 file changed, 39 insertions(+), 2 deletions(-)
diff --git a/tests/framework/caching/DbDependencyTest.php b/tests/framework/caching/DbDependencyTest.php
index bd801e3528..bb723be3a4 100644
--- a/tests/framework/caching/DbDependencyTest.php
+++ b/tests/framework/caching/DbDependencyTest.php
@@ -21,7 +21,6 @@ class DbDependencyTest extends DatabaseTestCase
*/
protected $driverName = 'sqlite';
-
/**
* {@inheritdoc}
*/
@@ -39,11 +38,14 @@ class DbDependencyTest extends DatabaseTestCase
$db->createCommand()->insert('dependency_item', ['value' => 'initial'])->execute();
}
- public function testIsChanged()
+ public function testQueryOneIsExecutedWhenQueryCacheEnabled()
{
$db = $this->getConnection(false);
$cache = new ArrayCache();
+ // Enable the query cache
+ $db->enableQueryCache = true;
+
$dependency = new DbDependency();
$dependency->db = $db;
$dependency->sql = 'SELECT [[id]] FROM {{dependency_item}} ORDER BY [[id]] DESC LIMIT 1';
@@ -56,4 +58,39 @@ class DbDependencyTest extends DatabaseTestCase
$this->assertTrue($dependency->isChanged($cache));
}
+
+ public function testQueryOneIsExecutedWhenQueryCacheDisabled()
+ {
+ $db = $this->getConnection(false);
+ $cache = new ArrayCache();
+
+ // Disable the query cache
+ $db->enableQueryCache = false;
+
+ $dependency = new DbDependency();
+ $dependency->db = $db;
+ $dependency->sql = 'SELECT [[id]] FROM {{dependency_item}} ORDER BY [[id]] DESC LIMIT 1';
+ $dependency->reusable = false;
+
+ $dependency->evaluateDependency($cache);
+ $this->assertFalse($dependency->isChanged($cache));
+
+ $db->createCommand()->insert('dependency_item', ['value' => 'new'])->execute();
+
+ $this->assertTrue($dependency->isChanged($cache));
+ }
+
+ public function testMissingSqlThrowsException()
+ {
+ $this->expectException('\yii\base\InvalidConfigException');
+
+ $db = $this->getConnection(false);
+ $cache = new ArrayCache();
+
+ $dependency = new DbDependency();
+ $dependency->db = $db;
+ $dependency->sql = null;
+
+ $dependency->evaluateDependency($cache);
+ }
}
From af1858c769330aae53f8291150a17ef31ce58576 Mon Sep 17 00:00:00 2001
From: Saleh Hashemi <81674631+salehhashemi1992@users.noreply.github.com>
Date: Fri, 27 Oct 2023 22:26:43 +0330
Subject: [PATCH 17/20] add test coverage for error handler
convertExceptionToString and convertExceptionToArray methods (#20051)
---
tests/framework/web/ErrorHandlerTest.php | 43 ++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/tests/framework/web/ErrorHandlerTest.php b/tests/framework/web/ErrorHandlerTest.php
index 1634c572d4..4ea8b15e6c 100644
--- a/tests/framework/web/ErrorHandlerTest.php
+++ b/tests/framework/web/ErrorHandlerTest.php
@@ -42,6 +42,49 @@ Message: This message is displayed to end user
Exception: yii\web\NotFoundHttpException', $out);
}
+ public function testFormatRaw()
+ {
+ Yii::$app->response->format = yii\web\Response::FORMAT_RAW;
+
+ /** @var ErrorHandler $handler */
+ $handler = Yii::$app->getErrorHandler();
+
+ ob_start(); // suppress response output
+ $this->invokeMethod($handler, 'renderException', [new \Exception('Test Exception')]);
+ $out = ob_get_clean();
+
+ $this->assertcontains('Test Exception', $out);
+
+ $this->assertTrue(is_string(Yii::$app->response->data));
+ $this->assertcontains("Exception 'Exception' with message 'Test Exception'", Yii::$app->response->data);
+ }
+
+ public function testFormatXml()
+ {
+ Yii::$app->response->format = yii\web\Response::FORMAT_XML;
+
+ /** @var ErrorHandler $handler */
+ $handler = Yii::$app->getErrorHandler();
+
+ ob_start(); // suppress response output
+ $this->invokeMethod($handler, 'renderException', [new \Exception('Test Exception')]);
+ $out = ob_get_clean();
+
+ $this->assertcontains('Test Exception', $out);
+
+ $outArray = Yii::$app->response->data;
+
+ $this->assertTrue(is_array(Yii::$app->response->data));
+
+ $this->assertEquals('Exception', $outArray['name']);
+ $this->assertEquals('Test Exception', $outArray['message']);
+ $this->assertArrayHasKey('code', $outArray);
+ $this->assertEquals('Exception', $outArray['type']);
+ $this->assertContains('ErrorHandlerTest.php', $outArray['file']);
+ $this->assertArrayHasKey('stack-trace', $outArray);
+ $this->assertArrayHasKey('line', $outArray);
+ }
+
public function testClearAssetFilesInErrorView()
{
Yii::$app->getView()->registerJsFile('somefile.js');
From caa2029ec70604747fe36cfd4fd7d01ff31680d6 Mon Sep 17 00:00:00 2001
From: salehhashemi1992 <81674631+salehhashemi1992@users.noreply.github.com>
Date: Mon, 23 Oct 2023 22:48:14 +0330
Subject: [PATCH 18/20] implement findBetween method to StringHelper
---
framework/helpers/BaseStringHelper.php | 28 ++++++++++++++++++++
tests/framework/helpers/StringHelperTest.php | 26 ++++++++++++++++++
2 files changed, 54 insertions(+)
diff --git a/framework/helpers/BaseStringHelper.php b/framework/helpers/BaseStringHelper.php
index e9c5327b03..1dda22be7c 100644
--- a/framework/helpers/BaseStringHelper.php
+++ b/framework/helpers/BaseStringHelper.php
@@ -527,4 +527,32 @@ class BaseStringHelper
return $masked;
}
+
+ /**
+ * Returns the portion of the string that lies between the first occurrence of the start string
+ * and the last occurrence of the end string after that.
+ *
+ * @param string $string The input string.
+ * @param string $start The string marking the start of the portion to extract.
+ * @param string $end The string marking the end of the portion to extract.
+ * @return string The portion of the string between the first occurrence of start and the last occurrence of end.
+ */
+ public static function findBetween($string, $start, $end)
+ {
+ $startPos = mb_strpos($string, $start);
+
+ if ($startPos === false) {
+ return '';
+ }
+
+ // Cut the string from the start position
+ $subString = mb_substr($string, $startPos + mb_strlen($start));
+ $endPos = mb_strrpos($subString, $end);
+
+ if ($endPos === false) {
+ return '';
+ }
+
+ return mb_substr($subString, 0, $endPos);
+ }
}
diff --git a/tests/framework/helpers/StringHelperTest.php b/tests/framework/helpers/StringHelperTest.php
index a640e5cdda..ff6298eacf 100644
--- a/tests/framework/helpers/StringHelperTest.php
+++ b/tests/framework/helpers/StringHelperTest.php
@@ -506,4 +506,30 @@ class StringHelperTest extends TestCase
$this->assertSame('em**l@email.com', StringHelper::mask('email@email.com', 2, 2));
$this->assertSame('******email.com', StringHelper::mask('email@email.com', 0, 6));
}
+
+ /**
+ * @param string $string
+ * @param string $start
+ * @param string $end
+ * @param string $expectedResult
+ * @dataProvider dataProviderFindBetween
+ */
+ public function testFindBetween($string, $start, $end, $expectedResult)
+ {
+ $this->assertSame($expectedResult, StringHelper::findBetween($string, $start, $end));
+ }
+
+ public function dataProviderFindBetween()
+ {
+ return [
+ ['hello world hello', 'hello ', ' world', ''], // end before start
+ ['This is a sample string', 'is ', ' string', 'is a sample'], // normal case
+ ['startendstart', 'start', 'end', ''], // end before start
+ ['startmiddleend', 'start', 'end', 'middle'], // normal case
+ ['startend', 'start', 'end', ''], // end immediately follows start
+ ['multiple start start end end', 'start ', ' end', 'start end'], // multiple starts and ends
+ ['', 'start', 'end', ''], // empty string
+ ['no delimiters here', 'start', 'end', ''], // no start and end
+ ];
+ }
}
From 26032ad686f140b019cb7abfe25ca3c88d1648a2 Mon Sep 17 00:00:00 2001
From: salehhashemi1992 <81674631+salehhashemi1992@users.noreply.github.com>
Date: Sat, 28 Oct 2023 22:19:51 +0330
Subject: [PATCH 19/20] refactor findBetween method and add other tests
---
framework/CHANGELOG.md | 1 +
framework/helpers/BaseStringHelper.php | 7 ++++---
tests/framework/helpers/StringHelperTest.php | 12 ++++++++----
3 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md
index a80668a452..206da6ab1a 100644
--- a/framework/CHANGELOG.md
+++ b/framework/CHANGELOG.md
@@ -14,6 +14,7 @@ Yii Framework 2 Change Log
- Enh #20030: Improve performance of handling `ErrorHandler::$memoryReserveSize` (antonshevelev, rob006)
- Enh #20042: Add empty array check to `ActiveQueryTrait::findWith()` (renkas)
- Enh #20032: Added `mask` method for string masking with multibyte support (salehhashemi1992)
+- Enh #20034: Added `findBetween` to retrieve a substring that lies between two strings (salehhashemi1992)
2.0.49.2 October 12, 2023
diff --git a/framework/helpers/BaseStringHelper.php b/framework/helpers/BaseStringHelper.php
index 1dda22be7c..21a1f9e643 100644
--- a/framework/helpers/BaseStringHelper.php
+++ b/framework/helpers/BaseStringHelper.php
@@ -535,14 +535,15 @@ class BaseStringHelper
* @param string $string The input string.
* @param string $start The string marking the start of the portion to extract.
* @param string $end The string marking the end of the portion to extract.
- * @return string The portion of the string between the first occurrence of start and the last occurrence of end.
+ * @return string|null The portion of the string between the first occurrence of
+ * start and the last occurrence of end, or null if either start or end cannot be found.
*/
public static function findBetween($string, $start, $end)
{
$startPos = mb_strpos($string, $start);
if ($startPos === false) {
- return '';
+ return null;
}
// Cut the string from the start position
@@ -550,7 +551,7 @@ class BaseStringHelper
$endPos = mb_strrpos($subString, $end);
if ($endPos === false) {
- return '';
+ return null;
}
return mb_substr($subString, 0, $endPos);
diff --git a/tests/framework/helpers/StringHelperTest.php b/tests/framework/helpers/StringHelperTest.php
index ff6298eacf..94efbf6713 100644
--- a/tests/framework/helpers/StringHelperTest.php
+++ b/tests/framework/helpers/StringHelperTest.php
@@ -522,14 +522,18 @@ class StringHelperTest extends TestCase
public function dataProviderFindBetween()
{
return [
- ['hello world hello', 'hello ', ' world', ''], // end before start
- ['This is a sample string', 'is ', ' string', 'is a sample'], // normal case
+ ['hello world hello', ' hello', ' world', null], // end before start
+ ['This is a sample string', ' is ', ' string', 'a sample'], // normal case
['startendstart', 'start', 'end', ''], // end before start
['startmiddleend', 'start', 'end', 'middle'], // normal case
['startend', 'start', 'end', ''], // end immediately follows start
['multiple start start end end', 'start ', ' end', 'start end'], // multiple starts and ends
- ['', 'start', 'end', ''], // empty string
- ['no delimiters here', 'start', 'end', ''], // no start and end
+ ['', 'start', 'end', null], // empty string
+ ['no delimiters here', 'start', 'end', null], // no start and end
+ ['start only', 'start', 'end', null], // start found but no end
+ ['end only', 'start', 'end', null], // end found but no start
+ ['spécial !@#$%^&*()', 'spé', '&*()', 'cial !@#$%^'], // Special characters
+ ['من صالح هاشمی هستم', 'من ', ' هستم', 'صالح هاشمی'], // other languages
];
}
}
From 3d4e108a3e0ac6393f34159e9255748545238ec3 Mon Sep 17 00:00:00 2001
From: Bizley
Date: Mon, 30 Oct 2023 12:18:25 +0100
Subject: [PATCH 20/20] Update CHANGELOG.md
---
framework/CHANGELOG.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md
index 206da6ab1a..e2c6747359 100644
--- a/framework/CHANGELOG.md
+++ b/framework/CHANGELOG.md
@@ -13,8 +13,8 @@ Yii Framework 2 Change Log
- Enh #12743: Added new methods `BaseActiveRecord::loadRelations()` and `BaseActiveRecord::loadRelationsFor()` to eager load related models for existing primary model instances (PowerGamer1)
- Enh #20030: Improve performance of handling `ErrorHandler::$memoryReserveSize` (antonshevelev, rob006)
- Enh #20042: Add empty array check to `ActiveQueryTrait::findWith()` (renkas)
-- Enh #20032: Added `mask` method for string masking with multibyte support (salehhashemi1992)
-- Enh #20034: Added `findBetween` to retrieve a substring that lies between two strings (salehhashemi1992)
+- Enh #20032: Added `yii\helpers\BaseStringHelper::mask()` method for string masking with multibyte support (salehhashemi1992)
+- Enh #20034: Added `yii\helpers\BaseStringHelper::findBetween()` to retrieve a substring that lies between two strings (salehhashemi1992)
2.0.49.2 October 12, 2023