PHP кодстайл в WordPress — стандарты
Чтобы код WordPress везде был оформлен в одном стиле и удобно читался в ядре, плагинах и темах, рекомендуется соблюдать стандарты написания кода, которые приняты разработчиками WordPress. Эти стандарты очень похожи на стандарт PEAR, однако есть и кардинальные отличия. Рекомендую ознакомится с ними и при создании плагинов или тем, по возможности, их соблюдать.
PHP Documentation Standards (англ.) — стандарты документирования PHP кода - это комментарии к функциям и хукам.
JavaScript Documentation Standards — стандарты документирования JS кода.
Дополнение к написанному здесь (обновления для версии PHP 5.6): Coding Standards Updates for PHP 5.6
Одинарные и двойные кавычки
Если в строке нет переменных, используйте одинарные кавычки, в других случаях двойные. Не нужно экранировать кавычки в строке и если они они есть, то рекомендуется их чередовать:
echo '<a href="/static/link" title="Yeah yeah!">Link name</a>'; echo "<a href='$link' title='$linktitle'>$linkname</a>";
Вторая строка в этом примере не очищает выводимые переменные, а это необходимо делать в целях безопасности. Поэтому для такой записи, переменные должны быть очищены заранее. В целом, такую запись можно считать неприемлемой! См. раздел учебника безопасный вывод.
Отступы
Отступ должен всегда показывать логическую структуру кода. Используйте табуляцию (клавиша Tab), а не пробелы - это дает больше гибкости. Пробелы стоит использовать, когда нужно выравнять что-либо внутри строки.
Правило: табуляция должна быть использована в начале строки для отступа, в то время как пробелы могут быть использованы в середине строки для выравнивания.
if ( условие ) { $foo = 'somevalue'; $foo2 = 'somevalue2'; $foo_bar = 'somevalue3'; $foo5 = 'somevalue4'; }
А так код выглядит, если показать невидимые символы табуляции и пробела:
if ( условие ) { ———$foo.....= 'somevalue'; ———$foo2....= 'somevalue2'; ———$foo_bar.= 'somevalue3'; ———$foo5....= 'somevalue4'; }
Для ассоциативных массивов, значения должны начинаться с новой строки. Рекомендуется ставить «последнюю» запятую при перечислении элементов массива - так удобнее добавлять новые элементы...
$my_array = array( ———'foo'...=> 'somevalue', ———'foo2'..=> 'somevalue2', ———'foo3'..=> 'somevalue3', ———'foo34'.=> 'somevalue3', );
Стиль фигурных скобок
Фигурные скобки должны использоваться для всех блоков в стиле, как показано ниже:
if ( условие ) { action1(); action2(); } elseif ( условие2 && условие3 ) { action3(); action4(); } else { defaultaction(); }
Если идет длинный блок, его по возможности нужно разбить на два или более коротких блоков или функций. Если такой длинный блок необходим, добавьте краткий комментарий в конце, чтобы можно было понять, что именно закрывает фигурная скобка. Такой подход логично применять для блока с 35 и более строк.
Следует комментировать любой код, который интуитивно не понятен.
Используйте фигурные скобки всегда, даже если они не требуются.
if ( условие ) { action0(); } if ( условие ) { action1(); } elseif ( условие2 ) { action2a(); action2b(); } foreach ( $items as $item ) { process_item( $item ); }
Обратите внимание, что требование использовать фигурные скобки всегда означает, что одиночные конструкции в стиле одной строки - запрещены.
Можно использовать альтернативный синтаксис для управляющих структур: if / endif
, while / endwhile
- особенно это актуально для шаблонов, где PHP код встраивается в HTML:
<?php if ( have_posts() ) : ?> <div class="hfeed"> <?php while ( have_posts() ) : the_post(); ?> <article id="post-<?php the_ID() ?>" class="<?php post_class() ?>"> <!-- ... --> </article> <?php endwhile; ?> </div> <?php endif; ?>
Используйте elseif
, а не else if
elseif
и else if
будут одинаково работать только при использовании фигурных скобок. Если используются синтаксис с двоеточием для определения условий нужно использовать elseif
, иначе мы получим фатальную ошибку PHP.
// Некорректный способ: if ( $a > $b ) { echo "$a больше, чем $b"; } else if ( $a == $b ) { echo "Строка выше вызывает фатальную ошибку."; } // Корректный способ: if ( $a > $b ) { echo "$a больше, чем $b"; } elseif ( $a == $b ) { echo "$a равно $b"; } else { echo "$a не больше и не равно $b"; }
Многострочный вызов функции
При разбиении параметров функции на несколько строк, каждый параметр должен находиться на отдельной строке. Так комментарии к параметрам смогут иметь собственную строку.
Каждый параметр должен занимать не более одной строки. Многострочные параметры нужно переносить в переменную, а в вызов функции передавать уже эту переменную.
$bar = array( 'use_this' => true, 'meta_key' => 'field_name', ); $baz = sprintf( // translators: %s: Friend's name esc_html__( 'Hello, %s!', 'yourtextdomain' ), $friend_name ); $a = foo( $bar, $baz, // translators: %s: cat sprintf( __( 'The best pet is a %s.' ), 'cat' ) );
Регулярные выражения
Используйте строки в одинарных кавычках для регулярных выражений, так как, в отличие от двойных кавычек, у них есть только два символа, который нужно экранировать: \'
и \\
.
Открытие и закрытие тегов PHP
При добавлении многострочных PHP кодов в HTML блок PHP теги открытия и закрытия должны быть расположены на отдельной строке.
Правильно (многострочный):
<?php function foo() { ?> <div> <?php echo bar( $baz, $bat ); ?> </div> <?php }
Правильно (однострочный):
<input name="<?php echo esc_attr( $name ); ?>" />
Неправильно:
<?php if ( $a === $b ) { ?> <some html> <?php }
Не используйте короткие PHP теги
Никогда не используйте короткие PHP теги для публичных проектов (например плагинов), потому что на некоторых серверах их обработка может быть отключена и ваш код не будет работать...
Правильно:
<?php ... ?> <?php echo $var; ?>
Неправильно:
<? ... ?> <?= $var ?>
Удаляйте пробелы на конце строк
Удаляйте замыкающие пробелы в конце каждой строки.
Опускайте закрывающий тег PHP в конце файла. Если закрывающий PHP тег все же используется, убедитесь, что после него нет пробелов или переносов строк.
Так писать не следует:
<?php $foo = 'строка';пробел ?> Конец файла
Нужно так:
<?php $foo = 'строка'; Конец файла
Использование пробела
Всегда ставьте пробелы после запятых, и на обеих сторонах логических операторов (! && ||), операторов сравнения (==), конкатенации (.) и операторов присваивания (.=).
x == 23 foo && bar ! foo array( 1, 2, 3 ) $baz . '-5' $term .= 'X'
Ставьте пробелы на обеих сторонах открывающих и закрывающих круглых скобок для блоков if, elseif, foreach, switch
.
if ( $foo ) { ... foreach ( $foo as $bar ) { ...
При определении функции, используйте пробелы так:
function my_function( $param1 = 'foo', $param2 = 'bar' ) { ...
При вызове функции, так:
my_function( $param1, func_param( $param2 ) );
При выполнении логических сравнений, так:
if ( ! $foo ) { ...
При приведении типа, так:
foreach ( (array) $foo as $bar ) { ... $foo = (boolean) $bar;
При обращении к элементам массива, добавляйте пробел вокруг индекса, только если это переменная:
$x = $foo['bar']; // правильно $x = $foo[ 'bar' ]; // неправильно $x = $foo[0]; // правильно $x = $foo[ 0 ]; // неправильно $x = $foo[ $bar ]; // правильно $x = $foo[$bar]; // неправильно
В блоке switch
перед двоеточием оператора case не должно быть пробела.
switch ( $foo ) { case 'bar': // correct case 'ba' : // incorrect }
Аналогично, перед двоеточием в объявлениях возвращаемого типа не должно быть пробела.
function sum( $a, $b ): float { return $a + $b; }
Скобки внутри скобок должны иметь пробелы:
if ( $foo && ( $bar || $baz ) ) { ... my_function( ( $x - 1 ) * 5, $y );
Форматирование SQL конструкций
При форматировании SQL запроса, если запрос сложный, его можно разбить на несколько строк и добавить где нужно отступы. Хотя большинство конструкций пишутся в одну строку. Всегда пишите прописными такие части SQL конструкций, как: UPDATE
, WHERE
, FROM
, JOIN
.
Очистка, экранирование запроса должна быть сделана как можно позднее. Для защиты запроса рекомендуется использовать $wpdb->prepare(), а не esc_sql().
$var = "dangerous'"; // необработанные данные, которые могут быть экранированы или не экранированы $id = some_foo_number(); // данные ожидаются как число, но мы не уверены $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET post_title = %s WHERE ID = %d", $var, $id ) );
%s
используется для строк и %d
для целых чисел. Обратите внимание, что они не 'в кавычках'
! $wpdb->prepare() сам экранирует строки и добавляет кавычки, если надо. Преимущество prepare() в том, что не нужно помнить о ручном использовании esc_sql(), а также, что строка запроса с плейсхолдерами более наглядна, чем если бы там использовались переменные обернутые в esc_sql().
Смотрите описание метода $wpdb->prepare().
Запросы базы данных
Старайтесь не писать прямых запросов к базе данных. Если есть подходящая функция, а их в WP много, которая может получить необходимые данные - используйте ее.
Использование функций вместо запросов, помогает сохранить будущую совместимость кода. Кроме того многие функции работают с кэшем, а это может значительно ускорить работу кода.
Имена классов, функций, файлов, констант, переменных
Имена функций, переменных, хуков
Используйте строчные буквы a-z
в переменных, хуках и названиях функций и никогда CamelCase. Разделяйте отдельные слова нижним подчеркиванием _
. Не сокращайте имена переменных без необходимости; пусть код будет однозначным и само-документированным.
function some_name( $some_variable ) { [...] }
Имена классов
Нужно использовать слова с Заглавных_Букв
, разделенные подчеркиванием. Любые сокращения (акронимы, аббревиатуры) должны быть ПРОПИСНЫМИ.
class Walker_Category extends Walker { [...] } class WP_HTTP { [...] }
Константы должны быть словами в ВЕРХНЕМ_РЕГИСТРЕ
, разделенные нижним подчеркиванием:
define( 'DOING_AJAX', true );
Названия файлов
Должны быть понятные и должны также содержать только строчные буквы, а слова должны разделяться дефисом -
.
my-plugin-name.php
Названия файлов классов
Должны быть основаны на имени класса с приставкой class-
, подчеркивания в имени класса заменены дефисом, например WP_Error становится:
class-wp-error.php
Этот стандарт именования файлов справедлив для всех существующих и новых файлов с классами. Однако существуют файлы исключения: class.wp-dependencies.php, class.wp-scripts.php, class.wp-styles.php. Эти файлы имеют префикс class.
, точка после слова class вместо дефиса.
В ядре, в папке wp-includes
есть подключаемые файлы, в которых находятся функции, которые обычно используются в темах (в шаблоне). Файлы с такими функциями - тегами шаблона заканчиваются на -template
.
- wp-includes/general-template.php
- wp-includes/comment-template.php
- wp-includes/embed-template.php
- wp-includes/author-template.php
- wp-includes/category-template.php
- wp-includes/link-template.php
- wp-includes/media-template.php
- wp-includes/nav-menu-template.php
- wp-includes/post-template.php
- wp-includes/post-thumbnail-template.php
Понятные значения переменных в параметрах функций
Булевам, предпочтительны строковые значения. Т.е. вместо true/false при вызове функций лучше использовать какую-то объясняющую значение параметра строку.
Плохой код:
function eat( $what, $slowly = true ) { ... } eat( 'mushrooms' ); eat( 'mushrooms', true ); // что означает true? eat( 'dogfood', false ); // что означает false, противоположность true?
Так как PHP не поддерживает именованные аргументы, значения флагов бессмысленны и каждый раз, когда мы сталкиваемся с вызовом функции, как в примерах выше, нам нужно смотреть документацию по функции. Код может быть более читаемым с помощью описательных строковых значений, вместо булевых.
Хороший код:
function eat( $what, $speed = 'slowly' ) { ... } eat( 'mushrooms' ); eat( 'mushrooms', 'slowly' ); eat( 'dogfood', 'quickly' );
Когда нужно больше параметров функции, используйте массив $args. Он даже лучше!
Очень хороший код:
function eat( $what, $args ) { ... } eat( 'noodles', array( 'speed' => 'moderate' ) );
Интерполяция для имен динамических хуков
Для удобства чтения и обнаружения, хуки с переменными в названии должны быть интерполирован (заключен в фигурные скобки {
и }
), и не должны конкатенироваться:
Скобки нужны, чтобы PHP мог корректно анализировать типы данных переменных в интерполированной строке.
// правильно do_action( "{$new_status}_{$post->post_type}", $post->ID, $post ); // неправильно do_action( $new_status .'_'. $post->post_type, $post->ID, $post );
Там, где это возможно, динамические значения в именах тегов также должны быть максимально краткими и точными. $user_id
гораздо понятнее чем, скажем, $this->id
.
Тернарный оператор
Тернарные операторы хороши, но в них рекомендуется всегда проверять правдивое утверждение, а не ложное. Иначе он просто вводит в заблуждение из-за двойного отрицания. Исключение - это использование ! empty()
, потому что по-другому иногда просто сложно записать.
Как нужно проверять:
// (если условие выполняется = true) ? (то делаем это) : (иначе это); $music_type = ( 'jazz' == $music ) ? 'cool' : 'blah'; // (если значение не пустое - ! empty ) ? (то делаем это) : (иначе это);
Как не следует писать:
// (если условие не выполняется != true) ? (то делаем это) : (иначе это); $music_type = ( 'jazz' != $music ) ? 'blah' : 'cool';
Тернарный оператор (короткий синтаксис)
Часто при установке значения переменной код записывается так:
$a = $b ? $b : $c;
Короткий синтаксис упрощает восприятие кода:
$a = $b ?: $c;
Рекомендуется использовать короткий синтаксис где это возможно.
Не путайте этот оператор с оператором null-coalescing — ??
, который появился в PHP 7. Тут если $b не определена, то php выведет notice.
Условия Магистра Йоды
При выполнении логических сравнений, всегда ставьте константы или литералы - слева, а переменную - справа.
if ( true == $the_force ) { $victorious = you_will( $be ); }
Если пропустить второй знак =
в приведенном примере (признаться, это происходит даже с самыми опытными из нас), то мы получим ошибку PHP и сразу её увидим, потому что код не будет работать. А вот если бы конструкция была обратной - $the_force = true
, то условие всегда будет выполняться и никакой ошибки мы не увидим, и можем пропустить такой серьезный баг, который к тому же иногда сложно отловить!
К такому «перевернутому» написанию просто нужно привыкнуть.
Это относится и к ==
, !=
, ===
и !==
. «Условия Йоды» для <
, >
, <=
или >=
значительно труднее читать и тут их лучше не использовать.
Умный код
Если говорить коротко, то читаемость кода должна быть на первом плане, она важнее краткости или каких-то не очевидных, но удобных сокращений.
isset( $var ) || $var = some_function(); // или ! isset( $var ) && $var = some_function();
Да - это крутая запись, видно что сделал её опытный программист. Но любому другому разработчику, а зачастую даже и автору, для того чтобы разобраться в такой записи нужно немного вникать и потратить лишние секунды или минуты. Это не очевидная и не понятная запись и её нужно избегать, и лучше её записать длиннее, но понятнее:
if ( ! isset( $var ) ) { $var = some_function(); }
Оператор подавления ошибок @
Из PHP документации:
PHP поддерживает один оператор управления ошибками: знак
@
. В случае, если он предшествует какому-либо выражению в PHP-коде, любые сообщения об ошибках, генерируемые этим выражением, будут проигнорированы.
В то время как этот оператор существует в ядре, он часто используется потому что лень нормально обработать переменную. Его использование настоятельно не рекомендуется, так как даже PHP документация заявляет:
Внимание: На сегодняшний день оператор "@" подавляет вывод сообщений даже о критических ошибках, прерывающих работу скрипта. Помимо всего прочего, это означает, что если вы использовали "@" для подавления ошибок, возникающих при работе какой-либо функции, в случае если она недоступна или написана неправильно, дальнейшая работа скрипта будет остановлена без каких-либо уведомлений.
Не используйте функцию extract()
По мотивам тикета #22400. extract() это ужасная функция, которая сильно усложняет отладку кода, также сильно усложняет читаемость и понимание кода. Поэтому никогда не используйте extract(), кроме случаев когда без неё не обойтись, т.е. никогда!
Почему extract() такая ужасная, хорошо объясняет Джозеф Скотт (англ.): I Don’t Like PHP’s extract() Function.
Анонимные функции
Анонимные функции полезны, когда нужно записать коротко цельную логическую конструкцию с использование PHP функции. Например, вызов preg_replace_callback() можно записать так:
$caption = preg_replace_callback( '/<[a-zA-Z0-9]+(?: [^<>]+>)*/', function ( $matches ) { return preg_replace( '/[\r\n\t]+/', ' ', $matches[0] ); }, $caption );
Такой код улучшает читаемость, так как разработчику не нужно «прыгать» к коду функции, чтобы посмотреть что там происходит.
Там, где разработчик считает это целесообразным, анонимные функции могут использоваться в качестве альтернативы созданию новых callback функций.
Однако, анонимные функции не должны использоваться в хуках в ядре WordPress в качестве коллбек функций, так как в этом случае их невозможно удалить через remove_action() или remove_filter(). За пределами ядра разработчики могут передавать анонимные функции в хуки при этом нужно учитывать что может понадобится удалить хук, в этом случае анонимную функцию лучше НЕ использовать.
Пространства имен (Namespaces)
Пространства имен-это простой способ инкапсуляции (отделения, изоляции) функциональности. Однако, как было выявлено, добавить пространства имен в ядро WordPress это не простая задача, которая требует хорошо продуманной архитектуры.
В настоящее время введение пространств имен в ядро WordPress не ожидается. Поэтому пространства имен не должны использоваться в ядре WordPress.
Короткий Синтаксис Массива — []
Вместо объявления массивов с использованием синтаксиса array( 1, 2, 3 ) теперь рекомендуется сокращать до [ 1, 2, 3 ]. Это соответствует тому, как массивы объявляются в стандартах кодирования WordPress JavaScript.
Определение переменной внутри условия
В целях нормальной читаемости кода, настоятельно рекомендуется определять переменные на отделенной строке, а не внутри if условия:
Правильно:
$sticky_posts = get_option( 'sticky_posts' ); if ( $sticky_posts ) { // ... }
Неправильно:
if ( $sticky_posts = get_option( 'sticky_posts' ) ) { // ... }
-
Ссылки: