1.前言
上一篇系列文章,我们简单的演示了一下PHP1的实现效果,因为没有复杂的东西,所以没有进行源码分析。今天,PHP2将引入解释器的概念。没错,是编译原理的东西,编译原理是一个好东西,它可以在现有语法的基础上,衍生出新的创新语言。比如:前端很火的Vue框架,它就是增加了一个编译环节,将定制化的虚拟DOM编译成了HTML的DOM,所以带来了革命性的特性。本文所精读的源码版本是PHP2.0b7,另外,需要读者对lex和yacc有所了解,这里可以阅读这些介绍。
2.main函数的结构
2.1 main函数的分类
PHP2的运行方式有两种,一种是作为Apache的CGI模块的运行方式,另一种,则是作为CLI命令行的运行方式。后者的功能比较简单,输入参数只能是需要解析的脚本文件。下面,我们主要分析后者的实现细节。
2.2 main函数的初始化工作
1995年,编码思想已经有组件化了,下面来看看main函数的部分内容:
#ifndef APACHE
int main(int argc, char **argv) {
int fd;
char *s;
int no_httpd=0;
long file_size;
#if PHPFASTCGI
while(FCGI_Accept() >= 0) {
#endif
s = getenv("PATH_TRANSLATED");
if(!s) no_httpd=1;
#ifdef HAVE_SETLOCALE
setlocale(LC_ALL,"");
#endif
/* Init all components */
php_init_pool();
php_init_log();
php_init_acc();
php_init_yacc();
php_init_lex();
php_init_error();
php_init_stack();
php_init_symbol_tree();
php_init_switch();
php_init_db();
php_init_while();
php_init_msql();
php_init_pg95();
php_init_file();
php_init_head();
php_init_dir();
#ifdef HAVE_LIBGD
php_init_gd();
#endif
php_init_cond();
if(argc>1) {
if(!strcasecmp(argv[argc-1],"info")) {
Info();
exit(0);
#if ACCESS_CONTROL
} else if(!strcasecmp(argv[argc-1],"config")) {
s = getenv("REQUEST_METHOD");
if(s && !strcasecmp(s,"post")) TreatData(0); /* POST Data */
Configuration(argc, argv);
exit(0);
#endif
#if TEXT_MAGIC
} else if(!strcasecmp(argv[argc-1],"text_magic")) {
set_text_magic(1);
#endif
}
if(!getenv("QUERY_STRING")) {
{
char *astr=NULL;
int ai, al=0;
for(ai=1;ai<argc;ai++) al+=strlen(argv[ai])+1;
astr = emalloc(0,al+14);
strcpy(astr,"QUERY_STRING=");
for(ai=1;ai<argc;ai++) {
strcat(astr,php_urlencode(argv[ai]));
if(ai<argc-1) strcat(astr,"+");
}
putenv(astr);
}
}
}
s = getenv("REQUEST_METHOD");
if(s && !strcasecmp(s,"post")) TreatData(0); /* POST Data */
TreatData(2); /* Cookie Data */
TreatData(1); /* GET Data */
if(no_httpd && argv[1]) fd=OpenFile(argv[1],1,&file_size);
else fd=OpenFile(NULL,1,&file_size);
if(fd==-1) return(-1);
ParserInit(fd,file_size,no_httpd,NULL);
yyparse();
Exit(1);
php_pool_free(1);
php_pool_free(2);
php_pool_free(0);
#if PHPFASTCGI
}
#endif
return(0);
}
PHP2里面通用的组件有十几个,其中我们今天会用到比较重要的是php_init_pool(内存池)、php_init_yacc()(词法分析)、php_init_lex()(语法分析)、php_init_stack(表达式堆栈),最后通过yyparse()进行解释器的翻译工作,下面,我们通过几种类型的语法,来尝试跟踪下PHP2的解释器实现细节。
3.解释器的实现细节
3.1 表达式的解释过程
PHP2的语法标签是以<? >形式解析的,这一点,我们可以快速的在示例代码中找到,下面,我们就以<? echo 1+1; > 的表达式,来追踪下解析过程。在这之前,我们需要先介绍下几个涉及到的相关数据结构。
3.1.1 变量的数据结构
/* Variable Tree */
typedef struct VarTree {
short type;
int count;
char *name;
char *strval;
char *iname;
long intval;
double douval;
int flag;
int scope; /* 0=local to frame, 4=global, 8=static to frame */
struct VarTree *left;
struct VarTree *right;
struct VarTree *next;
struct VarTree *prev;
struct VarTree *lacc;
struct VarTree *lastnode;
int deleted;
int allocated;
} VarTree;
变量是由一颗类似B+的矮胖树组成,除了左子树,右子树以外,还有双向链表结构的前指针和后指针,还有其他需要保存的节点和信息。
3.1.2 表达式堆栈的数据结构
/* Expression Stack */
typedef struct Stack {
short type;
char *strval;
long intval;
double douval;
VarTree *var;
struct Stack *next;
int flag;
} Stack;
表达式堆栈是一个单链表,其中存放了变量树及表达式运行的中间结果信息。
3.1.3 命令TOKEN的数据结构
typedef struct _cmd_table_t {
char *cmd;
unsigned int token;
void (*fnc)(void);
} cmd_table_t;
/* Command hash table
* The hash is extremely simplistic and just based on the length of the
* command.
*/
static cmd_table_t cmd_table[22][35] = {
{ { NULL,0,NULL } }, /* 0 */
{ { NULL,0,NULL } }, /* 1 */
{ { "if", IF, NULL }, /* 2 */
{ NULL,0,NULL } },
{ { "max", INTFUNC1,ArrayMax }, /* 3 */
{ "min", INTFUNC1,ArrayMin },
{ "key", KEY,NULL },
{ "end", END,NULL },
{ "sin", INTFUNC1,Sin },
{ "cos", INTFUNC1,Cos },
{ "tan", INTFUNC1,Tan },
{ "exp", INTFUNC1,Exp },
{ "log", INTFUNC1,mathLog },
{ "abs", INTFUNC1,Abs },
{ "ord", INTFUNC1,Ord },
{ "chr", INTFUNC1,Chr },
{ "pow", INTFUNC2,Pow },
{ NULL,0,NULL } },
{ { "echo",PHPECHO,NULL }, /* 4 */
{ "else",ELSE,NULL },
{ "case",CASE,NULL },
{ "ereg",EREG,NULL },
{ "feof",INTFUNC1,Feof },
{ "msql",INTFUNC2,Msql },
{ "exit",EXIT,NULL },
{ "eval",INTFUNC1,Eval },
{ "exec",EXEC,NULL },
{ "time",INTFUNC0,UnixTime },
{ "date",DATE,NULL },
{ "next",PHPNEXT,NULL },
{ "prev",PREV,NULL },
{ "sort",INTFUNC1,Sort },
{ "rand",INTFUNC0,Rand },
{ "sqrt",INTFUNC1,Sqrt },
{ "file",INTFUNC1,File },
{ "link",INTFUNC2,Link },
{ NULL,0,NULL } },
{ { "endif",ENDIF,NULL }, /* 5 */
{ "while",WHILE,NULL },
{ "break",BREAK,NULL },
{ "isset",ISSET,NULL },
{ "count",INTFUNC1,Count },
{ "eregi",EREGI,NULL },
{ "crypt",CRYPT,NULL },
{ "srand",INTFUNC1,Srand },
{ "sleep",INTFUNC1,Sleep },
{ "fopen",INTFUNC2,Fopen },
{ "popen",INTFUNC2,Popen },
{ "fgets",INTFUNC2,Fgets },
{ "fputs",INTFUNC2,Fputs },
{ "fseek",INTFUNC2,Fseek },
{ "ftell",INTFUNC1,Ftell },
{ "reset",RESET,NULL },
{ "chdir",INTFUNC1,ChDir },
{ "chmod",INTFUNC2,ChMod },
{ "chown",INTFUNC2,ChOwn },
{ "chgrp",INTFUNC2,ChGrp },
{ "mkdir",INTFUNC2,MkDir },
{ "rmdir",INTFUNC1,RmDir },
{ "log10",INTFUNC1,mathLog10 },
{ "unset",UNSET,NULL },
{ NULL,0,NULL } },
{ { "elseif",ELSEIF,NULL }, /* 6 */
{ "switch",SWITCH,NULL },
{ "strlen",INTFUNC1,StrLen },
{ "strval",INTFUNC1,StrVal },
{ "intval",INTFUNC1,IntVal },
{ "strtok",STRTOK,NULL },
{ "strstr",INTFUNC2,StrStr },
{ "strchr",INTFUNC2,StrStr },
{ "substr",INTFUNC3,SubStr },
{ "system",SYSTEM,NULL },
{ "header",HEADER,NULL },
{ "return",RETURN,NULL },
{ "global",GLOBAL,NULL },
{ "static",STATIC,NULL },
{ "gmdate",GMDATE,NULL },
{ "dblist",INTFUNC0,ListSupportedDBs },
{ "unlink",INTFUNC1,Unlink },
{ "rename",INTFUNC2,Rename },
{ "putenv",INTFUNC1,PutEnv },
{ "getenv",INTFUNC1,GetEnv },
{ "mktime",MKTIME,NULL },
{ "fclose",INTFUNC1,Fclose },
{ "pclose",INTFUNC1,Pclose },
{ "rewind",INTFUNC1,Rewind },
{ "bindec",INTFUNC1,BinDec },
{ "decbin",INTFUNC1,DecBin },
{ "hexdec",INTFUNC1,HexDec },
{ "dechex",INTFUNC1,DecHex },
{ "octdec",INTFUNC1,OctDec },
{ "decoct",INTFUNC1,DecOct },
{ "usleep",INTFUNC1,USleep },
{ "pg_tty",INTFUNC1,PGtty },
{ "fgetss",INTFUNC2,Fgetss },
{ NULL,0,NULL } },
{ { "default", DEFAULT,NULL }, /* 7 */
{ "imagesx", INTFUNC1,ImageSXFN },
{ "imagesy", INTFUNC1,ImageSYFN },
{ "include", INCLUDE,NULL },
{ "dbmopen", INTFUNC2,dbmOpen },
{ "strrchr", INTFUNC2,StrrChr },
{ "sprintf", INTFUNC2,Sprintf },
{ "opendir", INTFUNC1,OpenDir },
{ "readdir", INTFUNC0,ReadDir },
{ "tempnam", INTFUNC2,TempNam },
{ "settype", INTFUNC2,SetType },
{ "gettype", INTFUNC1,GetType },
{ "ucfirst", INTFUNC1,UcFirst },
{ "pg_exec", INTFUNC2,PGexec },
{ "pg_host", INTFUNC1,PGhost },
{ "pg_port", INTFUNC1,PGport },
{ "phpinfo", INTFUNC0,Info },
#if APACHE
{ "virtual", INTFUNC1,Virtual },
#endif
{ "symlink", INTFUNC2,SymLink },
{ NULL,0,NULL } },
{ { "endwhile",ENDWHILE,NULL }, /* 8 */
{ "function",FUNCTION,NULL },
{ "dbmclose",INTFUNC1,dbmClose },
{ "dbmfetch",INTFUNC2,dbmFetch },
{ "gettotal",INTFUNC0,GetTotal },
{ "gettoday",INTFUNC0,GetToday },
{ "closedir",INTFUNC0,CloseDir },
{ "filesize",FILESIZE,NULL },
{ "getmyuid",INTFUNC0,GetMyUid },
{ "getmypid",INTFUNC0,GetMyPid },
{ "imagegif",IMAGEGIF,NULL },
{ "imagearc",IMAGEARC,NULL },
{ "pg_close",INTFUNC1,PGclose },
{ "passthru",PASSTHRU,NULL },
{ "readlink",INTFUNC1,ReadLink },
{ "linkinfo",INTFUNC1,LinkInfo },
{ NULL,0,NULL } },
{ { "endswitch", ENDSWITCH,NULL }, /* 9 */
{ "reg_match", REG_MATCH,NULL },
{ "dbminsert", INTFUNC3,dbmInsert },
{ "dbmexists", INTFUNC2,dbmExists },
{ "dbmdelete", INTFUNC2,dbmDelete },
{ "rewinddir", INTFUNC0,RewindDir },
{ "fileperms", FILEPERMS,NULL },
{ "fileinode", FILEINODE,NULL },
{ "fileowner", FILEOWNER,NULL },
{ "filegroup", FILEGROUP,NULL },
{ "fileatime", FILEATIME,NULL },
{ "filemtime", FILEMTIME,NULL },
{ "filectime", FILECTIME,NULL },
{ "getlogdir", INTFUNC0,GetLogDir },
{ "getaccdir", INTFUNC0,GetAccDir },
{ "imageline", INTFUNC6,ImageLine },
{ "imagefill", INTFUNC4,ImageFill },
{ "imagechar", IMAGECHAR,NULL },
{ "doubleval", INTFUNC1,DoubleVal },
{ "securevar", INTFUNC1,SecureVar },
{ "fsockopen", INTFUNC2,FSockOpen },
{ "microtime", INTFUNC0,MicroTime },
{ "urlencode", INTFUNC1,UrlEncode },
{ "urldecode", INTFUNC1,UrlDecode },
{ "quotemeta", INTFUNC1,QuoteMeta },
{ "pg_result", INTFUNC3,PG_result },
{ "pg_dbname", INTFUNC1,PGdbName },
{ "setcookie", SETCOOKIE,NULL },
{ NULL,0,NULL } },
{ { "strtoupper", INTFUNC1,StrToUpper }, /* 10 */
{ "strtolower", INTFUNC1,StrToLower },
{ "reg_search", REG_SEARCH,NULL },
{ "dbmreplace", INTFUNC3,dbmReplace },
{ "dbmnextkey", INTFUNC2,dbmNextKey },
{ "getlogfile", INTFUNC0,GetLogFile },
{ "getlastref", INTFUNC0,GetLastRef },
{ "getlastmod", INTFUNC0,GetLastMod },
{ "getmyinode", INTFUNC0,GetMyInode },
{ "getrandmax", INTFUNC0,GetRandMax },
{ "setlogging", INTFUNC1,SetLogging },
{ "pg_numrows", INTFUNC1,PGnumRows },
{ "pg_options", INTFUNC1,PGoptions },
{ "pg_connect", INTFUNC5,PGconnect },
{ "phpversion", INTFUNC0,PHPVersion },
{ "addslashes", INTFUNC1,_AddSlashes },
{ NULL,0,NULL } },
{ { "msql_result", INTFUNC3,MsqlResult }, /* 11 */
{ "reg_replace", INTFUNC3,RegReplace },
{ "dbmfirstkey", INTFUNC1,dbmFirstKey },
{ "getlasthost", INTFUNC0,GetLastHost },
{ "imagecreate", INTFUNC2,ImageCreate },
{ "imagecharup", IMAGECHAR,NULL },
{ "imagestring", IMAGESTRING,NULL },
{ "setshowinfo", INTFUNC1,SetShowInfo },
{ "msql_dbname", INTFUNC2,MsqlDBName },
{ "msql_dropdb", INTFUNC1,MsqlDropDB },
{ "pg_fieldnum", INTFUNC2,PGfieldNum },
{ NULL,0,NULL } },
{ { "getlastemail", INTFUNC0,GetLastEmail }, /* 12 */
{ "ereg_replace", INTFUNC3,ERegReplace },
{ "msql_connect", INTFUNC1,MsqlConnect },
{ "msql_numrows", INTFUNC1,MsqlNumRows },
{ "msql_regcase", INTFUNC1,MsqlRegCase },
{ "imagedestroy", INTFUNC1,ImageDestroy },
{ "imagepolygon", IMAGEPOLYGON,NULL },
{ "msql_listdbs", INTFUNC0,MsqlListDBs },
{ "pg_numfields", INTFUNC1,PGnumFields },
{ "pg_fieldname", INTFUNC2,PGfieldName },
{ "pg_fieldtype", INTFUNC2,PGfieldType },
{ "pg_fieldsize", INTFUNC2,PGfieldSize },
{ "stripslashes", INTFUNC1,_StripSlashes },
{ NULL,0,NULL } },
{ { "gethostbyaddr", INTFUNC1,GetHostByAddr }, /* 13 */
{ "gethostbyname", INTFUNC1,GetHostByName },
{ "getlastaccess", INTFUNC0,GetLastAccess },
{ "eregi_replace", INTFUNC3,ERegiReplace },
{ "msql_fieldlen", MSQL_FIELDLEN,NULL },
{ "imagesetpixel", INTFUNC4,ImageSetPixel },
{ "imagestringup", IMAGESTRINGUP,NULL },
{ "msql_createdb", INTFUNC1,MsqlCreateDB },
{ "pg_freeresult", INTFUNC1,PGfreeResult },
{ "pg_getlastoid", INTFUNC0,PGgetlastoid },
{ NULL,0,NULL } },
{ { "getlastbrowser", INTFUNC0,GetLastBrowser }, /* 14 */
{ "msql_fieldname", MSQL_FIELDNAME,NULL },
{ "msql_fieldtype", MSQL_FIELDTYPE,NULL },
{ "msql_numfields", INTFUNC1,MsqlNumFields },
{ "imagerectangle", INTFUNC6,ImageRectangle },
{ "imageinterlace", INTFUNC2,ImageInterlace },
{ "msql_tablename", INTFUNC2,MsqlTableName },
{ "pg_fieldprtlen", INTFUNC3,PGfieldPrtLen },
{ "escapeshellcmd", INTFUNC1,EscapeShellCmd },
{ NULL,0,NULL } },
{ { "msql_freeresult", INTFUNC1,MsqlFreeResult }, /* 15 */
{ "msql_fieldflags", MSQL_FIELDFLAGS,NULL },
{ "msql_listtables", INTFUNC1,MsqlListTables },
{ "msql_listfields", INTFUNC2,MsqlListFields },
{ "getstartlogging", INTFUNC0,GetStartLogging },
{ "pg_errormessage", INTFUNC1,PGerrorMessage },
{ NULL,0,NULL } },
{ { "htmlspecialchars", INTFUNC1,HtmlSpecialChars }, /* 16 */
{ "imagecopyresized", IMAGECOPYRESIZED,NULL },
{ NULL,0,NULL } }, /* 16 */
{ { "imagefilltoborder", INTFUNC5,ImageFillToBorder }, /* 17 */
{ "seterrorreporting", INTFUNC1,SetErrorReporting },
{ NULL,0,NULL } },
{ { "imagecolorallocate", INTFUNC4,ImageColorAllocate }, /* 18 */
{ "imagefilledpolygon", IMAGEFILLEDPOLYGON,NULL },
{ "imagecreatefromgif", INTFUNC1,ImageCreateFromGif },
{ NULL,0,NULL } },
{ { NULL,0,NULL } }, /* 19 */
{ { "imagefilledrectangle", INTFUNC6,ImageFilledRectangle }, /* 20 */
{ NULL,0,NULL } },
{ { "imagecolortransparent", INTFUNC2,ImageColorTransparent }, /* 21 */
{ NULL,0,NULL } }
};
命令token是一个二维数组结构体的hash表,通过对读取到的token进行查表映射作为回调处理。
3.1.3 语法分析过程
bison解释器需要先通过lex语法分析,解析出token,然后再把token交给yacc处理,我们原先的那篇介绍lex和yacc的文章,只是站在配置文件的角度,去实践了一下应用。而PHP2中,它只提供了yacc的模板文件parse.raw,并没有提供lex.c的模板文件,我们下面,来追踪下lex.c中的 yylex函数实现细节。
/* Hand-crafted Lexical analyzer using Bison's Pure_Parser calling convention */
int yylex(YYSTYPE *lvalp) {
register char c;
int tokenlen=0;
char *s, d;
char temp[8];
int bs=0, cst, active, ret;
char *look = NULL;
php_pool_clear(1);
cst = GetCurrentState(&active);
if(lstate==99) {
#if DEBUG
Debug("Parser exiting\n");
#endif
return(0);
}
if(ClearIt && LastToken!=RETURN && !cur_func && !eval_mode && cst) {
ClearStack();
ClearIt=0;
} else ClearIt=0;
while(1) switch(state) {
case 0: /* start of token '<' gets us to state 1 */
lstate=0;
c = getnextchar();
if(!c) {
if(outpos) outputline(obuf);
state=99; break;
}
if(no_httpd && inpos==1 && c=='#') {
state=80;
break;
}
if(c!='<') {
obuf[outpos++]=c;
if(c==10 || c==13 || outpos > LINEBUFSIZE-1) outputline(obuf);
break;
}
if(outpos) outputline(obuf);
inmarker=inpos-1;
/* fall-through */
case 1: /* '?' or '!?' gets us to state 2 */
lstate=1;
c = getnextchar();
if(!c) { state=99; break; }
if(c=='!') {
d = getnextchar();
if(!d) {
putback(c);
state=99;
break;
}
if(d != '?') {
putback(d);
} else {
c = d; /* discard '!' */
}
}
if(c!='?') {
putback(c);
if(output_from_marker() < 0) {
state=99; break;
}
state=0;
break;
}
NewExpr=1;
state=2;
/* fall-through */
case 2: /* Start of a command - [a-z][A-Z] gets us to state 3 */
/* Start of a number - [0-9] takes us to state 10 */
lstate=2;
c = getnextchar();
if(!c) { state=99; break; }
if(c==VAR_INIT_CHAR) {
state=40;
tokenlen=0;
tokenmarker=inpos;
break;
}
if(c=='=') { state=15; break; }
if(isdigit(c)) {
state=10;
tokenlen=1;
tokenmarker=inpos-1;
break;
}
if(c==';') {
NewExpr=1;
ClearIt=1;
return(c);
}
if(c=='(') {
NewExpr=1;
if(inIf>-1) inIf++;
if(inWhile>-1) inWhile++;
return(c);
}
if(c==')') {
if(inIf>-1) inIf--;
if(inIf==0) inIf=-1;
if(inWhile>-1) inWhile--;
if(inWhile==0) inWhile=-1;
NewExpr=0;
return(c);
}
if(c=='>') { state=20; break; }
if(c=='<') { state=21; break; }
if(c==' ' || c=='\t' || c=='\n' || c==10 || c==13) break;
if(c=='\'') {
state = 9;
tokenlen=0;
tokenmarker=inpos;
break;
}
if(c=='\"') {
state=30;
tokenlen=0;
tokenmarker=inpos;
break;
}
if(c=='.') {
state=11;
tokenlen=1;
tokenmarker=inpos-1;
break;
}
if(c=='@') { return(c); }
if(c=='{') {
NewExpr=1;
ClearIt=1;
return(c);
}
if(c=='!') { state=12; break; }
if(c=='&') { state=13; break; }
if(c=='|') { state=14; break; }
if(c=='+') { state=16; break; }
if(c=='-') { state=17; break; }
if(c=='/') { state=18; break; }
if(c=='%') { state=8; break; }
if(c=='*') { state=19; break; }
if(c==',') {
NewExpr=1;
return(',');
}
if(c=='}') {
look = lookaheadword();
if(strcasecmp(look,"else") && strcasecmp(look,"elseif")) {
NewExpr=1;
ret = BraceCheck();
if(ret) {
putback(';');
ClearIt=1;
return(ret);
}
return('}');
} else {
break;
}
}
if(c==']') return(c);
if(c < 32) {
c = getnextchar();
if(!c) state=99;
break;
}
if(!isalpha(c) && c != '_') {
putback(c);
if(output_from_marker() < 0) {
state=99; break;
}
state=0;
break;
}
tokenlen=1;
tokenmarker=inpos-1;
/* fall-through */
case 3: /* continue command - non [a-z][A-Z] gets us to state 4 */
lstate=3;
while(isalpha((c=getnextchar())) || c=='_' || isdigit(c)) tokenlen++;
if(!c) { state=99; break; }
putback(c);
case 4: /* command finished */
if(active==-5) {
CondPop(&active);
CondPush(0,-6);
s = (char *) MakeToken(&inbuf[tokenmarker],tokenlen);
if(s && *s) {
*lvalp = (YYSTYPE) s;
lstate=4;
return(FUNCNAME);
}
}
if(tokenlen > MAX_CMD_LEN) {
/* unrecognized command */
if(output_from_marker()<0) {
lstate=4;
state=99; break;
}
state=0;
break;
}
state=2;
if(tokenlen==2 && !strncasecmp(&inbuf[tokenmarker],"if",2)) {
inIf++;
} else if(tokenlen==6 && !strncasecmp(&inbuf[tokenmarker],"elseif",6)) {
inIf++;
} else if(tokenlen==5 && !strncasecmp(&inbuf[tokenmarker],"while",5)) {
if(iterwhile==SeekPos) { /* Iteration of a previous loop */
inWhile++;
} else { /* new while loop */
inWhile++;
if(GetCurrentState(NULL)) {
WhilePush(SeekPos,tokenmarker,yylex_linenumber-1);
}
}
}
NewExpr=1;
lstate=4;
return(CommandLookup(tokenlen,lvalp));
break;
case 8: /* % */
lstate=8;
state=2;
NewExpr=1;
return('%');
break;
case 9: /* 'c' */
lstate=9;
NewExpr=0;
while(1) {
c=getnextchar();
if(c=='\\') {
bs=bs?0:1;
tokenlen++;
continue;
}
if(bs) {
tokenlen++;
bs=0;
continue;
}
if(c=='\'' || !c) break;
tokenlen++;
}
if(!c) { state=99; break; }
s = (char *) MakeToken(&inbuf[tokenmarker],tokenlen);
if(s && *s) {
sprintf((char *)temp,"%d",(int)*s);
} else strcpy((char *)temp,"0");
*lvalp = (YYSTYPE) MakeToken(temp,strlen((char *)temp));
state = 2;
return(LNUMBER);
case 10: /* LNUMBER */
lstate=10;
NewExpr=0;
while(isdigit((c=getnextchar()))) tokenlen++;
if(!c) { state=99; break; }
if(c=='.') { tokenlen++; state=11; break; }
putback(c);
*lvalp = (YYSTYPE) MakeToken(&inbuf[tokenmarker],tokenlen);
state = 2;
return(LNUMBER);
case 11: /* Double */
lstate=11;
NewExpr=0;
while(isdigit((c=getnextchar()))) tokenlen++;
if(!c) { state=99; break; }
putback(c);
*lvalp = (YYSTYPE) MakeToken(&inbuf[tokenmarker],tokenlen);
state = 2;
return(DNUMBER);
case 12: /* ! */
lstate=12;
NewExpr=1;
c = getnextchar();
state=2;
if(c=='=') return(COND_NE);
else {
putback(c);
return(NOT);
}
break;
case 13: /* & */
lstate=13;
NewExpr=1;
c = getnextchar();
state=2;
if(c=='&') return(LOG_AND);
else if(c=='=') return(ANDEQ);
else {
putback(c);
return('&');
}
break;
case 14: /* | */
lstate=14;
NewExpr=1;
c = getnextchar();
state=2;
if(c=='|') return(LOG_OR);
else if(c=='=') return(OREQ);
else {
putback(c);
return('|');
}
break;
case 15: /* = */
lstate=15;
NewExpr=1;
c = getnextchar();
state=2;
if(c=='=') return(COND_EQ);
else {
putback(c);
return('=');
}
break;
case 16: /* + */
lstate=16;
c = getnextchar();
state=2;
if(c=='+') return(INC);
else if(c=='=') return(PEQ);
else {
putback(c);
NewExpr=1;
return('+');
}
break;
case 17: /* - */
lstate=17;
c = getnextchar();
state=2;
if(c=='-') return(DEC);
else if(c=='=') return(MEQ);
else {
putback(c);
if(NewExpr) {
NewExpr=0;
return(NEG);
} else {
NewExpr=1;
return('-');
}
}
break;
case 18: /* / */
lstate=18;
c = getnextchar();
state=2;
if(c=='*') {
while(1) {
c=getnextchar();
if((c=='/' && inbuf[inpos-2]=='*') || (!c)) break;
}
if(!c) { state=99; break; }
tokenlen=0;
tokenmarker=inpos;
state=2;
break;
} else {
putback(c);
NewExpr=1;
return('/');
}
break;
case 19: /* * */
lstate=19;
state=2;
NewExpr=1;
return('*');
break;
case 20: /* > */
lstate=20;
NewExpr=1;
if(inIf>-1 || inWhile>-1) {
state=2;
c=getnextchar();
if(c=='=') return(COND_GE);
putback(c);
return(COND_GT);
}
state=0;
ClearIt=1;
return(END_TAG);
case 21: /* < */
lstate=21;
NewExpr=1;
if(inIf>-1 || inWhile>-1) {
state=2;
c=getnextchar();
if(c=='=') return(COND_LE);
putback(c);
return(COND_LT);
}
return('<');
case 30: /* string */
lstate=30;
NewExpr=0;
while(1) {
c=getnextchar();
if(c=='\\') {
bs=bs?0:1;
tokenlen++;
continue;
}
if(bs) {
tokenlen++;
bs=0;
continue;
}
if(c=='\"' || !c) break;
tokenlen++;
}
if(!c) { state=99; break; }
*lvalp = (YYSTYPE) MakeToken(&inbuf[tokenmarker],tokenlen);
state = 2;
return(STRING);
case 40: /* Variable */
lstate=40;
NewExpr=0;
while((c=getnextchar()) && ((isalnum(c) || c=='_') || (c==VAR_INIT_CHAR && tokenlen==0))) tokenlen++;
if(!c) { state=99; break; }
*lvalp = (YYSTYPE) MakeToken(&inbuf[tokenmarker],tokenlen);
state = 2;
while(c==' ' || c=='\t' || c=='\n') c=getnextchar();
if(c=='[') return(ARRAY);
putback(c);
return(VAR);
case 80: /* Ignore # lines in command line mode */
state=lstate;
lstate=80;
while((c=getnextchar()) && c!=10 && c!=13);
if(!c) { state=99; break; }
break;
case 99: /* EOF reached */
lstate=99;
state=0;
pa = FilePop();
return(END_OF_FILE);
}
} /* yylex */
1.读取语法开始标签(state:0 && 1)
我们看到,这个解析流程就是switch…case 逐行单个字符解析,首先state初始化为0,所以,它的第一步工作就是读取语法开始的标签'<‘。如果是'<‘字符,则switch会进行第二步工作,读取下一个字符是否是语法标签的结构,这里则是'<?’这两个字符。
2.开始语法处理(state:2)
state:2的工作主要是对下一个字符的分类处理,按字母和非字母的处理方式进行分配。这里,我们的表达式是<? echo 1 + 1; > 所以读取到的是字母’e’,然后,流转到了处理字母的state:3环节
3.字母的处理(state:3)
逐步读取下一个字母的处理,会将读取到的字母通过 putback函数存到输入缓冲区作为存储。直到遇到非字母时,进入state:4环节,进行token的解析。这里,当我们读取完echo的时候,就进入了这个环节。
4.命令token的解析(state:4)
解析的过程会把state重置到2的环节,然后通过 CommandLookup(tokenlen,lvalp) 函数,到命令token中去寻找对应的token指令,如下所示:
int CommandLookup(int cmdlen, YYSTYPE *lvalp) {
register int i=0;
while(cmd_table[cmdlen][i].cmd) {
if(!strncasecmp(&inbuf[tokenmarker],cmd_table[cmdlen][i].cmd,cmdlen)) {
*lvalp = (YYSTYPE) MakeToken(&inbuf[tokenmarker],cmdlen);
LastToken = cmd_table[cmdlen][i].token;
return(cmd_table[cmdlen][i].token);
}
i++;
}
*lvalp = (YYSTYPE) MakeToken(&inbuf[tokenmarker],cmdlen);
return(CUSTOMFUNC);
}
当输入缓冲区中的命令能够在hash表中找到时,会生成token,交给yyparse()函数去处理,这里,我们找到了echo 指令,然后,让我们看看yyparse是怎么处理的。



