Completed
Push — master ( 012895...9e0835 )
by Jean-Christophe
03:33
created

HtmlGrid::setStretched()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 0
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
86% 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
		$this->setRowsCount($numRows,$numCols);
36
	}
37
38
	/**
39
	 * Defines the grid width (alias for setWidth)
40
	 * @param int $wide
41
	 */
42
	public function setWide($wide){
43
		$wide=Wide::getConstants()["W".$wide];
44
		$this->addToPropertyCtrl("class", $wide, Wide::getConstants());
45
		return $this->addToPropertyCtrl("class","column",array("column"));
46
	}
47
48
	/**
49
	 * Defines the grid width
50
	 * @param int $width
51
	 * @return \Ajax\semantic\html\collections\HtmlGrid
52
	 */
53
	public function setWidth($width){
54
	return $this->setWide($width);
55
	}
56
57
	/**
58
	 * Adds a row with $colsCount columns
59
	 * @param int $colsCount number of columns to create
60
	 * @return mixed
61
	 */
62
	public function addRow($colsCount=NULL){
63
		$rowCount=$this->rowCount()+1;
64
		$this->setRowsCount($rowCount,$colsCount,true);
65
		return $this->content[$rowCount-1];
66
	}
67
68
	/**
69
	 * Adds a col
70
	 * @param int $width with of the column to add
71
	 * @return mixed|\Ajax\semantic\html\collections\HtmlGrid
72
	 */
73
	public function addCol($width=NULL){
74
		$colCount=$this->colCount()+1;
75
		$this->setColsCount($colCount,true,$width);
76
		if($this->hasOnlyCols($this->count()))
77
			return $this->content[$colCount-1];
78
		return $this;
79
	}
80
81
	/**
82
	 * @param array $sizes array of width of the columns to create
83
	 * @return \Ajax\semantic\html\collections\HtmlGrid
84
	 */
85
	public function addCols($sizes=array()){
86
		foreach ($sizes as $size){
87
			$this->addCol($size);
88
		}
89
		return $this;
90
	}
91
92
	/**
93
	 * Create $rowsCount rows
94
	 * @param int $rowsCount
95
	 * @param int $colsCount
96
	 * @return \Ajax\semantic\html\collections\HtmlGrid
97
	 */
98
	public function setRowsCount($rowsCount,$colsCount=NULL,$force=false){
99
		$count=$this->count();
100
		if($rowsCount<2 && $force===false){
101 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...
102
				$this->addItem(new HtmlGridCol("col-".$this->identifier."-".$i));
103
			}
104
		}else{
105
			if($this->hasOnlyCols($count)){
106
				$tmpContent=$this->content;
107
				$item=$this->addItem($colsCount);
108
				$item->setContent($tmpContent);
109
				$this->content=array();
110
				$count=1;
111
			}
112
			for($i=$count;$i<$rowsCount;$i++){
113
				$this->addItem($colsCount);
114
			}
115
		}
116
		return $this;
117
	}
118
119
	protected function hasOnlyCols($count){
120
		return $count>0 && $this->content[0] instanceof HtmlGridCol;
121
	}
122
123
	/**
124
	 * Defines the number of columns in the grid
125
	 * @param int $numCols
126
	 * @param boolean $toCreate
127
	 * @param int $width
128
	 * @return \Ajax\semantic\html\collections\HtmlGrid
129
	 */
130
	public function setColsCount($numCols,$toCreate=true,$width=NULL){
131
		if(isset($width)===false)
132
			$this->setWide($numCols);
133
		if($toCreate==true){
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
134
			$count=$this->count();
135
			if($count==0 || $this->hasOnlyCols($count)){
136 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...
137
					$this->addItem(new HtmlGridCol("col-".$this->identifier."-".$i,$width));
138
				}
139
			}else{
140
				for($i=0;$i<$count;$i++){
141
					$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...
142
				}
143
			}
144
		}
145
		return $this;
146
	}
147
148
	/**
149
	 * return the row at $index
150
	 * @param int $index
151
	 * @return \Ajax\semantic\html\collections\HtmlGridRow
152
	 */
153
	public function getRow($index){
154
		return $this->getItem($index);
155
	}
156
157
	/**
158
	 * Returns the row count
159
	 * @return int
160
	 */
161
	public function rowCount(){
162
		$count=$this->count();
163
		if($this->hasOnlyCols($count))
164
			return 0;
165
		return $count;
166
	}
167
168
	/**
169
	 * Returns the column count
170
	 * @return int
171
	 */
172
	public function colCount(){
173
		$count=$this->count();
174
		if($this->hasOnlyCols($count))
175
			return $count;
176
		if($count>0)
177
			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\content\HtmlGridRow, Ajax\semantic\html\elements\HtmlList, Ajax\semantic\html\elements\HtmlSegmentGroups, Ajax\semantic\html\modules\HtmlAccordion, Ajax\semantic\html\modules\HtmlDropdown. 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...
178
		return 0;
179
	}
