Complex classes like Gallery 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 Gallery, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
23 | class Gallery extends ResultPrinter { |
||
24 | |||
25 | /** |
||
26 | * @see SMWResultPrinter::getName |
||
27 | * |
||
28 | * @return string |
||
29 | */ |
||
30 | public function getName() { |
||
33 | |||
34 | /** |
||
35 | * @see SMWResultPrinter::buildResult |
||
36 | * |
||
37 | * @since 1.8 |
||
38 | * |
||
39 | * @param SMWQueryResult $results |
||
40 | * |
||
41 | * @return string |
||
42 | */ |
||
43 | 1 | protected function buildResult( SMWQueryResult $results ) { |
|
44 | |||
45 | // Intro/outro are not planned to work with the widget option |
||
46 | 1 | if ( ( $this->params['intro'] !== '' || $this->params['outro'] !== '' ) && $this->params['widget'] !== '' ){ |
|
47 | return $results->addErrors( [ |
||
48 | $this->msg( 'srf-error-option-mix', 'widget' )->inContentLanguage()->text() |
||
49 | ] ); |
||
50 | }; |
||
51 | |||
52 | 1 | return $this->getResultText( $results, $this->outputMode ); |
|
|
|||
53 | } |
||
54 | |||
55 | /** |
||
56 | * @see SMWResultPrinter::getResultText |
||
57 | * |
||
58 | * @param $results SMWQueryResult |
||
59 | * @param $outputmode integer |
||
60 | * |
||
61 | * @return string |
||
62 | */ |
||
63 | 1 | public function getResultText( SMWQueryResult $results, $outputmode ) { |
|
64 | |||
65 | // #224 |
||
66 | 1 | $ig = class_exists( '\TraditionalImageGallery' ) ? new \TraditionalImageGallery() : new \ImageGallery(); |
|
67 | |||
68 | 1 | $ig->setShowBytes( false ); |
|
69 | 1 | $ig->setShowFilename( false ); |
|
70 | 1 | $ig->setCaption( $this->mIntro ); // set caption to IQ header |
|
71 | |||
72 | // No need for a special page to use the parser but for the "normal" page |
||
73 | // view we have to ensure caption text is parsed correctly through the parser |
||
74 | 1 | if ( !$this->isSpecialPage() ) { |
|
75 | 1 | $ig->setParser( $GLOBALS['wgParser'] ); |
|
76 | } |
||
77 | |||
78 | // Initialize |
||
79 | 1 | static $statNr = 0; |
|
80 | 1 | $html = ''; |
|
81 | 1 | $processing = ''; |
|
82 | |||
83 | 1 | if ( $this->params['widget'] == 'carousel' ) { |
|
84 | // Carousel widget |
||
85 | $ig->setAttributes( $this->getCarouselWidget() ); |
||
86 | 1 | } elseif ( $this->params['widget'] == 'slideshow' ) { |
|
87 | // Slideshow widget |
||
88 | $ig->setAttributes( $this->getSlideshowWidget() ); |
||
89 | } else { |
||
90 | |||
91 | // Standard gallery attributes |
||
92 | $attribs = [ |
||
93 | 1 | 'id' => uniqid(), |
|
94 | 1 | 'class' => $this->getImageOverlay(), |
|
95 | ]; |
||
96 | |||
97 | 1 | $ig->setAttributes( $attribs ); |
|
98 | } |
||
99 | |||
100 | // Only use redirects where the overlay option is not used and redirect |
||
101 | // thumb images towards a different target |
||
102 | 1 | if ( $this->params['redirects'] !== '' && !$this->params['overlay'] ){ |
|
103 | SMWOutputs::requireResource( 'ext.srf.gallery.redirect' ); |
||
104 | } |
||
105 | |||
106 | // For the carousel widget, the perrow option should not be set |
||
107 | 1 | if ( $this->params['perrow'] !== '' && $this->params['widget'] !== 'carousel' ) { |
|
108 | $ig->setPerRow( $this->params['perrow'] ); |
||
109 | } |
||
110 | |||
111 | 1 | if ( $this->params['widths'] !== '' ) { |
|
112 | $ig->setWidths( $this->params['widths'] ); |
||
113 | } |
||
114 | |||
115 | 1 | if ( $this->params['heights'] !== '' ) { |
|
116 | $ig->setHeights( $this->params['heights'] ); |
||
117 | } |
||
118 | |||
119 | 1 | $printReqLabels = []; |
|
120 | 1 | $redirectType = ''; |
|
121 | |||
122 | /** |
||
123 | * @var SMWPrintRequest $printReq |
||
124 | */ |
||
125 | 1 | foreach ( $results->getPrintRequests() as $printReq ) { |
|
126 | 1 | $printReqLabels[] = $printReq->getLabel(); |
|
127 | |||
128 | // Get redirect type |
||
129 | 1 | if ( $this->params['redirects'] === $printReq->getLabel() ){ |
|
130 | 1 | $redirectType = $printReq->getTypeID(); |
|
131 | } |
||
132 | } |
||
133 | |||
134 | 1 | if ( $this->params['imageproperty'] !== '' && in_array( $this->params['imageproperty'], $printReqLabels ) || |
|
135 | 1 | $this->params['redirects'] !== '' && in_array( $this->params['redirects'], $printReqLabels ) ) { |
|
136 | |||
137 | $this->addImageProperties( |
||
138 | $results, |
||
139 | $ig, |
||
140 | $this->params['imageproperty'], |
||
141 | $this->params['captionproperty'], |
||
142 | $this->params['redirects'], |
||
143 | $outputmode |
||
144 | ); |
||
145 | } else { |
||
146 | 1 | $this->addImagePages( $results, $ig ); |
|
147 | } |
||
148 | |||
149 | // SRF Global settings |
||
150 | 1 | SRFUtils::addGlobalJSVariables(); |
|
151 | |||
152 | // Display a processing image as long as the DOM is no ready |
||
153 | 1 | if ( $this->params['widget'] !== '' ) { |
|
154 | $processing = SRFUtils::htmlProcessingElement(); |
||
155 | } |
||
156 | |||
157 | // Beautify the class selector |
||
158 | 1 | $class = $this->params['widget'] ? '-' . $this->params['widget'] . ' ' : ''; |
|
159 | 1 | $class = $this->params['redirects'] !== '' && $this->params['overlay'] === false ? $class . ' srf-redirect' . ' ': $class; |
|
160 | 1 | $class = $this->params['class'] ? $class . ' ' . $this->params['class'] : $class ; |
|
161 | |||
162 | // Separate content from result output |
||
163 | 1 | if ( !$ig->isEmpty() ) { |
|
164 | $attribs = [ |
||
165 | 1 | 'class' => 'srf-gallery' . $class, |
|
166 | 1 | 'data-redirect-type' => $redirectType, |
|
167 | 1 | 'data-ns-text' => $this->getFileNsTextForPageLanguage() |
|
168 | ]; |
||
169 | |||
170 | 1 | $html = Html::rawElement( 'div', $attribs, $processing . $ig->toHTML() ); |
|
171 | } |
||
172 | |||
173 | // If available, create a link that points to further results |
||
174 | 1 | if ( $this->linkFurtherResults( $results ) ) { |
|
175 | $html .= $this->getLink( $results, SMW_OUTPUT_HTML )->getText( SMW_OUTPUT_HTML, $this->mLinker ); |
||
176 | } |
||
177 | |||
178 | 1 | return [ $html, 'nowiki' => true, 'isHTML' => true ]; |
|
179 | } |
||
180 | |||
181 | /** |
||
182 | * Handles queries where the images (and optionally their captions) are specified as properties. |
||
183 | * |
||
184 | * @since 1.5.3 |
||
185 | * |
||
186 | * @param SMWQueryResult $results |
||
187 | * @param ImageGallery $ig |
||
188 | * @param string $imageProperty |
||
189 | * @param string $captionProperty |
||
190 | * @param string $redirectProperty |
||
191 | * @param $outputMode |
||
192 | */ |
||
193 | protected function addImageProperties( SMWQueryResult $results, &$ig, $imageProperty, $captionProperty, $redirectProperty, $outputMode ) { |
||
251 | |||
252 | /** |
||
253 | * Handles queries where the result objects are image pages. |
||
254 | * |
||
255 | * @since 1.5.3 |
||
256 | * |
||
257 | * @param SMWQueryResult $results |
||
258 | * @param ImageGallery $ig |
||
259 | */ |
||
260 | 1 | protected function addImagePages( SMWQueryResult $results, &$ig ) { |
|
261 | 1 | while ( $row = $results->getNext() ) { |
|
262 | /** |
||
263 | * @var SMWResultArray $firstField |
||
264 | */ |
||
265 | 1 | $firstField = $row[0]; |
|
266 | 1 | $nextObject = $firstField->getNextDataValue(); |
|
267 | |||
268 | 1 | if ( $nextObject !== false ) { |
|
269 | 1 | $imgTitle = $nextObject->getTitle(); |
|
270 | |||
271 | // Ensure the title belongs to the image namespace |
||
272 | 1 | if ( $imgTitle instanceof Title && $imgTitle->getNamespace() === NS_FILE ) { |
|
273 | 1 | $imgCaption = ''; |
|
274 | |||
275 | // Is there a property queried for display with ?property |
||
276 | 1 | if ( isset( $row[1] ) ) { |
|
277 | 1 | $imgCaption = $row[1]->getNextDataValue(); |
|
278 | 1 | if ( is_object( $imgCaption ) ) { |
|
279 | 1 | $imgCaption = $imgCaption->getShortText( $this->outputMode, $this->getLinker( true ) ); |
|
280 | } |
||
281 | } |
||
282 | |||
283 | 1 | $this->addImageToGallery( $ig, $imgTitle, $imgCaption ); |
|
284 | } |
||
285 | } |
||
286 | } |
||
287 | 1 | } |
|
288 | |||
289 | /** |
||
290 | * Adds a single image to the gallery. |
||
291 | * Takes care of automatically adding a caption when none is provided and parsing it's wikitext. |
||
292 | * |
||
293 | * @since 1.5.3 |
||
294 | * |
||
295 | * @param ImageGallery $ig The gallery to add the image to |
||
296 | * @param Title $imgTitle The title object of the page of the image |
||
297 | * @param string $imgCaption An optional caption for the image |
||
298 | * @param string $imgRedirect |
||
299 | */ |
||
300 | 1 | protected function addImageToGallery( &$ig, Title $imgTitle, $imgCaption, $imgRedirect = '' ) { |
|
301 | |||
302 | 1 | if ( empty( $imgCaption ) ) { |
|
303 | if ( $this->m_params['autocaptions'] ) { |
||
304 | $imgCaption = $imgTitle->getBaseText(); |
||
305 | |||
306 | if ( !$this->m_params['fileextensions'] ) { |
||
307 | $imgCaption = preg_replace( '#\.[^.]+$#', '', $imgCaption ); |
||
308 | } |
||
309 | } else { |
||
310 | $imgCaption = ''; |
||
311 | } |
||
312 | } else { |
||
313 | 1 | if ( $imgTitle instanceof Title && $imgTitle->getNamespace() == NS_FILE && !$this->isSpecialPage() ) { |
|
314 | 1 | $imgCaption = $ig->mParser->recursiveTagParse( $imgCaption ); |
|
315 | } |
||
316 | } |
||
317 | // Use image alt as helper for either text |
||
318 | 1 | $imgAlt = $this->params['redirects'] === '' ? $imgCaption : $imgRedirect !== '' ? $imgRedirect : '' ; |
|
319 | 1 | $ig->add( $imgTitle, $imgCaption, $imgAlt ); |
|
320 | 1 | } |
|
321 | |||
322 | /** |
||
323 | * Returns the overlay setting |
||
324 | * |
||
325 | * @since 1.8 |
||
326 | * |
||
327 | * @return string |
||
328 | */ |
||
329 | 1 | private function getImageOverlay() { |
|
330 | 1 | if ( array_key_exists( 'overlay', $this->params ) && $this->params['overlay'] == true ) { |
|
331 | SMWOutputs::requireResource( 'ext.srf.gallery.overlay' ); |
||
332 | return ' srf-overlay'; |
||
333 | } else { |
||
334 | 1 | return ''; |
|
335 | } |
||
336 | } |
||
337 | |||
338 | /** |
||
339 | * Init carousel widget |
||
340 | * |
||
341 | * @since 1.8 |
||
342 | * |
||
343 | * @return string |
||
344 | */ |
||
345 | private function getCarouselWidget() { |
||
376 | |||
377 | |||
378 | /** |
||
379 | * Init slideshow widget |
||
380 | * |
||
381 | * @since 1.8 |
||
382 | * |
||
383 | * @return string |
||
384 | */ |
||
385 | private function getSlideshowWidget() { |
||
398 | |||
399 | /** |
||
400 | * @see SMWResultPrinter::getParamDefinitions |
||
401 | * |
||
402 | * @since 1.8 |
||
403 | * |
||
404 | * @param $definitions array of IParamDefinition |
||
405 | * |
||
406 | * @return array of IParamDefinition|array |
||
407 | */ |
||
408 | 1 | public function getParamDefinitions( array $definitions ) { |
|
487 | |||
488 | 1 | private function isSpecialPage() { |
|
492 | |||
493 | 1 | private function getFileNsTextForPageLanguage() { |
|
497 | |||
498 | } |
||
499 |
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.