File Coverage

blib/lib/Apache/Voodoo/Debug/FirePHP.pm
Criterion Covered Total %
statement 65 179 36.3
branch 1 54 1.8
condition 2 27 7.4
subroutine 15 44 34.0
pod 0 23 0.0
total 83 327 25.3


line stmt bran cond sub pod time code
1             package Apache::Voodoo::Debug::FirePHP;
2              
3             $VERSION = "3.0200";
4              
5 2     2   1961 use strict;
  2         4  
  2         82  
6 2     2   12 use warnings;
  2         5  
  2         153  
7 2     2   11 no warnings 'uninitialized';
  2         4  
  2         84  
8              
9 2     2   11 use base("Apache::Voodoo::Debug::Common");
  2         5  
  2         185  
10              
11 2     2   13 use JSON::DWIW;
  2         12  
  2         22  
12              
13             use constant {
14 2         738 DEBUG => 'LOG',
15             INFO => 'INFO',
16             WARN => 'WARN',
17             ERROR => 'ERROR',
18             DUMP => 'DUMP',
19             TRACE => 'TRACE',
20             EXCEPTION => 'EXCEPTION',
21             TABLE => 'TABLE'
22 2     2   302 };
  2         13  
23              
24 2     2   13 use constant GROUP_START => 'GROUP_START';
  2         4  
  2         112  
25 2     2   13 use constant GROUP_END => 'GROUP_END';
  2         3  
  2         335  
26              
27 2     2   12 use constant WF_VERSION => "2.00";
  2         4  
  2         105  
28 2     2   14 use constant WF_PROTOCOL => 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2';
  2         4  
  2         143  
29 2     2   12 use constant WF_PLUGIN => 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/'.WF_VERSION;
  2         4  
  2         104  
30 2     2   10 use constant WF_STRUCTURE1 => 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1';
  2         3  
  2         494  
31 2     2   15 use constant WF_STRUCTURE2 => 'http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1';
  2         5  
  2         112  
32              
33 2     2   11 use constant BLOCK_LENGTH => 5000;
  2         11  
  2         6158  
