From c8e191229a9314f956994a2c4f5d67216fa135f0 Mon Sep 17 00:00:00 2001 From: Eugenio Tampieri Date: Thu, 6 Nov 2025 09:01:25 +0100 Subject: [PATCH 1/4] Add empty PHP generator --- Cargo.lock | 10 ++++++++ crates/target_php/Cargo.toml | 15 ++++++++++++ crates/target_php/src/lib.rs | 45 ++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 crates/target_php/Cargo.toml create mode 100644 crates/target_php/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 68a7f2e..c6e8b37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -250,6 +250,16 @@ dependencies = [ "serde_json", ] +[[package]] +name = "jetted_target_php" +version = "0.1.0" +dependencies = [ + "jetted_core", + "jetted_test", + "regex", + "serde_json", +] + [[package]] name = "jetted_target_python" version = "0.1.0" diff --git a/crates/target_php/Cargo.toml b/crates/target_php/Cargo.toml new file mode 100644 index 0000000..f10c729 --- /dev/null +++ b/crates/target_php/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "jetted_target_php" +version = "0.1.0" +authors = [ + "Eugenio Tampieri", +] +edition = "2024" + +[dependencies] +jetted_core = { path = "../core" } +serde_json = "1.0" +regex = "1" + +[dev-dependencies] +jetted_test = { path = "../test" } diff --git a/crates/target_php/src/lib.rs b/crates/target_php/src/lib.rs new file mode 100644 index 0000000..abd5b4b --- /dev/null +++ b/crates/target_php/src/lib.rs @@ -0,0 +1,45 @@ +pub struct Target {} +impl Target { + pub fn new() -> Self { + Self {} + } +} + +impl jetted_core::target::Target for Target { + type FileState = (); + + fn strategy(&self) -> jetted_core::target::Strategy { + todo!() + } + + fn name(&self, kind: jetted_core::target::NameableKind, name_parts: &[String]) -> String { + todo!() + } + + fn expr(&self, state: &mut Self::FileState, metadata: jetted_core::target::metadata::Metadata, expr: jetted_core::target::Expr) -> String { + todo!() + } + + fn item( + &self, + out: &mut dyn std::io::Write, + state: &mut Self::FileState, + item: jetted_core::target::Item, + ) -> jetted_core::Result> { + todo!() + } +} + +#[cfg(test)] +mod tests { + mod std_tests { + jetted_test::std_test_cases!(&crate::Target::new()); + } + + mod optional_std_tests { + jetted_test::strict_std_test_case!(&crate::Target::new(), empty_and_nonascii_properties); + + jetted_test::strict_std_test_case!(&crate::Target::new(), empty_and_nonascii_enum_values); + } +} + From 5dec170dec1b8db400957ec637440c9639a0991f Mon Sep 17 00:00:00 2001 From: Eugenio Tampieri Date: Thu, 6 Nov 2025 15:49:22 +0100 Subject: [PATCH 2/4] Basic PHP implementation --- crates/core/src/target/inflect.rs | 16 +-- .../BasicDiscriminator.php | 1 + .../BasicDiscriminatorBarBaz.php | 1 + .../BasicDiscriminatorQuux.php | 1 + .../output/basic_enum/BasicEnum.php | 1 + .../basic_properties/BasicProperties.php | 36 +++++ .../custom_overrides/CustomOverrides.php | 46 +++++++ ...stomOverridesOverrideTypeDiscriminator.php | 1 + ...mOverridesOverrideTypeDiscriminatorBar.php | 1 + ...mOverridesOverrideTypeDiscriminatorBaz.php | 1 + .../CustomOverridesOverrideTypeEnum.php | 1 + .../CustomOverridesOverrideTypeProperties.php | 16 +++ .../output/definition_name_collisions/Bar.php | 1 + .../definition_name_collisions/Bar0.php | 1 + .../DefinitionNameCollisions.php | 1 + .../output/definition_name_collisions/Foo.php | 1 + .../definition_name_collisions/Foo0.php | 1 + crates/target_php/output/description/Baz.php | 1 + .../output/description/Description.php | 46 +++++++ ...escriptionDiscriminatorWithDescription.php | 1 + ...riptionDiscriminatorWithDescriptionBar.php | 1 + .../DescriptionEnumWithDescription.php | 1 + .../DescriptionPropertiesWithDescription.php | 16 +++ .../DiscriminatorOptionalProperties.php | 1 + .../DiscriminatorOptionalPropertiesBar.php | 1 + .../target_php/output/elements/Elements.php | 1 + .../DefaultName.php | 1 + .../EmptyAndNonasciiDefinitions.php | 1 + .../empty_and_nonascii_definitions/Foo.php | 1 + .../empty_and_nonascii_definitions/Foo0.php | 1 + .../Foo0bar.php | 1 + .../empty_and_nonascii_definitions/Foo1.php | 1 + .../empty_and_nonascii_definitions/FooBar.php | 1 + .../FooBar0.php | 1 + .../FooBar1.php | 1 + .../EmptyAndNonasciiEnumValues.php | 1 + .../EmptyAndNonasciiProperties.php | 58 ++++++++ .../output/enum_collisions/EnumCollisions.php | 26 ++++ .../enum_collisions/EnumCollisionsFoo.php | 21 +++ .../enum_collisions/EnumCollisionsFooBar.php | 1 + .../enum_collisions/EnumCollisionsFooBar0.php | 1 + .../EnumVariantCollisions.php | 1 + .../target_php/output/geojson/BoundingBox.php | 1 + crates/target_php/output/geojson/Geojson.php | 1 + .../output/geojson/GeojsonObject.php | 1 + .../output/geojson/GeojsonObjectFeature.php | 1 + .../GeojsonObjectFeatureCollection.php | 1 + .../GeojsonObjectGeometryCollection.php | 1 + .../geojson/GeojsonObjectLineString.php | 1 + .../geojson/GeojsonObjectMultiLineString.php | 1 + .../geojson/GeojsonObjectMultiPoint.php | 1 + .../geojson/GeojsonObjectMultiPolygon.php | 1 + .../output/geojson/GeojsonObjectPoint.php | 1 + .../output/geojson/GeojsonObjectPolygon.php | 1 + .../target_php/output/geojson/LinearRing.php | 1 + crates/target_php/output/geojson/Position.php | 1 + .../output/initialisms/Initialisms.php | 46 +++++++ .../InitialismsNestedIdInitialism.php | 26 ++++ crates/target_php/output/keywords/For.php | 1 + .../target_php/output/keywords/Keywords.php | 26 ++++ crates/target_php/output/keywords/Object.php | 1 + .../NullableDiscriminator.php | 1 + .../NullableDiscriminatorBar.php | 1 + .../NullableDiscriminatorQuux.php | 1 + .../nullable_elements/NullableElements.php | 1 + .../output/nullable_enum/NullableEnum.php | 1 + .../NullableProperties.php | 36 +++++ .../NotnullRefNotnullString.php | 1 + .../NotnullRefNullString.php | 1 + .../nullable_references/NotnullString.php | 1 + .../NullRefNotnullString.php | 1 + .../nullable_references/NullRefNullString.php | 1 + .../output/nullable_references/NullString.php | 1 + .../NullableReferences.php | 46 +++++++ .../NullableTimestampProperty.php | 21 +++ .../OptionalProperties.php | 31 +++++ .../PropertyNameCollisions.php | 26 ++++ crates/target_php/output/reference/Bar.php | 1 + crates/target_php/output/reference/Baz.php | 1 + crates/target_php/output/reference/Foo.php | 1 + .../target_php/output/reference/Reference.php | 1 + .../output/root_boolean/RootBoolean.php | 1 + .../output/root_empty/RootEmpty.php | 1 + .../output/root_float32/RootFloat32.php | 1 + .../output/root_float64/RootFloat64.php | 1 + .../output/root_int16/RootInt16.php | 1 + .../output/root_int32/RootInt32.php | 1 + .../target_php/output/root_int8/RootInt8.php | 1 + .../RootNullableString.php | 1 + .../RootNullableTimestamp.php | 1 + .../output/root_string/RootString.php | 1 + .../output/root_timestamp/RootTimestamp.php | 1 + .../output/root_uint16/RootUint16.php | 1 + .../output/root_uint32/RootUint32.php | 1 + .../output/root_uint8/RootUint8.php | 1 + .../output/type_collisions/TypeCollisions.php | 26 ++++ .../type_collisions/TypeCollisionsFoo.php | 21 +++ .../type_collisions/TypeCollisionsFooBar.php | 21 +++ .../type_collisions/TypeCollisionsFooBar0.php | 21 +++ crates/target_php/output/values/Values.php | 1 + crates/target_php/src/codegen.rs | 95 +++++++++++++ crates/target_php/src/lib.rs | 126 +++++++++++++++++- 102 files changed, 914 insertions(+), 14 deletions(-) create mode 100644 crates/target_php/output/basic_discriminator/BasicDiscriminator.php create mode 100644 crates/target_php/output/basic_discriminator/BasicDiscriminatorBarBaz.php create mode 100644 crates/target_php/output/basic_discriminator/BasicDiscriminatorQuux.php create mode 100644 crates/target_php/output/basic_enum/BasicEnum.php create mode 100644 crates/target_php/output/basic_properties/BasicProperties.php create mode 100644 crates/target_php/output/custom_overrides/CustomOverrides.php create mode 100644 crates/target_php/output/custom_overrides/CustomOverridesOverrideTypeDiscriminator.php create mode 100644 crates/target_php/output/custom_overrides/CustomOverridesOverrideTypeDiscriminatorBar.php create mode 100644 crates/target_php/output/custom_overrides/CustomOverridesOverrideTypeDiscriminatorBaz.php create mode 100644 crates/target_php/output/custom_overrides/CustomOverridesOverrideTypeEnum.php create mode 100644 crates/target_php/output/custom_overrides/CustomOverridesOverrideTypeProperties.php create mode 100644 crates/target_php/output/definition_name_collisions/Bar.php create mode 100644 crates/target_php/output/definition_name_collisions/Bar0.php create mode 100644 crates/target_php/output/definition_name_collisions/DefinitionNameCollisions.php create mode 100644 crates/target_php/output/definition_name_collisions/Foo.php create mode 100644 crates/target_php/output/definition_name_collisions/Foo0.php create mode 100644 crates/target_php/output/description/Baz.php create mode 100644 crates/target_php/output/description/Description.php create mode 100644 crates/target_php/output/description/DescriptionDiscriminatorWithDescription.php create mode 100644 crates/target_php/output/description/DescriptionDiscriminatorWithDescriptionBar.php create mode 100644 crates/target_php/output/description/DescriptionEnumWithDescription.php create mode 100644 crates/target_php/output/description/DescriptionPropertiesWithDescription.php create mode 100644 crates/target_php/output/discriminator_optional_properties/DiscriminatorOptionalProperties.php create mode 100644 crates/target_php/output/discriminator_optional_properties/DiscriminatorOptionalPropertiesBar.php create mode 100644 crates/target_php/output/elements/Elements.php create mode 100644 crates/target_php/output/empty_and_nonascii_definitions/DefaultName.php create mode 100644 crates/target_php/output/empty_and_nonascii_definitions/EmptyAndNonasciiDefinitions.php create mode 100644 crates/target_php/output/empty_and_nonascii_definitions/Foo.php create mode 100644 crates/target_php/output/empty_and_nonascii_definitions/Foo0.php create mode 100644 crates/target_php/output/empty_and_nonascii_definitions/Foo0bar.php create mode 100644 crates/target_php/output/empty_and_nonascii_definitions/Foo1.php create mode 100644 crates/target_php/output/empty_and_nonascii_definitions/FooBar.php create mode 100644 crates/target_php/output/empty_and_nonascii_definitions/FooBar0.php create mode 100644 crates/target_php/output/empty_and_nonascii_definitions/FooBar1.php create mode 100644 crates/target_php/output/empty_and_nonascii_enum_values/EmptyAndNonasciiEnumValues.php create mode 100644 crates/target_php/output/empty_and_nonascii_properties/EmptyAndNonasciiProperties.php create mode 100644 crates/target_php/output/enum_collisions/EnumCollisions.php create mode 100644 crates/target_php/output/enum_collisions/EnumCollisionsFoo.php create mode 100644 crates/target_php/output/enum_collisions/EnumCollisionsFooBar.php create mode 100644 crates/target_php/output/enum_collisions/EnumCollisionsFooBar0.php create mode 100644 crates/target_php/output/enum_variant_collisions/EnumVariantCollisions.php create mode 100644 crates/target_php/output/geojson/BoundingBox.php create mode 100644 crates/target_php/output/geojson/Geojson.php create mode 100644 crates/target_php/output/geojson/GeojsonObject.php create mode 100644 crates/target_php/output/geojson/GeojsonObjectFeature.php create mode 100644 crates/target_php/output/geojson/GeojsonObjectFeatureCollection.php create mode 100644 crates/target_php/output/geojson/GeojsonObjectGeometryCollection.php create mode 100644 crates/target_php/output/geojson/GeojsonObjectLineString.php create mode 100644 crates/target_php/output/geojson/GeojsonObjectMultiLineString.php create mode 100644 crates/target_php/output/geojson/GeojsonObjectMultiPoint.php create mode 100644 crates/target_php/output/geojson/GeojsonObjectMultiPolygon.php create mode 100644 crates/target_php/output/geojson/GeojsonObjectPoint.php create mode 100644 crates/target_php/output/geojson/GeojsonObjectPolygon.php create mode 100644 crates/target_php/output/geojson/LinearRing.php create mode 100644 crates/target_php/output/geojson/Position.php create mode 100644 crates/target_php/output/initialisms/Initialisms.php create mode 100644 crates/target_php/output/initialisms/InitialismsNestedIdInitialism.php create mode 100644 crates/target_php/output/keywords/For.php create mode 100644 crates/target_php/output/keywords/Keywords.php create mode 100644 crates/target_php/output/keywords/Object.php create mode 100644 crates/target_php/output/nullable_discriminator/NullableDiscriminator.php create mode 100644 crates/target_php/output/nullable_discriminator/NullableDiscriminatorBar.php create mode 100644 crates/target_php/output/nullable_discriminator/NullableDiscriminatorQuux.php create mode 100644 crates/target_php/output/nullable_elements/NullableElements.php create mode 100644 crates/target_php/output/nullable_enum/NullableEnum.php create mode 100644 crates/target_php/output/nullable_properties/NullableProperties.php create mode 100644 crates/target_php/output/nullable_references/NotnullRefNotnullString.php create mode 100644 crates/target_php/output/nullable_references/NotnullRefNullString.php create mode 100644 crates/target_php/output/nullable_references/NotnullString.php create mode 100644 crates/target_php/output/nullable_references/NullRefNotnullString.php create mode 100644 crates/target_php/output/nullable_references/NullRefNullString.php create mode 100644 crates/target_php/output/nullable_references/NullString.php create mode 100644 crates/target_php/output/nullable_references/NullableReferences.php create mode 100644 crates/target_php/output/nullable_timestamp_property/NullableTimestampProperty.php create mode 100644 crates/target_php/output/optional_properties/OptionalProperties.php create mode 100644 crates/target_php/output/property_name_collisions/PropertyNameCollisions.php create mode 100644 crates/target_php/output/reference/Bar.php create mode 100644 crates/target_php/output/reference/Baz.php create mode 100644 crates/target_php/output/reference/Foo.php create mode 100644 crates/target_php/output/reference/Reference.php create mode 100644 crates/target_php/output/root_boolean/RootBoolean.php create mode 100644 crates/target_php/output/root_empty/RootEmpty.php create mode 100644 crates/target_php/output/root_float32/RootFloat32.php create mode 100644 crates/target_php/output/root_float64/RootFloat64.php create mode 100644 crates/target_php/output/root_int16/RootInt16.php create mode 100644 crates/target_php/output/root_int32/RootInt32.php create mode 100644 crates/target_php/output/root_int8/RootInt8.php create mode 100644 crates/target_php/output/root_nullable_string/RootNullableString.php create mode 100644 crates/target_php/output/root_nullable_timestamp/RootNullableTimestamp.php create mode 100644 crates/target_php/output/root_string/RootString.php create mode 100644 crates/target_php/output/root_timestamp/RootTimestamp.php create mode 100644 crates/target_php/output/root_uint16/RootUint16.php create mode 100644 crates/target_php/output/root_uint32/RootUint32.php create mode 100644 crates/target_php/output/root_uint8/RootUint8.php create mode 100644 crates/target_php/output/type_collisions/TypeCollisions.php create mode 100644 crates/target_php/output/type_collisions/TypeCollisionsFoo.php create mode 100644 crates/target_php/output/type_collisions/TypeCollisionsFooBar.php create mode 100644 crates/target_php/output/type_collisions/TypeCollisionsFooBar0.php create mode 100644 crates/target_php/output/values/Values.php create mode 100644 crates/target_php/src/codegen.rs diff --git a/crates/core/src/target/inflect.rs b/crates/core/src/target/inflect.rs index 394f063..f7f7e5f 100644 --- a/crates/core/src/target/inflect.rs +++ b/crates/core/src/target/inflect.rs @@ -35,7 +35,7 @@ pub struct CombiningInflector { } impl CombiningInflector { - pub fn new(case: Case) -> Self { + pub const fn new(case: Case) -> Self { Self { case } } } @@ -51,7 +51,7 @@ pub struct TailInflector { } impl TailInflector { - pub fn new(case: Case) -> Self { + pub const fn new(case: Case) -> Self { Self { case } } } @@ -104,7 +104,7 @@ pub struct Case { } impl Case { - pub fn new( + pub const fn new( first_capitalization: CaseCapitalization, rest_capitalization: CaseCapitalization, delimiter: Option, @@ -118,7 +118,7 @@ impl Case { } } - pub fn camel_case() -> Self { + pub const fn camel_case() -> Self { Self::new( CaseCapitalization::None, CaseCapitalization::Initial, @@ -127,7 +127,7 @@ impl Case { ) } - pub fn pascal_case() -> Self { + pub const fn pascal_case() -> Self { Self::new( CaseCapitalization::Initial, CaseCapitalization::Initial, @@ -136,7 +136,7 @@ impl Case { ) } - pub fn pascal_case_with_initialisms(initialisms: BTreeSet) -> Self { + pub const fn pascal_case_with_initialisms(initialisms: BTreeSet) -> Self { Self::new( CaseCapitalization::Initial, CaseCapitalization::Initial, @@ -145,7 +145,7 @@ impl Case { ) } - pub fn snake_case() -> Self { + pub const fn snake_case() -> Self { Self::new( CaseCapitalization::None, CaseCapitalization::None, @@ -154,7 +154,7 @@ impl Case { ) } - pub fn screaming_snake_case() -> Self { + pub const fn screaming_snake_case() -> Self { Self::new( CaseCapitalization::All, CaseCapitalization::All, diff --git a/crates/target_php/output/basic_discriminator/BasicDiscriminator.php b/crates/target_php/output/basic_discriminator/BasicDiscriminator.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/crates/target_php/output/basic_discriminator/BasicDiscriminator.php @@ -0,0 +1 @@ +bar = $bar; + $this->baz = $baz; + $this->foo = $foo; + $this->quux = $quux; + } + public function serialize() { + $result = []; + $result["bar"] = $this->bar; + $result["baz"] = $this->baz; + $result["foo"] = $this->foo; + $result["quux"] = $this->quux; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["bar"], + $result["baz"], + $result["foo"], + $result["quux"] + ); + } +} diff --git a/crates/target_php/output/custom_overrides/CustomOverrides.php b/crates/target_php/output/custom_overrides/CustomOverrides.php new file mode 100644 index 0000000..4cab586 --- /dev/null +++ b/crates/target_php/output/custom_overrides/CustomOverrides.php @@ -0,0 +1,46 @@ +override_elements_container = $override_elements_container; + $this->override_type_discriminator = $override_type_discriminator; + $this->override_type_enum = $override_type_enum; + $this->override_type_expr = $override_type_expr; + $this->override_type_properties = $override_type_properties; + $this->override_values_container = $override_values_container; + } + public function serialize() { + $result = []; + $result["override_elements_container"] = $this->override_elements_container; + $result["override_type_discriminator"] = $this->override_type_discriminator; + $result["override_type_enum"] = $this->override_type_enum; + $result["override_type_expr"] = $this->override_type_expr; + $result["override_type_properties"] = $this->override_type_properties; + $result["override_values_container"] = $this->override_values_container; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["override_elements_container"], + $result["override_type_discriminator"], + $result["override_type_enum"], + $result["override_type_expr"], + $result["override_type_properties"], + $result["override_values_container"] + ); + } +} diff --git a/crates/target_php/output/custom_overrides/CustomOverridesOverrideTypeDiscriminator.php b/crates/target_php/output/custom_overrides/CustomOverridesOverrideTypeDiscriminator.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/crates/target_php/output/custom_overrides/CustomOverridesOverrideTypeDiscriminator.php @@ -0,0 +1 @@ +discriminator_with_description = $discriminator_with_description; + $this->enum_with_description = $enum_with_description; + $this->long_description = $long_description; + $this->properties_with_description = $properties_with_description; + $this->ref_with_description = $ref_with_description; + $this->string_with_description = $string_with_description; + } + public function serialize() { + $result = []; + $result["discriminator_with_description"] = $this->discriminator_with_description; + $result["enum_with_description"] = $this->enum_with_description; + $result["long_description"] = $this->long_description; + $result["properties_with_description"] = $this->properties_with_description; + $result["ref_with_description"] = $this->ref_with_description; + $result["string_with_description"] = $this->string_with_description; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["discriminator_with_description"], + $result["enum_with_description"], + $result["long_description"], + $result["properties_with_description"], + $result["ref_with_description"], + $result["string_with_description"] + ); + } +} diff --git a/crates/target_php/output/description/DescriptionDiscriminatorWithDescription.php b/crates/target_php/output/description/DescriptionDiscriminatorWithDescription.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/crates/target_php/output/description/DescriptionDiscriminatorWithDescription.php @@ -0,0 +1 @@ +default_name = $default_name; + $this->foo = $foo; + $this->foo0 = $foo0; + $this->foo1 = $foo1; + $this->foo_bar = $foo_bar; + $this->foo_bar0 = $foo_bar0; + $this->foo0bar = $foo0bar; + $this->foo_bar1 = $foo_bar1; + } + public function serialize() { + $result = []; + $result[""] = $this->default_name; + $result["$foo"] = $this->foo; + $result["0foo"] = $this->foo0; + $result["_foo"] = $this->foo1; + $result["foo +bar"] = $this->foo_bar; + $result["foo bar"] = $this->foo_bar0; + $result["foo0bar"] = $this->foo0bar; + $result["foo﷽bar"] = $this->foo_bar1; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result[""], + $result["$foo"], + $result["0foo"], + $result["_foo"], + $result["foo +bar"], + $result["foo bar"], + $result["foo0bar"], + $result["foo﷽bar"] + ); + } +} diff --git a/crates/target_php/output/enum_collisions/EnumCollisions.php b/crates/target_php/output/enum_collisions/EnumCollisions.php new file mode 100644 index 0000000..61ea9f0 --- /dev/null +++ b/crates/target_php/output/enum_collisions/EnumCollisions.php @@ -0,0 +1,26 @@ +foo = $foo; + $this->foo_bar = $foo_bar; + } + public function serialize() { + $result = []; + $result["foo"] = $this->foo; + $result["foo_bar"] = $this->foo_bar; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["foo"], + $result["foo_bar"] + ); + } +} diff --git a/crates/target_php/output/enum_collisions/EnumCollisionsFoo.php b/crates/target_php/output/enum_collisions/EnumCollisionsFoo.php new file mode 100644 index 0000000..1bb4d83 --- /dev/null +++ b/crates/target_php/output/enum_collisions/EnumCollisionsFoo.php @@ -0,0 +1,21 @@ +bar = $bar; + } + public function serialize() { + $result = []; + $result["bar"] = $this->bar; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["bar"] + ); + } +} diff --git a/crates/target_php/output/enum_collisions/EnumCollisionsFooBar.php b/crates/target_php/output/enum_collisions/EnumCollisionsFooBar.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/crates/target_php/output/enum_collisions/EnumCollisionsFooBar.php @@ -0,0 +1 @@ +http = $http; + $this->id = $id; + $this->nested_id_initialism = $nested_id_initialism; + $this->utf8 = $utf8; + $this->word_with_embedded_id_initialism = $word_with_embedded_id_initialism; + $this->word_with_trailing_initialism_id = $word_with_trailing_initialism_id; + } + public function serialize() { + $result = []; + $result["http"] = $this->http; + $result["id"] = $this->id; + $result["nested_id_initialism"] = $this->nested_id_initialism; + $result["utf8"] = $this->utf8; + $result["word_with_embedded_id_initialism"] = $this->word_with_embedded_id_initialism; + $result["word_with_trailing_initialism_id"] = $this->word_with_trailing_initialism_id; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["http"], + $result["id"], + $result["nested_id_initialism"], + $result["utf8"], + $result["word_with_embedded_id_initialism"], + $result["word_with_trailing_initialism_id"] + ); + } +} diff --git a/crates/target_php/output/initialisms/InitialismsNestedIdInitialism.php b/crates/target_php/output/initialisms/InitialismsNestedIdInitialism.php new file mode 100644 index 0000000..0f82062 --- /dev/null +++ b/crates/target_php/output/initialisms/InitialismsNestedIdInitialism.php @@ -0,0 +1,26 @@ +json = $json; + $this->normalword = $normalword; + } + public function serialize() { + $result = []; + $result["json"] = $this->json; + $result["normalword"] = $this->normalword; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["json"], + $result["normalword"] + ); + } +} diff --git a/crates/target_php/output/keywords/For.php b/crates/target_php/output/keywords/For.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/crates/target_php/output/keywords/For.php @@ -0,0 +1 @@ +for = $for; + $this->object = $object; + } + public function serialize() { + $result = []; + $result["for"] = $this->for; + $result["object"] = $this->object; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["for"], + $result["object"] + ); + } +} diff --git a/crates/target_php/output/keywords/Object.php b/crates/target_php/output/keywords/Object.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/crates/target_php/output/keywords/Object.php @@ -0,0 +1 @@ +bar = $bar; + $this->baz = $baz; + $this->foo = $foo; + $this->quux = $quux; + } + public function serialize() { + $result = []; + $result["bar"] = $this->bar; + $result["baz"] = $this->baz; + $result["foo"] = $this->foo; + $result["quux"] = $this->quux; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["bar"], + $result["baz"], + $result["foo"], + $result["quux"] + ); + } +} diff --git a/crates/target_php/output/nullable_references/NotnullRefNotnullString.php b/crates/target_php/output/nullable_references/NotnullRefNotnullString.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/crates/target_php/output/nullable_references/NotnullRefNotnullString.php @@ -0,0 +1 @@ +notnull_ref_notnull_string = $notnull_ref_notnull_string; + $this->notnull_ref_null_string = $notnull_ref_null_string; + $this->notnull_string = $notnull_string; + $this->null_ref_notnull_string = $null_ref_notnull_string; + $this->null_ref_null_string = $null_ref_null_string; + $this->null_string = $null_string; + } + public function serialize() { + $result = []; + $result["notnull_ref_notnull_string"] = $this->notnull_ref_notnull_string; + $result["notnull_ref_null_string"] = $this->notnull_ref_null_string; + $result["notnull_string"] = $this->notnull_string; + $result["null_ref_notnull_string"] = $this->null_ref_notnull_string; + $result["null_ref_null_string"] = $this->null_ref_null_string; + $result["null_string"] = $this->null_string; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["notnull_ref_notnull_string"], + $result["notnull_ref_null_string"], + $result["notnull_string"], + $result["null_ref_notnull_string"], + $result["null_ref_null_string"], + $result["null_string"] + ); + } +} diff --git a/crates/target_php/output/nullable_timestamp_property/NullableTimestampProperty.php b/crates/target_php/output/nullable_timestamp_property/NullableTimestampProperty.php new file mode 100644 index 0000000..e5c7ac8 --- /dev/null +++ b/crates/target_php/output/nullable_timestamp_property/NullableTimestampProperty.php @@ -0,0 +1,21 @@ +foo = $foo; + } + public function serialize() { + $result = []; + $result["foo"] = $this->foo; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["foo"] + ); + } +} diff --git a/crates/target_php/output/optional_properties/OptionalProperties.php b/crates/target_php/output/optional_properties/OptionalProperties.php new file mode 100644 index 0000000..89c4d1b --- /dev/null +++ b/crates/target_php/output/optional_properties/OptionalProperties.php @@ -0,0 +1,31 @@ +bar = $bar; + $this->baz = $baz; + $this->foo = $foo; + } + public function serialize() { + $result = []; + $result["bar"] = $this->bar; + $result["baz"] = $this->baz; + $result["foo"] = $this->foo; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["bar"], + $result["baz"], + $result["foo"] + ); + } +} diff --git a/crates/target_php/output/property_name_collisions/PropertyNameCollisions.php b/crates/target_php/output/property_name_collisions/PropertyNameCollisions.php new file mode 100644 index 0000000..ed23b8f --- /dev/null +++ b/crates/target_php/output/property_name_collisions/PropertyNameCollisions.php @@ -0,0 +1,26 @@ +foo = $foo; + $this->foo0 = $foo0; + } + public function serialize() { + $result = []; + $result["Foo"] = $this->foo; + $result["foo"] = $this->foo0; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["Foo"], + $result["foo"] + ); + } +} diff --git a/crates/target_php/output/reference/Bar.php b/crates/target_php/output/reference/Bar.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/crates/target_php/output/reference/Bar.php @@ -0,0 +1 @@ +foo = $foo; + $this->foo_bar = $foo_bar; + } + public function serialize() { + $result = []; + $result["foo"] = $this->foo; + $result["foo_bar"] = $this->foo_bar; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["foo"], + $result["foo_bar"] + ); + } +} diff --git a/crates/target_php/output/type_collisions/TypeCollisionsFoo.php b/crates/target_php/output/type_collisions/TypeCollisionsFoo.php new file mode 100644 index 0000000..66ffff4 --- /dev/null +++ b/crates/target_php/output/type_collisions/TypeCollisionsFoo.php @@ -0,0 +1,21 @@ +bar = $bar; + } + public function serialize() { + $result = []; + $result["bar"] = $this->bar; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["bar"] + ); + } +} diff --git a/crates/target_php/output/type_collisions/TypeCollisionsFooBar.php b/crates/target_php/output/type_collisions/TypeCollisionsFooBar.php new file mode 100644 index 0000000..e0041da --- /dev/null +++ b/crates/target_php/output/type_collisions/TypeCollisionsFooBar.php @@ -0,0 +1,21 @@ +x = $x; + } + public function serialize() { + $result = []; + $result["x"] = $this->x; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["x"] + ); + } +} diff --git a/crates/target_php/output/type_collisions/TypeCollisionsFooBar0.php b/crates/target_php/output/type_collisions/TypeCollisionsFooBar0.php new file mode 100644 index 0000000..60cc166 --- /dev/null +++ b/crates/target_php/output/type_collisions/TypeCollisionsFooBar0.php @@ -0,0 +1,21 @@ +x = $x; + } + public function serialize() { + $result = []; + $result["x"] = $this->x; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["x"] + ); + } +} diff --git a/crates/target_php/output/values/Values.php b/crates/target_php/output/values/Values.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/crates/target_php/output/values/Values.php @@ -0,0 +1 @@ + String { + " ".repeat(nesting_level.into()) +} + +macro_rules! nested_writeln { + ($stream:expr, $nl:expr, $fmt:expr $(, $arg:expr)*) => {{ + write!($stream, "{}", nest($nl))?; + writeln!($stream, $fmt $(, $arg)*)?; + Ok::<(), std::io::Error>(()) + }}; +} +fn doc_comment( + out: &mut dyn std::io::Write, + nesting_level: u8, + fields: &[Field], +) -> std::io::Result<()> { + nested_writeln!(out, nesting_level, "/**")?; + for field in fields { + nested_writeln!( + out, + nesting_level, + " * @param {}: {}", + field.name, + field.type_ + )?; + } + nested_writeln!(out, nesting_level, " */")?; + + Ok(()) +} +pub fn generate_constructor( + out: &mut dyn std::io::Write, + nesting_level: u8, + fields: &[Field], +) -> std::io::Result<()> { + doc_comment(out, nesting_level, fields)?; + nested_writeln!( + out, + nesting_level, + "public function __construct({}) {{", + fields + .iter() + .map(|x| format!("${}", x.name)) + .collect::>() + .join(", ") + )?; + for field in fields { + nested_writeln!(out, nesting_level + 1, "$this->{0} = ${0};", field.name)?; + } + nested_writeln!(out, nesting_level, "}}")?; + Ok(()) +} + +pub fn generate_serializer( + out: &mut dyn std::io::Write, + nesting_level: u8, + fields: &[Field], +) -> std::io::Result<()> { + nested_writeln!( + out, + nesting_level, + "public function serialize() {{" + )?; + nested_writeln!(out, nesting_level + 1, "$result = [];")?; + + for field in fields { + nested_writeln!(out, nesting_level + 1, "$result[\"{}\"] = $this->{};", field.json_name, field.name)?; + } + nested_writeln!(out, nesting_level + 1, "return $result;")?; + nested_writeln!(out, nesting_level, "}}")?; + Ok(()) +} + +pub fn generate_deserializer( + out: &mut dyn std::io::Write, + nesting_level: u8, + fields: &[Field], +) -> std::io::Result<()> { + nested_writeln!( + out, + nesting_level, + "public static function deserialize($data) {{" + )?; + + nested_writeln!(out, nesting_level + 1, "return new self(")?; + for (i, field) in fields.into_iter().enumerate() { + let separator = if i == fields.len() - 1 { "" } else { "," }; + nested_writeln!(out, nesting_level + 2, "$data[\"{}\"]{}", field.json_name, separator)?; + } + nested_writeln!(out, nesting_level + 1, ");")?; + nested_writeln!(out, nesting_level, "}}")?; + Ok(()) +} diff --git a/crates/target_php/src/lib.rs b/crates/target_php/src/lib.rs index abd5b4b..cce9b0d 100644 --- a/crates/target_php/src/lib.rs +++ b/crates/target_php/src/lib.rs @@ -1,3 +1,11 @@ +use jetted_core::target::inflect::{self, Inflector}; + +mod codegen; + +const TYPE_INFLECTOR: inflect::CombiningInflector = + inflect::CombiningInflector::new(inflect::Case::pascal_case()); +const FIELD_INFLECTOR: inflect::TailInflector = + inflect::TailInflector::new(inflect::Case::snake_case()); pub struct Target {} impl Target { pub fn new() -> Self { @@ -9,15 +17,65 @@ impl jetted_core::target::Target for Target { type FileState = (); fn strategy(&self) -> jetted_core::target::Strategy { - todo!() + jetted_core::target::Strategy { + file_partitioning: jetted_core::target::FilePartitioningStrategy::FilePerType( + "php".to_owned(), + ), + enum_member_naming: jetted_core::target::EnumMemberNamingStrategy::Modularized, + optional_property_handling: + jetted_core::target::OptionalPropertyHandlingStrategy::NativeSupport, + booleans_are_nullable: true, + int8s_are_nullable: true, + uint8s_are_nullable: true, + int16s_are_nullable: true, + uint16s_are_nullable: true, + int32s_are_nullable: true, + uint32s_are_nullable: true, + float32s_are_nullable: true, + float64s_are_nullable: true, + strings_are_nullable: true, + timestamps_are_nullable: true, + arrays_are_nullable: true, + dicts_are_nullable: true, + aliases_are_nullable: true, + enums_are_nullable: true, + structs_are_nullable: true, + discriminators_are_nullable: true, + } } fn name(&self, kind: jetted_core::target::NameableKind, name_parts: &[String]) -> String { - todo!() + match kind { + jetted_core::target::NameableKind::Type => TYPE_INFLECTOR.inflect(name_parts), + jetted_core::target::NameableKind::Field => FIELD_INFLECTOR.inflect(name_parts), + jetted_core::target::NameableKind::EnumMember => TYPE_INFLECTOR.inflect(name_parts), + } } - fn expr(&self, state: &mut Self::FileState, metadata: jetted_core::target::metadata::Metadata, expr: jetted_core::target::Expr) -> String { - todo!() + fn expr( + &self, + state: &mut Self::FileState, + metadata: jetted_core::target::metadata::Metadata, + expr: jetted_core::target::Expr, + ) -> String { + match expr { + jetted_core::target::Expr::Empty => "", + jetted_core::target::Expr::Boolean => "boolean", + jetted_core::target::Expr::Int8 => "int", + jetted_core::target::Expr::Uint8 => "int", + jetted_core::target::Expr::Int16 => "int", + jetted_core::target::Expr::Uint16 => "int", + jetted_core::target::Expr::Int32 => "int", + jetted_core::target::Expr::Uint32 => "int", + jetted_core::target::Expr::Float32 => "float", + jetted_core::target::Expr::Float64 => "float", + jetted_core::target::Expr::String => "string", + jetted_core::target::Expr::Timestamp => "", + jetted_core::target::Expr::ArrayOf(_) => "mixed", + jetted_core::target::Expr::DictOf(_) => "mixed", + jetted_core::target::Expr::NullableOf(_) => "", + } + .to_owned() } fn item( @@ -26,7 +84,64 @@ impl jetted_core::target::Target for Target { state: &mut Self::FileState, item: jetted_core::target::Item, ) -> jetted_core::Result> { - todo!() + let result = match item { + jetted_core::target::Item::Auxiliary { .. } => None, + jetted_core::target::Item::Preamble => { + writeln!(out, " None, + jetted_core::target::Item::Struct { + metadata, + name, + has_additional, + fields, + } => { + writeln!(out, "final class {name} {{")?; + for field in &fields { + writeln!(out, " private ${};", field.name)? + } + write!(out, "\n")?; + codegen::generate_constructor(out, 1, &fields)?; + codegen::generate_serializer(out, 1, &fields)?; + codegen::generate_deserializer(out, 1, &fields)?; + + writeln!(out, "}}")?; + None + } + + _ => None, + /* + jetted_core::target::Item::Alias { + metadata, + name, + type_, + } => todo!(), + jetted_core::target::Item::Enum { + metadata, + name, + members, + } => todo!(), + jetted_core::target::Item::Discriminator { + metadata, + name, + tag_field_name, + tag_json_name, + variants, + } => todo!(), + jetted_core::target::Item::DiscriminatorVariant { + metadata, + name, + parent_name, + tag_field_name, + tag_json_name, + tag_value, + has_additional, + fields, + } => todo!(), + */ + }; + Ok(result) } } @@ -42,4 +157,3 @@ mod tests { jetted_test::strict_std_test_case!(&crate::Target::new(), empty_and_nonascii_enum_values); } } - From dcd2f185333b3811f264bea12c6677cec686950e Mon Sep 17 00:00:00 2001 From: Eugenio Tampieri Date: Thu, 6 Nov 2025 16:15:11 +0100 Subject: [PATCH 3/4] Reformat code --- crates/target_php/src/codegen.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/crates/target_php/src/codegen.rs b/crates/target_php/src/codegen.rs index 7887500..4d15b06 100644 --- a/crates/target_php/src/codegen.rs +++ b/crates/target_php/src/codegen.rs @@ -58,15 +58,17 @@ pub fn generate_serializer( nesting_level: u8, fields: &[Field], ) -> std::io::Result<()> { - nested_writeln!( - out, - nesting_level, - "public function serialize() {{" - )?; + nested_writeln!(out, nesting_level, "public function serialize() {{")?; nested_writeln!(out, nesting_level + 1, "$result = [];")?; for field in fields { - nested_writeln!(out, nesting_level + 1, "$result[\"{}\"] = $this->{};", field.json_name, field.name)?; + nested_writeln!( + out, + nesting_level + 1, + "$result[\"{}\"] = $this->{};", + field.json_name, + field.name + )?; } nested_writeln!(out, nesting_level + 1, "return $result;")?; nested_writeln!(out, nesting_level, "}}")?; @@ -87,7 +89,13 @@ pub fn generate_deserializer( nested_writeln!(out, nesting_level + 1, "return new self(")?; for (i, field) in fields.into_iter().enumerate() { let separator = if i == fields.len() - 1 { "" } else { "," }; - nested_writeln!(out, nesting_level + 2, "$data[\"{}\"]{}", field.json_name, separator)?; + nested_writeln!( + out, + nesting_level + 2, + "$data[\"{}\"]{}", + field.json_name, + separator + )?; } nested_writeln!(out, nesting_level + 1, ");")?; nested_writeln!(out, nesting_level, "}}")?; From 7c382f1393da3d64741c13ff2d58577d75734871 Mon Sep 17 00:00:00 2001 From: Eugenio Tampieri Date: Thu, 6 Nov 2025 18:20:21 +0100 Subject: [PATCH 4/4] Add PHP to the CLI --- Cargo.lock | 1 + crates/cli/Cargo.toml | 1 + crates/cli/src/cli.yaml | 14 ++++++++++++++ crates/cli/src/main.rs | 14 ++++++++++++++ crates/target_php/src/codegen.rs | 18 ++++++++++++++++++ crates/target_php/src/lib.rs | 24 ++++++++++++++++++------ 6 files changed, 66 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c6e8b37..76a0a84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -198,6 +198,7 @@ dependencies = [ "jetted_target_csharp_system_text", "jetted_target_go", "jetted_target_java_jackson", + "jetted_target_php", "jetted_target_python", "jetted_target_ruby", "jetted_target_ruby_sig", diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index f708850..468a343 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -21,6 +21,7 @@ jetted_target_ruby = { path = "../target_ruby" } jetted_target_ruby_sig = { path = "../target_ruby_sig" } jetted_target_rust = { path = "../target_rust" } jetted_target_typescript = { path = "../target_typescript" } +jetted_target_php = { path = "../target_php" } serde = "1.0" serde_json = "1.0" jtd = "0.2.1" diff --git a/crates/cli/src/cli.yaml b/crates/cli/src/cli.yaml index 8a9eb46..e5c7367 100644 --- a/crates/cli/src/cli.yaml +++ b/crates/cli/src/cli.yaml @@ -110,3 +110,17 @@ args: long: typescript-out takes_value: true value_name: dir + + # PHP + - php-out: + help: Output directory for PHP code generation + long: php-out + takes_value: true + value_name: dir + requires: + - php-namespace + - php-namespace: + help: Namespace for PHP generated types + long: php-namespace + takes_value: true + value_name: namespace diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 1d0b00f..1fc4e5e 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -155,6 +155,20 @@ fn main() -> Result<()> { log.finish("TypeScript", &codegen_info); } + if let Some(out_dir) = matches.value_of("php-out") { + log.start("PHP", out_dir); + + let namespace = matches.value_of("php-namespace").unwrap().to_owned(); + + let target = jetted_target_php::Target::new(Some(namespace)); + + let codegen_info = + jetted_core::codegen(&target, root_name.clone(), &schema, &Path::new(out_dir)) + .with_context(|| "Failed to generate PHP code")?; + + log.finish("PHP", &codegen_info); + } + log.flush(); Ok(()) } diff --git a/crates/target_php/src/codegen.rs b/crates/target_php/src/codegen.rs index 4d15b06..6415036 100644 --- a/crates/target_php/src/codegen.rs +++ b/crates/target_php/src/codegen.rs @@ -101,3 +101,21 @@ pub fn generate_deserializer( nested_writeln!(out, nesting_level, "}}")?; Ok(()) } + +pub fn generate_getters( + out: &mut dyn std::io::Write, + nesting_level: u8, + fields: &[Field], +) -> std::io::Result<()> { + for field in fields { + nested_writeln!( + out, + nesting_level, + "public function get_{}() {{", + field.name + )?; + nested_writeln!(out, nesting_level + 1, "return $this->{};", field.name)?; + nested_writeln!(out, nesting_level, "}}")?; + } + Ok(()) +} diff --git a/crates/target_php/src/lib.rs b/crates/target_php/src/lib.rs index cce9b0d..5ab6cbb 100644 --- a/crates/target_php/src/lib.rs +++ b/crates/target_php/src/lib.rs @@ -6,10 +6,12 @@ const TYPE_INFLECTOR: inflect::CombiningInflector = inflect::CombiningInflector::new(inflect::Case::pascal_case()); const FIELD_INFLECTOR: inflect::TailInflector = inflect::TailInflector::new(inflect::Case::snake_case()); -pub struct Target {} +pub struct Target { + namespace: Option, +} impl Target { - pub fn new() -> Self { - Self {} + pub fn new(namespace: Option) -> Self { + Self { namespace } } } @@ -88,6 +90,9 @@ impl jetted_core::target::Target for Target { jetted_core::target::Item::Auxiliary { .. } => None, jetted_core::target::Item::Preamble => { writeln!(out, " None, @@ -105,6 +110,7 @@ impl jetted_core::target::Target for Target { codegen::generate_constructor(out, 1, &fields)?; codegen::generate_serializer(out, 1, &fields)?; codegen::generate_deserializer(out, 1, &fields)?; + codegen::generate_getters(out, 1, &fields)?; writeln!(out, "}}")?; None @@ -148,12 +154,18 @@ impl jetted_core::target::Target for Target { #[cfg(test)] mod tests { mod std_tests { - jetted_test::std_test_cases!(&crate::Target::new()); + jetted_test::std_test_cases!(&crate::Target::new(None)); } mod optional_std_tests { - jetted_test::strict_std_test_case!(&crate::Target::new(), empty_and_nonascii_properties); + jetted_test::strict_std_test_case!( + &crate::Target::new(None), + empty_and_nonascii_properties + ); - jetted_test::strict_std_test_case!(&crate::Target::new(), empty_and_nonascii_enum_values); + jetted_test::strict_std_test_case!( + &crate::Target::new(None), + empty_and_nonascii_enum_values + ); } }