]> granicus.if.org Git - php/commitdiff
Add partial namespace support to gen_stub
authorNikita Popov <nikita.ppv@gmail.com>
Fri, 2 Oct 2020 15:07:46 +0000 (17:07 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Fri, 2 Oct 2020 15:08:50 +0000 (17:08 +0200)
build/gen_stub.php

index 4f3a8514b491f3c584c9d0f14f26650c12bf21c8..e2c6bb2eb225b31b42eaee5119ea883a987434a7 100755 (executable)
@@ -337,30 +337,73 @@ class ArgInfo {
     }
 }
 
-class FunctionName {
-    /** @var string|null */
+interface FunctionOrMethodName {
+    public function getDeclaration(): string;
+    public function getArgInfoName(): string;
+    public function __toString(): string;
+}
+
+class FunctionName implements FunctionOrMethodName {
+    /** @var Name */
+    private $name;
+
+    public function __construct(Name $name) {
+        $this->name = $name;
+    }
+
+    public function getNamespace(): ?string {
+        if ($this->name->isQualified()) {
+            return $this->name->slice(0, -1)->toString();
+        }
+        return null;
+    }
+
+    public function getShortName(): string {
+        return $this->name->getLast();
+    }
+
+    public function getNonNamespacedName(): string {
+        if ($this->name->isQualified()) {
+            throw new Exception("Namespaced name not supported here");
+        }
+        return $this->name->toString();
+    }
+
+    public function getDeclaration(): string {
+        return "ZEND_FUNCTION({$this->name->getLast()});\n";
+    }
+
+    public function getArgInfoName(): string {
+        $underscoreName = implode('_', $this->name->parts);
+        return "arginfo_$underscoreName";
+    }
+
+    public function __toString(): string {
+        return $this->name->toString();
+    }
+}
+
+class MethodName implements FunctionOrMethodName {
+    /** @var string */
     public $className;
     /** @var string */
     public $name;
 
-    public function __construct(?string $className, string $name)
-    {
+    public function __construct(string $className, string $name) {
         $this->className = $className;
         $this->name = $name;
     }
 
-    public function getDeclaration(): string
-    {
-        if ($this->className) {
-            return "ZEND_METHOD($this->className, $this->name);\n";
-        }
+    public function getDeclaration(): string {
+        return "ZEND_METHOD($this->className, $this->name);\n";
+    }
 
-        return "ZEND_FUNCTION($this->name);\n";
+    public function getArgInfoName(): string {
+        return "arginfo_class_{$this->className}_{$this->name}";
     }
 
-    public function __toString()
-    {
-        return $this->className ? "$this->className::$this->name" : $this->name;
+    public function __toString(): string {
+        return "$this->className::$this->name";
     }
 }
 
@@ -382,7 +425,7 @@ class ReturnInfo {
 }
 
 class FuncInfo {
-    /** @var FunctionName */
+    /** @var FunctionOrMethodName */
     public $name;
     /** @var int */
     public $flags;
@@ -402,10 +445,10 @@ class FuncInfo {
     public $cond;
 
     public function __construct(
-        FunctionName $name,
+        FunctionOrMethodName $name,
         int $flags,
         ?string $aliasType,
-        ?FunctionName $alias,
+        ?FunctionOrMethodName $alias,
         bool $isDeprecated,
         array $args,
         ReturnInfo $return,
@@ -440,10 +483,7 @@ class FuncInfo {
     }
 
     public function getArgInfoName(): string {
-        if ($this->name->className) {
-            return 'arginfo_class_' . $this->name->className . '_' . $this->name->name;
-        }
-        return 'arginfo_' . $this->name->name;
+        return $this->name->getArgInfoName();
     }
 
     public function getDeclarationKey(): string
@@ -465,18 +505,21 @@ class FuncInfo {
     }
 
     public function getFunctionEntry(): string {
-        if ($this->name->className) {
+        if ($this->name instanceof MethodName) {
             if ($this->alias) {
-                if ($this->alias->className) {
+                if ($this->alias instanceof MethodName) {
                     return sprintf(
                         "\tZEND_MALIAS(%s, %s, %s, %s, %s)\n",
                         $this->alias->className, $this->name->name, $this->alias->name, $this->getArgInfoName(), $this->getFlagsAsString()
                     );
-                } else {
+                } else if ($this->alias instanceof FunctionName) {
                     return sprintf(
                         "\tZEND_ME_MAPPING(%s, %s, %s, %s)\n",
-                        $this->name->name, $this->alias->name, $this->getArgInfoName(), $this->getFlagsAsString()
+                        $this->name->name, $this->alias->getNonNamespacedName(),
+                        $this->getArgInfoName(), $this->getFlagsAsString()
                     );
+                } else {
+                    throw new Error("Cannot happen");
                 }
             } else {
                 if ($this->flags & Class_::MODIFIER_ABSTRACT) {
@@ -491,26 +534,37 @@ class FuncInfo {
                     $this->name->className, $this->name->name, $this->getArgInfoName(), $this->getFlagsAsString()
                 );
             }
-        } else {
+        } else if ($this->name instanceof FunctionName) {
+            $namespace = $this->name->getNamespace();
+            $shortName = $this->name->getShortName();
+
             if ($this->alias && $this->isDeprecated) {
                 return sprintf(
                     "\tZEND_DEP_FALIAS(%s, %s, %s)\n",
-                    $this->name, $this->alias->name, $this->getArgInfoName()
+                    $shortName, $this->alias->getNonNamespacedName(), $this->getArgInfoName()
                 );
             }
 
             if ($this->alias) {
                 return sprintf(
                     "\tZEND_FALIAS(%s, %s, %s)\n",
-                    $this->name, $this->alias->name, $this->getArgInfoName()
+                    $shortName, $this->alias->getNonNamespacedName(), $this->getArgInfoName()
                 );
             }
 
             if ($this->isDeprecated) {
-                return sprintf("\tZEND_DEP_FE(%s, %s)\n", $this->name, $this->getArgInfoName());
+                return sprintf("\tZEND_DEP_FE(%s, %s)\n", $shortName, $this->getArgInfoName());
             }
 
-            return sprintf("\tZEND_FE(%s, %s)\n", $this->name, $this->getArgInfoName());
+            if ($namespace) {
+                return sprintf(
+                    "\tZEND_NS_FE(\"%s\", %s, %s)\n",
+                    $namespace, $shortName, $this->getArgInfoName());
+            } else {
+                return sprintf("\tZEND_FE(%s, %s)\n", $shortName, $this->getArgInfoName());
+            }
+        } else {
+            throw new Error("Cannot happen");
         }
     }
 
@@ -565,18 +619,11 @@ class ClassInfo {
 
 class FileInfo {
     /** @var FuncInfo[] */
-    public $funcInfos;
+    public $funcInfos = [];
     /** @var ClassInfo[] */
-    public $classInfos;
+    public $classInfos = [];
     /** @var bool */
-    public $generateFunctionEntries;
-
-    public function __construct(
-            array $funcInfos, array $classInfos, bool $generateFunctionEntries) {
-        $this->funcInfos = $funcInfos;
-        $this->classInfos = $classInfos;
-        $this->generateFunctionEntries = $generateFunctionEntries;
-    }
+    public $generateFunctionEntries = false;
 
     /**
      * @return iterable<FuncInfo>
@@ -646,7 +693,7 @@ function parseDocComment(DocComment $comment): array {
 
 function parseFunctionLike(
     PrettyPrinterAbstract $prettyPrinter,
-    FunctionName $name,
+    FunctionOrMethodName $name,
     int $flags,
     Node\FunctionLike $func,
     ?string $cond
@@ -672,9 +719,9 @@ function parseFunctionLike(
                 $aliasType = $tag->name;
                 $aliasParts = explode("::", $tag->getValue());
                 if (count($aliasParts) === 1) {
-                    $alias = new FunctionName(null, $aliasParts[0]);
+                    $alias = new FunctionName(new Name($aliasParts[0]));
                 } else {
-                    $alias = new FunctionName($aliasParts[0], $aliasParts[1]);
+                    $alias = new MethodName($aliasParts[0], $aliasParts[1]);
                 }
             } else if ($tag->name === 'deprecated') {
                 $isDeprecated = true;
@@ -814,46 +861,27 @@ function getFileDocComment(array $stmts): ?DocComment {
     return null;
 }
 
-function parseStubFile(string $code): FileInfo {
-    $lexer = new PhpParser\Lexer();
-    $parser = new PhpParser\Parser\Php7($lexer);
-    $nodeTraverser = new PhpParser\NodeTraverser;
-    $nodeTraverser->addVisitor(new PhpParser\NodeVisitor\NameResolver);
-    $prettyPrinter = new class extends Standard {
-        protected function pName_FullyQualified(Name\FullyQualified $node) {
-            return implode('\\', $node->parts);
-        }
-    };
-
-    $stmts = $parser->parse($code);
-    $nodeTraverser->traverse($stmts);
-
-    $generateFunctionEntries = false;
-    $fileDocComment = getFileDocComment($stmts);
-    if ($fileDocComment) {
-        if (strpos($fileDocComment->getText(), '@generate-function-entries') !== false) {
-            $generateFunctionEntries = true;
-        }
-    }
-
-    $funcInfos = [];
-    $classInfos = [];
+function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstract $prettyPrinter) {
     $conds = [];
     foreach ($stmts as $stmt) {
-        $cond = handlePreprocessorConditions($conds, $stmt);
         if ($stmt instanceof Stmt\Nop) {
             continue;
         }
 
+        if ($stmt instanceof Stmt\Namespace_) {
+            handleStatements($fileInfo, $stmt->stmts, $prettyPrinter);
+            continue;
+        }
+
+        $cond = handlePreprocessorConditions($conds, $stmt);
         if ($stmt instanceof Stmt\Function_) {
-            $funcInfos[] = parseFunctionLike(
+            $fileInfo->funcInfos[] = parseFunctionLike(
                 $prettyPrinter,
-                new FunctionName(null, $stmt->name->toString()),
+                new FunctionName($stmt->namespacedName),
                 0,
                 $stmt,
                 $cond
             );
-
             continue;
         }
 
@@ -881,21 +909,45 @@ function parseStubFile(string $code): FileInfo {
 
                 $methodInfos[] = parseFunctionLike(
                     $prettyPrinter,
-                    new FunctionName($className, $classStmt->name->toString()),
+                    new MethodName($className, $classStmt->name->toString()),
                     $flags,
                     $classStmt,
                     $cond
                 );
             }
 
-            $classInfos[] = new ClassInfo($className, $methodInfos);
+            $fileInfo->classInfos[] = new ClassInfo($className, $methodInfos);
             continue;
         }
 
         throw new Exception("Unexpected node {$stmt->getType()}");
     }
+}
+
+function parseStubFile(string $code): FileInfo {
+    $lexer = new PhpParser\Lexer();
+    $parser = new PhpParser\Parser\Php7($lexer);
+    $nodeTraverser = new PhpParser\NodeTraverser;
+    $nodeTraverser->addVisitor(new PhpParser\NodeVisitor\NameResolver);
+    $prettyPrinter = new class extends Standard {
+        protected function pName_FullyQualified(Name\FullyQualified $node) {
+            return implode('\\', $node->parts);
+        }
+    };
+
+    $stmts = $parser->parse($code);
+    $nodeTraverser->traverse($stmts);
+
+    $fileInfo = new FileInfo;
+    $fileDocComment = getFileDocComment($stmts);
+    if ($fileDocComment) {
+        if (strpos($fileDocComment->getText(), '@generate-function-entries') !== false) {
+            $fileInfo->generateFunctionEntries = true;
+        }
+    }
 
-    return new FileInfo($funcInfos, $classInfos, $generateFunctionEntries);
+    handleStatements($fileInfo, $stmts, $prettyPrinter);
+    return $fileInfo;
 }
 
 function funcInfoToCode(FuncInfo $funcInfo): string {