DataObject.php
8.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
<?php
/**
* Part of the Joomla Framework Data Package
*
* @copyright Copyright (C) 2005 - 2016 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Data;
use Joomla\Registry\Registry;
/**
* DataObject is a class that is used to store data but allowing you to access the data
* by mimicking the way PHP handles class properties.
*
* @since 1.0
*/
class DataObject implements DumpableInterface, \IteratorAggregate, \JsonSerializable, \Countable
{
/**
* The data object properties.
*
* @var array
* @since 1.0
*/
private $properties = array();
/**
* The class constructor.
*
* @param mixed $properties Either an associative array or another object
* by which to set the initial properties of the new object.
*
* @since 1.0
* @throws \InvalidArgumentException
*/
public function __construct($properties = array())
{
// Check the properties input.
if (!empty($properties))
{
// Bind the properties.
$this->bind($properties);
}
}
/**
* The magic get method is used to get a data property.
*
* This method is a public proxy for the protected getProperty method.
*
* Note: Magic __get does not allow recursive calls. This can be tricky because the error generated by recursing into
* __get is "Undefined property: {CLASS}::{PROPERTY}" which is misleading. This is relevant for this class because
* requesting a non-visible property can trigger a call to a sub-function. If that references the property directly in
* the object, it will cause a recursion into __get.
*
* @param string $property The name of the data property.
*
* @return mixed The value of the data property, or null if the data property does not exist.
*
* @see DataObject::getProperty()
* @since 1.0
*/
public function __get($property)
{
return $this->getProperty($property);
}
/**
* The magic isset method is used to check the state of an object property.
*
* @param string $property The name of the data property.
*
* @return boolean True if set, otherwise false is returned.
*
* @since 1.0
*/
public function __isset($property)
{
return isset($this->properties[$property]);
}
/**
* The magic set method is used to set a data property.
*
* This is a public proxy for the protected setProperty method.
*
* @param string $property The name of the data property.
* @param mixed $value The value to give the data property.
*
* @return void
*
* @see DataObject::setProperty()
* @since 1.0
*/
public function __set($property, $value)
{
$this->setProperty($property, $value);
}
/**
* The magic unset method is used to unset a data property.
*
* @param string $property The name of the data property.
*
* @return void
*
* @since 1.0
*/
public function __unset($property)
{
unset($this->properties[$property]);
}
/**
* Binds an array or object to this object.
*
* @param mixed $properties An associative array of properties or an object.
* @param boolean $updateNulls True to bind null values, false to ignore null values.
*
* @return DataObject Returns itself to allow chaining.
*
* @since 1.0
* @throws \InvalidArgumentException
*/
public function bind($properties, $updateNulls = true)
{
// Check the properties data type.
if (!is_array($properties) && !is_object($properties))
{
throw new \InvalidArgumentException(sprintf('%s(%s)', __METHOD__, gettype($properties)));
}
// Check if the object is traversable.
if ($properties instanceof \Traversable)
{
// Convert iterator to array.
$properties = iterator_to_array($properties);
}
elseif (is_object($properties))
// Check if the object needs to be converted to an array.
{
// Convert properties to an array.
$properties = (array) $properties;
}
// Bind the properties.
foreach ($properties as $property => $value)
{
// Check if the value is null and should be bound.
if ($value === null && !$updateNulls)
{
continue;
}
// Set the property.
$this->setProperty($property, $value);
}
return $this;
}
/**
* Dumps the data properties into a stdClass object, recursively if appropriate.
*
* @param integer $depth The maximum depth of recursion (default = 3).
* For example, a depth of 0 will return a stdClass with all the properties in native
* form. A depth of 1 will recurse into the first level of properties only.
* @param \SplObjectStorage $dumped An array of already serialized objects that is used to avoid infinite loops.
*
* @return \stdClass The data properties as a simple PHP stdClass object.
*
* @since 1.0
*/
public function dump($depth = 3, \SplObjectStorage $dumped = null)
{
// Check if we should initialise the recursion tracker.
if ($dumped === null)
{
$dumped = new \SplObjectStorage;
}
// Add this object to the dumped stack.
$dumped->attach($this);
// Setup a container.
$dump = new \stdClass;
// Dump all object properties.
foreach (array_keys($this->properties) as $property)
{
// Get the property.
$dump->$property = $this->dumpProperty($property, $depth, $dumped);
}
return $dump;
}
/**
* Gets this object represented as an ArrayIterator.
*
* This allows the data properties to be access via a foreach statement.
*
* @return \ArrayIterator This object represented as an ArrayIterator.
*
* @see IteratorAggregate::getIterator()
* @since 1.0
*/
public function getIterator()
{
return new \ArrayIterator($this->dump(0));
}
/**
* Gets the data properties in a form that can be serialised to JSON format.
*
* @return string An object that can be serialised by json_encode().
*
* @since 1.0
*/
public function jsonSerialize()
{
return $this->dump();
}
/**
* Dumps a data property.
*
* If recursion is set, this method will dump any object implementing Data\Dumpable (like Data\Object and Data\Set); it will
* convert a Date object to a string; and it will convert a Registry to an object.
*
* @param string $property The name of the data property.
* @param integer $depth The current depth of recursion (a value of 0 will ignore recursion).
* @param \SplObjectStorage $dumped An array of already serialized objects that is used to avoid infinite loops.
*
* @return mixed The value of the dumped property.
*
* @since 1.0
*/
protected function dumpProperty($property, $depth, \SplObjectStorage $dumped)
{
$value = $this->getProperty($property);
if ($depth > 0)
{
// Check if the object is also an dumpable object.
if ($value instanceof DumpableInterface)
{
// Do not dump the property if it has already been dumped.
if (!$dumped->contains($value))
{
$value = $value->dump($depth - 1, $dumped);
}
}
// Check if the object is a date.
if ($value instanceof \DateTime)
{
$value = $value->format('Y-m-d H:i:s');
}
elseif ($value instanceof Registry)
// Check if the object is a registry.
{
$value = $value->toObject();
}
}
return $value;
}
/**
* Gets a data property.
*
* @param string $property The name of the data property.
*
* @return mixed The value of the data property.
*
* @see DataObject::__get()
* @since 1.0
*/
protected function getProperty($property)
{
// Get the raw value.
$value = array_key_exists($property, $this->properties) ? $this->properties[$property] : null;
return $value;
}
/**
* Sets a data property.
*
* If the name of the property starts with a null byte, this method will return null.
*
* @param string $property The name of the data property.
* @param mixed $value The value to give the data property.
*
* @return mixed The value of the data property.
*
* @see DataObject::__set()
* @since 1.0
*/
protected function setProperty($property, $value)
{
/*
* Check if the property starts with a null byte. If so, discard it because a later attempt to try to access it
* can cause a fatal error. See http://us3.php.net/manual/en/language.types.array.php#language.types.array.casting
*/
if (strpos($property, "\0") === 0)
{
return null;
}
// Set the value.
$this->properties[$property] = $value;
return $value;
}
/**
* Count the number of data properties.
*
* @return integer The number of data properties.
*
* @since 1.0
*/
public function count()
{
return count($this->properties);
}
}