View source
<?php
namespace spec\Prophecy\Doubler\Generator;
use PhpSpec\ObjectBehavior;
use I\Simply;
use ReflectionClass;
use ReflectionMethod;
use ReflectionParameter;
class ClassMirrorSpec extends ObjectBehavior {
function it_reflects_a_class_by_mirroring_all_its_public_methods($class, $method1, $method2, $method3) {
$class
->getName()
->willReturn('Custom\\ClassName');
$class
->isInterface()
->willReturn(false);
$class
->isFinal()
->willReturn(false);
$class
->getMethods(ReflectionMethod::IS_ABSTRACT)
->willReturn(array());
$class
->getMethods(ReflectionMethod::IS_PUBLIC)
->willReturn(array(
$method1,
$method2,
$method3,
));
$method1
->getName()
->willReturn('getName');
$method2
->getName()
->willReturn('isPublic');
$method3
->getName()
->willReturn('isAbstract');
$method1
->isFinal()
->willReturn(false);
$method2
->isFinal()
->willReturn(false);
$method3
->isFinal()
->willReturn(false);
$method1
->isProtected()
->willReturn(false);
$method2
->isProtected()
->willReturn(false);
$method3
->isProtected()
->willReturn(false);
$method1
->isStatic()
->willReturn(false);
$method2
->isStatic()
->willReturn(false);
$method3
->isStatic()
->willReturn(false);
$method1
->returnsReference()
->willReturn(false);
$method2
->returnsReference()
->willReturn(false);
$method3
->returnsReference()
->willReturn(false);
$method1
->getParameters()
->willReturn(array());
$method2
->getParameters()
->willReturn(array());
$method3
->getParameters()
->willReturn(array());
if (version_compare(PHP_VERSION, '7.0', '>=')) {
$method1
->hasReturnType()
->willReturn(false);
$method2
->hasReturnType()
->willReturn(false);
$method3
->hasReturnType()
->willReturn(false);
}
$classNode = $this
->reflect($class, array());
$classNode
->shouldBeAnInstanceOf('Prophecy\\Doubler\\Generator\\Node\\ClassNode');
$classNode
->getParentClass()
->shouldReturn('Custom\\ClassName');
$methodNodes = $classNode
->getMethods();
$methodNodes
->shouldHaveCount(3);
$classNode
->hasMethod('getName')
->shouldReturn(true);
$classNode
->hasMethod('isPublic')
->shouldReturn(true);
$classNode
->hasMethod('isAbstract')
->shouldReturn(true);
}
function it_changes_argument_names_if_they_are_varying($class, $method, $parameter) {
$class
->getName()
->willReturn('Custom\\ClassName');
$class
->isInterface()
->willReturn(false);
$class
->isFinal()
->willReturn(false);
$class
->getMethods(ReflectionMethod::IS_PUBLIC)
->willReturn(array(
$method,
));
$class
->getMethods(ReflectionMethod::IS_ABSTRACT)
->willReturn(array());
$method
->getParameters()
->willReturn(array(
$parameter,
));
$method
->getName()
->willReturn('methodName');
$method
->isFinal()
->willReturn(false);
$method
->isProtected()
->willReturn(false);
$method
->isStatic()
->willReturn(false);
$method
->returnsReference()
->willReturn(false);
if (version_compare(PHP_VERSION, '7.0', '>=')) {
$method
->hasReturnType()
->willReturn(false);
}
$parameter
->getName()
->willReturn('...');
$parameter
->isDefaultValueAvailable()
->willReturn(true);
$parameter
->getDefaultValue()
->willReturn(null);
$parameter
->isPassedByReference()
->willReturn(false);
$parameter
->getClass()
->willReturn($class);
$classNode = $this
->reflect($class, array());
$methodNodes = $classNode
->getMethods();
$argumentNodes = $methodNodes['methodName']
->getArguments();
$argumentNode = $argumentNodes[0];
$argumentNode
->getName()
->shouldReturn('__dot_dot_dot__');
}
function it_reflects_protected_abstract_methods($class, $method) {
$class
->getName()
->willReturn('Custom\\ClassName');
$class
->isInterface()
->willReturn(false);
$class
->isFinal()
->willReturn(false);
$class
->getMethods(ReflectionMethod::IS_ABSTRACT)
->willReturn(array(
$method,
));
$class
->getMethods(ReflectionMethod::IS_PUBLIC)
->willReturn(array());
$method
->isProtected()
->willReturn(true);
$method
->isStatic()
->willReturn(false);
$method
->getParameters()
->willReturn(array());
$method
->getName()
->willReturn('innerDetail');
$method
->returnsReference()
->willReturn(false);
if (version_compare(PHP_VERSION, '7.0', '>=')) {
$method
->hasReturnType()
->willReturn(false);
}
$classNode = $this
->reflect($class, array());
$classNode
->shouldBeAnInstanceOf('Prophecy\\Doubler\\Generator\\Node\\ClassNode');
$classNode
->getParentClass()
->shouldReturn('Custom\\ClassName');
$methodNodes = $classNode
->getMethods();
$methodNodes
->shouldHaveCount(1);
$methodNodes['innerDetail']
->getVisibility()
->shouldReturn('protected');
}
function it_reflects_public_static_methods($class, $method) {
$class
->getName()
->willReturn('Custom\\ClassName');
$class
->isInterface()
->willReturn(false);
$class
->isFinal()
->willReturn(false);
$class
->getMethods(ReflectionMethod::IS_ABSTRACT)
->willReturn(array(
$method,
));
$class
->getMethods(ReflectionMethod::IS_PUBLIC)
->willReturn(array());
$method
->isProtected()
->willReturn(true);
$method
->isStatic()
->willReturn(true);
$method
->getParameters()
->willReturn(array());
$method
->getName()
->willReturn('innerDetail');
$method
->returnsReference()
->willReturn(false);
if (version_compare(PHP_VERSION, '7.0', '>=')) {
$method
->hasReturnType()
->willReturn(false);
}
$classNode = $this
->reflect($class, array());
$classNode
->shouldBeAnInstanceOf('Prophecy\\Doubler\\Generator\\Node\\ClassNode');
$classNode
->getParentClass()
->shouldReturn('Custom\\ClassName');
$methodNodes = $classNode
->getMethods();
$methodNodes
->shouldHaveCount(1);
$methodNodes['innerDetail']
->getVisibility()
->shouldReturn('protected');
$methodNodes['innerDetail']
->isStatic()
->shouldReturn(true);
}
function it_properly_reads_methods_arguments_with_types($class, $method, $param1, $param2, $typeHint, $param3) {
$class
->getName()
->willReturn('Custom\\ClassName');
$class
->isInterface()
->willReturn(false);
$class
->isFinal()
->willReturn(false);
$class
->getMethods(ReflectionMethod::IS_ABSTRACT)
->willReturn(array());
$class
->getMethods(ReflectionMethod::IS_PUBLIC)
->willReturn(array(
$method,
));
$method
->getName()
->willReturn('methodWithArgs');
$method
->isFinal()
->willReturn(false);
$method
->isProtected()
->willReturn(true);
$method
->isStatic()
->willReturn(false);
$method
->returnsReference()
->willReturn(false);
$method
->getParameters()
->willReturn(array(
$param1,
$param2,
$param3,
));
if (version_compare(PHP_VERSION, '7.0', '>=')) {
$method
->hasReturnType()
->willReturn(false);
}
$param1
->getName()
->willReturn('arg_1');
$param1
->isArray()
->willReturn(true);
$param1
->getClass()
->willReturn(null);
$param1
->isDefaultValueAvailable()
->willReturn(true);
$param1
->isPassedByReference()
->willReturn(false);
$param1
->allowsNull()
->willReturn(false);
$param1
->getDefaultValue()
->willReturn(array());
$param2
->getName()
->willReturn('arg2');
$param2
->isArray()
->willReturn(false);
$param2
->getClass()
->willReturn($typeHint);
$param2
->isDefaultValueAvailable()
->willReturn(false);
$param2
->isOptional()
->willReturn(false);
$param2
->isPassedByReference()
->willReturn(false);
$param2
->allowsNull()
->willReturn(false);
$typeHint
->getName()
->willReturn('ArrayAccess');
$param3
->getName()
->willReturn('arg_3');
$param3
->isArray()
->willReturn(false);
if (version_compare(PHP_VERSION, '5.4', '>=')) {
$param3
->isCallable()
->willReturn(true);
}
$param3
->getClass()
->willReturn(null);
$param3
->isOptional()
->willReturn(false);
$param3
->isDefaultValueAvailable()
->willReturn(false);
$param3
->isPassedByReference()
->willReturn(false);
$param3
->allowsNull()
->willReturn(true);
$classNode = $this
->reflect($class, array());
$methodNodes = $classNode
->getMethods();
$argNodes = $methodNodes['methodWithArgs']
->getArguments();
$argNodes[0]
->getName()
->shouldReturn('arg_1');
$argNodes[0]
->getTypeHint()
->shouldReturn('array');
$argNodes[0]
->isOptional()
->shouldReturn(true);
$argNodes[0]
->getDefault()
->shouldReturn(array());
$argNodes[1]
->getName()
->shouldReturn('arg2');
$argNodes[1]
->getTypeHint()
->shouldReturn('ArrayAccess');
$argNodes[1]
->isOptional()
->shouldReturn(false);
$argNodes[2]
->getName()
->shouldReturn('arg_3');
if (version_compare(PHP_VERSION, '5.4', '>=')) {
$argNodes[2]
->getTypeHint()
->shouldReturn('callable');
$argNodes[2]
->isOptional()
->shouldReturn(true);
$argNodes[2]
->getDefault()
->shouldReturn(null);
}
else {
$argNodes[2]
->isOptional()
->shouldReturn(false);
}
}
function it_marks_required_args_without_types_as_not_optional($class, $method, $param1) {
$class
->getName()
->willReturn('Custom\\ClassName');
$class
->isInterface()
->willReturn(false);
$class
->isFinal()
->willReturn(false);
$class
->getMethods(ReflectionMethod::IS_ABSTRACT)
->willReturn(array());
$class
->getMethods(ReflectionMethod::IS_PUBLIC)
->willReturn(array(
$method,
));
$method
->getName()
->willReturn('methodWithArgs');
$method
->isFinal()
->willReturn(false);
$method
->isProtected()
->willReturn(false);
$method
->isStatic()
->willReturn(false);
$method
->returnsReference()
->willReturn(false);
$method
->getParameters()
->willReturn(array(
$param1,
));
if (version_compare(PHP_VERSION, '7.0', '>=')) {
$method
->hasReturnType()
->willReturn(false);
}
$param1
->getName()
->willReturn('arg_1');
$param1
->isArray()
->willReturn(false);
if (version_compare(PHP_VERSION, '5.4', '>=')) {
$param1
->isCallable()
->willReturn(false);
}
$param1
->getClass()
->willReturn(null);
if (version_compare(PHP_VERSION, '7.0', '>=')) {
$param1
->hasType()
->willReturn(false);
}
$param1
->isDefaultValueAvailable()
->willReturn(false);
$param1
->isOptional()
->willReturn(false);
$param1
->isPassedByReference()
->willReturn(false);
$param1
->allowsNull()
->willReturn(true);
if (defined('HHVM_VERSION')) {
$param1
->getTypehintText()
->willReturn(null);
}
$classNode = $this
->reflect($class, array());
$methodNodes = $classNode
->getMethods();
$argNodes = $methodNodes['methodWithArgs']
->getArguments();
$argNodes[0]
->isOptional()
->shouldReturn(false);
}
function it_marks_passed_by_reference_args_as_passed_by_reference($class, $method, $param1, $param2, $typeHint) {
$class
->getName()
->willReturn('Custom\\ClassName');
$class
->isInterface()
->willReturn(false);
$class
->isFinal()
->willReturn(false);
$class
->getMethods(ReflectionMethod::IS_ABSTRACT)
->willReturn(array());
$class
->getMethods(ReflectionMethod::IS_PUBLIC)
->willReturn(array(
$method,
));
$method
->getName()
->willReturn('methodWithArgs');
$method
->isFinal()
->willReturn(false);
$method
->isProtected()
->willReturn(false);
$method
->isStatic()
->willReturn(false);
$method
->returnsReference()
->willReturn(false);
$method
->getParameters()
->willReturn(array(
$param1,
$param2,
));
if (version_compare(PHP_VERSION, '7.0', '>=')) {
$method
->hasReturnType()
->willReturn(false);
}
$param1
->getName()
->willReturn('arg_1');
$param1
->isArray()
->willReturn(false);
if (version_compare(PHP_VERSION, '5.4', '>=')) {
$param1
->isCallable()
->willReturn(false);
}
$param1
->getClass()
->willReturn(null);
$param1
->isDefaultValueAvailable()
->willReturn(false);
$param1
->isOptional()
->willReturn(true);
$param1
->isPassedByReference()
->willReturn(true);
if (version_compare(PHP_VERSION, '7.0', '>=')) {
$param1
->hasType()
->willReturn(false);
}
$param1
->allowsNull()
->willReturn(false);
if (defined('HHVM_VERSION')) {
$param1
->getTypehintText()
->willReturn(null);
}
$param2
->getName()
->willReturn('arg2');
$param2
->isArray()
->willReturn(false);
$param2
->getClass()
->willReturn($typeHint);
$param2
->isDefaultValueAvailable()
->willReturn(false);
$param2
->isOptional()
->willReturn(false);
$param2
->isPassedByReference()
->willReturn(false);
if (version_compare(PHP_VERSION, '7.0', '>=')) {
$param2
->hasType()
->willReturn(false);
}
$param2
->allowsNull()
->willReturn(false);
$typeHint
->getName()
->willReturn('ArrayAccess');
$classNode = $this
->reflect($class, array());
$methodNodes = $classNode
->getMethods();
$argNodes = $methodNodes['methodWithArgs']
->getArguments();
$argNodes[0]
->isPassedByReference()
->shouldReturn(true);
$argNodes[1]
->isPassedByReference()
->shouldReturn(false);
}
function it_throws_an_exception_if_class_is_final($class) {
$class
->isInterface()
->willReturn(false);
$class
->isFinal()
->willReturn(true);
$class
->getName()
->willReturn('Custom\\ClassName');
$this
->shouldThrow('Prophecy\\Exception\\Doubler\\ClassMirrorException')
->duringReflect($class, array());
}
function it_ignores_final_methods($class, $method) {
$class
->getName()
->willReturn('Custom\\ClassName');
$class
->isInterface()
->willReturn(false);
$class
->isFinal()
->willReturn(false);
$class
->getMethods(ReflectionMethod::IS_ABSTRACT)
->willReturn(array());
$class
->getMethods(ReflectionMethod::IS_PUBLIC)
->willReturn(array(
$method,
));
$method
->isFinal()
->willReturn(true);
$method
->getName()
->willReturn('finalImplementation');
$classNode = $this
->reflect($class, array());
$classNode
->getMethods()
->shouldHaveCount(0);
}
function it_throws_an_exception_if_interface_provided_instead_of_class($interface) {
$interface
->isInterface()
->willReturn(true);
$interface
->getName()
->willReturn('Custom\\ClassName');
$this
->shouldThrow('Prophecy\\Exception\\InvalidArgumentException')
->duringReflect($interface, array());
}
function it_reflects_all_interfaces_methods($interface1, $interface2, $method1, $method2, $method3) {
$interface1
->getName()
->willReturn('MyInterface1');
$interface2
->getName()
->willReturn('MyInterface2');
$interface1
->isInterface()
->willReturn(true);
$interface2
->isInterface()
->willReturn(true);
$interface1
->getMethods()
->willReturn(array(
$method1,
));
$interface2
->getMethods()
->willReturn(array(
$method2,
$method3,
));
$method1
->getName()
->willReturn('getName');
$method2
->getName()
->willReturn('isPublic');
$method3
->getName()
->willReturn('isAbstract');
$method1
->isProtected()
->willReturn(false);
$method2
->isProtected()
->willReturn(false);
$method3
->isProtected()
->willReturn(false);
$method1
->returnsReference()
->willReturn(false);
$method2
->returnsReference()
->willReturn(false);
$method3
->returnsReference()
->willReturn(false);
$method1
->isStatic()
->willReturn(false);
$method2
->isStatic()
->willReturn(false);
$method3
->isStatic()
->willReturn(false);
$method1
->getParameters()
->willReturn(array());
$method2
->getParameters()
->willReturn(array());
$method3
->getParameters()
->willReturn(array());
if (version_compare(PHP_VERSION, '7.0', '>=')) {
$method1
->hasReturnType()
->willReturn(false);
$method2
->hasReturnType()
->willReturn(false);
$method3
->hasReturnType()
->willReturn(false);
}
$classNode = $this
->reflect(null, array(
$interface1,
$interface2,
));
$classNode
->shouldBeAnInstanceOf('Prophecy\\Doubler\\Generator\\Node\\ClassNode');
$classNode
->getParentClass()
->shouldReturn('stdClass');
$classNode
->getInterfaces()
->shouldReturn(array(
'Prophecy\\Doubler\\Generator\\ReflectionInterface',
'MyInterface2',
'MyInterface1',
));
$methodNodes = $classNode
->getMethods();
$methodNodes
->shouldHaveCount(3);
$classNode
->hasMethod('getName')
->shouldReturn(true);
$classNode
->hasMethod('isPublic')
->shouldReturn(true);
$classNode
->hasMethod('isAbstract')
->shouldReturn(true);
}
function it_ignores_virtually_private_methods($class, $method1, $method2, $method3) {
$class
->getName()
->willReturn('SomeClass');
$class
->isInterface()
->willReturn(false);
$class
->isFinal()
->willReturn(false);
$class
->getMethods(ReflectionMethod::IS_ABSTRACT)
->willReturn(array());
$class
->getMethods(ReflectionMethod::IS_PUBLIC)
->willReturn(array(
$method1,
$method2,
$method3,
));
$method1
->getName()
->willReturn('_getName');
$method2
->getName()
->willReturn('__toString');
$method3
->getName()
->willReturn('isAbstract');
$method1
->isFinal()
->willReturn(false);
$method2
->isFinal()
->willReturn(false);
$method3
->isFinal()
->willReturn(false);
$method1
->isProtected()
->willReturn(false);
$method2
->isProtected()
->willReturn(false);
$method3
->isProtected()
->willReturn(false);
$method1
->isStatic()
->willReturn(false);
$method2
->isStatic()
->willReturn(false);
$method3
->isStatic()
->willReturn(false);
$method1
->returnsReference()
->willReturn(false);
$method2
->returnsReference()
->willReturn(false);
$method3
->returnsReference()
->willReturn(false);
$method1
->getParameters()
->willReturn(array());
$method2
->getParameters()
->willReturn(array());
$method3
->getParameters()
->willReturn(array());
if (version_compare(PHP_VERSION, '7.0', '>=')) {
$method1
->hasReturnType()
->willReturn(false);
$method2
->hasReturnType()
->willReturn(false);
$method3
->hasReturnType()
->willReturn(false);
}
$classNode = $this
->reflect($class, array());
$methodNodes = $classNode
->getMethods();
$methodNodes
->shouldHaveCount(2);
$classNode
->hasMethod('isAbstract')
->shouldReturn(true);
}
function it_does_not_throw_exception_for_virtually_private_finals($class, $method) {
$class
->getName()
->willReturn('SomeClass');
$class
->isInterface()
->willReturn(false);
$class
->isFinal()
->willReturn(false);
$class
->getMethods(ReflectionMethod::IS_ABSTRACT)
->willReturn(array());
$class
->getMethods(ReflectionMethod::IS_PUBLIC)
->willReturn(array(
$method,
));
$method
->getName()
->willReturn('__toString');
$method
->isFinal()
->willReturn(true);
$this
->shouldNotThrow()
->duringReflect($class, array());
}
function it_throws_an_exception_if_class_provided_in_interfaces_list($class) {
$class
->getName()
->willReturn('MyClass');
$class
->isInterface()
->willReturn(false);
$this
->shouldThrow('InvalidArgumentException')
->duringReflect(null, array(
$class,
));
}
function it_throws_an_exception_if_not_reflection_provided_as_interface() {
$this
->shouldThrow('InvalidArgumentException')
->duringReflect(null, array(
null,
));
}
function it_doesnt_fail_to_typehint_nonexistent_FQCN() {
$classNode = $this
->reflect(new ReflectionClass('spec\\Prophecy\\Doubler\\Generator\\OptionalDepsClass'), array());
$method = $classNode
->getMethod('iHaveAStrangeTypeHintedArg');
$arguments = $method
->getArguments();
$arguments[0]
->getTypeHint()
->shouldBe('I\\Simply\\Am\\Nonexistent');
}
function it_doesnt_fail_to_typehint_nonexistent_RQCN() {
$classNode = $this
->reflect(new ReflectionClass('spec\\Prophecy\\Doubler\\Generator\\OptionalDepsClass'), array());
$method = $classNode
->getMethod('iHaveAnEvenStrangerTypeHintedArg');
$arguments = $method
->getArguments();
$arguments[0]
->getTypeHint()
->shouldBe('I\\Simply\\Am\\Not');
}
function it_doesnt_use_scalar_typehints() {
$classNode = $this
->reflect(new ReflectionClass('ReflectionMethod'), array());
$method = $classNode
->getMethod('export');
$arguments = $method
->getArguments();
$arguments[0]
->getTypeHint()
->shouldReturn(null);
$arguments[1]
->getTypeHint()
->shouldReturn(null);
$arguments[2]
->getTypeHint()
->shouldReturn(null);
}
}
class OptionalDepsClass {
public function iHaveAStrangeTypeHintedArg(\I\Simply\Am\Nonexistent $class) {
}
public function iHaveAnEvenStrangerTypeHintedArg(Simply\Am\Not $class) {
}
}