Project

General

Profile

Bug #59889 ยป isotest.php

Lukas Domnick, 2014-07-10 20:39

 
<?php

//error_reporting(E_ERROR);
$content = array();
$conf = array();
$conf[] = array('id' => 'myid');
$conf[] = array('id' => 'myid', 'class' => 'myclass');
$conf[] = array('id' => 'stillfirsttag','_offset'=>1); // should still be the first
$conf[] = array('id' => 'secondtag','_offset'=>2); // should still be the first
$conf[] = array('id' => 'thirdtag','_offset'=>3); // should still be the first
$conf[] = array('id' => 'lasttag','_offset'=>-1); // should still be the first
$conf[] = array('id' => 'first-again','_offset'=>0);
$conf[] = array('id' => 'secondlast','_offset' => -2);

$content[] = '<h3>This example starts with a tag</h3><p>It also contains more tags.</p><p>And more.</p>';
$content[] = 'This example starts without a tag<p>It also contains more tags.</p><p>And more.</p>';
$content[] = '<!-- look, i am not hurt --> <h3>This example starts with a comment</h3><p>It also contains more tags.</p><p>And more.</p>';
$content[] = 'This example has no tags at all.';
$content[] = '<h3 id="oldid" style="background:blue">This example starts with a tag that already has one of the params</h3>';

function testwith($conf,$verbose) {
global $content;
foreach($content as $thiscontent) {
if( $verbose ) {
echo addParams($thiscontent,$conf);
echo "\n<!-- NEXT EXAMPLE -->\n";
} else {
addParams($thiscontent,$conf);
}
}
}

function dotest($verbose) {
global $conf, $content;
foreach($conf as $thisconf) {
if($verbose) {
echo "\n\n";
echo '<!--- STARTING WITH THE CONF: ' . "\n";
echo print_r($thisconf,1);
echo '-->';
}
testwith($thisconf,$verbose);
}
}


$start = microtime(TRUE);

$verbose = true;
$maxi = 1;
if($_GET['bench']) {
$maxi = 10000;
$verbose = false;
}
for($i=0;$i<$maxi;$i++) {
dotest($verbose);
}
$end = microtime(TRUE);
echo $end - $start;

function oldaddParams($content, $conf) {
// For XHTML compliance.
$lowerCaseAttributes = TRUE;
if (!is_array($conf)) {
return $content;
}
$key = 1;
$parts = explode('<', $content);
if ((int)$conf['_offset']) {
$key = (int)$conf['_offset'] < 0 ? count($parts) + (int)$conf['_offset'] : (int)$conf['_offset'];
}
$subparts = explode('>', $parts[$key]);
if (trim($subparts[0])) {
// Get attributes and name
$attribs = GeneralUtility::get_tag_attributes('<' . $subparts[0] . '>');
list($tagName) = explode(' ', $subparts[0], 2);
// adds/overrides attributes
foreach ($conf as $pkey => $val) {
if (substr($pkey, -1) !== '.' && $pkey[0] !== '_') {
$tmpVal = isset($conf[$pkey . '.']) ? $this->stdWrap($conf[$pkey], $conf[$pkey . '.']) : (string)$val;
if ($lowerCaseAttributes) {
$pkey = strtolower($pkey);
}
if ($tmpVal !== '') {
$attribs[$pkey] = $tmpVal;
}
}
}
// Re-assembles the tag and content
$subparts[0] = trim($tagName . ' ' . GeneralUtility::implodeAttributes($attribs));
$parts[$key] = implode('>', $subparts);
$content = implode('<', $parts);
}
return $content;
}



