Selaa lähdekoodia

[php] support php attributes with @:php.attribute (#9964)

Aleksandr Kuzmenko 3 vuotta sitten
vanhempi
commit
31472ca3fe
3 muutettua tiedostoa jossa 52 lisäystä ja 10 poistoa
  1. 7 0
      src-json/meta.json
  2. 38 10
      src/generators/genphp7.ml
  3. 7 0
      std/php/_std/Array.hx

+ 7 - 0
src-json/meta.json

@@ -922,6 +922,13 @@
 		"targets": ["TAnyField"],
 		"targets": ["TAnyField"],
 		"platforms": ["eval"]
 		"platforms": ["eval"]
 	},
 	},
+	{
+		"name": "PhpAttribute",
+		"metadata": ":php.attribute",
+		"doc": "Adds a PHP attribute to the annotated symbol. Meta argument expects a string constant. E.g. `@:php.attribute('\\\\my\\\\Attr(123)')` will be generated as `#[\\my\\Attr(123)]` in the compiled php file.",
+		"platforms": ["php"],
+		"links": ["https://www.php.net/manual/en/language.attributes.overview.php"]
+	},
 	{
 	{
 		"name": "PhpGlobal",
 		"name": "PhpGlobal",
 		"metadata": ":phpGlobal",
 		"metadata": ":phpGlobal",

+ 38 - 10
src/generators/genphp7.ml

@@ -3100,10 +3100,10 @@ class virtual type_builder ctx (wrapper:type_wrapper) =
 				writer#write_line ("namespace " ^ (String.concat "\\" namespace) ^ ";\n");
 				writer#write_line ("namespace " ^ (String.concat "\\" namespace) ^ ";\n");
 			writer#write_use
 			writer#write_use
 		(**
 		(**
-			Generates PHP docblock to output buffer.
+			Generates PHP docblock and attributes to output buffer.
 		*)
 		*)
-		method private write_doc doc_block =
-			match doc_block with
+		method private write_doc doc_block meta =
+			(match doc_block with
 				| DocVar (type_name, doc) ->
 				| DocVar (type_name, doc) ->
 					writer#write_line "/**";
 					writer#write_line "/**";
 					writer#write_line (" * @var " ^ type_name);
 					writer#write_line (" * @var " ^ type_name);
@@ -3122,6 +3122,8 @@ class virtual type_builder ctx (wrapper:type_wrapper) =
 					)
 					)
 				| DocMethod (args, return, doc) ->
 				| DocMethod (args, return, doc) ->
 					self#write_method_docblock args return doc
 					self#write_method_docblock args return doc
+			);
+			self#write_attributes meta;
 		(**
 		(**
 			Writes description section of docblocks
 			Writes description section of docblocks
 		*)
 		*)
@@ -3297,6 +3299,32 @@ class virtual type_builder ctx (wrapper:type_wrapper) =
 				end
 				end
 			else
 			else
 				false
 				false
+		(**
+			Generates PHP attributes to output buffer based on `@:php.attribute` metas.
+		*)
+		method private write_attributes meta =
+			let rec traverse found meta =
+				match meta with
+					| [] -> ()
+					| (m,el,p) :: rest ->
+						let found =
+							if m == PhpAttribute then begin
+								writer#write_indentation;
+								writer#write "#[";
+								(match el with
+									| [EConst (String (s,_)),p] ->
+										writer#write s
+									| _ ->
+										ctx.pgc_common.error ("@:php.attribute meta expects a single string constant as an argument.") p
+								);
+								writer#write "]\n";
+								true
+							end else
+								found
+						in
+						traverse found rest
+			in
+			traverse false meta
 		(**
 		(**
 			Set sourcemap generator
 			Set sourcemap generator
 		*)
 		*)
@@ -3318,7 +3346,7 @@ class enum_builder ctx (enm:tenum) =
 			E.g. "class SomeClass extends Another implements IFace"
 			E.g. "class SomeClass extends Another implements IFace"
 		*)
 		*)
 		method private write_declaration =
 		method private write_declaration =