为了便于理解,我们这里直接阅读yacc的模板配置文件,对于我们的 echo 1+1; 这种语法,它属于 PHPECHO expr eoc 这条指令,而expr 是由val定义的,而val中我们的数字属于LNUMBER,所以,它会继续往下读取,我们接着进行state:2的处理
5.语法处理(state:2)
我们读取到了数字1,这个时候,我们将进行state:10环节的解析。
6.数字的处理(state:10)
数字处理阶段,每次都会重置到state:2的状态,并且返回LNUMBER的TOKEN,而LNUMBER的处理如上图所示:会通过PUSH操作,将数字压入表达式堆栈,这里的$1是反向引用LNUMBER的数值。PUSH操作如下:
void Push(char *value, int type) {
Stack *new, *s=NULL;
VarTree *t;
int next=0;
if(!value) return;
if(type==ARRAY) {
s = Pop();
if(!s) {
Error("Stack Error in Push");
return;
}
} else if(type== -ARRAY) { type=ARRAY; next=1; }
new = emalloc(2,sizeof(Stack));
new->type = type;
new->next = NULL;
new->strval = NULL;
new->var = NULL;
if(type==DNUMBER || type==LNUMBER) {
new->strval = estrdup(2,value);
new->intval = atol(value);
new->douval = atof(value);
} else if(type==STRING) {
new->strval = SubVar(estrdup(2,value));
new->intval = atol(new->strval);
new->douval = atof(new->strval);
} else if(type==VAR) {
t = GetVar(value,NULL,0);
if(!t) {
#if DEBUG
Debug("Undefined variable: %s\n",value);
#endif
new->strval = estrdup(2,"");
new->intval = 0;
new->douval = 0;
new->type = STRING;
} else {
new->strval = estrdup(2,t->strval);
new->intval = t->intval;
new->douval = t->douval;
new->type = t->type;
new->var = t;
new->flag = 1;
}
} else if(type==ARRAY) {
if(!next) t = GetVar(value,s->strval,0);
else t = GetVar(value,NULL,1);
if(!t) {
#if DEBUG
Debug("Undefined array variable: %s\n",value);
#endif
new->strval = estrdup(2,"");
new->intval = 0;
new->douval = 0;
new->type = STRING;
} else {
new->strval = estrdup(2,t->strval);
new->intval = t->intval;
new->douval = t->douval;
new->type = t->type;
new->var = t;
new->flag = 0;
}
}
new->next = top;
top = new;
}
表达式堆栈,其本质是随机动态内存通过单链表连接,栈顶指针指向栈尾,如下图所示:

