Обработка «.»символ передается в запрос MySQL через URL - PullRequest
0 голосов
/ 28 июля 2010

Я натолкнулся на один из моих проектов.Как и многие ботаники, я решил создать свой собственный сайт с обзором видеоигр.Отзывы хранятся в базе данных, и их можно получить по названию игры с URL-адресом:

http://www.example.com/reviews/{gameName}/{optional pageOfReview}

К сожалению, при тестировании крайних случаев янаткнулся на странную ошибку - если в названии игры есть точка, я не могу ее найти.Если точка является ведущим персонажем в названии (например, .hack), я получаю экран ошибки трассировки стека Кохана, сообщающий мне, что обзор (или, точнее, игра) не существует.Если он находится в середине или в конце заголовка, я получаю одно из моих собственных сообщений об ошибках, в которых говорится, что обзор (игру) не может быть получен.Есть ли способ обойти это?Это проблема того, как MySQL анализирует точку, или что-то еще?

РЕДАКТИРОВАТЬ: Все запросы обрабатываются с помощью функциональности ORM Kohana 2 с использованием драйвера MySQLi.Сохранение отзыва (контроллер администратора):

public function saveReview()
{
    $this->checkAdmin();

    if (isset($_POST['submit'])) { $this->storeReview(); }
    else { header('Location: /admin'); }
}

private function storeReview($id = null)
{
    if (!preg_match("/^[a-zA-Z0-9\-_:!'. ]*$/", $_POST['gameTitle']) || empty($_POST['gameTitle'])) { $gameTitle = false; }
    else { $gameTitle = ucwords($this->clean($_POST['gameTitle'])); }

    if (!is_numeric($_POST['genre'])) { $genre = false; }
    else { $genre = $_POST['genre']; }

    $platformCheckArray = array_map('is_numeric', $_POST['platforms']);

    $platformCheck = true;
    foreach ($platformCheckArray as $pca)
    {
        if (!$pca)
        {
            $platformCheck = false;
            break;
        }
    }

    $proCheck = true;
    $cleanedPros = array();

    foreach ($_POST['pros'] as $pro)
    {
        if (!preg_match("/^[a-zA-Z0-9\-_:!' ]*$/", $pro))
        {
            $proCheck = false;
            break;
        }

        if (!empty($pro)) { $cleanedPros[] = $this->clean($pro); }
    }

    $conCheck = true;
    $cleanedCons = array();

    foreach ($_POST['cons'] as $con)
    {
        if (!preg_match("/^[a-zA-Z0-9\-_:!' ]*$/", $con))
        {
            $conCheck = false;
            break;
        }

        if (!empty($con)) { $cleanedCons[] = $this->clean($con); }
    }

    if (!is_numeric($_POST['score'])) { $score = false; }
    else { $score = $_POST['score']; }

    if (empty($_POST['content'])) { $content = false; }
    else { $content = true; }

    // save review if all tests pass, display error otherwise

    if ($gameTitle && $genre && $platformCheck && $proCheck && $conCheck && $score && $content)
    {
        $gameTitle = $gameTitle;
        $platforms = $_POST['platforms'];
        $reviewContent = $_POST['content'];
        $prosText = implode(', ', $cleanedPros);
        $consText = implode(', ', $cleanedCons);

        $game = ORM::factory('game');
        $game->title = $gameTitle;
        $game->genre_id = $genre;
        $game->platforms = $platforms;
        $game->save();

        $storedGenre = ORM::factory('genre')->where('id', $genre)->find();
        $storedGenre->platforms = $platforms;
        $storedGenre->save();

        $review = ORM::factory('review', $id);
        $review->content = $reviewContent;
        $review->score = $score;
        $review->game_id = $game->id;
        $review->date_added = date('Y-m-d H:i:s');
        $review->platforms = $platforms;
        $review->save();

        $pros = ORM::factory('pro');
        $pros->review_id = $review->id;
        $pros->text = $prosText;
        $pros->save();

        $cons = ORM::factory('con');
        $cons->review_id = $review->id;
        $cons->text = $consText;
        $cons->save();

        if ($game->saved && $storedGenre->saved && $review->saved && $pros->saved && $cons->saved) { $this->success('review'); }
        else { $this->showError("Something went wrong with saving the review.  Please try again."); }
    }
    else { $this->showError("All fields must contain values.  Please try again."); }
}

