]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/libs/GenericArrayObject.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / includes / libs / GenericArrayObject.php
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 < jeroendedauw@gmail.com >
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 int
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 int
60          */
61         protected function getNewOffset() {
62                 while ( $this->offsetExists( $this->indexOffset ) ) {
63                         $this->indexOffset++;
64                 }
65
66                 return $this->indexOffset;
67         }
68
69         /**
70          * @see ArrayObject::__construct
71          *
72          * @since 1.20
73          *
74          * @param null|array $input
75          * @param int $flags
76          * @param string $iterator_class
77          */
78         public function __construct( $input = null, $flags = 0, $iterator_class = 'ArrayIterator' ) {
79                 parent::__construct( [], $flags, $iterator_class );
80
81                 if ( !is_null( $input ) ) {
82                         foreach ( $input as $offset => $value ) {
83                                 $this->offsetSet( $offset, $value );
84                         }
85                 }
86         }
87
88         /**
89          * @see ArrayObject::append
90          *
91          * @since 1.20
92          *
93          * @param mixed $value
94          */
95         public function append( $value ) {
96                 $this->setElement( null, $value );
97         }
98
99         /**
100          * @see ArrayObject::offsetSet()
101          *
102          * @since 1.20
103          *
104          * @param mixed $index
105          * @param mixed $value
106          */
107         public function offsetSet( $index, $value ) {
108                 $this->setElement( $index, $value );
109         }
110
111         /**
112          * Returns if the provided value has the same type as the elements
113          * that can be added to this ArrayObject.
114          *
115          * @since 1.20
116          *
117          * @param mixed $value
118          *
119          * @return bool
120          */
121         protected function hasValidType( $value ) {
122                 $class = $this->getObjectType();
123                 return $value instanceof $class;
124         }
125
126         /**
127          * Method that actually sets the element and holds
128          * all common code needed for set operations, including
129          * type checking and offset resolving.
130          *
131          * If you want to do additional indexing or have code that
132          * otherwise needs to be executed whenever an element is added,
133          * you can overload @see preSetElement.
134          *
135          * @since 1.20
136          *
137          * @param mixed $index
138          * @param mixed $value
139          *
140          * @throws InvalidArgumentException
141          */
142         protected function setElement( $index, $value ) {
143                 if ( !$this->hasValidType( $value ) ) {
144                         throw new InvalidArgumentException(
145                                 'Can only add ' . $this->getObjectType() . ' implementing objects to '
146                                 . static::class . '.'
147                         );
148                 }
149
150                 if ( is_null( $index ) ) {
151                         $index = $this->getNewOffset();
152                 }
153
154                 if ( $this->preSetElement( $index, $value ) ) {
155                         parent::offsetSet( $index, $value );
156                 }
157         }
158
159         /**
160          * Gets called before a new element is added to the ArrayObject.
161          *
162          * At this point the index is always set (ie not null) and the
163          * value is always of the type returned by @see getObjectType.
164          *
165          * Should return a boolean. When false is returned the element
166          * does not get added to the ArrayObject.
167          *
168          * @since 1.20
169          *
170          * @param int|string $index
171          * @param mixed $value
172          *
173          * @return bool
174          */
175         protected function preSetElement( $index, $value ) {
176                 return true;
177         }
178
179         /**
180          * @see Serializable::serialize
181          *
182          * @since 1.20
183          *
184          * @return string
185          */
186         public function serialize() {
187                 return serialize( $this->getSerializationData() );
188         }
189
190         /**
191          * Returns an array holding all the data that should go into serialization calls.
192          * This is intended to allow overloading without having to reimplement the
193          * behavior of this base class.
194          *
195          * @since 1.20
196          *
197          * @return array
198          */
199         protected function getSerializationData() {
200                 return [
201                         'data' => $this->getArrayCopy(),
202                         'index' => $this->indexOffset,
203                 ];
204         }
205
206         /**
207          * @see Serializable::unserialize
208          *
209          * @since 1.20
210          *
211          * @param string $serialization
212          *
213          * @return array
214          */
215         public function unserialize( $serialization ) {
216                 $serializationData = unserialize( $serialization );
217
218                 foreach ( $serializationData['data'] as $offset => $value ) {
219                         // Just set the element, bypassing checks and offset resolving,
220                         // as these elements have already gone through this.
221                         parent::offsetSet( $offset, $value );
222                 }
223
224                 $this->indexOffset = $serializationData['index'];
225
226                 return $serializationData;
227         }
228
229         /**
230          * Returns if the ArrayObject has no elements.
231          *
232          * @since 1.20
233          *
234          * @return bool
235          */
236         public function isEmpty() {
237                 return $this->count() === 0;
238         }
239 }