-			self#write_doc (DocClass (gen_doc_text_opt enm.e_doc));
+			self#write_doc (DocClass (gen_doc_text_opt enm.e_doc)) enm.e_meta;
 			writer#write ("class " ^ self#get_name ^ " extends " ^ (writer#use hxenum_type_path))
 			writer#write ("class " ^ self#get_name ^ " extends " ^ (writer#use hxenum_type_path))
 		(**
 		(**
 			Writes type body to output buffer.
 			Writes type body to output buffer.
@@ -3347,7 +3375,7 @@ class enum_builder ctx (enm:tenum) =
 					| _ -> fail field.ef_pos __LOC__
 					| _ -> fail field.ef_pos __LOC__
 			in
 			in
 			writer#indent 1;
 			writer#indent 1;
-			self#write_doc (DocMethod (args, TEnum (enm, []), (gen_doc_text_opt field.ef_doc)));
+			self#write_doc (DocMethod (args, TEnum (enm, []), (gen_doc_text_opt field.ef_doc))) field.ef_meta;
 			writer#write_with_indentation ("static public function " ^ name ^ " (");
 			writer#write_with_indentation ("static public function " ^ name ^ " (");
 			write_args writer#write (writer#write_arg true) (fix_tsignature_args args);
 			write_args writer#write (writer#write_arg true) (fix_tsignature_args args);
 			writer#write ") {\n";
 			writer#write ") {\n";
@@ -3538,7 +3566,7 @@ class class_builder ctx (cls:tclass) =
 			E.g. "class SomeClass extends Another implements IFace"
 			E.g. "class SomeClass extends Another implements IFace"
 		*)
 		*)
 		method private write_declaration =
 		method private write_declaration =
-			self#write_doc (DocClass (gen_doc_text_opt cls.cl_doc));
+			self#write_doc (DocClass (gen_doc_text_opt cls.cl_doc)) cls.cl_meta;
 			if self#is_final then writer#write "final ";
 			if self#is_final then writer#write "final ";
 			if has_class_flag cls CAbstract then writer#write "abstract ";
 			if has_class_flag cls CAbstract then writer#write "abstract ";
 			writer#write (if (has_class_flag cls CInterface) then "interface " else "class ");
 			writer#write (if (has_class_flag cls CInterface) then "interface " else "class ");
@@ -3772,7 +3800,7 @@ class class_builder ctx (cls:tclass) =
 		*)
 		*)
 		method private write_var field is_static =
 		method private write_var field is_static =
 			writer#indent 1;
 			writer#indent 1;
-			self#write_doc (DocVar (writer#use_t ~for_doc:true field.cf_type, (gen_doc_text_opt field.cf_doc)));
+			self#write_doc (DocVar (writer#use_t ~for_doc:true field.cf_type, (gen_doc_text_opt field.cf_doc))) field.cf_meta;
 			writer#write_indentation;
 			writer#write_indentation;
 			if is_static then writer#write "static ";
 			if is_static then writer#write "static ";
 			let visibility = get_visibility field.cf_meta in
 			let visibility = get_visibility field.cf_meta in
@@ -3799,7 +3827,7 @@ class class_builder ctx (cls:tclass) =
 				| Some expr when not (is_constant expr) -> ()
 				| Some expr when not (is_constant expr) -> ()
 				| Some expr ->
 				| Some expr ->
 					writer#indent 1;
 					writer#indent 1;
-					self#write_doc (DocVar (writer#use_t field.cf_type, (gen_doc_text_opt field.cf_doc)));
+					self#write_doc (DocVar (writer#use_t field.cf_type, (gen_doc_text_opt field.cf_doc))) field.cf_meta;
 					writer#write_with_indentation ("const " ^ (field_name field) ^ " = ");
 					writer#write_with_indentation ("const " ^ (field_name field) ^ " = ");
 					writer#write_expr expr;
 					writer#write_expr expr;
 					writer#write ";\n"
 					writer#write ";\n"
