Changeset 853

Show
Ignore:
Timestamp:
02/25/03 16:43:38
Author:
miyagawa
Message:

Major rewrite of decoder

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • PHP-Session/trunk/Changes

    r852 r853  
    11Revision history for Perl extension PHP::Session 
     2 
     30.17  Tue Feb 25 16:43:05 JST 2003 
     4        * Fixed bug that fails to decode strings with "}" in it 
     5          This has made me rewrite decoder code from scratch! 
     6        * Fixed bug in object encoding 
    27 
    380.16  Mon Feb 24 20:35:20 JST 2003 
  • PHP-Session/trunk/Makefile.PL

    r548 r853  
    55    'PREREQ_PM' => { 
    66        Test::More => 0.32, 
    7         Text::Balanced => 1.89, 
    87        UNIVERSAL::require => 0.03, 
    98    }, 
  • PHP-Session/trunk/lib/PHP/Session.pm

    r852 r853  
    33use strict; 
    44use vars qw($VERSION); 
    5 $VERSION = 0.16
     5$VERSION = 0.17
    66 
    77use vars qw(%SerialImpl); 
     
    118118    my $cont = $self->_slurp_content; 
    119119    if (!$cont && !$self->{create}) { 
    120         _croak("no session file for ", $self->id); 
     120        _croak($self->_file_path, ": $!"); 
    121121    } 
    122122    $self->{_data} = $self->decode($cont); 
  • PHP-Session/trunk/lib/PHP/Session/Serializer/PHP.pm

    r852 r853  
    22 
    33use strict; 
    4 use Text::Balanced qw(extract_bracketed); 
    5  
    64use vars qw($VERSION); 
    7 $VERSION = 0.16
     5$VERSION = 0.17
    86 
    97sub _croak { require Carp; Carp::croak(@_) } 
     
    119sub new { 
    1210    my $class = shift; 
    13     bless { _data => {} }, $class; 
    14 
    15  
    16 my $var_re = '(\w+)\|'; 
    17 #my $str_re = 's:\d+:"(.*?)";'; 
    18 my $str_re = 's:(\d+):'; 
    19 my $int_re = 'i:(-?\d+);'; 
    20 my $dbl_re = 'd:(-?\d+(?:\.\d+)?);'; 
    21 my $arr_re = 'a:(\d+):'; 
    22 #my $obj_re = 'O:\d+:"(.*?)":\d+:'; 
    23 my $obj_re = 'O:(\d+):'; 
    24 my $nul_re = '(N);'; 
    25 my $bool_re = 'b:([01]);'; 
    26  
    27 use constant VARNAME   => 0; 
    28 use constant STRLEN    => 1; 
    29 use constant INTEGER   => 2; 
    30 use constant DOUBLE    => 3; 
    31 use constant ARRAY     => 4; 
    32 use constant CLASSLEN  => 5; 
    33 use constant NULL      => 6; 
    34 use constant BOOLEAN   => 7; 
    35  
    36 sub decode { 
    37     my($self, $data) = @_; 
    38     while ($data and $data =~ s/^(!?)$var_re(?:$str_re|$int_re|$dbl_re|$arr_re|$obj_re|$nul_re|$bool_re)?//s) { 
    39         my $UNDEF = $1; 
    40         my @match = ($2, $3, $4, $5, $6, $7, $8, $9); 
    41  
    42         # literal: integer, double, boolean 
    43         my @literal = grep defined, @match[INTEGER, DOUBLE, BOOLEAN]; 
    44         @literal and $self->{_data}->{$match[VARNAME]} = $literal[0], next; 
    45  
    46         # string 
    47         if (my $len = $match[STRLEN]) { 
    48             $data =~ s/^"(.{$len})";// or die "weird data: $data"; 
    49             $self->{_data}->{$match[VARNAME]} = $1; 
    50             next; 
    51         } 
    52  
    53         # undef or NULL 
    54         if ($UNDEF eq '!' or defined $match[NULL]) { 
    55             $self->{_data}->{$match[VARNAME]} = undef; 
    56             next; 
    57         } 
    58  
    59         # nested: array, object 
    60         my $class_name; 
    61         if (my $len = $match[CLASSLEN]) { 
    62             $data =~ s/^"(.{$len})":\d+:// or die "weird data: $data"; 
    63             $class_name = $1; 
    64         } 
    65  
    66         my $bracket = extract_bracketed($data, '{}'); 
    67         my %data    = $self->do_decode($bracket); 
    68         if (defined $match[ARRAY]) { 
    69             $self->{_data}->{$match[VARNAME]} = \%data; 
    70         } 
    71         elsif (defined $class_name) { 
    72             $self->{_data}->{$match[VARNAME]} = bless { 
    73                 _class => $class_name, 
    74                 %data, 
    75             }, 'PHP::Session::Object'; 
    76         } 
    77     } 
    78     return $self->{_data}; 
    79 
    80  
    81 sub do_decode { 
    82     my($self, $data) = @_; 
    83     $data =~ s/^{(.*)}$/$1/s; 
    84     my @data; 
    85     while ($data and $data =~ s/^($str_re|$int_re|$dbl_re|$arr_re|$obj_re|$nul_re|$bool_re)//) { 
    86         my @match = ($1, $2, $3, $4, $5, $6, $7, $8); 
    87  
    88         # literal: integer, double. boolean 
    89         my @literal = grep defined, @match[INTEGER, DOUBLE, BOOLEAN]; 
    90         @literal and push @data, $literal[0] and next; 
    91  
    92         # string 
    93         if (my $len = $match[STRLEN]) { 
    94             $data =~ s/^"(.{$len})";// or die "weird data: $data"; 
    95             push @data, $1; 
    96             next; 
    97         } 
    98  
    99         # NULL 
    100         if (defined $match[NULL]) { 
    101             push @data, undef; 
    102             next; 
    103         } 
    104  
    105         # nexted: array, object 
    106         my $class_name; 
    107         if (my $len = $match[CLASSLEN]) { 
    108             $data =~ s/^"(.{$len})":\d+:// or die "weird data: $data"; 
    109             $class_name = $1; 
    110         } 
    111  
    112         my $bracket = extract_bracketed($data, '{}'); 
    113         my %data    = $self->do_decode($bracket); 
    114         if (defined $match[ARRAY]) { 
    115             push @data, \%data; 
    116         } 
    117         elsif (defined $class_name) { 
    118             push @data, bless { 
    119                 _class => $class_name, 
    120                 %data, 
    121             }, 'PHP::Session::Object'; 
    122         } 
    123     } 
    124     return @data; 
    125 
     11    bless { 
     12        buffer => undef, 
     13        data   => {}, 
     14        state  => undef, 
     15        stack  => [], 
     16        array  => [],           # array-ref of array-ref 
     17    }, $class; 
     18
     19 
     20# encoder starts here 
    12621 
    12722sub encode { 
     
    14439    } 
    14540    elsif (! ref $value) { 
    146 #       if ($value =~ /^-?\d+$/) { 
    14741        if (is_int($value)) { 
    14842            return $self->encode_int($value); 
    14943        } 
    150 #       elsif ($value =~ /^-?\d+(?:\.\d+)?$/) { 
    15144        elsif (is_float($value)) { 
    15245            return $self->encode_double($value); 
     
    20093    my %impl = %$value; 
    20194    my $class = delete $impl{_class}; 
    202     return sprintf 'O:%d:"%s":%d:{%s}', length($class), $class, 2 * (keys %impl), 
     95    return sprintf 'O:%d:"%s":%d:{%s}', length($class), $class, scalar(keys %impl), 
    20396        join('', map $self->do_encode($_), %impl); 
    20497} 
     
    214107} 
    215108 
     109# decoder starts here 
     110 
     111sub decode { 
     112    my($self, $data) = @_; 
     113    $self->{buffer} = $data; 
     114    $self->change_state('VarName'); 
     115    while (defined $self->{buffer} && length $self->{buffer}) { 
     116        $self->{state}->parse($self); 
     117    } 
     118    return $self->{data}; 
     119} 
     120 
     121sub change_state { 
     122    my($self, $state) = @_; 
     123    $self->{state} = PHP::Session::Serializer::PHP::State->new($state); 
     124} 
     125 
     126sub set { 
     127    my($self, $key, $value) = @_; 
     128    $self->{data}->{$key} = $value; 
     129} 
     130 
     131sub push_stack { 
     132    my($self, $stuff) = @_; 
     133    push @{$self->{stack}}, $stuff; 
     134} 
     135 
     136sub pop_stack { 
     137    my $self = shift; 
     138    my $val = pop @{$self->{stack}}; 
     139    return $val; 
     140} 
     141 
     142sub extract_stack { 
     143    my($self, $num) = @_; 
     144    return $num ? splice(@{$self->{stack}}, -$num) : (); 
     145} 
     146 
     147# array: [ [ $length, $consuming, $class ], [ $length, $consuming, $class ]  .. ] 
     148 
     149sub start_array { 
     150    my($self, $length, $class) = @_; 
     151    unshift @{$self->{array}}, [ $length, 0, $class ]; 
     152} 
     153 
     154sub in_array { 
     155    my $self = shift; 
     156    return scalar @{$self->{array}}; 
     157} 
     158 
     159sub consume_array { 
     160    my $self = shift; 
     161    $self->{array}->[0]->[1]++; 
     162} 
     163 
     164sub finished_array { 
     165    my $self = shift; 
     166    return $self->{array}->[0]->[0] * 2 == $self->{array}->[0]->[1]; 
     167} 
     168 
     169sub elements_count { 
     170    my $self = shift; 
     171    return $self->{array}->[0]->[0]; 
     172} 
     173 
     174sub process_value { 
     175    my($self, $value, $empty_skip) = @_; 
     176    if ($self->in_array()) { 
     177        unless ($empty_skip) { 
     178            $self->push_stack($value); 
     179            $self->consume_array(); 
     180        } 
     181        if ($self->finished_array()) { 
     182            # just finished array 
     183            my $array  = shift @{$self->{array}}; # shift it 
     184            my @values = $self->extract_stack($array->[0] * 2); 
     185            my $class  = $array->[2]; 
     186            if (defined $class) { 
     187                # object 
     188                my $real_value = bless { 
     189                    _class => $class, 
     190                    @values, 
     191                }, 'PHP::Session::Object'; 
     192                $self->process_value($real_value); 
     193            } else { 
     194                # array is hash 
     195                $self->process_value({ @values }); 
     196            } 
     197            $self->change_state('ArrayEnd'); 
     198            $self->{state}->parse($self); 
     199        } else { 
     200            # not yet finished 
     201            $self->change_state('VarType'); 
     202        } 
     203    } 
     204    else { 
     205        # not in array 
     206        my $varname = $self->pop_stack; 
     207        $self->set($varname => $value); 
     208        $self->change_state('VarName'); 
     209    } 
     210} 
     211 
     212sub weird { 
     213    my $self = shift; 
     214    _croak("weird data: $self->{buffer}"); 
     215} 
     216 
     217package PHP::Session::Serializer::PHP::State; 
     218 
     219sub new { 
     220    my($class, $name) = @_; 
     221    bless {}, "$class\::$name"; 
     222} 
     223 
     224package PHP::Session::Serializer::PHP::State::VarName; 
     225use base qw(PHP::Session::Serializer::PHP::State); 
     226 
     227sub parse { 
     228    my($self, $decoder) = @_; 
     229    $decoder->{buffer} =~ s/^(!?)(.*?)\|// or $decoder->weird; 
     230    if ($1) { 
     231        $decoder->set($2 => undef); 
     232    } else { 
     233        $decoder->push_stack($2); 
     234        $decoder->change_state('VarType'); 
     235    } 
     236} 
     237 
     238package PHP::Session::Serializer::PHP::State::VarType; 
     239use base qw(PHP::Session::Serializer::PHP::State); 
     240 
     241my %re = ( 
     242    string  => 's:(\d+):', 
     243    integer => 'i:(-?\d+);', 
     244    double  => 'd:(-?\d+(?:\.\d+)?);', 
     245    array   => 'a:(\d+):', 
     246    object  => 'O:(\d+):', 
     247    null    => '(N);', 
     248    boolean => 'b:([01]);', 
     249); 
     250 
     251sub parse { 
     252    my($self, $decoder) = @_; 
     253    my $re = join "|", @re{qw(string integer double array object null boolean)}; 
     254    $decoder->{buffer} =~ s/^(?:$re)// or $decoder->weird; 
     255    if (defined $1) { 
     256        $decoder->push_stack($1); 
     257        $decoder->change_state('String'); 
     258    } 
     259    elsif (defined $2) { 
     260        $decoder->process_value($2); 
     261    } 
     262    elsif (defined $3) { 
     263        $decoder->process_value($3); 
     264    } 
     265    elsif (defined $4) { 
     266        $decoder->start_array($4); 
     267        $decoder->change_state('ArrayStart'); 
     268    } 
     269    elsif (defined $5) { 
     270        $decoder->push_stack($5); 
     271        $decoder->change_state('ClassName'); 
     272    } 
     273    elsif (defined $6) { 
     274        $decoder->process_value(undef); 
     275    } 
     276    elsif (defined $7) { 
     277        $decoder->process_value($7); 
     278    } 
     279} 
     280 
     281package PHP::Session::Serializer::PHP::State::String; 
     282use base qw(PHP::Session::Serializer::PHP::State); 
     283 
     284sub parse { 
     285    my($self, $decoder) = @_; 
     286    my $length = $decoder->pop_stack(); 
     287    $decoder->{buffer} =~ s/^"(.{$length})";// or $decoder->weird; 
     288    $decoder->process_value($1); 
     289} 
     290 
     291package PHP::Session::Serializer::PHP::State::ArrayStart; 
     292use base qw(PHP::Session::Serializer::PHP::State); 
     293 
     294sub parse { 
     295    my($self, $decoder) = @_; 
     296    $decoder->{buffer} =~ s/^{// or $decoder->weird; 
     297    if ($decoder->elements_count) { 
     298        $decoder->change_state('VarType'); 
     299    } else { 
     300        $decoder->process_value(undef, 1); 
     301    } 
     302} 
     303 
     304package PHP::Session::Serializer::PHP::State::ArrayEnd; 
     305use base qw(PHP::Session::Serializer::PHP::State); 
     306 
     307sub parse { 
     308    my($self, $decoder) = @_; 
     309    $decoder->{buffer} =~ s/^}// or $decoder->weird; 
     310    my $next_state = $decoder->in_array() ? 'VarType' : 'VarName'; 
     311    $decoder->change_state($next_state); 
     312} 
     313 
     314package PHP::Session::Serializer::PHP::State::ClassName; 
     315use base qw(PHP::Session::Serializer::PHP::State); 
     316 
     317sub parse { 
     318    my($self, $decoder) = @_; 
     319    my $length = $decoder->pop_stack(); 
     320    $decoder->{buffer} =~ s/^"(.{$length})":(\d+):// or $decoder->weird; 
     321    $decoder->start_array($2, $1); # $length, $class 
     322    $decoder->change_state('ArrayStart'); 
     323} 
     324 
     325 
    2163261; 
    217327__END__ 
     
    236346=item * 
    237347 
    238 clean up the code! 
     348Add option to restore PHP object as is. 
    239349 
    240350=item * 
    241351 
    242 Add option to restore PHP object as is. 
     352Get back PHP array as Perl array? 
    243353 
    244354=back