settings = $config; } function analyzeURL($url = FALSE) { $this->url = ($url) ? $url : $_SERVER['REQUEST_URI']; $this->url = urldecode( parse_url($this->url, PHP_URL_PATH) ); $this->pageData = $this->getPage($this->url); if ($this->pageData) { http_response_code(200); $this->runCode($this->pageData['code']); } else http_response_code(404); # Обрабатываем 404 // (надо проверить еще раз, т.к. он мог установиться // в результате исполнения кода, назначенного найденной странице) if (http_response_code() == 404 AND $this->pageData = $this->getPage('404') ) $this->runCode($this->pageData['code']); // у 404 может быть свой код - выполняем } function getPage($url) { # Ищем адрес в таблице: сначала среди страниц со статическими, # потом - среди страниц с динамическими адресами $table = $this->settings['table']; $page = mysql_getrow(" SELECT * FROM $table WHERE url = " . mysql_escape($url) . " "); if (!$page) { # Признак страницы с динамическим адресом - # наличие ( или + в поле url // С версии 1.1.2 для url можно указывать // целочисленный приоритет в виде '-10 (выражение)' и т.д. $sql = " SELECT url FROM $table WHERE url REGEXP '[+(]' ORDER BY 1 * url DESC -- преобразование в число "; foreach (mysql_getcolumn($sql) as $u) { // В url точно будут слэши, поэтому разделителем назначаем // двойную кавычку. Для порядка её нужно экранировать, // чтоб не сработала как закрывающий ограничитель // (хотя вряд ли она встретится в адресах страниц). $pattern = '"^' . str_replace( '"', '\\"', // Удаляем число-приоритет (на стороне MySQL trim(preg_replace('/^-?\d+/', '', $u)) // не получается) ) . '$"i' ; if (preg_match($pattern, $url, $this->matches)) { // Получаем полные данные записи из pages $page = mysql_getrow(" SELECT * FROM $table WHERE url = " . mysql_escape($u) . " "); break; } } } if ($page) { # Если страницу нашли - проводим дополнительную обработку # Устанавливаем основной шаблон $this->template = (trim($page['template'])) ? trim($page['template']) : $this->settings['template']; # Превращаем строку headers в массив [css,js] $tmp = array(); foreach (explode("\n", trim($page['headers'])) as $line) { $line = trim($line); if (preg_match('/^(\w+)\s+(.+)$/', $line, $matches)) { // явное указание расширения файла вида // js http://api-maps.yandex.ru/... $ext = $matches[1]; $address = $matches[2]; } else { // обычный случай - расширение получаем из пути $ext = pathinfo($line, PATHINFO_EXTENSION); $address = $line; } $tmp[$ext][] = $address; } $page['headers'] = $tmp; unset($tmp); } return $page; } function runCode($code) { $code = trim($code); if (!$code) return FALSE; $pattern = '/ (\w+) -> (\w+) # 1 - класс, 2 - метод \( ([^)]*) \) # 3 - ключ $this->matches \s* \[(\w+)\] # 4 - ключ массива pageData (\s*\S.*)? # 5 - файл или метка /x'; if (substr($code, 0, 5) == '' ) eval( substr($code, 5, -2) ); elseif (preg_match_all($pattern, $code, $matches_all, PREG_SET_ORDER)) foreach ($matches_all as $matches) { $classname = $matches[1]; $method = $matches[2]; // Не запутаться: // $this->matches - разбор пути запроса regexp'ом (url) страницы; // $matches - разбор выражения для исполняемого кода страницы. // Аргументом передается один из элементов $this->matches - // тот, чей ключ указан в выражении для исполняемого кода; // если ключ не указан, методу передается весь массив полностью. $arg = ($matches[3] !== '') // строгое равенство, иначе вместо ? $this->matches[$matches{3}] // нулевого элемента $matches : $this->matches ; // передастся весь массив $key = $matches[4]; // имя ключа для записи в pageData $extra = (isset($matches[5])) // подключаемый файл ? trim($matches[5]) // или указатель на другую страницу : '' ; $Obj = new $classname; $data = $Obj->$method($arg); if ($data) { http_response_code(200); $this->pageData[$key] = $data; if ($extra) { if (pathinfo($extra, PATHINFO_EXTENSION) == 'php') $this->requireFile($extra); // файл else { // указатель на страницу $page = $this->getPage($extra); if ($page) { // здесь данные не заменяем полностью, а дописываем http_response_code(200); $this->pageData = $page + $this->pageData; $this->runCode($this->pageData['code']); } else // если страницу, на которую ссылается указатель, http_response_code(404); // не нашли - отдаем 404 } } break; // т.к. "нашли", дальше указания не разбираем } else http_response_code(404); } elseif ( strpos($code, "\n") === FALSE ) // просто файл $this->requireFile($code); else // указания не распознаны trigger_error( 'Unrecognized code instruction "' . htmlspecialchars($code) . '"', E_USER_ERROR ); } function requireFile($filename) { $filename = trim($filename); $filename = preg_replace('/^\$/', $_SERVER['DOCUMENT_ROOT'], $filename); $this->filename = $filename; unset($filename); // чтобы полностью очистить область видимости файла require_once $this->filename; // В файле будет виден объект маршрутизатора // в виде переменной $this } } ?>