Current section
Files
Jump to
Current section
Files
src/ephp_stack.erl
-module(ephp_stack).
-author('manuel@altenwald.com').
-compile([warnings_as_errors, {no_auto_import, [get/1]}]).
-behaviour(gen_server).
-include("ephp.hrl").
-export([
start_link/0,
destroy/0,
get/0,
get_array/0,
get_array/1,
push/6,
pop/0
]).
-export([
init/1,
handle_cast/2,
handle_call/3,
handle_info/2,
code_change/3,
terminate/2
]).
start_link() ->
case erlang:get(stack) of
undefined ->
%% FIXME: generate this information under a supervisor with
%% the context.
{ok, PID} = gen_server:start_link(?MODULE, [self()], []),
erlang:put(stack, PID),
{ok, PID};
PID ->
case is_process_alive(PID) of
true ->
{ok, PID};
false ->
erlang:erase(stack),
start_link()
end
end.
destroy() ->
case erlang:get(stack) of
undefined -> ok;
PID -> gen_server:stop(PID)
end.
get() ->
PID = erlang:get(stack),
gen_server:call(PID, get).
get_array() ->
get_array(0).
get_array(PopElements) ->
PID = erlang:get(stack),
gen_server:call(PID, {get_array, PopElements}).
push(_File, undefined, _Fun, _Args, _Class, _Object) ->
ok;
push(File, {{line, Line}, _}, Fun, Args, Class, Object) ->
PID = erlang:get(stack),
Data = {push, File, Line, Fun, Args, Class, Object},
gen_server:cast(PID, Data).
pop() ->
PID = erlang:get(stack),
gen_server:call(PID, pop).
%% gen_server callbacks
init([Parent]) ->
monitor(process, Parent),
{ok, []}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
terminate(_Reason, _State) ->
erlang:erase(stack),
ok.
handle_cast({push, File, Line, Fun, Args, Class, Object}, Stack) ->
Type = if
Class =:= undefined -> undefined;
Object =:= undefined -> <<"::">>;
true -> <<"->">>
end,
New = #stack_trace{
function = Fun,
args = Args,
file = File,
line = Line,
object = Object,
class = Class,
type = Type
},
NewStack = case add_function(Fun) of
[] when File =:= undefined -> Stack;
_ -> [New|Stack]
end,
%% TODO: use filtering tables for tracing, implement ephp_tracer.
case ephp_config:get_bool(<<"tracer.enable">>) of
true -> gen_server:cast(ephp_tracer, New);
false -> ok
end,
{noreply, NewStack}.
handle_call({get_array, PopElements}, _From, Stack) ->
{_, GetStack} = lists:foldl(fun
(StackEl, {0, Array}) ->
#stack_trace{
function = Fun,
file = File,
line = Line,
class = Class,
object = Object,
type = Type,
args = Args
} = StackEl,
Element = ephp_array:from_list(
[
{<<"file">>, File},
{<<"line">>, Line}
] ++ add_function(Fun)
++ add_class(Class)
++ add_object(Object)
++ add_type(Type)
++ add_args(Fun, Args)),
{0, ephp_array:store(auto, Element, Array)};
(_Stack, {N, Array}) ->
{N-1, Array}
end, {PopElements, ephp_array:new()}, Stack),
{reply, GetStack, Stack};
handle_call(get, _From, Stack) ->
{reply, Stack, Stack};
handle_call(pop, _From, [] = Stack) ->
{reply, undefined, Stack};
handle_call(pop, _From, [Head|Stack]) ->
{reply, Head, Stack}.
handle_info({'DOWN', _Ref, process, _Pid, _Reason}, State) ->
{stop, normal, State}.
%% internal functions
add_function(Fun) ->
Incs = [<<"include">>, <<"include_once">>,
<<"require">>, <<"require_once">>],
case lists:member(ephp_string:to_lower(Fun), Incs) of
true -> [];
false -> [{<<"function">>, Fun}]
end.
add_class(undefined) -> [];
add_class(Class) -> [{<<"class">>, Class}].
add_object(undefined) -> [];
add_object(Object) -> [{<<"object">>, Object}].
add_type(undefined) -> [];
add_type(Type) -> [{<<"type">>, Type}].
add_args(Fun, Args) ->
[{<<"args">>, ephp_array:from_list(Args)}] ++
case add_function(Fun) of
[] -> [{<<"function">>, Fun}];
_ -> []
end.