Я тоже замечал их объявления раньше. Обратился - не подошел. Вот недавно опять увидел объявление, опять не взяли.)) Написано, что берут по результатам пройденного тестового задания. У меня просят портфилио, а у меня его практически нету). И за отсутствие опыта работы в в других местах не взяли). Короче, надо расти! Но тестовое задание решил допилить.
По теме: если задачу решать сверху-вниз, поcтепенно конкретизируя, то маршрутизатор + ошибка 404 могут выглядеть как-то так: Код (PHP): $action = findRoute($request, $matches); // return parameters into $matches by reference if ($action) { call_user_func($action, $matches); } else { error404(); } Это в первом приближении. А дальше начинается конкретика реального приложения. Например может присоседиться проверка на реферальный код, разворачивание сокращенного URL, правила доступа и т.д.
Ну в принципе я доделал класс только есть одно но я не очень плохо разбираюсь в регулярных выражениях и хотелось бы услышать критики, особенно по методу SearchVariables(). Код (PHP): <?php /** * Class Router Класс маршрутизатора * Пример работы: * // Где то мы уже создали объект регистра $registry * $router = new Router($registry); * $router->SetPath(Путь к папке с контролерами); * // Устанавливаем правила маршрутизации * $router->Set(null, 'index'); * $router->Set('404', '404'); * $router->Set('{name}/{id:([0-9]+)}', function ($name = 'Lexa', $id = '5') { * echo '<b>Name</b>: ' . $name . '<br>'; * echo '<b>Id</b>: ' . $id . '<br>'; * }); * // Старт * $router->Start(); * * @version 1.0 (29.12.2015) * @license http://opensource.org/licenses/gpl-license.php GNU Public License */ Class Router { /** * Класс хранилища * @var object * @access private */ private $registry; /** * Путь к папке с контроллерами * @var string * @access private */ private $path; /** * Правила для маршрутизации * @var array * @access private */ private $rules; /** * Инициализация * @access public * @param object $registry Объект хранилища */ function __construct($registry) { $this->registry = $registry; } /** * @method SetPath(string $path) Установка директории с контролерами * @access public * @param string $path Путь к директории с контроллерами * @return true|false В зависимости от существования папки */ public function SetPath($path) { $path = trim($path, '/\\'); if (!is_dir($path)) return false; $this->path = $path . DIRECTORY_SEPARATOR; return true; } /** * @method Start() Запуск маршрутизатора * @access public */ public function Start() { $this->ParseURL($controller, $action, $variables); // Вывод контролера if (is_string($controller)) if($this->CallController($controller, $action, $variables)) echo $controller; else if (is_callable($controller)) $this->CallFunction($controller, $variables); else $this->CallController('404'); } /** * @method ParseURL(mixed &$controller = null, string &$action = 'index', array &$variables = array()) Парсит URL * @access private * @param mixed &$controller Имя контроллера или функция * @param string &$action Имя метода * @param array &$variables Переменные метода */ private function ParseURL(&$controller, &$action, &$variables) { $route = isset($_GET['route']) ? rtrim($_GET['route'], '/') : 'index'; // Проверяем все ЧПУ правила на совпадения foreach ($this->rules as $rules => $array) { if ($rules == '/' or $rules == '/index' or $rules == 'index' or !$rules) $rules = 'index'; // Ищем переменные $this->SearchVariables($rules, $route, $variables, $regex); // Ищем совпадения if(preg_match($regex, $route)) { // Сохраняем контроллер и экшен $controller = $array['controller']; $action = $array['action']; } } } /** * @method SearchVariables(string &$rules, string $route, array &$variables, string &$regex) Ищет переменные * @access private * @param string &$rules Правило ЧПУ * @param string &$route URL * @param array &$variables Переменные метода * @param string &$regex Регулярное выражение для URL */ private function SearchVariables($rules, $route, &$variables, &$regex) { $regex = $rules; $variables = array(); // Формирую регулярное выражение и запоминаю имена переменных $varNames = array(); preg_match_all('/{(.+)}/U', $rules, $matches); foreach($matches[1] as $key => $value) { // Записываю имя переменной и регулярное выражение в массив $varInfo = explode(':', $value); $varInfo[1] = isset($varInfo[1]) ? $varInfo[1] : '(.+)'; // Создаю регулярное выражение $regex = preg_replace('/' . preg_quote($matches[0][$key]) . '/U', $varInfo[1], $regex); // Записываю имя переменной $varNames[] = $varInfo[0]; } // Регулярное выражение для ЧПУ $regex = '/^' . preg_replace('/\//U', '\/', $regex) . '$/i'; // Записываю переменные preg_match($regex, $route, $varValues); if(!empty($varValues)) foreach ($varNames as $key => $value) $variables[$value] = $varValues[$key + 1]; } /** * @method CallController(string $controller, string $action = 'index', array $variables = array()) Вызывает контроллер * @access public * @param string $controller Имя контроллера * @param string $action Имя метода * @param array $variables Переменные метода * @return true|false */ public function CallController($controller, $action = 'index', $variables = array()) { $controller_path = $this->path . $controller . '.php'; // Подгружаем файл контроллера if (is_file($controller_path)) require_once($controller_path); else return false; // Запоминаю класс контроллера $controllerClass = 'controller_' . $controller; // Если класс существует if (class_exists($controllerClass)) { // Обращаюсь к методу через Reflection API $reflection = new ReflectionMethod($controllerClass, $action); $reflection->invokeArgs(new $controllerClass($this->registry), $variables); return true; } return false; } /** * @method CallFunction(callable $controller, array $variables) Вызывает функцию * @access public * @param callable $function Функция * @param array $variables Переменные функции * @return true Если всё хорошо */ public function CallFunction($function, $variables) { // Обращаюсь к функции через Reflection API $reflection = new ReflectionFunction($function); $reflection->invokeArgs($variables); return true; } /** * @method Set(string $rules, mixed $controller, string $action = 'index') Добавление правил для ЧПУ * * $router->Set(Правило ЧПУ, Контролер|Функция, Экшен); * В правиле ЧПУ можно записывать будущие переменные которые * потом передадутся в функцию, * Синтаксис: * {Имя:Регулярное выражение} * Пример: * {id:([0-9]+)} * Регулярное выражение обязательно должно быть * обвёрнуто круглыми скобками. * * Пример маршрута: * Router->Set('{name}/{id:([0-9]+)}', function ($name = 'Lexa', $id = '5') { * echo '<b>Name</b>: ' . $name . '<br>'; * echo '<b>Id</b>: ' . $id . '<br>'; * }); * * Для маршрута главной страницы в правиле ЧПУ можно * писать '/', '/index', 'index', '', null и т.д. * * @access public * @param string $rules Правило ЧПУ * @param mixed $controller Имя контроллера или функция * @param string $action Метод контроллера * @return true Если всё хорошо */ public function Set($rules, $controller, $action = 'index') { // Удаляем лишние слэши $rules = trim($rules, '/'); // Запоминаем $this->rules[$rules]['controller'] = $controller; $this->rules[$rules]['action'] = $action; return true; } /** * @method Get(string $rules) Получение контролера для правила ЧПУ * @access public * @param string $rules Правило ЧПУ * @return string|null */ public function Get($rules) { return $this->rules[$rules]; } /** * @method Remove(string $rules) Удаление правил для ЧПУ * @access public * @param string $rules Правило ЧПУ * @return true Если всё хорошо */ public function Remove($rules) { unset($this->rules[$rules]); return true; } }