From: Jochen Rau Date: Mon, 15 Mar 2010 20:53:40 +0000 (+0000) Subject: [+BUGFIX] Extbase (Reflection): Fixed a bug that would occur if an array with a non... X-Git-Tag: TYPO3_6-2-0alpha1~21^2~679 X-Git-Url: http://git.typo3.org/Packages/TYPO3.CMS.git/commitdiff_plain/d64ba0cafacc61c4745c51ec746f77a3ab855065 [+BUGFIX] Extbase (Reflection): Fixed a bug that would occur if an array with a non-existing key would be passed to ObjectAccess::getProperty(). Related to #6073. [+BUGFIX] Extbase (Reflection): ObjectAccess::getProperty() now throws an exception when a property does not exist, fixes #6005. [~TASK] Extbase (Persistence): Removed implodeAnd() and implodeOr(). You can pass an array of constraints as an argument to logicalAnd() and logicalOr(), or you can pass one or more constraints directly as reguments. Resolves #6735. [~TASK] Extbase (Persistence): The unions are now translated into LEFT JOIN instead of INNER JOIN. And there is no pid constraint added for (internal) unions anymore. Related to #6735. [~TASK] Extbase: Empty DateTime field values are now translated into NULL as property value (0->NULL). This is necessary because of the way TYPO3 4.x treats 0 as a "special" timestamp. The same is with incoming values which are not accepted as constructor argument of a DateTime object. --- diff --git a/typo3/sysext/extbase/Classes/Persistence/Mapper/DataMap.php b/typo3/sysext/extbase/Classes/Persistence/Mapper/DataMap.php index d483d097ca59..a0223a8d64ec 100644 --- a/typo3/sysext/extbase/Classes/Persistence/Mapper/DataMap.php +++ b/typo3/sysext/extbase/Classes/Persistence/Mapper/DataMap.php @@ -594,7 +594,11 @@ class Tx_Extbase_Persistence_Mapper_DataMap { case Tx_Extbase_Persistence_PropertyType::DECIMAL: return (float) $string; case Tx_Extbase_Persistence_PropertyType::DATE: - return new DateTime(date('r', $string)); + if (empty($string)) { // 0 -> NULL !!! + return NULL; + } else { + return new DateTime(date('c', $string)); + } case Tx_Extbase_Persistence_PropertyType::BOOLEAN: return (boolean) $string; default: @@ -613,7 +617,7 @@ class Tx_Extbase_Persistence_Mapper_DataMap { $convertedValue = $propertyValue ? 1 : 0; } elseif ($propertyValue instanceof Tx_Extbase_DomainObject_AbstractDomainObject) { $convertedValue = $propertyValue->getUid(); - } elseif (is_a($propertyValue, 'DateTime')) { + } elseif ($propertyValue instanceof DateTime) { $convertedValue = $propertyValue->format('U'); } elseif (is_int($propertyValue)) { $convertedValue = $propertyValue; diff --git a/typo3/sysext/extbase/Classes/Persistence/Query.php b/typo3/sysext/extbase/Classes/Persistence/Query.php index c294db3c48a3..e4c5eefc18ea 100644 --- a/typo3/sysext/extbase/Classes/Persistence/Query.php +++ b/typo3/sysext/extbase/Classes/Persistence/Query.php @@ -341,97 +341,56 @@ class Tx_Extbase_Persistence_Query implements Tx_Extbase_Persistence_QueryInterf } /** - * Performs a logical conjunction of the given constraints. + * Performs a logical conjunction of the given constraints. The method takes one or more contraints and concatenates them with a boolean AND. + * It also scepts a single array of constraints to be concatenated. * - * @param object $constraint1 First constraint - * @param object $constraint2 Second constraint + * @param mixed $constraint1 The first of multiple constraints or an array of constraints. * @return Tx_Extbase_Persistence_QOM_AndInterface - * @api Passing more than two constraints as arguments is currently not yet conform to the api of FLOW3 + * @api */ - public function logicalAnd($constraint1, $constraint2) { - $constraints = func_get_args(); - $numberOfConstraints = func_num_args(); - if (func_num_args() > 1) { - $resultingConstraint = array_shift($constraints); - foreach ($constraints as $constraint) { - $resultingConstraint = $this->qomFactory->_and( - $resultingConstraint, - $constraint - ); - } + public function logicalAnd($constraint1) { + if (is_array($constraint1)) { + $resultingConstraint = array_shift($constraint1); + $constraints = $constraint1; } else { - throw new Tx_Extbase_Persistence_Exception_InvalidNumberOfConstraints('There must be at least two constraints. Got only ' . func_num_args() . '.', 1268056288); - } - return $resultingConstraint; - } - - /** - * Performs a logical conjunction of the given constraints. - * - * @param array $constraints An array of constraints - * @return Tx_Extbase_Persistence_QOM_AndInterface - */ - public function implodeAnd(array $constraints) { - $numberOfConstraints = count($constraints); - $resultingConstraint = NULL; - if ($numberOfConstraints === 1) { - return array_shift($constraints); - } elseif ($numberOfConstraints > 1) { + $constraints = func_get_args(); $resultingConstraint = array_shift($constraints); - foreach ($constraints as $constraint) { - $resultingConstraint = $this->qomFactory->_and( - $resultingConstraint, - $constraint - ); - } + } + if ($resultingConstraint === NULL) { + throw new Tx_Extbase_Persistence_Exception_InvalidNumberOfConstraints('There must be at least one constraint or a non-empty array of constraints given.', 1268056288); + } + foreach ($constraints as $constraint) { + $resultingConstraint = $this->qomFactory->_and( + $resultingConstraint, + $constraint + ); } return $resultingConstraint; } - + /** * Performs a logical disjunction of the two given constraints * - * @param object $constraint1 First constraint - * @param object $constraint2 Second constraint + * @param mixed $constraint1 The first of multiple constraints or an array of constraints. * @return Tx_Extbase_Persistence_QOM_OrInterface - * @api Passing more than two constraints as arguments is currently not yet conform to the api of FLOW3 + * @api */ - public function logicalOr($constraint1, $constraint2) { - $constraints = func_get_args(); - $numberOfConstraints = func_num_args(); - if (func_num_args() > 1) { - $resultingConstraint = array_shift($constraints); - foreach ($constraints as $constraint) { - $resultingConstraint = $this->qomFactory->_or( - $resultingConstraint, - $constraint - ); - } + public function logicalOr($constraint1) { + if (is_array($constraint1)) { + $resultingConstraint = array_shift($constraint1); + $constraints = $constraint1; } else { - throw new Tx_Extbase_Persistence_Exception_InvalidNumberOfConstraints('There must be at least two constraints. Got only ' . func_num_args() . '.', 1268056288); - } - return $resultingConstraint; - } - - /** - * Performs a logical conjunction of the given constraints. - * - * @param array $constraints An array of constraints - * @return Tx_Extbase_Persistence_QOM_AndInterface - */ - public function implodeOr(array $constraints) { - $numberOfConstraints = count($constraints); - $resultingConstraint = NULL; - if ($numberOfConstraints === 1) { - return array_shift($constraints); - } elseif ($numberOfConstraints > 1) { + $constraints = func_get_args(); $resultingConstraint = array_shift($constraints); - foreach ($constraints as $constraint) { - $resultingConstraint = $this->qomFactory->_or( - $resultingConstraint, - $constraint - ); - } + } + if ($resultingConstraint === NULL) { + throw new Tx_Extbase_Persistence_Exception_InvalidNumberOfConstraints('There must be at least one constraint or a non-empty array of constraints given.', 1268056288); + } + foreach ($constraints as $constraint) { + $resultingConstraint = $this->qomFactory->_or( + $resultingConstraint, + $constraint + ); } return $resultingConstraint; } diff --git a/typo3/sysext/extbase/Classes/Persistence/QueryInterface.php b/typo3/sysext/extbase/Classes/Persistence/QueryInterface.php index 67018e195e52..47c91a5ce5f9 100644 --- a/typo3/sysext/extbase/Classes/Persistence/QueryInterface.php +++ b/typo3/sysext/extbase/Classes/Persistence/QueryInterface.php @@ -172,22 +172,20 @@ interface Tx_Extbase_Persistence_QueryInterface { /** * Performs a logical conjunction of the two given constraints. * - * @param object $constraint1 First constraint - * @param object $constraint2 Second constraint + * @param mixed $constraint1 The first of multiple constraints or an array of constraints. * @return object * @api */ - public function logicalAnd($constraint1, $constraint2); + public function logicalAnd($constraint1); /** * Performs a logical disjunction of the two given constraints * - * @param object $constraint1 First constraint - * @param object $constraint2 Second constraint + * @param mixed $constraint1 The first of multiple constraints or an array of constraints. * @return object * @api */ - public function logicalOr($constraint1, $constraint2); + public function logicalOr($constraint1); /** * Performs a logical negation of the given constraint diff --git a/typo3/sysext/extbase/Classes/Persistence/Storage/Typo3DbBackend.php b/typo3/sysext/extbase/Classes/Persistence/Storage/Typo3DbBackend.php index 549609098dd6..9b631b0a079c 100644 --- a/typo3/sysext/extbase/Classes/Persistence/Storage/Typo3DbBackend.php +++ b/typo3/sysext/extbase/Classes/Persistence/Storage/Typo3DbBackend.php @@ -276,8 +276,7 @@ class Tx_Extbase_Persistence_Storage_Typo3DbBackend implements Tx_Extbase_Persis $this->parseOrderings($query->getOrderings(), $source, $sql); $this->parseLimitAndOffset($query->getLimit(), $query->getOffset(), $sql); - $tables = array_merge(array_keys($sql['tables']), array_keys($sql['unions'])); - foreach ($tables as $tableName) { + foreach (array_keys($sql['tables']) as $tableName) { if (is_string($tableName) && strlen($tableName) > 0) { $this->addAdditionalWhereClause($query->getQuerySettings(), $tableName, $sql); } @@ -397,7 +396,7 @@ class Tx_Extbase_Persistence_Storage_Typo3DbBackend implements Tx_Extbase_Persis } $sql['tables'][$leftTableName] = $leftTableName; - $sql['unions'][$rightTableName] = 'INNER JOIN ' . $rightTableName; + $sql['unions'][$rightTableName] = 'LEFT JOIN ' . $rightTableName; $joinCondition = $join->getJoinCondition(); if ($joinCondition instanceof Tx_Extbase_Persistence_QOM_EquiJoinCondition) { @@ -594,25 +593,25 @@ class Tx_Extbase_Persistence_Storage_Typo3DbBackend implements Tx_Extbase_Persis $childTableName = $columnMap->getChildTableName(); if ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_ONE) { if (isset($parentKeyFieldName)) { - $sql['unions'][$childTableName] = 'INNER JOIN ' . $childTableName . ' ON ' . $tableName . '.uid=' . $childTableName . '.' . $parentKeyFieldName; + $sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.uid=' . $childTableName . '.' . $parentKeyFieldName; } else { - $sql['unions'][$childTableName] = 'INNER JOIN ' . $childTableName . ' ON ' . $tableName . '.' . $columnName . '=' . $childTableName . '.uid'; + $sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.' . $columnName . '=' . $childTableName . '.uid'; } $className = $this->dataMapper->getType($className, $propertyName); } elseif ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY) { if (isset($parentKeyFieldName)) { - $sql['unions'][$childTableName] = 'INNER JOIN ' . $childTableName . ' ON ' . $tableName . '.uid=' . $childTableName . '.' . $parentKeyFieldName; + $sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.uid=' . $childTableName . '.' . $parentKeyFieldName; } else { $onStatement = '(' . $tableName . '.' . $columnName . ' LIKE CONCAT(\'%,\',' . $childTableName . '.uid,\',%\')'; $onStatement .= ' OR ' . $tableName . '.' . $columnName . ' LIKE CONCAT(\'%,\',' . $childTableName . '.uid)'; $onStatement .= ' OR ' . $tableName . '.' . $columnName . ' LIKE CONCAT(' . $childTableName . '.uid,\',%\'))'; - $sql['unions'][$childTableName] = 'INNER JOIN ' . $childTableName . ' ON ' . $onStatement; + $sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $onStatement; } $className = $this->dataMapper->getElementType($className, $propertyName); } elseif ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) { $relationTableName = $columnMap->getRelationTableName(); - $sql['unions'][$relationTableName] = 'INNER JOIN ' . $relationTableName . ' ON ' . $tableName . '.uid=' . $relationTableName . '.uid_local'; - $sql['unions'][$childTableName] = 'INNER JOIN ' . $childTableName . ' ON ' . $relationTableName . '.uid_foreign=' . $childTableName . '.uid'; + $sql['unions'][$relationTableName] = 'LEFT JOIN ' . $relationTableName . ' ON ' . $tableName . '.uid=' . $relationTableName . '.uid_local'; + $sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $relationTableName . '.uid_foreign=' . $childTableName . '.uid'; $className = $this->dataMapper->getElementType($className, $propertyName); } else { throw new Tx_Extbase_Persistence_Exception('Could not determine type of relation.', 1252502725); diff --git a/typo3/sysext/extbase/Classes/Property/Mapper.php b/typo3/sysext/extbase/Classes/Property/Mapper.php index 2a01257c7289..5a31bc568cee 100644 --- a/typo3/sysext/extbase/Classes/Property/Mapper.php +++ b/typo3/sysext/extbase/Classes/Property/Mapper.php @@ -253,7 +253,7 @@ class Tx_Extbase_Property_Mapper { try { $propertyValue = new $targetType($propertyValue); } catch (Exception $e) { - throw new InvalidArgumentException('Conversion to a ' . $targetType . ' object is not possible. Cause: ' . $e->getMessage(), 1190034628); + $propertyValue = NULL; } } } else { diff --git a/typo3/sysext/extbase/Classes/Reflection/ObjectAccess.php b/typo3/sysext/extbase/Classes/Reflection/ObjectAccess.php index 9fb267874e30..20372c1e5678 100644 --- a/typo3/sysext/extbase/Classes/Reflection/ObjectAccess.php +++ b/typo3/sysext/extbase/Classes/Reflection/ObjectAccess.php @@ -43,30 +43,40 @@ class Tx_Extbase_Reflection_ObjectAccess { /** * Get a property of a given object. * Tries to get the property the following ways: + * - if the target is an array, and has this property, we call it. + * - if public getter method exists, call it. * - if the target object is an instance of ArrayAccess, it gets the property * on it if it exists. - * - if public getter method exists, call it. * - if public property exists, return the value of it. * - else, throw exception * - * @param object $object Object to get the property from + * @param mixed $subject Object or array to get the property from * @param string $propertyName name of the property to retrieve * @return object Value of the property. + * @throws InvalidArgumentException in case $subject was not an object or $propertyName was not a string + * @throws RuntimeException if the property was not accessible */ - static public function getProperty($object, $propertyName) { - if (!is_object($object) && !is_array($object)) throw new InvalidArgumentException('$object must be an object or an array, ' . gettype($object). ' given.', 1237301367); + static public function getProperty($subject, $propertyName) { + if (!is_object($subject) && !is_array($subject)) throw new InvalidArgumentException('$subject must be an object or array, ' . gettype($subject). ' given.', 1237301367); if (!is_string($propertyName)) throw new InvalidArgumentException('Given property name is not of type string.', 1231178303); - if (is_array($object) && array_key_exists($propertyName, $object)) { - return $object[$propertyName]; - } elseif (is_callable(array($object, $getterMethodName = self::buildGetterMethodName($propertyName)))) { - return call_user_func(array($object, $getterMethodName)); - } elseif ($object instanceof ArrayAccess && isset($object[$propertyName])) { - return $object[$propertyName]; - } elseif (is_object($object) && array_key_exists($propertyName, get_object_vars($object))) { - return $object->$propertyName; + if (is_array($subject)) { + if (array_key_exists($propertyName, $subject)) { + return $subject[$propertyName]; + } + } else { + if (is_callable(array($subject, 'get' . ucfirst($propertyName)))) { + return call_user_func(array($subject, 'get' . ucfirst($propertyName))); + } elseif (is_callable(array($subject, 'is' . ucfirst($propertyName)))) { + return call_user_func(array($subject, 'is' . ucfirst($propertyName))); + } elseif ($subject instanceof ArrayAccess && isset($subject[$propertyName])) { + return $subject[$propertyName]; + } elseif (array_key_exists($propertyName, get_object_vars($subject))) { + return $subject->$propertyName; + } } - return NULL; + + throw new RuntimeException('The property "' . $propertyName . '" on the subject was not accessible.', 1263391473); } /** @@ -81,8 +91,11 @@ class Tx_Extbase_Reflection_ObjectAccess { static public function getPropertyPath($object, $propertyPath) { $propertyPathSegments = explode('.', $propertyPath); foreach ($propertyPathSegments as $pathSegment) { - $object = self::getProperty($object, $pathSegment); - if ($object === NULL) return NULL; + if (is_object($object) && self::isPropertyGettable($object, $pathSegment)) { + $object = self::getProperty($object, $pathSegment); + } else { + return NULL; + } } return $object; } @@ -162,16 +175,19 @@ class Tx_Extbase_Reflection_ObjectAccess { } return $properties; } - + /** - * Build the getter method name for a given property by capitalizing the - * first letter of the property, and prepending it with "get". + * Tells if the value of the specified property can be retrieved by this Object Accessor. * - * @param string $property Name of the property - * @return string Name of the getter method name + * @param object $object Object containting the property + * @param string $propertyName Name of the property to check + * @return boolean */ - static protected function buildGetterMethodName($property) { - return 'get' . ucfirst($property); + static public function isPropertyGettable($object, $propertyName) { + if (!is_object($object)) throw new InvalidArgumentException('$object must be an object, ' . gettype($object). ' given.', 1259828921); + if (array_search($propertyName, array_keys(get_class_vars(get_class($object)))) !== FALSE) return TRUE; + if (is_callable(array($object, 'get' . ucfirst($propertyName)))) return TRUE; + return is_callable(array($object, 'is' . ucfirst($propertyName))); } /**