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 PoStreamReader 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 PoStreamReader, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
18 | class PoStreamReader implements PoStreamInterface, PoReaderInterface { |
||
19 | |||
20 | /** |
||
21 | * Source line number of the stream being parsed. |
||
22 | * |
||
23 | * @var int |
||
24 | */ |
||
25 | private $_line_number = 0; |
||
26 | |||
27 | /** |
||
28 | * Parser context for the stream reader state machine. |
||
29 | * |
||
30 | * Possible contexts are: |
||
31 | * - 'COMMENT' (#) |
||
32 | * - 'MSGID' (msgid) |
||
33 | * - 'MSGID_PLURAL' (msgid_plural) |
||
34 | * - 'MSGCTXT' (msgctxt) |
||
35 | * - 'MSGSTR' (msgstr or msgstr[]) |
||
36 | * - 'MSGSTR_ARR' (msgstr_arg) |
||
37 | * |
||
38 | * @var string |
||
39 | */ |
||
40 | private $_context = 'COMMENT'; |
||
41 | |||
42 | /** |
||
43 | * Current entry being read. Incomplete. |
||
44 | * |
||
45 | * @var array |
||
46 | */ |
||
47 | private $_current_item = array(); |
||
48 | |||
49 | /** |
||
50 | * Current plural index for plural translations. |
||
51 | * |
||
52 | * @var int |
||
53 | */ |
||
54 | private $_current_plural_index = 0; |
||
55 | |||
56 | /** |
||
57 | * URI of the PO stream that is being read. |
||
58 | * |
||
59 | * @var string |
||
60 | */ |
||
61 | private $_uri = ''; |
||
62 | |||
63 | /** |
||
64 | * Language code for the PO stream being read. |
||
65 | * |
||
66 | * @var string |
||
67 | */ |
||
68 | private $_langcode = NULL; |
||
69 | |||
70 | /** |
||
71 | * File handle of the current PO stream. |
||
72 | * |
||
73 | * @var resource |
||
74 | */ |
||
75 | private $_fd; |
||
76 | |||
77 | /** |
||
78 | * The PO stream header. |
||
79 | * |
||
80 | * @var \Drupal\Component\Gettext\PoHeader |
||
81 | */ |
||
82 | private $_header; |
||
83 | |||
84 | /** |
||
85 | * Object wrapper for the last read source/translation pair. |
||
86 | * |
||
87 | * @var \Drupal\Component\Gettext\PoItem |
||
88 | */ |
||
89 | private $_last_item; |
||
90 | |||
91 | /** |
||
92 | * Indicator of whether the stream reading is finished. |
||
93 | * |
||
94 | * @var bool |
||
95 | */ |
||
96 | private $_finished; |
||
97 | |||
98 | /** |
||
99 | * Array of translated error strings recorded on reading this stream so far. |
||
100 | * |
||
101 | * @var array |
||
102 | */ |
||
103 | private $_errors; |
||
104 | |||
105 | /** |
||
106 | * {@inheritdoc} |
||
107 | */ |
||
108 | public function getLangcode() { |
||
111 | |||
112 | /** |
||
113 | * {@inheritdoc} |
||
114 | */ |
||
115 | public function setLangcode($langcode) { |
||
118 | |||
119 | /** |
||
120 | * {@inheritdoc} |
||
121 | */ |
||
122 | public function getHeader() { |
||
125 | |||
126 | /** |
||
127 | * Implements Drupal\Component\Gettext\PoMetadataInterface::setHeader(). |
||
128 | * |
||
129 | * Not applicable to stream reading and therefore not implemented. |
||
130 | */ |
||
131 | public function setHeader(PoHeader $header) { |
||
132 | } |
||
133 | |||
134 | /** |
||
135 | * {@inheritdoc} |
||
136 | */ |
||
137 | public function getURI() { |
||
140 | |||
141 | /** |
||
142 | * {@inheritdoc} |
||
143 | */ |
||
144 | public function setURI($uri) { |
||
147 | |||
148 | /** |
||
149 | * Implements Drupal\Component\Gettext\PoStreamInterface::open(). |
||
150 | * |
||
151 | * Opens the stream and reads the header. The stream is ready for reading |
||
152 | * items after. |
||
153 | * |
||
154 | * @throws Exception |
||
155 | * If the URI is not yet set. |
||
156 | */ |
||
157 | public function open() { |
||
158 | if (!empty($this->_uri)) { |
||
159 | $this->_fd = fopen($this->_uri, 'rb'); |
||
160 | $this->readHeader(); |
||
161 | } |
||
162 | else { |
||
163 | throw new \Exception('Cannot open stream without URI set.'); |
||
164 | } |
||
165 | } |
||
166 | |||
167 | /** |
||
168 | * Implements Drupal\Component\Gettext\PoStreamInterface::close(). |
||
169 | * |
||
170 | * @throws Exception |
||
171 | * If the stream is not open. |
||
172 | */ |
||
173 | public function close() { |
||
174 | if ($this->_fd) { |
||
175 | fclose($this->_fd); |
||
176 | } |
||
177 | else { |
||
178 | throw new \Exception('Cannot close stream that is not open.'); |
||
179 | } |
||
180 | } |
||
181 | |||
182 | /** |
||
183 | * {@inheritdoc} |
||
184 | */ |
||
185 | public function readItem() { |
||
196 | |||
197 | /** |
||
198 | * Sets the seek position for the current PO stream. |
||
199 | * |
||
200 | * @param int $seek |
||
201 | * The new seek position to set. |
||
202 | */ |
||
203 | public function setSeek($seek) { |
||
206 | |||
207 | /** |
||
208 | * Gets the pointer position of the current PO stream. |
||
209 | */ |
||
210 | public function getSeek() { |
||
213 | |||
214 | /** |
||
215 | * Read the header from the PO stream. |
||
216 | * |
||
217 | * The header is a special case PoItem, using the empty string as source and |
||
218 | * key-value pairs as translation. We just reuse the item reader logic to |
||
219 | * read the header. |
||
220 | */ |
||
221 | private function readHeader() { |
||
231 | |||
232 | /** |
||
233 | * Reads a line from the PO stream and stores data internally. |
||
234 | * |
||
235 | * Expands $this->_current_item based on new data for the current item. If |
||
236 | * this line ends the current item, it is saved with setItemFromArray() with |
||
237 | * data from $this->_current_item. |
||
238 | * |
||
239 | * An internal state machine is maintained in this reader using |
||
240 | * $this->_context as the reading state. PO items are in between COMMENT |
||
241 | * states (when items have at least one line or comment in between them) or |
||
242 | * indicated by MSGSTR or MSGSTR_ARR followed immediately by an MSGID or |
||
243 | * MSGCTXT (when items closely follow each other). |
||
244 | * |
||
245 | * @return |
||
246 | * FALSE if an error was logged, NULL otherwise. The errors are considered |
||
247 | * non-blocking, so reading can continue, while the errors are collected |
||
248 | * for later presentation. |
||
249 | */ |
||
250 | private function readLine() { |
||
515 | |||
516 | /** |
||
517 | * Store the parsed values as a PoItem object. |
||
518 | */ |
||
519 | public function setItemFromArray($value) { |
||
545 | |||
546 | /** |
||
547 | * Parses a string in quotes. |
||
548 | * |
||
549 | * @param $string |
||
550 | * A string specified with enclosing quotes. |
||
551 | * |
||
552 | * @return |
||
553 | * The string parsed from inside the quotes. |
||
554 | */ |
||
555 | function parseQuoted($string) { |
||
575 | |||
576 | /** |
||
577 | * Generates a short, one-string version of the passed comment array. |
||
578 | * |
||
579 | * @param $comment |
||
580 | * An array of strings containing a comment. |
||
581 | * |
||
582 | * @return |
||
583 | * Short one-string version of the comment. |
||
584 | */ |
||
585 | private function shortenComments($comment) { |
||
598 | |||
599 | } |
||
600 |
This method has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.