1<?php 2 3namespace Doctrine\DBAL\Driver; 4 5use Doctrine\DBAL\Driver\PDO\Exception; 6use Doctrine\DBAL\Driver\Statement as StatementInterface; 7use Doctrine\DBAL\FetchMode; 8use Doctrine\DBAL\ParameterType; 9use Doctrine\Deprecations\Deprecation; 10use PDO; 11use PDOException; 12use ReturnTypeWillChange; 13 14use function array_slice; 15use function assert; 16use function func_get_args; 17use function is_array; 18 19/** 20 * The PDO implementation of the Statement interface. 21 * Used by all PDO-based drivers. 22 * 23 * @deprecated Use {@link Statement} instead 24 */ 25class PDOStatement extends \PDOStatement implements StatementInterface, Result 26{ 27 use PDOStatementImplementations; 28 29 private const PARAM_TYPE_MAP = [ 30 ParameterType::NULL => PDO::PARAM_NULL, 31 ParameterType::INTEGER => PDO::PARAM_INT, 32 ParameterType::STRING => PDO::PARAM_STR, 33 ParameterType::ASCII => PDO::PARAM_STR, 34 ParameterType::BINARY => PDO::PARAM_LOB, 35 ParameterType::LARGE_OBJECT => PDO::PARAM_LOB, 36 ParameterType::BOOLEAN => PDO::PARAM_BOOL, 37 ]; 38 39 private const FETCH_MODE_MAP = [ 40 FetchMode::ASSOCIATIVE => PDO::FETCH_ASSOC, 41 FetchMode::NUMERIC => PDO::FETCH_NUM, 42 FetchMode::MIXED => PDO::FETCH_BOTH, 43 FetchMode::STANDARD_OBJECT => PDO::FETCH_OBJ, 44 FetchMode::COLUMN => PDO::FETCH_COLUMN, 45 FetchMode::CUSTOM_OBJECT => PDO::FETCH_CLASS, 46 ]; 47 48 /** 49 * Protected constructor. 50 * 51 * @internal The statement can be only instantiated by its driver connection. 52 */ 53 protected function __construct() 54 { 55 } 56 57 /** 58 * {@inheritdoc} 59 */ 60 #[ReturnTypeWillChange] 61 public function bindValue($param, $value, $type = ParameterType::STRING) 62 { 63 $type = $this->convertParamType($type); 64 65 try { 66 return parent::bindValue($param, $value, $type); 67 } catch (PDOException $exception) { 68 throw Exception::new($exception); 69 } 70 } 71 72 /** 73 * @param mixed $param 74 * @param mixed $variable 75 * @param int $type 76 * @param int|null $length 77 * @param mixed $driverOptions 78 * 79 * @return bool 80 */ 81 #[ReturnTypeWillChange] 82 public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null, $driverOptions = null) 83 { 84 $type = $this->convertParamType($type); 85 86 try { 87 return parent::bindParam($param, $variable, $type, ...array_slice(func_get_args(), 3)); 88 } catch (PDOException $exception) { 89 throw Exception::new($exception); 90 } 91 } 92 93 /** 94 * {@inheritdoc} 95 * 96 * @deprecated Use free() instead. 97 */ 98 #[ReturnTypeWillChange] 99 public function closeCursor() 100 { 101 try { 102 return parent::closeCursor(); 103 } catch (PDOException $exception) { 104 // Exceptions not allowed by the interface. 105 // In case driver implementations do not adhere to the interface, silence exceptions here. 106 return true; 107 } 108 } 109 110 /** 111 * {@inheritdoc} 112 */ 113 #[ReturnTypeWillChange] 114 public function execute($params = null) 115 { 116 try { 117 return parent::execute($params); 118 } catch (PDOException $exception) { 119 throw Exception::new($exception); 120 } 121 } 122 123 /** 124 * {@inheritdoc} 125 * 126 * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. 127 */ 128 #[ReturnTypeWillChange] 129 public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) 130 { 131 $args = func_get_args(); 132 133 if (isset($args[0])) { 134 $args[0] = $this->convertFetchMode($args[0]); 135 } 136 137 try { 138 return parent::fetch(...$args); 139 } catch (PDOException $exception) { 140 throw Exception::new($exception); 141 } 142 } 143 144 /** 145 * {@inheritdoc} 146 * 147 * @deprecated Use fetchOne() instead. 148 */ 149 #[ReturnTypeWillChange] 150 public function fetchColumn($columnIndex = 0) 151 { 152 try { 153 return parent::fetchColumn($columnIndex); 154 } catch (PDOException $exception) { 155 throw Exception::new($exception); 156 } 157 } 158 159 /** 160 * {@inheritdoc} 161 */ 162 public function fetchNumeric() 163 { 164 return $this->fetch(PDO::FETCH_NUM); 165 } 166 167 /** 168 * {@inheritdoc} 169 */ 170 public function fetchAssociative() 171 { 172 return $this->fetch(PDO::FETCH_ASSOC); 173 } 174 175 /** 176 * {@inheritdoc} 177 */ 178 public function fetchOne() 179 { 180 return $this->fetch(PDO::FETCH_COLUMN); 181 } 182 183 /** 184 * {@inheritdoc} 185 */ 186 public function fetchAllNumeric(): array 187 { 188 return $this->fetchAll(PDO::FETCH_NUM); 189 } 190 191 /** 192 * {@inheritdoc} 193 */ 194 public function fetchAllAssociative(): array 195 { 196 return $this->fetchAll(PDO::FETCH_ASSOC); 197 } 198 199 /** 200 * {@inheritdoc} 201 */ 202 public function fetchFirstColumn(): array 203 { 204 return $this->fetchAll(PDO::FETCH_COLUMN); 205 } 206 207 public function free(): void 208 { 209 parent::closeCursor(); 210 } 211 212 /** 213 * @param mixed ...$args 214 */ 215 private function doSetFetchMode(int $fetchMode, ...$args): bool 216 { 217 $fetchMode = $this->convertFetchMode($fetchMode); 218 219 // This thin wrapper is necessary to shield against the weird signature 220 // of PDOStatement::setFetchMode(): even if the second and third 221 // parameters are optional, PHP will not let us remove it from this 222 // declaration. 223 $slice = []; 224 225 foreach ($args as $arg) { 226 if ($arg === null) { 227 break; 228 } 229 230 $slice[] = $arg; 231 } 232 233 try { 234 return parent::setFetchMode($fetchMode, ...$slice); 235 } catch (PDOException $exception) { 236 throw Exception::new($exception); 237 } 238 } 239 240 /** 241 * @param mixed ...$args 242 * 243 * @return mixed[] 244 */ 245 private function doFetchAll(...$args): array 246 { 247 if (isset($args[0])) { 248 $args[0] = $this->convertFetchMode($args[0]); 249 } 250 251 $slice = []; 252 253 foreach ($args as $arg) { 254 if ($arg === null) { 255 break; 256 } 257 258 $slice[] = $arg; 259 } 260 261 try { 262 $data = parent::fetchAll(...$slice); 263 } catch (PDOException $exception) { 264 throw Exception::new($exception); 265 } 266 267 assert(is_array($data)); 268 269 return $data; 270 } 271 272 /** 273 * Converts DBAL parameter type to PDO parameter type 274 * 275 * @param int $type Parameter type 276 */ 277 private function convertParamType(int $type): int 278 { 279 if (! isset(self::PARAM_TYPE_MAP[$type])) { 280 // TODO: next major: throw an exception 281 Deprecation::trigger( 282 'doctrine/dbal', 283 'https://github.com/doctrine/dbal/pull/3088', 284 'Using a PDO parameter type (%d given) is deprecated, ' . 285 'use \Doctrine\DBAL\Types\Types constants instead.', 286 $type 287 ); 288 289 return $type; 290 } 291 292 return self::PARAM_TYPE_MAP[$type]; 293 } 294 295 /** 296 * Converts DBAL fetch mode to PDO fetch mode 297 * 298 * @param int $fetchMode Fetch mode 299 */ 300 private function convertFetchMode(int $fetchMode): int 301 { 302 if (! isset(self::FETCH_MODE_MAP[$fetchMode])) { 303 Deprecation::trigger( 304 'doctrine/dbal', 305 'https://github.com/doctrine/dbal/pull/3088', 306 'Using an unsupported PDO fetch mode or a bitmask of fetch modes (%d given)' . 307 ' is deprecated and will cause an error in Doctrine DBAL 3.0', 308 $fetchMode 309 ); 310 311 return $fetchMode; 312 } 313 314 return self::FETCH_MODE_MAP[$fetchMode]; 315 } 316} 317