1
1
#!/usr/bin/env php
2
2
<?php declare (strict_types=1 );
3
3
4
+ use PhpParser \Comment \Doc as DocComment ;
4
5
use PhpParser \Node ;
5
6
use PhpParser \Node \Expr ;
6
7
use PhpParser \Node \Stmt ;
@@ -46,8 +47,8 @@ function processStubFile(string $stubFile) {
46
47
$ arginfoFile = str_replace ('.stub.php ' , '' , $ stubFile ) . '_arginfo.h ' ;
47
48
48
49
try {
49
- $ funcInfos = parseStubFile ($ stubFile );
50
- $ arginfoCode = generateArgInfoCode ($ funcInfos );
50
+ $ fileInfo = parseStubFile ($ stubFile );
51
+ $ arginfoCode = generateArgInfoCode ($ fileInfo );
51
52
file_put_contents ($ arginfoFile , $ arginfoCode );
52
53
} catch (Exception $ e ) {
53
54
echo "In $ stubFile: \n{$ e ->getMessage ()}\n" ;
@@ -299,6 +300,10 @@ public function equals(ReturnInfo $other): bool {
299
300
class FuncInfo {
300
301
/** @var string */
301
302
public $ name ;
303
+ /** @var ?string */
304
+ public $ className ;
305
+ /** @var ?string */
306
+ public $ alias ;
302
307
/** @var ArgInfo[] */
303
308
public $ args ;
304
309
/** @var ReturnInfo */
@@ -309,9 +314,12 @@ class FuncInfo {
309
314
public $ cond ;
310
315
311
316
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
313
319
) {
314
320
$ this ->name = $ name ;
321
+ $ this ->className = $ className ;
322
+ $ this ->alias = $ alias ;
315
323
$ this ->args = $ args ;
316
324
$ this ->return = $ return ;
317
325
$ this ->numRequiredArgs = $ numRequiredArgs ;
@@ -333,11 +341,33 @@ public function equalsApartFromName(FuncInfo $other): bool {
333
341
&& $ this ->numRequiredArgs === $ other ->numRequiredArgs
334
342
&& $ this ->cond === $ other ->cond ;
335
343
}
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
+ }
336
363
}
337
364
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 {
339
368
$ comment = $ func ->getDocComment ();
340
369
$ paramMeta = [];
370
+ $ alias = null ;
341
371
342
372
if ($ comment ) {
343
373
$ commentText = substr ($ comment ->getText (), 2 , -2 );
@@ -349,6 +379,8 @@ function parseFunctionLike(string $name, Node\FunctionLike $func, ?string $cond)
349
379
$ paramMeta [$ varName ] = [];
350
380
}
351
381
$ paramMeta [$ varName ]['preferRef ' ] = true ;
382
+ } else if (preg_match ('/^\*\s*@alias\s+(.+)$/ ' , trim ($ commentLine ), $ matches )) {
383
+ $ alias = $ matches [1 ];
352
384
}
353
385
}
354
386
}
@@ -403,7 +435,7 @@ function parseFunctionLike(string $name, Node\FunctionLike $func, ?string $cond)
403
435
$ return = new ReturnInfo (
404
436
$ func ->returnsByRef (),
405
437
$ 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 );
407
439
}
408
440
409
441
function handlePreprocessorConditions (array &$ conds , Stmt $ stmt ): ?string {
@@ -434,8 +466,24 @@ function handlePreprocessorConditions(array &$conds, Stmt $stmt): ?string {
434
466
return empty ($ conds ) ? null : implode (' && ' , $ conds );
435
467
}
436
468
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 {
439
487
if (!file_exists ($ fileName )) {
440
488
throw new Exception ("File $ fileName does not exist " );
441
489
}
@@ -450,6 +498,14 @@ function parseStubFile(string $fileName) {
450
498
$ stmts = $ parser ->parse ($ code );
451
499
$ nodeTraverser ->traverse ($ stmts );
452
500
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
+
453
509
$ funcInfos = [];
454
510
$ conds = [];
455
511
foreach ($ stmts as $ stmt ) {
@@ -459,7 +515,7 @@ function parseStubFile(string $fileName) {
459
515
}
460
516
461
517
if ($ stmt instanceof Stmt \Function_) {
462
- $ funcInfos [] = parseFunctionLike ($ stmt ->name ->toString (), $ stmt , $ cond );
518
+ $ funcInfos [] = parseFunctionLike ($ stmt ->name ->toString (), null , $ stmt , $ cond );
463
519
continue ;
464
520
}
465
521
@@ -476,15 +532,15 @@ function parseStubFile(string $fileName) {
476
532
}
477
533
478
534
$ funcInfos [] = parseFunctionLike (
479
- ' class_ ' . $ className . ' _ ' . $ classStmt ->name ->toString (), $ classStmt , $ cond );
535
+ $ classStmt ->name ->toString (), $ className , $ classStmt , $ cond );
480
536
}
481
537
continue ;
482
538
}
483
539
484
540
throw new Exception ("Unexpected node {$ stmt ->getType ()}" );
485
541
}
486
542
487
- return $ funcInfos ;
543
+ return new FileInfo ( $ funcInfos, $ generateFunctionEntries ) ;
488
544
}
489
545
490
546
function funcInfoToCode (FuncInfo $ funcInfo ): string {
@@ -494,28 +550,32 @@ function funcInfoToCode(FuncInfo $funcInfo): string {
494
550
if (null !== $ simpleReturnType = $ returnType ->tryToSimpleType ()) {
495
551
if ($ simpleReturnType ->isBuiltin ) {
496
552
$ 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 ,
499
556
$ simpleReturnType ->toTypeCode (), $ returnType ->isNullable ()
500
557
);
501
558
} else {
502
559
$ 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 ,
505
563
$ simpleReturnType ->toEscapedName (), $ returnType ->isNullable ()
506
564
);
507
565
}
508
566
} else if (null !== $ representableType = $ returnType ->tryToRepresentableType ()) {
509
567
if ($ representableType ->classType !== null ) {
510
568
$ 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 ,
513
572
$ representableType ->classType ->toEscapedName (), $ representableType ->toTypeMask ()
514
573
);
515
574
} else {
516
575
$ 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 ,
519
579
$ representableType ->toTypeMask ()
520
580
);
521
581
}
@@ -524,8 +584,8 @@ function funcInfoToCode(FuncInfo $funcInfo): string {
524
584
}
525
585
} else {
526
586
$ 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
529
589
);
530
590
}
531
591
@@ -566,7 +626,7 @@ function funcInfoToCode(FuncInfo $funcInfo): string {
566
626
}
567
627
568
628
$ code .= "ZEND_END_ARG_INFO() " ;
569
- return $ code ;
629
+ return $ code . "\n" ;
570
630
}
571
631
572
632
function findEquivalentFuncInfo (array $ generatedFuncInfos , $ funcInfo ): ?FuncInfo {
@@ -578,33 +638,80 @@ function findEquivalentFuncInfo(array $generatedFuncInfos, $funcInfo): ?FuncInfo
578
638
return null ;
579
639
}
580
640
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 ;
589
648
}
590
649
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" ;
597
655
} else {
598
- $ code .= funcInfoToCode ( $ funcInfo ) ;
656
+ $ code .= $ funcCode ;
599
657
}
658
+ }
659
+ return $ code ;
660
+ }
600
661
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 ;
603
682
}
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
+ }
604
700
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" ;
606
712
}
607
- return $ code . "\n" ;
713
+
714
+ return $ code ;
608
715
}
609
716
610
717
function initPhpParser () {
0 commit comments