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

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.

Содержание

Используются технологии uCoz