|
В предыдущей части мы ознакомились с принципом работы отладчика с точки зрения модификации на PHP. А сейчас приступим к созданию самого отладчика. Именно отладчик модифицирует скрипт и управляет его работой. Сразу скажу, эта статья предназначена не для чайников. В создание отладчика будем использовать язык програмирования C++ и библиотеку MFC (Microsoft Foundation Classes).
Любая программа начинается с дизайна. Начнем с создания static library, в которой будут заключены все необходимые функции отладчика. Далее, static library можно использовать в создании графических интерфейсов. Давайте подумаем какие основные фунции на нужны:
- Инициализация отладчика
- Добавление/удаление точек останова. Ну как же без этого. :)
- Управление скриптом: начать отладку, продолжить отладку после точки останова, остановить отладку
- Получение/изменение значений переменных
Рассмотрим все по порядку.
Инициализация.
Здесь мы говорим отладчику где находятся файлы для отладки – наш путь к DocumentRoot. Кроме этого нам нужно задать адреса callback – функций, которые отладчик будет вызывать во время следующих событий:
- Файл начал свое выполнение (соединился для разговора с отладчиком).
- Файл закончил свое выполнение (рассоединился).
- Достигнута точка останова.
Добавление/удаление точек останова.
Обьект отладчика должен содержать список всех точек останова. В первую очередь этот список нужен для того, чтобы отладчик имел информацию о том, где модифицировать исходные файлы. Список нужно сформировать до запуска скрипта, так как во время выполнения скрипт модифицировать невозможно. Следоваетельно, функции AddBreakpoint / RemoveBreakpoint нужны только для составления списка. Параметры функций соответственно: имя файла и номер строки где нужно установить breakpoint.
Управление скриптом.
Пожалуй эта самая главная и сложная часть. Как мы сказали ранее, есть несколько функций управления:
Начать отладку. Тут все и начинается. После того, как сформировался список точек останова мы начинаем модифицировать файлы. В прошлой части мы говорили о том, как модифицировать файлы, поэтому я не буду на этом акцентировать внимание. Итак, файлы мы модифицировали. Теперь нужно запустить сервер, который будет ждать сообщений от скрипта. Скрипт в данном случае является клиентом.
После того, как функция StartToDebug запускает сервер, она возвращает результат - запустился сервер или нет. Сервер, бегущий в thread'е ждет соединения. После запуска отладчика пользователь запускает скрипт. Скрипт соединяется с сервером и передает ему свое имя. Сервер заносит это имя и сокет в список соединенных файлов. После этого происходит вызов callback-функции, чтобы сообщить пользователю, что файл соединился. Далее, для каждого файла создается поток, задача которого получать и обрабатывать сообщения от скрипта. И вот приходит сообщение: "привет сервер, приехали... 3 строка". Отладчик вызывает callback-функцию BreakPoint и передает в нее номер строки и имя файла, которое он находит в списке файлов по сокету и переходит в ожидание команды на продолжение выполнения работы скрипта.
Продолжить отладку после точки останова. Здесь ничего сложного нет. Функция просто дает команду продолжать потоку, который принимает сообщения от файла.
Остановить отладку. Все, что нужно сделать – уничтожить потоки, принимающие сообщения от скриптов; уничтожить поток, который принимает соединения и восстановить исходные файлы, то есть убрать наши модификации в скриптах.
Получение/изменение значений переменных.
Давайте вспомним, что же делает скрипт, когда достигнута точка останова? Он просто ждет команд от отладчика и выполняет их командой eval(). Следовательно, чтобы получить или изменить значение переменной нам надо послать скрипту php код, который получит/передаст значение переменной.
В итоге наш дизайн выглядит так:
//Декларации callback функций
typedef void (*DEBUG_FILE_CONNECTED)(CString csFilePath);
typedef void (*DEBUG_FILE_DISCONNECTED)(CString csFilePath);
typedef void (*DEBUG_BREAKPOINT_REACHED)(CString csFilePath, int nLine);
typedef void (*DEBUG_SCRIPT_ERROR)(CString csText);
//Декларация класса отладчика
class CPHPDebug
{
public:
//функции установки callback функций
BOOL SetCallback_ScriptError (void * pFunc);
BOOL SetCallback_FileDisconnected (void * pFunc);
BOOL SetCallback_BreakPointReached (void * pFunc);
BOOL SetCallback_FileConnected (void * pFunc);
//Операции для работы с переменными PHP
CString GetVariableType (CString csVarName);
//Получить тип переменной
CString GetObjectClass (CString csVarName);
//Получить класс обьекта
CString GetArrayDump (CString csVarName);
//Получить дамп массива
int GetStringVariableLen (CString csVarName);
//Получить длину строковой переменной
BOOL SetVariableValue (CString csVarName, CString csVarValue);
//Установить значение переменной
CString GetResourceType (CString csVarName);
//Получить тип ресурса переменной
CString GetVariableValue (CString csVarName);
//Получить значение переменной (integer/string)
//Функции управления отладчиком
BOOL ResumeDebug ();
BOOL RemoveBreakPoint (CString csFilePath, int nLine);
BOOL AddBreakPoint (CString csFFF, int nLine);
BOOL StopDebugger ();
BOOL StartToDebug ();
BOOL Init (CString csDocumentPath, CString csServerPath);
CPHPDebug();
virtual ~CPHPDebug();
private:
CList<CString, CString> lstFiles;
//Список отлаживаемых файлов
//декларация функций потоков
static void ServerListenThread (CPHPDebug * pObj);
//поток приема соединений
static void DebugFileThread (CPHPDebuggerFile * pdf);
//поток приема сообщений от скриптов
static void ErrorListenThread (CPHPDebug * pObj);
//поток приема сообщений ошибок скриптов
SOCKET m_sockListen; //сокет, слушающий соединения скриптов
SOCKET m_sockErrorListen; //сокет, слушающий сообщения об ошибках скриптов
CString m_csServerPath; //путь к серверу
CString m_csDocumentPath; //путь к DocumentRoot
BOOL m_bInited;
//список точек останова
CList<CPHPBreakPoint, CPHPBreakPoint> m_lstBreakPoints;
//Список соединеных файлов
CList<CPHPDebuggerFile, CPHPDebuggerFile> m_lstPHPDebuggedFiles;
CString m_csDebugBreakSTR;
CString m_csFilePrefixSTR;
CString m_csFilePrefixErrH;
BOOL m_bDebugging;
//event handles
HANDLE m_hServerStartedEvent;
HANDLE m_hServerListenThread;
DWORD m_dwServerListenThreadID;
HANDLE m_hErrorThread;
DWORD m_dwErrorThreadID;
CPHPBreakPoint * m_ppbActiveBreakPoint; //активная точка останова
DEBUG_FILE_CONNECTED m_fnFileConnected;
DEBUG_FILE_DISCONNECTED m_fnFileDisconnected;
DEBUG_BREAKPOINT_REACHED m_fnBreakPointReached;
DEBUG_SCRIPT_ERROR m_fnScriptError;
SOCKET m_sockCurClient; //todo: use critical_section
};
О подробностях имплементации я рассказывать не буду. Вы можете скачать библиотеку где имплементирован класс отладчика. В следущей части я приведу пример использования этой библиотеки. Также я расскажу о том, как сделать On-Line отладчик. |