Skip to content

Commit 12df654

Browse files
committed
wip
1 parent 7a71ad9 commit 12df654

File tree

3 files changed

+144
-39
lines changed

3 files changed

+144
-39
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: 102 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,58 @@
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+
public function setFetchMode( $mode, $params = null ): bool {
23+
$this->fetch_mode = $mode;
24+
return true;
25+
}
26+
27+
public function fetchAll( $mode = null, $class_name = null, $constructor_args = null ): array {
28+
return $this->fetchAllRows( $mode, $class_name, $constructor_args );
29+
}
30+
}
31+
} else {
32+
trait WP_PDO_Synthetic_Statement_PHP_Compat {
33+
#[ReturnTypeWillChange]
34+
public function setFetchMode( $mode, ...$args ): bool {
35+
$this->fetch_mode = $mode;
36+
return true;
37+
}
38+
39+
public function fetchAll( $mode = PDO::FETCH_DEFAULT, ...$args ): array {
40+
return $this->fetchAllRows( $mode, ...$args );
41+
}
42+
}
43+
}
44+
1545
class WP_PDO_Synthetic_Statement extends PDOStatement {
46+
use WP_PDO_Synthetic_Statement_PHP_Compat;
47+
48+
/**
49+
* The PDO connection.
50+
*
51+
* @var PDO
52+
*/
53+
private $pdo;
54+
1655
/**
17-
* Column names of the columns in the result set.
56+
* Column metadata as reported by SQLite.
1857
*
1958
* @var array<string>
2059
*/
21-
private $columns = array();
60+
private $sqlite_column_meta = array();
2261

2362
/**
2463
* Rows of the result set.
@@ -44,9 +83,18 @@ class WP_PDO_Synthetic_Statement extends PDOStatement {
4483
/**
4584
* The current fetch mode.
4685
*
86+
* The PDO::FETCH_DEFAULT constant is available from PHP 8.0.
87+
*
4788
* @var int
4889
*/
49-
private $fetch_mode = PDO::FETCH_DEFAULT;
90+
private $fetch_mode = 0; // PDO::FETCH_DEFAULT
91+
92+
/**
93+
* The PDO attributes of the statement.
94+
*
95+
* @var array<int, mixed>
96+
*/
97+
private $attributes = array();
5098

5199
/**
52100
* Additional arguments for the current fetch mode.
@@ -56,21 +104,23 @@ class WP_PDO_Synthetic_Statement extends PDOStatement {
56104
private $fetch_mode_args = array();
57105

58106
public function __construct(
107+
PDO $pdo,
108+
array $sqlite_column_metadata,
59109
array $rows,
60-
array $columns,
61110
int $affected_rows = 0
62111
) {
63-
$this->rows = $rows;
64-
$this->columns = $columns;
65-
$this->affected_rows = $affected_rows;
112+
$this->pdo = $pdo;
113+
$this->sqlite_column_meta = $sqlite_column_metadata;
114+
$this->rows = $rows;
115+
$this->affected_rows = $affected_rows;
66116
}
67117

68-
public function execute( ?array $params = null ): bool {
118+
public function execute( $params = null ): bool {
69119
return true;
70120
}
71121

72122
public function columnCount(): int {
73-
return count( $this->columns );
123+
return count( $this->sqlite_column_meta );
74124
}
75125

76126
public function rowCount(): int {
@@ -79,27 +129,41 @@ public function rowCount(): int {
79129

80130
#[ReturnTypeWillChange]
81131
public function fetch(
82-
int $mode = PDO::FETCH_DEFAULT,
83-
int $cursorOrientation = PDO::FETCH_ORI_NEXT,
84-
int $cursorOffset = 0
132+
$mode = PDO::FETCH_DEFAULT,
133+
$cursorOrientation = PDO::FETCH_ORI_NEXT,
134+
$cursorOffset = 0
85135
) {
86136
if ( ! array_key_exists( $this->row_offset, $this->rows ) ) {
87137
return false;
88138
}
89139
// TODO: $cursorOffset
90140

91-
if ( PDO::FETCH_DEFAULT === $mode ) {
141+
// 0 is PDO::FETCH_DEFAULT (the constant is available from PHP 8.0).
142+
if ( 0 === $mode ) {
92143
$mode = $this->fetch_mode;
93144
}
94145

95146
$row = $this->rows[ $this->row_offset ];
96147
$this->row_offset += 1;
97148

149+
$column_names = array_column( $this->sqlite_column_meta, 'name' );
150+
151+
if ( PHP_VERSION_ID < 80100 && ! $this->getAttribute( PDO::ATTR_STRINGIFY_FETCHES ) ) {
152+
foreach ( $row as $i => $value ) {
153+
$type = $this->sqlite_column_meta[ $i ]['native_type'];
154+
if ( 'integer' === $type ) {
155+
$row[ $i ] = (int) $value;
156+
} elseif ( 'float' === $type ) {
157+
$row[ $i ] = (float) $value;
158+
}
159+
}
160+
}
161+
98162
switch ( $mode ) {
99163
case PDO::FETCH_BOTH:
100164
$values = array();
101165
foreach ( $row as $i => $value ) {
102-
$name = $this->columns[ $i ];
166+
$name = $column_names[ $i ];
103167
$values[ $name ] = $value;
104168
if ( ! array_key_exists( $i, $values ) ) {
105169
$values[ $i ] = $value;
@@ -109,11 +173,11 @@ public function fetch(
109173
case PDO::FETCH_NUM:
110174
return $row;
111175
case PDO::FETCH_ASSOC:
112-
return array_combine( $this->columns, $row );
176+
return array_combine( $column_names, $row );
113177
case PDO::FETCH_NAMED:
114178
$values = array();
115179
foreach ( $row as $i => $value ) {
116-
$name = $this->columns[ $i ];
180+
$name = $column_names[ $i ];
117181
if ( is_array( $values[ $name ] ?? null ) ) {
118182
$values[ $name ][] = $value;
119183
} elseif ( array_key_exists( $name, $values ) ) {
@@ -124,32 +188,24 @@ public function fetch(
124188
}
125189
return $values;
126190
case PDO::FETCH_OBJ:
127-
$assoc = array_combine( $this->columns, $row );
191+
$assoc = array_combine( $column_names, $row );
128192
return (object) $assoc;
129193
default:
130194
throw new ValueError( sprintf( 'Unsupported fetch mode: %d', $mode ) );
131195
}
132196
}
133197

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-
142198
#[ReturnTypeWillChange]
143-
public function fetchColumn( int $column = 0 ) {
199+
public function fetchColumn( $column = 0 ) {
144200
return $this->rows[ $this->row_offset ][ $column ] ?? null;
145201
}
146202

147203
#[ReturnTypeWillChange]
148-
public function fetchObject( ?string $class = 'stdClass', array $constructorArgs = array() ) {
204+
public function fetchObject( $class = 'stdClass', $constructorArgs = array() ) {
149205
return new $class( $this->rows[ $this->row_offset ], $constructorArgs );
150206
}
151207

152-
public function getColumnMeta( int $column ): array {
208+
public function getColumnMeta( $column ): array {
153209
throw new RuntimeException( 'Not implemented' );
154210
}
155211

@@ -162,11 +218,27 @@ public function errorInfo(): array {
162218
}
163219

164220
#[ReturnTypeWillChange]
165-
public function setFetchMode( int $mode, ...$args ): bool {
166-
$this->fetch_mode = $mode;
221+
public function getAttribute( $attribute ) {
222+
return $this->attributes[ $attribute ] ?? $this->pdo->getAttribute( $attribute );
223+
}
224+
225+
public function setAttribute( $attribute, $value ): bool {
226+
$this->attributes[ $attribute ] = $value;
167227
return true;
168228
}
169229

230+
private function fetchAllRows( $mode = null, ...$args ): array {
231+
if ( null === $mode || 0 === $mode ) {
232+
$mode = $this->fetch_mode;
233+
}
234+
235+
$rows = array();
236+
while ( $row = $this->fetch( $mode, ...$args ) ) {
237+
$rows[] = $row;
238+
}
239+
return $rows;
240+
}
241+
170242
// TODO:
171243
// public function bindColumn()
172244
// public function bindParam()

0 commit comments

Comments
 (0)