Serialized PHP value: Perl parsing library

A serialized PHP value, in the format used by PHP's built-in serialize and unserialize functions. This format closely mirrors PHP's data model: it supports all of PHP's scalar types (NULL, booleans, numbers, strings), associative arrays, objects, and recursive data structures using references. The only PHP values not supported by this format are resources, which usually correspond to native file or connection handles and cannot be meaningfully serialized.

There is no official documentation for this data format; this spec was created based on the PHP source code and the behavior of serialize/unserialize. PHP makes no guarantees about compatibility of serialized data between PHP versions, but in practice, the format has remained fully backwards-compatible - values serialized by an older PHP version can be unserialized on any newer PHP version. This spec supports serialized values from PHP 7.3 or any earlier version.

Application

PHP

KS implementation details

License: CC0-1.0
Minimal Kaitai Struct required: 0.9

This page hosts a formal specification of Serialized PHP value using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Perl source code to parse Serialized PHP value

PhpSerializedValue.pm

# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

use strict;
use warnings;
use IO::KaitaiStruct 0.009_000;
use Encode;

########################################################################
package PhpSerializedValue;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

our $VALUE_TYPE_CUSTOM_SERIALIZED_OBJECT = 67;
our $VALUE_TYPE_NULL = 78;
our $VALUE_TYPE_OBJECT = 79;
our $VALUE_TYPE_VARIABLE_REFERENCE = 82;
our $VALUE_TYPE_PHP_6_STRING = 83;
our $VALUE_TYPE_ARRAY = 97;
our $VALUE_TYPE_BOOL = 98;
our $VALUE_TYPE_FLOAT = 100;
our $VALUE_TYPE_INT = 105;
our $VALUE_TYPE_PHP_3_OBJECT = 111;
our $VALUE_TYPE_OBJECT_REFERENCE = 114;
our $VALUE_TYPE_STRING = 115;

our $BOOL_VALUE_FALSE = 48;
our $BOOL_VALUE_TRUE = 49;

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{type} = $self->{_io}->read_u1();
    my $_on = $self->type();
    if ($_on == $PhpSerializedValue::VALUE_TYPE_CUSTOM_SERIALIZED_OBJECT) {
        $self->{contents} = PhpSerializedValue::CustomSerializedObjectContents->new($self->{_io}, $self, $self->{_root});
    }
    elsif ($_on == $PhpSerializedValue::VALUE_TYPE_PHP_3_OBJECT) {
        $self->{contents} = PhpSerializedValue::Php3ObjectContents->new($self->{_io}, $self, $self->{_root});
    }
    elsif ($_on == $PhpSerializedValue::VALUE_TYPE_OBJECT) {
        $self->{contents} = PhpSerializedValue::ObjectContents->new($self->{_io}, $self, $self->{_root});
    }
    elsif ($_on == $PhpSerializedValue::VALUE_TYPE_VARIABLE_REFERENCE) {
        $self->{contents} = PhpSerializedValue::IntContents->new($self->{_io}, $self, $self->{_root});
    }
    elsif ($_on == $PhpSerializedValue::VALUE_TYPE_PHP_6_STRING) {
        $self->{contents} = PhpSerializedValue::StringContents->new($self->{_io}, $self, $self->{_root});
    }
    elsif ($_on == $PhpSerializedValue::VALUE_TYPE_FLOAT) {
        $self->{contents} = PhpSerializedValue::FloatContents->new($self->{_io}, $self, $self->{_root});
    }
    elsif ($_on == $PhpSerializedValue::VALUE_TYPE_OBJECT_REFERENCE) {
        $self->{contents} = PhpSerializedValue::IntContents->new($self->{_io}, $self, $self->{_root});
    }
    elsif ($_on == $PhpSerializedValue::VALUE_TYPE_NULL) {
        $self->{contents} = PhpSerializedValue::NullContents->new($self->{_io}, $self, $self->{_root});
    }
    elsif ($_on == $PhpSerializedValue::VALUE_TYPE_BOOL) {
        $self->{contents} = PhpSerializedValue::BoolContents->new($self->{_io}, $self, $self->{_root});
    }
    elsif ($_on == $PhpSerializedValue::VALUE_TYPE_INT) {
        $self->{contents} = PhpSerializedValue::IntContents->new($self->{_io}, $self, $self->{_root});
    }
    elsif ($_on == $PhpSerializedValue::VALUE_TYPE_ARRAY) {
        $self->{contents} = PhpSerializedValue::ArrayContents->new($self->{_io}, $self, $self->{_root});
    }
    elsif ($_on == $PhpSerializedValue::VALUE_TYPE_STRING) {
        $self->{contents} = PhpSerializedValue::StringContents->new($self->{_io}, $self, $self->{_root});
    }
}

