public function translate($string) { $exploder = new StringExploder(); $flagMap = array(); $descriptorMap = array(); foreach ($this->descriptors as $descriptor) { $openTag = $descriptor->getTag(); $closeTag = '/' . $openTag; $exploder->addDescriptor(new StringExploderDescriptor("#\\[{$openTag}(=[^\\]]+)?\\]#", $openTag)); if ($descriptor->hasCloseTag()) { $exploder->addDescriptor(new StringExploderDescriptor("#\\[{$closeTag}\\]#", $closeTag)); $flagMap[$openTag] = $closeTag; } $descriptorMap[$openTag] = $descriptor; } $array = $exploder->parse($string); while (count($array) > 1 || count($array) > 0 && is_array($array[0])) { $closeIndex = -1; foreach ($array as $index => $chunk) { if (is_array($chunk) && $chunk[1][0] === '/') { $closeIndex = $index; break; } } $openIndex = -1; foreach ($array as $index => $chunk) { if (is_array($chunk) && $chunk[1][0] !== '/') { if ($closeIndex !== -1 && $index >= $closeIndex) { break; } else { $openIndex = $index; } } } if ($openIndex === -1) { if ($closeIndex === -1) { break; // no more tag can be computed } else { $tag = $array[$closeIndex][0]; throw new Exception("Alone closing tag '{$tag}' at {$closeIndex}"); } } $openTag = $array[$openIndex][0]; $openFlag = $array[$openIndex][1]; $descriptor = $descriptorMap[$openFlag]; unset($array[$openIndex]); $extractParameter = function ($openTag) { $parts = preg_split('#=#', substr($openTag, 1, strlen($openTag) - 2), 2, PREG_SPLIT_NO_EMPTY); return count($parts) > 1 ? $parts[1] : null; }; if (!$descriptor->hasCloseTag()) { $descriptor = new BBCodeDescriptor($descriptor->getTag(), $descriptor->getOpenTagCallback()); $descriptor->setContent(null); $descriptor->setParameter($extractParameter($openTag)); $array[$openIndex] = $descriptor; } else { if ($closeIndex === -1) { throw new Exception("Alone opening tag '{$openTag}' at {$openIndex}"); } else { $closeTag = $array[$closeIndex][0]; $closeFlag = $array[$closeIndex][1]; unset($array[$closeIndex]); if ($flagMap[$openFlag] !== $closeFlag) { $extract = substr($string, $openIndex, $closeIndex - $openIndex + strlen($closeTag)); throw new Exception("Crossing tags for '{$extract}' at {$openIndex}"); } else { $content = array(); $contentIndex = $openIndex + strlen($openTag); while ($contentIndex < $closeIndex) { $row = $array[$contentIndex]; unset($array[$contentIndex]); $content[] = $row; $row = $row instanceof BBCodeDescriptor ? $row->toString() : $row; $contentIndex += strlen($row); } if (count($content) == 1) { $content = $content[0]; } else { if (count($content) == 0) { $content = null; } } $descriptor = new BBCodeDescriptor($descriptor->getTag(), $descriptor->getOpenTagCallback(), $descriptor->getCloseTagCallback(), $descriptor->getContentCallback()); $descriptor->setContent($content); $descriptor->setParameter($extractParameter($openTag)); $array[$openIndex] = $descriptor; } } } ksort($array); } $root = new BBCodeDescriptor(null, null); $root->setContent($array); $html = $root->generateHTML(); return $html; }