[spoiler]
1. Защита от прямого вызова
Во всех PHP-файлах (component.php, .description.php, .parameters.php, template.php и др.) компонента первая строка должна быть такой:
if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die();
|
Данная строка гарантирует подключение компонента после подключения . В противном случае при вызове файла напрямую через HTTP-запрос получим Fatal Error и тем самым сообщим недоброжелателю DOCUMENT_ROOT.

2. Кеширование
Для упрощения разработки компонентов 2.0, использующих кеширование данных и выводимого HTML, был добавлен метод . Эта функция:
- учитывает параметры компонента CACHE_TYPE (тип кеширования) и CACHE_TIME (время кеширования);
- кеширует весь выводимый шаблоном HTML, а также массив $arResult.
Пример использования StartResultCache
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die();
//Module
if (!CModule::IncludeModule("learning"))
{
ShowError(GetMessage("LEARNING_MODULE_NOT_FOUND"));
return;
}
if($this->StartResultCache(false, $USER->GetGroups()))
{
/*... Выборка данных в массив $arResult*/
//Подключение шаблона
$this->IncludeComponentTemplate();
}
|
Этот код работает правильно, однако он неоптимален. Подключение модуля происходит всегда, даже если есть валидный кеш, поэтому CModule::IncludeModule следует включить в кешируемую область.
Пример более рационального использования StartResultCache
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die();
if($this->StartResultCache(false, $USER->GetGroups()))
{
//Module
if (!CModule::IncludeModule("learning"))
{
$this->AbortResultCache();
ShowError(GetMessage("LEARNING_MODULE_NOT_FOUND"));
return;
}
/*... Выборка данных в массив $arResult*/
//Подключение шаблона
$this->IncludeComponentTemplate();
}
|
Необходимо также учитывать, что в идентификатор кеша метод StartResultCache "подмешивает" адрес текущей страницы. Если компонент будет использоваться, например, в шаблоне сайта (т.е. будет вызываться на всех страницах веб-ресурса), то количество созданных файлов кеша будет соответствовать количеству страниц на сайте. Для кеширования таких компонентов следует использовать стандартный класс с идентификатором, независящим от текущей страницы. Пример использования класса CPHPCache есть в .
3. Безопасность данных в массиве $arResult
Для повышения безопасности шаблонов и уменьшения головной боли разработчика шаблонов в массиве $arResult должны содержаться уже обработанные данные (через htmlspecialchars или $cdbresult->GetNext()).
Если необходимы «сырые» данные, следует помещать их в отдельные поля, начинающиеся с тильды ($arResult["~NAME"]). Метод добавляет поля с тильдой автоматически.
4. Постраничная навигация
Постраничная навигация реализуется с помощью системного компонента system.pagenavigation. Этот компонент можно вызвать непосредственно с помощью , либо через метод-обертку CDBResult::GetPageNavString.
Входные параметры компонента system.pagenavigation:
string NAV_TITLE - название постраничной навигации;
CDBResult NAV_RESULT - ссылка на объект типа ;
bool SHOW_ALWAYS - показывать постраничную навигацию, если все записи помещаются на одну страницу.
Метод CDBResult::GetPageNavString подключает компонент system.pagenavigation и возвращает HTML постраничной навигации.
Сигнатура функции:
CDBResult::GetPageNavString( string [I]navigationTitle[/I], string [I]templateName[/I] = "", bool [I]showAlways[/I]=false ) |
navigationTitle – название постраничной навигации;
templateName – шаблон постраничной навигации;
showAlways – показывать постраничную навигацию, если все записи помещаются на одну страницу.
Типичный пример использования CDBResult::GetPageNavString
В логике компонента (component.php):
CPageOption::SetOptionString("main", "nav_page_in_session", "N"); // убирает запоминание страниц
$obTicket = CTicket::GetList(Array(), Array()); //Выбираем записи
$obTicket->NavStart(20); //Разбиваем по 20 элементов на страницу
$arResult["NAV_STRING"] = $obTicket->GetPageNavString("Обращения"); //HTML постраничного вывода
$arResult["NAV_RESULT"] = $obTicket; //ссылка на объект
|
В шаблоне (template.php):
<?=$arResult["NAV_STRING"]?> |
Ссылка на объект в $arResult["NAV_RESULT"] нужна для того, чтобы в шаблоне компонента была возможность подключить system.pagenavigation.
5. Идентификаторы картинок
API-методы возвращают только идентификатор картинки, которого в шаблоне компонента будет недостаточно. Необходимы все данные: высота, ширина, путь к файлу. Вместо стандартной функции рекомендую использовать новый метод CFile::GetFileArray.
Метод CFile::GetFileArray($FILE_ID) возвращает массив со всеми данными о файле (см. описание ), а также путь к файлу (в ключе SRC) относительно корня сайта. Если файл с идентификатором $FILE_ID не был найден, метод возвращает false.
Компонент:
$arResult["IMAGE"] = CFile::GetFileArray($arElement["IMAGE_ID"]); |
Шаблон:
<?if ($arResult["IMAGE"] !== false):?> <img src="<?=$arResult["IMAGE"]["SRC"]?>" width="<?=$arResult["IMAGE"]["WIDTH"]?>" height="<?=$arResult["IMAGE"]["HEIGHT"]?>" border="0" /> <?endif?> |
6. Защита форм, ссылок "Удалить" и других действий, зависящих от прав доступа текущего пользователя
Представим, что на вашем сайте выводится компонент новостей, в котором есть возможность удалить новость. Ссылка на удаление выглядит таким образом:
www.mysite.ru/news/detail.php?ID=123&delete=Y |
Далее недоброжелатель помещает эту ссылку в форуме, например, в теге <img src="" />. Зайдя на этот форум и будучи авторизованным на сайте , вы, ничего неподозревая, удалите новость c идентификатором 123. Т.о. даже несмотря на проверку прав доступа в компоненте, недображелатель сможет удалить новость на вашем сайте. Для защиты от таких атак следует дополнительно проверять идентификатор сессии текущего пользователя, передавая его в ссылке или в скрытом поле формы.
Для этого в продукте есть ряд функций в помощь:
string bitrix_sessid() - возвращает идентификатор сессии, предварительно обработанный функцией md5.
bool check_bitrix_sessid($varname='sessid') - возвращает true, если верно условие $_REQUEST[$varname] == bitrix_sessid(), иначе false.
string bitrix_sessid_get($varname='sessid') - возвращает строку вида $varname=идентификатор сессии
string bitrix_sessid_post($varname='sessid') - возвращает строку вида <input type="hidden" name="$varname" id="$varname" value="идентификатор сесии" />
Передача идентификатора сессии в форме
<form method="post" action="/news/add.php"> <?=bitrix_sessid_post()?> Title: <input type="text" name="NAME" size="40" maxlength="255" value=""> </form> |
Передача идентификатора в ссылке
<a href="/news/detail.php?ID=123&delete=Y&<?=bitrix_sessid_get()?>">Удалить</a> |
Проверка сессии
if($arResult["Perm"]>=BLOG_PERMS_MODERATE && intval($_GET["delete_comment_id"])>0 && check_bitrix_sessid())
{
/*Удаление комментария*/
}
|
Это не есть хорошо. Новый метод кеширования очень и очень удобен. И я думаю было бы прекрасно отказаться полностью от старого метода. Но в приведенном примере без него никак. Так почему бы не ввести в методе StartResultCache() дополнительный параметр, который отключал бы включение в ID кеша текущего адреса?