Complex classes like PreparedStatement often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use PreparedStatement, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 38 | class PreparedStatement |
||
| 39 | { |
||
| 40 | /** |
||
| 41 | * Represents the SQL NULL data type. |
||
| 42 | * |
||
| 43 | * @var int |
||
| 44 | */ |
||
| 45 | const PARAM_NULL = 0; |
||
| 46 | |||
| 47 | /** |
||
| 48 | * Represents the SQL INTEGER data type. |
||
| 49 | * |
||
| 50 | * @var int |
||
| 51 | */ |
||
| 52 | const PARAM_INT = 1; |
||
| 53 | |||
| 54 | /** |
||
| 55 | * Represents the SQL CHAR, VARCHAR, or other string data type. |
||
| 56 | * |
||
| 57 | * @var int |
||
| 58 | */ |
||
| 59 | const PARAM_STR = 2; |
||
| 60 | |||
| 61 | /** |
||
| 62 | * Represents a boolean data type. |
||
| 63 | * |
||
| 64 | * @var int |
||
| 65 | */ |
||
| 66 | const PARAM_BOOL = 3; |
||
| 67 | |||
| 68 | /** |
||
| 69 | * Automatically detects underlying type |
||
| 70 | * |
||
| 71 | * @var int |
||
| 72 | */ |
||
| 73 | const PARAM_AUTOTYPE = 4; |
||
| 74 | |||
| 75 | /** |
||
| 76 | * Specifies that the fetch method shall return each row as an array indexed by |
||
| 77 | * column name as returned in the corresponding result set. If the result set |
||
| 78 | * contains multiple columns with the same name, \Fab\Vidi\Database\PreparedStatement::FETCH_ASSOC |
||
| 79 | * returns only a single value per column name. |
||
| 80 | * |
||
| 81 | * @var int |
||
| 82 | */ |
||
| 83 | const FETCH_ASSOC = 2; |
||
| 84 | |||
| 85 | /** |
||
| 86 | * Specifies that the fetch method shall return each row as an array indexed by |
||
| 87 | * column number as returned in the corresponding result set, starting at column 0. |
||
| 88 | * |
||
| 89 | * @var int |
||
| 90 | */ |
||
| 91 | const FETCH_NUM = 3; |
||
| 92 | |||
| 93 | /** |
||
| 94 | * Query to be executed. |
||
| 95 | * |
||
| 96 | * @var string |
||
| 97 | */ |
||
| 98 | protected $query; |
||
| 99 | |||
| 100 | /** |
||
| 101 | * Components of the query to be executed. |
||
| 102 | * |
||
| 103 | * @var array |
||
| 104 | */ |
||
| 105 | protected $precompiledQueryParts; |
||
| 106 | |||
| 107 | /** |
||
| 108 | * Table (used to call $GLOBALS['TYPO3_DB']->fullQuoteStr(). |
||
| 109 | * |
||
| 110 | * @var string |
||
| 111 | */ |
||
| 112 | protected $table; |
||
| 113 | |||
| 114 | /** |
||
| 115 | * Binding parameters. |
||
| 116 | * |
||
| 117 | * @var array |
||
| 118 | */ |
||
| 119 | protected $parameters; |
||
| 120 | |||
| 121 | /** |
||
| 122 | * Default fetch mode. |
||
| 123 | * |
||
| 124 | * @var int |
||
| 125 | */ |
||
| 126 | protected $defaultFetchMode = self::FETCH_ASSOC; |
||
| 127 | |||
| 128 | /** |
||
| 129 | * MySQLi statement object / DBAL object |
||
| 130 | * |
||
| 131 | * @var \mysqli_stmt|object |
||
| 132 | */ |
||
| 133 | protected $statement; |
||
| 134 | |||
| 135 | /** |
||
| 136 | * @var array |
||
| 137 | */ |
||
| 138 | protected $fields; |
||
| 139 | |||
| 140 | /** |
||
| 141 | * @var array |
||
| 142 | */ |
||
| 143 | protected $buffer; |
||
| 144 | |||
| 145 | /** |
||
| 146 | * Random token which is wrapped around the markers |
||
| 147 | * that will be replaced by user input. |
||
| 148 | * |
||
| 149 | * @var string |
||
| 150 | */ |
||
| 151 | protected $parameterWrapToken; |
||
| 152 | |||
| 153 | /** |
||
| 154 | * Creates a new PreparedStatement. Either $query or $queryComponents |
||
| 155 | * should be used. Typically $query will be used by native MySQL TYPO3_DB |
||
| 156 | * on a ready-to-be-executed query. On the other hand, DBAL will have |
||
| 157 | * parse the query and will be able to safely know where parameters are used |
||
| 158 | * and will use $queryComponents instead. |
||
| 159 | * |
||
| 160 | * This constructor may only be used by \Fab\Vidi\Database\DatabaseConnection |
||
| 161 | * |
||
| 162 | * @param string $query SQL query to be executed |
||
| 163 | * @param string $table FROM table, used to call $GLOBALS['TYPO3_DB']->fullQuoteStr(). |
||
| 164 | * @param array $precompiledQueryParts Components of the query to be executed |
||
| 165 | * @access private |
||
| 166 | * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, use Doctrine DBAL as it does PreparedStatements built-in |
||
| 167 | */ |
||
| 168 | public function __construct($query, $table, array $precompiledQueryParts = []) |
||
| 186 | |||
| 187 | /** |
||
| 188 | * Binds an array of values to corresponding named or question mark placeholders in the SQL |
||
| 189 | * statement that was use to prepare the statement. |
||
| 190 | * |
||
| 191 | * Example 1: |
||
| 192 | * <code> |
||
| 193 | * $statement = $GLOBALS['TYPO3_DB']->prepare_SELECTquery('*', 'bugs', 'reported_by = ? AND bug_status = ?'); |
||
| 194 | * $statement->bindValues(array('goofy', 'FIXED')); |
||
| 195 | * </code> |
||
| 196 | * |
||
| 197 | * Example 2: |
||
| 198 | * <code> |
||
| 199 | * $statement = $GLOBALS['TYPO3_DB']->prepare_SELECTquery('*', 'bugs', 'reported_by = :nickname AND bug_status = :status'); |
||
| 200 | * $statement->bindValues(array(':nickname' => 'goofy', ':status' => 'FIXED')); |
||
| 201 | * </code> |
||
| 202 | * |
||
| 203 | * @param array $values The values to bind to the parameter. The PHP type of each array value will be used to decide which PARAM_* type to use (int, string, boolean, NULL), so make sure your variables are properly casted, if needed. |
||
| 204 | * @return \Fab\Vidi\Database\PreparedStatement The current prepared statement to allow method chaining |
||
| 205 | * @api |
||
| 206 | */ |
||
| 207 | public function bindValues(array $values) |
||
| 215 | |||
| 216 | /** |
||
| 217 | * Binds a value to a corresponding named or question mark placeholder in the SQL |
||
| 218 | * statement that was use to prepare the statement. |
||
| 219 | * |
||
| 220 | * Example 1: |
||
| 221 | * <code> |
||
| 222 | * $statement = $GLOBALS['TYPO3_DB']->prepare_SELECTquery('*', 'bugs', 'reported_by = ? AND bug_status = ?'); |
||
| 223 | * $statement->bindValue(1, 'goofy'); |
||
| 224 | * $statement->bindValue(2, 'FIXED'); |
||
| 225 | * </code> |
||
| 226 | * |
||
| 227 | * Example 2: |
||
| 228 | * <code> |
||
| 229 | * $statement = $GLOBALS['TYPO3_DB']->prepare_SELECTquery('*', 'bugs', 'reported_by = :nickname AND bug_status = :status'); |
||
| 230 | * $statement->bindValue(':nickname', 'goofy'); |
||
| 231 | * $statement->bindValue(':status', 'FIXED'); |
||
| 232 | * </code> |
||
| 233 | * |
||
| 234 | * @param mixed $parameter Parameter identifier. For a prepared statement using named placeholders, this will be a parameter name of the form :name. For a prepared statement using question mark placeholders, this will be the 1-indexed position of the parameter. |
||
| 235 | * @param mixed $value The value to bind to the parameter. |
||
| 236 | * @param int $data_type Explicit data type for the parameter using the \Fab\Vidi\Database\PreparedStatement::PARAM_* constants. If not given, the PHP type of the value will be used instead (int, string, boolean). |
||
| 237 | * @return \Fab\Vidi\Database\PreparedStatement The current prepared statement to allow method chaining |
||
| 238 | * @api |
||
| 239 | */ |
||
| 240 | public function bindValue($parameter, $value, $data_type = self::PARAM_AUTOTYPE) |
||
| 269 | |||
| 270 | /** |
||
| 271 | * Executes the prepared statement. If the prepared statement included parameter |
||
| 272 | * markers, you must either: |
||
| 273 | * <ul> |
||
| 274 | * <li>call {@link \Fab\Vidi\Database\PreparedStatement::bindParam()} to bind PHP variables |
||
| 275 | * to the parameter markers: bound variables pass their value as input</li> |
||
| 276 | * <li>or pass an array of input-only parameter values</li> |
||
| 277 | * </ul> |
||
| 278 | * |
||
| 279 | * $input_parameters behave as in {@link \Fab\Vidi\Database\PreparedStatement::bindParams()} |
||
| 280 | * and work for both named parameters and question mark parameters. |
||
| 281 | * |
||
| 282 | * Example 1: |
||
| 283 | * <code> |
||
| 284 | * $statement = $GLOBALS['TYPO3_DB']->prepare_SELECTquery('*', 'bugs', 'reported_by = ? AND bug_status = ?'); |
||
| 285 | * $statement->execute(array('goofy', 'FIXED')); |
||
| 286 | * </code> |
||
| 287 | * |
||
| 288 | * Example 2: |
||
| 289 | * <code> |
||
| 290 | * $statement = $GLOBALS['TYPO3_DB']->prepare_SELECTquery('*', 'bugs', 'reported_by = :nickname AND bug_status = :status'); |
||
| 291 | * $statement->execute(array(':nickname' => 'goofy', ':status' => 'FIXED')); |
||
| 292 | * </code> |
||
| 293 | * |
||
| 294 | * @param array $input_parameters An array of values with as many elements as there are bound parameters in the SQL statement being executed. The PHP type of each array value will be used to decide which PARAM_* type to use (int, string, boolean, NULL), so make sure your variables are properly casted, if needed. |
||
| 295 | * @return bool Returns TRUE on success or FALSE on failure. |
||
| 296 | * @throws \InvalidArgumentException |
||
| 297 | * @api |
||
| 298 | */ |
||
| 299 | public function execute(array $input_parameters = []) |
||
| 410 | |||
| 411 | /** |
||
| 412 | * Fetches a row from a result set associated with a \Fab\Vidi\Database\PreparedStatement object. |
||
| 413 | * |
||
| 414 | * @param int $fetch_style Controls how the next row will be returned to the caller. This value must be one of the \Fab\Vidi\Database\PreparedStatement::FETCH_* constants. If omitted, default fetch mode for this prepared query will be used. |
||
| 415 | * @return array Array of rows or FALSE if there are no more rows. |
||
| 416 | * @api |
||
| 417 | */ |
||
| 418 | public function fetch($fetch_style = 0) |
||
| 462 | |||
| 463 | /** |
||
| 464 | * Moves internal result pointer. |
||
| 465 | * |
||
| 466 | * @param int $rowNumber Where to place the result pointer (0 = start) |
||
| 467 | * @return bool Returns TRUE on success or FALSE on failure. |
||
| 468 | * @api |
||
| 469 | */ |
||
| 470 | public function seek($rowNumber) |
||
| 479 | |||
| 480 | /** |
||
| 481 | * Returns an array containing all of the result set rows. |
||
| 482 | * |
||
| 483 | * @param int $fetch_style Controls the contents of the returned array as documented in {@link \Fab\Vidi\Database\PreparedStatement::fetch()}. |
||
| 484 | * @return array Array of rows. |
||
| 485 | * @api |
||
| 486 | */ |
||
| 487 | public function fetchAll($fetch_style = 0) |
||
| 495 | |||
| 496 | /** |
||
| 497 | * Releases the cursor. Should always be call after having fetched rows from |
||
| 498 | * a query execution. |
||
| 499 | * |
||
| 500 | * @api |
||
| 501 | */ |
||
| 502 | public function free() |
||
| 506 | |||
| 507 | /** |
||
| 508 | * Returns the number of rows affected by the last SQL statement. |
||
| 509 | * |
||
| 510 | * @return int The number of rows. |
||
| 511 | * @api |
||
| 512 | */ |
||
| 513 | public function rowCount() |
||
| 517 | |||
| 518 | /** |
||
| 519 | * Returns the error number on the last execute() call. |
||
| 520 | * |
||
| 521 | * @return int Driver specific error code. |
||
| 522 | * @api |
||
| 523 | */ |
||
| 524 | public function errorCode() |
||
| 528 | |||
| 529 | /** |
||
| 530 | * Returns an array of error information about the last operation performed by this statement handle. |
||
| 531 | * The array consists of the following fields: |
||
| 532 | * <ol start="0"> |
||
| 533 | * <li>Driver specific error code.</li> |
||
| 534 | * <li>Driver specific error message</li> |
||
| 535 | * </ol> |
||
| 536 | * |
||
| 537 | * @return array Array of error information. |
||
| 538 | */ |
||
| 539 | public function errorInfo() |
||
| 546 | |||
| 547 | /** |
||
| 548 | * Sets the default fetch mode for this prepared query. |
||
| 549 | * |
||
| 550 | * @param int $mode One of the \Fab\Vidi\Database\PreparedStatement::FETCH_* constants |
||
| 551 | * @api |
||
| 552 | */ |
||
| 553 | public function setFetchMode($mode) |
||
| 564 | |||
| 565 | /** |
||
| 566 | * Guesses the type of a given value. |
||
| 567 | * |
||
| 568 | * @param mixed $value |
||
| 569 | * @return int One of the \Fab\Vidi\Database\PreparedStatement::PARAM_* constants |
||
| 570 | */ |
||
| 571 | protected function guessValueType($value) |
||
| 584 | |||
| 585 | /** |
||
| 586 | * Returns TRUE if named placeholers are used in a query. |
||
| 587 | * |
||
| 588 | * @param string $query |
||
| 589 | * @return bool |
||
| 590 | */ |
||
| 591 | protected function hasNamedPlaceholders($query) |
||
| 596 | |||
| 597 | /** |
||
| 598 | * Converts named placeholders into question mark placeholders in a query. |
||
| 599 | * |
||
| 600 | * @param string $query |
||
| 601 | * @param array $parameterValues |
||
| 602 | * @param array $precompiledQueryParts |
||
| 603 | */ |
||
| 604 | protected function convertNamedPlaceholdersToQuestionMarks(&$query, array &$parameterValues, array &$precompiledQueryParts) |
||
| 650 | |||
| 651 | /** |
||
| 652 | * Replace the markers with unpredictable token markers. |
||
| 653 | * |
||
| 654 | * @param string $query |
||
| 655 | * @param array $parameterValues |
||
| 656 | * @return string |
||
| 657 | * @throws \InvalidArgumentException |
||
| 658 | */ |
||
| 659 | protected function tokenizeQueryParameterMarkers($query, array $parameterValues) |
||
| 678 | |||
| 679 | /** |
||
| 680 | * Generate a random token that is used to wrap the query markers |
||
| 681 | * |
||
| 682 | * @return string |
||
| 683 | */ |
||
| 684 | protected function generateParameterWrapToken() |
||
| 688 | } |
||
| 689 |
If you define a variable conditionally, it can happen that it is not defined for all execution paths.
Let’s take a look at an example:
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.
Available Fixes
Check for existence of the variable explicitly:
Define a default value for the variable:
Add a value for the missing path: