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.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

PublicScopeSimulator.php

Zuletzt modifiziert: 02.04.2025, 15:04 - Dateigröße: 6.57 KiB


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