Получение отзыва (из контроллера отзывов):

public function show($id, $page = 1)
{
    if (is_numeric($id)) { $game = ORM::factory('game', $id); }
    else
    {
        $id = ucwords(stripslashes($id));
        $game = ORM::factory('game')->where('title', $id)->find();
    }

    if ($game->loaded) { $this->showReview($game->id, $page); }
    else { HandiError::factory('Could not retrieve the specified review.  Please check that you entered the correct value.'); }
}

private function showReview($id, $page = 1)
{
    $page = (int)$page;

    if ($page < 1) { $page = 1; }

    if ($id)
    {
        $game = ORM::factory('game', $id);
        $review = ORM::factory('review')->where('game_id', $game->id)->find();
        $genre = ORM::factory('genre')->where('id', $game->genre_id)->find();
        $revPlatforms = $this->db->query("SELECT * FROM platforms
                                      INNER JOIN platforms_reviews AS pr ON platforms.id = pr.platform_id 
                                      INNER JOIN reviews ON pr.review_id = reviews.id 
                                      WHERE reviews.id = ?", $review->id);
        $revPros = ORM::factory('pro')->where('review_id', $review->id)->find();
        $revCons = ORM::factory('con')->where('review_id', $review->id)->find();

        $platforms = array();
        foreach($revPlatforms as $rp) { $platforms[] = $rp->name; }
        $pros = explode(', ', $revPros->text);
        $cons = explode(', ', $revCons->text);

        $pages = explode('&lt;split /&gt;', $review->content);
        $count = count($pages);

        if ($page > ($count)) { $content = $pages[0]; }
        else { $content = $pages[$page - 1]; }

        $view = new View('reviews/show_review');
        $view->content = $content;
        $view->gameTitle = $game->title;
        $view->genre = $genre->name;
        $view->platforms = implode(', ', $platforms);
        $view->pros = $pros;
        $view->cons = $cons;
        $view->score = $review->score;
        $view->pages = $pages;
        $view->render(true);
    }
    else { HandiError::factory('Could not retrieve the specified review.  Please check that you entered the correct value.'); }
}

РЕДАКТИРОВАТЬ 2: Что ж, я узнал кое-что о случае с ведущим периодом:

В индексе моего контроллера у меня есть несколько запросов, которые я использую, чтобы перечислить обзоры по названию игры, платформе, жанру и т. Д. В основном это вики для бедного человека.См .:

public function index()
{
    /* show a wiki-like page with reviews listed by title, 
     * game title, genre, and platform
     */

    $numGenres = $this->db->query("SELECT COUNT(id) AS num FROM genres");
    $numPlatforms = $this->db->query("SELECT COUNT(id) AS num FROM platforms"); 

    $genreCount = $numGenres[0]->num;
    $platformCount = $numPlatforms[0]->num;
    $scoreCount = 5;

    $genreResults = array();
    $platformResults = array();
    $scoreResults = array();

    $gameResults = $this->db->query("SELECT LEFT(title, 1) AS letter, COUNT(id) AS count FROM games GROUP BY letter ORDER BY letter ASC");

    for($i = 1; $i < ($genreCount + 1); ++$i)
    {
        $genreResults[] = $this->db->query("SELECT genres.id AS id, genres.name AS name, COUNT(reviews.id) AS num FROM reviews 
                                            INNER JOIN games ON reviews.game_id = games.id 
                                            INNER JOIN genres ON games.genre_id = genres.id 
                                            WHERE genres.id = ?", $i);
    }

    for($j = 1; $j < ($platformCount + 1); ++$j)
    {
        $platformResults[] = $this->db->query("SELECT platforms.id AS id, platforms.name AS name, COUNT(reviews.id) AS num FROM reviews 
                                               INNER JOIN platforms_reviews AS pr ON reviews.id = pr.review_id 
                                               INNER JOIN platforms ON pr.platform_id = platforms.id 
                                               WHERE platforms.id = ?", $j);
    }

    for($k = 1; $k < ($scoreCount + 1); ++$k)
    {
        $scoreResults[] = $this->db->query("SELECT score, COUNT(id) AS num FROM reviews WHERE score = ?", $k);
    }

    $view = new View('reviews/index');
    $view->gamesByLetter = $gameResults;
    $view->genres = $genreResults;
    $view->platforms = $platformResults;
    $view->scores = $scoreResults;
    $view->render(true);
}

Когда я передаю результаты этих запросов представлению, я перебираю их и создаю ссылки на основе мета-категории.Таким образом, он показывает, сколько игр начинается с буквы A, B и т. Д., И нажатие на одну из этих ссылок приводит пользователя к списку ссылок, каждая с обзором (так, A-> Afterburner (среди прочих)-> обзор для Afterburner).

При наведении указателя мыши на группу с начальным периодом моя строка состояния показывает, что период отсутствует в ссылке, даже если он отображается в источнике.Так что, хотя источник показывает ссылку как site.com/reviews/game/.браузер показывает его как site.com/reviews/game/. Это заставляет меня поверить, что период даже не передается в метод, и трассировка стека, кажется, подтверждает (он утверждает, что отсутствует аргумент, который будет периодом).

РЕДАКТИРОВАТЬ 3: Хорошо, я посмотрел на свои маршруты, и не могу найти там ничего.Тем не менее, у меня есть файл .htaccess, в котором mod_rewrites маршруты выглядят привлекательно для SEO, поэтому мне интересно, может ли это быть проблемой.Я никогда не писал сам файл mod_rewrite - люди на форумах Kohana дали мне это, и это сработало, поэтому я пошел с этим.Я могу понять некоторые из задействованных регулярных выражений, но мой слабый форекс.Я считаю, что последняя строка делает «магию».

# Turn on URL rewriting
Options +FollowSymlinks
RewriteEngine On

# Put your installation directory here:
# If your URL is www.example.com/, use /
# If your URL is www.example.com/kohana/, use /kohana/
RewriteBase /

# Do not enable rewriting for files or directories that exist
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

# For reuests that are not actual files or directories,
# Rewrite to index.php/URL

# Original rule:
# RewriteRule ^(.*)$ index.php/$1 [PT,L]

# Alternative rule:
# RewriteRule .* index.php/$0 [PT,L]

# 2nd alternative rule that works on ICDSoft:
RewriteRule .* index.php?kohana_uri=$0 [PT,QSA,L]

Если я правильно понял, то «.»просто означает любой отдельный символ.

Может "."использоваться в правильно сформированном URL-адресе, кроме того, где он обозначает расширение файла или веб-суффикс (.com, .org и т. д.)?Я имею в виду, что они не отображаются в строке состояния Firefox, когда я наведу на них ссылку, и это заставляет меня поверить, что это проблема с браузером / исправностью, а не проблема с кодированием.

Ответы [ 4 ]

0 голосов
/ 28 июля 2010

Время проверить все сгенерированные запросы с помощью Profiler.

в Controller :: __ construct () положить

new Profiler;

и найти, возможно, некорректный запрос.*

Другое возможное решение: Пройдите через ваш код, иногда незакрытый / неразорванный экземпляр Database Query может прервать (или объединить) другой запрос ...

0 голосов
/ 28 июля 2010

Я думаю, что проблема в рамках Kohana, а не в SQL. Чек для фильтрации параметров из URL. Попробуйте напечатать ваш запрос и посмотреть, как он выглядит точно в тот момент, когда он будет выполнен, и посмотреть, что произошло с вашим периодом.

0 голосов
/ 28 июля 2010

РЕДАКТИРОВАТЬ: Извините, я не видел, что вы используете Kohana версии 2.x. Я сомневаюсь, что это относится к вам.

Просто предположение, но вы установили свой маршрут, чтобы учесть периоды в URL? По умолчанию Kohana не допускает периоды. Вам необходимо установить третий аргумент Route :: set () примерно так:

Route::set('reviews', 'reviews/<name>(/<page>)', array('name' => '[^/,;?]++', 'page' => '\d+')
    ->defaults(array(
        'controller' => 'reviews',
        'action'     => 'load',
        'name'       => NULL,
        'page'       => 1,
    ));

Посмотреть сообщение на форуме http://forum.kohanaframework.org/comments.php?DiscussionID=4320

0 голосов
/ 28 июля 2010

MySQL не имеет проблем с точками в данных столбца. Однако точка используется для отделения имен таблиц от имен столбцов: table.column. Если ваши запросы не экранированы и не заключены в кавычки, период может быть неправильно интерпретирован как разделитель таблицы / столбца.

Как вы готовите свои запросы?

...