Current section

Files

Jump to
ephp src ephp_lib_func.erl
Raw

src/ephp_lib_func.erl

-module(ephp_lib_func).
-author('manuel@altenwald.com').
-behaviour(ephp_lib).
-export([init_func/0, init_config/0, init_const/0, get_classes/0,
register_shutdown_function/3, get_defined_functions/2, function_exists/3, func_num_args/2,
func_get_arg/3, func_get_args/2, call_user_func/3, call_user_func_array/4,
create_function/4]).
-include("ephp.hrl").
-include("ephp_parser.hrl").
-include("ephp_array.hrl").
-spec init_func() -> ephp_lib:php_function_results().
init_func() ->
[{register_shutdown_function, [pack_args]},
get_defined_functions,
function_exists,
func_num_args,
func_get_args,
{func_get_arg, [{args, [integer]}]},
{call_user_func, [pack_args]},
{call_user_func_array, [callable, array]},
{create_function, [{args, [string, string]}]}].
-spec init_config() -> ephp_lib:php_config_results().
init_config() ->
[].
-spec init_const() -> ephp_lib:php_const_results().
init_const() ->
[].
-spec get_classes() -> [class()].
get_classes() ->
ephp_class_closure:get_classes().
-spec register_shutdown_function(ephp:context_id(), line(), [var_value()]) -> ok.
register_shutdown_function(Context, _Line, [{_, Callback} | _RawArgs]) ->
%% TODO: add params to call the functions.
ephp_context:register_shutdown_func(Context, Callback),
ok.
-spec func_num_args(ephp:context_id(), line()) -> non_neg_integer().
func_num_args(Context, _Line) ->
ephp_context:get_active_function_arity(Context).
-spec func_get_args(ephp:context_id(), line()) -> mixed().
func_get_args(Context, _Line) ->
ephp_context:get_active_function_args(Context).
-spec func_get_arg(ephp:context_id(), line(), Pos :: var_value()) -> mixed().
func_get_arg(Context, _Line, {_, Pos}) ->
ephp_context:get_active_function_arg(Context, Pos).
-spec get_defined_functions(ephp:context_id(), line()) -> ephp_array:ephp_array().
get_defined_functions(Context, _Line) ->
Append =
fun({Func, Type}, {I, Dict}) ->
NewTypeDict =
case ephp_array:find(Type, Dict) of
{ok, TypeDict} ->
ephp_array:store(I, Func, TypeDict)
end,
{I + 1, ephp_array:store(Type, NewTypeDict, Dict)}
end,
BaseDict =
ephp_array:store(<<"user">>,
ephp_array:new(),
ephp_array:store(<<"internal">>, ephp_array:new(), ephp_array:new())),
Functions = ephp_context:get_functions(Context),
{_, FuncList} = lists:foldl(Append, {0, BaseDict}, Functions),
FuncList.
-spec function_exists(ephp:context_id(), line(), FuncName :: var_value()) -> boolean().
function_exists(Context, _Line, {_, FuncName}) ->
%% TODO split NS from FuncName
ephp_context:get_function(Context, FuncName) =/= error.
-spec call_user_func_array(ephp:context_id(), line(), var_value(), var_value()) ->
mixed().
call_user_func_array(Context, Line, {_, FuncName}, {_, Args}) ->
call(Context, Line, FuncName, ephp_array:values(Args)).
-spec call_user_func(ephp:context_id(), line(), [var_value()]) -> mixed().
call_user_func(Context, Line, [{_, FuncName} | Args])
when is_binary(FuncName) orelse ?IS_ARRAY(FuncName) orelse ?IS_OBJECT(FuncName) ->
call(Context, Line, FuncName, [Arg || {_, Arg} <- Args]);
call_user_func(Context, Line, _Args) ->
File = ephp_context:get_active_file(Context),
Data = {<<"call_user_func">>, 1, <<"a valid callback">>, <<"no array or string">>},
Error = {error, ewrongarg, Line, File, ?E_WARNING, Data},
ephp_error:handle_error(Context, Error),
false.
-spec create_function(ephp:context_id(),
line(),
Args :: var_value(),
Code :: var_value()) ->
obj_ref().
create_function(Context, {{line, Line}, _}, {_, Args}, {_, Code}) ->
Pos = #parser{level = code, row = Line},
{_, _, A} = ephp_parser_func:funct_args(<<Args/binary, ")">>, Pos, []),
{_, _, C} = ephp_parser:code(Code, Pos, []),
Closure = ephp_parser:add_line(#function{args = A, code = C}, Pos),
ObjRef = ephp_context:solve(Context, Closure),
ObjCtx = ephp_object:get_context(ObjRef),
ephp_context:set_meta(ObjCtx, is_lambda, true),
ObjRef.
call(Context, Line, FuncName, Args) when is_binary(FuncName) andalso is_list(Args) ->
case binary:split(FuncName, <<"::">>) of
[ClassName, StaticMethod] ->
Call =
#call{name = StaticMethod,
class = ClassName,
type = class,
line = Line},
ephp_context:call_function(Context, Call);
[FuncName] ->
Call = #call{name = FuncName, args = Args},
ephp_context:call_function(Context, Call)
end;
call(Context, Line, Callable, Args) when ?IS_ARRAY(Callable) andalso is_list(Args) ->
case ephp_array:to_list(Callable) of
[{_, Object}, {_, Method}] when ?IS_OBJECT(Object) andalso is_binary(Method) ->
Call =
#call{name = Method,
type = object,
args = Args,
line = Line},
ephp_context:call_method(Context, Object, Call);
[{_, Class}, {_, Method}] when is_binary(Class) andalso is_binary(Method) ->
case binary:split(Method, <<"::">>) of
[Method] ->
Call =
#call{name = Method,
class = Class,
type = class,
line = Line},
ephp_context:call_function(Context, Call);
[<<"parent">>, ParentMethod] ->
%% TODO: when parent isn't defined
{ClassNS, ClassName} = ephp_ns:parse(Class),
{ParentNS, ParentName} = ephp_class:get_parent(Context, ClassNS, ClassName),
Call =
#call{name = ParentMethod,
class = ParentName,
namespace = ParentNS,
type = class},
ephp_context:call_function(Context, Call)
end
end;
call(Context, Line, Object, Args) when ?IS_OBJECT(Object) andalso is_list(Args) ->
%% TODO: error when __invoke is not defined
Call =
#call{name = <<"__invoke">>,
type = object,
args = Args,
line = Line},
ephp_context:call_method(Context, Object, Call).