Xdebug на удалённом сервере

Некоторое время назад захотелось мне дебажить PHP код с помощью Xdebug`а. Чтоб не просто сухими принт-эр-дайами, эхами и вардампами код захламлять, а по-взрослому, как говориться, с дебаггером на перевес бросаться в любой бой, смело жать step into, step over, step out, run to cursor и наблюдать значения всех переменных в скоупе сразу. Тем более, что в используемой мною IDE Netbeans функционал отладки встроен, и Xdebug поддерживается. Ну и собрался я однажды осилить эту задачу.

В нашей компании используется один общий дев-сервер на Solaris OS, с личными зонами для каждого разработчика и Xdebug`ом. Но что это? Отладка ничерта не работает. При нажатии на заветную кнопу «Debug project» запускается браузер с мордой сайта, NetBeans гоняет прогрессбар в «ожидании подключения», но дальше ничего не происходит. В результате нескольких часов гугления и разбирательства я понял насколько невежествен я был. Я совершенно не задумывался и не понимал работу отладчика. Ниже излагаю суть процессов и решения возникших передо мной проблем. Начну с самого простого.

Кейс первый. Вся работа ведётся на локалхосте — браузер, IDE и веб-сервер находятся на одной машине.

Рис.1

На рисунке 1 показана всем знакомая клиент-серверная (слегка упрощённая) модель и не совсем очевидная клиент-серверная модель отладчика. Цифрами с двоеточием слева я обозначил порты, которые прослушиваются (серверные); цифрами с двоеточием справа — порты, на которые стучатся клиенты. Так нарисовал потому, что для тоннелей номера портов с разных сторон могут не совпадать.

Итак. Когда программист жмёт кнопу «Debug project», NetBeans начинает слушать 9000й порт (порт настраивается)  и запускает браузер (браузер и параметры настраиваются) с параметром XDEBUG_SESSION_START=netbeans-xdebug (значение настраивается). При этом, запускаемый веб-сервером Xdebug, видя этот GET параметр, сохраняет клиенту соответствующую куку о том, что, стартовала сессия отладки, и далее, при последующих действиях от браузера, при передаче этой куки последующими запросами начинает тесное дружеское общение с нетбинсом в 9000й порт (номер порта настраивается) по протоколу DBGP. Т.е. грубо говоря — в NetBeans`е запускается внутренний сервер для отладки, а клиент для него находится на стороне веб-сервера — Xdebug.

Кейс второй. Чуть сложнее. Девелоперский сервер находится отдельно от клиентской машины, за которой сидит программист, но в той же сети. По крайней мере с простой маршрутизацией (не знаю, как это правильно называется) и без NAT`ов. Это иллюстрирует рисунок 2.

Рис.2

Здесь почти ничего не меняется, кроме имени удалённого клиента (машины разработчика — client.local) в конфиге Xdebug`а:

xdebug.remote_host=client.local

При этом на client.local должны быть разрешены внешние подключения на 9000й порт.

Кейс третий. Самый весёлый. Девелоперский сервер находится … где-то в отдалении, а мы, в офисе, сидим за NAT`ом, или ещё каким-нибудь фаерволом. И при этом нам нужно принимать подключения на 9000й порт локалхоста извне. Тут можно, было бы обойтись пробросами портов, но это не наш путь, так как слишком заморочно, не гибко, и вообще — не православно. Используем ssh-тоннель. Точнее обратный ssh-тоннель. Говорим локально так:

ssh -f -N -R 9000:127.0.0.1:9000 server.dev

Эта маленькая магия запускает туннель с локалхоста на наш дев сервер. Да такой, что при коннекте там на 9000й порт локалхоста (127.0.0.1, или какой он там на деве?) пакеты уйдут по ssh`у к нам на клиент и попадут … прямиком в прослушиваемый  NetBeans`ом 9000й порт. См. рис. 3.

Рис.3

Конечно, конфиг Xdebug`а должен быть примерно таким:

zend_extension=/usr/php/5.4/modules/xdebug.so
xdebug.remote_enable=1
xdebug.remote_handler=dbgp
xdebug.remote_host=localhost
xdebug.idekey=netbeans-xdebug
xdebug.remote_port=9000

Но при этом отладка всё равно не  запустилась. Я уж выяснил, что нужные порты слушаются, что в правильные порты инициируются соединения, но всё тщетно. Пошёл смотреть документацию по DBGP. Ну, чтоб телнетом сходить, сэмулировать хотя бы подключение, посмотреть, что хоть что-то где-то дёргается. И на первой же команде Connection Initialization вижу такой параметр: «fileuri — URI of the script file being debugged». На этой строке у меня ещё одно просветление наступило. Ведь Xdebug говорит NetBeans`у в каком файле на какой строке выполняется оператор, чтоб тот разместил маркер на нужной строке. А в обратню сторону — NetBeans говорит Xdebug`у на какой строке какого файла я поставил брекпоинт, чтоб остановить выполнение сценария. И ведь эти двое и не подозревают, что работают на разных машинах. А пути то к файлам абсолютные…

В общем, перемонтировал я каталог с проектом, чтоб он локально находился по такому же пути, как и на удалённом сервере, перезапустил сессию отладчика и … моё маленькое чудо свершилось. Курсор текущего оператора залип на ожидаемом брейкпоинте в скрипте. Торжество.