8
8
use PhpParser \Node \Name ;
9
9
use PhpParser \Node \Stmt ;
10
10
use PhpParser \Node \Stmt \Class_ ;
11
+ use PhpParser \Node \Stmt \Enum_ ;
11
12
use PhpParser \Node \Stmt \Interface_ ;
13
+ use PhpParser \Node \Stmt \Trait_ ;
12
14
use PhpParser \PrettyPrinter \Standard ;
13
15
use PhpParser \PrettyPrinterAbstract ;
14
16
@@ -1324,6 +1326,51 @@ public function getMethodSynopsisElement(array $funcMap, array $aliasMap, DOMDoc
1324
1326
}
1325
1327
}
1326
1328
1329
+ function initializeZval (string $ zvalName , $ value ): string
1330
+ {
1331
+ $ code = "\tzval $ zvalName; \n" ;
1332
+
1333
+ switch (gettype ($ value )) {
1334
+ case "NULL " :
1335
+ $ code .= "\tZVAL_NULL(& $ zvalName); \n" ;
1336
+ break ;
1337
+
1338
+ case "boolean " :
1339
+ $ code .= "\tZVAL_BOOL(& $ zvalName, " . ((int ) $ value ) . "); \n" ;
1340
+ break ;
1341
+
1342
+ case "integer " :
1343
+ $ code .= "\tZVAL_LONG(& $ zvalName, $ value); \n" ;
1344
+ break ;
1345
+
1346
+ case "double " :
1347
+ $ code .= "\tZVAL_DOUBLE(& $ zvalName, $ value); \n" ;
1348
+ break ;
1349
+
1350
+ case "string " :
1351
+ if ($ value === "" ) {
1352
+ $ code .= "\tZVAL_EMPTY_STRING(& $ zvalName); \n" ;
1353
+ } else {
1354
+ $ code .= "\tzend_string * {$ zvalName }_str = zend_string_init( \"$ value \", sizeof( \"$ value \") - 1, 1); \n" ;
1355
+ $ code .= "\tZVAL_STR(& $ zvalName, {$ zvalName }_str); \n" ;
1356
+ }
1357
+ break ;
1358
+
1359
+ case "array " :
1360
+ if (empty ($ value )) {
1361
+ $ code .= "\tZVAL_EMPTY_ARRAY(& $ zvalName); \n" ;
1362
+ } else {
1363
+ throw new Exception ("Unimplemented default value " );
1364
+ }
1365
+ break ;
1366
+
1367
+ default :
1368
+ throw new Exception ("Invalid default value " );
1369
+ }
1370
+
1371
+ return $ code ;
1372
+ }
1373
+
1327
1374
class PropertyInfo
1328
1375
{
1329
1376
/** @var PropertyName */
@@ -1361,10 +1408,8 @@ public function getDeclaration(): string {
1361
1408
$ defaultValueConstant = false ;
1362
1409
if ($ this ->defaultValue === null ) {
1363
1410
$ defaultValue = null ;
1364
- $ defaultValueType = "undefined " ;
1365
1411
} else {
1366
1412
$ defaultValue = $ this ->evaluateDefaultValue ($ defaultValueConstant );
1367
- $ defaultValueType = gettype ($ defaultValue );
1368
1413
}
1369
1414
1370
1415
if ($ defaultValueConstant ) {
@@ -1411,80 +1456,26 @@ public function getDeclaration(): string {
1411
1456
}
1412
1457
}
1413
1458
1414
- $ code .= $ this ->initializeValue ($ defaultValueType , $ defaultValue , $ this ->type !== null );
1459
+ $ zvalName = "property_ {$ this ->name ->property }_default_value " ;
1460
+ if ($ this ->defaultValue === null && $ this ->type !== null ) {
1461
+ $ code .= "\tzval $ zvalName; \n\tZVAL_UNDEF(& $ zvalName); \n" ;
1462
+ } else {
1463
+ $ code .= initializeZval ($ zvalName , $ defaultValue );
1464
+ }
1415
1465
1416
1466
$ code .= "\tzend_string *property_ {$ propertyName }_name = zend_string_init( \"$ propertyName \", sizeof( \"$ propertyName \") - 1, 1); \n" ;
1417
1467
$ nameCode = "property_ {$ propertyName }_name " ;
1418
1468
1419
1469
if ($ this ->type !== null ) {
1420
- $ code .= "\tzend_declare_typed_property(class_entry, $ nameCode, &property_ { $ propertyName } _default_value , " . $ this ->getFlagsAsString () . ", NULL, $ typeCode); \n" ;
1470
+ $ code .= "\tzend_declare_typed_property(class_entry, $ nameCode, & $ zvalName , " . $ this ->getFlagsAsString () . ", NULL, $ typeCode); \n" ;
1421
1471
} else {
1422
- $ code .= "\tzend_declare_property_ex(class_entry, $ nameCode, &property_ { $ propertyName } _default_value , " . $ this ->getFlagsAsString () . ", NULL); \n" ;
1472
+ $ code .= "\tzend_declare_property_ex(class_entry, $ nameCode, & $ zvalName , " . $ this ->getFlagsAsString () . ", NULL); \n" ;
1423
1473
}
1424
1474
$ code .= "\tzend_string_release(property_ {$ propertyName }_name); \n" ;
1425
1475
1426
1476
return $ code ;
1427
1477
}
1428
1478
1429
- /**
1430
- * @param mixed $value
1431
- */
1432
- private function initializeValue (string $ type , $ value , bool $ isTyped ): string
1433
- {
1434
- $ name = $ this ->name ->property ;
1435
- $ zvalName = "property_ {$ name }_default_value " ;
1436
-
1437
- $ code = "\tzval $ zvalName; \n" ;
1438
-
1439
- switch ($ type ) {
1440
- case "undefined " :
1441
- if ($ isTyped ) {
1442
- $ code .= "\tZVAL_UNDEF(& $ zvalName); \n" ;
1443
- } else {
1444
- $ code .= "\tZVAL_NULL(& $ zvalName); \n" ;
1445
- }
1446
- break ;
1447
-
1448
- case "NULL " :
1449
- $ code .= "\tZVAL_NULL(& $ zvalName); \n" ;
1450
- break ;
1451
-
1452
- case "boolean " :
1453
- $ code .= "\tZVAL_BOOL(& $ zvalName, " . ((int ) $ value ) . "); \n" ;
1454
- break ;
1455
-
1456
- case "integer " :
1457
- $ code .= "\tZVAL_LONG(& $ zvalName, $ value); \n" ;
1458
- break ;
1459
-
1460
- case "double " :
1461
- $ code .= "\tZVAL_DOUBLE(& $ zvalName, $ value); \n" ;
1462
- break ;
1463
-
1464
- case "string " :
1465
- if ($ value === "" ) {
1466
- $ code .= "\tZVAL_EMPTY_STRING(& $ zvalName); \n" ;
1467
- } else {
1468
- $ code .= "\tzend_string * {$ zvalName }_str = zend_string_init( \"$ value \", sizeof( \"$ value \") - 1, 1); \n" ;
1469
- $ code .= "\tZVAL_STR(& $ zvalName, {$ zvalName }_str); \n" ;
1470
- }
1471
- break ;
1472
-
1473
- case "array " :
1474
- if (empty ($ value )) {
1475
- $ code .= "\tZVAL_EMPTY_ARRAY(& $ zvalName); \n" ;
1476
- } else {
1477
- throw new Exception ("Unimplemented property default value " );
1478
- }
1479
- break ;
1480
-
1481
- default :
1482
- throw new Exception ("Invalid property default value " );
1483
- }
1484
-
1485
- return $ code ;
1486
- }
1487
-
1488
1479
private function getFlagsAsString (): string
1489
1480
{
1490
1481
$ flags = "ZEND_ACC_PUBLIC " ;
@@ -1568,6 +1559,33 @@ function (Expr $expr) use (&$defaultValueConstant) {
1568
1559
}
1569
1560
}
1570
1561
1562
+ class EnumCaseInfo {
1563
+ /** @var string */
1564
+ public $ name ;
1565
+ /** @var Expr|null */
1566
+ public $ value ;
1567
+
1568
+ public function __construct (string $ name , ?Expr $ value ) {
1569
+ $ this ->name = $ name ;
1570
+ $ this ->value = $ value ;
1571
+ }
1572
+
1573
+ public function getDeclaration (): string {
1574
+ $ escapedName = addslashes ($ this ->name );
1575
+ if ($ this ->value === null ) {
1576
+ $ code = "\n\tzend_enum_add_case_cstr(class_entry, \"$ escapedName \", NULL); \n" ;
1577
+ } else {
1578
+ $ evaluator = new ConstExprEvaluator (function (Expr $ expr ) {
1579
+ throw new Exception ("Enum case $ this ->name has an unsupported value " );
1580
+ });
1581
+ $ zvalName = "enum_case_ {$ escapedName }_value " ;
1582
+ $ code = "\n" . initializeZval ($ zvalName , $ evaluator ->evaluateDirectly ($ this ->value ));
1583
+ $ code .= "\tzend_enum_add_case_cstr(class_entry, \"$ escapedName \", & $ zvalName); \n" ;
1584
+ }
1585
+ return $ code ;
1586
+ }
1587
+ }
1588
+
1571
1589
class ClassInfo {
1572
1590
/** @var Name */
1573
1591
public $ name ;
@@ -1577,6 +1595,8 @@ class ClassInfo {
1577
1595
public $ type ;
1578
1596
/** @var string|null */
1579
1597
public $ alias ;
1598
+ /** @var SimpleType|null */
1599
+ public $ enumBackingType ;
1580
1600
/** @var bool */
1581
1601
public $ isDeprecated ;
1582
1602
/** @var bool */
@@ -1591,37 +1611,44 @@ class ClassInfo {
1591
1611
public $ propertyInfos ;
1592
1612
/** @var FuncInfo[] */
1593
1613
public $ funcInfos ;
1614
+ /** @var EnumCaseInfo[] */
1615
+ public $ enumCaseInfos ;
1594
1616
1595
1617
/**
1596
1618
* @param Name[] $extends
1597
1619
* @param Name[] $implements
1598
1620
* @param PropertyInfo[] $propertyInfos
1599
1621
* @param FuncInfo[] $funcInfos
1622
+ * @param EnumCaseInfo[] $enumCaseInfos
1600
1623
*/
1601
1624
public function __construct (
1602
1625
Name $ name ,
1603
1626
int $ flags ,
1604
1627
string $ type ,
1605
1628
?string $ alias ,
1629
+ ?SimpleType $ enumBackingType ,
1606
1630
bool $ isDeprecated ,
1607
1631
bool $ isStrictProperties ,
1608
1632
bool $ isNotSerializable ,
1609
1633
array $ extends ,
1610
1634
array $ implements ,
1611
1635
array $ propertyInfos ,
1612
- array $ funcInfos
1636
+ array $ funcInfos ,
1637
+ array $ enumCaseInfos
1613
1638
) {
1614
1639
$ this ->name = $ name ;
1615
1640
$ this ->flags = $ flags ;
1616
1641
$ this ->type = $ type ;
1617
1642
$ this ->alias = $ alias ;
1643
+ $ this ->enumBackingType = $ enumBackingType ;
1618
1644
$ this ->isDeprecated = $ isDeprecated ;
1619
1645
$ this ->isStrictProperties = $ isStrictProperties ;
1620
1646
$ this ->isNotSerializable = $ isNotSerializable ;
1621
1647
$ this ->extends = $ extends ;
1622
1648
$ this ->implements = $ implements ;
1623
1649
$ this ->propertyInfos = $ propertyInfos ;
1624
1650
$ this ->funcInfos = $ funcInfos ;
1651
+ $ this ->enumCaseInfos = $ enumCaseInfos ;
1625
1652
}
1626
1653
1627
1654
public function getRegistration (): string
@@ -1639,21 +1666,29 @@ public function getRegistration(): string
1639
1666
$ code = "static zend_class_entry *register_class_ $ escapedName( " . (empty ($ params ) ? "void " : implode (", " , $ params )) . ") \n" ;
1640
1667
1641
1668
$ code .= "{ \n" ;
1642
- $ code .= "\tzend_class_entry ce, *class_entry; \n\n" ;
1643
- if (count ($ this ->name ->parts ) > 1 ) {
1644
- $ className = $ this ->name ->getLast ();
1645
- $ namespace = addslashes ((string ) $ this ->name ->slice (0 , -1 ));
1646
-
1647
- $ code .= "\tINIT_NS_CLASS_ENTRY(ce, \"$ namespace \", \"$ className \", class_ {$ escapedName }_methods); \n" ;
1669
+ if ($ this ->type == "enum " ) {
1670
+ $ name = addslashes ((string ) $ this ->name );
1671
+ $ backingType = $ this ->enumBackingType
1672
+ ? $ this ->enumBackingType ->toTypeCode () : "IS_UNDEF " ;
1673
+ $ code .= "\tzend_class_entry *class_entry = zend_register_internal_enum( \"$ name \", $ backingType, class_ {$ escapedName }_methods); \n" ;
1648
1674
} else {
1649
- $ code .= "\tINIT_CLASS_ENTRY(ce, \"$ this ->name \", class_ {$ escapedName }_methods); \n" ;
1650
- }
1675
+ $ code .= "\tzend_class_entry ce, *class_entry; \n\n" ;
1676
+ if (count ($ this ->name ->parts ) > 1 ) {
1677
+ $ className = $ this ->name ->getLast ();
1678
+ $ namespace = addslashes ((string ) $ this ->name ->slice (0 , -1 ));
1651
1679
1652
- if ($ this ->type === "class " || $ this ->type === "trait " ) {
1653
- $ code .= "\tclass_entry = zend_register_internal_class_ex(&ce, " . (isset ($ this ->extends [0 ]) ? "class_entry_ " . str_replace ("\\" , "_ " , $ this ->extends [0 ]->toString ()) : "NULL " ) . "); \n" ;
1654
- } else {
1655
- $ code .= "\tclass_entry = zend_register_internal_interface(&ce); \n" ;
1680
+ $ code .= "\tINIT_NS_CLASS_ENTRY(ce, \"$ namespace \", \"$ className \", class_ {$ escapedName }_methods); \n" ;
1681
+ } else {
1682
+ $ code .= "\tINIT_CLASS_ENTRY(ce, \"$ this ->name \", class_ {$ escapedName }_methods); \n" ;
1683
+ }
1684
+
1685
+ if ($ this ->type === "class " || $ this ->type === "trait " ) {
1686
+ $ code .= "\tclass_entry = zend_register_internal_class_ex(&ce, " . (isset ($ this ->extends [0 ]) ? "class_entry_ " . str_replace ("\\" , "_ " , $ this ->extends [0 ]->toString ()) : "NULL " ) . "); \n" ;
1687
+ } else {
1688
+ $ code .= "\tclass_entry = zend_register_internal_interface(&ce); \n" ;
1689
+ }
1656
1690
}
1691
+
1657
1692
if ($ this ->getFlagsAsString ()) {
1658
1693
$ code .= "\tclass_entry->ce_flags |= " . $ this ->getFlagsAsString () . "; \n" ;
1659
1694
}
@@ -1673,6 +1708,10 @@ function (Name $item) {
1673
1708
$ code .= "\tzend_register_class_alias( \"" . str_replace ("\\" , "_ " , $ this ->alias ) . "\", class_entry); \n" ;
1674
1709
}
1675
1710
1711
+ foreach ($ this ->enumCaseInfos as $ enumCase ) {
1712
+ $ code .= $ enumCase ->getDeclaration ();
1713
+ }
1714
+
1676
1715
foreach ($ this ->propertyInfos as $ property ) {
1677
1716
$ code .= $ property ->getDeclaration ();
1678
1717
}
@@ -2306,8 +2345,11 @@ function parseProperty(
2306
2345
/**
2307
2346
* @param PropertyInfo[] $properties
2308
2347
* @param FuncInfo[] $methods
2348
+ * @param EnumCaseInfo[] $enumCases
2309
2349
*/
2310
- function parseClass (Name $ name , Stmt \ClassLike $ class , array $ properties , array $ methods ): ClassInfo {
2350
+ function parseClass (
2351
+ Name $ name , Stmt \ClassLike $ class , array $ properties , array $ methods , array $ enumCases
2352
+ ): ClassInfo {
2311
2353
$ flags = $ class instanceof Class_ ? $ class ->flags : 0 ;
2312
2354
$ comment = $ class ->getDocComment ();
2313
2355
$ alias = null ;
@@ -2334,26 +2376,38 @@ function parseClass(Name $name, Stmt\ClassLike $class, array $properties, array
2334
2376
$ implements = [];
2335
2377
2336
2378
if ($ class instanceof Class_) {
2379
+ $ classKind = "class " ;
2337
2380
if ($ class ->extends ) {
2338
2381
$ extends [] = $ class ->extends ;
2339
2382
}
2340
2383
$ implements = $ class ->implements ;
2341
2384
} elseif ($ class instanceof Interface_) {
2385
+ $ classKind = "interface " ;
2342
2386
$ extends = $ class ->extends ;
2387
+ } else if ($ class instanceof Trait_) {
2388
+ $ classKind = "trait " ;
2389
+ } else if ($ class instanceof Enum_) {
2390
+ $ classKind = "enum " ;
2391
+ $ implements = $ class ->implements ;
2392
+ } else {
2393
+ throw new Exception ("Unknown class kind " . get_class ($ class ));
2343
2394
}
2344
2395
2345
2396
return new ClassInfo (
2346
2397
$ name ,
2347
2398
$ flags ,
2348
- $ class instanceof Class_ ? " class " : ( $ class instanceof Interface_ ? " interface " : " trait " ) ,
2399
+ $ classKind ,
2349
2400
$ alias ,
2401
+ $ class instanceof Enum_ && $ class ->scalarType !== null
2402
+ ? SimpleType::fromNode ($ class ->scalarType ) : null ,
2350
2403
$ isDeprecated ,
2351
2404
$ isStrictProperties ,
2352
2405
$ isNotSerializable ,
2353
2406
$ extends ,
2354
2407
$ implements ,
2355
2408
$ properties ,
2356
- $ methods
2409
+ $ methods ,
2410
+ $ enumCases
2357
2411
);
2358
2412
}
2359
2413
@@ -2431,6 +2485,7 @@ function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstrac
2431
2485
$ className = $ stmt ->namespacedName ;
2432
2486
$ propertyInfos = [];
2433
2487
$ methodInfos = [];
2488
+ $ enumCaseInfos = [];
2434
2489
foreach ($ stmt ->stmts as $ classStmt ) {
2435
2490
$ cond = handlePreprocessorConditions ($ conds , $ classStmt );
2436
2491
if ($ classStmt instanceof Stmt \Nop) {
@@ -2466,12 +2521,16 @@ function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstrac
2466
2521
$ classStmt ,
2467
2522
$ cond
2468
2523
);
2524
+ } else if ($ classStmt instanceof Stmt \EnumCase) {
2525
+ $ enumCaseInfos [] = new EnumCaseInfo (
2526
+ $ classStmt ->name ->toString (), $ classStmt ->expr );
2469
2527
} else {
2470
2528
throw new Exception ("Not implemented {$ classStmt ->getType ()}" );
2471
2529
}
2472
2530
}
2473
2531
2474
- $ fileInfo ->classInfos [] = parseClass ($ className , $ stmt , $ propertyInfos , $ methodInfos );
2532
+ $ fileInfo ->classInfos [] = parseClass (
2533
+ $ className , $ stmt , $ propertyInfos , $ methodInfos , $ enumCaseInfos );
2475
2534
continue ;
2476
2535
}
2477
2536
0 commit comments