POP出栈的时候,就是通过移动Top指针,来将数值弹出堆栈处理。将中间结果再进行压栈存储。下面,我们继续执行state:2的环节。
7.语法处理(state:2)
这个时候,我们读取到的是’+’,进行state:16的加号处理。
8.加号处理(state:16)
加号处理,每次一样都会重置到state:2,我们这里返回’+’的token处理,’+’的parse处理是声明在结合性这里,左结合,如下图所示:

然后,我们继续回到了state:2,读取数字,直到满足expr+expr时,会调用Calc函数进行表达式计算。

int Calc(int op) {
Stack *s, a, b;
char temp[1024];
static char *atemp=NULL;
int l;
long intval;
double douval=0.0;
a.strval = NULL;
b.strval = NULL;
s = Pop();
if(!s) {
Error("Stack Error");
return(0);
}
b.type = s->type;
b.intval = s->intval;
b.douval = s->douval;
if(s->strval) b.strval = estrdup(0,s->strval);
s = Pop();
if(!s) {
Error("Stack Error");
return(0);
}
a.type = s->type;
a.intval = s->intval;
a.douval = s->douval;
if(s->strval) a.strval = estrdup(0,s->strval);
switch(a.type) {
case LNUMBER:
switch(op) {
case '+':
intval = a.intval + b.intval;
sprintf(temp,"%ld",intval);
Push(temp,LNUMBER);
break;
case '-':
intval = a.intval - b.intval;
sprintf(temp,"%ld",intval);
Push(temp,LNUMBER);
break;
case '*':
intval = a.intval * b.intval;
sprintf(temp,"%ld",intval);
Push(temp,LNUMBER);
break;
case '/':
if(b.intval != 0) {
intval = a.intval / b.intval;
sprintf(temp,"%ld",intval);
Push(temp,LNUMBER);
} else {
Push("undefined",STRING);
}
break;
case '%':
intval = a.intval % b.intval;
sprintf(temp,"%ld",intval);
Push(temp,LNUMBER);
break;
case '&':
intval = a.intval & b.intval;
sprintf(temp,"%ld",intval);
Push(temp,LNUMBER);
break;
case '|':
intval = a.intval | b.intval;
sprintf(temp,"%ld",intval);
Push(temp,LNUMBER);
break;
case '^':
intval = a.intval ^ b.intval;
sprintf(temp,"%ld",intval);
Push(temp,LNUMBER);
break;
}
break;
case DNUMBER:
switch(op) {
case '+':
douval = a.douval + b.douval;
sprintf(temp,"%.10f",douval);
Push(temp,DNUMBER);
break;
case '-':
douval = a.douval - b.douval;
sprintf(temp,"%.10f",douval);
Push(temp,DNUMBER);
break;
case '*':
douval = a.douval * b.douval;
sprintf(temp,"%.10f",douval);
Push(temp,DNUMBER);
break;
case '/':
if(b.douval != 0) {
douval = a.douval / b.douval;
sprintf(temp,"%.10f",douval);
Push(temp,DNUMBER);
} else {
Push("undefined",STRING);
}
break;
case '%':
douval = (double)((int)a.douval % (int)b.douval);
sprintf(temp,"%ld",(long)douval);
Push(temp,DNUMBER);
break;
case '&':
douval = (double)((int)a.douval & (int)b.douval);
sprintf(temp,"%ld",(long)douval);
Push(temp,DNUMBER);
break;
case '|':
douval = (double)((int)a.douval | (int)b.douval);
sprintf(temp,"%ld",(long)douval);
Push(temp,DNUMBER);
break;
case '^':
douval = (double)((int)a.douval ^ (int)b.douval);
sprintf(temp,"%ld",(long)douval);
Push(temp,DNUMBER);
break;
}
break;
case STRING:
switch(op) {
case '+':
if((l=strlen(a.strval)+strlen(b.strval)) > 1023) {
atemp = emalloc(0,l+1);
sprintf(atemp,"%s%s",a.strval,b.strval);
Push(atemp,STRING);
} else {
sprintf(temp,"%s%s",a.strval,b.strval);
Push(temp,STRING);
}
break;
case '-':
if(strlen(b.strval) && strlen(a.strval)) {
Push(b.strval,STRING);
Push("",STRING);
Push(a.strval,STRING);
RegReplace();
} else if(strlen(b.strval)) Push(b.strval,STRING);
else Push(a.strval,STRING);
break;
case '*':
/* Multiplying strings? */
break;
case '/':
/* Dividing strings? */
break;
case '%':
/* Modding strings? */
break;
}
break;
}
return(0);
}
计算的过程,会将表达式堆栈进行POP操作,我们上面也讲过了,然后进行计算,将最后的结果再次入栈,比如1 + 1 + 2,会被优化成: 2 + 2,这种形式。然后,继续读取到’;’,才满足我们的词法分析条件进行Echo的处理:

