153 lines
4.8 KiB
PHP
153 lines
4.8 KiB
PHP
|
<?php
|
||
|
|
||
|
namespace PhpParser\Unserializer;
|
||
|
|
||
|
use XMLReader;
|
||
|
use DomainException;
|
||
|
use PhpParser\Unserializer;
|
||
|
|
||
|
class XML implements 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 DomainException('AST root element not found');
|
||
|
}
|
||
|
|
||
|
return $this->read($this->reader->depth);
|
||
|
}
|
||
|
|
||
|
protected function read($depthLimit, $throw = true, &$nodeFound = null) {
|
||
|
$nodeFound = true;
|
||
|
while ($this->reader->read() && $depthLimit < $this->reader->depth) {
|
||
|
if (XMLReader::ELEMENT !== $this->reader->nodeType) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ('node' === $this->reader->prefix) {
|
||
|
return $this->readNode();
|
||
|
} elseif ('scalar' === $this->reader->prefix) {
|
||
|
return $this->readScalar();
|
||
|
} elseif ('comment' === $this->reader->name) {
|
||
|
return $this->readComment();
|
||
|
} else {
|
||
|
throw new DomainException(sprintf('Unexpected node of type "%s"', $this->reader->name));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$nodeFound = false;
|
||
|
if ($throw) {
|
||
|
throw new DomainException('Expected node or scalar');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected function readNode() {
|
||
|
$className = $this->getClassNameFromType($this->reader->localName);
|
||
|
|
||
|
// create the node without calling it's constructor
|
||
|
$node = unserialize(
|
||
|
sprintf(
|
||
|
"O:%d:\"%s\":1:{s:13:\"\0*\0attributes\";a:0:{}}",
|
||
|
strlen($className), $className
|
||
|
)
|
||
|
);
|
||
|
|
||
|
$depthLimit = $this->reader->depth;
|
||
|
while ($this->reader->read() && $depthLimit < $this->reader->depth) {
|
||
|
if (XMLReader::ELEMENT !== $this->reader->nodeType) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$type = $this->reader->prefix;
|
||
|
if ('subNode' !== $type && 'attribute' !== $type) {
|
||
|
throw new DomainException(
|
||
|
sprintf('Expected sub node or attribute, got node of type "%s"', $this->reader->name)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
$name = $this->reader->localName;
|
||
|
$value = $this->read($this->reader->depth);
|
||
|
|
||
|
if ('subNode' === $type) {
|
||
|
$node->$name = $value;
|
||
|
} else {
|
||
|
$node->setAttribute($name, $value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $node;
|
||
|
}
|
||
|
|
||
|
protected function readScalar() {
|
||
|
switch ($name = $this->reader->localName) {
|
||
|
case 'array':
|
||
|
$depth = $this->reader->depth;
|
||
|
$array = array();
|
||
|
while (true) {
|
||
|
$node = $this->read($depth, false, $nodeFound);
|
||
|
if (!$nodeFound) {
|
||
|
break;
|
||
|
}
|
||
|
$array[] = $node;
|
||
|
}
|
||
|
return $array;
|
||
|
case 'string':
|
||
|
return $this->reader->readString();
|
||
|
case 'int':
|
||
|
return $this->parseInt($this->reader->readString());
|
||
|
case 'float':
|
||
|
$text = $this->reader->readString();
|
||
|
if (false === $float = filter_var($text, FILTER_VALIDATE_FLOAT)) {
|
||
|
throw new DomainException(sprintf('"%s" is not a valid float', $text));
|
||
|
}
|
||
|
return $float;
|
||
|
case 'true':
|
||
|
case 'false':
|
||
|
case 'null':
|
||
|
if (!$this->reader->isEmptyElement) {
|
||
|
throw new DomainException(sprintf('"%s" scalar must be empty', $name));
|
||
|
}
|
||
|
return constant($name);
|
||
|
default:
|
||
|
throw new DomainException(sprintf('Unknown scalar type "%s"', $name));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private function parseInt($text) {
|
||
|
if (false === $int = filter_var($text, FILTER_VALIDATE_INT)) {
|
||
|
throw new DomainException(sprintf('"%s" is not a valid integer', $text));
|
||
|
}
|
||
|
return $int;
|
||
|
}
|
||
|
|
||
|
protected function readComment() {
|
||
|
$className = $this->reader->getAttribute('isDocComment') === 'true'
|
||
|
? 'PhpParser\Comment\Doc'
|
||
|
: 'PhpParser\Comment'
|
||
|
;
|
||
|
return new $className(
|
||
|
$this->reader->readString(),
|
||
|
$this->parseInt($this->reader->getAttribute('line'))
|
||
|
);
|
||
|
}
|
||
|
|
||
|
protected function getClassNameFromType($type) {
|
||
|
$className = 'PhpParser\\Node\\' . strtr($type, '_', '\\');
|
||
|
if (!class_exists($className)) {
|
||
|
$className .= '_';
|
||
|
}
|
||
|
if (!class_exists($className)) {
|
||
|
throw new DomainException(sprintf('Unknown node type "%s"', $type));
|
||
|
}
|
||
|
return $className;
|
||
|
}
|
||
|
}
|