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";
}