sub type {
    my ($self) = @_;
    return $self->{type};
}

sub contents {
    my ($self) = @_;
    return $self->{contents};
}

########################################################################
package PhpSerializedValue::CountPrefixedMapping;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{num_entries_dec} = Encode::decode("ASCII", $self->{_io}->read_bytes_term(58, 0, 1, 1));
    $self->{opening_brace} = $self->{_io}->read_bytes(1);
    $self->{entries} = ();
    my $n_entries = $self->num_entries();
    for (my $i = 0; $i < $n_entries; $i++) {
        push @{$self->{entries}}, PhpSerializedValue::MappingEntry->new($self->{_io}, $self, $self->{_root});
    }
    $self->{closing_brace} = $self->{_io}->read_bytes(1);
}

sub num_entries {
    my ($self) = @_;
    return $self->{num_entries} if ($self->{num_entries});
    $self->{num_entries} = $self->num_entries_dec() + 0;
    return $self->{num_entries};
}

sub num_entries_dec {
    my ($self) = @_;
    return $self->{num_entries_dec};
}

sub opening_brace {
    my ($self) = @_;
    return $self->{opening_brace};
}

sub entries {
    my ($self) = @_;
    return $self->{entries};
}

sub closing_brace {
    my ($self) = @_;
    return $self->{closing_brace};
}

########################################################################
package PhpSerializedValue::FloatContents;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{colon} = $self->{_io}->read_bytes(1);
    $self->{value_dec} = Encode::decode("ASCII", $self->{_io}->read_bytes_term(59, 0, 1, 1));
}

sub colon {
    my ($self) = @_;
    return $self->{colon};
}

sub value_dec {
    my ($self) = @_;
    return $self->{value_dec};
}

########################################################################
package PhpSerializedValue::LengthPrefixedQuotedString;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{len_data_dec} = Encode::decode("ASCII", $self->{_io}->read_bytes_term(58, 0, 1, 1));
    $self->{opening_quote} = $self->{_io}->read_bytes(1);
    $self->{data} = $self->{_io}->read_bytes($self->len_data());
    $self->{closing_quote} = $self->{_io}->read_bytes(1);
}

sub len_data {
    my ($self) = @_;
    return $self->{len_data} if ($self->{len_data});
    $self->{len_data} = $self->len_data_dec() + 0;
    return $self->{len_data};
}

sub len_data_dec {
    my ($self) = @_;
    return $self->{len_data_dec};
}

sub opening_quote {
    my ($self) = @_;
    return $self->{opening_quote};
}

sub data {
    my ($self) = @_;
    return $self->{data};
}

sub closing_quote {
    my ($self) = @_;
    return $self->{closing_quote};
}

########################################################################
package PhpSerializedValue::ObjectContents;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{colon1} = $self->{_io}->read_bytes(1);
    $self->{class_name} = PhpSerializedValue::LengthPrefixedQuotedString->new($self->{_io}, $self, $self->{_root});
    $self->{colon2} = $self->{_io}->read_bytes(1);
    $self->{properties} = PhpSerializedValue::CountPrefixedMapping->new($self->{_io}, $self, $self->{_root});
}

sub colon1 {
    my ($self) = @_;
    return $self->{colon1};
}

sub class_name {
    my ($self) = @_;
    return $self->{class_name};
}

sub colon2 {
    my ($self) = @_;
    return $self->{colon2};
}

sub properties {
    my ($self) = @_;
    return $self->{properties};
}

########################################################################
package PhpSerializedValue::ArrayContents;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{colon} = $self->{_io}->read_bytes(1);
    $self->{elements} = PhpSerializedValue::CountPrefixedMapping->new($self->{_io}, $self, $self->{_root});
}

sub colon {
    my ($self) = @_;
    return $self->{colon};
}

sub elements {
    my ($self) = @_;
    return $self->{elements};
}

