Let's say that you remove the validation logic and just apply the preg_replace function to the string in steps.
Let's start with [b]: for [b] tags which do not have [b] or [img] tags within their enclosing [/b] block:
$bbcode=preg_replace('#\[b\]((?:(?!\[/?(?:img|b)\]|\n).)*?)\[/b\]#i','<b>$1</b>',$bbcode);
If that preg_replace function works for you as desired that should give you the framework for [i] and [img].
For [url]:
<?php
$bbcode='[url]http://test.com[/url]
[url]http://test2.com|test2[/url]';
$bbcode=preg_replace('#\[url\]((?:(?!\[(?:b|img|i)\]).)*?)\|(.*)\[/url\]#i',"<a href='$1'>$2</a>",$bbcode);
$bbcode=preg_replace('#\[url\]((?:(?!\[(?:b|img|i)\]).)*?)\[/url\]#i',"<a href='$1'>$1</a>",$bbcode);
echo '<pre>'.$bbcode;
?>