Completed
Push — master ( 13fe45...9e80e5 )
by Jean-Christophe
04:06
created

HtmlGrid   C

Complexity

Total Complexity 60

Size/Duplication

Total Lines 304
Duplicated Lines 4.61 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 4
Bugs 1 Features 0
Metric Value
wmc 60
c 4
b 1
f 0
lcom 1
cbo 7
dl 14
loc 304
rs 6.0975

27 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 3
A asSegment() 0 3 1
A asContainer() 0 3 1
A setWide() 8 8 2
A setWidth() 0 3 1
A addRow() 0 5 1
A addCol() 0 7 2
A addCols() 0 6 2
B setRowsCount() 3 20 6
A hasOnlyCols() 0 3 2
B setColsCount() 3 18 7
A getRow() 0 3 1
A rowCount() 0 6 2
A colCount() 0 8 3
A getCell() 0 9 4
A setDivided() 0 4 2
A setCelled() 0 4 2
A setCentered() 0 3 1
A setEqualWidth() 0 3 1
A setPadded() 0 5 2
A setRelaxed() 0 4 2
A setVerticalAlignment() 0 3 1
A createItem() 0 6 2
B setValues() 0 14 5
A setColWidth() 0 6 2
A setStretched() 0 3 1
A addDivider() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

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 Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like HtmlGrid 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 HtmlGrid, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Ajax\semantic\html\collections;
4
5
use Ajax\common\html\HtmlCollection;
6
use Ajax\semantic\html\content\HtmlGridRow;
7
use Ajax\semantic\html\base\constants\Wide;
8
use Ajax\semantic\html\base\constants\VerticalAlignment;
9
use Ajax\semantic\html\base\HtmlSemCollection;
10
use Ajax\semantic\html\base\traits\TextAlignmentTrait;
11
use Ajax\semantic\html\content\HtmlGridCol;
12
13
/**
14
 * Semantic Grid component
15
 * @see http://semantic-ui.com/collections/grid.html
16
 * @author jc
17
 * @version 1.001
18
 */
19
class HtmlGrid extends HtmlSemCollection {
20
	use TextAlignmentTrait;
21
	private $_createCols;
22
	private $_colSizing=true;
23
	private $_implicitRows=false;
24
25
	public function __construct($identifier, $numRows=1, $numCols=NULL, $createCols=true, $implicitRows=false) {
26
		parent::__construct($identifier, "div", "ui grid");
27
		$this->_implicitRows=$implicitRows;
28
		$this->_createCols=$createCols;
29
		if (isset($numCols)) {
30
			// if($this->_createCols){
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
31
			$this->_colSizing=false;
32
			// }
33
			$this->setWide($numCols);
34
		}
35
		if($createCols)
36
			$this->setRowsCount($numRows, $numCols);
37
	}
38
39
	public function asSegment() {
40
		return $this->addToPropertyCtrl("class", "segment", array ("segment" ));
41
	}
42
43
	public function asContainer() {
44
		return $this->addToPropertyCtrl("class", "container", array ("container" ));
45
	}
46
47
	/**
48
	 * Defines the grid width (alias for setWidth)
49
	 * @param int $wide
50
	 */
51 View Code Duplication
	public function setWide($wide) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
52
		if(isset(Wide::getConstants()["W" . $wide])){
53
			$wide=Wide::getConstants()["W" . $wide];
54
			$this->addToPropertyCtrl("class", $wide, Wide::getConstants());
55
			return $this->addToPropertyCtrl("class", "column", array ("column" ));
56
		}
57
		return $this;
58
	}
59
60
	/**
61
	 * Defines the grid width
62
	 * @param int $width
63
	 * @return \Ajax\semantic\html\collections\HtmlGrid
64
	 */
65
	public function setWidth($width) {
66
		return $this->setWide($width);
67
	}
68
69
	/**
70
	 * Adds a row with $colsCount columns
71
	 * @param int $colsCount number of columns to create
72
	 * @return mixed
73
	 */
74
	public function addRow($colsCount=NULL) {
75
		$rowCount=$this->rowCount() + 1;
76
		$this->setRowsCount($rowCount, $colsCount, true);
77
		return $this->content[$rowCount - 1];
78
	}
79
80
	/**
81
	 * Adds a col
82
	 * @param int $width with of the column to add
83
	 * @return mixed|\Ajax\semantic\html\collections\HtmlGrid
84
	 */
85
	public function addCol($width=NULL) {
86
		$colCount=$this->colCount() + 1;
87
		$this->setColsCount($colCount, true, $width);
88
		if ($this->hasOnlyCols($this->count()))
89
			return $this->content[$colCount - 1];
90
		return $this;
91
	}
92
93
	/**
94
	 *
95
	 * @param array $sizes array of width of the columns to create
96
	 * @return \Ajax\semantic\html\collections\HtmlGrid
97
	 */
98
	public function addCols($sizes=array()) {
99
		foreach ( $sizes as $size ) {
100
			$this->addCol($size);
101
		}
102
		return $this;
103
	}
