Tu primer intento es bastante bueno. La razón por la que está haciendo la transformación de ese modo es que, en las expresiones regulares, caracteres como '*' y '+' son "codiciosos" por omisión. Es decir, tratan de coincidir con tantos caracteres como les sea posible.
Como ves, en tu ejemplo la expresión regular coincide con la etiqueta de apertura [codigo], luego empieza a buscar uno o más caracteres antes de encontrarse con el cierre [/codigo]. La expresión pasa por el primer cierre, pero al ser "codicioso" el segmento (.|\s)+ continúa su trabajo en caso de que encuentre otro cierre más, y así poder coincidir con tantos caracteres como pueda. Efectivamente, encuentra la cadena '[/codigo]' otra vez, así que coincide con toda la cadena completa, y la transformación culmina del modo que observas.
Una solución es usar el caracter '?', el cual usado después de '+' o '*' le quita su naturaleza codiciosa al operador, de modo que la expresión trata de coincidir con la menor cantidad de caracteres posible.
Considera por ejemplo:
Código PHP:
function elCode ($codigo) {
$hay = preg_match_all ('/\\[(codigo)\\]((.|\s)+?)\\[\\/\\1\\]/i', $codigo, $resultado);
if (! $hay)
return $codigo;
for($i = 0; $i < count ($resultado[1]); $i++) { // por cada code
$HTML = '<div class="code">' . nl2br($resultado[2][$i]) . '</div>';
$codigo = str_replace ($resultado[0][$i], $HTML ,$codigo);
}
return $codigo;
}
Disculpa que hice algunos cambios de estilo :), en realidad el detalle importante es que la expresión (.|\s)+ tiene un signo '?' al final para que no coincida "codiciosamente".