Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/develop' into 4.6
Browse files Browse the repository at this point in the history
  • Loading branch information
kenjis committed Jul 1, 2024
2 parents 1bffa3c + 90cff96 commit 457a1e7
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 53 deletions.
12 changes: 0 additions & 12 deletions phpstan-baseline.php
Original file line number Diff line number Diff line change
Expand Up @@ -1807,12 +1807,6 @@
'count' => 1,
'path' => __DIR__ . '/system/Database/BaseBuilder.php',
];
$ignoreErrors[] = [
// identifier: missingType.iterableValue
'message' => '#^Method CodeIgniter\\\\Database\\\\BaseBuilder\\:\\:select\\(\\) has parameter \\$select with no value type specified in iterable type array\\.$#',
'count' => 1,
'path' => __DIR__ . '/system/Database/BaseBuilder.php',
];
$ignoreErrors[] = [
// identifier: missingType.iterableValue
'message' => '#^Method CodeIgniter\\\\Database\\\\BaseBuilder\\:\\:set\\(\\) has parameter \\$key with no value type specified in iterable type array\\.$#',
Expand Down Expand Up @@ -1981,12 +1975,6 @@
'count' => 1,
'path' => __DIR__ . '/system/Database/BaseBuilder.php',
];
$ignoreErrors[] = [
// identifier: missingType.iterableValue
'message' => '#^Property CodeIgniter\\\\Database\\\\BaseBuilder\\:\\:\\$QBNoEscape type has no value type specified in iterable type array\\.$#',
'count' => 1,
'path' => __DIR__ . '/system/Database/BaseBuilder.php',
];
$ignoreErrors[] = [
// identifier: missingType.iterableValue
'message' => '#^Property CodeIgniter\\\\Database\\\\BaseBuilder\\:\\:\\$QBOptions type has no value type specified in iterable type array\\.$#',
Expand Down
14 changes: 2 additions & 12 deletions rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,9 @@
use Rector\EarlyReturn\Rector\Return_\PreparedValueToEarlyReturnRector;
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
use Rector\Php70\Rector\FuncCall\RandomFunctionRector;
use Rector\Php80\Rector\Class_\AnnotationToAttributeRector;
use Rector\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector;
use Rector\Php80\Rector\FunctionLike\MixedTypeRector;
use Rector\Php81\Rector\FuncCall\NullToStrictStringFuncCallArgRector;
use Rector\PHPUnit\AnnotationsToAttributes\Rector\Class_\AnnotationWithValueToAttributeRector;
use Rector\PHPUnit\AnnotationsToAttributes\Rector\Class_\CoversAnnotationWithValueToAttributeRector;
use Rector\PHPUnit\AnnotationsToAttributes\Rector\ClassMethod\DataProviderAnnotationToAttributeRector;
use Rector\PHPUnit\AnnotationsToAttributes\Rector\ClassMethod\DependsAnnotationWithValueToAttributeRector;
use Rector\PHPUnit\CodeQuality\Rector\Class_\YieldDataProviderRector;
use Rector\PHPUnit\Set\PHPUnitSetList;
use Rector\Privatization\Rector\Property\PrivatizeFinalClassPropertyRector;
Expand All @@ -58,6 +53,7 @@
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector;
use Rector\TypeDeclaration\Rector\Closure\AddClosureVoidReturnTypeWhereNoReturnRector;
use Rector\TypeDeclaration\Rector\Empty_\EmptyOnNullableObjectToInstanceOfRector;
use Rector\TypeDeclaration\Rector\Function_\AddFunctionVoidReturnTypeWhereNoReturnRector;
use Rector\TypeDeclaration\Rector\StmtsAwareInterface\DeclareStrictTypesRector;
use Utils\Rector\PassStrictParameterToFunctionParameterRector;
use Utils\Rector\RemoveErrorSuppressInTryCatchStmtsRector;
Expand Down Expand Up @@ -175,13 +171,6 @@

// Unnecessary (string) is inserted
NullToStrictStringFuncCallArgRector::class,

// PHPUnit 10 (requires PHP 8.1) features
DataProviderAnnotationToAttributeRector::class,
DependsAnnotationWithValueToAttributeRector::class,
AnnotationWithValueToAttributeRector::class,
AnnotationToAttributeRector::class,
CoversAnnotationWithValueToAttributeRector::class,
])
// auto import fully qualified class names
->withImportNames(removeUnusedImports: true)
Expand Down Expand Up @@ -219,6 +208,7 @@
VersionCompareFuncCallToConstantRector::class,
ExplicitBoolCompareRector::class,
AddClosureVoidReturnTypeWhereNoReturnRector::class,
AddFunctionVoidReturnTypeWhereNoReturnRector::class,
])
->withConfiguredRule(StringClassNameToClassConstantRector::class, [
// keep '\\' prefix string on string '\Foo\Bar'
Expand Down
4 changes: 1 addition & 3 deletions system/Common.php
Original file line number Diff line number Diff line change
Expand Up @@ -768,10 +768,8 @@ function lang(string $line, array $args = [], ?string $locale = null)
* - notice
* - info
* - debug
*
* @return void
*/
function log_message(string $level, string $message, array $context = [])
function log_message(string $level, string $message, array $context = []): void
{
// When running tests, we want to always ensure that the
// TestLogger is running, which provides utilities for
Expand Down
36 changes: 23 additions & 13 deletions system/Database/BaseBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,9 @@ class BaseBuilder
protected array $QBUnion = [];

/**
* QB NO ESCAPE data
* Whether to protect identifiers in SELECT
*
* @var array
* @var list<bool|null> true=protect, false=not protect
*/
public $QBNoEscape = [];

Expand Down Expand Up @@ -390,7 +390,8 @@ public function ignore(bool $ignore = true)
/**
* Generates the SELECT portion of the query
*
* @param array|RawSql|string $select
* @param list<RawSql|string>|RawSql|string $select
* @param bool|null $escape Whether to protect identifiers
*
* @return $this
*/
Expand All @@ -402,16 +403,21 @@ public function select($select = '*', ?bool $escape = null)
}

if ($select instanceof RawSql) {
$this->QBSelect[] = $select;

return $this;
$select = [$select];
}

if (is_string($select)) {
$select = $escape === false ? [$select] : explode(',', $select);
$select = ($escape === false) ? [$select] : explode(',', $select);
}

foreach ($select as $val) {
if ($val instanceof RawSql) {
$this->QBSelect[] = $val;
$this->QBNoEscape[] = false;

continue;
}

$val = trim($val);

if ($val !== '') {
Expand All @@ -424,8 +430,10 @@ public function select($select = '*', ?bool $escape = null)
* This prevents NULL being escaped
* @see https://github.com/codeigniter4/CodeIgniter4/issues/1169
*/
if (mb_stripos(trim($val), 'NULL') === 0) {
$escape = false;
if (mb_stripos($val, 'NULL') === 0) {
$this->QBNoEscape[] = false;

continue;
}

$this->QBNoEscape[] = $escape;
Expand Down Expand Up @@ -3054,15 +3062,17 @@ protected function compileSelect($selectOverride = false): string

if (empty($this->QBSelect)) {
$sql .= '*';
} elseif ($this->QBSelect[0] instanceof RawSql) {
$sql .= (string) $this->QBSelect[0];
} else {
// Cycle through the "select" portion of the query and prep each column name.
// The reason we protect identifiers here rather than in the select() function
// is because until the user calls the from() function we don't know if there are aliases
foreach ($this->QBSelect as $key => $val) {
$noEscape = $this->QBNoEscape[$key] ?? null;
$this->QBSelect[$key] = $this->db->protectIdentifiers($val, false, $noEscape);
if ($val instanceof RawSql) {
$this->QBSelect[$key] = (string) $val;
} else {
$protect = $this->QBNoEscape[$key] ?? null;
$this->QBSelect[$key] = $this->db->protectIdentifiers($val, false, $protect);
}
}

$sql .= implode(', ', $this->QBSelect);
Expand Down
8 changes: 2 additions & 6 deletions system/Helpers/cookie_helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@
* @param bool|null $httpOnly True makes the cookie accessible via http(s) only (no javascript)
* @param string|null $sameSite The cookie SameSite value
*
* @return void
*
* @see \CodeIgniter\HTTP\Response::setCookie()
*/
function set_cookie(
Expand All @@ -49,7 +47,7 @@ function set_cookie(
?bool $secure = null,
?bool $httpOnly = null,
?string $sameSite = null
) {
): void {
$response = service('response');
$response->setCookie($name, $value, $expire, $domain, $path, $prefix, $secure, $httpOnly, $sameSite);
}
Expand Down Expand Up @@ -92,11 +90,9 @@ function get_cookie($index, bool $xssClean = false, ?string $prefix = '')
* @param string $path the cookie path
* @param string $prefix the cookie prefix
*
* @return void
*
* @see \CodeIgniter\HTTP\Response::deleteCookie()
*/
function delete_cookie($name, string $domain = '', string $path = '/', string $prefix = '')
function delete_cookie($name, string $domain = '', string $path = '/', string $prefix = ''): void
{
service('response')->deleteCookie($name, $domain, $path, $prefix);
}
Expand Down
6 changes: 2 additions & 4 deletions system/Helpers/kint_helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
*
* @codeCoverageIgnore Can't be tested ... exits
*/
function dd(...$vars)
function dd(...$vars): void
{
// @codeCoverageIgnoreStart
Kint::$aliases[] = 'dd';
Expand Down Expand Up @@ -71,10 +71,8 @@ function d(...$vars)
*/
/**
* trace function
*
* @return void
*/
function trace()
function trace(): void
{
Kint::$aliases[] = 'trace';
Kint::trace();
Expand Down
5 changes: 2 additions & 3 deletions system/Test/DatabaseTestTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Config\Database;
use Config\Migrations;
use Config\Services;
use PHPUnit\Framework\Attributes\AfterClass;

/**
* DatabaseTestTrait
Expand Down Expand Up @@ -228,14 +229,12 @@ public function seed(string $name)
// --------------------------------------------------------------------
// Utility
// --------------------------------------------------------------------

/**
* Reset $doneMigration and $doneSeed
*
* @afterClass
*
* @return void
*/
#[AfterClass]
public static function resetMigrationSeedCount()
{
self::$doneMigration = false;
Expand Down
82 changes: 82 additions & 0 deletions tests/system/Database/Builder/SelectTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use CodeIgniter\Database\SQLSRV\Builder as SQLSRVBuilder;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\Mock\MockConnection;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;

/**
Expand Down Expand Up @@ -67,6 +68,65 @@ public function testSelectAcceptsArray(): void
$this->assertSame($expected, str_replace("\n", ' ', $builder->getCompiledSelect()));
}

/**
* @param list<RawSql|string> $select
*/
#[DataProvider('provideSelectAcceptsArrayWithRawSql')]
public function testSelectAcceptsArrayWithRawSql(array $select, string $expected): void
{
$builder = new BaseBuilder('employees', $this->db);

$builder->select($select);

$this->assertSame($expected, str_replace("\n", ' ', $builder->getCompiledSelect()));
}

/**
* @return list<list<RawSql|string>|string>
*/
public static function provideSelectAcceptsArrayWithRawSql(): iterable
{
yield from [
[
[
new RawSql("IF(salary > 5000, 'High', 'Low') AS salary_level"),
'employee_id',
],
<<<'SQL'
SELECT IF(salary > 5000, 'High', 'Low') AS salary_level, "employee_id" FROM "employees"
SQL,
],
[
[
'employee_id',
new RawSql("IF(salary > 5000, 'High', 'Low') AS salary_level"),
],
<<<'SQL'
SELECT "employee_id", IF(salary > 5000, 'High', 'Low') AS salary_level FROM "employees"
SQL,
],
[
[
new RawSql("CONCAT(first_name, ' ', last_name) AS full_name"),
new RawSql("IF(salary > 5000, 'High', 'Low') AS salary_level"),
],
<<<'SQL'
SELECT CONCAT(first_name, ' ', last_name) AS full_name, IF(salary > 5000, 'High', 'Low') AS salary_level FROM "employees"
SQL,
],
[
[
new RawSql("CONCAT(first_name, ' ', last_name) AS full_name"),
'employee_id',
new RawSql("IF(salary > 5000, 'High', 'Low') AS salary_level"),
],
<<<'SQL'
SELECT CONCAT(first_name, ' ', last_name) AS full_name, "employee_id", IF(salary > 5000, 'High', 'Low') AS salary_level FROM "employees"
SQL,
],
];
}

public function testSelectAcceptsMultipleColumns(): void
{
$builder = new BaseBuilder('users', $this->db);
Expand Down Expand Up @@ -100,6 +160,28 @@ public function testSelectWorksWithComplexSelects(): void
$this->assertSame($expected, str_replace("\n", ' ', $builder->getCompiledSelect()));
}

public function testSelectNullAsInString(): void
{
$builder = new BaseBuilder('users', $this->db);

$builder->select('NULL as field_alias, name');

$expected = 'SELECT NULL as field_alias, "name" FROM "users"';

$this->assertSame($expected, str_replace("\n", ' ', $builder->getCompiledSelect()));
}

public function testSelectNullAsInArray(): void
{
$builder = new BaseBuilder('users', $this->db);

$builder->select(['NULL as field_alias', 'name']);

$expected = 'SELECT NULL as field_alias, "name" FROM "users"';

$this->assertSame($expected, str_replace("\n", ' ', $builder->getCompiledSelect()));
}

/**
* @see https://github.com/codeigniter4/CodeIgniter4/issues/4355
*/
Expand Down

0 comments on commit 457a1e7

Please sign in to comment.