34              
35             sub new {
36 3     3 0 9 my $class = shift;
37 3         7 my $id = shift;
38 3         10 my $conf = shift;
39              
40 3         6 my $self = {};
41 3         14 bless $self,$class;
42              
43 3         195 $self->{json} = JSON::DWIW->new({bad_char_policy => 'convert'});
44              
45 3     0   179 $self->{setHeader} = sub { return; };
  0         0  
46 3     0   12 $self->{userAgent} = sub { return; };
  0         0  
47              
48 3         15 my @flags = qw(debug info warn error exception table trace);
49              
50 3         8 $self->{enabled} = 0;
51 3 50 33     45 if ($conf eq "1" || (ref($conf) eq "HASH" && $conf->{all})) {
    0 33        
52 3         11 $self->{conf}->{LOG} = 1;
53 3         8 $self->{conf}->{INFO} = 1;
54 3         8 $self->{conf}->{WARN} = 1;
55 3         8 $self->{conf}->{ERROR} = 1;
56 3         8 $self->{conf}->{DUMP} = 1;
57 3         8 $self->{conf}->{TRACE} = 1;
58 3         5 $self->{conf}->{EXCEPTION} = 1;
59 3         9 $self->{conf}->{TABLE} = 1;
60 3         8 $self->{conf}->{GROUP_START} = 1;
61 3         7 $self->{conf}->{GROUP_END} = 1;
62              
63 3         8 $self->{enabled} = 1;
64             }
65             elsif (ref($conf) eq "HASH") {
66 0 0       0 $self->{conf}->{LOG} = 1 if $conf->{debug};
67 0 0       0 $self->{conf}->{INFO} = 1 if $conf->{info};
68 0 0       0 $self->{conf}->{WARN} = 1 if $conf->{warn};
69 0 0       0 $self->{conf}->{ERROR} = 1 if $conf->{error};
70 0 0       0 $self->{conf}->{DUMP} = 1 if $conf->{dump};
71 0 0       0 $self->{conf}->{TRACE} = 1 if $conf->{trace};
72 0 0       0 $self->{conf}->{EXCEPTION} = 1 if $conf->{exception};
73 0 0       0 $self->{conf}->{TABLE} = 1 if $conf->{table};
74              
75 0 0       0 if (scalar keys %{$self->{'conf'}}) {
  0         0  
76 0         0 $self->{enabled} = 1;
77 0         0 $self->{conf}->{GROUP_START} = 1;
78 0         0 $self->{conf}->{GROUP_END} = 1;
79             }
80             }
81              
82 3         17 return $self;
83             }
84              
85             sub init {
86 0     0 0   my $self = shift;
87              
88 0           $self->{mp} = shift;
89              
90 0           $self->{enabled} = 0;
91              
92 0 0         return unless $self->_detectClientExtension();
93              
94 0           $self->{enable} = $self->{conf};
95 0           $self->{messageIndex} = 1;
96             }
97              
98 0     0 0   sub shutdown { return; }
99              
100             sub setProcessorUrl {
101 0     0 0   my $self = shift;
102 0           my $URL = shift;
103              
104 0           $self->setHeader('X-FirePHP-ProcessorURL' => $URL);
105             }
106              
107             sub setRendererUrl {
108 0     0 0   my $self = shift;
109 0           my $URL = shift;
110              
111 0           $self->setHeader('X-FirePHP-RendererURL' => $URL);
112             }
113              
114 0     0 0   sub debug { return $_[0]->_fb($_[1], $_[2], DEBUG); }
115 0     0 0   sub info { return $_[0]->_fb($_[1], $_[2], INFO); }
116 0     0 0   sub warn { return $_[0]->_fb($_[1], $_[2], WARN); }
117 0     0 0   sub error { return $_[0]->_fb($_[1], $_[2], ERROR); }
118 0     0 0   sub exception { return $_[0]->_fb($_[1], $_[2], EXCEPTION); }
119 0     0 0   sub trace { return $_[0]->_fb($_[1], undef, TRACE); }
120 0     0 0   sub table { return $_[0]->_fb($_[1], $_[2], TABLE); }
121              
122 0     0     sub _group { return $_[0]->_fb($_[1], undef, GROUP_START); }
123 0     0     sub _groupEnd { return $_[0]->_fb(undef, undef, GROUP_END); }
124              
125             #
126             # At some point in the future we might push this info out
127             # through FirePHP, but not right now.
128             #
129 0     0 0   sub mark { return; }
130 0     0 0   sub return_data { return; }
131 0     0 0   sub session_id { return; }
132 0     0 0   sub url { return; }
133 0     0 0   sub status { return; }
134 0     0 0   sub params { return; }
135 0     0 0   sub template_conf { return; }
136 0     0 0   sub session { return; }
137              
138             # This is here for API compliance.
139             # FirePHP has no finalize step
140 0     0 0   sub finalize { return (); }
141              
142             #
143             # Relies on having a callback setup in the constructor that returns the user agent
144             #
145             sub _detectClientExtension {
146 0     0     my $self = shift;
147              
148 0           my $useragent = $self->{mp}->header_in('User-Agent');
149              
150 0 0 0       if ($useragent =~ /\bFirePHP\/([.\d]+)/ && $self->_compareVersion($1,'0.0.6')) {
151 0           return 1;
152             }
153             else {
154 0           return 0;
155             }
156             }
157              
158             sub _compareVersion {
159 0     0     my $self = shift;
160              
161 0           my @f = split(/\./,shift);
162 0           my @s = split(/\./,shift);
163              
164 0 0         my $c = (scalar(@f) > scalar(@s))?scalar(@f):scalar(@s);
165              
166 0           for (my $i=0; $i < $c; $i++) {
167 0 0 0       if ($f[$i] < $s[$i] || (!defined($f[$i]) && defined($s[$i]))) {
    0 0        
      0        
      0        
168 0           return 0;
169             }
170             elsif ($f[$i] > $s[$i] || (defined($f[$i]) && !defined($s[$i]))) {
171 0           return 1;
172             }
173             }
174 0           return 1;
175             }
176              
177             sub _fb {
178 0     0     my $self = shift;
179              
180 0           my $Label = shift;
181 0           my $Object = shift;
182 0           my $Type = shift;
183              
184 0 0         return unless $self->{enable}->{$Type};
185              
186 0 0 0       unless (defined($Object) || $Type eq GROUP_START) {
187 0           $Object = $Label;
188 0           $Label = undef;
189             }
190              
191 0           my %meta = ();
192              
193 0 0 0       if ($Type eq EXCEPTION || $Type eq TRACE) {
194 0           my @trace = $self->stack_trace(1);
195              
196 0           my $t = shift @trace;
197              
198 0           $meta{'File'} = $t->{class}.$t->{type}.$t->{function};
199 0           $meta{'Line'} = $t->{line};
200              
201 0           $Object = {
202             'Class' => $t->{class},
203             'Type' => $t->{type},
204             'Function'=> $t->{function},
205             'Message' => $Object,
206             'File' => $t->{file},
207             'Line' => $t->{line},
208             'Args' => $t->{args},
209             'Trace' => \@trace
210             };
211             }
212             else {
213 0           my @trace = $self->stack_trace(1);
214              
215 0           $meta{'File'} = $trace[0]->{class}.$trace[0]->{type}.$trace[0]->{function};
216 0           $meta{'Line'} = $trace[0]->{line};
217             }
218              
219 0           my $structure_index = 1;
220 0 0         if ($self->{messageIndex} == 1) {
221 0           $self->setHeader('X-Wf-Protocol-1',WF_PROTOCOL);
222 0           $self->setHeader('X-Wf-1-Plugin-1',WF_PLUGIN);
223              
224 0 0         if ($Type eq DUMP) {
225 0           $structure_index = 2;
226 0           $self->setHeader('X-Wf-1-Structure-2',WF_STRUCTURE2);
227             }
228             else {
229 0           $self->setHeader('X-Wf-1-Structure-1',WF_STRUCTURE1);
230             }
231             }
232              
233 0           my $msg;
234 0 0         if ($Type eq DUMP) {
235 0           $msg = '{"'.$Label.'":'.$self->jsonEncode($Object).'}';
236             }
237             else {
238 0           $meta{'Type'} = $Type;
239 0           $meta{'Label'} = $Label;
240              
241 0           $msg = '['.$self->jsonEncode(\%meta).','.$self->jsonEncode($Object).']';
242             }
243              
244             # FirePHP wants the number of bytes, not characters. So we can't use length() here, a 2 or 3 byte
245             # character counts as 1 as far as length is concerned.
246 0           my $l = length(unpack('b*',$msg))/8;
247              
248 0 0         if ($l < BLOCK_LENGTH) {
249             # The message can be send in one block
250 0           $self->setHeader(
251             'X-Wf-1-'.$structure_index.'-1-'.$self->{'messageIndex'},
252             $l . '|' . $msg . '|'
253             );
254              
255 0           $self->{'messageIndex'}++;
256             }
257             else {
258             # Message needs to be split into multiple parts
259 0 0         my $c = ($l % BLOCK_LENGTH)?int($l/BLOCK_LENGTH)+1:$l/BLOCK_LENGTH;
260              
261 0           foreach (my $i=0; $i < $c; $i++) {
262 0           my $part = substr($msg, $i*BLOCK_LENGTH, BLOCK_LENGTH);
263              
264 0           my $v;
265             # length prefix on the first part
266 0 0         $v .= $l if ($i==0);
267              
268             # the data
269 0           $v .= '|'.$part.'|';
270              
271             # \ on the end of the line, on all but the last part
272 0 0         $v .= '\\' if ($i < ($c-1));
273              
274 0           $self->setHeader('X-Wf-1-'.$structure_index.'-1-'.$self->{'messageIndex'}, $v);
275              
276 0           $self->{'messageIndex'}++;
277              
278 0 0         if ($self->{'messageIndex'} > 99999) {
279             #throw new Exception('Maximum number (99,999) of messages reached!');
280             }
281             }
282             }
283              
284             #$self->setHeader('X-Wf-1-Index',$self->{'messageIndex'}-1);
285              
286 0           return 1;
287             }
288              
289             sub setHeader() {
290 0     0 0   my $self = shift;
291 0           my $name = shift;
292 0           my $value = shift;
293              
294 0           $self->{mp}->header_out($name,$value);
295             }
296              
297             sub jsonEncode {
298 0     0 0   my $self = shift;
299 0           my $Object = shift;
300              
301 0           return $self->{'json'}->to_json($Object);
302             }
303              
304             1;
305              
306             ################################################################################
307             # Copyright (c) 2005-2010 Steven Edwards (maverick@smurfbane.org).
308             # All rights reserved.
309             #
310             # You may use and distribute Apache::Voodoo under the terms described in the
311             # LICENSE file include in this package. The summary is it's a legalese version
312             # of the Artistic License :)
313             #
314             ################################################################################