Руководство по написанию программного кода для phpBB3 (Олимпус)

Здесь изложены основные правила написания кода для phpBB «Олимпус», которые рекомендуется соблюдать как можно точнее.

[url=http://www.phpbbguru.net/useful/codebook/coding-guidelines/]Руководство по написанию программного кода[/url]

Основные положения 
Настройки редактора
Заголовок файла
Расположение файлов
Оформление кода, рекомендации
Названия переменных и функций
Оформление кода
Оформление SQL
Оптимизация
Общие рекомендации
Работа со стилями
Файлы конфигурации стилей
Общие правила работы со стилями
Работа с шаблонами
Общие правила работы с шаблонами
Наследование шаблонов
Наборы символов и кодировки
Правила перевода (интернационализация, локализация)
Стандартизация
Другие положения
Стиль написания
История версий данного руководства
Авторское право и отказ от ответственности

1. Основные положения

1.i. Настройки редактора

Отступы и пробелы:

Чтобы максимально всё упростить, мы будем использовать вместо пробелов отступы (табуляцию). Мы рекомендуем принять 4 (четыре) пробела за один символ табуляции, поэтому вам нужно установить ширину табуляции в 4 пробела в своем редакторе. Убедитесь, что при сохранении файла редактор сохраняет табуляцию и не преобразует ее в пробелы. Таким образом, наш код будет отображаться так, как мы хотим, без нарушений форматирования текущих файлов.

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

{TAB}$mode{TAB}{TAB}= request_var('mode', '');
{TAB}$search_id{TAB}= request_var('search_id', '');

Если в коде использованы символы табуляции (обозначены знаком {TAB}), то оба знака равенства должны находиться в одном и том же столбце.

Переводы строк:

Убедитесь, что ваш редактор сохраняет файлы в формате перевода строк UNIX (LF). Это означает, что строки должны оканчиваться только символом перевода строки, а не в формате перевода строк Windows (комбинация CR/LF), используемый в Win32, или в формате перевода строк Classic Mac (CR). Любой достойный редактор должен уметь это делать, но не всегда эта настройка установлена по умолчанию. Изучите ваш редактор. Если вам нужен совет по использованию редактора для вашей операционной системы, попросите помощи у кого-нибудь из разработчиков. Некоторые из них работают в Win32.

1.ii. Заголовок файла

Стандартный заголовок для новых файлов:

Этот шаблон заголовка должен присутствовать в начале каждого файла phpBB:

/**
*
* @package {НАЗВАНИЕ ПАКЕТА}
* @version $Id: $
* @copyright (c) 2007 phpBB Group
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*
*/

См. раздел Расположение файлов для получения информации о правильном названии пакета.

Файлы, содержащие управляющий код (inline code):

Для таких файлов вы должны поместить пустой комментарий непосредственно после заголовка, чтобы программа обработки документации не присваивала заголовок первому найденному элементу кода.
/**
* {ЗАГОЛОВОК}
*/

/**
*/
{КОД}

Файлы, содержащие только функции:

Не забывайте документировать, снабжая комментариями, функции (особенно первую функцию, следующую за заголовком файла). Каждая функция должна иметь, по крайней мере, комментарий о том, что эта функция делает. Для более сложных функций рекомендуется также документировать и аргументы.

Файлы, содержащие только классы:

Не забывайте документировать класс. Для классов необходимо отдельное определение @package, точно так же, как и имя пакета в заголовке. Кроме этого специального случая, вышеупомянутое утверждение распространяется и на файлы, содержащие только функции, необходимые для применения к классам и их методам.

Код, следующий за заголовком в файлах с функциями или классами:

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

/**
* {ЗАГОЛОВОК}
*/

/**
* @ignore
*/
Небольшой кусочек кода, вроде одного — двух определений или условий

/**
* {ДОКУМЕНТАЦИЯ}
*/
class ...

1.iii. Расположение файлов


Функции, используемые более чем в одном файле скрипта, должны быть размещены в functions.php, функции для одного файла должны быть помещены в этом самом файле (в конце) или в соответствующем файле функций раздела, к которому они относятся. Некоторые файлы в /includes содержат функции, ответственные за особые разделы, такие, как, например, загрузка файлов на сервер, отображение разных объектов, функции, связанные с пользователями, и так далее.

Ниже приведен список существующих пакетов, и связанные с ними элементы и функции должны быть расположены в соответствующих файлах и папках, с указанием правильного имени пакета в заголовке. Названия пакетов в списке выделены жирным шрифтом:

phpBB3

Основные файлы и все файлы, не выделенные в отдельный пакет

acm

/includes/acm, /includes/cache.php

Система кэширования

acp
/adm, /includes/acp, /includes/functions_admin.php
Администраторский раздел


dbal
/includes/db
Инструментарий работы с базой данных.
Базовый класс — dbal


/includes/db/dbal.php
Базовый класс инструментария, определяющий основную структуру и некоторые общие определения


/includes/db/firebird.php
Инструментарий базы данных Firebird/Interbase


/includes/db/msssql.php
Инструментарий базы данных MSSQL


/includes/db/mssql_odbc.php
Инструментарий базы данных MSSQL ODBC для MSSQL


/includes/db/mysql.php
Инструментарий базы данных MySQL для MySQL 3.x/4.0.x/4.1.x/5.x


/includes/db/mysqli.php
Инструментарий базы данных MySQLi


/includes/db/oracle.php
Инструментарий базы данных Oracle


/includes/db/postgres.php
Инструментарий базы данных PostgreSQL


/includes/db/sqlite.php
Инструментарий базы данных Sqlite


diff
/includes/diff
Модуль Diff


docs
/docs
Документация phpBB


images
/images
Все глобальные изображения, не связанные со стилями


install
/install
Система установки


language
/language
Все языковые файлы


login
/includes/auth
Модули аутентификации


VC
/includes/captcha
CAPTCHA (визуальное подтверждение)


mcp
mcp.php, /includes/mcp, report.php
Модераторский раздел


ucp
ucp.php, /includes/ucp
Личный раздел


utf
/includes/utf
Функции и классы, связанные с UTF-8


search
/includes/search, search.php
Система поиска


styles
/styles, style.php
Стили, шаблоны, темы и наборы изображений phpBB

 Наверх


2. Оформление кода, рекомендации


Обратите внимание, что данные рекомендации касаются всех файлов php, html, javascript и css.


2.i. Названия переменных и функций


Мы не будем использовать ни одну из форм венгерской нотации. Многие из нас считают, что венгерская нотация — это один из основных способов запутывания кода, широко используемых в настоящее время.


Названия переменных:

Названия переменных должны быть выполнены в нижнем регистре, слова следует разделять знаком подчеркивания, например:

$current_user  — верно, а вот $currentuser и $currentUser  — неверно.

Названия должны отражать назначение, но быть краткими. Мы не используем огромные предложения в качестве названий переменных, но всегда лучше напечатать пару лишних символов, чем угадывать, для чего же нужна та или иная переменная.

Индексы в циклах:

Единственная ситуация, при которой допустимо использовать название переменной из одного символа, — это когда она является индексом в некоторой циклической конструкции. В таком случае, индекс внешнего цикла всегда должен быть $i. Если цикл находится внутри другого цикла, то его индекс должен быть $j, следующий — $k и так далее. Это правило не относится к тем случаям, когда в качестве индекса используется некоторая уже существующая переменная со значащим названием. Пример:

for ($i = 0; $i < $outer_size; $i++)
{
   for ($j = 0; $j < $inner_size; $j++)
   {
      foo($i, $j);
   }
}

Названия функций:

Функции также должны быть названы содержательно. Мы не программируем на языке C и мы не хотим давать функциям имена наподобие «stristr()». Напомним, названия должны быть написаны в нижнем регистре, все слова разделены знаком подчеркивания. Желательно, чтобы названия функций имели глагол в своем составе. Примерами хороших названий могут послужить следующие: print_login_status(), get_user_data() и так далее.

Аргументы функций:

Аргументы подчиняются тем же самым правилам, что и названия переменных. Нам не нужен десяток функций наподобие такой: do_stuff($a, $b, $c). В большинстве случаев мы бы хотели иметь возможность понять, как использовать функцию, просто лишь взглянув на ее объявление.

Вывод: Основная идея — не приносить ясность кода в жертву лени. Всё должно балансироваться здравым смыслом; print_login_status_for_a_given_user() — это название слишком громоздкое, функцию лучше назвать так: print_user_login_status() или просто print_login_status() .

Специальные наименования: 

Для обозначения всех смайлов используется термин smiley в единственном числе и smilies во множественном.

2.ii. Оформление кода


Всегда ставьте фигурные скобки:

Это еще один случай, когда не напечатанные из-за лени два дополнительных символа ухудшают читаемость кода. Даже если тело какой-то конструкции состоит всего из одной строки, не опускайте скобки. Ни в коем случае! Примеры:

// Здесь неправильно.

if (condition) do_stuff();

if (condition)
do_stuff();

while (condition)
do_stuff();

for ($i = 0; $i < size; $i++)
do_stuff($i);

 // Здесь правильно. 

if (condition)
{
do_stuff();
}

while (condition)
{
do_stuff();
}

for ($i = 0; $i < size; $i++)
{
do_stuff();
}

Где размещать фигурные скобки:

Это что-то вроде священной войны, но мы используем следующий стиль: фигурные скобки всегда расположены в отдельной строке. Закрывающая скобка всегда должна быть в том же столбце, что и соответствующая ей открывающая скобка, например:

if (condition)
{
	while (condition2)
	{
		...
	}
}
else
{
	...
}

for ($i = 0; $i < $size; $i++)
{
	...
}

while (condition)
{
	...
}

function do_stuff()
{
	...
}

Ставьте пробелы между лексемами:

Это еще один простой и легкий способ содержать код в читаемом виде без особых усилий. Когда вы пишете присвоение, выражение и т. д., всегда ставьте один пробел между лексемами. Другими словами, пишите код так, как если бы он был английским языком. Помещайте пробелы между переменными и операторами. Не помещайте пробелы сразу же за открывающей скобкой или перед закрывающей. Не помещайте пробелы перед запятой или точкой с запятой. Лучшим образом всё наглядно объясняют следующие примеры:

// Каждая пара содержит сначала неверное написание, затем верное.

$i=0;
$i = 0;

if($i<7) ...
if ($i < 7) ...

if ( ($i < 7)&&($j > 8) ) ...
if ($i < 7 && $j > 8) ...

do_stuff( $i, 'foo', $b );
do_stuff($i, 'foo', $b);

for($i=0; $i<$size; $i++) ...
for ($i = 0; $i < $size; $i++) ...

$i=($j < $size)?0:1;
$i = ($j < $size) ? 0 : 1;

Приоритет операторов:

Вы знаете точные сведения о приоритете всех операторов в PHP? И я тоже не знаю. Не пытайтесь угадать. Всегда вносите очевидность, используя круглые скобки в выражениях, чтобы точно понять приоритет выполнения операторов. Однако, не злоупотребляйте скобками, чтобы не ухудшить читаемость кода. То есть не используйте скобки в простых и понятных выражениях. Примеры:

// какой будет результат? кто знает?
$bool = ($i < 7 && $j > 8 || $k == 4);


// теперь вы будете уверены, каков будет результат.
$bool = (($i < 7) && (($j < 8) || ($k == 4)));


// Но этот вариант самый лучший, поскольку он и на глаз понятнее, и суть ясна
$bool = ($i < 7 && ($j < 8 || $k == 4));

Выделение строк кавычками:

В PHP существуют два разных способа выделения строк кавычками: или одиночными кавычками, или двойными. Основное отличие состоит в том, что обработчик выполняет интерполяцию переменных в строках с двойными кавычками и не делает этого в строках с одиночными кавычками. Поэтому вам следует всегда использовать одиночные кавычки, кроме случаев, когда вам нужна интерполяция переменных в строке. Таким образом, мы предотвращаем разбор десятков строк обработчиком в тех строках, где интерполяция не требуется.

Кроме того, если вы используете строковую переменную как часть вызова функции, то не нужно обрамлять переменную кавычками. Опять же, это заставляет обработчик строк проделывать бесполезную работу. Однако, обратите внимание, что почти все обозначения спецсимволов, предваряемые символом обратной косой черты, которые существуют для использования внутри двойных кавычек, не будут работать в строках с одиночными кавычками. Будьте внимательны и не бойтесь нарушать это правило, если код будет выглядеть проще для чтения. Примеры:

// неверное написание

$str = "Это очень длинная строка, где отсутствуют переменные, которые обработчик строк мог бы найти.";

do_stuff("$str");


// верное написание
$str = 'Это очень длинная строка, где отсутствуют переменные, которые обработчик строк мог бы найти.';

do_stuff($str);

// Иногда одиночные кавычки неуместны
$post_url = $phpbb_root_path . 'posting.' . $phpEx . '?mode=' . $mode . '&amp;start=' . $start;


// Двойные кавычки иногда необходимы, чтобы не перегружать строку конкатенацией
$post_url = "{$phpbb_root_path}posting.$phpEx?mode=$mode&amp;start=$start";


В запросах SQL отчасти допускается смешивание двойных и одиночных кавычек (согласно правилам касательно оформления SQL), во всех остальных случаях предпочтительно использовать только один стиль — главным образом, одиночные кавычки.


Ключи ассоциативных массивов:

В PHP можно использовать в качестве ключей ассоциативного массива строки, не заключенные в кавычки. Мы не используем такой способ — строка всегда должна быть заключена в кавычки, чтобы избежать недоразумений. Обратите внимание, что это правило применимо только когда мы используем литерал, а не переменную. Примеры:

// неверно

$foo = $assoc_array[blah];

// верно

$foo = $assoc_array['blah'];

// неверно

$foo = $assoc_array["$var"];

// верно

$foo = $assoc_array[$var];

Комментарии:

Каждая сложная функция должна быть снабжена комментарием, который описывает программисту всё, что ему нужно о ней знать. Значение каждого параметра, ожидаемые входные и возвращаемые данные — вот тот минимум, который требуется для такого комментария. Также должно быть представлено поведение функции в случае ошибок (и что это за случаи) — как правило, это входит в комментарий о возвращаемых значениях.

Особенно важно документировать любые соглашения, которые используются в коде, а также предварительные условия, накладываемые на выполнение данной операции. Любой разработчик должен быть в состоянии за короткое время разобраться, что делает та или иная часть пограммы.

Избегайте использования блочных комментариев /* */ для одной — двух строк: для них должен использоваться такой способ: // .


«Волшебные» числа:

Не используйте их. Применяйте именованные константы для каждого значения литерала, кроме особых очевидных случаев. Другими словами, использовать проверку на то, что массив имеет 0 элементов, используя литерал 0, — это нормально. Но ненормальным является присвоение какого-то особого значения числу и дальнейшее использование его везде в качестве литерала. Это идет в ущерб читаемости и удобства дальнейшей работы с кодом. Константы true и false должны использоваться вместо литералов 0 и 1: даже несмотря на то что они имеют одни и те же значения (но не тип!), логику программы намного легче понять, если вы используете именованные константы. Приводите типы данных, где это необходимо, не полагайтесь на автоматическое приведение (в настоящее время PHP очень слабо работает с типами данных, что может привести к проблемам безопасности, если разработчик не уделяет этому должное внимание).


Сокращенные операторы:

Единственные сокращенные операторы, которые могут вызвать проблему с легкостью чтения кода, — это операторы инкремента $i++ и декремента $j--. Эти операторы не следует использовать в качестве частей выражений. Однако, они могут быть использованы отдельной строкой. Использование этих операторов в выражениях просто не стоит тех проблем, которые могут возникнуть при отладке. Например:

// неверно

$array[++$i] = $j;
$array[$i++] = $k;

 // верно  

$i++;
$array[$i] = $j;

$array[$i] = $k;
$i++;


Условия, используемые внутри строки:

Условия, которые используются внутри строк, должны употребляться только в случае выполнения простых операций. Лучше всего, если они будут использоваться только для выполнения присваивания, но не для вызова функций или даже для чего-нибудь более сложного. Их неправильное употребление может нанести вред читаемости кода, так что ради экономии места не увлекайтесь их использованием, примеры:

// Неудачное место для их использования

($i < $size && $j > $size) ? do_stuff($foo) : do_stuff($bar);


// А здесь они вполне уместны
$min = ($i < $j) ? $i : $j;

 

Не используйте неинициализированные переменные.

В phpBB3 мы используем более высокий уровень сообщения об ошибках времени исполнения. Это значит, что использование неинициализированных переменных вызовет предупреждение. Эти предупреждения можно избежать, если использовать встроенную функцию isset() для проверки того, была ли установлена переменная или нет — но лучше всегда инициализировать переменные. Эта функция может пригодиться в том случае, если требуется проверить, существует ли ключ в массиве, например:

// Неверно

if ($forum) ...

// Верно  

if (isset($forum)) ...

// Также возможно
if (isset($forum) && $forum == 5)

Функция empty() полезна в том случае, если вы хотите проверить, не была ли установлена переменная или пустая ли она (пустая строка, 0 как строка или число, NULL, false, пустой массив или переменная класса, объявленная, но не имеющая значения). Таким образом, empty следует использовать вместо isset($array) && sizeof($array) > 0 — такой код можно написать короче: !empty($array).

Оператор switch:

Блоки switch/case иногда могут быть очень длинными. Чтобы иметь понятный код, к таким блокам, а также операторам break тоже необходимо применять правило расположения фигурных скобок (расположение в отдельных строках, но строго друг под другом). Пример:

// Неверно

switch ($mode)
{
case 'mode1':
// Я что-то здесь делаю
break;
case 'mode2':
// Здесь я делаю что-то другое
break;
}


// Верно 
switch ($mode)
{
case 'mode1':
// Я что-то здесь делаю
break;

case 'mode2':
// Здесь я делаю что-то другое
break;

default:
// Всегда допускайте, что ни один из вышеописанных case не выполнился
break;
}

// Также верно, если у вас много кода между case и break
switch ($mode)
{
case 'mode1':

// Я что-то здесь делаю

break;

case 'mode2':

// Здесь я делаю что-то другоe

break;

default:

// Всегда допускайте, что ни один из вышеописанных case не выполнилсяt

break;
}

Даже хотя оператор break для случая default не нужен, иногда лучше его поставить просто для лучшей читаемости и полноты кода.

Если break опускается, пожалуйста, ставьте вместо него комментарий. Пример:

// Пример с отсутствием оператора break
switch ($mode)
{
case 'mode1':

// Я что-то здесь делаe

// no break here

case 'mode2':

// Здесь я делаю что-то другоe

break;

default:

// Всегда допускайте, что ни один из вышеописанных case не выполнился

break;


2.iii. Оформление SQL


Общие правила оформления SQL:

Все запросы SQL должны быть совместимы со всеми поддерживаемыми СУБД. Если используется SQL, специфичный для определенной СУБД, то должны быть предоставлены альтернативные запросы для остальных СУБД (MySQL3/4/5, MSSQL (7.0 и 2000), PostgreSQL (7.0+), Firebird, SQLite, Oracle8, ODBC (обобщенный, если возможно).

Все команды SQL должны использовать инструментарий работы с базой данных (DBAL)


Оформление кода SQL:

Выражения SQL часто нечитаемы, если не применять некоторые правила форматирования. Это происходит из за того, что они порой становятся очень громоздкими. Тем не менее, форматирование запросов SQL значительно повышает удобство чтения кода. SQL-запросы должны быть отформатированы следующим образом, основываясь на ключевых словах:

$sql = 'SELECT *
<-один отступ->FROM ' . SOME_TABLE . '
<-один отступ->WHERE a = 1
<- два отступа ->AND (b = 2
<-  три отступа  ->OR b = 3)
<-один отступ->ORDER BY b'
Ниже приведен пример с отступами:
$sql = 'SELECT *
	FROM ' . SOME_TABLE . '
	WHERE a = 1
		AND (b = 2
			OR b = 3)
	ORDER BY b';

Кавычки и SQL: 

Используйте двойные кавычки там, где это применимо (переменные в следующих примерах заранее были приведены к целому типу). Примеры: 

// Неверно.

"UPDATE " . SOME_TABLE . " SET something = something_else WHERE a = $b";

'UPDATE ' . SOME_TABLE . ' SET something = ' . $user_id . ' WHERE a = ' . $something;

 // Верно. 

'UPDATE ' . SOME_TABLE . " SET something = something_else WHERE a = $b";

'UPDATE ' . SOME_TABLE . " SET something = $user_id WHERE a = $something";
Другими словами, используйте одиночные кавычки, если не требуется подстановка переменных или где переменная не может объявляться в двойных кавычках. В остальных случаях используйте двойные кавычки.

Избегайте запросов SQL, специфичных для определенных СУБД:

Оператор «не равно», в соответствии со стандартом SQL:2003, — это „<>“

// Неверный пример.

$sql = 'SELECT *
	FROM ' . SOME_TABLE . '
	WHERE a != 2';


// Верный пример.
$sql = 'SELECT *
	FROM ' . SOME_TABLE . '
	WHERE a <> 2';

Общие методы DBAL: 

sql_escape():

Всегда используйте $db->sql_escape()  для проверки строки в выражении SQL (даже если вы уверены, что ваша строка не содержит одиночных кавычек, никогда не доверяйте входным данным), например:

$sql = 'SELECT *
	FROM ' . SOME_TABLE . "
	WHERE username = '" . $db->sql_escape($username) . "'";

sql_query_limit():

Мы не используем выражения LIMIT в SQL-запросе, а вместо этого вызываем $db->sql_query_limit(). Вы передаете запрос, общее количество строк для получения и смещение.

Примечание:: Поскольку Oracle обрабатывает LIMIT по-другому и вледствие того, как мы реализовали эту обработку, вам особенно нужно быть осторожным, если вы используете sql_query_limit для SQL-запросов, извлекающих данные из нескольких таблиц.

При использовании «SELECT x.*, y.jars» убедитесь, что в x нет столбца с именем jars; убедитесь, что нет совпадения между именами неявно заданной колонки и явно заданных колонок.


sql_build_array():

При необходимости обновить или вставить данные (команды UPDATE и INSERT соответственно) используйте функцию $db->sql_build_array() . Эта функция уже экранирует строки и проверяет другие типы, так что здесь нет необходимости этого делать. Вставляемые данные должны быть представлены в виде массива — $sql_ary — или прямо внутри выражения, если нужно вставить или обновить одну или две переменные. Пример вставки данных:

$sql_ary = array(
	'somedata'		=> $my_string,
	'otherdata'		=> $an_int,
	'moredata'		=> $another_int
);

$db->sql_query('INSERT INTO ' . SOME_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));

Для полноты картины приведем пример того, как будет выглядеть обновление данных:
$sql_ary = array(
	'somedata'		=> $my_string,
	'otherdata'		=> $an_int,
	'moredata'		=> $another_int
);

$sql = 'UPDATE ' . SOME_TABLE . '
	SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
	WHERE user_id = ' . (int) $user_id;
$db->sql_query($sql);

Функция $db->sql_build_array()  поддерживает следующие режимы: INSERT (пример выше), INSERT_SELECT (построение запросов для выражений INSERT INTO table (...) SELECT value, column ...), UPDATE (пример выше) и SELECT (для построения выражений WHERE [с логикой AND]).


sql_multi_insert():

Если вы хотите вставить несколько выражений за один раз, используйте отдельный метод sql_multi_insert(). Пример:

$sql_ary = array();

$sql_ary[] = array(
	'somedata'		=> $my_string_1,
	'otherdata'		=> $an_int_1,
	'moredata'		=> $another_int_1,
);

$sql_ary[] = array(
	'somedata'		=> $my_string_2,
	'otherdata'		=> $an_int_2,
	'moredata'		=> $another_int_2,
);

$db->sql_multi_insert(SOME_TABLE, $sql_ary);

sql_in_set():

Функция $db->sql_in_set()  используется для построения конструкций IN () и NOT IN (). Поскольку MySQL работает быстрее, если для одного значения для сравнения используются операторы = и <>, мы оставляем за DBAL право выбирать, как поступить. Типичный пример выборки при совпадении с любым из ряда значений:
$sql = 'SELECT *
	FROM ' . FORUMS_TABLE . '
	WHERE ' . $db->sql_in_set('forum_id', $forum_ids);
$db->sql_query($sql);

В зависимости от количества значений $forum_ids запрос будет выглядеть по-разному.

// SQL-выражение, если $forum_ids = array(1, 2, 3);

SELECT FROM phpbb_forums WHERE forum_id IN (1, 2, 3)


// SQL-выражение, если $forum_ids = array(1) или $forum_ids = 1

SELECT FROM phpbb_forums WHERE forum_id = 1


Естественно, то же самое возможно и с выполнением выборки при несовпадении ни с одним из ряда значений:

$sql = 'SELECT *
	FROM ' . FORUMS_TABLE . '
	WHERE ' . $db->sql_in_set('forum_id', $forum_ids, true);
$db->sql_query($sql);

В зависимости от количества значений $forum_ids здесь запрос также будет выглядеть по-разному.

// SQL-выражение, если $forum_ids = array(1, 2, 3);

SELECT FROM phpbb_forums WHERE forum_id NOT IN (1, 2, 3)

// SQL-выражение, если $forum_ids = array(1) или $forum_ids = 1

SELECT FROM phpbb_forums WHERE forum_id <> 1

Если предоставленный массив пуст, результатом будет ошибка.


sql_build_query():

Функция $db->sql_build_query()  предназначена для построения SQL-выражений запросов SELECT и SELECT DISTINCT, если вам необходимо объединить результат (команда JOIN) нескольких таблиц или в случае выборки из нескольких таблиц с присоединением (JOIN). Использование функции необходимо для того, чтобы удостовериться в работоспособности итогового выражения во всех поддерживаемых СУБД. Вместо объяснения каждого возможного случая лучше предоставить небольшой пример:

$sql_array = array(
	'SELECT'	=> 'f.*, ft.mark_time',

	'FROM'		=> array(
		FORUMS_WATCH_TABLE	=> 'fw',
		FORUMS_TABLE		=> 'f'
	),

	'LEFT_JOIN'	=> array(
		array(
			'FROM'	=> array(FORUMS_TRACK_TABLE => 'ft'),
			'ON'	=> 'ft.user_id = ' . $user->data['user_id'] . ' AND ft.forum_id = f.forum_id'
		)
	),

	'WHERE'		=> 'fw.user_id = ' . $user->data['user_id'] . '
		AND f.forum_id = fw.forum_id',

	'ORDER_BY'	=> 'left_id'
);

$sql = $db->sql_build_query('SELECT', $sql_array);

Возможные значения первого параметра sql_build_query() — это SELECT или SELECT_DISTINCT. Как вы можете видеть, логика вполне очевидна. Например, для LEFT_JOIN просто добавьте другой массив, если вы хотите присоединить таблицы. Дополнительная выгода от использования этой конструкции состоит в том, что вы легко можете строить выражения запроса, основанные на условиях: например, вышеприведенный LEFT_JOIN необходим лишь тогда, когда включено отслеживание тем на стороне сервера; возможна небольшая корректировка:
$sql_array = array(
	'SELECT'	=> 'f.*',

	'FROM'		=> array(
		FORUMS_WATCH_TABLE	=> 'fw',
		FORUMS_TABLE		=> 'f'
	),

	'WHERE'		=> 'fw.user_id = ' . $user->data['user_id'] . '
		AND f.forum_id = fw.forum_id',

	'ORDER_BY'	=> 'left_id'
);

if ($config['load_db_lastread'])
{
	$sql_array['LEFT_JOIN'] = array(
		array(
			'FROM'	=> array(FORUMS_TRACK_TABLE => 'ft'),
			'ON'	=> 'ft.user_id = ' . $user->data['user_id'] . ' AND ft.forum_id = f.forum_id'
		)
	);

	$sql_array['SELECT'] .= ', ft.mark_time ';
}
else
{
	// Here we read the cookie data
}

$sql = $db->sql_build_query('SELECT', $sql_array);


2.iv. Оптимизация


Определения в цикле:

Всегда старайтесь оптимизировать ваши циклы, не допуская выполнения операций в условиях цикла, поскольку эти условия проверяются на каждом шаге цикла. Для присваиваний следует использовать содержательные имена. Пример:

// На каждом шаге цикла будет вызвана функция sizeof

for ($i = 0; $i < sizeof($post_data); $i++)
{
	do_something();
}

 // Вы можете присвоить неизменяемый результат прямо внутри определения цикла 

for ($i = 0, $size = sizeof($post_data); $i < $size; $i++)
{
	do_something();
}

Использование in_array(): 

Старайтесь избегать использования in_array() для очень больших массивов, а также старайтесь не помещать их в циклы, если обрабатываемый массив состоит из более чем 20 элементов. in_array() может отнять много времени на выполнение, а также использовать много ресурсов процессора. Для маленьких обработок это незаметно, но если используется большой массив в цикле, то такие обработки сами по себе могут занимать до нескольких секунд времени. Если вам требуется данная функциональность, то используйте isset() для ключей массивов, перенося значения на место ключей и наоборот. Например, вызов isset($array[$var])  сработает значительно быстрее, чем in_array($var, array_keys($array)) .


2.v. Общие рекомендации


Общие положения:

Никода не доверяйте пользовательскому вводу (это правило распространяется и на серверные переменные, и на cookies).

Старайтесь проверять значения, возвращенные функциями.

Старайтесь проверять переданные в функцию аргументы внутри этой самой функции.

Класс auth должен использоваться для всех проверок авторизации.

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


Переменные:

Используйте функцию request_var(), за исключением ввода или проверки единичного параметра. 

Функция request_var определяет устанавливаемый тип данных из второго параметра (который также определяет и значение по умолчанию). Если вам нужно получить переменную скалярного типа, то вы должны сообщить это явно функции request_var. Примеры:

// Старый метод, не используйте его

$start = (isset($HTTP_GET_VARS['start'])) ? intval($HTTP_GET_VARS['start']) : intval($HTTP_POST_VARS['start']);
$submit = (isset($HTTP_POST_VARS['submit'])) ? true : false;


// Используйте request var и определяйте значение по умолчанию (используйте правильный тип)
$start = request_var('start', 0);
$submit = (isset($_POST['submit'])) ? true : false;

// $start — целое, поэтому следующее использование request_var не допускается
$start = request_var('start', '0');

// Получение массива, ключи — целые, значение по умолчанию — 0
$mark_array = request_var('mark', array(0));

// Получение массива, ключи — строки, значение по умолчанию — 0
$action_ary = request_var('action', array('' => 0));

Проверки входа, перенаправление: 

Чтобы отобразить форму входа на форум, используйте login_forum_box($forum_data) , иначе — функцию login_box().

Функция login_box() может иметь первый параметр, указывающий на перенаправление. Указывайте пустую строку в том случае, если вы хотите перенаправить пользователя на его текущее местоположение, в остальных случаях не добавляйте $SID к строке перенаправления (например, на странице «Личный раздел > Вход» мы перенаправляем на главную страницу конференции, иначе пользователь был бы перенаправлен на страницу входа).


Ответственные операции:

Для ответственных операций всегда позволяйте пользователю подтвердить свое действие. Для отображения подтверждения используйте функцию confirm_box().


Операции изменения:

При выполнении операций, вносящих изменения в базу данных, например, при отправке сообщения, всегда проверяйте идентификатор формы, за исключением случая использования confirm_box(). Для этого вызывайте функции add_form_key() и check_form_key().

	add_form_key('my_form');

	if ($submit)
	{
		if (!check_form_key('my_form'))
		{
			trigger_error('FORM_INVALID');
		}
	}

 Строка, переданная в add_form_key(), должна совпадать со строкой, переданной в check_form_key(). Еще одним требованием правильной работы данной системы является добавление переменной {S_FORM_TOKEN} в шаблон.

Сессии: 

Сессии должны начинаться на каждой странице, максимально близко к началу, используя следующий код:

$user->session_begin();
$auth->acl($user->data);
$user->setup();
Вызов $user->setup()  может быть использован для добавления дополнительного языкового файла и своего стиля (используется в viewforum).


Ошибки и сообщения:

Все сообщения и ошибки должны отображаться при помощи вызов функции trigger_error(), используя соответствующий тип сообщения и языковую строку. Примеры:

trigger_error('NO_FORUM');
	
trigger_error($user->lang['NO_FORUM']);
	
trigger_error('NO_MODE', E_USER_ERROR);

Форматирование URL

Все URL, указывающие на внутренние файлы, должны предваряться переменной $phpbb_root_path. В администраторском разделе все URL, указывающие на внутренние файлы, должны предваряться переменной $phpbb_admin_path. Это гарантирует правильность пути, а также позволяет администратору просто переименовать папку администраторского раздела, оставляя сам раздел работающим как прежде (хотя некоторые ссылки перестанут работать и потребуются небольшие изменения в коде).

Функция append_sid(), появившаяся в версии 2.0.x, всё также доступна, хотя она не управляет изменениями в URL автоматически. Взгляните на документацию к коду, если вы хотите получить более подробные сведения по использованию append_sid(). Пример вызова функции append_sid():

append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=group&amp;g=' . $row['group_id'])

Использование общих функций: 

Из данных функций выбраны только какие-то определенные из-за индивидуальных предпочтений: кроме как единства в коде, других преимуществ они не дают.

  • Используйте sizeof  вместо count
  • Используйте strpos  вместо strstr
  • Используйте else if  вместо elseif
  • Используйте false  (в нижнем регистре) вместо FALSE
  • Используйте true  (в нижнем регистре) вместо TRUE


Завершение работы

Ваша страница должна вызывать либо page_footer() в конце, чтобы обработать вывод шаблона и завершить работу скрипта, или, по крайней мере, вызывать exit_handler(). Этот вызов необходим потому, что он вызывает внешние приложения, присоединенные к phpBB, необходимые для выполнения в конце работы скрипта.

Наверх

3. Работа со стилями


3.i. Файлы конфигурации стилей



Файлы конфигурации стилей представляют собой обычные списки «имя — значение» с информацией, необходимой для установки стиля. Похожие файлы конфигурации существуют для шаблонов, тем и наборов изображений. Все они построены по одному и тому же принципу и не будут представлены каждый отдельно. Стили могут использовать установленные компоненты при помощи элементов required_theme, required_template и required_imageset. Важная часть файла конфигурации стиля — это использование уникального имени.

        # Общая информация по данному стилю
        name = prosilver_duplicate
        copyright = © phpBB Group, 2007
        version = 3.0.3
        required_template = prosilver
        required_theme = prosilver
        required_imageset = prosilver
 

 3.ii. Общие правила работы со стилями



Шаблоны должны быть написаны в единообразном стиле. В подходящих случаях они должны быть основаны на существующих экземплярах, например, index, viewforum или viewtopic (сочетание которых образует ряд форм условий и переменных). Также обратите внимание, что правила написания кода применяются и к шаблонам там, где это возможно.

Внешний класс таблицы forumline заменен на tablebg.

При написании <table> порядок <table class="" cellspacing="" cellpadding="" border="" align=""> создает единство и позволяет каждому легко понять, какая таблица как будет выглядеть. То же самое относится и к большинству других тегов, для которых могут быть установлены дополнительные параметры; единство — главная цель в таких случаях.

Каждый элемент блочного уровня должен быть сдвинут на один символ табуляции, то же самое и для табличных элементов, таких как <tr> <td> и так далее, но положение элемента <table> и последующего (и закрывающего) <tr> должны быть друг под другом, в одной колонке. Конечно, это не распространяется на элементы div.

Не используйте <span> чаще, чем необходимо... CSS работает таким образом, что размеры текста зависят от родительского класса. Так что запись <span class="gensmall"><span class="gensmall">TEST</span></span>  приведет в итоге к очень маленькому тексту. Подобным же образом, не используйте span совсем, если другой элемент может содержать определение класса, например:

<td><span class="gensmall">TEST</span></td>
этот пример может быть записан гораздо проще:
<td class="gensmall">TEST</td>

Старайтесь использовать типы текстовых классов в соответствии с уже использущимися, например, не применяйте класс nav там, где viewtopic применяет gensmall.

Цвета и классы рядов таблиц теперь определяются в шаблонах, используйте переключатель IF S_ROW_COUNT, для примера взгляните в шаблон viewtopic или viewforum.

Помните, порядок блочных уровней важен... хотя не все страницы проходят проверку на совместимость стандарту XHTML 1.0 Strict, но мы работаем над этим.

Используйте стандартные значения cellpadding, равное 2, и cellspacing, равное 0, для внешних таблиц. Внутренние таблицы могут иметь значения от 0 до 3 или даже 4, в зависимости от прендазначения.

Используйте контейнер div и CSS для стилизации, а table — для представления данных.

Отдельные классы catXXXX и thXXX ушли в прошлое. При определении заголовочной ячейки просто используйте <th> вместо <th class="thHead">  и т. д. Подобным же образом и для категорий: не используйте <td class="catLeft"> , вместо этого — <td class="cat">  и т. д.

Старайтесь сохранять единство основных принципов разметки и использования классов, например, текст _EXPLAIN должен, как правило, располагаться ниже заголовка, который он объясняет, например {L_POST_USERNAME}<br /><span class="gensmall">{L_POST_USERNAME_EXPLAIN}</span> — это типичный способ предоставить поясняющий текст... могут быть и исключения, так что это не жесткое и не категоричное правило.

Старайтесь соблюдать отступы для шаблонных условий и других служебных выражений в соответствии с блоком, для которых они написаны.

здесь всё верно
<!-- BEGIN test -->
	<tr>
		<td>{test.TEXT}</td>
	</tr>
<!-- END test -->

это тоже верно:
<!-- BEGIN test -->
<tr>
	<td>{test.TEXT}</td>
</tr>
<!-- END test -->

такой способ дает немедленное понимание того, какая сущность будет выводиться циклом — решайте, как оформлять код, основываясь на легкости чтения и понимания.

Наверх

4. Работа с шаблонами


4.i. Общие правила работы с шаблонами


Именование файлов

Во-первых, теперь шаблоны имеют расширение «.html» вместо прежнего «.tpl». Это было сделано для того, чтобы упростить жизнь некоторым людям в отношении подсветки синтаксиса и т. д.


Переменные

Все переменные шаблона должны быть названы в соответствии с правилами (используя знаки подчеркивания вместо пробелов), языковые элементы должны предваряться приставкой L_, системные данные — S_, URL — U_, JavaScript URL — UA_, языковые строки, вставляемые в выражения JavaScript, — LA_, все остальные переменные должны быть представлены «как есть».

Шаблонные переменные L_* автоматически преобразовываются в соответствующий элемент языкового массива, если в коде этой переменной не присваивается (то есть не переопределяется) особое значение. Например, {L_USERNAME} преобразуется в $user->lang['USERNAME']. Шаблонные переменные LA_* обрабатываются таким же образом, но они экранируются для помещения в код JavaScript. Таким образом, уменьшится необходимость присваивать массу значений языковых переменных в модификациях.


Блоки, циклы

Основной цикл блочного уровня остается прежним и принимает следующий вид:

<!-- BEGIN loopname -->
	markup, {loopname.X_YYYYY}, etc.
<!-- END loopname -->
Чуть позже циклы будут описаны более подробно. Чтобы не надоедать вам, мы сначала рассмотрим условные конструкции, а также прочие выражения.


Подключение файлов

Есть одна вещь, которая существовала в 2.0.x, но не существует в 3.0.x, — это возможность присвоения шаблона переменной. Эта возможность использовалась (например) для вывода списка перехода на другой форум. Вместо этого (может быть, это лучше, а может быть, и нет, но это гораздо более гибкая возможность) у нас сейчас есть INCLUDE. Выглядит просто:
<!-- INCLUDE filename -->

Вы заметите, что в большинстве шаблонов 3.0 код начинается со строки <!-- INCLUDE overall_header.html --> или <!-- INCLUDE simple_header.html --> и т. д. В 2.0.x выбор того, какой заголовок использовать, определялся прямо в коде. В 3.0.x дизайнер шаблонов может сам определять то, что он хочет. Обратите внимание, что вы можете создать новые шаблоны (то есть отличные от тех, что поставляются в наборе по умолчанию), используя систему, и подключить их как вам угодно... возможно, пригодится при создании общей панели «меню» или чего-то в этом роде. Нет нужды изменять массу файлов, как это было в 2.0.x.


PHP

Спорным решением видится возможность подключать PHP прямо внутри шаблонов. Это можно сделать, заключив PHP в соответствующие теги:

<!-- PHP -->
	echo "привет!";
<!-- ENDPHP -->
Вы также можете подключить PHP из внешнего файла:
<!-- INCLUDEPHP somefile.php -->

он будет подключен и выполнен прямо здесь.

Учтите, что дизайнерам настоятельно рекомендуется не использовать PHP. Возможность подключать сырой PHP была предоставлена, главным образом, для того, чтобы конечные пользователи смогли внедрять код баннеров и пр. без изменения многочисленных файлов (как в 2.0.x). Эта возможность не предполагает общее использование... поэтому www.phpbb.com не создает наборы шаблонов, включающих код PHP. И, по умолчанию, в шаблонах отключена обработка PHP (администратору потребуется специально включать обработку PHP в шаблонах).


Конструкции условий и управления

Наиболее важным нововведением в 3.0.x являются конструкции условий и управления: «если выполняется такое-то условие, делаем это, иначе делаем то». Внедренная система очень похожа на Smarty. Это может поначалу ввести в заблуждение некоторых людей, но такая система имеет огромный потенциал, большую гибкость, требуя лишь небольшого представления о себе. В самых простых случаях такие конструкции имеют следующий вид:

<!-- IF выражение -->
	разметка
<!-- ENDIF -->
«выражение» может принимать самые разнообразные формы, например:
<!-- IF loop.S_ROW_COUNT is even -->
	разметка
<!-- ENDIF -->
Этот пример выведет разметку, если переменная S_ROW_COUNT на текущем шаге цикла имеет в качестве значения четное число (то есть если все выражение истинно — TRUE). Вы можете использовать разные способы сравнения (стандартные, а также соответствующие им текстовые эквиваленты, указанные в квадратных скобках), включая (not, or, and, eq, neq, is должны быть использованы, если возможно, для улучшенной читаемости):
== [eq]
!= [neq, ne]
<> (same as !=)
!== (not equivalent in value and type)
=== (equivalent in value and type)
> [gt]
< [lt]
>= [gte]
<= [lte]
&& [and]
|| [or]
% [mod]
! [not]
+
-
*
/
,
<< (побитовый сдвиг влево)
>> (побитовый сдвиг вправо)
| (побитовое «или»)
^ (побитовое исключающее «или» — xor)
& (побитовое «и»)
~ (побитовое отрицание)
is (используется для связки операций сравнения)
Круглые скобки также используются для наглядности правил выполнения приоритета операций. Кроме того, определены некоторые основные типы сравнений:
even (четное)
odd (нечетное)
div (делится без остатка)
Помимо простого использования IF, вы можете составлять цепочки сравнений:
<!-- IF выражение1 -->
	разметка
<!-- ELSEIF выражение2 -->
	разметка
	.
	.
	.
<!-- ELSEIF выражениеN -->
	разметка
<!-- ELSE -->
	разметка
<!-- ENDIF -->
Каждое выражение будет проверено по очереди, и соответствующий результат будет выведен при обнаружении соответствия. Необязательно каждый раз использовать ELSEIF: ELSE может применяться самостоятельно для соответствия «всем остальным случаям».

Итак, что вы можете сделать при помощи всего этого? Хорошо, возьмем для примера окрашивание рядов таблицы в viewforum. В 2.0.x цвета рядов были предопределены в исходном коде или как row color1, row color2, или row class1, row class2. В 3.0.x это возложено на шаблон, поначалу это может немного привести в растерянность, но запомните весь ход управления от начала до конца, и вы поймете, что это не так сложно:
<table>
	<!-- IF loop.S_ROW_COUNT is even -->
		<tr class="row1">
	<!-- ELSE -->
		<tr class="row2">
	<!-- ENDIF -->
	<td>ПРИВЕТ!</td>
</tr>
</table>
Этот код выведет ячейку ряда таблицы, используя класс row1, если номер ряда четный, и класс row2, если нечетный. Параметр S_ROW_COUNT присваивается циклам по умолчанию. Другой пример выглядит так: 
<table>
	<!-- IF loop.S_ROW_COUNT > 10 -->
		<tr bgcolor="#FF0000">
	<!-- ELSEIF loop.S_ROW_COUNT > 5 -->
		<tr bgcolor="#00FF00">
	<!-- ELSEIF loop.S_ROW_COUNT > 2 -->
		<tr bgcolor="#0000FF">
	<!-- ELSE -->
		<tr bgcolor="#FF00FF">
	<!-- ENDIF -->
	<td>привет!</td>
</tr>
</table>
Этот пример выведет ячейку ряда пурпурным цветом для первых двух рядов, синим для рядов от 2 до 5, зеленым для рядов от 5 до 10 и красным для всех остальных. Таким образом, вы, например, сможете воспроизвести красивый эффект плавного перехода цвета («градиент»).

Что еще вы сможете сделать? Вы можете использовать IF для выполнения общих проверок, например, выполнил ли пользователь вход на форум:
<!-- IF S_USER_LOGGED_IN -->
	разметка
<!-- ENDIF -->
Этот способ является заменой старому в 2.0.x, когда использовался массив нулевой длины и BEGIN/END (полная чушь!).


Расширенный синтаксис для блоков и циклов

Вернемся к нашим циклам — они могут быть расширены следующими дополнениями. Во-первых, вы можете установить начальную и конечную точку цикла. Например:

<!-- BEGIN loopname(2) -->
	разметка
<!-- END loopname -->

Цикл начнет выполняться с третьего шага (заметьте, что отсчет индексов начинается с нуля). Вот доступные расширения: 

loopname(2) : Цикл начинает выполнение с третьего шага
loopname(-2) : Цикл начинает выполнение со второго с конца шага
loopname(3,4) : Цикл начинает выполнение с четвертого шага и заканчивает на пятом
loopname(3,-4) : Цикл начинает выполнение с четвертого шага и заканчивает на четвертом с конца

Следующим расширением к BEGIN является BEGINELSE:
<!-- BEGIN loop -->
	разметка
<!-- BEGINELSE -->
	разметка
<!-- END loop -->

Этот код выводит разметку между BEGINELSE и END в том случае, если цикл не содержит значений. Такое бывает полезно для форумов, не содержащих тем (например)... частично это расширение заменяет существующий элемент управления «switch_» (это последнее, что было заменено условными конструкциями).

Другой способ проверки, содержит ли цикл значения, — это предварение названия цикла точкой:
<!-- IF .loop -->
	<!-- BEGIN loop -->
		разметка
	<!-- END loop -->
<!-- ELSE -->
	разметка
<!-- ENDIF -->

Вы даже можете проверить количество элементов в цикле, используя данное обозначение в условии IF:
<!-- IF .loop > 2 -->
	<!-- BEGIN loop -->
		разметка
	<!-- END loop -->
<!-- ELSE -->
	разметка
<!-- ENDIF -->

Вложенные циклы требуют, чтобы выражения в условиях предварялись названиями всех циклов, начиная от внешнего и заканчивая самым внутренним. Вот наглядный пример:
<!-- BEGIN firstloop -->
	{firstloop.MY_VARIABLE_FROM_FIRSTLOOP}

	<!-- BEGIN secondloop -->
		{firstloop.secondloop.MY_VARIABLE_FROM_SECONDLOOP}
	<!-- END secondloop -->
<!-- END firstloop -->

Иногда необходимо выйти из вложенных циклов, чтобы иметь возможность вызывать другой цикл в пределах текущей итерации. Звучит несколько сбивающе с толку и не используется часто. Следующий (довольно сложный) пример очень хорошо это демонстрирует — он также показывает проверку на первый и последний ряд цикла (я объясню пример подробно ниже):
<!-- BEGIN l_block1 -->
	<!-- IF l_block1.S_SELECTED -->
		<strong>{l_block1.L_TITLE}</strong>
		<!-- IF S_PRIVMSGS -->

			<!-- the ! at the beginning of the loop name forces the loop to be not a nested one of l_block1 -->
			<!-- BEGIN !folder -->
				<!-- IF folder.S_FIRST_ROW -->
					<ul class="nav">
				<!-- ENDIF -->

				<li><a href="/{folder.U_FOLDER}">{folder.FOLDER_NAME}</a></li>

				<!-- IF folder.S_LAST_ROW -->
					</ul>
				<!-- ENDIF -->
			<!-- END !folder -->

		<!-- ENDIF -->

		<ul class="nav">
		<!-- BEGIN l_block2 -->
			<li>
				<!-- IF l_block1.l_block2.S_SELECTED -->
					<strong>{l_block1.l_block2.L_TITLE}</strong>
				<!-- ELSE -->
					<a href="/{l_block1.l_block2.U_TITLE}">{l_block1.l_block2.L_TITLE}</a>
				<!-- ENDIF -->
			</li>
		<!-- END l_block2 -->
		</ul>
	<!-- ELSE -->
		<a class="nav" href="/{l_block1.U_TITLE}">{l_block1.L_TITLE}</a>
	<!-- ENDIF -->
<!-- END l_block1 -->
Давайте для начала сконцентрируем внимание на этой части примера:
<!-- BEGIN l_block1 -->
	<!-- IF l_block1.S_SELECTED -->
		разметка
	<!-- ELSE -->
		<a class="nav" href="/{l_block1.U_TITLE}">{l_block1.L_TITLE}</a>
	<!-- ENDIF -->
<!-- END l_block1 -->

Здесь мы открываем цикл l_block1 и делаем определенные вещи, если значение S_SELECTED на текущей итерации цикла истинно, иначе мы записываем ссылку и заголовок блока. Здесь вы видите ссылку на {l_block1.L_TITLE} — помните, что все переменные L_* автоматически получают значения соответствующих элементов языкового массива? Это верно, но не в пределах циклов. Переменной L_TITLE внутри цикла l_block1 присваивается значение в коде.

Теперь давайте получше взглянем на такой код:
<!-- BEGIN l_block1 -->
.
.
	<!-- IF S_PRIVMSGS -->

		<!-- BEGIN !folder -->
			<!-- IF folder.S_FIRST_ROW -->
				<ul class="nav">
			<!-- ENDIF -->

			<li><a href="/{folder.U_FOLDER}">{folder.FOLDER_NAME}</a></li>

			<!-- IF folder.S_LAST_ROW -->
				</ul>
			<!-- ENDIF -->
		<!-- END !folder -->

	<!-- ENDIF -->
.
.
<!-- END l_block1 -->
Выражение <!-- IF S_PRIVMSGS -->, естественно, проверяет глобальную переменную, а не ту, что внутри цикла, поскольку название цикла здесь не указано. Так что, если S_PRIVMSGS истинно, то мы выводим показанную разметку. Далее мы видим выражение <!-- BEGIN !folder -->. Восклицательный знак указывает механизму шаблонов отрабатывать по главному циклу folder. Так что, мы сейчас внутри цикла folder, а при указании <!-- BEGIN folder --> мы были бы автоматически внутри цикла l_block1.folder, как это происходит в случае с l_block2:
<!-- BEGIN l_block1 -->
.
.
	<ul class="nav">
	<!-- BEGIN l_block2 -->
		<li>
			<!-- IF l_block1.l_block2.S_SELECTED -->
				<strong>{l_block1.l_block2.L_TITLE}</strong>
			<!-- ELSE -->
				<a href="/{l_block1.l_block2.U_TITLE}">{l_block1.l_block2.L_TITLE}</a>
			<!-- ENDIF -->
		</li>
	<!-- END l_block2 -->
	</ul>
.
.
<!-- END l_block1 -->

Видите разницу? Цикл l_block2 — член цикла l_block1, а цикл folder — это основной цикл.

Теперь вернемся к главному циклу loop:
<!-- IF folder.S_FIRST_ROW -->
	<ul class="nav">
<!-- ENDIF -->

<li><a href="/{folder.U_FOLDER}">{folder.FOLDER_NAME}</a></li>

<!-- IF folder.S_LAST_ROW -->
	</ul>
<!-- ENDIF -->

Вы могли задаться вопросом, что означает проверка S_FIRST_ROW и S_LAST_ROW. Если вы еще не догадались, то поясним: это проверка на первую итерацию цикла (S_FIRST_ROW) и последнюю (S_LAST_ROW). Это может пригодиться довольно часто, если вы хотите открыть или закрыть элементы дизайна, такие как вышеприведенный список. Представим себе цикл folder, сформированный тремя итерациями; он будет работать следующим образом:
<ul class="nav"> <!-- записывается на первой итерации -->
	<li>first element</li> <!-- записывается на первой итерации -->
	<li>second element</li> <!-- записывается на второй итерации -->
	<li>third element</li> <!-- записывается на третьей итерации -->
</ul> <!-- записывается на третьей итерации -->

Как видно, записываются все три элемента, равно как и разметка для первой итерации и последней. Иногда вы можете опускать общую разметку, например:
<!-- IF folder.S_FIRST_ROW -->
	<ul class="nav">
<!-- ELSEIF folder.S_LAST_ROW -->
	</ul>
<!-- ELSE -->
	<li><a href="/{folder.U_FOLDER}">{folder.FOLDER_NAME}</a></li>
<!-- ENDIF -->

такой код создаст такую разметку:
<ul class="nav"> <!-- записывается на первой итерации -->
	<li>second element</li> <!-- записывается на второй итерации -->
</ul> <!-- записывается на третьей итерации -->

Просто всегда помните, что обработка происходит сверху вниз.


Формы

Если форма используется для нетривиальных операций (например, нечто большее, чем форма перехода на другой форум), то она должна содержать шаблонную переменную {S_FORM_TOKEN}.

<form method="post" id="mcp" action="{U_POST_ACTION}">

	<fieldset class="submit-buttons">
		<input type="reset" value="{L_RESET}" name="reset" class="button2" /> 
		<input type="submit" name="action[add_warning]" value="{L_SUBMIT}" class="button1" />
		{S_FORM_TOKEN}
	</fieldset>
</form>


4.ii. Наследование шаблонов



Если новый шаблон основывается на существующем, то необязательно предоставлять все файлы шаблонов. Можно объявить шаблон «наследником» («inheriting») в файле конфигурации шаблона.

Ограничение состоит в том, что базовый стиль должен быть установлен и должен быть полон, то есть он не наследуется сам по себе.

Эффект в использовании такой системы состоит в том, что механизм шаблонов будет использовать файлы нового шаблона, если они имеются, и обращаться к файлам базового шаблона в другом случае. Объявление стиля наследующим также позволяет ему использовать некоторые настройки конфигурации от базового стиля, особенно хранение в базе данных.

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

        # Общая информация по данному шаблону
        name = inherits
        copyright = © phpBB Group, 2007
        version = 3.0.3

        # Определение другого битового поля шаблона
        template_bitfield = lNg=

        # Наследуем?
        inherit_from = prosilver

Наверх

5. Наборы символов и кодировки


Что такое Unicode, UCS и UTF-8?

Универсальный набор символов (Universal Character Set — UCS), описанный в ISO/IEC 10646, состоит из большого числа символов. Каждый из них имеет уникальное имя и кодовый указатель, являющийся целым числом. Unicode, являющийся отраслевым стандартом, дополняет универсальный набор символов дальнейшей информацией о свойствах символов и их альтернативных кодировках. Более подробную информацию по Unicode можно найти на веб-сайте Unicode Consortium. Одна из кодировок Unicode — это 8-битный формат преобразования Unicode (Unicode Transformation Format — UTF-8). Она кодирует символы, используя до четырех байтов, нацеливаясь на совместимость с Американским стандартным кодом обмена информацией (American Standard Code for Information Interchange), являющийся 7-битной кодировкой относительно небольшого подмножества UCS.


Использование Unicode в phpBB

К сожалению, PHP до версии 6 не имеет встроенной поддержки Unicode. Большинство функций просто рассматривают строку как последовательность байтов, предполагая, что каждый символ занимает ровно один байт. Такое поведение, тем не менее, позволяет хранить закодированный при помощи UTF-8 текст в строках PHP, но многие операции со строками приведут к неожидаемым результатам. Чтобы обойти эту проблему, мы написали несколько альтернативных функций по отношению к родным строковым операциям PHP, эти функции используют кодовые указатели вместо байтов. Они расположены в файле /includes/utf/utf_tools.php. Также они рассмотрены в Документации по исходному коду phpBB3. Большое количество родных функций PHP работают с UTF-8, если соблюдать определенные ограничения. Например, explode работает верно, если первый и последний символы строки-разделителя являются ASCII-символами.

Из кодировок символов phpBB использует только ASCII и UTF-8. Точнее сказать, все символы закодированы в UTF-8, поскольку ASCII является подмножеством UTF-8. Единственное исключение из этого правила составляют участки кода, которые работают с внешними системами, использующими другие кодировки и наборы символов. Такие внешние данные должны быть преобразованы в UTF-8 при помощи функции utf8_recode(), имеющейся в phpBB. Она поддерживает множество других наборов символов и кодировок, полный список приведен ниже.

При помощи request_var() вы можете позволить либо все символы UCS в пользовательском вводе, либо ограничить ввод только ASCII-символами. Эта возможность управляется третьим параметром функции $multibyte. Вы должны включать многобайтные кодировки в сообщениях, ЛС, заголовках тем, названиях форумов и так далее. Однако, это не требуется для служебных нужд, например, для переменной $mode, которая, как бы то ни было, должна содержать только предопределенный список ASCII-строк.

// входная строка, которая должна содержать многобайтный набор символов
$_REQUEST['multibyte_string'] = 'Kase';
// печатаем запрошенную переменную как строка UTF-8, включив многобайтный набор символов
echo request_var('multibyte_string', '', true);
// печатаем запрошенную переменную как ASCII-строку
echo request_var('multibyte_string', '');

Этот кусок кода выдаст следующий результат:
Kase
K??se

Нормализация Unicode

Если вы получаете пользовательский ввод с многобайтным набором символов, вы дополнительно должны нормализовать строки при помощи функции utf8_normalize_nfc() прежде чем работать с ними. Это необходимо для уверенности в том, что два одинаковых символа могут быть представлены в двоичном виде строго одинаково. Например, символ A может быть представлен и как U+00C5 (LATIN CAPITAL LETTER A WITH RING ABOVE), и как U+212B (ANGSTROM SIGN). phpBB использует каноническую декомпозицию с последующей канонической композицией (Normalization Form Canonical Composition, NFC) для всего текста. Итак, верный вариант вышеприведенного примера будет выглядеть так:

$_REQUEST['multibyte_string'] = 'Kase';

// нормализуем многобайтные строки
echo utf8_normalize_nfc(request_var('multibyte_string', '', true));
// ASCII-строки не нуждаются в нормализации
echo request_var('multibyte_string', '');

Приведение регистра

Теперь регистронезависимое сравнение строк невозможно при помощи strtolower или strtoupper, поскольку некоторые символы имеют несколько форм нижнего регистра или несколько форм верхнего регистра, в зависимости от их положения в слове. Функции utf8_strtolower и utf8_strtoupper испытывают такую же проблему, так что они могут использоваться только для отображения верхнерегистровой или нижнерегистровой версии строки, но они бесполезны при выполнении регистронезависимых сравнений. Итак, вместо этого вам нужно использовать приведение регистра, что даст вам регистронезависимую версию строки, так что вы сможете выполнять регистронезависимые сравнения. Нормализованная по NFC строка может быть приведена по регистру при помощи utf8_case_fold_nfc().

// Плохо — строки могут быть одинаковыми, даже если strtolower различаются

if (strtolower($string1) == strtolower($string2))
{
	echo '$string1 и $string2 равны либо различны по регистру';
}
// Хорошо — Приведение регистров действительно регистронезависимо
if (utf8_case_fold_nfc($string1) == utf8_case_fold_nfc($string2))
{
	echo '$string1 и $string2 равны либо различны по регистру';
}

Распознавание схожих имен

phpBB предлагает специальный метод utf8_clean_string, который используется для проверки того, что идентификаторы строк уникальны. Этот метод использует совместную декомпозицию с последующей канонической композицией (Normalization Form Compatibility Composition, NFKC) вместо NFC и заменяет символы, выглядящие схожим образом, на особый экземпляр из класса эквивалентов. Этот метод на данный момент используется для имен пользователей и названий групп, чтобы избежать путаницу в том случае, если имена и названия выглядят схожим образом.

Наверх

6. Правила перевода (интернационализация, локализация)


6.i. Стандартизация

Причина:

phpBB — один из наиболее широко переведенных проектов с открытым исходным кодом, текущая стабильная версия доступна в более чем 60 локализациях. Пока специальный подход к наименованию языковых пакетов работает, для phpBB3 и далее мы надеемся сделать этот процесс более разумным, что позволит улучшить взаимодействие текущих и будущих веб-обозревателей.


Кодировка:

В phpBB3 кодировкой для результата выполнения программы является UTF-8, универсальная кодировка символов, созданная Unicode Consortium, которая является надможеством US-ASCII и ISO-8859-1. Использование одного набора символов, который одновременно поддерживает все скрипты, в прошлом бы потребовавшие различные кодировки (например: с ISO-8859-1 по ISO-8859-15 (латинский, греческий, кириллический, тайский, иврит, арабский); GB2312 (упрощенный китайский); Big5 (традиционный китайский), EUC-JP (японский), EUC-KR (корейский), VISCII (вьетнамский); и так далее), избавляет от необходимости преобразовывать текст между разными кодировками, а также улучшает доступность многоязычных форумов.

Смысл в том, что языковые файлы phpBB теперь должны быть сохранены в кодировке UTF-8, а также они не должны содержать BOM из соображений совместимости с версиями PHP без поддержки Unicode. Для них на форумах используется латинский набор символов (то есть большинство европейских языков), данное изменение будет прозрачным, поскольку UTF-8 является надможеством US-ASCII и ISO-8859-1.


Метка языка:

Комитет IETF недавно опубликовал RFC 4646 для меток, идентифицирующих языки; этот документ, в сочетании с RFC 4647, вытесняет устаревший RFC 3006 и еще более старый RFC 1766. RFC 4646 использует ISO 639-1/ISO 639-2, ISO 3166-1 alpha-2, ISO 15924 и UN M.49 для определения меток языков. Каждая метка составлена из подметок, которые не являются регистрозависимыми, а также могут быть пустыми.

Порядок следования подметок, в случае если ни одна из них не пуста, следующий: язык-письмо-регион-разновидность-расширение-частное применение. Если какая-то подметка пустая, соответствующий дефис также опускается. Таким образом, языковая метка для английского языка будет en, а не en-----.

Большинство языковых меток состоит из двух- или трехбуквенных языковых подметок (согласно ISO 639-1/ISO 639-2). Иногда следом идет двухбуквенная или трехзначная цифровая метка региона (согласно ISO 3166-1 alpha-2 или UN M.49). Вот некоторые примеры:

Языковая метка

Описание Компоненты-подметки
en Английский язык
mas Масаи язык
fr-CA Французский, употребляемый в Канаде язык+регион
en-833 Английский, употребляемый на острове Мэн язык+регион
zh-Hans Китайский (упрощенное письмо) язык+письмо
zh-Hant-HK Китайский (традиционное письмо), употребляемый в Гон-Конге язык+письмо+регион
de-AT-1996 Немецкий, употребляемый в Австрии, с орфографией 1996 года язык+регион+разновидность

Основная цель языковых меток — это сообщить необходимую полезную отличительную информацию, в то же время используя как можно более короткие имена. Например, использование en, fr и ja в отличие от en-GB, fr-FR и ja-JP, поскольку мы знаем, что английский, французский и японский языки — родные для Великобритании, Франции и Японии соответственно.

Далее рассмотрим код письма языка согласно ISO 15924, когда его следует использовать, а когда — нет. Например, в то время как en-Latn синтаксически верно для описания англоязычного текста, написанного латинским письмом, на самом деле написание слов на английском языке выполняется более или менее исключительно на английской письменности. Для таких языков, как английский, использующих в своей записи единственный вид письма, реестр языковых подметок IANA содержит поле «Без письма», означающее, что код письма должен опускаться, если особая языковая метка не требует особый код письма. Некоторые языки используют более одного вида письменности, и в таких случаях код письма рекомендуется, поскольку конечный пользователь может читать свой язык на одной системе письма, но не может на другой. Приведем несколько примеров:

Языковая метка Описание Компоненты-подметки
en-Brai Английский, письмо Брайля язык+письмо
en-Dsrt Английский, письмо Дезерет (Мормон) язык+письмо
sr-Latn Сербский язык, латинское письмо язык+письмо
sr-Cyrl Сербский язык, кириллическое письмо язык+письмо
mn-Cyrl Монгольский язык, кириллическое письмо язык+письмо
mn-Phag Монгольский шрифт, письмо Пагба-ламы язык+письмо
az-Cyrl-AZ Азербайджанский язык, кириллическое письмо, употребление в Азербайджане язык+письмо+регион
az-Latn-AZ Азербайджанский язык, латинское письмо, употребление в Азербайджане язык+письмо+регион
az-Arab-IR Азербайджанский язык, арабское письмо, употребление в Иране язык+письмо+регион

Использование трехзначного цифрового кода UN M.49 вместо двухбуквенного ISO 3166-1 alpha-2 применимо в случае, если требуется макрогеографический объект и/или ISO 3166-1 alpha-2 неоднозначно.

Примеры указания английского языка с макрогеографическим регионом:

ISO 639-1/ISO 639-2 + ISO 3166-1 alpha-2 ISO 639-1/ISO 639-2 + UN M.49 (Примеры макрорегионов)
en-AU Английский (Австралия) en-053 Английский (Австралия & Новая Зеландия) en-009 Английский (Океания)
en-NZ Английский (Новая Зеландия)
en-FJ Английский (Фиджи) en-054 Английский (Меланезия)

Примеры указания испанского языка с макрогеографическим регионом:

ISO 639-1/ISO 639-2 + ISO 3166-1 alpha-2 ISO 639-1/ISO 639-2 + UN M.49 (Примеры макрорегионов)
es-PR Испанский (Пуэрто-Рико) es-419 Испанский (Латинская Америка & Карибские о-ва) es-019 Испанский (Америка)
es-HN Испанский (Гондурас)
es-AR Испанский (Аргентина)
es-US Испанский (США) es-021 Испанский (Северная Америка)

Примеры мест, где ISO 3166-1 alpha-2 неоднозначно и потому UN M.49 предпочтительнее:

CS назначение до 1994 CS назначение после 1994
CS Чехословакия (ISO 3166-1)
200 Чехословакия (UN M.49)
CS Сербия & Черногория (ISO 3166-1)
891 Сербия& Черногория (UN M.49)
CZ Чешская Республика (ISO 3166-1)
203 Чешская Республика (UN M.49)
SK Словакия (ISO 3166-1)
703 Словакия (UN M.49)
RS Сербия (ISO 3166-1)
688 Сербия (UN M.49)
ME Черногория (ISO 3166-1)
499 Черногория (UN M.49)


Макроязыки и наддиалекты:

В RFC 4646 предвидятся возможности, которые должны появиться в ISO 639-3 (пока это проект), где будет предоставлено наиболее полное перечисление всех языков, включая живые, мертвые, древние и искусственные языки, как хорошо распространенные, так и редкие, и бесписьменные. Новая особенность документа ISO 639-3, в сравнении с двумя предыдущими редакциями, — это концепция макроязыков, примерами которых служат арабский и китайский языки. В таких случаях их соответствующие коды: ar и zh — очень неясны в плане используемого диалекта/наддиалекта или, возможно, более краткого классического варианта, который может быть различным для всех пользователей, кроме очень образованных. Для таких макроязыков рекомендуется использование метки подъязыка в качестве суффикса к метке макроязыка, например:

Языковая метка Описание Компоненты-подметки
zh-cmn Мандаринский диалект (Путунхуа/Гоюй) китайского языка макроязык+подъязык
zh-yue Диалект Юэ (кантонский) китайского языка макроязык+подъязык
zh-cmn-Hans Мандаринский диалект (Путунхуа/Гоюй) китайского языка, упрощенное письмо макроязык+подъязык+письмо
zh-cmn-Hant Мандаринский диалект (Путунхуа/Гоюй) китайского языка, традиционное письмо макроязык+подъязык+письмо
zh-nan-Latn-TW Южноминьский диалект (Холо) китайского языка, латинское письмо (романизация [латинизация] POJ), употребление в Тайвани макроязык+подъязык+письмо+регион

6.ii. Другие положения


Нормализация языковых меток для phpBB:

Для phpBB языковые метки не используются в их сырой форме, вместо этого они преобразуются к нижнему регистру, затем дефисы -заменяются на знаки подчеркивания _, где необходимо. Несколько примеров:

Сырая языковая метка Описание Значение USER_LANG
в ./common.php
Название директории языкового пакета в /language/
en Британский английский en en
de-AT Немеский, употребляемый в Австрии de-at de_at
es-419 Испанский, употребляемый в Латинской Америке и на Карибских островах en-419 en_419
zh-yue-Hant-HK Кантонский, с традиционным письмом, употребляемый в Гон-Конге zh-yue-hant-hk zh_yue_hant_hk

Как использовать iso.txt:

iso.txt — это небольшой текстовый файл в кодировке UTF-8, состоящий из трех строк:

  1. Английское наименование языка
  2. Местное название языка
  3. Информация об авторах

iso.txt автоматически создается системой отправки языковых пакетов на phpBB.com. Вам не нужно создавать этот файл самим, если вы планируете публиковать свой языковой пакет на phpBB.com, но имейте в виду, что phpBB сам по себе требует наличия этого файла.

Вследствие того что метки языков сами по себе подразумевают их чтение машиной, они могут быть непонятны и запутаны для человека, поэтому требуется содержательное описание в iso.txt. В то время как обозначение en-US довольно легко понимается как «Английский язык, употребляемый в Соединенных Штатах», de-CH уже более сложно, соответственно, меньше людей знают, что de обозначает «Deutch», немецкое слово «Немецкий», и CH является аббревиатурой официального латинского названия Швейцарии «Confoederatio Helvetica».

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

Сырая языковая меткаv Описание на английском языке в iso.txt Описание
en British English Британский английский
en-US English (United States) Английский (Соединенные Штаты)
en-053 English (Australia & New Zealand) Английский (Австралия и Новая Зеландия)
de German Немецкий
de-CH-1996 German (Switzerland, 1996 orthography) Немецкий (Швейцария, орфография 1996)
gws-1996 Swiss German (1996 orthography) Швейцарский немецкий (орфография 1996)
zh-cmn-Hans-CN Mandarin Chinese (Simplified, Mainland China) Мандаринский китайский (упрощенный, Континентальный Китай)
zh-yue-Hant-HK Cantonese Chinese (Traditional, Hong Kong) Кантонский китайский (традиционный, Гон-Конг)

Для описаний на местных языках просто переводите английский вариант, но сохраняя, где возможно, пунктуацию, применимую к вашему местоположению, принимая во внимание, что язык вообще использует пунктуацию.


Рассмотрение двунаправленного Unicode:

Поскольку phpBB теперь использует UTF-8, все переводчики должны принять во внимание, что определенные строки могут отрображаться, когда направление письма в документе или противоположно обычному, или неопределено.

Различные управляющие символы Unicode для двунаправленного текста и их HTML-эквивалетны следующие:

Аббревиатура символа Unicode Код-указатель Unicode Название символа Unicode Эквивалетнтный элемент HTML Сырой символ(заключен между '')
LRM U+200E Left-to-Right Mark &lrm; '?'
RLM U+200F Right-to-Left Mark &rlm; '?'
LRE U+202A Left-to-Right Embedding dir="ltr" '?'
RLE U+202B Right-to-Left Embedding dir="rtl" '?'
PDF U+202C Pop Directional Formatting </bdo> '?'
LRO U+202D Left-to-Right Override <bdo dir="ltr"> '?'
RLO U+202E Right-to-Left Override <bdo dir="rtl"> '?'

Для файла iso.txt направление текста может быть явно задано при помощи специальных символов Unicode, используя любой из трех способов: символы Left-to-Right/Right-to-Left Mark/Embedding/Override — без них порядок следования символов может быть неверен, и т. д.:

Направление Просмотр символов Отображение локализованного написания в iso.txt Порядок
dir="ltr" English (Australia & New Zealand) English (Australia & New Zealand) Верный
dir="rtl" English (Australia & New Zealand) English (Australia & New Zealand) Неверный
dir="rtl" с LRM English (Australia & New Zealand)U+200E English (Australia & New Zealand) Верный
dir="rtl" с LRE & PDF U+202AEnglish (Australia & New Zealand)U+202C English (Australia & New Zealand) Верный
dir="rtl" с LRO & PDF U+202DEnglish (Australia & New Zealand)U+202C English (Australia & New Zealand) Верный

При выборе того, какой из трех способов использовать, в большинстве случаев достаточно поместить «сильные» символы LRM или RLM, чтобы полностью заключить в себе неоднозначный символ пунктуации и, таким образом, заставить наследовать верное направление письма.

В некоторых случаях могут быть смешанные виды письма (слева направо и справа налево), так что использование LRE и RLE совместно с PDF может быть уместно. Наконец, в очень редких случаях, где направление должно быть выставлено принудительно, используйте LRO и RLO вместе с PDF.

За дальнейшей информацией по техническим приемам авторинга двунаправленного текста обращайтесь к руководству W3C по техническим приемам для XHTML-страниц с двунаправленным текстом.


Работа с заполнителями:

Поскольку phpBB переводится на языки с различными правилами порядка следования слов, в отличие от английского, то рассмотрим особые значения, которые могут встречаться. Возьмем для примера чрезвычайно простую строку «Страница X из Y», которая на английском языке может быть закодирована следующим образом:

	...
'PAGE_OF'	=>	'Page %s of %s',
		/* Просто делаем замену так, как она
		следует в тексте с надеждой, что порядок будет верным */
	...

 …более ясный способ показать явно порядок замены будет выглядеть так:
	...
'PAGE_OF'	=>	'Page %1$s of %2$s',
		/* Явное указание порядка замены,
		даже если на английском порядок был бы таким же */
	...

 Зачем вообще заморачиваться по этому поводу? Потому что для некоторых языков, если транслитерировать строку обратно на английский, может получится нечто подобное:
	...
'PAGE_OF'	=>	'Total of %2$s pages, currently on page %1$s',
		/* Явное указание порядка замены,
		но для английского языка получилось все наоборот,
		так как общее количество стоит на первом месте */
	...

6.iii. Стиль написания


Разные советы:

Поскольку языковые файлы — это PHP-файлы, в которых разные строки для phpBB хранятся внутри массива и которые по очереди используются для отображения на HTML-странице, правила синтаксиса для обоих типов должны соблюдаться. Потенциально проблематичные символы: ' (одиночная прямая кавычка/апостроф), " (двойная прямая кавычка), < (знак «меньше»), > (знак «больше») и & (амперсанд).

// Неверно — Неэкранированный символ одиночной прямой кавычки (апострофа) вызовет ошибку синтаксического анализа PHP

	...
'CONV_ERROR_NO_AVATAR_PATH'
	=>	'Замечание для разработчика: вы должны указать $convertor['avatar_path'] для использования %s.',
	...

 // Правильно — Литералы с одиночными прямыми кавычками должны быть экранированы обратной косой чертой: \
	...
'CONV_ERROR_NO_AVATAR_PATH'
	=>	'Замечание для разработчика: вы должны указать $convertor[\'avatar_path\'] для использования %s.',
	...

 Тем не менее, так как phpBB3 работает исключительно в кодировке UTF-8, мы можем возыметь с этого преимущество и не задумываться об экранировании прямых одиночных кавычек:

// Неверно — Неэкранированная прямая одиночная кавычка (апостроф) вызовет ошибку синтаксического анализа PHP
	...
'USE_PERMISSIONS'	=>	'Test out user's permissions',
	...

 // Здесь все правильно — Тем не менее, те, кто не занимается программированием, не напишут «user\'s» сами
	...
'USE_PERMISSIONS'	=>	'Test out user\'s permissions',
	...

 // Лучший вариант — Использование символа Unicode Right-Single-Quotation-Mark (правосторонняя одиночная кавычка)
	...
'USE_PERMISSIONS'	=>	'Test out user’s permissions',
	...

 Символы " (двойная прямая кавычка), < (знак «меньше») и > (знак «больше») могут все использоваться для отображения глифов или как часть HTML-разметки, например:

// Плохо — Некорректный HTML, поскольку кавычки, не являющиеся частью элемента, не заменены на HTML-мнемоники
	...
'FOO_BAR'	=>	'PHP version < 4.3.3.<br />
	Visit "Downloads" at <a href="http://www.php.net/">www.php.net</a>.',
	...
// Хорошо — HTML корректен, но «&quot;» выглядит неуклюже
	...
'FOO_BAR'	=>	'PHP version &lt; 4.3.3.<br />
	Visit &quot;Downloads&quot; at <a href="http://www.php.net/">www.php.net</a>.',
	...

 // Лучший вариант — HTML корректен, вместе с тем использование типографически правильных кавычек
	...
'FOO_BAR'	=>	'PHP version &lt; 4.3.3.<br />
	Visit “Downloads” at <a href="http://www.php.net/">www.php.net</a>.',
	...

 Наконец, & (амперсанд) всегда должен быть представлен в виде HTML-мнемоники не зависимо от места использования:

// Плохо — Некорректный HTML, ни один из амперсандов не представлен HTML-мнемониками
	...
'FOO_BAR'	=>	'<a href="http://somedomain.tld/?foo=1&bar=2">Foo & Bar</a>.',
	...

 // Хорошо — Корректный HTML, амперсанды правильно заменены на мнемоники во всех случаях
	...
'FOO_BAR'	=>	'<a href="http://somedomain.tld/?foo=1&amp;bar=2">Foo &amp; Bar</a>.',
	...

 Что касается того, как вводить данные символы, то здесь имеет место быть зависимость от операционной системы, текущей конфигурации региональных стандартов, языка и клавиатуры, а также от встроенных возможностей текстовых редакторов, используемых для правки языковых файлов phpBB. Обратитесь за более подробной информацией (материал на английском) или  (материал на русском)


Орфография, пунктуация, грамматика и так далее:

Языковой пакет по умолчанию, представленный в комлекте phpBB, — это британский английский с орфографией «Кембридж юниверсити пресс (Cambridge University Press)» и имеющий языковой код en. Стиль и характер написания устремлен к официальному, и переводчики должны подражать этому стилю, по крайней мере, в вопросе по использованию максимально короткого языкового кода. Менее официальные переводы или переводы с разговорными выражениями должны обозначаться таковыми либо через метку расширение, либо через метку частное использование внутри кода языка.

Наверх


7. История версий данного руководства


Редакция 8732

Добавлена информация о файлах конфигурации.
Добавлено наследование шаблонов.

Редакция 8596+

Удалены выражения sql_build_array('MULTI_INSERT'....
Добавлено объяснение sql_multi_insert().

Редакция 1.31

Добавлено: add_form_key и check_form_key.

Редакция 1.24

Добавлена глава 5. Наборы символов и кодировки, чтобы объяснить рекомендуемые способы работы со строками в phpBB.

Редакция 1.16

Добавлена глава 6. Правила перевода (интернационализация, локализация), чтобы объяснить принятый формат и соображения авторинга языковых пакетов, создаваемых для phpBB.

Редакция 1.11-1.15

Различные поправки в форматировании документа, орфографии, пунктуации и грамматике.

Редакция 1.9-1.10

Добавлено sql_query_limit в 2.iii. Оформление SQL.

Редакция 1.8

Некоторые поправки в формулировках
Обновлен параграф 1.iii. Расположение файлов для отражения недавних изменений
Расширен параграф 2.ii. Оформление кода.
Добавлено объяснение sql_in_set и sql_build_query в 2.iii. Оформление SQL.
Обновлена глава 3. Работа со стилями.
Обновлена глава 4. Работа с шаблонами, чтобы объяснить проверку циклов, прерывание циклов и прочие изменения, которые мы недавно проделали.

Редакция 1.5

Изменен параграф «Использование общих функций» в 2.v. Общие рекомендации

Наверх

8. Авторское право и отказ от ответственности

Данное приложение является программным продуктом с открытым исходным кодом, выпускаемым в соответствии с лицензией GPL. См. исходный код и директорию docs для получения более подробной информации. Авторское право на данный программный пакет и его содержимое: (c) 2000, 2002, 2005, 2007 phpBB Group, все права защищены.

Перевод документа выполнен командой официальной русской поддержки phpBB.

Наверх

$Id: coding-guidelines.html 9036 2008-10-25 14:16:17Z acydburn $   

Joomla SEF URLs by Artio