line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package PHP::Session::Serializer::PHP; |
2
|
|
|
|
|
|
|
|
3
|
13
|
|
|
13
|
|
72
|
use strict; |
|
13
|
|
|
|
|
21
|
|
|
13
|
|
|
|
|
609
|
|
4
|
13
|
|
|
13
|
|
191
|
use vars qw($VERSION); |
|
13
|
|
|
|
|
41
|
|
|
13
|
|
|
|
|
63550
|
|
5
|
|
|
|
|
|
|
$VERSION = 0.26; |
6
|
|
|
|
|
|
|
|
7
|
0
|
|
|
0
|
|
0
|
sub _croak { require Carp; Carp::croak(@_) } |
|
0
|
|
|
|
|
0
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
sub new { |
10
|
49
|
|
|
49
|
0
|
83
|
my $class = shift; |
11
|
49
|
|
|
|
|
455
|
bless { |
12
|
|
|
|
|
|
|
buffer => undef, |
13
|
|
|
|
|
|
|
data => {}, |
14
|
|
|
|
|
|
|
state => undef, |
15
|
|
|
|
|
|
|
stack => [], |
16
|
|
|
|
|
|
|
array => [], # array-ref of hash-ref |
17
|
|
|
|
|
|
|
}, $class; |
18
|
|
|
|
|
|
|
} |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
# encoder starts here |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
sub encode { |
23
|
14
|
|
|
14
|
0
|
24
|
my($self, $data) = @_; |
24
|
14
|
|
|
|
|
24
|
my $body; |
25
|
14
|
|
|
|
|
48
|
for my $key (keys %$data) { |
26
|
22
|
100
|
|
|
|
65
|
if (defined $data->{$key}) { |
27
|
20
|
|
|
|
|
63
|
$body .= "$key|" . $self->do_encode($data->{$key}); |
28
|
|
|
|
|
|
|
} else { |
29
|
2
|
|
|
|
|
7
|
$body .= "!$key|"; |
30
|
|
|
|
|
|
|
} |
31
|
|
|
|
|
|
|
} |
32
|
14
|
|
|
|
|
86
|
return $body; |
33
|
|
|
|
|
|
|
} |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
sub do_encode { |
36
|
42
|
|
|
42
|
0
|
112
|
my($self, $value) = @_; |
37
|
42
|
50
|
|
|
|
130
|
if (! defined $value) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
38
|
0
|
|
|
|
|
0
|
return $self->encode_null($value); |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
elsif (! ref $value) { |
41
|
35
|
100
|
|
|
|
73
|
if (is_int($value)) { |
|
|
100
|
|
|
|
|
|
42
|
4
|
|
|
|
|
46
|
return $self->encode_int($value); |
43
|
|
|
|
|
|
|
} |
44
|
|
|
|
|
|
|
elsif (is_float($value)) { |
45
|
2
|
|
|
|
|
8
|
return $self->encode_double($value); |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
else { |
48
|
29
|
|
|
|
|
65
|
return $self->encode_string($value); |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
elsif (ref $value eq 'HASH') { |
52
|
3
|
|
|
|
|
9
|
return $self->encode_array($value); |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
elsif (ref $value eq 'ARRAY') { |
55
|
0
|
|
|
|
|
0
|
return $self->encode_array($value); |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
elsif (ref $value eq 'PHP::Session::Object') { |
58
|
4
|
|
|
|
|
7
|
return $self->encode_object($value); |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
else { |
61
|
0
|
|
|
|
|
0
|
_croak("Can't encode ", ref($value)); |
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
sub encode_null { |
66
|
0
|
|
|
0
|
0
|
0
|
my($self, $value) = @_; |
67
|
0
|
|
|
|
|
0
|
return 'N;'; |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
sub encode_int { |
71
|
4
|
|
|
4
|
0
|
9
|
my($self, $value) = @_; |
72
|
4
|
|
|
|
|
25
|
return sprintf 'i:%d;', $value; |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
sub encode_double { |
76
|
2
|
|
|
2
|
0
|
3
|
my($self, $value) = @_; |
77
|
2
|
|
|
|
|
12
|
return sprintf "d:%s;", $value; # XXX hack |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
sub encode_string { |
81
|
29
|
|
|
29
|
0
|
63
|
my($self, $value) = @_; |
82
|
29
|
|
|
|
|
475
|
return sprintf 's:%d:"%s";', length($value), $value; |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
sub encode_array { |
86
|
3
|
|
|
3
|
0
|
5
|
my($self, $value) = @_; |
87
|
3
|
50
|
|
|
|
14
|
my %array = ref $value eq 'HASH' ? %$value : map { $_ => $value->[$_] } 0..$#{$value}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
88
|
3
|
|
|
|
|
13
|
return sprintf 'a:%d:{%s}', scalar(keys %array), join('', map $self->do_encode($_), %array); |
89
|
|
|
|
|
|
|
} |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
sub encode_object { |
92
|
4
|
|
|
4
|
0
|
4
|
my($self, $value) = @_; |
93
|
4
|
|
|
|
|
20
|
my %impl = %$value; |
94
|
4
|
|
|
|
|
8
|
my $class = delete $impl{_class}; |
95
|
4
|
|
|
|
|
15
|
return sprintf 'O:%d:"%s":%d:{%s}', length($class), $class, scalar(keys %impl), |
96
|
|
|
|
|
|
|
join('', map $self->do_encode($_), %impl); |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
sub is_int { |
100
|
35
|
|
|
35
|
0
|
96
|
local $_ = shift; |
101
|
35
|
|
|
|
|
172
|
/^-?(0|[1-9]\d{0,8})$/; |
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
sub is_float { |
105
|
31
|
|
|
31
|
0
|
44
|
local $_ = shift; |
106
|
31
|
|
|
|
|
92
|
/^-?(0|[1-9]\d{0,8})\.\d+$/; |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# decoder starts here |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
sub decode { |
112
|
35
|
|
|
35
|
0
|
67
|
my($self, $data) = @_; |
113
|
35
|
|
|
|
|
151
|
$self->{buffer} = $data; |
114
|
35
|
|
|
|
|
95
|
$self->change_state('VarName'); |
115
|
35
|
|
100
|
|
|
239
|
while (defined $self->{buffer} && length $self->{buffer}) { |
116
|
6438
|
|
|
|
|
15029
|
$self->{state}->parse($self); |
117
|
|
|
|
|
|
|
} |
118
|
35
|
|
|
|
|
183
|
return $self->{data}; |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
sub change_state { |
122
|
7081
|
|
|
7081
|
0
|
9178
|
my($self, $state) = @_; |
123
|
7081
|
|
|
|
|
42815
|
$self->{state} = "PHP::Session::Serializer::PHP::State::$state"; # optimization |
124
|
|
|
|
|
|
|
# $self->{state} = PHP::Session::Serializer::PHP::State->new($state); |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
sub set { |
129
|
73
|
|
|
73
|
0
|
172
|
my($self, $key, $value) = @_; |
130
|
73
|
|
|
|
|
294
|
$self->{data}->{$key} = $value; |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
sub push_stack { |
134
|
6056
|
|
|
6056
|
0
|
9542
|
my($self, $stuff) = @_; |
135
|
6056
|
|
|
|
|
5628
|
push @{$self->{stack}}, $stuff; |
|
6056
|
|
|
|
|
14686
|
|
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
sub pop_stack { |
139
|
2542
|
|
|
2542
|
0
|
2586
|
my $self = shift; |
140
|
2542
|
|
|
|
|
2331
|
pop @{$self->{stack}}; |
|
2542
|
|
|
|
|
5281
|
|
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
sub extract_stack { |
144
|
309
|
|
|
309
|
0
|
362
|
my($self, $num) = @_; |
145
|
309
|
100
|
|
|
|
525
|
return $num ? splice(@{$self->{stack}}, -$num) : (); |
|
258
|
|
|
|
|
1491
|
|
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
# array: [ [ $length, $consuming, $class ], [ $length, $consuming, $class ] .. ] |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
sub start_array { |
151
|
309
|
|
|
309
|
0
|
539
|
my($self, $length, $class) = @_; |
152
|
309
|
|
|
|
|
298
|
unshift @{$self->{array}}, [ $length, 0, $class ]; |
|
309
|
|
|
|
|
1070
|
|
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
sub in_array { |
156
|
3937
|
|
|
3937
|
0
|
9660
|
my $self = shift; |
157
|
3937
|
|
|
|
|
3595
|
return scalar @{$self->{array}}; |
|
3937
|
|
|
|
|
10560
|
|
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
sub consume_array { |
161
|
3514
|
|
|
3514
|
0
|
3724
|
my $self = shift; |
162
|
3514
|
|
|
|
|
6799
|
$self->{array}->[0]->[1]++; |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
sub finished_array { |
166
|
3565
|
|
|
3565
|
0
|
3710
|
my $self = shift; |
167
|
3565
|
|
|
|
|
11289
|
return $self->{array}->[0]->[0] * 2 == $self->{array}->[0]->[1]; |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
sub elements_count { |
171
|
309
|
|
|
309
|
0
|
349
|
my $self = shift; |
172
|
309
|
|
|
|
|
878
|
return $self->{array}->[0]->[0]; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
sub process_value { |
176
|
3628
|
|
|
3628
|
0
|
5528
|
my($self, $value, $empty_skip) = @_; |
177
|
3628
|
100
|
|
|
|
6091
|
if ($self->in_array()) { |
178
|
3565
|
100
|
|
|
|
6236
|
unless ($empty_skip) { |
179
|
3514
|
|
|
|
|
5687
|
$self->push_stack($value); |
180
|
3514
|
|
|
|
|
6024
|
$self->consume_array(); |
181
|
|
|
|
|
|
|
} |
182
|
3565
|
100
|
|
|
|
5577
|
if ($self->finished_array()) { |
183
|
|
|
|
|
|
|
# just finished array |
184
|
309
|
|
|
|
|
326
|
my $array = shift @{$self->{array}}; # shift it |
|
309
|
|
|
|
|
505
|
|
185
|
309
|
|
|
|
|
749
|
my @values = $self->extract_stack($array->[0] * 2); |
186
|
309
|
|
|
|
|
624
|
my $class = $array->[2]; |
187
|
309
|
100
|
|
|
|
476
|
if (defined $class) { |
188
|
|
|
|
|
|
|
# object |
189
|
65
|
|
|
|
|
686
|
my $real_value = bless { |
190
|
|
|
|
|
|
|
_class => $class, |
191
|
|
|
|
|
|
|
@values, |
192
|
|
|
|
|
|
|
}, 'PHP::Session::Object'; |
193
|
65
|
|
|
|
|
159
|
$self->process_value($real_value); |
194
|
|
|
|
|
|
|
} else { |
195
|
|
|
|
|
|
|
# array is hash |
196
|
244
|
|
|
|
|
1492
|
$self->process_value({ @values }); |
197
|
|
|
|
|
|
|
} |
198
|
309
|
|
|
|
|
637
|
$self->change_state('ArrayEnd'); |
199
|
309
|
|
|
|
|
719
|
$self->{state}->parse($self); |
200
|
|
|
|
|
|
|
} else { |
201
|
|
|
|
|
|
|
# not yet finished |
202
|
3256
|
|
|
|
|
5815
|
$self->change_state('VarType'); |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
else { |
206
|
|
|
|
|
|
|
# not in array |
207
|
63
|
|
|
|
|
199
|
my $varname = $self->pop_stack; |
208
|
63
|
|
|
|
|
160
|
$self->set($varname => $value); |
209
|
63
|
|
|
|
|
486
|
$self->change_state('VarName'); |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
sub weird { |
214
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
215
|
0
|
|
|
|
|
0
|
_croak("weird data: $self->{buffer}"); |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
package PHP::Session::Serializer::PHP::State::VarName; |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
sub parse { |
221
|
73
|
|
|
73
|
|
119
|
my($self, $decoder) = @_; |
222
|
73
|
50
|
|
|
|
756
|
$decoder->{buffer} =~ s/^(!?)(.*?)\|// or $decoder->weird; |
223
|
73
|
100
|
|
|
|
234
|
if ($1) { |
224
|
10
|
|
|
|
|
74
|
$decoder->set($2 => undef); |
225
|
|
|
|
|
|
|
} else { |
226
|
63
|
|
|
|
|
152
|
$decoder->push_stack($2); |
227
|
63
|
|
|
|
|
148
|
$decoder->change_state('VarType'); |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
package PHP::Session::Serializer::PHP::State::VarType; |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
my @re = ( |
234
|
|
|
|
|
|
|
's:(\d+):', # string |
235
|
|
|
|
|
|
|
'i:(-?\d+);', # integer |
236
|
|
|
|
|
|
|
'd:(-?\d+(?:\.\d+)?);', # double |
237
|
|
|
|
|
|
|
'a:(\d+):', # array |
238
|
|
|
|
|
|
|
'O:(\d+):', # object |
239
|
|
|
|
|
|
|
'(N);', # null |
240
|
|
|
|
|
|
|
'b:([01]);', # boolean |
241
|
|
|
|
|
|
|
'[Rr]:(\d+);', # reference count? |
242
|
|
|
|
|
|
|
); |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
sub parse { |
245
|
3577
|
|
|
3577
|
|
5044
|
my($self, $decoder) = @_; |
246
|
3577
|
|
|
|
|
8292
|
my $re = join "|", @re; |
247
|
3577
|
50
|
|
|
|
31440
|
$decoder->{buffer} =~ s/^(?:$re)// or $decoder->weird; |
248
|
3577
|
100
|
|
|
|
9228
|
if (defined $1) { # string |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
249
|
2414
|
|
|
|
|
4274
|
$decoder->push_stack($1); |
250
|
2414
|
|
|
|
|
4582
|
$decoder->change_state('String'); |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
elsif (defined $2) { # integer |
253
|
730
|
|
|
|
|
1743
|
$decoder->process_value($2); |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
elsif (defined $3) { # double |
256
|
5
|
|
|
|
|
20
|
$decoder->process_value($3); |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
elsif (defined $4) { # array |
259
|
244
|
|
|
|
|
462
|
$decoder->start_array($4); |
260
|
244
|
|
|
|
|
486
|
$decoder->change_state('ArrayStart'); |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
elsif (defined $5) { # object |
263
|
65
|
|
|
|
|
132
|
$decoder->push_stack($5); |
264
|
65
|
|
|
|
|
126
|
$decoder->change_state('ClassName'); |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
elsif (defined $6) { # null |
267
|
33
|
|
|
|
|
64
|
$decoder->process_value(undef); |
268
|
|
|
|
|
|
|
} |
269
|
|
|
|
|
|
|
elsif (defined $7) { # boolean |
270
|
74
|
|
|
|
|
128
|
$decoder->process_value($7); |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
elsif (defined $8) { # reference |
273
|
12
|
|
|
|
|
23
|
$decoder->process_value($8); |
274
|
|
|
|
|
|
|
} |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
package PHP::Session::Serializer::PHP::State::String; |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
sub parse { |
280
|
2414
|
|
|
2414
|
|
3132
|
my($self, $decoder) = @_; |
281
|
2414
|
|
|
|
|
3735
|
my $length = $decoder->pop_stack(); |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
# .{$length} has a limit on length |
284
|
|
|
|
|
|
|
# $decoder->{buffer} =~ s/^"(.{$length})";//s or $decoder->weird; |
285
|
2414
|
|
|
|
|
6144
|
my $value = substr($decoder->{buffer}, 0, $length + 3, ""); |
286
|
2414
|
50
|
33
|
|
|
17578
|
$value =~ s/^"// and $value =~ s/";$// or $decoder->weird; |
287
|
2414
|
|
|
|
|
4903
|
$decoder->process_value($value); |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
package PHP::Session::Serializer::PHP::State::ArrayStart; |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
sub parse { |
293
|
309
|
|
|
309
|
|
439
|
my($self, $decoder) = @_; |
294
|
309
|
50
|
|
|
|
1692
|
$decoder->{buffer} =~ s/^{// or $decoder->weird; |
295
|
309
|
100
|
|
|
|
553
|
if ($decoder->elements_count) { |
296
|
258
|
|
|
|
|
436
|
$decoder->change_state('VarType'); |
297
|
|
|
|
|
|
|
} else { |
298
|
51
|
|
|
|
|
111
|
$decoder->process_value(undef, 1); |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
package PHP::Session::Serializer::PHP::State::ArrayEnd; |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
sub parse { |
305
|
309
|
|
|
309
|
|
424
|
my($self, $decoder) = @_; |
306
|
309
|
50
|
|
|
|
1686
|
$decoder->{buffer} =~ s/^}// or $decoder->weird; |
307
|
309
|
100
|
|
|
|
645
|
my $next_state = $decoder->in_array() ? 'VarType' : 'VarName'; |
308
|
309
|
|
|
|
|
591
|
$decoder->change_state($next_state); |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
package PHP::Session::Serializer::PHP::State::ClassName; |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
sub parse { |
314
|
65
|
|
|
65
|
|
98
|
my($self, $decoder) = @_; |
315
|
65
|
|
|
|
|
127
|
my $length = $decoder->pop_stack(); |
316
|
|
|
|
|
|
|
# $decoder->{buffer} =~ s/^"(.{$length})":(\d+):// or $decoder->weird; |
317
|
65
|
|
|
|
|
185
|
my $value = substr($decoder->{buffer}, 0, $length + 3, ""); |
318
|
65
|
50
|
33
|
|
|
523
|
$value =~ s/^"// and $value =~ s/":$// or $decoder->weird; |
319
|
65
|
50
|
|
|
|
477
|
$decoder->{buffer} =~ s/^(\d+):// or $decoder->weird; |
320
|
65
|
|
|
|
|
157
|
$decoder->start_array($1, $value); # $length, $class |
321
|
65
|
|
|
|
|
129
|
$decoder->change_state('ArrayStart'); |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
1; |
326
|
|
|
|
|
|
|
__END__ |