|
<?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);
|
|
}
|
|
}
|