Определяем корень сайта на php

24th Декабрь 2012 | Категории: PHP | Метки:

Часто возникает необходимость подгрузить из одного скрипта другой. Всё хорошо, когда эти скрипты физически расположены в одном каталоге. Делаем include и нет проблем. Проблемы возникают при развитой системе скриптов. Когда исполняемые файлы сгруппированы по каталогам и нужно настроить взаимодействие между ними.

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

Способ 1. Некрасивый

Способ, которым я пользовался более трех лет. Основная идея: получить путь к каталогу текущего файла и относительно этого каталога подгружать другие скрипты.
В переменной $_SERVER["SCRIPT_FILENAME"] содержится абсолютный путь к скрипту. С помощью функций mb_strrpos и mb_substr мы вырезаем из исходной строки имя файла. В итоге в константе PATH будет содержаться текущий каталог.

define ('PATH', mb_substr($_SERVER["SCRIPT_FILENAME"], 0, mb_strrpos($_SERVER["SCRIPT_FILENAME"], "/")));
require_once(PATH."/../connect.php");

Плюсы данного решения

– Должен работать даже на самых древних версиях PHP.

– Инициализация константы происходит в одну строку.

Минусы данного решения

– В зависимости от вложенности скрипта, необходимо менять количество "../". То есть, если вложенность один каталог:

require_once(PATH."/../connect.php");

Если вложенность 4 каталога:

require_once(PATH."/../../../../connect.php");

– Весь этот код вообще не нужен ведь, начиная с PHP 5.3, появилась константа __DIR__, которая содержит путь к каталогу.

– При запуске скрипта с консоли в $_SERVER["SCRIPT_FILENAME"] будет содержаться относительный путь. То есть, если вы запускаете скрипт так:

php /var/www/sites/lgnd.ru/public_html/parser/test.php

В $_SERVER["SCRIPT_FILENAME"] будет содержаться полный путь.

Но если вы сначала перейдете в каталог скрипта, а затем запустите его:

cd /var/www/sites/lgnd.ru/public_html/parser/test.php
php test.php

то в $_SERVER["SCRIPT_FILENAME"] будет содержаться только test.php

– На некоторых конфигурациях содержимое $_SERVER["SCRIPT_FILENAME"] может быть пустым.

Способ 2. Элегантный, но не идеальный

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

Для начала определим: какое название имеет корневой каталог. В зависимости от настроек вашего веб-сервера, это может быть public_html, public, www или что-то другое. В константе __DIR__ будет такой путь:
/var/www/sites/lgnd.ru/public_html/parser

При помощи функции explode мы разобьем этот путь на две части. Разделителем будет служить название корневого каталога (в моем случае — public_html). В итоге в $dir[0] будет содержаться левая часть (всё, что было до public_html):
/var/www/sites/lgnd.ru/

К этой строке мы добавляем название корневого каталога и слэш.

$root_dir = 'public_html';
$dir = explode($root_dir, __DIR__);
define ('PATH', $dir[0].$root_dir.'/');
require_once(PATH."/connect.php");

Плюсы

– Будет корректно определён путь до корневого каталога, вне зависимости от вложенности скрипта.

– Нет проблем с относительным/абсолютным путем при работе из командной строки или при запуске в экзотических конфигурациях

– Не нужно постоянно указывать дополнительные "../" при переносе скрипта в другой каталог.

Минусы

– Требует PHP 5.3+

– Инициализация занимает больше строк и её нельзя сократить (записать три строки в одну не комильфо)

– Требует указания корневого каталога, а значит скрипт нельзя просто скопировать с одного сервера на другой

Способ 3. Нет предела совершенству

Дальнейшие улучшения способа номер 2.

В PHP 5.4 появилось разыменование массивов. Эта штука позволяет обращаться к результатам работы функции explode без создания временной переменной:

PHP 5.3

$dir = explode('public_html', __DIR__);
echo dir[0];

PHP 5.4

echo explode('public_html', __DIR__)[0];

В итоге код из способа 2 становится более компактным:

$root_dir = 'public_html';
define ('PATH', explode($root_dir, __DIR__)[0].$root_dir.'/');

А если очень хочется всё записать в одну строку:

define ('PATH', explode('public_html', __DIR__)[0].'public_html'.'/');

Subscribe without commenting


  1. Vladimir
    11th Апрель 2013 в 20:52

    А DOCUMENT_ROOT чем не устраивает суперглобальный массив $_SERVER доступен всегда.
    $_SERVER[DOCUMENT_ROOT] = /home/userdomen/www/gamatic.ru/public_html/dmx1

  2. Тарлюн Максим
    11th Апрель 2013 в 21:11

    @Vladimir
    При запуске скрипта из консоли (напрямую или по CRON) эта переменная пуста:

    простой файл с одной строкой:

    <? var_dump($_SERVER['DOCUMENT_ROOT']); ?>

    Запускаем файл из консоли:
    string(0) ""

    Запускаем файл из браузера:
    string(35) "/var/www/sites/lgnd.ru/public_html/"

  3. Pilotka
    17th Февраль 2015 в 13:44

    Ну таки из консоли и $_SERVER[«SCRIPT_FILENAME»] пуста, и от константы __DIR__ тот же прок !

  4. Тарлюн Максим
    17th Февраль 2015 в 13:55

    @Pilotka
    Даже сейчас не на всех хостингах доступна PHP 5.3, а 2 года назад ситуация была еще хуже.

    В 2015 году принято делать автозагрузку по стандарту PSR-4 (или хотя бы PSR-0)