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