function addParams($content, $conf) {
// For XHTML compliance.
$lowerCaseAttributes = TRUE;
if (!is_array($conf)) {
return $content;
}

$tags = array();
// find all html candidates, and their tag name in a capture group
$tagsCount = preg_match_all('/\<([A-Za-z0-9]+)[[:space:]]?.*?\>/', $content, $tags, PREG_SET_ORDER|PREG_OFFSET_CAPTURE);
if(!$tagsCount) {
// no tags found
return $content;;
}

// Thanks to PREG_SET_ORDER, we have $tags[0] => first found tag, $tags[1] => second found tag, ...
// by default, use the first found tag. Offset = 1 means: use the FIRST tag.
$offset = 1;
$requestedOffset = null;
if(isset($conf['_offset'])) {
$requestedOffset = (int)$conf['_offset'];
}
// negative offset will count from the right. Positive offsets will count from zero
if ($requestedOffset && $requestedOffset < 0) {
$offset = $tagsCount + $requestedOffset;
// at this point, if we have 4 tags, and requested offset is -1, the offset is now 3
// this is not intended, as we actually want offset = 4. therefore:
$offset ++;
} else if ($requestedOffset && $requestedOffset > 0) {
$offset = $requestedOffset;
}
// not overriding the offset when _offset = 0 (that means: _offset=1 and _offset=0 are the same thing)


// offset=1 means the FIRST found tag, which is index 0 in our array.
$offset = $offset - 1;

// if the requested key does not exist, don't do anything.
if($offset >= $tagsCount || $offset < 0) {
return $content;
}

// we are using this tag:
$tagHtml = $tags[$offset][0][0];
// as we have PREG_OFFSET_CAPTURE enabled, we also get the start position for free, so we know where to replace later
$originalStrPos = $tags[$offset][0][1];
$tagName = $tags[$offset][1][0]; // from the capture group

// get the attributes
$attribs = GeneralUtility::get_tag_attributes($tagHtml);

// add/override attributes as needed
foreach ($conf as $pkey => $val) {
if (substr($pkey, -1) !== '.' && $pkey[0] !== '_') {
$tmpVal = isset($conf[$pkey . '.']) ? $this->stdWrap($conf[$pkey], $conf[$pkey . '.']) : (string)$val;
if ($lowerCaseAttributes) {
$pkey = strtolower($pkey);
}
if ($tmpVal !== '') {
$attribs[$pkey] = $tmpVal;
}
}
}

// re-assemble the tag content
$updatedTagContent = rtrim('<' . $tagName . ' ' . GeneralUtility::implodeAttributes($attribs)) . '>';

$content = substr_replace($content, $updatedTagContent, $originalStrPos, strlen($tagHtml));
return $content;
}

class GeneralUtility {

public static function get_tag_attributes($tag) {
$components = self::split_tag_attributes($tag);
// Attribute name is stored here
$name = '';
$valuemode = FALSE;
$attributes = array();
foreach ($components as $key => $val) {
// Only if $name is set (if there is an attribute, that waits for a value), that valuemode is enabled. This ensures that the attribute is assigned it's value
if ($val != '=') {
if ($valuemode) {
if ($name) {
$attributes[$name] = $val;
$name = '';
}
} else {
if ($key = strtolower(preg_replace('/[^[:alnum:]_\\:\\-]/', '', $val))) {
$attributes[$key] = '';
$name = $key;
}
}
$valuemode = FALSE;
} else {
$valuemode = TRUE;
}
}
return $attributes;
}

public static function split_tag_attributes($tag) {
$tag_tmp = trim(preg_replace('/^<[^[:space:]]*/', '', trim($tag)));
// Removes any > in the end of the string
$tag_tmp = trim(rtrim($tag_tmp, '>'));
$value = array();
// Compared with empty string instead , 030102
while ($tag_tmp !== '') {
$firstChar = $tag_tmp[0];
if ($firstChar === '"' || $firstChar === '\'') {
$reg = explode($firstChar, $tag_tmp, 3);
$value[] = $reg[1];
$tag_tmp = trim($reg[2]);
} elseif ($firstChar === '=') {
$value[] = '=';
// Removes = chars.
$tag_tmp = trim(substr($tag_tmp, 1));
} else {
// There are '' around the value. We look for the next ' ' or '>'
$reg = preg_split('/[[:space:]=]/', $tag_tmp, 2);
$value[] = trim($reg[0]);
$tag_tmp = trim(substr($tag_tmp, strlen($reg[0]), 1) . $reg[1]);
}
}
reset($value);
return $value;
}

public static function implodeAttributes(array $arr, $xhtmlSafe = FALSE, $dontOmitBlankAttribs = FALSE) {
if ($xhtmlSafe) {
$newArr = array();
foreach ($arr as $p => $v) {
if (!isset($newArr[strtolower($p)])) {
$newArr[strtolower($p)] = htmlspecialchars($v);
}
}
$arr = $newArr;
}
$list = array();
foreach ($arr as $p => $v) {
if ((string)$v !== '' || $dontOmitBlankAttribs) {
$list[] = $p . '="' . $v . '"';
}
}
return implode(' ', $list);
}
}
    (1-1/1)