Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
PublicScopeSimulator.php
001 <?php
002
003 declare(strict_types=1);
004
005 namespace ProxyManager\ProxyGenerator\Util;
006
007 use Zend\Code\Generator\PropertyGenerator;
008
009 /**
010 * Generates code necessary to simulate a fatal error in case of unauthorized
011 * access to class members in magic methods even when in child classes and dealing
012 * with protected members.
013 *
014 * @author Marco Pivetta <ocramius@gmail.com>
015 * @license MIT
016 */
017 class PublicScopeSimulator
018 {
019 const OPERATION_SET = 'set';
020 const OPERATION_GET = 'get';
021 const OPERATION_ISSET = 'isset';
022 const OPERATION_UNSET = 'unset';
023
024 /**
025 * Generates code for simulating access to a property from the scope that is accessing a proxy.
026 * This is done by introspecting `debug_backtrace()` and then binding a closure to the scope
027 * of the parent caller.
028 *
029 * @param string $operationType operation to execute: one of 'get', 'set', 'isset' or 'unset'
030 * @param string $nameParameter name of the `name` parameter of the magic method
031 * @param string|null $valueParameter name of the `value` parameter of the magic method
032 * @param PropertyGenerator $valueHolder name of the property containing the target object from which
033 * to read the property. `$this` if none provided
034 * @param string|null $returnPropertyName name of the property to which we want to assign the result of
035 * the operation. Return directly if none provided
036 *
037 * @throws \InvalidArgumentException
038 */
039 public static function getPublicAccessSimulationCode(
040 string $operationType,
041 string $nameParameter,
042 $valueParameter = null,
043 PropertyGenerator $valueHolder = null,
044 $returnPropertyName = null
045 ) : string {
046 $byRef = self::getByRefReturnValue($operationType);
047 $value = static::OPERATION_SET === $operationType ? ', $value' : '';
048 $target = '$this';
049
050 if ($valueHolder) {
051 $target = '$this->' . $valueHolder->getName();
052 }
053
054 return '$realInstanceReflection = new \\ReflectionClass(get_parent_class($this));' . "\n\n"
055 . 'if (! $realInstanceReflection->hasProperty($' . $nameParameter . ')) {' . "\n"
056 . ' $targetObject = ' . $target . ';' . "\n\n"
057 . self::getUndefinedPropertyNotice($operationType, $nameParameter)
058 . ' ' . self::getOperation($operationType, $nameParameter, $valueParameter) . "\n"
059 . " return;\n"
060 . '}' . "\n\n"
061 . '$targetObject = ' . self::getTargetObject($valueHolder) . ";\n"
062 . '$accessor = function ' . $byRef . '() use ($targetObject, $name' . $value . ') {' . "\n"
063 . ' ' . self::getOperation($operationType, $nameParameter, $valueParameter) . "\n"
064 . "};\n"
065 . self::getScopeReBind()
066 . (
067 $returnPropertyName
068 ? '$' . $returnPropertyName . ' = ' . $byRef . '$accessor();'
069 : '$returnValue = ' . $byRef . '$accessor();' . "\n\n" . 'return $returnValue;'
070 );
071 }
072
073 /**
074 * This will generate code that triggers a notice if access is attempted on a non-existing property
075 *
076 * @param string $operationType
077 * @param string $nameParameter
078 *
079 * @return string
080 */
081 private static function getUndefinedPropertyNotice(string $operationType, string $nameParameter) : string
082 {
083 if (static::OPERATION_GET !== $operationType) {
084 return '';
085 }
086
087 return ' $backtrace = debug_backtrace(false);' . "\n"
088 . ' trigger_error(' . "\n"
089 . ' sprintf(' . "\n"
090 . ' \'Undefined property: %s::$%s in %s on line %s\',' . "\n"
091 . ' get_parent_class($this),' . "\n"
092 . ' $' . $nameParameter . ',' . "\n"
093 . ' $backtrace[0][\'file\'],' . "\n"
094 . ' $backtrace[0][\'line\']' . "\n"
095 . ' ),' . "\n"
096 . ' \E_USER_NOTICE' . "\n"
097 . ' );' . "\n";
098 }
099
100 /**
101 * Defines whether the given operation produces a reference.
102 *
103 * Note: if the object is a wrapper, the wrapped instance is accessed directly. If the object
104 * is a ghost or the proxy has no wrapper, then an instance of the parent class is created via
105 * on-the-fly unserialization
106 */
107 private static function getByRefReturnValue(string $operationType) : string
108 {
109 return (static::OPERATION_GET === $operationType || static::OPERATION_SET === $operationType) ? '& ' : '';
110 }
111
112 /**
113 * Retrieves the logic to fetch the object on which access should be attempted
114 *
115 * @param PropertyGenerator $valueHolder
116 *
117 * @return string
118 */
119 private static function getTargetObject(PropertyGenerator $valueHolder = null) : string
120 {
121 if ($valueHolder) {
122 return '$this->' . $valueHolder->getName();
123 }
124
125 return 'unserialize(sprintf(\'O:%d:"%s":0:{}\', strlen(get_parent_class($this)), get_parent_class($this)))';
126 }
127
128 /**
129 * @throws \InvalidArgumentException
130 */
131 private static function getOperation(string $operationType, string $nameParameter, ?string $valueParameter) : string
132 {
133 switch ($operationType) {
134 case static::OPERATION_GET:
135 return 'return $targetObject->$' . $nameParameter . ';';
136 case static::OPERATION_SET:
137 if (null === $valueParameter) {
138 throw new \InvalidArgumentException('Parameter $valueParameter not provided');
139 }
140
141 return 'return $targetObject->$' . $nameParameter . ' = $' . $valueParameter . ';';
142 case static::OPERATION_ISSET:
143 return 'return isset($targetObject->$' . $nameParameter . ');';
144 case static::OPERATION_UNSET:
145 return 'unset($targetObject->$' . $nameParameter . ');';
146 }
147
148 throw new \InvalidArgumentException(sprintf('Invalid operation "%s" provided', $operationType));
149 }
150
151 /**
152 * Generates code to bind operations to the parent scope
153 *
154 * @return string
155 */
156 private static function getScopeReBind() : string
157 {
158 return '$backtrace = debug_backtrace(true);' . "\n"
159 . '$scopeObject = isset($backtrace[1][\'object\'])'
160 . ' ? $backtrace[1][\'object\'] : new \ProxyManager\Stub\EmptyClassStub();' . "\n"
161 . '$accessor = $accessor->bindTo($scopeObject, get_class($scopeObject));' . "\n";
162 }
163 }
164