I spent a few minutes writing out an example parser:
<?php
$msg = "Hello [\rimg\f \aalt\z="\nHello World\n" \atitle\z=\nHi\n thisisempty\n\n\r]\qworld.png\t[/img]\t";
$nextChar = strpos($msg, "\r");
if ($nextChar === false) {
cleanString($msg);
}
$newMsg = '';
// I don't think we need a do/while, actually. A while should work.
do {
$tagEndPosition = strpos($msg, "\f", $nextChar);
$tag = substr($msg, $nextChar, $tagEndPosition - $nextChar);
if (isset($bbc[$tag])) {
$checkCodes = $bbc[$tag];
// Find the next \r
$paramStringEndPos = strpos($msg, "\r", $tagEndPosition);
// If the next \r is not 0 or 1 difference:
// Find the next \a
$paramEndPos = $tagEndPosition;
$params = [];
while(false !== ($paramPos = strpos($msg, "\a", $paramEndPos))) {
// Find the param
$paramEndPos = strpos($msg, "\z", $paramPos);
$param = substr($msg, $paramPos, $paramEndPos - $paramPos);
// Get the value (empty values use \n\n to be easy)
$valuePos = strpos($msg, "\n", $paramEndPos);
$valueEndPos = strpos($msg, "\n", $valuePos);
$value = substr($msg, $valuePos, $valueEndPos - $valuePos);
// Comma delimited strings are a bit different, but their key would just be 1, 2, 3, etc.
$params[$param] = $value;
}
ksort($params);
$foundCode = false;
foreach ($checkCodes as $code) {
if (!checkRequiredParameters($code, $params)) {
continue;
}
$optionalParameters = array_diff_key($code->getRequiredParameters(), $params);
if (!checkOptionalParameters($code, $optionalParameters)) {
continue;
}
// Cool... now we have a tag and parameters.
// Next step is to get any content it has. You can guess how that will go.
// Finally, we parse the code.
parseCode($code, $params, $content);
}
$nextChar = $foundCode ? $closingTagClosingPos : $valueEndPos;
} else {
cleanString($msg);
}
} while ($nextChar = strpos($msg, "\r"));
function checkRequiredParameters($code, array $params)
{
foreach ($code->getRequiredParameters() as $requiredParameter) {
if (!isset($params[$requiredParameter])) {
return false;
}
}
return true;
}
function checkOptionalParameters($code, array $params)
{
return array_intersect($params, $code->getOptionalParameters()) !== array();
}
// Clean any remaining special characters
function cleanString($msg)
{
return str_replace(["\r", "\f", "\a", "\n"], '', $msg);
}