Вы не проверяете вставляемый роут, а что если я туда объект вместо строки влеплю? Если что-то влетело не так - бросайте исключение.
Не используйте супер глобальные переменные. В начале создайте объект
Request и уже с ним работайте.
Не ясно, зачем нужны trim-ы, urldecode и т.д. Если что-то пришло не так как надо - роут не найден, и ничего более. Это не проблема роутера, что ему могут каку вбросить.
callable - это довольно специфическая хрень. Это может быть массив из двух строк, функция, объект со строкой, просто строка. Нахрен это дерьмо. Используйте тогда уже \Closure.
Если у вас все равно регулярки всюду - имеет смысл использовать именованные последовательности:
|(?P<id>\d+)|
|(?P<name>[a-Z]+)|
Замены паттернов имеет смысл делать при вставке роута, а не на момент диспатча.
Форматирование...
Почитайте про
PSR-2
З.Ы. Когда наиграетесь - возьмите готовое и качественное в Symfony/Silex))