@@ -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
@@ -453,7 +474,7 @@ public function equals(ReturnInfo $other): bool {
453
474
}
454
475
}
455
476
456
- class FuncInfo {
477
+ class FuncInfo extends MemberInfo {
457
478
/** @var FunctionOrMethodName */
458
479
public $ name ;
459
480
/** @var int */
@@ -484,8 +505,8 @@ public function __construct(
484
505
int $ numRequiredArgs ,
485
506
?string $ cond
486
507
) {
508
+ parent ::__construct ($ flags );
487
509
$ this ->name = $ name ;
488
- $ this ->flags = $ flags ;
489
510
$ this ->aliasType = $ aliasType ;
490
511
$ this ->alias = $ alias ;
491
512
$ this ->isDeprecated = $ isDeprecated ;
@@ -602,7 +623,60 @@ public function getFunctionEntry(): string {
602
623
}
603
624
}
604
625
605
- private function getFlagsAsString (): string
626
+ public function discardInfoForOldPhpVersions (): void {
627
+ $ this ->return ->type = null ;
628
+ foreach ($ this ->args as $ arg ) {
629
+ $ arg ->type = null ;
630
+ $ arg ->defaultValue = null ;
631
+ }
632
+ }
633
+ }
634
+
635
+ class PropertyInfo extends MemberInfo
636
+ {
637
+ /** @var string */
638
+ public $ name ;
639
+ /** @var bool */
640
+ public $ isKnownName ;
641
+ /** @var bool */
642
+ public $ isDeprecated ;
643
+ /** @var Type|null */
644
+ public $ type ;
645
+ /** @var string|null */
646
+ public $ value ;
647
+
648
+ public function __construct (string $ name , int $ flags , bool $ isKnownName , bool $ isDeprecated , ?Type $ type , ?string $ value )
649
+ {
650
+ parent ::__construct ($ flags );
651
+ $ this ->name = $ name ;
652
+ $ this ->isKnownName = $ isKnownName ;
653
+ $ this ->isDeprecated = $ isDeprecated ;
654
+ $ this ->type = $ type ;
655
+ $ this ->value = $ value ;
656
+ }
657
+
658
+ public function discardInfoForOldPhpVersions (): void {
659
+ $ this ->type = null ;
660
+ }
661
+
662
+ public function getDeclaration (): string {
663
+ $ code = "" ;
664
+ $ code .= " zend_declare_property_null(ce, \"$ this ->name \", sizeof( \"$ this ->name \") - 1, " . $ this ->getFlagsAsString () . ") \n" ;
665
+
666
+ return $ code ;
667
+ }
668
+ }
669
+
670
+ abstract class MemberInfo {
671
+ /** @var int */
672
+ public $ flags ;
673
+
674
+ public function __construct (int $ flags )
675
+ {
676
+ $ this ->flags = $ flags ;
677
+ }
678
+
679
+ protected function getFlagsAsString (): string
606
680
{
607
681
$ flags = "ZEND_ACC_PUBLIC " ;
608
682
if ($ this ->flags & Class_::MODIFIER_PROTECTED ) {
@@ -629,25 +703,20 @@ private function getFlagsAsString(): string
629
703
630
704
return $ flags ;
631
705
}
632
-
633
- public function discardInfoForOldPhpVersions (): void {
634
- $ this ->return ->type = null ;
635
- foreach ($ this ->args as $ arg ) {
636
- $ arg ->type = null ;
637
- $ arg ->defaultValue = null ;
638
- }
639
- }
640
706
}
641
707
642
708
class ClassInfo {
643
709
/** @var Name */
644
710
public $ name ;
645
711
/** @var FuncInfo[] */
646
712
public $ funcInfos ;
713
+ /** @var PropertyInfo[] */
714
+ public $ propertyInfos ;
647
715
648
- public function __construct (Name $ name , array $ funcInfos ) {
716
+ public function __construct (Name $ name , array $ funcInfos, array $ propertyInfos ) {
649
717
$ this ->name = $ name ;
650
718
$ this ->funcInfos = $ funcInfos ;
719
+ $ this ->propertyInfos = $ propertyInfos ;
651
720
}
652
721
}
653
722
@@ -662,6 +731,10 @@ class FileInfo {
662
731
public $ declarationPrefix = "" ;
663
732
/** @var bool */
664
733
public $ generateLegacyArginfo = false ;
734
+ /** @var bool */
735
+ public $ generatePropertyDeclarations = false ;
736
+ /** @var bool */
737
+ public $ generateLegacyPropertyDeclarations = false ;
665
738
666
739
/**
667
740
* @return iterable<FuncInfo>
@@ -672,6 +745,15 @@ public function getAllFuncInfos(): iterable {
672
745
yield from $ classInfo ->funcInfos ;
673
746
}
674
747
}
748
+
749
+ /**
750
+ * @return iterable<PropertyInfo>
751
+ */
752
+ public function getAllPropertyInfos (): iterable {
753
+ foreach ($ this ->classInfos as $ classInfo ) {
754
+ yield from $ classInfo ->propertyInfos ;
755
+ }
756
+ }
675
757
}
676
758
677
759
class DocCommentTag {
@@ -854,6 +936,57 @@ function parseFunctionLike(
854
936
);
855
937
}
856
938
939
+ function parseProperty (
940
+ Name $ class ,
941
+ PrettyPrinterAbstract $ prettyPrinter ,
942
+ int $ flags ,
943
+ Stmt \PropertyProperty $ property ,
944
+ ?Node $ type
945
+ ): PropertyInfo {
946
+ $ comment = $ property ->getDocComment ();
947
+ $ isDeprecated = false ;
948
+ $ isKnownName = false ;
949
+ $ docType = false ;
950
+
951
+ if ($ comment ) {
952
+ $ tags = parseDocComment ($ comment );
953
+ foreach ($ tags as $ tag ) {
954
+ if ($ tag ->name === 'deprecated ' ) {
955
+ $ isDeprecated = true ;
956
+ } else if ($ tag ->name === 'known ' ) {
957
+ $ isKnownName = true ;
958
+ } else if ($ tag ->name === 'var ' ) {
959
+ $ docType = true ;
960
+ }
961
+ }
962
+ }
963
+
964
+ $ propertyType = $ type ? Type::fromNode ($ type ) : null ;
965
+ if ($ propertyType === null && !$ docType ) {
966
+ //throw new Exception("Missing type for property $class::\$$property->name");
967
+ }
968
+
969
+ if ($ property ->default instanceof Expr \ConstFetch &&
970
+ $ property ->default ->name ->toLowerString () === "null " &&
971
+ $ propertyType && !$ propertyType ->isNullable ()
972
+ ) {
973
+ $ simpleType = $ propertyType ->tryToSimpleType ();
974
+ if ($ simpleType === null ) {
975
+ throw new Exception (
976
+ "Property $ class:: \$$ property ->name has null default, but is not nullable " );
977
+ }
978
+ }
979
+
980
+ return new PropertyInfo (
981
+ $ property ->name ->__toString (),
982
+ $ flags ,
983
+ $ isKnownName ,
984
+ $ isDeprecated ,
985
+ $ propertyType ,
986
+ $ property ->default ? $ prettyPrinter ->prettyPrintExpr ($ property ->default ) : null
987
+ );
988
+ }
989
+
857
990
function handlePreprocessorConditions (array &$ conds , Stmt $ stmt ): ?string {
858
991
foreach ($ stmt ->getComments () as $ comment ) {
859
992
$ text = trim ($ comment ->getText ());
@@ -926,13 +1059,14 @@ function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstrac
926
1059
if ($ stmt instanceof Stmt \ClassLike) {
927
1060
$ className = $ stmt ->namespacedName ;
928
1061
$ methodInfos = [];
1062
+ $ propertyInfos = [];
929
1063
foreach ($ stmt ->stmts as $ classStmt ) {
930
1064
$ cond = handlePreprocessorConditions ($ conds , $ classStmt );
931
1065
if ($ classStmt instanceof Stmt \Nop) {
932
1066
continue ;
933
1067
}
934
1068
935
- if (!$ classStmt instanceof Stmt \ClassMethod) {
1069
+ if (!$ classStmt instanceof Stmt \ClassMethod && ! $ classStmt instanceof Stmt \Property ) {
936
1070
throw new Exception ("Not implemented {$ classStmt ->getType ()}" );
937
1071
}
938
1072
@@ -942,19 +1076,32 @@ function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstrac
942
1076
}
943
1077
944
1078
if (!($ flags & Class_::VISIBILITY_MODIFIER_MASK )) {
945
- throw new Exception ("Method visibility modifier is required " );
1079
+ throw new Exception ("Visibility modifier is required " );
946
1080
}
947
1081
948
- $ methodInfos [] = parseFunctionLike (
949
- $ prettyPrinter ,
950
- new MethodName ($ className , $ classStmt ->name ->toString ()),
951
- $ flags ,
952
- $ classStmt ,
953
- $ cond
954
- );
1082
+ if ($ classStmt instanceof Stmt \Property) {
1083
+ foreach ($ classStmt ->props as $ property ) {
1084
+ $ propertyInfos [] = parseProperty (
1085
+ $ className ,
1086
+ $ prettyPrinter ,
1087
+ $ flags ,
1088
+ $ property ,
1089
+ $ classStmt ->type
1090
+ );
1091
+ }
1092
+
1093
+ } else if ($ classStmt instanceof Stmt \ClassMethod) {
1094
+ $ methodInfos [] = parseFunctionLike (
1095
+ $ prettyPrinter ,
1096
+ new MethodName ($ className , $ classStmt ->name ->toString ()),
1097
+ $ flags ,
1098
+ $ classStmt ,
1099
+ $ cond
1100
+ );
1101
+ }
955
1102
}
956
1103
957
- $ fileInfo ->classInfos [] = new ClassInfo ($ className , $ methodInfos );
1104
+ $ fileInfo ->classInfos [] = new ClassInfo ($ className , $ methodInfos, $ propertyInfos );
958
1105
continue ;
959
1106
}
960
1107
@@ -986,6 +1133,10 @@ protected function pName_FullyQualified(Name\FullyQualified $node) {
986
1133
$ fileInfo ->declarationPrefix = $ tag ->value ? $ tag ->value . " " : "" ;
987
1134
} else if ($ tag ->name === 'generate-legacy-arginfo ' ) {
988
1135
$ fileInfo ->generateLegacyArginfo = true ;
1136
+ } else if ($ tag ->name === 'generate-property-declarations ' ) {
1137
+ $ fileInfo ->generatePropertyDeclarations = true ;
1138
+ } else if ($ tag ->name === 'generate-legacy-property-declarations ' ) {
1139
+ $ fileInfo ->generateLegacyPropertyDeclarations = true ;
989
1140
}
990
1141
}
991
1142
}
@@ -1177,6 +1328,21 @@ function (FuncInfo $funcInfo) use($fileInfo, &$generatedFunctionDeclarations) {
1177
1328
return $ code ;
1178
1329
}
1179
1330
1331
+ function generatePropertyDeclarationsCode (FileInfo $ fileInfo , string $ stubHash ): string {
1332
+ $ code = "/* This is a generated file, edit the .stub.php file instead. \n"
1333
+ . " * Stub hash: $ stubHash */ \n\n" ;
1334
+
1335
+ foreach ($ fileInfo ->classInfos as $ class ) {
1336
+ $ code .= "void declare_class_ {$ class ->name }_properties(zend_class_entry *ce) { \n" ;
1337
+ foreach ($ class ->propertyInfos as $ property ) {
1338
+ $ code .= $ property ->getDeclaration ();
1339
+ }
1340
+ $ code .= "} \n" ;
1341
+ }
1342
+
1343
+ return $ code ;
1344
+ }
1345
+
1180
1346
/** @param FuncInfo[] $funcInfos */
1181
1347
function generateFunctionEntries (?Name $ className , array $ funcInfos ): string {
1182
1348
$ code = "" ;
0 commit comments