180
181
	/**
182
	 * Returns the cell (HtmlGridCol) at position rrow,$col
183
	 * @param int $row
184
	 * @param int $col
185
	 * @return \Ajax\semantic\html\collections\HtmlGridCol
186
	 */
187
	public function getCell($row,$col){
188
		if($row<2 && $this->hasOnlyCols($this->count()))
189
			return $this->getItem($col);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getItem($col); (Ajax\common\html\HtmlDoubleElement) is incompatible with the return type documented by Ajax\semantic\html\collections\HtmlGrid::getCell of type Ajax\semantic\html\collections\HtmlGridCol.

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:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
190
		$row=$this->getItem($row);
191
		if(isset($row)){
192
			$col=$row->getItem($col);
193
		}
194
		return $col;
195
	}
196
197
	/**
198
	 * Adds dividers between columns ($vertically=false) or between rows ($vertically=true)
199
	 * @param boolean $vertically
200
	 * @return \Ajax\semantic\html\collections\HtmlGrid
201
	 */
202
	public function setDivided($vertically=false){
203
		$value=($vertically===true)?"vertically divided":"divided";
204
		return $this->addToPropertyCtrl("class", $value,array("divided"));
205
	}
206
207
	/**
208
	 * Divides rows into cells
209
	 * @param boolean $internally true for internal cells
210
	 * @return \Ajax\semantic\html\collections\HtmlGrid
211
	 */
212
	public function setCelled($internally=false){
213
		$value=($internally===true)?"internally celled":"celled";
214
		return $this->addToPropertyCtrl("class", $value,array("celled","internally celled"));
215
	}
216
217
	/**
218
	 * A grid can have its columns centered
219
	 */
220
	public function setCentered(){
221
		return $this->addToPropertyCtrl("class", "centered",array("centered"));
222
	}
223
224
	/**
225
	 * automatically resize all elements to split the available width evenly
226
	 * @return \Ajax\semantic\html\collections\HtmlGrid
227
	 */
228
	public function setEqualWidth(){
229
		return $this->addToProperty("class", "equal width");
230
	}
231
232
	/**
233
	 * Adds vertical or/and horizontal gutters
234
	 * @param string $value
235
	 * @return \Ajax\semantic\html\collections\HtmlGrid
236
	 */
237
	public function setPadded($value=NULL){
238
		if(isset($value))
239
			$this->addToPropertyCtrl("class", $value,array("vertically","horizontally"));
240
		return $this->addToProperty("class", "padded");
241
	}
242
243
	/**
244
	 * @param boolean $very
245
	 * @return \Ajax\semantic\html\collections\HtmlGrid
246
	 */
247
	public function setRelaxed($very=false){
248
		$value=($very===true)?"very relaxed":"relaxed";
249
		return $this->addToPropertyCtrl("class", $value,array("relaxed","very relaxed"));
250
	}
251
252
	public function setVerticalAlignment($value=VerticalAlignment::MIDDLE){
253
		return $this->addToPropertyCtrl("class", $value." aligned",VerticalAlignment::getConstantValues("aligned"));
254
	}
255
256
	/**
257
	 * {@inheritDoc}
258
	 * @see \Ajax\common\html\HtmlCollection::createItem()
259
	 */
260
	protected function createItem($value){
261
		if($this->_createCols===false)
262
			$value=null;
263
		$item=new HtmlGridRow($this->identifier."-row-".($this->count()+1),$value,$this->_colSizing,$this->_implicitRows);
264
		return $item;
265
	}
266
267
	/**
268
	 * Sets $values to the grid
269
	 * @param array $values
270
	 */
271
	public function setValues($values){
272
		$count=$this->count();
273
		if($this->_createCols===false){
274
			for($i=$count;$i<\sizeof($values);$i++){
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function sizeof() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
275
				$colSize=\sizeof($values[$i]);
276
				$this->addItem(new HtmlGridRow($this->identifier."-row-".($this->count()+1),$colSize,$this->_colSizing,$this->_implicitRows));
277
			}
278
		}
279
		$count=\min(array($this->count(),\sizeof($values)));
280
		for($i=0;$i<$count;$i++){
281
			$this->content[$i]->setValues($values[$i],$this->_createCols===false);
282
		}
283
	}
284
285
	/**
286
	 * stretch the row contents to take up the entire column height
287
	 * @return \Ajax\semantic\html\content\HtmlGridRow
288
	 */
289
	public function setStretched(){
290
		return $this->addToProperty("class", "stretched");
291
	}
292
293
}