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 midcom_db_attachment 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 midcom_db_attachment, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 14 | class midcom_db_attachment extends midcom_core_dbaobject |
||
|
1 ignored issue
–
show
|
|||
| 15 | { |
||
| 16 | public $__midcom_class_name__ = __CLASS__; |
||
| 17 | public $__mgdschema_class_name__ = 'midgard_attachment'; |
||
| 18 | |||
| 19 | public $_use_activitystream = false; |
||
| 20 | public $_use_rcs = false; |
||
| 21 | |||
| 22 | /** |
||
| 23 | * Internal tracking state variable, holds the file handle of any open |
||
| 24 | * attachment. |
||
| 25 | * |
||
| 26 | * @var resource |
||
| 27 | */ |
||
| 28 | private $_open_handle = null; |
||
| 29 | |||
| 30 | /** |
||
| 31 | * Internal tracking state variable, true if the attachment has a handle opened in write mode |
||
| 32 | */ |
||
| 33 | private $_open_write_mode = false; |
||
| 34 | |||
| 35 | public function get_parent_guid_uncached() |
||
| 39 | |||
| 40 | /** |
||
| 41 | * Returns the Parent of the Attachment, which is identified by the table/id combination |
||
| 42 | * in the attachment record. The table in question is used to identify the object to |
||
| 43 | * use. If multiple objects are registered for a given table, the first matching class |
||
| 44 | * returned by the dbfactory is used (which is usually rather arbitrary). |
||
| 45 | * |
||
| 46 | * @return string Parent GUID. |
||
| 47 | */ |
||
| 48 | View Code Duplication | public static function get_parent_guid_uncached_static($guid, $classname = __CLASS__) |
|
| 49 | { |
||
| 50 | $mc = new midgard_collector('midgard_attachment', 'guid', $guid); |
||
| 51 | $mc->set_key_property('parentguid'); |
||
| 52 | $mc->execute(); |
||
| 53 | $link_values = $mc->list_keys(); |
||
| 54 | if (empty($link_values)) |
||
| 55 | { |
||
| 56 | return null; |
||
| 57 | } |
||
| 58 | return key($link_values); |
||
| 59 | } |
||
| 60 | |||
| 61 | /** |
||
| 62 | * Opens the attachment for file IO. |
||
| 63 | * |
||
| 64 | * Returns a filehandle that can be used with the usual PHP file functions if successful, |
||
| 65 | * the handle has to be closed with the close() method when you no longer need it, don't |
||
| 66 | * let it fall over the end of the script. |
||
| 67 | * |
||
| 68 | * <b>Important Note:</b> It is important to use the close() member function of |
||
| 69 | * this class to close the file handle, not just fclose(). Otherwise, the upgrade |
||
| 70 | * notification switches will fail. |
||
| 71 | * |
||
| 72 | * @param string $mode The mode which should be used to open the attachment, same as |
||
| 73 | * the mode parameter of the PHP fopen call. This defaults to write access (see |
||
| 74 | * mgd_open_attachmentl for details). |
||
| 75 | * @return resource A file handle to the attachment if successful, false on failure. |
||
| 76 | */ |
||
| 77 | public function open($mode = 'default') |
||
| 78 | { |
||
| 79 | View Code Duplication | if (! $this->id) |
|
| 80 | { |
||
| 81 | debug_add('Cannot open a non-persistent attachment.', MIDCOM_LOG_WARN); |
||
| 82 | debug_print_r('Object state:', $this); |
||
| 83 | return false; |
||
| 84 | } |
||
| 85 | |||
| 86 | if ($this->_open_handle !== null) |
||
| 87 | { |
||
| 88 | debug_add("Warning, the Attachment {$this->id} already had an open file handle, we close it implicitly.", MIDCOM_LOG_WARN); |
||
| 89 | fclose($this->_open_handle); |
||
| 90 | $this->_open_handle = null; |
||
| 91 | } |
||
| 92 | |||
| 93 | $blob = new midgard_blob($this->__object); |
||
| 94 | if ($mode == 'default') |
||
| 95 | { |
||
| 96 | $this->_open_write_mode = true; |
||
| 97 | $handle = $blob->get_handler(); |
||
| 98 | } |
||
| 99 | else |
||
| 100 | { |
||
| 101 | /* WARNING, read mode not supported by midgard_blob! */ |
||
| 102 | $this->_open_write_mode = ($mode{0} != 'r'); |
||
| 103 | $handle = fopen($blob->get_path(), $mode); |
||
| 104 | } |
||
| 105 | |||
| 106 | if (!$handle) |
||
| 107 | { |
||
| 108 | debug_add("Failed to open attachment with mode {$mode}, last Midgard error was: " . midcom_connection::get_error_string(), MIDCOM_LOG_WARN); |
||
| 109 | } |
||
| 110 | |||
| 111 | $this->_open_handle = $handle; |
||
| 112 | |||
| 113 | return $handle; |
||
| 114 | } |
||
| 115 | |||
| 116 | /** |
||
| 117 | * Read the file and return its contents |
||
| 118 | * |
||
| 119 | * @return string |
||
| 120 | */ |
||
| 121 | public function read() |
||
| 128 | |||
| 129 | /** |
||
| 130 | * Close the open write handle obtained by the open() call again. |
||
| 131 | * It is required to call this function instead of a simple fclose to ensure proper |
||
| 132 | * upgrade notifications. |
||
| 133 | */ |
||
| 134 | public function close() |
||
| 158 | |||
| 159 | /** |
||
| 160 | * Rewrite a filename to URL safe form |
||
| 161 | * |
||
| 162 | * @param string $filename file name to rewrite |
||
| 163 | * @param boolean $force_single_extension force file to single extension (defaults to true) |
||
| 164 | * @return string rewritten filename |
||
| 165 | * @todo add possibility to use the file utility to determine extension if missing. |
||
| 166 | */ |
||
| 167 | public static function safe_filename($filename, $force_single_extension = true) |
||
| 190 | |||
| 191 | /** |
||
| 192 | * Get the path to the document in the static cache |
||
| 193 | * |
||
| 194 | * @return string |
||
| 195 | */ |
||
| 196 | public function get_cache_path() |
||
| 213 | |||
| 214 | public static function get_url($attachment, $name = null) |
||
| 249 | |||
| 250 | function file_to_cache() |
||
| 308 | |||
| 309 | /** |
||
| 310 | * Simple wrapper for stat() on the blob object. |
||
| 311 | * |
||
| 312 | * @return mixed Either a stat array as for stat() or false on failure. |
||
| 313 | */ |
||
| 314 | public function stat() |
||
| 315 | { |
||
| 316 | View Code Duplication | if (!$this->id) |
|
| 317 | { |
||
| 318 | debug_add('Cannot open a non-persistent attachment.', MIDCOM_LOG_WARN); |
||
| 319 | debug_print_r('Object state:', $this); |
||
| 320 | return false; |
||
| 321 | } |
||
| 322 | |||
| 323 | $blob = new midgard_blob($this->__object); |
||
| 324 | |||
| 325 | $path = $blob->get_path(); |
||
| 326 | if (!file_exists($path)) |
||
| 327 | { |
||
| 328 | debug_add("File {$path} that blob {$this->guid} points to cannot be found", MIDCOM_LOG_WARN); |
||
| 329 | return false; |
||
| 330 | } |
||
| 331 | |||
| 332 | return stat($path); |
||
| 333 | } |
||
| 334 | |||
| 335 | /** |
||
| 336 | * Internal helper, computes an MD5 string which is used as an attachment location. |
||
| 337 | * It should be random enough, even if the algorithm used does not match the one |
||
| 338 | * Midgard uses. If the location already exists, it will iterate until an unused |
||
| 339 | * location is found. |
||
| 340 | * |
||
| 341 | * @return string An unused attachment location. |
||
| 342 | */ |
||
| 343 | private function _create_attachment_location() |
||
| 366 | |||
| 367 | /** |
||
| 368 | * Simple creation event handler which fills out the location field if it |
||
| 369 | * is still empty with a location generated by _create_attachment_location(). |
||
| 370 | * |
||
| 371 | * @return boolean True if creation may commence. |
||
| 372 | */ |
||
| 373 | public function _on_creating() |
||
| 384 | |||
| 385 | function update_cache() |
||
| 400 | |||
| 401 | /** |
||
| 402 | * Updated callback, triggers watches on the parent(!) object. |
||
| 403 | */ |
||
| 404 | public function _on_updated() |
||
| 408 | |||
| 409 | /** |
||
| 410 | * Deleted callback, triggers watches on the parent(!) object. |
||
| 411 | */ |
||
| 412 | public function _on_deleted() |
||
| 424 | |||
| 425 | /** |
||
| 426 | * Updates the contents of the attachments with the contents given. |
||
| 427 | * |
||
| 428 | * @param mixed $source File contents. |
||
| 429 | * @return boolean Indicating success. |
||
| 430 | */ |
||
| 431 | View Code Duplication | public function copy_from_memory($source) |
|
| 445 | |||
| 446 | /** |
||
| 447 | * Updates the contents of the attachments with the contents of the resource identified |
||
| 448 | * by the filehandle passed. |
||
| 449 | * |
||
| 450 | * @param resource $source The handle to read from. |
||
| 451 | * @return boolean Indicating success. |
||
| 452 | */ |
||
| 453 | View Code Duplication | public function copy_from_handle($source) |
|
| 467 | |||
| 468 | /** |
||
| 469 | * Updates the contents of the attachments with the contents of the file specified. |
||
| 470 | * This is a wrapper for copy_from_handle. |
||
| 471 | * |
||
| 472 | * @param string $filename The file to read. |
||
| 473 | * @return boolean Indicating success. |
||
| 474 | */ |
||
| 475 | public function copy_from_file($filename) |
||
| 488 | } |
||
| 489 |
You can fix this by adding a namespace to your class:
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.