Skip to content

Instantly share code, notes, and snippets.

@dasl-
Created November 12, 2019 19:18
Show Gist options
  • Save dasl-/01cfc3030c2a03817b282dfb50ad2a5a to your computer and use it in GitHub Desktop.
Save dasl-/01cfc3030c2a03817b282dfb50ad2a5a to your computer and use it in GitHub Desktop.
<?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();
$ ./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
<?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