phpspec / prophecy

Highly opinionated mocking framework for PHP 5.3+
MIT License
8.53k stars 241 forks source link

Prophecy of magic __call method not working anymore with 1.6.1 #277

Open jenschude opened 8 years ago

jenschude commented 8 years ago

When using the latest version 1.6.1 with reflection-docblock 3.x I now get errors on prophecies using magic methods. When enforcing the use of reflection-docblock 2.x the error is gone.

This is the eror which popped up:

Method `Double\MagicClass\P1::getName()` is not defined.

Example code:

class MagicClassCollection
{
    private $objects = [];

    public function add(MagicClass $object)
    {
        $this->objects[$object->getName()] = $object;
    }

    public function getByName($name)
    {
        return $this->objects[$name];
    }
}

/**
 * @method string getName()
 * @method getValue()
 */
class MagicClass
{

    private $name;
    private $value;

    public function __construct($name, $value = null)
    {
        $this->name = $name;
        $this->value = $value;
    }

    private function get($name)
    {
        return $this->$name;
    }

    public function __call($name, $args)
    {
        $property = substr($name, 3);
        return $this->get($property);
    }
}

Example Test:

class MagicValueTest extends \PHPUnit_Framework_TestCase
    public function testDynamicProphecy()
    {
        $collection = new MagicClassCollection();

        $observer = $this->prophesize('MagicClass');
        $observer->getName()->shouldBeCalled()->willReturn('test');

        $test = $observer->reveal();
        $collection->add($test);
    }
}
jenschude commented 8 years ago

Btw i fixed the issue for me now by creating a method inside the MagicClass called getName, but there could be use cases where you don't know which function name may be called

stof commented 8 years ago

I will try to look at this issue this evening to see if I can identify the issue with reflection-docblock 3.x

jenschude commented 8 years ago

Found the underlying issue in the reflection docblock library. It's located in the DocBlockFactory::parseTagBlock()

When there are magic methods declared without a return type, the tagFactory throws an InvalidArgumentException with message Attempted to resolve "" but it appears to be empty and as the ClassTagReceiver in prophecy returns an empty array when there is an exception thrown from the docBlockFactory, prophecy doesn't know anything about the magic methods.

So the error itself could be fixed by changing the class doc block from the example to this:

/**
 * @method string getName()
 * @method mixed getValue()
 */

or for void methods:

/**
 * @method string getName()
 * @method void getValue()
 */
dunglas commented 8 years ago

Is @method getValue() valid according to PHPDock standards?

jenschude commented 8 years ago

The documentation from phpDocumentor says that the return type is optional, even the name is optional

https://phpdoc.org/docs/latest/references/phpdoc/tags/method.html

terinjokes commented 7 years ago

It doesn't look like @method declarations from the inheritance tree are considered.