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 BaseHtml 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 BaseHtml, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
16 | abstract class BaseHtml extends BaseWidget { |
||
17 | protected $_template; |
||
18 | protected $tagName; |
||
19 | protected $properties=array (); |
||
20 | protected $_events=array (); |
||
21 | protected $_wrapBefore=array (); |
||
22 | protected $_wrapAfter=array (); |
||
23 | protected $_bsComponent; |
||
24 | |||
25 | public function getBsComponent() { |
||
28 | |||
29 | public function setBsComponent($bsComponent) { |
||
33 | |||
34 | protected function getTemplate(JsUtils $js=NULL) { |
||
35 | return PropertyWrapper::wrap($this->_wrapBefore, $js) . $this->_template . PropertyWrapper::wrap($this->_wrapAfter, $js); |
||
36 | } |
||
37 | |||
38 | public function getProperties() { |
||
41 | |||
42 | public function setProperties($properties) { |
||
46 | |||
47 | public function setProperty($name, $value) { |
||
51 | |||
52 | public function getProperty($name) { |
||
56 | |||
57 | public function addToProperty($name, $value, $separator=" ") { |
||
58 | if (\is_array($value)) { |
||
59 | foreach ( $value as $v ) { |
||
60 | $this->addToProperty($name, $v, $separator); |
||
61 | } |
||
62 | } else if ($value !== "" && $this->propertyContains($name, $value) === false) { |
||
63 | $v=@$this->properties[$name]; |
||
64 | if (isset($v) && $v !== "") |
||
65 | $v=$v . $separator . $value; |
||
66 | else |
||
67 | $v=$value; |
||
68 | |||
69 | return $this->setProperty($name, $v); |
||
70 | } |
||
71 | return $this; |
||
72 | } |
||
73 | |||
74 | public function addProperties($properties) { |
||
75 | $this->properties=array_merge($this->properties, $properties); |
||
76 | return $this; |
||
77 | } |
||
78 | |||
79 | public function compile(JsUtils $js=NULL, $view=NULL) { |
||
80 | $result=$this->getTemplate($js); |
||
81 | foreach ( $this as $key => $value ) { |
||
|
|||
82 | if (JString::startswith($key, "_") === false && $key !== "events") { |
||
83 | if (is_array($value)) { |
||
84 | $v=PropertyWrapper::wrap($value, $js); |
||
85 | } else { |
||
86 | $v=$value; |
||
87 | } |
||
88 | $result=str_ireplace("%" . $key . "%", $v, $result); |
||
89 | } |
||
90 | } |
||
91 | if (isset($js)===true) { |
||
92 | $this->run($js); |
||
93 | if (isset($view) === true) { |
||
94 | $js->addViewElement($this->identifier, $result, $view); |
||
95 | } |
||
96 | } |
||
97 | return $result; |
||
98 | } |
||
99 | |||
100 | protected function ctrl($name, $value, $typeCtrl) { |
||
101 | View Code Duplication | if (is_array($typeCtrl)) { |
|
102 | if (array_search($value, $typeCtrl) === false) { |
||
103 | throw new \Exception("La valeur passée `" . $value . "` à la propriété `" . $name . "` ne fait pas partie des valeurs possibles : {" . implode(",", $typeCtrl) . "}"); |
||
104 | } |
||
105 | } else { |
||
106 | if (!$typeCtrl($value)) { |
||
107 | throw new \Exception("La fonction " . $typeCtrl . " a retourné faux pour l'affectation de la propriété " . $name); |
||
108 | } |
||
109 | } |
||
110 | return true; |
||
111 | } |
||
112 | |||
113 | protected function propertyContains($propertyName, $value) { |
||
114 | $values=$this->getProperty($propertyName); |
||
115 | if (isset($values)) { |
||
116 | return JString::contains($values, $value); |
||
117 | } |
||
118 | return false; |
||
119 | } |
||
120 | |||
121 | protected function setPropertyCtrl($name, $value, $typeCtrl) { |
||
122 | if ($this->ctrl($name, $value, $typeCtrl) === true) |
||
123 | return $this->setProperty($name, $value); |
||
124 | return $this; |
||
125 | } |
||
126 | |||
127 | protected function setMemberCtrl(&$name, $value, $typeCtrl) { |
||
128 | if ($this->ctrl($name, $value, $typeCtrl) === true) { |
||
129 | return $name=$value; |
||
130 | } |
||
131 | return $this; |
||
132 | } |
||
133 | |||
134 | protected function addToMemberUnique(&$name, $value, $typeCtrl, $separator=" ") { |
||
135 | if (is_array($typeCtrl)) { |
||
136 | $this->removeOldValues($name, $typeCtrl); |
||
137 | $name.=$separator . $value; |
||
138 | } |
||
139 | return $this; |
||
140 | } |
||
141 | |||
142 | protected function removePropertyValue($name, $value) { |
||
143 | $this->properties[$name]=\str_replace($value, "", $this->properties[$name]); |
||
144 | return $this; |
||
145 | } |
||
146 | |||
147 | protected function removePropertyValues($name, $values) { |
||
148 | $this->removeOldValues($this->properties[$name], $values); |
||
149 | return $this; |
||
150 | } |
||
151 | |||
152 | protected function removeProperty($name) { |
||
153 | if (\array_key_exists($name, $this->properties)) |
||
154 | unset($this->properties[$name]); |
||
155 | return $this; |
||
156 | } |
||
157 | |||
158 | protected function addToMemberCtrl(&$name, $value, $typeCtrl, $separator=" ") { |
||
159 | if ($this->ctrl($name, $value, $typeCtrl) === true) { |
||
160 | if (is_array($typeCtrl)) { |
||
161 | $this->removeOldValues($name, $typeCtrl); |
||
162 | } |
||
163 | $name.=$separator . $value; |
||
164 | } |
||
165 | return $this; |
||
166 | } |
||
167 | |||
168 | protected function addToMember(&$name, $value, $separator=" ") { |
||
169 | $name=str_ireplace($value, "", $name) . $separator . $value; |
||
170 | return $this; |
||
171 | } |
||
172 | |||
173 | protected function addToPropertyUnique($name, $value, $typeCtrl) { |
||
174 | if (@class_exists($typeCtrl, true)) |
||
175 | $typeCtrl=$typeCtrl::getConstants(); |
||
176 | if (is_array($typeCtrl)) { |
||
177 | $this->removeOldValues($this->properties[$name], $typeCtrl); |
||
178 | } |
||
179 | return $this->addToProperty($name, $value); |
||
180 | } |
||
181 | |||
182 | public function addToPropertyCtrl($name, $value, $typeCtrl) { |
||
183 | return $this->addToPropertyUnique($name, $value, $typeCtrl); |
||
184 | } |
||
185 | |||
186 | public function addToPropertyCtrlCheck($name, $value, $typeCtrl) { |
||
187 | if ($this->ctrl($name, $value, $typeCtrl) === true) { |
||
188 | return $this->addToProperty($name, $value); |
||
189 | } |
||
190 | return $this; |
||
191 | } |
||
192 | |||
193 | protected function removeOldValues(&$oldValue, $allValues) { |
||
194 | $oldValue=str_ireplace($allValues, "", $oldValue); |
||
195 | $oldValue=trim($oldValue); |
||
196 | } |
||
197 | |||
198 | /** |
||
199 | * |
||
200 | * @param JsUtils $js |
||
201 | * @return SimpleExtComponent |
||
202 | */ |
||
203 | public abstract function run(JsUtils $js); |
||
204 | |||
205 | public function getTagName() { |
||
206 | return $this->tagName; |
||
207 | } |
||
208 | |||
209 | public function setTagName($tagName) { |
||
210 | $this->tagName=$tagName; |
||
211 | return $this; |
||
212 | } |
||
213 | |||
214 | public function fromArray($array) { |
||
215 | foreach ( $this as $key => $value ) { |
||
216 | if (array_key_exists($key, $array) && !JString::startswith($key, "_")) { |
||
217 | $setter="set" . ucfirst($key); |
||
218 | $this->$setter($array[$key]); |
||
219 | unset($array[$key]); |
||
220 | } |
||
221 | } |
||
222 | foreach ( $array as $key => $value ) { |
||
223 | if (method_exists($this, $key)) { |
||
224 | try { |
||
225 | $this->$key($value); |
||
226 | unset($array[$key]); |
||
227 | } catch ( \Exception $e ) { |
||
228 | // Nothing to do |
||
229 | } |
||
230 | } else { |
||
231 | $setter="set" . ucfirst($key); |
||
232 | if (method_exists($this, $setter)) { |
||
233 | try { |
||
234 | $this->$setter($value); |
||
235 | unset($array[$key]); |
||
236 | } catch ( \Exception $e ) { |
||
237 | // Nothing to do |
||
238 | } |
||
239 | } |
||
240 | } |
||
241 | } |
||
242 | return $array; |
||
243 | } |
||
244 | |||
245 | public function fromDatabaseObjects($objects, $function) { |
||
246 | if (isset($objects)) { |
||
247 | foreach ( $objects as $object ) { |
||
248 | $this->fromDatabaseObject($object, $function); |
||
249 | } |
||
250 | } |
||
251 | return $this; |
||
252 | } |
||
253 | |||
254 | public function fromDatabaseObject($object, $function) { |
||
255 | } |
||
256 | |||
257 | public function wrap($before, $after="") { |
||
258 | if (isset($before)) { |
||
259 | array_unshift($this->_wrapBefore, $before); |
||
260 | // $this->_wrapBefore[]=$before; |
||
261 | } |
||
262 | $this->_wrapAfter[]=$after; |
||
263 | return $this; |
||
264 | } |
||
265 | |||
266 | public function addEvent($event, $jsCode, $stopPropagation=false, $preventDefault=false) { |
||
267 | if ($stopPropagation === true) { |
||
268 | $jsCode="event.stopPropagation();" . $jsCode; |
||
269 | } |
||
270 | if ($preventDefault === true) { |
||
271 | $jsCode="event.preventDefault();" . $jsCode; |
||
272 | } |
||
273 | return $this->_addEvent($event, $jsCode); |
||
274 | } |
||
275 | |||
276 | public function _addEvent($event, $jsCode) { |
||
277 | if (array_key_exists($event, $this->_events)) { |
||
278 | if (is_array($this->_events[$event])) { |
||
279 | $this->_events[$event][]=$jsCode; |
||
280 | } else { |
||
281 | $this->_events[$event]=array ($this->_events[$event],$jsCode ); |
||
282 | } |
||
283 | } else { |
||
284 | $this->_events[$event]=$jsCode; |
||
285 | } |
||
286 | return $this; |
||
287 | } |
||
288 | |||
289 | public function on($event, $jsCode, $stopPropagation=false, $preventDefault=false) { |
||
290 | return $this->addEvent($event, $jsCode, $stopPropagation, $preventDefault); |
||
291 | } |
||
292 | |||
293 | public function onClick($jsCode, $stopPropagation=false, $preventDefault=true) { |
||
294 | return $this->on("click", $jsCode, $stopPropagation, $preventDefault); |
||
295 | } |
||
296 | |||
297 | public function setClick($jsCode) { |
||
298 | return $this->onClick($jsCode); |
||
299 | } |
||
300 | |||
301 | public function addEventsOnRun(JsUtils $js) { |
||
302 | if (isset($this->_bsComponent)) { |
||
303 | foreach ( $this->_events as $event => $jsCode ) { |
||
304 | $code=$jsCode; |
||
305 | if (is_array($jsCode)) { |
||
306 | $code=""; |
||
307 | foreach ( $jsCode as $jsC ) { |
||
308 | if ($jsC instanceof AjaxCall) { |
||
309 | $code.="\n" . $jsC->compile($js); |
||
310 | } else { |
||
311 | $code.="\n" . $jsC; |
||
312 | } |
||
313 | } |
||
314 | } elseif ($jsCode instanceof AjaxCall) { |
||
315 | $code=$jsCode->compile($js); |
||
316 | } |
||
317 | $this->_bsComponent->addEvent($event, $code); |
||
318 | } |
||
319 | $this->_events=array (); |
||
320 | } |
||
321 | } |
||
322 | |||
323 | public function _ajaxOn($operation, $event, $url, $responseElement="", $parameters=array()) { |
||
324 | $params=array ("url" => $url,"responseElement" => $responseElement ); |
||
325 | $params=array_merge($params, $parameters); |
||
326 | $this->_addEvent($event, new AjaxCall($operation, $params)); |
||
327 | return $this; |
||
328 | } |
||
329 | |||
330 | public function getOn($event, $url, $responseElement="", $parameters=array()) { |
||
333 | |||
334 | public function getOnClick($url, $responseElement="", $parameters=array()) { |
||
337 | |||
338 | public function postOn($event, $url, $params="{}", $responseElement="", $parameters=array()) { |
||
342 | |||
343 | public function postOnClick($url, $params="{}", $responseElement="", $parameters=array()) { |
||
346 | |||
347 | public function postFormOn($event, $url, $form, $responseElement="", $parameters=array()) { |
||
351 | |||
352 | public function postFormOnClick($url, $form, $responseElement="", $parameters=array()) { |
||
355 | |||
356 | public function getElementById($identifier, $elements) { |
||
357 | if (is_array($elements)) { |
||
358 | $flag=false; |
||
359 | $index=0; |
||
360 | View Code Duplication | while ( !$flag && $index < sizeof($elements) ) { |
|
361 | if ($elements[$index] instanceof BaseHtml) |
||
362 | $flag=($elements[$index]->getIdentifier() === $identifier); |
||
363 | $index++; |
||
364 | } |
||
365 | if ($flag === true) |
||
366 | return $elements[$index - 1]; |
||
367 | } elseif ($elements instanceof BaseHtml) { |
||
368 | if ($elements->getIdentifier() === $identifier) |
||
369 | return $elements; |
||
370 | } |
||
371 | return null; |
||
372 | } |
||
373 | |||
374 | protected function getElementByPropertyValue($propertyName,$value, $elements) { |
||
375 | if (is_array($elements)) { |
||
376 | $flag=false; |
||
377 | $index=0; |
||
378 | View Code Duplication | while ( !$flag && $index < sizeof($elements) ) { |
|
379 | if ($elements[$index] instanceof BaseHtml) |
||
380 | $flag=($elements[$index]->propertyContains($propertyName, $value) === true); |
||
381 | $index++; |
||
382 | } |
||
383 | if ($flag === true) |
||
384 | return $elements[$index - 1]; |
||
385 | } elseif ($elements instanceof BaseHtml) { |
||
386 | if ($elements->propertyContains($propertyName, $value) === true) |
||
387 | return $elements; |
||
388 | } |
||
391 | |||
392 | public function __toString() { |
||
395 | |||
396 | /** |
||
397 | * Puts HTML values in quotes for use in jQuery code |
||
398 | * unless the supplied value contains the Javascript 'this' or 'event' |
||
399 | * object, in which case no quotes are added |
||
400 | * |
||
401 | * @param string $value |
||
402 | * @return string |
||
403 | */ |
||
404 | public function _prep_value($value) { |
||
413 | |||
414 | public function jsDoJquery($jqueryCall, $param="") { |
||
417 | |||
418 | public function executeOnRun($jsCode) { |
||
421 | |||
422 | public function jsHtml($content="") { |
||
425 | |||
426 | public function jsShow() { |
||
429 | |||
430 | public function jsHide() { |
||
433 | |||
434 | protected function setWrapBefore($wrapBefore) { |
||
438 | |||
439 | protected function setWrapAfter($wrapAfter) { |
||
443 | } |