[ Index ]

PHP Cross Reference of phpBB-3.2.11-deutsch

title

Body

[close]

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

   1  <?php
   2  /*
   3   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   4   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   5   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   6   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   7   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   8   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   9   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  10   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  11   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  12   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  13   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  14   *
  15   * This software consists of voluntary contributions made by many individuals
  16   * and is licensed under the MIT license.
  17   */
  18  
  19  namespace ProxyManager\ProxyGenerator\Util;
  20  
  21  use Zend\Code\Generator\PropertyGenerator;
  22  
  23  /**
  24   * Generates code necessary to simulate a fatal error in case of unauthorized
  25   * access to class members in magic methods even when in child classes and dealing
  26   * with protected members.
  27   *
  28   * @author Marco Pivetta <ocramius@gmail.com>
  29   * @license MIT
  30   */
  31  class PublicScopeSimulator
  32  {
  33      const OPERATION_SET   = 'set';
  34      const OPERATION_GET   = 'get';
  35      const OPERATION_ISSET = 'isset';
  36      const OPERATION_UNSET = 'unset';
  37  
  38      /**
  39       * Generates code for simulating access to a property from the scope that is accessing a proxy.
  40       * This is done by introspecting `debug_backtrace()` and then binding a closure to the scope
  41       * of the parent caller.
  42       *
  43       * @param string            $operationType      operation to execute: one of 'get', 'set', 'isset' or 'unset'
  44       * @param string            $nameParameter      name of the `name` parameter of the magic method
  45       * @param string|null       $valueParameter     name of the `value` parameter of the magic method
  46       * @param PropertyGenerator $valueHolder        name of the property containing the target object from which
  47       *                                              to read the property. `$this` if none provided
  48       * @param string|null       $returnPropertyName name of the property to which we want to assign the result of
  49       *                                              the operation. Return directly if none provided
  50       *
  51       * @return string
  52       *
  53       * @throws \InvalidArgumentException
  54       */
  55      public static function getPublicAccessSimulationCode(
  56          $operationType,
  57          $nameParameter,
  58          $valueParameter = null,
  59          PropertyGenerator $valueHolder = null,
  60          $returnPropertyName = null
  61      ) {
  62          $byRef  = self::getByRefReturnValue($operationType);
  63          $value  = static::OPERATION_SET === $operationType ? ', $value' : '';
  64          $target = '$this';
  65  
  66          if ($valueHolder) {
  67              $target = '$this->' . $valueHolder->getName();
  68          }
  69  
  70          return '$realInstanceReflection = new \\ReflectionClass(get_parent_class($this));' . "\n\n"
  71              . 'if (! $realInstanceReflection->hasProperty($' . $nameParameter . ')) {'   . "\n"
  72              . '    $targetObject = ' . $target . ';' . "\n\n"
  73              . self::getUndefinedPropertyNotice($operationType, $nameParameter)
  74              . '    ' . self::getOperation($operationType, $nameParameter, $valueParameter) . ";\n"
  75              . "    return;\n"
  76              . '}' . "\n\n"
  77              . '$targetObject = ' . self::getTargetObject($valueHolder) . ";\n"
  78              . '$accessor = function ' . $byRef . '() use ($targetObject, $name' . $value . ') {' . "\n"
  79              . '    ' . self::getOperation($operationType, $nameParameter, $valueParameter) . "\n"
  80              . "};\n"
  81              . self::getScopeReBind()
  82              . (
  83                  $returnPropertyName
  84                      ? '$' . $returnPropertyName . ' = ' . $byRef . '$accessor();'
  85                      : '$returnValue = ' . $byRef . '$accessor();' . "\n\n" . 'return $returnValue;'
  86              );
  87      }
  88  
  89      /**
  90       * This will generate code that triggers a notice if access is attempted on a non-existing property
  91       *
  92       * @param string $operationType
  93       * @param string $nameParameter
  94       *
  95       * @return string
  96       */
  97      private static function getUndefinedPropertyNotice($operationType, $nameParameter)
  98      {
  99          if (static::OPERATION_GET !== $operationType) {
 100              return '';
 101          }
 102  
 103          //
 104          return '    $backtrace = debug_backtrace(false);' . "\n"
 105              . '    trigger_error(\'Undefined property: \' . get_parent_class($this) . \'::$\' . $'
 106              . $nameParameter
 107              . ' . \' in \' . $backtrace[0][\'file\'] . \' on line \' . $backtrace[0][\'line\'], \E_USER_NOTICE);'
 108              . "\n";
 109      }
 110  
 111      /**
 112       * Defines whether the given operation produces a reference.
 113       *
 114       * Note: if the object is a wrapper, the wrapped instance is accessed directly. If the object
 115       * is a ghost or the proxy has no wrapper, then an instance of the parent class is created via
 116       * on-the-fly unserialization
 117       *
 118       * @param string $operationType
 119       *
 120       * @return string
 121       */
 122      private static function getByRefReturnValue($operationType)
 123      {
 124          return (static::OPERATION_GET === $operationType || static::OPERATION_SET === $operationType) ? '& ' : '';
 125      }
 126  
 127      /**
 128       * Retrieves the logic to fetch the object on which access should be attempted
 129       *
 130       * @param PropertyGenerator $valueHolder
 131       *
 132       * @return string
 133       */
 134      private static function getTargetObject(PropertyGenerator $valueHolder = null)
 135      {
 136          if ($valueHolder) {
 137              return '$this->' . $valueHolder->getName();
 138          }
 139  
 140          return 'unserialize(sprintf(\'O:%d:"%s":0:{}\', strlen(get_parent_class($this)), get_parent_class($this)))';
 141      }
 142  
 143      /**
 144       * @param string      $operationType
 145       * @param string      $nameParameter
 146       * @param string|null $valueParameter
 147       *
 148       * @return string
 149       *
 150       * @throws \InvalidArgumentException
 151       */
 152      private static function getOperation($operationType, $nameParameter, $valueParameter)
 153      {
 154          switch ($operationType) {
 155              case static::OPERATION_GET:
 156                  return 'return $targetObject->$' . $nameParameter . ";";
 157              case static::OPERATION_SET:
 158                  if (! $valueParameter) {
 159                      throw new \InvalidArgumentException('Parameter $valueParameter not provided');
 160                  }
 161  
 162                  return 'return $targetObject->$' . $nameParameter . ' = $' . $valueParameter . ';';
 163              case static::OPERATION_ISSET:
 164                  return 'return isset($targetObject->$' . $nameParameter . ');';
 165              case static::OPERATION_UNSET:
 166                  return 'unset($targetObject->$' . $nameParameter . ');';
 167          }
 168  
 169          throw new \InvalidArgumentException(sprintf('Invalid operation "%s" provided', $operationType));
 170      }
 171  
 172      /**
 173       * Generates code to bind operations to the parent scope if supported by the current PHP implementation
 174       *
 175       * @return string
 176       */
 177      private static function getScopeReBind()
 178      {
 179          if (! method_exists('Closure', 'bind')) {
 180              // @codeCoverageIgnoreStart
 181              return '';
 182              // @codeCoverageIgnoreEnd
 183          }
 184  
 185          return '    $backtrace = debug_backtrace(true);' . "\n"
 186              . '    $scopeObject = isset($backtrace[1][\'object\'])'
 187              . ' ? $backtrace[1][\'object\'] : new \stdClass();' . "\n"
 188              . '    $accessor = $accessor->bindTo($scopeObject, get_class($scopeObject));' . "\n";
 189      }
 190  }


Generated: Wed Nov 11 20:33:01 2020 Cross-referenced by PHPXref 0.7.1