@@ -3812,7 +3840,7 @@ class class_builder ctx (cls:tclass) =
 			writer#indent 1;
 			writer#indent 1;
 			let (args, return_type) = get_function_signature field in
 			let (args, return_type) = get_function_signature field in
 			List.iter (fun (arg_name, _, _) -> writer#declared_local_var arg_name) args;
 			List.iter (fun (arg_name, _, _) -> writer#declared_local_var arg_name) args;
-			self#write_doc (DocMethod (args, return_type, (gen_doc_text_opt field.cf_doc)));
+			self#write_doc (DocMethod (args, return_type, (gen_doc_text_opt field.cf_doc))) field.cf_meta;
 			writer#write_indentation;
 			writer#write_indentation;
 			if self#is_final_field field then writer#write "final ";
 			if self#is_final_field field then writer#write "final ";
 			if has_class_field_flag field CfAbstract then writer#write "abstract ";
 			if has_class_field_flag field CfAbstract then writer#write "abstract ";
@@ -3839,7 +3867,7 @@ class class_builder ctx (cls:tclass) =
 			writer#indent 1;
 			writer#indent 1;
 			let (args, return_type) = get_function_signature field in
 			let (args, return_type) = get_function_signature field in
 			List.iter (fun (arg_name, _, _) -> writer#declared_local_var arg_name) args;
 			List.iter (fun (arg_name, _, _) -> writer#declared_local_var arg_name) args;
-			self#write_doc (DocMethod (args, return_type, (gen_doc_text_opt field.cf_doc)));
+			self#write_doc (DocMethod (args, return_type, (gen_doc_text_opt field.cf_doc))) field.cf_meta;
 			writer#write_with_indentation ((get_visibility field.cf_meta) ^ " function " ^ (field_name field));
 			writer#write_with_indentation ((get_visibility field.cf_meta) ^ " function " ^ (field_name field));
 			(match field.cf_expr with
 			(match field.cf_expr with
 				| None -> (* interface *)
 				| None -> (* interface *)

+ 7 - 0
std/php/_std/Array.hx

@@ -207,11 +207,13 @@ final class Array<T> implements ArrayAccess<Int, T> implements IteratorAggregate
 	}
 	}
 
 
 	@:noCompletion @:keep
 	@:noCompletion @:keep
+	@:php.attribute('\\ReturnTypeWillChange')
 	function offsetExists(offset:Int):Bool {
 	function offsetExists(offset:Int):Bool {
 		return offset < length;
 		return offset < length;
 	}
 	}
 
 
 	@:noCompletion @:keep
 	@:noCompletion @:keep
+	@:php.attribute('\\ReturnTypeWillChange')
 	function offsetGet(offset:Int):Ref<T> {
 	function offsetGet(offset:Int):Ref<T> {
 		try {
 		try {
 			return arr[offset];
 			return arr[offset];
@@ -221,6 +223,7 @@ final class Array<T> implements ArrayAccess<Int, T> implements IteratorAggregate
 	}
 	}
 
 
 	@:noCompletion @:keep
 	@:noCompletion @:keep
+	@:php.attribute('\\ReturnTypeWillChange')
 	function offsetSet(offset:Int, value:T):Void {
 	function offsetSet(offset:Int, value:T):Void {
 		if (length <= offset) {
 		if (length <= offset) {
 			for(i in length...offset + 1) {
 			for(i in length...offset + 1) {
@@ -233,6 +236,7 @@ final class Array<T> implements ArrayAccess<Int, T> implements IteratorAggregate
 	}
 	}
 
 
 	@:noCompletion @:keep
 	@:noCompletion @:keep
+	@:php.attribute('\\ReturnTypeWillChange')
 	function offsetUnset(offset:Int):Void {
 	function offsetUnset(offset:Int):Void {
 		if (offset >= 0 && offset < length) {
 		if (offset >= 0 && offset < length) {
 			Global.array_splice(arr, offset, 1);
 			Global.array_splice(arr, offset, 1);
@@ -241,17 +245,20 @@ final class Array<T> implements ArrayAccess<Int, T> implements IteratorAggregate
 	}
 	}
 
 
 	@:noCompletion @:keep
 	@:noCompletion @:keep
+	@:php.attribute('\\ReturnTypeWillChange')
 	private function getIterator():Traversable {
 	private function getIterator():Traversable {
 		return new NativeArrayIterator(arr);
 		return new NativeArrayIterator(arr);
 	}
 	}
 
 
 	@:noCompletion @:keep
 	@:noCompletion @:keep
 	@:native('count') //to not interfere with `Lambda.count`
 	@:native('count') //to not interfere with `Lambda.count`
+	@:php.attribute('\\ReturnTypeWillChange')
 	private function _hx_count():Int {
 	private function _hx_count():Int {
 		return length;
 		return length;
 	}
 	}
 
 
 	@:noCompletion @:keep
 	@:noCompletion @:keep
+	@:php.attribute('\\ReturnTypeWillChange')
 	function jsonSerialize():NativeIndexedArray<T> {
 	function jsonSerialize():NativeIndexedArray<T> {
 		return arr;
 		return arr;
 	}
 	}