Completed
Push — master ( 0c3815...012895 )
by Jean-Christophe
03:40
created

HtmlGrid::setRowsCount()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 20
Code Lines 15

Duplication

Lines 3
Ratio 15 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 3
loc 20
rs 8.8571
cc 6
eloc 15
nc 6
nop 3
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
	public function __construct( $identifier,$numRows=1,$numCols=NULL,$createCols=true,$implicitRows=false){
25
		parent::__construct( $identifier, "div","ui grid");
26
		$this->_implicitRows=$implicitRows;
27
		$this->_createCols=$createCols;
28
		if(isset($numCols)){
29
			//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...
30
				$this->_colSizing=false;
31
			//}
32
			$this->setWide($numCols);
33
		}
34
		$this->setRowsCount($numRows,$numCols);
35
	}
36
37
	public function setWide($wide){
38
		$wide=Wide::getConstants()["W".$wide];
39
		$this->addToPropertyCtrl("class", $wide, Wide::getConstants());
40
		return $this->addToPropertyCtrl("class","column",array("column"));
41
	}
42
43
	public function addRow($colsCount=NULL){
44
		$rowCount=$this->rowCount()+1;
45
		$this->setRowsCount($rowCount,$colsCount,true);
46
		return $this->content[$rowCount-1];
47
	}
48
49
	public function addCol($width=NULL){
50
		$colCount=$this->colCount()+1;
51
		$this->setColsCount($colCount,true,$width);
52
		if($this->hasOnlyCols($this->count()))
53
			return $this->content[$colCount-1];
54
		return $this;
55
	}
56
57
	public function addCols($sizes=array()){
58
		foreach ($sizes as $size){
59
			$this->addCol($size);
60
		}
61
		return $this;
62
	}
63
64
	/**
65
	 * Create $rowsCount rows
66
	 * @param int $rowsCount
67
	 * @param int $colsCount
68
	 * @return \Ajax\semantic\html\collections\HtmlGrid
69
	 */
70
	public function setRowsCount($rowsCount,$colsCount=NULL,$force=false){
71
		$count=$this->count();
72
		if($rowsCount<2 && $force===false){
73 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...
74
				$this->addItem(new HtmlGridCol("col-".$this->identifier."-".$i));
75
			}
76
		}else{
77
			if($this->hasOnlyCols($count)){
78
				$tmpContent=$this->content;
79
				$item=$this->addItem($colsCount);
80
				$item->setContent($tmpContent);
81
				$this->content=array();
82
				$count=1;
83
			}
84
			for($i=$count;$i<$rowsCount;$i++){
85
				$this->addItem($colsCount);
86
			}
87
		}
88
		return $this;
89
	}
90
91
	protected function hasOnlyCols($count){
92
		return $count>0 && $this->content[0] instanceof HtmlGridCol;
93
	}
94
95
	public function setColsCount($numCols,$toCreate=true,$width=NULL){
96
		if(isset($width)===false)
97
			$this->setWide($numCols);
98
		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...
99
			$count=$this->count();
100
			if($count==0 || $this->hasOnlyCols($count)){
101 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...
102
					$this->addItem(new HtmlGridCol("col-".$this->identifier."-".$i,$width));
103
				}
104
			}else{
105
				for($i=0;$i<$count;$i++){
106
					$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...
107
				}
108
			}
109
		}
110
		return $this;
111
	}
112
113
	/**
114
	 * return the row at $index
115
	 * @param int $index
116
	 * @return \Ajax\semantic\html\collections\HtmlGridRow
117
	 */
118
	public function getRow($index){
119
		return $this->getItem($index);
120
	}
121
122
	public function rowCount(){
123
		$count=$this->count();
124
		if($this->hasOnlyCols($count))
125
			return 0;
126
		return $count;
127
	}
128
129
	public function colCount(){
130
		$count=$this->count();
131
		if($this->hasOnlyCols($count))
132
			return $count;
133
		if($count>0)
134
			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...
135
		return 0;
136
	}
