Skip to content

Commit

Permalink
Merge pull request #521 from ciaranmcnulty/virtual-magic-methods
Browse files Browse the repository at this point in the history
Support arguments on virtual magic methods
  • Loading branch information
ciaranmcnulty committed Mar 17, 2021
2 parents 26ec19b + 34b3408 commit e8c7f46
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 9 deletions.
3 changes: 1 addition & 2 deletions fixtures/WithPhpdocClass.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
<?php


namespace Fixtures\Prophecy;


/**
* @method string name(string $gender = null)
* @method mixed randomElement(array $array = array('a', 'b', 'c'))
* @method mixed __unserialize($data)
*/
class WithPhpdocClass
{
Expand Down
13 changes: 12 additions & 1 deletion src/Prophecy/Doubler/ClassPatch/MagicCallPatch.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Prophecy\Doubler\ClassPatch;

use Prophecy\Doubler\Generator\Node\ArgumentNode;
use Prophecy\Doubler\Generator\Node\ClassNode;
use Prophecy\Doubler\Generator\Node\MethodNode;
use Prophecy\PhpDocumentor\ClassAndInterfaceTagRetriever;
Expand All @@ -25,6 +26,8 @@
*/
class MagicCallPatch implements ClassPatchInterface
{
const MAGIC_METHODS_WITH_ARGUMENTS = ['__call', '__callStatic', '__get', '__isset', '__set', '__set_state', '__unserialize', '__unset'];

private $tagRetriever;

public function __construct(MethodTagRetrieverInterface $tagRetriever = null)
Expand Down Expand Up @@ -71,6 +74,15 @@ public function apply(ClassNode $node)

if (!$reflectionClass->hasMethod($methodName)) {
$methodNode = new MethodNode($methodName);

// only magic methods can have a contract that needs to be enforced
if (in_array($methodName, self::MAGIC_METHODS_WITH_ARGUMENTS)) {
foreach($tag->getArguments() as $argument) {
$argumentNode = new ArgumentNode($argument['name']);
$methodNode->addArgument($argumentNode);
}
}

$methodNode->setStatic($tag->isStatic());
$node->addMethod($methodNode);
}
Expand All @@ -91,4 +103,3 @@ public function getPriority()
return 50;
}
}

32 changes: 26 additions & 6 deletions tests/Doubler/ClassPatch/MagicCallPatchTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
use PHPUnit\Framework\TestCase;
use Prophecy\Doubler\ClassPatch\MagicCallPatch;
use Prophecy\Doubler\Generator\ClassMirror;
use Prophecy\Doubler\Generator\Node\ArgumentNode;
use Prophecy\Doubler\Generator\Node\ClassNode;

class MagicCallPatchTest extends TestCase
{
Expand All @@ -15,12 +17,7 @@ public function it_supports_classes_with_invalid_tags()
{
$class = new \ReflectionClass('Fixtures\Prophecy\WithPhpdocClass');

$mirror = new ClassMirror();
$classNode = $mirror->reflect($class, array());

$patch = new MagicCallPatch();

$patch->apply($classNode);
$classNode = $this->applyPatchTo($class);

// Newer phpDocumentor versions allow reading valid method tags, even when some other tags are invalid
// Some older versions might also have this method due to not considering the method tag invalid as rule evolved, but we don't track that.
Expand All @@ -31,4 +28,27 @@ public function it_supports_classes_with_invalid_tags()
// We expect no error when processing the class patch. But we still need to increment the assertion count.
$this->assertTrue(true);
}

/**
* @test
*/
public function it_supports_arguments_for_magic_methods()
{
$class = new \ReflectionClass('Fixtures\Prophecy\WithPhpdocClass');

$classNode = $this->applyPatchTo($class);

$this->assertEquals([new ArgumentNode('data')], $classNode->getMethod('__unserialize')->getArguments());
}

private function applyPatchTo(\ReflectionClass $class): ClassNode
{
$mirror = new ClassMirror();
$classNode = $mirror->reflect($class, array());

$patch = new MagicCallPatch();

$patch->apply($classNode);
return $classNode;
}
}

0 comments on commit e8c7f46

Please sign in to comment.