void t_hack_generator::generate_php_struct_shape_methods()

in thrift/compiler/generate/t_hack_generator.cc [2742:3013]


void t_hack_generator::generate_php_struct_shape_methods(
    std::ofstream& out, const t_struct* tstruct) {
  generate_php_struct_stringifyMapKeys_method(out);

  indent(out)
      << "public static function __fromShape(self::TShape $shape)[]: this {\n";
  indent_up();
  indent(out) << "return new static(\n";
  indent_up();

  t_name_generator namer;
  for (const auto& field : tstruct->fields()) {
    const t_type* t = field.type()->get_true_type();

    std::string dval = "";
    if (field.default_value() != nullptr &&
        !(t->is_struct() || t->is_xception())) {
      dval = render_const_value(t, field.default_value());
    } else {
      dval = render_default_value(t);
    }

    bool nullable =
        field_is_nullable(tstruct, &field, dval) || nullable_everything_;

    std::stringstream source;
    source << "$shape['" << field.name() << "']";

    std::stringstream inner;
    bool is_simple_shape_index = true;

    if (t->is_set()) {
      if (arraysets_ || arrays_ || no_use_hack_collections_) {
        inner << source.str();
      } else {
        is_simple_shape_index = false;
        inner << "new Set(Keyset\\keys(" << source.str() << "))";
      }
    } else if (t->is_map() || t->is_list()) {
      bool stringify_map_keys = false;
      if (t->is_map() && shape_arraykeys_) {
        const t_type* key_type = static_cast<const t_map*>(t)->get_key_type();
        if (key_type->is_base_type() && key_type->is_string_or_binary()) {
          stringify_map_keys = true;
        }
      }

      if (stringify_map_keys) {
        is_simple_shape_index = false;
        inner << "self::__stringifyMapKeys(";
      }

      if (arrays_ || no_use_hack_collections_) {
        inner << source.str();
        if (type_has_nested_struct(t)) {
          is_simple_shape_index = false;
          indent_up();
          inner << std::endl;
          indent(inner) << "|> ";
          generate_hack_array_from_shape_lambda(inner, namer, t);
          indent_down();
        }
        if (stringify_map_keys) {
          inner << ")";
        }
      } else {
        is_simple_shape_index = false;
        inner << (stringify_map_keys ? "" : "(");
        if (t->is_map()) {
          inner << "new Map(";
        } else {
          inner << "new Vector(";
        }

        inner << source.str() << "))";

        int nest = 0;
        while (true) {
          const t_type* val_type;
          if (t->is_map()) {
            val_type = static_cast<const t_map*>(t)->get_val_type();
          } else {
            val_type = static_cast<const t_list*>(t)->get_elem_type();
          }
          val_type = val_type->get_true_type();

          if ((val_type->is_set() && !arraysets_) || val_type->is_map() ||
              val_type->is_list() || val_type->is_struct()) {
            indent_up();
            nest++;
            inner << "->map(\n";

            if (val_type->is_set()) {
              std::string tmp = namer("val");
              indent(inner) << "$" << tmp << " ==> new Set(Keyset\\keys($"
                            << tmp << ")),\n";
              break;
            } else if (val_type->is_map() || val_type->is_list()) {
              std::string tmp = namer("val");

              stringify_map_keys = false;
              if (val_type->is_map() && shape_arraykeys_) {
                const t_type* key_type =
                    static_cast<const t_map*>(val_type)->get_key_type();
                if (key_type->is_base_type() &&
                    key_type->is_string_or_binary()) {
                  stringify_map_keys = true;
                }
              }

              indent(inner)
                  << "$" << tmp << " ==> "
                  << (stringify_map_keys ? "self::__stringifyMapKeys" : "")
                  << "(new ";
              if (val_type->is_map()) {
                inner << "Map";
              } else {
                inner << "Vector";
              }
              inner << "($" << tmp << "))";
              t = val_type;
            } else if (val_type->is_struct()) {
              std::string tmp = namer("val");
              std::string type = hack_name(val_type);
              indent(inner) << "$" << tmp << " ==> " << type << "::__fromShape("
                            << "$" << tmp << "),\n";
              break;
            }
          } else {
            if (nest > 0) {
              inner << ",\n";
            }
            break;
          }
        }
        while (nest-- > 0) {
          indent_down();
          indent(inner) << ")";
          if (nest > 0) {
            inner << ",\n";
          }
        }
      }
    } else if (t->is_struct()) {
      is_simple_shape_index = false;
      std::string type = hack_name(t);
      inner << type << "::__fromShape(" << source.str() << ")";
    } else {
      inner << source.str();
    }

    std::stringstream val;
    indent(val);
    if (tstruct->is_union() || nullable) {
      val << "Shapes::idx($shape, '" << field.name() << "')";
      if (!is_simple_shape_index) {
        val << " === null ? null : (" << inner.str() << ")";
      }
    } else {
      val << inner.str();
    }
    val << ",\n";
    out << val.str();
  }
  indent_down();
  indent(out) << ");\n";
  indent_down();
  indent(out) << "}\n";
  out << "\n";

  indent(out) << "public function __toShape()[]: self::TShape {\n";
  indent_up();

  indent(out) << "return shape(\n";
  indent_up();

  for (const auto& field : tstruct->fields()) {
    const t_type* t = field.type()->get_true_type();
    t_name_generator ngen;

    indent(out) << "'" << field.name() << "' => ";

    std::stringstream val;

    bool nullable =
        field_is_nullable(tstruct, &field, render_default_value(t)) ||
        nullable_everything_;
    auto fieldRef = "$this->" + field.name();

    if (t->is_container()) {
      if (t->is_map() || t->is_list()) {
        if (arrays_ || no_use_hack_collections_) {
          val << fieldRef;
          if (type_has_nested_struct(t)) {
            val << "\n";
            indent_up();
            indent(val) << "|> ";
            if (nullable) {
              val << "$$ === null \n";
              indent_up();
              indent(val) << "? null \n";
              indent(val) << ": ";
            }
            generate_shape_from_hack_array_lambda(val, ngen, t);
            if (nullable) {
              indent_down();
            }
            indent_down();
          } else {
            val << ",\n";
          }
        } else {
          const t_type* val_type;
          if (t->is_map()) {
            val_type = static_cast<const t_map*>(t)->get_val_type();
          } else {
            val_type = static_cast<const t_list*>(t)->get_elem_type();
          }
          val_type = val_type->get_true_type();

          if (val_type->is_container() || val_type->is_struct() || nullable) {
            val << fieldRef;
            if (val_type->is_container() || val_type->is_struct()) {
              val << (nullable ? "?" : "") << "->map(\n";
              indent_up();
              generate_php_struct_shape_collection_value_lambda(
                  val, ngen, val_type);
              indent_down();
              indent(val) << ")";
            }
            val << std::endl;
            indent_up();
            indent(val) << "|> " << (nullable ? "$$ === null ? null : " : "")
                        << generate_to_array_method(t, "$$") << ",\n";
            indent_down();
          } else {
            val << generate_to_array_method(t, fieldRef) << ",\n";
          }
        }
      } else if (arraysets_ || arrays_ || no_use_hack_collections_) {
        val << fieldRef << ",\n";
      } else {
        if (nullable) {
          val << fieldRef << "\n";
          indent_up();
          indent(val) << "|> $$ === null ? null : ";
        }
        val << "ThriftUtil::toDArray(Dict\\fill_keys(";
        if (nullable) {
          val << "$$";
        } else {
          val << fieldRef;
        }
        val << "->toValuesArray(), true), static::class),\n";
        if (nullable) {
          indent_down();
        }
      }
    } else if (t->is_struct()) {
      val << fieldRef;
      val << (nullable ? "?" : "") << "->__toShape(),\n";
    } else {
      val << fieldRef << ",\n";
    }

    out << val.str();
  }
  indent_down();
  indent(out) << ");\n";
  indent_down();
  indent(out) << "}\n";
}