Your escaping the last @
symbol by using \@
so php is interpreting the @ as a literal @ rather than the delimiting character.
EDIT:
function bbCode($str)
{
$values = array(
'@\[b\](.*?)\[\/b\]@i' => '<b>$1</b>',
'@\[i\](.*?)\[\/i\]@i' => '<em>$1</em>'
);
return preg_replace(array_keys($values), array_values($values), $str);
}