Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

[hotfix/ZF-3257] Forward-port ZF-3257 and ZF-11385 #356

Closed
wants to merge 2 commits into from

2 participants

Adam Lundrigan Matthew Weier O'Phinney
Adam Lundrigan

Forward-port patches:
ZF-3257 - Attributes or character data lost in Zend_Json::fromXml() in specific circumstances
ZF-11385 - Zend_Json doesn't seem to count recursion correctly

adamlundrigan added some commits
Adam Lundrigan adamlundrigan Applies patch for ZF-3257 to correct issue where attributes or charac…
…ter data lost in Zend\Json\Json::fromXml() in specific circumstances
b4d79d5
Adam Lundrigan adamlundrigan Integrated unit tests from ZF-11385 which prove that recursion count …
…problem introduced (and subsequently fixed) in ZF-3257
995b043
Matthew Weier O'Phinney

Reviewed, merged, and pushed to master.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Aug 26, 2011
  1. Adam Lundrigan

    Applies patch for ZF-3257 to correct issue where attributes or charac…

    adamlundrigan authored
    …ter data lost in Zend\Json\Json::fromXml() in specific circumstances
  2. Adam Lundrigan

    Integrated unit tests from ZF-11385 which prove that recursion count …

    adamlundrigan authored
    …problem introduced (and subsequently fixed) in ZF-3257
This page is out of date. Refresh to see the latest.
Showing with 205 additions and 104 deletions.
  1. +93 −104 library/Zend/Json/Json.php
  2. +112 −0 tests/Zend/Json/JsonXmlTest.php
