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

Содержание