Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware
Онлайн-курс по SQL для новичков.
Теория, практика, поддержка, сертификат.

Добавление ваших собственных внутренних функций к PHP/FI

Может случиться так, что набор функций, обеспечиваемых 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(), для иллюстрирации, как добавить функцию.

Шаг 1 - Определение грамматики вашей Функции

если ваша функция принимает от 0 до 6 аргументов, то доступны - предопределенные грамматики. Вы можете пропустить этот шаг.

Грамматика вашей функции определяется в файле parse.raw. Первым делом нужно добавить лексему. Лексема - ключевое слово, из букв верхнего регистра, которое обычно совпадает с именем вашей функции. Все лексемы определены вначале файла parse.raw. Порядок не имеет значения. Затем нужно сформировать фактическое правило грамматики YACC. Рассмотрите существующие правила, и найдите функцию, которая похожа на добавляемую. Имейте в виду, что большинство нормальных функции - стандартные функции, которые считывают параметры из стека выражений. Ваша функция скорее всего будет отнесена к этой группе, в этом случае вам не нужно будет трогать файл parse.raw.

Шаг 2 - Добавление вашей функции к хэш-таблице лексического анализатора

чтобы сделать это, подредактируйте 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, это реально вызываемая функция.

Шаг 3 - Написание вашей реалбной функции Вы можете фактически писать вашу функцию на любом языке, который вам нравится, лишь бы только он поддерживал соглашение о вызовах для нормальных функций C, и у вас есть выбор создать ли ее объектным файлом или библиотечным файлом, совместимым с компоновщиком на вашей системе. Вообще, мы будем предполагать, что функцию написана на C. Все функции, идущие с PHP/FI, были написаны на C. Функция Time() или UnixTime(), как она вызывается внутри PHP, может быть найдена в файле date.c и выглядит:

	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);

Шаг 4 - Добавление прототипа вашей функции в php.h

В нижней половине файла php.h Вы найдете полный список прототипов всех функций в php. Они сгруппированы по файлам, в которых они появляются. Просто добавьте ваш прототип к соответствующему месту в этом файле. В нашего примера Time() будет добавлена следующая строка:

	void UnixTime(void);

Шаг 5 - Компилирование

Вы должны помнить о том что нужно переделывать синтаксический анализатор всякий раз, когда измененяется файл parse.raw. Введите: make parser, чтобы сделать это. Затем сделайте нормальную компиляцию, введя: make, если только это выполнено.

Шаг 6 - Вышлите мне ваши добавления! Если Вы хотите, чтобы ваши функции, были добавленны к следующему выпуску PHP/FI, пошлите их мне. Возможно самый лучший способ это сделать - это с помощью контекстно-зависимого diff. Чтобы сделать это, вам нужна будет копия немодифицированной дистрибуции. Просто сделайте, diff -c, для файлов, которые были изменены, сравнивая их с первоначальными файлми. Пожалуйста не высылайте мне изменения в файле parse.c, так как тот файл генерируется автоматически. Вместо этого высылайте мне различия в файле parse.raw.

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.

[Назад] [Содержание] [Вперед]

Новости мира IT:

Архив новостей

Последние комментарии:

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 985 1945361
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2015 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...