########################################################################
package PhpSerializedValue::CustomSerializedObjectContents;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{colon1} = $self->{_io}->read_bytes(1);
    $self->{class_name} = PhpSerializedValue::LengthPrefixedQuotedString->new($self->{_io}, $self, $self->{_root});
    $self->{colon2} = $self->{_io}->read_bytes(1);
    $self->{len_data_dec} = Encode::decode("ASCII", $self->{_io}->read_bytes_term(58, 0, 1, 1));
    $self->{opening_brace} = $self->{_io}->read_bytes(1);
    $self->{data} = $self->{_io}->read_bytes($self->len_data());
    $self->{closing_quote} = $self->{_io}->read_bytes(1);
}

sub len_data {
    my ($self) = @_;
    return $self->{len_data} if ($self->{len_data});
    $self->{len_data} = $self->len_data_dec() + 0;
    return $self->{len_data};
}

sub colon1 {
    my ($self) = @_;
    return $self->{colon1};
}

sub class_name {
    my ($self) = @_;
    return $self->{class_name};
}

sub colon2 {
    my ($self) = @_;
    return $self->{colon2};
}

sub len_data_dec {
    my ($self) = @_;
    return $self->{len_data_dec};
}

sub opening_brace {
    my ($self) = @_;
    return $self->{opening_brace};
}

sub data {
    my ($self) = @_;
    return $self->{data};
}

sub closing_quote {
    my ($self) = @_;
    return $self->{closing_quote};
}

########################################################################
package PhpSerializedValue::NullContents;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{semicolon} = $self->{_io}->read_bytes(1);
}

sub semicolon {
    my ($self) = @_;
    return $self->{semicolon};
}

########################################################################
package PhpSerializedValue::Php3ObjectContents;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{colon} = $self->{_io}->read_bytes(1);
    $self->{properties} = PhpSerializedValue::CountPrefixedMapping->new($self->{_io}, $self, $self->{_root});
}

sub colon {
    my ($self) = @_;
    return $self->{colon};
}

sub properties {
    my ($self) = @_;
    return $self->{properties};
}

########################################################################
package PhpSerializedValue::BoolContents;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{colon} = $self->{_io}->read_bytes(1);
    $self->{value_dec} = $self->{_io}->read_u1();
    $self->{semicolon} = $self->{_io}->read_bytes(1);
}

sub value {
    my ($self) = @_;
    return $self->{value} if ($self->{value});
    $self->{value} = $self->value_dec() == $PhpSerializedValue::BOOL_VALUE_TRUE;
    return $self->{value};
}

sub colon {
    my ($self) = @_;
    return $self->{colon};
}

sub value_dec {
    my ($self) = @_;
    return $self->{value_dec};
}

sub semicolon {
    my ($self) = @_;
    return $self->{semicolon};
}

########################################################################
package PhpSerializedValue::StringContents;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{colon} = $self->{_io}->read_bytes(1);
    $self->{string} = PhpSerializedValue::LengthPrefixedQuotedString->new($self->{_io}, $self, $self->{_root});
    $self->{semicolon} = $self->{_io}->read_bytes(1);
}

sub value {
    my ($self) = @_;
    return $self->{value} if ($self->{value});
    $self->{value} = $self->string()->data();
    return $self->{value};
}

sub colon {
    my ($self) = @_;
    return $self->{colon};
}

sub string {
    my ($self) = @_;
    return $self->{string};
}

sub semicolon {
    my ($self) = @_;
    return $self->{semicolon};
}

########################################################################
package PhpSerializedValue::IntContents;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{colon} = $self->{_io}->read_bytes(1);
    $self->{value_dec} = Encode::decode("ASCII", $self->{_io}->read_bytes_term(59, 0, 1, 1));
}

sub value {
    my ($self) = @_;
    return $self->{value} if ($self->{value});
    $self->{value} = $self->value_dec() + 0;
    return $self->{value};
}

sub colon {
    my ($self) = @_;
    return $self->{colon};
}

sub value_dec {
    my ($self) = @_;
    return $self->{value_dec};
}

########################################################################
package PhpSerializedValue::MappingEntry;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{key} = PhpSerializedValue->new($self->{_io});
    $self->{value} = PhpSerializedValue->new($self->{_io});
}

sub key {
    my ($self) = @_;
    return $self->{key};
}

sub value {
    my ($self) = @_;
    return $self->{value};
}

1;