197 library/Zend/Json/Json.php
View
@@ -196,6 +196,99 @@ protected static function _recursiveJsonExprFinder(
}
return $value;
}
+ /**
+ * Return the value of an XML attribute text or the text between
+ * the XML tags
+ *
+ * In order to allow Zend_Json_Expr from xml, we check if the node
+ * matchs the pattern that try to detect if it is a new Zend_Json_Expr
+ * if it matches, we return a new Zend_Json_Expr instead of a text node
+ *
+ * @param SimpleXMLElement $simpleXmlElementObject
+ * @return Zend_Json_Expr|string
+ */
+ protected static function _getXmlValue($simpleXmlElementObject) {
+ $pattern = '/^[\s]*new Zend_Json_Expr[\s]*\([\s]*[\"\']{1}(.*)[\"\']{1}[\s]*\)[\s]*$/';
+ $matchings = array();
+ $match = preg_match ($pattern, $simpleXmlElementObject, $matchings);
+ if ($match) {
+ return new Expr($matchings[1]);
+ } else {
+ return (trim(strval($simpleXmlElementObject)));
+ }
+ }
+ /**
+ * _processXml - Contains the logic for xml2json
+ *
+ * The logic in this function is a recursive one.
+ *
+ * The main caller of this function (i.e. fromXml) needs to provide
+ * only the first two parameters i.e. the SimpleXMLElement object and
+ * the flag for ignoring or not ignoring XML attributes. The third parameter
+ * will be used internally within this function during the recursive calls.
+ *
+ * This function converts the SimpleXMLElement object into a PHP array by
+ * calling a recursive (protected static) function in this class. Once all
+ * the XML elements are stored in the PHP array, it is returned to the caller.
+ *
+ * Throws a Zend\Json\RecursionException if the XML tree is deeper than the allowed limit.
+ *
+ * @param SimpleXMLElement $simpleXmlElementObject
+ * @param boolean $ignoreXmlAttributes
+ * @param integer $recursionDepth
+ * @return array
+ */
+ protected static function _processXml ($simpleXmlElementObject, $ignoreXmlAttributes, $recursionDepth=0) {
+ // Keep an eye on how deeply we are involved in recursion.
+ if ($recursionDepth > self::$maxRecursionDepthAllowed) {
+ // XML tree is too deep. Exit now by throwing an exception.
+ throw new RecursionException(
+ "Function _processXml exceeded the allowed recursion depth of " .
+ self::$maxRecursionDepthAllowed);
+ } // End of if ($recursionDepth > self::$maxRecursionDepthAllowed)
+ $childrens= $simpleXmlElementObject->children();
+ $name= $simpleXmlElementObject->getName();
+ $value= self::_getXmlValue($simpleXmlElementObject);
+ $attributes= (array) $simpleXmlElementObject->attributes();
+ if (count($childrens)==0) {
+ if (!empty($attributes) && !$ignoreXmlAttributes) {
+ foreach ($attributes['@attributes'] as $k => $v) {
+ $attributes['@attributes'][$k]= self::_getXmlValue($v);
+ }
+ if (!empty($value)) {
+ $attributes['@text']= $value;
+ }
+ return array($name => $attributes);
+ } else {
+ return array($name => $value);
+ }
+ } else {
+ $childArray= array();
+ foreach ($childrens as $child) {
+ $childname= $child->getName();
+ $element= self::_processXml($child,$ignoreXmlAttributes,$recursionDepth+1);
+ if (array_key_exists($childname, $childArray)) {
+ if (empty($subChild[$childname])) {
+ $childArray[$childname]=array($childArray[$childname]);
+ $subChild[$childname]=true;
+ }
+ $childArray[$childname][]= $element[$childname];
+ } else {
+ $childArray[$childname]= $element[$childname];
+ }
+ }
+ if (!empty($attributes) && !$ignoreXmlAttributes) {
+ foreach ($attributes['@attributes'] as $k => $v) {
+ $attributes['@attributes'][$k]= self::_getXmlValue($v);
+ }
+ $childArray['@attributes']= $attributes['@attributes'];
+ }
+ if (!empty($value)) {
+ $childArray['@text']= $value;
+ }
+ return array($name => $childArray);
+ }
+ }
/**
* fromXml - Converts XML to JSON
@@ -243,110 +336,6 @@ public static function fromXml ($xmlStringContents, $ignoreXmlAttributes=true) {
}
/**
- * _processXml - Contains the logic for xml2json
- *
- * The logic in this function is a recursive one.
- *
- * The main caller of this function (i.e. fromXml) needs to provide
- * only the first two parameters i.e. the SimpleXMLElement object and
- * the flag for ignoring or not ignoring XML attributes. The third parameter
- * will be used internally within this function during the recursive calls.
- *
- * This function converts the SimpleXMLElement object into a PHP array by
- * calling a recursive (protected static) function in this class. Once all
- * the XML elements are stored in the PHP array, it is returned to the caller.
- *
- * @static
- * @access protected
- * @param SimpleXMLElement $simpleXmlElementObject XML element to be converted
- * @param boolean $ignoreXmlAttributes Include or exclude XML attributes in
- * the xml2json conversion process.
- * @param int $recursionDepth Current recursion depth of this function
- * @return mixed - On success, a PHP associative array of traversed XML elements
- * @throws Zend\Json\Exception\RecursionException if the XML tree is deeper than the allowed limit
- */
- protected static function _processXml ($simpleXmlElementObject, $ignoreXmlAttributes, $recursionDepth=0) {
- // Keep an eye on how deeply we are involved in recursion.
- if ($recursionDepth > self::$maxRecursionDepthAllowed) {
- // XML tree is too deep. Exit now by throwing an exception.
- throw new RecursionException(
- "Function _processXml exceeded the allowed recursion depth of " .
- self::$maxRecursionDepthAllowed);
- } // End of if ($recursionDepth > self::$maxRecursionDepthAllowed)
-
- if ($recursionDepth == 0) {
- // Store the original SimpleXmlElementObject sent by the caller.
- // We will need it at the very end when we return from here for good.
- $callerProvidedSimpleXmlElementObject = $simpleXmlElementObject;
- } // End of if ($recursionDepth == 0)
-
- if ($simpleXmlElementObject instanceof \SimpleXMLElement) {
- // Get a copy of the simpleXmlElementObject
- $copyOfSimpleXmlElementObject = $simpleXmlElementObject;
- // Get the object variables in the SimpleXmlElement object for us to iterate.
- $simpleXmlElementObject = get_object_vars($simpleXmlElementObject);
- } // End of if (get_class($simpleXmlElementObject) == "SimpleXMLElement")
-
- // It needs to be an array of object variables.
- if (is_array($simpleXmlElementObject)) {
- // Initialize a result array.
- $resultArray = array();
- // Is the input array size 0? Then, we reached the rare CDATA text if any.
- if (count($simpleXmlElementObject) <= 0) {
- // Let us return the lonely CDATA. It could even be
- // an empty element or just filled with whitespaces.
- return (trim(strval($copyOfSimpleXmlElementObject)));
- } // End of if (count($simpleXmlElementObject) <= 0)
-
- // Let us walk through the child elements now.
- foreach($simpleXmlElementObject as $key=>$value) {
- // Check if we need to ignore the XML attributes.
- // If yes, you can skip processing the XML attributes.
- // Otherwise, add the XML attributes to the result array.
- if(($ignoreXmlAttributes == true) && (is_string($key)) && ($key == "@attributes")) {
- continue;
- } // End of if(($ignoreXmlAttributes == true) && ($key == "@attributes"))
-
- // Let us recursively process the current XML element we just visited.
- // Increase the recursion depth by one.
- $recursionDepth++;
- $resultArray[$key] = self::_processXml ($value, $ignoreXmlAttributes, $recursionDepth);
-
- // Decrease the recursion depth by one.
- $recursionDepth--;
- } // End of foreach($simpleXmlElementObject as $key=>$value) {
-
- if ($recursionDepth == 0) {
- // That is it. We are heading to the exit now.
- // Set the XML root element name as the root [top-level] key of
- // the associative array that we are going to return to the original
- // caller of this recursive function.
- $tempArray = $resultArray;
- $resultArray = array();
- $resultArray[$callerProvidedSimpleXmlElementObject->getName()] = $tempArray;
- } // End of if ($recursionDepth == 0)
-
- return($resultArray);
- } else {
- // We are now looking at either the XML attribute text or
- // the text between the XML tags.
-
- // In order to allow Zend_Json_Expr from xml, we check if the node
- // matchs the pattern that try to detect if it is a new Zend_Json_Expr
- // if it matches, we return a new Zend_Json_Expr instead of a text node
- $pattern = '/^[\s]*new Zend_Json_Expr[\s]*\([\s]*[\"\']{1}(.*)[\"\']{1}[\s]*\)[\s]*$/';
- $matchings = array();
- $match = preg_match($pattern, $simpleXmlElementObject, $matchings);
- if ($match) {
- return new Expr($matchings[1]);
- } else {
- return (trim(strval($simpleXmlElementObject)));
- }
-
- } // End of if (is_array($simpleXmlElementObject))
- } // End of function _processXml.
-
- /**
* Pretty-print JSON string
*
* Use 'indent' option to select indentation string - by default it's a tab
112 tests/Zend/Json/JsonXmlTest.php
View
@@ -508,6 +508,118 @@ public function testUsingXML7()
}
*/
+ /**
+ * @group ZF-3257
+ */
+ public function testUsingXML8() {
+
+ // Set the XML contents that will be tested here.
+ $xmlStringContents = <<<EOT
+<?xml version="1.0"?>
+<a><b id="foo" />bar</a>
+
+EOT;
+
+ // There are not going to be any XML attributes in this test XML.
+ // Hence, set the flag to ignore XML attributes.
+ $ignoreXmlAttributes = false;
+ $jsonContents = "";
+ $ex = null;
+
+ // Convert XML to JSON now.
+ // fromXml function simply takes a String containing XML contents as input.
+ try {
+ $jsonContents = Json\Json::fromXml($xmlStringContents, $ignoreXmlAttributes);
+ } catch (Exception $ex) {
+ ;
+ }
+ $this->assertSame($ex, null, "Zend_JSON::fromXml returned an exception.");
+
+ // Convert the JSON string into a PHP array.
+ $phpArray = Json\Json::decode($jsonContents, Json\Json::TYPE_ARRAY);
+ // Test if it is not a NULL object.
+ $this->assertNotNull($phpArray, "JSON result for XML input 1 is NULL");
+
+ $this->assertSame("bar", $phpArray['a']['@text'], "The text element of a is not correct");
+ $this->assertSame("foo", $phpArray['a']['b']['@attributes']['id'], "The id attribute of b is not correct");
+
+ }
+
+ /**
+ * @group ZF-11385
+ * @expectedException Zend\Json\Exception\RecursionException
+ * @dataProvider providerNestingDepthIsHandledProperly
+ */
+ public function testNestingDepthIsHandledProperlyWhenNestingDepthExceedsMaximum($xmlStringContents)
+ {
+ Json\Json::$maxRecursionDepthAllowed = 1;
+ Json\Json::fromXml($xmlStringContents, true);
+ }
+
+ /**
+ * @group ZF-11385
+ * @dataProvider providerNestingDepthIsHandledProperly
+ */
+ public function testNestingDepthIsHandledProperlyWhenNestingDepthDoesNotExceedMaximum($xmlStringContents)
+ {
+ try {
+ Json\Json::$maxRecursionDepthAllowed = 25;
+ $jsonString = Json\Json::fromXml($xmlStringContents, true);
+ $jsonArray = Json\Json::decode($jsonString, Json\Json::TYPE_ARRAY);
+ $this->assertNotNull($jsonArray, "JSON decode result is NULL");
+ $this->assertSame('A', $jsonArray['response']['message_type']['defaults']['close_rules']['after_responses']);
+ } catch ( Zend\Json\Exception\RecursionException $ex ) {
+ $this->fail('Zend_Json::fromXml does not implement recursion check properly');
+ }
+ }
+
+ /**
+ * XML document provider for ZF-11385 tests
+ * @return array
+ */
+ public static function providerNestingDepthIsHandledProperly()
+ {
+ $xmlStringContents = <<<EOT
+<response>
+ <status>success</status>
+ <description>200 OK</description>
+ <message_type>
+ <system_name>A</system_name>
+ <shortname>B</shortname>
+ <long_name>C</long_name>
+ <as_verb>D</as_verb>
+ <as_noun>E</as_noun>
+ <challenge_phrase>F</challenge_phrase>
+ <recipient_details>G</recipient_details>
+ <sender_details>H</sender_details>
+ <example_text>A</example_text>
+ <short_description>B</short_description>
+ <long_description>C</long_description>
+ <version>D</version>
+ <developer>E</developer>
+ <config_instructions>A</config_instructions>
+ <config_fragment>B</config_fragment>
+ <icon_small>C</icon_small>
+ <icon_medium>D</icon_medium>
+ <icon_large>E</icon_large>
+ <defaults>
+ <close_rules>
+ <after_responses>A</after_responses>
+ </close_rules>
+ <recipient_visibility>B</recipient_visibility>
+ <recipient_invite>C</recipient_invite>
+ <results_visibility>A</results_visibility>
+ <response_visibility>B</response_visibility>
+ <recipient_resubmit>C</recipient_resubmit>
+ <feed_status>D</feed_status>
+ </defaults>
+ </message_type>
+ <execution_time>0.0790269374847</execution_time>
+</response>
+EOT;
+ return array(array($xmlStringContents));
+ }
+
}
Something went wrong with that request. Please try again.