@@ -32,7 +32,10 @@ function processStubFile(string $stubFile, Context $context) {
32
32
}
33
33
34
34
$ arginfoFile = str_replace ('.stub.php ' , '_arginfo.h ' , $ stubFile );
35
- $ legacyFile = str_replace ('.stub.php ' , '_legacy_arginfo.h ' , $ stubFile );
35
+ $ legacyArginfoFile = str_replace ('.stub.php ' , '_legacy_arginfo.h ' , $ stubFile );
36
+
37
+ $ propertyDeclarationsFile = str_replace ('.stub.php ' , '_properties.h ' , $ stubFile );
38
+ $ legacyPropertyDeclarationsFile = str_replace ('.stub.php ' , '_legacy_properties.h ' , $ stubFile );
36
39
37
40
$ stubCode = file_get_contents ($ stubFile );
38
41
$ stubHash = computeStubHash ($ stubCode );
@@ -54,8 +57,26 @@ function processStubFile(string $stubFile, Context $context) {
54
57
$ funcInfo ->discardInfoForOldPhpVersions ();
55
58
}
56
59
$ arginfoCode = generateArgInfoCode ($ fileInfo , $ stubHash );
57
- if (file_put_contents ($ legacyFile , $ arginfoCode )) {
58
- echo "Saved $ legacyFile \n" ;
60
+ if (file_put_contents ($ legacyArginfoFile , $ arginfoCode )) {
61
+ echo "Saved $ legacyArginfoFile \n" ;
62
+ }
63
+ }
64
+
65
+ if ($ fileInfo ->generatePropertyDeclarations ) {
66
+ $ propertyDeclarationsCode = generatePropertyDeclarationsCode ($ fileInfo , $ stubHash );
67
+ if (file_put_contents ($ propertyDeclarationsFile , $ propertyDeclarationsCode )) {
68
+ echo "Saved $ propertyDeclarationsFile \n" ;
69
+ }
70
+ }
71
+
72
+ if ($ fileInfo ->generateLegacyPropertyDeclarations ) {
73
+ foreach ($ fileInfo ->getAllPropertyInfos () as $ propertyInfo ) {
74
+ $ propertyInfo ->discardInfoForOldPhpVersions ();
75
+ }
76
+
77
+ $ propertyDeclarationsCode = generateArgInfoCode ($ fileInfo , $ stubHash );
78
+ if (file_put_contents ($ legacyPropertyDeclarationsFile , $ propertyDeclarationsCode )) {
79
+ echo "Saved $ legacyPropertyDeclarationsFile \n" ;
59
80
}
60
81
}
61
82
@@ -431,7 +452,7 @@ public function equals(ReturnInfo $other): bool {
431
452
}
432
453
}
433
454
434
- class FuncInfo {
455
+ class FuncInfo extends MemberInfo {
435
456
/** @var FunctionOrMethodName */
436
457
public $ name ;
437
458
/** @var int */
@@ -462,8 +483,8 @@ public function __construct(
462
483
int $ numRequiredArgs ,
463
484
?string $ cond
464
485
) {
486
+ parent ::__construct ($ flags );
465
487
$ this ->name = $ name ;
466
- $ this ->flags = $ flags ;
467
488
$ this ->aliasType = $ aliasType ;
468
489
$ this ->alias = $ alias ;
469
490
$ this ->isDeprecated = $ isDeprecated ;
@@ -575,7 +596,60 @@ public function getFunctionEntry(): string {
575
596
}
576
597
}
577
598
578
- private function getFlagsAsString (): string
599
+ public function discardInfoForOldPhpVersions (): void {
600
+ $ this ->return ->type = null ;
601
+ foreach ($ this ->args as $ arg ) {
602
+ $ arg ->type = null ;
603
+ $ arg ->defaultValue = null ;
604
+ }
605
+ }
606
+ }
607
+
608
+ class PropertyInfo extends MemberInfo
609
+ {
610
+ /** @var string */
611
+ public $ name ;
612
+ /** @var bool */
613
+ public $ isKnownName ;
614
+ /** @var bool */
615
+ public $ isDeprecated ;
616
+ /** @var Type|null */
617
+ public $ type ;
618
+ /** @var string|null */
619
+ public $ value ;
620
+
621
+ public function __construct (string $ name , int $ flags , bool $ isKnownName , bool $ isDeprecated , ?Type $ type , ?string $ value )
622
+ {
623
+ parent ::__construct ($ flags );
624
+ $ this ->name = $ name ;
625
+ $ this ->isKnownName = $ isKnownName ;
626
+ $ this ->isDeprecated = $ isDeprecated ;
627
+ $ this ->type = $ type ;
628
+ $ this ->value = $ value ;
629
+ }
630
+
631
+ public function discardInfoForOldPhpVersions (): void {
632
+ $ this ->type = null ;
633
+ }
634
+
635
+ public function getDeclaration (): string {
636
+ $ code = "" ;
637
+ $ code .= " zend_declare_property_null(ce, \"$ this ->name \", sizeof( \"$ this ->name \") - 1, " . $ this ->getFlagsAsString () . ") \n" ;
638
+
639
+ return $ code ;
640
+ }
641
+ }
642
+
643
+ abstract class MemberInfo {
644
+ /** @var int */
645
+ public $ flags ;
646
+
647
+ public function __construct (int $ flags )
648
+ {
649
+ $ this ->flags = $ flags ;
650
+ }
651
+
652
+ protected function getFlagsAsString (): string
579
653
{
580
654
$ flags = "ZEND_ACC_PUBLIC " ;
581
655
if ($ this ->flags & Class_::MODIFIER_PROTECTED ) {
@@ -602,25 +676,20 @@ private function getFlagsAsString(): string
602
676
603
677
return $ flags ;
604
678
}
605
-
606
- public function discardInfoForOldPhpVersions (): void {
607
- $ this ->return ->type = null ;
608
- foreach ($ this ->args as $ arg ) {
609
- $ arg ->type = null ;
610
- $ arg ->defaultValue = null ;
611
- }
612
- }
613
679
}
614
680
615
681
class ClassInfo {
616
682
/** @var string */
617
683
public $ name ;
618
684
/** @var FuncInfo[] */
619
685
public $ funcInfos ;
686
+ /** @var PropertyInfo[] */
687
+ public $ propertyInfos ;
620
688
621
- public function __construct (string $ name , array $ funcInfos ) {
689
+ public function __construct (string $ name , array $ funcInfos, array $ propertyInfos ) {
622
690
$ this ->name = $ name ;
623
691
$ this ->funcInfos = $ funcInfos ;
692
+ $ this ->propertyInfos = $ propertyInfos ;
624
693
}
625
694
}
626
695
@@ -635,6 +704,10 @@ class FileInfo {
635
704
public $ declarationPrefix = "" ;
636
705
/** @var bool */
637
706
public $ generateLegacyArginfo = false ;
707
+ /** @var bool */
708
+ public $ generatePropertyDeclarations = false ;
709
+ /** @var bool */
710
+ public $ generateLegacyPropertyDeclarations = false ;
638
711
639
712
/**
640
713
* @return iterable<FuncInfo>
@@ -645,6 +718,15 @@ public function getAllFuncInfos(): iterable {
645
718
yield from $ classInfo ->funcInfos ;
646
719
}
647
720
}
721
+
722
+ /**
723
+ * @return iterable<PropertyInfo>
724
+ */
725
+ public function getAllPropertyInfos (): iterable {
726
+ foreach ($ this ->classInfos as $ classInfo ) {
727
+ yield from $ classInfo ->propertyInfos ;
728
+ }
729
+ }
648
730
}
649
731
650
732
class DocCommentTag {
@@ -827,6 +909,57 @@ function parseFunctionLike(
827
909
);
828
910
}
829
911
912
+ function parseProperty (
913
+ string $ class ,
914
+ PrettyPrinterAbstract $ prettyPrinter ,
915
+ int $ flags ,
916
+ Stmt \PropertyProperty $ property ,
917
+ ?Node $ type
918
+ ): PropertyInfo {
919
+ $ comment = $ property ->getDocComment ();
920
+ $ isDeprecated = false ;
921
+ $ isKnownName = false ;
922
+ $ docType = false ;
923
+
924
+ if ($ comment ) {
925
+ $ tags = parseDocComment ($ comment );
926
+ foreach ($ tags as $ tag ) {
927
+ if ($ tag ->name === 'deprecated ' ) {
928
+ $ isDeprecated = true ;
929
+ } else if ($ tag ->name === 'known ' ) {
930
+ $ isKnownName = true ;
931
+ } else if ($ tag ->name === 'var ' ) {
932
+ $ docType = true ;
933
+ }
934
+ }
935
+ }
936
+
937
+ $ propertyType = $ type ? Type::fromNode ($ type ) : null ;
938
+ if ($ propertyType === null && !$ docType ) {
939
+ //throw new Exception("Missing type for property $class::\$$property->name");
940
+ }
941
+
942
+ if ($ property ->default instanceof Expr \ConstFetch &&
943
+ $ property ->default ->name ->toLowerString () === "null " &&
944
+ $ propertyType && !$ propertyType ->isNullable ()
945
+ ) {
946
+ $ simpleType = $ propertyType ->tryToSimpleType ();
947
+ if ($ simpleType === null ) {
948
+ throw new Exception (
949
+ "Property $ class:: \$$ property ->name has null default, but is not nullable " );
950
+ }
951
+ }
952
+
953
+ return new PropertyInfo (
954
+ $ property ->name ->__toString (),
955
+ $ flags ,
956
+ $ isKnownName ,
957
+ $ isDeprecated ,
958
+ $ propertyType ,
959
+ $ property ->default ? $ prettyPrinter ->prettyPrintExpr ($ property ->default ) : null
960
+ );
961
+ }
962
+
830
963
function handlePreprocessorConditions (array &$ conds , Stmt $ stmt ): ?string {
831
964
foreach ($ stmt ->getComments () as $ comment ) {
832
965
$ text = trim ($ comment ->getText ());
@@ -899,13 +1032,14 @@ function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstrac
899
1032
if ($ stmt instanceof Stmt \ClassLike) {
900
1033
$ className = $ stmt ->name ->toString ();
901
1034
$ methodInfos = [];
1035
+ $ propertyInfos = [];
902
1036
foreach ($ stmt ->stmts as $ classStmt ) {
903
1037
$ cond = handlePreprocessorConditions ($ conds , $ classStmt );
904
1038
if ($ classStmt instanceof Stmt \Nop) {
905
1039
continue ;
906
1040
}
907
1041
908
- if (!$ classStmt instanceof Stmt \ClassMethod) {
1042
+ if (!$ classStmt instanceof Stmt \ClassMethod && ! $ classStmt instanceof Stmt \Property ) {
909
1043
throw new Exception ("Not implemented {$ classStmt ->getType ()}" );
910
1044
}
911
1045
@@ -915,19 +1049,32 @@ function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstrac
915
1049
}
916
1050
917
1051
if (!($ flags & Class_::VISIBILITY_MODIFIER_MASK )) {
918
- throw new Exception ("Method visibility modifier is required " );
1052
+ throw new Exception ("Visibility modifier is required " );
919
1053
}
920
1054
921
- $ methodInfos [] = parseFunctionLike (
922
- $ prettyPrinter ,
923
- new MethodName ($ className , $ classStmt ->name ->toString ()),
924
- $ flags ,
925
- $ classStmt ,
926
- $ cond
927
- );
1055
+ if ($ classStmt instanceof Stmt \Property) {
1056
+ foreach ($ classStmt ->props as $ property ) {
1057
+ $ propertyInfos [] = parseProperty (
1058
+ $ className ,
1059
+ $ prettyPrinter ,
1060
+ $ flags ,
1061
+ $ property ,
1062
+ $ classStmt ->type
1063
+ );
1064
+ }
1065
+
1066
+ } else if ($ classStmt instanceof Stmt \ClassMethod) {
1067
+ $ methodInfos [] = parseFunctionLike (
1068
+ $ prettyPrinter ,
1069
+ new MethodName ($ className , $ classStmt ->name ->toString ()),
1070
+ $ flags ,
1071
+ $ classStmt ,
1072
+ $ cond
1073
+ );
1074
+ }
928
1075
}
929
1076
930
- $ fileInfo ->classInfos [] = new ClassInfo ($ className , $ methodInfos );
1077
+ $ fileInfo ->classInfos [] = new ClassInfo ($ className , $ methodInfos, $ propertyInfos );
931
1078
continue ;
932
1079
}
933
1080
@@ -959,6 +1106,10 @@ protected function pName_FullyQualified(Name\FullyQualified $node) {
959
1106
$ fileInfo ->declarationPrefix = $ tag ->value ? $ tag ->value . " " : "" ;
960
1107
} else if ($ tag ->name === 'generate-legacy-arginfo ' ) {
961
1108
$ fileInfo ->generateLegacyArginfo = true ;
1109
+ } else if ($ tag ->name === 'generate-property-declarations ' ) {
1110
+ $ fileInfo ->generatePropertyDeclarations = true ;
1111
+ } else if ($ tag ->name === 'generate-legacy-property-declarations ' ) {
1112
+ $ fileInfo ->generateLegacyPropertyDeclarations = true ;
962
1113
}
963
1114
}
964
1115
}
@@ -1150,6 +1301,21 @@ function (FuncInfo $funcInfo) use($fileInfo, &$generatedFunctionDeclarations) {
1150
1301
return $ code ;
1151
1302
}
1152
1303
1304
+ function generatePropertyDeclarationsCode (FileInfo $ fileInfo , string $ stubHash ): string {
1305
+ $ code = "/* This is a generated file, edit the .stub.php file instead. \n"
1306
+ . " * Stub hash: $ stubHash */ \n\n" ;
1307
+
1308
+ foreach ($ fileInfo ->classInfos as $ class ) {
1309
+ $ code .= "void declare_class_ {$ class ->name }_properties(zend_class_entry *ce) { \n" ;
1310
+ foreach ($ class ->propertyInfos as $ property ) {
1311
+ $ code .= $ property ->getDeclaration ();
1312
+ }
1313
+ $ code .= "} \n" ;
1314
+ }
1315
+
1316
+ return $ code ;
1317
+ }
1318
+
1153
1319
/** @param FuncInfo[] $funcInfos */
1154
1320
function generateFunctionEntries (?string $ className , array $ funcInfos ): string {
1155
1321
$ code = "" ;
0 commit comments