protected function find_slice($offset, $name, array $attr)
{
// find start:
$foundStart = false;
for (; $offset < count($this->tokens); $offset++) {
// short circuit if possible
if ($this->tokens[$offset]['type'] != HTMLTokenizer::NODE_TYPE_ELEMENT_OPEN) {
continue;
}
if ($this->tokens[$offset]['name'] != $name) {
continue;
}
// check attributes
if (!count($attr)) {
$foundStart = true;
break;
// To: FOUNDSTARTBREAKPOINT
}
foreach ($attr as $compareName => $compareVal) {
if (isset($this->tokens[$offset]['attrs'][$compareName]) && stripos($this->tokens[$offset]['attrs'][$compareName], $compareVal) !== false) {
$foundStart = true;
break 2;
// To: FOUNDSTARTBREAKPOINT
}
}
}
// Fake label: FOUNDSTARTBREAKPOINT
// short circuit if possible:
if (!$foundStart) {
return false;
}
$startOffset = $offset;
// find the closing tag
// (keep a stack so we don't mistake a nested node for this closing node)
$stackDepth = 0;
$foundEnd = false;
for (; $offset < count($this->tokens); $offset++) {
switch ($this->tokens[$offset]['type']) {
case HTMLTokenizer::NODE_TYPE_ELEMENT_OPEN:
if ($this->tokens[$offset]['name'] == $name) {
++$stackDepth;
}
break;
case HTMLTokenizer::NODE_TYPE_ELEMENT_CLOSE:
if ($this->tokens[$offset]['name'] == $name) {
--$stackDepth;
}
break;
// default: skip
}
if ($stackDepth <= 0) {
$foundEnd = true;
break;
}
}
// short circuit if possible:
if (!$foundEnd) {
return false;
}
$offsetLength = $offset - $startOffset + 1;
// now, place the found set into a new HTMLTokenSet:
$slice = new HTMLTokenSet($this->escape);
$slice->sliceOffsetBegin = $startOffset;
$slice->sliceOffsetLength = $offsetLength;
$slice->tokens = array_slice($this->tokens, $slice->sliceOffsetBegin, $slice->sliceOffsetLength);
return $slice;
}