mirror of
https://github.com/yiisoft/yii2.git
synced 2025-11-01 11:39:41 +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
|
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 #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 #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)
|
- 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)
|
- 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 #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 #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
|
2.0.49.2 October 12, 2023
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|||||||
@ -375,6 +375,7 @@ SQL;
|
|||||||
*/
|
*/
|
||||||
protected function loadColumnSchema($info)
|
protected function loadColumnSchema($info)
|
||||||
{
|
{
|
||||||
|
$isVersion2017orLater = version_compare($this->db->getSchema()->getServerVersion(), '14', '>=');
|
||||||
$column = $this->createColumnSchema();
|
$column = $this->createColumnSchema();
|
||||||
|
|
||||||
$column->name = $info['column_name'];
|
$column->name = $info['column_name'];
|
||||||
@ -393,20 +394,21 @@ SQL;
|
|||||||
if (isset($this->typeMap[$type])) {
|
if (isset($this->typeMap[$type])) {
|
||||||
$column->type = $this->typeMap[$type];
|
$column->type = $this->typeMap[$type];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($isVersion2017orLater && $type === 'bit') {
|
||||||
|
$column->type = 'boolean';
|
||||||
|
}
|
||||||
|
|
||||||
if (!empty($matches[2])) {
|
if (!empty($matches[2])) {
|
||||||
$values = explode(',', $matches[2]);
|
$values = explode(',', $matches[2]);
|
||||||
$column->size = $column->precision = (int) $values[0];
|
$column->size = $column->precision = (int) $values[0];
|
||||||
|
|
||||||
if (isset($values[1])) {
|
if (isset($values[1])) {
|
||||||
$column->scale = (int) $values[1];
|
$column->scale = (int) $values[1];
|
||||||
}
|
}
|
||||||
if ($column->size === 1 && ($type === 'tinyint' || $type === 'bit')) {
|
|
||||||
$column->type = 'boolean';
|
if ($isVersion2017orLater === false) {
|
||||||
} elseif ($type === 'bit') {
|
$column->type = $this->booleanTypeLegacy($column->size, $type);
|
||||||
if ($column->size > 32) {
|
|
||||||
$column->type = 'bigint';
|
|
||||||
} elseif ($column->size === 32) {
|
|
||||||
$column->type = 'integer';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -813,4 +815,27 @@ SQL;
|
|||||||
{
|
{
|
||||||
return Yii::createObject(ColumnSchemaBuilder::className(), [$type, $length, $this->db]);
|
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);
|
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