При создании собственных модулей необходимо учитывать возможность работы с учетом многосайтовости. Ниже один из вариантов решения.
Существует два основных метода организации многосайтовости в CMS 1C-Битрикс. Один из них основан на том, что общие для всех сайтов, работающих на одной установке Битрикс, каталоги (bitrix, upload, local) выносятся за пределы дерева каталогов конкретного сайта, а в файловых структурах отдельных сайтов создаются символические ссылки. Реальная структура каталогов может быть следующей:
/var/www/bitrix/ /var/www/local/ /var/www/upload/ /var/www/site1/ /var/www/site2/
Разрабатываемые модули могут располагаются в одном из двух каталогов: local/modules и bitrix/modules. При установке модуля некоторые скрипты должны размещаться в специальных каталогах Bitrix. Например страницы административного раздела. Хорошим тоном можно считать не копирование файла, а создание другого файла в котором содержится лишь одна команда include. Например имеем файл local/modules/mymodule/admin/mylist.php, следовательно в каталоге bitrix/admin нам необходимо разместить файл myorg_mymodule_mylist.php со следующим содержанием:
<?require $_SERVER['DOCUMENT_ROOT'].'/local/modules/mymodule/admin/mylist.php';?>
И вот на этом этапе возникает вопрос о правильном пути к файлу. Указывать абсолютный путь не очень хорошее решение - в случае переезда, возможно, придется править пути в файлах. По этому лучше строить от корня сайта. А поскольку у нас модуль может быть установлен из разных каталогов, то вторую часть пути необходимо определить программно в момент установки.
Неплохое решение по определению пути на этапе установки было предложено в своих обучающих роликах Академией Битрикс. Выглядит решение примерно следующим образом. (В примере привожу только интересные для данной темы методы)
class myorg_mymodule extends CModule { function installFiles() { if (Bitrix\Main\IO\Directory::isDirectoryExists($path=$this->GetPath().'/admin')) { if ($dir = opendir($path)) { while(false !== $item = readdir($dir)) { if(in_array($item, $this->execlusionAdminFiles)) { continue; } $subName = str_replace('.','_',$this->MODULE_ID); file_put_contents($_SERVER['DOCUMENT_ROOT'].'/bitrix/admin/'.$subName.'_'.$item, '<'.'? require_once($_SERVER["DOCUMENT_ROOT"]."'.$this->GetPath(true).'/admin/'.$item.'");?'.'>'); } closedir($dir); } } } private function GetPath($notDocumentRoot=false) { return ($notDocumentRoot) ? str_ireplace(Bitrix\Main\Application::getDocumentRoot(),'', dirname(__DIR__)) : dirname(__DIR__); } }
В этом решении для определения относительного пути используется константа __DIR__. Из нее убирается корневой каталог сайта. Вот тут и возникает проблема. Если модуль расположен в каталоге local, а каталог в структуре сайта представлен в виде символической ссылки то путь в __DIR__ не будет содержать каталога текущего сайта.Соответственно путь в административной части будет указан ошибочный.
Для решения данной задаче я немного изменил метод получения пути
function GetPath($notDocumentRoot=false) { return ($notDocumentRoot) ? preg_replace('#^(.*)\/(local|bitrix)\/modules#','/$2/modules',dirname(__DIR__)) : dirname(__DIR__); }
Как вариант можно использовать более производительную функцию str_pos.
Недостатком данного метода является возможность "ложных срабатываний". В том случае если в пути встретится два раза последовательность local/modules или bitrix/modules.