Skip to content

Commit 9e8e453

Browse files
committed
wip
1 parent 7a71ad9 commit 9e8e453

File tree

3 files changed

+165
-42
lines changed

3 files changed

+165
-42
lines changed

tests/WP_PDO_MySQL_On_SQLite_PDO_API_Tests.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ class WP_PDO_MySQL_On_SQLite_PDO_API_Tests extends TestCase {
77
private $driver;
88

99
public function setUp(): void {
10-
$this->driver = new WP_PDO_MySQL_On_SQLite( 'mysql-on-sqlite:path=:memory:;dbname=WordPress;' );
10+
$this->driver = new WP_PDO_MySQL_On_SQLite( 'mysql-on-sqlite:path=:memory:;dbname=wp;' );
11+
$this->driver->setAttribute( PDO::ATTR_STRINGIFY_FETCHES, false );
1112
}
1213

1314
public function test_connection(): void {
@@ -246,8 +247,8 @@ public function data_pdo_fetch_methods(): Generator {
246247
"SELECT 1, 'abc', 2, 'two' as `2`",
247248
PDO::FETCH_BOTH,
248249
array(
249-
1 => 1, // int
250-
0 => 1, // int
250+
1 => 1,
251+
0 => 1,
251252
'abc' => 'abc',
252253
'2' => 'two',
253254
'3' => 'two',
@@ -266,7 +267,7 @@ public function data_pdo_fetch_methods(): Generator {
266267
"SELECT 1, 'abc', 2, 'two' as `2`",
267268
PDO::FETCH_ASSOC,
268269
array(
269-
'1' => 1, // int
270+
'1' => 1,
270271
'abc' => 'abc',
271272
'2' => 'two',
272273
),
@@ -277,7 +278,7 @@ public function data_pdo_fetch_methods(): Generator {
277278
"SELECT 1, 'abc', 2, 'two' as `2`",
278279
PDO::FETCH_NAMED,
279280
array(
280-
'1' => 1, // int
281+
'1' => 1,
281282
'abc' => 'abc',
282283
'2' => array( 2, 'two' ),
283284
),
@@ -288,7 +289,7 @@ public function data_pdo_fetch_methods(): Generator {
288289
"SELECT 1, 'abc', 2, 'two' as `2`",
289290
PDO::FETCH_OBJ,
290291
(object) array(
291-
'1' => 1, // int
292+
'1' => 1,
292293
'abc' => 'abc',
293294
'2' => 'two',
294295
),

wp-includes/sqlite-ast/class-wp-pdo-mysql-on-sqlite.php

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,15 @@ class WP_PDO_MySQL_On_SQLite extends PDO {
443443
*/
444444
private $information_schema_builder;
445445

446+
/**
447+
* PDO API: The PDO attributes of the connection.
448+
*
449+
* @var array<int, mixed>
450+
*/
451+
private $pdo_attributes = array(
452+
PDO::ATTR_STRINGIFY_FETCHES => PHP_VERSION_ID < 80100 ? true : false,
453+
);
454+
446455
/**
447456
* Last executed MySQL query.
448457
*
@@ -853,9 +862,9 @@ public function query( string $query, ?int $fetch_mode = null, ...$fetch_mode_ar
853862

854863
$affected_rows = is_int( $this->last_return_value ) ? $this->last_return_value : 0;
855864
$rows = is_array( $this->last_result ) ? $this->last_result : array();
856-
$columns = is_array( $this->last_column_meta ) ? array_column( $this->last_column_meta, 'name' ) : array();
865+
$columns = is_array( $this->last_column_meta ) ? $this->last_column_meta : array();
857866

858-
$stmt = new WP_PDO_Synthetic_Statement( $rows, $columns, $affected_rows );
867+
$stmt = new WP_PDO_Synthetic_Statement( $this, $columns, $rows, $affected_rows );
859868
$stmt->setFetchMode( $fetch_mode, ...$fetch_mode_args );
860869
return $stmt;
861870
} catch ( Throwable $e ) {
@@ -880,7 +889,7 @@ public function query( string $query, ?int $fetch_mode = null, ...$fetch_mode_ar
880889
* @return int|false The number of affected rows or false on failure.
881890
*/
882891
#[ReturnTypeWillChange]
883-
public function exec( string $query ) {
892+
public function exec( $query ) {
884893
$stmt = $this->query( $query );
885894
return $stmt->rowCount();
886895
}
@@ -945,6 +954,29 @@ public function inTransaction(): bool {
945954
return $this->connection->get_pdo()->inTransaction();
946955
}
947956

957+
/**
958+
* PDO API: Set a PDO attribute.
959+
*
960+
* @param int $attribute The attribute to set.
961+
* @param mixed $value The value of the attribute.
962+
* @return bool True on success, false on failure.
963+
*/
964+
public function setAttribute( $attribute, $value ): bool {
965+
$this->pdo_attributes[ $attribute ] = $value;
966+
return true;
967+
}
968+
969+
/**
970+
* PDO API: Get a PDO attribute.
971+
*
972+
* @param int $attribute The attribute to get.
973+
* @return mixed The value of the attribute, null when not set.
974+
*/
975+
#[ReturnTypeWillChange]
976+
public function getAttribute( $attribute ) {
977+
return $this->pdo_attributes[ $attribute ] ?? null;
978+
}
979+
948980
/**
949981
* Get the SQLite connection instance.
950982
*

wp-includes/sqlite-ast/class-wp-pdo-synthetic-statement.php

Lines changed: 123 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,60 @@
66
* phpcs:disable WordPress.DB.RestrictedClasses.mysql__PDOStatement
77
*
88
* PDO uses camel case naming, enable non-snake:
9+
* phpcs:disable WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
910
* phpcs:disable WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
1011
*
1112
* PDO uses $class as a variable name, enable it:
1213
* phpcs:disable Universal.NamingConventions.NoReservedKeywordParameterNames.classFound
14+
*
15+
* We use traits to support different PHP versions with incompatible PDO statement
16+
* method signatures. For that, enable multiple object structures in one file:
17+
* phpcs:disable Generic.Files.OneObjectStructurePerFile.MultipleFound
1318
*/
1419

20+
if ( PHP_VERSION_ID < 80000 ) {
21+
trait WP_PDO_Synthetic_Statement_PHP_Compat {
22+
23+
public function setFetchMode( $mode, $params = null ): bool {
24+
$this->fetch_mode = $mode;
25+
return true;
26+
}
27+
28+
public function fetchAll( $mode = null, $class_name = null, $constructor_args = null ): array {
29+
return $this->fetchAllRows( $mode, $class_name, $constructor_args );
30+
}
31+
}
32+
} else {
33+
trait WP_PDO_Synthetic_Statement_PHP_Compat {
34+
35+
#[ReturnTypeWillChange]
36+
public function setFetchMode( $mode, ...$args ): bool {
37+
$this->fetch_mode = $mode;
38+
return true;
39+
}
40+
41+
public function fetchAll( $mode = PDO::FETCH_DEFAULT, ...$args ): array {
42+
return $this->fetchAllRows( $mode, ...$args );
43+
}
44+
}
45+
}
46+
1547
class WP_PDO_Synthetic_Statement extends PDOStatement {
48+
use WP_PDO_Synthetic_Statement_PHP_Compat;
49+
1650
/**
17-
* Column names of the columns in the result set.
51+
* The PDO connection.
52+
*
53+
* @var PDO
54+
*/
55+
private $pdo;
56+
57+
/**
58+
* Column metadata as reported by SQLite.
1859
*
1960
* @var array<string>
2061
*/
21-
private $columns = array();
62+
private $sqlite_column_meta = array();
2263

2364
/**
2465
* Rows of the result set.
@@ -44,9 +85,18 @@ class WP_PDO_Synthetic_Statement extends PDOStatement {
4485
/**
4586
* The current fetch mode.
4687
*
88+
* The PDO::FETCH_DEFAULT constant is available from PHP 8.0.
89+
*
4790
* @var int
4891
*/
49-
private $fetch_mode = PDO::FETCH_DEFAULT;
92+
private $fetch_mode = 0; // PDO::FETCH_DEFAULT
93+
94+
/**
95+
* The PDO attributes of the statement.
96+
*
97+
* @var array<int, mixed>
98+
*/
99+
private $attributes = array();
50100

51101
/**
52102
* Additional arguments for the current fetch mode.
@@ -56,50 +106,82 @@ class WP_PDO_Synthetic_Statement extends PDOStatement {
56106
private $fetch_mode_args = array();
57107

58108
public function __construct(
109+
PDO $pdo,
110+
array $sqlite_column_metadata,
59111
array $rows,
60-
array $columns,
61112
int $affected_rows = 0
62113
) {
63-
$this->rows = $rows;
64-
$this->columns = $columns;
65-
$this->affected_rows = $affected_rows;
114+
$this->pdo = $pdo;
115+
$this->sqlite_column_meta = $sqlite_column_metadata;
116+
$this->rows = $rows;
117+
$this->affected_rows = $affected_rows;
66118
}
67119

68-
public function execute( ?array $params = null ): bool {
120+
public function execute( $params = null ): bool {
69121
return true;
70122
}
71123

72124
public function columnCount(): int {
73-
return count( $this->columns );
125+
return count( $this->sqlite_column_meta );
74126
}
75127

76128
public function rowCount(): int {
77129
return $this->affected_rows;
78130
}
79131

132+
/**
133+
* Fetch a row from the result set.
134+
*
135+
* @param int|null $mode The fetch mode.
136+
* Default: PDO::FETCH_DEFAULT (null for PHP < 8.0)
137+
* @param int|null $cursorOrientation The cursor orientation.
138+
* Default: PDO::FETCH_ORI_NEXT (null for PHP < 8.0)
139+
* @param int|null $cursorOffset The cursor offset.
140+
* Default: 0 (null for PHP < 8.0)
141+
* @return array|false The row or false if there are no more rows.
142+
*/
80143
#[ReturnTypeWillChange]
81144
public function fetch(
82-
int $mode = PDO::FETCH_DEFAULT,
83-
int $cursorOrientation = PDO::FETCH_ORI_NEXT,
84-
int $cursorOffset = 0
145+
$mode = 0,
146+
$cursorOrientation = PDO::FETCH_ORI_NEXT,
147+
$cursorOffset = 0
85148
) {
149+
if ( 0 === $mode || null === $mode ) {
150+
$mode = $this->fetch_mode;
151+
}
152+
if ( null === $cursorOrientation ) {
153+
$cursorOrientation = 0; // PDO::FETCH_ORI_NEXT (avaliable from PHP 8.0)
154+
}
155+
if ( null === $cursorOffset ) {
156+
$cursorOffset = 0;
157+
}
158+
86159
if ( ! array_key_exists( $this->row_offset, $this->rows ) ) {
87160
return false;
88161
}
89162
// TODO: $cursorOffset
90163

91-
if ( PDO::FETCH_DEFAULT === $mode ) {
92-
$mode = $this->fetch_mode;
93-
}
94-
95164
$row = $this->rows[ $this->row_offset ];
96165
$this->row_offset += 1;
97166

167+
$column_names = array_column( $this->sqlite_column_meta, 'name' );
168+
169+
if ( PHP_VERSION_ID < 80100 && ! $this->getAttribute( PDO::ATTR_STRINGIFY_FETCHES ) ) {
170+
foreach ( $row as $i => $value ) {
171+
$type = $this->sqlite_column_meta[ $i ]['native_type'];
172+
if ( 'integer' === $type ) {
173+
$row[ $i ] = (int) $value;
174+
} elseif ( 'float' === $type ) {
175+
$row[ $i ] = (float) $value;
176+
}
177+
}
178+
}
179+
98180
switch ( $mode ) {
99181
case PDO::FETCH_BOTH:
100182
$values = array();
101183
foreach ( $row as $i => $value ) {
102-
$name = $this->columns[ $i ];
184+
$name = $column_names[ $i ];
103185
$values[ $name ] = $value;
104186
if ( ! array_key_exists( $i, $values ) ) {
105187
$values[ $i ] = $value;
@@ -109,11 +191,11 @@ public function fetch(
109191
case PDO::FETCH_NUM:
110192
return $row;
111193
case PDO::FETCH_ASSOC:
112-
return array_combine( $this->columns, $row );
194+
return array_combine( $column_names, $row );
113195
case PDO::FETCH_NAMED:
114196
$values = array();
115197
foreach ( $row as $i => $value ) {
116-
$name = $this->columns[ $i ];
198+
$name = $column_names[ $i ];
117199
if ( is_array( $values[ $name ] ?? null ) ) {
118200
$values[ $name ][] = $value;
119201
} elseif ( array_key_exists( $name, $values ) ) {
@@ -124,32 +206,24 @@ public function fetch(
124206
}
125207
return $values;
126208
case PDO::FETCH_OBJ:
127-
$assoc = array_combine( $this->columns, $row );
209+
$assoc = array_combine( $column_names, $row );
128210
return (object) $assoc;
129211
default:
130212
throw new ValueError( sprintf( 'Unsupported fetch mode: %d', $mode ) );
131213
}
132214
}
133215

134-
public function fetchAll( int $mode = PDO::FETCH_DEFAULT, ...$args ): array {
135-
$rows = array();
136-
while ( $row = $this->fetch( $mode, ...$args ) ) {
137-
$rows[] = $row;
138-
}
139-
return $rows;
140-
}
141-
142216
#[ReturnTypeWillChange]
143-
public function fetchColumn( int $column = 0 ) {
217+
public function fetchColumn( $column = 0 ) {
144218
return $this->rows[ $this->row_offset ][ $column ] ?? null;
145219
}
146220

147221
#[ReturnTypeWillChange]
148-
public function fetchObject( ?string $class = 'stdClass', array $constructorArgs = array() ) {
222+
public function fetchObject( $class = 'stdClass', $constructorArgs = array() ) {
149223
return new $class( $this->rows[ $this->row_offset ], $constructorArgs );
150224
}
151225

152-
public function getColumnMeta( int $column ): array {
226+
public function getColumnMeta( $column ): array {
153227
throw new RuntimeException( 'Not implemented' );
154228
}
155229

@@ -162,11 +236,27 @@ public function errorInfo(): array {
162236
}
163237

164238
#[ReturnTypeWillChange]
165-
public function setFetchMode( int $mode, ...$args ): bool {
166-
$this->fetch_mode = $mode;
239+
public function getAttribute( $attribute ) {
240+
return $this->attributes[ $attribute ] ?? $this->pdo->getAttribute( $attribute );
241+
}
242+
243+
public function setAttribute( $attribute, $value ): bool {
244+
$this->attributes[ $attribute ] = $value;
167245
return true;
168246
}
169247

248+
private function fetchAllRows( $mode = null, ...$args ): array {
249+
if ( null === $mode || 0 === $mode ) {
250+
$mode = $this->fetch_mode;
251+
}
252+
253+
$rows = array();
254+
while ( $row = $this->fetch( $mode, ...$args ) ) {
255+
$rows[] = $row;
256+
}
257+
return $rows;
258+
}
259+
170260
// TODO:
171261
// public function bindColumn()
172262
// public function bindParam()

0 commit comments

Comments
 (0)