1
0
mirror of https://github.com/danog/PHP-Parser.git synced 2024-11-30 04:19:30 +01:00

Cover, fix and cleanup XML unserializer

This commit is contained in:
nikic 2011-12-03 15:15:20 +01:00
parent b49c55c9e5
commit c3df371998
3 changed files with 169 additions and 142 deletions

View File

@ -1,5 +1,8 @@
<?php <?php
/**
* @codeCoverageIgnore
*/
class PHPParser_NodeVisitorAbstract implements PHPParser_NodeVisitor class PHPParser_NodeVisitorAbstract implements PHPParser_NodeVisitor
{ {
public function beforeTraverse(array $nodes) { } public function beforeTraverse(array $nodes) { }

View File

@ -16,17 +16,33 @@ class PHPParser_Unserializer_XML implements PHPParser_Unserializer
throw new DomainException('AST root element not found'); throw new DomainException('AST root element not found');
} }
return $this->read(); return $this->read($this->reader->depth);
} }
protected function read() { protected function read($depthLimit, $throw = true, &$nodeFound = null) {
$depth = $this->reader->depth; $nodeFound = true;
while ($this->reader->read() && $depth <= $this->reader->depth) { while ($this->reader->read() && $depthLimit < $this->reader->depth) {
if (XMLReader::ELEMENT !== $this->reader->nodeType) { if (XMLReader::ELEMENT !== $this->reader->nodeType) {
continue; continue;
} }
if ('node' === $this->reader->prefix) { if ('node' === $this->reader->prefix) {
return $this->readNode();
} elseif ('scalar' === $this->reader->prefix) {
return $this->readScalar();
} 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 = 'PHPParser_Node_' . $this->reader->localName; $className = 'PHPParser_Node_' . $this->reader->localName;
// create the node without calling it's constructor // create the node without calling it's constructor
@ -40,59 +56,63 @@ class PHPParser_Unserializer_XML implements PHPParser_Unserializer
$docComment = $this->reader->getAttribute('docComment'); $docComment = $this->reader->getAttribute('docComment');
$node->setDocComment($docComment); $node->setDocComment($docComment);
$depth2 = $this->reader->depth; $depthLimit = $this->reader->depth;
while ($this->reader->read() && $depth2 < $this->reader->depth) { while ($this->reader->read() && $depthLimit < $this->reader->depth) {
if (XMLReader::ELEMENT !== $this->reader->nodeType) { if (XMLReader::ELEMENT !== $this->reader->nodeType) {
continue; continue;
} }
if ('subNode' !== $this->reader->prefix) { if ('subNode' !== $this->reader->prefix) {
throw new Exception('Expected sub node'); throw new DomainException(
sprintf('Expected sub node, got node of type "%s"', $this->reader->name)
);
} }
$subNodeName = $this->reader->localName; $subNodeName = $this->reader->localName;
$subNodeContent = $this->read(); $subNodeContent = $this->read($this->reader->depth);
$node->$subNodeName = $subNodeContent; $node->$subNodeName = $subNodeContent;
} }
return $node; return $node;
} elseif ('scalar' === $this->reader->prefix) { }
if ('array' === $this->reader->localName) {
protected function readScalar() {
switch ($name = $this->reader->localName) {
case 'array':
$depth = $this->reader->depth;
$array = array(); $array = array();
while ($node = $this->read()) { while (true) {
$node = $this->read($depth, false, $nodeFound);
if (!$nodeFound) {
break;
}
$array[] = $node; $array[] = $node;
} }
return $array; return $array;
} elseif ('string' === $this->reader->localName) { case 'string':
return $this->readText(); return $this->reader->readString();
} elseif ('int' === $this->reader->localName) { case 'int':
return (int) $this->readText(); $text = $this->reader->readString();
} elseif ('float' === $this->reader->localName) { if (false === $int = filter_var($text, FILTER_VALIDATE_INT)) {
return (float) $this->readText(); throw new DomainException(sprintf('"%s" is not a valid integer', $text));
} 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 $int;
return constant($this->reader->localName); case 'float':
} else { $text = $this->reader->readString();
throw new Exception('Unexpected scalar type'); if (false === $float = filter_var($text, FILTER_VALIDATE_FLOAT)) {
throw new DomainException(sprintf('"%s" is not a valid float', $text));
} }
} else { return $float;
throw new Exception('Unexpected node type'); 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));
} }
} }
} }
protected function readText() {
if (!$this->reader->read() || XMLReader::TEXT !== $this->reader->nodeType) {
throw new Exception('Expected text node');
}
return $this->reader->value;
}
}

View File

