[+FEATURE] Extbase (Reflection): Tx_Extbase_Reflection_ObjectAccess supports stdClass...
authorBastian Waidelich <bastian@typo3.org>
Thu, 2 Dec 2010 17:44:06 +0000 (17:44 +0000)
committerBastian Waidelich <bastian@typo3.org>
Thu, 2 Dec 2010 17:44:06 +0000 (17:44 +0000)
I've backported the ObjectAccess class and its unit tests from FLOW3.
Note: FLOW3's implementation allows to specify a third argument for the
method getPropertyPath() in order to support Closures.
This part is not backported as it's not compatible with PHP < 5.3.

typo3/sysext/extbase/Classes/Reflection/ObjectAccess.php
typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/ArrayAccessClass.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithGettersAndSetters.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/Unit/Reflection/ObjectAccessTest.php [new file with mode: 0644]

index 049af58..ed301bf 100644 (file)
@@ -76,7 +76,7 @@ class Tx_Extbase_Reflection_ObjectAccess {
                        }
                }
 
-               throw new RuntimeException('The property "' . $propertyName . '" on the subject was not accessible.', 1263391473);
+               throw new Tx_Extbase_Reflection_Exception_PropertyNotAccessibleException('The property "' . $propertyName . '" on the subject was not accessible.', 1263391473);
        }
 
        /**
@@ -93,8 +93,8 @@ class Tx_Extbase_Reflection_ObjectAccess {
                foreach ($propertyPathSegments as $pathSegment) {
                        if (is_object($subject) && self::isPropertyGettable($subject, $pathSegment)) {
                                $subject = self::getProperty($subject, $pathSegment);
-                       } elseif (is_array($subject) && array_key_exists($pathSegment, $subject)) {
-                               $subject = self::getProperty($subject, $pathSegment);
+                       } elseif ((is_array($subject) || $subject instanceof ArrayAccess) && isset($subject[$pathSegment])) {
+                               $subject = $subject[$pathSegment];
                        } else {
                                return NULL;
                        }
@@ -117,7 +117,11 @@ class Tx_Extbase_Reflection_ObjectAccess {
         * @return void
         * @throws Tx_Extbase_Reflection_Exception if property was could not be set
         */
-       static public function setProperty($object, $propertyName, $propertyValue) {
+       static public function setProperty(&$object, $propertyName, $propertyValue) {
+               if (is_array($object)) {
+                       $object[$propertyName] = $propertyValue;
+                       return TRUE;
+               }
                if (!is_object($object)) throw new InvalidArgumentException('$object must be an object, ' . gettype($object). ' given.', 1237301368);
                if (!is_string($propertyName)) throw new InvalidArgumentException('Given property name is not of type string.', 1231178878);
 
@@ -145,14 +149,67 @@ class Tx_Extbase_Reflection_ObjectAccess {
         * @todo What to do with ArrayAccess
         */
        static public function getAccessiblePropertyNames($object) {
+               return self::getGettablePropertyNames($object);
+       }
+
+       /**
+        * Returns an array of properties which can be get with the getProperty()
+        * method.
+        * Includes the following properties:
+        * - which can be get through a public getter method.
+        * - public properties which can be directly get.
+        *
+        * @param object $object Object to receive property names for
+        * @return array Array of all gettable property names
+        * @author Sebastian Kurfürst <sebastian@typo3.org>
+        * @author Karsten Dambekalns <karsten@typo3.org>
+        */
+       static public function getGettablePropertyNames($object) {
                if (!is_object($object)) throw new InvalidArgumentException('$object must be an object, ' . gettype($object). ' given.', 1237301369);
-               $declaredPropertyNames = array_keys(get_class_vars(get_class($object)));
+               if ($object instanceof stdClass) {
+                       $declaredPropertyNames = array_keys(get_object_vars($object));
+               } else {
+                       $declaredPropertyNames = array_keys(get_class_vars(get_class($object)));
+               }
+
+               foreach (get_class_methods($object) as $methodName) {
+                       if (is_callable(array($object, $methodName))) {
+                               if (substr($methodName, 0, 2) === 'is') {
+                                       $declaredPropertyNames[] = strtolower(substr($methodName, 2, 1)) . (substr($methodName, 3));
+                               }
+                               if (substr($methodName, 0, 3) === 'get') {
+                                       $declaredPropertyNames[] = strtolower(substr($methodName, 3, 1)) . (substr($methodName, 4));
+                               }
+                       }
+               }
+
+               $propertyNames = array_unique($declaredPropertyNames);
+               sort($propertyNames);
+               return $propertyNames;
+       }
+
+       /**
+        * Returns an array of properties which can be set with the setProperty()
+        * method.
+        * Includes the following properties:
+        * - which can be set through a public setter method.
+        * - public properties which can be directly set.
+        *
+        * @param object $object Object to receive property names for
+        * @return array Array of all settable property names
+        * @author Karsten Dambekalns <karsten@typo3.org>
+        */
+       static public function getSettablePropertyNames($object) {
+               if (!is_object($object)) throw new InvalidArgumentException('$object must be an object, ' . gettype($object). ' given.', 1264022994);
+               if ($object instanceof stdClass) {
+                       $declaredPropertyNames = array_keys(get_object_vars($object));
+               } else {
+                       $declaredPropertyNames = array_keys(get_class_vars(get_class($object)));
+               }
 
                foreach (get_class_methods($object) as $methodName) {
-                       if (substr($methodName, 0, 3) === 'get') {
-                               $propertyName = substr($methodName, 3);
-                               $propertyName[0] = strtolower($propertyName[0]);
-                               $declaredPropertyNames[] = $propertyName;
+                       if (substr($methodName, 0, 3) === 'set' && is_callable(array($object, $methodName))) {
+                               $declaredPropertyNames[] = strtolower(substr($methodName, 3, 1)) . (substr($methodName, 4));
                        }
                }
 
@@ -167,17 +224,50 @@ class Tx_Extbase_Reflection_ObjectAccess {
         *
         * @param object $object Object to get all properties from.
         * @return array Associative array of all properties.
-        * @todo What to do with ArrayAccess
+        * @deprecated since Extbase 1.3.0; will be removed in Extbase 1.5.0. Please use getGettableProperties() instead
         */
        static public function getAccessibleProperties($object) {
+               t3lib_div::logDeprecatedFunction();
+               return self::getGettableProperties($object);
+       }
+
+       /**
+        * Get all properties (names and their current values) of the current
+        * $object that are accessible through this class.
+        *
+        * @param object $object Object to get all properties from.
+        * @return array Associative array of all properties.
+        * @author Sebastian Kurfürst <sebastian@typo3.org>
+        * @todo What to do with ArrayAccess
+        */
+       static public function getGettableProperties($object) {
                if (!is_object($object)) throw new InvalidArgumentException('$object must be an object, ' . gettype($object). ' given.', 1237301370);
                $properties = array();
-               foreach (self::getAccessiblePropertyNames($object) as $propertyName) {
+               foreach (self::getGettablePropertyNames($object) as $propertyName) {
                        $properties[$propertyName] = self::getProperty($object, $propertyName);
                }
                return $properties;
        }
-       
+
+
+       /**
+        * Tells if the value of the specified property can be set by this Object Accessor.
+        *
+        * @param object $object Object containting the property
+        * @param string $propertyName Name of the property to check
+        * @return boolean
+        * @author Robert Lemke <robert@typo3.org>
+        */
+       static public function isPropertySettable($object, $propertyName) {
+               if (!is_object($object)) throw new InvalidArgumentException('$object must be an object, ' . gettype($object). ' given.', 1259828920);
+               if ($object instanceof stdClass && array_search($propertyName, array_keys(get_object_vars($object))) !== FALSE) {
+                       return TRUE;
+               } elseif (array_search($propertyName, array_keys(get_class_vars(get_class($object)))) !== FALSE) {
+                       return TRUE;
+               }
+               return is_callable(array($object, self::buildSetterMethodName($propertyName)));
+       }
+
        /**
         * Tells if the value of the specified property can be retrieved by this Object Accessor.
         *
@@ -187,7 +277,11 @@ class Tx_Extbase_Reflection_ObjectAccess {
         */
        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 ($object instanceof stdClass && array_search($propertyName, array_keys(get_object_vars($object))) !== FALSE) {
+                       return TRUE;
+               } elseif (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)));
        }
diff --git a/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/ArrayAccessClass.php b/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/ArrayAccessClass.php
new file mode 100644 (file)
index 0000000..1c097be
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  All rights reserved
+*
+*  This class is a backport of the corresponding class of FLOW3.
+*  All credits go to the v5 team.
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * ArrayAccess class for the Reflection tests
+ *
+ * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
+ */
+class Tx_Extbase_Tests_Unit_Reflection_Fixture_ArrayAccessClass implements ArrayAccess {
+
+       protected $array = array();
+
+       public function __construct(array $array) {
+               $this->array = $array;
+       }
+
+       public function offsetExists($offset) {
+               return array_key_exists($offset, $this->array);
+       }
+
+       public function offsetGet($offset) {
+               return $this->array[$offset];
+       }
+
+       public function offsetSet($offset, $value) {
+               $this->array[$offset] = $value;
+       }
+
+       public function offsetUnset($offset) {
+               unset($this->array[$offset]);
+       }
+
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithGettersAndSetters.php b/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithGettersAndSetters.php
new file mode 100644 (file)
index 0000000..9099293
--- /dev/null
@@ -0,0 +1,90 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  All rights reserved
+*
+*  This class is a backport of the corresponding class of FLOW3.
+*  All credits go to the v5 team.
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * Fixture class with getters and setters
+ *
+ * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
+ */
+class Tx_Extbase_Tests_Unit_Reflection_Fixture_DummyClassWithGettersAndSetters {
+
+       protected $property;
+       protected $anotherProperty;
+       protected $property2;
+       protected $booleanProperty = TRUE;
+
+       protected $protectedProperty;
+
+       protected $unexposedProperty;
+
+       public $publicProperty;
+       public $publicProperty2 = 42;
+
+       public function setProperty($property) {
+               $this->property = $property;
+       }
+
+       public function getProperty() {
+               return $this->property;
+       }
+
+       public function setAnotherProperty($anotherProperty) {
+               $this->anotherProperty = $anotherProperty;
+       }
+
+       public function getAnotherProperty() {
+               return $this->anotherProperty;
+       }
+
+       public function getProperty2() {
+               return $this->property2;
+       }
+       public function setProperty2($property2) {
+               $this->property2 = $property2;
+       }
+
+       protected function getProtectedProperty() {
+               return '42';
+       }
+
+       protected function setProtectedProperty($value) {
+               $this->protectedProperty = $value;
+       }
+
+       public function isBooleanProperty() {
+               return 'method called ' . $this->booleanProperty;
+       }
+
+       protected function getPrivateProperty() {
+               return '21';
+       }
+
+       public function setWriteOnlyMagicProperty($value) {
+       }
+}
+
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/extbase/Tests/Unit/Reflection/ObjectAccessTest.php b/typo3/sysext/extbase/Tests/Unit/Reflection/ObjectAccessTest.php
new file mode 100644 (file)
index 0000000..ab3a40e
--- /dev/null
@@ -0,0 +1,315 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  All rights reserved
+*
+*  This class is a backport of the corresponding class of FLOW3.
+*  All credits go to the v5 team.
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+require_once('Fixture/DummyClassWithGettersAndSetters.php');
+require_once('Fixture/ArrayAccessClass.php');
+
+class Tx_Extbase_Tests_Unit_Reflection_ObjectAccessTest extends Tx_Extbase_Tests_Unit_BaseTestCase {
+
+       protected $dummyObject;
+
+       public function setUp() {
+               $this->dummyObject = new Tx_Extbase_Tests_Unit_Reflection_Fixture_DummyClassWithGettersAndSetters();
+               $this->dummyObject->setProperty('string1');
+               $this->dummyObject->setAnotherProperty(42);
+               $this->dummyObject->shouldNotBePickedUp = TRUE;
+       }
+
+       /**
+        * @test
+        */
+       public function getPropertyReturnsExpectedValueForGetterProperty() {
+               $property = Tx_Extbase_Reflection_ObjectAccess::getProperty($this->dummyObject, 'property');
+               $this->assertEquals($property, 'string1');
+       }
+
+       /**
+        * @test
+        */
+       public function getPropertyReturnsExpectedValueForPublicProperty() {
+               $property = Tx_Extbase_Reflection_ObjectAccess::getProperty($this->dummyObject, 'publicProperty2');
+               $this->assertEquals($property, 42, 'A property of a given object was not returned correctly.');
+       }
+
+       /**
+        * @test
+        * @expectedException Tx_Extbase_Reflection_Exception_PropertyNotAccessibleException
+        */
+       public function getPropertyReturnsThrowsExceptionIfPropertyDoesNotExist() {
+               Tx_Extbase_Reflection_ObjectAccess::getProperty($this->dummyObject, 'notExistingProperty');
+       }
+
+       /**
+        * @test
+        * @expectedException Tx_Extbase_Reflection_Exception_PropertyNotAccessibleException
+        */
+       public function getPropertyReturnsThrowsExceptionIfArrayKeyDoesNotExist() {
+               Tx_Extbase_Reflection_ObjectAccess::getProperty(array(), 'notExistingProperty');
+       }
+
+       /**
+        * @test
+        */
+       public function getPropertyTriesToCallABooleanGetterMethodIfItExists() {
+               $property = Tx_Extbase_Reflection_ObjectAccess::getProperty($this->dummyObject, 'booleanProperty');
+               $this->assertSame('method called 1', $property);
+       }
+
+       /**
+        * @test
+        * @expectedException InvalidArgumentException
+        */
+       public function getPropertyThrowsExceptionIfThePropertyNameIsNotAString() {
+               Tx_Extbase_Reflection_ObjectAccess::getProperty($this->dummyObject, new ArrayObject());
+       }
+
+       /**
+        * @test
+        * @expectedException InvalidArgumentException
+        */
+       public function setPropertyThrowsExceptionIfThePropertyNameIsNotAString() {
+               Tx_Extbase_Reflection_ObjectAccess::setProperty($this->dummyObject, new ArrayObject(), 42);
+       }
+
+       /**
+        * @test
+        */
+       public function setPropertyReturnsFalseIfPropertyIsNotAccessible() {
+               $this->assertFalse(Tx_Extbase_Reflection_ObjectAccess::setProperty($this->dummyObject, 'protectedProperty', 42));
+       }
+
+       /**
+        * @test
+        */
+       public function setPropertyCallsASetterMethodToSetThePropertyValueIfOneIsAvailable() {
+               Tx_Extbase_Reflection_ObjectAccess::setProperty($this->dummyObject, 'property', 4242);
+               $this->assertEquals($this->dummyObject->getProperty(), 4242, 'setProperty does not work with setter.');
+       }
+
+       /**
+        * @test
+        */
+       public function setPropertyWorksWithPublicProperty() {
+               Tx_Extbase_Reflection_ObjectAccess::setProperty($this->dummyObject, 'publicProperty', 4242);
+               $this->assertEquals($this->dummyObject->publicProperty, 4242, 'setProperty does not work with public property.');
+       }
+
+       /**
+        * @test
+        */
+       public function setPropertyCanDirectlySetValuesInAnArrayObjectOrArray() {
+               $arrayObject = new ArrayObject();
+               $array = array();
+
+               Tx_Extbase_Reflection_ObjectAccess::setProperty($arrayObject, 'publicProperty', 4242);
+               Tx_Extbase_Reflection_ObjectAccess::setProperty($array, 'key', 'value');
+
+               $this->assertEquals(4242, $arrayObject['publicProperty']);
+               $this->assertEquals('value', $array['key']);
+       }
+
+       /**
+        * @test
+        */
+       public function getPropertyCanAccessPropertiesOfAnArrayObject() {
+               $arrayObject = new ArrayObject(array('key' => 'value'));
+               $expected = Tx_Extbase_Reflection_ObjectAccess::getProperty($arrayObject, 'key');
+               $this->assertEquals($expected, 'value', 'getProperty does not work with ArrayObject property.');
+       }
+
+       /**
+        * @test
+        */
+       public function getPropertyCanAccessPropertiesOfAnObjectImplementingArrayAccess() {
+               $arrayAccessInstance = new Tx_Extbase_Tests_Unit_Reflection_Fixture_ArrayAccessClass(array('key' => 'value'));
+               $expected = Tx_Extbase_Reflection_ObjectAccess::getProperty($arrayAccessInstance, 'key');
+               $this->assertEquals($expected, 'value', 'getPropertyPath does not work with Array Access property.');
+       }
+
+       /**
+        * @test
+        */
+       public function getPropertyCanAccessPropertiesOfAnArray() {
+               $array = array('key' => 'value');
+               $expected = Tx_Extbase_Reflection_ObjectAccess::getProperty($array, 'key');
+               $this->assertEquals($expected, 'value', 'getProperty does not work with Array property.');
+       }
+
+       /**
+        * @test
+        */
+       public function getPropertyPathCanAccessPropertiesOfAnArray() {
+               $array = array('parent' => array('key' => 'value'));
+               $expected = Tx_Extbase_Reflection_ObjectAccess::getPropertyPath($array, 'parent.key');
+               $this->assertEquals($expected, 'value', 'getPropertyPath does not work with Array property.');
+       }
+
+       /**
+        * @test
+        */
+       public function getPropertyPathCanAccessPropertiesOfAnObjectImplementingArrayAccess() {
+               $array = array('parent' => new ArrayObject(array('key' => 'value')));
+               $expected = Tx_Extbase_Reflection_ObjectAccess::getPropertyPath($array, 'parent.key');
+               $this->assertEquals($expected, 'value', 'getPropertyPath does not work with Array Access property.');
+       }
+
+       /**
+        * @test
+        */
+       public function getGettablePropertyNamesReturnsAllPropertiesWhichAreAvailable() {
+               $gettablePropertyNames = Tx_Extbase_Reflection_ObjectAccess::getGettablePropertyNames($this->dummyObject);
+               $expectedPropertyNames = array('anotherProperty', 'booleanProperty', 'property', 'property2', 'publicProperty', 'publicProperty2');
+               $this->assertEquals($gettablePropertyNames, $expectedPropertyNames, 'getGettablePropertyNames returns not all gettable properties.');
+       }
+
+       /**
+        * @test
+        */
+       public function getSettablePropertyNamesReturnsAllPropertiesWhichAreAvailable() {
+               $settablePropertyNames = Tx_Extbase_Reflection_ObjectAccess::getSettablePropertyNames($this->dummyObject);
+               $expectedPropertyNames = array('anotherProperty', 'property', 'property2', 'publicProperty', 'publicProperty2', 'writeOnlyMagicProperty');
+               $this->assertEquals($settablePropertyNames, $expectedPropertyNames, 'getSettablePropertyNames returns not all settable properties.');
+       }
+
+       /**
+        * @test
+        */
+       public function getSettablePropertyNamesReturnsPropertyNamesOfStdClass() {
+               $stdClassObject = new stdClass();
+               $stdClassObject->property = 'string1';
+               $stdClassObject->property2 = NULL;
+
+               $settablePropertyNames = Tx_Extbase_Reflection_ObjectAccess::getSettablePropertyNames($stdClassObject);
+               $expectedPropertyNames = array('property', 'property2');
+               $this->assertEquals($expectedPropertyNames, $settablePropertyNames, 'getSettablePropertyNames returns not all settable properties.');
+       }
+
+       /**
+        * @test
+        */
+       public function getGettablePropertiesReturnsTheCorrectValuesForAllProperties() {
+               $allProperties = Tx_Extbase_Reflection_ObjectAccess::getGettableProperties($this->dummyObject);
+               $expectedProperties = array(
+                       'anotherProperty' => 42,
+                       'booleanProperty' => TRUE,
+                       'property' => 'string1',
+                       'property2' => NULL,
+                       'publicProperty' => NULL,
+                       'publicProperty2' => 42);
+               $this->assertEquals($allProperties, $expectedProperties, 'expectedProperties did not return the right values for the properties.');
+       }
+
+       /**
+        * @test
+        */
+       public function getGettablePropertiesReturnsPropertiesOfStdClass() {
+               $stdClassObject = new stdClass();
+               $stdClassObject->property = 'string1';
+               $stdClassObject->property2 = NULL;
+               $stdClassObject->publicProperty2 = 42;
+               $allProperties = Tx_Extbase_Reflection_ObjectAccess::getGettableProperties($stdClassObject);
+               $expectedProperties = array(
+                       'property' => 'string1',
+                       'property2' => NULL,
+                       'publicProperty2' => 42);
+               $this->assertEquals($expectedProperties, $allProperties, 'expectedProperties did not return the right values for the properties.');
+       }
+
+       /**
+        * @test
+        */
+       public function isPropertySettableTellsIfAPropertyCanBeSet() {
+               $this->assertTrue(Tx_Extbase_Reflection_ObjectAccess::isPropertySettable($this->dummyObject, 'writeOnlyMagicProperty'));
+               $this->assertTrue(Tx_Extbase_Reflection_ObjectAccess::isPropertySettable($this->dummyObject, 'publicProperty'));
+               $this->assertTrue(Tx_Extbase_Reflection_ObjectAccess::isPropertySettable($this->dummyObject, 'property'));
+
+               $this->assertFalse(Tx_Extbase_Reflection_ObjectAccess::isPropertySettable($this->dummyObject, 'privateProperty'));
+               $this->assertFalse(Tx_Extbase_Reflection_ObjectAccess::isPropertySettable($this->dummyObject, 'shouldNotBePickedUp'));
+       }
+
+       /**
+        * @test
+        */
+       public function isPropertySettableWorksOnStdClass() {
+               $stdClassObject = new stdClass();
+               $stdClassObject->property = 'foo';
+
+               $this->assertTrue(Tx_Extbase_Reflection_ObjectAccess::isPropertySettable($stdClassObject, 'property'));
+
+               $this->assertFalse(Tx_Extbase_Reflection_ObjectAccess::isPropertySettable($stdClassObject, 'undefinedProperty'));
+       }
+
+       /**
+        * @test
+        */
+       public function isPropertyGettableTellsIfAPropertyCanBeRetrieved() {
+               $this->assertTrue(Tx_Extbase_Reflection_ObjectAccess::isPropertyGettable($this->dummyObject, 'publicProperty'));
+               $this->assertTrue(Tx_Extbase_Reflection_ObjectAccess::isPropertyGettable($this->dummyObject, 'property'));
+               $this->assertTrue(Tx_Extbase_Reflection_ObjectAccess::isPropertyGettable($this->dummyObject, 'booleanProperty'));
+
+               $this->assertFalse(Tx_Extbase_Reflection_ObjectAccess::isPropertyGettable($this->dummyObject, 'privateProperty'));
+               $this->assertFalse(Tx_Extbase_Reflection_ObjectAccess::isPropertyGettable($this->dummyObject, 'writeOnlyMagicProperty'));
+               $this->assertFalse(Tx_Extbase_Reflection_ObjectAccess::isPropertyGettable($this->dummyObject, 'shouldNotBePickedUp'));
+       }
+
+       /**
+        * @test
+        */
+       public function isPropertyGettableWorksOnStdClass() {
+               $stdClassObject = new stdClass();
+               $stdClassObject->property = 'foo';
+
+               $this->assertTrue(Tx_Extbase_Reflection_ObjectAccess::isPropertyGettable($stdClassObject, 'property'));
+
+               $this->assertFalse(Tx_Extbase_Reflection_ObjectAccess::isPropertyGettable($stdClassObject, 'undefinedProperty'));
+       }
+
+       /**
+        * @test
+        */
+       public function getPropertyPathCanRecursivelyGetPropertiesOfAnObject() {
+               $alternativeObject = new Tx_Extbase_Tests_Unit_Reflection_Fixture_DummyClassWithGettersAndSetters();
+               $alternativeObject->setProperty('test');
+               $this->dummyObject->setProperty2($alternativeObject);
+
+               $expected = 'test';
+               $actual = Tx_Extbase_Reflection_ObjectAccess::getPropertyPath($this->dummyObject, 'property2.property');
+               $this->assertEquals($expected, $actual);
+       }
+
+       /**
+        * @test
+        */
+       public function getPropertyPathReturnsNullForNonExistingPropertyPath() {
+               $alternativeObject = new Tx_Extbase_Tests_Unit_Reflection_Fixture_DummyClassWithGettersAndSetters();
+               $alternativeObject->setProperty(new stdClass());
+               $this->dummyObject->setProperty2($alternativeObject);
+
+               $this->assertNull(Tx_Extbase_Reflection_ObjectAccess::getPropertyPath($this->dummyObject, 'property2.property.not.existing'));
+       }
+
+}
+?>
\ No newline at end of file