Может случиться так, что набор функций, обеспечиваемых PHP/FI не включает в себя специфическую функцию, в которая может вам потребоваться. Тщательно следуя пунктам, описанным ниже, вы сможете добавить ваши собственные функции PHP/FI.
Прежде, чем Вы начнете хачить внутреннюю организацию PHP/FI, нужно найти копию последней версии Bison. Bison - GNU реализация YACC (Yet Another Compiler Compiler). YACC, который шел с вашей операционной системой, может оказаться, а может и не оказаться достаточно приемлимым, но просто чтобы удостовериться, лучше добыть Bison. Вы можете найти его в ftp://prep.ai.mit.edu/pub/gnu.
Нужно также просмотреть Makefile и включить отладку. Просто разкомментируйте строку DEBUG в файле Makefile. Выходной файл информации отладки определяется переменной DEBUG_FILE в php.h. По умолчанию установлен в /tmp/php.err. Вы можете изменять его, согласно вашим потребностям.
Заключительная вещь которую нужно иметь в виду - то, что php выполняется с тем же идентификатор пользователя что и httpd на вашей системе, если конечно Вы не выполняете, его с установленным битом setuid, и этот пользователь httpd вообще не имеет доступа для записи к различным каталогам. Это означает это, если Вы делаете что-либо, что вызывает php к дампу памяти, Вы можете не получить файл дампа. Простой способ решения состоит в том что нужно сделать каталог, где Вы храните ваш тестовые .html файлы, доступным всем по записи. PHP изменяет текущий каталог на каталог .html файла, который считаетывается, и таким образом отбрасывать корку туда, если сможет.
В последующих шагах мы будем использовать функцию Time(), для иллюстрирации, как добавить функцию.
если ваша функция принимает от 0 до 6 аргументов, то доступны - предопределенные грамматики. Вы можете пропустить этот шаг.
Грамматика вашей функции определяется в файле parse.raw. Первым делом нужно добавить лексему. Лексема - ключевое слово, из букв верхнего регистра, которое обычно совпадает с именем вашей функции. Все лексемы определены вначале файла parse.raw. Порядок не имеет значения. Затем нужно сформировать фактическое правило грамматики YACC. Рассмотрите существующие правила, и найдите функцию, которая похожа на добавляемую. Имейте в виду, что большинство нормальных функции - стандартные функции, которые считывают параметры из стека выражений. Ваша функция скорее всего будет отнесена к этой группе, в этом случае вам не нужно будет трогать файл parse.raw.
чтобы сделать это, подредактируйте lex.c, и найдите хэш-таблицу вблизи верхней части файла. Найдите строку, static cmd_table_t cmd_table[22][30] = {, которая определяет начало хэш-таблицы. [22] [30] определяет размер 2 мерного массива, который содержит хэш-таблицу. 22 это на единицу большая максимальная длина имени функции, и 30 относится к максимальному числу функций в любом хэш списке. Если Вы превышаете любое из этих ограничений, просто увеличьте их прямо здесь.
Эта хэш-таблица завоевала бы в соревнованиях абсолютное звание самой простой хэш-таблицы во всем мире. Хэш значение - это длина строки имени функции, которую нужно хэшировать. Так для нашего примера Time(), нужно добавить вход для значения хэша 4 . Таким образом мы добавляем следующую строку к хэш-списку для 4:
{ "time",INTFUNC0,UnixTime },
Этот запись отображает строку на лексему INTFUNC0. Вы можете поискать грамматику для лексемы INTFUNC0 в parse.raw, и увидете, что это - общая грамматика для внутреннего вызова функции без параметров. Строка в кавычках, является фактической строкой, которая будет использоваться в .html файлах, для вызова функцию. Имейте в виду, что имена функции PHP/FI регистронезависимы. И заключительный элемент - UnixTime, это реально вызываемая функция.
void UnixTime(void) { char temp[32]; sprintf(temp,"%ld",(long)time(NULL)); Push(temp,LNUMBER); }
Обратите внимание, что функция является фунцией void.Это указывает, что она не возвращает ничего. Это может показаться путанным, потому что очевидно функция так или иначе должна возвратить время. Время возвращается, но не как значение возвращаемое функцией. Оно помещается в то, что называется стеком выражений. Стек выражений - просто стек строк и связанных с ними типов. PHP/FI понимает только 3 основных типа переменных: STRING, LNUMBER и DNUMBER. STRING - символьная строка, LNUMBER - длинное целое число, и DNUMBER - значение double или float. В этом примере Time() , значение, которое будет возвращено - время, выраженное в формате Unix (число секунд начиная с января. 1 1970) и - таким образом целое число. Стек выражения принимает только строки, таким образом мы преобразуем, с помощью sprintf, длинное целое число в строку и помещаем это значение в стек, указывая, что это фактически является длинным целым числом с помощью строки: Push(temp,LNUMBER);
В нижней половине файла php.h Вы найдете полный список прототипов всех функций в php. Они сгруппированы по файлам, в которых они появляются. Просто добавьте ваш прототип к соответствующему месту в этом файле. В нашего примера Time() будет добавлена следующая строка:
void UnixTime(void);
Вы должны помнить о том что нужно переделывать синтаксический анализатор всякий раз, когда измененяется файл parse.raw. Введите: make parser, чтобы сделать это. Затем сделайте нормальную компиляцию, введя: make, если только это выполнено.
Time() - это пример, иллюстрирующий шаги, при добавлении функции. Возможно, что функция, которую Вы захотите добавить будет немного более сложной чем этот пример. Возможно вы захотите передавать параметры вашей функции и манипулировать этими параметрами каким-либо способом. Возможно вы даже захотите чтобы она вызывалась различными способами. Эти понятия будут проиллюстрированы PHP/FI функцией Crypt(). См. также раздел, озаглавленный Замечания по хаканию Кода для несколько большего числа технических деталей относительно написания кода для PHP/FI.
Грамматика Crypt() в parse.raw:
%token CRYPT . . . | CRYPT '(' expr ',' expr ')' { if(GetCurrentState(NULL) || inCase || inElseIf) Crypt(1); } | CRYPT '(' expr ')' { if(GetCurrentState(NULL) || inCase || inElseIf) Crypt(0); }
Здесь показано, как определить грамматику, которая позволяет, вызывать функцию с 1 или 2 параметрами. Вы можете написать различные функции, чтобы обрабатывать оба случая, или просто посылать параметр режима, как выполнено здесь, для указания режима, в котором функция вызвана. Обратите внимание, что в этом случае нельзя использовать одну из предопределенных INTFUNC грамматик, так как ваша функция может принимать переменное число параметров.
Другой иллюстрируемый аспект - как фактически представить параметры функции . В большинстве случаев Вы захотите использовать идентификатор expr. Этот идентификатор означает, что параметр - выражение. Выражение может быть литеральное значение, обращение к функции или комбинация многих выражений. См. parse.raw для полного определения грамматики yacc для выражений для большего количества деталей.
Запись Хэш-Таблицы в lex.c:
{ "crypt",CRYPT,NULL },
Обратите внимание, что последний элемент - NULL, в этом случае обращение к функции обрабатывается прямо в parse.raw. Если Вы использовали INTFUNC грамматику, то Вы поместите имя вашей функции вместо NULL. Фактическая функция Crypt находится в crypt.c:
/* * If mode is non-zero, a salt is expected. * If mode is zero, a pseudo-random salt will be selected. */ void Crypt(int mode) { #if HAVE_CRYPT Stack *s; char salt[8]; char *enc; salt[0] = '\0'; if(mode) { s = Pop(); if(!s) { Error("Stack error in crypt"); return; } if(s->strval) strncpy(salt,s->strval,2); } s = Pop(); if(!s) { Error("Stack error in crypt"); return; } if(!salt[0]) { salt[0] = 'A' + (time(NULL) % 26); salt[1] = 'a' + (time(NULL) % 26); salt[2] = '\0'; } enc = (char *)crypt(s->strval,salt); #if DEBUG Debug("Crypt returned [%s]\n",enc); #endif Push(enc,STRING); #else Error("No crypt support compiled into this version"); #endif }
Наиболее важный аспект этой функции - это вызов s = Pop(). Параметры для функции должны быть вытолкнуты из стека выражений один за другим. Когда Вы пишите функцию, которая принимает несколько аргументов, не забывайте, что стек - это структура данных "последним пришел", "первым вышел" . Это означает это, параметры будут выталкиваться из стека в обратном порядке. Последний параметр выталкивается первым. В вышеупомянутом примере мы выясняем, вызвана ли функция с 2 параметрами. Если да, параметр выталкивается из стека и сохраняется. Затем из стека выталкивается следующий параметр. Pop() возвращает указатель на структуру Stack (s). Структура Stack похожа на (из php.h):
/* Expression Stack */ typedef struct Stack { short type; unsigned char *strval; long intval; double douval; VarTree *var; struct Stack *next; } Stack;
Тип type будет один из STRING, LNUMBER или DNUMBER. Strval, intval и douval компоненты - строки, integer и double представления значения соответственно. Если выражение - фактически определенная переменная, компонента var содержит указатель на переменную структуру, которая определяет эту переменную.
В нашей функции Crypt() нас интересует только строковое значение параметра, так что мы используем s->strval. Много функций PHP/FI могут делать различные вещи в зависимости от типа переменной просто проверяя s->type и используя s->strval, s->intval и/или s->douval соответственно.
После вызова реальной функции Crypt() и получения шифрованной строки, наша функции Crypt() вызывает Push(enc, STRING); помещая возвращаемое значение в стек выражений. Нужно отметить, что стек выражений очищается после каждой строки PHP/FI, так что, если Вы помещаете выражения в стек, которые никогда не выталкиваются чем-либо, это не будет иметь значения.
Вызов Debug() в примере Crypt() показывает, как добавить вывод отладочной информации к вашей функции. Debug() - это функция с переменным списком параметров, точно так же как printf.
[Назад]
[Содержание]
[Вперед]