void Echo(char *format, int args) {
Stack *s=NULL;
Stack sarg[5]; /* Max 5 args to keep things simple in the parser */
#if APACHE
char *buf;
#endif
int num=args, done=0, type;
char *t,*st,*beg,*fmt;
#if DEBUG
Debug("Calling php_header from Echo\n");
#endif
php_header(0,NULL);
while(num) {
s = Pop();
if(!s) {
return;
}
num--;
memcpy(&(sarg[num]),s,sizeof(Stack));
if(s->strval) sarg[num].strval = estrdup(1,s->strval);
}
if(!format) {
for(num=0; num<args; num++) {
if(sarg[num].strval) {
ParseEscapes(sarg[num].strval);
StripSlashes(sarg[num].strval);
if(PUTS(sarg[num].strval) < 0) Exit(0);
}
}
return;
}
st = t;
ParseEscapes(format);
t = format;
num=0;
while(num<args && !done) {
st = t;
type = FormatCheck(&t,&beg,&fmt);
if(type==0 || type==-1) break;
if(beg && *beg) {
if(PUTS(beg)<0) { Exit(0); break; }
}
if(type==1) {
if(PUTS("%")<0) { Exit(0); break; }
continue;
}
switch(type) {
case LNUMBER:
ParseEscapes(fmt);
StripSlashes(fmt);
#if APACHE
buf = emalloc(1,strlen(fmt)+strlen(sarg[num].strval)+ECHO_BUF);
sprintf(buf,fmt,sarg[num].intval);
if(PUTS(buf)<0) { Exit(0); done=1; }
#else
if(printf(fmt,sarg[num].intval)<0) { Exit(0); done=1; }
#endif
num++;
break;
case DNUMBER:
ParseEscapes(fmt);
StripSlashes(fmt);
#if APACHE
buf = emalloc(1,strlen(fmt)+strlen(sarg[num].strval)+ECHO_BUF);
sprintf(buf,fmt,sarg[num].douval);
if(PUTS(buf)<0) { Exit(0); done=1; }
#else
if(printf(fmt,sarg[num].douval) < 0) { Exit(0); done=1; }
#endif
num++;
break;
case STRING:
ParseEscapes(fmt);
StripSlashes(fmt);
ParseEscapes(sarg[num].strval);
StripSlashes(sarg[num].strval);
#if APACHE
buf = emalloc(1,strlen(fmt)+strlen(sarg[num].strval)+ECHO_BUF);
sprintf(buf,fmt,sarg[num].strval);
if(PUTS(buf)<0) Exit(0);
#else
if(printf(fmt,sarg[num].strval) < 0) Exit(0);
#endif
num++;
break;
}
}
if(t && *t) {
if(PUTS(t)<0) Exit(0);
}
}
Echo函数通过弹出表达式堆栈的数值,在经过字符的安全预处理后,通过PUTS方法输出结果。然后,继续读取’>’标签结束,跳转到state:20这个环节。
9.标签结束处理(state:20)
state重置到0环节,然后返回END_TAG的token处理,如下:

