TYPO3 CMS  TYPO3_7-6
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 
54  public static function getProperty($subject, $propertyName, $forceDirectAccess = false)
55  {
56  if (!is_object($subject) && !is_array($subject)) {
57  throw new \InvalidArgumentException('$subject must be an object or array, ' . gettype($subject) . ' given.', 1237301367);
58  }
59  if (!is_string($propertyName) && (!is_array($subject) && !$subject instanceof \ArrayAccess)) {
60  throw new \InvalidArgumentException('Given property name is not of type string.', 1231178303);
61  }
62  $propertyExists = false;
63  $propertyValue = self::getPropertyInternal($subject, $propertyName, $forceDirectAccess, $propertyExists);
64  if ($propertyExists === true) {
65  return $propertyValue;
66  }
67  throw new Exception\PropertyNotAccessibleException('The property "' . $propertyName . '" on the subject was not accessible.', 1263391473);
68  }
69 
86  public static function getPropertyInternal($subject, $propertyName, $forceDirectAccess, &$propertyExists)
87  {
88  if ($subject === null || is_scalar($subject)) {
89  return null;
90  }
91  $propertyExists = true;
92  if (is_array($subject)) {
93  if (array_key_exists($propertyName, $subject)) {
94  return $subject[$propertyName];
95  }
96  $propertyExists = false;
97  return null;
98  }
99  if ($forceDirectAccess === true) {
100  if (property_exists(get_class($subject), $propertyName)) {
101  $propertyReflection = new PropertyReflection(get_class($subject), $propertyName);
102  return $propertyReflection->getValue($subject);
103  } elseif (property_exists($subject, $propertyName)) {
104  return $subject->{$propertyName};
105  } else {
106  throw new Exception\PropertyNotAccessibleException('The property "' . $propertyName . '" on the subject does not exist.', 1302855001);
107  }
108  }
109  if ($subject instanceof \SplObjectStorage || $subject instanceof \TYPO3\CMS\Extbase\Persistence\ObjectStorage) {
110  if (MathUtility::canBeInterpretedAsInteger($propertyName)) {
111  $index = 0;
112  foreach ($subject as $value) {
113  if ($index === (int)$propertyName) {
114  return $value;
115  }
116  $index++;
117  }
118  $propertyExists = false;
119  return null;
120  }
121  } elseif ($subject instanceof \ArrayAccess && isset($subject[$propertyName])) {
122  return $subject[$propertyName];
123  }
124  $getterMethodName = 'get' . ucfirst($propertyName);
125  if (is_callable([$subject, $getterMethodName])) {
126  return $subject->{$getterMethodName}();
127  }
128  $getterMethodName = 'is' . ucfirst($propertyName);
129  if (is_callable([$subject, $getterMethodName])) {
130  return $subject->{$getterMethodName}();
131  }
132  $getterMethodName = 'has' . ucfirst($propertyName);
133  if (is_callable([$subject, $getterMethodName])) {
134  return $subject->{$getterMethodName}();
135  }
136  if (is_object($subject) && array_key_exists($propertyName, get_object_vars($subject))) {
137  return $subject->{$propertyName};
138  }
139  $propertyExists = false;
140  return null;
141  }
142 
156  public static function getPropertyPath($subject, $propertyPath)
157  {
158  $propertyPathSegments = explode('.', $propertyPath);
159  foreach ($propertyPathSegments as $pathSegment) {
160  $propertyExists = false;
161  $subject = self::getPropertyInternal($subject, $pathSegment, false, $propertyExists);
162  if (!$propertyExists || $subject === null) {
163  return $subject;
164  }
165  }
166  return $subject;
167  }
168 
188  public static function setProperty(&$subject, $propertyName, $propertyValue, $forceDirectAccess = false)
189  {
190  if (is_array($subject)) {
191  $subject[$propertyName] = $propertyValue;
192  return true;
193  }
194  if (!is_object($subject)) {
195  throw new \InvalidArgumentException('subject must be an object or array, ' . gettype($subject) . ' given.', 1237301368);
196  }
197  if (!is_string($propertyName)) {
198  throw new \InvalidArgumentException('Given property name is not of type string.', 1231178878);
199  }
200  if ($forceDirectAccess === true) {
201  if (property_exists(get_class($subject), $propertyName)) {
202  $propertyReflection = new PropertyReflection(get_class($subject), $propertyName);
203  $propertyReflection->setAccessible(true);
204  $propertyReflection->setValue($subject, $propertyValue);
205  } else {
206  $subject->{$propertyName} = $propertyValue;
207  }
208  } elseif (is_callable([$subject, $setterMethodName = self::buildSetterMethodName($propertyName)])) {
209  $subject->{$setterMethodName}($propertyValue);
210  } elseif ($subject instanceof \ArrayAccess) {
211  $subject[$propertyName] = $propertyValue;
212  } elseif (array_key_exists($propertyName, get_object_vars($subject))) {
213  $subject->{$propertyName} = $propertyValue;
214  } else {
215  return false;
216  }
217  return true;
218  }
219 
232  public static function getGettablePropertyNames($object)
233  {
234  if (!is_object($object)) {
235  throw new \InvalidArgumentException('$object must be an object, ' . gettype($object) . ' given.', 1237301369);
236  }
237  if ($object instanceof \stdClass) {
238  $properties = array_keys((array)$object);
239  sort($properties);
240  return $properties;
241  }
242 
243  $reflection = new \ReflectionClass($object);
244  $declaredPropertyNames = array_map(
245  function (\ReflectionProperty $property) {
246  return $property->getName();
247  },
248  $reflection->getProperties(\ReflectionProperty::IS_PUBLIC)
249  );
250  foreach ($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
251  $methodParameters = $method->getParameters();
252  if (!empty($methodParameters)) {
253  foreach ($methodParameters as $parameter) {
254  if (!$parameter->isOptional()) {
255  continue 2;
256  }
257  }
258  }
259  $methodName = $method->getName();
260  if (substr($methodName, 0, 2) === 'is') {
261  $declaredPropertyNames[] = lcfirst(substr($methodName, 2));
262  }
263  if (substr($methodName, 0, 3) === 'get') {
264  $declaredPropertyNames[] = lcfirst(substr($methodName, 3));
265  }
266  if (substr($methodName, 0, 3) === 'has') {
267  $declaredPropertyNames[] = lcfirst(substr($methodName, 3));
268  }
269  }
270  $propertyNames = array_unique($declaredPropertyNames);
271  sort($propertyNames);
272 
273  return $propertyNames;
274  }
275 
288  public static function getSettablePropertyNames($object)
289  {
290  if (!is_object($object)) {
291  throw new \InvalidArgumentException('$object must be an object, ' . gettype($object) . ' given.', 1264022994);
292  }
293  if ($object instanceof \stdClass) {
294  $declaredPropertyNames = array_keys(get_object_vars($object));
295  } else {
296  $declaredPropertyNames = array_keys(get_class_vars(get_class($object)));
297  }
298  foreach (get_class_methods($object) as $methodName) {
299  if (substr($methodName, 0, 3) === 'set' && is_callable([$object, $methodName])) {
300  $declaredPropertyNames[] = lcfirst(substr($methodName, 3));
301  }
302  }
303  $propertyNames = array_unique($declaredPropertyNames);
304  sort($propertyNames);
305  return $propertyNames;
306  }
307 
317  public static function isPropertySettable($object, $propertyName)
318  {
319  if (!is_object($object)) {
320  throw new \InvalidArgumentException('$object must be an object, ' . gettype($object) . ' given.', 1259828920);
321  }
322  if ($object instanceof \stdClass && array_search($propertyName, array_keys(get_object_vars($object))) !== false) {
323  return true;
324  } elseif (array_search($propertyName, array_keys(get_class_vars(get_class($object)))) !== false) {
325  return true;
326  }
327  return is_callable([$object, self::buildSetterMethodName($propertyName)]);
328  }
329 
339  public static function isPropertyGettable($object, $propertyName)
340  {
341  if (!is_object($object)) {
342  throw new \InvalidArgumentException('$object must be an object, ' . gettype($object) . ' given.', 1259828921);
343  }
344  if ($object instanceof \ArrayAccess && isset($object[$propertyName]) === true) {
345  return true;
346  } elseif ($object instanceof \stdClass && array_search($propertyName, array_keys(get_object_vars($object))) !== false) {
347  return true;
348  } elseif ($object instanceof \ArrayAccess && isset($object[$propertyName]) === true) {
349  return true;
350  }
351  if (is_callable([$object, 'get' . ucfirst($propertyName)])) {
352  return true;
353  }
354  if (is_callable([$object, 'has' . ucfirst($propertyName)])) {
355  return true;
356  }
357  if (is_callable([$object, 'is' . ucfirst($propertyName)])) {
358  return true;
359  }
360  return array_search($propertyName, array_keys(get_class_vars(get_class($object)))) !== false;
361  }
362 
373  public static function getGettableProperties($object)
374  {
375  if (!is_object($object)) {
376  throw new \InvalidArgumentException('$object must be an object, ' . gettype($object) . ' given.', 1237301370);
377  }
378  $properties = [];
379  foreach (self::getGettablePropertyNames($object) as $propertyName) {
380  $propertyExists = false;
381  $propertyValue = self::getPropertyInternal($object, $propertyName, false, $propertyExists);
382  if ($propertyExists === true) {
383  $properties[$propertyName] = $propertyValue;
384  }
385  }
386  return $properties;
387  }
388 
397  public static function buildSetterMethodName($propertyName)
398  {
399  return 'set' . ucfirst($propertyName);
400  }
401 }
static getPropertyInternal($subject, $propertyName, $forceDirectAccess, &$propertyExists)
static isPropertyGettable($object, $propertyName)
static isPropertySettable($object, $propertyName)
static getProperty($subject, $propertyName, $forceDirectAccess=false)
static getPropertyPath($subject, $propertyPath)
static setProperty(&$subject, $propertyName, $propertyValue, $forceDirectAccess=false)