@ -2,93 +2,54 @@
class PHPParser_Tests_Unserializer_XMLTest extends PHPUnit_Framework_TestCase class PHPParser_Tests_Unserializer_XMLTest extends PHPUnit_Framework_TestCase
{ {
/** public function testNode() {
* @covers PHPParser_Unserializer_XML<extended>
*/
public function testUnserialize() {
$xml = <<<XML $xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<AST xmlns:node="http://nikic.github.com/PHPParser/XML/node" xmlns:subNode="http://nikic.github.com/PHPParser/XML/subNode" xmlns:scalar="http://nikic.github.com/PHPParser/XML/scalar"> <AST xmlns:node="http://nikic.github.com/PHPParser/XML/node" xmlns:subNode="http://nikic.github.com/PHPParser/XML/subNode" xmlns:scalar="http://nikic.github.com/PHPParser/XML/scalar">
<scalar:array> <node:Scalar_String line="1" docComment="/** doc comment */">
<node:Stmt_Function line="3" docComment="/** doc comment */">
<subNode:byRef>
<scalar:false/>
</subNode:byRef>
<subNode:params>
<scalar:array>
<node:Param line="3">
<subNode:name>
<scalar:string>a</scalar:string>
</subNode:name>
<subNode:default>
<node:Scalar_LNumber line="3">
<subNode:value> <subNode:value>
<scalar:int>0</scalar:int> <scalar:string>Test</scalar:string>
</subNode:value>
</node:Scalar_LNumber>
</subNode:default>
<subNode:type>
<scalar:null/>
</subNode:type>
<subNode:byRef>
<scalar:true/>
</subNode:byRef>
</node:Param>
<node:Param line="3">
<subNode:name>
<scalar:string>b</scalar:string>
</subNode:name>
<subNode:default>
<node:Scalar_DNumber line="3">
<subNode:value>
<scalar:float>1</scalar:float>
</subNode:value>
</node:Scalar_DNumber>
</subNode:default>
<subNode:type>
<scalar:null/>
</subNode:type>
<subNode:byRef>
<scalar:false/>
</subNode:byRef>
</node:Param>
</scalar:array>
</subNode:params>
<subNode:stmts>
<scalar:array>
<node:Stmt_Echo line="4">
<subNode:exprs>
<scalar:array>
<node:Scalar_String line="4">
<subNode:value>
<scalar:string>Foo</scalar:string>
</subNode:value> </subNode:value>
</node:Scalar_String> </node:Scalar_String>
</scalar:array> </AST>
</subNode:exprs> XML;
</node:Stmt_Echo>
</scalar:array> $unserializer = new PHPParser_Unserializer_XML;
</subNode:stmts> $this->assertEquals(
<subNode:name> new PHPParser_Node_Scalar_String('Test', 1, '/** doc comment */'),
<scalar:string>functionName</scalar:string> $unserializer->unserialize($xml)
</subNode:name> );
</node:Stmt_Function> }
public function testScalars() {
$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<AST xmlns:scalar="http://nikic.github.com/PHPParser/XML/scalar">
<scalar:array>
<scalar:array></scalar:array>
<scalar:array/>
<scalar:string>test</scalar:string>
<scalar:string></scalar:string>
<scalar:string/>
<scalar:int>1</scalar:int>
<scalar:float>1</scalar:float>
<scalar:float>1.5</scalar:float>
<scalar:true/>
<scalar:false/>
<scalar:null/>
</scalar:array> </scalar:array>
</AST> </AST>
XML; XML;
$code = <<<'CODE' $result = array(
/** doc comment */ array(), array(),
function functionName(&$a = 0, $b = 1.0) 'test', '', '',
{ 1,
echo 'Foo'; 1, 1.5,
} true, false, null
CODE; );
$unserializer = new PHPParser_Unserializer_XML; $unserializer = new PHPParser_Unserializer_XML;
$prettyPrinter = new PHPParser_PrettyPrinter_Zend; $this->assertEquals($result, $unserializer->unserialize($xml));
$stmts = $unserializer->unserialize($xml);
$this->assertEquals($code, $prettyPrinter->prettyPrint($stmts), '', 0, 10, true);
} }
/** /**
@ -104,4 +65,47 @@ XML;
$unserializer = new PHPParser_Unserializer_XML; $unserializer = new PHPParser_Unserializer_XML;
$unserializer->unserialize($xml); $unserializer->unserialize($xml);
} }
/**
* @dataProvider provideTestErrors
* @expectedException DomainException
* @expectedExceptionMessage false, true and null scalars must be empty elements
*/
public function testErrors($xml, $errorMsg) {
$this->setExpectedException('DomainException', $errorMsg);
$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<AST xmlns:scalar="http://nikic.github.com/PHPParser/XML/scalar"
xmlns:node="http://nikic.github.com/PHPParser/XML/node"
xmlns:subNode="http://nikic.github.com/PHPParser/XML/subNode"
xmlns:foo="http://nikic.github.com/PHPParser/XML/foo">
$xml
</AST>
XML;
$unserializer = new PHPParser_Unserializer_XML;
$unserializer->unserialize($xml);
}
public function provideTestErrors() {
return array(
array('<scalar:true>test</scalar:true>', '"true" scalar must be empty'),
array('<scalar:false>test</scalar:false>', '"false" scalar must be empty'),
array('<scalar:null>test</scalar:null>', '"null" scalar must be empty'),
array('<scalar:foo>bar</scalar:foo>', 'Unknown scalar type "foo"'),
array('<scalar:int>x</scalar:int>', '"x" is not a valid int'),
array('<scalar:float>x</scalar:float>', '"x" is not a valid float'),
array('', 'Expected node or scalar'),
array('<foo:bar>test</foo:bar>', 'Unexpected node of type "foo:bar"'),
array(
'<node:Scalar_String><foo:bar>test</foo:bar></node:Scalar_String>',
'Expected sub node, got node of type "foo:bar"'
),
array(
'<node:Scalar_String><subNode:value/></node:Scalar_String>',
'Expected node or scalar'
),
);
}
} }