通过YYACCEPT结束yyparse的解析并成功返回。
3.2 系统函数的调用
在经过上面一轮基础的表达式解释过程后,我们对整个解释流程已经基本熟悉,所以,我们直接写一些重点流程,这里我们按phpinfo()函数为例。在进行命令token,hash解析的时候,会返回INTFUNC0,token。而yyarse的处理如下:

我们会调用IntFunc()来回调的处理token:
void IntFunc(char *fnc_name) {
int i=0;
int cmdlen = strlen(fnc_name);
while(cmd_table[cmdlen][i].cmd) {
if(!strncasecmp(fnc_name,cmd_table[cmdlen][i].cmd,cmdlen)) {
cmd_table[cmdlen][i].fnc();
break;
}
i++;
}
}

然后调用回调函数Info:
void Info(void) {
struct stat sb;
char *path;
struct passwd *pw;
struct group *gr;
char buf[512];
#if APACHE
array_header *env_arr;
table_entry *env;
int i;
VarTree *var;
#endif
FILE *fp;
php_header(0,NULL);
sprintf(buf,"<html><head><title>PHP/FI</title></head><body><h1>PHP/FI Version %s</h1>by Rasmus Lerdorf (<a href=\"mailto:rasmus@vex.net\">rasmus@vex.net</a>)<p>The PHP/FI Web Site is at <a href=\"http://www.vex.net/php\">http://www.vex.net/php</a><p>\n",PHP_VERSION);
PUTS(buf);
PUTS("This program is free software; you can redistribute it and/or modify\n");
PUTS("it under the terms of the GNU General Public License as published by\n");
PUTS("the Free Software Foundation; either version 2 of the License, or\n");
PUTS("(at your option) any later version.<p>\n");
PUTS("This program is distributed in the hope that it will be useful,\n");
PUTS("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
PUTS("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
PUTS("GNU General Public License for more details.<p>\n");
PUTS("You should have received a copy of the GNU General Public License\n");
PUTS("along with this program; if not, write to the Free Software\n");
PUTS("Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.<p>\n");
PUTS("<hr><b><i>Unix version:</i></b> ");
fp = popen("uname -a","r");
if(fp) {
while(fgets(buf,255,fp)) {
PUTS(buf);
}
pclose(fp);
}
PUTS("<hr><b><i>Environment:</i></b><pre>");
fp = popen("env","r");
if(fp) {
while(fgets(buf,255,fp)) {
PUTS(buf);
}
pclose(fp);
}
#ifndef APACHE
{
char *sn, *pi;
sn = getenv("SCRIPT_NAME"); pi = getenv("PATH_INFO");
sprintf(buf,"PHP_SELF=%s%s\n",sn?sn:"",pi?pi:"");
PUTS(buf);
}
#endif
PUTS("</pre>\n");
#if APACHE
PUTS("<hr><b><i>Apache defined variables available to PHP/FI:</i></b><p>\n");
env_arr = table_elts(php_rqst->subprocess_env);
env = (table_entry *)env_arr->elts;
for(i = 0; i < env_arr->nelts; ++i) {
if(!env[i].key) continue;
if(strcasecmp(env[i].key,"SCRIPT_NAME")) {
sprintf(buf,"%s = %s<br>\n",env[i].key,env[i].val);
PUTS(buf);
if(!strcasecmp(env[i].key,"SCRIPT_FILENAME")) {
sprintf(buf,"PATH_TRANSLATED = %s<br>\n",env[i].val);
PUTS(buf);
}
}
}
sprintf(buf,"PATH_INFO = %s<br>\n",php_rqst->uri); /* Faked by GetVar */
PUTS(buf);
sprintf(buf,"PHP_SELF = %s<br>\n",php_rqst->uri); /* Faked by GetVar */
PUTS(buf);
#if APACHE
var = GetVar("PHP_AUTH_USER",0,0);
if(var) {
sprintf(buf,"PHP_AUTH_USER = %s<br>\n",var->strval);
PUTS(buf);
}
var = GetVar("PHP_AUTH_PW",0,0);
if(var) {
sprintf(buf,"PHP_AUTH_PW = %s<br>\n",var->strval);
PUTS(buf);
}
var = GetVar("PHP_AUTH_TYPE",0,0);
if(var) {
sprintf(buf,"PHP_AUTH_TYPE = %s<br>\n",var->strval);
PUTS(buf);
}
#endif
PUTS("<hr><b><i>Request HTTP Headers:</i></b><p>\n");
env_arr = table_elts(php_rqst->headers_in);
env = (table_entry *)env_arr->elts;
for(i = 0; i < env_arr->nelts; ++i) {
if(!env[i].key) continue;
sprintf(buf,"%s:%s<br>\n",env[i].key,env[i].val);
PUTS(buf);
}
PUTS("<hr><b><i>Send HTTP Headers:</i></b><p>\n");
env_arr = table_elts(php_rqst->headers_out);
env = (table_entry *)env_arr->elts;
for(i = 0; i < env_arr->nelts; ++i) {
if(!env[i].key) continue;
sprintf(buf,"%s:%s<br>\n",env[i].key,env[i].val);
PUTS(buf);
}
#endif
#if APACHE
path = php_rqst->filename;
#else
path = getenv("PATH_TRANSLATED");
#endif
if(path) {
#ifndef APACHE
if(stat(path,&sb)!=-1) {
#endif
PUTS("<hr><font size=+1>Information on <i>");
PUTS(path);
PUTS("</i></font><br>\n");
#if APACHE
switch(php_rqst->finfo.st_mode&S_IFMT) {
#else
switch(sb.st_mode&S_IFMT) {
#endif
case S_IFIFO:
PUTS("<b>Type:</b> <i>fifo special</i><br>\n");
break;
case S_IFCHR:
PUTS("<b>Type:</b> <i>character special</i><br>\n");
break;
case S_IFDIR:
PUTS("<b>Type:</b> <i>directory</i><br>\n");
break;
case S_IFBLK:
PUTS("<b>Type:</b> <i>block special</i><br>\n");
break;
case S_IFREG:
PUTS("<b>Type:</b> <i>ordinary file</i><br>\n");
break;
default:
PUTS("<b>Type:</b> <i>unknown</i><br>\n");
break;
}
#if APACHE
sprintf(buf,"<b>Permission Bits:</b> <i>%04lo</i><br>\n",(long)php_rqst->finfo.st_mode&07777);
#else
sprintf(buf,"<b>Permission Bits:</b> <i>%04lo</i><br>\n",(long)sb.st_mode&07777);
#endif
PUTS(buf);
sprintf(buf,"<b>Number of Links:</b> <i>%ld</i><br>\n",(long)sb.st_nlink);
PUTS(buf);
#if APACHE
pw = getpwuid(php_rqst->finfo.st_uid);
#else
pw = getpwuid(sb.st_uid);
#endif
if(pw) {
gr = getgrgid(pw->pw_gid);
sprintf(buf,"<b>Owner:</b> <i>%s</i> <b>From Group:</b> <i>%s</i><br>\n",pw->pw_name,gr->gr_name);
PUTS(buf);
}
#if APACHE
gr = getgrgid(php_rqst->finfo.st_gid);
#else
gr = getgrgid(sb.st_gid);
#endif
if(gr) {
sprintf(buf,"<b>Group:</b> <i>%s</i><br>\n",gr->gr_name);
PUTS(buf);
}
sprintf(buf,"<b>Size:</b> <i>%ld</i><br>\n",
#if APACHE
(long)php_rqst->finfo.st_size);
#else
(long)sb.st_size);
#endif
PUTS(buf);
sprintf(buf,"<b>Last Access:</b> <i>%s</i><br>",
#if APACHE
ctime(&php_rqst->finfo.st_atime));
#else
ctime(&sb.st_atime));
#endif
PUTS(buf);
sprintf(buf,"<b>Last Modification:</b> <i>%s</i><br>",
#if APACHE
ctime(&php_rqst->finfo.st_mtime));
#else
ctime(&sb.st_mtime));
#endif
PUTS(buf);
sprintf(buf,"<b>Last Status Change:</b> <i>%s</i><br>",
#if APACHE
ctime(&php_rqst->finfo.st_ctime));
#else
ctime(&sb.st_ctime));
#endif
PUTS(buf);
#ifndef APACHE
}
#endif
}
path = getcwd(NULL,1024);
if(path) {
sprintf(buf,"<hr><b>Working Directory:</b> <i>%s</i><br>\n",path);
PUTS(buf);
free(path);
}
#if ACCESS_CONTROL
sprintf(buf,"<b>Access Control enabled using:</b> <i>%s</i><br>\n",getaccdir());
PUTS(buf);
#endif
#if LOGGING
sprintf(buf,"<b>Access Logging enabled using:</b> <i>%s</i><br>\n",getlogdir());
PUTS(buf);
#endif
#if MSQLLOGGING
sprintf(buf,"<b>Access Logging enabled using mSQL in db:</b> <i>%s</i><br>\n",getlogdir());
PUTS(buf);
#endif
#ifdef HAVE_LIBMSQL
PUTS("<b>mSQL support enabled</b><br>\n");
#endif
#ifdef HAVE_LIBPQ
PUTS("<B>Postgres95 support enabled</b><br>\n");
#endif
#ifdef GDBM
PUTS("<b>GDBM support enabled</b><br>\n");
#else
#ifdef NDBM
PUTS("<b>NDBM support enabled</b><br>\n");
#else
PUTS("<b>FlatFile support enabled</b><br>\n");
#endif
#endif
#if APACHE
PUTS("<b>Apache module support enabled</b><br>\n");
#endif
#ifdef HAVE_LIBGD
PUTS("<b>GD support enabled</b><br>\n");
#endif
#if PHPFASTCGI
PUTS("<b>FastCGI support enabled</b><br>\n");
#endif
PUTS("</body></html>");
}
这就是系统函数的调用流程。
4.结语
上面,我们追踪了PHP2/CI解释器对于表达式和系统函数的主要解释流程,剩下的比如条件判断、自定义函数、等等,大家有兴趣也可以去阅读下细节。
5.参考
PHP2
https://museum.php.net/
如无特殊说明,文章均为本站原创,转载请注明出处。如发现有什么不对的地方,希望得到您的指点。