diff --git a/cmd/tlgen/main2.go b/cmd/tlgen/main2.go index 2687ddb..bdb9093 100644 --- a/cmd/tlgen/main2.go +++ b/cmd/tlgen/main2.go @@ -76,6 +76,10 @@ func parseFlags(opt *tlcodegen.Gen2Options) { flag.StringVar(&opt.RootCPPNamespace, "cpp-namespace", "", `c++ root namespace, separated by '::' if more than 1 element`) + // PHP + flag.BoolVar(&opt.AddFunctionBodies, "php-serialization-bodies", false, + `whether to generate body to write/read generated structs and functions`) + // .tlo flag.StringVar(&opt.TLOPath, "tloPath", "", "whether to serialize TL schema in binary form") diff --git a/internal/tlcodegen/tlgen.go b/internal/tlcodegen/tlgen.go index 9001243..ab905ce 100644 --- a/internal/tlcodegen/tlgen.go +++ b/internal/tlcodegen/tlgen.go @@ -331,6 +331,9 @@ type Gen2Options struct { RootCPPNamespace string SeparateFiles bool + // PHP + AddFunctionBodies bool + // .tlo TLOPath string CanonicalFormPath string // combinators in canonical form, with comment of source schema file path diff --git a/internal/tlcodegen/type_rw_bool.go b/internal/tlcodegen/type_rw_bool.go index 4a3e085..5093806 100644 --- a/internal/tlcodegen/type_rw_bool.go +++ b/internal/tlcodegen/type_rw_bool.go @@ -8,7 +8,6 @@ package tlcodegen import ( "fmt" - "strings" ) type TypeRWBool struct { @@ -110,26 +109,3 @@ func (trw *TypeRWBool) typeJSONReadingCode(bytesVersion bool, directImports *Dir func (trw *TypeRWBool) typeJSON2ReadingCode(bytesVersion bool, directImports *DirectImports, ins *InternalNamespace, jvalue string, val string, natArgs []string, ref bool) string { return wrapLast(false, fmt.Sprintf("%sJson2ReadBool(%s, %s)", trw.wr.gen.InternalPrefix(), jvalue, addAmpersand(ref, val))) } - -func (trw *TypeRWBool) PhpClassName(withPath bool, bare bool) string { - return "boolean" -} - -func (trw *TypeRWBool) PhpClassNameReplaced() bool { - return true -} - -func (trw *TypeRWBool) PhpTypeName(withPath bool, bare bool) string { - return trw.PhpClassName(withPath, true) -} - -func (trw *TypeRWBool) PhpGenerateCode(code *strings.Builder, bytes bool) error { - return fmt.Errorf("boolean doesn't have php code") -} - -func (trw *TypeRWBool) PhpDefaultValue() string { - return "false" -} - -func (trw *TypeRWBool) PhpIterateReachableTypes(reachableTypes *map[*TypeRWWrapper]bool) { -} diff --git a/internal/tlcodegen/type_rw_bool_php.go b/internal/tlcodegen/type_rw_bool_php.go new file mode 100644 index 0000000..1a2097b --- /dev/null +++ b/internal/tlcodegen/type_rw_bool_php.go @@ -0,0 +1,29 @@ +package tlcodegen + +import ( + "fmt" + "strings" +) + +func (trw *TypeRWBool) PhpClassName(withPath bool, bare bool) string { + return "boolean" +} + +func (trw *TypeRWBool) PhpClassNameReplaced() bool { + return true +} + +func (trw *TypeRWBool) PhpTypeName(withPath bool, bare bool) string { + return trw.PhpClassName(withPath, true) +} + +func (trw *TypeRWBool) PhpGenerateCode(code *strings.Builder, bytes bool) error { + return fmt.Errorf("boolean doesn't have php code") +} + +func (trw *TypeRWBool) PhpDefaultValue() string { + return "false" +} + +func (trw *TypeRWBool) PhpIterateReachableTypes(reachableTypes *map[*TypeRWWrapper]bool) { +} diff --git a/internal/tlcodegen/type_rw_maybe.go b/internal/tlcodegen/type_rw_maybe.go index d9e4ee0..b2068e4 100644 --- a/internal/tlcodegen/type_rw_maybe.go +++ b/internal/tlcodegen/type_rw_maybe.go @@ -8,7 +8,6 @@ package tlcodegen import ( "fmt" - "strings" ) type TypeRWMaybe struct { @@ -119,37 +118,3 @@ func (trw *TypeRWMaybe) typeJSONReadingCode(bytesVersion bool, directImports *Di func (trw *TypeRWMaybe) typeJSON2ReadingCode(bytesVersion bool, directImports *DirectImports, ins *InternalNamespace, jvalue string, val string, natArgs []string, ref bool) string { return fmt.Sprintf("if err := %s.ReadJSON(legacyTypeNames, %s %s); err != nil { return err }", val, jvalue, joinWithCommas(natArgs)) } - -func (trw *TypeRWMaybe) PhpClassName(withPath bool, bare bool) string { - target := trw.getInnerTarget() - return "maybe_" + target.t.trw.PhpClassName(withPath, target.bare) -} - -func (trw *TypeRWMaybe) PhpClassNameReplaced() bool { - return true -} - -func (trw *TypeRWMaybe) PhpTypeName(withPath bool, bare bool) string { - target := trw.getInnerTarget() - return target.t.trw.PhpTypeName(withPath, target.t.PHPIsBare()) + "|null" -} - -func (trw *TypeRWMaybe) getInnerTarget() Field { - if inner, ok := trw.element.t.trw.(*TypeRWMaybe); ok { - return inner.getInnerTarget() - } else { - return trw.element - } -} - -func (trw *TypeRWMaybe) PhpGenerateCode(code *strings.Builder, bytes bool) error { - return fmt.Errorf("maybe doesn't have php code") -} - -func (trw *TypeRWMaybe) PhpDefaultValue() string { - return "null" -} - -func (trw *TypeRWMaybe) PhpIterateReachableTypes(reachableTypes *map[*TypeRWWrapper]bool) { - trw.element.t.PhpIterateReachableTypes(reachableTypes) -} diff --git a/internal/tlcodegen/type_rw_maybe_php.go b/internal/tlcodegen/type_rw_maybe_php.go new file mode 100644 index 0000000..b3805cc --- /dev/null +++ b/internal/tlcodegen/type_rw_maybe_php.go @@ -0,0 +1,40 @@ +package tlcodegen + +import ( + "fmt" + "strings" +) + +func (trw *TypeRWMaybe) PhpClassName(withPath bool, bare bool) string { + target := trw.getInnerTarget() + return "maybe_" + target.t.trw.PhpClassName(withPath, target.bare) +} + +func (trw *TypeRWMaybe) PhpClassNameReplaced() bool { + return true +} + +func (trw *TypeRWMaybe) PhpTypeName(withPath bool, bare bool) string { + target := trw.getInnerTarget() + return target.t.trw.PhpTypeName(withPath, target.t.PHPIsBare()) + "|null" +} + +func (trw *TypeRWMaybe) getInnerTarget() Field { + if inner, ok := trw.element.t.trw.(*TypeRWMaybe); ok { + return inner.getInnerTarget() + } else { + return trw.element + } +} + +func (trw *TypeRWMaybe) PhpGenerateCode(code *strings.Builder, bytes bool) error { + return fmt.Errorf("maybe doesn't have php code") +} + +func (trw *TypeRWMaybe) PhpDefaultValue() string { + return "null" +} + +func (trw *TypeRWMaybe) PhpIterateReachableTypes(reachableTypes *map[*TypeRWWrapper]bool) { + trw.element.t.PhpIterateReachableTypes(reachableTypes) +} diff --git a/internal/tlcodegen/type_rw_primitive.go b/internal/tlcodegen/type_rw_primitive.go index 526524d..b789a0a 100644 --- a/internal/tlcodegen/type_rw_primitive.go +++ b/internal/tlcodegen/type_rw_primitive.go @@ -9,7 +9,6 @@ package tlcodegen import ( "fmt" "log" - "strings" ) type TypeRWPrimitive struct { @@ -171,43 +170,3 @@ func (trw *TypeRWPrimitive) typeJSON2ReadingCode(bytesVersion bool, directImport func (trw *TypeRWPrimitive) GenerateCode(byteVersion bool, directImports *DirectImports) string { return "" } - -func (trw *TypeRWPrimitive) PhpClassName(withPath bool, bare bool) string { - switch trw.goType { - case "int32", "int64", "uint32": - return "int" - case "string": - return "string" - case "float32", "float64": - return "float" - default: - return fmt.Sprintf("", trw.tlType) - } -} - -func (trw *TypeRWPrimitive) PhpClassNameReplaced() bool { - return true -} - -func (trw *TypeRWPrimitive) PhpTypeName(withPath bool, bare bool) string { - return trw.PhpClassName(withPath, true) -} - -func (trw *TypeRWPrimitive) PhpGenerateCode(code *strings.Builder, bytes bool) error { - return fmt.Errorf("primitives don't have php code") -} - -func (trw *TypeRWPrimitive) PhpDefaultValue() string { - switch trw.goType { - case "int32", "int64", "uint32": - return "0" - case "string": - return "''" - case "float32", "float64": - return "0.0" - default: - return fmt.Sprintf("", trw.tlType) - } -} - -func (trw *TypeRWPrimitive) PhpIterateReachableTypes(reachableTypes *map[*TypeRWWrapper]bool) {} diff --git a/internal/tlcodegen/type_rw_primitive_php.go b/internal/tlcodegen/type_rw_primitive_php.go new file mode 100644 index 0000000..81438f6 --- /dev/null +++ b/internal/tlcodegen/type_rw_primitive_php.go @@ -0,0 +1,46 @@ +package tlcodegen + +import ( + "fmt" + "strings" +) + +func (trw *TypeRWPrimitive) PhpClassName(withPath bool, bare bool) string { + switch trw.goType { + case "int32", "int64", "uint32": + return "int" + case "string": + return "string" + case "float32", "float64": + return "float" + default: + return fmt.Sprintf("", trw.tlType) + } +} + +func (trw *TypeRWPrimitive) PhpClassNameReplaced() bool { + return true +} + +func (trw *TypeRWPrimitive) PhpTypeName(withPath bool, bare bool) string { + return trw.PhpClassName(withPath, true) +} + +func (trw *TypeRWPrimitive) PhpGenerateCode(code *strings.Builder, bytes bool) error { + return fmt.Errorf("primitives don't have php code") +} + +func (trw *TypeRWPrimitive) PhpDefaultValue() string { + switch trw.goType { + case "int32", "int64", "uint32": + return "0" + case "string": + return "''" + case "float32", "float64": + return "0.0" + default: + return fmt.Sprintf("", trw.tlType) + } +} + +func (trw *TypeRWPrimitive) PhpIterateReachableTypes(reachableTypes *map[*TypeRWWrapper]bool) {} diff --git a/internal/tlcodegen/type_rw_struct.go b/internal/tlcodegen/type_rw_struct.go index 01cf477..8d4f505 100644 --- a/internal/tlcodegen/type_rw_struct.go +++ b/internal/tlcodegen/type_rw_struct.go @@ -8,10 +8,8 @@ package tlcodegen import ( "fmt" - "github.com/vkcom/tl/internal/utils" "log" "sort" - "strings" ) type TypeRWStruct struct { @@ -566,616 +564,3 @@ func (trw *TypeRWStruct) typeJSON2ReadingCode(bytesVersion bool, directImports * } return fmt.Sprintf("if err := %s.ReadJSON(legacyTypeNames, %s %s); err != nil { return err }", val, jvalue, joinWithCommas(natArgs)) } - -func (trw *TypeRWStruct) PhpClassNameReplaced() bool { - unionParent := trw.PhpConstructorNeedsUnion() - if unionParent == nil { - if len(trw.Fields) == 1 && trw.ResultType == nil && trw.Fields[0].fieldMask == nil { - return true - } - - if trw.ResultType == nil && trw.wr.PHPIsTrueType() { - return true - } - - isDict, _, _, _ := isDictionaryElement(trw.wr) - if isDict && trw.wr.tlName.Namespace == "" { // TODO NOT A SOLUTION, BUT... - return true - } - } - return false -} - -func (trw *TypeRWStruct) PhpClassName(withPath bool, bare bool) string { - if PHPSpecialMembersTypes(trw.wr) != "" { - return "" - } - unionParent := trw.PhpConstructorNeedsUnion() - if unionParent == nil { - if len(trw.Fields) == 1 && trw.ResultType == nil && trw.Fields[0].fieldMask == nil { - return trw.Fields[0].t.trw.PhpClassName(withPath, bare) - } - - if trw.ResultType == nil && trw.wr.PHPIsTrueType() { - return "boolean" - } - - isDict, _, _, valueType := isDictionaryElement(trw.wr) - if isDict && trw.wr.tlName.Namespace == "" { // TODO NOT A SOLUTION, BUT... - return valueType.t.trw.PhpClassName(withPath, bare) - } - } - - name := trw.wr.tlName.Name - if !bare { - name = trw.wr.origTL[0].TypeDecl.Name.Name - } - if trw.wr.tlName.Namespace != "" { - name = fmt.Sprintf("%s_%s", trw.wr.tlName.Namespace, name) - } - - elems := make([]string, 0, len(trw.wr.arguments)) - for _, arg := range trw.wr.arguments { - if arg.tip != nil { - argText := arg.tip.trw.PhpClassName(false, false) - if argText != "" { - elems = append(elems, "__", argText) - } - } - } - - name += strings.Join(elems, "") - if withPath { - name = trw.wr.PHPTypePath() + name - } - return name -} - -func (trw *TypeRWStruct) PhpTypeName(withPath bool, bare bool) string { - if specialCase := PHPSpecialMembersTypes(trw.wr); specialCase != "" { - return specialCase - } - unionParent := trw.PhpConstructorNeedsUnion() - if unionParent == nil { - if len(trw.Fields) == 1 && trw.ResultType == nil && trw.Fields[0].fieldMask == nil { - return trw.Fields[0].t.trw.PhpTypeName(withPath, trw.Fields[0].bare) - } - isDict, _, _, valueType := isDictionaryElement(trw.wr) - if isDict && trw.wr.tlName.Namespace == "" { // TODO NOT A SOLUTION, BUT... - return valueType.t.trw.PhpTypeName(withPath, bare) - } - } - return trw.PhpClassName(withPath, bare) -} - -func (trw *TypeRWStruct) PhpGenerateCode(code *strings.Builder, bytes bool) error { - trw.PHPStructHeader(code) - trw.PHPStructFieldMasks(code) - trw.PHPStructFields(code) - trw.PHPStructResultType(code) - - necessaryFieldsInConstructor := make([]Field, 0) - usedFieldMasksIndecies := make([]int, 0) - usedFieldMasks := make(map[int][]Field) - for _, f := range trw.Fields { - if f.fieldMask == nil { - necessaryFieldsInConstructor = append(necessaryFieldsInConstructor, f) - } else { - index := f.fieldMask.FieldIndex - if !f.fieldMask.isField { - for i, argument := range trw.wr.origTL[0].TemplateArguments { - if argument.IsNat && argument.FieldName == f.fieldMask.name { - index = -(i + 1) - break - } - } - } - if usedFieldMasks[index] == nil { - usedFieldMasksIndecies = append(usedFieldMasksIndecies, index) - } - usedFieldMasks[index] = append(usedFieldMasks[index], f) - } - } - - trw.PHPStructConstructor(code, necessaryFieldsInConstructor) - trw.PHPStructRPCSpecialGetters(code) - trw.PHPStructFieldMaskCalculators(code, usedFieldMasksIndecies, usedFieldMasks) - trw.PHPStructFunctionSpecificMethods(code) - - code.WriteString("\n}\n") - - trw.PHPStructFunctionSpecificTypes(code) - return nil -} - -func (trw *TypeRWStruct) PHPStructFunctionSpecificTypes(code *strings.Builder) { - if trw.ResultType != nil { - code.WriteString( - fmt.Sprintf( - ` -/** - * @kphp-tl-class - */ -class %[1]s_result implements TL\RpcFunctionReturnResult { - - /** @var %[2]s */ - public $value = %[3]s; - -} -`, - trw.PhpClassName(false, true), - phpResultType(trw), - trw.ResultType.trw.PhpDefaultValue(), - ), - ) - } -} - -func (trw *TypeRWStruct) PHPStructFunctionSpecificMethods(code *strings.Builder) { - // print function specific methods and types - if trw.ResultType != nil { - kphpSpecialCode := "" - if trw.wr.HasAnnotation("kphp") { - kphpSpecialCode = fmt.Sprintf( - ` - - /** - * @param %[1]s $value - * @return %[2]s_result - */ - public static function createRpcServerResponse($value) { - $response = new %[2]s_result(); - $response->value = $value; - return $response; - }`, - trw.ResultType.trw.PhpTypeName(true, true), - trw.PhpClassName(true, true), - ) - } - - code.WriteString( - fmt.Sprintf( - ` - /** - * @param TL\RpcFunctionReturnResult $function_return_result - * @return %[4]s - */ - public static function functionReturnValue($function_return_result) { - if ($function_return_result instanceof %[1]s_result) { - return $function_return_result->value; - } - warning('Unexpected result type in functionReturnValue: ' . ($function_return_result ? get_class($function_return_result) : 'null')); - return (new %[1]s_result())->value; - } - - /** - * @kphp-inline - * - * @param TL\RpcResponse $response - * @return %[4]s - */ - public static function result(TL\RpcResponse $response) { - return self::functionReturnValue($response->getResult()); - }%[5]s - - /** - * @kphp-inline - * - * @return string - */ - public function getTLFunctionName() { - return '%[3]s'; - } -`, - trw.PhpClassName(false, true), - trw.PhpClassName(true, true), - trw.wr.tlName.String(), - phpResultType(trw), - kphpSpecialCode, - ), - ) - - } -} - -func (trw *TypeRWStruct) PHPStructFieldMaskCalculators(code *strings.Builder, usedFieldMasksIndecies []int, usedFieldMasks map[int][]Field) { - // print methods to calculate fieldmasks - // fix order - names := utils.MapSlice(usedFieldMasksIndecies, func(natIndex int) string { - natName := "" - if natIndex < 0 { - natName = trw.wr.origTL[0].TemplateArguments[-(natIndex + 1)].FieldName - } else { - natName = trw.Fields[natIndex].originalName - } - return natName - }) - - namesToIndices := make(map[string]int) - for i := range names { - namesToIndices[names[i]] = usedFieldMasksIndecies[i] - } - sort.Strings(names) - - fieldNameToFieldOrder := make(map[string]int) - for i := range trw.Fields { - fieldNameToFieldOrder[trw.Fields[i].originalName] = i - } - - for _, name := range names { - natIndex := namesToIndices[name] - natName := name - code.WriteString(` - /**`) - additionalArgs := make([]string, 0) - // arguments with ambiguous existence - for _, dependentField := range usedFieldMasks[natIndex] { - if _, isMaybe := dependentField.t.PHPGenCoreType().trw.(*TypeRWMaybe); isMaybe { - additionalArgs = append(additionalArgs, fmt.Sprintf("$has_%s", dependentField.originalName)) - code.WriteString(fmt.Sprintf("\n * @param bool $has_%s", dependentField.originalName)) - } - } - code.WriteString(` - * @return int - */ -`, - ) - code.WriteString( - fmt.Sprintf( - " public function calculate%[1]s(%[2]s) {\n $mask = 0;\n", - toPhpFieldMaskName(natName), - strings.Join(additionalArgs, ", "), - ), - ) - - fields := usedFieldMasks[natIndex] - sort.Slice(fields, func(i, j int) bool { - if fields[i].BitNumber == fields[j].BitNumber { - return i < j - } - return fields[i].BitNumber < fields[j].BitNumber - }) - - fieldsGroupedByBitNumber := make([][]Field, 0) - for _, dependentField := range fields { - if len(fieldsGroupedByBitNumber) == 0 || - fieldsGroupedByBitNumber[len(fieldsGroupedByBitNumber)-1][0].BitNumber != dependentField.BitNumber { - fieldsGroupedByBitNumber = append(fieldsGroupedByBitNumber, make([]Field, 0)) - } - fieldsGroupedByBitNumber[len(fieldsGroupedByBitNumber)-1] = append(fieldsGroupedByBitNumber[len(fieldsGroupedByBitNumber)-1], dependentField) - } - - for _, dependentFields := range fieldsGroupedByBitNumber { - conditions := make([]string, 0) - bitConstants := make([]string, 0) - sort.Slice(dependentFields, func(i, j int) bool { - return fieldNameToFieldOrder[dependentFields[i].originalName] < fieldNameToFieldOrder[dependentFields[j].originalName] - }) - for _, dependentField := range dependentFields { - condition := "" - if dependentField.t.PHPIsTrueType() || dependentField.t.PHPNeedsCode() { - condition = fmt.Sprintf( - "$this->%[1]s", - dependentField.originalName, - ) - } else if _, isMaybe := dependentField.t.PHPGenCoreType().trw.(*TypeRWMaybe); isMaybe { - condition = fmt.Sprintf("$has_%s", dependentField.originalName) - } else { - condition = fmt.Sprintf( - "$this->%[1]s !== null", - dependentField.originalName, - ) - } - conditions = append(conditions, condition) - bitConstants = append(bitConstants, fmt.Sprintf( - "self::BIT_%[1]s_%[2]d", - strings.ToUpper(dependentField.originalName), - dependentField.BitNumber)) - } - - finalCondition := conditions[0] - finalMask := bitConstants[0] - - if len(conditions) > 1 { - finalCondition = strings.Join(conditions, " && ") - finalMask = "(" + strings.Join(bitConstants, " | ") + ")" - } - - code.WriteString( - fmt.Sprintf( - ` - if (%[1]s) { - $mask |= %[2]s; - } -`, - finalCondition, - finalMask, - ), - ) - } - - code.WriteString("\n return $mask;\n") - code.WriteString(" }\n") - } -} - -func (trw *TypeRWStruct) PHPStructConstructor(code *strings.Builder, necessaryFieldsInConstructor []Field) { - // print constructor - code.WriteString(` - /** -`) - for _, f := range necessaryFieldsInConstructor { - fieldType, _ := fieldTypeAndDefaultValue(f) - code.WriteString(fmt.Sprintf(" * @param %[1]s $%[2]s\n", fieldType, f.originalName)) - } - if len(necessaryFieldsInConstructor) == 0 { - code.WriteString(" * @kphp-inline\n") - } - - code.WriteString(` */ -`) - code.WriteString(" public function __construct(") - - for i, f := range necessaryFieldsInConstructor { - _, defaultValue := fieldTypeAndDefaultValue(f) - if i != 0 { - code.WriteString(", ") - } - code.WriteString(fmt.Sprintf("$%[1]s = %[2]s", f.originalName, defaultValue)) - } - - code.WriteString(") {\n") - for _, f := range necessaryFieldsInConstructor { - code.WriteString(fmt.Sprintf(" $this->%[1]s = $%[1]s;\n", f.originalName)) - } - code.WriteString(" }\n") -} - -func (trw *TypeRWStruct) PHPStructRPCSpecialGetters(code *strings.Builder) { - if unionParent := trw.wr.PHPUnionParent(); unionParent == nil || PHPSpecialMembersTypes(unionParent) == "" { - return - } - - const ThisType = "__this" - type SpecialField struct { - Name string - Type string - Default string - NullTypeIfNullValue bool - AddHasMethod bool - } - - fields := []SpecialField{ - { - "result", - "TL\\RpcFunctionReturnResult", - "null", - true, - false, - }, - { - "header", - ThisType, - "null", - true, - false, - }, - { - "error", - ThisType, - "null", - true, - true, - }, - } - - containsSuchField := func(name, ifTrue, ifFalse string) string { - for _, field := range trw.Fields { - if field.originalName == name { - return ifTrue - } - } - return ifFalse - } - - for _, field := range fields { - returnObject := field.Default - returnType := field.Default - if field.Type == ThisType && - strings.Contains(strings.ToLower(trw.PhpClassName(false, true)), strings.ToLower(field.Name)) { - returnObject = "$this" - returnType = trw.PhpTypeName(true, true) - } else { - if field.Type != ThisType { - returnObject = "$this->" + field.Name - returnType = field.Type - } - if field.NullTypeIfNullValue { - returnType = containsSuchField(field.Name, returnType, "null") - returnObject = containsSuchField(field.Name, returnObject, "null") - } - } - if field.AddHasMethod { - code.WriteString( - fmt.Sprintf( - ` - /** - * @return bool - */ - public function is%[1]s() { - return %[2]s; - } -`, - ToUpperFirst(field.Name), - containsSuchField(field.Name, "true", "false"), - ), - ) - } - code.WriteString( - fmt.Sprintf( - ` - /** - * @return %[3]s - */ - public function get%[1]s() { - return %[2]s; - } -`, - ToUpperFirst(field.Name), - returnObject, - returnType, - ), - ) - } -} - -func (trw *TypeRWStruct) PHPStructResultType(code *strings.Builder) { - // print result type for function - if trw.ResultType != nil { - code.WriteString( - fmt.Sprintf( - ` - /** Allows kphp implicitly load function result class */ - private const RESULT = %s_result::class; -`, - trw.PhpClassName(true, true), - ), - ) - } -} - -func (trw *TypeRWStruct) PHPStructFields(code *strings.Builder) { - // print fields declarations - for _, f := range trw.Fields { - fieldType, defaultValue := fieldTypeAndDefaultValue(f) - code.WriteString( - fmt.Sprintf( - ` - /** @var %[1]s */ - public $%[2]s = %[3]s; -`, - fieldType, - f.originalName, - defaultValue, - ), - ) - } -} - -func (trw *TypeRWStruct) PHPStructFieldMasks(code *strings.Builder) { - // print fieldmasks - for _, f := range trw.Fields { - if f.fieldMask == nil { - continue - } - code.WriteString( - fmt.Sprintf( - ` - /** Field mask for $%[1]s field */ - const BIT_%[2]s_%[3]d = (1 << %[3]d); -`, - f.originalName, - strings.ToUpper(f.originalName), - f.BitNumber, - ), - ) - } -} - -func (trw *TypeRWStruct) PHPStructHeader(code *strings.Builder) { - unionParent := trw.PhpConstructorNeedsUnion() - - if isUsingTLImport(trw) || - trw.ResultType != nil || - unionParent != nil { - code.WriteString("\nuse VK\\TL;\n") - } - code.WriteString(` -/** - * @kphp-tl-class - */ -`) - code.WriteString(fmt.Sprintf("class %s ", trw.PhpClassName(false, true))) - if unionParent != nil { - code.WriteString(fmt.Sprintf("implements %s ", unionParent.trw.PhpClassName(true, false))) - } - if trw.ResultType != nil { - code.WriteString("implements TL\\RpcFunction ") - } - code.WriteString("{\n") -} - -func phpResultType(trw *TypeRWStruct) string { - return trw.ResultType.trw.PhpTypeName(true, trw.ResultType.PHPIsBare()) -} - -func toPhpFieldMaskName(natName string) string { - parts := strings.Split(natName, "_") - for i := range parts { - parts[i] = ToUpperFirst(parts[i]) - } - return strings.Join(parts, "") -} - -func isUsingTLImport(trw *TypeRWStruct) bool { - for _, field := range trw.Fields { - fieldType, _ := fieldTypeAndDefaultValue(field) - if strings.Contains(fieldType, "TL\\") { - return true - } - } - return false -} - -func fieldTypeAndDefaultValue(f Field) (string, string) { - fieldType := f.t.trw.PhpTypeName(true, f.t.PHPIsBare()) - defaultValue := f.t.trw.PhpDefaultValue() - if f.t.PHPIsTrueType() { - fieldType = "boolean" - defaultValue = "true" - if f.fieldMask != nil { - defaultValue = "false" - } - } else { - if f.fieldMask != nil { - defaultValue = "null" - if _, isMaybe := f.t.PHPGenCoreType().trw.(*TypeRWMaybe); !isMaybe { - fieldType = fieldType + "|null" - } - } - } - return fieldType, defaultValue -} - -func (trw *TypeRWStruct) PhpDefaultValue() string { - core := trw.wr.PHPGenCoreType() - if core != trw.wr { - return core.PHPDefaultValue() - } - if core.PHPIsTrueType() { - return "true" - } - return "null" -} - -func (trw *TypeRWStruct) PhpIterateReachableTypes(reachableTypes *map[*TypeRWWrapper]bool) { - for _, field := range trw.Fields { - field.t.PhpIterateReachableTypes(reachableTypes) - } - if trw.ResultType != nil { - trw.ResultType.PhpIterateReachableTypes(reachableTypes) - } -} - -func (trw *TypeRWStruct) PhpConstructorNeedsUnion() (unionParent *TypeRWWrapper) { - if trw.ResultType == nil { - if trw.wr.unionParent != nil { - return trw.wr.unionParent.wr - } else if !strings.EqualFold(trw.wr.tlName.Name, trw.wr.origTL[0].TypeDecl.Name.Name) { - // NOTE: constructor name is not same as type => type can become union in future? - return trw.wr - } - } - - return nil -} diff --git a/internal/tlcodegen/type_rw_struct_php.go b/internal/tlcodegen/type_rw_struct_php.go new file mode 100644 index 0000000..95b7cf0 --- /dev/null +++ b/internal/tlcodegen/type_rw_struct_php.go @@ -0,0 +1,621 @@ +package tlcodegen + +import ( + "fmt" + "github.com/vkcom/tl/internal/utils" + "sort" + "strings" +) + +func (trw *TypeRWStruct) PhpClassNameReplaced() bool { + unionParent := trw.PhpConstructorNeedsUnion() + if unionParent == nil { + if len(trw.Fields) == 1 && trw.ResultType == nil && trw.Fields[0].fieldMask == nil { + return true + } + + if trw.ResultType == nil && trw.wr.PHPIsTrueType() { + return true + } + + isDict, _, _, _ := isDictionaryElement(trw.wr) + if isDict && trw.wr.tlName.Namespace == "" { // TODO NOT A SOLUTION, BUT... + return true + } + } + return false +} + +func (trw *TypeRWStruct) PhpClassName(withPath bool, bare bool) string { + if PHPSpecialMembersTypes(trw.wr) != "" { + return "" + } + unionParent := trw.PhpConstructorNeedsUnion() + if unionParent == nil { + if len(trw.Fields) == 1 && trw.ResultType == nil && trw.Fields[0].fieldMask == nil { + return trw.Fields[0].t.trw.PhpClassName(withPath, bare) + } + + if trw.ResultType == nil && trw.wr.PHPIsTrueType() { + return "boolean" + } + + isDict, _, _, valueType := isDictionaryElement(trw.wr) + if isDict && trw.wr.tlName.Namespace == "" { // TODO NOT A SOLUTION, BUT... + return valueType.t.trw.PhpClassName(withPath, bare) + } + } + + name := trw.wr.tlName.Name + if !bare { + name = trw.wr.origTL[0].TypeDecl.Name.Name + } + if trw.wr.tlName.Namespace != "" { + name = fmt.Sprintf("%s_%s", trw.wr.tlName.Namespace, name) + } + + elems := make([]string, 0, len(trw.wr.arguments)) + for _, arg := range trw.wr.arguments { + if arg.tip != nil { + argText := arg.tip.trw.PhpClassName(false, false) + if argText != "" { + elems = append(elems, "__", argText) + } + } + } + + name += strings.Join(elems, "") + if withPath { + name = trw.wr.PHPTypePath() + name + } + return name +} + +func (trw *TypeRWStruct) PhpTypeName(withPath bool, bare bool) string { + if specialCase := PHPSpecialMembersTypes(trw.wr); specialCase != "" { + return specialCase + } + unionParent := trw.PhpConstructorNeedsUnion() + if unionParent == nil { + if len(trw.Fields) == 1 && trw.ResultType == nil && trw.Fields[0].fieldMask == nil { + return trw.Fields[0].t.trw.PhpTypeName(withPath, trw.Fields[0].bare) + } + isDict, _, _, valueType := isDictionaryElement(trw.wr) + if isDict && trw.wr.tlName.Namespace == "" { // TODO NOT A SOLUTION, BUT... + return valueType.t.trw.PhpTypeName(withPath, bare) + } + } + return trw.PhpClassName(withPath, bare) +} + +func (trw *TypeRWStruct) PhpGenerateCode(code *strings.Builder, bytes bool) error { + trw.PHPStructHeader(code) + trw.PHPStructFieldMasks(code) + trw.PHPStructFields(code) + trw.PHPStructResultType(code) + + necessaryFieldsInConstructor := make([]Field, 0) + usedFieldMasksIndecies := make([]int, 0) + usedFieldMasks := make(map[int][]Field) + for _, f := range trw.Fields { + if f.fieldMask == nil { + necessaryFieldsInConstructor = append(necessaryFieldsInConstructor, f) + } else { + index := f.fieldMask.FieldIndex + if !f.fieldMask.isField { + for i, argument := range trw.wr.origTL[0].TemplateArguments { + if argument.IsNat && argument.FieldName == f.fieldMask.name { + index = -(i + 1) + break + } + } + } + if usedFieldMasks[index] == nil { + usedFieldMasksIndecies = append(usedFieldMasksIndecies, index) + } + usedFieldMasks[index] = append(usedFieldMasks[index], f) + } + } + + trw.PHPStructConstructor(code, necessaryFieldsInConstructor) + trw.PHPStructRPCSpecialGetters(code) + trw.PHPStructFieldMaskCalculators(code, usedFieldMasksIndecies, usedFieldMasks) + trw.PHPStructFunctionSpecificMethods(code) + + code.WriteString("\n}\n") + + trw.PHPStructFunctionSpecificTypes(code) + return nil +} + +func (trw *TypeRWStruct) PHPStructFunctionSpecificTypes(code *strings.Builder) { + if trw.ResultType != nil { + code.WriteString( + fmt.Sprintf( + ` +/** + * @kphp-tl-class + */ +class %[1]s_result implements TL\RpcFunctionReturnResult { + + /** @var %[2]s */ + public $value = %[3]s; + +} +`, + trw.PhpClassName(false, true), + phpResultType(trw), + trw.ResultType.trw.PhpDefaultValue(), + ), + ) + } +} + +func (trw *TypeRWStruct) PHPStructFunctionSpecificMethods(code *strings.Builder) { + // print function specific methods and types + if trw.ResultType != nil { + kphpSpecialCode := "" + if trw.wr.HasAnnotation("kphp") { + kphpSpecialCode = fmt.Sprintf( + ` + + /** + * @param %[1]s $value + * @return %[2]s_result + */ + public static function createRpcServerResponse($value) { + $response = new %[2]s_result(); + $response->value = $value; + return $response; + }`, + trw.ResultType.trw.PhpTypeName(true, true), + trw.PhpClassName(true, true), + ) + } + + code.WriteString( + fmt.Sprintf( + ` + /** + * @param TL\RpcFunctionReturnResult $function_return_result + * @return %[4]s + */ + public static function functionReturnValue($function_return_result) { + if ($function_return_result instanceof %[1]s_result) { + return $function_return_result->value; + } + warning('Unexpected result type in functionReturnValue: ' . ($function_return_result ? get_class($function_return_result) : 'null')); + return (new %[1]s_result())->value; + } + + /** + * @kphp-inline + * + * @param TL\RpcResponse $response + * @return %[4]s + */ + public static function result(TL\RpcResponse $response) { + return self::functionReturnValue($response->getResult()); + }%[5]s + + /** + * @kphp-inline + * + * @return string + */ + public function getTLFunctionName() { + return '%[3]s'; + } +`, + trw.PhpClassName(false, true), + trw.PhpClassName(true, true), + trw.wr.tlName.String(), + phpResultType(trw), + kphpSpecialCode, + ), + ) + + } +} + +func (trw *TypeRWStruct) PHPStructFieldMaskCalculators(code *strings.Builder, usedFieldMasksIndecies []int, usedFieldMasks map[int][]Field) { + // print methods to calculate fieldmasks + // fix order + names := utils.MapSlice(usedFieldMasksIndecies, func(natIndex int) string { + natName := "" + if natIndex < 0 { + natName = trw.wr.origTL[0].TemplateArguments[-(natIndex + 1)].FieldName + } else { + natName = trw.Fields[natIndex].originalName + } + return natName + }) + + namesToIndices := make(map[string]int) + for i := range names { + namesToIndices[names[i]] = usedFieldMasksIndecies[i] + } + sort.Strings(names) + + fieldNameToFieldOrder := make(map[string]int) + for i := range trw.Fields { + fieldNameToFieldOrder[trw.Fields[i].originalName] = i + } + + for _, name := range names { + natIndex := namesToIndices[name] + natName := name + code.WriteString(` + /**`) + additionalArgs := make([]string, 0) + // arguments with ambiguous existence + for _, dependentField := range usedFieldMasks[natIndex] { + if _, isMaybe := dependentField.t.PHPGenCoreType().trw.(*TypeRWMaybe); isMaybe { + additionalArgs = append(additionalArgs, fmt.Sprintf("$has_%s", dependentField.originalName)) + code.WriteString(fmt.Sprintf("\n * @param bool $has_%s", dependentField.originalName)) + } + } + code.WriteString(` + * @return int + */ +`, + ) + code.WriteString( + fmt.Sprintf( + " public function calculate%[1]s(%[2]s) {\n $mask = 0;\n", + toPhpFieldMaskName(natName), + strings.Join(additionalArgs, ", "), + ), + ) + + fields := usedFieldMasks[natIndex] + sort.Slice(fields, func(i, j int) bool { + if fields[i].BitNumber == fields[j].BitNumber { + return i < j + } + return fields[i].BitNumber < fields[j].BitNumber + }) + + fieldsGroupedByBitNumber := make([][]Field, 0) + for _, dependentField := range fields { + if len(fieldsGroupedByBitNumber) == 0 || + fieldsGroupedByBitNumber[len(fieldsGroupedByBitNumber)-1][0].BitNumber != dependentField.BitNumber { + fieldsGroupedByBitNumber = append(fieldsGroupedByBitNumber, make([]Field, 0)) + } + fieldsGroupedByBitNumber[len(fieldsGroupedByBitNumber)-1] = append(fieldsGroupedByBitNumber[len(fieldsGroupedByBitNumber)-1], dependentField) + } + + for _, dependentFields := range fieldsGroupedByBitNumber { + conditions := make([]string, 0) + bitConstants := make([]string, 0) + sort.Slice(dependentFields, func(i, j int) bool { + return fieldNameToFieldOrder[dependentFields[i].originalName] < fieldNameToFieldOrder[dependentFields[j].originalName] + }) + for _, dependentField := range dependentFields { + condition := "" + if dependentField.t.PHPIsTrueType() || dependentField.t.PHPNeedsCode() { + condition = fmt.Sprintf( + "$this->%[1]s", + dependentField.originalName, + ) + } else if _, isMaybe := dependentField.t.PHPGenCoreType().trw.(*TypeRWMaybe); isMaybe { + condition = fmt.Sprintf("$has_%s", dependentField.originalName) + } else { + condition = fmt.Sprintf( + "$this->%[1]s !== null", + dependentField.originalName, + ) + } + conditions = append(conditions, condition) + bitConstants = append(bitConstants, fmt.Sprintf( + "self::BIT_%[1]s_%[2]d", + strings.ToUpper(dependentField.originalName), + dependentField.BitNumber)) + } + + finalCondition := conditions[0] + finalMask := bitConstants[0] + + if len(conditions) > 1 { + finalCondition = strings.Join(conditions, " && ") + finalMask = "(" + strings.Join(bitConstants, " | ") + ")" + } + + code.WriteString( + fmt.Sprintf( + ` + if (%[1]s) { + $mask |= %[2]s; + } +`, + finalCondition, + finalMask, + ), + ) + } + + code.WriteString("\n return $mask;\n") + code.WriteString(" }\n") + } +} + +func (trw *TypeRWStruct) PHPStructConstructor(code *strings.Builder, necessaryFieldsInConstructor []Field) { + // print constructor + code.WriteString(` + /** +`) + for _, f := range necessaryFieldsInConstructor { + fieldType, _ := fieldTypeAndDefaultValue(f) + code.WriteString(fmt.Sprintf(" * @param %[1]s $%[2]s\n", fieldType, f.originalName)) + } + if len(necessaryFieldsInConstructor) == 0 { + code.WriteString(" * @kphp-inline\n") + } + + code.WriteString(` */ +`) + code.WriteString(" public function __construct(") + + for i, f := range necessaryFieldsInConstructor { + _, defaultValue := fieldTypeAndDefaultValue(f) + if i != 0 { + code.WriteString(", ") + } + code.WriteString(fmt.Sprintf("$%[1]s = %[2]s", f.originalName, defaultValue)) + } + + code.WriteString(") {\n") + for _, f := range necessaryFieldsInConstructor { + code.WriteString(fmt.Sprintf(" $this->%[1]s = $%[1]s;\n", f.originalName)) + } + code.WriteString(" }\n") +} + +func (trw *TypeRWStruct) PHPStructRPCSpecialGetters(code *strings.Builder) { + if unionParent := trw.wr.PHPUnionParent(); unionParent == nil || PHPSpecialMembersTypes(unionParent) == "" { + return + } + + const ThisType = "__this" + type SpecialField struct { + Name string + Type string + Default string + NullTypeIfNullValue bool + AddHasMethod bool + } + + fields := []SpecialField{ + { + "result", + "TL\\RpcFunctionReturnResult", + "null", + true, + false, + }, + { + "header", + ThisType, + "null", + true, + false, + }, + { + "error", + ThisType, + "null", + true, + true, + }, + } + + containsSuchField := func(name, ifTrue, ifFalse string) string { + for _, field := range trw.Fields { + if field.originalName == name { + return ifTrue + } + } + return ifFalse + } + + for _, field := range fields { + returnObject := field.Default + returnType := field.Default + if field.Type == ThisType && + strings.Contains(strings.ToLower(trw.PhpClassName(false, true)), strings.ToLower(field.Name)) { + returnObject = "$this" + returnType = trw.PhpTypeName(true, true) + } else { + if field.Type != ThisType { + returnObject = "$this->" + field.Name + returnType = field.Type + } + if field.NullTypeIfNullValue { + returnType = containsSuchField(field.Name, returnType, "null") + returnObject = containsSuchField(field.Name, returnObject, "null") + } + } + if field.AddHasMethod { + code.WriteString( + fmt.Sprintf( + ` + /** + * @return bool + */ + public function is%[1]s() { + return %[2]s; + } +`, + ToUpperFirst(field.Name), + containsSuchField(field.Name, "true", "false"), + ), + ) + } + code.WriteString( + fmt.Sprintf( + ` + /** + * @return %[3]s + */ + public function get%[1]s() { + return %[2]s; + } +`, + ToUpperFirst(field.Name), + returnObject, + returnType, + ), + ) + } +} + +func (trw *TypeRWStruct) PHPStructResultType(code *strings.Builder) { + // print result type for function + if trw.ResultType != nil { + code.WriteString( + fmt.Sprintf( + ` + /** Allows kphp implicitly load function result class */ + private const RESULT = %s_result::class; +`, + trw.PhpClassName(true, true), + ), + ) + } +} + +func (trw *TypeRWStruct) PHPStructFields(code *strings.Builder) { + // print fields declarations + for _, f := range trw.Fields { + fieldType, defaultValue := fieldTypeAndDefaultValue(f) + code.WriteString( + fmt.Sprintf( + ` + /** @var %[1]s */ + public $%[2]s = %[3]s; +`, + fieldType, + f.originalName, + defaultValue, + ), + ) + } +} + +func (trw *TypeRWStruct) PHPStructFieldMasks(code *strings.Builder) { + // print fieldmasks + for _, f := range trw.Fields { + if f.fieldMask == nil { + continue + } + code.WriteString( + fmt.Sprintf( + ` + /** Field mask for $%[1]s field */ + const BIT_%[2]s_%[3]d = (1 << %[3]d); +`, + f.originalName, + strings.ToUpper(f.originalName), + f.BitNumber, + ), + ) + } +} + +func (trw *TypeRWStruct) PHPStructHeader(code *strings.Builder) { + unionParent := trw.PhpConstructorNeedsUnion() + + if isUsingTLImport(trw) || + trw.ResultType != nil || + unionParent != nil { + code.WriteString("\nuse VK\\TL;\n") + } + code.WriteString(` +/** + * @kphp-tl-class + */ +`) + code.WriteString(fmt.Sprintf("class %s ", trw.PhpClassName(false, true))) + if unionParent != nil { + code.WriteString(fmt.Sprintf("implements %s ", unionParent.trw.PhpClassName(true, false))) + } + if trw.ResultType != nil { + code.WriteString("implements TL\\RpcFunction ") + } + code.WriteString("{\n") +} + +func phpResultType(trw *TypeRWStruct) string { + return trw.ResultType.trw.PhpTypeName(true, trw.ResultType.PHPIsBare()) +} + +func toPhpFieldMaskName(natName string) string { + parts := strings.Split(natName, "_") + for i := range parts { + parts[i] = ToUpperFirst(parts[i]) + } + return strings.Join(parts, "") +} + +func isUsingTLImport(trw *TypeRWStruct) bool { + for _, field := range trw.Fields { + fieldType, _ := fieldTypeAndDefaultValue(field) + if strings.Contains(fieldType, "TL\\") { + return true + } + } + return false +} + +func fieldTypeAndDefaultValue(f Field) (string, string) { + fieldType := f.t.trw.PhpTypeName(true, f.t.PHPIsBare()) + defaultValue := f.t.trw.PhpDefaultValue() + if f.t.PHPIsTrueType() { + fieldType = "boolean" + defaultValue = "true" + if f.fieldMask != nil { + defaultValue = "false" + } + } else { + if f.fieldMask != nil { + defaultValue = "null" + if _, isMaybe := f.t.PHPGenCoreType().trw.(*TypeRWMaybe); !isMaybe { + fieldType = fieldType + "|null" + } + } + } + return fieldType, defaultValue +} + +func (trw *TypeRWStruct) PhpDefaultValue() string { + core := trw.wr.PHPGenCoreType() + if core != trw.wr { + return core.PHPDefaultValue() + } + if core.PHPIsTrueType() { + return "true" + } + return "null" +} + +func (trw *TypeRWStruct) PhpIterateReachableTypes(reachableTypes *map[*TypeRWWrapper]bool) { + for _, field := range trw.Fields { + field.t.PhpIterateReachableTypes(reachableTypes) + } + if trw.ResultType != nil { + trw.ResultType.PhpIterateReachableTypes(reachableTypes) + } +} + +func (trw *TypeRWStruct) PhpConstructorNeedsUnion() (unionParent *TypeRWWrapper) { + if trw.ResultType == nil { + if trw.wr.unionParent != nil { + return trw.wr.unionParent.wr + } else if !strings.EqualFold(trw.wr.tlName.Name, trw.wr.origTL[0].TypeDecl.Name.Name) { + // NOTE: constructor name is not same as type => type can become union in future? + return trw.wr + } + } + + return nil +} diff --git a/internal/tlcodegen/type_rw_tuple.go b/internal/tlcodegen/type_rw_tuple.go index 2dacc5d..7757855 100644 --- a/internal/tlcodegen/type_rw_tuple.go +++ b/internal/tlcodegen/type_rw_tuple.go @@ -210,39 +210,3 @@ func (trw *TypeRWBrackets) typeJSON2ReadingCode(bytesVersion bool, directImports goGlobalName := addBytes(trw.wr.goGlobalName, bytesVersion) return fmt.Sprintf("if err := %sReadJSON(legacyTypeNames, %s, %s%s); err != nil { return err }", trw.wr.ins.Prefix(directImports, ins)+goGlobalName, jvalue, addAmpersand(ref, val), joinWithCommas(natArgs)) } - -func (trw *TypeRWBrackets) PhpClassName(withPath bool, bare bool) string { - if strings.HasPrefix(trw.wr.tlName.String(), BuiltinTupleName) || - strings.HasPrefix(trw.wr.tlName.String(), BuiltinVectorName) { - return "array_" + trw.element.t.trw.PhpClassName(false, false) - } - return fmt.Sprintf("", trw.wr.goGlobalName) -} - -func (trw *TypeRWBrackets) PhpClassNameReplaced() bool { - return true -} - -func (trw *TypeRWBrackets) PhpTypeName(withPath bool, bare bool) string { - if strings.HasPrefix(trw.wr.tlName.String(), BuiltinTupleName) || - strings.HasPrefix(trw.wr.tlName.String(), BuiltinVectorName) { - elementText := trw.element.t.trw.PhpTypeName(withPath, trw.element.t.PHPIsBare()) - if _, ok := trw.element.t.trw.(*TypeRWMaybe); ok { - elementText = "(" + elementText + ")" - } - return elementText + "[]" - } - return fmt.Sprintf("", trw.wr.goGlobalName) -} - -func (trw *TypeRWBrackets) PhpGenerateCode(code *strings.Builder, bytes bool) error { - return fmt.Errorf("tuples don't have php code") -} - -func (trw *TypeRWBrackets) PhpDefaultValue() string { - return "[]" -} - -func (trw *TypeRWBrackets) PhpIterateReachableTypes(reachableTypes *map[*TypeRWWrapper]bool) { - trw.element.t.PhpIterateReachableTypes(reachableTypes) -} diff --git a/internal/tlcodegen/type_rw_tuple_php.go b/internal/tlcodegen/type_rw_tuple_php.go new file mode 100644 index 0000000..6e99c3c --- /dev/null +++ b/internal/tlcodegen/type_rw_tuple_php.go @@ -0,0 +1,42 @@ +package tlcodegen + +import ( + "fmt" + "strings" +) + +func (trw *TypeRWBrackets) PhpClassName(withPath bool, bare bool) string { + if strings.HasPrefix(trw.wr.tlName.String(), BuiltinTupleName) || + strings.HasPrefix(trw.wr.tlName.String(), BuiltinVectorName) { + return "array_" + trw.element.t.trw.PhpClassName(false, false) + } + return fmt.Sprintf("", trw.wr.goGlobalName) +} + +func (trw *TypeRWBrackets) PhpClassNameReplaced() bool { + return true +} + +func (trw *TypeRWBrackets) PhpTypeName(withPath bool, bare bool) string { + if strings.HasPrefix(trw.wr.tlName.String(), BuiltinTupleName) || + strings.HasPrefix(trw.wr.tlName.String(), BuiltinVectorName) { + elementText := trw.element.t.trw.PhpTypeName(withPath, trw.element.t.PHPIsBare()) + if _, ok := trw.element.t.trw.(*TypeRWMaybe); ok { + elementText = "(" + elementText + ")" + } + return elementText + "[]" + } + return fmt.Sprintf("", trw.wr.goGlobalName) +} + +func (trw *TypeRWBrackets) PhpGenerateCode(code *strings.Builder, bytes bool) error { + return fmt.Errorf("tuples don't have php code") +} + +func (trw *TypeRWBrackets) PhpDefaultValue() string { + return "[]" +} + +func (trw *TypeRWBrackets) PhpIterateReachableTypes(reachableTypes *map[*TypeRWWrapper]bool) { + trw.element.t.PhpIterateReachableTypes(reachableTypes) +} diff --git a/internal/tlcodegen/type_rw_union.go b/internal/tlcodegen/type_rw_union.go index 89f9587..21e5bf1 100644 --- a/internal/tlcodegen/type_rw_union.go +++ b/internal/tlcodegen/type_rw_union.go @@ -8,8 +8,6 @@ package tlcodegen import ( "fmt" - "github.com/vkcom/tl/internal/utils" - "strings" ) type TypeRWUnion struct { @@ -190,80 +188,3 @@ func (trw *TypeRWUnion) HasShortFieldCollision(wr *TypeRWWrapper) bool { } return false } - -func (trw *TypeRWUnion) PhpClassNameReplaced() bool { - return false -} - -func (trw *TypeRWUnion) PhpClassName(withPath bool, bare bool) string { - if specialCase := PHPSpecialMembersTypes(trw.wr); specialCase != "" { - return specialCase - } - name := trw.wr.tlName.Name - if len(trw.wr.tlName.Namespace) != 0 { - name = fmt.Sprintf("%s_%s", trw.wr.tlName.Namespace, name) - } - - elems := make([]string, 0, len(trw.wr.arguments)) - for _, arg := range trw.wr.arguments { - if arg.tip != nil { - argText := arg.tip.trw.PhpClassName(false, false) - if argText != "" { - elems = append(elems, "__", argText) - } - } - } - - name += strings.Join(elems, "") - if withPath { - name = trw.wr.PHPTypePath() + name - } - return name -} - -func (trw *TypeRWUnion) PhpTypeName(withPath bool, bare bool) string { - return trw.PhpClassName(withPath, true) -} - -func (trw *TypeRWUnion) PhpGenerateCode(code *strings.Builder, bytes bool) error { - return PhpGenerateInterfaceCode(code, bytes, trw.wr, utils.MapSlice(trw.Fields, func(f Field) *TypeRWWrapper { return f.t })) -} - -func PhpGenerateInterfaceCode(code *strings.Builder, bytes bool, targetType *TypeRWWrapper, itsConstructors []*TypeRWWrapper) error { - constructors := make([]string, len(itsConstructors)) - for i, constructor := range itsConstructors { - constructors[i] = fmt.Sprintf("%s::class", constructor.trw.PhpClassName(true, true)) - } - - code.WriteString(` -use VK\TL; - -/** - * @kphp-tl-class - */ -`) - code.WriteString(fmt.Sprintf( - `interface %[1]s { - - /** Allows kphp implicitly load all available constructors */ - const CONSTRUCTORS = [ - %[2]s - ]; - -} -`, - targetType.trw.PhpClassName(false, false), - strings.Join(constructors, ",\n "), - )) - return nil -} - -func (trw *TypeRWUnion) PhpDefaultValue() string { - return "null" -} - -func (trw *TypeRWUnion) PhpIterateReachableTypes(reachableTypes *map[*TypeRWWrapper]bool) { - for _, field := range trw.Fields { - field.t.PhpIterateReachableTypes(reachableTypes) - } -} diff --git a/internal/tlcodegen/type_rw_union_php.go b/internal/tlcodegen/type_rw_union_php.go new file mode 100644 index 0000000..561176d --- /dev/null +++ b/internal/tlcodegen/type_rw_union_php.go @@ -0,0 +1,84 @@ +package tlcodegen + +import ( + "fmt" + "github.com/vkcom/tl/internal/utils" + "strings" +) + +func (trw *TypeRWUnion) PhpClassNameReplaced() bool { + return false +} + +func (trw *TypeRWUnion) PhpClassName(withPath bool, bare bool) string { + if specialCase := PHPSpecialMembersTypes(trw.wr); specialCase != "" { + return specialCase + } + name := trw.wr.tlName.Name + if len(trw.wr.tlName.Namespace) != 0 { + name = fmt.Sprintf("%s_%s", trw.wr.tlName.Namespace, name) + } + + elems := make([]string, 0, len(trw.wr.arguments)) + for _, arg := range trw.wr.arguments { + if arg.tip != nil { + argText := arg.tip.trw.PhpClassName(false, false) + if argText != "" { + elems = append(elems, "__", argText) + } + } + } + + name += strings.Join(elems, "") + if withPath { + name = trw.wr.PHPTypePath() + name + } + return name +} + +func (trw *TypeRWUnion) PhpTypeName(withPath bool, bare bool) string { + return trw.PhpClassName(withPath, true) +} + +func (trw *TypeRWUnion) PhpGenerateCode(code *strings.Builder, bytes bool) error { + return PhpGenerateInterfaceCode(code, bytes, trw.wr, utils.MapSlice(trw.Fields, func(f Field) *TypeRWWrapper { return f.t })) +} + +func PhpGenerateInterfaceCode(code *strings.Builder, bytes bool, targetType *TypeRWWrapper, itsConstructors []*TypeRWWrapper) error { + constructors := make([]string, len(itsConstructors)) + for i, constructor := range itsConstructors { + constructors[i] = fmt.Sprintf("%s::class", constructor.trw.PhpClassName(true, true)) + } + + code.WriteString(` +use VK\TL; + +/** + * @kphp-tl-class + */ +`) + code.WriteString(fmt.Sprintf( + `interface %[1]s { + + /** Allows kphp implicitly load all available constructors */ + const CONSTRUCTORS = [ + %[2]s + ]; + +} +`, + targetType.trw.PhpClassName(false, false), + strings.Join(constructors, ",\n "), + )) + return nil +} + +func (trw *TypeRWUnion) PhpDefaultValue() string { + return "null" +} + +func (trw *TypeRWUnion) PhpIterateReachableTypes(reachableTypes *map[*TypeRWWrapper]bool) { + for _, field := range trw.Fields { + field.t.PhpIterateReachableTypes(reachableTypes) + } +}