3v4l.org

run code in 300+ PHP versions simultaneously
<?php class TestingAccessWrapper { /** @var mixed The object, or the class name for static-only access */ public $object; /** * Return a proxy object which can be used the same way as the original, * except that access restrictions can be ignored (protected and private methods and properties * are available for any caller). * @param object $object * @return TestingAccessWrapper * @throws InvalidArgumentException */ public static function newFromObject( $object ) { if ( !is_object( $object ) ) { throw new InvalidArgumentException( __METHOD__ . ' must be called with an object' ); } $wrapper = new TestingAccessWrapper(); $wrapper->object = $object; return $wrapper; } /** * Allow access to non-public static methods and properties of the class. * Returns an object whose methods/properties will correspond to the * static methods/properties of the given class. * @param string $className * @return TestingAccessWrapper * @throws InvalidArgumentException */ public static function newFromClass( $className ) { if ( !is_string( $className ) ) { throw new InvalidArgumentException( __METHOD__ . ' must be called with a class name' ); } $wrapper = new TestingAccessWrapper(); $wrapper->object = $className; return $wrapper; } public function __call( $method, $args ) { $methodReflection = $this->getMethod( $method ); if ( $this->isStatic() && !$methodReflection->isStatic() ) { throw new DomainException( __METHOD__ . ': Cannot call non-static method when wrapping static class' ); } return $methodReflection->invokeArgs( $methodReflection->isStatic() ? null : $this->object, $args ); } public function __set( $name, $value ) { $propertyReflection = $this->getProperty( $name ); if ( $this->isStatic() && !$propertyReflection->isStatic() ) { throw new DomainException( __METHOD__ . ': Cannot set non-static property when wrapping static class' ); } $propertyReflection->setValue( $this->object, $value ); } public function __get( $name ) { $propertyReflection = $this->getProperty( $name ); if ( $this->isStatic() && !$propertyReflection->isStatic() ) { throw new DomainException( __METHOD__ . ': Cannot get non-static property when wrapping static class' ); } return $propertyReflection->getValue( $this->object ); } /** * Tells whether this object was created for an object or a class. * @return bool */ private function isStatic() { return is_string( $this->object ); } /** * Return a method and make it accessible. * @param string $name * @return ReflectionMethod */ private function getMethod( $name ) { $classReflection = new ReflectionClass( $this->object ); $methodReflection = $classReflection->getMethod( $name ); $methodReflection->setAccessible( true ); return $methodReflection; } /** * Return a property and make it accessible. * * ReflectionClass::getProperty() fails if the private property is defined * in a parent class. This works more like ReflectionClass::getMethod(). * * @param string $name * @return ReflectionProperty * @throws ReflectionException */ private function getProperty( $name ) { $classReflection = new ReflectionClass( $this->object ); try { $propertyReflection = $classReflection->getProperty( $name ); } catch ( ReflectionException $ex ) { while ( true ) { $classReflection = $classReflection->getParentClass(); if ( !$classReflection ) { throw $ex; } try { $propertyReflection = $classReflection->getProperty( $name ); } catch ( ReflectionException $ex2 ) { continue; } if ( $propertyReflection->isPrivate() ) { break; } else { // @codeCoverageIgnoreStart throw $ex; // @codeCoverageIgnoreEnd } } } $propertyReflection->setAccessible( true ); return $propertyReflection; } } class A { private static function func( &$ref ) { $ref = 42; } } $a = TestingAccessWrapper::newFromClass( A::class ); $b = 0; $a->func( &$b ); echo $b;
Output for 8.0.0 - 8.0.8
Parse error: syntax error, unexpected token "&" in /in/mqEne on line 145
Process exited with code 255.
Output for 7.0.0 - 7.0.33, 7.1.0 - 7.1.33, 7.2.0 - 7.2.34, 7.3.0 - 7.3.29, 7.4.0 - 7.4.21
Parse error: syntax error, unexpected '&' in /in/mqEne on line 145
Process exited with code 255.
Output for 5.5.0 - 5.5.38, 5.6.0 - 5.6.40
Fatal error: Call-time pass-by-reference has been removed in /in/mqEne on line 145
Process exited with code 255.
Output for 5.4.0 - 5.4.45
Parse error: syntax error, unexpected 'class' (T_CLASS), expecting identifier (T_STRING) or variable (T_VARIABLE) or '{' or '$' in /in/mqEne on line 142
Process exited with code 255.
Output for 5.1.0 - 5.1.6, 5.2.0 - 5.2.17, 5.3.0 - 5.3.29
Parse error: syntax error, unexpected T_CLASS, expecting T_STRING or T_VARIABLE or '$' in /in/mqEne on line 142
Process exited with code 255.
Output for 5.0.0 - 5.0.5
Parse error: parse error, unexpected T_CLASS, expecting T_STRING or T_VARIABLE or '$' in /in/mqEne on line 142
Process exited with code 255.
Output for 4.4.2 - 4.4.9
Parse error: syntax error, unexpected T_STRING, expecting T_OLD_FUNCTION or T_FUNCTION or T_VAR or '}' in /in/mqEne on line 5
Process exited with code 255.
Output for 4.3.0 - 4.3.1, 4.3.5 - 4.3.11, 4.4.0 - 4.4.1
Parse error: parse error, unexpected T_STRING, expecting T_OLD_FUNCTION or T_FUNCTION or T_VAR or '}' in /in/mqEne on line 5
Process exited with code 255.
Output for 4.3.2 - 4.3.4
Parse error: parse error, expecting `T_OLD_FUNCTION' or `T_FUNCTION' or `T_VAR' or `'}'' in /in/mqEne on line 5
Process exited with code 255.

preferences:
397.42 ms | 1395 KiB | 378 Q