Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like MongoDB 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 MongoDB, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
29 | class MongoDB |
||
30 | { |
||
31 | use Helper\ReadPreference; |
||
32 | use Helper\SlaveOkay; |
||
33 | use Helper\WriteConcern; |
||
34 | |||
35 | const PROFILING_OFF = 0; |
||
36 | const PROFILING_SLOW = 1; |
||
37 | const PROFILING_ON = 2; |
||
38 | |||
39 | /** |
||
40 | * @var MongoClient |
||
41 | */ |
||
42 | protected $connection; |
||
43 | |||
44 | /** |
||
45 | * @var \MongoDB\Database |
||
46 | */ |
||
47 | protected $db; |
||
48 | |||
49 | /** |
||
50 | * @var string |
||
51 | */ |
||
52 | protected $name; |
||
53 | |||
54 | /** |
||
55 | * Creates a new database |
||
56 | * |
||
57 | * This method is not meant to be called directly. The preferred way to create an instance of MongoDB is through {@see Mongo::__get()} or {@see Mongo::selectDB()}. |
||
58 | * @link http://www.php.net/manual/en/mongodb.construct.php |
||
59 | * @param MongoClient $conn Database connection. |
||
60 | * @param string $name Database name. |
||
61 | * @throws Exception |
||
62 | */ |
||
63 | public function __construct(MongoClient $conn, $name) |
||
64 | { |
||
65 | $this->checkDatabaseName($name); |
||
66 | $this->connection = $conn; |
||
67 | $this->name = (string) $name; |
||
68 | |||
69 | $this->setReadPreferenceFromArray($conn->getReadPreference()); |
||
70 | $this->setWriteConcernFromArray($conn->getWriteConcern()); |
||
71 | |||
72 | $this->createDatabaseObject(); |
||
73 | } |
||
74 | |||
75 | /** |
||
76 | * @return \MongoDB\Database |
||
77 | * @internal This method is not part of the ext-mongo API |
||
78 | */ |
||
79 | public function getDb() |
||
83 | |||
84 | /** |
||
85 | * The name of this database |
||
86 | * |
||
87 | * @link http://www.php.net/manual/en/mongodb.--tostring.php |
||
88 | * @return string Returns this database's name. |
||
89 | */ |
||
90 | public function __toString() |
||
94 | |||
95 | /** |
||
96 | * Gets a collection |
||
97 | * |
||
98 | * @link http://www.php.net/manual/en/mongodb.get.php |
||
99 | * @param string $name The name of the collection. |
||
100 | * @return MongoCollection |
||
101 | */ |
||
102 | public function __get($name) |
||
111 | |||
112 | /** |
||
113 | * @param string $name |
||
114 | * @param mixed $value |
||
115 | */ |
||
116 | public function __set($name, $value) |
||
122 | |||
123 | /** |
||
124 | * Returns information about collections in this database |
||
125 | * |
||
126 | * @link http://www.php.net/manual/en/mongodb.getcollectioninfo.php |
||
127 | * @param array $options An array of options for listing the collections. |
||
128 | * @return array |
||
129 | */ |
||
130 | public function getCollectionInfo(array $options = []) |
||
170 | |||
171 | /** |
||
172 | * Get all collections from this database |
||
173 | * |
||
174 | * @link http://www.php.net/manual/en/mongodb.getcollectionnames.php |
||
175 | * @param array $options An array of options for listing the collections. |
||
176 | * @return array Returns the names of the all the collections in the database as an array |
||
177 | */ |
||
178 | public function getCollectionNames(array $options = []) |
||
204 | |||
205 | /** |
||
206 | * @return MongoClient |
||
207 | * @internal This method is not part of the ext-mongo API |
||
208 | */ |
||
209 | public function getConnection() |
||
213 | |||
214 | /** |
||
215 | * Fetches toolkit for dealing with files stored in this database |
||
216 | * |
||
217 | * @link http://www.php.net/manual/en/mongodb.getgridfs.php |
||
218 | * @param string $prefix The prefix for the files and chunks collections. |
||
219 | * @return MongoGridFS Returns a new gridfs object for this database. |
||
220 | */ |
||
221 | public function getGridFS($prefix = "fs") |
||
225 | |||
226 | /** |
||
227 | * Gets this database's profiling level |
||
228 | * |
||
229 | * @link http://www.php.net/manual/en/mongodb.getprofilinglevel.php |
||
230 | * @return int Returns the profiling level. |
||
231 | */ |
||
232 | public function getProfilingLevel() |
||
238 | |||
239 | /** |
||
240 | * Sets this database's profiling level |
||
241 | * |
||
242 | * @link http://www.php.net/manual/en/mongodb.setprofilinglevel.php |
||
243 | * @param int $level Profiling level. |
||
244 | * @return int Returns the previous profiling level. |
||
245 | */ |
||
246 | public function setProfilingLevel($level) |
||
252 | |||
253 | /** |
||
254 | * Drops this database |
||
255 | * |
||
256 | * @link http://www.php.net/manual/en/mongodb.drop.php |
||
257 | * @return array Returns the database response. |
||
258 | */ |
||
259 | public function drop() |
||
263 | |||
264 | /** |
||
265 | * Repairs and compacts this database |
||
266 | * |
||
267 | * @link http://www.php.net/manual/en/mongodb.repair.php |
||
268 | * @param bool $preserve_cloned_files [optional] <p>If cloned files should be kept if the repair fails.</p> |
||
269 | * @param bool $backup_original_files [optional] <p>If original files should be backed up.</p> |
||
270 | * @return array <p>Returns db response.</p> |
||
271 | */ |
||
272 | public function repair($preserve_cloned_files = false, $backup_original_files = false) |
||
282 | |||
283 | /** |
||
284 | * Gets a collection |
||
285 | * |
||
286 | * @link http://www.php.net/manual/en/mongodb.selectcollection.php |
||
287 | * @param string $name <b>The collection name.</b> |
||
288 | * @throws Exception if the collection name is invalid. |
||
289 | * @return MongoCollection Returns a new collection object. |
||
290 | */ |
||
291 | public function selectCollection($name) |
||
295 | |||
296 | /** |
||
297 | * Creates a collection |
||
298 | * |
||
299 | * @link http://www.php.net/manual/en/mongodb.createcollection.php |
||
300 | * @param string $name The name of the collection. |
||
301 | * @param array $options |
||
302 | * @return MongoCollection Returns a collection object representing the new collection. |
||
303 | */ |
||
304 | public function createCollection($name, $options = []) |
||
318 | |||
319 | /** |
||
320 | * Drops a collection |
||
321 | * |
||
322 | * @link http://www.php.net/manual/en/mongodb.dropcollection.php |
||
323 | * @param MongoCollection|string $coll MongoCollection or name of collection to drop. |
||
324 | * @return array Returns the database response. |
||
325 | * |
||
326 | * @deprecated Use MongoCollection::drop() instead. |
||
327 | */ |
||
328 | public function dropCollection($coll) |
||
336 | |||
337 | /** |
||
338 | * Get a list of collections in this database |
||
339 | * |
||
340 | * @link http://www.php.net/manual/en/mongodb.listcollections.php |
||
341 | * @param array $options |
||
342 | * @return MongoCollection[] Returns a list of MongoCollections. |
||
343 | */ |
||
344 | public function listCollections(array $options = []) |
||
348 | |||
349 | /** |
||
350 | * Creates a database reference |
||
351 | * |
||
352 | * @link http://www.php.net/manual/en/mongodb.createdbref.php |
||
353 | * @param string $collection The collection to which the database reference will point. |
||
354 | * @param mixed $document_or_id |
||
355 | * @return array Returns a database reference array. |
||
356 | */ |
||
357 | public function createDBRef($collection, $document_or_id) |
||
379 | |||
380 | |||
381 | /** |
||
382 | * Fetches the document pointed to by a database reference |
||
383 | * |
||
384 | * @link http://www.php.net/manual/en/mongodb.getdbref.php |
||
385 | * @param array $ref A database reference. |
||
386 | * @return array Returns the document pointed to by the reference. |
||
387 | */ |
||
388 | public function getDBRef(array $ref) |
||
393 | |||
394 | /** |
||
395 | * Runs JavaScript code on the database server. |
||
396 | * |
||
397 | * @link http://www.php.net/manual/en/mongodb.execute.php |
||
398 | * @param MongoCode|string $code Code to execute. |
||
399 | * @param array $args [optional] Arguments to be passed to code. |
||
400 | * @return array Returns the result of the evaluation. |
||
401 | */ |
||
402 | public function execute($code, array $args = []) |
||
406 | |||
407 | /** |
||
408 | * Execute a database command |
||
409 | * |
||
410 | * @link http://www.php.net/manual/en/mongodb.command.php |
||
411 | * @param array $data The query to send. |
||
412 | * @param array $options |
||
413 | * @return array Returns database response. |
||
414 | */ |
||
415 | public function command(array $data, $options = [], &$hash = null) |
||
426 | |||
427 | /** |
||
428 | * Check if there was an error on the most recent db operation performed |
||
429 | * |
||
430 | * @link http://www.php.net/manual/en/mongodb.lasterror.php |
||
431 | * @return array Returns the error, if there was one. |
||
432 | */ |
||
433 | public function lastError() |
||
437 | |||
438 | /** |
||
439 | * Checks for the last error thrown during a database operation |
||
440 | * |
||
441 | * @link http://www.php.net/manual/en/mongodb.preverror.php |
||
442 | * @return array Returns the error and the number of operations ago it occurred. |
||
443 | */ |
||
444 | public function prevError() |
||
448 | |||
449 | /** |
||
450 | * Clears any flagged errors on the database |
||
451 | * |
||
452 | * @link http://www.php.net/manual/en/mongodb.reseterror.php |
||
453 | * @return array Returns the database response. |
||
454 | */ |
||
455 | public function resetError() |
||
459 | |||
460 | /** |
||
461 | * Creates a database error |
||
462 | * |
||
463 | * @link http://www.php.net/manual/en/mongodb.forceerror.php |
||
464 | * @return boolean Returns the database response. |
||
465 | */ |
||
466 | public function forceError() |
||
470 | |||
471 | /** |
||
472 | * Log in to this database |
||
473 | * |
||
474 | * @link http://www.php.net/manual/en/mongodb.authenticate.php |
||
475 | * @param string $username The username. |
||
476 | * @param string $password The password (in plaintext). |
||
477 | * @return array Returns database response. If the login was successful, it will return 1. |
||
478 | * |
||
479 | * @deprecated This method is not implemented, supply authentication credentials through the connection string instead. |
||
480 | */ |
||
481 | public function authenticate($username, $password) |
||
485 | |||
486 | /** |
||
487 | * {@inheritdoc} |
||
488 | */ |
||
489 | public function setReadPreference($readPreference, $tags = null) |
||
496 | |||
497 | /** |
||
498 | * {@inheritdoc} |
||
499 | */ |
||
500 | public function setWriteConcern($wstring, $wtimeout = 0) |
||
507 | |||
508 | protected function notImplemented() |
||
512 | |||
513 | /** |
||
514 | * @return \MongoDB\Database |
||
515 | */ |
||
516 | View Code Duplication | private function createDatabaseObject() |
|
529 | |||
530 | private function checkDatabaseName($name) |
||
549 | |||
550 | /** |
||
551 | * @param bool $includeSystemCollections |
||
552 | * @return Closure |
||
553 | */ |
||
554 | private function getSystemCollectionFilterClosure($includeSystemCollections = false) |
||
560 | |||
561 | /** |
||
562 | * @return array |
||
563 | */ |
||
564 | public function __sleep() |
||
568 | } |
||
569 |
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.