Add XML serialization and unserialization support

The unserializiation implementation currently is very hacky => needs some refactoring.
This commit is contained in:
nikic 2011-11-06 17:07:38 +01:00
parent 4e656fc58d
commit b84553e011
7 changed files with 231 additions and 1 deletions

View File

@ -8,7 +8,7 @@ class PHPParser_Autoloader
static public function register()
{
ini_set('unserialize_callback_func', 'spl_autoload_call');
spl_autoload_register(array(new self, 'autoload'));
spl_autoload_register(array(__CLASS__, 'autoload'));
}
/**

View File

@ -16,10 +16,24 @@ interface PHPParser_Node
*/
public function getLine();
/**
* Sets line the node started in.
*
* @param int $line Line
*/
public function setLine($line);
/**
* Gets the nearest doc comment.
*
* @return null|string Nearest doc comment or null
*/
public function getDocComment();
/**
* Sets the nearest doc comment.
*
* @param null|string $docComment Nearest doc comment or null
*/
public function setDocComment($docComment);
}

View File

@ -37,6 +37,15 @@ abstract class PHPParser_NodeAbstract implements PHPParser_Node, IteratorAggrega
return $this->line;
}
/**
* Sets line the node started in.
*
* @param int $line Line
*/
public function setLine($line) {
$this->line = (int) $line;
}
/**
* Gets the nearest doc comment.
*
@ -46,6 +55,15 @@ abstract class PHPParser_NodeAbstract implements PHPParser_Node, IteratorAggrega
return $this->docComment;
}
/**
* Sets the nearest doc comment.
*
* @param null|string $docComment Nearest doc comment or null
*/
public function setDocComment($docComment) {
$this->docComment = $docComment;
}
/* Magic interfaces */
public function &__get($name) {

View File

@ -0,0 +1,13 @@
<?php
interface PHPParser_Serializer
{
/**
* Serializes statements into some string format.
*
* @param array $nodes Statements
*
* @return string Serialized string
*/
public function serialize(array $nodes);
}

View File

@ -0,0 +1,74 @@
<?php
class PHPParser_Serializer_XML implements PHPParser_Serializer
{
protected $writer;
/**
* Constructs a XML serializer.
*/
public function __construct() {
$this->writer = new XMLWriter;
$this->writer->openMemory();
$this->writer->setIndent(true);
}
public function serialize(array $nodes) {
$this->writer->flush();
$this->writer->startDocument('1.0', 'UTF-8');
$this->writer->startElement('AST');
$this->writer->writeAttribute('xmlns:node', 'PHPParser/node');
$this->writer->writeAttribute('xmlns:subNode', 'PHPParser/subNode');
$this->writer->writeAttribute('xmlns:scalar', 'PHPParser/scalar');
$this->_serialize($nodes);
$this->writer->endElement();
return $this->writer->outputMemory();
}
public function _serialize($node) {
if ($node instanceof PHPParser_Node) {
$this->writer->startElement('node:' . $node->getType());
if (-1 !== $line = $node->getLine()) {
$this->writer->writeAttribute('line', $line);
}
if (null !== $docComment = $node->getDocComment()) {
$this->writer->writeAttribute('docComment', $docComment);
}
foreach ($node as $name => $subNode) {
$this->writer->startElement('subNode:' . $name);
$this->_serialize($subNode);
$this->writer->endElement();
}
$this->writer->endElement();
} elseif (is_array($node)) {
$this->writer->startElement('scalar:array');
foreach ($node as $subNode) {
$this->_serialize($subNode);
}
$this->writer->endElement();
} elseif (is_string($node)) {
$this->writer->writeElement('scalar:string', $node);
} elseif (is_int($node)) {
$this->writer->writeElement('scalar:int', $node);
} elseif (is_float($node)) {
$this->writer->writeElement('scalar:float', $node);
} elseif (true === $node) {
$this->writer->writeElement('scalar:true');
} elseif (false === $node) {
$this->writer->writeElement('scalar:false');
} elseif (null === $node) {
$this->writer->writeElement('scalar:null');
} else {
throw new Exception('Unexpected node type');
}
}
}

View File

@ -0,0 +1,13 @@
<?php
interface PHPParser_Unserializer
{
/**
* Unserializes a string in some format into a node tree.
*
* @param string $string Serialized string
*
* @return array Statements
*/
public function unserialize($string);
}

View File

@ -0,0 +1,98 @@
<?php
class PHPParser_Unserializer_XML implements PHPParser_Unserializer
{
protected $reader;
public function __construct() {
$this->reader = new XMLReader;
}
public function unserialize($string) {
$this->reader->XML($string);
$this->reader->read();
if ('AST' !== $this->reader->name) {
throw new Exception('AST root element not found');
}
return $this->read();
}
protected function read() {
$depth = $this->reader->depth;
while ($this->reader->read() && $depth <= $this->reader->depth) {
if (XMLReader::ELEMENT !== $this->reader->nodeType) {
continue;
}
if ('node' === $this->reader->prefix) {
$className = 'PHPParser_Node_' . $this->reader->localName;
// create the node without calling it's constructor
$node = unserialize(
sprintf('O:%d:"%s":0:{}', strlen($className), $className)
);
$line = $this->reader->getAttribute('line');
$node->setLine(null !== $line ? $line : -1);
$docComment = $this->reader->getAttribute('docComment');
$node->setDocComment($docComment);
$depth2 = $this->reader->depth;
while ($this->reader->read() && $depth2 < $this->reader->depth) {
if (XMLReader::ELEMENT !== $this->reader->nodeType) {
continue;
}
if ('subNode' !== $this->reader->prefix) {
throw new Exception('Expected sub node');
}
$subNodeName = $this->reader->localName;
$subNodeContent = $this->read();
$node->$subNodeName = $subNodeContent;
}
return $node;
} elseif ('scalar' === $this->reader->prefix) {
if ('array' === $this->reader->localName) {
$array = array();
while ($node = $this->read()) {
$array[] = $node;
}
return $array;
} elseif ('string' === $this->reader->localName) {
return $this->readText();
} elseif ('int' === $this->reader->localName) {
return (int) $this->readText();
} elseif ('float' === $this->reader->localName) {
return (float) $this->readText();
} elseif ('false' === $this->reader->localName
|| 'true' === $this->reader->localName
|| 'null' === $this->reader->localName
) {
if ($this->reader->hasValue) {
throw new Exception('false, true and null nodes cannot have a value');
}
return constant($this->reader->localName);
} else {
throw new Exception('Unexpected scalar type');
}
} else {
throw new Exception('Unexpected node type');
}
}
}
protected function readText() {
if (!$this->reader->read() || XMLReader::TEXT !== $this->reader->nodeType) {
throw new Exception('Expected text node');
}
return $this->reader->value;
}
}