TYPO3 CMS  TYPO3_8-7
ObjectAccess.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
18 
28 {
29  const ACCESS_GET = 0;
30 
31  const ACCESS_SET = 1;
32 
33  const ACCESS_PUBLIC = 2;
34 
53  public static function getProperty($subject, $propertyName, $forceDirectAccess = false)
54  {
55  if (!is_object($subject) && !is_array($subject)) {
56  throw new \InvalidArgumentException('$subject must be an object or array, ' . gettype($subject) . ' given.', 1237301367);
57  }
58  if (!is_string($propertyName) && (!is_array($subject) && !$subject instanceof \ArrayAccess)) {
59  throw new \InvalidArgumentException('Given property name is not of type string.', 1231178303);
60  }
61  return self::getPropertyInternal($subject, $propertyName, $forceDirectAccess);
62  }
63 
79  public static function getPropertyInternal($subject, $propertyName, $forceDirectAccess = false)
80  {
81  // type check and conversion of iterator to numerically indexed array
82  if ($subject === null || is_scalar($subject)) {
83  return null;
84  }
85  if (!$forceDirectAccess && ($subject instanceof \SplObjectStorage || $subject instanceof ObjectStorage)) {
86  $subject = iterator_to_array(clone $subject, false);
87  }
88 
89  // value get based on data type of $subject (possibly converted above)
90  if (($subject instanceof \ArrayAccess && $subject->offsetExists($propertyName)) || is_array($subject)) {
91  // isset() is safe; array_key_exists would only be needed to determine
92  // if the value is NULL - and that's already what we return as fallback.
93  if (isset($subject[$propertyName])) {
94  return $subject[$propertyName];
95  }
96  } elseif (is_object($subject)) {
97  if ($forceDirectAccess) {
98  if (property_exists($subject, $propertyName)) {
99  $propertyReflection = new PropertyReflection($subject, $propertyName);
100  return $propertyReflection->getValue($subject);
101  }
102  throw new Exception\PropertyNotAccessibleException('The property "' . $propertyName . '" on the subject does not exist.', 1302855001);
103  }
104  $upperCasePropertyName = ucfirst($propertyName);
105  $getterMethodName = 'get' . $upperCasePropertyName;
106  if (is_callable([$subject, $getterMethodName])) {
107  return $subject->{$getterMethodName}();
108  }
109  $getterMethodName = 'is' . $upperCasePropertyName;
110  if (is_callable([$subject, $getterMethodName])) {
111  return $subject->{$getterMethodName}();
112  }
113  $getterMethodName = 'has' . $upperCasePropertyName;
114  if (is_callable([$subject, $getterMethodName])) {
115  return $subject->{$getterMethodName}();
116  }
117  if (property_exists($subject, $propertyName)) {
118  return $subject->{$propertyName};
119  }
120  throw new Exception\PropertyNotAccessibleException('The property "' . $propertyName . '" on the subject does not exist.', 1476109666);
121  }
122 
123  return null;
124  }
125 
139  public static function getPropertyPath($subject, $propertyPath)
140  {
141  $propertyPathSegments = explode('.', $propertyPath);
142  try {
143  foreach ($propertyPathSegments as $pathSegment) {
144  $subject = self::getPropertyInternal($subject, $pathSegment);
145  }
146  } catch (Exception\PropertyNotAccessibleException $error) {
147  return null;
148  }
149  return $subject;
150  }
151 
171  public static function setProperty(&$subject, $propertyName, $propertyValue, $forceDirectAccess = false)
172  {
173  if (is_array($subject) || ($subject instanceof \ArrayAccess && !$forceDirectAccess)) {
174  $subject[$propertyName] = $propertyValue;
175  return true;
176  }
177  if (!is_object($subject)) {
178  throw new \InvalidArgumentException('subject must be an object or array, ' . gettype($subject) . ' given.', 1237301368);
179  }
180  if (!is_string($propertyName)) {
181  throw new \InvalidArgumentException('Given property name is not of type string.', 1231178878);
182  }
183  $result = true;
184  if ($forceDirectAccess) {
185  if (property_exists($subject, $propertyName)) {
186  $propertyReflection = new PropertyReflection($subject, $propertyName);
187  $propertyReflection->setAccessible(true);
188  $propertyReflection->setValue($subject, $propertyValue);
189  } else {
190  $subject->{$propertyName} = $propertyValue;
191  }
192  return $result;
193  }
194  $setterMethodName = self::buildSetterMethodName($propertyName);
195  if (is_callable([$subject, $setterMethodName])) {
196  $subject->{$setterMethodName}($propertyValue);
197  } elseif (property_exists($subject, $propertyName)) {
198  $reflection = new PropertyReflection($subject, $propertyName);
199  if ($reflection->isPublic()) {
200  $subject->{$propertyName} = $propertyValue;
201  } else {
202  $result = false;
203  }
204  } else {
205  $result = false;
206  }
207  return $result;
208  }
209 
222  public static function getGettablePropertyNames($object)
223  {
224  if (!is_object($object)) {
225  throw new \InvalidArgumentException('$object must be an object, ' . gettype($object) . ' given.', 1237301369);
226  }
227  if ($object instanceof \stdClass) {
228  $properties = array_keys((array)$object);
229  sort($properties);
230  return $properties;
231  }
232 
233  $reflection = new \ReflectionClass($object);
234  $declaredPropertyNames = array_map(
235  function (\ReflectionProperty $property) {
236  return $property->getName();
237  },
238  $reflection->getProperties(\ReflectionProperty::IS_PUBLIC)
239  );
240  foreach ($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
241  $methodParameters = $method->getParameters();
242  if (!empty($methodParameters)) {
243  foreach ($methodParameters as $parameter) {
244  if (!$parameter->isOptional()) {
245  continue 2;
246  }
247  }
248  }
249  $methodName = $method->getName();
250  if (substr($methodName, 0, 2) === 'is') {
251  $declaredPropertyNames[] = lcfirst(substr($methodName, 2));
252  }
253  if (substr($methodName, 0, 3) === 'get') {
254  $declaredPropertyNames[] = lcfirst(substr($methodName, 3));
255  }
256  if (substr($methodName, 0, 3) === 'has') {
257  $declaredPropertyNames[] = lcfirst(substr($methodName, 3));
258  }
259  }
260  $propertyNames = array_unique($declaredPropertyNames);
261  sort($propertyNames);
262 
263  return $propertyNames;
264  }
265 
278  public static function getSettablePropertyNames($object)
279  {
280  if (!is_object($object)) {
281  throw new \InvalidArgumentException('$object must be an object, ' . gettype($object) . ' given.', 1264022994);
282  }
283  if ($object instanceof \stdClass) {
284  $declaredPropertyNames = array_keys((array)$object);
285  } else {
286  $declaredPropertyNames = array_keys(get_class_vars(get_class($object)));
287  }
288  foreach (get_class_methods($object) as $methodName) {
289  if (substr($methodName, 0, 3) === 'set' && is_callable([$object, $methodName])) {
290  $declaredPropertyNames[] = lcfirst(substr($methodName, 3));
291  }
292  }
293  $propertyNames = array_unique($declaredPropertyNames);
294  sort($propertyNames);
295  return $propertyNames;
296  }
297 
307  public static function isPropertySettable($object, $propertyName)
308  {
309  if (!is_object($object)) {
310  throw new \InvalidArgumentException('$object must be an object, ' . gettype($object) . ' given.', 1259828920);
311  }
312  if ($object instanceof \stdClass && array_search($propertyName, array_keys(get_object_vars($object))) !== false) {
313  return true;
314  }
315  if (array_search($propertyName, array_keys(get_class_vars(get_class($object)))) !== false) {
316  return true;
317  }
318  return is_callable([$object, self::buildSetterMethodName($propertyName)]);
319  }
320 
330  public static function isPropertyGettable($object, $propertyName)
331  {
332  if (!is_object($object)) {
333  throw new \InvalidArgumentException('$object must be an object, ' . gettype($object) . ' given.', 1259828921);
334  }
335  if ($object instanceof \ArrayAccess && isset($object[$propertyName])) {
336  return true;
337  }
338  if ($object instanceof \stdClass && isset($object->$propertyName)) {
339  return true;
340  }
341  if (is_callable([$object, 'get' . ucfirst($propertyName)])) {
342  return true;
343  }
344  if (is_callable([$object, 'has' . ucfirst($propertyName)])) {
345  return true;
346  }
347  if (is_callable([$object, 'is' . ucfirst($propertyName)])) {
348  return true;
349  }
350  if (property_exists($object, $propertyName)) {
351  $propertyReflection = new PropertyReflection($object, $propertyName);
352  return $propertyReflection->isPublic();
353  }
354  return false;
355  }
356 
367  public static function getGettableProperties($object)
368  {
369  if (!is_object($object)) {
370  throw new \InvalidArgumentException('$object must be an object, ' . gettype($object) . ' given.', 1237301370);
371  }
372  $properties = [];
373  foreach (self::getGettablePropertyNames($object) as $propertyName) {
374  $properties[$propertyName] = self::getPropertyInternal($object, $propertyName);
375  }
376  return $properties;
377  }
378 
387  public static function buildSetterMethodName($propertyName)
388  {
389  return 'set' . ucfirst($propertyName);
390  }
391 }
static isPropertyGettable($object, $propertyName)
static isPropertySettable($object, $propertyName)
static getProperty($subject, $propertyName, $forceDirectAccess=false)
static getPropertyInternal($subject, $propertyName, $forceDirectAccess=false)
static getPropertyPath($subject, $propertyPath)
static setProperty(&$subject, $propertyName, $propertyValue, $forceDirectAccess=false)