diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php index e0bd7bb51217..b597c6d6cd10 100644 --- a/system/Database/BaseConnection.php +++ b/system/Database/BaseConnection.php @@ -17,6 +17,7 @@ use CodeIgniter\Database\Exceptions\DatabaseException; use CodeIgniter\Events\Events; use stdClass; +use Stringable; use Throwable; /** @@ -1309,12 +1310,15 @@ public function escape($str) return array_map($this->escape(...), $str); } - /** @psalm-suppress NoValue I don't know why ERROR. */ - if (is_string($str) || (is_object($str) && method_exists($str, '__toString'))) { + if ($str instanceof Stringable) { if ($str instanceof RawSql) { return $str->__toString(); } + $str = (string) $str; + } + + if (is_string($str)) { return "'" . $this->escapeString($str) . "'"; } @@ -1328,8 +1332,8 @@ public function escape($str) /** * Escape String * - * @param list|string $str Input string - * @param bool $like Whether or not the string will be used in a LIKE condition + * @param list|string|Stringable $str Input string + * @param bool $like Whether the string will be used in a LIKE condition * * @return list|string */ @@ -1343,6 +1347,14 @@ public function escapeString($str, bool $like = false) return $str; } + if ($str instanceof Stringable) { + if ($str instanceof RawSql) { + return $str->__toString(); + } + + $str = (string) $str; + } + $str = $this->_escapeString($str); // escape LIKE condition wildcards @@ -1371,7 +1383,7 @@ public function escapeString($str, bool $like = false) * Calls the individual driver for platform * specific escaping for LIKE conditions * - * @param list|string $str + * @param list|string|Stringable $str * * @return list|string */ diff --git a/system/Database/Postgre/Connection.php b/system/Database/Postgre/Connection.php index 45e30eb65365..bce4209c64d2 100644 --- a/system/Database/Postgre/Connection.php +++ b/system/Database/Postgre/Connection.php @@ -20,6 +20,7 @@ use PgSql\Connection as PgSqlConnection; use PgSql\Result as PgSqlResult; use stdClass; +use Stringable; /** * Connection for Postgre @@ -233,12 +234,15 @@ public function escape($str) $this->initialize(); } - /** @psalm-suppress NoValue I don't know why ERROR. */ - if (is_string($str) || (is_object($str) && method_exists($str, '__toString'))) { + if ($str instanceof Stringable) { if ($str instanceof RawSql) { return $str->__toString(); } + $str = (string) $str; + } + + if (is_string($str)) { return pg_escape_literal($this->connID, $str); } @@ -246,7 +250,6 @@ public function escape($str) return $str ? 'TRUE' : 'FALSE'; } - /** @psalm-suppress NoValue I don't know why ERROR. */ return parent::escape($str); } diff --git a/system/I18n/Time.php b/system/I18n/Time.php index 2c6f3c4fab70..906479470b17 100644 --- a/system/I18n/Time.php +++ b/system/I18n/Time.php @@ -14,6 +14,7 @@ namespace CodeIgniter\I18n; use DateTimeImmutable; +use Stringable; /** * A localized date/time package inspired @@ -21,26 +22,26 @@ * * Requires the intl PHP extension. * - * @property int $age read-only - * @property string $day read-only - * @property string $dayOfWeek read-only - * @property string $dayOfYear read-only - * @property bool $dst read-only - * @property string $hour read-only - * @property bool $local read-only - * @property string $minute read-only - * @property string $month read-only - * @property string $quarter read-only - * @property string $second read-only - * @property int $timestamp read-only - * @property bool $utc read-only - * @property string $weekOfMonth read-only - * @property string $weekOfYear read-only - * @property string $year read-only + * @property-read int $age + * @property-read string $day + * @property-read string $dayOfWeek + * @property-read string $dayOfYear + * @property-read bool $dst + * @property-read string $hour + * @property-read bool $local + * @property-read string $minute + * @property-read string $month + * @property-read string $quarter + * @property-read string $second + * @property-read int $timestamp + * @property-read bool $utc + * @property-read string $weekOfMonth + * @property-read string $weekOfYear + * @property-read string $year * * @see \CodeIgniter\I18n\TimeTest */ -class Time extends DateTimeImmutable +class Time extends DateTimeImmutable implements Stringable { use TimeTrait; } diff --git a/tests/system/Database/Live/EscapeTest.php b/tests/system/Database/Live/EscapeTest.php index e1b700547c0c..0f931cdbe85c 100644 --- a/tests/system/Database/Live/EscapeTest.php +++ b/tests/system/Database/Live/EscapeTest.php @@ -14,6 +14,7 @@ namespace CodeIgniter\Database\Live; use CodeIgniter\Database\RawSql; +use CodeIgniter\I18n\Time; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; @@ -54,6 +55,14 @@ public function testEscape(): void $this->assertSame($expected, $sql); } + public function testEscapeStringable(): void + { + $expected = "SELECT * FROM brands WHERE name = '2024-01-01 12:00:00'"; + $sql = 'SELECT * FROM brands WHERE name = ' . $this->db->escape(new Time('2024-01-01 12:00:00')); + + $this->assertSame($expected, $sql); + } + public function testEscapeString(): void { $expected = "SELECT * FROM brands WHERE name = 'O" . $this->char . "'Doules'"; @@ -62,6 +71,15 @@ public function testEscapeString(): void $this->assertSame($expected, $sql); } + public function testEscapeStringStringable(): void + { + $expected = "SELECT * FROM brands WHERE name = '2024-01-01 12:00:00'"; + $sql = "SELECT * FROM brands WHERE name = '" + . $this->db->escapeString(new Time('2024-01-01 12:00:00')) . "'"; + + $this->assertSame($expected, $sql); + } + public function testEscapeLikeString(): void { $expected = "SELECT * FROM brands WHERE column LIKE '%10!% more%' ESCAPE '!'"; @@ -70,6 +88,15 @@ public function testEscapeLikeString(): void $this->assertSame($expected, $sql); } + public function testEscapeLikeStringStringable(): void + { + $expected = "SELECT * FROM brands WHERE column LIKE '%2024-01-01 12:00:00%' ESCAPE '!'"; + $sql = "SELECT * FROM brands WHERE column LIKE '%" + . $this->db->escapeLikeString(new Time('2024-01-01 12:00:00')) . "%' ESCAPE '!'"; + + $this->assertSame($expected, $sql); + } + public function testEscapeLikeStringDirect(): void { if ($this->db->DBDriver === 'MySQLi') {