137
138
	/**
139
	 * @param int $row
140
	 * @param int $col
141
	 * @return \Ajax\semantic\html\collections\HtmlGridCol
142
	 */
143
	public function getCell($row,$col){
144
		if($row<2 && $this->hasOnlyCols($this->count()))
145
			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...
146
		$row=$this->getItem($row);
147
		if(isset($row)){
148
			$col=$row->getItem($col);
149
		}
150
		return $col;
151
	}
152
153
	/**
154
	 * Adds dividers between columns ($vertically=false) or between rows ($vertically=true)
155
	 * @param boolean $vertically
156
	 * @return \Ajax\semantic\html\collections\HtmlGrid
157
	 */
158
	public function setDivided($vertically=false){
159
		$value=($vertically===true)?"vertically divided":"divided";
160
		return $this->addToPropertyCtrl("class", $value,array("divided"));
161
	}
162
163
	/**
164
	 * Divides rows into cells
165
	 * @param boolean $internally true for internal cells
166
	 * @return \Ajax\semantic\html\collections\HtmlGrid
167
	 */
168
	public function setCelled($internally=false){
169
		$value=($internally===true)?"internally celled":"celled";
170
		return $this->addToPropertyCtrl("class", $value,array("celled","internally celled"));
171
	}
172
173
	/**
174
	 * A grid can have its columns centered
175
	 */
176
	public function setCentered(){
177
		return $this->addToPropertyCtrl("class", "centered",array("centered"));
178
	}
179
180
	/**
181
	 * automatically resize all elements to split the available width evenly
182
	 * @return \Ajax\semantic\html\collections\HtmlGrid
183
	 */
184
	public function setEqualWidth(){
185
		return $this->addToProperty("class", "equal width");
186
	}
187
188
	/**
189
	 * Adds vertical or/and horizontal gutters
190
	 * @param string $value
191
	 * @return \Ajax\semantic\html\collections\HtmlGrid
192
	 */
193
	public function setPadded($value=NULL){
194
		if(isset($value))
195
			$this->addToPropertyCtrl("class", $value,array("vertically","horizontally"));
196
		return $this->addToProperty("class", "padded");
197
	}
198
199
	/**
200
	 * @param boolean $very
201
	 * @return \Ajax\semantic\html\collections\HtmlGrid
202
	 */
203
	public function setRelaxed($very=false){
204
		$value=($very===true)?"very relaxed":"relaxed";
205
		return $this->addToPropertyCtrl("class", $value,array("relaxed","very relaxed"));
206
	}
207
208
	public function setVerticalAlignment($value=VerticalAlignment::MIDDLE){
209
		return $this->addToPropertyCtrl("class", $value." aligned",VerticalAlignment::getConstantValues("aligned"));
210
	}
211
212
	/**
213
	 * {@inheritDoc}
214
	 * @see \Ajax\common\html\HtmlCollection::createItem()
215
	 */
216
	protected function createItem($value){
217
		if($this->_createCols===false)
218
			$value=null;
219
		$item=new HtmlGridRow($this->identifier."-row-".($this->count()+1),$value,$this->_colSizing,$this->_implicitRows);
220
		return $item;
221
	}
222
223
	public function setValues($values){
224
		$count=$this->count();
225
		if($this->_createCols===false){
226
			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...
227
				$colSize=\sizeof($values[$i]);
228
				$this->addItem(new HtmlGridRow($this->identifier."-row-".($this->count()+1),$colSize,$this->_colSizing,$this->_implicitRows));
229
			}
230
		}
231
		$count=\min(array($this->count(),\sizeof($values)));
232
		for($i=0;$i<$count;$i++){
233
			$this->content[$i]->setValues($values[$i],$this->_createCols===false);
234
		}
235
	}
236
237
	/**
238
	 * stretch the row contents to take up the entire column height
239
	 * @return \Ajax\semantic\html\content\HtmlGridRow
240
	 */
241
	public function setStretched(){
242
		return $this->addToProperty("class", "stretched");
243
	}
244
245
}