Просмотр исходного кода

[php] Deprecated support for `untyped __thing__` (#6708)

* [php] dropped support for untyped __thing__

* reverted removal of untyped __thing__. Just deprecate it for now.
Alexander Kuzmenko 7 лет назад
Родитель
Сommit
530ddc5c8f

+ 4 - 0
extra/CHANGES.txt

@@ -8,6 +8,10 @@
 
 	Removals:
 
+	Deprecations:
+
+	php : deprecated support for `untyped __php__`, `untyped __call__` etc. Use `php.Syntax` instead.
+
 	Bugfixes:
 
 	js : fixed saving setter to `tmp` var before invocation (#6672)

+ 103 - 131
src/generators/genphp7.ml

@@ -238,20 +238,21 @@ let error_message pos message = (stringify_pos pos) ^ ": " ^ message
 (**
 	Terminates compiler process and prints user-friendly instructions about filing an issue in compiler repo.
 *)
-let fail hxpos mlpos =
+let fail ?msg hxpos mlpos =
+	let msg =
+		error_message
+			hxpos
+			(
+				(match msg with Some msg -> msg | _ -> "")
+				^ " Unexpected expression. Please submit an issue with expression example and following information:"
+			)
+	in
 	match mlpos with
 		| (file, line, _, _) ->
-			Printf.printf "%s\n" (error_message hxpos "Unexpected expression. Please submit an issue with expression example and following information:");
-			Printf.printf "%s:%d\n" file line;
+			Printf.eprintf "%s\n" msg;
+			Printf.eprintf "%s:%d\n" file line;
 			assert false
 
-(**
-	Print compilation error message and abort compilation process.
-*)
-let error_and_exit pos message =
-	Printf.printf "%s" (error_message pos message);
-	exit 1
-
 (**
 	Check if `target` is a `Dynamic` type
 *)
@@ -298,7 +299,7 @@ let is_string expr = is_string_type expr.etype
 (**
 	Check if `expr` is an access to a method of special `php.PHP` class
 *)
-let is_lang_extern expr =
+let is_syntax_extern expr =
 	match expr.eexpr with
 		| TField ({ eexpr = TTypeExpr (TClassDecl { cl_path = path }) }, _) when path = syntax_type_path -> true
 		| _ -> false
@@ -393,7 +394,7 @@ let needs_dereferencing for_assignment expr =
 			(* some of `php.Syntax` methods *)
 			| TCall ({ eexpr = TField (_, FStatic ({ cl_path = syntax_type_path }, { cf_name = name })) }, _) ->
 				(match name with
-					| "binop" | "object" | "array" -> for_assignment
+					| "codeDeref" -> for_assignment
 					| _ -> false
 				)
 			| _ -> false
@@ -818,8 +819,13 @@ let is_object_declaration expr =
 	Check if `subject_arg` and `type_arg` can be generated as `$subject instanceof Type` expression.
 *)
 let instanceof_compatible (subject_arg:texpr) (type_arg:texpr) : bool =
+	let is_real_class path =
+		match path with
+			| ([], "String") | ([], "Class") | (["php";"_NativeArray"], "NativeArray_Impl_") -> false
+			| _ -> true
+	in
 	match (reveal_expr_with_parenthesis type_arg).eexpr with
-		| TTypeExpr (TClassDecl { cl_path = path }) when path <> ([], "String") && path <> ([], "Class") ->
+		| TTypeExpr (TClassDecl { cl_path = path }) when is_real_class path ->
 			let subject_arg = reveal_expr_with_parenthesis subject_arg in
 			(match subject_arg.eexpr with
 				| TLocal _ | TField _ | TCall _ | TArray _ | TConst TThis -> not (is_magic subject_arg)
@@ -1306,7 +1312,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 						let alias_source = ref (List.rev module_path) in
 						let get_alias_next_part () =
 							match !alias_source with
-								| [] ->  failwith ("Failed to find already used type: " ^ get_full_type_name type_path)
+								| [] ->  fail ~msg:("Failed to find already used type: " ^ get_full_type_name type_path) self#pos __POS__
 								| name :: rest ->
 									alias_source := (match rest with
 										| [] -> [name]
@@ -1353,13 +1359,13 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 				| TFun _ -> self#use ~prefix:false ([], "Closure")
 				| TAnon _ -> "object"
 				| TDynamic _ -> "mixed"
-				| TLazy _ -> failwith "TLazy not implemented"
+				| TLazy _ -> fail ~msg:"TLazy not implemented" self#pos __POS__
 				| TMono mono ->
 					(match !mono with
 						| None -> "mixed"
 						| Some t -> self#use_t t
 					)
-				| TType _ -> failwith "TType not implemented"
+				| TType _ -> fail ~msg:"TType not implemented" self#pos __POS__
 				| TAbstract (abstr, _) ->
 					match abstr.a_path with
 						| ([],"Int") -> "int"
@@ -1550,17 +1556,19 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 			Hashtbl.iter write use_table
 		(**
 			Writes array item declaration to output buffer and appends ",\n"
+			Adds indentation and ",\n" if `separate_line` is `true`.
 		*)
-		method write_array_item ?key value_expr =
+		method write_array_item ?separate_line ?key value_expr =
+			let separate_line = match separate_line with Some true -> true | _ -> false in
+			if separate_line then self#write_indentation;
 			(match key with
 				| None ->
-					self#write_indentation;
 					self#write_expr value_expr;
 				| Some key_str ->
-					self#write (indentation  ^ "\"" ^ (String.escaped key_str) ^ "\" => ");
+					self#write ("\"" ^ (String.escaped key_str) ^ "\" => ");
 					self#write_expr value_expr
 			);
-			self#write ",\n"
+			if separate_line then self#write ",\n"
 		(**
 			Writes expression to output buffer
 		*)
@@ -1587,11 +1595,14 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 					self#write ")"
 				| TObjectDecl fields -> self#write_expr_object_declaration fields
 				| TArrayDecl exprs -> self#write_expr_array_decl exprs
-				| TCall (target, [arg1; arg2]) when is_std_is target && instanceof_compatible arg1 arg2 -> self#write_expr_lang_instanceof [arg1; arg2]
-				| TCall (_, [arg]) when is_native_struct_array_cast expr && is_object_declaration arg -> self#write_assoc_array_decl arg
-				| TCall ({ eexpr = TIdent name}, args) when is_magic expr -> self#write_expr_magic name args
+				| TCall (target, [arg1; arg2]) when is_std_is target && instanceof_compatible arg1 arg2 -> self#write_expr_syntax_instanceof [arg1; arg2]
+				| TCall (_, [arg]) when is_native_struct_array_cast expr && is_object_declaration arg ->
+					(match (reveal_expr arg).eexpr with TObjectDecl fields -> self#write_assoc_array_decl fields | _ -> fail self#pos __POS__)
+				| TCall ({ eexpr = TIdent name}, args) when is_magic expr ->
+					ctx.warning ("untyped " ^ name ^ " is deprecated. Use php.Syntax instead.") self#pos;
+					self#write_expr_magic name args
 				| TCall ({ eexpr = TField (expr, access) }, args) when is_string expr -> self#write_expr_call_string expr access args
-				| TCall (expr, args) when is_lang_extern expr -> self#write_expr_call_lang_extern expr args
+				| TCall (expr, args) when is_syntax_extern expr -> self#write_expr_call_syntax_extern expr args
 				| TCall (target, args) when is_sure_var_field_access target -> self#write_expr_call (parenthesis target) args
 				| TCall (target, args) -> self#write_expr_call target args
 				| TNew (_, _, args) when is_string expr -> write_args self#write self#write_expr args
@@ -1675,35 +1686,28 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 				| _ ->
 					self#write ((self#use array_type_path) ^ "::wrap([\n");
 					self#indent_more;
-					List.iter (fun expr -> self#write_array_item expr) exprs;
+					List.iter (fun expr -> self#write_array_item ~separate_line:true expr) exprs;
 					self#indent_less;
 					self#write_indentation;
 					self#write "])"
 		(**
-			Write associative array declaration (used for NativeStructArray)
+			Write associative array declaration
 		*)
-		method write_assoc_array_decl object_decl =
-			match (reveal_expr object_decl).eexpr with
-				| TObjectDecl fields ->
-					if List.length fields = 0 then
-						self#write "[]"
-					else begin
-						self#write "[\n";
-						self#indent_more;
-						List.iter
-							(fun ((name,_,_), field) ->
-								self#write_indentation;
-								self#write_const_string name;
-								self#write " => ";
-								self#write_expr field;
-								self#write ",\n"
-							)
-							fields;
-						self#indent_less;
-						self#write_indentation;
-						self#write "]";
-					end
-				| _ -> fail object_decl.epos __POS__
+		method write_assoc_array_decl fields =
+			match fields with
+				| [] -> self#write "[]"
+				| [((key, _, _), value)] ->
+					self#write "[";
+					self#write_array_item ~key:key value;
+					self#write "]"
+				| _ ->
+					self#write "[\n";
+					self#indent_more;
+					let write_field ((key,_,_), value) = self#write_array_item ~separate_line:true ~key:key value in
+					List.iter write_field fields;
+					self#indent_less;
+					self#write_indentation;
+					self#write "]"
 		(**
 			Writes TArray to output buffer
 		*)
@@ -1983,9 +1987,9 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 			@see http://old.haxe.org/doc/advanced/magic#php-magic
 		*)
 		method write_expr_magic name args =
-			let error = error_message self#pos ("Invalid arguments for " ^ name ^ " magic call") in
+			let error = ("Invalid arguments for " ^ name ^ " magic call") in
 			match args with
-				| [] -> failwith error
+				| [] -> fail ~msg:error self#pos __POS__
 				| { eexpr = TConst (TString code) } as expr :: args ->
 					(match name with
 						| "__php__" ->
@@ -2001,7 +2005,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 						| "__physeq__" ->
 							(match args with
 								| [expr2] -> self#write_expr_binop OpEq expr expr2
-								| _ -> failwith error
+								| _ -> fail ~msg:error self#pos __POS__
 							)
 						| "__var__" ->
 							(match args with
@@ -2011,20 +2015,20 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 									self#write ("$" ^ code ^ "[");
 									self#write_expr expr2;
 									self#write "]"
-								| _ -> failwith error
+								| _ -> fail ~msg:error self#pos __POS__
 							)
-						| _ -> failwith error
+						| _ -> fail ~msg:error self#pos __POS__
 					)
 				| [expr1; expr2] ->
 					(match name with
 						| "__physeq__" ->
 							(match args with
 								| [expr1; expr2] -> self#write_expr_binop OpEq expr1 expr2
-								| _ -> failwith error
+								| _ -> fail ~msg:error self#pos __POS__
 							)
-						| _ -> failwith error
+						| _ -> fail ~msg:error self#pos __POS__
 					)
-				| _ -> failwith error
+				| _ -> fail ~msg:error self#pos __POS__
 		(**
 			Writes TTypeExpr to output buffer
 		*)
@@ -2332,13 +2336,9 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 			match fields with
 				| [] ->  self#write ("new " ^ (self#use hxanon_type_path) ^ "()")
 				| _ ->
-					self#write ("new " ^ (self#use hxanon_type_path)  ^ "([\n");
-					self#indent_more;
-					let write_field ((key,_,_), value) = self#write_array_item ~key:key value in
-					List.iter write_field fields;
-					self#indent_less;
-					self#write_indentation;
-					self#write "])"
+					self#write ("new " ^ (self#use hxanon_type_path)  ^ "(");
+					self#write_assoc_array_decl fields;
+					self#write ")"
 		(**
 			Writes specified type to output buffer depending on type of expression.
 		*)
@@ -2357,43 +2357,40 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 		(**
 			Write language specific expression declared in `php.Syntax` extern
 		*)
-		method write_expr_call_lang_extern expr args =
+		method write_expr_call_syntax_extern expr args =
 			let name = match expr.eexpr with
 				| TField (_, FStatic (_, field)) -> field_name field
 				| _ -> fail self#pos __POS__
 			in
 			match name with
-				| "int" | "float"
-				| "string" | "bool"
-				| "object" | "array" -> self#write_expr_lang_cast name args
-				| "binop" -> self#write_expr_lang_binop args
-				| "instanceof" -> self#write_expr_lang_instanceof args
-				| "foreach" -> self#write_expr_lang_foreach args
-				| "construct" -> self#write_expr_lang_construct args
-				| "getField" -> self#write_expr_lang_get_field args
-				| "setField" -> self#write_expr_lang_set_field args
-				| "getStaticField" -> self#write_expr_lang_get_static_field args
-				| "setStaticField" -> self#write_expr_lang_set_static_field args
-				| "call" -> self#write_expr_lang_call args
-				| "staticCall" -> self#write_expr_lang_static_call args
-				| "arrayDecl" -> self#write_expr_lang_array_decl args
-				| "splat" -> self#write_expr_lang_splat args
-				| "suppress" -> self#write_expr_lang_suppress args
+				| "code" | "codeDeref" -> self#write_expr_syntax_code args
+				| "instanceof" -> self#write_expr_syntax_instanceof args
+				| "foreach" -> self#write_expr_syntax_foreach args
+				| "construct" -> self#write_expr_syntax_construct args
+				| "getField" -> self#write_expr_syntax_get_field args
+				| "setField" -> self#write_expr_syntax_set_field args
+				| "getStaticField" -> self#write_expr_syntax_get_static_field args
+				| "setStaticField" -> self#write_expr_syntax_set_static_field args
+				| "call" -> self#write_expr_syntax_call args
+				| "staticCall" -> self#write_expr_syntax_static_call args
+				| "arrayDecl" -> self#write_expr_syntax_array_decl args
+				| "assocDecl" -> self#write_expr_syntax_assoc_decl args
+				| "suppress" -> self#write_expr_syntax_suppress args
 				| "keepVar" -> ()
-				| _ -> fail self#pos __POS__
+				| _ -> ctx.error ("php.Syntax." ^ name ^ "() is not supported.") self#pos
 		(**
-			Writes splat operator (for `php.Syntax.splat()`)
+			Writes plain php code (for `php.Syntax.php()`)
 		*)
-		method write_expr_lang_splat args =
+		method write_expr_syntax_code args =
 			match args with
-				| [ args_expr ] ->
-					self#write "...";
-					self#write_expr args_expr
-				| _ -> fail self#pos __POS__
+				| [] -> fail self#pos __POS__
+				| { eexpr = TConst (TString php) } :: args ->
+					Codegen.interpolate_code ctx php args self#write self#write_expr self#pos
+				| _ -> ctx.error "First argument of php.Syntax.php() must be a constant string." self#pos
 		(**
 			Writes error suppression operator (for `php.Syntax.suppress()`)
 		*)
-		method write_expr_lang_suppress args =
+		method write_expr_syntax_suppress args =
 			match args with
 				| [ args_expr ] ->
 					self#write "@";
@@ -2402,14 +2399,21 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 		(**
 			Writes native array declaration (for `php.Syntax.arrayDecl()`)
 		*)
-		method write_expr_lang_array_decl args =
+		method write_expr_syntax_array_decl args =
 			self#write "[";
 			write_args self#write (fun e -> self#write_expr e) args;
 			self#write "]"
+		(**
+			Writes native array declaration (for `php.Syntax.arrayDecl()`)
+		*)
+		method write_expr_syntax_assoc_decl args =
+			match args with
+				| { eexpr = TObjectDecl fields } :: [] -> self#write_assoc_array_decl fields
+				| _ -> ctx.error "php.Syntax.assocDecl() accepts object declaration only." self#pos
 		(**
 			Writes a call to instance method (for `php.Syntax.call()`)
 		*)
-		method write_expr_lang_call args =
+		method write_expr_syntax_call args =
 			match args with
 				| obj_expr :: method_expr :: args ->
 					self#write_expr obj_expr;
@@ -2422,7 +2426,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 		(**
 			Writes a call to a static method (for `php.Syntax.staticCall()`)
 		*)
-		method write_expr_lang_static_call args =
+		method write_expr_syntax_static_call args =
 			match args with
 				| type_expr :: method_expr :: args ->
 					self#write_type type_expr;
@@ -2435,7 +2439,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 		(**
 			Writes field access for reading (for `php.Syntax.getField()`)
 		*)
-		method write_expr_lang_get_field args =
+		method write_expr_syntax_get_field args =
 			match args with
 				| obj_expr :: field_expr :: [] ->
 					self#write_expr obj_expr;
@@ -2446,7 +2450,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 		(**
 			Writes field access for writing (for `php.Syntax.setField()`)
 		*)
-		method write_expr_lang_set_field args =
+		method write_expr_syntax_set_field args =
 			match args with
 				| obj_expr :: field_expr :: value_expr :: [] ->
 					self#write_expr obj_expr;
@@ -2459,7 +2463,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 		(**
 			Writes static field access for reading (for `php.Syntax.getStaticField()`)
 		*)
-		method write_expr_lang_get_static_field args =
+		method write_expr_syntax_get_static_field args =
 			match args with
 				| type_expr :: field_expr :: [] ->
 					self#write_type type_expr;
@@ -2470,7 +2474,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 		(**
 			Writes static field access for writing (for `php.Syntax.setField()`)
 		*)
-		method write_expr_lang_set_static_field args =
+		method write_expr_syntax_set_static_field args =
 			match args with
 				| type_expr :: field_expr :: value_expr :: [] ->
 					self#write_expr type_expr;
@@ -2483,7 +2487,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 		(**
 			Writes `new` expression with class name taken local variable (for `php.Syntax.construct()`)
 		*)
-		method write_expr_lang_construct args =
+		method write_expr_syntax_construct args =
 			let (class_expr, args) = match args with
 				| class_expr :: args -> (class_expr, args)
 				| _ -> fail self#pos __POS__
@@ -2493,42 +2497,10 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 			self#write "(";
 			write_args self#write (fun e -> self#write_expr e) args;
 			self#write ")"
-		(**
-			Writes native php type conversion to output buffer (e.g. `php.Syntax.int()`)
-		*)
-		method write_expr_lang_cast type_name args =
-			match args with
-				| expr :: [] ->
-					let add_parentheses = match self#parent_expr with Some e -> is_access e | None -> false
-					and expr = match expr.eexpr with
-						| TLocal e -> expr
-						| _ -> parenthesis expr
-					in
-					if add_parentheses then self#write "(";
-					self#write ("(" ^ type_name ^")");
-					self#write_expr expr;
-					if add_parentheses then self#write ")"
-				| _ -> fail self#pos __POS__
-		(**
-			Generates binary operation to output buffer (for `php.Syntax.binop()`)
-		*)
-		method write_expr_lang_binop args =
-			match args with
-				| val_expr1 :: operator_expr :: val_expr2 :: [] ->
-					let operator = match operator_expr.eexpr with
-						| TConst (TString operator) -> operator
-						| _ -> error_and_exit self#pos "Second argument for php.Syntax.binop() must be a constant string"
-					in
-					self#write "(";
-					self#write_expr val_expr1;
-					self#write (" " ^ operator ^ " ");
-					self#write_expr val_expr2;
-					self#write ")"
-				| _ -> fail self#pos __POS__
 		(**
 			Writes `instanceof` expression to output buffer (for `php.Syntax.instanceof()`)
 		*)
-		method write_expr_lang_instanceof args =
+		method write_expr_syntax_instanceof args =
 			match args with
 				| val_expr :: type_expr :: [] ->
 					self#write "(";
@@ -2546,7 +2518,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 		(**
 			Writes `foreach` expression to output buffer (for `php.Syntax.foreach()`)
 		*)
-		method write_expr_lang_foreach args =
+		method write_expr_syntax_foreach args =
 			match args with
 				| collection_expr :: { eexpr = TFunction fn } :: [] ->
 					let (key_name, value_name) = match fn.tf_args with
@@ -2564,7 +2536,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 					self#write (" as $" ^ key_name ^ " => $" ^ value_name ^ ") ");
 					self#write_as_block fn.tf_expr
 				| _ ->
-					error_and_exit self#pos "PHP.foreach() only accepts anonymous function declaration for second argument."
+					ctx.error "php.Syntax.foreach() only accepts anonymous function declaration for second argument." self#pos
 		(**
 			Writes TCall to output buffer
 		*)

+ 2 - 2
std/DateTools.hx

@@ -37,7 +37,7 @@ class DateTools {
 	static var DAY_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
 	static var MONTH_SHORT_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
 	static var MONTH_NAMES = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
-	
+
 	private static function __format_get( d : Date, e : String ) : String {
 		return switch( e ){
 			case "%":
@@ -237,7 +237,7 @@ class DateTools {
 	    #if (js || flash || python)
 		   return untyped Date.UTC(year, month, day, hour, min, sec);
 		#elseif php
-		   return untyped __call__("gmmktime", hour, min, sec, month + 1, day, year) * 1000;
+		   return php.Global.gmmktime(hour, min, sec, month + 1, day, year) * 1000;
 		#elseif cpp
 		  return untyped __global__.__hxcpp_utc_date(year,month,day,hour,min,sec)*1000.0 ;
 		#else

+ 1 - 1
std/haxe/Serializer.hx

@@ -472,7 +472,7 @@ class Serializer {
 			} else
 				serializeString(v.tag);
 			buf.add(":");
-			var l : Int = untyped __call__("count", v.params);
+			var l : Int = php.Syntax.code("count({0})", v.params);
 			if( l == 0 || v.params == null)
 				buf.add(0);
 			else {

+ 5 - 5
std/php/Boot.hx

@@ -71,7 +71,7 @@ class Boot {
 		Returns empty string if no `--php-prefix` provided.
 	**/
 	public static inline function getPrefix() : String {
-		return untyped __php__('self::PHP_PREFIX');
+		return Syntax.code('self::PHP_PREFIX');
 	}
 
 	/**
@@ -397,9 +397,9 @@ class Boot {
 	**/
 	public static function equal( left:Dynamic, right:Dynamic ) : Bool {
 		if (isNumber(left) && isNumber(right)) {
-			return Syntax.binop(left, '==', right);
+			return Syntax.equal(left, right);
 		}
-		return Syntax.binop(left, '===', right);
+		return Syntax.strictEqual(left, right);
 	}
 
 	/**
@@ -410,7 +410,7 @@ class Boot {
 		if (left.is_string() || right.is_string()) {
 			return (left:String) + (right:String);
 		}
-		return Syntax.binop(left, '+', right);
+		return Syntax.add(left, right);
 	}
 
 	/**
@@ -428,7 +428,7 @@ class Boot {
 						value.is_int()
 						|| (
 							value.is_float()
-							&& Syntax.binop(Syntax.int(value), '==', value)
+							&& Syntax.equal(Syntax.int(value), value)
 							&& !Global.is_nan(value)
 						)
 					)

+ 20 - 0
std/php/Const.hx

@@ -15,6 +15,7 @@ extern class Const {
 	**/
 	static var PHP_OS : String;
 	static var PHP_SAPI : String;
+	static var PHP_BINARY : String;
 	static var PHP_EOL : String;
 	static var PHP_INT_MAX : Int;
 	static var PHP_INT_MIN : Int;
@@ -309,4 +310,23 @@ extern class Const {
 	static var GLOB_BRACE : Int;
 	static var GLOB_ONLYDIR : Int;
 	static var GLOB_ERR : Int;
+	/**
+		@see http://php.net/manual/en/zlib.constants.php
+	**/
+	static var FORCE_GZIP : Int;
+	static var FORCE_DEFLATE : Int;
+	static var ZLIB_ENCODING_RAW : Int;
+	static var ZLIB_ENCODING_DEFLATE : Int;
+	static var ZLIB_ENCODING_GZIP : Int;
+	static var ZLIB_FILTERED : Int;
+	static var ZLIB_HUFFMAN_ONLY : Int;
+	static var ZLIB_FIXED : Int;
+	static var ZLIB_RLE : Int;
+	static var ZLIB_DEFAULT_STRATEGY : Int;
+	static var ZLIB_BLOCK : Int;
+	static var ZLIB_NO_FLUSH : Int;
+	static var ZLIB_PARTIAL_FLUSH : Int;
+	static var ZLIB_SYNC_FLUSH : Int;
+	static var ZLIB_FULL_FLUSH : Int;
+	static var ZLIB_FINISH : Int;
 }

+ 15 - 0
std/php/Global.hx

@@ -1092,6 +1092,11 @@ extern class Global {
 	**/
 	static function mktime( ?hour:Int, ?minute:Int, ?second:Int, ?month:Int, ?day:Int, ?year:Int, ?is_dst:Int ) : EitherType<Int,Bool>;
 
+	/**
+		@see http://php.net/manual/en/function.gmmktime.php
+	**/
+	static function gmmktime( ?hour:Int, ?minute:Int, ?second:Int, ?month:Int, ?day:Int, ?year:Int, ?is_dst:Int ) : Int;
+
 	/**
 		@see http://php.net/manual/en/function.date.php
 	**/
@@ -1197,4 +1202,14 @@ extern class Global {
 		@see http://php.net/manual/en/function.include-once.php
 	**/
 	static function include_once( include_path:String ) : Void;
+
+	/**
+		@see http://php.net/manual/en/function.gzcompress.php
+	**/
+	static function gzcompress( data:String, ?level:Int, ?encoding:Int ) : EitherType<String,Bool>;
+
+	/**
+		@see http://php.net/manual/en/function.gzuncompress.php
+	**/
+	static function gzuncompress( data:String, ?length:Int ) : EitherType<String,Bool>;
 }

+ 9 - 11
std/php/SuperGlobal.hx

@@ -1,60 +1,58 @@
 package php;
 
-
-
 class SuperGlobal {
     /**
         @see http://php.net/manual/en/reserved.variables.globals.php
     **/
     public static var GLOBALS (get,never):NativeAssocArray<Dynamic>;
-    static inline function get_GLOBALS() return untyped __php__("$GLOBALS");
+    static inline function get_GLOBALS() return Syntax.code("$GLOBALS");
 
     /**
         @see http://php.net/manual/en/reserved.variables.server.php
     **/
     public static var _SERVER (get,never):NativeAssocArray<Dynamic>;
-    static inline function get__SERVER() return untyped __php__("$_SERVER");
+    static inline function get__SERVER() return Syntax.code("$_SERVER");
 
     /**
         @see http://php.net/manual/en/reserved.variables.get.php
     **/
     public static var _GET (get,never):NativeAssocArray<Dynamic>;
-    static inline function get__GET() return untyped __php__("$_GET");
+    static inline function get__GET() return Syntax.code("$_GET");
 
     /**
         @see http://php.net/manual/en/reserved.variables.post.php
     **/
     public static var _POST (get,never):NativeAssocArray<Dynamic>;
-    static inline function get__POST() return untyped __php__("$_POST");
+    static inline function get__POST() return Syntax.code("$_POST");
 
     /**
         @see http://php.net/manual/en/reserved.variables.files.php
     **/
     public static var _FILES (get,never):NativeAssocArray<Dynamic>;
-    static inline function get__FILES() return untyped __php__("$_FILES");
+    static inline function get__FILES() return Syntax.code("$_FILES");
 
     /**
         @see http://php.net/manual/en/reserved.variables.cookie.php
     **/
     public static var _COOKIE (get,never):NativeAssocArray<Dynamic>;
-    static inline function get__COOKIE() return untyped __php__("$_COOKIE");
+    static inline function get__COOKIE() return Syntax.code("$_COOKIE");
 
     /**
         @see http://php.net/manual/en/reserved.variables.session.php
     **/
     public static var _SESSION (get,never):NativeAssocArray<Dynamic>;
-    static inline function get__SESSION() return untyped __php__("$_SESSION");
+    static inline function get__SESSION() return Syntax.code("$_SESSION");
 
     /**
         @see http://php.net/manual/en/reserved.variables.request.php
     **/
     public static var _REQUEST (get,never):NativeAssocArray<Dynamic>;
-    static inline function get__REQUEST() return untyped __php__("$_REQUEST");
+    static inline function get__REQUEST() return Syntax.code("$_REQUEST");
 
     /**
         @see http://php.net/manual/en/reserved.variables.env.php
     **/
     public static var _ENV (get,never):NativeAssocArray<Dynamic>;
-    static inline function get__ENV() return untyped __php__("$_ENV");
+    static inline function get__ENV() return Syntax.code("$_ENV");
 
 }

+ 142 - 13
std/php/Syntax.hx

@@ -9,46 +9,158 @@ import haxe.extern.EitherType;
     Don't use these functions unless you are really sure what you are doing.
 **/
 extern class Syntax {
+
+    /**
+        Embeds plain php code.
+        `php` should be a string literal with php code.
+        It can contain placeholders like `{0}`, `{1}` which will be replaced with corresponding arguments from `args`.
+        E.g.:
+        ```
+        Syntax.code("var_dump({0}, {1})", a, b);
+        ```
+        will generate
+        ```
+        var_dump($a, $b);
+        ```
+    **/
+    static function code(php:String, args:Rest<Dynamic>):Dynamic;
+
+    /**
+        The same as `code()`, but adds dereferencing
+        when required to workaround "cannot use temporary expression in write context" php error.
+    **/
+    static function codeDeref(php:String, args:Rest<Dynamic>):Dynamic;
+
+    /**
+        Generates `$left <=> $right`
+    **/
+    static inline function spaceship( left:Dynamic, right:Dynamic ) : Int {
+        return code('({0} <=> {1})', left, right);
+    }
+
+    /**
+        Generates `$left ?? $right`
+    **/
+    static inline function coalesce<T>( left:T, right:T ) : T {
+        return codeDeref('({0} ?? {1})', left, right);
+    }
+
+    /**
+        Generates `$left . $right`
+    **/
+    static inline function concat( left:String, right:String ) : String {
+        return code('({0} . {1})', left, right);
+    }
+
+    /**
+        Generates `$left == $right`
+    **/
+    static inline function equal( left:Dynamic, right:Dynamic ) : Bool {
+        return code('({0} == {1})', left, right);
+    }
+
+    /**
+        Generates `$left === $right`
+    **/
+    static inline function strictEqual( left:Dynamic, right:Dynamic ) : Bool {
+        return code('({0} === {1})', left, right);
+    }
+
     /**
-        This method allows one to force specified binary operation for `left` and `right` values.
-        `operator` must be a constant string like "+" or "==".
+        Generates `$left != $right`
     **/
-    static function binop( left:Dynamic, operator:String, right:Dynamic ) : Dynamic;
+    static inline function notEqual( left:Dynamic, right:Dynamic ) : Bool {
+        return code('({0} != {1})', left, right);
+    }
+
+    /**
+        Generates `$left !== $right`
+    **/
+    static inline function strictNotEqual( left:Dynamic, right:Dynamic ) : Bool {
+        return code('({0} !== {1})', left, right);
+    }
+
+    /**
+        Generates `$left + $right` for numbers.
+    **/
+    static inline function add<T:Float>( left:T, right:T ) : T {
+        return code('({0} + {1})', left, right);
+    }
+
+    /**
+        Generates `$left + $right` for php arrays.
+    **/
+    static inline function union( left:NativeArray, right:NativeArray ) : NativeArray {
+        return codeDeref('({0} + {1})', left, right);
+    }
+
+    /**
+        Generates `$left ** $right`
+    **/
+    static inline function exp<T:Float>( left:T, right:T ) : T {
+        return code('({0} ** {1})', left, right);
+    }
+
+    /**
+        Generates `$left ?: $right`
+    **/
+    static inline function shortTernary<T>( left:T, right:T ) : T {
+        return codeDeref('({0} ?: {1})', left, right);
+    }
+
+    /**
+        Generates `$left xor $right`
+    **/
+    static inline function xor( left:Bool, right:Bool ) : Bool {
+        return code('({0} xor {1})', left, right);
+    }
 
     /**
         Generates `(int)$value`
     **/
-    static function int( value:Dynamic ) : Int;
+    static inline function int( value:Dynamic ) : Int {
+        return code('(int)({0})', value);
+    }
 
     /**
         Generates `(float)$value`
     **/
-    static function float( value:Dynamic ) : Float;
+    static inline function float( value:Dynamic ) : Float {
+        return code('(float)({0})', value);
+    }
 
     /**
         Generates `(string)$value`
     **/
-    static function string( value:Dynamic ) : String;
+    static inline function string( value:Dynamic ) : String {
+        return code('(string)({0})', value);
+    }
 
     /**
         Generates `(bool)$value`
     **/
-    static function bool( value:Dynamic ) : Bool;
+    static inline function bool( value:Dynamic ) : Bool {
+        return code('(bool)({0})', value);
+    }
 
     /**
         Generates `(object)$value`
     **/
-    static function object( value:Dynamic ) : Dynamic;
+    static inline function object( value:Dynamic ) : StdClass {
+        return codeDeref('(object)({0})', value);
+    }
 
     /**
         Generates `(array)$value`
     **/
-    static function array( value:Dynamic ) : NativeArray;
+    static inline function array( value:Dynamic ) : NativeArray {
+        return codeDeref('(array)({0})', value);
+    }
 
     /**
-        Ggenerates `$value instanceof $phpClassName`.
-        Haxe generates `Std.is(value, Type)` calls to `$value instanceof Type` automatically where possible.
-        `type` only accepts direct class names. That means `Type.resolveClass('MyClass')` is not allowed, but `MyClass` is.
+        Generates `$value instanceof $phpClassName`.
+        Haxe generates `Std.is(value, Type)` calls as `$value instanceof Type` automatically where possible.
+        So you may need this only if you have a `Class` stored in a variable.
     **/
     static function instanceof<V,C>( value:AsVar<V>,  type:AsVar<Class<C>> ) : Bool;
 
@@ -110,6 +222,21 @@ extern class Syntax {
     **/
     static function arrayDecl<T>( args:Rest<T> ) : NativeIndexedArray<T>;
 
+    /**
+        ```
+        Syntax.assocDecl({field1:'first', field2:2}});
+        ```
+        Generates native associative array declaration:
+        ```
+        ["field1" => "first", "field2" => 2];
+        ```
+        This method is not recursive.
+        Accepts object declarations only.
+        That means you can't pass an object stored in a variable to this method like `Syntax.assocDecl(someVar)`.
+        Use `php.Lib.associativeArrayOfObject(someVar)` instead.
+    **/
+    static function assocDecl<T:{}>( arg:T ) : NativeAssocArray<Dynamic>;
+
     /**
         Don't let compiler to optimize away local var passed to this method.
     **/
@@ -118,7 +245,9 @@ extern class Syntax {
     /**
         Adds `...` operator before `args`
     **/
-    static function splat( args:EitherType<NativeArray, Traversable> ) : Rest<Dynamic>;
+    static inline function splat( args:EitherType<NativeArray, Traversable> ) : Rest<Dynamic> {
+        return code('...{0}', args);
+    }
 
     /**
         Add errors suppression operator `@` before `expression`

+ 1 - 1
std/php/Web.hx

@@ -272,7 +272,7 @@ class Web {
 		var data : String = null;
 		var counter = 0;
 		while (!feof(h) && counter < max) {
-			data = Syntax.binop(data, ' . ', fread(h, bsize));
+			data = Syntax.concat(data, fread(h, bsize));
 			counter++;
 		}
 		fclose(h);

+ 3 - 3
std/php/_std/StringBuf.hx

@@ -38,11 +38,11 @@ import php.Syntax;
 
 	public function add<T>( x : T ) : Void {
 		if( x == null ) {
-			b = Syntax.binop(b, '.', 'null');
+			b = Syntax.concat(b, 'null');
 		} else if( Global.is_bool(x) ) {
-			b = Syntax.binop(b, '.', ((x:Dynamic) ? 'true' : 'false'));
+			b = Syntax.concat(b, ((x:Dynamic) ? 'true' : 'false'));
 		} else if( Global.is_string(x) ) {
-			b = Syntax.binop(b, '.', x);
+			b = Syntax.concat(b, cast x);
 		} else {
 			b += x;
 		}

+ 1 - 1
std/php/_std/haxe/ds/IntMap.hx

@@ -48,7 +48,7 @@ import php.NativeIndexedArray;
 		See `Map.get`
 	**/
 	public inline function get( key : Int ) : Null<T> {
-		return Syntax.binop(data[key], '??', null);
+		return Syntax.coalesce(data[key], null);
 	}
 
 	/**

+ 2 - 2
std/php/_std/haxe/ds/StringMap.hx

@@ -39,7 +39,7 @@ import haxe.Constraints;
 	}
 
 	public inline function get( key : String ) : Null<T> {
-		return Syntax.binop(data[key], '??', null);
+		return Syntax.coalesce(data[key], null);
 	}
 
 	public inline function exists( key : String ) : Bool {
@@ -62,7 +62,7 @@ import haxe.Constraints;
 	public inline function iterator() : Iterator<T> {
 		return data.iterator();
 	}
-	
+
 	public function copy() : StringMap<T> {
 		var copied = new StringMap();
 		copied.data = data;

+ 4 - 4
std/php/_std/haxe/io/BytesBuffer.hx

@@ -34,15 +34,15 @@ class BytesBuffer {
 	}
 
 	public inline function addByte( byte : Int ) {
-		b = Syntax.binop(b, '.', Global.chr(byte));
+		b = Syntax.concat(b, Global.chr(byte));
 	}
 
 	public inline function add( src : Bytes ) {
-		b = Syntax.binop(b, '.', src.getData().toNativeString());
+		b = Syntax.concat(b, src.getData().toNativeString());
 	}
 
 	public inline function addString( v : String ) {
-		b = Syntax.binop(b, '.', v);
+		b = Syntax.concat(b, v);
 	}
 
 	public function addInt32( v : Int ) {
@@ -69,7 +69,7 @@ class BytesBuffer {
 		if( pos < 0 || len < 0 || pos + len > src.length ) {
 			throw Error.OutsideBounds;
 		} else {
-			b = Syntax.binop(b, '.', src.getData().sub(pos, len).toString());
+			b = Syntax.concat(b, src.getData().sub(pos, len).toString());
 		}
 	}
 

+ 4 - 2
std/php/_std/haxe/io/BytesData.hx

@@ -23,6 +23,8 @@ package haxe.io;
 
 import php.*;
 
+using php.Syntax;
+
 typedef BytesData = BytesDataAbstract;
 
 private class Container {
@@ -49,7 +51,7 @@ private abstract BytesDataAbstract(Container) from Container to Container {
 	}
 
 	public inline function compare (other:BytesDataAbstract):Int {
-		return Syntax.binop(this.s, '<=>', (other:Container).s);
+		return Syntax.spaceship(this.s, (other:Container).s);
 	}
 
 	public inline function getString (pos:Int, len:Int):String {
@@ -61,7 +63,7 @@ private abstract BytesDataAbstract(Container) from Container to Container {
 	}
 
 	public inline function blit (pos : Int, src : BytesDataAbstract, srcpos : Int, len : Int):Void {
-		this.s = Syntax.binop(Syntax.binop(Global.substr(this.s, 0, pos), '.', Global.substr(src, srcpos, len)), '.', Global.substr(this.s, pos + len));
+		this.s = Global.substr(this.s, 0, pos).concat(Global.substr(src, srcpos, len)).concat(Global.substr(this.s, pos + len));
 	}
 
 	inline function get_length ():Int {

+ 1 - 1
std/php/_std/haxe/zip/Compress.hx

@@ -49,7 +49,7 @@ class Compress {
 	}
 
 	public static function run( s : haxe.io.Bytes, level : Int ) : haxe.io.Bytes {
-		var c = untyped __call__("gzcompress", s.toString(), level);
+		var c = php.Global.gzcompress(s.toString(), level);
 		return haxe.io.Bytes.ofString(c);
 	}
 }

+ 1 - 1
std/php/_std/haxe/zip/Uncompress.hx

@@ -38,7 +38,7 @@ class Uncompress {
 	}
 
 	public static function run( src : haxe.io.Bytes, ?bufsize : Int ) : haxe.io.Bytes {
-		var c = untyped __call__("gzuncompress", src.toString());
+		var c = php.Global.gzuncompress(src.toString());
 		return haxe.io.Bytes.ofString(c);
 	}
 

+ 1 - 1
std/php/_std/sys/FileSystem.hx

@@ -62,7 +62,7 @@ class FileSystem {
 	}
 
 	public static inline function fullPath( relPath : String ) : String {
-		return (Syntax.binop(Global.realpath(relPath), "?:", null));
+		return Syntax.shortTernary(Global.realpath(relPath), null);
 	}
 
 	public static function absolutePath ( relPath : String ) : String {

+ 2 - 2
tests/sys/src/TestCommandBase.hx

@@ -41,7 +41,7 @@ class TestCommandBase {
 			#elseif hl
 				run("hl", [bin].concat(args));
 			#elseif php
-				run(untyped __php__("defined('PHP_BINARY') ? PHP_BINARY : 'php'"), [bin].concat(args));
+				run(php.Global.defined('PHP_BINARY') ? php.Const.PHP_BINARY : 'php', [bin].concat(args));
 			#elseif lua
 				run("lua", [bin].concat(args));
 			#else
@@ -123,7 +123,7 @@ class TestCommandBase {
 				#elseif hl
 					run("hl", [bin].concat(args));
 				#elseif php
-					run(untyped __php__("defined('PHP_BINARY') ? PHP_BINARY : 'php'"), [bin].concat(args));
+					run(php.Global.defined('PHP_BINARY') ? php.Const.PHP_BINARY : 'php', [bin].concat(args));
 				#elseif lua
 					run("lua", [bin].concat(args));
 				#else

+ 1 - 1
tests/unit/src/unit/TestJson.hx

@@ -50,7 +50,7 @@ class TestJson extends Test {
 	function testHaxeJson() {
 		#if php
 		// php's haxe.Utf8 uses mbstring
-		if (untyped __call__("extension_loaded", "mbstring")) {
+		if (php.Global.extension_loaded("mbstring")) {
 		#end
 
 		var str = haxe.format.JsonPrinter.print( { x : -4500, y : 1.456, a : ["hello", "wor'\"\n\t\rd"], b : function() {} } );

+ 2 - 2
tests/unit/src/unit/issues/Issue4695.hx

@@ -17,8 +17,8 @@ class Issue4695 extends unit.Test {
 
 	#if php
 	public function testNativeArray() {
-		var a1 = (untyped __php__)('array("f1" => 1, "f2" => 2, "f3" => 3)');
-		var a2 = (untyped __php__)('array("f1" => true, "f2" => "2", "f3" => 3)');
+		var a1 = php.Syntax.assocDecl({"f1" : 1, "f2" : 2, "f3" : 3});
+		var a2 = php.Syntax.assocDecl({"f1" : true, "f2" : "2", "f3" : 3});
 		f(a1 == a2);
 		f(eqCheck(a1, a2));
 	}

+ 6 - 2
tests/unit/src/unit/issues/Issue4973.hx

@@ -1,11 +1,15 @@
 package unit.issues;
 
+#if php
+import php.Exception;
+#end
+
 class Issue4973 extends Test {
 	#if php
 	function test() {
 		try sys.io.File.getContent("not-existant")
-		catch(exc:php.Exception) t(untyped __php__("{0} instanceof \\Exception", exc))
-		catch(exc:Dynamic) t(untyped __php__("{0} instanceof Exception", exc));
+		catch(exc:Exception) t(Std.is(exc, Exception))
+		catch(exc:Dynamic) t(Std.is(exc, Exception));
 	}
 	#end
 }

+ 3 - 5
tests/unit/src/unit/issues/Issue5270.hx

@@ -5,11 +5,9 @@ import php.*;
 class Issue5270 extends unit.Test {
 #if php
 	function test() {
-		untyped __php__("
-			$_SERVER['CONTENT_TYPE'] = 'type';
-			$_SERVER['HTTP_USER_AGENT'] = 'browser';
-			$_SERVER['REDIRECT_HTTP_AUTHORIZATION'] = 'auth1';
-		");
+		SuperGlobal._SERVER['CONTENT_TYPE'] = 'type';
+		SuperGlobal._SERVER['HTTP_USER_AGENT'] = 'browser';
+		SuperGlobal._SERVER['REDIRECT_HTTP_AUTHORIZATION'] = 'auth1';
 
 		eq(Web.getClientHeader('Content-Type'), 'type');
 		eq(Web.getClientHeader('User-Agent'), 'browser');

+ 2 - 2
tests/unit/src/unit/issues/Issue5565.hx

@@ -3,7 +3,7 @@ package unit.issues;
 class Issue5565 extends Test {
 	function test() {
 		#if php
-        t(Std.is(untyped __php__('[]'), php.NativeArray));
-        #end
+		t(Std.is(php.Syntax.arrayDecl(), php.NativeArray));
+		#end
 	}
 }

+ 1 - 1
tests/unit/src/unit/issues/Issue5923.hx

@@ -8,7 +8,7 @@ class Issue5923 extends unit.Test {
 #if php
 	function test() {
 		//These expressions should not fail at runtime
-		binop({}, '??', null).value = 1;
+		coalesce(({}:Dynamic), null).value = 1;
 		object(arrayDecl()).value = 1;
 		array({})['value'] = 1;
 

+ 1 - 1
tests/unit/src/unitstd/haxe/Utf8.unit.hx

@@ -1,6 +1,6 @@
 #if php
 // php's haxe.Utf8 uses mbstring
-if (untyped __call__("extension_loaded", "mbstring")) {
+if (php.Global.extension_loaded("mbstring")) {
 #end
 
 #if false