11#!/usr/bin/env php
22<?php declare (strict_types=1 );
33
4+ use PhpParser \Comment \Doc as DocComment ;
45use PhpParser \Node ;
56use PhpParser \Node \Expr ;
67use PhpParser \Node \Stmt ;
@@ -46,8 +47,8 @@ function processStubFile(string $stubFile) {
4647 $ arginfoFile = str_replace ('.stub.php ' , '' , $ stubFile ) . '_arginfo.h ' ;
4748
4849 try {
49- $ funcInfos = parseStubFile ($ stubFile );
50- $ arginfoCode = generateArgInfoCode ($ funcInfos );
50+ $ fileInfo = parseStubFile ($ stubFile );
51+ $ arginfoCode = generateArgInfoCode ($ fileInfo );
5152 file_put_contents ($ arginfoFile , $ arginfoCode );
5253 } catch (Exception $ e ) {
5354 echo "In $ stubFile: \n{$ e ->getMessage ()}\n" ;
@@ -299,6 +300,10 @@ public function equals(ReturnInfo $other): bool {
299300class FuncInfo {
300301 /** @var string */
301302 public $ name ;
303+ /** @var ?string */
304+ public $ className ;
305+ /** @var ?string */
306+ public $ alias ;
302307 /** @var ArgInfo[] */
303308 public $ args ;
304309 /** @var ReturnInfo */
@@ -309,9 +314,12 @@ class FuncInfo {
309314 public $ cond ;
310315
311316 public function __construct (
312- string $ name , array $ args , ReturnInfo $ return , int $ numRequiredArgs , ?string $ cond
317+ string $ name , ?string $ className , ?string $ alias , array $ args , ReturnInfo $ return ,
318+ int $ numRequiredArgs , ?string $ cond
313319 ) {
314320 $ this ->name = $ name ;
321+ $ this ->className = $ className ;
322+ $ this ->alias = $ alias ;
315323 $ this ->args = $ args ;
316324 $ this ->return = $ return ;
317325 $ this ->numRequiredArgs = $ numRequiredArgs ;
@@ -333,11 +341,33 @@ public function equalsApartFromName(FuncInfo $other): bool {
333341 && $ this ->numRequiredArgs === $ other ->numRequiredArgs
334342 && $ this ->cond === $ other ->cond ;
335343 }
344+
345+ public function getArgInfoName (): string {
346+ if ($ this ->className ) {
347+ return 'arginfo_class_ ' . $ this ->className . '_ ' . $ this ->name ;
348+ }
349+ return 'arginfo_ ' . $ this ->name ;
350+ }
351+ }
352+
353+ class FileInfo {
354+ /** @var FuncInfo[] */
355+ public $ funcInfos ;
356+ /** @var bool */
357+ public $ generateFunctionEntries ;
358+
359+ public function __construct (array $ funcInfos , bool $ generateFunctionEntries ) {
360+ $ this ->funcInfos = $ funcInfos ;
361+ $ this ->generateFunctionEntries = $ generateFunctionEntries ;
362+ }
336363}
337364
338- function parseFunctionLike (string $ name , Node \FunctionLike $ func , ?string $ cond ): FuncInfo {
365+ function parseFunctionLike (
366+ string $ name , ?string $ className , Node \FunctionLike $ func , ?string $ cond
367+ ): FuncInfo {
339368 $ comment = $ func ->getDocComment ();
340369 $ paramMeta = [];
370+ $ alias = null ;
341371
342372 if ($ comment ) {
343373 $ commentText = substr ($ comment ->getText (), 2 , -2 );
@@ -349,6 +379,8 @@ function parseFunctionLike(string $name, Node\FunctionLike $func, ?string $cond)
349379 $ paramMeta [$ varName ] = [];
350380 }
351381 $ paramMeta [$ varName ]['preferRef ' ] = true ;
382+ } else if (preg_match ('/^\*\s*@alias\s+(.+)$/ ' , trim ($ commentLine ), $ matches )) {
383+ $ alias = $ matches [1 ];
352384 }
353385 }
354386 }
@@ -403,7 +435,7 @@ function parseFunctionLike(string $name, Node\FunctionLike $func, ?string $cond)
403435 $ return = new ReturnInfo (
404436 $ func ->returnsByRef (),
405437 $ returnType ? Type::fromNode ($ returnType ) : null );
406- return new FuncInfo ($ name , $ args , $ return , $ numRequiredArgs , $ cond );
438+ return new FuncInfo ($ name , $ className , $ alias , $ args , $ return , $ numRequiredArgs , $ cond );
407439}
408440
409441function handlePreprocessorConditions (array &$ conds , Stmt $ stmt ): ?string {
@@ -434,8 +466,24 @@ function handlePreprocessorConditions(array &$conds, Stmt $stmt): ?string {
434466 return empty ($ conds ) ? null : implode (' && ' , $ conds );
435467}
436468
437- /** @return FuncInfo[] */
438- function parseStubFile (string $ fileName ) {
469+ function getFileDocComment (array $ stmts ): ?DocComment {
470+ if (empty ($ stmts )) {
471+ return null ;
472+ }
473+
474+ $ comments = $ stmts [0 ]->getComments ();
475+ if (empty ($ comments )) {
476+ return null ;
477+ }
478+
479+ if ($ comments [0 ] instanceof DocComment) {
480+ return $ comments [0 ];
481+ }
482+
483+ return null ;
484+ }
485+
486+ function parseStubFile (string $ fileName ): FileInfo {
439487 if (!file_exists ($ fileName )) {
440488 throw new Exception ("File $ fileName does not exist " );
441489 }
@@ -450,6 +498,14 @@ function parseStubFile(string $fileName) {
450498 $ stmts = $ parser ->parse ($ code );
451499 $ nodeTraverser ->traverse ($ stmts );
452500
501+ $ generateFunctionEntries = false ;
502+ $ fileDocComment = getFileDocComment ($ stmts );
503+ if ($ fileDocComment ) {
504+ if (strpos ($ fileDocComment ->getText (), '@generate-function-entries ' ) !== false ) {
505+ $ generateFunctionEntries = true ;
506+ }
507+ }
508+
453509 $ funcInfos = [];
454510 $ conds = [];
455511 foreach ($ stmts as $ stmt ) {
@@ -459,7 +515,7 @@ function parseStubFile(string $fileName) {
459515 }
460516
461517 if ($ stmt instanceof Stmt \Function_) {
462- $ funcInfos [] = parseFunctionLike ($ stmt ->name ->toString (), $ stmt , $ cond );
518+ $ funcInfos [] = parseFunctionLike ($ stmt ->name ->toString (), null , $ stmt , $ cond );
463519 continue ;
464520 }
465521
@@ -476,15 +532,15 @@ function parseStubFile(string $fileName) {
476532 }
477533
478534 $ funcInfos [] = parseFunctionLike (
479- ' class_ ' . $ className . ' _ ' . $ classStmt ->name ->toString (), $ classStmt , $ cond );
535+ $ classStmt ->name ->toString (), $ className , $ classStmt , $ cond );
480536 }
481537 continue ;
482538 }
483539
484540 throw new Exception ("Unexpected node {$ stmt ->getType ()}" );
485541 }
486542
487- return $ funcInfos ;
543+ return new FileInfo ( $ funcInfos, $ generateFunctionEntries ) ;
488544}
489545
490546function funcInfoToCode (FuncInfo $ funcInfo ): string {
@@ -494,28 +550,32 @@ function funcInfoToCode(FuncInfo $funcInfo): string {
494550 if (null !== $ simpleReturnType = $ returnType ->tryToSimpleType ()) {
495551 if ($ simpleReturnType ->isBuiltin ) {
496552 $ code .= sprintf (
497- "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_%s, %d, %d, %s, %d) \n" ,
498- $ funcInfo ->name , $ funcInfo ->return ->byRef , $ funcInfo ->numRequiredArgs ,
553+ "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(%s, %d, %d, %s, %d) \n" ,
554+ $ funcInfo ->getArgInfoName (), $ funcInfo ->return ->byRef ,
555+ $ funcInfo ->numRequiredArgs ,
499556 $ simpleReturnType ->toTypeCode (), $ returnType ->isNullable ()
500557 );
501558 } else {
502559 $ code .= sprintf (
503- "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_%s, %d, %d, %s, %d) \n" ,
504- $ funcInfo ->name , $ funcInfo ->return ->byRef , $ funcInfo ->numRequiredArgs ,
560+ "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(%s, %d, %d, %s, %d) \n" ,
561+ $ funcInfo ->getArgInfoName (), $ funcInfo ->return ->byRef ,
562+ $ funcInfo ->numRequiredArgs ,
505563 $ simpleReturnType ->toEscapedName (), $ returnType ->isNullable ()
506564 );
507565 }
508566 } else if (null !== $ representableType = $ returnType ->tryToRepresentableType ()) {
509567 if ($ representableType ->classType !== null ) {
510568 $ code .= sprintf (
511- "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_%s, %d, %d, %s, %s) \n" ,
512- $ funcInfo ->name , $ funcInfo ->return ->byRef , $ funcInfo ->numRequiredArgs ,
569+ "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(%s, %d, %d, %s, %s) \n" ,
570+ $ funcInfo ->getArgInfoName (), $ funcInfo ->return ->byRef ,
571+ $ funcInfo ->numRequiredArgs ,
513572 $ representableType ->classType ->toEscapedName (), $ representableType ->toTypeMask ()
514573 );
515574 } else {
516575 $ code .= sprintf (
517- "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_%s, %d, %d, %s) \n" ,
518- $ funcInfo ->name , $ funcInfo ->return ->byRef , $ funcInfo ->numRequiredArgs ,
576+ "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(%s, %d, %d, %s) \n" ,
577+ $ funcInfo ->getArgInfoName (), $ funcInfo ->return ->byRef ,
578+ $ funcInfo ->numRequiredArgs ,
519579 $ representableType ->toTypeMask ()
520580 );
521581 }
@@ -524,8 +584,8 @@ function funcInfoToCode(FuncInfo $funcInfo): string {
524584 }
525585 } else {
526586 $ code .= sprintf (
527- "ZEND_BEGIN_ARG_INFO_EX(arginfo_ %s, 0, %d, %d) \n" ,
528- $ funcInfo ->name , $ funcInfo ->return ->byRef , $ funcInfo ->numRequiredArgs
587+ "ZEND_BEGIN_ARG_INFO_EX(%s, 0, %d, %d) \n" ,
588+ $ funcInfo ->getArgInfoName () , $ funcInfo ->return ->byRef , $ funcInfo ->numRequiredArgs
529589 );
530590 }
531591
@@ -566,7 +626,7 @@ function funcInfoToCode(FuncInfo $funcInfo): string {
566626 }
567627
568628 $ code .= "ZEND_END_ARG_INFO() " ;
569- return $ code ;
629+ return $ code . "\n" ;
570630}
571631
572632function findEquivalentFuncInfo (array $ generatedFuncInfos , $ funcInfo ): ?FuncInfo {
@@ -578,33 +638,80 @@ function findEquivalentFuncInfo(array $generatedFuncInfos, $funcInfo): ?FuncInfo
578638 return null ;
579639}
580640
581- /** @param FuncInfo[] $funcInfos */
582- function generateArginfoCode (array $ funcInfos ): string {
583- $ code = "/* This is a generated file, edit the .stub.php file instead. */ " ;
584- $ generatedFuncInfos = [];
585- foreach ($ funcInfos as $ funcInfo ) {
586- $ code .= "\n\n" ;
587- if ($ funcInfo ->cond ) {
588- $ code .= "#if {$ funcInfo ->cond }\n" ;
641+ function generateCodeWithConditions (
642+ FileInfo $ fileInfo , string $ separator , Closure $ codeGenerator ): string {
643+ $ code = "" ;
644+ foreach ($ fileInfo ->funcInfos as $ funcInfo ) {
645+ $ funcCode = $ codeGenerator ($ funcInfo );
646+ if ($ funcCode === null ) {
647+ continue ;
589648 }
590649
591- /* If there already is an equivalent arginfo structure, only emit a #define */
592- if ($ generatedFuncInfo = findEquivalentFuncInfo ($ generatedFuncInfos , $ funcInfo )) {
593- $ code .= sprintf (
594- "#define arginfo_%s arginfo_%s " ,
595- $ funcInfo ->name , $ generatedFuncInfo ->name
596- );
650+ $ code .= $ separator ;
651+ if ($ funcInfo ->cond ) {
652+ $ code .= "#if {$ funcInfo ->cond }\n" ;
653+ $ code .= $ funcCode ;
654+ $ code .= "#endif \n" ;
597655 } else {
598- $ code .= funcInfoToCode ( $ funcInfo ) ;
656+ $ code .= $ funcCode ;
599657 }
658+ }
659+ return $ code ;
660+ }
600661
601- if ($ funcInfo ->cond ) {
602- $ code .= "\n#endif " ;
662+ function generateArgInfoCode (FileInfo $ fileInfo ): string {
663+ $ funcInfos = $ fileInfo ->funcInfos ;
664+
665+ $ code = "/* This is a generated file, edit the .stub.php file instead. */ \n" ;
666+ $ generatedFuncInfos = [];
667+ $ code .= generateCodeWithConditions (
668+ $ fileInfo , "\n" ,
669+ function (FuncInfo $ funcInfo ) use (&$ generatedFuncInfos ) {
670+ /* If there already is an equivalent arginfo structure, only emit a #define */
671+ if ($ generatedFuncInfo = findEquivalentFuncInfo ($ generatedFuncInfos , $ funcInfo )) {
672+ $ code = sprintf (
673+ "#define %s %s \n" ,
674+ $ funcInfo ->getArgInfoName (), $ generatedFuncInfo ->getArgInfoName ()
675+ );
676+ } else {
677+ $ code = funcInfoToCode ($ funcInfo );
678+ }
679+
680+ $ generatedFuncInfos [] = $ funcInfo ;
681+ return $ code ;
603682 }
683+ );
684+
685+ if ($ fileInfo ->generateFunctionEntries ) {
686+ $ code .= "\n\n" ;
687+ $ code .= generateCodeWithConditions ($ fileInfo , "" , function (FuncInfo $ funcInfo ) {
688+ if ($ funcInfo ->className || $ funcInfo ->alias ) {
689+ return null ;
690+ }
691+
692+ return "ZEND_FUNCTION( $ funcInfo ->name ); \n" ;
693+ });
694+
695+ $ code .= "\n\nstatic const zend_function_entry ext_functions[] = { \n" ;
696+ $ code .= generateCodeWithConditions ($ fileInfo , "" , function (FuncInfo $ funcInfo ) {
697+ if ($ funcInfo ->className ) {
698+ return null ;
699+ }
604700
605- $ generatedFuncInfos [] = $ funcInfo ;
701+ if ($ funcInfo ->alias ) {
702+ return sprintf (
703+ "\tZEND_FALIAS(%s, %s, %s) \n" ,
704+ $ funcInfo ->name , $ funcInfo ->alias , $ funcInfo ->getArgInfoName ()
705+ );
706+ } else {
707+ return sprintf ("\tZEND_FE(%s, %s) \n" , $ funcInfo ->name , $ funcInfo ->getArgInfoName ());
708+ }
709+ });
710+ $ code .= "\tZEND_FE_END \n" ;
711+ $ code .= "}; \n" ;
606712 }
607- return $ code . "\n" ;
713+
714+ return $ code ;
608715}
609716
610717function initPhpParser () {
0 commit comments