AbstractDomainObject::getUid()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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