104
105
	/**
106
	 * Create $rowsCount rows
107
	 * @param int $rowsCount
108
	 * @param int $colsCount
109
	 * @return \Ajax\semantic\html\collections\HtmlGrid
110
	 */
111
	public function setRowsCount($rowsCount, $colsCount=NULL, $force=false) {
112
		$count=$this->count();
113
		if ($rowsCount < 2 && $force === false) {
114 View Code Duplication
			for($i=$count; $i < $colsCount; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
115
				$this->addItem(new HtmlGridCol("col-" . $this->identifier . "-" . $i));
116
			}
117
		} else {
118
			if ($this->hasOnlyCols($count)) {
119
				$tmpContent=$this->content;
120
				$item=$this->addItem($colsCount);
121
				$item->setContent($tmpContent);
122
				$this->content=array ();
123
				$count=1;
124
			}
125
			for($i=$count; $i < $rowsCount; $i++) {
126
				$this->addItem($colsCount);
127
			}
128
		}
129
		return $this;
130
	}
131
132
	protected function hasOnlyCols($count) {
133
		return $count > 0 && $this->content[0] instanceof HtmlGridCol;
134
	}
135
136
	/**
137
	 * Defines the number of columns in the grid
138
	 * @param int $numCols
139
	 * @param boolean $toCreate
140
	 * @param int $width
141
	 * @return \Ajax\semantic\html\collections\HtmlGrid
142
	 */
143
	public function setColsCount($numCols, $toCreate=true, $width=NULL) {
144
		if (isset($width)===false) {
145
			$this->setWide($numCols);
146
		}
147
		if ($toCreate === true) {
148
			$count=$this->count();
149
			if ($count == 0 || $this->hasOnlyCols($count)) {
150 View Code Duplication
				for($i=$count; $i < $numCols; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
151
					$this->addItem(new HtmlGridCol("col-" . $this->identifier . "-" . $i, $width));
152
				}
153
			} else {
154
				for($i=0; $i < $count; $i++) {
155
					$this->getItem($i)->setColsCount($numCols);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Ajax\common\html\HtmlDoubleElement as the method setColsCount() does only exist in the following sub-classes of Ajax\common\html\HtmlDoubleElement: Ajax\semantic\html\collections\HtmlGrid, Ajax\semantic\html\content\HtmlGridRow. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
156
				}
157
			}
158
		}
159
		return $this;
160
	}
161
162
	/**
163
	 * return the row at $index
164
	 * @param int $index
165
	 * @return \Ajax\semantic\html\collections\HtmlGridRow
166
	 */
167
	public function getRow($index) {
168
		return $this->getItem($index);
169
	}
170
171
	/**
172
	 * Returns the row count
173
	 * @return int
174
	 */
175
	public function rowCount() {
176
		$count=$this->count();
177
		if ($this->hasOnlyCols($count))
178
			return 0;
179
		return $count;
180
	}
181
182
	/**
183
	 * Returns the column count
184
	 * @return int
185
	 */
186
	public function colCount() {
187
		$count=$this->count();
188
		if ($this->hasOnlyCols($count))
189
			return $count;
190
		if ($count > 0)
191
			return $this->getItem(0)->count();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Ajax\common\html\HtmlDoubleElement as the method count() does only exist in the following sub-classes of Ajax\common\html\HtmlDoubleElement: Ajax\common\html\HtmlCollection, Ajax\common\html\html5\HtmlList, Ajax\semantic\html\base\HtmlSemCollection, Ajax\semantic\html\base\HtmlSemNavElement, Ajax\semantic\html\collections\HtmlBreadcrumb, Ajax\semantic\html\collections\HtmlGrid, Ajax\semantic\html\collections\form\HtmlForm, Ajax\semantic\html\collections\form\HtmlFormFields, Ajax\semantic\html\colle...menus\HtmlAccordionMenu, Ajax\semantic\html\collections\menus\HtmlIconMenu, Ajax\semantic\html\colle...nus\HtmlLabeledIconMenu, Ajax\semantic\html\collections\menus\HtmlMenu, Ajax\semantic\html\colle...enus\HtmlPaginationMenu, Ajax\semantic\html\content\HtmlGridRow, Ajax\semantic\html\content\table\HtmlTR, Ajax\semantic\html\content\table\HtmlTableContent, Ajax\semantic\html\elements\HtmlButtonGroups, Ajax\semantic\html\elements\HtmlIconGroups, Ajax\semantic\html\elements\HtmlLabelGroups, Ajax\semantic\html\elements\HtmlList, Ajax\semantic\html\elements\HtmlSegmentGroups, Ajax\semantic\html\elements\HtmlStep, Ajax\semantic\html\modules\HtmlAccordion, Ajax\semantic\html\modules\HtmlDropdown, Ajax\semantic\html\modules\HtmlShape, Ajax\semantic\html\modules\HtmlTab, Ajax\semantic\html\views\HtmlCardGroups. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
192
		return 0;
193
	}
