src/Decoder.php
protected function decodeValue(\XMLReader $reader, array $allowedTypes = array())
    {
        // Basic types.
        $types = array(
            // Support for the <nil> extension
            // (http://ontosys.com/xml-rpc/extensions.php)
            'nil'               => '\\fpoirotte\\XRL\\Types\\Nil',
            'i4'                => '\\fpoirotte\\XRL\\Types\\I4',
            'i8'                => '\\fpoirotte\\XRL\\Types\\I8',
            'int'               => '\\fpoirotte\\XRL\\Types\\IntType',
            'boolean'           => '\\fpoirotte\\XRL\\Types\\Boolean',
            'string'            => '\\fpoirotte\\XRL\\Types\\StringType',
            'double'            => '\\fpoirotte\\XRL\\Types\\Double',
            'dateTime.iso8601'  => '\\fpoirotte\\XRL\\Types\\DateTimeIso8601',
            'base64'            => '\\fpoirotte\\XRL\\Types\\Base64',

            // Some Apache extensions.
            // See http://ws.apache.org/xmlrpc/types.html
            '{http://ws.apache.org/xmlrpc/namespaces/extensions}nil'
                => '\\fpoirotte\\XRL\\Types\\Nil',
            '{http://ws.apache.org/xmlrpc/namespaces/extensions}i1'
                => '\\fpoirotte\\XRL\\Types\\I1',
            '{http://ws.apache.org/xmlrpc/namespaces/extensions}i8'
                => '\\fpoirotte\\XRL\\Types\\I8',
            '{http://ws.apache.org/xmlrpc/namespaces/extensions}i2'
                => '\\fpoirotte\\XRL\\Types\\I2',
            '{http://ws.apache.org/xmlrpc/namespaces/extensions}biginteger'
                => '\\fpoirotte\\XRL\\Types\\BigInteger',
            '{http://ws.apache.org/xmlrpc/namespaces/extensions}dateTime'
                => '\\fpoirotte\\XRL\\Types\\DateTime',
        );

        foreach ($types as $type => $cls) {
            try {
                $this->expectStartTag($reader, $type);
            } catch (\InvalidArgumentException $e) {
                continue;
            }

            try {
                $value = $this->parseText($reader);
            } catch (\InvalidArgumentException $e) {
                // Both "string" & "base64" may refer
                // to an empty string.
                if ($type !== 'string' && $type !== 'base64') {
                    throw $e;
                }
                $value = '';
            }
            $this->expectEndTag($reader, $type);
            $value = $cls::read($value, $this->timezone);
            return self::checkType($allowedTypes, $type, $value);
        }

        // Handle structures.
        $error = null;
        try {
            $this->expectStartTag($reader, 'struct');
        } catch (\InvalidArgumentException $error) {
        }

        if (!$error) {
            $value = new \fpoirotte\XRL\Types\Struct(array());
            // Read values.
            while (true) {
                $error = null;
                try {
                    $this->expectStartTag($reader, 'member');
                } catch (\InvalidArgumentException $error) {
                }

                if ($error) {
                    break;
                }

                // Read key.
                $this->expectStartTag($reader, 'name');
                $key = $this->parseText($reader);
                $this->expectEndTag($reader, 'name');

                $this->expectStartTag($reader, 'value');
                $value[$key] = $this->decodeValue($reader);
                $this->expectEndTag($reader, 'value');
                $this->expectEndTag($reader, 'member');
            }
            $this->expectEndTag($reader, 'struct');
            return self::checkType($allowedTypes, 'struct', $value);
        }

        // Handle arrays.
        $error = null;
        try {
            $this->expectStartTag($reader, 'array');
        } catch (\InvalidArgumentException $error) {
        }

        if (!$error) {
            $value = new \fpoirotte\XRL\Types\ArrayType(array());
            $this->expectStartTag($reader, 'data');
            // Read values.
            while (true) {
                $error = null;
                try {
                    $this->expectStartTag($reader, 'value');
                } catch (\InvalidArgumentException $error) {
                }

                if ($error) {
                    break;
                }

                $value[] = $this->decodeValue($reader);
                $this->expectEndTag($reader, 'value');
            }
            $this->expectEndTag($reader, 'data');
            $this->expectEndTag($reader, 'array');
            return self::checkType($allowedTypes, 'array', $value);
        }

        // Handle Apache's <dom> type.
        $error = null;
        try {
            $this->expectStartTag($reader, '{http://ws.apache.org/xmlrpc/namespaces/extensions}dom');
        } catch (\InvalidArgumentException $error) {
        }

        if (!$error) {
            $value = \fpoirotte\XRL\Types\Dom::read($reader->readInnerXML());
            // Move to next sibling, skipping subtrees, and save the result.
            $this->currentNode = new \fpoirotte\XRL\Node($reader, $this->validate, false);
            return self::checkType(
                $allowedTypes,
                '{http://ws.apache.org/xmlrpc/namespaces/extensions}dom',
                $value
            );
        }

        // Default type (string).
        try {
            $value = $this->parseText($reader);
        } catch (\InvalidArgumentException $e) {
            $value = '';
        }
        $value = new \fpoirotte\XRL\Types\StringType($value);
        return self::checkType($allowedTypes, 'string', $value);
    }
