mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-10-31 18:47:33 +08:00 
			
		
		
		
	Merge branch 'master' into fix-type-boolean-mysql
This commit is contained in:
		| @ -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) | ||||
| @ -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 | ||||
| ------------------------- | ||||
|  | ||||
| @ -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; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										198
									
								
								tests/framework/db/mssql/type/BooleanTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								tests/framework/db/mssql/type/BooleanTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,198 @@ | ||||
| <?php | ||||
| /** | ||||
|  * @link https://www.yiiframework.com/ | ||||
|  * @copyright Copyright (c) 2008 Yii Software LLC | ||||
|  * @license https://www.yiiframework.com/license/ | ||||
|  */ | ||||
|  | ||||
| namespace yiiunit\framework\db\mssql\type; | ||||
|  | ||||
| use yii\db\mssql\Schema; | ||||
| use yiiunit\framework\db\DatabaseTestCase; | ||||
|  | ||||
| /** | ||||
|  * @group db | ||||
|  * @group mssql | ||||
|  */ | ||||
| class BooleanTest extends DatabaseTestCase | ||||
| { | ||||
|     protected $driverName = 'sqlsrv'; | ||||
|  | ||||
|     public function testBoolean() | ||||
|     { | ||||
|         $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 `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); | ||||
|     } | ||||
| } | ||||
| @ -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)); | ||||
|     } | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Wilmer Arambula
					Wilmer Arambula