Skip to content
Snippets Groups Projects
Commit 4a18072a authored by Stefan Bürk's avatar Stefan Bürk
Browse files

[BUGFIX] Avoid invalid property access when persisting extbase objects

With #95819 the detection of Domain Object properties was changed to use
ClassSchema in order to support uninitialized properties is models.
This was not possible with `DomainObjectInterface::_getProperties()`
which used `get_object_vars()` under the hood.

However, with mentioned change, other properties which had been
ignored by `get_object_vars()` were now propagated to persistence,
even if those properties were not configured in TCA. The change in
behavior is considered a regression and is now addressed:

The retrieval of the property value is delayed until after it is
ensured that the property is a persistable property (which asserts
that we are not reading from private properties).

Resolves: #98148
Related: #95819
Releases: main, 11.5
Change-Id: I94d41715a49953045d32361a00bc274c15112a1e
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/75534


Reviewed-by: default avatarBenjamin Franzke <bfr@qbus.de>
Reviewed-by: default avatartheline <typo3@root-access.de>
Reviewed-by: default avatarAlexander Schnitzler <git@alexanderschnitzler.de>
Reviewed-by: default avatarOliver Hader <oliver.hader@typo3.org>
Reviewed-by: default avatarStefan Bürk <stefan@buerk.tech>
Tested-by: default avatarBenjamin Franzke <bfr@qbus.de>
Tested-by: default avatarcore-ci <typo3@b13.com>
Tested-by: default avatartheline <typo3@root-access.de>
Tested-by: default avatarAlexander Schnitzler <git@alexanderschnitzler.de>
Tested-by: default avatarOliver Hader <oliver.hader@typo3.org>
Tested-by: default avatarStefan Bürk <stefan@buerk.tech>
parent 43c1dd41
Branches
Tags
No related merge requests found
......@@ -310,8 +310,11 @@ class Backend implements BackendInterface, SingletonInterface
$classSchema = $this->reflectionService->getClassSchema($className);
foreach ($classSchema->getDomainObjectProperties() as $property) {
$propertyName = $property->getName();
if (!$dataMap->isPersistableProperty($propertyName)) {
continue;
}
$propertyValue = $object->_getProperty($propertyName);
if (!$dataMap->isPersistableProperty($propertyName) || $this->propertyValueIsLazyLoaded($propertyValue)) {
if ($this->propertyValueIsLazyLoaded($propertyValue)) {
continue;
}
$columnMap = $dataMap->getColumnMap($propertyName);
......@@ -601,8 +604,11 @@ class Backend implements BackendInterface, SingletonInterface
$classSchema = $this->reflectionService->getClassSchema($className);
foreach ($classSchema->getDomainObjectProperties() as $property) {
$propertyName = $property->getName();
if (!$dataMap->isPersistableProperty($propertyName)) {
continue;
}
$propertyValue = $object->_getProperty($propertyName);
if (!$dataMap->isPersistableProperty($propertyName) || $this->propertyValueIsLazyLoaded($propertyValue)) {
if ($this->propertyValueIsLazyLoaded($propertyValue)) {
continue;
}
$columnMap = $dataMap->getColumnMap($propertyName);
......@@ -906,11 +912,11 @@ class Backend implements BackendInterface, SingletonInterface
$classSchema = $this->reflectionService->getClassSchema($className);
foreach ($classSchema->getDomainObjectProperties() as $property) {
$propertyName = $property->getName();
$propertyValue = $object->_getProperty($propertyName);
$columnMap = $dataMap->getColumnMap($propertyName);
if ($columnMap === null) {
continue;
}
$propertyValue = $object->_getProperty($propertyName);
if ($property->getCascadeValue() === 'remove') {
if ($columnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_MANY) {
foreach ($propertyValue as $containedObject) {
......
......@@ -383,10 +383,9 @@ class Typo3DbBackend implements BackendInterface, SingletonInterface
$classSchema = $this->reflectionService->getClassSchema($className);
foreach ($classSchema->getDomainObjectProperties() as $property) {
$propertyName = $property->getName();
$propertyValue = $object->_getProperty($propertyName);
// @todo We couple the Backend to the Entity implementation (uid, isClone); changes there breaks this method
if ($dataMap->isPersistableProperty($propertyName) && $propertyName !== AbstractDomainObject::PROPERTY_UID && $propertyName !== AbstractDomainObject::PROPERTY_PID && $propertyName !== 'isClone') {
$propertyValue = $object->_getProperty($propertyName);
$fieldName = $dataMap->getColumnMap($propertyName)->getColumnName();
if ($propertyValue === null) {
$whereClause[] = $queryBuilder->expr()->isNull($fieldName);
......
......@@ -21,6 +21,24 @@ use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
class DateTimeImmutableExample extends AbstractEntity
{
/**
* Static value which is not part of an "entity".
* (this property has to be ignored by Extbase when persisting this entity)
*/
public static string $publicStaticValue;
/**
* Transient value, having a name starting with `_`.
* (this property has to be ignored by Extbase when persisting this entity)
*/
public string $_publicTransientValue;
/**
* Transient value without any getter or setter.
* (this property has to be ignored by Extbase when persisting this entity)
*/
private string $privateTransientValue; // @phpstan-ignore-line since it is unused on purpose
/**
* A datetimeImmutable stored in a text field
*/
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment