* Raising Extbase and Fluid versions to 1.1.0alpha1
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Reflection / ObjectAccess.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2009 Christopher Hlubek <hlubek@networkteam.com>
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 *
17 * This script is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * This copyright notice MUST APPEAR in all copies of the script!
23 ***************************************************************/
24
25 /**
26 * Provides methods to call appropriate getter/setter on an object given the
27 * property name. It does this following these rules:
28 * - if the target object is an instance of ArrayAccess, it gets/sets the property
29 * - if public getter/setter method exists, call it.
30 * - if public property exists, return/set the value of it.
31 * - else, throw exception
32 *
33 * @package Extbase
34 * @subpackage Reflection
35 * @version $Id: ObjectAccess.php 2040 2010-03-16 08:32:32Z jocrau $
36 */
37 class Tx_Extbase_Reflection_ObjectAccess {
38
39 const ACCESS_GET = 0;
40 const ACCESS_SET = 1;
41 const ACCESS_PUBLIC = 2;
42
43 /**
44 * Get a property of a given object.
45 * Tries to get the property the following ways:
46 * - if the target is an array, and has this property, we call it.
47 * - if public getter method exists, call it.
48 * - if the target object is an instance of ArrayAccess, it gets the property
49 * on it if it exists.
50 * - if public property exists, return the value of it.
51 * - else, throw exception
52 *
53 * @param mixed $subject Object or array to get the property from
54 * @param string $propertyName name of the property to retrieve
55 * @return object Value of the property.
56 * @throws InvalidArgumentException in case $subject was not an object or $propertyName was not a string
57 * @throws RuntimeException if the property was not accessible
58 */
59 static public function getProperty($subject, $propertyName) {
60 if (!is_object($subject) && !is_array($subject)) throw new InvalidArgumentException('$subject must be an object or array, ' . gettype($subject). ' given.', 1237301367);
61 if (!is_string($propertyName)) throw new InvalidArgumentException('Given property name is not of type string.', 1231178303);
62
63 if (is_array($subject)) {
64 if (array_key_exists($propertyName, $subject)) {
65 return $subject[$propertyName];
66 }
67 } else {
68 if (is_callable(array($subject, 'get' . ucfirst($propertyName)))) {
69 return call_user_func(array($subject, 'get' . ucfirst($propertyName)));
70 } elseif (is_callable(array($subject, 'is' . ucfirst($propertyName)))) {
71 return call_user_func(array($subject, 'is' . ucfirst($propertyName)));
72 } elseif ($subject instanceof ArrayAccess && isset($subject[$propertyName])) {
73 return $subject[$propertyName];
74 } elseif (array_key_exists($propertyName, get_object_vars($subject))) {
75 return $subject->$propertyName;
76 }
77 }
78
79 throw new RuntimeException('The property "' . $propertyName . '" on the subject was not accessible.', 1263391473);
80 }
81
82 /**
83 * Gets a property path from a given object or array.
84 * If propertyPath is "bla.blubb", then we first call getProperty($object, 'bla'),
85 * and on the resulting object we call getProperty(..., 'blubb')
86 *
87 * @param mixed $subject Object or array to get the property path from
88 * @param string $propertyPath
89 * @return mixed Value of the property
90 */
91 static public function getPropertyPath($subject, $propertyPath) {
92 $propertyPathSegments = explode('.', $propertyPath);
93 foreach ($propertyPathSegments as $pathSegment) {
94 if (is_array($subject) || (is_object($subject) && self::isPropertyGettable($subject, $pathSegment))) {
95 $subject = self::getProperty($subject, $pathSegment);
96 } else {
97 return NULL;
98 }
99 }
100 return $subject;
101 }
102
103 /**
104 * Set a property for a given object.
105 * Tries to set the property the following ways:
106 * - if public setter method exists, call it.
107 * - if public property exists, set it directly.
108 * - if the target object is an instance of ArrayAccess, it sets the property
109 * on it without checking if it existed.
110 * - else, return FALSE
111 *
112 * @param object $object The target object
113 * @param string $propertyName Name of the property to set
114 * @param object $propertyValue Value of the property
115 * @return void
116 * @throws Tx_Extbase_Reflection_Exception if property was could not be set
117 */
118 static public function setProperty($object, $propertyName, $propertyValue) {
119 if (!is_object($object)) throw new InvalidArgumentException('$object must be an object, ' . gettype($object). ' given.', 1237301368);
120 if (!is_string($propertyName)) throw new InvalidArgumentException('Given property name is not of type string.', 1231178878);
121
122 if (is_callable(array($object, $setterMethodName = self::buildSetterMethodName($propertyName)))) {
123 call_user_func(array($object, $setterMethodName), $propertyValue);
124 } elseif ($object instanceof ArrayAccess) {
125 $object[$propertyName] = $propertyValue;
126 } elseif (array_key_exists($propertyName, get_object_vars($object))) {
127 $object->$propertyName = $propertyValue;
128 } else {
129 return FALSE;
130 }
131 return TRUE;
132 }
133
134 /**
135 * Returns an array of properties which can be get/set with the getProperty
136 * and setProperty methods.
137 * Includes the following properties:
138 * - which can be set through a public setter method.
139 * - public properties which can be directly set.
140 *
141 * @param object $object Object to receive property names for
142 * @return array Array of all declared property names
143 * @todo What to do with ArrayAccess
144 */
145 static public function getAccessiblePropertyNames($object) {
146 if (!is_object($object)) throw new InvalidArgumentException('$object must be an object, ' . gettype($object). ' given.', 1237301369);
147 $declaredPropertyNames = array_keys(get_class_vars(get_class($object)));
148
149 foreach (get_class_methods($object) as $methodName) {
150 if (substr($methodName, 0, 3) === 'get') {
151 $propertyName = substr($methodName, 3);
152 $propertyName[0] = strtolower($propertyName[0]);
153 $declaredPropertyNames[] = $propertyName;
154 }
155 }
156
157 $propertyNames = array_unique($declaredPropertyNames);
158 sort($propertyNames);
159 return $propertyNames;
160 }
161
162 /**
163 * Get all properties (names and their current values) of the current
164 * $object that are accessible through this class.
165 *
166 * @param object $object Object to get all properties from.
167 * @return array Associative array of all properties.
168 * @todo What to do with ArrayAccess
169 */
170 static public function getAccessibleProperties($object) {
171 if (!is_object($object)) throw new InvalidArgumentException('$object must be an object, ' . gettype($object). ' given.', 1237301370);
172 $properties = array();
173 foreach (self::getAccessiblePropertyNames($object) as $propertyName) {
174 $properties[$propertyName] = self::getProperty($object, $propertyName);
175 }
176 return $properties;
177 }
178
179 /**
180 * Tells if the value of the specified property can be retrieved by this Object Accessor.
181 *
182 * @param object $object Object containting the property
183 * @param string $propertyName Name of the property to check
184 * @return boolean
185 */
186 static public function isPropertyGettable($object, $propertyName) {
187 if (!is_object($object)) throw new InvalidArgumentException('$object must be an object, ' . gettype($object). ' given.', 1259828921);
188 if (array_search($propertyName, array_keys(get_class_vars(get_class($object)))) !== FALSE) return TRUE;
189 if (is_callable(array($object, 'get' . ucfirst($propertyName)))) return TRUE;
190 return is_callable(array($object, 'is' . ucfirst($propertyName)));
191 }
192
193 /**
194 * Build the setter method name for a given property by capitalizing the
195 * first letter of the property, and prepending it with "set".
196 *
197 * @param string $property Name of the property
198 * @return string Name of the setter method name
199 */
200 static protected function buildSetterMethodName($property) {
201 return 'set' . ucfirst($property);
202 }
203 }
204
205
206 ?>