Complex classes like Driver 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 Driver, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
42 | abstract class Driver |
||
43 | { |
||
44 | /** |
||
45 | * The internal PDO connection that is wrapped by this driver. |
||
46 | * |
||
47 | * @var \PDO |
||
48 | */ |
||
49 | private $pdo; |
||
50 | |||
51 | /** |
||
52 | * A logger for logging queries during debug. |
||
53 | * |
||
54 | * @var LoggerInterface |
||
55 | */ |
||
56 | private $logger; |
||
57 | |||
58 | /** |
||
59 | * The default schema used in the connection. |
||
60 | * |
||
61 | * @var string |
||
62 | */ |
||
63 | protected $defaultSchema; |
||
64 | |||
65 | /** |
||
66 | * The connection parameters with which this connection was established. |
||
67 | * |
||
68 | * @var array |
||
69 | */ |
||
70 | protected $config; |
||
71 | |||
72 | /** |
||
73 | * An instance of the descriptor used internally. |
||
74 | * |
||
75 | * @var \ntentan\atiaa\Descriptor |
||
76 | */ |
||
77 | private $descriptor; |
||
78 | private static $transactionCount = 0; |
||
79 | |||
80 | /** |
||
81 | * Creates a new instance of the Atiaa driver. This class is usually initiated |
||
82 | * through the \ntentan\atiaa\Atiaa::getConnection() method. For example |
||
83 | * to create a new instance of a connection to a mysql database. |
||
84 | * |
||
85 | * ````php |
||
86 | * use ntentan\atiaa\Driver; |
||
87 | * |
||
88 | * \\ This automatically insitatiates the driver class |
||
89 | * $driver = Driver::getConnection( |
||
90 | * array( |
||
91 | * 'driver' => 'mysql', |
||
92 | * 'user' => 'root', |
||
93 | * 'password' => 'rootpassy', |
||
94 | * 'host' => 'localhost', |
||
95 | * 'dbname' => 'somedb' |
||
96 | * ) |
||
97 | * ); |
||
98 | * |
||
99 | * var_dump($driver->query("SELECT * FROM some_table"); |
||
100 | * var_dump($driver->describe()); |
||
101 | * ```` |
||
102 | * |
||
103 | * @param array <string> $config The configuration with which to connect to the database. |
||
104 | */ |
||
105 | 32 | public function __construct(array $config) |
|
109 | |||
110 | 32 | public function connect() |
|
124 | |||
125 | 20 | public function __destruct() |
|
129 | |||
130 | /** |
||
131 | * Close a connection to the database server. |
||
132 | */ |
||
133 | 20 | public function disconnect() |
|
138 | |||
139 | /** |
||
140 | * Get the default schema of the current connection. |
||
141 | * |
||
142 | * @return string |
||
143 | */ |
||
144 | 5 | public function getDefaultSchema() |
|
148 | |||
149 | /** |
||
150 | * Use the PDO driver to quote a string. |
||
151 | * |
||
152 | * @param type $string |
||
153 | * |
||
154 | * @throws ConnectionException |
||
155 | * |
||
156 | * @return string |
||
157 | */ |
||
158 | 3 | public function quote($string) |
|
162 | |||
163 | /** |
||
164 | * @param $statement |
||
165 | * |
||
166 | * @return mixed |
||
167 | */ |
||
168 | 24 | private function fetchRows($statement) |
|
178 | |||
179 | 21 | private function prepareQuery($query, $bindData) |
|
180 | { |
||
181 | 21 | $statement = $this->pdo->prepare($query); |
|
182 | 21 | foreach ($bindData as $key => $value) { |
|
183 | 21 | switch (gettype($value)) { |
|
184 | 21 | case 'integer': |
|
185 | 21 | case 'boolean': // casts to boolean seems unstable |
|
186 | $type = \PDO::PARAM_INT; |
||
187 | break; |
||
188 | default: |
||
189 | 21 | $type = \PDO::PARAM_STR; |
|
190 | 21 | break; |
|
191 | } |
||
192 | // Bind values while adjusting numerical indices to start from 1 |
||
193 | 21 | $statement->bindValue(is_numeric($key) ? $key + 1 : $key, $value, $type); |
|
194 | } |
||
195 | |||
196 | 21 | return $statement; |
|
197 | } |
||
198 | |||
199 | /** |
||
200 | * Pepare and execute a query, while binding data at the same time. Prevents |
||
201 | * the writing of repetitive prepare and execute statements. This method |
||
202 | * returns an array which contains the results of the query that was |
||
203 | * executed. For queries which do not return any results a null is returned. |
||
204 | * |
||
205 | * @todo Add a parameter to cache prepared statements so they can be reused easily. |
||
206 | * |
||
207 | * @param string $query The query to be executed quoted in PDO style |
||
208 | * @param array $bindData |
||
209 | * |
||
210 | * @throws DatabaseDriverException |
||
211 | * |
||
212 | * @return array <mixed> |
||
213 | */ |
||
214 | 28 | public function query($query, $bindData = []) |
|
215 | { |
||
216 | try { |
||
217 | 28 | if (empty($bindData)) { |
|
218 | 20 | $statement = $this->pdo->query($query); |
|
219 | } else { |
||
220 | 21 | $statement = $this->prepareQuery($query, $bindData); |
|
221 | 21 | $statement->execute(); |
|
222 | 24 | $statement->errorCode(); |
|
223 | } |
||
224 | 6 | } catch (\PDOException $e) { |
|
225 | 3 | $boundData = json_encode($bindData); |
|
226 | |||
227 | 3 | throw new DatabaseDriverException("{$e->getMessage()} [$query] [BOUND DATA:$boundData]"); |
|
228 | } |
||
229 | 24 | if ($this->logger) { |
|
230 | $this->logger->debug($query, $bindData); |
||
231 | } |
||
232 | 24 | $rows = $this->fetchRows($statement); |
|
233 | 24 | $statement->closeCursor(); |
|
234 | |||
235 | 24 | return $rows; |
|
236 | } |
||
237 | |||
238 | /** |
||
239 | * Runs a query but ensures that all identifiers are properly quoted by calling |
||
240 | * the Driver::quoteQueryIdentifiers method on the query before executing it. |
||
241 | * |
||
242 | * @param string $query |
||
243 | * @param bool $bindData |
||
244 | * |
||
245 | * @throws DatabaseDriverException |
||
246 | * |
||
247 | * @return array <mixed> |
||
248 | */ |
||
249 | 15 | public function quotedQuery($query, $bindData = []) |
|
253 | |||
254 | /** |
||
255 | * Expands the configuration array into a format that can easily be passed |
||
256 | * to PDO. |
||
257 | * |
||
258 | * @param array $params The query parameters |
||
259 | * |
||
260 | * @return string |
||
261 | */ |
||
262 | 32 | private function expand($params) |
|
282 | |||
283 | /** |
||
284 | * This method provides a system independent way of quoting identifiers in |
||
285 | * queries. By default all identifiers can be quoted with double quotes ("). |
||
286 | * When a query quoted with double quotes is passed through this method the |
||
287 | * output generated has the double quotes replaced with the quoting character |
||
288 | * of the target database platform. |
||
289 | * |
||
290 | * @param string $query |
||
291 | * |
||
292 | * @return string |
||
293 | */ |
||
294 | 18 | public function quoteQueryIdentifiers($query) |
|
302 | |||
303 | /** |
||
304 | * Returns an array description of the schema represented by the connection. |
||
305 | * The description returns contains information about `tables`, `columns`, `keys`, |
||
306 | * `constraints`, `views` and `indices`. |
||
307 | * |
||
308 | * @return array<mixed> |
||
309 | */ |
||
310 | 6 | public function describe() |
|
314 | |||
315 | /** |
||
316 | * Returns the description of a database table as an associative array. |
||
317 | * |
||
318 | * @param string $table |
||
319 | * |
||
320 | * @return array<mixed> |
||
321 | */ |
||
322 | 6 | public function describeTable($table) |
|
323 | { |
||
324 | 6 | $table = explode('.', $table); |
|
325 | 6 | if (count($table) > 1) { |
|
326 | $schema = $table[0]; |
||
327 | $table = $table[1]; |
||
328 | } else { |
||
329 | 6 | $schema = $this->getDefaultSchema(); |
|
330 | 6 | $table = $table[0]; |
|
331 | } |
||
332 | |||
333 | 6 | return $this->getDescriptor()->describeTables($schema, [$table], true); |
|
334 | } |
||
335 | |||
336 | /** |
||
337 | * A wrapper arround PDO's beginTransaction method which uses a static reference |
||
338 | * counter to implement nested transactions. |
||
339 | */ |
||
340 | 6 | public function beginTransaction() |
|
346 | |||
347 | /** |
||
348 | * A wrapper around PDO's commit transaction method which uses a static reference |
||
349 | * counter to implement nested transactions. |
||
350 | */ |
||
351 | 3 | public function commit() |
|
357 | |||
358 | /** |
||
359 | * A wrapper around PDO's rollback transaction methd which rolls back all |
||
360 | * activities performed since the first call to begin transaction. |
||
361 | * Unfortunately, transactions cannot be rolled back in a nested fashion. |
||
362 | */ |
||
363 | 3 | public function rollback() |
|
370 | |||
371 | /** |
||
372 | * Return the underlying PDO object. |
||
373 | * |
||
374 | * @throws ConnectionException |
||
375 | * |
||
376 | 3 | * @return \PDO |
|
377 | */ |
||
378 | 3 | public function getPDO() |
|
386 | |||
387 | /** |
||
388 | * Returns an instance of a descriptor for a given driver. |
||
389 | * |
||
390 | 12 | * @return \ntentan\atiaa\Descriptor |
|
391 | */ |
||
392 | 12 | private function getDescriptor() |
|
401 | |||
402 | /** |
||
403 | * A wrapper around PDO's lastInsertId() method. |
||
404 | * |
||
405 | * @return mixed |
||
406 | */ |
||
407 | public function getLastInsertId() |
||
411 | |||
412 | /** |
||
413 | * Specify the default schema to use in cases where a schema is not provided |
||
414 | * as part of the table reference. |
||
415 | * |
||
416 | * @param string $defaultSchema |
||
417 | */ |
||
418 | public function setDefaultSchema($defaultSchema) |
||
422 | |||
423 | abstract protected function getDriverName(); |
||
424 | |||
425 | 3 | abstract public function quoteIdentifier($identifier); |
|
426 | |||
427 | 3 | public function setCleanDefaults($cleanDefaults) |
|
431 | } |
||
432 |
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.