Skip to content

Commit

Permalink
add a COLLAPSE_NONE option
Browse files Browse the repository at this point in the history
  • Loading branch information
ashnazg committed Feb 2, 2017
1 parent 5013719 commit d22a6c4
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 31 deletions.
98 changes: 67 additions & 31 deletions XML/Util.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@
*/
define('XML_UTIL_ENTITIES_HTML', 3);

/**
* Do not collapse any empty tags.
*/
define('XML_UTIL_COLLAPSE_NONE', 0);

/**
* Collapse all empty tags.
*/
Expand Down Expand Up @@ -454,15 +459,40 @@ public static function attributesToString(
*/
public static function collapseEmptyTags($xml, $mode = XML_UTIL_COLLAPSE_ALL)
{
if ($mode == XML_UTIL_COLLAPSE_XHTML_ONLY) {
return preg_replace(
'/<(area|base(?:font)?|br|col|frame|hr|img|input|isindex|link|meta|'
. 'param)([^>]*)><\/\\1>/s',
'<\\1\\2 />',
$xml
);
} else {
return preg_replace('/<(\w+)([^>]*)><\/\\1>/s', '<\\1\\2 />', $xml);
switch ($mode) {
case XML_UTIL_COLLAPSE_ALL:
$preg1 =
'~<' .
'(?:' .
'(https?://[^:\s]+:\w+)' . // <http://foo.com:bar ($1)
'|(\w+:\w+)' . // <foo:bar ($2)
'|(\w+)' . // <foo ($3)
')+' .
'([^>]*)' . // attributes ($4)
'>' .
'<\/(\1|\2|\3)>' .
'~s'
;
$preg2 =
'<' .
'${1}${2}${3}' . // tag
'${4}' . // attributes
' />'
;
return preg_replace($preg1, $preg2, $xml);
break;
case XML_UTIL_COLLAPSE_XHTML_ONLY:
return preg_replace(
'/<(area|base(?:font)?|br|col|frame|hr|img|input|isindex|link|meta|'
. 'param)([^>]*)><\/\\1>/s',
'<\\1\\2 />',
$xml
);
break;
case XML_UTIL_COLLAPSE_NONE:
// fall thru
default:
return $xml;
}
}

Expand Down Expand Up @@ -496,6 +526,7 @@ public static function collapseEmptyTags($xml, $mode = XML_UTIL_COLLAPSE_ALL)
* at the same column)
* @param string $linebreak string used for linebreaks
* @param bool $sortAttributes Whether to sort the attributes or not
* @param int $collapseTagMode How to handle a content-less, and thus collapseable, tag
*
* @return string XML tag
* @see createTagFromArray()
Expand All @@ -505,7 +536,7 @@ public static function createTag(
$qname, $attributes = array(), $content = null,
$namespaceUri = null, $replaceEntities = XML_UTIL_REPLACE_ENTITIES,
$multiline = false, $indent = '_auto', $linebreak = "\n",
$sortAttributes = true
$sortAttributes = true, $collapseTagMode = XML_UTIL_COLLAPSE_ALL
) {
$tag = array(
'qname' => $qname,
Expand All @@ -524,7 +555,8 @@ public static function createTag(

return XML_Util::createTagFromArray(
$tag, $replaceEntities, $multiline,
$indent, $linebreak, $sortAttributes
$indent, $linebreak, $sortAttributes,
$collapseTagMode
);
}

Expand Down Expand Up @@ -577,19 +609,21 @@ public static function createTag(
* at the same column)
* @param string $linebreak string used for linebreaks
* @param bool $sortAttributes Whether to sort the attributes or not
* @param int $collapseTagMode How to handle a content-less, and thus collapseable, tag
*
* @return string XML tag
*
* @see createTag()
* @uses attributesToString() to serialize the attributes of the tag
* @uses splitQualifiedName() to get local part and namespace of a qualified name
* @uses createCDataSection()
* @uses collapseEmptyTags()
* @uses raiseError()
*/
public static function createTagFromArray(
$tag, $replaceEntities = XML_UTIL_REPLACE_ENTITIES,
$multiline = false, $indent = '_auto', $linebreak = "\n",
$sortAttributes = true
$sortAttributes = true, $collapseTagMode = XML_UTIL_COLLAPSE_ALL
) {
if (isset($tag['content']) && !is_scalar($tag['content'])) {
return XML_Util::raiseError(
Expand Down Expand Up @@ -648,6 +682,10 @@ public static function createTagFromArray(
}
}

if (!array_key_exists('content', $tag)) {
$tag['content'] = '';
}

// check for multiline attributes
if ($multiline === true) {
if ($indent === '_auto') {
Expand All @@ -660,27 +698,25 @@ public static function createTagFromArray(
$tag['attributes'],
$sortAttributes, $multiline, $indent, $linebreak
);
if (!isset($tag['content']) || (string)$tag['content'] == '') {
$tag = sprintf('<%s%s />', $tag['qname'], $attList);
} else {
switch ($replaceEntities) {
case XML_UTIL_ENTITIES_NONE:
break;
case XML_UTIL_CDATA_SECTION:
$tag['content'] = XML_Util::createCDataSection($tag['content']);
break;
default:
$tag['content'] = XML_Util::replaceEntities(
$tag['content'], $replaceEntities
);
break;
}
$tag = sprintf(
'<%s%s>%s</%s>', $tag['qname'], $attList, $tag['content'],
$tag['qname']

switch ($replaceEntities) {
case XML_UTIL_ENTITIES_NONE:
break;
case XML_UTIL_CDATA_SECTION:
$tag['content'] = XML_Util::createCDataSection($tag['content']);
break;
default:
$tag['content'] = XML_Util::replaceEntities(
$tag['content'], $replaceEntities
);
break;
}
return $tag;
$tag = sprintf(
'<%s%s>%s</%s>', $tag['qname'], $attList, $tag['content'],
$tag['qname']
);

return self::collapseEmptyTags($tag, $collapseTagMode);
}

/**
Expand Down
21 changes: 21 additions & 0 deletions tests/CollapseEmptyTagsTests.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,25 @@ public function testCollapseEmptyTagsOnOneEmptyTagAlongsideNonemptyTagWithCollap
$expected = "<foo></foo><br /><bar>baz</bar>";
$this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag . $xhtmlTag . $otherTag, XML_UTIL_COLLAPSE_XHTML_ONLY));
}

/**
* @covers XML_Util::collapseEmptyTags()
*/
public function testCollapseEmptyTagsOnOneEmptyTagWithCollapseNone()
{
$emptyTag = "<foo></foo>";
$expected = "<foo></foo>";
$this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag, XML_UTIL_COLLAPSE_NONE));
}

/**
* @covers XML_Util::collapseEmptyTags()
*/
public function testCollapseEmptyTagsOnOneEmptyTagAlongsideNonemptyTagWithCollapseNone()
{
$emptyTag = "<foo></foo>";
$otherTag = "<bar>baz</bar>";
$expected = "<foo></foo><bar>baz</bar>";
$this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag . $otherTag, XML_UTIL_COLLAPSE_NONE));
}
}
19 changes: 19 additions & 0 deletions tests/CreateTagFromArrayTests.php
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,25 @@ public function testCreateTagFromArrayWithQnameDerivedFromLocalPart()
$this->assertEquals($expected, XML_Util::createTagFromArray($original));
}

/**
* @covers XML_Util::createTagFromArray()
*/
public function testCreateTagFromArrayWithImplicitlyEmptyContentAndCollapseNoneDoesNotCollapseTag()
{
$original = array('qname' => 'tag1');
$expected = "<tag1></tag1>";
$actual = XML_Util::createTagFromArray(
$original,
XML_UTIL_REPLACE_ENTITIES, // default $replaceEntities
false, // default $multiline
'_auto', // default $indent
"\n", // default $linebreak
true, // default $sortAttributes
XML_UTIL_COLLAPSE_NONE
);
$this->assertEquals($expected, $actual);
}

/**
* @covers XML_Util::createTagFromArray()
*/
Expand Down

0 comments on commit d22a6c4

Please sign in to comment.