瀏覽代碼

Retrieve subset anyway

Yury Kozyrev 10 年之前
父節點
當前提交
910a0b17b6

+ 40 - 1
src/JMS/Serializer/XmlDeserializationVisitor.php

@@ -63,7 +63,7 @@ class XmlDeserializationVisitor extends AbstractVisitor
         $dom->loadXML($data);
         foreach ($dom->childNodes as $child) {
             if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
-                $internalSubset = str_replace(array("\n", "\r"), '', $child->internalSubset);
+                $internalSubset = $this->getDomDocumentTypeEntitySubset($child, $data);
                 if (!in_array($internalSubset, $this->doctypeWhitelist, true)) {
                     throw new InvalidArgumentException(sprintf(
                         'The document type "%s" is not allowed. If it is safe, you may add it to the whitelist configuration.',
@@ -346,4 +346,43 @@ class XmlDeserializationVisitor extends AbstractVisitor
     {
         return $this->doctypeWhitelist;
     }
+
+    /**
+     * Retrieves internalSubset even in bugfixed php versions
+     * @param \DOMDocumentType $child
+     * @param $data
+     * @return string
+     */
+    private function getDomDocumentTypeEntitySubset(\DOMDocumentType $child, $data)
+    {
+        if(!$this->isBugFixedPhpVersion()){
+            return str_replace(array("\n", "\r"), '', $child->internalSubset);
+        }
+        $startPos = $endPos = stripos($data, '<!doctype');
+        $braces = 0;
+        do {
+            $char = $data[$endPos++];
+            if($char === '<'){
+                ++$braces;
+            }
+            if($char === '>'){
+                --$braces;
+            }
+        } while ($braces > 0);
+        $internalSubset = substr($data, $startPos, $endPos-$startPos);
+        $internalSubset = str_replace(array("\n", "\r"), '', $internalSubset);
+        $internalSubset = preg_replace('/\s{2,}/', ' ', $internalSubset);
+        $internalSubset = str_replace(array("[ <!", "> ]>"), array('[<!', '>]>'), $internalSubset);
+        return $internalSubset;
+    }
+
+    /**
+     * Whether or not PHP internalSubset bug is fixed in current version
+     * @return boolean
+     * @link https://bugs.php.net/bug.php?id=67081
+     **/
+    private function isBugFixedPhpVersion()
+    {
+        return (PHP_VERSION_ID >= 50513) || (PHP_VERSION_ID >= 50429 && PHP_VERSION_ID < 50500);
+    }
 }

+ 0 - 24
tests/JMS/Serializer/Tests/Serializer/XmlSerializationTest.php

@@ -93,12 +93,6 @@ class XmlSerializationTest extends BaseSerializationTest
      */
     public function testExternalEntitiesAreDisabledByDefault()
     {
-        if ($this->isBugFixedPhpVersion()){
-            $this->setExpectedException(
-              'JMS\Serializer\Exception\InvalidArgumentException',
-              'The document type "<!ENTITY foo SYSTEM "php://filter/read=convert.base64-encode/resource=XmlSerializationTest.php">" is not allowed. If it is safe, you may add it to the whitelist configuration.'
-            );
-        }
         
         $this->deserialize('<?xml version="1.0"?>
             <!DOCTYPE author [
@@ -115,20 +109,12 @@ class XmlSerializationTest extends BaseSerializationTest
      */
     public function testDocumentTypesAreNotAllowed()
     {
-        if ($this->isBugFixedPhpVersion()) {
-            $this->setExpectedException(
-              'JMS\Serializer\Exception\InvalidArgumentException',
-              'The document type "" is not allowed. If it is safe, you may add it to the whitelist configuration.');
-        }
         
         $this->deserialize('<?xml version="1.0"?><!DOCTYPE foo><foo></foo>', 'stdClass');
     }
 
     public function testWhitelistedDocumentTypesAreAllowed()
     {
-        if ($this->isBugFixedPhpVersion()) {
-            $this->markTestSkipped(sprintf('PHP version %s does not support this behavior', phpversion()));
-        }
         
         $this->deserializationVisitors->get('xml')->get()->setDoctypeWhitelist(array(
             '<!DOCTYPE authorized SYSTEM "http://authorized_url.dtd">',
@@ -273,16 +259,6 @@ class XmlSerializationTest extends BaseSerializationTest
         $nodes = $xml->xpath($xpath);
         return (string) reset($nodes);
     }
-    
-    /**
-     * Whether or not PHP internalSubset bug is fixed in current version
-     * @return boolean 
-     * @link https://bugs.php.net/bug.php?id=67081
-     **/
-    private function isBugFixedPhpVersion()
-    {
-        return (PHP_VERSION_ID >= 50513) || (PHP_VERSION_ID >= 50429 && PHP_VERSION_ID < 50500);
-    }
 
     /**
      * @param string $key