[TASK] Use $x[n] instead of substr($x, n, 1)
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / DomainObject / AbstractDomainObject.php
1 <?php
2 namespace TYPO3\CMS\Extbase\DomainObject;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2010-2013 Extbase Team (http://forge.typo3.org/projects/typo3v4-mvc)
8 * Extbase is a backport of TYPO3 Flow. All credits go to the TYPO3 Flow team.
9 * All rights reserved
10 *
11 * This script is part of the TYPO3 project. The TYPO3 project is
12 * free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * The GNU General Public License can be found at
18 * http://www.gnu.org/copyleft/gpl.html.
19 * A copy is found in the text file GPL.txt and important notices to the license
20 * from the author is found in LICENSE.txt distributed with these scripts.
21 *
22 *
23 * This script is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * This copyright notice MUST APPEAR in all copies of the script!
29 ***************************************************************/
30 /**
31 * A generic Domain Object.
32 *
33 * All Model domain objects need to inherit from either AbstractEntity or AbstractValueObject, as this provides important framework information.
34 */
35 abstract class AbstractDomainObject implements \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface, \TYPO3\CMS\Extbase\Persistence\ObjectMonitoringInterface {
36
37 /**
38 * @var int The uid of the record. The uid is only unique in the context of the database table.
39 */
40 protected $uid;
41
42 /**
43 * @var int The uid of the localized record. In TYPO3 v4.x the property "uid" holds the uid of the record in default language (the translationOrigin).
44 */
45 protected $_localizedUid;
46
47 /**
48 * @var int The uid of the language of the object. In TYPO3 v4.x this is the uid of the language record in the table sys_language.
49 */
50 protected $_languageUid;
51
52 /**
53 * @var int The id of the page the record is "stored".
54 */
55 protected $pid;
56
57 /**
58 * TRUE if the object is a clone
59 *
60 * @var boolean
61 */
62 private $_isClone = FALSE;
63
64 /**
65 * @var array An array holding the clean property values. Set right after reconstitution of the object
66 */
67 private $_cleanProperties;
68
69 /**
70 * This is the magic __wakeup() method. It's invoked by the unserialize statement in the reconstitution process
71 * of the object. If you want to implement your own __wakeup() method in your Domain Object you have to call
72 * parent::__wakeup() first!
73 *
74 * @return void
75 */
76 public function __wakeup() {
77 $this->initializeObject();
78 }
79
80 public function initializeObject() {
81 }
82
83 /**
84 * Getter for uid.
85 *
86 * @return integer the uid or NULL if none set yet.
87 */
88 public function getUid() {
89 if ($this->uid !== NULL) {
90 return (int)$this->uid;
91 } else {
92 return NULL;
93 }
94 }
95
96 /**
97 * Setter for the pid.
98 *
99 * @param integer|NULL $pid
100 * @return void
101 */
102 public function setPid($pid) {
103 if ($pid === NULL) {
104 $this->pid = NULL;
105 } else {
106 $this->pid = (int)$pid;
107 }
108 }
109
110 /**
111 * Getter for the pid.
112 *
113 * @return integer The pid or NULL if none set yet.
114 */
115 public function getPid() {
116 if ($this->pid === NULL) {
117 return NULL;
118 } else {
119 return (int)$this->pid;
120 }
121 }
122
123 /**
124 * Reconstitutes a property. Only for internal use.
125 *
126 * @param string $propertyName
127 * @param mixed $propertyValue
128 * @return boolean
129 */
130 public function _setProperty($propertyName, $propertyValue) {
131 if ($this->_hasProperty($propertyName)) {
132 $this->{$propertyName} = $propertyValue;
133 return TRUE;
134 }
135 return FALSE;
136 }
137
138 /**
139 * Returns the property value of the given property name. Only for internal use.
140 *
141 * @param string $propertyName
142 * @return mixed The propertyValue
143 */
144 public function _getProperty($propertyName) {
145 return $this->{$propertyName};
146 }
147
148 /**
149 * Returns a hash map of property names and property values. Only for internal use.
150 *
151 * @return array The properties
152 */
153 public function _getProperties() {
154 $properties = get_object_vars($this);
155 foreach ($properties as $propertyName => $propertyValue) {
156 if ($propertyName[0] === '_') {
157 unset($properties[$propertyName]);
158 }
159 }
160 return $properties;
161 }
162
163 /**
164 * Returns the property value of the given property name. Only for internal use.
165 *
166 * @param string $propertyName
167 * @return boolean TRUE bool true if the property exists, FALSE if it doesn't exist or NULL in case of an error.
168 */
169 public function _hasProperty($propertyName) {
170 return property_exists($this, $propertyName);
171 }
172
173 /**
174 * Returns TRUE if the object is new (the uid was not set, yet). Only for internal use
175 *
176 * @return boolean
177 */
178 public function _isNew() {
179 return $this->uid === NULL;
180 }
181
182 /**
183 * Register an object's clean state, e.g. after it has been reconstituted
184 * from the database.
185 *
186 * @param string $propertyName The name of the property to be memorized. If omitted all persistable properties are memorized.
187 * @return void
188 */
189 public function _memorizeCleanState($propertyName = NULL) {
190 if ($propertyName !== NULL) {
191 $this->_memorizePropertyCleanState($propertyName);
192 } else {
193 $this->_cleanProperties = array();
194 $properties = get_object_vars($this);
195 foreach ($properties as $propertyName => $propertyValue) {
196 if ($propertyName[0] === '_') {
197 continue;
198 }
199 // Do not memorize "internal" properties
200 $this->_memorizePropertyCleanState($propertyName);
201 }
202 }
203 }
204
205 /**
206 * Register an properties's clean state, e.g. after it has been reconstituted
207 * from the database.
208 *
209 * @param string $propertyName The name of the property to be memorized. If omittet all persistable properties are memorized.
210 * @return void
211 */
212 public function _memorizePropertyCleanState($propertyName) {
213 $propertyValue = $this->{$propertyName};
214 if (!is_array($this->_cleanProperties)) {
215 $this->_cleanProperties = array();
216 }
217 if (is_object($propertyValue)) {
218 $this->_cleanProperties[$propertyName] = clone $propertyValue;
219 // We need to make sure the clone and the original object
220 // are identical when compared with == (see _isDirty()).
221 // After the cloning, the Domain Object will have the property
222 // "isClone" set to TRUE, so we manually have to set it to FALSE
223 // again. Possible fix: Somehow get rid of the "isClone" property,
224 // which is currently needed in Fluid.
225 if ($propertyValue instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject) {
226 $this->_cleanProperties[$propertyName]->_setClone(FALSE);
227 }
228 } else {
229 $this->_cleanProperties[$propertyName] = $propertyValue;
230 }
231 }
232
233 /**
234 * Returns a hash map of clean properties and $values.
235 *
236 * @return array
237 */
238 public function _getCleanProperties() {
239 return $this->_cleanProperties;
240 }
241
242 /**
243 * Returns the clean value of the given property. The returned value will be NULL if the clean state was not memorized before, or
244 * if the clean value is NULL.
245 *
246 * @param string $propertyName The name of the property to be memorized. If omittet all persistable properties are memorized.
247 * @return mixed The clean property value or NULL
248 */
249 public function _getCleanProperty($propertyName) {
250 if (is_array($this->_cleanProperties)) {
251 return isset($this->_cleanProperties[$propertyName]) ? $this->_cleanProperties[$propertyName] : NULL;
252 } else {
253 return NULL;
254 }
255 }
256
257 /**
258 * Returns TRUE if the properties were modified after reconstitution
259 *
260 * @param string $propertyName An optional name of a property to be checked if its value is dirty
261 * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\TooDirtyException
262 * @return boolean
263 */
264 public function _isDirty($propertyName = NULL) {
265 if ($this->uid !== NULL && $this->_getCleanProperty('uid') !== NULL && $this->uid != $this->_getCleanProperty('uid')) {
266 throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\TooDirtyException('The uid "' . $this->uid . '" has been modified, that is simply too much.', 1222871239);
267 }
268
269 if ($propertyName === NULL) {
270 foreach ($this->_getCleanProperties() as $propertyName => $cleanPropertyValue) {
271 if ($this->isPropertyDirty($cleanPropertyValue, $this->{$propertyName}) === TRUE) {
272 return TRUE;
273 }
274 }
275 } else {
276 if ($this->isPropertyDirty($this->_getCleanProperty($propertyName), $this->{$propertyName}) === TRUE) {
277 return TRUE;
278 }
279 }
280 return FALSE;
281 }
282
283 /**
284 * Checks the $value against the $cleanState.
285 *
286 * @param mixed $previousValue
287 * @param mixed $currentValue
288 * @return boolean
289 */
290 protected function isPropertyDirty($previousValue, $currentValue) {
291 // In case it is an object and it implements the ObjectMonitoringInterface, we call _isDirty() instead of a simple comparison of objects.
292 // We do this, because if the object itself contains a lazy loaded property, the comparison of the objects might fail even if the object didn't change
293 if (is_object($currentValue)) {
294 if ($currentValue instanceof \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface) {
295 $result = !is_object($previousValue) || get_class($previousValue) !== get_class($currentValue) || $currentValue->getUid() !== $previousValue->getUid();
296 } elseif ($currentValue instanceof \TYPO3\CMS\Extbase\Persistence\ObjectMonitoringInterface) {
297 $result = !is_object($previousValue) || $currentValue->_isDirty() || get_class($previousValue) !== get_class($currentValue);
298 } else {
299 // For all other objects we do only a simple comparison (!=) as we want cloned objects to return the same values.
300 $result = $previousValue != $currentValue;
301 }
302 } else {
303 $result = $previousValue !== $currentValue;
304 }
305 return $result;
306 }
307
308 /**
309 * Returns TRUE if the object has been clonesd, cloned, FALSE otherwise.
310 *
311 * @return boolean TRUE if the object has been cloned
312 */
313 public function _isClone() {
314 return $this->_isClone;
315 }
316
317 /**
318 * Setter whether this Domain Object is a clone of another one.
319 * NEVER SET THIS PROPERTY DIRECTLY. We currently need it to make the
320 * _isDirty check inside AbstractEntity work, but it is just a work-
321 * around right now.
322 *
323 * @param boolean $clone
324 */
325 public function _setClone($clone) {
326 $this->_isClone = (boolean) $clone;
327 }
328
329 /**
330 * Clone method. Sets the _isClone property.
331 *
332 * @return void
333 */
334 public function __clone() {
335 $this->_isClone = TRUE;
336 }
337
338 /**
339 * Returns the class name and the uid of the object as string
340 *
341 * @return string
342 */
343 public function __toString() {
344 return get_class($this) . ':' . (string) $this->uid;
345 }
346 }