From 84bfe8cb81276ca4f56fcaf09f0429f150545ca5 Mon Sep 17 00:00:00 2001 From: Martin Herndl Date: Mon, 2 May 2022 15:39:21 +0200 Subject: [PATCH 1/3] Ignore inaccessible class child nodes --- src/NodeVisitor.php | 9 +++++++ test/files/classes.in.php | 53 ++++++++++++++++++++++++++++++++++++++ test/files/classes.out.php | 30 +++++++++++++++++++++ 3 files changed, 92 insertions(+) diff --git a/src/NodeVisitor.php b/src/NodeVisitor.php index 57b4ce0..dc074da 100644 --- a/src/NodeVisitor.php +++ b/src/NodeVisitor.php @@ -11,6 +11,7 @@ use PhpParser\Node\Scalar\String_; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\ClassConst; use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Const_; @@ -19,6 +20,7 @@ use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Interface_; use PhpParser\Node\Stmt\Namespace_; +use PhpParser\Node\Stmt\Property; use PhpParser\Node\Stmt\Trait_; use PhpParser\NodeTraverser; use PhpParser\NodeVisitorAbstract; @@ -238,6 +240,13 @@ public function leaveNode(Node $node, bool $preserveStack = false) // Implies `$parent instanceof ClassLike`, which means $node is a // either a method, property, or constant, or its part of the // declaration itself (e.g., `extends`). + + if ($parent instanceof Class_ && ($node instanceof ClassMethod || $node instanceof ClassConst || $node instanceof Property)) { + if ($node->isPrivate() || ($parent->isFinal() && $node->isProtected())) { + return NodeTraverser::REMOVE_NODE; + } + } + return; } diff --git a/test/files/classes.in.php b/test/files/classes.in.php index fa787cd..1181858 100644 --- a/test/files/classes.in.php +++ b/test/files/classes.in.php @@ -5,9 +5,14 @@ abstract class A extends B implements C /** doc */ protected const A = 'B'; + /** doc */ + private const B = 'C'; + /** doc */ public static $a = 'a'; + private static $b = 'b'; + /** doc */ public static function b($a): void { @@ -16,6 +21,21 @@ public static function b($a): void /** doc */ abstract public function c($a): string; + + /** doc */ + protected function d($a): void + { + return; + } + + /** doc */ + abstract protected function e($a): string; + + /** doc */ + private function f($a): void + { + return; + } } if (!class_exists('D')) { @@ -27,3 +47,36 @@ public function a(string $a): string } } } + +final class E +{ + /** doc */ + public $a = 'a'; + + /** doc */ + protected $b = 'b'; + + /** doc */ + public function a($a): void + { + return; + } + + /** doc */ + protected function b($a): void + { + return; + } +} + +trait F +{ + /** doc */ + private $a = 'a'; + + /** doc */ + private function a($a): void + { + return; + } +} diff --git a/test/files/classes.out.php b/test/files/classes.out.php index 947613b..6ea8c14 100644 --- a/test/files/classes.out.php +++ b/test/files/classes.out.php @@ -15,6 +15,14 @@ public static function b($a): void /** doc */ abstract public function c($a): string; + + /** doc */ + protected function d($a) : void + { + } + + /** doc */ + protected abstract function e($a) : string; } class D @@ -23,3 +31,25 @@ public function a(string $a): string { } } + +final class E +{ + /** doc */ + public $a = 'a'; + + /** doc */ + public function a($a) : void + { + } +} + +trait F +{ + /** doc */ + private $a = 'a'; + + /** doc */ + private function a($a) : void + { + } +} From df763715fcb8449f6fd84be449e3e86693d738b9 Mon Sep 17 00:00:00 2001 From: Martin Herndl Date: Wed, 8 Feb 2023 20:58:28 +0100 Subject: [PATCH 2/3] Add config flag to include inaccessible class nodes --- src/GenerateStubsCommand.php | 2 ++ src/NodeVisitor.php | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/GenerateStubsCommand.php b/src/GenerateStubsCommand.php index 03433ff..11f0423 100644 --- a/src/GenerateStubsCommand.php +++ b/src/GenerateStubsCommand.php @@ -62,6 +62,7 @@ public function configure(): void ->addOption('visitor', null, InputOption::VALUE_REQUIRED, 'Path to a PHP file which returns a `StubsGenerator\NodeVisitor` instance to replace the default node visitor.') ->addOption('header', null, InputOption::VALUE_REQUIRED, 'A doc comment to prepend to the top of the generated stubs file. (Will be added below the opening `addOption('nullify-globals', null, InputOption::VALUE_NONE, 'Initialize all global variables with a value of `null`, instead of their assigned value.') + ->addOption('include-inaccessible-class-nodes', null, InputOption::VALUE_NONE, 'Include inaccessible class nodes like private members.') ->addOption('stats', null, InputOption::VALUE_NONE, 'Whether to print stats instead of outputting stubs. Stats will always be printed if --out is provided.'); foreach (self::SYMBOL_OPTIONS as $opt) { @@ -117,6 +118,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $finder = $this->parseSources($input); $generator = new StubsGenerator($this->parseSymbols($input), [ 'nullify_globals' => $input->getOption('nullify-globals'), + 'include_inaccessible_class_nodes' => $input->getOption('include-inaccessible-class-nodes') ]); $result = $generator->generate($finder, $visitor); diff --git a/src/NodeVisitor.php b/src/NodeVisitor.php index dc074da..b74a97c 100644 --- a/src/NodeVisitor.php +++ b/src/NodeVisitor.php @@ -49,6 +49,8 @@ class NodeVisitor extends NodeVisitorAbstract private $needsConstants; /** @var bool */ private $nullifyGlobals; + /** @var bool */ + private $includeInaccessibleClassNodes; /** * @psalm-suppress PropertyNotSetInConstructor @@ -103,6 +105,7 @@ public function init(int $symbols = StubsGenerator::DEFAULT, array $config = []) $this->needsConstants = ($symbols & StubsGenerator::CONSTANTS) !== 0; $this->nullifyGlobals = !empty($config['nullify_globals']); + $this->includeInaccessibleClassNodes = ($config['include_inaccessible_class_nodes'] ?? false) === true; $this->globalNamespace = new Namespace_(); } @@ -241,7 +244,7 @@ public function leaveNode(Node $node, bool $preserveStack = false) // either a method, property, or constant, or its part of the // declaration itself (e.g., `extends`). - if ($parent instanceof Class_ && ($node instanceof ClassMethod || $node instanceof ClassConst || $node instanceof Property)) { + if (!$this->includeInaccessibleClassNodes && $parent instanceof Class_ && ($node instanceof ClassMethod || $node instanceof ClassConst || $node instanceof Property)) { if ($node->isPrivate() || ($parent->isFinal() && $node->isProtected())) { return NodeTraverser::REMOVE_NODE; } From 3daed097ce29852841cb5b65d0f66b8dd4dd1617 Mon Sep 17 00:00:00 2001 From: Martin Herndl Date: Wed, 8 Feb 2023 21:29:10 +0100 Subject: [PATCH 3/3] Add test for include-inaccessible-class-nodes config --- test/NodeVisitorTest.php | 1 + ...s-include-inaccessible-class-nodes.out.php | 73 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 test/files/classes-include-inaccessible-class-nodes.out.php diff --git a/test/NodeVisitorTest.php b/test/NodeVisitorTest.php index 35c151e..7af26e4 100644 --- a/test/NodeVisitorTest.php +++ b/test/NodeVisitorTest.php @@ -36,6 +36,7 @@ public function inputOutputProvider(): array { $cases = [ 'classes', + ['classes', 'classes-include-inaccessible-class-nodes', null, ['include_inaccessible_class_nodes' => true]], 'classes-with-dependencies', 'circular-dependency', 'functions', diff --git a/test/files/classes-include-inaccessible-class-nodes.out.php b/test/files/classes-include-inaccessible-class-nodes.out.php new file mode 100644 index 0000000..f246623 --- /dev/null +++ b/test/files/classes-include-inaccessible-class-nodes.out.php @@ -0,0 +1,73 @@ +