Current section

Files

Jump to
ephp src ephp_stack.erl
Raw

src/ephp_stack.erl

-module(ephp_stack).
-author('manuel@altenwald.com').
-compile([{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.