Skip to content

Commit 924ac2b

Browse files
committed
Add partial namespace support to gen_stub
1 parent 2e32a09 commit 924ac2b

File tree

1 file changed

+125
-73
lines changed

1 file changed

+125
-73
lines changed

build/gen_stub.php

Lines changed: 125 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -337,30 +337,73 @@ public function getDefaultValueString(): string {
337337
}
338338
}
339339

340-
class FunctionName {
341-
/** @var string|null */
340+
interface FunctionOrMethodName {
341+
public function getDeclaration(): string;
342+
public function getArgInfoName(): string;
343+
public function __toString(): string;
344+
}
345+
346+
class FunctionName implements FunctionOrMethodName {
347+
/** @var Name */
348+
private $name;
349+
350+
public function __construct(Name $name) {
351+
$this->name = $name;
352+
}
353+
354+
public function getNamespace(): ?string {
355+
if ($this->name->isQualified()) {
356+
return $this->name->slice(0, -1)->toString();
357+
}
358+
return null;
359+
}
360+
361+
public function getShortName(): string {
362+
return $this->name->getLast();
363+
}
364+
365+
public function getNonNamespacedName(): string {
366+
if ($this->name->isQualified()) {
367+
throw new Exception("Namespaced name not supported here");
368+
}
369+
return $this->name->toString();
370+
}
371+
372+
public function getDeclaration(): string {
373+
return "ZEND_FUNCTION({$this->name->getLast()});\n";
374+
}
375+
376+
public function getArgInfoName(): string {
377+
$underscoreName = implode('_', $this->name->parts);
378+
return "arginfo_$underscoreName";
379+
}
380+
381+
public function __toString(): string {
382+
return $this->name->toString();
383+
}
384+
}
385+
386+
class MethodName implements FunctionOrMethodName {
387+
/** @var string */
342388
public $className;
343389
/** @var string */
344390
public $name;
345391

346-
public function __construct(?string $className, string $name)
347-
{
392+
public function __construct(string $className, string $name) {
348393
$this->className = $className;
349394
$this->name = $name;
350395
}
351396

352-
public function getDeclaration(): string
353-
{
354-
if ($this->className) {
355-
return "ZEND_METHOD($this->className, $this->name);\n";
356-
}
397+
public function getDeclaration(): string {
398+
return "ZEND_METHOD($this->className, $this->name);\n";
399+
}
357400

358-
return "ZEND_FUNCTION($this->name);\n";
401+
public function getArgInfoName(): string {
402+
return "arginfo_class_{$this->className}_{$this->name}";
359403
}
360404

361-
public function __toString()
362-
{
363-
return $this->className ? "$this->className::$this->name" : $this->name;
405+
public function __toString(): string {
406+
return "$this->className::$this->name";
364407
}
365408
}
366409

@@ -382,7 +425,7 @@ public function equals(ReturnInfo $other): bool {
382425
}
383426

384427
class FuncInfo {
385-
/** @var FunctionName */
428+
/** @var FunctionOrMethodName */
386429
public $name;
387430
/** @var int */
388431
public $flags;
@@ -402,10 +445,10 @@ class FuncInfo {
402445
public $cond;
403446

404447
public function __construct(
405-
FunctionName $name,
448+
FunctionOrMethodName $name,
406449
int $flags,
407450
?string $aliasType,
408-
?FunctionName $alias,
451+
?FunctionOrMethodName $alias,
409452
bool $isDeprecated,
410453
array $args,
411454
ReturnInfo $return,
@@ -440,10 +483,7 @@ public function equalsApartFromName(FuncInfo $other): bool {
440483
}
441484

442485
public function getArgInfoName(): string {
443-
if ($this->name->className) {
444-
return 'arginfo_class_' . $this->name->className . '_' . $this->name->name;
445-
}
446-
return 'arginfo_' . $this->name->name;
486+
return $this->name->getArgInfoName();
447487
}
448488

449489
public function getDeclarationKey(): string
@@ -465,18 +505,21 @@ public function getDeclaration(): ?string
465505
}
466506

467507
public function getFunctionEntry(): string {
468-
if ($this->name->className) {
508+
if ($this->name instanceof MethodName) {
469509
if ($this->alias) {
470-
if ($this->alias->className) {
510+
if ($this->alias instanceof MethodName) {
471511
return sprintf(
472512
"\tZEND_MALIAS(%s, %s, %s, %s, %s)\n",
473513
$this->alias->className, $this->name->name, $this->alias->name, $this->getArgInfoName(), $this->getFlagsAsString()
474514
);
475-
} else {
515+
} else if ($this->alias instanceof FunctionName) {
476516
return sprintf(
477517
"\tZEND_ME_MAPPING(%s, %s, %s, %s)\n",
478-
$this->name->name, $this->alias->name, $this->getArgInfoName(), $this->getFlagsAsString()
518+
$this->name->name, $this->alias->getNonNamespacedName(),
519+
$this->getArgInfoName(), $this->getFlagsAsString()
479520
);
521+
} else {
522+
throw new Error("Cannot happen");
480523
}
481524
} else {
482525
if ($this->flags & Class_::MODIFIER_ABSTRACT) {
@@ -491,26 +534,37 @@ public function getFunctionEntry(): string {
491534
$this->name->className, $this->name->name, $this->getArgInfoName(), $this->getFlagsAsString()
492535
);
493536
}
494-
} else {
537+
} else if ($this->name instanceof FunctionName) {
538+
$namespace = $this->name->getNamespace();
539+
$shortName = $this->name->getShortName();
540+
495541
if ($this->alias && $this->isDeprecated) {
496542
return sprintf(
497543
"\tZEND_DEP_FALIAS(%s, %s, %s)\n",
498-
$this->name, $this->alias->name, $this->getArgInfoName()
544+
$shortName, $this->alias->getNonNamespacedName(), $this->getArgInfoName()
499545
);
500546
}
501547

502548
if ($this->alias) {
503549
return sprintf(
504550
"\tZEND_FALIAS(%s, %s, %s)\n",
505-
$this->name, $this->alias->name, $this->getArgInfoName()
551+
$shortName, $this->alias->getNonNamespacedName(), $this->getArgInfoName()
506552
);
507553
}
508554

509555
if ($this->isDeprecated) {
510-
return sprintf("\tZEND_DEP_FE(%s, %s)\n", $this->name, $this->getArgInfoName());
556+
return sprintf("\tZEND_DEP_FE(%s, %s)\n", $shortName, $this->getArgInfoName());
511557
}
512558

513-
return sprintf("\tZEND_FE(%s, %s)\n", $this->name, $this->getArgInfoName());
559+
if ($namespace) {
560+
return sprintf(
561+
"\tZEND_NS_FE(\"%s\", %s, %s)\n",
562+
$namespace, $shortName, $this->getArgInfoName());
563+
} else {
564+
return sprintf("\tZEND_FE(%s, %s)\n", $shortName, $this->getArgInfoName());
565+
}
566+
} else {
567+
throw new Error("Cannot happen");
514568
}
515569
}
516570

@@ -565,18 +619,11 @@ public function __construct(string $name, array $funcInfos) {
565619

566620
class FileInfo {
567621
/** @var FuncInfo[] */
568-
public $funcInfos;
622+
public $funcInfos = [];
569623
/** @var ClassInfo[] */
570-
public $classInfos;
624+
public $classInfos = [];
571625
/** @var bool */
572-
public $generateFunctionEntries;
573-
574-
public function __construct(
575-
array $funcInfos, array $classInfos, bool $generateFunctionEntries) {
576-
$this->funcInfos = $funcInfos;
577-
$this->classInfos = $classInfos;
578-
$this->generateFunctionEntries = $generateFunctionEntries;
579-
}
626+
public $generateFunctionEntries = false;
580627

581628
/**
582629
* @return iterable<FuncInfo>
@@ -646,7 +693,7 @@ function parseDocComment(DocComment $comment): array {
646693

647694
function parseFunctionLike(
648695
PrettyPrinterAbstract $prettyPrinter,
649-
FunctionName $name,
696+
FunctionOrMethodName $name,
650697
int $flags,
651698
Node\FunctionLike $func,
652699
?string $cond
@@ -672,9 +719,9 @@ function parseFunctionLike(
672719
$aliasType = $tag->name;
673720
$aliasParts = explode("::", $tag->getValue());
674721
if (count($aliasParts) === 1) {
675-
$alias = new FunctionName(null, $aliasParts[0]);
722+
$alias = new FunctionName(new Name($aliasParts[0]));
676723
} else {
677-
$alias = new FunctionName($aliasParts[0], $aliasParts[1]);
724+
$alias = new MethodName($aliasParts[0], $aliasParts[1]);
678725
}
679726
} else if ($tag->name === 'deprecated') {
680727
$isDeprecated = true;
@@ -814,46 +861,27 @@ function getFileDocComment(array $stmts): ?DocComment {
814861
return null;
815862
}
816863

817-
function parseStubFile(string $code): FileInfo {
818-
$lexer = new PhpParser\Lexer();
819-
$parser = new PhpParser\Parser\Php7($lexer);
820-
$nodeTraverser = new PhpParser\NodeTraverser;
821-
$nodeTraverser->addVisitor(new PhpParser\NodeVisitor\NameResolver);
822-
$prettyPrinter = new class extends Standard {
823-
protected function pName_FullyQualified(Name\FullyQualified $node) {
824-
return implode('\\', $node->parts);
825-
}
826-
};
827-
828-
$stmts = $parser->parse($code);
829-
$nodeTraverser->traverse($stmts);
830-
831-
$generateFunctionEntries = false;
832-
$fileDocComment = getFileDocComment($stmts);
833-
if ($fileDocComment) {
834-
if (strpos($fileDocComment->getText(), '@generate-function-entries') !== false) {
835-
$generateFunctionEntries = true;
836-
}
837-
}
838-
839-
$funcInfos = [];
840-
$classInfos = [];
864+
function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstract $prettyPrinter) {
841865
$conds = [];
842866
foreach ($stmts as $stmt) {
843-
$cond = handlePreprocessorConditions($conds, $stmt);
844867
if ($stmt instanceof Stmt\Nop) {
845868
continue;
846869
}
847870

871+
if ($stmt instanceof Stmt\Namespace_) {
872+
handleStatements($fileInfo, $stmt->stmts, $prettyPrinter);
873+
continue;
874+
}
875+
876+
$cond = handlePreprocessorConditions($conds, $stmt);
848877
if ($stmt instanceof Stmt\Function_) {
849-
$funcInfos[] = parseFunctionLike(
878+
$fileInfo->funcInfos[] = parseFunctionLike(
850879
$prettyPrinter,
851-
new FunctionName(null, $stmt->name->toString()),
880+
new FunctionName($stmt->namespacedName),
852881
0,
853882
$stmt,
854883
$cond
855884
);
856-
857885
continue;
858886
}
859887

@@ -881,21 +909,45 @@ protected function pName_FullyQualified(Name\FullyQualified $node) {
881909

882910
$methodInfos[] = parseFunctionLike(
883911
$prettyPrinter,
884-
new FunctionName($className, $classStmt->name->toString()),
912+
new MethodName($className, $classStmt->name->toString()),
885913
$flags,
886914
$classStmt,
887915
$cond
888916
);
889917
}
890918

891-
$classInfos[] = new ClassInfo($className, $methodInfos);
919+
$fileInfo->classInfos[] = new ClassInfo($className, $methodInfos);
892920
continue;
893921
}
894922

895923
throw new Exception("Unexpected node {$stmt->getType()}");
896924
}
925+
}
926+
927+
function parseStubFile(string $code): FileInfo {
928+
$lexer = new PhpParser\Lexer();
929+
$parser = new PhpParser\Parser\Php7($lexer);
930+
$nodeTraverser = new PhpParser\NodeTraverser;
931+
$nodeTraverser->addVisitor(new PhpParser\NodeVisitor\NameResolver);
932+
$prettyPrinter = new class extends Standard {
933+
protected function pName_FullyQualified(Name\FullyQualified $node) {
934+
return implode('\\', $node->parts);
935+
}
936+
};
937+
938+
$stmts = $parser->parse($code);
939+
$nodeTraverser->traverse($stmts);
940+
941+
$fileInfo = new FileInfo;
942+
$fileDocComment = getFileDocComment($stmts);
943+
if ($fileDocComment) {
944+
if (strpos($fileDocComment->getText(), '@generate-function-entries') !== false) {
945+
$fileInfo->generateFunctionEntries = true;
946+
}
947+
}
897948

898-
return new FileInfo($funcInfos, $classInfos, $generateFunctionEntries);
949+
handleStatements($fileInfo, $stmts, $prettyPrinter);
950+
return $fileInfo;
899951
}
900952

901953
function funcInfoToCode(FuncInfo $funcInfo): string {

0 commit comments

Comments
 (0)