protected function decodeValue(\XMLReader $reader, array $allowedTypes = array())
    {
        // Basic types.
        $types = array(
            // Support for the <nil> extension
            // (http://ontosys.com/xml-rpc/extensions.php)
            'nil'               => '\\fpoirotte\\XRL\\Types\\Nil',
            'i4'                => '\\fpoirotte\\XRL\\Types\\I4',
            'i8'                => '\\fpoirotte\\XRL\\Types\\I8',
            'int'               => '\\fpoirotte\\XRL\\Types\\IntType',
            'boolean'           => '\\fpoirotte\\XRL\\Types\\Boolean',
            'string'            => '\\fpoirotte\\XRL\\Types\\StringType',
            'double'            => '\\fpoirotte\\XRL\\Types\\Double',
            'dateTime.iso8601'  => '\\fpoirotte\\XRL\\Types\\DateTimeIso8601',
            'base64'            => '\\fpoirotte\\XRL\\Types\\Base64',

            // Some Apache extensions.
            // See http://ws.apache.org/xmlrpc/types.html
            '{http://ws.apache.org/xmlrpc/namespaces/extensions}nil'
                => '\\fpoirotte\\XRL\\Types\\Nil',
            '{http://ws.apache.org/xmlrpc/namespaces/extensions}i1'
                => '\\fpoirotte\\XRL\\Types\\I1',
            '{http://ws.apache.org/xmlrpc/namespaces/extensions}i8'
                => '\\fpoirotte\\XRL\\Types\\I8',
            '{http://ws.apache.org/xmlrpc/namespaces/extensions}i2'
                => '\\fpoirotte\\XRL\\Types\\I2',
            '{http://ws.apache.org/xmlrpc/namespaces/extensions}biginteger'
                => '\\fpoirotte\\XRL\\Types\\BigInteger',
            '{http://ws.apache.org/xmlrpc/namespaces/extensions}dateTime'
                => '\\fpoirotte\\XRL\\Types\\DateTime',
        );

        foreach ($types as $type => $cls) {
            try {
                $this->expectStartTag($reader, $type);
            } catch (\InvalidArgumentException $e) {
                continue;
            }

            try {
                $value = $this->parseText($reader);
            } catch (\InvalidArgumentException $e) {
                // Both "string" & "base64" may refer
                // to an empty string.
                if ($type !== 'string' && $type !== 'base64') {
                    throw $e;
                }
                $value = '';
            }
            $this->expectEndTag($reader, $type);
            $value = $cls::read($value, $this->timezone);
            return self::checkType($allowedTypes, $type, $value);
        }

        // Handle structures.
        $error = null;
        try {
            $this->expectStartTag($reader, 'struct');
        } catch (\InvalidArgumentException $error) {
        }

        if (!$error) {
            $value = new \fpoirotte\XRL\Types\Struct(array());
            // Read values.
            while (true) {
                $error = null;
                try {
                    $this->expectStartTag($reader, 'member');
                } catch (\InvalidArgumentException $error) {
                }

                if ($error) {
                    break;
                }

                // Read key.
                $this->expectStartTag($reader, 'name');
                $key = $this->parseText($reader);
                $this->expectEndTag($reader, 'name');

                $this->expectStartTag($reader, 'value');
                $value[$key] = $this->decodeValue($reader);
                $this->expectEndTag($reader, 'value');
                $this->expectEndTag($reader, 'member');
            }
            $this->expectEndTag($reader, 'struct');
            return self::checkType($allowedTypes, 'struct', $value);
        }

        // Handle arrays.
        $error = null;
        try {
            $this->expectStartTag($reader, 'array');
        } catch (\InvalidArgumentException $error) {
        }

        if (!$error) {
            $value = new \fpoirotte\XRL\Types\ArrayType(array());
            $this->expectStartTag($reader, 'data');
            // Read values.
            while (true) {
                $error = null;
                try {
                    $this->expectStartTag($reader, 'value');
                } catch (\InvalidArgumentException $error) {
                }

                if ($error) {
                    break;
                }

                $value[] = $this->decodeValue($reader);
                $this->expectEndTag($reader, 'value');
            }
            $this->expectEndTag($reader, 'data');
            $this->expectEndTag($reader, 'array');
            return self::checkType($allowedTypes, 'array', $value);
        }

        // Handle Apache's <dom> type.
        $error = null;
        try {
            $this->expectStartTag($reader, '{http://ws.apache.org/xmlrpc/namespaces/extensions}dom');
        } catch (\InvalidArgumentException $error) {
        }

        if (!$error) {
            $value = \fpoirotte\XRL\Types\Dom::read($reader->readInnerXML());
            // Move to next sibling, skipping subtrees, and save the result.
            $this->currentNode = new \fpoirotte\XRL\Node($reader, $this->validate, false);
            return self::checkType(
                $allowedTypes,
                '{http://ws.apache.org/xmlrpc/namespaces/extensions}dom',
                $value
            );
        }

        // Default type (string).
        try {
            $value = $this->parseText($reader);
        } catch (\InvalidArgumentException $e) {
            $value = '';
        }
        $value = new \fpoirotte\XRL\Types\StringType($value);
        return self::checkType($allowedTypes, 'string', $value);
    }
Decodes a value encoded using XML-RPC types.

\param XMLReader $reader
Reader the value will be read from.

\param array $allowedTypes
Whitelist will the names of the types
that are allowed in this context.

\retval mixed
The value that was decoded, if any,
with the appropriate PHP type.

\throw InvalidArgumentException
No value could be decoded (probably because
the input document was invalid) or the decoded
value was of a type that is not allowed in this
context.