Created
November 12, 2019 19:18
-
-
Save dasl-/01cfc3030c2a03817b282dfb50ad2a5a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php declare(strict_types=1); | |
use ast\Node; | |
use Phan\CodeBase; | |
use Phan\Language\Element\Clazz; | |
use Phan\Language\UnionType; | |
use Phan\Language\Element\Method; | |
use Phan\PluginV3; | |
use Phan\PluginV3\AnalyzeFunctionCapability; | |
use Phan\PluginV3\AnalyzeMethodCapability; | |
use Phan\PluginV3\AnalyzeClassCapability; | |
use Phan\PluginV3\PluginAwarePostAnalysisVisitor; | |
use Phan\PluginV3\PostAnalyzeNodeCapability; | |
use Phan\Language\FQSEN\FullyQualifiedMethodName; | |
final class MagicPlugin extends PluginV3 implements PostAnalyzeNodeCapability | |
{ | |
/** | |
* @return string - name of PluginAwarePostAnalysisVisitor subclass | |
*/ | |
public static function getPostAnalyzeNodeVisitorClassName() : string | |
{ | |
return MagicVisitor::class; | |
} | |
} | |
class MagicVisitor extends PluginAwarePostAnalysisVisitor | |
{ | |
// A plugin's visitors should not override visit() unless they need to. | |
/** | |
* @param Node $node | |
* A node to analyze | |
* @override | |
*/ | |
public function visitMethodCall(Node $node) : void | |
{ | |
if (!isset($node->children['method']) || $node->children['method'] !== 'registerMagicMethod') { | |
return; | |
} | |
if (!isset($node->children['args']->children[0]) || !is_string($node->children['args']->children[0])) { | |
return; | |
} | |
$method_name = $node->children['args']->children[0]; | |
$return_type = UnionType::fromFullyQualifiedRealString("mixed"); | |
$fqsen = FullyQualifiedMethodName::make($this->context->getClassFQSEN(), $method_name); | |
$method = new Method( | |
$this->context, | |
$method_name, | |
$return_type, | |
$flags = 0, | |
$fqsen, | |
$parameter_list = null | |
); | |
$this->code_base->addMethod($method); | |
return; | |
} | |
} | |
// Every plugin needs to return an instance of itself at the | |
// end of the file in which it's defined. | |
return new MagicPlugin(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ ./vendor/bin/phan -p -j 1 test.php | |
test.php:36 PhanUndeclaredMethod Call to undeclared method \Child::magic2 (Did you mean expr->magic1()) | |
test.php:37 PhanUndeclaredMethod Call to undeclared method \Child::methodDoesntExist |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @phan-forbid-undeclared-magic-methods | |
*/ | |
abstract class Base { | |
private $magic_methods = []; | |
protected function registerMagicMethod($name) { | |
$this->magic_methods[$name] = true; | |
} | |
public function __call(string $name, array $arguments) { | |
if (!isset($this->magic_methods[$name])) { | |
throw new Exception("Method '$name' not implemented"); | |
} | |
var_dump($name, $arguments); | |
} | |
} | |
class Child extends Base { | |
public function __construct() { | |
$this->registerMagicMethod("magic1"); | |
$magic_meth_name = "magic2"; | |
$this->registerMagicMethod($magic_meth_name); | |
} | |
} | |
$c = new Child(); | |
$c->magic1(); | |
$c->magic2(); | |
$c->methodDoesntExist(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment