[ Index ]

PHP Cross Reference of phpBB-3.3.14-deutsch

title

Body

[close]

/vendor/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/Util/ -> PublicScopeSimulator.php (source)

   1  <?php
   2  
   3  declare(strict_types=1);
   4  
   5  namespace ProxyManager\ProxyGenerator\Util;
   6  
   7  use Zend\Code\Generator\PropertyGenerator;
   8  
   9  /**
  10   * Generates code necessary to simulate a fatal error in case of unauthorized
  11   * access to class members in magic methods even when in child classes and dealing
  12   * with protected members.
  13   *
  14   * @author Marco Pivetta <ocramius@gmail.com>
  15   * @license MIT
  16   */
  17  class PublicScopeSimulator
  18  {
  19      const OPERATION_SET   = 'set';
  20      const OPERATION_GET   = 'get';
  21      const OPERATION_ISSET = 'isset';
  22      const OPERATION_UNSET = 'unset';
  23  
  24      /**
  25       * Generates code for simulating access to a property from the scope that is accessing a proxy.
  26       * This is done by introspecting `debug_backtrace()` and then binding a closure to the scope
  27       * of the parent caller.
  28       *
  29       * @param string            $operationType      operation to execute: one of 'get', 'set', 'isset' or 'unset'
  30       * @param string            $nameParameter      name of the `name` parameter of the magic method
  31       * @param string|null       $valueParameter     name of the `value` parameter of the magic method
  32       * @param PropertyGenerator $valueHolder        name of the property containing the target object from which
  33       *                                              to read the property. `$this` if none provided
  34       * @param string|null       $returnPropertyName name of the property to which we want to assign the result of
  35       *                                              the operation. Return directly if none provided
  36       *
  37       * @throws \InvalidArgumentException
  38       */
  39      public static function getPublicAccessSimulationCode(
  40          string $operationType,
  41          string $nameParameter,
  42          $valueParameter = null,
  43          PropertyGenerator $valueHolder = null,
  44          $returnPropertyName = null
  45      ) : string {
  46          $byRef  = self::getByRefReturnValue($operationType);
  47          $value  = static::OPERATION_SET === $operationType ? ', $value' : '';
  48          $target = '$this';
  49  
  50          if ($valueHolder) {
  51              $target = '$this->' . $valueHolder->getName();
  52          }
  53  
  54          return '$realInstanceReflection = new \\ReflectionClass(get_parent_class($this));' . "\n\n"
  55              . 'if (! $realInstanceReflection->hasProperty($' . $nameParameter . ')) {'   . "\n"
  56              . '    $targetObject = ' . $target . ';' . "\n\n"
  57              . self::getUndefinedPropertyNotice($operationType, $nameParameter)
  58              . '    ' . self::getOperation($operationType, $nameParameter, $valueParameter) . "\n"
  59              . "    return;\n"
  60              . '}' . "\n\n"
  61              . '$targetObject = ' . self::getTargetObject($valueHolder) . ";\n"
  62              . '$accessor = function ' . $byRef . '() use ($targetObject, $name' . $value . ') {' . "\n"
  63              . '    ' . self::getOperation($operationType, $nameParameter, $valueParameter) . "\n"
  64              . "};\n"
  65              . self::getScopeReBind()
  66              . (
  67                  $returnPropertyName
  68                      ? '$' . $returnPropertyName . ' = ' . $byRef . '$accessor();'
  69                      : '$returnValue = ' . $byRef . '$accessor();' . "\n\n" . 'return $returnValue;'
  70              );
  71      }
  72  
  73      /**
  74       * This will generate code that triggers a notice if access is attempted on a non-existing property
  75       *
  76       * @param string $operationType
  77       * @param string $nameParameter
  78       *
  79       * @return string
  80       */
  81      private static function getUndefinedPropertyNotice(string $operationType, string $nameParameter) : string
  82      {
  83          if (static::OPERATION_GET !== $operationType) {
  84              return '';
  85          }
  86  
  87          return '    $backtrace = debug_backtrace(false);' . "\n"
  88              . '    trigger_error(' . "\n"
  89              . '        sprintf(' . "\n"
  90              . '            \'Undefined property: %s::$%s in %s on line %s\',' . "\n"
  91              . '            get_parent_class($this),' . "\n"
  92              . '            $' . $nameParameter . ',' . "\n"
  93              . '            $backtrace[0][\'file\'],' . "\n"
  94              . '            $backtrace[0][\'line\']' . "\n"
  95              . '        ),' . "\n"
  96              . '        \E_USER_NOTICE' . "\n"
  97              . '    );' . "\n";
  98      }
  99  
 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  }


Generated: Mon Nov 25 19:05:08 2024 Cross-referenced by PHPXref 0.7.1