* $formatter = $title->getTitleFormatter();
*
* TODO:
- * - Provide access to static methods and properties.
* - Organize other helper classes in tests/testHelpers.inc into a directory.
*/
class TestingAccessWrapper {
+ /** @var mixed The object, or the class name for static-only access */
public $object;
/**
* Return the same object, without access restrictions.
*/
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.
+ * Use non-static access,
+ */
+ 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 ) {
- $classReflection = new ReflectionClass( $this->object );
- $methodReflection = $classReflection->getMethod( $method );
- $methodReflection->setAccessible( true );
- return $methodReflection->invokeArgs( $this->object, $args );
+ $methodReflection = $this->getMethod( $method );
+
+ if ( $this->isStatic() && !$methodReflection->isStatic() ) {
+ throw new DomainException( __METHOD__ . ': Cannot call non-static when wrapping static class' );
+ }
+
+ return $methodReflection->invokeArgs( $methodReflection->isStatic() ? null : $this->object,
+ $args );
}
public function __set( $name, $value ) {
- $classReflection = new ReflectionClass( $this->object );
- $propertyReflection = $classReflection->getProperty( $name );
- $propertyReflection->setAccessible( true );
+ $propertyReflection = $this->getProperty( $name );
+
+ if ( $this->isStatic() && !$propertyReflection->isStatic() ) {
+ throw new DomainException( __METHOD__ . ': Cannot set 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 property when wrapping static class' );
+ }
+
+ return $propertyReflection->getValue( $this->object );
+ }
+
+ private function isStatic() {
+ return is_string( $this->object );
+ }
+
+ /**
+ * Return a property and make it accessible.
+ * @param string $name
+ * @return ReflectionMethod
+ */
+ private function getMethod( $name ) {
$classReflection = new ReflectionClass( $this->object );
- $propertyReflection = $classReflection->getProperty( $name );
+ $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 {
+ throw $ex;
+ }
+ }
+ }
$propertyReflection->setAccessible( true );
- return $propertyReflection->getValue( $this->object );
+ return $propertyReflection;
}
}