GenericArrayObject::getNewOffset()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Extends ArrayObject and does two things:
5
 *
6
 * Allows for deriving classes to easily intercept additions
7
 * and deletions for purposes such as additional indexing.
8
 *
9
 * Enforces the objects to be of a certain type, so this
10
 * can be replied upon, much like if this had true support
11
 * for generics, which sadly enough is not possible in PHP.
12
 *
13
 * This program is free software; you can redistribute it and/or modify
14
 * it under the terms of the GNU General Public License as published by
15
 * the Free Software Foundation; either version 2 of the License, or
16
 * (at your option) any later version.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
 * GNU General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU General Public License along
24
 * with this program; if not, write to the Free Software Foundation, Inc.,
25
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26
 * http://www.gnu.org/copyleft/gpl.html
27
 *
28
 * @since 1.20
29
 *
30
 * @file
31
 *
32
 * @license GNU GPL v2+
33
 * @author Jeroen De Dauw < [email protected] >
34
 */
35
abstract class GenericArrayObject extends ArrayObject {
36
	/**
37
	 * Returns the name of an interface/class that the element should implement/extend.
38
	 *
39
	 * @since 1.20
40
	 *
41
	 * @return string
42
	 */
43
	abstract public function getObjectType();
44
45
	/**
46
	 * @see SiteList::getNewOffset()
47
	 * @since 1.20
48
	 * @var integer
49
	 */
50
	protected $indexOffset = 0;
51
52
	/**
53
	 * Finds a new offset for when appending an element.
54
	 * The base class does this, so it would be better to integrate,
55
	 * but there does not appear to be any way to do this...
56
	 *
57
	 * @since 1.20
58
	 *
59
	 * @return integer
60
	 */
61
	protected function getNewOffset() {
62
		while ( $this->offsetExists( $this->indexOffset ) ) {
63
			$this->indexOffset++;
64
		}
65
66
		return $this->indexOffset;
67
	}
68
69
	/**
70
	 * Constructor.
71
	 * @see ArrayObject::__construct
72
	 *
73
	 * @since 1.20
74
	 *
75
	 * @param null|array $input
76
	 * @param int $flags
77
	 * @param string $iterator_class
78
	 */
79
	public function __construct( $input = null, $flags = 0, $iterator_class = 'ArrayIterator' ) {
80
		parent::__construct( [], $flags, $iterator_class );
81
82
		if ( !is_null( $input ) ) {
83
			foreach ( $input as $offset => $value ) {
84
				$this->offsetSet( $offset, $value );
85
			}
86
		}
87
	}
88
89
	/**
90
	 * @see ArrayObject::append
91
	 *
92
	 * @since 1.20
93
	 *
94
	 * @param mixed $value
95
	 */
96
	public function append( $value ) {
97
		$this->setElement( null, $value );
98
	}
99
100
	/**
101
	 * @see ArrayObject::offsetSet()
102
	 *
103
	 * @since 1.20
104
	 *
105
	 * @param mixed $index
106
	 * @param mixed $value
107
	 */
108
	public function offsetSet( $index, $value ) {
109
		$this->setElement( $index, $value );
110
	}
111
112
	/**
113
	 * Returns if the provided value has the same type as the elements
114
	 * that can be added to this ArrayObject.
115
	 *
116
	 * @since 1.20
117
	 *
118
	 * @param mixed $value
119
	 *
120
	 * @return bool
121
	 */
122
	protected function hasValidType( $value ) {
123
		$class = $this->getObjectType();
124
		return $value instanceof $class;
125
	}
126
127
	/**
128
	 * Method that actually sets the element and holds
129
	 * all common code needed for set operations, including
130
	 * type checking and offset resolving.
131
	 *
132
	 * If you want to do additional indexing or have code that
133
	 * otherwise needs to be executed whenever an element is added,
134
	 * you can overload @see preSetElement.
135
	 *
136
	 * @since 1.20
137
	 *
138
	 * @param mixed $index
139
	 * @param mixed $value
140
	 *
141
	 * @throws InvalidArgumentException
142
	 */
143
	protected function setElement( $index, $value ) {
144
		if ( !$this->hasValidType( $value ) ) {
145
			throw new InvalidArgumentException(
146
				'Can only add '	. $this->getObjectType() . ' implementing objects to '
147
				. static::class . '.'
148
			);
149
		}
150
151
		if ( is_null( $index ) ) {
152
			$index = $this->getNewOffset();
153
		}
154
155
		if ( $this->preSetElement( $index, $value ) ) {
156
			parent::offsetSet( $index, $value );
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (offsetSet() instead of setElement()). Are you sure this is correct? If so, you might want to change this to $this->offsetSet().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
157
		}
158
	}
159
160
	/**
161
	 * Gets called before a new element is added to the ArrayObject.
162
	 *
163
	 * At this point the index is always set (ie not null) and the
164
	 * value is always of the type returned by @see getObjectType.
165
	 *
166
	 * Should return a boolean. When false is returned the element
167
	 * does not get added to the ArrayObject.
168
	 *
169
	 * @since 1.20
170
	 *
171
	 * @param integer|string $index
172
	 * @param mixed $value
173
	 *
174
	 * @return bool
175
	 */
176
	protected function preSetElement( $index, $value ) {
177
		return true;
178
	}
179
180
	/**
181
	 * @see Serializable::serialize
182
	 *
183
	 * @since 1.20
184
	 *
185
	 * @return string
186
	 */
187
	public function serialize() {
188
		return serialize( $this->getSerializationData() );
189
	}
190
191
	/**
192
	 * Returns an array holding all the data that should go into serialization calls.
193
	 * This is intended to allow overloading without having to reimplement the
194
	 * behavior of this base class.
195
	 *
196
	 * @since 1.20
197
	 *
198
	 * @return array
199
	 */
200
	protected function getSerializationData() {
201
		return [
202
			'data' => $this->getArrayCopy(),
203
			'index' => $this->indexOffset,
204
		];
205
	}
206
207
	/**
208
	 * @see Serializable::unserialize
209
	 *
210
	 * @since 1.20
211
	 *
212
	 * @param string $serialization
213
	 *
214
	 * @return array
215
	 */
216
	public function unserialize( $serialization ) {
217
		$serializationData = unserialize( $serialization );
218
219
		foreach ( $serializationData['data'] as $offset => $value ) {
220
			// Just set the element, bypassing checks and offset resolving,
221
			// as these elements have already gone through this.
222
			parent::offsetSet( $offset, $value );
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (offsetSet() instead of unserialize()). Are you sure this is correct? If so, you might want to change this to $this->offsetSet().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
223
		}
224
225
		$this->indexOffset = $serializationData['index'];
226
227
		return $serializationData;
228
	}
229
230
	/**
231
	 * Returns if the ArrayObject has no elements.
232
	 *
233
	 * @since 1.20
234
	 *
235
	 * @return bool
236
	 */
237
	public function isEmpty() {
238
		return $this->count() === 0;
239
	}
240
}
241