}
class WellProtectedClass extends WellProtectedParentClass {
+ protected static $staticProperty = 'sp';
+ private static $staticPrivateProperty = 'spp';
+
protected $property;
private $privateProperty;
+ protected static function staticMethod() {
+ return 'sm';
+ }
+
+ private static function staticPrivateMethod() {
+ return 'spm';
+ }
+
public function __construct() {
parent::__construct();
$this->property = 1;
* $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 ) {
+ $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 ) {
+ $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 );
- $methodReflection = $classReflection->getMethod( $method );
+ $methodReflection = $classReflection->getMethod( $name );
$methodReflection->setAccessible( true );
- return $methodReflection->invokeArgs( $this->object, $args );
+ 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 {
- return $classReflection->getProperty( $name );
+ $propertyReflection = $classReflection->getProperty( $name );
} catch ( ReflectionException $ex ) {
while ( true ) {
$classReflection = $classReflection->getParentClass();
continue;
}
if ( $propertyReflection->isPrivate() ) {
- return $propertyReflection;
+ break;
} else {
throw $ex;
}
}
}
- }
-
- public function __set( $name, $value ) {
- $propertyReflection = $this->getProperty( $name );
$propertyReflection->setAccessible( true );
- $propertyReflection->setValue( $this->object, $value );
- }
-
- public function __get( $name ) {
- $propertyReflection = $this->getProperty( $name );
- $propertyReflection->setAccessible( true );
- return $propertyReflection->getValue( $this->object );
+ return $propertyReflection;
}
}
class TestingAccessWrapperTest extends MediaWikiTestCase {
protected $raw;
protected $wrapped;
+ protected $wrappedStatic;
function setUp() {
parent::setUp();
require_once __DIR__ . '/../data/helpers/WellProtectedClass.php';
$this->raw = new WellProtectedClass();
$this->wrapped = TestingAccessWrapper::newFromObject( $this->raw );
+ $this->wrappedStatic = TestingAccessWrapper::newFromClass( 'WellProtectedClass' );
+ }
+
+ /**
+ * @expectedException InvalidArgumentException
+ */
+ function testConstructorException() {
+ TestingAccessWrapper::newFromObject( 'WellProtectedClass' );
+ }
+
+ /**
+ * @expectedException InvalidArgumentException
+ */
+ function testStaticConstructorException() {
+ TestingAccessWrapper::newFromClass( new WellProtectedClass() );
}
function testGetProperty() {
$this->assertSame( 1, $this->wrapped->property );
$this->assertSame( 42, $this->wrapped->privateProperty );
$this->assertSame( 9000, $this->wrapped->privateParentProperty );
+ $this->assertSame( 'sp', $this->wrapped->staticProperty );
+ $this->assertSame( 'spp', $this->wrapped->staticPrivateProperty );
+ $this->assertSame( 'sp', $this->wrappedStatic->staticProperty );
+ $this->assertSame( 'spp', $this->wrappedStatic->staticPrivateProperty );
+ }
+
+ /**
+ * @expectedException DomainException
+ */
+ function testGetException() {
+ $this->wrappedStatic->property;
}
function testSetProperty() {
$this->wrapped->privateParentProperty = 12;
$this->assertSame( 12, $this->wrapped->privateParentProperty );
$this->assertSame( 12, $this->raw->getPrivateParentProperty() );
+
+ $this->wrapped->staticProperty = 'x';
+ $this->assertSame( 'x', $this->wrapped->staticProperty );
+ $this->assertSame( 'x', $this->wrappedStatic->staticProperty );
+
+ $this->wrapped->staticPrivateProperty = 'y';
+ $this->assertSame( 'y', $this->wrapped->staticPrivateProperty );
+ $this->assertSame( 'y', $this->wrappedStatic->staticPrivateProperty );
+
+ $this->wrappedStatic->staticProperty = 'X';
+ $this->assertSame( 'X', $this->wrapped->staticProperty );
+ $this->assertSame( 'X', $this->wrappedStatic->staticProperty );
+
+ $this->wrappedStatic->staticPrivateProperty = 'Y';
+ $this->assertSame( 'Y', $this->wrapped->staticPrivateProperty );
+ $this->assertSame( 'Y', $this->wrappedStatic->staticPrivateProperty );
+
+ // don't rely on PHPUnit to restore static properties
+ $this->wrapped->staticProperty = 'sp';
+ $this->wrapped->staticPrivateProperty = 'spp';
+ }
+
+ /**
+ * @expectedException DomainException
+ */
+ function testSetException() {
+ $this->wrappedStatic->property = 1;
}
function testCallMethod() {
$this->wrapped->incrementPrivateParentPropertyValue();
$this->assertSame( 9001, $this->wrapped->privateParentProperty );
$this->assertSame( 9001, $this->raw->getPrivateParentProperty() );
+
+ $this->assertSame( 'sm', $this->wrapped->staticMethod() );
+ $this->assertSame( 'spm', $this->wrapped->staticPrivateMethod() );
+ $this->assertSame( 'sm', $this->wrappedStatic->staticMethod() );
+ $this->assertSame( 'spm', $this->wrappedStatic->staticPrivateMethod() );
}
function testCallMethodTwoArgs() {
$this->assertSame( 'two', $this->wrapped->whatSecondArg( 'one', 'two' ) );
}
+
+ /**
+ * @expectedException DomainException
+ */
+ function testCallMethodException() {
+ $this->wrappedStatic->incrementPropertyValue();
+ }
+
}