-module(ephp_func). -author('manuel@altenwald.com'). -compile([warnings_as_errors]). -include("ephp.hrl"). % init_conf -type php_config_results() :: [{config_section(), [config_param()]}]. -type config_section() :: binary(). -type config_param() :: mixed(). % init_const -type php_const_results() :: [{const_name(), number() | binary()}]. -type const_name() :: binary(). % init_func -type php_function_results() :: [php_function_defs()]. -type php_function_defs() :: php_function() | {php_function(), php_function_opts()}. -type php_function() :: atom(). -type php_function_opts() :: [php_function_opt()]. -type php_function_opt() :: pack_args | {alias, php_function_alias()} | php_function_args(). -type php_function_alias() :: binary(). -type php_function_args() :: {args, validation_args()}. -type default_value() :: mixed(). -type error_return_value() :: mixed(). -type min_args() :: non_neg_integer(). -type max_args() :: non_neg_integer(). -type type_arg() :: mixed | string | integer | array | object | resource | raw | type_ref. -type validation_arg() :: {type_arg(), default_value()} | type_arg(). -type validation_args() :: {min_args(), max_args(), error_return_value(), [validation_arg()]} | [validation_arg()] | undefined | no_resolve. -export_type([ php_function/0, php_function_results/0, php_function_opts/0, php_function_opt/0, config_section/0, config_param/0, php_config_results/0, validation_args/0 ]). -callback init_func() -> php_function_results(). -callback init_config() -> php_config_results(). -callback init_const() -> php_const_results(). %% ------------------------------------------------------------------ %% API Function Exports %% ------------------------------------------------------------------ -export([ start_link/0, destroy/1, get/2, is_defined/2, get_functions/1, get_static_value/3, set_static_value/4, set_static/4, init_static_value/4, run/2, register_func/5, register_func/6, register_func/7 ]). %% ------------------------------------------------------------------ %% API Function Definitions %% ------------------------------------------------------------------ start_link() -> Ref = make_ref(), erlang:put(Ref, dict:new()), {ok, Ref}. destroy(Funcs) -> erlang:erase(Funcs), ok. get(Ref, FuncName) -> Funcs = erlang:get(Ref), IFuncName = ephp_string:to_lower(FuncName), dict:find(IFuncName, Funcs). get_static_value(Ref, FuncName, VarName) -> case get(Ref, FuncName) of {ok, #reg_func{static = Static}} -> case orddict:find(VarName, Static) of {ok, Value} -> Value; error -> undefined end; error -> throw({error, enofunc}) end. set_static(Ref, FuncName, Vars, Context) -> Funcs = erlang:get(Ref), IFuncName = ephp_string:to_lower(FuncName), {ok, #reg_func{static = Static} = RegFunc} = dict:find(IFuncName, Funcs), NewStatic = lists:map(fun({Key, _}) -> %% TODO check behaviour when use unset NewValue = ephp_vars:get(Vars, #variable{name = Key}, Context), {Key, NewValue} end, Static), NewRegFunc = RegFunc#reg_func{static = NewStatic}, NewFuncs = dict:store(IFuncName, NewRegFunc, Funcs), erlang:put(Ref, NewFuncs), ok. set_static_value(Ref, FuncName, VarName, Value) -> Funcs = erlang:get(Ref), IFuncName = ephp_string:to_lower(FuncName), case dict:find(IFuncName, Funcs) of {ok, #reg_func{static = Static} = RegFunc} -> NewStatic = orddict:store(VarName, Value, Static), NewRegFunc = RegFunc#reg_func{static = NewStatic}, NewFuncs = dict:store(IFuncName, NewRegFunc, Funcs), erlang:put(Ref, NewFuncs), ok; error -> throw({error, enofunc}) end. init_static_value(Ref, FuncName, VarName, Value) -> Funcs = erlang:get(Ref), IFuncName = ephp_string:to_lower(FuncName), case dict:find(IFuncName, Funcs) of {ok, #reg_func{static = Static}} -> case orddict:find(VarName, Static) of {ok, RealValue} -> RealValue; error -> UpdateFunc = fun(RegFunc) -> NewStatic = orddict:store(VarName, Value, Static), RegFunc#reg_func{static = NewStatic} end, NewFuncs = dict:update(IFuncName, UpdateFunc, Funcs), erlang:put(Ref, NewFuncs), Value end; error -> throw({error, enofunc}) end. is_defined(Ref, FuncName) -> Funcs = erlang:get(Ref), dict:is_key(FuncName, Funcs). get_functions(Ref) -> Funcs = erlang:get(Ref), Get = fun (#reg_func{name=Name, type=builtin}) -> {Name, <<"internal">>}; (#reg_func{name=Name, type=php}) -> {Name, <<"user">>} end, [ Get(FuncName) || {_,FuncName} <- dict:to_list(Funcs) ]. register_func(Ref, File, PHPFunc, Args, Code) -> register_func(Ref, File, PHPFunc, Args, Code, false, undefined). register_func(Ref, File, PHPFunc, Module, Fun, ValArgs) when is_atom(Module) andalso is_atom(Fun) -> register_func(Ref, File, PHPFunc, Module, Fun, false, ValArgs). register_func(Ref, File, PHPFunc, Module, Fun, PackArgs, ValArgs) when is_atom(Module) andalso is_atom(Fun) -> Funcs = erlang:get(Ref), IPHPFunc = ephp_string:to_lower(PHPFunc), RegFunc = #reg_func{ name=IPHPFunc, type=builtin, file=File, builtin={Module, Fun}, pack_args=PackArgs, validation_args=ValArgs}, erlang:put(Ref, dict:store(IPHPFunc, RegFunc, Funcs)), ok; register_func(Ref, File, PHPFunc, Args, Code, PackArgs, ValArgs) -> Funcs = erlang:get(Ref), IPHPFunc = ephp_string:to_lower(PHPFunc), RegFunc = #reg_func{ name=IPHPFunc, type=php, file=File, args=Args, code=Code, pack_args=PackArgs, validation_args=ValArgs}, erlang:put(Ref, dict:store(IPHPFunc, RegFunc, Funcs)), ok. run(Context, #call{line=Line}=Call) -> try ephp_context:solve(Context, Call), false catch throw:{error, erequired, _, ReqFile} -> File = ephp_context:get_active_file(Context), Data = {ReqFile}, ephp_error:handle_error(Context, {error, erequired, Line, File, ?E_ERROR, Data}); throw:{error, eundefun, _, Fun} -> File = ephp_context:get_active_file(Context), Data = {Fun}, ephp_error:handle_error(Context, {error, eundefun, Line, File, ?E_ERROR, Data}) end.