Проблемы WordPress темы LineDay и их решение

После установки темы LineDay на свой блог заметил пару серьезных проблем.
Го под кат, чтобы узнать что это за проблемы и как с ними бороться.

Первая проблема (security issue)

Если у поста кирилический url (http://my-blog.com/урл-поста.html), то любой пользователь сможет увидеть полный путь к вашему блогу на сервере.

Так как вместо времени публикации поста будет выводится ошибка:

Warning: sprintf() [function.sprintf]: Too few arguments in /home/username/public_html/sitename/wp-content/themes/lineday/library/underscores/template-tags.php on line 27

Если попробуете гуглянуть “wp-content/themes/lineday/library/underscores/template-tags.php on line 27” – найдёте не один сайт с данной проблемой.

И так, в чём же проблема? Почему так происходит?

Открываем файл темы wp-content/themes/lineday/library/underscores/template-tags.php
На 25-27 строках видим код:

$posted_on = sprintf(
 '<a href="' . esc_url( get_permalink() ) . '" rel="bookmark">' . $time_string . '</a>'
);

Виновником является функция sprintf(), которая здесь вообще не понятно зачем.

Предположим, что у поста урл http://my-blog.com/урл-поста.html

Идем по шагам:

  • get_permalink() возвращает вместо http://my-blog.com/урл-поста.html енкодированый урл http://my-blog.com/%d1%83%d1%80%d0%bb-%d0%bf%d0%be%d1%81%d1%82%d0%b0.html
  • esc_url() эскейпит различные спецсимволы в урле
  • sprintf() всё ломает 🙂

Ломается всё из-за того, что строки вида %d являются шаблонами для sprintf() и функция ожидает, что будут ещё аргументы, которые она подставит вместо этих шаблонов.

По коду видно, что никаких других аргументов нет и быть не может. Так что мы просто убираем использование sprintf():

$posted_on = '<a href="' . esc_url( get_permalink() ) . '" rel="bookmark">' . $time_string . '</a>';

Вторая проблема (декоративная)

Автор темы LineDay забыл о существовании языков, которые используют мультибайтовые строки для кодирования символов (это практически все остальные языки, кроме английского).

Из-за этого ссылки на следующий/предыдущий пост могут выглядеть вот так:

Вот пример кода функции lineday_truncate_text() из файла wp-content/themes/lineday/library/lineday.php

function lineday_truncate_text( $string, $character_limit = 50, $truncation_indicator = '...' ) {
    $truncated = null == $string ? '' : $string;
    if ( strlen( $string ) >= ( $character_limit + 1 ) ) {
        $truncated = substr( $string, 0, $character_limit );

        if ( substr_count( $truncated, ' ') > 1 ) {
            $last_space = strrpos( $truncated, ' ' );
            $truncated = substr( $truncated, 0, $last_space );
        } // end if

        $truncated = $truncated . $truncation_indicator;

    } // end if/else

    eturn $truncated;
}

Эта функция режет строку до определенной длины.

Как видно, lineday_truncate_text() использует внутри себя функции: strlen(), substr(), substr_count(), strrpos() и substr(). Эти функции не предназначены для работы с мультибайтовыми строками.

Чтобы пофиксить проблему отображения урезанных строк на кирилице, нам нужно заменить вышеперечисленные функции на их аналоги: mb_strlen(), mb_substr(), mb_substr_count(), mb_strrpos() и mb_substr().

function lineday_truncate_text( $string, $character_limit = 50, $truncation_indicator = '...' ) {
    $truncated = null == $string ? '' : $string;
    if ( mb_strlen( $string ) >= ( $character_limit + 1 ) ) {
        $truncated = mb_substr( $string, 0, $character_limit );

        if ( mb_substr_count( $truncated, ' ') > 1 ) {
            $last_space = mb_strrpos( $truncated, ' ' );
            $truncated = mb_substr( $truncated, 0, $last_space );
        } // end if

        $truncated = $truncated . $truncation_indicator;

    } // end if/else

    eturn $truncated;
}

Функции mb_* предназначены для работы с мультибайтовыми строками.

Ну вот и всё, проблема решена.

Если заметили ещё какие-то проблемы в теме LineDay – пишите, с радостью добавлю их описание в пост.