194
195
	/**
196
	 * Returns the cell (HtmlGridCol) at position rrow,$col
197
	 * @param int $row
198
	 * @param int $col
199
	 * @return \Ajax\semantic\html\collections\HtmlGridCol
200
	 */
201
	public function getCell($row, $col) {
202
		if ($row < 2 && $this->hasOnlyCols($this->count()))
203
			return $this->getItem($col);
204
		$row=$this->getItem($row);
205
		if (isset($row)) {
206
			$col=$row->getItem($col);
207
		}
208
		return $col;
209
	}
210
211
	/**
212
	 * Adds dividers between columns ($vertically=false) or between rows ($vertically=true)
213
	 * @param boolean $vertically
214
	 * @return \Ajax\semantic\html\collections\HtmlGrid
215
	 */
216
	public function setDivided($vertically=false) {
217
		$value=($vertically === true) ? "vertically divided" : "divided";
218
		return $this->addToPropertyCtrl("class", $value, array ("divided" ));
219
	}
220
221
	/**
222
	 * Divides rows into cells
223
	 * @param boolean $internally true for internal cells
224
	 * @return \Ajax\semantic\html\collections\HtmlGrid
225
	 */
226
	public function setCelled($internally=false) {
227
		$value=($internally === true) ? "internally celled" : "celled";
228
		return $this->addToPropertyCtrl("class", $value, array ("celled","internally celled" ));
229
	}
230
231
	/**
232
	 * A grid can have its columns centered
233
	 */
234
	public function setCentered() {
235
		return $this->addToPropertyCtrl("class", "centered", array ("centered" ));
236
	}
237
238
	/**
239
	 * automatically resize all elements to split the available width evenly
240
	 * @return \Ajax\semantic\html\collections\HtmlGrid
241
	 */
242
	public function setEqualWidth() {
243
		return $this->addToProperty("class", "equal width");
244
	}
245
246
	/**
247
	 * Adds vertical or/and horizontal gutters
248
	 * @param string $value
249
	 * @return \Ajax\semantic\html\collections\HtmlGrid
250
	 */
251
	public function setPadded($value=NULL) {
252
		if (isset($value))
253
			$this->addToPropertyCtrl("class", $value, array ("vertically","horizontally" ));
254
		return $this->addToProperty("class", "padded");
255
	}
256
257
	/**
258
	 *
259
	 * @param boolean $very
260
	 * @return \Ajax\semantic\html\collections\HtmlGrid
261
	 */
262
	public function setRelaxed($very=false) {
263
		$value=($very === true) ? "very relaxed" : "relaxed";
264
		return $this->addToPropertyCtrl("class", $value, array ("relaxed","very relaxed" ));
265
	}
266
267
	public function setVerticalAlignment($value=VerticalAlignment::MIDDLE) {
268
		return $this->addToPropertyCtrl("class", $value . " aligned", VerticalAlignment::getConstantValues("aligned"));
269
	}
270
271
	/**
272
	 *
273
	 * {@inheritDoc}
274
	 *
275
	 * @see \Ajax\common\html\HtmlCollection::createItem()
276
	 */
277
	protected function createItem($value) {
278
		if ($this->_createCols === false)
279
			$value=null;
280
		$item=new HtmlGridRow($this->identifier . "-row-" . ($this->count() + 1), $value, $this->_colSizing, $this->_implicitRows);
281
		return $item;
282
	}
283
284
	/**
285
	 * Sets $values to the grid
286
	 * @param array $values
287
	 */
288
	public function setValues($values, $force=true) {
289
		$count=$this->count();
290
		$valuesSize=\sizeof($values);
291
		if ($this->_createCols === false || $force === true) {
292
			for($i=$count; $i < $valuesSize; $i++) {
293
				$colSize=\sizeof($values[$i]);
294
				$this->addItem(new HtmlGridRow($this->identifier . "-row-" . ($this->count() + 1), $colSize, $this->_colSizing, $this->_implicitRows));
295
			}
296
		}
297
		$count=\min(array ($this->count(),$valuesSize ));
298
		for($i=0; $i < $count; $i++) {
299
			$this->content[$i]->setValues($values[$i], $this->_createCols === false);
300
		}
301
	}
302
	
303
	public function setColWidth($numCol,$width){
304
		foreach ($this->content as $row){
305
			$row->getCol($numCol)->setWidth($width);
306
		}
307
		return $this;
308
	}
309
310
	/**
311
	 * stretch the row contents to take up the entire column height
312
	 * @return \Ajax\semantic\html\content\HtmlGridRow
313
	 */
314
	public function setStretched() {
315
		return $this->addToProperty("class", "stretched");
316
	}
317
318
	public function addDivider($afterColIndex, $vertical=true, $content=NULL) {
319
		$col=$this->getCell(0, $afterColIndex);
320
		return $col->addDivider($vertical, $content);
321
	}
322
}