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+
1545class 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