/* * Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language. * Copyright (C) 2011-2012, Symisc Systems http://ph7.symisc.net/ * Version 2.1.4 * For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net * licensing@symisc.net * contact@symisc.net * or visit: * http://ph7.symisc.net/ */ /* $SymiscID: vm.c v1.4 FreeBSD 2012-09-10 00:06 stable $ */ #include "ph7int.h" /* * The code in this file implements execution method of the PH7 Virtual Machine. * The PH7 compiler (implemented in 'compiler.c' and 'parse.c') generates a bytecode program * which is then executed by the virtual machine implemented here to do the work of the PHP * statements. * PH7 bytecode programs are similar in form to assembly language. The program consists * of a linear sequence of operations .Each operation has an opcode and 3 operands. * Operands P1 and P2 are integers where the first is signed while the second is unsigned. * Operand P3 is an arbitrary pointer specific to each instruction. The P2 operand is usually * the jump destination used by the OP_JMP,OP_JZ,OP_JNZ,... instructions. * Opcodes will typically ignore one or more operands. Many opcodes ignore all three operands. * Computation results are stored on a stack. Each entry on the stack is of type ph7_value. * PH7 uses the ph7_value object to represent all values that can be stored in a PHP variable. * Since PHP uses dynamic typing for the values it stores. Values stored in ph7_value objects * can be integers,floating point values,strings,arrays,class instances (object in the PHP jargon) * and so on. * Internally,the PH7 virtual machine manipulates nearly all PHP values as ph7_values structures. * Each ph7_value may cache multiple representations(string,integer etc.) of the same value. * An implicit conversion from one type to the other occurs as necessary. * Most of the code in this file is taken up by the [VmByteCodeExec()] function which does * the work of interpreting a PH7 bytecode program. But other routines are also provided * to help in building up a program instruction by instruction. Also note that sepcial * functions that need access to the underlying virtual machine details such as [die()], * [func_get_args()],[call_user_func()],[ob_start()] and many more are implemented here. */ /* * Each active virtual machine frame is represented by an instance * of the following structure. * VM Frame hold local variables and other stuff related to function call. */ struct VmFrame { VmFrame *pParent; /* Parent frame or NULL if global scope */ void *pUserData; /* Upper layer private data associated with this frame */ ph7_class_instance *pThis; /* Current class instance [i.e: the '$this' variable].NULL otherwise */ SySet sLocal; /* Local variables container (VmSlot instance) */ ph7_vm *pVm; /* VM that own this frame */ SyHash hVar; /* Variable hashtable for fast lookup */ SySet sArg; /* Function arguments container */ SySet sRef; /* Local reference table (VmSlot instance) */ sxi32 iFlags; /* Frame configuration flags (See below)*/ sxu32 iExceptionJump; /* Exception jump destination */ }; #define VM_FRAME_EXCEPTION 0x01 /* Special Exception frame */ #define VM_FRAME_THROW 0x02 /* An exception was thrown */ #define VM_FRAME_CATCH 0x04 /* Catch frame */ /* * When a user defined variable is released (via manual unset($x) or garbage collected) * memory object index is stored in an instance of the following structure and put * in the free object table so that it can be reused again without allocating * a new memory object. */ typedef struct VmSlot VmSlot; struct VmSlot { sxu32 nIdx; /* Index in pVm->aMemObj[] */ void *pUserData; /* Upper-layer private data */ }; /* * An entry in the reference table is represented by an instance of the * follwoing table. * The implementation of the reference mechanism in the PH7 engine * differ greatly from the one used by the zend engine. That is, * the reference implementation is consistent,solid and it's * behavior resemble the C++ reference mechanism. * Refer to the official for more information on this powerful * extension. */ struct VmRefObj { SySet aReference; /* Table of references to this memory object */ SySet aArrEntries; /* Foreign hashmap entries [i.e: array(&$a) ] */ sxu32 nIdx; /* Referenced object index */ sxi32 iFlags; /* Configuration flags */ VmRefObj *pNextCollide,*pPrevCollide; /* Collision link */ VmRefObj *pNext,*pPrev; /* List of all referenced objects */ }; #define VM_REF_IDX_KEEP 0x001 /* Do not restore the memory object to the free list */ /* * Output control buffer entry. * Refer to the implementation of [ob_start()] for more information. */ typedef struct VmObEntry VmObEntry; struct VmObEntry { ph7_value sCallback; /* User defined callback */ SyBlob sOB; /* Output buffer consumer */ }; /* * Each installed shutdown callback (registered using [register_shutdown_function()] ) * is stored in an instance of the following structure. * Refer to the implementation of [register_shutdown_function(()] for more information. */ typedef struct VmShutdownCB VmShutdownCB; struct VmShutdownCB { ph7_value sCallback; /* Shutdown callback */ ph7_value aArg[10]; /* Callback arguments (10 maximum arguments) */ int nArg; /* Total number of given arguments */ }; /* Uncaught exception code value */ #define PH7_EXCEPTION -255 /* * Each parsed URI is recorded and stored in an instance of the following structure. * This structure and it's related routines are taken verbatim from the xHT project * [A modern embeddable HTTP engine implementing all the RFC2616 methods] * the xHT project is developed internally by Symisc Systems. */ typedef struct SyhttpUri SyhttpUri; struct SyhttpUri { SyString sHost; /* Hostname or IP address */ SyString sPort; /* Port number */ SyString sPath; /* Mandatory resource path passed verbatim (Not decoded) */ SyString sQuery; /* Query part */ SyString sFragment; /* Fragment part */ SyString sScheme; /* Scheme */ SyString sUser; /* Username */ SyString sPass; /* Password */ SyString sRaw; /* Raw URI */ }; /* * An instance of the following structure is used to record all MIME headers seen * during a HTTP interaction. * This structure and it's related routines are taken verbatim from the xHT project * [A modern embeddable HTTP engine implementing all the RFC2616 methods] * the xHT project is developed internally by Symisc Systems. */ typedef struct SyhttpHeader SyhttpHeader; struct SyhttpHeader { SyString sName; /* Header name [i.e:"Content-Type","Host","User-Agent"]. NOT NUL TERMINATED */ SyString sValue; /* Header values [i.e: "text/html"]. NOT NUL TERMINATED */ }; /* * Supported HTTP methods. */ #define HTTP_METHOD_GET 1 /* GET */ #define HTTP_METHOD_HEAD 2 /* HEAD */ #define HTTP_METHOD_POST 3 /* POST */ #define HTTP_METHOD_PUT 4 /* PUT */ #define HTTP_METHOD_OTHR 5 /* Other HTTP methods [i.e: DELETE,TRACE,OPTIONS...]*/ /* * Supported HTTP protocol version. */ #define HTTP_PROTO_10 1 /* HTTP/1.0 */ #define HTTP_PROTO_11 2 /* HTTP/1.1 */ /* * Register a constant and it's associated expansion callback so that * it can be expanded from the target PHP program. * The constant expansion mechanism under PH7 is extremely powerful yet * simple and work as follows: * Each registered constant have a C procedure associated with it. * This procedure known as the constant expansion callback is responsible * of expanding the invoked constant to the desired value,for example: * The C procedure associated with the "__PI__" constant expands to 3.14 (the value of PI). * The "__OS__" constant procedure expands to the name of the host Operating Systems * (Windows,Linux,...) and so on. * Please refer to the official documentation for additional information. */ PH7_PRIVATE sxi32 PH7_VmRegisterConstant( ph7_vm *pVm, /* Target VM */ const SyString *pName, /* Constant name */ ProcConstant xExpand, /* Constant expansion callback */ void *pUserData /* Last argument to xExpand() */ ) { ph7_constant *pCons; SyHashEntry *pEntry; char *zDupName; sxi32 rc; pEntry = SyHashGet(&pVm->hConstant,(const void *)pName->zString,pName->nByte); if( pEntry ){ /* Overwrite the old definition and return immediately */ pCons = (ph7_constant *)pEntry->pUserData; pCons->xExpand = xExpand; pCons->pUserData = pUserData; return SXRET_OK; } /* Allocate a new constant instance */ pCons = (ph7_constant *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(ph7_constant)); if( pCons == 0 ){ return 0; } /* Duplicate constant name */ zDupName = SyMemBackendStrDup(&pVm->sAllocator,pName->zString,pName->nByte); if( zDupName == 0 ){ SyMemBackendPoolFree(&pVm->sAllocator,pCons); return 0; } /* Install the constant */ SyStringInitFromBuf(&pCons->sName,zDupName,pName->nByte); pCons->xExpand = xExpand; pCons->pUserData = pUserData; rc = SyHashInsert(&pVm->hConstant,(const void *)zDupName,SyStringLength(&pCons->sName),pCons); if( rc != SXRET_OK ){ SyMemBackendFree(&pVm->sAllocator,zDupName); SyMemBackendPoolFree(&pVm->sAllocator,pCons); return rc; } /* All done,constant can be invoked from PHP code */ return SXRET_OK; } /* * Allocate a new foreign function instance. * This function return SXRET_OK on success. Any other * return value indicates failure. * Please refer to the official documentation for an introduction to * the foreign function mechanism. */ static sxi32 PH7_NewForeignFunction( ph7_vm *pVm, /* Target VM */ const SyString *pName, /* Foreign function name */ ProchHostFunction xFunc, /* Foreign function implementation */ void *pUserData, /* Foreign function private data */ ph7_user_func **ppOut /* OUT: VM image of the foreign function */ ) { ph7_user_func *pFunc; char *zDup; /* Allocate a new user function */ pFunc = (ph7_user_func *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(ph7_user_func)); if( pFunc == 0 ){ return SXERR_MEM; } /* Duplicate function name */ zDup = SyMemBackendStrDup(&pVm->sAllocator,pName->zString,pName->nByte); if( zDup == 0 ){ SyMemBackendPoolFree(&pVm->sAllocator,pFunc); return SXERR_MEM; } /* Zero the structure */ SyZero(pFunc,sizeof(ph7_user_func)); /* Initialize structure fields */ SyStringInitFromBuf(&pFunc->sName,zDup,pName->nByte); pFunc->pVm = pVm; pFunc->xFunc = xFunc; pFunc->pUserData = pUserData; SySetInit(&pFunc->aAux,&pVm->sAllocator,sizeof(ph7_aux_data)); /* Write a pointer to the new function */ *ppOut = pFunc; return SXRET_OK; } /* * Install a foreign function and it's associated callback so that * it can be invoked from the target PHP code. * This function return SXRET_OK on successful registration. Any other * return value indicates failure. * Please refer to the official documentation for an introduction to * the foreign function mechanism. */ PH7_PRIVATE sxi32 PH7_VmInstallForeignFunction( ph7_vm *pVm, /* Target VM */ const SyString *pName, /* Foreign function name */ ProchHostFunction xFunc, /* Foreign function implementation */ void *pUserData /* Foreign function private data */ ) { ph7_user_func *pFunc; SyHashEntry *pEntry; sxi32 rc; /* Overwrite any previously registered function with the same name */ pEntry = SyHashGet(&pVm->hHostFunction,pName->zString,pName->nByte); if( pEntry ){ pFunc = (ph7_user_func *)pEntry->pUserData; pFunc->pUserData = pUserData; pFunc->xFunc = xFunc; SySetReset(&pFunc->aAux); return SXRET_OK; } /* Create a new user function */ rc = PH7_NewForeignFunction(&(*pVm),&(*pName),xFunc,pUserData,&pFunc); if( rc != SXRET_OK ){ return rc; } /* Install the function in the corresponding hashtable */ rc = SyHashInsert(&pVm->hHostFunction,SyStringData(&pFunc->sName),pName->nByte,pFunc); if( rc != SXRET_OK ){ SyMemBackendFree(&pVm->sAllocator,(void *)SyStringData(&pFunc->sName)); SyMemBackendPoolFree(&pVm->sAllocator,pFunc); return rc; } /* User function successfully installed */ return SXRET_OK; } /* * Initialize a VM function. */ PH7_PRIVATE sxi32 PH7_VmInitFuncState( ph7_vm *pVm, /* Target VM */ ph7_vm_func *pFunc, /* Target Fucntion */ const char *zName, /* Function name */ sxu32 nByte, /* zName length */ sxi32 iFlags, /* Configuration flags */ void *pUserData /* Function private data */ ) { /* Zero the structure */ SyZero(pFunc,sizeof(ph7_vm_func)); /* Initialize structure fields */ /* Arguments container */ SySetInit(&pFunc->aArgs,&pVm->sAllocator,sizeof(ph7_vm_func_arg)); /* Static variable container */ SySetInit(&pFunc->aStatic,&pVm->sAllocator,sizeof(ph7_vm_func_static_var)); /* Bytecode container */ SySetInit(&pFunc->aByteCode,&pVm->sAllocator,sizeof(VmInstr)); /* Preallocate some instruction slots */ SySetAlloc(&pFunc->aByteCode,0x10); /* Closure environment */ SySetInit(&pFunc->aClosureEnv,&pVm->sAllocator,sizeof(ph7_vm_func_closure_env)); pFunc->iFlags = iFlags; pFunc->pUserData = pUserData; SyStringInitFromBuf(&pFunc->sName,zName,nByte); return SXRET_OK; } /* * Install a user defined function in the corresponding VM container. */ PH7_PRIVATE sxi32 PH7_VmInstallUserFunction( ph7_vm *pVm, /* Target VM */ ph7_vm_func *pFunc, /* Target function */ SyString *pName /* Function name */ ) { SyHashEntry *pEntry; sxi32 rc; if( pName == 0 ){ /* Use the built-in name */ pName = &pFunc->sName; } /* Check for duplicates (functions with the same name) first */ pEntry = SyHashGet(&pVm->hFunction,pName->zString,pName->nByte); if( pEntry ){ ph7_vm_func *pLink = (ph7_vm_func *)pEntry->pUserData; if( pLink != pFunc ){ /* Link */ pFunc->pNextName = pLink; pEntry->pUserData = pFunc; } return SXRET_OK; } /* First time seen */ pFunc->pNextName = 0; rc = SyHashInsert(&pVm->hFunction,pName->zString,pName->nByte,pFunc); return rc; } /* * Install a user defined class in the corresponding VM container. */ PH7_PRIVATE sxi32 PH7_VmInstallClass( ph7_vm *pVm, /* Target VM */ ph7_class *pClass /* Target Class */ ) { SyString *pName = &pClass->sName; SyHashEntry *pEntry; sxi32 rc; /* Check for duplicates */ pEntry = SyHashGet(&pVm->hClass,(const void *)pName->zString,pName->nByte); if( pEntry ){ ph7_class *pLink = (ph7_class *)pEntry->pUserData; /* Link entry with the same name */ pClass->pNextName = pLink; pEntry->pUserData = pClass; return SXRET_OK; } pClass->pNextName = 0; /* Perform a simple hashtable insertion */ rc = SyHashInsert(&pVm->hClass,(const void *)pName->zString,pName->nByte,pClass); return rc; } /* * Instruction builder interface. */ PH7_PRIVATE sxi32 PH7_VmEmitInstr( ph7_vm *pVm, /* Target VM */ sxi32 iOp, /* Operation to perform */ sxi32 iP1, /* First operand */ sxu32 iP2, /* Second operand */ void *p3, /* Third operand */ sxu32 *pIndex /* Instruction index. NULL otherwise */ ) { VmInstr sInstr; sxi32 rc; /* Fill the VM instruction */ sInstr.iOp = (sxu8)iOp; sInstr.iP1 = iP1; sInstr.iP2 = iP2; sInstr.p3 = p3; if( pIndex ){ /* Instruction index in the bytecode array */ *pIndex = SySetUsed(pVm->pByteContainer); } /* Finally,record the instruction */ rc = SySetPut(pVm->pByteContainer,(const void *)&sInstr); if( rc != SXRET_OK ){ PH7_GenCompileError(&pVm->sCodeGen,E_ERROR,1,"Fatal,Cannot emit instruction due to a memory failure"); /* Fall throw */ } return rc; } /* * Swap the current bytecode container with the given one. */ PH7_PRIVATE sxi32 PH7_VmSetByteCodeContainer(ph7_vm *pVm,SySet *pContainer) { if( pContainer == 0 ){ /* Point to the default container */ pVm->pByteContainer = &pVm->aByteCode; }else{ /* Change container */ pVm->pByteContainer = &(*pContainer); } return SXRET_OK; } /* * Return the current bytecode container. */ PH7_PRIVATE SySet * PH7_VmGetByteCodeContainer(ph7_vm *pVm) { return pVm->pByteContainer; } /* * Extract the VM instruction rooted at nIndex. */ PH7_PRIVATE VmInstr * PH7_VmGetInstr(ph7_vm *pVm,sxu32 nIndex) { VmInstr *pInstr; pInstr = (VmInstr *)SySetAt(pVm->pByteContainer,nIndex); return pInstr; } /* * Return the total number of VM instructions recorded so far. */ PH7_PRIVATE sxu32 PH7_VmInstrLength(ph7_vm *pVm) { return SySetUsed(pVm->pByteContainer); } /* * Pop the last VM instruction. */ PH7_PRIVATE VmInstr * PH7_VmPopInstr(ph7_vm *pVm) { return (VmInstr *)SySetPop(pVm->pByteContainer); } /* * Peek the last VM instruction. */ PH7_PRIVATE VmInstr * PH7_VmPeekInstr(ph7_vm *pVm) { return (VmInstr *)SySetPeek(pVm->pByteContainer); } PH7_PRIVATE VmInstr * PH7_VmPeekNextInstr(ph7_vm *pVm) { VmInstr *aInstr; sxu32 n; n = SySetUsed(pVm->pByteContainer); if( n < 2 ){ return 0; } aInstr = (VmInstr *)SySetBasePtr(pVm->pByteContainer); return &aInstr[n - 2]; } /* * Allocate a new virtual machine frame. */ static VmFrame * VmNewFrame( ph7_vm *pVm, /* Target VM */ void *pUserData, /* Upper-layer private data */ ph7_class_instance *pThis /* Top most class instance [i.e: Object in the PHP jargon]. NULL otherwise */ ) { VmFrame *pFrame; /* Allocate a new vm frame */ pFrame = (VmFrame *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(VmFrame)); if( pFrame == 0 ){ return 0; } /* Zero the structure */ SyZero(pFrame,sizeof(VmFrame)); /* Initialize frame fields */ pFrame->pUserData = pUserData; pFrame->pThis = pThis; pFrame->pVm = pVm; SyHashInit(&pFrame->hVar,&pVm->sAllocator,0,0); SySetInit(&pFrame->sArg,&pVm->sAllocator,sizeof(VmSlot)); SySetInit(&pFrame->sLocal,&pVm->sAllocator,sizeof(VmSlot)); SySetInit(&pFrame->sRef,&pVm->sAllocator,sizeof(VmSlot)); return pFrame; } /* * Enter a VM frame. */ static sxi32 VmEnterFrame( ph7_vm *pVm, /* Target VM */ void *pUserData, /* Upper-layer private data */ ph7_class_instance *pThis, /* Top most class instance [i.e: Object in the PHP jargon]. NULL otherwise */ VmFrame **ppFrame /* OUT: Top most active frame */ ) { VmFrame *pFrame; /* Allocate a new frame */ pFrame = VmNewFrame(&(*pVm),pUserData,pThis); if( pFrame == 0 ){ return SXERR_MEM; } /* Link to the list of active VM frame */ pFrame->pParent = pVm->pFrame; pVm->pFrame = pFrame; if( ppFrame ){ /* Write a pointer to the new VM frame */ *ppFrame = pFrame; } return SXRET_OK; } /* * Link a foreign variable with the TOP most active frame. * Refer to the PH7_OP_UPLINK instruction implementation for more * information. */ static sxi32 VmFrameLink(ph7_vm *pVm,SyString *pName) { VmFrame *pTarget,*pFrame; SyHashEntry *pEntry = 0; sxi32 rc; /* Point to the upper frame */ pFrame = pVm->pFrame; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } pTarget = pFrame; pFrame = pTarget->pParent; while( pFrame ){ if( (pFrame->iFlags & VM_FRAME_EXCEPTION) == 0 ){ /* Query the current frame */ pEntry = SyHashGet(&pFrame->hVar,(const void *)pName->zString,pName->nByte); if( pEntry ){ /* Variable found */ break; } } /* Point to the upper frame */ pFrame = pFrame->pParent; } if( pEntry == 0 ){ /* Inexistant variable */ return SXERR_NOTFOUND; } /* Link to the current frame */ rc = SyHashInsert(&pTarget->hVar,pEntry->pKey,pEntry->nKeyLen,pEntry->pUserData); if( rc == SXRET_OK ){ sxu32 nIdx; nIdx = SX_PTR_TO_INT(pEntry->pUserData); PH7_VmRefObjInstall(&(*pVm),nIdx,SyHashLastEntry(&pTarget->hVar),0,0); } return rc; } /* * Leave the top-most active frame. */ static void VmLeaveFrame(ph7_vm *pVm) { VmFrame *pFrame = pVm->pFrame; if( pFrame ){ /* Unlink from the list of active VM frame */ pVm->pFrame = pFrame->pParent; if( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) == 0 ){ VmSlot *aSlot; sxu32 n; /* Restore local variable to the free pool so that they can be reused again */ aSlot = (VmSlot *)SySetBasePtr(&pFrame->sLocal); for(n = 0 ; n < SySetUsed(&pFrame->sLocal) ; ++n ){ /* Unset the local variable */ PH7_VmUnsetMemObj(&(*pVm),aSlot[n].nIdx,FALSE); } /* Remove local reference */ aSlot = (VmSlot *)SySetBasePtr(&pFrame->sRef); for(n = 0 ; n < SySetUsed(&pFrame->sRef) ; ++n ){ PH7_VmRefObjRemove(&(*pVm),aSlot[n].nIdx,(SyHashEntry *)aSlot[n].pUserData,0); } } /* Release internal containers */ SyHashRelease(&pFrame->hVar); SySetRelease(&pFrame->sArg); SySetRelease(&pFrame->sLocal); SySetRelease(&pFrame->sRef); /* Release the whole structure */ SyMemBackendPoolFree(&pVm->sAllocator,pFrame); } } /* * Compare two functions signature and return the comparison result. */ static int VmOverloadCompare(SyString *pFirst,SyString *pSecond) { const char *zSend = &pSecond->zString[pSecond->nByte]; const char *zFend = &pFirst->zString[pFirst->nByte]; const char *zSin = pSecond->zString; const char *zFin = pFirst->zString; const char *zPtr = zFin; for(;;){ if( zFin >= zFend || zSin >= zSend ){ break; } if( zFin[0] != zSin[0] ){ /* mismatch */ break; } zFin++; zSin++; } return (int)(zFin-zPtr); } /* * Select the appropriate VM function for the current call context. * This is the implementation of the powerful 'function overloading' feature * introduced by the version 2 of the PH7 engine. * Refer to the official documentation for more information. */ static ph7_vm_func * VmOverload( ph7_vm *pVm, /* Target VM */ ph7_vm_func *pList, /* Linked list of candidates for overloading */ ph7_value *aArg, /* Array of passed arguments */ int nArg /* Total number of passed arguments */ ) { int iTarget,i,j,iCur,iMax; ph7_vm_func *apSet[10]; /* Maximum number of candidates */ ph7_vm_func *pLink; SyString sArgSig; SyBlob sSig; pLink = pList; i = 0; /* Put functions expecting the same number of passed arguments */ while( i < (int)SX_ARRAYSIZE(apSet) ){ if( pLink == 0 ){ break; } if( (int)SySetUsed(&pLink->aArgs) == nArg ){ /* Candidate for overloading */ apSet[i++] = pLink; } /* Point to the next entry */ pLink = pLink->pNextName; } if( i < 1 ){ /* No candidates,return the head of the list */ return pList; } if( nArg < 1 || i < 2 ){ /* Return the only candidate */ return apSet[0]; } /* Calculate function signature */ SyBlobInit(&sSig,&pVm->sAllocator); for( j = 0 ; j < nArg ; j++ ){ int c = 'n'; /* null */ if( aArg[j].iFlags & MEMOBJ_HASHMAP ){ /* Hashmap */ c = 'h'; }else if( aArg[j].iFlags & MEMOBJ_BOOL ){ /* bool */ c = 'b'; }else if( aArg[j].iFlags & MEMOBJ_INT ){ /* int */ c = 'i'; }else if( aArg[j].iFlags & MEMOBJ_STRING ){ /* String */ c = 's'; }else if( aArg[j].iFlags & MEMOBJ_REAL ){ /* Float */ c = 'f'; }else if( aArg[j].iFlags & MEMOBJ_OBJ ){ /* Class instance */ ph7_class *pClass = ((ph7_class_instance *)aArg[j].x.pOther)->pClass; SyString *pName = &pClass->sName; SyBlobAppend(&sSig,(const void *)pName->zString,pName->nByte); c = -1; } if( c > 0 ){ SyBlobAppend(&sSig,(const void *)&c,sizeof(char)); } } SyStringInitFromBuf(&sArgSig,SyBlobData(&sSig),SyBlobLength(&sSig)); iTarget = 0; iMax = -1; /* Select the appropriate function */ for( j = 0 ; j < i ; j++ ){ /* Compare the two signatures */ iCur = VmOverloadCompare(&sArgSig,&apSet[j]->sSignature); if( iCur > iMax ){ iMax = iCur; iTarget = j; } } SyBlobRelease(&sSig); /* Appropriate function for the current call context */ return apSet[iTarget]; } /* Forward declaration */ static sxi32 VmLocalExec(ph7_vm *pVm,SySet *pByteCode,ph7_value *pResult); static sxi32 VmErrorFormat(ph7_vm *pVm,sxi32 iErr,const char *zFormat,...); /* * Mount a compiled class into the freshly created vitual machine so that * it can be instanciated from the executed PHP script. */ static sxi32 VmMountUserClass( ph7_vm *pVm, /* Target VM */ ph7_class *pClass /* Class to be mounted */ ) { ph7_class_method *pMeth; ph7_class_attr *pAttr; SyHashEntry *pEntry; sxi32 rc; /* Reset the loop cursor */ SyHashResetLoopCursor(&pClass->hAttr); /* Process only static and constant attribute */ while( (pEntry = SyHashGetNextEntry(&pClass->hAttr)) != 0 ){ /* Extract the current attribute */ pAttr = (ph7_class_attr *)pEntry->pUserData; if( pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT|PH7_CLASS_ATTR_STATIC) ){ ph7_value *pMemObj; /* Reserve a memory object for this constant/static attribute */ pMemObj = PH7_ReserveMemObj(&(*pVm)); if( pMemObj == 0 ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR, "Cannot reserve a memory object for class attribute '%z->%z' due to a memory failure", &pClass->sName,&pAttr->sName ); return SXERR_MEM; } if( SySetUsed(&pAttr->aByteCode) > 0 ){ /* Initialize attribute default value (any complex expression) */ VmLocalExec(&(*pVm),&pAttr->aByteCode,pMemObj); } /* Record attribute index */ pAttr->nIdx = pMemObj->nIdx; /* Install static attribute in the reference table */ PH7_VmRefObjInstall(&(*pVm),pMemObj->nIdx,0,0,VM_REF_IDX_KEEP); } } /* Install class methods */ if( pClass->iFlags & PH7_CLASS_INTERFACE ){ /* Do not mount interface methods since they are signatures only. */ return SXRET_OK; } /* Create constructor alias if not yet done */ if( SyHashGet(&pClass->hMethod,"__construct",sizeof("__construct")-1) == 0 ){ /* User constructor with the same base class name */ pEntry = SyHashGet(&pClass->hMethod,SyStringData(&pClass->sName),SyStringLength(&pClass->sName)); if( pEntry ){ pMeth = (ph7_class_method *)pEntry->pUserData; /* Create the alias */ SyHashInsert(&pClass->hMethod,"__construct",sizeof("__construct")-1,pMeth); } } /* Install the methods now */ SyHashResetLoopCursor(&pClass->hMethod); while((pEntry = SyHashGetNextEntry(&pClass->hMethod)) != 0 ){ pMeth = (ph7_class_method *)pEntry->pUserData; if( (pMeth->iFlags & PH7_CLASS_ATTR_ABSTRACT) == 0 ){ rc = PH7_VmInstallUserFunction(&(*pVm),&pMeth->sFunc,&pMeth->sVmName); if( rc != SXRET_OK ){ return rc; } } } return SXRET_OK; } /* * Allocate a private frame for attributes of the given * class instance (Object in the PHP jargon). */ PH7_PRIVATE sxi32 PH7_VmCreateClassInstanceFrame( ph7_vm *pVm, /* Target VM */ ph7_class_instance *pObj /* Class instance */ ) { ph7_class *pClass = pObj->pClass; ph7_class_attr *pAttr; SyHashEntry *pEntry; sxi32 rc; /* Install class attribute in the private frame associated with this instance */ SyHashResetLoopCursor(&pClass->hAttr); while( (pEntry = SyHashGetNextEntry(&pClass->hAttr)) != 0 ){ VmClassAttr *pVmAttr; /* Extract the current attribute */ pAttr = (ph7_class_attr *)pEntry->pUserData; pVmAttr = (VmClassAttr *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(VmClassAttr)); if( pVmAttr == 0 ){ return SXERR_MEM; } pVmAttr->pAttr = pAttr; if( (pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT|PH7_CLASS_ATTR_STATIC)) == 0 ){ ph7_value *pMemObj; /* Reserve a memory object for this attribute */ pMemObj = PH7_ReserveMemObj(&(*pVm)); if( pMemObj == 0 ){ SyMemBackendPoolFree(&pVm->sAllocator,pVmAttr); return SXERR_MEM; } pVmAttr->nIdx = pMemObj->nIdx; if( SySetUsed(&pAttr->aByteCode) > 0 ){ /* Initialize attribute default value (any complex expression) */ VmLocalExec(&(*pVm),&pAttr->aByteCode,pMemObj); } rc = SyHashInsert(&pObj->hAttr,SyStringData(&pAttr->sName),SyStringLength(&pAttr->sName),pVmAttr); if( rc != SXRET_OK ){ VmSlot sSlot; /* Restore memory object */ sSlot.nIdx = pMemObj->nIdx; sSlot.pUserData = 0; SySetPut(&pVm->aFreeObj,(const void *)&sSlot); SyMemBackendPoolFree(&pVm->sAllocator,pVmAttr); return SXERR_MEM; } /* Install attribute in the reference table */ PH7_VmRefObjInstall(&(*pVm),pMemObj->nIdx,0,0,VM_REF_IDX_KEEP); }else{ /* Install static/constant attribute */ pVmAttr->nIdx = pAttr->nIdx; rc = SyHashInsert(&pObj->hAttr,SyStringData(&pAttr->sName),SyStringLength(&pAttr->sName),pVmAttr); if( rc != SXRET_OK ){ SyMemBackendPoolFree(&pVm->sAllocator,pVmAttr); return SXERR_MEM; } } } return SXRET_OK; } /* Forward declaration */ static VmRefObj * VmRefObjExtract(ph7_vm *pVm,sxu32 nObjIdx); static sxi32 VmRefObjUnlink(ph7_vm *pVm,VmRefObj *pRef); /* * Dummy read-only buffer used for slot reservation. */ static const char zDummy[sizeof(ph7_value)] = { 0 }; /* Must be >= sizeof(ph7_value) */ /* * Reserve a constant memory object. * Return a pointer to the raw ph7_value on success. NULL on failure. */ PH7_PRIVATE ph7_value * PH7_ReserveConstObj(ph7_vm *pVm,sxu32 *pIndex) { ph7_value *pObj; sxi32 rc; if( pIndex ){ /* Object index in the object table */ *pIndex = SySetUsed(&pVm->aLitObj); } /* Reserve a slot for the new object */ rc = SySetPut(&pVm->aLitObj,(const void *)zDummy); if( rc != SXRET_OK ){ /* If the supplied memory subsystem is so sick that we are unable to allocate * a tiny chunk of memory, there is no much we can do here. */ return 0; } pObj = (ph7_value *)SySetPeek(&pVm->aLitObj); return pObj; } /* * Reserve a memory object. * Return a pointer to the raw ph7_value on success. NULL on failure. */ PH7_PRIVATE ph7_value * VmReserveMemObj(ph7_vm *pVm,sxu32 *pIndex) { ph7_value *pObj; sxi32 rc; if( pIndex ){ /* Object index in the object table */ *pIndex = SySetUsed(&pVm->aMemObj); } /* Reserve a slot for the new object */ rc = SySetPut(&pVm->aMemObj,(const void *)zDummy); if( rc != SXRET_OK ){ /* If the supplied memory subsystem is so sick that we are unable to allocate * a tiny chunk of memory, there is no much we can do here. */ return 0; } pObj = (ph7_value *)SySetPeek(&pVm->aMemObj); return pObj; } /* Forward declaration */ static sxi32 VmEvalChunk(ph7_vm *pVm,ph7_context *pCtx,SyString *pChunk,int iFlags,int bTrueReturn); /* * Built-in classes/interfaces and some functions that cannot be implemented * directly as foreign functions. */ #define PH7_BUILTIN_LIB \ "class Exception { "\ "protected $message = 'Unknown exception';"\ "protected $code = 0;"\ "protected $file;"\ "protected $line;"\ "protected $trace;"\ "protected $previous;"\ "public function __construct($message = null, $code = 0, Exception $previous = null){"\ " if( isset($message) ){"\ " $this->message = $message;"\ " }"\ " $this->code = $code;"\ " $this->file = __FILE__;"\ " $this->line = __LINE__;"\ " $this->trace = debug_backtrace();"\ " if( isset($previous) ){"\ " $this->previous = $previous;"\ " }"\ "}"\ "public function getMessage(){"\ " return $this->message;"\ "}"\ " public function getCode(){"\ " return $this->code;"\ "}"\ "public function getFile(){"\ " return $this->file;"\ "}"\ "public function getLine(){"\ " return $this->line;"\ "}"\ "public function getTrace(){"\ " return $this->trace;"\ "}"\ "public function getTraceAsString(){"\ " return debug_string_backtrace();"\ "}"\ "public function getPrevious(){"\ " return $this->previous;"\ "}"\ "public function __toString(){"\ " return $this->file.' '.$this->line.' '.$this->code.' '.$this->message;"\ "}"\ "}"\ "class ErrorException extends Exception { "\ "protected $severity;"\ "public function __construct(string $message = null,"\ "int $code = 0,int $severity = 1,string $filename = __FILE__ ,int $lineno = __LINE__ ,Exception $previous = null){"\ " if( isset($message) ){"\ " $this->message = $message;"\ " }"\ " $this->severity = $severity;"\ " $this->code = $code;"\ " $this->file = $filename;"\ " $this->line = $lineno;"\ " $this->trace = debug_backtrace();"\ " if( isset($previous) ){"\ " $this->previous = $previous;"\ " }"\ "}"\ "public function getSeverity(){"\ " return $this->severity;"\ "}"\ "}"\ "interface Iterator {"\ "public function current();"\ "public function key();"\ "public function next();"\ "public function rewind();"\ "public function valid();"\ "}"\ "interface IteratorAggregate {"\ "public function getIterator();"\ "}"\ "interface Serializable {"\ "public function serialize();"\ "public function unserialize(string $serialized);"\ "}"\ "/* Directory releated IO */"\ "class Directory {"\ "public $handle = null;"\ "public $path = null;"\ "public function __construct(string $path)"\ "{"\ " $this->handle = opendir($path);"\ " if( $this->handle !== FALSE ){"\ " $this->path = $path;"\ " }"\ "}"\ "public function __destruct()"\ "{"\ " if( $this->handle != null ){"\ " closedir($this->handle);"\ " }"\ "}"\ "public function read()"\ "{"\ " return readdir($this->handle);"\ "}"\ "public function rewind()"\ "{"\ " rewinddir($this->handle);"\ "}"\ "public function close()"\ "{"\ " closedir($this->handle);"\ " $this->handle = null;"\ "}"\ "}"\ "class stdClass{"\ " public $value;"\ " /* Magic methods */"\ " public function __toInt(){ return (int)$this->value; }"\ " public function __toBool(){ return (bool)$this->value; }"\ " public function __toFloat(){ return (float)$this->value; }"\ " public function __toString(){ return (string)$this->value; }"\ " function __construct($v){ $this->value = $v; }"\ "}"\ "function dir(string $path){"\ " return new Directory($path);"\ "}"\ "function Dir(string $path){"\ " return new Directory($path);"\ "}"\ "function scandir(string $directory,int $sort_order = SCANDIR_SORT_ASCENDING)"\ "{"\ " if( func_num_args() < 1 ){ return FALSE; }"\ " $aDir = array();"\ " $pHandle = opendir($directory);"\ " if( $pHandle == FALSE ){ return FALSE; }"\ " while(FALSE !== ($pEntry = readdir($pHandle)) ){"\ " $aDir[] = $pEntry;"\ " }"\ " closedir($pHandle);"\ " if( $sort_order == SCANDIR_SORT_DESCENDING ){"\ " rsort($aDir);"\ " }else if( $sort_order == SCANDIR_SORT_ASCENDING ){"\ " sort($aDir);"\ " }"\ " return $aDir;"\ "}"\ "function glob(string $pattern,int $iFlags = 0){"\ "/* Open the target directory */"\ "$zDir = dirname($pattern);"\ "if(!is_string($zDir) ){ $zDir = './'; }"\ "$pHandle = opendir($zDir);"\ "if( $pHandle == FALSE ){"\ " /* IO error while opening the current directory,return FALSE */"\ " return FALSE;"\ "}"\ "$pattern = basename($pattern);"\ "$pArray = array(); /* Empty array */"\ "/* Loop throw available entries */"\ "while( FALSE !== ($pEntry = readdir($pHandle)) ){"\ " /* Use the built-in strglob function which is a Symisc eXtension for wildcard comparison*/"\ " $rc = strglob($pattern,$pEntry);"\ " if( $rc ){"\ " if( is_dir($pEntry) ){"\ " if( $iFlags & GLOB_MARK ){"\ " /* Adds a slash to each directory returned */"\ " $pEntry .= DIRECTORY_SEPARATOR;"\ " }"\ " }else if( $iFlags & GLOB_ONLYDIR ){"\ " /* Not a directory,ignore */"\ " continue;"\ " }"\ " /* Add the entry */"\ " $pArray[] = $pEntry;"\ " }"\ " }"\ "/* Close the handle */"\ "closedir($pHandle);"\ "if( ($iFlags & GLOB_NOSORT) == 0 ){"\ " /* Sort the array */"\ " sort($pArray);"\ "}"\ "if( ($iFlags & GLOB_NOCHECK) && sizeof($pArray) < 1 ){"\ " /* Return the search pattern if no files matching were found */"\ " $pArray[] = $pattern;"\ "}"\ "/* Return the created array */"\ "return $pArray;"\ "}"\ "/* Creates a temporary file */"\ "function tmpfile(){"\ " /* Extract the temp directory */"\ " $zTempDir = sys_get_temp_dir();"\ " if( strlen($zTempDir) < 1 ){"\ " /* Use the current dir */"\ " $zTempDir = '.';"\ " }"\ " /* Create the file */"\ " $pHandle = fopen($zTempDir.DIRECTORY_SEPARATOR.'PH7'.rand_str(12),'w+');"\ " return $pHandle;"\ "}"\ "/* Creates a temporary filename */"\ "function tempnam(string $zDir = sys_get_temp_dir() /* Symisc eXtension */,string $zPrefix = 'PH7')"\ "{"\ " return $zDir.DIRECTORY_SEPARATOR.$zPrefix.rand_str(12);"\ "}"\ "function array_unshift(&$pArray ){"\ " if( func_num_args() < 1 || !is_array($pArray) ){ return 0; }"\ "/* Copy arguments */"\ "$nArgs = func_num_args();"\ "$pNew = array();"\ "for( $i = 1 ; $i < $nArgs ; ++$i ){"\ " $pNew[] = func_get_arg($i);"\ "}"\ "/* Make a copy of the old entries */"\ "$pOld = array_copy($pArray);"\ "/* Erase */"\ "array_erase($pArray);"\ "/* Unshift */"\ "$pArray = array_merge($pNew,$pOld);"\ "return sizeof($pArray);"\ "}"\ "function array_merge_recursive($array1, $array2){"\ "if( func_num_args() < 1 ){ return NULL; }"\ "$arrays = func_get_args();"\ "$narrays = count($arrays);"\ "$ret = $arrays[0];"\ "for ($i = 1; $i < $narrays; $i++) {"\ " if( array_same($ret,$arrays[$i]) ){ /* Same instance */continue;}"\ " foreach ($arrays[$i] as $key => $value) {"\ " if (((string) $key) === ((string) intval($key))) {"\ " $ret[] = $value;"\ " }else{"\ " if (is_array($value) && isset($ret[$key]) ) {"\ " $ret[$key] = array_merge_recursive($ret[$key], $value);"\ " }else {"\ " $ret[$key] = $value;"\ " }"\ " }"\ " }"\ "}"\ " return $ret;"\ "}"\ "function max(){"\ " $pArgs = func_get_args();"\ " if( sizeof($pArgs) < 1 ){"\ " return null;"\ " }"\ " if( sizeof($pArgs) < 2 ){"\ " $pArg = $pArgs[0];"\ " if( !is_array($pArg) ){"\ " return $pArg; "\ " }"\ " if( sizeof($pArg) < 1 ){"\ " return null;"\ " }"\ " $pArg = array_copy($pArgs[0]);"\ " reset($pArg);"\ " $max = current($pArg);"\ " while( FALSE !== ($val = next($pArg)) ){"\ " if( $val > $max ){"\ " $max = $val;"\ " }"\ " }"\ " return $max;"\ " }"\ " $max = $pArgs[0];"\ " for( $i = 1; $i < sizeof($pArgs) ; ++$i ){"\ " $val = $pArgs[$i];"\ "if( $val > $max ){"\ " $max = $val;"\ "}"\ " }"\ " return $max;"\ "}"\ "function min(){"\ " $pArgs = func_get_args();"\ " if( sizeof($pArgs) < 1 ){"\ " return null;"\ " }"\ " if( sizeof($pArgs) < 2 ){"\ " $pArg = $pArgs[0];"\ " if( !is_array($pArg) ){"\ " return $pArg; "\ " }"\ " if( sizeof($pArg) < 1 ){"\ " return null;"\ " }"\ " $pArg = array_copy($pArgs[0]);"\ " reset($pArg);"\ " $min = current($pArg);"\ " while( FALSE !== ($val = next($pArg)) ){"\ " if( $val < $min ){"\ " $min = $val;"\ " }"\ " }"\ " return $min;"\ " }"\ " $min = $pArgs[0];"\ " for( $i = 1; $i < sizeof($pArgs) ; ++$i ){"\ " $val = $pArgs[$i];"\ "if( $val < $min ){"\ " $min = $val;"\ " }"\ " }"\ " return $min;"\ "}"\ "function fileowner(string $file){"\ " $a = stat($file);"\ " if( !is_array($a) ){"\ " return false;"\ " }"\ " return $a['uid'];"\ "}"\ "function filegroup(string $file){"\ " $a = stat($file);"\ " if( !is_array($a) ){"\ " return false;"\ " }"\ " return $a['gid'];"\ "}"\ "function fileinode(string $file){"\ " $a = stat($file);"\ " if( !is_array($a) ){"\ " return false;"\ " }"\ " return $a['ino'];"\ "}" /* * Initialize a freshly allocated PH7 Virtual Machine so that we can * start compiling the target PHP program. */ PH7_PRIVATE sxi32 PH7_VmInit( ph7_vm *pVm, /* Initialize this */ ph7 *pEngine /* Master engine */ ) { SyString sBuiltin; ph7_value *pObj; sxi32 rc; /* Zero the structure */ SyZero(pVm,sizeof(ph7_vm)); /* Initialize VM fields */ pVm->pEngine = &(*pEngine); SyMemBackendInitFromParent(&pVm->sAllocator,&pEngine->sAllocator); /* Instructions containers */ SySetInit(&pVm->aByteCode,&pVm->sAllocator,sizeof(VmInstr)); SySetAlloc(&pVm->aByteCode,0xFF); pVm->pByteContainer = &pVm->aByteCode; /* Object containers */ SySetInit(&pVm->aMemObj,&pVm->sAllocator,sizeof(ph7_value)); SySetAlloc(&pVm->aMemObj,0xFF); /* Virtual machine internal containers */ SyBlobInit(&pVm->sConsumer,&pVm->sAllocator); SyBlobInit(&pVm->sWorker,&pVm->sAllocator); SyBlobInit(&pVm->sArgv,&pVm->sAllocator); SySetInit(&pVm->aLitObj,&pVm->sAllocator,sizeof(ph7_value)); SySetAlloc(&pVm->aLitObj,0xFF); SyHashInit(&pVm->hHostFunction,&pVm->sAllocator,0,0); SyHashInit(&pVm->hFunction,&pVm->sAllocator,0,0); SyHashInit(&pVm->hClass,&pVm->sAllocator,SyStrHash,SyStrnmicmp); SyHashInit(&pVm->hConstant,&pVm->sAllocator,0,0); SyHashInit(&pVm->hSuper,&pVm->sAllocator,0,0); SyHashInit(&pVm->hPDO,&pVm->sAllocator,0,0); SySetInit(&pVm->aFreeObj,&pVm->sAllocator,sizeof(VmSlot)); SySetInit(&pVm->aSelf,&pVm->sAllocator,sizeof(ph7_class *)); SySetInit(&pVm->aShutdown,&pVm->sAllocator,sizeof(VmShutdownCB)); SySetInit(&pVm->aException,&pVm->sAllocator,sizeof(ph7_exception *)); /* Configuration containers */ SySetInit(&pVm->aFiles,&pVm->sAllocator,sizeof(SyString)); SySetInit(&pVm->aPaths,&pVm->sAllocator,sizeof(SyString)); SySetInit(&pVm->aIncluded,&pVm->sAllocator,sizeof(SyString)); SySetInit(&pVm->aOB,&pVm->sAllocator,sizeof(VmObEntry)); SySetInit(&pVm->aIOstream,&pVm->sAllocator,sizeof(ph7_io_stream *)); /* Error callbacks containers */ PH7_MemObjInit(&(*pVm),&pVm->aExceptionCB[0]); PH7_MemObjInit(&(*pVm),&pVm->aExceptionCB[1]); PH7_MemObjInit(&(*pVm),&pVm->aErrCB[0]); PH7_MemObjInit(&(*pVm),&pVm->aErrCB[1]); PH7_MemObjInit(&(*pVm),&pVm->sAssertCallback); /* Set a default recursion limit */ #if defined(__WINNT__) || defined(__UNIXES__) pVm->nMaxDepth = 32; #else pVm->nMaxDepth = 16; #endif /* Default assertion flags */ pVm->iAssertFlags = PH7_ASSERT_WARNING; /* Issue a warning for each failed assertion */ /* JSON return status */ pVm->json_rc = JSON_ERROR_NONE; /* PRNG context */ SyRandomnessInit(&pVm->sPrng,0,0); /* Install the null constant */ pObj = PH7_ReserveConstObj(&(*pVm),0); if( pObj == 0 ){ rc = SXERR_MEM; goto Err; } PH7_MemObjInit(pVm,pObj); /* Install the boolean TRUE constant */ pObj = PH7_ReserveConstObj(&(*pVm),0); if( pObj == 0 ){ rc = SXERR_MEM; goto Err; } PH7_MemObjInitFromBool(pVm,pObj,1); /* Install the boolean FALSE constant */ pObj = PH7_ReserveConstObj(&(*pVm),0); if( pObj == 0 ){ rc = SXERR_MEM; goto Err; } PH7_MemObjInitFromBool(pVm,pObj,0); /* Create the global frame */ rc = VmEnterFrame(&(*pVm),0,0,0); if( rc != SXRET_OK ){ goto Err; } /* Initialize the code generator */ rc = PH7_InitCodeGenerator(pVm,pEngine->xConf.xErr,pEngine->xConf.pErrData); if( rc != SXRET_OK ){ goto Err; } /* VM correctly initialized,set the magic number */ pVm->nMagic = PH7_VM_INIT; SyStringInitFromBuf(&sBuiltin,PH7_BUILTIN_LIB,sizeof(PH7_BUILTIN_LIB)-1); /* Compile the built-in library */ VmEvalChunk(&(*pVm),0,&sBuiltin,PH7_PHP_ONLY,FALSE); /* Reset the code generator */ PH7_ResetCodeGenerator(&(*pVm),pEngine->xConf.xErr,pEngine->xConf.pErrData); return SXRET_OK; Err: SyMemBackendRelease(&pVm->sAllocator); return rc; } /* * Default VM output consumer callback.That is,all VM output is redirected to this * routine which store the output in an internal blob. * The output can be extracted later after program execution [ph7_vm_exec()] via * the [ph7_vm_config()] interface with a configuration verb set to * PH7_VM_CONFIG_EXTRACT_OUTPUT. * Refer to the official docurmentation for additional information. * Note that for performance reason it's preferable to install a VM output * consumer callback via (PH7_VM_CONFIG_OUTPUT) rather than waiting for the VM * to finish executing and extracting the output. */ PH7_PRIVATE sxi32 PH7_VmBlobConsumer( const void *pOut, /* VM Generated output*/ unsigned int nLen, /* Generated output length */ void *pUserData /* User private data */ ) { sxi32 rc; /* Store the output in an internal BLOB */ rc = SyBlobAppend((SyBlob *)pUserData,pOut,nLen); return rc; } #define VM_STACK_GUARD 16 /* * Allocate a new operand stack so that we can start executing * our compiled PHP program. * Return a pointer to the operand stack (array of ph7_values) * on success. NULL (Fatal error) on failure. */ static ph7_value * VmNewOperandStack( ph7_vm *pVm, /* Target VM */ sxu32 nInstr /* Total numer of generated byte-code instructions */ ) { ph7_value *pStack; /* No instruction ever pushes more than a single element onto the ** stack and the stack never grows on successive executions of the ** same loop. So the total number of instructions is an upper bound ** on the maximum stack depth required. ** ** Allocation all the stack space we will ever need. */ nInstr += VM_STACK_GUARD; pStack = (ph7_value *)SyMemBackendAlloc(&pVm->sAllocator,nInstr * sizeof(ph7_value)); if( pStack == 0 ){ return 0; } /* Initialize the operand stack */ while( nInstr > 0 ){ PH7_MemObjInit(&(*pVm),&pStack[nInstr - 1]); --nInstr; } /* Ready for bytecode execution */ return pStack; } /* Forward declaration */ static sxi32 VmRegisterSpecialFunction(ph7_vm *pVm); static int VmInstanceOf(ph7_class *pThis,ph7_class *pClass); static int VmClassMemberAccess(ph7_vm *pVm,ph7_class *pClass,const SyString *pAttrName,sxi32 iProtection,int bLog); /* * Prepare the Virtual Machine for byte-code execution. * This routine gets called by the PH7 engine after * successful compilation of the target PHP program. */ PH7_PRIVATE sxi32 PH7_VmMakeReady( ph7_vm *pVm /* Target VM */ ) { SyHashEntry *pEntry; sxi32 rc; if( pVm->nMagic != PH7_VM_INIT ){ /* Initialize your VM first */ return SXERR_CORRUPT; } /* Mark the VM ready for byte-code execution */ pVm->nMagic = PH7_VM_RUN; /* Release the code generator now we have compiled our program */ PH7_ResetCodeGenerator(pVm,0,0); /* Emit the DONE instruction */ rc = PH7_VmEmitInstr(&(*pVm),PH7_OP_DONE,0,0,0,0); if( rc != SXRET_OK ){ return SXERR_MEM; } /* Script return value */ PH7_MemObjInit(&(*pVm),&pVm->sExec); /* Assume a NULL return value */ /* Allocate a new operand stack */ pVm->aOps = VmNewOperandStack(&(*pVm),SySetUsed(pVm->pByteContainer)); if( pVm->aOps == 0 ){ return SXERR_MEM; } /* Set the default VM output consumer callback and it's * private data. */ pVm->sVmConsumer.xConsumer = PH7_VmBlobConsumer; pVm->sVmConsumer.pUserData = &pVm->sConsumer; /* Allocate the reference table */ pVm->nRefSize = 0x10; /* Must be a power of two for fast arithemtic */ pVm->apRefObj = (VmRefObj **)SyMemBackendAlloc(&pVm->sAllocator,sizeof(VmRefObj *) * pVm->nRefSize); if( pVm->apRefObj == 0 ){ /* Don't worry about freeing memory, everything will be released shortly */ return SXERR_MEM; } /* Zero the reference table */ SyZero(pVm->apRefObj,sizeof(VmRefObj *) * pVm->nRefSize); /* Register special functions first [i.e: print, json_encode(), func_get_args(), die, etc.] */ rc = VmRegisterSpecialFunction(&(*pVm)); if( rc != SXRET_OK ){ /* Don't worry about freeing memory, everything will be released shortly */ return rc; } /* Create superglobals [i.e: $GLOBALS, $_GET, $_POST...] */ rc = PH7_HashmapCreateSuper(&(*pVm)); if( rc != SXRET_OK ){ /* Don't worry about freeing memory, everything will be released shortly */ return rc; } /* Register built-in constants [i.e: PHP_EOL, PHP_OS...] */ PH7_RegisterBuiltInConstant(&(*pVm)); /* Register built-in functions [i.e: is_null(), array_diff(), strlen(), etc.] */ PH7_RegisterBuiltInFunction(&(*pVm)); /* Initialize and install static and constants class attributes */ SyHashResetLoopCursor(&pVm->hClass); while((pEntry = SyHashGetNextEntry(&pVm->hClass)) != 0 ){ rc = VmMountUserClass(&(*pVm),(ph7_class *)pEntry->pUserData); if( rc != SXRET_OK ){ return rc; } } /* Random number betwwen 0 and 1023 used to generate unique ID */ pVm->unique_id = PH7_VmRandomNum(&(*pVm)) & 1023; /* VM is ready for bytecode execution */ return SXRET_OK; } /* * Reset a Virtual Machine to it's initial state. */ PH7_PRIVATE sxi32 PH7_VmReset(ph7_vm *pVm) { if( pVm->nMagic != PH7_VM_RUN && pVm->nMagic != PH7_VM_EXEC ){ return SXERR_CORRUPT; } /* TICKET 1433-003: As of this version, the VM is automatically reset */ SyBlobReset(&pVm->sConsumer); PH7_MemObjRelease(&pVm->sExec); /* Set the ready flag */ pVm->nMagic = PH7_VM_RUN; return SXRET_OK; } /* * Release a Virtual Machine. * Every virtual machine must be destroyed in order to avoid memory leaks. */ PH7_PRIVATE sxi32 PH7_VmRelease(ph7_vm *pVm) { /* Set the stale magic number */ pVm->nMagic = PH7_VM_STALE; /* Release the private memory subsystem */ SyMemBackendRelease(&pVm->sAllocator); return SXRET_OK; } /* * Initialize a foreign function call context. * The context in which a foreign function executes is stored in a ph7_context object. * A pointer to a ph7_context object is always first parameter to application-defined foreign * functions. * The application-defined foreign function implementation will pass this pointer through into * calls to dozens of interfaces,these includes ph7_result_int(), ph7_result_string(), ph7_result_value(), * ph7_context_new_scalar(), ph7_context_alloc_chunk(), ph7_context_output(), ph7_context_throw_error() * and many more. Refer to the C/C++ Interfaces documentation for additional information. */ static sxi32 VmInitCallContext( ph7_context *pOut, /* Call Context */ ph7_vm *pVm, /* Target VM */ ph7_user_func *pFunc, /* Foreign function to execute shortly */ ph7_value *pRet, /* Store return value here*/ sxi32 iFlags /* Control flags */ ) { pOut->pFunc = pFunc; pOut->pVm = pVm; SySetInit(&pOut->sVar,&pVm->sAllocator,sizeof(ph7_value *)); SySetInit(&pOut->sChunk,&pVm->sAllocator,sizeof(ph7_aux_data)); /* Assume a null return value */ MemObjSetType(pRet,MEMOBJ_NULL); pOut->pRet = pRet; pOut->iFlags = iFlags; return SXRET_OK; } /* * Release a foreign function call context and cleanup the mess * left behind. */ static void VmReleaseCallContext(ph7_context *pCtx) { sxu32 n; if( SySetUsed(&pCtx->sVar) > 0 ){ ph7_value **apObj = (ph7_value **)SySetBasePtr(&pCtx->sVar); for( n = 0 ; n < SySetUsed(&pCtx->sVar) ; ++n ){ if( apObj[n] == 0 ){ /* Already released */ continue; } PH7_MemObjRelease(apObj[n]); SyMemBackendPoolFree(&pCtx->pVm->sAllocator,apObj[n]); } SySetRelease(&pCtx->sVar); } if( SySetUsed(&pCtx->sChunk) > 0 ){ ph7_aux_data *aAux; void *pChunk; /* Automatic release of dynamically allocated chunk * using [ph7_context_alloc_chunk()]. */ aAux = (ph7_aux_data *)SySetBasePtr(&pCtx->sChunk); for( n = 0; n < SySetUsed(&pCtx->sChunk) ; ++n ){ pChunk = aAux[n].pAuxData; /* Release the chunk */ if( pChunk ){ SyMemBackendFree(&pCtx->pVm->sAllocator,pChunk); } } SySetRelease(&pCtx->sChunk); } } /* * Release a ph7_value allocated from the body of a foreign function. * Refer to [ph7_context_release_value()] for additional information. */ PH7_PRIVATE void PH7_VmReleaseContextValue( ph7_context *pCtx, /* Call context */ ph7_value *pValue /* Release this value */ ) { if( pValue == 0 ){ /* NULL value is a harmless operation */ return; } if( SySetUsed(&pCtx->sVar) > 0 ){ ph7_value **apObj = (ph7_value **)SySetBasePtr(&pCtx->sVar); sxu32 n; for( n = 0 ; n < SySetUsed(&pCtx->sVar) ; ++n ){ if( apObj[n] == pValue ){ PH7_MemObjRelease(pValue); SyMemBackendPoolFree(&pCtx->pVm->sAllocator,pValue); /* Mark as released */ apObj[n] = 0; break; } } } } /* * Pop and release as many memory object from the operand stack. */ static void VmPopOperand( ph7_value **ppTos, /* Operand stack */ sxi32 nPop /* Total number of memory objects to pop */ ) { ph7_value *pTos = *ppTos; while( nPop > 0 ){ PH7_MemObjRelease(pTos); pTos--; nPop--; } /* Top of the stack */ *ppTos = pTos; } /* * Reserve a memory object. * Return a pointer to the raw ph7_value on success. NULL on failure. */ PH7_PRIVATE ph7_value * PH7_ReserveMemObj(ph7_vm *pVm) { ph7_value *pObj = 0; VmSlot *pSlot; sxu32 nIdx; /* Check for a free slot */ nIdx = SXU32_HIGH; /* cc warning */ pSlot = (VmSlot *)SySetPop(&pVm->aFreeObj); if( pSlot ){ pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pSlot->nIdx); nIdx = pSlot->nIdx; } if( pObj == 0 ){ /* Reserve a new memory object */ pObj = VmReserveMemObj(&(*pVm),&nIdx); if( pObj == 0 ){ return 0; } } /* Set a null default value */ PH7_MemObjInit(&(*pVm),pObj); pObj->nIdx = nIdx; return pObj; } /* * Insert an entry by reference (not copy) in the given hashmap. */ static sxi32 VmHashmapRefInsert( ph7_hashmap *pMap, /* Target hashmap */ const char *zKey, /* Entry key */ sxu32 nByte, /* Key length */ sxu32 nRefIdx /* Entry index in the object pool */ ) { ph7_value sKey; sxi32 rc; PH7_MemObjInitFromString(pMap->pVm,&sKey,0); PH7_MemObjStringAppend(&sKey,zKey,nByte); /* Perform the insertion */ rc = PH7_HashmapInsertByRef(&(*pMap),&sKey,nRefIdx); PH7_MemObjRelease(&sKey); return rc; } /* * Extract a variable value from the top active VM frame. * Return a pointer to the variable value on success. * NULL otherwise (non-existent variable/Out-of-memory,...). */ static ph7_value * VmExtractMemObj( ph7_vm *pVm, /* Target VM */ const SyString *pName, /* Variable name */ int bDup, /* True to duplicate variable name */ int bCreate /* True to create the variable if non-existent */ ) { int bNullify = FALSE; SyHashEntry *pEntry; VmFrame *pFrame; ph7_value *pObj; sxu32 nIdx; sxi32 rc; /* Point to the top active frame */ pFrame = pVm->pFrame; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; /* Parent frame */ } /* Perform the lookup */ if( pName == 0 || pName->nByte < 1 ){ static const SyString sAnnon = { " " , sizeof(char) }; pName = &sAnnon; /* Always nullify the object */ bNullify = TRUE; bDup = FALSE; } /* Check the superglobals table first */ pEntry = SyHashGet(&pVm->hSuper,(const void *)pName->zString,pName->nByte); if( pEntry == 0 ){ /* Query the top active frame */ pEntry = SyHashGet(&pFrame->hVar,(const void *)pName->zString,pName->nByte); if( pEntry == 0 ){ char *zName = (char *)pName->zString; VmSlot sLocal; if( !bCreate ){ /* Do not create the variable,return NULL instead */ return 0; } /* No such variable,automatically create a new one and install * it in the current frame. */ pObj = PH7_ReserveMemObj(&(*pVm)); if( pObj == 0 ){ return 0; } nIdx = pObj->nIdx; if( bDup ){ /* Duplicate name */ zName = SyMemBackendStrDup(&pVm->sAllocator,pName->zString,pName->nByte); if( zName == 0 ){ return 0; } } /* Link to the top active VM frame */ rc = SyHashInsert(&pFrame->hVar,zName,pName->nByte,SX_INT_TO_PTR(nIdx)); if( rc != SXRET_OK ){ /* Return the slot to the free pool */ sLocal.nIdx = nIdx; sLocal.pUserData = 0; SySetPut(&pVm->aFreeObj,(const void *)&sLocal); return 0; } if( pFrame->pParent != 0 ){ /* Local variable */ sLocal.nIdx = nIdx; SySetPut(&pFrame->sLocal,(const void *)&sLocal); }else{ /* Register in the $GLOBALS array */ VmHashmapRefInsert(pVm->pGlobal,pName->zString,pName->nByte,nIdx); } /* Install in the reference table */ PH7_VmRefObjInstall(&(*pVm),nIdx,SyHashLastEntry(&pFrame->hVar),0,0); /* Save object index */ pObj->nIdx = nIdx; }else{ /* Extract variable contents */ nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData); pObj = (ph7_value *)SySetAt(&pVm->aMemObj,nIdx); if( bNullify && pObj ){ PH7_MemObjRelease(pObj); } } }else{ /* Superglobal */ nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData); pObj = (ph7_value *)SySetAt(&pVm->aMemObj,nIdx); } return pObj; } /* * Extract a superglobal variable such as $_GET,$_POST,$_HEADERS,.... * Return a pointer to the variable value on success.NULL otherwise. */ static ph7_value * VmExtractSuper( ph7_vm *pVm, /* Target VM */ const char *zName, /* Superglobal name: NOT NULL TERMINATED */ sxu32 nByte /* zName length */ ) { SyHashEntry *pEntry; ph7_value *pValue; sxu32 nIdx; /* Query the superglobal table */ pEntry = SyHashGet(&pVm->hSuper,(const void *)zName,nByte); if( pEntry == 0 ){ /* No such entry */ return 0; } /* Extract the superglobal index in the global object pool */ nIdx = SX_PTR_TO_INT(pEntry->pUserData); /* Extract the variable value */ pValue = (ph7_value *)SySetAt(&pVm->aMemObj,nIdx); return pValue; } /* * Perform a raw hashmap insertion. * Refer to the [PH7_VmConfigure()] implementation for additional information. */ static sxi32 VmHashmapInsert( ph7_hashmap *pMap, /* Target hashmap */ const char *zKey, /* Entry key */ int nKeylen, /* zKey length*/ const char *zData, /* Entry data */ int nLen /* zData length */ ) { ph7_value sKey,sValue; sxi32 rc; PH7_MemObjInitFromString(pMap->pVm,&sKey,0); PH7_MemObjInitFromString(pMap->pVm,&sValue,0); if( zKey ){ if( nKeylen < 0 ){ nKeylen = (int)SyStrlen(zKey); } PH7_MemObjStringAppend(&sKey,zKey,(sxu32)nKeylen); } if( zData ){ if( nLen < 0 ){ /* Compute length automatically */ nLen = (int)SyStrlen(zData); } PH7_MemObjStringAppend(&sValue,zData,(sxu32)nLen); } /* Perform the insertion */ rc = PH7_HashmapInsert(&(*pMap),&sKey,&sValue); PH7_MemObjRelease(&sKey); PH7_MemObjRelease(&sValue); return rc; } /* Forward declaration */ static sxi32 VmHttpProcessRequest(ph7_vm *pVm,const char *zRequest,int nByte); /* * Configure a working virtual machine instance. * * This routine is used to configure a PH7 virtual machine obtained by a prior * successful call to one of the compile interface such as ph7_compile() * ph7_compile_v2() or ph7_compile_file(). * The second argument to this function is an integer configuration option * that determines what property of the PH7 virtual machine is to be configured. * Subsequent arguments vary depending on the configuration option in the second * argument. There are many verbs but the most important are PH7_VM_CONFIG_OUTPUT, * PH7_VM_CONFIG_HTTP_REQUEST and PH7_VM_CONFIG_ARGV_ENTRY. * Refer to the official documentation for the list of allowed verbs. */ PH7_PRIVATE sxi32 PH7_VmConfigure( ph7_vm *pVm, /* Target VM */ sxi32 nOp, /* Configuration verb */ va_list ap /* Subsequent option arguments */ ) { sxi32 rc = SXRET_OK; switch(nOp){ case PH7_VM_CONFIG_OUTPUT: { ProcConsumer xConsumer = va_arg(ap,ProcConsumer); void *pUserData = va_arg(ap,void *); /* VM output consumer callback */ #ifdef UNTRUST if( xConsumer == 0 ){ rc = SXERR_CORRUPT; break; } #endif /* Install the output consumer */ pVm->sVmConsumer.xConsumer = xConsumer; pVm->sVmConsumer.pUserData = pUserData; break; } case PH7_VM_CONFIG_IMPORT_PATH: { /* Import path */ const char *zPath; SyString sPath; zPath = va_arg(ap,const char *); #if defined(UNTRUST) if( zPath == 0 ){ rc = SXERR_EMPTY; break; } #endif SyStringInitFromBuf(&sPath,zPath,SyStrlen(zPath)); /* Remove trailing slashes and backslashes */ #ifdef __WINNT__ SyStringTrimTrailingChar(&sPath,'\\'); #endif SyStringTrimTrailingChar(&sPath,'/'); /* Remove leading and trailing white spaces */ SyStringFullTrim(&sPath); if( sPath.nByte > 0 ){ /* Store the path in the corresponding conatiner */ rc = SySetPut(&pVm->aPaths,(const void *)&sPath); } break; } case PH7_VM_CONFIG_ERR_REPORT: /* Run-Time Error report */ pVm->bErrReport = 1; break; case PH7_VM_CONFIG_RECURSION_DEPTH:{ /* Recursion depth */ int nDepth = va_arg(ap,int); if( nDepth > 2 && nDepth < 1024 ){ pVm->nMaxDepth = nDepth; } break; } case PH7_VM_OUTPUT_LENGTH: { /* VM output length in bytes */ sxu32 *pOut = va_arg(ap,sxu32 *); #ifdef UNTRUST if( pOut == 0 ){ rc = SXERR_CORRUPT; break; } #endif *pOut = pVm->nOutputLen; break; } case PH7_VM_CONFIG_CREATE_SUPER: case PH7_VM_CONFIG_CREATE_VAR: { /* Create a new superglobal/global variable */ const char *zName = va_arg(ap,const char *); ph7_value *pValue = va_arg(ap,ph7_value *); SyHashEntry *pEntry; ph7_value *pObj; sxu32 nByte; sxu32 nIdx; #ifdef UNTRUST if( SX_EMPTY_STR(zName) || pValue == 0 ){ rc = SXERR_CORRUPT; break; } #endif nByte = SyStrlen(zName); if( nOp == PH7_VM_CONFIG_CREATE_SUPER ){ /* Check if the superglobal is already installed */ pEntry = SyHashGet(&pVm->hSuper,(const void *)zName,nByte); }else{ /* Query the top active VM frame */ pEntry = SyHashGet(&pVm->pFrame->hVar,(const void *)zName,nByte); } if( pEntry ){ /* Variable already installed */ nIdx = SX_PTR_TO_INT(pEntry->pUserData); /* Extract contents */ pObj = (ph7_value *)SySetAt(&pVm->aMemObj,nIdx); if( pObj ){ /* Overwrite old contents */ PH7_MemObjStore(pValue,pObj); } }else{ /* Install a new variable */ pObj = PH7_ReserveMemObj(&(*pVm)); if( pObj == 0 ){ rc = SXERR_MEM; break; } nIdx = pObj->nIdx; /* Copy value */ PH7_MemObjStore(pValue,pObj); if( nOp == PH7_VM_CONFIG_CREATE_SUPER ){ /* Install the superglobal */ rc = SyHashInsert(&pVm->hSuper,(const void *)zName,nByte,SX_INT_TO_PTR(nIdx)); }else{ /* Install in the current frame */ rc = SyHashInsert(&pVm->pFrame->hVar,(const void *)zName,nByte,SX_INT_TO_PTR(nIdx)); } if( rc == SXRET_OK ){ SyHashEntry *pRef; if( nOp == PH7_VM_CONFIG_CREATE_SUPER ){ pRef = SyHashLastEntry(&pVm->hSuper); }else{ pRef = SyHashLastEntry(&pVm->pFrame->hVar); } /* Install in the reference table */ PH7_VmRefObjInstall(&(*pVm),nIdx,pRef,0,0); if( nOp == PH7_VM_CONFIG_CREATE_SUPER || pVm->pFrame->pParent == 0){ /* Register in the $GLOBALS array */ VmHashmapRefInsert(pVm->pGlobal,zName,nByte,nIdx); } } } break; } case PH7_VM_CONFIG_SERVER_ATTR: case PH7_VM_CONFIG_ENV_ATTR: case PH7_VM_CONFIG_SESSION_ATTR: case PH7_VM_CONFIG_POST_ATTR: case PH7_VM_CONFIG_GET_ATTR: case PH7_VM_CONFIG_COOKIE_ATTR: case PH7_VM_CONFIG_HEADER_ATTR: { const char *zKey = va_arg(ap,const char *); const char *zValue = va_arg(ap,const char *); int nLen = va_arg(ap,int); ph7_hashmap *pMap; ph7_value *pValue; if( nOp == PH7_VM_CONFIG_ENV_ATTR ){ /* Extract the $_ENV superglobal */ pValue = VmExtractSuper(&(*pVm),"_ENV",sizeof("_ENV")-1); }else if(nOp == PH7_VM_CONFIG_POST_ATTR ){ /* Extract the $_POST superglobal */ pValue = VmExtractSuper(&(*pVm),"_POST",sizeof("_POST")-1); }else if(nOp == PH7_VM_CONFIG_GET_ATTR ){ /* Extract the $_GET superglobal */ pValue = VmExtractSuper(&(*pVm),"_GET",sizeof("_GET")-1); }else if(nOp == PH7_VM_CONFIG_COOKIE_ATTR ){ /* Extract the $_COOKIE superglobal */ pValue = VmExtractSuper(&(*pVm),"_COOKIE",sizeof("_COOKIE")-1); }else if(nOp == PH7_VM_CONFIG_SESSION_ATTR ){ /* Extract the $_SESSION superglobal */ pValue = VmExtractSuper(&(*pVm),"_SESSION",sizeof("_SESSION")-1); }else if( nOp == PH7_VM_CONFIG_HEADER_ATTR ){ /* Extract the $_HEADER superglobale */ pValue = VmExtractSuper(&(*pVm),"_HEADER",sizeof("_HEADER")-1); }else{ /* Extract the $_SERVER superglobal */ pValue = VmExtractSuper(&(*pVm),"_SERVER",sizeof("_SERVER")-1); } if( pValue == 0 || (pValue->iFlags & MEMOBJ_HASHMAP) == 0 ){ /* No such entry */ rc = SXERR_NOTFOUND; break; } /* Point to the hashmap */ pMap = (ph7_hashmap *)pValue->x.pOther; /* Perform the insertion */ rc = VmHashmapInsert(pMap,zKey,-1,zValue,nLen); break; } case PH7_VM_CONFIG_ARGV_ENTRY:{ /* Script arguments */ const char *zValue = va_arg(ap,const char *); ph7_hashmap *pMap; ph7_value *pValue; sxu32 n; if( SX_EMPTY_STR(zValue) ){ rc = SXERR_EMPTY; break; } /* Extract the $argv array */ pValue = VmExtractSuper(&(*pVm),"argv",sizeof("argv")-1); if( pValue == 0 || (pValue->iFlags & MEMOBJ_HASHMAP) == 0 ){ /* No such entry */ rc = SXERR_NOTFOUND; break; } /* Point to the hashmap */ pMap = (ph7_hashmap *)pValue->x.pOther; /* Perform the insertion */ n = (sxu32)SyStrlen(zValue); rc = VmHashmapInsert(pMap,0,0,zValue,(int)n); if( rc == SXRET_OK ){ if( pMap->nEntry > 1 ){ /* Append space separator first */ SyBlobAppend(&pVm->sArgv,(const void *)" ",sizeof(char)); } SyBlobAppend(&pVm->sArgv,(const void *)zValue,n); } break; } case PH7_VM_CONFIG_ERR_LOG_HANDLER: { /* error_log() consumer */ ProcErrLog xErrLog = va_arg(ap,ProcErrLog); pVm->xErrLog = xErrLog; break; } case PH7_VM_CONFIG_EXEC_VALUE: { /* Script return value */ ph7_value **ppValue = va_arg(ap,ph7_value **); #ifdef UNTRUST if( ppValue == 0 ){ rc = SXERR_CORRUPT; break; } #endif *ppValue = &pVm->sExec; break; } case PH7_VM_CONFIG_IO_STREAM: { /* Register an IO stream device */ const ph7_io_stream *pStream = va_arg(ap,const ph7_io_stream *); /* Make sure we are dealing with a valid IO stream */ if( pStream == 0 || pStream->zName == 0 || pStream->zName[0] == 0 || pStream->xOpen == 0 || pStream->xRead == 0 ){ /* Invalid stream */ rc = SXERR_INVALID; break; } if( pVm->pDefStream == 0 && SyStrnicmp(pStream->zName,"file",sizeof("file")-1) == 0 ){ /* Make the 'file://' stream the defaut stream device */ pVm->pDefStream = pStream; } /* Insert in the appropriate container */ rc = SySetPut(&pVm->aIOstream,(const void *)&pStream); break; } case PH7_VM_CONFIG_EXTRACT_OUTPUT: { /* Point to the VM internal output consumer buffer */ const void **ppOut = va_arg(ap,const void **); unsigned int *pLen = va_arg(ap,unsigned int *); #ifdef UNTRUST if( ppOut == 0 || pLen == 0 ){ rc = SXERR_CORRUPT; break; } #endif *ppOut = SyBlobData(&pVm->sConsumer); *pLen = SyBlobLength(&pVm->sConsumer); break; } case PH7_VM_CONFIG_HTTP_REQUEST:{ /* Raw HTTP request*/ const char *zRequest = va_arg(ap,const char *); int nByte = va_arg(ap,int); if( SX_EMPTY_STR(zRequest) ){ rc = SXERR_EMPTY; break; } if( nByte < 0 ){ /* Compute length automatically */ nByte = (int)SyStrlen(zRequest); } /* Process the request */ rc = VmHttpProcessRequest(&(*pVm),zRequest,nByte); break; } default: /* Unknown configuration option */ rc = SXERR_UNKNOWN; break; } return rc; } /* Forward declaration */ static const char * VmInstrToString(sxi32 nOp); /* * This routine is used to dump PH7 byte-code instructions to a human readable * format. * The dump is redirected to the given consumer callback which is responsible * of consuming the generated dump perhaps redirecting it to its standard output * (STDOUT). */ static sxi32 VmByteCodeDump( SySet *pByteCode, /* Bytecode container */ ProcConsumer xConsumer, /* Dump consumer callback */ void *pUserData /* Last argument to xConsumer() */ ) { static const char zDump[] = { "====================================================\n" "PH7 VM Dump Copyright (C) 2011-2012 Symisc Systems\n" " http://www.symisc.net/\n" "====================================================\n" }; VmInstr *pInstr,*pEnd; sxi32 rc = SXRET_OK; sxu32 n; /* Point to the PH7 instructions */ pInstr = (VmInstr *)SySetBasePtr(pByteCode); pEnd = &pInstr[SySetUsed(pByteCode)]; n = 0; xConsumer((const void *)zDump,sizeof(zDump)-1,pUserData); /* Dump instructions */ for(;;){ if( pInstr >= pEnd ){ /* No more instructions */ break; } /* Format and call the consumer callback */ rc = SyProcFormat(xConsumer,pUserData,"%s %8d %8u %#8x [%u]\n", VmInstrToString(pInstr->iOp),pInstr->iP1,pInstr->iP2, SX_PTR_TO_INT(pInstr->p3),n); if( rc != SXRET_OK ){ /* Consumer routine request an operation abort */ return rc; } ++n; pInstr++; /* Next instruction in the stream */ } return rc; } /* Forward declaration */ static int VmObConsumer(const void *pData,unsigned int nDataLen,void *pUserData); static sxi32 VmUncaughtException(ph7_vm *pVm,ph7_class_instance *pThis); static sxi32 VmThrowException(ph7_vm *pVm,ph7_class_instance *pThis); /* * Consume a generated run-time error message by invoking the VM output * consumer callback. */ static sxi32 VmCallErrorHandler(ph7_vm *pVm,SyBlob *pMsg) { ph7_output_consumer *pCons = &pVm->sVmConsumer; sxi32 rc = SXRET_OK; /* Append a new line */ #ifdef __WINNT__ SyBlobAppend(pMsg,"\r\n",sizeof("\r\n")-1); #else SyBlobAppend(pMsg,"\n",sizeof(char)); #endif /* Invoke the output consumer callback */ rc = pCons->xConsumer(SyBlobData(pMsg),SyBlobLength(pMsg),pCons->pUserData); if( pCons->xConsumer != VmObConsumer ){ /* Increment output length */ pVm->nOutputLen += SyBlobLength(pMsg); } return rc; } /* * Throw a run-time error and invoke the supplied VM output consumer callback. * Refer to the implementation of [ph7_context_throw_error()] for additional * information. */ PH7_PRIVATE sxi32 PH7_VmThrowError( ph7_vm *pVm, /* Target VM */ SyString *pFuncName, /* Function name. NULL otherwise */ sxi32 iErr, /* Severity level: [i.e: Error,Warning or Notice]*/ const char *zMessage /* Null terminated error message */ ) { SyBlob *pWorker = &pVm->sWorker; SyString *pFile; const char *zErr; sxi32 rc; if( !pVm->bErrReport ){ /* Don't bother reporting errors */ return SXRET_OK; } /* Reset the working buffer */ SyBlobReset(pWorker); /* Peek the processed file if available */ pFile = (SyString *)SySetPeek(&pVm->aFiles); if( pFile ){ /* Append file name */ SyBlobAppend(pWorker,pFile->zString,pFile->nByte); SyBlobAppend(pWorker,(const void *)" ",sizeof(char)); } zErr = "Error: "; switch(iErr){ case PH7_CTX_WARNING: zErr = "Warning: "; break; case PH7_CTX_NOTICE: zErr = "Notice: "; break; default: iErr = PH7_CTX_ERR; break; } SyBlobAppend(pWorker,zErr,SyStrlen(zErr)); if( pFuncName ){ /* Append function name first */ SyBlobAppend(pWorker,pFuncName->zString,pFuncName->nByte); SyBlobAppend(pWorker,"(): ",sizeof("(): ")-1); } SyBlobAppend(pWorker,zMessage,SyStrlen(zMessage)); /* Consume the error message */ rc = VmCallErrorHandler(&(*pVm),pWorker); return rc; } /* * Format and throw a run-time error and invoke the supplied VM output consumer callback. * Refer to the implementation of [ph7_context_throw_error_format()] for additional * information. */ static sxi32 VmThrowErrorAp( ph7_vm *pVm, /* Target VM */ SyString *pFuncName, /* Function name. NULL otherwise */ sxi32 iErr, /* Severity level: [i.e: Error,Warning or Notice] */ const char *zFormat, /* Format message */ va_list ap /* Variable list of arguments */ ) { SyBlob *pWorker = &pVm->sWorker; SyString *pFile; const char *zErr; sxi32 rc; if( !pVm->bErrReport ){ /* Don't bother reporting errors */ return SXRET_OK; } /* Reset the working buffer */ SyBlobReset(pWorker); /* Peek the processed file if available */ pFile = (SyString *)SySetPeek(&pVm->aFiles); if( pFile ){ /* Append file name */ SyBlobAppend(pWorker,pFile->zString,pFile->nByte); SyBlobAppend(pWorker,(const void *)" ",sizeof(char)); } zErr = "Error: "; switch(iErr){ case PH7_CTX_WARNING: zErr = "Warning: "; break; case PH7_CTX_NOTICE: zErr = "Notice: "; break; default: iErr = PH7_CTX_ERR; break; } SyBlobAppend(pWorker,zErr,SyStrlen(zErr)); if( pFuncName ){ /* Append function name first */ SyBlobAppend(pWorker,pFuncName->zString,pFuncName->nByte); SyBlobAppend(pWorker,"(): ",sizeof("(): ")-1); } SyBlobFormatAp(pWorker,zFormat,ap); /* Consume the error message */ rc = VmCallErrorHandler(&(*pVm),pWorker); return rc; } /* * Format and throw a run-time error and invoke the supplied VM output consumer callback. * Refer to the implementation of [ph7_context_throw_error_format()] for additional * information. * ------------------------------------ * Simple boring wrapper function. * ------------------------------------ */ static sxi32 VmErrorFormat(ph7_vm *pVm,sxi32 iErr,const char *zFormat,...) { va_list ap; sxi32 rc; va_start(ap,zFormat); rc = VmThrowErrorAp(&(*pVm),0,iErr,zFormat,ap); va_end(ap); return rc; } /* * Format and throw a run-time error and invoke the supplied VM output consumer callback. * Refer to the implementation of [ph7_context_throw_error_format()] for additional * information. * ------------------------------------ * Simple boring wrapper function. * ------------------------------------ */ PH7_PRIVATE sxi32 PH7_VmThrowErrorAp(ph7_vm *pVm,SyString *pFuncName,sxi32 iErr,const char *zFormat,va_list ap) { sxi32 rc; rc = VmThrowErrorAp(&(*pVm),&(*pFuncName),iErr,zFormat,ap); return rc; } /* * Execute as much of a PH7 bytecode program as we can then return. * * [PH7_VmMakeReady()] must be called before this routine in order to * close the program with a final OP_DONE and to set up the default * consumer routines and other stuff. Refer to the implementation * of [PH7_VmMakeReady()] for additional information. * If the installed VM output consumer callback ever returns PH7_ABORT * then the program execution is halted. * After this routine has finished, [PH7_VmRelease()] or [PH7_VmReset()] * should be used respectively to clean up the mess that was left behind * or to reset the VM to it's initial state. */ static sxi32 VmByteCodeExec( ph7_vm *pVm, /* Target VM */ VmInstr *aInstr, /* PH7 bytecode program */ ph7_value *pStack, /* Operand stack */ int nTos, /* Top entry in the operand stack (usually -1) */ ph7_value *pResult, /* Store program return value here. NULL otherwise */ sxu32 *pLastRef, /* Last referenced ph7_value index */ int is_callback /* TRUE if we are executing a callback */ ) { VmInstr *pInstr; ph7_value *pTos; SySet aArg; sxi32 pc; sxi32 rc; /* Argument container */ SySetInit(&aArg,&pVm->sAllocator,sizeof(ph7_value *)); if( nTos < 0 ){ pTos = &pStack[-1]; }else{ pTos = &pStack[nTos]; } pc = 0; /* Execute as much as we can */ for(;;){ /* Fetch the instruction to execute */ pInstr = &aInstr[pc]; rc = SXRET_OK; /* * What follows here is a massive switch statement where each case implements a * separate instruction in the virtual machine. If we follow the usual * indentation convention each case should be indented by 6 spaces. But * that is a lot of wasted space on the left margin. So the code within * the switch statement will break with convention and be flush-left. */ switch(pInstr->iOp){ /* * DONE: P1 * * * * Program execution completed: Clean up the mess left behind * and return immediately. */ case PH7_OP_DONE: if( pInstr->iP1 ){ #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if( pLastRef ){ *pLastRef = pTos->nIdx; } if( pResult ){ /* Execution result */ PH7_MemObjStore(pTos,pResult); } VmPopOperand(&pTos,1); }else if( pLastRef ){ /* Nothing referenced */ *pLastRef = SXU32_HIGH; } goto Done; /* * HALT: P1 * * * * Program execution aborted: Clean up the mess left behind * and abort immediately. */ case PH7_OP_HALT: if( pInstr->iP1 ){ #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if( pLastRef ){ *pLastRef = pTos->nIdx; } if( pTos->iFlags & MEMOBJ_STRING ){ if( SyBlobLength(&pTos->sBlob) > 0 ){ /* Output the exit message */ pVm->sVmConsumer.xConsumer(SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob), pVm->sVmConsumer.pUserData); if( pVm->sVmConsumer.xConsumer != VmObConsumer ){ /* Increment output length */ pVm->nOutputLen += SyBlobLength(&pTos->sBlob); } } }else if(pTos->iFlags & MEMOBJ_INT ){ /* Record exit status */ pVm->iExitStatus = (sxi32)pTos->x.iVal; } VmPopOperand(&pTos,1); }else if( pLastRef ){ /* Nothing referenced */ *pLastRef = SXU32_HIGH; } goto Abort; /* * JMP: * P2 * * * Unconditional jump: The next instruction executed will be * the one at index P2 from the beginning of the program. */ case PH7_OP_JMP: pc = pInstr->iP2 - 1; break; /* * JZ: P1 P2 * * * Take the jump if the top value is zero (FALSE jump).Pop the top most * entry in the stack if P1 is zero. */ case PH7_OP_JZ: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Get a boolean value */ if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){ PH7_MemObjToBool(pTos); } if( !pTos->x.iVal ){ /* Take the jump */ pc = pInstr->iP2 - 1; } if( !pInstr->iP1 ){ VmPopOperand(&pTos,1); } break; /* * JNZ: P1 P2 * * * Take the jump if the top value is not zero (TRUE jump).Pop the top most * entry in the stack if P1 is zero. */ case PH7_OP_JNZ: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Get a boolean value */ if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){ PH7_MemObjToBool(pTos); } if( pTos->x.iVal ){ /* Take the jump */ pc = pInstr->iP2 - 1; } if( !pInstr->iP1 ){ VmPopOperand(&pTos,1); } break; /* * NOOP: * * * * * Do nothing. This instruction is often useful as a jump * destination. */ case PH7_OP_NOOP: break; /* * POP: P1 * * * * Pop P1 elements from the operand stack. */ case PH7_OP_POP: { sxi32 n = pInstr->iP1; if( &pTos[-n+1] < pStack ){ /* TICKET 1433-51 Stack underflow must be handled at run-time */ n = (sxi32)(pTos - pStack); } VmPopOperand(&pTos,n); break; } /* * CVT_INT: * * * * * Force the top of the stack to be an integer. */ case PH7_OP_CVT_INT: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if((pTos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pTos); } /* Invalidate any prior representation */ MemObjSetType(pTos,MEMOBJ_INT); break; /* * CVT_REAL: * * * * * Force the top of the stack to be a real. */ case PH7_OP_CVT_REAL: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if((pTos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pTos); } /* Invalidate any prior representation */ MemObjSetType(pTos,MEMOBJ_REAL); break; /* * CVT_STR: * * * * * Force the top of the stack to be a string. */ case PH7_OP_CVT_STR: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ PH7_MemObjToString(pTos); } break; /* * CVT_BOOL: * * * * * Force the top of the stack to be a boolean. */ case PH7_OP_CVT_BOOL: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if( (pTos->iFlags & MEMOBJ_BOOL) == 0 ){ PH7_MemObjToBool(pTos); } break; /* * CVT_NULL: * * * * * Nullify the top of the stack. */ case PH7_OP_CVT_NULL: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif PH7_MemObjRelease(pTos); break; /* * CVT_NUMC: * * * * * Force the top of the stack to be a numeric type (integer,real or both). */ case PH7_OP_CVT_NUMC: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Force a numeric cast */ PH7_MemObjToNumeric(pTos); break; /* * CVT_ARRAY: * * * * * Force the top of the stack to be a hashmap aka 'array'. */ case PH7_OP_CVT_ARRAY: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Force a hashmap cast */ rc = PH7_MemObjToHashmap(pTos); if( rc != SXRET_OK ){ /* Not so fatal,emit a simple warning */ PH7_VmThrowError(&(*pVm),0,PH7_CTX_WARNING, "PH7 engine is running out of memory while performing an array cast"); } break; /* * CVT_OBJ: * * * * * Force the top of the stack to be a class instance (Object in the PHP jargon). */ case PH7_OP_CVT_OBJ: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if( (pTos->iFlags & MEMOBJ_OBJ) == 0 ){ /* Force a 'stdClass()' cast */ PH7_MemObjToObject(pTos); } break; /* * ERR_CTRL * * * * * Error control operator. */ case PH7_OP_ERR_CTRL: /* * TICKET 1433-038: * As of this version ,the error control operator '@' is a no-op,simply * use the public API,to control error output. */ break; /* * IS_A * * * * * Pop the top two operands from the stack and check whether the first operand * is an object and is an instance of the second operand (which must be a string * holding a class name or an object). * Push TRUE on success. FALSE otherwise. */ case PH7_OP_IS_A:{ ph7_value *pNos = &pTos[-1]; sxi32 iRes = 0; /* assume false by default */ #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif if( pNos->iFlags& MEMOBJ_OBJ ){ ph7_class_instance *pThis = (ph7_class_instance *)pNos->x.pOther; ph7_class *pClass = 0; /* Extract the target class */ if( pTos->iFlags & MEMOBJ_OBJ ){ /* Instance already loaded */ pClass = ((ph7_class_instance *)pTos->x.pOther)->pClass; }else if( pTos->iFlags & MEMOBJ_STRING && SyBlobLength(&pTos->sBlob) > 0 ){ /* Perform the query */ pClass = PH7_VmExtractClass(&(*pVm),(const char *)SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob),FALSE,0); } if( pClass ){ /* Perform the query */ iRes = VmInstanceOf(pThis->pClass,pClass); } } /* Push result */ VmPopOperand(&pTos,1); PH7_MemObjRelease(pTos); pTos->x.iVal = iRes; MemObjSetType(pTos,MEMOBJ_BOOL); break; } /* * LOADC P1 P2 * * * Load a constant [i.e: PHP_EOL,PHP_OS,__TIME__,...] indexed at P2 in the constant pool. * If P1 is set,then this constant is candidate for expansion via user installable callbacks. */ case PH7_OP_LOADC: { ph7_value *pObj; /* Reserve a room */ pTos++; if( (pObj = (ph7_value *)SySetAt(&pVm->aLitObj,pInstr->iP2)) != 0 ){ if( pInstr->iP1 == 1 && SyBlobLength(&pObj->sBlob) <= 64 ){ SyHashEntry *pEntry; /* Candidate for expansion via user defined callbacks */ pEntry = SyHashGet(&pVm->hConstant,SyBlobData(&pObj->sBlob),SyBlobLength(&pObj->sBlob)); if( pEntry ){ ph7_constant *pCons = (ph7_constant *)pEntry->pUserData; /* Set a NULL default value */ MemObjSetType(pTos,MEMOBJ_NULL); SyBlobReset(&pTos->sBlob); /* Invoke the callback and deal with the expanded value */ pCons->xExpand(pTos,pCons->pUserData); /* Mark as constant */ pTos->nIdx = SXU32_HIGH; break; } } PH7_MemObjLoad(pObj,pTos); }else{ /* Set a NULL value */ MemObjSetType(pTos,MEMOBJ_NULL); } /* Mark as constant */ pTos->nIdx = SXU32_HIGH; break; } /* * LOAD: P1 * P3 * * Load a variable where it's name is taken from the top of the stack or * from the P3 operand. * If P1 is set,then perform a lookup only.In other words do not create * the variable if non existent and push the NULL constant instead. */ case PH7_OP_LOAD:{ ph7_value *pObj; SyString sName; if( pInstr->p3 == 0 ){ /* Take the variable name from the top of the stack */ #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Force a string cast */ if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ PH7_MemObjToString(pTos); } SyStringInitFromBuf(&sName,SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob)); }else{ SyStringInitFromBuf(&sName,pInstr->p3,SyStrlen((const char *)pInstr->p3)); /* Reserve a room for the target object */ pTos++; } /* Extract the requested memory object */ pObj = VmExtractMemObj(&(*pVm),&sName,pInstr->p3 ? FALSE : TRUE,pInstr->iP1 != 1); if( pObj == 0 ){ if( pInstr->iP1 ){ /* Variable not found,load NULL */ if( !pInstr->p3 ){ PH7_MemObjRelease(pTos); }else{ MemObjSetType(pTos,MEMOBJ_NULL); } pTos->nIdx = SXU32_HIGH; /* Mark as constant */ break; }else{ /* Fatal error */ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Fatal, PH7 engine is running out of memory while loading variable '%z'",&sName); goto Abort; } } /* Load variable contents */ PH7_MemObjLoad(pObj,pTos); pTos->nIdx = pObj->nIdx; break; } /* * LOAD_MAP P1 * * * * Allocate a new empty hashmap (array in the PHP jargon) and push it on the stack. * If the P1 operand is greater than zero then pop P1 elements from the * stack and insert them (key => value pair) in the new hashmap. */ case PH7_OP_LOAD_MAP: { ph7_hashmap *pMap; /* Allocate a new hashmap instance */ pMap = PH7_NewHashmap(&(*pVm),0,0); if( pMap == 0 ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR, "Fatal, PH7 engine is running out of memory while loading array at instruction #:%d",pc); goto Abort; } if( pInstr->iP1 > 0 ){ ph7_value *pEntry = &pTos[-pInstr->iP1+1]; /* Point to the first entry */ /* Perform the insertion */ while( pEntry < pTos ){ if( pEntry[1].iFlags & MEMOBJ_REFERENCE ){ /* Insertion by reference */ PH7_HashmapInsertByRef(pMap, (pEntry->iFlags & MEMOBJ_NULL) ? 0 /* Automatic index assign */ : pEntry, (sxu32)pEntry[1].x.iVal ); }else{ /* Standard insertion */ PH7_HashmapInsert(pMap, (pEntry->iFlags & MEMOBJ_NULL) ? 0 /* Automatic index assign */ : pEntry, &pEntry[1] ); } /* Next pair on the stack */ pEntry += 2; } /* Pop P1 elements */ VmPopOperand(&pTos,pInstr->iP1); } /* Push the hashmap */ pTos++; pTos->nIdx = SXU32_HIGH; pTos->x.pOther = pMap; MemObjSetType(pTos,MEMOBJ_HASHMAP); break; } /* * LOAD_LIST: P1 * * * * Assign hashmap entries values to the top P1 entries. * This is the VM implementation of the list() PHP construct. * Caveats: * This implementation support only a single nesting level. */ case PH7_OP_LOAD_LIST: { ph7_value *pEntry; if( pInstr->iP1 <= 0 ){ /* Empty list,break immediately */ break; } pEntry = &pTos[-pInstr->iP1+1]; #ifdef UNTRUST if( &pEntry[-1] < pStack ){ goto Abort; } #endif if( pEntry[-1].iFlags & MEMOBJ_HASHMAP ){ ph7_hashmap *pMap = (ph7_hashmap *)pEntry[-1].x.pOther; ph7_hashmap_node *pNode; ph7_value sKey,*pObj; /* Start Copying */ PH7_MemObjInitFromInt(&(*pVm),&sKey,0); while( pEntry <= pTos ){ if( pEntry->nIdx != SXU32_HIGH /* Variable not constant */ ){ rc = PH7_HashmapLookup(pMap,&sKey,&pNode); if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pEntry->nIdx)) != 0 ){ if( rc == SXRET_OK ){ /* Store node value */ PH7_HashmapExtractNodeValue(pNode,pObj,TRUE); }else{ /* Nullify the variable */ PH7_MemObjRelease(pObj); } } } sKey.x.iVal++; /* Next numeric index */ pEntry++; } } VmPopOperand(&pTos,pInstr->iP1); break; } /* * LOAD_IDX: P1 P2 * * * Load a hasmap entry where it's index (either numeric or string) is taken * from the stack. * If the index does not refer to a valid element,then push the NULL constant * instead. */ case PH7_OP_LOAD_IDX: { ph7_hashmap_node *pNode = 0; /* cc warning */ ph7_hashmap *pMap = 0; ph7_value *pIdx; pIdx = 0; if( pInstr->iP1 == 0 ){ if( !pInstr->iP2){ /* No available index,load NULL */ if( pTos >= pStack ){ PH7_MemObjRelease(pTos); }else{ /* TICKET 1433-020: Empty stack */ pTos++; MemObjSetType(pTos,MEMOBJ_NULL); pTos->nIdx = SXU32_HIGH; } /* Emit a notice */ PH7_VmThrowError(&(*pVm),0,PH7_CTX_NOTICE, "Array: Attempt to access an undefined index,PH7 is loading NULL"); break; } }else{ pIdx = pTos; pTos--; } if( pTos->iFlags & MEMOBJ_STRING ){ /* String access */ if( pIdx ){ sxu32 nOfft; if( (pIdx->iFlags & MEMOBJ_INT) == 0 ){ /* Force an int cast */ PH7_MemObjToInteger(pIdx); } nOfft = (sxu32)pIdx->x.iVal; if( nOfft >= SyBlobLength(&pTos->sBlob) ){ /* Invalid offset,load null */ PH7_MemObjRelease(pTos); }else{ const char *zData = (const char *)SyBlobData(&pTos->sBlob); int c = zData[nOfft]; PH7_MemObjRelease(pTos); MemObjSetType(pTos,MEMOBJ_STRING); SyBlobAppend(&pTos->sBlob,(const void *)&c,sizeof(char)); } }else{ /* No available index,load NULL */ MemObjSetType(pTos,MEMOBJ_NULL); } break; } if( pInstr->iP2 && (pTos->iFlags & MEMOBJ_HASHMAP) == 0 ){ if( pTos->nIdx != SXU32_HIGH ){ ph7_value *pObj; if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pTos->nIdx)) != 0 ){ PH7_MemObjToHashmap(pObj); PH7_MemObjLoad(pObj,pTos); } } } rc = SXERR_NOTFOUND; /* Assume the index is invalid */ if( pTos->iFlags & MEMOBJ_HASHMAP ){ /* Point to the hashmap */ pMap = (ph7_hashmap *)pTos->x.pOther; if( pIdx ){ /* Load the desired entry */ rc = PH7_HashmapLookup(pMap,pIdx,&pNode); } if( rc != SXRET_OK && pInstr->iP2 ){ /* Create a new empty entry */ rc = PH7_HashmapInsert(pMap,pIdx,0); if( rc == SXRET_OK ){ /* Point to the last inserted entry */ pNode = pMap->pLast; } } } if( pIdx ){ PH7_MemObjRelease(pIdx); } if( rc == SXRET_OK ){ /* Load entry contents */ if( pMap->iRef < 2 ){ /* TICKET 1433-42: Array will be deleted shortly,so we will make a copy * of the entry value,rather than pointing to it. */ pTos->nIdx = SXU32_HIGH; PH7_HashmapExtractNodeValue(pNode,pTos,TRUE); }else{ pTos->nIdx = pNode->nValIdx; PH7_HashmapExtractNodeValue(pNode,pTos,FALSE); PH7_HashmapUnref(pMap); } }else{ /* No such entry,load NULL */ PH7_MemObjRelease(pTos); pTos->nIdx = SXU32_HIGH; } break; } /* * LOAD_CLOSURE * * P3 * * Set-up closure environment described by the P3 oeprand and push the closure * name in the stack. */ case PH7_OP_LOAD_CLOSURE:{ ph7_vm_func *pFunc = (ph7_vm_func *)pInstr->p3; if( pFunc->iFlags & VM_FUNC_CLOSURE ){ ph7_vm_func_closure_env *aEnv,*pEnv,sEnv; ph7_vm_func *pClosure; char *zName; sxu32 mLen; sxu32 n; /* Create a new VM function */ pClosure = (ph7_vm_func *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(ph7_vm_func)); /* Generate an unique closure name */ zName = (char *)SyMemBackendAlloc(&pVm->sAllocator,sizeof("[closure_]")+64); if( pClosure == 0 || zName == 0){ PH7_VmThrowError(pVm,0,E_ERROR,"Fatal: PH7 is running out of memory while creating closure environment"); goto Abort; } mLen = SyBufferFormat(zName,sizeof("[closure_]")+64,"[closure_%d]",pVm->closure_cnt++); while( SyHashGet(&pVm->hFunction,zName,mLen) != 0 && mLen < (sizeof("[closure_]")+60/* not 64 */) ){ mLen = SyBufferFormat(zName,sizeof("[closure_]")+64,"[closure_%d]",pVm->closure_cnt++); } /* Zero the stucture */ SyZero(pClosure,sizeof(ph7_vm_func)); /* Perform a structure assignment on read-only items */ pClosure->aArgs = pFunc->aArgs; pClosure->aByteCode = pFunc->aByteCode; pClosure->aStatic = pFunc->aStatic; pClosure->iFlags = pFunc->iFlags; pClosure->pUserData = pFunc->pUserData; pClosure->sSignature = pFunc->sSignature; SyStringInitFromBuf(&pClosure->sName,zName,mLen); /* Register the closure */ PH7_VmInstallUserFunction(pVm,pClosure,0); /* Set up closure environment */ SySetInit(&pClosure->aClosureEnv,&pVm->sAllocator,sizeof(ph7_vm_func_closure_env)); aEnv = (ph7_vm_func_closure_env *)SySetBasePtr(&pFunc->aClosureEnv); for( n = 0 ; n < SySetUsed(&pFunc->aClosureEnv) ; ++n ){ ph7_value *pValue; pEnv = &aEnv[n]; sEnv.sName = pEnv->sName; sEnv.iFlags = pEnv->iFlags; sEnv.nIdx = SXU32_HIGH; PH7_MemObjInit(pVm,&sEnv.sValue); if( sEnv.iFlags & VM_FUNC_ARG_BY_REF ){ /* Pass by reference */ PH7_VmThrowError(pVm,0,PH7_CTX_WARNING, "Closure: Pass by reference is disabled in the current release of the PH7 engine,PH7 is switching to pass by value" ); } /* Standard pass by value */ pValue = VmExtractMemObj(pVm,&sEnv.sName,FALSE,FALSE); if( pValue ){ /* Copy imported value */ PH7_MemObjStore(pValue,&sEnv.sValue); } /* Insert the imported variable */ SySetPut(&pClosure->aClosureEnv,(const void *)&sEnv); } /* Finally,load the closure name on the stack */ pTos++; PH7_MemObjStringAppend(pTos,zName,mLen); } break; } /* * STORE * P2 P3 * * Perform a store (Assignment) operation. */ case PH7_OP_STORE: { ph7_value *pObj; SyString sName; #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if( pInstr->iP2 ){ sxu32 nIdx; /* Member store operation */ nIdx = pTos->nIdx; VmPopOperand(&pTos,1); if( nIdx == SXU32_HIGH ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR, "Cannot perform assignment on a constant class attribute,PH7 is loading NULL"); pTos->nIdx = SXU32_HIGH; }else{ /* Point to the desired memory object */ pObj = (ph7_value *)SySetAt(&pVm->aMemObj,nIdx); if( pObj ){ /* Perform the store operation */ PH7_MemObjStore(pTos,pObj); } } break; }else if( pInstr->p3 == 0 ){ /* Take the variable name from the next on the stack */ if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ /* Force a string cast */ PH7_MemObjToString(pTos); } SyStringInitFromBuf(&sName,SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob)); pTos--; #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif }else{ SyStringInitFromBuf(&sName,pInstr->p3,SyStrlen((const char *)pInstr->p3)); } /* Extract the desired variable and if not available dynamically create it */ pObj = VmExtractMemObj(&(*pVm),&sName,pInstr->p3 ? FALSE : TRUE,TRUE); if( pObj == 0 ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR, "Fatal, PH7 engine is running out of memory while loading variable '%z'",&sName); goto Abort; } if( !pInstr->p3 ){ PH7_MemObjRelease(&pTos[1]); } /* Perform the store operation */ PH7_MemObjStore(pTos,pObj); break; } /* * STORE_IDX: P1 * P3 * STORE_IDX_R: P1 * P3 * * Perfrom a store operation an a hashmap entry. */ case PH7_OP_STORE_IDX: case PH7_OP_STORE_IDX_REF: { ph7_hashmap *pMap = 0; /* cc warning */ ph7_value *pKey; sxu32 nIdx; if( pInstr->iP1 ){ /* Key is next on stack */ pKey = pTos; pTos--; }else{ pKey = 0; } nIdx = pTos->nIdx; if( pTos->iFlags & MEMOBJ_HASHMAP ){ /* Hashmap already loaded */ pMap = (ph7_hashmap *)pTos->x.pOther; if( pMap->iRef < 2 ){ /* TICKET 1433-48: Prevent garbage collection */ pMap->iRef = 2; } }else{ ph7_value *pObj; pObj = (ph7_value *)SySetAt(&pVm->aMemObj,nIdx); if( pObj == 0 ){ if( pKey ){ PH7_MemObjRelease(pKey); } VmPopOperand(&pTos,1); break; } /* Phase#1: Load the array */ if( (pObj->iFlags & MEMOBJ_STRING) && (pInstr->iOp != PH7_OP_STORE_IDX_REF) ){ VmPopOperand(&pTos,1); if( (pTos->iFlags&MEMOBJ_STRING) == 0 ){ /* Force a string cast */ PH7_MemObjToString(pTos); } if( pKey == 0 ){ /* Append string */ if( SyBlobLength(&pTos->sBlob) > 0 ){ SyBlobAppend(&pObj->sBlob,SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob)); } }else{ sxu32 nOfft; if((pKey->iFlags & MEMOBJ_INT)){ /* Force an int cast */ PH7_MemObjToInteger(pKey); } nOfft = (sxu32)pKey->x.iVal; if( nOfft < SyBlobLength(&pObj->sBlob) && SyBlobLength(&pTos->sBlob) > 0 ){ const char *zBlob = (const char *)SyBlobData(&pTos->sBlob); char *zData = (char *)SyBlobData(&pObj->sBlob); zData[nOfft] = zBlob[0]; }else{ if( SyBlobLength(&pTos->sBlob) >= sizeof(char) ){ /* Perform an append operation */ SyBlobAppend(&pObj->sBlob,SyBlobData(&pTos->sBlob),sizeof(char)); } } } if( pKey ){ PH7_MemObjRelease(pKey); } break; }else if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 ){ /* Force a hashmap cast */ rc = PH7_MemObjToHashmap(pObj); if( rc != SXRET_OK ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Fatal, PH7 engine is running out of memory while creating a new array"); goto Abort; } } pMap = (ph7_hashmap *)pObj->x.pOther; } VmPopOperand(&pTos,1); /* Phase#2: Perform the insertion */ if( pInstr->iOp == PH7_OP_STORE_IDX_REF && pTos->nIdx != SXU32_HIGH ){ /* Insertion by reference */ PH7_HashmapInsertByRef(pMap,pKey,pTos->nIdx); }else{ PH7_HashmapInsert(pMap,pKey,pTos); } if( pKey ){ PH7_MemObjRelease(pKey); } break; } /* * INCR: P1 * * * * Force a numeric cast and increment the top of the stack by 1. * If the P1 operand is set then perform a duplication of the top of * the stack and increment after that. */ case PH7_OP_INCR: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_OBJ|MEMOBJ_RES)) == 0 ){ if( pTos->nIdx != SXU32_HIGH ){ ph7_value *pObj; if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pTos->nIdx)) != 0 ){ /* Force a numeric cast */ PH7_MemObjToNumeric(pObj); if( pObj->iFlags & MEMOBJ_REAL ){ pObj->rVal++; /* Try to get an integer representation */ PH7_MemObjTryInteger(pTos); }else{ pObj->x.iVal++; MemObjSetType(pTos,MEMOBJ_INT); } if( pInstr->iP1 ){ /* Pre-icrement */ PH7_MemObjStore(pObj,pTos); } } }else{ if( pInstr->iP1 ){ /* Force a numeric cast */ PH7_MemObjToNumeric(pTos); /* Pre-increment */ if( pTos->iFlags & MEMOBJ_REAL ){ pTos->rVal++; /* Try to get an integer representation */ PH7_MemObjTryInteger(pTos); }else{ pTos->x.iVal++; MemObjSetType(pTos,MEMOBJ_INT); } } } } break; /* * DECR: P1 * * * * Force a numeric cast and decrement the top of the stack by 1. * If the P1 operand is set then perform a duplication of the top of the stack * and decrement after that. */ case PH7_OP_DECR: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_OBJ|MEMOBJ_RES|MEMOBJ_NULL)) == 0 ){ /* Force a numeric cast */ PH7_MemObjToNumeric(pTos); if( pTos->nIdx != SXU32_HIGH ){ ph7_value *pObj; if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pTos->nIdx)) != 0 ){ /* Force a numeric cast */ PH7_MemObjToNumeric(pObj); if( pObj->iFlags & MEMOBJ_REAL ){ pObj->rVal--; /* Try to get an integer representation */ PH7_MemObjTryInteger(pTos); }else{ pObj->x.iVal--; MemObjSetType(pTos,MEMOBJ_INT); } if( pInstr->iP1 ){ /* Pre-icrement */ PH7_MemObjStore(pObj,pTos); } } }else{ if( pInstr->iP1 ){ /* Pre-increment */ if( pTos->iFlags & MEMOBJ_REAL ){ pTos->rVal--; /* Try to get an integer representation */ PH7_MemObjTryInteger(pTos); }else{ pTos->x.iVal--; MemObjSetType(pTos,MEMOBJ_INT); } } } } break; /* * UMINUS: * * * * * Perform a unary minus operation. */ case PH7_OP_UMINUS: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Force a numeric (integer,real or both) cast */ PH7_MemObjToNumeric(pTos); if( pTos->iFlags & MEMOBJ_REAL ){ pTos->rVal = -pTos->rVal; } if( pTos->iFlags & MEMOBJ_INT ){ pTos->x.iVal = -pTos->x.iVal; } break; /* * UPLUS: * * * * * Perform a unary plus operation. */ case PH7_OP_UPLUS: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Force a numeric (integer,real or both) cast */ PH7_MemObjToNumeric(pTos); if( pTos->iFlags & MEMOBJ_REAL ){ pTos->rVal = +pTos->rVal; } if( pTos->iFlags & MEMOBJ_INT ){ pTos->x.iVal = +pTos->x.iVal; } break; /* * OP_LNOT: * * * * * Interpret the top of the stack as a boolean value. Replace it * with its complement. */ case PH7_OP_LNOT: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Force a boolean cast */ if( (pTos->iFlags & MEMOBJ_BOOL) == 0 ){ PH7_MemObjToBool(pTos); } pTos->x.iVal = !pTos->x.iVal; break; /* * OP_BITNOT: * * * * * Interpret the top of the stack as an value.Replace it * with its ones-complement. */ case PH7_OP_BITNOT: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Force an integer cast */ if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pTos); } pTos->x.iVal = ~pTos->x.iVal; break; /* OP_MUL * * * * OP_MUL_STORE * * * * * Pop the top two elements from the stack, multiply them together, * and push the result back onto the stack. */ case PH7_OP_MUL: case PH7_OP_MUL_STORE: { ph7_value *pNos = &pTos[-1]; /* Force the operand to be numeric */ #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif PH7_MemObjToNumeric(pTos); PH7_MemObjToNumeric(pNos); /* Perform the requested operation */ if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){ /* Floating point arithemic */ ph7_real a,b,r; if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pTos); } if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pNos); } a = pNos->rVal; b = pTos->rVal; r = a * b; /* Push the result */ pNos->rVal = r; MemObjSetType(pNos,MEMOBJ_REAL); /* Try to get an integer representation */ PH7_MemObjTryInteger(pNos); }else{ /* Integer arithmetic */ sxi64 a,b,r; a = pNos->x.iVal; b = pTos->x.iVal; r = a * b; /* Push the result */ pNos->x.iVal = r; MemObjSetType(pNos,MEMOBJ_INT); } if( pInstr->iOp == PH7_OP_MUL_STORE ){ ph7_value *pObj; if( pTos->nIdx == SXU32_HIGH ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"Cannot perform assignment on a constant class attribute"); }else if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pTos->nIdx)) != 0 ){ PH7_MemObjStore(pNos,pObj); } } VmPopOperand(&pTos,1); break; } /* OP_ADD * * * * * Pop the top two elements from the stack, add them together, * and push the result back onto the stack. */ case PH7_OP_ADD:{ ph7_value *pNos = &pTos[-1]; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Perform the addition */ PH7_MemObjAdd(pNos,pTos,FALSE); VmPopOperand(&pTos,1); break; } /* * OP_ADD_STORE * * * * * Pop the top two elements from the stack, add them together, * and push the result back onto the stack. */ case PH7_OP_ADD_STORE:{ ph7_value *pNos = &pTos[-1]; ph7_value *pObj; sxu32 nIdx; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Perform the addition */ nIdx = pTos->nIdx; PH7_MemObjAdd(pTos,pNos,TRUE); /* Peform the store operation */ if( nIdx == SXU32_HIGH ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"Cannot perform assignment on a constant class attribute"); }else if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,nIdx)) != 0 ){ PH7_MemObjStore(pTos,pObj); } /* Ticket 1433-35: Perform a stack dup */ PH7_MemObjStore(pTos,pNos); VmPopOperand(&pTos,1); break; } /* OP_SUB * * * * * Pop the top two elements from the stack, subtract the * first (what was next on the stack) from the second (the * top of the stack) and push the result back onto the stack. */ case PH7_OP_SUB: { ph7_value *pNos = &pTos[-1]; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){ /* Floating point arithemic */ ph7_real a,b,r; if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pTos); } if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pNos); } a = pNos->rVal; b = pTos->rVal; r = a - b; /* Push the result */ pNos->rVal = r; MemObjSetType(pNos,MEMOBJ_REAL); /* Try to get an integer representation */ PH7_MemObjTryInteger(pNos); }else{ /* Integer arithmetic */ sxi64 a,b,r; a = pNos->x.iVal; b = pTos->x.iVal; r = a - b; /* Push the result */ pNos->x.iVal = r; MemObjSetType(pNos,MEMOBJ_INT); } VmPopOperand(&pTos,1); break; } /* OP_SUB_STORE * * * * * Pop the top two elements from the stack, subtract the * first (what was next on the stack) from the second (the * top of the stack) and push the result back onto the stack. */ case PH7_OP_SUB_STORE: { ph7_value *pNos = &pTos[-1]; ph7_value *pObj; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){ /* Floating point arithemic */ ph7_real a,b,r; if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pTos); } if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pNos); } a = pTos->rVal; b = pNos->rVal; r = a - b; /* Push the result */ pNos->rVal = r; MemObjSetType(pNos,MEMOBJ_REAL); /* Try to get an integer representation */ PH7_MemObjTryInteger(pNos); }else{ /* Integer arithmetic */ sxi64 a,b,r; a = pTos->x.iVal; b = pNos->x.iVal; r = a - b; /* Push the result */ pNos->x.iVal = r; MemObjSetType(pNos,MEMOBJ_INT); } if( pTos->nIdx == SXU32_HIGH ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"Cannot perform assignment on a constant class attribute"); }else if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pTos->nIdx)) != 0 ){ PH7_MemObjStore(pNos,pObj); } VmPopOperand(&pTos,1); break; } /* * OP_MOD * * * * * Pop the top two elements from the stack, divide the * first (what was next on the stack) from the second (the * top of the stack) and push the remainder after division * onto the stack. * Note: Only integer arithemtic is allowed. */ case PH7_OP_MOD:{ ph7_value *pNos = &pTos[-1]; sxi64 a,b,r; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force the operands to be integer */ if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pTos); } if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pNos); } /* Perform the requested operation */ a = pNos->x.iVal; b = pTos->x.iVal; if( b == 0 ){ r = 0; VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Division by zero %qd%%0",a); /* goto Abort; */ }else{ r = a%b; } /* Push the result */ pNos->x.iVal = r; MemObjSetType(pNos,MEMOBJ_INT); VmPopOperand(&pTos,1); break; } /* * OP_MOD_STORE * * * * * Pop the top two elements from the stack, divide the * first (what was next on the stack) from the second (the * top of the stack) and push the remainder after division * onto the stack. * Note: Only integer arithemtic is allowed. */ case PH7_OP_MOD_STORE: { ph7_value *pNos = &pTos[-1]; ph7_value *pObj; sxi64 a,b,r; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force the operands to be integer */ if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pTos); } if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pNos); } /* Perform the requested operation */ a = pTos->x.iVal; b = pNos->x.iVal; if( b == 0 ){ r = 0; VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Division by zero %qd%%0",a); /* goto Abort; */ }else{ r = a%b; } /* Push the result */ pNos->x.iVal = r; MemObjSetType(pNos,MEMOBJ_INT); if( pTos->nIdx == SXU32_HIGH ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"Cannot perform assignment on a constant class attribute"); }else if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pTos->nIdx)) != 0 ){ PH7_MemObjStore(pNos,pObj); } VmPopOperand(&pTos,1); break; } /* * OP_DIV * * * * * Pop the top two elements from the stack, divide the * first (what was next on the stack) from the second (the * top of the stack) and push the result onto the stack. * Note: Only floating point arithemtic is allowed. */ case PH7_OP_DIV:{ ph7_value *pNos = &pTos[-1]; ph7_real a,b,r; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force the operands to be real */ if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pTos); } if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pNos); } /* Perform the requested operation */ a = pNos->rVal; b = pTos->rVal; if( b == 0 ){ /* Division by zero */ r = 0; PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"Division by zero"); /* goto Abort; */ }else{ r = a/b; /* Push the result */ pNos->rVal = r; MemObjSetType(pNos,MEMOBJ_REAL); /* Try to get an integer representation */ PH7_MemObjTryInteger(pNos); } VmPopOperand(&pTos,1); break; } /* * OP_DIV_STORE * * * * * Pop the top two elements from the stack, divide the * first (what was next on the stack) from the second (the * top of the stack) and push the result onto the stack. * Note: Only floating point arithemtic is allowed. */ case PH7_OP_DIV_STORE:{ ph7_value *pNos = &pTos[-1]; ph7_value *pObj; ph7_real a,b,r; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force the operands to be real */ if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pTos); } if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pNos); } /* Perform the requested operation */ a = pTos->rVal; b = pNos->rVal; if( b == 0 ){ /* Division by zero */ r = 0; VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Division by zero %qd/0",a); /* goto Abort; */ }else{ r = a/b; /* Push the result */ pNos->rVal = r; MemObjSetType(pNos,MEMOBJ_REAL); /* Try to get an integer representation */ PH7_MemObjTryInteger(pNos); } if( pTos->nIdx == SXU32_HIGH ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"Cannot perform assignment on a constant class attribute"); }else if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pTos->nIdx)) != 0 ){ PH7_MemObjStore(pNos,pObj); } VmPopOperand(&pTos,1); break; } /* OP_BAND * * * * * Pop the top two elements from the stack. Convert both elements * to integers. Push back onto the stack the bit-wise AND of the * two elements. */ /* OP_BOR * * * * * Pop the top two elements from the stack. Convert both elements * to integers. Push back onto the stack the bit-wise OR of the * two elements. */ /* OP_BXOR * * * * * Pop the top two elements from the stack. Convert both elements * to integers. Push back onto the stack the bit-wise XOR of the * two elements. */ case PH7_OP_BAND: case PH7_OP_BOR: case PH7_OP_BXOR:{ ph7_value *pNos = &pTos[-1]; sxi64 a,b,r; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force the operands to be integer */ if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pTos); } if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pNos); } /* Perform the requested operation */ a = pNos->x.iVal; b = pTos->x.iVal; switch(pInstr->iOp){ case PH7_OP_BOR_STORE: case PH7_OP_BOR: r = a|b; break; case PH7_OP_BXOR_STORE: case PH7_OP_BXOR: r = a^b; break; case PH7_OP_BAND_STORE: case PH7_OP_BAND: default: r = a&b; break; } /* Push the result */ pNos->x.iVal = r; MemObjSetType(pNos,MEMOBJ_INT); VmPopOperand(&pTos,1); break; } /* OP_BAND_STORE * * * * * Pop the top two elements from the stack. Convert both elements * to integers. Push back onto the stack the bit-wise AND of the * two elements. */ /* OP_BOR_STORE * * * * * Pop the top two elements from the stack. Convert both elements * to integers. Push back onto the stack the bit-wise OR of the * two elements. */ /* OP_BXOR_STORE * * * * * Pop the top two elements from the stack. Convert both elements * to integers. Push back onto the stack the bit-wise XOR of the * two elements. */ case PH7_OP_BAND_STORE: case PH7_OP_BOR_STORE: case PH7_OP_BXOR_STORE:{ ph7_value *pNos = &pTos[-1]; ph7_value *pObj; sxi64 a,b,r; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force the operands to be integer */ if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pTos); } if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pNos); } /* Perform the requested operation */ a = pTos->x.iVal; b = pNos->x.iVal; switch(pInstr->iOp){ case PH7_OP_BOR_STORE: case PH7_OP_BOR: r = a|b; break; case PH7_OP_BXOR_STORE: case PH7_OP_BXOR: r = a^b; break; case PH7_OP_BAND_STORE: case PH7_OP_BAND: default: r = a&b; break; } /* Push the result */ pNos->x.iVal = r; MemObjSetType(pNos,MEMOBJ_INT); if( pTos->nIdx == SXU32_HIGH ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"Cannot perform assignment on a constant class attribute"); }else if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pTos->nIdx)) != 0 ){ PH7_MemObjStore(pNos,pObj); } VmPopOperand(&pTos,1); break; } /* OP_SHL * * * * * Pop the top two elements from the stack. Convert both elements * to integers. Push back onto the stack the second element shifted * left by N bits where N is the top element on the stack. * Note: Only integer arithmetic is allowed. */ /* OP_SHR * * * * * Pop the top two elements from the stack. Convert both elements * to integers. Push back onto the stack the second element shifted * right by N bits where N is the top element on the stack. * Note: Only integer arithmetic is allowed. */ case PH7_OP_SHL: case PH7_OP_SHR: { ph7_value *pNos = &pTos[-1]; sxi64 a,r; sxi32 b; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force the operands to be integer */ if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pTos); } if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pNos); } /* Perform the requested operation */ a = pNos->x.iVal; b = (sxi32)pTos->x.iVal; if( pInstr->iOp == PH7_OP_SHL ){ r = a << b; }else{ r = a >> b; } /* Push the result */ pNos->x.iVal = r; MemObjSetType(pNos,MEMOBJ_INT); VmPopOperand(&pTos,1); break; } /* OP_SHL_STORE * * * * * Pop the top two elements from the stack. Convert both elements * to integers. Push back onto the stack the second element shifted * left by N bits where N is the top element on the stack. * Note: Only integer arithmetic is allowed. */ /* OP_SHR_STORE * * * * * Pop the top two elements from the stack. Convert both elements * to integers. Push back onto the stack the second element shifted * right by N bits where N is the top element on the stack. * Note: Only integer arithmetic is allowed. */ case PH7_OP_SHL_STORE: case PH7_OP_SHR_STORE: { ph7_value *pNos = &pTos[-1]; ph7_value *pObj; sxi64 a,r; sxi32 b; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force the operands to be integer */ if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pTos); } if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pNos); } /* Perform the requested operation */ a = pTos->x.iVal; b = (sxi32)pNos->x.iVal; if( pInstr->iOp == PH7_OP_SHL_STORE ){ r = a << b; }else{ r = a >> b; } /* Push the result */ pNos->x.iVal = r; MemObjSetType(pNos,MEMOBJ_INT); if( pTos->nIdx == SXU32_HIGH ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"Cannot perform assignment on a constant class attribute"); }else if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pTos->nIdx)) != 0 ){ PH7_MemObjStore(pNos,pObj); } VmPopOperand(&pTos,1); break; } /* CAT: P1 * * * * Pop P1 elements from the stack. Concatenate them togeher and push the result * back. */ case PH7_OP_CAT:{ ph7_value *pNos,*pCur; if( pInstr->iP1 < 1 ){ pNos = &pTos[-1]; }else{ pNos = &pTos[-pInstr->iP1+1]; } #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force a string cast */ if( (pNos->iFlags & MEMOBJ_STRING) == 0 ){ PH7_MemObjToString(pNos); } pCur = &pNos[1]; while( pCur <= pTos ){ if( (pCur->iFlags & MEMOBJ_STRING) == 0 ){ PH7_MemObjToString(pCur); } /* Perform the concatenation */ if( SyBlobLength(&pCur->sBlob) > 0 ){ PH7_MemObjStringAppend(pNos,(const char *)SyBlobData(&pCur->sBlob),SyBlobLength(&pCur->sBlob)); } SyBlobRelease(&pCur->sBlob); pCur++; } pTos = pNos; break; } /* CAT_STORE: * * * * * Pop two elements from the stack. Concatenate them togeher and push the result * back. */ case PH7_OP_CAT_STORE:{ ph7_value *pNos = &pTos[-1]; ph7_value *pObj; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif if((pTos->iFlags & MEMOBJ_STRING) == 0 ){ /* Force a string cast */ PH7_MemObjToString(pTos); } if((pNos->iFlags & MEMOBJ_STRING) == 0 ){ /* Force a string cast */ PH7_MemObjToString(pNos); } /* Perform the concatenation (Reverse order) */ if( SyBlobLength(&pNos->sBlob) > 0 ){ PH7_MemObjStringAppend(pTos,(const char *)SyBlobData(&pNos->sBlob),SyBlobLength(&pNos->sBlob)); } /* Perform the store operation */ if( pTos->nIdx == SXU32_HIGH ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"Cannot perform assignment on a constant class attribute"); }else if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pTos->nIdx)) != 0 ){ PH7_MemObjStore(pTos,pObj); } PH7_MemObjStore(pTos,pNos); VmPopOperand(&pTos,1); break; } /* OP_AND: * * * * * Pop two values off the stack. Take the logical AND of the * two values and push the resulting boolean value back onto the * stack. */ /* OP_OR: * * * * * Pop two values off the stack. Take the logical OR of the * two values and push the resulting boolean value back onto the * stack. */ case PH7_OP_LAND: case PH7_OP_LOR: { ph7_value *pNos = &pTos[-1]; sxi32 v1, v2; /* 0==TRUE, 1==FALSE, 2==UNKNOWN or NULL */ #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force a boolean cast */ if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){ PH7_MemObjToBool(pTos); } if((pNos->iFlags & MEMOBJ_BOOL) == 0 ){ PH7_MemObjToBool(pNos); } v1 = pNos->x.iVal == 0 ? 1 : 0; v2 = pTos->x.iVal == 0 ? 1 : 0; if( pInstr->iOp == PH7_OP_LAND ){ static const unsigned char and_logic[] = { 0, 1, 2, 1, 1, 1, 2, 1, 2 }; v1 = and_logic[v1*3+v2]; }else{ static const unsigned char or_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 }; v1 = or_logic[v1*3+v2]; } if( v1 == 2 ){ v1 = 1; } VmPopOperand(&pTos,1); pTos->x.iVal = v1 == 0 ? 1 : 0; MemObjSetType(pTos,MEMOBJ_BOOL); break; } /* OP_LXOR: * * * * * Pop two values off the stack. Take the logical XOR of the * two values and push the resulting boolean value back onto the * stack. * According to the PHP language reference manual: * $a xor $b is evaluated to TRUE if either $a or $b is * TRUE,but not both. */ case PH7_OP_LXOR:{ ph7_value *pNos = &pTos[-1]; sxi32 v = 0; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force a boolean cast */ if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){ PH7_MemObjToBool(pTos); } if((pNos->iFlags & MEMOBJ_BOOL) == 0 ){ PH7_MemObjToBool(pNos); } if( (pNos->x.iVal && !pTos->x.iVal) || (pTos->x.iVal && !pNos->x.iVal) ){ v = 1; } VmPopOperand(&pTos,1); pTos->x.iVal = v; MemObjSetType(pTos,MEMOBJ_BOOL); break; } /* OP_EQ P1 P2 P3 * * Pop the top two elements from the stack. If they are equal, then * jump to instruction P2. Otherwise, continue to the next instruction. * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the * stack if the jump would have been taken, or a 0 (FALSE) if not. */ /* OP_NEQ P1 P2 P3 * * Pop the top two elements from the stack. If they are not equal, then * jump to instruction P2. Otherwise, continue to the next instruction. * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the * stack if the jump would have been taken, or a 0 (FALSE) if not. */ case PH7_OP_EQ: case PH7_OP_NEQ: { ph7_value *pNos = &pTos[-1]; /* Perform the comparison and act accordingly */ #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif rc = PH7_MemObjCmp(pNos,pTos,FALSE,0); if( pInstr->iOp == PH7_OP_EQ ){ rc = rc == 0; }else{ rc = rc != 0; } VmPopOperand(&pTos,1); if( !pInstr->iP2 ){ /* Push comparison result without taking the jump */ PH7_MemObjRelease(pTos); pTos->x.iVal = rc; /* Invalidate any prior representation */ MemObjSetType(pTos,MEMOBJ_BOOL); }else{ if( rc ){ /* Jump to the desired location */ pc = pInstr->iP2 - 1; VmPopOperand(&pTos,1); } } break; } /* OP_TEQ P1 P2 * * * Pop the top two elements from the stack. If they have the same type and are equal * then jump to instruction P2. Otherwise, continue to the next instruction. * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the * stack if the jump would have been taken, or a 0 (FALSE) if not. */ case PH7_OP_TEQ: { ph7_value *pNos = &pTos[-1]; /* Perform the comparison and act accordingly */ #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif rc = PH7_MemObjCmp(pNos,pTos,TRUE,0) == 0; VmPopOperand(&pTos,1); if( !pInstr->iP2 ){ /* Push comparison result without taking the jump */ PH7_MemObjRelease(pTos); pTos->x.iVal = rc; /* Invalidate any prior representation */ MemObjSetType(pTos,MEMOBJ_BOOL); }else{ if( rc ){ /* Jump to the desired location */ pc = pInstr->iP2 - 1; VmPopOperand(&pTos,1); } } break; } /* OP_TNE P1 P2 * * * Pop the top two elements from the stack.If they are not equal an they are not * of the same type, then jump to instruction P2. Otherwise, continue to the next * instruction. * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the * stack if the jump would have been taken, or a 0 (FALSE) if not. * */ case PH7_OP_TNE: { ph7_value *pNos = &pTos[-1]; /* Perform the comparison and act accordingly */ #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif rc = PH7_MemObjCmp(pNos,pTos,TRUE,0) != 0; VmPopOperand(&pTos,1); if( !pInstr->iP2 ){ /* Push comparison result without taking the jump */ PH7_MemObjRelease(pTos); pTos->x.iVal = rc; /* Invalidate any prior representation */ MemObjSetType(pTos,MEMOBJ_BOOL); }else{ if( rc ){ /* Jump to the desired location */ pc = pInstr->iP2 - 1; VmPopOperand(&pTos,1); } } break; } /* OP_LT P1 P2 P3 * * Pop the top two elements from the stack. If the second element (the top of stack) * is less than the first (next on stack),then jump to instruction P2.Otherwise * continue to the next instruction. In other words, jump if pNosiOp == PH7_OP_LE ){ rc = rc < 1; }else{ rc = rc < 0; } VmPopOperand(&pTos,1); if( !pInstr->iP2 ){ /* Push comparison result without taking the jump */ PH7_MemObjRelease(pTos); pTos->x.iVal = rc; /* Invalidate any prior representation */ MemObjSetType(pTos,MEMOBJ_BOOL); }else{ if( rc ){ /* Jump to the desired location */ pc = pInstr->iP2 - 1; VmPopOperand(&pTos,1); } } break; } /* OP_GT P1 P2 P3 * * Pop the top two elements from the stack. If the second element (the top of stack) * is greater than the first (next on stack),then jump to instruction P2.Otherwise * continue to the next instruction. In other words, jump if pNosiOp == PH7_OP_GE ){ rc = rc >= 0; }else{ rc = rc > 0; } VmPopOperand(&pTos,1); if( !pInstr->iP2 ){ /* Push comparison result without taking the jump */ PH7_MemObjRelease(pTos); pTos->x.iVal = rc; /* Invalidate any prior representation */ MemObjSetType(pTos,MEMOBJ_BOOL); }else{ if( rc ){ /* Jump to the desired location */ pc = pInstr->iP2 - 1; VmPopOperand(&pTos,1); } } break; } /* OP_SEQ P1 P2 * * Strict string comparison. * Pop the top two elements from the stack. If they are equal (pure text comparison) * then jump to instruction P2. Otherwise, continue to the next instruction. * If either operand is NULL then the comparison result is FALSE. * The SyMemcmp() routine is used for the comparison. For a numeric comparison * use PH7_OP_EQ. * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the * stack if the jump would have been taken, or a 0 (FALSE) if not. */ /* OP_SNE P1 P2 * * Strict string comparison. * Pop the top two elements from the stack. If they are not equal (pure text comparison) * then jump to instruction P2. Otherwise, continue to the next instruction. * If either operand is NULL then the comparison result is FALSE. * The SyMemcmp() routine is used for the comparison. For a numeric comparison * use PH7_OP_EQ. * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the * stack if the jump would have been taken, or a 0 (FALSE) if not. */ case PH7_OP_SEQ: case PH7_OP_SNE: { ph7_value *pNos = &pTos[-1]; SyString s1,s2; /* Perform the comparison and act accordingly */ #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force a string cast */ if((pTos->iFlags & MEMOBJ_STRING) == 0 ){ PH7_MemObjToString(pTos); } if((pNos->iFlags & MEMOBJ_STRING) == 0 ){ PH7_MemObjToString(pNos); } SyStringInitFromBuf(&s1,SyBlobData(&pNos->sBlob),SyBlobLength(&pNos->sBlob)); SyStringInitFromBuf(&s2,SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob)); rc = SyStringCmp(&s1,&s2,SyMemcmp); if( pInstr->iOp == PH7_OP_NEQ ){ rc = rc != 0; }else{ rc = rc == 0; } VmPopOperand(&pTos,1); if( !pInstr->iP2 ){ /* Push comparison result without taking the jump */ PH7_MemObjRelease(pTos); pTos->x.iVal = rc; /* Invalidate any prior representation */ MemObjSetType(pTos,MEMOBJ_BOOL); }else{ if( rc ){ /* Jump to the desired location */ pc = pInstr->iP2 - 1; VmPopOperand(&pTos,1); } } break; } /* * OP_LOAD_REF * * * * Push the index of a referenced object on the stack. */ case PH7_OP_LOAD_REF: { sxu32 nIdx; #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Extract memory object index */ nIdx = pTos->nIdx; if( nIdx != SXU32_HIGH /* Not a constant */ ){ /* Nullify the object */ PH7_MemObjRelease(pTos); /* Mark as constant and store the index on the top of the stack */ pTos->x.iVal = (sxi64)nIdx; pTos->nIdx = SXU32_HIGH; pTos->iFlags = MEMOBJ_INT|MEMOBJ_REFERENCE; } break; } /* * OP_STORE_REF * * P3 * Perform an assignment operation by reference. */ case PH7_OP_STORE_REF: { SyString sName = { 0 , 0 }; SyHashEntry *pEntry; sxu32 nIdx; #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if( pInstr->p3 == 0 ){ char *zName; /* Take the variable name from the Next on the stack */ if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ /* Force a string cast */ PH7_MemObjToString(pTos); } if( SyBlobLength(&pTos->sBlob) > 0 ){ zName = SyMemBackendStrDup(&pVm->sAllocator, (const char *)SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob)); if( zName ){ SyStringInitFromBuf(&sName,zName,SyBlobLength(&pTos->sBlob)); } } PH7_MemObjRelease(pTos); pTos--; }else{ SyStringInitFromBuf(&sName,pInstr->p3,SyStrlen((const char *)pInstr->p3)); } nIdx = pTos->nIdx; if(nIdx == SXU32_HIGH ){ if( (pTos->iFlags & (MEMOBJ_OBJ|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0 ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR, "Reference operator require a variable not a constant as it's right operand"); }else{ ph7_value *pObj; /* Extract the desired variable and if not available dynamically create it */ pObj = VmExtractMemObj(&(*pVm),&sName,FALSE,TRUE); if( pObj == 0 ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR, "Fatal, PH7 engine is running out of memory while loading variable '%z'",&sName); goto Abort; } /* Perform the store operation */ PH7_MemObjStore(pTos,pObj); pTos->nIdx = pObj->nIdx; } }else if( sName.nByte > 0){ if( (pTos->iFlags & MEMOBJ_HASHMAP) && (pVm->pGlobal == (ph7_hashmap *)pTos->x.pOther) ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"$GLOBALS is a read-only array and therefore cannot be referenced"); }else{ VmFrame *pFrame = pVm->pFrame; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } /* Query the local frame */ pEntry = SyHashGet(&pFrame->hVar,(const void *)sName.zString,sName.nByte); if( pEntry ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Referenced variable name '%z' already exists",&sName); }else{ rc = SyHashInsert(&pFrame->hVar,(const void *)sName.zString,sName.nByte,SX_INT_TO_PTR(nIdx)); if( pFrame->pParent == 0 ){ /* Insert in the $GLOBALS array */ VmHashmapRefInsert(pVm->pGlobal,sName.zString,sName.nByte,nIdx); } if( rc == SXRET_OK ){ PH7_VmRefObjInstall(&(*pVm),nIdx,SyHashLastEntry(&pFrame->hVar),0,0); } } } } break; } /* * OP_UPLINK P1 * * * Link a variable to the top active VM frame. * This is used to implement the 'global' PHP construct. */ case PH7_OP_UPLINK: { if( pVm->pFrame->pParent ){ ph7_value *pLink = &pTos[-pInstr->iP1+1]; SyString sName; /* Perform the link */ while( pLink <= pTos ){ if((pLink->iFlags & MEMOBJ_STRING) == 0 ){ /* Force a string cast */ PH7_MemObjToString(pLink); } SyStringInitFromBuf(&sName,SyBlobData(&pLink->sBlob),SyBlobLength(&pLink->sBlob)); if( sName.nByte > 0 ){ VmFrameLink(&(*pVm),&sName); } pLink++; } } VmPopOperand(&pTos,pInstr->iP1); break; } /* * OP_LOAD_EXCEPTION * P2 P3 * Push an exception in the corresponding container so that * it can be thrown later by the OP_THROW instruction. */ case PH7_OP_LOAD_EXCEPTION: { ph7_exception *pException = (ph7_exception *)pInstr->p3; VmFrame *pFrame; SySetPut(&pVm->aException,(const void *)&pException); /* Create the exception frame */ rc = VmEnterFrame(&(*pVm),0,0,&pFrame); if( rc != SXRET_OK ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Fatal PH7 engine is runnig out of memory"); goto Abort; } /* Mark the special frame */ pFrame->iFlags |= VM_FRAME_EXCEPTION; pFrame->iExceptionJump = pInstr->iP2; /* Point to the frame that trigger the exception */ pFrame = pFrame->pParent; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ pFrame = pFrame->pParent; } pException->pFrame = pFrame; break; } /* * OP_POP_EXCEPTION * * P3 * Pop a previously pushed exception from the corresponding container. */ case PH7_OP_POP_EXCEPTION: { ph7_exception *pException = (ph7_exception *)pInstr->p3; if( SySetUsed(&pVm->aException) > 0 ){ ph7_exception **apException; /* Pop the loaded exception */ apException = (ph7_exception **)SySetBasePtr(&pVm->aException); if( pException == apException[SySetUsed(&pVm->aException) - 1] ){ (void)SySetPop(&pVm->aException); } } pException->pFrame = 0; /* Leave the exception frame */ VmLeaveFrame(&(*pVm)); break; } /* * OP_THROW * P2 * * Throw an user exception. */ case PH7_OP_THROW: { VmFrame *pFrame = pVm->pFrame; sxu32 nJump = pInstr->iP2; #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } /* Tell the upper layer that an exception was thrown */ pFrame->iFlags |= VM_FRAME_THROW; if( pTos->iFlags & MEMOBJ_OBJ ){ ph7_class_instance *pThis = (ph7_class_instance *)pTos->x.pOther; ph7_class *pException; /* Make sure the loaded object is an instance of the 'Exception' base class. */ pException = PH7_VmExtractClass(&(*pVm),"Exception",sizeof("Exception")-1,TRUE,0); if( pException == 0 || !VmInstanceOf(pThis->pClass,pException) ){ /* Exceptions must be valid objects derived from the Exception base class */ rc = VmUncaughtException(&(*pVm),pThis); if( rc == SXERR_ABORT ){ /* Abort processing immediately */ goto Abort; } }else{ /* Throw the exception */ rc = VmThrowException(&(*pVm),pThis); if( rc == SXERR_ABORT ){ /* Abort processing immediately */ goto Abort; } } }else{ /* Expecting a class instance */ VmUncaughtException(&(*pVm),0); if( rc == SXERR_ABORT ){ /* Abort processing immediately */ goto Abort; } } /* Pop the top entry */ VmPopOperand(&pTos,1); /* Perform an unconditional jump */ pc = nJump - 1; break; } /* * OP_FOREACH_INIT * P2 P3 * Prepare a foreach step. */ case PH7_OP_FOREACH_INIT: { ph7_foreach_info *pInfo = (ph7_foreach_info *)pInstr->p3; void *pName; #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if( SyStringLength(&pInfo->sValue) < 1 ){ /* Take the variable name from the top of the stack */ if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ /* Force a string cast */ PH7_MemObjToString(pTos); } /* Duplicate name */ if( SyBlobLength(&pTos->sBlob) > 0 ){ pName = SyMemBackendDup(&pVm->sAllocator,SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob)); SyStringInitFromBuf(&pInfo->sValue,pName,SyBlobLength(&pTos->sBlob)); } VmPopOperand(&pTos,1); } if( (pInfo->iFlags & PH7_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) < 1 ){ if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ /* Force a string cast */ PH7_MemObjToString(pTos); } /* Duplicate name */ if( SyBlobLength(&pTos->sBlob) > 0 ){ pName = SyMemBackendDup(&pVm->sAllocator,SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob)); SyStringInitFromBuf(&pInfo->sKey,pName,SyBlobLength(&pTos->sBlob)); } VmPopOperand(&pTos,1); } /* Make sure we are dealing with a hashmap aka 'array' or an object */ if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_OBJ)) == 0 || SyStringLength(&pInfo->sValue) < 1 ){ /* Jump out of the loop */ if( (pTos->iFlags & MEMOBJ_NULL) == 0 ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_WARNING,"Invalid argument supplied for the foreach statement,expecting array or class instance"); } pc = pInstr->iP2 - 1; }else{ ph7_foreach_step *pStep; pStep = (ph7_foreach_step *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(ph7_foreach_step)); if( pStep == 0 ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"PH7 is running out of memory while preparing the 'foreach' step"); /* Jump out of the loop */ pc = pInstr->iP2 - 1; }else{ /* Zero the structure */ SyZero(pStep,sizeof(ph7_foreach_step)); /* Prepare the step */ pStep->iFlags = pInfo->iFlags; if( pTos->iFlags & MEMOBJ_HASHMAP ){ ph7_hashmap *pMap = (ph7_hashmap *)pTos->x.pOther; /* Reset the internal loop cursor */ PH7_HashmapResetLoopCursor(pMap); /* Mark the step */ pStep->iFlags |= PH7_4EACH_STEP_HASHMAP; pStep->xIter.pMap = pMap; pMap->iRef++; }else{ ph7_class_instance *pThis = (ph7_class_instance *)pTos->x.pOther; /* Reset the loop cursor */ SyHashResetLoopCursor(&pThis->hAttr); /* Mark the step */ pStep->iFlags |= PH7_4EACH_STEP_OBJECT; pStep->xIter.pThis = pThis; pThis->iRef++; } } if( SXRET_OK != SySetPut(&pInfo->aStep,(const void *)&pStep) ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"PH7 is running out of memory while preparing the 'foreach' step"); SyMemBackendPoolFree(&pVm->sAllocator,pStep); /* Jump out of the loop */ pc = pInstr->iP2 - 1; } } VmPopOperand(&pTos,1); break; } /* * OP_FOREACH_STEP * P2 P3 * Perform a foreach step. Jump to P2 at the end of the step. */ case PH7_OP_FOREACH_STEP: { ph7_foreach_info *pInfo = (ph7_foreach_info *)pInstr->p3; ph7_foreach_step **apStep,*pStep; ph7_value *pValue; VmFrame *pFrame; /* Peek the last step */ apStep = (ph7_foreach_step **)SySetBasePtr(&pInfo->aStep); pStep = apStep[SySetUsed(&pInfo->aStep) - 1]; pFrame = pVm->pFrame; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } if( pStep->iFlags & PH7_4EACH_STEP_HASHMAP ){ ph7_hashmap *pMap = pStep->xIter.pMap; ph7_hashmap_node *pNode; /* Extract the current node value */ pNode = PH7_HashmapGetNextEntry(pMap); if( pNode == 0 ){ /* No more entry to process */ pc = pInstr->iP2 - 1; /* Jump to this destination */ if( pStep->iFlags & PH7_4EACH_STEP_REF ){ /* Break the reference with the last element */ SyHashDeleteEntry(&pFrame->hVar,SyStringData(&pInfo->sValue),SyStringLength(&pInfo->sValue),0); } /* Automatically reset the loop cursor */ PH7_HashmapResetLoopCursor(pMap); /* Cleanup the mess left behind */ SyMemBackendPoolFree(&pVm->sAllocator,pStep); SySetPop(&pInfo->aStep); PH7_HashmapUnref(pMap); }else{ if( (pStep->iFlags & PH7_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) > 0 ){ ph7_value *pKey = VmExtractMemObj(&(*pVm),&pInfo->sKey,FALSE,TRUE); if( pKey ){ PH7_HashmapExtractNodeKey(pNode,pKey); } } if( pStep->iFlags & PH7_4EACH_STEP_REF ){ SyHashEntry *pEntry; /* Pass by reference */ pEntry = SyHashGet(&pFrame->hVar,SyStringData(&pInfo->sValue),SyStringLength(&pInfo->sValue)); if( pEntry ){ pEntry->pUserData = SX_INT_TO_PTR(pNode->nValIdx); }else{ SyHashInsert(&pFrame->hVar,SyStringData(&pInfo->sValue),SyStringLength(&pInfo->sValue), SX_INT_TO_PTR(pNode->nValIdx)); } }else{ /* Make a copy of the entry value */ pValue = VmExtractMemObj(&(*pVm),&pInfo->sValue,FALSE,TRUE); if( pValue ){ PH7_HashmapExtractNodeValue(pNode,pValue,TRUE); } } } }else{ ph7_class_instance *pThis = pStep->xIter.pThis; VmClassAttr *pVmAttr = 0; /* Stupid cc -06 warning */ SyHashEntry *pEntry; /* Point to the next attribute */ while((pEntry = SyHashGetNextEntry(&pThis->hAttr)) != 0 ){ pVmAttr = (VmClassAttr *)pEntry->pUserData; /* Check access permission */ if( VmClassMemberAccess(&(*pVm),pThis->pClass,&pVmAttr->pAttr->sName, pVmAttr->pAttr->iProtection,FALSE) ){ break; /* Access is granted */ } } if( pEntry == 0 ){ /* Clean up the mess left behind */ pc = pInstr->iP2 - 1; /* Jump to this destination */ if( pStep->iFlags & PH7_4EACH_STEP_REF ){ /* Break the reference with the last element */ SyHashDeleteEntry(&pFrame->hVar,SyStringData(&pInfo->sValue),SyStringLength(&pInfo->sValue),0); } SyMemBackendPoolFree(&pVm->sAllocator,pStep); SySetPop(&pInfo->aStep); PH7_ClassInstanceUnref(pThis); }else{ SyString *pAttrName = &pVmAttr->pAttr->sName; ph7_value *pAttrValue; if( (pStep->iFlags & PH7_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) > 0){ /* Fill with the current attribute name */ ph7_value *pKey = VmExtractMemObj(&(*pVm),&pInfo->sKey,FALSE,TRUE); if( pKey ){ SyBlobReset(&pKey->sBlob); SyBlobAppend(&pKey->sBlob,pAttrName->zString,pAttrName->nByte); MemObjSetType(pKey,MEMOBJ_STRING); } } /* Extract attribute value */ pAttrValue = PH7_ClassInstanceExtractAttrValue(pThis,pVmAttr); if( pAttrValue ){ if( pStep->iFlags & PH7_4EACH_STEP_REF ){ /* Pass by reference */ pEntry = SyHashGet(&pFrame->hVar,SyStringData(&pInfo->sValue),SyStringLength(&pInfo->sValue)); if( pEntry ){ pEntry->pUserData = SX_INT_TO_PTR(pVmAttr->nIdx); }else{ SyHashInsert(&pFrame->hVar,SyStringData(&pInfo->sValue),SyStringLength(&pInfo->sValue), SX_INT_TO_PTR(pVmAttr->nIdx)); } }else{ /* Make a copy of the attribute value */ pValue = VmExtractMemObj(&(*pVm),&pInfo->sValue,FALSE,TRUE); if( pValue ){ PH7_MemObjStore(pAttrValue,pValue); } } } } } break; } /* * OP_MEMBER P1 P2 * Load class attribute/method on the stack. */ case PH7_OP_MEMBER: { ph7_class_instance *pThis; ph7_value *pNos; SyString sName; if( !pInstr->iP1 ){ pNos = &pTos[-1]; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif if( pNos->iFlags & MEMOBJ_OBJ ){ ph7_class *pClass; /* Class already instantiated */ pThis = (ph7_class_instance *)pNos->x.pOther; /* Point to the instantiated class */ pClass = pThis->pClass; /* Extract attribute name first */ SyStringInitFromBuf(&sName,(const char *)SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob)); if( pInstr->iP2 ){ /* Method call */ ph7_class_method *pMeth = 0; if( sName.nByte > 0 ){ /* Extract the target method */ pMeth = PH7_ClassExtractMethod(pClass,sName.zString,sName.nByte); } if( pMeth == 0 ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Undefined class method '%z->%z',PH7 is loading NULL", &pClass->sName,&sName ); /* Call the '__Call()' magic method if available */ PH7_ClassInstanceCallMagicMethod(&(*pVm),pClass,pThis,"__call",sizeof("__call")-1,&sName); /* Pop the method name from the stack */ VmPopOperand(&pTos,1); PH7_MemObjRelease(pTos); }else{ /* Push method name on the stack */ PH7_MemObjRelease(pTos); SyBlobAppend(&pTos->sBlob,SyStringData(&pMeth->sVmName),SyStringLength(&pMeth->sVmName)); MemObjSetType(pTos,MEMOBJ_STRING); } pTos->nIdx = SXU32_HIGH; }else{ /* Attribute access */ VmClassAttr *pObjAttr = 0; SyHashEntry *pEntry; /* Extract the target attribute */ if( sName.nByte > 0 ){ pEntry = SyHashGet(&pThis->hAttr,(const void *)sName.zString,sName.nByte); if( pEntry ){ /* Point to the attribute value */ pObjAttr = (VmClassAttr *)pEntry->pUserData; } } if( pObjAttr == 0 ){ /* No such attribute,load null */ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Undefined class attribute '%z->%z',PH7 is loading NULL", &pClass->sName,&sName); /* Call the __get magic method if available */ PH7_ClassInstanceCallMagicMethod(&(*pVm),pClass,pThis,"__get",sizeof("__get")-1,&sName); } VmPopOperand(&pTos,1); /* TICKET 1433-49: Deffer garbage collection until attribute loading. * This is due to the following case: * (new TestClass())->foo; */ pThis->iRef++; PH7_MemObjRelease(pTos); pTos->nIdx = SXU32_HIGH; /* Assume we are loading a constant */ if( pObjAttr ){ ph7_value *pValue = 0; /* cc warning */ /* Check attribute access */ if( VmClassMemberAccess(&(*pVm),pClass,&pObjAttr->pAttr->sName,pObjAttr->pAttr->iProtection,TRUE) ){ /* Load attribute */ pValue = (ph7_value *)SySetAt(&pVm->aMemObj,pObjAttr->nIdx); if( pValue ){ if( pThis->iRef < 2 ){ /* Perform a store operation,rather than a load operation since * the class instance '$this' will be deleted shortly. */ PH7_MemObjStore(pValue,pTos); }else{ /* Simple load */ PH7_MemObjLoad(pValue,pTos); } if( (pObjAttr->pAttr->iFlags & PH7_CLASS_ATTR_CONSTANT) == 0 ){ if( pThis->iRef > 1 ){ /* Load attribute index */ pTos->nIdx = pObjAttr->nIdx; } } } } } /* Safely unreference the object */ PH7_ClassInstanceUnref(pThis); } }else{ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"'->': Expecting class instance as left operand,PH7 is loading NULL"); VmPopOperand(&pTos,1); PH7_MemObjRelease(pTos); pTos->nIdx = SXU32_HIGH; /* Assume we are loading a constant */ } }else{ /* Static member access using class name */ pNos = pTos; pThis = 0; if( !pInstr->p3 ){ SyStringInitFromBuf(&sName,(const char *)SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob)); pNos--; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif }else{ /* Attribute name already computed */ SyStringInitFromBuf(&sName,pInstr->p3,SyStrlen((const char *)pInstr->p3)); } if( pNos->iFlags & (MEMOBJ_STRING|MEMOBJ_OBJ) ){ ph7_class *pClass = 0; if( pNos->iFlags & MEMOBJ_OBJ ){ /* Class already instantiated */ pThis = (ph7_class_instance *)pNos->x.pOther; pClass = pThis->pClass; pThis->iRef++; /* Deffer garbage collection */ }else{ /* Try to extract the target class */ if( SyBlobLength(&pNos->sBlob) > 0 ){ pClass = PH7_VmExtractClass(&(*pVm),(const char *)SyBlobData(&pNos->sBlob), SyBlobLength(&pNos->sBlob),FALSE,0); } } if( pClass == 0 ){ /* Undefined class */ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Call to undefined class '%.*s',PH7 is loading NULL", SyBlobLength(&pNos->sBlob),(const char *)SyBlobData(&pNos->sBlob) ); if( !pInstr->p3 ){ VmPopOperand(&pTos,1); } PH7_MemObjRelease(pTos); pTos->nIdx = SXU32_HIGH; }else{ if( pInstr->iP2 ){ /* Method call */ ph7_class_method *pMeth = 0; if( sName.nByte > 0 && (pClass->iFlags & PH7_CLASS_INTERFACE) == 0){ /* Extract the target method */ pMeth = PH7_ClassExtractMethod(pClass,sName.zString,sName.nByte); } if( pMeth == 0 || (pMeth->iFlags & PH7_CLASS_ATTR_ABSTRACT) ){ if( pMeth ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Cannot call abstract method '%z:%z',PH7 is loading NULL", &pClass->sName,&sName ); }else{ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Undefined class static method '%z::%z',PH7 is loading NULL", &pClass->sName,&sName ); /* Call the '__CallStatic()' magic method if available */ PH7_ClassInstanceCallMagicMethod(&(*pVm),pClass,0,"__callStatic",sizeof("__callStatic")-1,&sName); } /* Pop the method name from the stack */ if( !pInstr->p3 ){ VmPopOperand(&pTos,1); } PH7_MemObjRelease(pTos); }else{ /* Push method name on the stack */ PH7_MemObjRelease(pTos); SyBlobAppend(&pTos->sBlob,SyStringData(&pMeth->sVmName),SyStringLength(&pMeth->sVmName)); MemObjSetType(pTos,MEMOBJ_STRING); } pTos->nIdx = SXU32_HIGH; }else{ /* Attribute access */ ph7_class_attr *pAttr = 0; /* Extract the target attribute */ if( sName.nByte > 0 ){ pAttr = PH7_ClassExtractAttribute(pClass,sName.zString,sName.nByte); } if( pAttr == 0 ){ /* No such attribute,load null */ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Undefined class attribute '%z::%z',PH7 is loading NULL", &pClass->sName,&sName); /* Call the __get magic method if available */ PH7_ClassInstanceCallMagicMethod(&(*pVm),pClass,0,"__get",sizeof("__get")-1,&sName); } /* Pop the attribute name from the stack */ if( !pInstr->p3 ){ VmPopOperand(&pTos,1); } PH7_MemObjRelease(pTos); pTos->nIdx = SXU32_HIGH; if( pAttr ){ if( (pAttr->iFlags & (PH7_CLASS_ATTR_STATIC|PH7_CLASS_ATTR_CONSTANT)) == 0 ){ /* Access to a non static attribute */ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Access to a non-static class attribute '%z::%z',PH7 is loading NULL", &pClass->sName,&pAttr->sName ); }else{ ph7_value *pValue; /* Check if the access to the attribute is allowed */ if( VmClassMemberAccess(&(*pVm),pClass,&pAttr->sName,pAttr->iProtection,TRUE) ){ /* Load the desired attribute */ pValue = (ph7_value *)SySetAt(&pVm->aMemObj,pAttr->nIdx); if( pValue ){ PH7_MemObjLoad(pValue,pTos); if( pAttr->iFlags & PH7_CLASS_ATTR_STATIC ){ /* Load index number */ pTos->nIdx = pAttr->nIdx; } } } } } } if( pThis ){ /* Safely unreference the object */ PH7_ClassInstanceUnref(pThis); } } }else{ /* Pop operands */ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"Invalid class name,PH7 is loading NULL"); if( !pInstr->p3 ){ VmPopOperand(&pTos,1); } PH7_MemObjRelease(pTos); pTos->nIdx = SXU32_HIGH; } } break; } /* * OP_NEW P1 * * * * Create a new class instance (Object in the PHP jargon) and push that object on the stack. */ case PH7_OP_NEW: { ph7_value *pArg = &pTos[-pInstr->iP1]; /* Constructor arguments (if available) */ ph7_class *pClass = 0; ph7_class_instance *pNew; if( (pTos->iFlags & MEMOBJ_STRING) && SyBlobLength(&pTos->sBlob) > 0 ){ /* Try to extract the desired class */ pClass = PH7_VmExtractClass(&(*pVm),(const char *)SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob),TRUE /* Only loadable class but not 'interface' or 'abstract' class*/,0); }else if( pTos->iFlags & MEMOBJ_OBJ ){ /* Take the base class from the loaded instance */ pClass = ((ph7_class_instance *)pTos->x.pOther)->pClass; } if( pClass == 0 ){ /* No such class */ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Class '%.*s' is not defined,PH7 is loading NULL", SyBlobLength(&pTos->sBlob),(const char *)SyBlobData(&pTos->sBlob) ); PH7_MemObjRelease(pTos); if( pInstr->iP1 > 0 ){ /* Pop given arguments */ VmPopOperand(&pTos,pInstr->iP1); } }else{ ph7_class_method *pCons; /* Create a new class instance */ pNew = PH7_NewClassInstance(&(*pVm),pClass); if( pNew == 0 ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR, "Cannot create new class '%z' instance due to a memory failure,PH7 is loading NULL", &pClass->sName ); PH7_MemObjRelease(pTos); if( pInstr->iP1 > 0 ){ /* Pop given arguments */ VmPopOperand(&pTos,pInstr->iP1); } break; } /* Check if a constructor is available */ pCons = PH7_ClassExtractMethod(pClass,"__construct",sizeof("__construct")-1); if( pCons == 0 ){ SyString *pName = &pClass->sName; /* Check for a constructor with the same base class name */ pCons = PH7_ClassExtractMethod(pClass,pName->zString,pName->nByte); } if( pCons ){ /* Call the class constructor */ SySetReset(&aArg); while( pArg < pTos ){ SySetPut(&aArg,(const void *)&pArg); pArg++; } if( pVm->bErrReport ){ ph7_vm_func_arg *pFuncArg; sxu32 n; n = SySetUsed(&aArg); /* Emit a notice for missing arguments */ while( n < SySetUsed(&pCons->sFunc.aArgs) ){ pFuncArg = (ph7_vm_func_arg *)SySetAt(&pCons->sFunc.aArgs,n); if( pFuncArg ){ if( SySetUsed(&pFuncArg->aByteCode) < 1 ){ VmErrorFormat(&(*pVm),PH7_CTX_NOTICE,"Missing constructor argument %u($%z) for class '%z'", n+1,&pFuncArg->sName,&pClass->sName); } } n++; } } PH7_VmCallClassMethod(&(*pVm),pNew,pCons,0,(int)SySetUsed(&aArg),(ph7_value **)SySetBasePtr(&aArg)); /* TICKET 1433-52: Unsetting $this in the constructor body */ if( pNew->iRef < 1 ){ pNew->iRef = 1; } } if( pInstr->iP1 > 0 ){ /* Pop given arguments */ VmPopOperand(&pTos,pInstr->iP1); } PH7_MemObjRelease(pTos); pTos->x.pOther = pNew; MemObjSetType(pTos,MEMOBJ_OBJ); } break; } /* * OP_CLONE * * * * Perfome a clone operation. */ case PH7_OP_CLONE: { ph7_class_instance *pSrc,*pClone; #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Make sure we are dealing with a class instance */ if( (pTos->iFlags & MEMOBJ_OBJ) == 0 ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR, "Clone: Expecting a class instance as left operand,PH7 is loading NULL"); PH7_MemObjRelease(pTos); break; } /* Point to the source */ pSrc = (ph7_class_instance *)pTos->x.pOther; /* Perform the clone operation */ pClone = PH7_CloneClassInstance(pSrc); PH7_MemObjRelease(pTos); if( pClone == 0 ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR, "Clone: cannot make an object clone due to a memory failure,PH7 is loading NULL"); }else{ /* Load the cloned object */ pTos->x.pOther = pClone; MemObjSetType(pTos,MEMOBJ_OBJ); } break; } /* * OP_SWITCH * * P3 * This is the bytecode implementation of the complex switch() PHP construct. */ case PH7_OP_SWITCH: { ph7_switch *pSwitch = (ph7_switch *)pInstr->p3; ph7_case_expr *aCase,*pCase; ph7_value sValue,sCaseValue; sxu32 n,nEntry; #ifdef UNTRUST if( pSwitch == 0 || pTos < pStack ){ goto Abort; } #endif /* Point to the case table */ aCase = (ph7_case_expr *)SySetBasePtr(&pSwitch->aCaseExpr); nEntry = SySetUsed(&pSwitch->aCaseExpr); /* Select the appropriate case block to execute */ PH7_MemObjInit(pVm,&sValue); PH7_MemObjInit(pVm,&sCaseValue); for( n = 0 ; n < nEntry ; ++n ){ pCase = &aCase[n]; PH7_MemObjLoad(pTos,&sValue); /* Execute the case expression first */ VmLocalExec(pVm,&pCase->aByteCode,&sCaseValue); /* Compare the two expression */ rc = PH7_MemObjCmp(&sValue,&sCaseValue,FALSE,0); PH7_MemObjRelease(&sValue); PH7_MemObjRelease(&sCaseValue); if( rc == 0 ){ /* Value match,jump to this block */ pc = pCase->nStart - 1; break; } } VmPopOperand(&pTos,1); if( n >= nEntry ){ /* No approprite case to execute,jump to the default case */ if( pSwitch->nDefault > 0 ){ pc = pSwitch->nDefault - 1; }else{ /* No default case,jump out of this switch */ pc = pSwitch->nOut - 1; } } break; } /* * OP_CALL P1 * * * Call a PHP or a foreign function and push the return value of the called * function on the stack. */ case PH7_OP_CALL: { ph7_value *pArg = &pTos[-pInstr->iP1]; SyHashEntry *pEntry; SyString sName; /* Extract function name */ if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ if( pTos->iFlags & MEMOBJ_HASHMAP ){ ph7_value sResult; SySetReset(&aArg); while( pArg < pTos ){ SySetPut(&aArg,(const void *)&pArg); pArg++; } PH7_MemObjInit(pVm,&sResult); /* May be a class instance and it's static method */ PH7_VmCallUserFunction(pVm,pTos,(int)SySetUsed(&aArg),(ph7_value **)SySetBasePtr(&aArg),&sResult); SySetReset(&aArg); /* Pop given arguments */ if( pInstr->iP1 > 0 ){ VmPopOperand(&pTos,pInstr->iP1); } /* Copy result */ PH7_MemObjStore(&sResult,pTos); PH7_MemObjRelease(&sResult); }else{ if( pTos->iFlags & MEMOBJ_OBJ ){ ph7_class_instance *pThis = (ph7_class_instance *)pTos->x.pOther; /* Call the magic method '__invoke' if available */ PH7_ClassInstanceCallMagicMethod(&(*pVm),pThis->pClass,pThis,"__invoke",sizeof("__invoke")-1,0); }else{ /* Raise exception: Invalid function name */ VmErrorFormat(&(*pVm),PH7_CTX_WARNING,"Invalid function name,NULL will be returned"); } /* Pop given arguments */ if( pInstr->iP1 > 0 ){ VmPopOperand(&pTos,pInstr->iP1); } /* Assume a null return value so that the program continue it's execution normally */ PH7_MemObjRelease(pTos); } break; } SyStringInitFromBuf(&sName,SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob)); /* Check for a compiled function first */ pEntry = SyHashGet(&pVm->hFunction,(const void *)sName.zString,sName.nByte); if( pEntry ){ ph7_vm_func_arg *aFormalArg; ph7_class_instance *pThis; ph7_value *pFrameStack; ph7_vm_func *pVmFunc; ph7_class *pSelf; VmFrame *pFrame; ph7_value *pObj; VmSlot sArg; sxu32 n; /* initialize fields */ pVmFunc = (ph7_vm_func *)pEntry->pUserData; pThis = 0; pSelf = 0; if( pVmFunc->iFlags & VM_FUNC_CLASS_METHOD ){ ph7_class_method *pMeth; /* Class method call */ ph7_value *pTarget = &pTos[-1]; if( pTarget >= pStack && (pTarget->iFlags & (MEMOBJ_STRING|MEMOBJ_OBJ|MEMOBJ_NULL)) ){ /* Extract the 'this' pointer */ if(pTarget->iFlags & MEMOBJ_OBJ ){ /* Instance already loaded */ pThis = (ph7_class_instance *)pTarget->x.pOther; pThis->iRef++; pSelf = pThis->pClass; } if( pSelf == 0 ){ if( (pTarget->iFlags & MEMOBJ_STRING) && SyBlobLength(&pTarget->sBlob) > 0 ){ /* "Late Static Binding" class name */ pSelf = PH7_VmExtractClass(&(*pVm),(const char *)SyBlobData(&pTarget->sBlob), SyBlobLength(&pTarget->sBlob),FALSE,0); } if( pSelf == 0 ){ pSelf = (ph7_class *)pVmFunc->pUserData; } } if( pThis == 0 ){ VmFrame *pFrame = pVm->pFrame; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } if( pFrame->pParent ){ /* TICKET-1433-52: Make sure the '$this' variable is available to the current scope */ pThis = pFrame->pThis; if( pThis ){ pThis->iRef++; } } } VmPopOperand(&pTos,1); PH7_MemObjRelease(pTos); /* Synchronize pointers */ pArg = &pTos[-pInstr->iP1]; /* TICKET 1433-50: This is a very very unlikely scenario that occurs when the 'genius' * user have already computed the random generated unique class method name * and tries to call it outside it's context [i.e: global scope]. In that * case we have to synchrnoize pointers to avoid stack underflow. */ while( pArg < pStack ){ pArg++; } if( pSelf ){ /* Paranoid edition */ /* Check if the call is allowed */ pMeth = PH7_ClassExtractMethod(pSelf,pVmFunc->sName.zString,pVmFunc->sName.nByte); if( pMeth && pMeth->iProtection != PH7_CLASS_PROT_PUBLIC ){ if( !VmClassMemberAccess(&(*pVm),pSelf,&pVmFunc->sName,pMeth->iProtection,TRUE) ){ /* Pop given arguments */ if( pInstr->iP1 > 0 ){ VmPopOperand(&pTos,pInstr->iP1); } /* Assume a null return value so that the program continue it's execution normally */ PH7_MemObjRelease(pTos); break; } } } } } /* Check The recursion limit */ if( pVm->nRecursionDepth > pVm->nMaxDepth ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR, "Recursion limit reached while invoking user function '%z',PH7 will set a NULL return value", &pVmFunc->sName); /* Pop given arguments */ if( pInstr->iP1 > 0 ){ VmPopOperand(&pTos,pInstr->iP1); } /* Assume a null return value so that the program continue it's execution normally */ PH7_MemObjRelease(pTos); break; } if( pVmFunc->pNextName ){ /* Function is candidate for overloading,select the appropriate function to call */ pVmFunc = VmOverload(&(*pVm),pVmFunc,pArg,(int)(pTos-pArg)); } /* Extract the formal argument set */ aFormalArg = (ph7_vm_func_arg *)SySetBasePtr(&pVmFunc->aArgs); /* Create a new VM frame */ rc = VmEnterFrame(&(*pVm),pVmFunc,pThis,&pFrame); if( rc != SXRET_OK ){ /* Raise exception: Out of memory */ VmErrorFormat(&(*pVm),PH7_CTX_ERR, "PH7 is running out of memory while calling function '%z',NULL will be returned", &pVmFunc->sName); /* Pop given arguments */ if( pInstr->iP1 > 0 ){ VmPopOperand(&pTos,pInstr->iP1); } /* Assume a null return value so that the program continue it's execution normally */ PH7_MemObjRelease(pTos); break; } if( (pVmFunc->iFlags & VM_FUNC_CLASS_METHOD) && pThis ){ /* Install the '$this' variable */ static const SyString sThis = { "this" , sizeof("this") - 1 }; pObj = VmExtractMemObj(&(*pVm),&sThis,FALSE,TRUE); if( pObj ){ /* Reflect the change */ pObj->x.pOther = pThis; MemObjSetType(pObj,MEMOBJ_OBJ); } } if( SySetUsed(&pVmFunc->aStatic) > 0 ){ ph7_vm_func_static_var *pStatic,*aStatic; /* Install static variables */ aStatic = (ph7_vm_func_static_var *)SySetBasePtr(&pVmFunc->aStatic); for( n = 0 ; n < SySetUsed(&pVmFunc->aStatic) ; ++n ){ pStatic = &aStatic[n]; if( pStatic->nIdx == SXU32_HIGH ){ /* Initialize the static variables */ pObj = VmReserveMemObj(&(*pVm),&pStatic->nIdx); if( pObj ){ /* Assume a NULL initialization value */ PH7_MemObjInit(&(*pVm),pObj); if( SySetUsed(&pStatic->aByteCode) > 0 ){ /* Evaluate initialization expression (Any complex expression) */ VmLocalExec(&(*pVm),&pStatic->aByteCode,pObj); } pObj->nIdx = pStatic->nIdx; }else{ continue; } } /* Install in the current frame */ SyHashInsert(&pFrame->hVar,SyStringData(&pStatic->sName),SyStringLength(&pStatic->sName), SX_INT_TO_PTR(pStatic->nIdx)); } } /* Push arguments in the local frame */ n = 0; while( pArg < pTos ){ if( n < SySetUsed(&pVmFunc->aArgs) ){ if( (pArg->iFlags & MEMOBJ_NULL) && SySetUsed(&aFormalArg[n].aByteCode) > 0 ){ /* NULL values are redirected to default arguments */ rc = VmLocalExec(&(*pVm),&aFormalArg[n].aByteCode,pArg); if( rc == PH7_ABORT ){ goto Abort; } } /* Make sure the given arguments are of the correct type */ if( aFormalArg[n].nType > 0 ){ if ( aFormalArg[n].nType == SXU32_HIGH ){ /* Argument must be a class instance [i.e: object] */ SyString *pName = &aFormalArg[n].sClass; ph7_class *pClass; /* Try to extract the desired class */ pClass = PH7_VmExtractClass(&(*pVm),pName->zString,pName->nByte,TRUE,0); if( pClass ){ if( (pArg->iFlags & MEMOBJ_OBJ) == 0 ){ if( (pArg->iFlags & MEMOBJ_NULL) == 0 ){ VmErrorFormat(&(*pVm),PH7_CTX_WARNING, "Function '%z()':Argument %u must be an object of type '%z',PH7 is loading NULL instead", &pVmFunc->sName,n+1,pName); PH7_MemObjRelease(pArg); } }else{ ph7_class_instance *pThis = (ph7_class_instance *)pArg->x.pOther; /* Make sure the object is an instance of the given class */ if( ! VmInstanceOf(pThis->pClass,pClass) ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR, "Function '%z()':Argument %u must be an object of type '%z',PH7 is loading NULL instead", &pVmFunc->sName,n+1,pName); PH7_MemObjRelease(pArg); } } } }else if( ((pArg->iFlags & aFormalArg[n].nType) == 0) ){ ProcMemObjCast xCast = PH7_MemObjCastMethod(aFormalArg[n].nType); /* Cast to the desired type */ xCast(pArg); } } if( aFormalArg[n].iFlags & VM_FUNC_ARG_BY_REF ){ /* Pass by reference */ if( pArg->nIdx == SXU32_HIGH ){ /* Expecting a variable,not a constant,raise an exception */ if((pArg->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_OBJ|MEMOBJ_RES|MEMOBJ_NULL)) == 0){ VmErrorFormat(&(*pVm),PH7_CTX_WARNING, "Function '%z',%d argument: Pass by reference,expecting a variable not a " "constant,PH7 is switching to pass by value",&pVmFunc->sName,n+1); } /* Switch to pass by value */ pObj = VmExtractMemObj(&(*pVm),&aFormalArg[n].sName,FALSE,TRUE); }else{ SyHashEntry *pRefEntry; /* Install the referenced variable in the private function frame */ pRefEntry = SyHashGet(&pFrame->hVar,SyStringData(&aFormalArg[n].sName),SyStringLength(&aFormalArg[n].sName)); if( pRefEntry == 0 ){ SyHashInsert(&pFrame->hVar,SyStringData(&aFormalArg[n].sName), SyStringLength(&aFormalArg[n].sName),SX_INT_TO_PTR(pArg->nIdx)); sArg.nIdx = pArg->nIdx; sArg.pUserData = 0; SySetPut(&pFrame->sArg,(const void *)&sArg); } pObj = 0; } }else{ /* Pass by value,make a copy of the given argument */ pObj = VmExtractMemObj(&(*pVm),&aFormalArg[n].sName,FALSE,TRUE); } }else{ char zName[32]; SyString sName; /* Set a dummy name */ sName.nByte = SyBufferFormat(zName,sizeof(zName),"[%u]apArg",n); sName.zString = zName; /* Annonymous argument */ pObj = VmExtractMemObj(&(*pVm),&sName,TRUE,TRUE); } if( pObj ){ PH7_MemObjStore(pArg,pObj); /* Insert argument index */ sArg.nIdx = pObj->nIdx; sArg.pUserData = 0; SySetPut(&pFrame->sArg,(const void *)&sArg); } PH7_MemObjRelease(pArg); pArg++; ++n; } /* Set up closure environment */ if( pVmFunc->iFlags & VM_FUNC_CLOSURE ){ ph7_vm_func_closure_env *aEnv,*pEnv; ph7_value *pValue; sxu32 n; aEnv = (ph7_vm_func_closure_env *)SySetBasePtr(&pVmFunc->aClosureEnv); for(n = 0 ; n < SySetUsed(&pVmFunc->aClosureEnv) ; ++n ){ pEnv = &aEnv[n]; if( (pEnv->iFlags & VM_FUNC_ARG_IGNORE) && (pEnv->sValue.iFlags & MEMOBJ_NULL) ){ /* Do not install null value */ continue; } pValue = VmExtractMemObj(pVm,&pEnv->sName,FALSE,TRUE); if( pValue == 0 ){ continue; } /* Invalidate any prior representation */ PH7_MemObjRelease(pValue); /* Duplicate bound variable value */ PH7_MemObjStore(&pEnv->sValue,pValue); } } /* Process default values */ while( n < SySetUsed(&pVmFunc->aArgs) ){ if( SySetUsed(&aFormalArg[n].aByteCode) > 0 ){ pObj = VmExtractMemObj(&(*pVm),&aFormalArg[n].sName,FALSE,TRUE); if( pObj ){ /* Evaluate the default value and extract it's result */ rc = VmLocalExec(&(*pVm),&aFormalArg[n].aByteCode,pObj); if( rc == PH7_ABORT ){ goto Abort; } /* Insert argument index */ sArg.nIdx = pObj->nIdx; sArg.pUserData = 0; SySetPut(&pFrame->sArg,(const void *)&sArg); /* Make sure the default argument is of the correct type */ if( aFormalArg[n].nType > 0 && ((pObj->iFlags & aFormalArg[n].nType) == 0) ){ ProcMemObjCast xCast = PH7_MemObjCastMethod(aFormalArg[n].nType); /* Cast to the desired type */ xCast(pObj); } } } ++n; } /* Pop arguments,function name from the operand stack and assume the function * does not return anything. */ PH7_MemObjRelease(pTos); pTos = &pTos[-pInstr->iP1]; /* Allocate a new operand stack and evaluate the function body */ pFrameStack = VmNewOperandStack(&(*pVm),SySetUsed(&pVmFunc->aByteCode)); if( pFrameStack == 0 ){ /* Raise exception: Out of memory */ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"PH7 is running out of memory while calling function '%z',NULL will be returned", &pVmFunc->sName); if( pInstr->iP1 > 0 ){ VmPopOperand(&pTos,pInstr->iP1); } break; } if( pSelf ){ /* Push class name */ SySetPut(&pVm->aSelf,(const void *)&pSelf); } /* Increment nesting level */ pVm->nRecursionDepth++; /* Execute function body */ rc = VmByteCodeExec(&(*pVm),(VmInstr *)SySetBasePtr(&pVmFunc->aByteCode),pFrameStack,-1,pTos,&n,FALSE); /* Decrement nesting level */ pVm->nRecursionDepth--; if( pSelf ){ /* Pop class name */ (void)SySetPop(&pVm->aSelf); } /* Cleanup the mess left behind */ if( (pVmFunc->iFlags & VM_FUNC_REF_RETURN) && rc == SXRET_OK ){ /* Return by reference,reflect that */ if( n != SXU32_HIGH ){ VmSlot *aSlot = (VmSlot *)SySetBasePtr(&pFrame->sLocal); sxu32 i; /* Make sure the referenced object is not a local variable */ for( i = 0 ; i < SySetUsed(&pFrame->sLocal) ; ++i ){ if( n == aSlot[i].nIdx ){ pObj = (ph7_value *)SySetAt(&pVm->aMemObj,n); if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_OBJ|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0 ){ VmErrorFormat(&(*pVm),PH7_CTX_NOTICE, "Function '%z',return by reference: Cannot reference local variable,PH7 is switching to return by value", &pVmFunc->sName); } n = SXU32_HIGH; break; } } }else{ if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_OBJ|MEMOBJ_NULL|MEMOBJ_RES)) == 0 ){ VmErrorFormat(&(*pVm),PH7_CTX_NOTICE, "Function '%z',return by reference: Cannot reference constant expression,PH7 is switching to return by value", &pVmFunc->sName); } } pTos->nIdx = n; } /* Cleanup the mess left behind */ if( rc != PH7_ABORT && ((pFrame->iFlags & VM_FRAME_THROW) || rc == PH7_EXCEPTION) ){ /* An exception was throw in this frame */ pFrame = pFrame->pParent; if( !is_callback && pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) && pFrame->iExceptionJump > 0 ){ /* Pop the resutlt */ VmPopOperand(&pTos,1); /* Jump to this destination */ pc = pFrame->iExceptionJump - 1; rc = PH7_OK; }else{ if( pFrame->pParent ){ rc = PH7_EXCEPTION; }else{ /* Continue normal execution */ rc = PH7_OK; } } } /* Free the operand stack */ SyMemBackendFree(&pVm->sAllocator,pFrameStack); /* Leave the frame */ VmLeaveFrame(&(*pVm)); if( rc == PH7_ABORT ){ /* Abort processing immeditaley */ goto Abort; }else if( rc == PH7_EXCEPTION ){ goto Exception; } }else{ ph7_user_func *pFunc; ph7_context sCtx; ph7_value sRet; /* Look for an installed foreign function */ pEntry = SyHashGet(&pVm->hHostFunction,(const void *)sName.zString,sName.nByte); if( pEntry == 0 ){ /* Call to undefined function */ VmErrorFormat(&(*pVm),PH7_CTX_WARNING,"Call to undefined function '%z',NULL will be returned",&sName); /* Pop given arguments */ if( pInstr->iP1 > 0 ){ VmPopOperand(&pTos,pInstr->iP1); } /* Assume a null return value so that the program continue it's execution normally */ PH7_MemObjRelease(pTos); break; } pFunc = (ph7_user_func *)pEntry->pUserData; /* Start collecting function arguments */ SySetReset(&aArg); while( pArg < pTos ){ SySetPut(&aArg,(const void *)&pArg); pArg++; } /* Assume a null return value */ PH7_MemObjInit(&(*pVm),&sRet); /* Init the call context */ VmInitCallContext(&sCtx,&(*pVm),pFunc,&sRet,0); /* Call the foreign function */ rc = pFunc->xFunc(&sCtx,(int)SySetUsed(&aArg),(ph7_value **)SySetBasePtr(&aArg)); /* Release the call context */ VmReleaseCallContext(&sCtx); if( rc == PH7_ABORT ){ goto Abort; } if( pInstr->iP1 > 0 ){ /* Pop function name and arguments */ VmPopOperand(&pTos,pInstr->iP1); } /* Save foreign function return value */ PH7_MemObjStore(&sRet,pTos); PH7_MemObjRelease(&sRet); } break; } /* * OP_CONSUME: P1 * * * Consume (Invoke the installed VM output consumer callback) and POP P1 elements from the stack. */ case PH7_OP_CONSUME: { ph7_output_consumer *pCons = &pVm->sVmConsumer; ph7_value *pCur,*pOut = pTos; pOut = &pTos[-pInstr->iP1 + 1]; pCur = pOut; /* Start the consume process */ while( pOut <= pTos ){ /* Force a string cast */ if( (pOut->iFlags & MEMOBJ_STRING) == 0 ){ PH7_MemObjToString(pOut); } if( SyBlobLength(&pOut->sBlob) > 0 ){ /*SyBlobNullAppend(&pOut->sBlob);*/ /* Invoke the output consumer callback */ rc = pCons->xConsumer(SyBlobData(&pOut->sBlob),SyBlobLength(&pOut->sBlob),pCons->pUserData); if( pCons->xConsumer != VmObConsumer ){ /* Increment output length */ pVm->nOutputLen += SyBlobLength(&pOut->sBlob); } SyBlobRelease(&pOut->sBlob); if( rc == SXERR_ABORT ){ /* Output consumer callback request an operation abort. */ goto Abort; } } pOut++; } pTos = &pCur[-1]; break; } } /* Switch() */ pc++; /* Next instruction in the stream */ } /* For(;;) */ Done: SySetRelease(&aArg); return SXRET_OK; Abort: SySetRelease(&aArg); while( pTos >= pStack ){ PH7_MemObjRelease(pTos); pTos--; } return PH7_ABORT; Exception: SySetRelease(&aArg); while( pTos >= pStack ){ PH7_MemObjRelease(pTos); pTos--; } return PH7_EXCEPTION; } /* * Execute as much of a local PH7 bytecode program as we can then return. * This function is a wrapper around [VmByteCodeExec()]. * See block-comment on that function for additional information. */ static sxi32 VmLocalExec(ph7_vm *pVm,SySet *pByteCode,ph7_value *pResult) { ph7_value *pStack; sxi32 rc; /* Allocate a new operand stack */ pStack = VmNewOperandStack(&(*pVm),SySetUsed(pByteCode)); if( pStack == 0 ){ return SXERR_MEM; } /* Execute the program */ rc = VmByteCodeExec(&(*pVm),(VmInstr *)SySetBasePtr(pByteCode),pStack,-1,&(*pResult),0,FALSE); /* Free the operand stack */ SyMemBackendFree(&pVm->sAllocator,pStack); /* Execution result */ return rc; } /* * Invoke any installed shutdown callbacks. * Shutdown callbacks are kept in a stack and are registered using one * or more calls to [register_shutdown_function()]. * These callbacks are invoked by the virtual machine when the program * execution ends. * Refer to the implementation of [register_shutdown_function()] for * additional information. */ static void VmInvokeShutdownCallbacks(ph7_vm *pVm) { VmShutdownCB *pEntry; ph7_value *apArg[10]; sxu32 n,nEntry; int i; /* Point to the stack of registered callbacks */ nEntry = SySetUsed(&pVm->aShutdown); for( i = 0 ; i < (int)SX_ARRAYSIZE(apArg) ; i++ ){ apArg[i] = 0; } for( n = 0 ; n < nEntry ; ++n ){ pEntry = (VmShutdownCB *)SySetAt(&pVm->aShutdown,n); if( pEntry ){ /* Prepare callback arguments if any */ for( i = 0 ; i < pEntry->nArg ; i++ ){ if( i >= (int)SX_ARRAYSIZE(apArg) ){ break; } apArg[i] = &pEntry->aArg[i]; } /* Invoke the callback */ PH7_VmCallUserFunction(&(*pVm),&pEntry->sCallback,pEntry->nArg,apArg,0); /* * TICKET 1433-56: Try re-access the same entry since the invoked * callback may call [register_shutdown_function()] in it's body. */ pEntry = (VmShutdownCB *)SySetAt(&pVm->aShutdown,n); if( pEntry ){ PH7_MemObjRelease(&pEntry->sCallback); for( i = 0 ; i < pEntry->nArg ; ++i ){ PH7_MemObjRelease(apArg[i]); } } } } SySetReset(&pVm->aShutdown); } /* * Execute as much of a PH7 bytecode program as we can then return. * This function is a wrapper around [VmByteCodeExec()]. * See block-comment on that function for additional information. */ PH7_PRIVATE sxi32 PH7_VmByteCodeExec(ph7_vm *pVm) { /* Make sure we are ready to execute this program */ if( pVm->nMagic != PH7_VM_RUN ){ return pVm->nMagic == PH7_VM_EXEC ? SXERR_LOCKED /* Locked VM */ : SXERR_CORRUPT; /* Stale VM */ } /* Set the execution magic number */ pVm->nMagic = PH7_VM_EXEC; /* Execute the program */ VmByteCodeExec(&(*pVm),(VmInstr *)SySetBasePtr(pVm->pByteContainer),pVm->aOps,-1,&pVm->sExec,0,FALSE); /* Invoke any shutdown callbacks */ VmInvokeShutdownCallbacks(&(*pVm)); /* * TICKET 1433-100: Do not remove the PH7_VM_EXEC magic number * so that any following call to [ph7_vm_exec()] without calling * [ph7_vm_reset()] first would fail. */ return SXRET_OK; } /* * Invoke the installed VM output consumer callback to consume * the desired message. * Refer to the implementation of [ph7_context_output()] defined * in 'api.c' for additional information. */ PH7_PRIVATE sxi32 PH7_VmOutputConsume( ph7_vm *pVm, /* Target VM */ SyString *pString /* Message to output */ ) { ph7_output_consumer *pCons = &pVm->sVmConsumer; sxi32 rc = SXRET_OK; /* Call the output consumer */ if( pString->nByte > 0 ){ rc = pCons->xConsumer((const void *)pString->zString,pString->nByte,pCons->pUserData); if( pCons->xConsumer != VmObConsumer ){ /* Increment output length */ pVm->nOutputLen += pString->nByte; } } return rc; } /* * Format a message and invoke the installed VM output consumer * callback to consume the formatted message. * Refer to the implementation of [ph7_context_output_format()] defined * in 'api.c' for additional information. */ PH7_PRIVATE sxi32 PH7_VmOutputConsumeAp( ph7_vm *pVm, /* Target VM */ const char *zFormat, /* Formatted message to output */ va_list ap /* Variable list of arguments */ ) { ph7_output_consumer *pCons = &pVm->sVmConsumer; sxi32 rc = SXRET_OK; SyBlob sWorker; /* Format the message and call the output consumer */ SyBlobInit(&sWorker,&pVm->sAllocator); SyBlobFormatAp(&sWorker,zFormat,ap); if( SyBlobLength(&sWorker) > 0 ){ /* Consume the formatted message */ rc = pCons->xConsumer(SyBlobData(&sWorker),SyBlobLength(&sWorker),pCons->pUserData); } if( pCons->xConsumer != VmObConsumer ){ /* Increment output length */ pVm->nOutputLen += SyBlobLength(&sWorker); } /* Release the working buffer */ SyBlobRelease(&sWorker); return rc; } /* * Return a string representation of the given PH7 OP code. * This function never fail and always return a pointer * to a null terminated string. */ static const char * VmInstrToString(sxi32 nOp) { const char *zOp = "Unknown "; switch(nOp){ case PH7_OP_DONE: zOp = "DONE "; break; case PH7_OP_HALT: zOp = "HALT "; break; case PH7_OP_LOAD: zOp = "LOAD "; break; case PH7_OP_LOADC: zOp = "LOADC "; break; case PH7_OP_LOAD_MAP: zOp = "LOAD_MAP "; break; case PH7_OP_LOAD_LIST: zOp = "LOAD_LIST "; break; case PH7_OP_LOAD_IDX: zOp = "LOAD_IDX "; break; case PH7_OP_LOAD_CLOSURE: zOp = "LOAD_CLOSR "; break; case PH7_OP_NOOP: zOp = "NOOP "; break; case PH7_OP_JMP: zOp = "JMP "; break; case PH7_OP_JZ: zOp = "JZ "; break; case PH7_OP_JNZ: zOp = "JNZ "; break; case PH7_OP_POP: zOp = "POP "; break; case PH7_OP_CAT: zOp = "CAT "; break; case PH7_OP_CVT_INT: zOp = "CVT_INT "; break; case PH7_OP_CVT_STR: zOp = "CVT_STR "; break; case PH7_OP_CVT_REAL: zOp = "CVT_REAL "; break; case PH7_OP_CALL: zOp = "CALL "; break; case PH7_OP_UMINUS: zOp = "UMINUS "; break; case PH7_OP_UPLUS: zOp = "UPLUS "; break; case PH7_OP_BITNOT: zOp = "BITNOT "; break; case PH7_OP_LNOT: zOp = "LOGNOT "; break; case PH7_OP_MUL: zOp = "MUL "; break; case PH7_OP_DIV: zOp = "DIV "; break; case PH7_OP_MOD: zOp = "MOD "; break; case PH7_OP_ADD: zOp = "ADD "; break; case PH7_OP_SUB: zOp = "SUB "; break; case PH7_OP_SHL: zOp = "SHL "; break; case PH7_OP_SHR: zOp = "SHR "; break; case PH7_OP_LT: zOp = "LT "; break; case PH7_OP_LE: zOp = "LE "; break; case PH7_OP_GT: zOp = "GT "; break; case PH7_OP_GE: zOp = "GE "; break; case PH7_OP_EQ: zOp = "EQ "; break; case PH7_OP_NEQ: zOp = "NEQ "; break; case PH7_OP_TEQ: zOp = "TEQ "; break; case PH7_OP_TNE: zOp = "TNE "; break; case PH7_OP_BAND: zOp = "BITAND "; break; case PH7_OP_BXOR: zOp = "BITXOR "; break; case PH7_OP_BOR: zOp = "BITOR "; break; case PH7_OP_LAND: zOp = "LOGAND "; break; case PH7_OP_LOR: zOp = "LOGOR "; break; case PH7_OP_LXOR: zOp = "LOGXOR "; break; case PH7_OP_STORE: zOp = "STORE "; break; case PH7_OP_STORE_IDX: zOp = "STORE_IDX "; break; case PH7_OP_STORE_IDX_REF: zOp = "STORE_IDX_R"; break; case PH7_OP_PULL: zOp = "PULL "; break; case PH7_OP_SWAP: zOp = "SWAP "; break; case PH7_OP_YIELD: zOp = "YIELD "; break; case PH7_OP_CVT_BOOL: zOp = "CVT_BOOL "; break; case PH7_OP_CVT_NULL: zOp = "CVT_NULL "; break; case PH7_OP_CVT_ARRAY: zOp = "CVT_ARRAY "; break; case PH7_OP_CVT_OBJ: zOp = "CVT_OBJ "; break; case PH7_OP_CVT_NUMC: zOp = "CVT_NUMC "; break; case PH7_OP_INCR: zOp = "INCR "; break; case PH7_OP_DECR: zOp = "DECR "; break; case PH7_OP_SEQ: zOp = "SEQ "; break; case PH7_OP_SNE: zOp = "SNE "; break; case PH7_OP_NEW: zOp = "NEW "; break; case PH7_OP_CLONE: zOp = "CLONE "; break; case PH7_OP_ADD_STORE: zOp = "ADD_STORE "; break; case PH7_OP_SUB_STORE: zOp = "SUB_STORE "; break; case PH7_OP_MUL_STORE: zOp = "MUL_STORE "; break; case PH7_OP_DIV_STORE: zOp = "DIV_STORE "; break; case PH7_OP_MOD_STORE: zOp = "MOD_STORE "; break; case PH7_OP_CAT_STORE: zOp = "CAT_STORE "; break; case PH7_OP_SHL_STORE: zOp = "SHL_STORE "; break; case PH7_OP_SHR_STORE: zOp = "SHR_STORE "; break; case PH7_OP_BAND_STORE: zOp = "BAND_STORE "; break; case PH7_OP_BOR_STORE: zOp = "BOR_STORE "; break; case PH7_OP_BXOR_STORE: zOp = "BXOR_STORE "; break; case PH7_OP_CONSUME: zOp = "CONSUME "; break; case PH7_OP_LOAD_REF: zOp = "LOAD_REF "; break; case PH7_OP_STORE_REF: zOp = "STORE_REF "; break; case PH7_OP_MEMBER: zOp = "MEMBER "; break; case PH7_OP_UPLINK: zOp = "UPLINK "; break; case PH7_OP_ERR_CTRL: zOp = "ERR_CTRL "; break; case PH7_OP_IS_A: zOp = "IS_A "; break; case PH7_OP_SWITCH: zOp = "SWITCH "; break; case PH7_OP_LOAD_EXCEPTION: zOp = "LOAD_EXCEP "; break; case PH7_OP_POP_EXCEPTION: zOp = "POP_EXCEP "; break; case PH7_OP_THROW: zOp = "THROW "; break; case PH7_OP_FOREACH_INIT: zOp = "4EACH_INIT "; break; case PH7_OP_FOREACH_STEP: zOp = "4EACH_STEP "; break; default: break; } return zOp; } /* * Dump PH7 bytecodes instructions to a human readable format. * The xConsumer() callback which is an used defined function * is responsible of consuming the generated dump. */ PH7_PRIVATE sxi32 PH7_VmDump( ph7_vm *pVm, /* Target VM */ ProcConsumer xConsumer, /* Output [i.e: dump] consumer callback */ void *pUserData /* Last argument to xConsumer() */ ) { sxi32 rc; rc = VmByteCodeDump(pVm->pByteContainer,xConsumer,pUserData); return rc; } /* * Default constant expansion callback used by the 'const' statement if used * outside a class body [i.e: global or function scope]. * Refer to the implementation of [PH7_CompileConstant()] defined * in 'compile.c' for additional information. */ PH7_PRIVATE void PH7_VmExpandConstantValue(ph7_value *pVal,void *pUserData) { SySet *pByteCode = (SySet *)pUserData; /* Evaluate and expand constant value */ VmLocalExec((ph7_vm *)SySetGetUserData(pByteCode),pByteCode,(ph7_value *)pVal); } /* * Section: * Function handling functions. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * int func_num_args(void) * Returns the number of arguments passed to the function. * Parameters * None. * Return * Total number of arguments passed into the current user-defined function * or -1 if called from the globe scope. */ static int vm_builtin_func_num_args(ph7_context *pCtx,int nArg,ph7_value **apArg) { VmFrame *pFrame; ph7_vm *pVm; /* Point to the target VM */ pVm = pCtx->pVm; /* Current frame */ pFrame = pVm->pFrame; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } if( pFrame->pParent == 0 ){ SXUNUSED(nArg); SXUNUSED(apArg); /* Global frame,return -1 */ ph7_result_int(pCtx,-1); return SXRET_OK; } /* Total number of arguments passed to the enclosing function */ nArg = (int)SySetUsed(&pFrame->sArg); ph7_result_int(pCtx,nArg); return SXRET_OK; } /* * value func_get_arg(int $arg_num) * Return an item from the argument list. * Parameters * Argument number(index start from zero). * Return * Returns the specified argument or FALSE on error. */ static int vm_builtin_func_get_arg(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pObj = 0; VmSlot *pSlot = 0; VmFrame *pFrame; ph7_vm *pVm; /* Point to the target VM */ pVm = pCtx->pVm; /* Current frame */ pFrame = pVm->pFrame; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } if( nArg < 1 || pFrame->pParent == 0 ){ /* Global frame or Missing arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Called in the global scope"); ph7_result_bool(pCtx,0); return SXRET_OK; } /* Extract the desired index */ nArg = ph7_value_to_int(apArg[0]); if( nArg < 0 || nArg >= (int)SySetUsed(&pFrame->sArg) ){ /* Invalid index,return FALSE */ ph7_result_bool(pCtx,0); return SXRET_OK; } /* Extract the desired argument */ if( (pSlot = (VmSlot *)SySetAt(&pFrame->sArg,(sxu32)nArg)) != 0 ){ if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pSlot->nIdx)) != 0 ){ /* Return the desired argument */ ph7_result_value(pCtx,(ph7_value *)pObj); }else{ /* No such argument,return false */ ph7_result_bool(pCtx,0); } }else{ /* CAN'T HAPPEN */ ph7_result_bool(pCtx,0); } return SXRET_OK; } /* * array func_get_args_byref(void) * Returns an array comprising a function's argument list. * Parameters * None. * Return * Returns an array in which each element is a POINTER to the corresponding * member of the current user-defined function's argument list. * Otherwise FALSE is returned on failure. * NOTE: * Arguments are returned to the array by reference. */ static int vm_builtin_func_get_args_byref(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pArray; VmFrame *pFrame; VmSlot *aSlot; sxu32 n; /* Point to the current frame */ pFrame = pCtx->pVm->pFrame; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } if( pFrame->pParent == 0 ){ /* Global frame,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Called in the global scope"); ph7_result_bool(pCtx,0); return SXRET_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); ph7_result_bool(pCtx,0); return SXRET_OK; } /* Start filling the array with the given arguments (Pass by reference) */ aSlot = (VmSlot *)SySetBasePtr(&pFrame->sArg); for( n = 0; n < SySetUsed(&pFrame->sArg) ; n++ ){ PH7_HashmapInsertByRef((ph7_hashmap *)pArray->x.pOther,0/*Automatic index assign*/,aSlot[n].nIdx); } /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return SXRET_OK; } /* * array func_get_args(void) * Returns an array comprising a copy of function's argument list. * Parameters * None. * Return * Returns an array in which each element is a copy of the corresponding * member of the current user-defined function's argument list. * Otherwise FALSE is returned on failure. */ static int vm_builtin_func_get_args(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pObj = 0; ph7_value *pArray; VmFrame *pFrame; VmSlot *aSlot; sxu32 n; /* Point to the current frame */ pFrame = pCtx->pVm->pFrame; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } if( pFrame->pParent == 0 ){ /* Global frame,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Called in the global scope"); ph7_result_bool(pCtx,0); return SXRET_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); ph7_result_bool(pCtx,0); return SXRET_OK; } /* Start filling the array with the given arguments */ aSlot = (VmSlot *)SySetBasePtr(&pFrame->sArg); for( n = 0; n < SySetUsed(&pFrame->sArg) ; n++ ){ pObj = (ph7_value *)SySetAt(&pCtx->pVm->aMemObj,aSlot[n].nIdx); if( pObj ){ ph7_array_add_elem(pArray,0/* Automatic index assign*/,pObj); } } /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return SXRET_OK; } /* * bool function_exists(string $name) * Return TRUE if the given function has been defined. * Parameters * The name of the desired function. * Return * Return TRUE if the given function has been defined.False otherwise */ static int vm_builtin_func_exists(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zName; ph7_vm *pVm; int nLen; int res; if( nArg < 1 ){ /* Missing argument,return FALSE */ ph7_result_bool(pCtx,0); return SXRET_OK; } /* Point to the target VM */ pVm = pCtx->pVm; /* Extract the function name */ zName = ph7_value_to_string(apArg[0],&nLen); /* Assume the function is not defined */ res = 0; /* Perform the lookup */ if( SyHashGet(&pVm->hFunction,(const void *)zName,(sxu32)nLen) != 0 || SyHashGet(&pVm->hHostFunction,(const void *)zName,(sxu32)nLen) != 0 ){ /* Function is defined */ res = 1; } ph7_result_bool(pCtx,res); return SXRET_OK; } /* Forward declaration */ static ph7_class * VmExtractClassFromValue(ph7_vm *pVm,ph7_value *pArg); /* * Verify that the contents of a variable can be called as a function. * [i.e: Whether it is callable or not]. * Return TRUE if callable.FALSE otherwise. */ PH7_PRIVATE int PH7_VmIsCallable(ph7_vm *pVm,ph7_value *pValue,int CallInvoke) { int res = 0; if( pValue->iFlags & MEMOBJ_OBJ ){ /* Call the magic method __invoke if available */ ph7_class_instance *pThis = (ph7_class_instance *)pValue->x.pOther; ph7_class_method *pMethod; pMethod = PH7_ClassExtractMethod(pThis->pClass,"__invoke",sizeof("__invoke")-1); if( pMethod && CallInvoke ){ ph7_value sResult; sxi32 rc; /* Invoke the magic method and extract the result */ PH7_MemObjInit(pVm,&sResult); rc = PH7_VmCallClassMethod(pVm,pThis,pMethod,&sResult,0,0); if( rc == SXRET_OK && (sResult.iFlags & (MEMOBJ_BOOL|MEMOBJ_INT)) ){ res = sResult.x.iVal != 0; } PH7_MemObjRelease(&sResult); } }else if( pValue->iFlags & MEMOBJ_HASHMAP ){ ph7_hashmap *pMap = (ph7_hashmap *)pValue->x.pOther; if( pMap->nEntry > 1 ){ ph7_class *pClass; ph7_value *pV; /* Extract the target class */ pV = (ph7_value *)SySetAt(&pVm->aMemObj,pMap->pFirst->nValIdx); if( pV ){ pClass = VmExtractClassFromValue(pVm,pV); if( pClass ){ ph7_class_method *pMethod; /* Extract the target method */ pV = (ph7_value *)SySetAt(&pVm->aMemObj,pMap->pFirst->pPrev->nValIdx); if( pV && (pV->iFlags & MEMOBJ_STRING) && SyBlobLength(&pV->sBlob) > 0 ){ /* Perform the lookup */ pMethod = PH7_ClassExtractMethod(pClass,(const char *)SyBlobData(&pV->sBlob),SyBlobLength(&pV->sBlob)); if( pMethod ){ /* Method is callable */ res = 1; } } } } } }else if( pValue->iFlags & MEMOBJ_STRING ){ const char *zName; int nLen; /* Extract the name */ zName = ph7_value_to_string(pValue,&nLen); /* Perform the lookup */ if( SyHashGet(&pVm->hFunction,(const void *)zName,(sxu32)nLen) != 0 || SyHashGet(&pVm->hHostFunction,(const void *)zName,(sxu32)nLen) != 0 ){ /* Function is callable */ res = 1; } } return res; } /* * bool is_callable(callable $name[,bool $syntax_only = false]) * Verify that the contents of a variable can be called as a function. * Parameters * $name * The callback function to check * $syntax_only * If set to TRUE the function only verifies that name might be a function or method. * It will only reject simple variables that are not strings, or an array that does * not have a valid structure to be used as a callback. The valid ones are supposed * to have only 2 entries, the first of which is an object or a string, and the second * a string. * Return * TRUE if name is callable, FALSE otherwise. */ static int vm_builtin_is_callable(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm; int res; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return SXRET_OK; } /* Point to the target VM */ pVm = pCtx->pVm; /* Perform the requested operation */ res = PH7_VmIsCallable(pVm,apArg[0],TRUE); ph7_result_bool(pCtx,res); return SXRET_OK; } /* * Hash walker callback used by the [get_defined_functions()] function * defined below. */ static int VmHashFuncStep(SyHashEntry *pEntry,void *pUserData) { ph7_value *pArray = (ph7_value *)pUserData; ph7_value sName; sxi32 rc; /* Prepare the function name for insertion */ PH7_MemObjInitFromString(pArray->pVm,&sName,0); PH7_MemObjStringAppend(&sName,(const char *)pEntry->pKey,pEntry->nKeyLen); /* Perform the insertion */ rc = ph7_array_add_elem(pArray,0/* Automatic index assign */,&sName); /* Will make it's own copy */ PH7_MemObjRelease(&sName); return rc; } /* * array get_defined_functions(void) * Returns an array of all defined functions. * Parameter * None. * Return * Returns an multidimensional array containing a list of all defined functions * both built-in (internal) and user-defined. * The internal functions will be accessible via $arr["internal"], and the user * defined ones using $arr["user"]. * Note: * NULL is returned on failure. */ static int vm_builtin_get_defined_func(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pArray,*pEntry; /* NOTE: * Don't worry about freeing memory here,every allocated resource will be released * automatically by the engine as soon we return from this foreign function. */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Return NULL */ ph7_result_null(pCtx); return SXRET_OK; } pEntry = ph7_context_new_array(pCtx); if( pEntry == 0 ){ /* Return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* Fill with the appropriate information */ SyHashForEach(&pCtx->pVm->hHostFunction,VmHashFuncStep,pEntry); /* Create the 'internal' index */ ph7_array_add_strkey_elem(pArray,"internal",pEntry); /* Will make it's own copy */ /* Create the user-func array */ pEntry = ph7_context_new_array(pCtx); if( pEntry == 0 ){ /* Return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* Fill with the appropriate information */ SyHashForEach(&pCtx->pVm->hFunction,VmHashFuncStep,pEntry); /* Create the 'user' index */ ph7_array_add_strkey_elem(pArray,"user",pEntry); /* Will make it's own copy */ /* Return the multi-dimensional array */ ph7_result_value(pCtx,pArray); return SXRET_OK; } /* * void register_shutdown_function(callable $callback[,mixed $param,...) * Register a function for execution on shutdown. * Note * Multiple calls to register_shutdown_function() can be made, and each will * be called in the same order as they were registered. * Parameters * $callback * The shutdown callback to register. * $param * One or more Parameter to pass to the registered callback. * Return * Nothing. */ static int vm_builtin_register_shutdown_function(ph7_context *pCtx,int nArg,ph7_value **apArg) { VmShutdownCB sEntry; int i,j; if( nArg < 1 || (apArg[0]->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP)) == 0 ){ /* Missing/Invalid arguments,return immediately */ return PH7_OK; } /* Zero the Entry */ SyZero(&sEntry,sizeof(VmShutdownCB)); /* Initialize fields */ PH7_MemObjInit(pCtx->pVm,&sEntry.sCallback); /* Save the callback name for later invocation name */ PH7_MemObjStore(apArg[0],&sEntry.sCallback); for( i = 0 ; i < (int)SX_ARRAYSIZE(sEntry.aArg) ; ++i ){ PH7_MemObjInit(pCtx->pVm,&sEntry.aArg[i]); } /* Copy arguments */ for(j = 0, i = 1 ; i < nArg ; j++,i++ ){ if( j >= (int)SX_ARRAYSIZE(sEntry.aArg) ){ /* Limit reached */ break; } PH7_MemObjStore(apArg[i],&sEntry.aArg[j]); } sEntry.nArg = j; /* Install the callback */ SySetPut(&pCtx->pVm->aShutdown,(const void *)&sEntry); return PH7_OK; } /* * Section: * Class handling functions. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * Extract the top active class. NULL is returned * if the class stack is empty. */ PH7_PRIVATE ph7_class * PH7_VmPeekTopClass(ph7_vm *pVm) { SySet *pSet = &pVm->aSelf; ph7_class **apClass; if( SySetUsed(pSet) <= 0 ){ /* Empty stack,return NULL */ return 0; } /* Peek the last entry */ apClass = (ph7_class **)SySetBasePtr(pSet); return apClass[pSet->nUsed - 1]; } /* * string get_class ([ object $object = NULL ] ) * Returns the name of the class of an object * Parameters * object * The tested object. This parameter may be omitted when inside a class. * Return * The name of the class of which object is an instance. * Returns FALSE if object is not an object. * If object is omitted when inside a class, the name of that class is returned. */ static int vm_builtin_get_class(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_class *pClass; SyString *pName; if( nArg < 1 ){ /* Check if we are inside a class */ pClass = PH7_VmPeekTopClass(pCtx->pVm); if( pClass ){ /* Point to the class name */ pName = &pClass->sName; ph7_result_string(pCtx,pName->zString,(int)pName->nByte); }else{ /* Not inside class,return FALSE */ ph7_result_bool(pCtx,0); } }else{ /* Extract the target class */ pClass = VmExtractClassFromValue(pCtx->pVm,apArg[0]); if( pClass ){ pName = &pClass->sName; /* Return the class name */ ph7_result_string(pCtx,pName->zString,(int)pName->nByte); }else{ /* Not a class instance,return FALSE */ ph7_result_bool(pCtx,0); } } return PH7_OK; } /* * string get_parent_class([object $object = NULL ] ) * Returns the name of the parent class of an object * Parameters * object * The tested object. This parameter may be omitted when inside a class. * Return * The name of the parent class of which object is an instance. * Returns FALSE if object is not an object or if the object does * not have a parent. * If object is omitted when inside a class, the name of that class is returned. */ static int vm_builtin_get_parent_class(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_class *pClass; SyString *pName; if( nArg < 1 ){ /* Check if we are inside a class [i.e: a method call]*/ pClass = PH7_VmPeekTopClass(pCtx->pVm); if( pClass && pClass->pBase ){ /* Point to the class name */ pName = &pClass->pBase->sName; ph7_result_string(pCtx,pName->zString,(int)pName->nByte); }else{ /* Not inside class,return FALSE */ ph7_result_bool(pCtx,0); } }else{ /* Extract the target class */ pClass = VmExtractClassFromValue(pCtx->pVm,apArg[0]); if( pClass ){ if( pClass->pBase ){ pName = &pClass->pBase->sName; /* Return the parent class name */ ph7_result_string(pCtx,pName->zString,(int)pName->nByte); }else{ /* Object does not have a parent class */ ph7_result_bool(pCtx,0); } }else{ /* Not a class instance,return FALSE */ ph7_result_bool(pCtx,0); } } return PH7_OK; } /* * string get_called_class(void) * Gets the name of the class the static method is called in. * Parameters * None. * Return * Returns the class name. Returns FALSE if called from outside a class. */ static int vm_builtin_get_called_class(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_class *pClass; /* Check if we are inside a class [i.e: a method call] */ pClass = PH7_VmPeekTopClass(pCtx->pVm); if( pClass ){ SyString *pName; /* Point to the class name */ pName = &pClass->sName; ph7_result_string(pCtx,pName->zString,(int)pName->nByte); }else{ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Not inside class,return FALSE */ ph7_result_bool(pCtx,0); } return PH7_OK; } /* * Extract a ph7_class from the given ph7_value. * The given value must be of type object [i.e: class instance] or * string which hold the class name. */ static ph7_class * VmExtractClassFromValue(ph7_vm *pVm,ph7_value *pArg) { ph7_class *pClass = 0; if( ph7_value_is_object(pArg) ){ /* Class instance already loaded,no need to perform a lookup */ pClass = ((ph7_class_instance *)pArg->x.pOther)->pClass; }else if( ph7_value_is_string(pArg) ){ const char *zClass; int nLen; /* Extract class name */ zClass = ph7_value_to_string(pArg,&nLen); if( nLen > 0 ){ SyHashEntry *pEntry; /* Perform a lookup */ pEntry = SyHashGet(&pVm->hClass,(const void *)zClass,(sxu32)nLen); if( pEntry ){ /* Point to the desired class */ pClass = (ph7_class *)pEntry->pUserData; } } } return pClass; } /* * bool property_exists(mixed $class,string $property) * Checks if the object or class has a property. * Parameters * class * The class name or an object of the class to test for * property * The name of the property * Return * Returns TRUE if the property exists,FALSE otherwise. */ static int vm_builtin_property_exists(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume attribute does not exists */ if( nArg > 1 ){ ph7_class *pClass; pClass = VmExtractClassFromValue(pCtx->pVm,apArg[0]); if( pClass ){ const char *zName; int nLen; /* Extract attribute name */ zName = ph7_value_to_string(apArg[1],&nLen); if( nLen > 0 ){ /* Perform the lookup in the attribute and method table */ if( SyHashGet(&pClass->hAttr,(const void *)zName,(sxu32)nLen) != 0 || SyHashGet(&pClass->hMethod,(const void *)zName,(sxu32)nLen) != 0 ){ /* property exists,flag that */ res = 1; } } } } ph7_result_bool(pCtx,res); return PH7_OK; } /* * bool method_exists(mixed $class,string $method) * Checks if the given method is a class member. * Parameters * class * The class name or an object of the class to test for * property * The name of the method * Return * Returns TRUE if the method exists,FALSE otherwise. */ static int vm_builtin_method_exists(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume method does not exists */ if( nArg > 1 ){ ph7_class *pClass; pClass = VmExtractClassFromValue(pCtx->pVm,apArg[0]); if( pClass ){ const char *zName; int nLen; /* Extract method name */ zName = ph7_value_to_string(apArg[1],&nLen); if( nLen > 0 ){ /* Perform the lookup in the method table */ if( SyHashGet(&pClass->hMethod,(const void *)zName,(sxu32)nLen) != 0 ){ /* method exists,flag that */ res = 1; } } } } ph7_result_bool(pCtx,res); return PH7_OK; } /* * bool class_exists(string $class_name [, bool $autoload = true ] ) * Checks if the class has been defined. * Parameters * class_name * The class name. The name is matched in a case-sensitive manner * unlinke the standard PHP engine. * autoload * Whether or not to call __autoload by default. * Return * TRUE if class_name is a defined class, FALSE otherwise. */ static int vm_builtin_class_exists(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume class does not exists */ if( nArg > 0 ){ const char *zName; int nLen; /* Extract given name */ zName = ph7_value_to_string(apArg[0],&nLen); /* Perform a hashlookup */ if( nLen > 0 && SyHashGet(&pCtx->pVm->hClass,(const void *)zName,(sxu32)nLen) != 0 ){ /* class is available */ res = 1; } } ph7_result_bool(pCtx,res); return PH7_OK; } /* * bool interface_exists(string $class_name [, bool $autoload = true ] ) * Checks if the interface has been defined. * Parameters * class_name * The class name. The name is matched in a case-sensitive manner * unlinke the standard PHP engine. * autoload * Whether or not to call __autoload by default. * Return * TRUE if class_name is a defined class, FALSE otherwise. */ static int vm_builtin_interface_exists(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume class does not exists */ if( nArg > 0 ){ SyHashEntry *pEntry = 0; const char *zName; int nLen; /* Extract given name */ zName = ph7_value_to_string(apArg[0],&nLen); /* Perform a hashlookup */ if( nLen > 0 ){ pEntry = SyHashGet(&pCtx->pVm->hClass,(const void *)zName,(sxu32)nLen); } if( pEntry ){ ph7_class *pClass = (ph7_class *)pEntry->pUserData; while( pClass ){ if( pClass->iFlags & PH7_CLASS_INTERFACE ){ /* interface is available */ res = 1; break; } /* Next with the same name */ pClass = pClass->pNextName; } } } ph7_result_bool(pCtx,res); return PH7_OK; } /* * bool class_alias([string $original[,string $alias ]]) * Creates an alias for a class. * Parameters * original * The original class. * alias * The alias name for the class. * Return * Returns TRUE on success or FALSE on failure. */ static int vm_builtin_class_alias(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zOld,*zNew; int nOldLen,nNewLen; SyHashEntry *pEntry; ph7_class *pClass; char *zDup; sxi32 rc; if( nArg < 2 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract old class name */ zOld = ph7_value_to_string(apArg[0],&nOldLen); /* Extract alias name */ zNew = ph7_value_to_string(apArg[1],&nNewLen); if( nNewLen < 1 ){ /* Invalid alias name,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform a hash lookup */ pEntry = SyHashGet(&pCtx->pVm->hClass,(const void *)zOld,(sxu32)nOldLen); if( pEntry == 0 ){ /* No such class,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the class */ pClass = (ph7_class *)pEntry->pUserData; /* Duplicate alias name */ zDup = SyMemBackendStrDup(&pCtx->pVm->sAllocator,zNew,(sxu32)nNewLen); if( zDup == 0 ){ /* Out of memory,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Create the alias */ rc = SyHashInsert(&pCtx->pVm->hClass,(const void *)zDup,(sxu32)nNewLen,pClass); if( rc != SXRET_OK ){ SyMemBackendFree(&pCtx->pVm->sAllocator,zDup); } ph7_result_bool(pCtx,rc == SXRET_OK); return PH7_OK; } /* * array get_declared_classes(void) * Returns an array with the name of the defined classes * Parameters * None * Return * Returns an array of the names of the declared classes * in the current script. * Note: * NULL is returned on failure. */ static int vm_builtin_get_declared_classes(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pName,*pArray; SyHashEntry *pEntry; /* Create a new array first */ pArray = ph7_context_new_array(pCtx); pName = ph7_context_new_scalar(pCtx); if( pArray == 0 || pName == 0){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Out of memory,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Fill the array with the defined classes */ SyHashResetLoopCursor(&pCtx->pVm->hClass); while((pEntry = SyHashGetNextEntry(&pCtx->pVm->hClass)) != 0 ){ ph7_class *pClass = (ph7_class *)pEntry->pUserData; /* Do not register classes defined as interfaces */ if( (pClass->iFlags & PH7_CLASS_INTERFACE) == 0 ){ ph7_value_string(pName,SyStringData(&pClass->sName),(int)SyStringLength(&pClass->sName)); /* insert class name */ ph7_array_add_elem(pArray,0/*Automatic index assign*/,pName); /* Will make it's own copy */ /* Reset the cursor */ ph7_value_reset_string_cursor(pName); } } /* Return the created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array get_declared_interfaces(void) * Returns an array with the name of the defined interfaces * Parameters * None * Return * Returns an array of the names of the declared interfaces * in the current script. * Note: * NULL is returned on failure. */ static int vm_builtin_get_declared_interfaces(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pName,*pArray; SyHashEntry *pEntry; /* Create a new array first */ pArray = ph7_context_new_array(pCtx); pName = ph7_context_new_scalar(pCtx); if( pArray == 0 || pName == 0 ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Out of memory,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Fill the array with the defined classes */ SyHashResetLoopCursor(&pCtx->pVm->hClass); while((pEntry = SyHashGetNextEntry(&pCtx->pVm->hClass)) != 0 ){ ph7_class *pClass = (ph7_class *)pEntry->pUserData; /* Register classes defined as interfaces only */ if( pClass->iFlags & PH7_CLASS_INTERFACE ){ ph7_value_string(pName,SyStringData(&pClass->sName),(int)SyStringLength(&pClass->sName)); /* insert interface name */ ph7_array_add_elem(pArray,0/*Automatic index assign*/,pName); /* Will make it's own copy */ /* Reset the cursor */ ph7_value_reset_string_cursor(pName); } } /* Return the created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array get_class_methods(string/object $class_name) * Returns an array with the name of the class methods * Parameters * class_name * The class name or class instance * Return * Returns an array of method names defined for the class specified by class_name. * In case of an error, it returns NULL. * Note: * NULL is returned on failure. */ static int vm_builtin_get_class_methods(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pName,*pArray; SyHashEntry *pEntry; ph7_class *pClass; /* Extract the target class first */ pClass = 0; if( nArg > 0 ){ pClass = VmExtractClassFromValue(pCtx->pVm,apArg[0]); } if( pClass == 0 ){ /* No such class,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); pName = ph7_context_new_scalar(pCtx); if( pArray == 0 || pName == 0){ /* Out of memory,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Fill the array with the defined methods */ SyHashResetLoopCursor(&pClass->hMethod); while((pEntry = SyHashGetNextEntry(&pClass->hMethod)) != 0 ){ ph7_class_method *pMethod = (ph7_class_method *)pEntry->pUserData; /* Insert method name */ ph7_value_string(pName,SyStringData(&pMethod->sFunc.sName),(int)SyStringLength(&pMethod->sFunc.sName)); ph7_array_add_elem(pArray,0/*Automatic index assign*/,pName); /* Will make it's own copy */ /* Reset the cursor */ ph7_value_reset_string_cursor(pName); } /* Return the created array */ ph7_result_value(pCtx,pArray); /* * Don't worry about freeing memory here,everything will be relased * automatically as soon we return from this foreign function. */ return PH7_OK; } /* * This function return TRUE(1) if the given class attribute stored * in the pAttrName parameter is visible and thus can be extracted * from the current scope.Otherwise FALSE is returned. */ static int VmClassMemberAccess( ph7_vm *pVm, /* Target VM */ ph7_class *pClass, /* Target Class */ const SyString *pAttrName, /* Attribute name */ sxi32 iProtection, /* Attribute protection level [i.e: public,protected or private] */ int bLog /* TRUE to log forbidden access. */ ) { if( iProtection != PH7_CLASS_PROT_PUBLIC ){ VmFrame *pFrame = pVm->pFrame; ph7_vm_func *pVmFunc; while( pFrame->pParent && (pFrame->iFlags & (VM_FRAME_EXCEPTION|VM_FRAME_CATCH) ) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } pVmFunc = (ph7_vm_func *)pFrame->pUserData; if( pVmFunc == 0 || (pVmFunc->iFlags & VM_FUNC_CLASS_METHOD) == 0 ){ goto dis; /* Access is forbidden */ } if( iProtection == PH7_CLASS_PROT_PRIVATE ){ /* Must be the same instance */ if( (ph7_class *)pVmFunc->pUserData != pClass ){ goto dis; /* Access is forbidden */ } }else{ /* Protected */ ph7_class *pBase = (ph7_class *)pVmFunc->pUserData; /* Must be a derived class */ if( !VmInstanceOf(pClass,pBase) ){ goto dis; /* Access is forbidden */ } } } return 1; /* Access is granted */ dis: if( bLog ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR, "Access to the class attribute '%z->%z' is forbidden", &pClass->sName,pAttrName); } return 0; /* Access is forbidden */ } /* * array get_class_vars(string/object $class_name) * Get the default properties of the class * Parameters * class_name * The class name or class instance * Return * Returns an associative array of declared properties visible from the current scope * with their default value. The resulting array elements are in the form * of varname => value. * Note: * NULL is returned on failure. */ static int vm_builtin_get_class_vars(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pName,*pArray,sValue; SyHashEntry *pEntry; ph7_class *pClass; /* Extract the target class first */ pClass = 0; if( nArg > 0 ){ pClass = VmExtractClassFromValue(pCtx->pVm,apArg[0]); } if( pClass == 0 ){ /* No such class,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); pName = ph7_context_new_scalar(pCtx); PH7_MemObjInit(pCtx->pVm,&sValue); if( pArray == 0 || pName == 0){ /* Out of memory,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Fill the array with the defined attribute visible from the current scope */ SyHashResetLoopCursor(&pClass->hAttr); while((pEntry = SyHashGetNextEntry(&pClass->hAttr)) != 0 ){ ph7_class_attr *pAttr = (ph7_class_attr *)pEntry->pUserData; /* Check if the access is allowed */ if( VmClassMemberAccess(pCtx->pVm,pClass,&pAttr->sName,pAttr->iProtection,FALSE) ){ SyString *pAttrName = &pAttr->sName; ph7_value *pValue = 0; if( pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT|PH7_CLASS_ATTR_STATIC) ){ /* Extract static attribute value which is always computed */ pValue = (ph7_value *)SySetAt(&pCtx->pVm->aMemObj,pAttr->nIdx); }else{ if( SySetUsed(&pAttr->aByteCode) > 0 ){ PH7_MemObjRelease(&sValue); /* Compute default value (any complex expression) associated with this attribute */ VmLocalExec(pCtx->pVm,&pAttr->aByteCode,&sValue); pValue = &sValue; } } /* Fill in the array */ ph7_value_string(pName,pAttrName->zString,pAttrName->nByte); ph7_array_add_elem(pArray,pName,pValue); /* Will make it's own copy */ /* Reset the cursor */ ph7_value_reset_string_cursor(pName); } } PH7_MemObjRelease(&sValue); /* Return the created array */ ph7_result_value(pCtx,pArray); /* * Don't worry about freeing memory here,everything will be relased * automatically as soon we return from this foreign function. */ return PH7_OK; } /* * array get_object_vars(object $this) * Gets the properties of the given object * Parameters * this * A class instance * Return * Returns an associative array of defined object accessible non-static properties * for the specified object in scope. If a property have not been assigned a value * it will be returned with a NULL value. * Note: * NULL is returned on failure. */ static int vm_builtin_get_object_vars(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_class_instance *pThis = 0; ph7_value *pName,*pArray; SyHashEntry *pEntry; if( nArg > 0 && (apArg[0]->iFlags & MEMOBJ_OBJ) ){ /* Extract the target instance */ pThis = (ph7_class_instance *)apArg[0]->x.pOther; } if( pThis == 0 ){ /* No such instance,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); pName = ph7_context_new_scalar(pCtx); if( pArray == 0 || pName == 0){ /* Out of memory,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Fill the array with the defined attribute visible from the current scope */ SyHashResetLoopCursor(&pThis->hAttr); while((pEntry = SyHashGetNextEntry(&pThis->hAttr)) != 0 ){ VmClassAttr *pVmAttr = (VmClassAttr *)pEntry->pUserData; SyString *pAttrName; if( pVmAttr->pAttr->iFlags & (PH7_CLASS_ATTR_STATIC|PH7_CLASS_ATTR_CONSTANT) ){ /* Only non-static/constant attributes are extracted */ continue; } pAttrName = &pVmAttr->pAttr->sName; /* Check if the access is allowed */ if( VmClassMemberAccess(pCtx->pVm,pThis->pClass,pAttrName,pVmAttr->pAttr->iProtection,FALSE) ){ ph7_value *pValue = 0; /* Extract attribute */ pValue = PH7_ClassInstanceExtractAttrValue(pThis,pVmAttr); if( pValue ){ /* Insert attribute name in the array */ ph7_value_string(pName,pAttrName->zString,pAttrName->nByte); ph7_array_add_elem(pArray,pName,pValue); /* Will make it's own copy */ } /* Reset the cursor */ ph7_value_reset_string_cursor(pName); } } /* Return the created array */ ph7_result_value(pCtx,pArray); /* * Don't worry about freeing memory here,everything will be relased * automatically as soon we return from this foreign function. */ return PH7_OK; } /* * This function returns TRUE if the given class is an implemented * interface.Otherwise FALSE is returned. */ static int VmQueryInterfaceSet(ph7_class *pClass,SySet *pSet) { ph7_class **apInterface; sxu32 n; if( SySetUsed(pSet) < 1 ){ /* Empty interface container */ return FALSE; } /* Point to the set of implemented interfaces */ apInterface = (ph7_class **)SySetBasePtr(pSet); /* Perform the lookup */ for( n = 0 ; n < SySetUsed(pSet) ; n++ ){ if( apInterface[n] == pClass ){ return TRUE; } } return FALSE; } /* * This function returns TRUE if the given class (first argument) * is an instance of the main class (second argument). * Otherwise FALSE is returned. */ static int VmInstanceOf(ph7_class *pThis,ph7_class *pClass) { ph7_class *pParent; sxi32 rc; if( pThis == pClass ){ /* Instance of the same class */ return TRUE; } /* Check implemented interfaces */ rc = VmQueryInterfaceSet(pClass,&pThis->aInterface); if( rc ){ return TRUE; } /* Check parent classes */ pParent = pThis->pBase; while( pParent ){ if( pParent == pClass ){ /* Same instance */ return TRUE; } /* Check the implemented interfaces */ rc = VmQueryInterfaceSet(pClass,&pParent->aInterface); if( rc ){ return TRUE; } /* Point to the parent class */ pParent = pParent->pBase; } /* Not an instance of the the given class */ return FALSE; } /* * This function returns TRUE if the given class (first argument) * is a subclass of the main class (second argument). * Otherwise FALSE is returned. */ static int VmSubclassOf(ph7_class *pClass,ph7_class *pBase) { SySet *pInterface = &pClass->aInterface; SyHashEntry *pEntry; SyString *pName; sxi32 rc; while( pClass ){ pName = &pClass->sName; /* Query the derived hashtable */ pEntry = SyHashGet(&pBase->hDerived,(const void *)pName->zString,pName->nByte); if( pEntry ){ return TRUE; } pClass = pClass->pBase; } rc = VmQueryInterfaceSet(pBase,pInterface); if( rc ){ return TRUE; } /* Not a subclass */ return FALSE; } /* * bool is_a(object $object,string $class_name) * Checks if the object is of this class or has this class as one of its parents. * Parameters * object * The tested object * class_name * The class name * Return * Returns TRUE if the object is of this class or has this class as one of its * parents, FALSE otherwise. */ static int vm_builtin_is_a(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume FALSE by default */ if( nArg > 1 && ph7_value_is_object(apArg[0]) ){ ph7_class_instance *pThis = (ph7_class_instance *)apArg[0]->x.pOther; ph7_class *pClass; /* Extract the given class */ pClass = VmExtractClassFromValue(pCtx->pVm,apArg[1]); if( pClass ){ /* Perform the query */ res = VmInstanceOf(pThis->pClass,pClass); } } /* Query result */ ph7_result_bool(pCtx,res); return PH7_OK; } /* * bool is_subclass_of(object/string $object,object/string $class_name) * Checks if the object has this class as one of its parents. * Parameters * object * The tested object * class_name * The class name * Return * This function returns TRUE if the object , belongs to a class * which is a subclass of class_name, FALSE otherwise. */ static int vm_builtin_is_subclass_of(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume FALSE by default */ if( nArg > 1 ){ ph7_class *pClass,*pMain; /* Extract the given classes */ pClass = VmExtractClassFromValue(pCtx->pVm,apArg[0]); pMain = VmExtractClassFromValue(pCtx->pVm,apArg[1]); if( pClass && pMain ){ /* Perform the query */ res = VmSubclassOf(pClass,pMain); } } /* Query result */ ph7_result_bool(pCtx,res); return PH7_OK; } /* * Call a class method where the name of the method is stored in the pMethod * parameter and the given arguments are stored in the apArg[] array. * Return SXRET_OK if the method was successfuly called.Any other * return value indicates failure. */ PH7_PRIVATE sxi32 PH7_VmCallClassMethod( ph7_vm *pVm, /* Target VM */ ph7_class_instance *pThis, /* Target class instance [i.e: Object in the PHP jargon]*/ ph7_class_method *pMethod, /* Method name */ ph7_value *pResult, /* Store method return value here. NULL otherwise */ int nArg, /* Total number of given arguments */ ph7_value **apArg /* Method arguments */ ) { ph7_value *aStack; VmInstr aInstr[2]; int iCursor; int i; /* Create a new operand stack */ aStack = VmNewOperandStack(&(*pVm),2/* Method name + Aux data */+nArg); if( aStack == 0 ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR, "PH7 is running out of memory while invoking class method"); return SXERR_MEM; } /* Fill the operand stack with the given arguments */ for( i = 0 ; i < nArg ; i++ ){ PH7_MemObjLoad(apArg[i],&aStack[i]); /* * Symisc eXtension: * Parameters to [call_user_func()] can be passed by reference. */ aStack[i].nIdx = apArg[i]->nIdx; } iCursor = nArg + 1; if( pThis ){ /* * Push the class instance so that the '$this' variable will be available. */ pThis->iRef++; /* Increment reference count */ aStack[i].x.pOther = pThis; aStack[i].iFlags = MEMOBJ_OBJ; } aStack[i].nIdx = SXU32_HIGH; /* Mark as constant */ i++; /* Push method name */ SyBlobReset(&aStack[i].sBlob); SyBlobAppend(&aStack[i].sBlob,(const void *)SyStringData(&pMethod->sVmName),SyStringLength(&pMethod->sVmName)); aStack[i].iFlags = MEMOBJ_STRING; aStack[i].nIdx = SXU32_HIGH; /* Emit the CALL istruction */ aInstr[0].iOp = PH7_OP_CALL; aInstr[0].iP1 = nArg; /* Total number of given arguments */ aInstr[0].iP2 = 0; aInstr[0].p3 = 0; /* Emit the DONE instruction */ aInstr[1].iOp = PH7_OP_DONE; aInstr[1].iP1 = 1; /* Extract method return value */ aInstr[1].iP2 = 0; aInstr[1].p3 = 0; /* Execute the method body (if available) */ VmByteCodeExec(&(*pVm),aInstr,aStack,iCursor,pResult,0,TRUE); /* Clean up the mess left behind */ SyMemBackendFree(&pVm->sAllocator,aStack); return PH7_OK; } /* * Call a user defined or foreign function where the name of the function * is stored in the pFunc parameter and the given arguments are stored * in the apArg[] array. * Return SXRET_OK if the function was successfuly called.Any other * return value indicates failure. */ PH7_PRIVATE sxi32 PH7_VmCallUserFunction( ph7_vm *pVm, /* Target VM */ ph7_value *pFunc, /* Callback name */ int nArg, /* Total number of given arguments */ ph7_value **apArg, /* Callback arguments */ ph7_value *pResult /* Store callback return value here. NULL otherwise */ ) { ph7_value *aStack; VmInstr aInstr[2]; int i; if((pFunc->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP)) == 0 ){ /* Don't bother processing,it's invalid anyway */ if( pResult ){ /* Assume a null return value */ PH7_MemObjRelease(pResult); } return SXERR_INVALID; } if( pFunc->iFlags & MEMOBJ_HASHMAP ){ /* Class method */ ph7_hashmap *pMap = (ph7_hashmap *)pFunc->x.pOther; ph7_class_method *pMethod = 0; ph7_class_instance *pThis = 0; ph7_class *pClass = 0; ph7_value *pValue; sxi32 rc; if( pMap->nEntry < 2 /* Class name/instance + method name */){ /* Empty hashmap,nothing to call */ if( pResult ){ /* Assume a null return value */ PH7_MemObjRelease(pResult); } return SXRET_OK; } /* Extract the class name or an instance of it */ pValue = (ph7_value *)SySetAt(&pVm->aMemObj,pMap->pFirst->nValIdx); if( pValue ){ pClass = VmExtractClassFromValue(&(*pVm),pValue); } if( pClass == 0 ){ /* No such class,return NULL */ if( pResult ){ PH7_MemObjRelease(pResult); } return SXRET_OK; } if( pValue->iFlags & MEMOBJ_OBJ ){ /* Point to the class instance */ pThis = (ph7_class_instance *)pValue->x.pOther; } /* Try to extract the method */ pValue = (ph7_value *)SySetAt(&pVm->aMemObj,pMap->pFirst->pPrev->nValIdx); if( pValue ){ if( (pValue->iFlags & MEMOBJ_STRING) && SyBlobLength(&pValue->sBlob) > 0 ){ pMethod = PH7_ClassExtractMethod(pClass,(const char *)SyBlobData(&pValue->sBlob), SyBlobLength(&pValue->sBlob)); } } if( pMethod == 0 ){ /* No such method,return NULL */ if( pResult ){ PH7_MemObjRelease(pResult); } return SXRET_OK; } /* Call the class method */ rc = PH7_VmCallClassMethod(&(*pVm),pThis,pMethod,pResult,nArg,apArg); return rc; } /* Create a new operand stack */ aStack = VmNewOperandStack(&(*pVm),1+nArg); if( aStack == 0 ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR, "PH7 is running out of memory while invoking user callback"); if( pResult ){ /* Assume a null return value */ PH7_MemObjRelease(pResult); } return SXERR_MEM; } /* Fill the operand stack with the given arguments */ for( i = 0 ; i < nArg ; i++ ){ PH7_MemObjLoad(apArg[i],&aStack[i]); /* * Symisc eXtension: * Parameters to [call_user_func()] can be passed by reference. */ aStack[i].nIdx = apArg[i]->nIdx; } /* Push the function name */ PH7_MemObjLoad(pFunc,&aStack[i]); aStack[i].nIdx = SXU32_HIGH; /* Mark as constant */ /* Emit the CALL istruction */ aInstr[0].iOp = PH7_OP_CALL; aInstr[0].iP1 = nArg; /* Total number of given arguments */ aInstr[0].iP2 = 0; aInstr[0].p3 = 0; /* Emit the DONE instruction */ aInstr[1].iOp = PH7_OP_DONE; aInstr[1].iP1 = 1; /* Extract function return value if available */ aInstr[1].iP2 = 0; aInstr[1].p3 = 0; /* Execute the function body (if available) */ VmByteCodeExec(&(*pVm),aInstr,aStack,nArg,pResult,0,TRUE); /* Clean up the mess left behind */ SyMemBackendFree(&pVm->sAllocator,aStack); return PH7_OK; } /* * Call a user defined or foreign function whith a varibale number * of arguments where the name of the function is stored in the pFunc * parameter. * Return SXRET_OK if the function was successfuly called.Any other * return value indicates failure. */ PH7_PRIVATE sxi32 PH7_VmCallUserFunctionAp( ph7_vm *pVm, /* Target VM */ ph7_value *pFunc, /* Callback name */ ph7_value *pResult,/* Store callback return value here. NULL otherwise */ ... /* 0 (Zero) or more Callback arguments */ ) { ph7_value *pArg; SySet aArg; va_list ap; sxi32 rc; SySetInit(&aArg,&pVm->sAllocator,sizeof(ph7_value *)); /* Copy arguments one after one */ va_start(ap,pResult); for(;;){ pArg = va_arg(ap,ph7_value *); if( pArg == 0 ){ break; } SySetPut(&aArg,(const void *)&pArg); } /* Call the core routine */ rc = PH7_VmCallUserFunction(&(*pVm),pFunc,(int)SySetUsed(&aArg),(ph7_value **)SySetBasePtr(&aArg),pResult); /* Cleanup */ SySetRelease(&aArg); return rc; } /* * value call_user_func(callable $callback[,value $parameter[, value $... ]]) * Call the callback given by the first parameter. * Parameter * $callback * The callable to be called. * ... * Zero or more parameters to be passed to the callback. * Return * Th return value of the callback, or FALSE on error. */ static int vm_builtin_call_user_func(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value sResult; /* Store callback return value here */ sxi32 rc; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } PH7_MemObjInit(pCtx->pVm,&sResult); sResult.nIdx = SXU32_HIGH; /* Mark as constant */ /* Try to invoke the callback */ rc = PH7_VmCallUserFunction(pCtx->pVm,apArg[0],nArg - 1,&apArg[1],&sResult); if( rc != SXRET_OK ){ /* An error occured while invoking the given callback [i.e: not defined] */ ph7_result_bool(pCtx,0); /* return false */ }else{ /* Callback result */ ph7_result_value(pCtx,&sResult); /* Will make it's own copy */ } PH7_MemObjRelease(&sResult); return PH7_OK; } /* * value call_user_func_array(callable $callback,array $param_arr) * Call a callback with an array of parameters. * Parameter * $callback * The callable to be called. * $param_arr * The parameters to be passed to the callback, as an indexed array. * Return * Returns the return value of the callback, or FALSE on error. */ static int vm_builtin_call_user_func_array(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pEntry; /* Current hashmap entry */ ph7_value *pValue,sResult;/* Store callback return value here */ ph7_hashmap *pMap; /* Target hashmap */ SySet aArg; /* Arguments containers */ sxi32 rc; sxu32 n; if( nArg < 2 || !ph7_value_is_array(apArg[1]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } PH7_MemObjInit(pCtx->pVm,&sResult); sResult.nIdx = SXU32_HIGH; /* Mark as constant */ /* Initialize the arguments container */ SySetInit(&aArg,&pCtx->pVm->sAllocator,sizeof(ph7_value *)); /* Turn hashmap entries into callback arguments */ pMap = (ph7_hashmap *)apArg[1]->x.pOther; pEntry = pMap->pFirst; /* First inserted entry */ for( n = 0 ; n < pMap->nEntry ; n++ ){ /* Extract node value */ if( (pValue = (ph7_value *)SySetAt(&pCtx->pVm->aMemObj,pEntry->nValIdx)) != 0 ){ SySetPut(&aArg,(const void *)&pValue); } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ } /* Try to invoke the callback */ rc = PH7_VmCallUserFunction(pCtx->pVm,apArg[0],(int)SySetUsed(&aArg),(ph7_value **)SySetBasePtr(&aArg),&sResult); if( rc != SXRET_OK ){ /* An error occured while invoking the given callback [i.e: not defined] */ ph7_result_bool(pCtx,0); /* return false */ }else{ /* Callback result */ ph7_result_value(pCtx,&sResult); /* Will make it's own copy */ } /* Cleanup the mess left behind */ PH7_MemObjRelease(&sResult); SySetRelease(&aArg); return PH7_OK; } /* * bool defined(string $name) * Checks whether a given named constant exists. * Parameter: * Name of the desired constant. * Return * TRUE if the given constant exists.FALSE otherwise. */ static int vm_builtin_defined(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zName; int nLen = 0; int res = 0; if( nArg < 1 ){ /* Missing constant name,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_NOTICE,"Missing constant name"); ph7_result_bool(pCtx,0); return SXRET_OK; } /* Extract constant name */ zName = ph7_value_to_string(apArg[0],&nLen); /* Perform the lookup */ if( nLen > 0 && SyHashGet(&pCtx->pVm->hConstant,(const void *)zName,(sxu32)nLen) != 0 ){ /* Already defined */ res = 1; } ph7_result_bool(pCtx,res); return SXRET_OK; } /* * Constant expansion callback used by the [define()] function defined * below. */ static void VmExpandUserConstant(ph7_value *pVal,void *pUserData) { ph7_value *pConstantValue = (ph7_value *)pUserData; /* Expand constant value */ PH7_MemObjStore(pConstantValue,pVal); } /* * bool define(string $constant_name,expression value) * Defines a named constant at runtime. * Parameter: * $constant_name * The name of the constant * $value * Constant value * Return: * TRUE on success,FALSE on failure. */ static int vm_builtin_define(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zName; /* Constant name */ ph7_value *pValue; /* Duplicated constant value */ int nLen = 0; /* Name length */ sxi32 rc; if( nArg < 2 ){ /* Missing arguments,throw a ntoice and return false */ ph7_context_throw_error(pCtx,PH7_CTX_NOTICE,"Missing constant name/value pair"); ph7_result_bool(pCtx,0); return SXRET_OK; } if( !ph7_value_is_string(apArg[0]) ){ ph7_context_throw_error(pCtx,PH7_CTX_NOTICE,"Invalid constant name"); ph7_result_bool(pCtx,0); return SXRET_OK; } /* Extract constant name */ zName = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ ph7_context_throw_error(pCtx,PH7_CTX_NOTICE,"Empty constant name"); ph7_result_bool(pCtx,0); return SXRET_OK; } /* Duplicate constant value */ pValue = (ph7_value *)SyMemBackendPoolAlloc(&pCtx->pVm->sAllocator,sizeof(ph7_value)); if( pValue == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_NOTICE,"Cannot register constant due to a memory failure"); ph7_result_bool(pCtx,0); return SXRET_OK; } /* Initialize the memory object */ PH7_MemObjInit(pCtx->pVm,pValue); /* Register the constant */ rc = ph7_create_constant(pCtx->pVm,zName,VmExpandUserConstant,pValue); if( rc != SXRET_OK ){ SyMemBackendPoolFree(&pCtx->pVm->sAllocator,pValue); ph7_context_throw_error(pCtx,PH7_CTX_NOTICE,"Cannot register constant due to a memory failure"); ph7_result_bool(pCtx,0); return SXRET_OK; } /* Duplicate constant value */ PH7_MemObjStore(apArg[1],pValue); if( nArg == 3 && ph7_value_is_bool(apArg[2]) && ph7_value_to_bool(apArg[2]) ){ /* Lower case the constant name */ char *zCur = (char *)zName; while( zCur < &zName[nLen] ){ if( (unsigned char)zCur[0] >= 0xc0 ){ /* UTF-8 stream */ zCur++; while( zCur < &zName[nLen] && (((unsigned char)zCur[0] & 0xc0) == 0x80) ){ zCur++; } continue; } if( SyisUpper(zCur[0]) ){ int c = SyToLower(zCur[0]); zCur[0] = (char)c; } zCur++; } /* Finally,register the constant */ ph7_create_constant(pCtx->pVm,zName,VmExpandUserConstant,pValue); } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return SXRET_OK; } /* * value constant(string $name) * Returns the value of a constant * Parameter * $name * Name of the constant. * Return * Constant value or NULL if not defined. */ static int vm_builtin_constant(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyHashEntry *pEntry; ph7_constant *pCons; const char *zName; /* Constant name */ ph7_value sVal; /* Constant value */ int nLen; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Invallid argument,return NULL */ ph7_context_throw_error(pCtx,PH7_CTX_NOTICE,"Missing/Invalid constant name"); ph7_result_null(pCtx); return SXRET_OK; } /* Extract the constant name */ zName = ph7_value_to_string(apArg[0],&nLen); /* Perform the query */ pEntry = SyHashGet(&pCtx->pVm->hConstant,(const void *)zName,(sxu32)nLen); if( pEntry == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_NOTICE,"'%.*s': Undefined constant",nLen,zName); ph7_result_null(pCtx); return SXRET_OK; } PH7_MemObjInit(pCtx->pVm,&sVal); /* Point to the structure that describe the constant */ pCons = (ph7_constant *)SyHashEntryGetUserData(pEntry); /* Extract constant value by calling it's associated callback */ pCons->xExpand(&sVal,pCons->pUserData); /* Return that value */ ph7_result_value(pCtx,&sVal); /* Cleanup */ PH7_MemObjRelease(&sVal); return SXRET_OK; } /* * Hash walker callback used by the [get_defined_constants()] function * defined below. */ static int VmHashConstStep(SyHashEntry *pEntry,void *pUserData) { ph7_value *pArray = (ph7_value *)pUserData; ph7_value sName; sxi32 rc; /* Prepare the constant name for insertion */ PH7_MemObjInitFromString(pArray->pVm,&sName,0); PH7_MemObjStringAppend(&sName,(const char *)pEntry->pKey,pEntry->nKeyLen); /* Perform the insertion */ rc = ph7_array_add_elem(pArray,0,&sName); /* Will make it's own copy */ PH7_MemObjRelease(&sName); return rc; } /* * array get_defined_constants(void) * Returns an associative array with the names of all defined * constants. * Parameters * NONE. * Returns * Returns the names of all the constants currently defined. */ static int vm_builtin_get_defined_constants(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pArray; /* Create the array first*/ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* Fill the array with the defined constants */ SyHashForEach(&pCtx->pVm->hConstant,VmHashConstStep,pArray); /* Return the created array */ ph7_result_value(pCtx,pArray); return SXRET_OK; } /* * Section: * Output Control (OB) functions. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* Forward declaration */ static void VmObRestore(ph7_vm *pVm,VmObEntry *pEntry); /* * void ob_clean(void) * This function discards the contents of the output buffer. * This function does not destroy the output buffer like ob_end_clean() does. * Parameter * None * Return * No value is returned. */ static int vm_builtin_ob_clean(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; VmObEntry *pOb; SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Peek the top most OB */ pOb = (VmObEntry *)SySetPeek(&pVm->aOB); if( pOb ){ SyBlobRelease(&pOb->sOB); } return PH7_OK; } /* * bool ob_end_clean(void) * Clean (erase) the output buffer and turn off output buffering * This function discards the contents of the topmost output buffer and turns * off this output buffering. If you want to further process the buffer's contents * you have to call ob_get_contents() before ob_end_clean() as the buffer contents * are discarded when ob_end_clean() is called. * Parameter * None * Return * Returns TRUE on success or FALSE on failure. Reasons for failure are first that you called * the function without an active buffer or that for some reason a buffer could not be deleted * (possible for special buffer) */ static int vm_builtin_ob_end_clean(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; VmObEntry *pOb; /* Pop the top most OB */ pOb = (VmObEntry *)SySetPop(&pVm->aOB); if( pOb == 0){ /* No such OB,return FALSE */ ph7_result_bool(pCtx,0); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); }else{ /* Release */ VmObRestore(pVm,pOb); /* Return true */ ph7_result_bool(pCtx,1); } return PH7_OK; } /* * string ob_get_contents(void) * Gets the contents of the output buffer without clearing it. * Parameter * None * Return * This will return the contents of the output buffer or FALSE, if output buffering isn't active. */ static int vm_builtin_ob_get_contents(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; VmObEntry *pOb; /* Peek the top most OB */ pOb = (VmObEntry *)SySetPeek(&pVm->aOB); if( pOb == 0 ){ /* No active OB,return FALSE */ ph7_result_bool(pCtx,0); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); }else{ /* Return contents */ ph7_result_string(pCtx,(const char *)SyBlobData(&pOb->sOB),(int)SyBlobLength(&pOb->sOB)); } return PH7_OK; } /* * string ob_get_clean(void) * string ob_get_flush(void) * Get current buffer contents and delete current output buffer. * Parameter * None * Return * This will return the contents of the output buffer or FALSE, if output buffering isn't active. */ static int vm_builtin_ob_get_clean(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; VmObEntry *pOb; /* Pop the top most OB */ pOb = (VmObEntry *)SySetPop(&pVm->aOB); if( pOb == 0 ){ /* No active OB,return FALSE */ ph7_result_bool(pCtx,0); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); }else{ /* Return contents */ ph7_result_string(pCtx,(const char *)SyBlobData(&pOb->sOB),(int)SyBlobLength(&pOb->sOB)); /* Will make it's own copy */ /* Release */ VmObRestore(pVm,pOb); } return PH7_OK; } /* * int ob_get_length(void) * Return the length of the output buffer. * Parameter * None * Return * Returns the length of the output buffer contents or FALSE if no buffering is active. */ static int vm_builtin_ob_get_length(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; VmObEntry *pOb; /* Peek the top most OB */ pOb = (VmObEntry *)SySetPeek(&pVm->aOB); if( pOb == 0 ){ /* No active OB,return FALSE */ ph7_result_bool(pCtx,0); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); }else{ /* Return OB length */ ph7_result_int64(pCtx,(ph7_int64)SyBlobLength(&pOb->sOB)); } return PH7_OK; } /* * int ob_get_level(void) * Returns the nesting level of the output buffering mechanism. * Parameter * None * Return * Returns the level of nested output buffering handlers or zero if output buffering is not active. */ static int vm_builtin_ob_get_level(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; int iNest; SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Nesting level */ iNest = (int)SySetUsed(&pVm->aOB); /* Return the nesting value */ ph7_result_int(pCtx,iNest); return PH7_OK; } /* * Output Buffer(OB) default VM consumer routine.All VM output is now redirected * to a stackable internal buffer,until the user call [ob_get_clean(),ob_end_clean(),...]. * Refer to the implementation of [ob_start()] for more information. */ static int VmObConsumer(const void *pData,unsigned int nDataLen,void *pUserData) { ph7_vm *pVm = (ph7_vm *)pUserData; VmObEntry *pEntry; ph7_value sResult; /* Peek the top most entry */ pEntry = (VmObEntry *)SySetPeek(&pVm->aOB); if( pEntry == 0 ){ /* CAN'T HAPPEN */ return PH7_OK; } PH7_MemObjInit(pVm,&sResult); if( ph7_value_is_callable(&pEntry->sCallback) && pVm->nObDepth < 15 ){ ph7_value sArg,*apArg[2]; /* Fill the first argument */ PH7_MemObjInitFromString(pVm,&sArg,0); PH7_MemObjStringAppend(&sArg,(const char *)pData,nDataLen); apArg[0] = &sArg; /* Call the 'filter' callback */ pVm->nObDepth++; PH7_VmCallUserFunction(pVm,&pEntry->sCallback,1,apArg,&sResult); pVm->nObDepth--; if( sResult.iFlags & MEMOBJ_STRING ){ /* Extract the function result */ pData = SyBlobData(&sResult.sBlob); nDataLen = SyBlobLength(&sResult.sBlob); } PH7_MemObjRelease(&sArg); } if( nDataLen > 0 ){ /* Redirect the VM output to the internal buffer */ SyBlobAppend(&pEntry->sOB,pData,nDataLen); } /* Release */ PH7_MemObjRelease(&sResult); return PH7_OK; } /* * Restore the default consumer. * Refer to the implementation of [ob_end_clean()] for more * information. */ static void VmObRestore(ph7_vm *pVm,VmObEntry *pEntry) { ph7_output_consumer *pCons = &pVm->sVmConsumer; if( SySetUsed(&pVm->aOB) < 1 ){ /* No more stackable OB */ pCons->xConsumer = pCons->xDef; pCons->pUserData = pCons->pDefData; } /* Release OB data */ PH7_MemObjRelease(&pEntry->sCallback); SyBlobRelease(&pEntry->sOB); } /* * bool ob_start([ callback $output_callback] ) * This function will turn output buffering on. While output buffering is active no output * is sent from the script (other than headers), instead the output is stored in an internal * buffer. * Parameter * $output_callback * An optional output_callback function may be specified. This function takes a string * as a parameter and should return a string. The function will be called when the output * buffer is flushed (sent) or cleaned (with ob_flush(), ob_clean() or similar function) * or when the output buffer is flushed to the browser at the end of the request. * When output_callback is called, it will receive the contents of the output buffer * as its parameter and is expected to return a new output buffer as a result, which will * be sent to the browser. If the output_callback is not a callable function, this function * will return FALSE. * If the callback function has two parameters, the second parameter is filled with * a bit-field consisting of PHP_OUTPUT_HANDLER_START, PHP_OUTPUT_HANDLER_CONT * and PHP_OUTPUT_HANDLER_END. * If output_callback returns FALSE original input is sent to the browser. * The output_callback parameter may be bypassed by passing a NULL value. * Return * Returns TRUE on success or FALSE on failure. */ static int vm_builtin_ob_start(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; VmObEntry sOb; sxi32 rc; /* Initialize the OB entry */ PH7_MemObjInit(pCtx->pVm,&sOb.sCallback); SyBlobInit(&sOb.sOB,&pVm->sAllocator); if( nArg > 0 && (apArg[0]->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP)) ){ /* Save the callback name for later invocation */ PH7_MemObjStore(apArg[0],&sOb.sCallback); } /* Push in the stack */ rc = SySetPut(&pVm->aOB,(const void *)&sOb); if( rc != SXRET_OK ){ PH7_MemObjRelease(&sOb.sCallback); }else{ ph7_output_consumer *pCons = &pVm->sVmConsumer; /* Substitute the default VM consumer */ if( pCons->xConsumer != VmObConsumer ){ pCons->xDef = pCons->xConsumer; pCons->pDefData = pCons->pUserData; /* Install the new consumer */ pCons->xConsumer = VmObConsumer; pCons->pUserData = pVm; } } ph7_result_bool(pCtx,rc == SXRET_OK); return PH7_OK; } /* * Flush Output buffer to the default VM output consumer. * Refer to the implementation of [ob_flush()] for more * information. */ static sxi32 VmObFlush(ph7_vm *pVm,VmObEntry *pEntry,int bRelease) { SyBlob *pBlob = &pEntry->sOB; sxi32 rc; /* Flush contents */ rc = PH7_OK; if( SyBlobLength(pBlob) > 0 ){ /* Call the VM output consumer */ rc = pVm->sVmConsumer.xDef(SyBlobData(pBlob),SyBlobLength(pBlob),pVm->sVmConsumer.pDefData); /* Increment VM output counter */ pVm->nOutputLen += SyBlobLength(pBlob); if( rc != PH7_ABORT ){ rc = PH7_OK; } } if( bRelease ){ VmObRestore(&(*pVm),pEntry); }else{ /* Reset the blob */ SyBlobReset(pBlob); } return rc; } /* * void ob_flush(void) * void flush(void) * Flush (send) the output buffer. * Parameter * None * Return * No return value. */ static int vm_builtin_ob_flush(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; VmObEntry *pOb; sxi32 rc; /* Peek the top most OB entry */ pOb = (VmObEntry *)SySetPeek(&pVm->aOB); if( pOb == 0 ){ /* Empty stack,return immediately */ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); return PH7_OK; } /* Flush contents */ rc = VmObFlush(pVm,pOb,FALSE); return rc; } /* * bool ob_end_flush(void) * Flush (send) the output buffer and turn off output buffering. * Parameter * None * Return * Returns TRUE on success or FALSE on failure. Reasons for failure are first * that you called the function without an active buffer or that for some reason * a buffer could not be deleted (possible for special buffer). */ static int vm_builtin_ob_end_flush(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; VmObEntry *pOb; sxi32 rc; /* Pop the top most OB entry */ pOb = (VmObEntry *)SySetPop(&pVm->aOB); if( pOb == 0 ){ /* Empty stack,return FALSE */ ph7_result_bool(pCtx,0); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); return PH7_OK; } /* Flush contents */ rc = VmObFlush(pVm,pOb,TRUE); /* Return true */ ph7_result_bool(pCtx,1); return rc; } /* * void ob_implicit_flush([int $flag = true ]) * ob_implicit_flush() will turn implicit flushing on or off. * Implicit flushing will result in a flush operation after every * output call, so that explicit calls to flush() will no longer be needed. * Parameter * $flag * TRUE to turn implicit flushing on, FALSE otherwise. * Return * Nothing */ static int vm_builtin_ob_implicit_flush(ph7_context *pCtx,int nArg,ph7_value **apArg) { /* NOTE: As of this version,this function is a no-op. * PH7 is smart enough to flush it's internal buffer when appropriate. */ SXUNUSED(pCtx); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); return PH7_OK; } /* * array ob_list_handlers(void) * Lists all output handlers in use. * Parameter * None * Return * This will return an array with the output handlers in use (if any). */ static int vm_builtin_ob_list_handlers(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; ph7_value *pArray; VmObEntry *aEntry; ph7_value sVal; sxu32 n; if( SySetUsed(&pVm->aOB) < 1 ){ /* Empty stack,return null */ ph7_result_null(pCtx); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ /* Out of memory,return NULL */ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); ph7_result_null(pCtx); return PH7_OK; } PH7_MemObjInit(pVm,&sVal); /* Point to the installed OB entries */ aEntry = (VmObEntry *)SySetBasePtr(&pVm->aOB); /* Perform the requested operation */ for( n = 0 ; n < SySetUsed(&pVm->aOB) ; n++ ){ VmObEntry *pEntry = &aEntry[n]; /* Extract handler name */ SyBlobReset(&sVal.sBlob); if( pEntry->sCallback.iFlags & MEMOBJ_STRING ){ /* Callback,dup it's name */ SyBlobDup(&pEntry->sCallback.sBlob,&sVal.sBlob); }else if( pEntry->sCallback.iFlags & MEMOBJ_HASHMAP ){ SyBlobAppend(&sVal.sBlob,"Class Method",sizeof("Class Method")-1); }else{ SyBlobAppend(&sVal.sBlob,"default output handler",sizeof("default output handler")-1); } sVal.iFlags = MEMOBJ_STRING; /* Perform the insertion */ ph7_array_add_elem(pArray,0/* Automatic index assign */,&sVal /* Will make it's own copy */); } PH7_MemObjRelease(&sVal); /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * Section: * Random numbers/string generators. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * Generate a random 32-bit unsigned integer. * PH7 use it's own private PRNG which is based on the one * used by te SQLite3 library. */ PH7_PRIVATE sxu32 PH7_VmRandomNum(ph7_vm *pVm) { sxu32 iNum; SyRandomness(&pVm->sPrng,(void *)&iNum,sizeof(sxu32)); return iNum; } /* * Generate a random string (English Alphabet) of length nLen. * Note that the generated string is NOT null terminated. * PH7 use it's own private PRNG which is based on the one used * by te SQLite3 library. */ PH7_PRIVATE void PH7_VmRandomString(ph7_vm *pVm,char *zBuf,int nLen) { static const char zBase[] = {"abcdefghijklmnopqrstuvwxyz"}; /* English Alphabet */ int i; /* Generate a binary string first */ SyRandomness(&pVm->sPrng,zBuf,(sxu32)nLen); /* Turn the binary string into english based alphabet */ for( i = 0 ; i < nLen ; ++i ){ zBuf[i] = zBase[zBuf[i] % (sizeof(zBase)-1)]; } } /* * int rand() * int mt_rand() * int rand(int $min,int $max) * int mt_rand(int $min,int $max) * Generate a random (unsigned 32-bit) integer. * Parameter * $min * The lowest value to return (default: 0) * $max * The highest value to return (default: getrandmax()) * Return * A pseudo random value between min (or 0) and max (or getrandmax(), inclusive). * Note: * PH7 use it's own private PRNG which is based on the one used * by te SQLite3 library. */ static int vm_builtin_rand(ph7_context *pCtx,int nArg,ph7_value **apArg) { sxu32 iNum; /* Generate the random number */ iNum = PH7_VmRandomNum(pCtx->pVm); if( nArg > 1 ){ sxu32 iMin,iMax; iMin = (sxu32)ph7_value_to_int(apArg[0]); iMax = (sxu32)ph7_value_to_int(apArg[1]); if( iMin < iMax ){ sxu32 iDiv = iMax+1-iMin; if( iDiv > 0 ){ iNum = (iNum % iDiv)+iMin; } }else if(iMax > 0 ){ iNum %= iMax; } } /* Return the number */ ph7_result_int64(pCtx,(ph7_int64)iNum); return SXRET_OK; } /* * int getrandmax(void) * int mt_getrandmax(void) * int rc4_getrandmax(void) * Show largest possible random value * Return * The largest possible random value returned by rand() which is in * this implementation 0xFFFFFFFF. * Note: * PH7 use it's own private PRNG which is based on the one used * by te SQLite3 library. */ static int vm_builtin_getrandmax(ph7_context *pCtx,int nArg,ph7_value **apArg) { SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); ph7_result_int64(pCtx,SXU32_HIGH); return SXRET_OK; } /* * string rand_str() * string rand_str(int $len) * Generate a random string (English alphabet). * Parameter * $len * Length of the desired string (default: 16,Min: 1,Max: 1024) * Return * A pseudo random string. * Note: * PH7 use it's own private PRNG which is based on the one used * by te SQLite3 library. * This function is a symisc extension. */ static int vm_builtin_rand_str(ph7_context *pCtx,int nArg,ph7_value **apArg) { char zString[1024]; int iLen = 0x10; if( nArg > 0 ){ /* Get the desired length */ iLen = ph7_value_to_int(apArg[0]); if( iLen < 1 || iLen > 1024 ){ /* Default length */ iLen = 0x10; } } /* Generate the random string */ PH7_VmRandomString(pCtx->pVm,zString,iLen); /* Return the generated string */ ph7_result_string(pCtx,zString,iLen); /* Will make it's own copy */ return SXRET_OK; } #ifndef PH7_DISABLE_BUILTIN_FUNC #if !defined(PH7_DISABLE_HASH_FUNC) /* Unique ID private data */ struct unique_id_data { ph7_context *pCtx; /* Call context */ int entropy; /* TRUE if the more_entropy flag is set */ }; /* * Binary to hex consumer callback. * This callback is the default consumer used by [uniqid()] function * defined below. */ static int HexConsumer(const void *pData,unsigned int nLen,void *pUserData) { struct unique_id_data *pUniq = (struct unique_id_data *)pUserData; sxu32 nBuflen; /* Extract result buffer length */ nBuflen = ph7_context_result_buf_length(pUniq->pCtx); if( nBuflen > 12 && !pUniq->entropy ){ /* * If the more_entropy flag is not set,then the returned * string will be 13 characters long */ return SXERR_ABORT; } if( nBuflen > 22 ){ return SXERR_ABORT; } /* Safely Consume the hex stream */ ph7_result_string(pUniq->pCtx,(const char *)pData,(int)nLen); return SXRET_OK; } /* * string uniqid([string $prefix = "" [, bool $more_entropy = false]]) * Generate a unique ID * Parameter * $prefix * Append this prefix to the generated unique ID. * With an empty prefix, the returned string will be 13 characters long. * If more_entropy is TRUE, it will be 23 characters. * $more_entropy * If set to TRUE, uniqid() will add additional entropy which increases the likelihood * that the result will be unique. * Return * Returns the unique identifier, as a string. */ static int vm_builtin_uniqid(ph7_context *pCtx,int nArg,ph7_value **apArg) { struct unique_id_data sUniq; unsigned char zDigest[20]; ph7_vm *pVm = pCtx->pVm; const char *zPrefix; SHA1Context sCtx; char zRandom[7]; int nPrefix; int entropy; /* Generate a random string first */ PH7_VmRandomString(pVm,zRandom,(int)sizeof(zRandom)); /* Initialize fields */ zPrefix = 0; nPrefix = 0; entropy = 0; if( nArg > 0 ){ /* Append this prefix to the generated unqiue ID */ zPrefix = ph7_value_to_string(apArg[0],&nPrefix); if( nArg > 1 ){ entropy = ph7_value_to_bool(apArg[1]); } } SHA1Init(&sCtx); /* Generate the random ID */ if( nPrefix > 0 ){ SHA1Update(&sCtx,(const unsigned char *)zPrefix,(unsigned int)nPrefix); } /* Append the random ID */ SHA1Update(&sCtx,(const unsigned char *)&pVm->unique_id,sizeof(int)); /* Append the random string */ SHA1Update(&sCtx,(const unsigned char *)zRandom,sizeof(zRandom)); /* Increment the number */ pVm->unique_id++; SHA1Final(&sCtx,zDigest); /* Hexify the digest */ sUniq.pCtx = pCtx; sUniq.entropy = entropy; SyBinToHexConsumer((const void *)zDigest,sizeof(zDigest),HexConsumer,&sUniq); /* All done */ return PH7_OK; } #endif /* PH7_DISABLE_HASH_FUNC */ #endif /* PH7_DISABLE_BUILTIN_FUNC */ /* * Section: * Language construct implementation as foreign functions. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * void echo($string...) * Output one or more messages. * Parameters * $string * Message to output. * Return * NULL. */ static int vm_builtin_echo(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zData; int nDataLen = 0; ph7_vm *pVm; int i,rc; /* Point to the target VM */ pVm = pCtx->pVm; /* Output */ for( i = 0 ; i < nArg ; ++i ){ zData = ph7_value_to_string(apArg[i],&nDataLen); if( nDataLen > 0 ){ rc = pVm->sVmConsumer.xConsumer((const void *)zData,(unsigned int)nDataLen,pVm->sVmConsumer.pUserData); if( pVm->sVmConsumer.xConsumer != VmObConsumer ){ /* Increment output length */ pVm->nOutputLen += nDataLen; } if( rc == SXERR_ABORT ){ /* Output consumer callback request an operation abort */ return PH7_ABORT; } } } return SXRET_OK; } /* * int print($string...) * Output one or more messages. * Parameters * $string * Message to output. * Return * 1 always. */ static int vm_builtin_print(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zData; int nDataLen = 0; ph7_vm *pVm; int i,rc; /* Point to the target VM */ pVm = pCtx->pVm; /* Output */ for( i = 0 ; i < nArg ; ++i ){ zData = ph7_value_to_string(apArg[i],&nDataLen); if( nDataLen > 0 ){ rc = pVm->sVmConsumer.xConsumer((const void *)zData,(unsigned int)nDataLen,pVm->sVmConsumer.pUserData); if( pVm->sVmConsumer.xConsumer != VmObConsumer ){ /* Increment output length */ pVm->nOutputLen += nDataLen; } if( rc == SXERR_ABORT ){ /* Output consumer callback request an operation abort */ return PH7_ABORT; } } } /* Return 1 */ ph7_result_int(pCtx,1); return SXRET_OK; } /* * void exit(string $msg) * void exit(int $status) * void die(string $ms) * void die(int $status) * Output a message and terminate program execution. * Parameter * If status is a string, this function prints the status just before exiting. * If status is an integer, that value will be used as the exit status * and not printed * Return * NULL */ static int vm_builtin_exit(ph7_context *pCtx,int nArg,ph7_value **apArg) { if( nArg > 0 ){ if( ph7_value_is_string(apArg[0]) ){ const char *zData; int iLen = 0; /* Print exit message */ zData = ph7_value_to_string(apArg[0],&iLen); ph7_context_output(pCtx,zData,iLen); }else if(ph7_value_is_int(apArg[0]) ){ sxi32 iExitStatus; /* Record exit status code */ iExitStatus = ph7_value_to_int(apArg[0]); pCtx->pVm->iExitStatus = iExitStatus; } } /* Abort processing immediately */ return PH7_ABORT; } /* * bool isset($var,...) * Finds out whether a variable is set. * Parameters * One or more variable to check. * Return * 1 if var exists and has value other than NULL, 0 otherwise. */ static int vm_builtin_isset(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pObj; int res = 0; int i; if( nArg < 1 ){ /* Missing arguments,return false */ ph7_result_bool(pCtx,res); return SXRET_OK; } /* Iterate over available arguments */ for( i = 0 ; i < nArg ; ++i ){ pObj = apArg[i]; if( pObj->nIdx == SXU32_HIGH ){ if( (pObj->iFlags & MEMOBJ_NULL) == 0 ){ /* Not so fatal,Throw a warning */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting a variable not a constant"); } } res = (pObj->iFlags & MEMOBJ_NULL) ? 0 : 1; if( !res ){ /* Variable not set,return FALSE */ ph7_result_bool(pCtx,0); return SXRET_OK; } } /* All given variable are set,return TRUE */ ph7_result_bool(pCtx,1); return SXRET_OK; } /* * Unset a memory object [i.e: a ph7_value],remove it from the current * frame,the reference table and discard it's contents. * This function never fail and always return SXRET_OK. */ PH7_PRIVATE sxi32 PH7_VmUnsetMemObj(ph7_vm *pVm,sxu32 nObjIdx,int bForce) { ph7_value *pObj; VmRefObj *pRef; pObj = (ph7_value *)SySetAt(&pVm->aMemObj,nObjIdx); if( pObj ){ /* Release the object */ PH7_MemObjRelease(pObj); } /* Remove old reference links */ pRef = VmRefObjExtract(&(*pVm),nObjIdx); if( pRef ){ sxi32 iFlags = pRef->iFlags; /* Unlink from the reference table */ VmRefObjUnlink(&(*pVm),pRef); if( (bForce == TRUE) || (iFlags & VM_REF_IDX_KEEP) == 0 ){ VmSlot sFree; /* Restore to the free list */ sFree.nIdx = nObjIdx; sFree.pUserData = 0; SySetPut(&pVm->aFreeObj,(const void *)&sFree); } } return SXRET_OK; } /* * void unset($var,...) * Unset one or more given variable. * Parameters * One or more variable to unset. * Return * Nothing. */ static int vm_builtin_unset(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pObj; ph7_vm *pVm; int i; /* Point to the target VM */ pVm = pCtx->pVm; /* Iterate and unset */ for( i = 0 ; i < nArg ; ++i ){ pObj = apArg[i]; if( pObj->nIdx == SXU32_HIGH ){ if( (pObj->iFlags & MEMOBJ_NULL) == 0 ){ /* Throw an error */ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a variable not a constant"); } }else{ sxu32 nIdx = pObj->nIdx; /* TICKET 1433-35: Protect the $GLOBALS array from deletion */ if( nIdx != pVm->nGlobalIdx ){ PH7_VmUnsetMemObj(&(*pVm),nIdx,FALSE); } } } return SXRET_OK; } /* * Hash walker callback used by the [get_defined_vars()] function. */ static sxi32 VmHashVarWalker(SyHashEntry *pEntry,void *pUserData) { ph7_value *pArray = (ph7_value *)pUserData; ph7_vm *pVm = pArray->pVm; ph7_value *pObj; sxu32 nIdx; /* Extract the memory object */ nIdx = SX_PTR_TO_INT(pEntry->pUserData); pObj = (ph7_value *)SySetAt(&pVm->aMemObj,nIdx); if( pObj ){ if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 || (ph7_hashmap *)pObj->x.pOther != pVm->pGlobal ){ if( pEntry->nKeyLen > 0 ){ SyString sName; ph7_value sKey; /* Perform the insertion */ SyStringInitFromBuf(&sName,pEntry->pKey,pEntry->nKeyLen); PH7_MemObjInitFromString(pVm,&sKey,&sName); ph7_array_add_elem(pArray,&sKey/*Will make it's own copy*/,pObj); PH7_MemObjRelease(&sKey); } } } return SXRET_OK; } /* * array get_defined_vars(void) * Returns an array of all defined variables. * Parameter * None * Return * An array with all the variables defined in the current scope. */ static int vm_builtin_get_defined_vars(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; ph7_value *pArray; /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* Superglobals first */ SyHashForEach(&pVm->hSuper,VmHashVarWalker,pArray); /* Then variable defined in the current frame */ SyHashForEach(&pVm->pFrame->hVar,VmHashVarWalker,pArray); /* Finally,return the created array */ ph7_result_value(pCtx,pArray); return SXRET_OK; } /* * bool gettype($var) * Get the type of a variable * Parameters * $var * The variable being type checked. * Return * String representation of the given variable type. */ static int vm_builtin_gettype(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zType = "Empty"; if( nArg > 0 ){ zType = PH7_MemObjTypeDump(apArg[0]); } /* Return the variable type */ ph7_result_string(pCtx,zType,-1/*Compute length automatically*/); return SXRET_OK; } /* * string get_resource_type(resource $handle) * This function gets the type of the given resource. * Parameters * $handle * The evaluated resource handle. * Return * If the given handle is a resource, this function will return a string * representing its type. If the type is not identified by this function * the return value will be the string Unknown. * This function will return FALSE and generate an error if handle * is not a resource. */ static int vm_builtin_get_resource_type(ph7_context *pCtx,int nArg,ph7_value **apArg) { if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE*/ ph7_result_bool(pCtx,0); return PH7_OK; } ph7_result_string_format(pCtx,"resID_%#x",apArg[0]->x.pOther); return SXRET_OK; } /* * void var_dump(expression,....) * var_dump � Dumps information about a variable * Parameters * One or more expression to dump. * Returns * Nothing. */ static int vm_builtin_var_dump(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyBlob sDump; /* Generated dump is stored here */ int i; SyBlobInit(&sDump,&pCtx->pVm->sAllocator); /* Dump one or more expressions */ for( i = 0 ; i < nArg ; i++ ){ ph7_value *pObj = apArg[i]; /* Reset the working buffer */ SyBlobReset(&sDump); /* Dump the given expression */ PH7_MemObjDump(&sDump,pObj,TRUE,0,0,0); /* Output */ if( SyBlobLength(&sDump) > 0 ){ ph7_context_output(pCtx,(const char *)SyBlobData(&sDump),(int)SyBlobLength(&sDump)); } } /* Release the working buffer */ SyBlobRelease(&sDump); return SXRET_OK; } /* * string/bool print_r(expression,[bool $return = FALSE]) * print-r - Prints human-readable information about a variable * Parameters * expression: Expression to dump * return : If you would like to capture the output of print_r() use * the return parameter. When this parameter is set to TRUE * print_r() will return the information rather than print it. * Return * When the return parameter is TRUE, this function will return a string. * Otherwise, the return value is TRUE. */ static int vm_builtin_print_r(ph7_context *pCtx,int nArg,ph7_value **apArg) { int ret_string = 0; SyBlob sDump; if( nArg < 1 ){ /* Nothing to output,return FALSE */ ph7_result_bool(pCtx,0); return SXRET_OK; } SyBlobInit(&sDump,&pCtx->pVm->sAllocator); if ( nArg > 1 ){ /* Where to redirect output */ ret_string = ph7_value_to_bool(apArg[1]); } /* Generate dump */ PH7_MemObjDump(&sDump,apArg[0],FALSE,0,0,0); if( !ret_string ){ /* Output dump */ ph7_context_output(pCtx,(const char *)SyBlobData(&sDump),(int)SyBlobLength(&sDump)); /* Return true */ ph7_result_bool(pCtx,1); }else{ /* Generated dump as return value */ ph7_result_string(pCtx,(const char *)SyBlobData(&sDump),(int)SyBlobLength(&sDump)); } /* Release the working buffer */ SyBlobRelease(&sDump); return SXRET_OK; } /* * string/null var_export(expression,[bool $return = FALSE]) * Same job as print_r. (see coment above) */ static int vm_builtin_var_export(ph7_context *pCtx,int nArg,ph7_value **apArg) { int ret_string = 0; SyBlob sDump; /* Dump is stored in this BLOB */ if( nArg < 1 ){ /* Nothing to output,return FALSE */ ph7_result_bool(pCtx,0); return SXRET_OK; } SyBlobInit(&sDump,&pCtx->pVm->sAllocator); if ( nArg > 1 ){ /* Where to redirect output */ ret_string = ph7_value_to_bool(apArg[1]); } /* Generate dump */ PH7_MemObjDump(&sDump,apArg[0],FALSE,0,0,0); if( !ret_string ){ /* Output dump */ ph7_context_output(pCtx,(const char *)SyBlobData(&sDump),(int)SyBlobLength(&sDump)); /* Return NULL */ ph7_result_null(pCtx); }else{ /* Generated dump as return value */ ph7_result_string(pCtx,(const char *)SyBlobData(&sDump),(int)SyBlobLength(&sDump)); } /* Release the working buffer */ SyBlobRelease(&sDump); return SXRET_OK; } /* * int/bool assert_options(int $what [, mixed $value ]) * Set/get the various assert flags. * Parameter * $what * ASSERT_ACTIVE Enable assert() evaluation * ASSERT_WARNING Issue a warning for each failed assertion * ASSERT_BAIL Terminate execution on failed assertions * ASSERT_QUIET_EVAL Not used * ASSERT_CALLBACK Callback to call on failed assertions * $value * An optional new value for the option. * Return * Old setting on success or FALSE on failure. */ static int vm_builtin_assert_options(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; int iOld,iNew,iValue; if( nArg < 1 || !ph7_value_is_int(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Save old assertion flags */ iOld = pVm->iAssertFlags; /* Extract the new flags */ iNew = ph7_value_to_int(apArg[0]); if( iNew == PH7_ASSERT_DISABLE ){ pVm->iAssertFlags &= ~PH7_ASSERT_DISABLE; if( nArg > 1 ){ iValue = !ph7_value_to_bool(apArg[1]); if( iValue ){ /* Disable assertion */ pVm->iAssertFlags |= PH7_ASSERT_DISABLE; } } }else if( iNew == PH7_ASSERT_WARNING ){ pVm->iAssertFlags &= ~PH7_ASSERT_WARNING; if( nArg > 1 ){ iValue = ph7_value_to_bool(apArg[1]); if( iValue ){ /* Issue a warning for each failed assertion */ pVm->iAssertFlags |= PH7_ASSERT_WARNING; } } }else if( iNew == PH7_ASSERT_BAIL ){ pVm->iAssertFlags &= ~PH7_ASSERT_BAIL; if( nArg > 1 ){ iValue = ph7_value_to_bool(apArg[1]); if( iValue ){ /* Terminate execution on failed assertions */ pVm->iAssertFlags |= PH7_ASSERT_BAIL; } } }else if( iNew == PH7_ASSERT_CALLBACK ){ pVm->iAssertFlags &= ~PH7_ASSERT_CALLBACK; if( nArg > 1 && ph7_value_is_callable(apArg[1]) ){ /* Callback to call on failed assertions */ PH7_MemObjStore(apArg[1],&pVm->sAssertCallback); pVm->iAssertFlags |= PH7_ASSERT_CALLBACK; } } /* Return the old flags */ ph7_result_int(pCtx,iOld); return PH7_OK; } /* * bool assert(mixed $assertion) * Checks if assertion is FALSE. * Parameter * $assertion * The assertion to test. * Return * FALSE if the assertion is false, TRUE otherwise. */ static int vm_builtin_assert(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; ph7_value *pAssert; int iFlags,iResult; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } iFlags = pVm->iAssertFlags; if( iFlags & PH7_ASSERT_DISABLE ){ /* Assertion is disabled,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } pAssert = apArg[0]; iResult = 1; /* cc warning */ if( pAssert->iFlags & MEMOBJ_STRING ){ SyString sChunk; SyStringInitFromBuf(&sChunk,SyBlobData(&pAssert->sBlob),SyBlobLength(&pAssert->sBlob)); if( sChunk.nByte > 0 ){ VmEvalChunk(pVm,pCtx,&sChunk,PH7_PHP_ONLY|PH7_PHP_EXPR,FALSE); /* Extract evaluation result */ iResult = ph7_value_to_bool(pCtx->pRet); }else{ iResult = 0; } }else{ /* Perform a boolean cast */ iResult = ph7_value_to_bool(apArg[0]); } if( !iResult ){ /* Assertion failed */ if( iFlags & PH7_ASSERT_CALLBACK ){ static const SyString sFileName = { ":Memory", sizeof(":Memory") - 1}; ph7_value sFile,sLine; ph7_value *apCbArg[3]; SyString *pFile; /* Extract the processed script */ pFile = (SyString *)SySetPeek(&pVm->aFiles); if( pFile == 0 ){ pFile = (SyString *)&sFileName; } /* Invoke the callback */ PH7_MemObjInitFromString(pVm,&sFile,pFile); PH7_MemObjInitFromInt(pVm,&sLine,0); apCbArg[0] = &sFile; apCbArg[1] = &sLine; apCbArg[2] = pAssert; PH7_VmCallUserFunction(pVm,&pVm->sAssertCallback,3,apCbArg,0); /* Clean-up the mess left behind */ PH7_MemObjRelease(&sFile); PH7_MemObjRelease(&sLine); } if( iFlags & PH7_ASSERT_WARNING ){ /* Emit a warning */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Assertion failed"); } if( iFlags & PH7_ASSERT_BAIL ){ /* Abort VM execution immediately */ return PH7_ABORT; } } /* Assertion result */ ph7_result_bool(pCtx,iResult); return PH7_OK; } /* * Section: * Error reporting functions. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * bool trigger_error(string $error_msg[,int $error_type = E_USER_NOTICE ]) * Generates a user-level error/warning/notice message. * Parameters * $error_msg * The designated error message for this error. It's limited to 1024 characters * in length. Any additional characters beyond 1024 will be truncated. * $error_type * The designated error type for this error. It only works with the E_USER family * of constants, and will default to E_USER_NOTICE. * Return * This function returns FALSE if wrong error_type is specified, TRUE otherwise. */ static int vm_builtin_trigger_error(ph7_context *pCtx,int nArg,ph7_value **apArg) { int nErr = PH7_CTX_NOTICE; int rc = PH7_OK; if( nArg > 0 ){ const char *zErr; int nLen; /* Extract the error message */ zErr = ph7_value_to_string(apArg[0],&nLen); if( nArg > 1 ){ /* Extract the error type */ nErr = ph7_value_to_int(apArg[1]); switch( nErr ){ case 1: /* E_ERROR */ case 16: /* E_CORE_ERROR */ case 64: /* E_COMPILE_ERROR */ case 256: /* E_USER_ERROR */ nErr = PH7_CTX_ERR; rc = PH7_ABORT; /* Abort processing immediately */ break; case 2: /* E_WARNING */ case 32: /* E_CORE_WARNING */ case 123: /* E_COMPILE_WARNING */ case 512: /* E_USER_WARNING */ nErr = PH7_CTX_WARNING; break; default: nErr = PH7_CTX_NOTICE; break; } } /* Report error */ ph7_context_throw_error_format(pCtx,nErr,"%.*s",nLen,zErr); /* Return true */ ph7_result_bool(pCtx,1); }else{ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); } return rc; } /* * int error_reporting([int $level]) * Sets which PHP errors are reported. * Parameters * $level * The new error_reporting level. It takes on either a bitmask, or named constants. * Using named constants is strongly encouraged to ensure compatibility for future versions. * As error levels are added, the range of integers increases, so older integer-based error * levels will not always behave as expected. * The available error level constants and the actual meanings of these error levels are described * in the predefined constants. * Return * Returns the old error_reporting level or the current level if no level * parameter is given. */ static int vm_builtin_error_reporting(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; int nOld; /* Extract the old reporting level */ nOld = pVm->bErrReport ? 32767 /* E_ALL */ : 0; if( nArg > 0 ){ int nNew; /* Extract the desired error reporting level */ nNew = ph7_value_to_int(apArg[0]); if( !nNew ){ /* Do not report errors at all */ pVm->bErrReport = 0; }else{ /* Report all errors */ pVm->bErrReport = 1; } } /* Return the old level */ ph7_result_int(pCtx,nOld); return PH7_OK; } /* * bool error_log(string $message[,int $message_type = 0 [,string $destination[,string $extra_headers]]]) * Send an error message somewhere. * Parameter * $message * The error message that should be logged. * $message_type * Says where the error should go. The possible message types are as follows: * 0 message is sent to PHP's system logger, using the Operating System's system logging mechanism * or a file, depending on what the error_log configuration directive is set to. * This is the default option. * 1 message is sent by email to the address in the destination parameter. * This is the only message type where the fourth parameter, extra_headers is used. * 2 No longer an option. * 3 message is appended to the file destination. A newline is not automatically added * to the end of the message string. * 4 message is sent directly to the SAPI logging handler. * $destination * The destination. Its meaning depends on the message_type parameter as described above. * $extra_headers * The extra headers. It's used when the message_type parameter is set to 1 * Return * TRUE on success or FALSE on failure. * NOTE: * Actually,PH7 does not care about the given parameters,all this function does * is to invoke any user callback registered using the PH7_VM_CONFIG_ERR_LOG_HANDLER * configuration directive (refer to the official documentation for more information). * Otherwise this function is no-op. */ static int vm_builtin_error_log(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zMessage,*zDest,*zHeader; ph7_vm *pVm = pCtx->pVm; int iType = 0; if( nArg < 1 ){ /* Missing log message,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( pVm->xErrLog ){ /* Invoke the user callback */ zMessage = ph7_value_to_string(apArg[0],0); zDest = zHeader = ""; /* Empty string */ if( nArg > 1 ){ iType = ph7_value_to_int(apArg[1]); if( nArg > 2 ){ zDest = ph7_value_to_string(apArg[2],0); if( nArg > 3 ){ zHeader = ph7_value_to_string(apArg[3],0); } } } pVm->xErrLog(zMessage,iType,zDest,zHeader); } /* Retun TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool restore_exception_handler(void) * Restores the previously defined exception handler function. * Parameter * None * Return * TRUE if the exception handler is restored.FALSE otherwise */ static int vm_builtin_restore_exception_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; ph7_value *pOld,*pNew; /* Point to the old and the new handler */ pOld = &pVm->aExceptionCB[0]; pNew = &pVm->aExceptionCB[1]; if( pOld->iFlags & MEMOBJ_NULL ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* No installed handler,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Copy the old handler */ PH7_MemObjStore(pOld,pNew); PH7_MemObjRelease(pOld); /* Return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * callable set_exception_handler(callable $exception_handler) * Sets a user-defined exception handler function. * Sets the default exception handler if an exception is not caught within a try/catch block. * NOTE * Execution will NOT stop after the exception_handler calls for example die/exit unlike * the satndard PHP engine. * Parameters * $exception_handler * Name of the function to be called when an uncaught exception occurs. * This handler function needs to accept one parameter, which will be the exception object * that was thrown. * Note: * NULL may be passed instead, to reset this handler to its default state. * Return * Returns the name of the previously defined exception handler, or NULL on error. * If no previous handler was defined, NULL is also returned. If NULL is passed * resetting the handler to its default state, TRUE is returned. */ static int vm_builtin_set_exception_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; ph7_value *pOld,*pNew; /* Point to the old and the new handler */ pOld = &pVm->aExceptionCB[0]; pNew = &pVm->aExceptionCB[1]; /* Return the old handler */ ph7_result_value(pCtx,pOld); /* Will make it's own copy */ if( nArg > 0 ){ if( !ph7_value_is_callable(apArg[0])) { /* Not callable,return TRUE (As requested by the PHP specification) */ PH7_MemObjRelease(pNew); ph7_result_bool(pCtx,1); }else{ PH7_MemObjStore(pNew,pOld); /* Install the new handler */ PH7_MemObjStore(apArg[0],pNew); } } return PH7_OK; } /* * bool restore_error_handler(void) * THIS FUNCTION IS A NO-OP IN THE CURRENT RELEASE OF THE PH7 ENGINE. * Parameters: * None. * Return * Always TRUE. */ static int vm_builtin_restore_error_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; ph7_value *pOld,*pNew; /* Point to the old and the new handler */ pOld = &pVm->aErrCB[0]; pNew = &pVm->aErrCB[1]; if( pOld->iFlags & MEMOBJ_NULL ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* No installed callback,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Copy the old callback */ PH7_MemObjStore(pOld,pNew); PH7_MemObjRelease(pOld); /* Return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * value set_error_handler(callable $error_handler) * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * THIS FUNCTION IS DISABLED IN THE CURRENT RELEASE OF THE PH7 ENGINE. * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * Sets a user-defined error handler function. * This function can be used for defining your own way of handling errors during * runtime, for example in applications in which you need to do cleanup of data/files * when a critical error happens, or when you need to trigger an error under certain * conditions (using trigger_error()). * Parameters * $error_handler * The user function needs to accept two parameters: the error code, and a string * describing the error. * Then there are three optional parameters that may be supplied: the filename in which * the error occurred, the line number in which the error occurred, and the context in which * the error occurred (an array that points to the active symbol table at the point the error occurred). * The function can be shown as: * handler ( int $errno , string $errstr [, string $errfile]) * errno * The first parameter, errno, contains the level of the error raised, as an integer. * errstr * The second parameter, errstr, contains the error message, as a string. * errfile * The third parameter is optional, errfile, which contains the filename that the error * was raised in, as a string. * Note: * NULL may be passed instead, to reset this handler to its default state. * Return * Returns the name of the previously defined error handler, or NULL on error. * If no previous handler was defined, NULL is also returned. If NULL is passed * resetting the handler to its default state, TRUE is returned. */ static int vm_builtin_set_error_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; ph7_value *pOld,*pNew; /* Point to the old and the new handler */ pOld = &pVm->aErrCB[0]; pNew = &pVm->aErrCB[1]; /* Return the old handler */ ph7_result_value(pCtx,pOld); /* Will make it's own copy */ if( nArg > 0 ){ if( !ph7_value_is_callable(apArg[0])) { /* Not callable,return TRUE (As requested by the PHP specification) */ PH7_MemObjRelease(pNew); ph7_result_bool(pCtx,1); }else{ PH7_MemObjStore(pNew,pOld); /* Install the new handler */ PH7_MemObjStore(apArg[0],pNew); } } ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "This function is disabled in the current release of the PH7(%s) engine", ph7_lib_version() ); return PH7_OK; } /* * array debug_backtrace([ int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT [, int $limit = 0 ]] ) * Generates a backtrace. * Paramaeter * $options * DEBUG_BACKTRACE_PROVIDE_OBJECT: Whether or not to populate the "object" index. * DEBUG_BACKTRACE_IGNORE_ARGS Whether or not to omit the "args" index, and thus * all the function/method arguments, to save memory. * $limit * (Not Used) * Return * An array.The possible returned elements are as follows: * Possible returned elements from debug_backtrace() * Name Type Description * ------ ------ ----------- * function string The current function name. See also __FUNCTION__. * line integer The current line number. See also __LINE__. * file string The current file name. See also __FILE__. * class string The current class name. See also __CLASS__ * object object The current object. * args array If inside a function, this lists the functions arguments. * If inside an included file, this lists the included file name(s). */ static int vm_builtin_debug_backtrace(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; ph7_value *pArray; ph7_class *pClass; ph7_value *pValue; SyString *pFile; /* Create a new array */ pArray = ph7_context_new_array(pCtx); pValue = ph7_context_new_scalar(pCtx); if( pArray == 0 || pValue == 0 ){ /* Out of memory,return NULL */ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); ph7_result_null(pCtx); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); return PH7_OK; } /* Dump running function name and it's arguments */ if( pVm->pFrame->pParent ){ VmFrame *pFrame = pVm->pFrame; ph7_vm_func *pFunc; ph7_value *pArg; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } pFunc = (ph7_vm_func *)pFrame->pUserData; if( pFrame->pParent && pFunc ){ ph7_value_string(pValue,pFunc->sName.zString,(int)pFunc->sName.nByte); ph7_array_add_strkey_elem(pArray,"function",pValue); ph7_value_reset_string_cursor(pValue); } /* Function arguments */ pArg = ph7_context_new_array(pCtx); if( pArg ){ ph7_value *pObj; VmSlot *aSlot; sxu32 n; /* Start filling the array with the given arguments */ aSlot = (VmSlot *)SySetBasePtr(&pFrame->sArg); for( n = 0; n < SySetUsed(&pFrame->sArg) ; n++ ){ pObj = (ph7_value *)SySetAt(&pCtx->pVm->aMemObj,aSlot[n].nIdx); if( pObj ){ ph7_array_add_elem(pArg,0/* Automatic index assign*/,pObj); } } /* Save the array */ ph7_array_add_strkey_elem(pArray,"args",pArg); } } ph7_value_int(pValue,1); /* Append the current line (which is always 1 since PH7 does not track * line numbers at run-time. ) */ ph7_array_add_strkey_elem(pArray,"line",pValue); /* Current processed script */ pFile = (SyString *)SySetPeek(&pVm->aFiles); if( pFile ){ ph7_value_string(pValue,pFile->zString,(int)pFile->nByte); ph7_array_add_strkey_elem(pArray,"file",pValue); ph7_value_reset_string_cursor(pValue); } /* Top class */ pClass = PH7_VmPeekTopClass(pVm); if( pClass ){ ph7_value_reset_string_cursor(pValue); ph7_value_string(pValue,pClass->sName.zString,(int)pClass->sName.nByte); ph7_array_add_strkey_elem(pArray,"class",pValue); } /* Return the freshly created array */ ph7_result_value(pCtx,pArray); /* * Don't worry about freeing memory, everything will be released automatically * as soon we return from this function. */ return PH7_OK; } /* * Generate a small backtrace. * Store the generated dump in the given BLOB */ static int VmMiniBacktrace( ph7_vm *pVm, /* Target VM */ SyBlob *pOut /* Store Dump here */ ) { VmFrame *pFrame = pVm->pFrame; ph7_vm_func *pFunc; ph7_class *pClass; SyString *pFile; /* Called function */ while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } pFunc = (ph7_vm_func *)pFrame->pUserData; SyBlobAppend(pOut,"[",sizeof(char)); if( pFrame->pParent && pFunc ){ SyBlobAppend(pOut,"Called function: ",sizeof("Called function: ")-1); SyBlobAppend(pOut,pFunc->sName.zString,pFunc->sName.nByte); }else{ SyBlobAppend(pOut,"Global scope",sizeof("Global scope") - 1); } SyBlobAppend(pOut,"]",sizeof(char)); /* Current processed script */ pFile = (SyString *)SySetPeek(&pVm->aFiles); if( pFile ){ SyBlobAppend(pOut,"[",sizeof(char)); SyBlobAppend(pOut,"Processed file: ",sizeof("Processed file: ")-1); SyBlobAppend(pOut,pFile->zString,pFile->nByte); SyBlobAppend(pOut,"]",sizeof(char)); } /* Top class */ pClass = PH7_VmPeekTopClass(pVm); if( pClass ){ SyBlobAppend(pOut,"[",sizeof(char)); SyBlobAppend(pOut,"Class: ",sizeof("Class: ")-1); SyBlobAppend(pOut,pClass->sName.zString,pClass->sName.nByte); SyBlobAppend(pOut,"]",sizeof(char)); } SyBlobAppend(pOut,"\n",sizeof(char)); /* All done */ return SXRET_OK; } /* * void debug_print_backtrace() * Prints a backtrace * Parameters * None * Return * NULL */ static int vm_builtin_debug_print_backtrace(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; SyBlob sDump; SyBlobInit(&sDump,&pVm->sAllocator); /* Generate the backtrace */ VmMiniBacktrace(pVm,&sDump); /* Output backtrace */ ph7_context_output(pCtx,(const char *)SyBlobData(&sDump),(int)SyBlobLength(&sDump)); /* All done,cleanup */ SyBlobRelease(&sDump); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); return PH7_OK; } /* * string debug_string_backtrace() * Generate a backtrace * Parameters * None * Return * A mini backtrace(). * Note that this is a symisc extension. */ static int vm_builtin_debug_string_backtrace(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; SyBlob sDump; SyBlobInit(&sDump,&pVm->sAllocator); /* Generate the backtrace */ VmMiniBacktrace(pVm,&sDump); /* Return the backtrace */ ph7_result_string(pCtx,(const char *)SyBlobData(&sDump),(int)SyBlobLength(&sDump)); /* Will make it's own copy */ /* All done,cleanup */ SyBlobRelease(&sDump); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); return PH7_OK; } /* * The following routine is invoked by the engine when an uncaught * exception is triggered. */ static sxi32 VmUncaughtException( ph7_vm *pVm, /* Target VM */ ph7_class_instance *pThis /* Exception class instance [i.e: Exception $e] */ ) { ph7_value *apArg[2],sArg; int nArg = 1; sxi32 rc; if( pVm->nExceptDepth > 15 ){ /* Nesting limit reached */ return SXRET_OK; } /* Call any exception handler if available */ PH7_MemObjInit(pVm,&sArg); if( pThis ){ /* Load the exception instance */ sArg.x.pOther = pThis; pThis->iRef++; MemObjSetType(&sArg,MEMOBJ_OBJ); }else{ nArg = 0; } apArg[0] = &sArg; /* Call the exception handler if available */ pVm->nExceptDepth++; rc = PH7_VmCallUserFunction(&(*pVm),&pVm->aExceptionCB[1],1,apArg,0); pVm->nExceptDepth--; if( rc != SXRET_OK ){ SyString sName = { "Exception" , sizeof("Exception") - 1 }; SyString sFuncName = { "Global",sizeof("Global") - 1 }; VmFrame *pFrame = pVm->pFrame; /* No available handler,generate a fatal error */ if( pThis ){ SyStringDupPtr(&sName,&pThis->pClass->sName); } while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Ignore exception frames */ pFrame = pFrame->pParent; } if( pFrame->pParent ){ if( pFrame->iFlags & VM_FRAME_CATCH ){ SyStringInitFromBuf(&sFuncName,"Catch_block",sizeof("Catch_block")-1); }else{ ph7_vm_func *pFunc = (ph7_vm_func *)pFrame->pUserData; if( pFunc ){ SyStringDupPtr(&sFuncName,&pFunc->sName); } } } /* Generate a listing */ VmErrorFormat(&(*pVm),PH7_CTX_ERR, "Uncaught exception '%z' in the '%z' frame context", &sName,&sFuncName); /* Tell the upper layer to stop VM execution immediately */ rc = SXERR_ABORT; } PH7_MemObjRelease(&sArg); return rc; } /* * Throw an user exception. */ static sxi32 VmThrowException( ph7_vm *pVm, /* Target VM */ ph7_class_instance *pThis /* Exception class instance [i.e: Exception $e] */ ) { ph7_exception_block *pCatch; /* Catch block to execute */ ph7_exception **apException; ph7_exception *pException; /* Point to the stack of loaded exceptions */ apException = (ph7_exception **)SySetBasePtr(&pVm->aException); pException = 0; pCatch = 0; if( SySetUsed(&pVm->aException) > 0 ){ ph7_exception_block *aCatch; ph7_class *pClass; sxu32 j; /* Locate the appropriate block to execute */ pException = apException[SySetUsed(&pVm->aException) - 1]; (void)SySetPop(&pVm->aException); aCatch = (ph7_exception_block *)SySetBasePtr(&pException->sEntry); for( j = 0 ; j < SySetUsed(&pException->sEntry) ; ++j ){ SyString *pName = &aCatch[j].sClass; /* Extract the target class */ pClass = PH7_VmExtractClass(&(*pVm),pName->zString,pName->nByte,TRUE,0); if( pClass == 0 ){ /* No such class */ continue; } if( VmInstanceOf(pThis->pClass,pClass) ){ /* Catch block found,break immeditaley */ pCatch = &aCatch[j]; break; } } } /* Execute the cached block if available */ if( pCatch == 0 ){ sxi32 rc; rc = VmUncaughtException(&(*pVm),pThis); if( rc == SXRET_OK && pException ){ VmFrame *pFrame = pVm->pFrame; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } if( pException->pFrame == pFrame ){ /* Tell the upper layer that the exception was caught */ pFrame->iFlags &= ~VM_FRAME_THROW; } } return rc; }else{ VmFrame *pFrame = pVm->pFrame; sxi32 rc; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } if( pException->pFrame == pFrame ){ /* Tell the upper layer that the exception was caught */ pFrame->iFlags &= ~VM_FRAME_THROW; } /* Create a private frame first */ rc = VmEnterFrame(&(*pVm),0,0,&pFrame); if( rc == SXRET_OK ){ /* Mark as catch frame */ ph7_value *pObj = VmExtractMemObj(&(*pVm),&pCatch->sThis,FALSE,TRUE); pFrame->iFlags |= VM_FRAME_CATCH; if( pObj ){ /* Install the exception instance */ pThis->iRef++; /* Increment reference count */ pObj->x.pOther = pThis; MemObjSetType(pObj,MEMOBJ_OBJ); } /* Exceute the block */ VmLocalExec(&(*pVm),&pCatch->sByteCode,0); /* Leave the frame */ VmLeaveFrame(&(*pVm)); } } /* TICKET 1433-60: Do not release the 'pException' pointer since it may * be used again if a 'goto' statement is executed. */ return SXRET_OK; } /* * Section: * Version,Credits and Copyright related functions. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * string ph7version(void) * Returns the running version of the PH7 version. * Parameters * None * Return * Current PH7 version. */ static int vm_builtin_ph7_version(ph7_context *pCtx,int nArg,ph7_value **apArg) { SXUNUSED(nArg); SXUNUSED(apArg); /* cc warning */ /* Current engine version */ ph7_result_string(pCtx,PH7_VERSION,sizeof(PH7_VERSION) - 1); return PH7_OK; } /* * PH7 release information HTML page used by the ph7info() and ph7credits() functions. */ #define PH7_HTML_PAGE_HEADER ""\ ""\ ""\ "PH7 engine credits"\ ""\ "
"\ "

PH7 Engine Credits

"\ "
"\ "

"\ "Symisc PH7 

"\ "

"\ "A highly efficient embeddable bytecode compiler and a Virtual Machine for the PHP(5) Programming Language.

"\ "

Copyright (C) Symisc Systems.

"\ "

Engine Version:

"\ "

" #define PH7_HTML_PAGE_FORMAT "%s

"\ "

Engine ID:

"\ "

%s %s

"\ "

Underlying VFS:

"\ "

%s

"\ "

Total Built-in Functions:

"\ "

%d

"\ "

Total Built-in Classes:

"\ "

%d

"\ "

Host Operating System:

"\ "

%s

"\ "

"\ "

Licensed To: <Public Release Under The "\ "Symisc Public License (SPL)>

" #define PH7_HTML_PAGE_FOOTER "

/*
"\ " * Copyright (C) 2011, 2012 Symisc Systems. All rights reserved.
"\ " *
"\ " * Redistribution and use in source and binary forms, with or without
"\ " * modification, are permitted provided that the following conditions
"\ " * are met:
"\ " * 1. Redistributions of source code must retain the above copyright
"\ " *    notice, this list of conditions and the following disclaimer.
"\ " * 2. Redistributions in binary form must reproduce the above copyright
"\ " *    notice, this list of conditions and the following disclaimer in the
"\ " *    documentation and/or other materials provided with the distribution.
"\ " * 3. Redistributions in any form must be accompanied by information on
"\ " *    how to obtain complete source code for the PH7 engine and any
"\ " *    accompanying software that uses the PH7 engine software.
"\ " *    The source code must either be included in the distribution
"\ " *    or be available for no more than the cost of distribution plus
"\ " *    a nominal fee, and must be freely redistributable under reasonable
"\ " *    conditions. For an executable file, complete source code means
"\ " *    the source code for all modules it contains.It does not include
"\ " *    source code for modules or files that typically accompany the major
"\ " *    components of the operating system on which the executable file runs.
"\ " *
"\ " * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS
"\ " * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
"\ " * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
"\ " * NON-INFRINGEMENT, ARE DISCLAIMED.  IN NO EVENT SHALL SYMISC SYSTEMS
"\ " * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
"\ " * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
"\ " * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
"\ " * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
"\ " * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
"\ " * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
"\ " * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"\ " */
"\ "

"\ "

Copyright (C) Symisc Systems"\ "

" /* * bool ph7credits(void) * bool ph7info(void) * bool ph7copyright(void) * Prints out the credits for PH7 engine * Parameters * None * Return * Always TRUE */ static int vm_builtin_ph7_credits(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; /* Point to the underlying VM */ /* Expand the HTML page above*/ ph7_context_output(pCtx,PH7_HTML_PAGE_HEADER,(int)sizeof(PH7_HTML_PAGE_HEADER)-1); ph7_context_output_format( pCtx, PH7_HTML_PAGE_FORMAT, ph7_lib_version(), /* Engine version */ ph7_lib_signature(), /* Engine signature */ ph7_lib_ident(), /* Engine ID */ pVm->pEngine->pVfs ? pVm->pEngine->pVfs->zName : "null_vfs", SyHashTotalEntry(&pVm->hFunction) + SyHashTotalEntry(&pVm->hHostFunction),/* # built-in functions */ SyHashTotalEntry(&pVm->hClass), #ifdef __WINNT__ "Windows NT" #elif defined(__UNIXES__) "UNIX-Like" #else "Other OS" #endif ); ph7_context_output(pCtx,PH7_HTML_PAGE_FOOTER,(int)sizeof(PH7_HTML_PAGE_FOOTER)-1); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Return TRUE */ //ph7_result_bool(pCtx,1); return PH7_OK; } /* * Section: * URL related routines. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* Forward declaration */ static sxi32 VmHttpSplitURI(SyhttpUri *pOut,const char *zUri,sxu32 nLen); /* * value parse_url(string $url [, int $component = -1 ]) * Parse a URL and return its fields. * Parameters * $url * The URL to parse. * $component * Specify one of PHP_URL_SCHEME, PHP_URL_HOST, PHP_URL_PORT, PHP_URL_USER * PHP_URL_PASS, PHP_URL_PATH, PHP_URL_QUERY or PHP_URL_FRAGMENT to retrieve * just a specific URL component as a string (except when PHP_URL_PORT is given * in which case the return value will be an integer). * Return * If the component parameter is omitted, an associative array is returned. * At least one element will be present within the array. Potential keys within * this array are: * scheme - e.g. http * host * port * user * pass * path * query - after the question mark ? * fragment - after the hashmark # * Note: * FALSE is returned on failure. * This function work with relative URL unlike the one shipped * with the standard PHP engine. */ static int vm_builtin_parse_url(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zStr; /* Input string */ SyString *pComp; /* Pointer to the URI component */ SyhttpUri sURI; /* Parse of the given URI */ int nLen; sxi32 rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the given URI */ zStr = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Nothing to process,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Get a parse */ rc = VmHttpSplitURI(&sURI,zStr,(sxu32)nLen); if( rc != SXRET_OK ){ /* Malformed input,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ int nComponent = ph7_value_to_int(apArg[1]); /* Refer to constant.c for constants values */ switch(nComponent){ case 1: /* PHP_URL_SCHEME */ pComp = &sURI.sScheme; if( pComp->nByte < 1 ){ /* No available value,return NULL */ ph7_result_null(pCtx); }else{ ph7_result_string(pCtx,pComp->zString,(int)pComp->nByte); } break; case 2: /* PHP_URL_HOST */ pComp = &sURI.sHost; if( pComp->nByte < 1 ){ /* No available value,return NULL */ ph7_result_null(pCtx); }else{ ph7_result_string(pCtx,pComp->zString,(int)pComp->nByte); } break; case 3: /* PHP_URL_PORT */ pComp = &sURI.sPort; if( pComp->nByte < 1 ){ /* No available value,return NULL */ ph7_result_null(pCtx); }else{ int iPort = 0; /* Cast the value to integer */ SyStrToInt32(pComp->zString,pComp->nByte,(void *)&iPort,0); ph7_result_int(pCtx,iPort); } break; case 4: /* PHP_URL_USER */ pComp = &sURI.sUser; if( pComp->nByte < 1 ){ /* No available value,return NULL */ ph7_result_null(pCtx); }else{ ph7_result_string(pCtx,pComp->zString,(int)pComp->nByte); } break; case 5: /* PHP_URL_PASS */ pComp = &sURI.sPass; if( pComp->nByte < 1 ){ /* No available value,return NULL */ ph7_result_null(pCtx); }else{ ph7_result_string(pCtx,pComp->zString,(int)pComp->nByte); } break; case 7: /* PHP_URL_QUERY */ pComp = &sURI.sQuery; if( pComp->nByte < 1 ){ /* No available value,return NULL */ ph7_result_null(pCtx); }else{ ph7_result_string(pCtx,pComp->zString,(int)pComp->nByte); } break; case 8: /* PHP_URL_FRAGMENT */ pComp = &sURI.sFragment; if( pComp->nByte < 1 ){ /* No available value,return NULL */ ph7_result_null(pCtx); }else{ ph7_result_string(pCtx,pComp->zString,(int)pComp->nByte); } break; case 6: /* PHP_URL_PATH */ pComp = &sURI.sPath; if( pComp->nByte < 1 ){ /* No available value,return NULL */ ph7_result_null(pCtx); }else{ ph7_result_string(pCtx,pComp->zString,(int)pComp->nByte); } break; default: /* No such entry,return NULL */ ph7_result_null(pCtx); break; } }else{ ph7_value *pArray,*pValue; /* Return an associative array */ pArray = ph7_context_new_array(pCtx); /* Empty array */ pValue = ph7_context_new_scalar(pCtx); /* Array value */ if( pArray == 0 || pValue == 0 ){ /* Out of memory */ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 engine is running out of memory"); /* Return false */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Fill the array */ pComp = &sURI.sScheme; if( pComp->nByte > 0 ){ ph7_value_string(pValue,pComp->zString,(int)pComp->nByte); ph7_array_add_strkey_elem(pArray,"scheme",pValue); /* Will make it's own copy */ } /* Reset the string cursor */ ph7_value_reset_string_cursor(pValue); pComp = &sURI.sHost; if( pComp->nByte > 0 ){ ph7_value_string(pValue,pComp->zString,(int)pComp->nByte); ph7_array_add_strkey_elem(pArray,"host",pValue); /* Will make it's own copy */ } /* Reset the string cursor */ ph7_value_reset_string_cursor(pValue); pComp = &sURI.sPort; if( pComp->nByte > 0 ){ int iPort = 0;/* cc warning */ /* Convert to integer */ SyStrToInt32(pComp->zString,pComp->nByte,(void *)&iPort,0); ph7_value_int(pValue,iPort); ph7_array_add_strkey_elem(pArray,"port",pValue); /* Will make it's own copy */ } /* Reset the string cursor */ ph7_value_reset_string_cursor(pValue); pComp = &sURI.sUser; if( pComp->nByte > 0 ){ ph7_value_string(pValue,pComp->zString,(int)pComp->nByte); ph7_array_add_strkey_elem(pArray,"user",pValue); /* Will make it's own copy */ } /* Reset the string cursor */ ph7_value_reset_string_cursor(pValue); pComp = &sURI.sPass; if( pComp->nByte > 0 ){ ph7_value_string(pValue,pComp->zString,(int)pComp->nByte); ph7_array_add_strkey_elem(pArray,"pass",pValue); /* Will make it's own copy */ } /* Reset the string cursor */ ph7_value_reset_string_cursor(pValue); pComp = &sURI.sPath; if( pComp->nByte > 0 ){ ph7_value_string(pValue,pComp->zString,(int)pComp->nByte); ph7_array_add_strkey_elem(pArray,"path",pValue); /* Will make it's own copy */ } /* Reset the string cursor */ ph7_value_reset_string_cursor(pValue); pComp = &sURI.sQuery; if( pComp->nByte > 0 ){ ph7_value_string(pValue,pComp->zString,(int)pComp->nByte); ph7_array_add_strkey_elem(pArray,"query",pValue); /* Will make it's own copy */ } /* Reset the string cursor */ ph7_value_reset_string_cursor(pValue); pComp = &sURI.sFragment; if( pComp->nByte > 0 ){ ph7_value_string(pValue,pComp->zString,(int)pComp->nByte); ph7_array_add_strkey_elem(pArray,"fragment",pValue); /* Will make it's own copy */ } /* Return the created array */ ph7_result_value(pCtx,pArray); /* NOTE: * Don't worry about freeing 'pValue',everything will be released * automatically as soon we return from this function. */ } /* All done */ return PH7_OK; } /* * Section: * Array related routines. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. * Note 2012-5-21 01:04:15: * Array related functions that need access to the underlying * virtual machine are implemented here rather than 'hashmap.c' */ /* * The [compact()] function store it's state information in an instance * of the following structure. */ struct compact_data { ph7_value *pArray; /* Target array */ int nRecCount; /* Recursion count */ }; /* * Walker callback for the [compact()] function defined below. */ static int VmCompactCallback(ph7_value *pKey,ph7_value *pValue,void *pUserData) { struct compact_data *pData = (struct compact_data *)pUserData; ph7_value *pArray = (ph7_value *)pData->pArray; ph7_vm *pVm = pArray->pVm; /* Act according to the hashmap value */ if( ph7_value_is_string(pValue) ){ SyString sVar; SyStringInitFromBuf(&sVar,SyBlobData(&pValue->sBlob),SyBlobLength(&pValue->sBlob)); if( sVar.nByte > 0 ){ /* Query the current frame */ pKey = VmExtractMemObj(pVm,&sVar,FALSE,FALSE); /* ^ * | Avoid wasting variable and use 'pKey' instead */ if( pKey ){ /* Perform the insertion */ ph7_array_add_elem(pArray,pValue/* Variable name*/,pKey/* Variable value */); } } }else if( ph7_value_is_array(pValue) && pData->nRecCount < 32) { int rc; /* Recursively traverse this array */ pData->nRecCount++; rc = PH7_HashmapWalk((ph7_hashmap *)pValue->x.pOther,VmCompactCallback,pUserData); pData->nRecCount--; return rc; } return SXRET_OK; } /* * array compact(mixed $varname [, mixed $... ]) * Create array containing variables and their values. * For each of these, compact() looks for a variable with that name * in the current symbol table and adds it to the output array such * that the variable name becomes the key and the contents of the variable * become the value for that key. In short, it does the opposite of extract(). * Any strings that are not set will simply be skipped. * Parameters * $varname * compact() takes a variable number of parameters. Each parameter can be either * a string containing the name of the variable, or an array of variable names. * The array can contain other arrays of variable names inside it; compact() handles * it recursively. * Return * The output array with all the variables added to it or NULL on failure */ static int vm_builtin_compact(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pArray,*pObj; ph7_vm *pVm = pCtx->pVm; const char *zName; SyString sVar; int i,nLen; if( nArg < 1 ){ /* Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Create the array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ /* Out of memory */ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 engine is running out of memory"); /* Return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Perform the requested operation */ for( i = 0 ; i < nArg ; i++ ){ if( !ph7_value_is_string(apArg[i]) ){ if( ph7_value_is_array(apArg[i]) ){ struct compact_data sData; ph7_hashmap *pMap = (ph7_hashmap *)apArg[i]->x.pOther; /* Recursively walk the array */ sData.nRecCount = 0; sData.pArray = pArray; PH7_HashmapWalk(pMap,VmCompactCallback,&sData); } }else{ /* Extract variable name */ zName = ph7_value_to_string(apArg[i],&nLen); if( nLen > 0 ){ SyStringInitFromBuf(&sVar,zName,nLen); /* Check if the variable is available in the current frame */ pObj = VmExtractMemObj(pVm,&sVar,FALSE,FALSE); if( pObj ){ ph7_array_add_elem(pArray,apArg[i]/*Variable name*/,pObj/* Variable value */); } } } } /* Return the array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * The [extract()] function store it's state information in an instance * of the following structure. */ typedef struct extract_aux_data extract_aux_data; struct extract_aux_data { ph7_vm *pVm; /* VM that own this instance */ int iCount; /* Number of variables successfully imported */ const char *zPrefix; /* Prefix name */ int Prefixlen; /* Prefix length */ int iFlags; /* Control flags */ char zWorker[1024]; /* Working buffer */ }; /* Forward declaration */ static int VmExtractCallback(ph7_value *pKey,ph7_value *pValue,void *pUserData); /* * int extract(array &$var_array[,int $extract_type = EXTR_OVERWRITE[,string $prefix = NULL ]]) * Import variables into the current symbol table from an array. * Parameters * $var_array * An associative array. This function treats keys as variable names and values * as variable values. For each key/value pair it will create a variable in the current symbol * table, subject to extract_type and prefix parameters. * You must use an associative array; a numerically indexed array will not produce results * unless you use EXTR_PREFIX_ALL or EXTR_PREFIX_INVALID. * $extract_type * The way invalid/numeric keys and collisions are treated is determined by the extract_type. * It can be one of the following values: * EXTR_OVERWRITE * If there is a collision, overwrite the existing variable. * EXTR_SKIP * If there is a collision, don't overwrite the existing variable. * EXTR_PREFIX_SAME * If there is a collision, prefix the variable name with prefix. * EXTR_PREFIX_ALL * Prefix all variable names with prefix. * EXTR_PREFIX_INVALID * Only prefix invalid/numeric variable names with prefix. * EXTR_IF_EXISTS * Only overwrite the variable if it already exists in the current symbol table * otherwise do nothing. * This is useful for defining a list of valid variables and then extracting only those * variables you have defined out of $_REQUEST, for example. * EXTR_PREFIX_IF_EXISTS * Only create prefixed variable names if the non-prefixed version of the same variable exists in * the current symbol table. * $prefix * Note that prefix is only required if extract_type is EXTR_PREFIX_SAME, EXTR_PREFIX_ALL * EXTR_PREFIX_INVALID or EXTR_PREFIX_IF_EXISTS. If the prefixed result is not a valid variable name * it is not imported into the symbol table. Prefixes are automatically separated from the array key by an * underscore character. * Return * Returns the number of variables successfully imported into the symbol table. */ static int vm_builtin_extract(ph7_context *pCtx,int nArg,ph7_value **apArg) { extract_aux_data sAux; ph7_hashmap *pMap; if( nArg < 1 || !ph7_value_is_array(apArg[0]) ){ /* Missing/Invalid arguments,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } /* Point to the target hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; if( pMap->nEntry < 1 ){ /* Empty map,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } /* Prepare the aux data */ SyZero(&sAux,sizeof(extract_aux_data)-sizeof(sAux.zWorker)); if( nArg > 1 ){ sAux.iFlags = ph7_value_to_int(apArg[1]); if( nArg > 2 ){ sAux.zPrefix = ph7_value_to_string(apArg[2],&sAux.Prefixlen); } } sAux.pVm = pCtx->pVm; /* Invoke the worker callback */ PH7_HashmapWalk(pMap,VmExtractCallback,&sAux); /* Number of variables successfully imported */ ph7_result_int(pCtx,sAux.iCount); return PH7_OK; } /* * Worker callback for the [extract()] function defined * below. */ static int VmExtractCallback(ph7_value *pKey,ph7_value *pValue,void *pUserData) { extract_aux_data *pAux = (extract_aux_data *)pUserData; int iFlags = pAux->iFlags; ph7_vm *pVm = pAux->pVm; ph7_value *pObj; SyString sVar; if( (iFlags & 0x10/* EXTR_PREFIX_INVALID */) && (pKey->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL|MEMOBJ_REAL))){ iFlags |= 0x08; /*EXTR_PREFIX_ALL*/ } /* Perform a string cast */ PH7_MemObjToString(pKey); if( SyBlobLength(&pKey->sBlob) < 1 ){ /* Unavailable variable name */ return SXRET_OK; } sVar.nByte = 0; /* cc warning */ if( (iFlags & 0x08/*EXTR_PREFIX_ALL*/ ) && pAux->Prefixlen > 0 ){ sVar.nByte = (sxu32)SyBufferFormat(pAux->zWorker,sizeof(pAux->zWorker),"%.*s_%.*s", pAux->Prefixlen,pAux->zPrefix, SyBlobLength(&pKey->sBlob),SyBlobData(&pKey->sBlob) ); }else{ sVar.nByte = (sxu32) SyMemcpy(SyBlobData(&pKey->sBlob),pAux->zWorker, SXMIN(SyBlobLength(&pKey->sBlob),sizeof(pAux->zWorker))); } sVar.zString = pAux->zWorker; /* Try to extract the variable */ pObj = VmExtractMemObj(pVm,&sVar,TRUE,FALSE); if( pObj ){ /* Collision */ if( iFlags & 0x02 /* EXTR_SKIP */ ){ return SXRET_OK; } if( iFlags & 0x04 /* EXTR_PREFIX_SAME */ ){ if( (iFlags & 0x08/*EXTR_PREFIX_ALL*/) || pAux->Prefixlen < 1){ /* Already prefixed */ return SXRET_OK; } sVar.nByte = (sxu32)SyBufferFormat(pAux->zWorker,sizeof(pAux->zWorker),"%.*s_%.*s", pAux->Prefixlen,pAux->zPrefix, SyBlobLength(&pKey->sBlob),SyBlobData(&pKey->sBlob) ); pObj = VmExtractMemObj(pVm,&sVar,TRUE,TRUE); } }else{ /* Create the variable */ pObj = VmExtractMemObj(pVm,&sVar,TRUE,TRUE); } if( pObj ){ /* Overwrite the old value */ PH7_MemObjStore(pValue,pObj); /* Increment counter */ pAux->iCount++; } return SXRET_OK; } /* * Worker callback for the [import_request_variables()] function * defined below. */ static int VmImportRequestCallback(ph7_value *pKey,ph7_value *pValue,void *pUserData) { extract_aux_data *pAux = (extract_aux_data *)pUserData; ph7_vm *pVm = pAux->pVm; ph7_value *pObj; SyString sVar; /* Perform a string cast */ PH7_MemObjToString(pKey); if( SyBlobLength(&pKey->sBlob) < 1 ){ /* Unavailable variable name */ return SXRET_OK; } sVar.nByte = 0; /* cc warning */ if( pAux->Prefixlen > 0 ){ sVar.nByte = (sxu32)SyBufferFormat(pAux->zWorker,sizeof(pAux->zWorker),"%.*s%.*s", pAux->Prefixlen,pAux->zPrefix, SyBlobLength(&pKey->sBlob),SyBlobData(&pKey->sBlob) ); }else{ sVar.nByte = (sxu32) SyMemcpy(SyBlobData(&pKey->sBlob),pAux->zWorker, SXMIN(SyBlobLength(&pKey->sBlob),sizeof(pAux->zWorker))); } sVar.zString = pAux->zWorker; /* Extract the variable */ pObj = VmExtractMemObj(pVm,&sVar,TRUE,TRUE); if( pObj ){ PH7_MemObjStore(pValue,pObj); } return SXRET_OK; } /* * bool import_request_variables(string $types[,string $prefix]) * Import GET/POST/Cookie variables into the global scope. * Parameters * $types * Using the types parameter, you can specify which request variables to import. * You can use 'G', 'P' and 'C' characters respectively for GET, POST and Cookie. * These characters are not case sensitive, so you can also use any combination of 'g', 'p' and 'c'. * POST includes the POST uploaded file information. * Note: * Note that the order of the letters matters, as when using "GP", the POST variables will overwrite * GET variables with the same name. Any other letters than GPC are discarded. * $prefix * Variable name prefix, prepended before all variable's name imported into the global scope. * So if you have a GET value named "userid", and provide a prefix "pref_", then you'll get a global * variable named $pref_userid. * Return * TRUE on success or FALSE on failure. */ static int vm_builtin_import_request_variables(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPrefix,*zEnd,*zImport; extract_aux_data sAux; int nLen,nPrefixLen; ph7_value *pSuper; ph7_vm *pVm; /* By default import only $_GET variables */ zImport = "G"; nLen = (int)sizeof(char); zPrefix = 0; nPrefixLen = 0; if( nArg > 0 ){ if( ph7_value_is_string(apArg[0]) ){ zImport = ph7_value_to_string(apArg[0],&nLen); } if( nArg > 1 && ph7_value_is_string(apArg[1]) ){ zPrefix = ph7_value_to_string(apArg[1],&nPrefixLen); } } /* Point to the underlying VM */ pVm = pCtx->pVm; /* Initialize the aux data */ SyZero(&sAux,sizeof(sAux)-sizeof(sAux.zWorker)); sAux.zPrefix = zPrefix; sAux.Prefixlen = nPrefixLen; sAux.pVm = pVm; /* Extract */ zEnd = &zImport[nLen]; while( zImport < zEnd ){ int c = zImport[0]; pSuper = 0; if( c == 'G' || c == 'g' ){ /* Import $_GET variables */ pSuper = VmExtractSuper(pVm,"_GET",sizeof("_GET")-1); }else if( c == 'P' || c == 'p' ){ /* Import $_POST variables */ pSuper = VmExtractSuper(pVm,"_POST",sizeof("_POST")-1); }else if( c == 'c' || c == 'C' ){ /* Import $_COOKIE variables */ pSuper = VmExtractSuper(pVm,"_COOKIE",sizeof("_COOKIE")-1); } if( pSuper ){ /* Iterate throw array entries */ ph7_array_walk(pSuper,VmImportRequestCallback,&sAux); } /* Advance the cursor */ zImport++; } /* All done,return TRUE*/ ph7_result_bool(pCtx,0); return PH7_OK; } /* * Compile and evaluate a PHP chunk at run-time. * Refer to the eval() language construct implementation for more * information. */ static sxi32 VmEvalChunk( ph7_vm *pVm, /* Underlying Virtual Machine */ ph7_context *pCtx, /* Call Context */ SyString *pChunk, /* PHP chunk to evaluate */ int iFlags, /* Compile flag */ int bTrueReturn /* TRUE to return execution result */ ) { SySet *pByteCode,aByteCode; ProcConsumer xErr = 0; void *pErrData = 0; /* Initialize bytecode container */ SySetInit(&aByteCode,&pVm->sAllocator,sizeof(VmInstr)); SySetAlloc(&aByteCode,0x20); /* Reset the code generator */ if( bTrueReturn ){ /* Included file,log compile-time errors */ xErr = pVm->pEngine->xConf.xErr; pErrData = pVm->pEngine->xConf.pErrData; } PH7_ResetCodeGenerator(pVm,xErr,pErrData); /* Swap bytecode container */ pByteCode = pVm->pByteContainer; pVm->pByteContainer = &aByteCode; /* Compile the chunk */ PH7_CompileScript(pVm,pChunk,iFlags); if( pVm->sCodeGen.nErr > 0 ){ /* Compilation error,return false */ if( pCtx ){ ph7_result_bool(pCtx,0); } }else{ ph7_value sResult; /* Return value */ SyHashEntry *pEntry; /* Initialize and install static and constants class attributes */ SyHashResetLoopCursor(&pVm->hClass); while((pEntry = SyHashGetNextEntry(&pVm->hClass)) != 0 ){ if (VmMountUserClass(&(*pVm),(ph7_class *)pEntry->pUserData) != SXRET_OK ){ if( pCtx ){ ph7_result_bool(pCtx,0); } goto Cleanup; } } if( SXRET_OK != PH7_VmEmitInstr(pVm,PH7_OP_DONE,0,0,0,0) ){ /* Out of memory */ if( pCtx ){ ph7_result_bool(pCtx,0); } goto Cleanup; } if( bTrueReturn ){ /* Assume a boolean true return value */ PH7_MemObjInitFromBool(pVm,&sResult,1); }else{ /* Assume a null return value */ PH7_MemObjInit(pVm,&sResult); } /* Execute the compiled chunk */ VmLocalExec(pVm,&aByteCode,&sResult); if( pCtx ){ /* Set the execution result */ ph7_result_value(pCtx,&sResult); } PH7_MemObjRelease(&sResult); } Cleanup: /* Cleanup the mess left behind */ pVm->pByteContainer = pByteCode; SySetRelease(&aByteCode); return SXRET_OK; } /* * value eval(string $code) * Evaluate a string as PHP code. * Parameter * code: PHP code to evaluate. * Return * eval() returns NULL unless return is called in the evaluated code, in which case * the value passed to return is returned. If there is a parse error in the evaluated * code, eval() returns FALSE and execution of the following code continues normally. */ static int vm_builtin_eval(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyString sChunk; /* Chunk to evaluate */ if( nArg < 1 ){ /* Nothing to evaluate,return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* Chunk to evaluate */ sChunk.zString = ph7_value_to_string(apArg[0],(int *)&sChunk.nByte); if( sChunk.nByte < 1 ){ /* Empty string,return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* Eval the chunk */ VmEvalChunk(pCtx->pVm,&(*pCtx),&sChunk,PH7_PHP_ONLY,FALSE); return SXRET_OK; } /* * Check if a file path is already included. */ static int VmIsIncludedFile(ph7_vm *pVm,SyString *pFile) { SyString *aEntries; sxu32 n; aEntries = (SyString *)SySetBasePtr(&pVm->aIncluded); /* Perform a linear search */ for( n = 0 ; n < SySetUsed(&pVm->aIncluded) ; ++n ){ if( SyStringCmp(pFile,&aEntries[n],SyMemcmp) == 0 ){ /* Already included */ return TRUE; } } return FALSE; } /* * Push a file path in the appropriate VM container. */ PH7_PRIVATE sxi32 PH7_VmPushFilePath(ph7_vm *pVm,const char *zPath,int nLen,sxu8 bMain,sxi32 *pNew) { SyString sPath; char *zDup; #ifdef __WINNT__ char *zCur; #endif sxi32 rc; if( nLen < 0 ){ nLen = SyStrlen(zPath); } /* Duplicate the file path first */ zDup = SyMemBackendStrDup(&pVm->sAllocator,zPath,nLen); if( zDup == 0 ){ return SXERR_MEM; } #ifdef __WINNT__ /* Normalize path on windows * Example: * Path/To/File.php * becomes * path\to\file.php */ zCur = zDup; while( zCur[0] != 0 ){ if( zCur[0] == '/' ){ zCur[0] = '\\'; }else if( (unsigned char)zCur[0] < 0xc0 && SyisUpper(zCur[0]) ){ int c = SyToLower(zCur[0]); zCur[0] = (char)c; /* MSVC stupidity */ } zCur++; } #endif /* Install the file path */ SyStringInitFromBuf(&sPath,zDup,nLen); if( !bMain ){ if( VmIsIncludedFile(&(*pVm),&sPath) ){ /* Already included */ *pNew = 0; }else{ /* Insert in the corresponding container */ rc = SySetPut(&pVm->aIncluded,(const void *)&sPath); if( rc != SXRET_OK ){ SyMemBackendFree(&pVm->sAllocator,zDup); return rc; } *pNew = 1; } } SySetPut(&pVm->aFiles,(const void *)&sPath); return SXRET_OK; } /* * Compile and Execute a PHP script at run-time. * SXRET_OK is returned on sucessful evaluation.Any other return values * indicates failure. * Note that the PHP script to evaluate can be a local or remote file.In * either cases the [PH7_StreamReadWholeFile()] function handle all the underlying * operations. * If the [PH7_DISABLE_BUILTIN_FUNC] compile-time directive is defined,then * this function is a no-op. * Refer to the implementation of the include(),include_once() language * constructs for more information. */ static sxi32 VmExecIncludedFile( ph7_context *pCtx, /* Call Context */ SyString *pPath, /* Script path or URL*/ int IncludeOnce /* TRUE if called from include_once() or require_once() */ ) { sxi32 rc; #ifndef PH7_DISABLE_BUILTIN_FUNC const ph7_io_stream *pStream; SyBlob sContents; void *pHandle; ph7_vm *pVm; int isNew; /* Initialize fields */ pVm = pCtx->pVm; SyBlobInit(&sContents,&pVm->sAllocator); isNew = 0; /* Extract the associated stream */ pStream = PH7_VmGetStreamDevice(pVm,&pPath->zString,pPath->nByte); /* * Open the file or the URL [i.e: http://ph7.symisc.net/example/hello.php"] * in a read-only mode. */ pHandle = PH7_StreamOpenHandle(pVm,pStream,pPath->zString,PH7_IO_OPEN_RDONLY,TRUE,0,TRUE,&isNew); if( pHandle == 0 ){ return SXERR_IO; } rc = SXRET_OK; /* Stupid cc warning */ if( IncludeOnce && !isNew ){ /* Already included */ rc = SXERR_EXISTS; }else{ /* Read the whole file contents */ rc = PH7_StreamReadWholeFile(pHandle,pStream,&sContents); if( rc == SXRET_OK ){ SyString sScript; /* Compile and execute the script */ SyStringInitFromBuf(&sScript,SyBlobData(&sContents),SyBlobLength(&sContents)); VmEvalChunk(pCtx->pVm,&(*pCtx),&sScript,0,TRUE); } } /* Pop from the set of included file */ (void)SySetPop(&pVm->aFiles); /* Close the handle */ PH7_StreamCloseHandle(pStream,pHandle); /* Release the working buffer */ SyBlobRelease(&sContents); #else pCtx = 0; /* cc warning */ pPath = 0; IncludeOnce = 0; rc = SXERR_IO; #endif /* PH7_DISABLE_BUILTIN_FUNC */ return rc; } /* * string get_include_path(void) * Gets the current include_path configuration option. * Parameter * None * Return * Included paths as a string */ static int vm_builtin_get_include_path(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; SyString *aEntry; int dir_sep; sxu32 n; #ifdef __WINNT__ dir_sep = ';'; #else /* Assume UNIX path separator */ dir_sep = ':'; #endif SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Point to the list of import paths */ aEntry = (SyString *)SySetBasePtr(&pVm->aPaths); for( n = 0 ; n < SySetUsed(&pVm->aPaths) ; n++ ){ SyString *pEntry = &aEntry[n]; if( n > 0 ){ /* Append dir seprator */ ph7_result_string(pCtx,(const char *)&dir_sep,sizeof(char)); } /* Append path */ ph7_result_string(pCtx,pEntry->zString,(int)pEntry->nByte); } return PH7_OK; } /* * string get_get_included_files(void) * Gets the current include_path configuration option. * Parameter * None * Return * Included paths as a string */ static int vm_builtin_get_included_files(ph7_context *pCtx,int nArg,ph7_value **apArg) { SySet *pFiles = &pCtx->pVm->aFiles; ph7_value *pArray,*pWorker; SyString *pEntry; int c,d; /* Create an array and a working value */ pArray = ph7_context_new_array(pCtx); pWorker = ph7_context_new_scalar(pCtx); if( pArray == 0 || pWorker == 0 ){ /* Out of memory,return null */ ph7_result_null(pCtx); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); return PH7_OK; } c = d = '/'; #ifdef __WINNT__ d = '\\'; #endif /* Iterate throw entries */ SySetResetCursor(pFiles); while( SXRET_OK == SySetGetNextEntry(pFiles,(void **)&pEntry) ){ const char *zBase,*zEnd; int iLen; /* reset the string cursor */ ph7_value_reset_string_cursor(pWorker); /* Extract base name */ zEnd = &pEntry->zString[pEntry->nByte - 1]; /* Ignore trailing '/' */ while( zEnd > pEntry->zString && ( (int)zEnd[0] == c || (int)zEnd[0] == d ) ){ zEnd--; } iLen = (int)(&zEnd[1]-pEntry->zString); while( zEnd > pEntry->zString && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){ zEnd--; } zBase = (zEnd > pEntry->zString) ? &zEnd[1] : pEntry->zString; zEnd = &pEntry->zString[iLen]; /* Copy entry name */ ph7_value_string(pWorker,zBase,(int)(zEnd-zBase)); /* Perform the insertion */ ph7_array_add_elem(pArray,0/* Automatic index assign*/,pWorker); /* Will make it's own copy */ } /* All done,return the created array */ ph7_result_value(pCtx,pArray); /* Note that 'pWorker' will be automatically destroyed * by the engine as soon we return from this foreign * function. */ return PH7_OK; } /* * include: * According to the PHP reference manual. * The include() function includes and evaluates the specified file. * Files are included based on the file path given or, if none is given * the include_path specified.If the file isn't found in the include_path * include() will finally check in the calling script's own directory * and the current working directory before failing. The include() * construct will emit a warning if it cannot find a file; this is different * behavior from require(), which will emit a fatal error. * If a path is defined � whether absolute (starting with a drive letter * or \ on Windows, or / on Unix/Linux systems) or relative to the current * directory (starting with . or ..) � the include_path will be ignored altogether. * For example, if a filename begins with ../, the parser will look in the parent * directory to find the requested file. * When a file is included, the code it contains inherits the variable scope * of the line on which the include occurs. Any variables available at that line * in the calling file will be available within the called file, from that point forward. * However, all functions and classes defined in the included file have the global scope. */ static int vm_builtin_include(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyString sFile; sxi32 rc; if( nArg < 1 ){ /* Nothing to evaluate,return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* File to include */ sFile.zString = ph7_value_to_string(apArg[0],(int *)&sFile.nByte); if( sFile.nByte < 1 ){ /* Empty string,return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* Open,compile and execute the desired script */ rc = VmExecIncludedFile(&(*pCtx),&sFile,FALSE); if( rc != SXRET_OK ){ /* Emit a warning and return false */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING,"IO error while importing: '%z'",&sFile); ph7_result_bool(pCtx,0); } return SXRET_OK; } /* * include_once: * According to the PHP reference manual. * The include_once() statement includes and evaluates the specified file during * the execution of the script. This is a behavior similar to the include() * statement, with the only difference being that if the code from a file has already * been included, it will not be included again. As the name suggests, it will be included * just once. */ static int vm_builtin_include_once(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyString sFile; sxi32 rc; if( nArg < 1 ){ /* Nothing to evaluate,return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* File to include */ sFile.zString = ph7_value_to_string(apArg[0],(int *)&sFile.nByte); if( sFile.nByte < 1 ){ /* Empty string,return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* Open,compile and execute the desired script */ rc = VmExecIncludedFile(&(*pCtx),&sFile,TRUE); if( rc == SXERR_EXISTS ){ /* File already included,return TRUE */ ph7_result_bool(pCtx,1); return SXRET_OK; } if( rc != SXRET_OK ){ /* Emit a warning and return false */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING,"IO error while importing: '%z'",&sFile); ph7_result_bool(pCtx,0); } return SXRET_OK; } /* * require. * According to the PHP reference manual. * require() is identical to include() except upon failure it will * also produce a fatal level error. * In other words, it will halt the script whereas include() only * emits a warning which allows the script to continue. */ static int vm_builtin_require(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyString sFile; sxi32 rc; if( nArg < 1 ){ /* Nothing to evaluate,return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* File to include */ sFile.zString = ph7_value_to_string(apArg[0],(int *)&sFile.nByte); if( sFile.nByte < 1 ){ /* Empty string,return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* Open,compile and execute the desired script */ rc = VmExecIncludedFile(&(*pCtx),&sFile,FALSE); if( rc != SXRET_OK ){ /* Fatal,abort VM execution immediately */ ph7_context_throw_error_format(pCtx,PH7_CTX_ERR,"Fatal IO error while importing: '%z'",&sFile); ph7_result_bool(pCtx,0); return PH7_ABORT; } return SXRET_OK; } /* * require_once: * According to the PHP reference manual. * The require_once() statement is identical to require() except PHP will check * if the file has already been included, and if so, not include (require) it again. * See the include_once() documentation for information about the _once behaviour * and how it differs from its non _once siblings. */ static int vm_builtin_require_once(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyString sFile; sxi32 rc; if( nArg < 1 ){ /* Nothing to evaluate,return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* File to include */ sFile.zString = ph7_value_to_string(apArg[0],(int *)&sFile.nByte); if( sFile.nByte < 1 ){ /* Empty string,return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* Open,compile and execute the desired script */ rc = VmExecIncludedFile(&(*pCtx),&sFile,TRUE); if( rc == SXERR_EXISTS ){ /* File already included,return TRUE */ ph7_result_bool(pCtx,1); return SXRET_OK; } if( rc != SXRET_OK ){ /* Fatal,abort VM execution immediately */ ph7_context_throw_error_format(pCtx,PH7_CTX_ERR,"Fatal IO error while importing: '%z'",&sFile); ph7_result_bool(pCtx,0); return PH7_ABORT; } return SXRET_OK; } /* * Section: * Command line arguments processing. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * Check if a short option argument [i.e: -c] is available in the command * line string. Return a pointer to the start of the stream on success. * NULL otherwise. */ static const char * VmFindShortOpt(int c,const char *zIn,const char *zEnd) { while( zIn < zEnd ){ if( zIn[0] == '-' && &zIn[1] < zEnd && (int)zIn[1] == c ){ /* Got one */ return &zIn[1]; } /* Advance the cursor */ zIn++; } /* No such option */ return 0; } /* * Check if a long option argument [i.e: --opt] is available in the command * line string. Return a pointer to the start of the stream on success. * NULL otherwise. */ static const char * VmFindLongOpt(const char *zLong,int nByte,const char *zIn,const char *zEnd) { const char *zOpt; while( zIn < zEnd ){ if( zIn[0] == '-' && &zIn[1] < zEnd && (int)zIn[1] == '-' ){ zIn += 2; zOpt = zIn; while( zIn < zEnd && !SyisSpace(zIn[0]) ){ if( zIn[0] == '=' /* --opt=val */){ break; } zIn++; } /* Test */ if( (int)(zIn-zOpt) == nByte && SyMemcmp(zOpt,zLong,nByte) == 0 ){ /* Got one,return it's value */ return zIn; } }else{ zIn++; } } /* No such option */ return 0; } /* * Long option [i.e: --opt] arguments private data structure. */ struct getopt_long_opt { const char *zArgIn,*zArgEnd; /* Command line arguments */ ph7_value *pWorker; /* Worker variable*/ ph7_value *pArray; /* getopt() return value */ ph7_context *pCtx; /* Call Context */ }; /* Forward declaration */ static int VmProcessLongOpt(ph7_value *pKey,ph7_value *pValue,void *pUserData); /* * Extract short or long argument option values. */ static void VmExtractOptArgValue( ph7_value *pArray, /* getopt() return value */ ph7_value *pWorker, /* Worker variable */ const char *zArg, /* Argument stream */ const char *zArgEnd,/* End of the argument stream */ int need_val, /* TRUE to fetch option argument */ ph7_context *pCtx, /* Call Context */ const char *zName /* Option name */) { ph7_value_bool(pWorker,0); if( !need_val ){ /* * Option does not need arguments. * Insert the option name and a boolean FALSE. */ ph7_array_add_strkey_elem(pArray,(const char *)zName,pWorker); /* Will make it's own copy */ }else{ const char *zCur; /* Extract option argument */ zArg++; if( zArg < zArgEnd && zArg[0] == '=' ){ zArg++; } while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){ zArg++; } if( zArg >= zArgEnd || zArg[0] == '-' ){ /* * Argument not found. * Insert the option name and a boolean FALSE. */ ph7_array_add_strkey_elem(pArray,(const char *)zName,pWorker); /* Will make it's own copy */ return; } /* Delimit the value */ zCur = zArg; if( zArg[0] == '\'' || zArg[0] == '"' ){ int d = zArg[0]; /* Delimt the argument */ zArg++; zCur = zArg; while( zArg < zArgEnd ){ if( zArg[0] == d && zArg[-1] != '\\' ){ /* Delimiter found,exit the loop */ break; } zArg++; } /* Save the value */ ph7_value_string(pWorker,zCur,(int)(zArg-zCur)); if( zArg < zArgEnd ){ zArg++; } }else{ while( zArg < zArgEnd && !SyisSpace(zArg[0]) ){ zArg++; } /* Save the value */ ph7_value_string(pWorker,zCur,(int)(zArg-zCur)); } /* * Check if we are dealing with multiple values. * If so,create an array to hold them,rather than a scalar variable. */ while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){ zArg++; } if( zArg < zArgEnd && zArg[0] != '-' ){ ph7_value *pOptArg; /* Array of option arguments */ pOptArg = ph7_context_new_array(pCtx); if( pOptArg == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); }else{ /* Insert the first value */ ph7_array_add_elem(pOptArg,0,pWorker); /* Will make it's own copy */ for(;;){ if( zArg >= zArgEnd || zArg[0] == '-' ){ /* No more value */ break; } /* Delimit the value */ zCur = zArg; if( zArg < zArgEnd && zArg[0] == '\\' ){ zArg++; zCur = zArg; } while( zArg < zArgEnd && !SyisSpace(zArg[0]) ){ zArg++; } /* Reset the string cursor */ ph7_value_reset_string_cursor(pWorker); /* Save the value */ ph7_value_string(pWorker,zCur,(int)(zArg-zCur)); /* Insert */ ph7_array_add_elem(pOptArg,0,pWorker); /* Will make it's own copy */ /* Jump trailing white spaces */ while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){ zArg++; } } /* Insert the option arg array */ ph7_array_add_strkey_elem(pArray,(const char *)zName,pOptArg); /* Will make it's own copy */ /* Safely release */ ph7_context_release_value(pCtx,pOptArg); } }else{ /* Single value */ ph7_array_add_strkey_elem(pArray,(const char *)zName,pWorker); /* Will make it's own copy */ } } } /* * array getopt(string $options[,array $longopts ]) * Gets options from the command line argument list. * Parameters * $options * Each character in this string will be used as option characters * and matched against options passed to the script starting with * a single hyphen (-). For example, an option string "x" recognizes * an option -x. Only a-z, A-Z and 0-9 are allowed. * $longopts * An array of options. Each element in this array will be used as option * strings and matched against options passed to the script starting with * two hyphens (--). For example, an longopts element "opt" recognizes an * option --opt. * Return * This function will return an array of option / argument pairs or FALSE * on failure. */ static int vm_builtin_getopt(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zIn,*zEnd,*zArg,*zArgIn,*zArgEnd; struct getopt_long_opt sLong; ph7_value *pArray,*pWorker; SyBlob *pArg; int nByte; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Missing/Invalid option arguments"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract option arguments */ zIn = ph7_value_to_string(apArg[0],&nByte); zEnd = &zIn[nByte]; /* Point to the string representation of the $argv[] array */ pArg = &pCtx->pVm->sArgv; /* Create a new empty array and a worker variable */ pArray = ph7_context_new_array(pCtx); pWorker = ph7_context_new_scalar(pCtx); if( pArray == 0 || pWorker == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); ph7_result_bool(pCtx,0); return PH7_OK; } if( SyBlobLength(pArg) < 1 ){ /* Empty command line,return the empty array*/ ph7_result_value(pCtx,pArray); /* Everything will be released automatically when we return * from this function. */ return PH7_OK; } zArgIn = (const char *)SyBlobData(pArg); zArgEnd = &zArgIn[SyBlobLength(pArg)]; /* Fill the long option structure */ sLong.pArray = pArray; sLong.pWorker = pWorker; sLong.zArgIn = zArgIn; sLong.zArgEnd = zArgEnd; sLong.pCtx = pCtx; /* Start processing */ while( zIn < zEnd ){ int c = zIn[0]; int need_val = 0; /* Advance the stream cursor */ zIn++; /* Ignore non-alphanum characters */ if( !SyisAlphaNum(c) ){ continue; } if( zIn < zEnd && zIn[0] == ':' ){ zIn++; need_val = 1; if( zIn < zEnd && zIn[0] == ':' ){ zIn++; } } /* Find option */ zArg = VmFindShortOpt(c,zArgIn,zArgEnd); if( zArg == 0 ){ /* No such option */ continue; } /* Extract option argument value */ VmExtractOptArgValue(pArray,pWorker,zArg,zArgEnd,need_val,pCtx,(const char *)&c); } if( nArg > 1 && ph7_value_is_array(apArg[1]) && ph7_array_count(apArg[1]) > 0 ){ /* Process long options */ ph7_array_walk(apArg[1],VmProcessLongOpt,&sLong); } /* Return the option array */ ph7_result_value(pCtx,pArray); /* * Don't worry about freeing memory, everything will be released * automatically as soon we return from this foreign function. */ return PH7_OK; } /* * Array walker callback used for processing long options values. */ static int VmProcessLongOpt(ph7_value *pKey,ph7_value *pValue,void *pUserData) { struct getopt_long_opt *pOpt = (struct getopt_long_opt *)pUserData; const char *zArg,*zOpt,*zEnd; int need_value = 0; int nByte; /* Value must be of type string */ if( !ph7_value_is_string(pValue) ){ /* Simply ignore */ return PH7_OK; } zOpt = ph7_value_to_string(pValue,&nByte); if( nByte < 1 ){ /* Empty string,ignore */ return PH7_OK; } zEnd = &zOpt[nByte - 1]; if( zEnd[0] == ':' ){ char *zTerm; /* Try to extract a value */ need_value = 1; while( zEnd >= zOpt && zEnd[0] == ':' ){ zEnd--; } if( zOpt >= zEnd ){ /* Empty string,ignore */ SXUNUSED(pKey); return PH7_OK; } zEnd++; zTerm = (char *)zEnd; zTerm[0] = 0; }else{ zEnd = &zOpt[nByte]; } /* Find the option */ zArg = VmFindLongOpt(zOpt,(int)(zEnd-zOpt),pOpt->zArgIn,pOpt->zArgEnd); if( zArg == 0 ){ /* No such option,return immediately */ return PH7_OK; } /* Try to extract a value */ VmExtractOptArgValue(pOpt->pArray,pOpt->pWorker,zArg,pOpt->zArgEnd,need_value,pOpt->pCtx,zOpt); return PH7_OK; } /* * Section: * JSON encoding/decoding routines. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Devel. */ /* Forward reference */ static int VmJsonArrayEncode(ph7_value *pKey,ph7_value *pValue,void *pUserData); static int VmJsonObjectEncode(const char *zAttr,ph7_value *pValue,void *pUserData); /* * JSON encoder state is stored in an instance * of the following structure. */ typedef struct json_private_data json_private_data; struct json_private_data { ph7_context *pCtx; /* Call context */ int isFirst; /* True if first encoded entry */ int iFlags; /* JSON encoding flags */ int nRecCount; /* Recursion count */ }; /* * Returns the JSON representation of a value.In other word perform a JSON encoding operation. * According to wikipedia * JSON's basic types are: * Number (double precision floating-point format in JavaScript, generally depends on implementation) * String (double-quoted Unicode, with backslash escaping) * Boolean (true or false) * Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values * do not need to be of the same type) * Object (an unordered collection of key:value pairs with the ':' character separating the key * and the value, comma-separated and enclosed in curly braces; the keys must be strings and should * be distinct from each other) * null (empty) * Non-significant white space may be added freely around the "structural characters" * (i.e. the brackets "[{]}", colon ":" and comma ","). */ static sxi32 VmJsonEncode( ph7_value *pIn, /* Encode this value */ json_private_data *pData /* Context data */ ){ ph7_context *pCtx = pData->pCtx; int iFlags = pData->iFlags; int nByte; if( ph7_value_is_null(pIn) || ph7_value_is_resource(pIn)){ /* null */ ph7_result_string(pCtx,"null",(int)sizeof("null")-1); }else if( ph7_value_is_bool(pIn) ){ int iBool = ph7_value_to_bool(pIn); int iLen; /* true/false */ iLen = iBool ? (int)sizeof("true") : (int)sizeof("false"); ph7_result_string(pCtx,iBool ? "true" : "false",iLen-1); }else if( ph7_value_is_numeric(pIn) && !ph7_value_is_string(pIn) ){ const char *zNum; /* Get a string representation of the number */ zNum = ph7_value_to_string(pIn,&nByte); ph7_result_string(pCtx,zNum,nByte); }else if( ph7_value_is_string(pIn) ){ if( (iFlags & JSON_NUMERIC_CHECK) && ph7_value_is_numeric(pIn) ){ const char *zNum; /* Encodes numeric strings as numbers. */ PH7_MemObjToReal(pIn); /* Force a numeric cast */ /* Get a string representation of the number */ zNum = ph7_value_to_string(pIn,&nByte); ph7_result_string(pCtx,zNum,nByte); }else{ const char *zIn,*zEnd; int c; /* Encode the string */ zIn = ph7_value_to_string(pIn,&nByte); zEnd = &zIn[nByte]; /* Append the double quote */ ph7_result_string(pCtx,"\"",(int)sizeof(char)); for(;;){ if( zIn >= zEnd ){ /* No more input to process */ break; } c = zIn[0]; /* Advance the stream cursor */ zIn++; if( (c == '<' || c == '>') && (iFlags & JSON_HEX_TAG) ){ /* All < and > are converted to \u003C and \u003E */ if( c == '<' ){ ph7_result_string(pCtx,"\\u003C",(int)sizeof("\\u003C")-1); }else{ ph7_result_string(pCtx,"\\u003E",(int)sizeof("\\u003E")-1); } continue; }else if( c == '&' && (iFlags & JSON_HEX_AMP) ){ /* All &s are converted to \u0026. */ ph7_result_string(pCtx,"\\u0026",(int)sizeof("\\u0026")-1); continue; }else if( c == '\'' && (iFlags & JSON_HEX_APOS) ){ /* All ' are converted to \u0027. */ ph7_result_string(pCtx,"\\u0027",(int)sizeof("\\u0027")-1); continue; }else if( c == '"' && (iFlags & JSON_HEX_QUOT) ){ /* All " are converted to \u0022. */ ph7_result_string(pCtx,"\\u0022",(int)sizeof("\\u0022")-1); continue; } if( c == '"' || (c == '\\' && ((iFlags & JSON_UNESCAPED_SLASHES)==0)) ){ /* Unescape the character */ ph7_result_string(pCtx,"\\",(int)sizeof(char)); } /* Append character verbatim */ ph7_result_string(pCtx,(const char *)&c,(int)sizeof(char)); } /* Append the double quote */ ph7_result_string(pCtx,"\"",(int)sizeof(char)); } }else if( ph7_value_is_array(pIn) ){ int c = '[',d = ']'; /* Encode the array */ pData->isFirst = 1; if( iFlags & JSON_FORCE_OBJECT ){ /* Outputs an object rather than an array */ c = '{'; d = '}'; } /* Append the square bracket or curly braces */ ph7_result_string(pCtx,(const char *)&c,(int)sizeof(char)); /* Iterate throw array entries */ ph7_array_walk(pIn,VmJsonArrayEncode,pData); /* Append the closing square bracket or curly braces */ ph7_result_string(pCtx,(const char *)&d,(int)sizeof(char)); }else if( ph7_value_is_object(pIn) ){ /* Encode the class instance */ pData->isFirst = 1; /* Append the curly braces */ ph7_result_string(pCtx,"{",(int)sizeof(char)); /* Iterate throw class attribute */ ph7_object_walk(pIn,VmJsonObjectEncode,pData); /* Append the closing curly braces */ ph7_result_string(pCtx,"}",(int)sizeof(char)); }else{ /* Can't happen */ ph7_result_string(pCtx,"null",(int)sizeof("null")-1); } /* All done */ return PH7_OK; } /* * The following walker callback is invoked each time we need * to encode an array to JSON. */ static int VmJsonArrayEncode(ph7_value *pKey,ph7_value *pValue,void *pUserData) { json_private_data *pJson = (json_private_data *)pUserData; if( pJson->nRecCount > 31 ){ /* Recursion limit reached,return immediately */ return PH7_OK; } if( !pJson->isFirst ){ /* Append the colon first */ ph7_result_string(pJson->pCtx,",",(int)sizeof(char)); } if( pJson->iFlags & JSON_FORCE_OBJECT ){ /* Outputs an object rather than an array */ const char *zKey; int nByte; /* Extract a string representation of the key */ zKey = ph7_value_to_string(pKey,&nByte); /* Append the key and the double colon */ ph7_result_string_format(pJson->pCtx,"\"%.*s\":",nByte,zKey); } /* Encode the value */ pJson->nRecCount++; VmJsonEncode(pValue,pJson); pJson->nRecCount--; pJson->isFirst = 0; return PH7_OK; } /* * The following walker callback is invoked each time we need to encode * a class instance [i.e: Object in the PHP jargon] to JSON. */ static int VmJsonObjectEncode(const char *zAttr,ph7_value *pValue,void *pUserData) { json_private_data *pJson = (json_private_data *)pUserData; if( pJson->nRecCount > 31 ){ /* Recursion limit reached,return immediately */ return PH7_OK; } if( !pJson->isFirst ){ /* Append the colon first */ ph7_result_string(pJson->pCtx,",",(int)sizeof(char)); } /* Append the attribute name and the double colon first */ ph7_result_string_format(pJson->pCtx,"\"%s\":",zAttr); /* Encode the value */ pJson->nRecCount++; VmJsonEncode(pValue,pJson); pJson->nRecCount--; pJson->isFirst = 0; return PH7_OK; } /* * string json_encode(mixed $value [, int $options = 0 ]) * Returns a string containing the JSON representation of value. * Parameters * $value * The value being encoded. Can be any type except a resource. * $options * Bitmask consisting of: * JSON_HEX_TAG All < and > are converted to \u003C and \u003E. * JSON_HEX_AMP All &s are converted to \u0026. * JSON_HEX_APOS All ' are converted to \u0027. * JSON_HEX_QUOT All " are converted to \u0022. * JSON_FORCE_OBJECT Outputs an object rather than an array. * JSON_NUMERIC_CHECK Encodes numeric strings as numbers. * JSON_BIGINT_AS_STRING Not used * JSON_PRETTY_PRINT Use whitespace in returned data to format it. * JSON_UNESCAPED_SLASHES Don't escape '/' * JSON_UNESCAPED_UNICODE Not used. * Return * Returns a JSON encoded string on success. FALSE otherwise */ static int vm_builtin_json_encode(ph7_context *pCtx,int nArg,ph7_value **apArg) { json_private_data sJson; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Prepare the JSON data */ sJson.nRecCount = 0; sJson.pCtx = pCtx; sJson.isFirst = 1; sJson.iFlags = 0; if( nArg > 1 && ph7_value_is_int(apArg[1]) ){ /* Extract option flags */ sJson.iFlags = ph7_value_to_int(apArg[1]); } /* Perform the encoding operation */ VmJsonEncode(apArg[0],&sJson); /* All done */ return PH7_OK; } /* * int json_last_error(void) * Returns the last error (if any) occurred during the last JSON encoding/decoding. * Parameters * None * Return * Returns an integer, the value can be one of the following constants: * JSON_ERROR_NONE No error has occurred. * JSON_ERROR_DEPTH The maximum stack depth has been exceeded. * JSON_ERROR_STATE_MISMATCH Invalid or malformed JSON. * JSON_ERROR_CTRL_CHAR Control character error, possibly incorrectly encoded. * JSON_ERROR_SYNTAX Syntax error. * JSON_ERROR_UTF8_CHECK Malformed UTF-8 characters. */ static int vm_builtin_json_last_error(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; /* Return the error code */ ph7_result_int(pCtx,pVm->json_rc); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); return PH7_OK; } /* Possible tokens from the JSON tokenization process */ #define JSON_TK_TRUE 0x001 /* Boolean true */ #define JSON_TK_FALSE 0x002 /* Boolean false */ #define JSON_TK_STR 0x004 /* String enclosed in double quotes */ #define JSON_TK_NULL 0x008 /* null */ #define JSON_TK_NUM 0x010 /* Numeric */ #define JSON_TK_OCB 0x020 /* Open curly braces '{' */ #define JSON_TK_CCB 0x040 /* Closing curly braces '}' */ #define JSON_TK_OSB 0x080 /* Open square bracke '[' */ #define JSON_TK_CSB 0x100 /* Closing square bracket ']' */ #define JSON_TK_COLON 0x200 /* Single colon ':' */ #define JSON_TK_COMMA 0x400 /* Single comma ',' */ #define JSON_TK_INVALID 0x800 /* Unexpected token */ /* * Tokenize an entire JSON input. * Get a single low-level token from the input file. * Update the stream pointer so that it points to the first * character beyond the extracted token. */ static sxi32 VmJsonTokenize(SyStream *pStream,SyToken *pToken,void *pUserData,void *pCtxData) { int *pJsonErr = (int *)pUserData; SyString *pStr; int c; /* Ignore leading white spaces */ while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisSpace(pStream->zText[0]) ){ /* Advance the stream cursor */ if( pStream->zText[0] == '\n' ){ /* Update line counter */ pStream->nLine++; } pStream->zText++; } if( pStream->zText >= pStream->zEnd ){ /* End of input reached */ SXUNUSED(pCtxData); /* cc warning */ return SXERR_EOF; } /* Record token starting position and line */ pToken->nLine = pStream->nLine; pToken->pUserData = 0; pStr = &pToken->sData; SyStringInitFromBuf(pStr,pStream->zText,0); if( pStream->zText[0] == '{' || pStream->zText[0] == '[' || pStream->zText[0] == '}' || pStream->zText[0] == ']' || pStream->zText[0] == ':' || pStream->zText[0] == ',' ){ /* Single character */ c = pStream->zText[0]; /* Set token type */ switch(c){ case '[': pToken->nType = JSON_TK_OSB; break; case '{': pToken->nType = JSON_TK_OCB; break; case '}': pToken->nType = JSON_TK_CCB; break; case ']': pToken->nType = JSON_TK_CSB; break; case ':': pToken->nType = JSON_TK_COLON; break; case ',': pToken->nType = JSON_TK_COMMA; break; default: break; } /* Advance the stream cursor */ pStream->zText++; }else if( pStream->zText[0] == '"') { /* JSON string */ pStream->zText++; pStr->zString++; /* Delimit the string */ while( pStream->zText < pStream->zEnd ){ if( pStream->zText[0] == '"' && pStream->zText[-1] != '\\' ){ break; } if( pStream->zText[0] == '\n' ){ /* Update line counter */ pStream->nLine++; } pStream->zText++; } if( pStream->zText >= pStream->zEnd ){ /* Missing closing '"' */ pToken->nType = JSON_TK_INVALID; *pJsonErr = JSON_ERROR_SYNTAX; }else{ pToken->nType = JSON_TK_STR; pStream->zText++; /* Jump the closing double quotes */ } }else if( pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ /* Number */ pStream->zText++; pToken->nType = JSON_TK_NUM; while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ pStream->zText++; } if( pStream->zText < pStream->zEnd ){ c = pStream->zText[0]; if( c == '.' ){ /* Real number */ pStream->zText++; while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ pStream->zText++; } if( pStream->zText < pStream->zEnd ){ c = pStream->zText[0]; if( c=='e' || c=='E' ){ pStream->zText++; if( pStream->zText < pStream->zEnd ){ c = pStream->zText[0]; if( c =='+' || c=='-' ){ pStream->zText++; } while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ pStream->zText++; } } } } }else if( c=='e' || c=='E' ){ /* Real number */ pStream->zText++; if( pStream->zText < pStream->zEnd ){ c = pStream->zText[0]; if( c =='+' || c=='-' ){ pStream->zText++; } while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ pStream->zText++; } } } } }else if( XLEX_IN_LEN(pStream) >= sizeof("true") -1 && SyStrnicmp((const char *)pStream->zText,"true",sizeof("true")-1) == 0 ){ /* boolean true */ pToken->nType = JSON_TK_TRUE; /* Advance the stream cursor */ pStream->zText += sizeof("true")-1; }else if( XLEX_IN_LEN(pStream) >= sizeof("false") -1 && SyStrnicmp((const char *)pStream->zText,"false",sizeof("false")-1) == 0 ){ /* boolean false */ pToken->nType = JSON_TK_FALSE; /* Advance the stream cursor */ pStream->zText += sizeof("false")-1; }else if( XLEX_IN_LEN(pStream) >= sizeof("null") -1 && SyStrnicmp((const char *)pStream->zText,"null",sizeof("null")-1) == 0 ){ /* NULL */ pToken->nType = JSON_TK_NULL; /* Advance the stream cursor */ pStream->zText += sizeof("null")-1; }else{ /* Unexpected token */ pToken->nType = JSON_TK_INVALID; /* Advance the stream cursor */ pStream->zText++; *pJsonErr = JSON_ERROR_SYNTAX; /* Abort processing immediatley */ return SXERR_ABORT; } /* record token length */ pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); if( pToken->nType == JSON_TK_STR ){ pStr->nByte--; } /* Return to the lexer */ return SXRET_OK; } /* * JSON decoded input consumer callback signature. */ typedef int (*ProcJsonConsumer)(ph7_context *,ph7_value *,ph7_value *,void *); /* * JSON decoder state is kept in the following structure. */ typedef struct json_decoder json_decoder; struct json_decoder { ph7_context *pCtx; /* Call context */ ProcJsonConsumer xConsumer; /* Consumer callback */ void *pUserData; /* Last argument to xConsumer() */ int iFlags; /* Configuration flags */ SyToken *pIn; /* Token stream */ SyToken *pEnd; /* End of the token stream */ int rec_depth; /* Recursion limit */ int rec_count; /* Current nesting level */ int *pErr; /* JSON decoding error if any */ }; #define JSON_DECODE_ASSOC 0x01 /* Decode a JSON object as an associative array */ /* Forward declaration */ static int VmJsonArrayDecoder(ph7_context *pCtx,ph7_value *pKey,ph7_value *pWorker,void *pUserData); /* * Dequote [i.e: Resolve all backslash escapes ] a JSON string and store * the result in the given ph7_value. */ static void VmJsonDequoteString(const SyString *pStr,ph7_value *pWorker) { const char *zIn = pStr->zString; const char *zEnd = &pStr->zString[pStr->nByte]; const char *zCur; int c; /* Mark the value as a string */ ph7_value_string(pWorker,"",0); /* Empty string */ for(;;){ zCur = zIn; while( zIn < zEnd && zIn[0] != '\\' ){ zIn++; } if( zIn > zCur ){ /* Append chunk verbatim */ ph7_value_string(pWorker,zCur,(int)(zIn-zCur)); } zIn++; if( zIn >= zEnd ){ /* End of the input reached */ break; } c = zIn[0]; /* Unescape the character */ switch(c){ case '"': ph7_value_string(pWorker,(const char *)&c,(int)sizeof(char)); break; case '\\': ph7_value_string(pWorker,(const char *)&c,(int)sizeof(char)); break; case 'n': ph7_value_string(pWorker,"\n",(int)sizeof(char)); break; case 'r': ph7_value_string(pWorker,"\r",(int)sizeof(char)); break; case 't': ph7_value_string(pWorker,"\t",(int)sizeof(char)); break; case 'f': ph7_value_string(pWorker,"\f",(int)sizeof(char)); break; default: ph7_value_string(pWorker,(const char *)&c,(int)sizeof(char)); break; } /* Advance the stream cursor */ zIn++; } } /* * Returns a ph7_value holding the image of a JSON string. In other word perform a JSON decoding operation. * According to wikipedia * JSON's basic types are: * Number (double precision floating-point format in JavaScript, generally depends on implementation) * String (double-quoted Unicode, with backslash escaping) * Boolean (true or false) * Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values * do not need to be of the same type) * Object (an unordered collection of key:value pairs with the ':' character separating the key * and the value, comma-separated and enclosed in curly braces; the keys must be strings and should * be distinct from each other) * null (empty) * Non-significant white space may be added freely around the "structural characters" (i.e. the brackets "[{]}", colon ":" and comma ","). */ static sxi32 VmJsonDecode( json_decoder *pDecoder, /* JSON decoder */ ph7_value *pArrayKey /* Key for the decoded array */ ){ ph7_value *pWorker; /* Worker variable */ sxi32 rc; /* Check if we do not nest to much */ if( pDecoder->rec_count >= pDecoder->rec_depth ){ /* Nesting limit reached,abort decoding immediately */ *pDecoder->pErr = JSON_ERROR_DEPTH; return SXERR_ABORT; } if( pDecoder->pIn->nType & (JSON_TK_STR|JSON_TK_TRUE|JSON_TK_FALSE|JSON_TK_NULL|JSON_TK_NUM) ){ /* Scalar value */ pWorker = ph7_context_new_scalar(pDecoder->pCtx); if( pWorker == 0 ){ ph7_context_throw_error(pDecoder->pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); /* Abort the decoding operation immediately */ return SXERR_ABORT; } /* Reflect the JSON image */ if( pDecoder->pIn->nType & JSON_TK_NULL ){ /* Nullify the value.*/ ph7_value_null(pWorker); }else if( pDecoder->pIn->nType & (JSON_TK_TRUE|JSON_TK_FALSE) ){ /* Boolean value */ ph7_value_bool(pWorker,(pDecoder->pIn->nType & JSON_TK_TRUE) ? 1 : 0 ); }else if( pDecoder->pIn->nType & JSON_TK_NUM ){ SyString *pStr = &pDecoder->pIn->sData; /* * Numeric value. * Get a string representation first then try to get a numeric * value. */ ph7_value_string(pWorker,pStr->zString,(int)pStr->nByte); /* Obtain a numeric representation */ PH7_MemObjToNumeric(pWorker); }else{ /* Dequote the string */ VmJsonDequoteString(&pDecoder->pIn->sData,pWorker); } /* Invoke the consumer callback */ rc = pDecoder->xConsumer(pDecoder->pCtx,pArrayKey,pWorker,pDecoder->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } /* All done,advance the stream cursor */ pDecoder->pIn++; }else if( pDecoder->pIn->nType & JSON_TK_OSB /*'[' */) { ProcJsonConsumer xOld; void *pOld; /* Array representation*/ pDecoder->pIn++; /* Create a working array */ pWorker = ph7_context_new_array(pDecoder->pCtx); if( pWorker == 0 ){ ph7_context_throw_error(pDecoder->pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); /* Abort the decoding operation immediately */ return SXERR_ABORT; } /* Save the old consumer */ xOld = pDecoder->xConsumer; pOld = pDecoder->pUserData; /* Set the new consumer */ pDecoder->xConsumer = VmJsonArrayDecoder; pDecoder->pUserData = pWorker; /* Decode the array */ for(;;){ /* Jump trailing comma. Note that the standard PHP engine will not let you * do this. */ while( (pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA) ){ pDecoder->pIn++; } if( pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CSB) /*']'*/ ){ if( pDecoder->pIn < pDecoder->pEnd ){ pDecoder->pIn++; /* Jump the trailing ']' */ } break; } /* Recurse and decode the entry */ pDecoder->rec_count++; rc = VmJsonDecode(pDecoder,0); pDecoder->rec_count--; if( rc == SXERR_ABORT ){ /* Abort processing immediately */ return SXERR_ABORT; } /*The cursor is automatically advanced by the VmJsonDecode() function */ if( (pDecoder->pIn < pDecoder->pEnd) && ((pDecoder->pIn->nType & (JSON_TK_CSB/*']'*/|JSON_TK_COMMA/*','*/))==0) ){ /* Unexpected token,abort immediatley */ *pDecoder->pErr = JSON_ERROR_SYNTAX; return SXERR_ABORT; } } /* Restore the old consumer */ pDecoder->xConsumer = xOld; pDecoder->pUserData = pOld; /* Invoke the old consumer on the decoded array */ xOld(pDecoder->pCtx,pArrayKey,pWorker,pOld); }else if( pDecoder->pIn->nType & JSON_TK_OCB /*'{' */) { ProcJsonConsumer xOld; ph7_value *pKey; void *pOld; /* Object representation*/ pDecoder->pIn++; /* Return the object as an associative array */ if( (pDecoder->iFlags & JSON_DECODE_ASSOC) == 0 ){ ph7_context_throw_error(pDecoder->pCtx,PH7_CTX_WARNING, "JSON Objects are always returned as an associative array" ); } /* Create a working array */ pWorker = ph7_context_new_array(pDecoder->pCtx); pKey = ph7_context_new_scalar(pDecoder->pCtx); if( pWorker == 0 || pKey == 0){ ph7_context_throw_error(pDecoder->pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); /* Abort the decoding operation immediately */ return SXERR_ABORT; } /* Save the old consumer */ xOld = pDecoder->xConsumer; pOld = pDecoder->pUserData; /* Set the new consumer */ pDecoder->xConsumer = VmJsonArrayDecoder; pDecoder->pUserData = pWorker; /* Decode the object */ for(;;){ /* Jump trailing comma. Note that the standard PHP engine will not let you * do this. */ while( (pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA) ){ pDecoder->pIn++; } if( pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CCB) /*'}'*/ ){ if( pDecoder->pIn < pDecoder->pEnd ){ pDecoder->pIn++; /* Jump the trailing ']' */ } break; } if( (pDecoder->pIn->nType & JSON_TK_STR) == 0 || &pDecoder->pIn[1] >= pDecoder->pEnd || (pDecoder->pIn[1].nType & JSON_TK_COLON) == 0){ /* Syntax error,return immediately */ *pDecoder->pErr = JSON_ERROR_SYNTAX; return SXERR_ABORT; } /* Dequote the key */ VmJsonDequoteString(&pDecoder->pIn->sData,pKey); /* Jump the key and the colon */ pDecoder->pIn += 2; /* Recurse and decode the value */ pDecoder->rec_count++; rc = VmJsonDecode(pDecoder,pKey); pDecoder->rec_count--; if( rc == SXERR_ABORT ){ /* Abort processing immediately */ return SXERR_ABORT; } /* Reset the internal buffer of the key */ ph7_value_reset_string_cursor(pKey); /*The cursor is automatically advanced by the VmJsonDecode() function */ } /* Restore the old consumer */ pDecoder->xConsumer = xOld; pDecoder->pUserData = pOld; /* Invoke the old consumer on the decoded object*/ xOld(pDecoder->pCtx,pArrayKey,pWorker,pOld); /* Release the key */ ph7_context_release_value(pDecoder->pCtx,pKey); }else{ /* Unexpected token */ return SXERR_ABORT; /* Abort immediately */ } /* Release the worker variable */ ph7_context_release_value(pDecoder->pCtx,pWorker); return SXRET_OK; } /* * The following JSON decoder callback is invoked each time * a JSON array representation [i.e: [15,"hello",FALSE] ] * is being decoded. */ static int VmJsonArrayDecoder(ph7_context *pCtx,ph7_value *pKey,ph7_value *pWorker,void *pUserData) { ph7_value *pArray = (ph7_value *)pUserData; /* Insert the entry */ ph7_array_add_elem(pArray,pKey,pWorker); /* Will make it's own copy */ SXUNUSED(pCtx); /* cc warning */ /* All done */ return SXRET_OK; } /* * Standard JSON decoder callback. */ static int VmJsonDefaultDecoder(ph7_context *pCtx,ph7_value *pKey,ph7_value *pWorker,void *pUserData) { /* Return the value directly */ ph7_result_value(pCtx,pWorker); /* Will make it's own copy */ SXUNUSED(pKey); /* cc warning */ SXUNUSED(pUserData); /* All done */ return SXRET_OK; } /* * mixed json_decode(string $json[,bool $assoc = false[,int $depth = 32[,int $options = 0 ]]]) * Takes a JSON encoded string and converts it into a PHP variable. * Parameters * $json * The json string being decoded. * $assoc * When TRUE, returned objects will be converted into associative arrays. * $depth * User specified recursion depth. * $options * Bitmask of JSON decode options. Currently only JSON_BIGINT_AS_STRING is supported * (default is to cast large integers as floats) * Return * The value encoded in json in appropriate PHP type. Values true, false and null (case-insensitive) * are returned as TRUE, FALSE and NULL respectively. NULL is returned if the json cannot be decoded * or if the encoded data is deeper than the recursion limit. */ static int vm_builtin_json_decode(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; json_decoder sDecoder; const char *zIn; SySet sToken; SyLex sLex; int nByte; sxi32 rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments, return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the JSON string */ zIn = ph7_value_to_string(apArg[0],&nByte); if( nByte < 1 ){ /* Empty string,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Clear JSON error code */ pVm->json_rc = JSON_ERROR_NONE; /* Tokenize the input */ SySetInit(&sToken,&pVm->sAllocator,sizeof(SyToken)); SyLexInit(&sLex,&sToken,VmJsonTokenize,&pVm->json_rc); SyLexTokenizeInput(&sLex,zIn,(sxu32)nByte,0,0,0); if( pVm->json_rc != JSON_ERROR_NONE ){ /* Something goes wrong while tokenizing input. [i.e: Unexpected token] */ SyLexRelease(&sLex); SySetRelease(&sToken); /* return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Fill the decoder */ sDecoder.pCtx = pCtx; sDecoder.pErr = &pVm->json_rc; sDecoder.pIn = (SyToken *)SySetBasePtr(&sToken); sDecoder.pEnd = &sDecoder.pIn[SySetUsed(&sToken)]; sDecoder.iFlags = 0; if( nArg > 1 && ph7_value_to_bool(apArg[1]) != 0 ){ /* Returned objects will be converted into associative arrays */ sDecoder.iFlags |= JSON_DECODE_ASSOC; } sDecoder.rec_depth = 32; if( nArg > 2 && ph7_value_is_int(apArg[2]) ){ int nDepth = ph7_value_to_int(apArg[2]); if( nDepth > 1 && nDepth < 32 ){ sDecoder.rec_depth = nDepth; } } sDecoder.rec_count = 0; /* Set a default consumer */ sDecoder.xConsumer = VmJsonDefaultDecoder; sDecoder.pUserData = 0; /* Decode the raw JSON input */ rc = VmJsonDecode(&sDecoder,0); if( rc == SXERR_ABORT || pVm->json_rc != JSON_ERROR_NONE ){ /* * Something goes wrong while decoding JSON input.Return NULL. */ ph7_result_null(pCtx); } /* Clean-up the mess left behind */ SyLexRelease(&sLex); SySetRelease(&sToken); /* All done */ return PH7_OK; } #ifndef PH7_DISABLE_BUILTIN_FUNC /* * XML processing Functions. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Devel. */ enum ph7_xml_handler_id{ PH7_XML_START_TAG = 0, /* Start element handlers ID */ PH7_XML_END_TAG, /* End element handler ID*/ PH7_XML_CDATA, /* Character data handler ID*/ PH7_XML_PI, /* Processing instruction (PI) handler ID*/ PH7_XML_DEF, /* Default handler ID */ PH7_XML_UNPED, /* Unparsed entity declaration handler */ PH7_XML_ND, /* Notation declaration handler ID*/ PH7_XML_EER, /* External entity reference handler */ PH7_XML_NS_START, /* Start namespace declaration handler */ PH7_XML_NS_END /* End namespace declaration handler */ }; #define XML_TOTAL_HANDLER (PH7_XML_NS_END + 1) /* An instance of the following structure describe a working * XML engine instance. */ typedef struct ph7_xml_engine ph7_xml_engine; struct ph7_xml_engine { ph7_vm *pVm; /* VM that own this instance */ ph7_context *pCtx; /* Call context */ SyXMLParser sParser; /* Underlying XML parser */ ph7_value aCB[XML_TOTAL_HANDLER]; /* User-defined callbacks */ ph7_value sParserValue; /* ph7_value holding this instance which is forwarded * as the first argument to the user callbacks. */ int ns_sep; /* Namespace separator */ SyBlob sErr; /* Error message consumer */ sxi32 iErrCode; /* Last error code */ sxi32 iNest; /* Nesting level */ sxu32 nLine; /* Last processed line */ sxu32 nMagic; /* Magic number so that we avoid misuse */ }; #define XML_ENGINE_MAGIC 0x851EFC52 #define IS_INVALID_XML_ENGINE(XML) (XML == 0 || (XML)->nMagic != XML_ENGINE_MAGIC) /* * Allocate and initialize an XML engine. */ static ph7_xml_engine * VmCreateXMLEngine(ph7_context *pCtx,int process_ns,int ns_sep) { ph7_xml_engine *pEngine; ph7_vm *pVm = pCtx->pVm; ph7_value *pValue; sxu32 n; /* Allocate a new instance */ pEngine = (ph7_xml_engine *)SyMemBackendAlloc(&pVm->sAllocator,sizeof(ph7_xml_engine)); if( pEngine == 0 ){ /* Out of memory */ return 0; } /* Zero the structure */ SyZero(pEngine,sizeof(ph7_xml_engine)); /* Initialize fields */ pEngine->pVm = pVm; pEngine->pCtx = 0; pEngine->ns_sep = ns_sep; SyXMLParserInit(&pEngine->sParser,&pVm->sAllocator,process_ns ? SXML_ENABLE_NAMESPACE : 0); SyBlobInit(&pEngine->sErr,&pVm->sAllocator); PH7_MemObjInit(pVm,&pEngine->sParserValue); for( n = 0 ; n < SX_ARRAYSIZE(pEngine->aCB) ; ++n ){ pValue = &pEngine->aCB[n]; /* NULLIFY the array entries,until someone register an event handler */ PH7_MemObjInit(&(*pVm),pValue); } ph7_value_resource(&pEngine->sParserValue,pEngine); pEngine->iErrCode = SXML_ERROR_NONE; /* Finally set the magic number */ pEngine->nMagic = XML_ENGINE_MAGIC; return pEngine; } /* * Release an XML engine. */ static void VmReleaseXMLEngine(ph7_xml_engine *pEngine) { ph7_vm *pVm = pEngine->pVm; ph7_value *pValue; sxu32 n; /* Release fields */ SyBlobRelease(&pEngine->sErr); SyXMLParserRelease(&pEngine->sParser); PH7_MemObjRelease(&pEngine->sParserValue); for( n = 0 ; n < SX_ARRAYSIZE(pEngine->aCB) ; ++n ){ pValue = &pEngine->aCB[n]; PH7_MemObjRelease(pValue); } pEngine->nMagic = 0x2621; /* Finally,release the whole instance */ SyMemBackendFree(&pVm->sAllocator,pEngine); } /* * resource xml_parser_create([ string $encoding ]) * Create an UTF-8 XML parser. * Parameter * $encoding * (Only UTF-8 encoding is used) * Return * Returns a resource handle for the new XML parser. */ static int vm_builtin_xml_parser_create(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; /* Allocate a new instance */ pEngine = VmCreateXMLEngine(&(*pCtx),0,':'); if( pEngine == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); /* Return null */ ph7_result_null(pCtx); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); return PH7_OK; } /* Return the engine as a resource */ ph7_result_resource(pCtx,pEngine); return PH7_OK; } /* * resource xml_parser_create_ns([ string $encoding[,string $separator = ':']]) * Create an UTF-8 XML parser with namespace support. * Parameter * $encoding * (Only UTF-8 encoding is supported) * $separtor * Namespace separator (a single character) * Return * Returns a resource handle for the new XML parser. */ static int vm_builtin_xml_parser_create_ns(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; int ns_sep = ':'; if( nArg > 1 && ph7_value_is_string(apArg[1]) ){ const char *zSep = ph7_value_to_string(apArg[1],0); if( zSep[0] != 0 ){ ns_sep = zSep[0]; } } /* Allocate a new instance */ pEngine = VmCreateXMLEngine(&(*pCtx),TRUE,ns_sep); if( pEngine == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); /* Return null */ ph7_result_null(pCtx); return PH7_OK; } /* Return the engine as a resource */ ph7_result_resource(pCtx,pEngine); return PH7_OK; } /* * bool xml_parser_free(resource $parser) * Release an XML engine. * Parameter * $parser * A reference to the XML parser to free. * Return * This function returns FALSE if parser does not refer * to a valid parser, or else it frees the parser and returns TRUE. */ static int vm_builtin_xml_parser_free(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Safely release the engine */ VmReleaseXMLEngine(pEngine); /* Return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool xml_set_element_handler(resource $parser,callback $start_element_handler,[callback $end_element_handler]) * Sets the element handler functions for the XML parser. start_element_handler and end_element_handler * are strings containing the names of functions. * Parameters * $parser * A reference to the XML parser to set up start and end element handler functions. * $start_element_handler * The function named by start_element_handler must accept three parameters: * start_element_handler(resource $parser,string $name,array $attribs) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $name * The second parameter, name, contains the name of the element for which this handler * is called.If case-folding is in effect for this parser, the element name will be in uppercase letters. * $attribs * The third parameter, attribs, contains an associative array with the element's attributes (if any). * The keys of this array are the attribute names, the values are the attribute values. * Attribute names are case-folded on the same criteria as element names.Attribute values are not case-folded. * The original order of the attributes can be retrieved by walking through attribs the normal way, using each(). * The first key in the array was the first attribute, and so on. * Note: Instead of a function name, an array containing an object reference and a method name can also be supplied. * $end_element_handler * The function named by end_element_handler must accept two parameters: * end_element_handler(resource $parser,string $name) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $name * The second parameter, name, contains the name of the element for which this handler * is called.If case-folding is in effect for this parser, the element name will be in uppercase * letters. * If a handler function is set to an empty string, or FALSE, the handler in question is disabled. * Return * TRUE on success or FALSE on failure. */ static int vm_builtin_xml_set_element_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ /* Save the start_element_handler callback for later invocation */ PH7_MemObjStore(apArg[1]/* User callback*/,&pEngine->aCB[PH7_XML_START_TAG]); if( nArg > 2 ){ /* Save the end_element_handler callback for later invocation */ PH7_MemObjStore(apArg[2]/* User callback*/,&pEngine->aCB[PH7_XML_END_TAG]); } } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool xml_set_character_data_handler(resource $parser,callback $handler) * Sets the character data handler function for the XML parser parser. * Parameters * $parser * A reference to the XML parser to set up character data handler function. * $handler * handler is a string containing the name of the callback. * The function named by handler must accept two parameters: * handler(resource $parser,string $data) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $data * The second parameter, data, contains the character data as a string. * Character data handler is called for every piece of a text in the XML document. * It can be called multiple times inside each fragment (e.g. for non-ASCII strings). * If a handler function is set to an empty string, or FALSE, the handler in question is disabled. * Note: Instead of a function name, an array containing an object reference and a method name * can also be supplied. * Return * TRUE on success or FALSE on failure. */ static int vm_builtin_xml_set_character_data_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ /* Save the user callback for later invocation */ PH7_MemObjStore(apArg[1]/* User callback*/,&pEngine->aCB[PH7_XML_CDATA]); } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool xml_set_default_handler(resource $parser,callback $handler) * Set up default handler. * Parameters * $parser * A reference to the XML parser to set up character data handler function. * $handler * handler is a string containing the name of the callback. * The function named by handler must accept two parameters: * handler(resource $parser,string $data) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $data * The second parameter, data, contains the character data.This may be the XML declaration * document type declaration, entities or other data for which no other handler exists. * Note: Instead of a function name, an array containing an object reference and a method name * can also be supplied. * Return * TRUE on success or FALSE on failure. */ static int vm_builtin_xml_set_default_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ /* Save the user callback for later invocation */ PH7_MemObjStore(apArg[1]/* User callback*/,&pEngine->aCB[PH7_XML_DEF]); } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool xml_set_end_namespace_decl_handler(resource $parser,callback $handler) * Set up end namespace declaration handler. * Parameters * $parser * A reference to the XML parser to set up character data handler function. * $handler * handler is a string containing the name of the callback. * The function named by handler must accept two parameters: * handler(resource $parser,string $prefix) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $prefix * The prefix is a string used to reference the namespace within an XML object. * Note: Instead of a function name, an array containing an object reference and a method name * can also be supplied. * Return * TRUE on success or FALSE on failure. */ static int vm_builtin_xml_set_end_namespace_decl_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ /* Save the user callback for later invocation */ PH7_MemObjStore(apArg[1]/* User callback*/,&pEngine->aCB[PH7_XML_NS_END]); } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool xml_set_start_namespace_decl_handler(resource $parser,callback $handler) * Set up start namespace declaration handler. * Parameters * $parser * A reference to the XML parser to set up character data handler function. * $handler * handler is a string containing the name of the callback. * The function named by handler must accept two parameters: * handler(resource $parser,string $prefix,string $uri) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $prefix * The prefix is a string used to reference the namespace within an XML object. * $uri * Uniform Resource Identifier (URI) of namespace. * Note: Instead of a function name, an array containing an object reference and a method name * can also be supplied. * Return * TRUE on success or FALSE on failure. */ static int vm_builtin_xml_set_start_namespace_decl_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ /* Save the user callback for later invocation */ PH7_MemObjStore(apArg[1]/* User callback*/,&pEngine->aCB[PH7_XML_NS_START]); } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool xml_set_processing_instruction_handler(resource $parser,callback $handler) * Set up processing instruction (PI) handler. * Parameters * $parser * A reference to the XML parser to set up character data handler function. * $handler * handler is a string containing the name of the callback. * The function named by handler must accept three parameters: * handler(resource $parser,string $target,string $data) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $target * The second parameter, target, contains the PI target. * $data The third parameter, data, contains the PI data. * Note: Instead of a function name, an array containing an object reference and a method name * can also be supplied. * Return * TRUE on success or FALSE on failure. */ static int vm_builtin_xml_set_processing_instruction_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ /* Save the user callback for later invocation */ PH7_MemObjStore(apArg[1]/* User callback*/,&pEngine->aCB[PH7_XML_PI]); } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool xml_set_unparsed_entity_decl_handler(resource $parser,callback $handler) * Set up unparsed entity declaration handler. * Parameters * $parser * A reference to the XML parser to set up character data handler function. * $handler * handler is a string containing the name of the callback. * The function named by handler must accept six parameters: * handler(resource $parser,string $entity_name,string $base,string $system_id,string $public_id,string $notation_name) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $entity_name * The name of the entity that is about to be defined. * $base * This is the base for resolving the system identifier (systemId) of the external entity. * Currently this parameter will always be set to an empty string. * $system_id * System identifier for the external entity. * $public_id * Public identifier for the external entity. * $notation_name * Name of the notation of this entity (see xml_set_notation_decl_handler()). * Note: Instead of a function name, an array containing an object reference and a method name * can also be supplied. * Return * TRUE on success or FALSE on failure. */ static int vm_builtin_xml_set_unparsed_entity_decl_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ /* Save the user callback for later invocation */ PH7_MemObjStore(apArg[1]/* User callback*/,&pEngine->aCB[PH7_XML_UNPED]); } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool xml_set_notation_decl_handler(resource $parser,callback $handler) * Set up notation declaration handler. * Parameters * $parser * A reference to the XML parser to set up character data handler function. * $handler * handler is a string containing the name of the callback. * The function named by handler must accept five parameters: * handler(resource $parser,string $entity_name,string $base,string $system_id,string $public_id) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $entity_name * The name of the entity that is about to be defined. * $base * This is the base for resolving the system identifier (systemId) of the external entity. * Currently this parameter will always be set to an empty string. * $system_id * System identifier for the external entity. * $public_id * Public identifier for the external entity. * Note: Instead of a function name, an array containing an object reference and a method name * can also be supplied. * Return * TRUE on success or FALSE on failure. */ static int vm_builtin_xml_set_notation_decl_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ /* Save the user callback for later invocation */ PH7_MemObjStore(apArg[1]/* User callback*/,&pEngine->aCB[PH7_XML_ND]); } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool xml_set_external_entity_ref_handler(resource $parser,callback $handler) * Set up external entity reference handler. * Parameters * $parser * A reference to the XML parser to set up character data handler function. * $handler * handler is a string containing the name of the callback. * The function named by handler must accept five parameters: * handler(resource $parser,string $open_entity_names,string $base,string $system_id,string $public_id) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $open_entity_names * The second parameter, open_entity_names, is a space-separated list of the names * of the entities that are open for the parse of this entity (including the name of the referenced entity). * $base * This is the base for resolving the system identifier (system_id) of the external entity. * Currently this parameter will always be set to an empty string. * $system_id * The fourth parameter, system_id, is the system identifier as specified in the entity declaration. * $public_id * The fifth parameter, public_id, is the public identifier as specified in the entity declaration * or an empty string if none was specified; the whitespace in the public identifier will have been * normalized as required by the XML spec. * Note: Instead of a function name, an array containing an object reference and a method name * can also be supplied. * Return * TRUE on success or FALSE on failure. */ static int vm_builtin_xml_set_external_entity_ref_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ /* Save the user callback for later invocation */ PH7_MemObjStore(apArg[1]/* User callback*/,&pEngine->aCB[PH7_XML_EER]); } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * int xml_get_current_line_number(resource $parser) * Gets the current line number for the given XML parser. * Parameters * $parser * A reference to the XML parser. * Return * This function returns FALSE if parser does not refer * to a valid parser, or else it returns which line the parser * is currently at in its data buffer. */ static int vm_builtin_xml_get_current_line_number(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Return the line number */ ph7_result_int(pCtx,(int)pEngine->nLine); return PH7_OK; } /* * int xml_get_current_byte_index(resource $parser) * Gets the current byte index of the given XML parser. * Parameters * $parser * A reference to the XML parser. * Return * This function returns FALSE if parser does not refer to a valid * parser, or else it returns which byte index the parser is currently * at in its data buffer (starting at 0). */ static int vm_builtin_xml_get_current_byte_index(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; SyStream *pStream; SyToken *pToken; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the current processed token */ pToken = (SyToken *)SySetPeekCurrentEntry(&pEngine->sParser.sToken); if( pToken == 0 ){ /* Stream not yet processed */ ph7_result_int(pCtx,0); return 0; } /* Point to the input stream */ pStream = &pEngine->sParser.sLex.sStream; /* Return the byte index */ ph7_result_int64(pCtx,(ph7_int64)(pToken->sData.zString-(const char *)pStream->zInput)); return PH7_OK; } /* * bool xml_set_object(resource $parser,object &$object) * Use XML Parser within an object. * NOTE * This function is depreceated and is a no-op. * Parameters * $parser * A reference to the XML parser. * $object * The object where to use the XML parser. * Return * Always FALSE. */ static int vm_builtin_xml_set_object(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 2 || !ph7_value_is_resource(apArg[0]) || !ph7_value_is_object(apArg[1]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Throw a notice and return */ ph7_context_throw_error(pCtx,PH7_CTX_NOTICE,"This function is depreceated and is a no-op." "In order to mimic this behaviour,you can supply instead of a function name an array " "containing an object reference and a method name." ); /* Return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* * int xml_get_current_column_number(resource $parser) * Gets the current column number of the given XML parser. * Parameters * $parser * A reference to the XML parser. * Return * This function returns FALSE if parser does not refer to a valid parser, or else it returns * which column on the current line (as given by xml_get_current_line_number()) the parser * is currently at. */ static int vm_builtin_xml_get_current_column_number(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; SyStream *pStream; SyToken *pToken; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the current processed token */ pToken = (SyToken *)SySetPeekCurrentEntry(&pEngine->sParser.sToken); if( pToken == 0 ){ /* Stream not yet processed */ ph7_result_int(pCtx,0); return 0; } /* Point to the input stream */ pStream = &pEngine->sParser.sLex.sStream; /* Return the byte index */ ph7_result_int64(pCtx,(ph7_int64)(pToken->sData.zString-(const char *)pStream->zInput)/80); return PH7_OK; } /* * int xml_get_error_code(resource $parser) * Get XML parser error code. * Parameters * $parser * A reference to the XML parser. * Return * This function returns FALSE if parser does not refer to a valid * parser, or else it returns one of the error codes listed in the error * codes section. */ static int vm_builtin_xml_get_error_code(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Return the error code if any */ ph7_result_int(pCtx,pEngine->iErrCode); return PH7_OK; } /* * XML parser event callbacks * Each time the unserlying XML parser extract a single token * from the input,one of the following callbacks are invoked. * IMP-XML-ENGINE-07-07-2012 22:02 FreeBSD [chm@symisc.net] */ /* * Create a scalar ph7_value holding the value * of an XML tag/attribute/CDATA and so on. */ static ph7_value * VmXMLValue(ph7_xml_engine *pEngine,SyXMLRawStr *pXML,SyXMLRawStr *pNsUri) { ph7_value *pValue; /* Allocate a new scalar variable */ pValue = ph7_context_new_scalar(pEngine->pCtx); if( pValue == 0 ){ ph7_context_throw_error(pEngine->pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); return 0; } if( pNsUri && pNsUri->nByte > 0 ){ /* Append namespace URI and the separator */ ph7_value_string_format(pValue,"%.*s%c",pNsUri->nByte,pNsUri->zString,pEngine->ns_sep); } /* Copy the tag value */ ph7_value_string(pValue,pXML->zString,(int)pXML->nByte); return pValue; } /* * Create a 'ph7_value' of type array holding the values * of an XML tag attributes. */ static ph7_value * VmXMLAttrValue(ph7_xml_engine *pEngine,SyXMLRawStr *aAttr,sxu32 nAttr) { ph7_value *pArray; /* Create an empty array */ pArray = ph7_context_new_array(pEngine->pCtx); if( pArray == 0 ){ ph7_context_throw_error(pEngine->pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); return 0; } if( nAttr > 0 ){ ph7_value *pKey,*pValue; sxu32 n; /* Create worker variables */ pKey = ph7_context_new_scalar(pEngine->pCtx); pValue = ph7_context_new_scalar(pEngine->pCtx); if( pKey == 0 || pValue == 0 ){ ph7_context_throw_error(pEngine->pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); return 0; } /* Copy attributes */ for( n = 0 ; n < nAttr ; n += 2 ){ /* Reset string cursors */ ph7_value_reset_string_cursor(pKey); ph7_value_reset_string_cursor(pValue); /* Copy attribute name and it's associated value */ ph7_value_string(pKey,aAttr[n].zString,(int)aAttr[n].nByte); /* Attribute name */ ph7_value_string(pValue,aAttr[n+1].zString,(int)aAttr[n+1].nByte); /* Attribute value */ /* Insert in the array */ ph7_array_add_elem(pArray,pKey,pValue); /* Will make it's own copy */ } /* Release the worker variables */ ph7_context_release_value(pEngine->pCtx,pKey); ph7_context_release_value(pEngine->pCtx,pValue); } /* Return the freshly created array */ return pArray; } /* * Start element handler. * The user defined callback must accept three parameters: * start_element_handler(resource $parser,string $name,array $attribs ) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $name * The second parameter, name, contains the name of the element for which this handler * is called.If case-folding is in effect for this parser, the element name will be in uppercase letters. * $attribs * The third parameter, attribs, contains an associative array with the element's attributes (if any). * The keys of this array are the attribute names, the values are the attribute values. * Attribute names are case-folded on the same criteria as element names.Attribute values are not case-folded. * The original order of the attributes can be retrieved by walking through attribs the normal way, using each(). * The first key in the array was the first attribute, and so on. * Note: Instead of a function name, an array containing an object reference and a method name can also be supplied. */ static sxi32 VmXMLStartElementHandler(SyXMLRawStr *pStart,SyXMLRawStr *pNS,sxu32 nAttr,SyXMLRawStr *aAttr,void *pUserData) { ph7_xml_engine *pEngine = (ph7_xml_engine *)pUserData; ph7_value *pCallback,*pTag,*pAttr; /* Point to the target user defined callback */ pCallback = &pEngine->aCB[PH7_XML_START_TAG]; /* Make sure the given callback is callable */ if( !PH7_VmIsCallable(pEngine->pVm,pCallback,0) ){ /* Not callable,return immediately*/ return SXRET_OK; } /* Create a ph7_value holding the tag name */ pTag = VmXMLValue(pEngine,pStart,pNS); /* Create a ph7_value holding the tag attributes */ pAttr = VmXMLAttrValue(pEngine,aAttr,nAttr); if( pTag == 0 || pAttr == 0 ){ SXUNUSED(pNS); /* cc warning */ /* Out of mem,return immediately */ return SXRET_OK; } /* Invoke the user callback */ PH7_VmCallUserFunctionAp(pEngine->pVm,pCallback,0,&pEngine->sParserValue,pTag,pAttr,0); /* Clean-up the mess left behind */ ph7_context_release_value(pEngine->pCtx,pTag); ph7_context_release_value(pEngine->pCtx,pAttr); return SXRET_OK; } /* * End element handler. * The user defined callback must accept two parameters: * end_element_handler(resource $parser,string $name) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $name * The second parameter, name, contains the name of the element for which this handler is called. * If case-folding is in effect for this parser, the element name will be in uppercase letters. * Note: Instead of a function name, an array containing an object reference and a method name * can also be supplied. */ static sxi32 VmXMLEndElementHandler(SyXMLRawStr *pEnd,SyXMLRawStr *pNS,void *pUserData) { ph7_xml_engine *pEngine = (ph7_xml_engine *)pUserData; ph7_value *pCallback,*pTag; /* Point to the target user defined callback */ pCallback = &pEngine->aCB[PH7_XML_END_TAG]; /* Make sure the given callback is callable */ if( !PH7_VmIsCallable(pEngine->pVm,pCallback,0) ){ /* Not callable,return immediately*/ return SXRET_OK; } /* Create a ph7_value holding the tag name */ pTag = VmXMLValue(pEngine,pEnd,pNS); if( pTag == 0 ){ SXUNUSED(pNS); /* cc warning */ /* Out of mem,return immediately */ return SXRET_OK; } /* Invoke the user callback */ PH7_VmCallUserFunctionAp(pEngine->pVm,pCallback,0,&pEngine->sParserValue,pTag,0); /* Clean-up the mess left behind */ ph7_context_release_value(pEngine->pCtx,pTag); return SXRET_OK; } /* * Character data handler. * The user defined callback must accept two parameters: * handler(resource $parser,string $data) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $data * The second parameter, data, contains the character data as a string. * Character data handler is called for every piece of a text in the XML document. * It can be called multiple times inside each fragment (e.g. for non-ASCII strings). * If a handler function is set to an empty string, or FALSE, the handler in question is disabled. * Note: Instead of a function name, an array containing an object reference and a method name can also be supplied. */ static sxi32 VmXMLTextHandler(SyXMLRawStr *pText,void *pUserData) { ph7_xml_engine *pEngine = (ph7_xml_engine *)pUserData; ph7_value *pCallback,*pData; /* Point to the target user defined callback */ pCallback = &pEngine->aCB[PH7_XML_CDATA]; /* Make sure the given callback is callable */ if( !PH7_VmIsCallable(pEngine->pVm,pCallback,0) ){ /* Not callable,return immediately*/ return SXRET_OK; } /* Create a ph7_value holding the data */ pData = VmXMLValue(pEngine,&(*pText),0); if( pData == 0 ){ /* Out of mem,return immediately */ return SXRET_OK; } /* Invoke the user callback */ PH7_VmCallUserFunctionAp(pEngine->pVm,pCallback,0,&pEngine->sParserValue,pData,0); /* Clean-up the mess left behind */ ph7_context_release_value(pEngine->pCtx,pData); return SXRET_OK; } /* * Processing instruction (PI) handler. * The user defined callback must accept two parameters: * handler(resource $parser,string $target,string $data) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $target * The second parameter, target, contains the PI target. * $data * The third parameter, data, contains the PI data. * Note: Instead of a function name, an array containing an object reference * and a method name can also be supplied. */ static sxi32 VmXMLPIHandler(SyXMLRawStr *pTargetStr,SyXMLRawStr *pDataStr,void *pUserData) { ph7_xml_engine *pEngine = (ph7_xml_engine *)pUserData; ph7_value *pCallback,*pTarget,*pData; /* Point to the target user defined callback */ pCallback = &pEngine->aCB[PH7_XML_PI]; /* Make sure the given callback is callable */ if( !PH7_VmIsCallable(pEngine->pVm,pCallback,0) ){ /* Not callable,return immediately*/ return SXRET_OK; } /* Get a ph7_value holding the data */ pTarget = VmXMLValue(pEngine,&(*pTargetStr),0); pData = VmXMLValue(pEngine,&(*pDataStr),0); if( pTarget == 0 || pData == 0 ){ /* Out of mem,return immediately */ return SXRET_OK; } /* Invoke the user callback */ PH7_VmCallUserFunctionAp(pEngine->pVm,pCallback,0,&pEngine->sParserValue,pTarget,pData,0); /* Clean-up the mess left behind */ ph7_context_release_value(pEngine->pCtx,pTarget); ph7_context_release_value(pEngine->pCtx,pData); return SXRET_OK; } /* * Namespace declaration handler. * The user defined callback must accept two parameters: * handler(resource $parser,string $prefix,string $uri) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $prefix * The prefix is a string used to reference the namespace within an XML object. * $uri * Uniform Resource Identifier (URI) of namespace. * Note: Instead of a function name, an array containing an object reference * and a method name can also be supplied. */ static sxi32 VmXMLNSStartHandler(SyXMLRawStr *pUriStr,SyXMLRawStr *pPrefixStr,void *pUserData) { ph7_xml_engine *pEngine = (ph7_xml_engine *)pUserData; ph7_value *pCallback,*pUri,*pPrefix; /* Point to the target user defined callback */ pCallback = &pEngine->aCB[PH7_XML_NS_START]; /* Make sure the given callback is callable */ if( !PH7_VmIsCallable(pEngine->pVm,pCallback,0) ){ /* Not callable,return immediately*/ return SXRET_OK; } /* Get a ph7_value holding the PREFIX/URI */ pUri = VmXMLValue(pEngine,pUriStr,0); pPrefix = VmXMLValue(pEngine,pPrefixStr,0); if( pUri == 0 || pPrefix == 0 ){ /* Out of mem,return immediately */ return SXRET_OK; } /* Invoke the user callback */ PH7_VmCallUserFunctionAp(pEngine->pVm,pCallback,0,&pEngine->sParserValue,pUri,pPrefix,0); /* Clean-up the mess left behind */ ph7_context_release_value(pEngine->pCtx,pUri); ph7_context_release_value(pEngine->pCtx,pPrefix); return SXRET_OK; } /* * Namespace end declaration handler. * The user defined callback must accept two parameters: * handler(resource $parser,string $prefix) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $prefix * The prefix is a string used to reference the namespace within an XML object. * Note: Instead of a function name, an array containing an object reference * and a method name can also be supplied. */ static sxi32 VmXMLNSEndHandler(SyXMLRawStr *pPrefixStr,void *pUserData) { ph7_xml_engine *pEngine = (ph7_xml_engine *)pUserData; ph7_value *pCallback,*pPrefix; /* Point to the target user defined callback */ pCallback = &pEngine->aCB[PH7_XML_NS_END]; /* Make sure the given callback is callable */ if( !PH7_VmIsCallable(pEngine->pVm,pCallback,0) ){ /* Not callable,return immediately*/ return SXRET_OK; } /* Get a ph7_value holding the prefix */ pPrefix = VmXMLValue(pEngine,pPrefixStr,0); if( pPrefix == 0 ){ /* Out of mem,return immediately */ return SXRET_OK; } /* Invoke the user callback */ PH7_VmCallUserFunctionAp(pEngine->pVm,pCallback,0,&pEngine->sParserValue,pPrefix,0); /* Clean-up the mess left behind */ ph7_context_release_value(pEngine->pCtx,pPrefix); return SXRET_OK; } /* * Error Message consumer handler. * Each time the XML parser encounter a syntaxt error or any other error * related to XML processing,the following callback is invoked by the * underlying XML parser. */ static sxi32 VmXMLErrorHandler(const char *zMessage,sxi32 iErrCode,SyToken *pToken,void *pUserData) { ph7_xml_engine *pEngine = (ph7_xml_engine *)pUserData; /* Save the error code */ pEngine->iErrCode = iErrCode; SXUNUSED(zMessage); /* cc warning */ if( pToken ){ pEngine->nLine = pToken->nLine; } /* Abort XML processing immediately */ return SXERR_ABORT; } /* * int xml_parse(resource $parser,string $data[,bool $is_final = false ]) * Parses an XML document. The handlers for the configured events are called * as many times as necessary. * Parameters * $parser * A reference to the XML parser. * $data * Chunk of data to parse. A document may be parsed piece-wise by calling * xml_parse() several times with new data, as long as the is_final parameter * is set and TRUE when the last data is parsed. * $is_final * NOT USED. This implementation require that all the processed input be * entirely loaded in memory. * Return * Returns 1 on success or 0 on failure. */ static int vm_builtin_xml_parse(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; SyXMLParser *pParser; const char *zData; int nByte; if( nArg < 2 || !ph7_value_is_resource(apArg[0]) || !ph7_value_is_string(apArg[1]) ){ /* Missing/Ivalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( pEngine->iNest > 0 ){ /* This can happen when the user callback call xml_parse() again * in it's body which is forbidden. */ ph7_context_throw_error_format(pCtx,PH7_CTX_ERR, "Recursive call to %s,PH7 is returning false", ph7_function_name(pCtx) ); /* Return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } pEngine->pCtx = pCtx; /* Point to the underlying XML parser */ pParser = &pEngine->sParser; /* Register elements handler */ SyXMLParserSetEventHandler(pParser,pEngine, VmXMLStartElementHandler, VmXMLTextHandler, VmXMLErrorHandler, 0, VmXMLEndElementHandler, VmXMLPIHandler, 0, 0, VmXMLNSStartHandler, VmXMLNSEndHandler ); pEngine->iErrCode = SXML_ERROR_NONE; /* Extract the raw XML input */ zData = ph7_value_to_string(apArg[1],&nByte); /* Start the parse process */ pEngine->iNest++; SyXMLProcess(pParser,zData,(sxu32)nByte); pEngine->iNest--; /* Return the parse result */ ph7_result_int(pCtx,pEngine->iErrCode == SXML_ERROR_NONE ? 1 : 0); return PH7_OK; } /* * bool xml_parser_set_option(resource $parser,int $option,mixed $value) * Sets an option in an XML parser. * Parameters * $parser * A reference to the XML parser to set an option in. * $option * Which option to set. See below. * The following options are available: * XML_OPTION_CASE_FOLDING integer Controls whether case-folding is enabled for this XML parser. * XML_OPTION_SKIP_TAGSTART integer Specify how many characters should be skipped in the beginning of a tag name. * XML_OPTION_SKIP_WHITE integer Whether to skip values consisting of whitespace characters. * XML_OPTION_TARGET_ENCODING string Sets which target encoding to use in this XML parser. * $value * The option's new value. * Return * Returns 1 on success or 0 on failure. * Note: * Well,none of these options have meaning under the built-in XML parser so a call to this * function is a no-op. */ static int vm_builtin_xml_parser_set_option(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 2 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Always return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* * mixed xml_parser_get_option(resource $parser,int $option) * Get options from an XML parser. * Parameters * $parser * A reference to the XML parser to set an option in. * $option * Which option to fetch. * Return * This function returns FALSE if parser does not refer to a valid parser * or if option isn't valid.Else the option's value is returned. */ static int vm_builtin_xml_parser_get_option(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; int nOp; if( nArg < 2 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the option */ nOp = ph7_value_to_int(apArg[1]); switch(nOp){ case SXML_OPTION_SKIP_TAGSTART: case SXML_OPTION_SKIP_WHITE: case SXML_OPTION_CASE_FOLDING: ph7_result_int(pCtx,0); break; case SXML_OPTION_TARGET_ENCODING: ph7_result_string(pCtx,"UTF-8",(int)sizeof("UTF-8")-1); break; default: /* Unknown option,return FALSE*/ ph7_result_bool(pCtx,0); break; } return PH7_OK; } /* * string xml_error_string(int $code) * Gets the XML parser error string associated with the given code. * Parameters * $code * An error code from xml_get_error_code(). * Return * Returns a string with a textual description of the error * code, or FALSE if no description was found. */ static int vm_builtin_xml_error_string(ph7_context *pCtx,int nArg,ph7_value **apArg) { int nErr = -1; if( nArg > 0 ){ nErr = ph7_value_to_int(apArg[0]); } switch(nErr){ case SXML_ERROR_DUPLICATE_ATTRIBUTE: ph7_result_string(pCtx,"Duplicate attribute",-1/*Compute length automatically*/); break; case SXML_ERROR_INCORRECT_ENCODING: ph7_result_string(pCtx,"Incorrect encoding",-1); break; case SXML_ERROR_INVALID_TOKEN: ph7_result_string(pCtx,"Unexpected token",-1); break; case SXML_ERROR_MISPLACED_XML_PI: ph7_result_string(pCtx,"Misplaced processing instruction",-1); break; case SXML_ERROR_NO_MEMORY: ph7_result_string(pCtx,"Out of memory",-1); break; case SXML_ERROR_NONE: ph7_result_string(pCtx,"Not an error",-1); break; case SXML_ERROR_TAG_MISMATCH: ph7_result_string(pCtx,"Tag mismatch",-1); break; case -1: ph7_result_string(pCtx,"Unknown error code",-1); break; default: ph7_result_string(pCtx,"Syntax error",-1); break; } return PH7_OK; } #endif /* PH7_DISABLE_BUILTIN_FUNC */ /* * int utf8_encode(string $input) * UTF-8 encoding. * This function encodes the string data to UTF-8, and returns the encoded version. * UTF-8 is a standard mechanism used by Unicode for encoding wide character values * into a byte stream. UTF-8 is transparent to plain ASCII characters, is self-synchronized * (meaning it is possible for a program to figure out where in the bytestream characters start) * and can be used with normal string comparison functions for sorting and such. * Notes on UTF-8 (According to SQLite3 authors): * Byte-0 Byte-1 Byte-2 Byte-3 Value * 0xxxxxxx 00000000 00000000 0xxxxxxx * 110yyyyy 10xxxxxx 00000000 00000yyy yyxxxxxx * 1110zzzz 10yyyyyy 10xxxxxx 00000000 zzzzyyyy yyxxxxxx * 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 000uuuuu zzzzyyyy yyxxxxxx * Parameters * $input * String to encode or NULL on failure. * Return * An UTF-8 encoded string. */ static int vm_builtin_utf8_encode(ph7_context *pCtx,int nArg,ph7_value **apArg) { const unsigned char *zIn,*zEnd; int nByte,c,e; if( nArg < 1 ){ /* Missing arguments,return null */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the target string */ zIn = (const unsigned char *)ph7_value_to_string(apArg[0],&nByte); if( nByte < 1 ){ /* Empty string,return null */ ph7_result_null(pCtx); return PH7_OK; } zEnd = &zIn[nByte]; /* Start the encoding process */ for(;;){ if( zIn >= zEnd ){ /* End of input */ break; } c = zIn[0]; /* Advance the stream cursor */ zIn++; /* Encode */ if( c<0x00080 ){ e = (c&0xFF); ph7_result_string(pCtx,(const char *)&e,(int)sizeof(char)); }else if( c<0x00800 ){ e = 0xC0 + ((c>>6)&0x1F); ph7_result_string(pCtx,(const char *)&e,(int)sizeof(char)); e = 0x80 + (c & 0x3F); ph7_result_string(pCtx,(const char *)&e,(int)sizeof(char)); }else if( c<0x10000 ){ e = 0xE0 + ((c>>12)&0x0F); ph7_result_string(pCtx,(const char *)&e,(int)sizeof(char)); e = 0x80 + ((c>>6) & 0x3F); ph7_result_string(pCtx,(const char *)&e,(int)sizeof(char)); e = 0x80 + (c & 0x3F); ph7_result_string(pCtx,(const char *)&e,(int)sizeof(char)); }else{ e = 0xF0 + ((c>>18) & 0x07); ph7_result_string(pCtx,(const char *)&e,(int)sizeof(char)); e = 0x80 + ((c>>12) & 0x3F); ph7_result_string(pCtx,(const char *)&e,(int)sizeof(char)); e = 0x80 + ((c>>6) & 0x3F); ph7_result_string(pCtx,(const char *)&e,(int)sizeof(char)); e = 0x80 + (c & 0x3F); ph7_result_string(pCtx,(const char *)&e,(int)sizeof(char)); } } /* All done */ return PH7_OK; } /* * UTF-8 decoding routine extracted from the sqlite3 source tree. * Original author: D. Richard Hipp (http://www.sqlite.org) * Status: Public Domain */ /* ** This lookup table is used to help decode the first byte of ** a multi-byte UTF8 character. */ static const unsigned char UtfTrans1[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00, }; /* ** Translate a single UTF-8 character. Return the unicode value. ** ** During translation, assume that the byte that zTerm points ** is a 0x00. ** ** Write a pointer to the next unread byte back into *pzNext. ** ** Notes On Invalid UTF-8: ** ** * This routine never allows a 7-bit character (0x00 through 0x7f) to ** be encoded as a multi-byte character. Any multi-byte character that ** attempts to encode a value between 0x00 and 0x7f is rendered as 0xfffd. ** ** * This routine never allows a UTF16 surrogate value to be encoded. ** If a multi-byte character attempts to encode a value between ** 0xd800 and 0xe000 then it is rendered as 0xfffd. ** ** * Bytes in the range of 0x80 through 0xbf which occur as the first ** byte of a character are interpreted as single-byte characters ** and rendered as themselves even though they are technically ** invalid characters. ** ** * This routine accepts an infinite number of different UTF8 encodings ** for unicode values 0x80 and greater. It do not change over-length ** encodings to 0xfffd as some systems recommend. */ #define READ_UTF8(zIn, zTerm, c) \ c = *(zIn++); \ if( c>=0xc0 ){ \ c = UtfTrans1[c-0xc0]; \ while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ c = (c<<6) + (0x3f & *(zIn++)); \ } \ if( c<0x80 \ || (c&0xFFFFF800)==0xD800 \ || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ } PH7_PRIVATE int PH7_Utf8Read( const unsigned char *z, /* First byte of UTF-8 character */ const unsigned char *zTerm, /* Pretend this byte is 0x00 */ const unsigned char **pzNext /* Write first byte past UTF-8 char here */ ){ int c; READ_UTF8(z, zTerm, c); *pzNext = z; return c; } /* * string utf8_decode(string $data) * This function decodes data, assumed to be UTF-8 encoded, to unicode. * Parameters * data * An UTF-8 encoded string. * Return * Unicode decoded string or NULL on failure. */ static int vm_builtin_utf8_decode(ph7_context *pCtx,int nArg,ph7_value **apArg) { const unsigned char *zIn,*zEnd; int nByte,c; if( nArg < 1 ){ /* Missing arguments,return null */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the target string */ zIn = (const unsigned char *)ph7_value_to_string(apArg[0],&nByte); if( nByte < 1 ){ /* Empty string,return null */ ph7_result_null(pCtx); return PH7_OK; } zEnd = &zIn[nByte]; /* Start the decoding process */ while( zIn < zEnd ){ c = PH7_Utf8Read(zIn,zEnd,&zIn); if( c == 0x0 ){ break; } ph7_result_string(pCtx,(const char *)&c,(int)sizeof(char)); } return PH7_OK; } /* Table of built-in VM functions. */ static const ph7_builtin_func aVmFunc[] = { { "func_num_args" , vm_builtin_func_num_args }, { "func_get_arg" , vm_builtin_func_get_arg }, { "func_get_args" , vm_builtin_func_get_args }, { "func_get_args_byref" , vm_builtin_func_get_args_byref }, { "function_exists", vm_builtin_func_exists }, { "is_callable" , vm_builtin_is_callable }, { "get_defined_functions", vm_builtin_get_defined_func }, { "register_shutdown_function",vm_builtin_register_shutdown_function }, { "call_user_func", vm_builtin_call_user_func }, { "call_user_func_array", vm_builtin_call_user_func_array }, { "forward_static_call", vm_builtin_call_user_func }, { "forward_static_call_array",vm_builtin_call_user_func_array }, /* Constants management */ { "defined", vm_builtin_defined }, { "define", vm_builtin_define }, { "constant", vm_builtin_constant }, { "get_defined_constants", vm_builtin_get_defined_constants }, /* Class/Object functions */ { "class_alias", vm_builtin_class_alias }, { "class_exists", vm_builtin_class_exists }, { "property_exists", vm_builtin_property_exists }, { "method_exists", vm_builtin_method_exists }, { "interface_exists",vm_builtin_interface_exists }, { "get_class", vm_builtin_get_class }, { "get_parent_class",vm_builtin_get_parent_class }, { "get_called_class",vm_builtin_get_called_class }, { "get_declared_classes", vm_builtin_get_declared_classes }, { "get_defined_classes", vm_builtin_get_declared_classes }, { "get_declared_interfaces", vm_builtin_get_declared_interfaces}, { "get_class_methods", vm_builtin_get_class_methods }, { "get_class_vars", vm_builtin_get_class_vars }, { "get_object_vars", vm_builtin_get_object_vars }, { "is_subclass_of", vm_builtin_is_subclass_of }, { "is_a", vm_builtin_is_a }, /* Random numbers/strings generators */ { "rand", vm_builtin_rand }, { "mt_rand", vm_builtin_rand }, { "rand_str", vm_builtin_rand_str }, { "getrandmax", vm_builtin_getrandmax }, { "mt_getrandmax", vm_builtin_getrandmax }, #ifndef PH7_DISABLE_BUILTIN_FUNC #if !defined(PH7_DISABLE_HASH_FUNC) { "uniqid", vm_builtin_uniqid }, #endif /* PH7_DISABLE_HASH_FUNC */ #endif /* PH7_DISABLE_BUILTIN_FUNC */ /* Language constructs functions */ { "echo", vm_builtin_echo }, { "print", vm_builtin_print }, { "exit", vm_builtin_exit }, { "die", vm_builtin_exit }, { "eval", vm_builtin_eval }, /* Variable handling functions */ { "get_defined_vars",vm_builtin_get_defined_vars}, { "gettype", vm_builtin_gettype }, { "get_resource_type", vm_builtin_get_resource_type}, { "isset", vm_builtin_isset }, { "unset", vm_builtin_unset }, { "var_dump", vm_builtin_var_dump }, { "print_r", vm_builtin_print_r }, { "var_export",vm_builtin_var_export }, /* Ouput control functions */ { "flush", vm_builtin_ob_flush }, { "ob_clean", vm_builtin_ob_clean }, { "ob_end_clean", vm_builtin_ob_end_clean }, { "ob_end_flush", vm_builtin_ob_end_flush }, { "ob_flush", vm_builtin_ob_flush }, { "ob_get_clean", vm_builtin_ob_get_clean }, { "ob_get_contents", vm_builtin_ob_get_contents}, { "ob_get_flush", vm_builtin_ob_get_clean }, { "ob_get_length", vm_builtin_ob_get_length }, { "ob_get_level", vm_builtin_ob_get_level }, { "ob_implicit_flush", vm_builtin_ob_implicit_flush}, { "ob_get_level", vm_builtin_ob_get_level }, { "ob_list_handlers", vm_builtin_ob_list_handlers }, { "ob_start", vm_builtin_ob_start }, /* Assertion functions */ { "assert_options", vm_builtin_assert_options }, { "assert", vm_builtin_assert }, /* Error reporting functions */ { "trigger_error",vm_builtin_trigger_error }, { "user_error", vm_builtin_trigger_error }, { "error_reporting",vm_builtin_error_reporting }, { "error_log", vm_builtin_error_log }, { "restore_exception_handler", vm_builtin_restore_exception_handler }, { "set_exception_handler", vm_builtin_set_exception_handler }, { "restore_error_handler", vm_builtin_restore_error_handler }, { "set_error_handler",vm_builtin_set_error_handler }, { "debug_backtrace", vm_builtin_debug_backtrace}, { "error_get_last" , vm_builtin_debug_backtrace }, { "debug_print_backtrace", vm_builtin_debug_print_backtrace }, { "debug_string_backtrace",vm_builtin_debug_string_backtrace }, /* Release info */ {"ph7version", vm_builtin_ph7_version }, {"ph7credits", vm_builtin_ph7_credits }, {"ph7info", vm_builtin_ph7_credits }, {"ph7_info", vm_builtin_ph7_credits }, {"phpinfo", vm_builtin_ph7_credits }, {"ph7copyright", vm_builtin_ph7_credits }, /* hashmap */ {"compact", vm_builtin_compact }, {"extract", vm_builtin_extract }, {"import_request_variables", vm_builtin_import_request_variables}, /* URL related function */ {"parse_url", vm_builtin_parse_url }, /* Refer to 'builtin.c' for others string processing functions. */ #ifndef PH7_DISABLE_BUILTIN_FUNC /* XML processing functions */ {"xml_parser_create", vm_builtin_xml_parser_create }, {"xml_parser_create_ns", vm_builtin_xml_parser_create_ns}, {"xml_parser_free", vm_builtin_xml_parser_free }, {"xml_set_element_handler", vm_builtin_xml_set_element_handler}, {"xml_set_character_data_handler", vm_builtin_xml_set_character_data_handler}, {"xml_set_default_handler", vm_builtin_xml_set_default_handler }, {"xml_set_end_namespace_decl_handler", vm_builtin_xml_set_end_namespace_decl_handler}, {"xml_set_start_namespace_decl_handler",vm_builtin_xml_set_start_namespace_decl_handler}, {"xml_set_processing_instruction_handler",vm_builtin_xml_set_processing_instruction_handler}, {"xml_set_unparsed_entity_decl_handler",vm_builtin_xml_set_unparsed_entity_decl_handler}, {"xml_set_notation_decl_handler",vm_builtin_xml_set_notation_decl_handler}, {"xml_set_external_entity_ref_handler",vm_builtin_xml_set_external_entity_ref_handler}, {"xml_get_current_line_number", vm_builtin_xml_get_current_line_number}, {"xml_get_current_byte_index", vm_builtin_xml_get_current_byte_index }, {"xml_set_object", vm_builtin_xml_set_object}, {"xml_get_current_column_number",vm_builtin_xml_get_current_column_number}, {"xml_get_error_code", vm_builtin_xml_get_error_code }, {"xml_parse", vm_builtin_xml_parse }, {"xml_parser_set_option", vm_builtin_xml_parser_set_option}, {"xml_parser_get_option", vm_builtin_xml_parser_get_option}, {"xml_error_string", vm_builtin_xml_error_string }, #endif /* PH7_DISABLE_BUILTIN_FUNC */ /* UTF-8 encoding/decoding */ {"utf8_encode", vm_builtin_utf8_encode}, {"utf8_decode", vm_builtin_utf8_decode}, /* Command line processing */ {"getopt", vm_builtin_getopt }, /* JSON encoding/decoding */ {"json_encode", vm_builtin_json_encode }, {"json_last_error",vm_builtin_json_last_error}, {"json_decode", vm_builtin_json_decode }, {"serialize", vm_builtin_json_encode }, {"unserialize", vm_builtin_json_decode }, /* Files/URI inclusion facility */ { "get_include_path", vm_builtin_get_include_path }, { "get_included_files",vm_builtin_get_included_files}, { "include", vm_builtin_include }, { "include_once", vm_builtin_include_once }, { "require", vm_builtin_require }, { "require_once", vm_builtin_require_once }, }; /* * Register the built-in VM functions defined above. */ static sxi32 VmRegisterSpecialFunction(ph7_vm *pVm) { sxi32 rc; sxu32 n; for( n = 0 ; n < SX_ARRAYSIZE(aVmFunc) ; ++n ){ /* Note that these special functions have access * to the underlying virtual machine as their * private data. */ rc = ph7_create_function(&(*pVm),aVmFunc[n].zName,aVmFunc[n].xFunc,&(*pVm)); if( rc != SXRET_OK ){ return rc; } } return SXRET_OK; } /* * Check if the given name refer to an installed class. * Return a pointer to that class on success. NULL on failure. */ PH7_PRIVATE ph7_class * PH7_VmExtractClass( ph7_vm *pVm, /* Target VM */ const char *zName, /* Name of the target class */ sxu32 nByte, /* zName length */ sxi32 iLoadable, /* TRUE to return only loadable class * [i.e: no abstract classes or interfaces] */ sxi32 iNest /* Nesting level (Not used) */ ) { SyHashEntry *pEntry; ph7_class *pClass; /* Perform a hash lookup */ pEntry = SyHashGet(&pVm->hClass,(const void *)zName,nByte); if( pEntry == 0 ){ /* No such entry,return NULL */ iNest = 0; /* cc warning */ return 0; } pClass = (ph7_class *)pEntry->pUserData; if( !iLoadable ){ /* Return the first class seen */ return pClass; }else{ /* Check the collision list */ while(pClass){ if( (pClass->iFlags & (PH7_CLASS_INTERFACE|PH7_CLASS_ABSTRACT)) == 0 ){ /* Class is loadable */ return pClass; } /* Point to the next entry */ pClass = pClass->pNextName; } } /* No such loadable class */ return 0; } /* * Reference Table Implementation * Status: stable * Intro * The implementation of the reference mechanism in the PH7 engine * differ greatly from the one used by the zend engine. That is, * the reference implementation is consistent,solid and it's * behavior resemble the C++ reference mechanism. * Refer to the official for more information on this powerful * extension. */ /* * Allocate a new reference entry. */ static VmRefObj * VmNewRefObj(ph7_vm *pVm,sxu32 nIdx) { VmRefObj *pRef; /* Allocate a new instance */ pRef = (VmRefObj *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(VmRefObj)); if( pRef == 0 ){ return 0; } /* Zero the structure */ SyZero(pRef,sizeof(VmRefObj)); /* Initialize fields */ SySetInit(&pRef->aReference,&pVm->sAllocator,sizeof(SyHashEntry *)); SySetInit(&pRef->aArrEntries,&pVm->sAllocator,sizeof(ph7_hashmap_node *)); pRef->nIdx = nIdx; return pRef; } /* * Default hash function used by the reference table * for lookup/insertion operations. */ static sxu32 VmRefHash(sxu32 nIdx) { /* Calculate the hash based on the memory object index */ return nIdx ^ (nIdx << 8) ^ (nIdx >> 8); } /* * Check if a memory object [i.e: a variable] is already installed * in the reference table. * Return a pointer to the entry (VmRefObj instance) on success.NULL * otherwise. * The implementation of the reference mechanism in the PH7 engine * differ greatly from the one used by the zend engine. That is, * the reference implementation is consistent,solid and it's * behavior resemble the C++ reference mechanism. * Refer to the official for more information on this powerful * extension. */ static VmRefObj * VmRefObjExtract(ph7_vm *pVm,sxu32 nObjIdx) { VmRefObj *pRef; sxu32 nBucket; /* Point to the appropriate bucket */ nBucket = VmRefHash(nObjIdx) & (pVm->nRefSize - 1); /* Perform the lookup */ pRef = pVm->apRefObj[nBucket]; for(;;){ if( pRef == 0 ){ break; } if( pRef->nIdx == nObjIdx ){ /* Entry found */ return pRef; } /* Point to the next entry */ pRef = pRef->pNextCollide; } /* No such entry,return NULL */ return 0; } /* * Install a memory object [i.e: a variable] in the reference table. * * The implementation of the reference mechanism in the PH7 engine * differ greatly from the one used by the zend engine. That is, * the reference implementation is consistent,solid and it's * behavior resemble the C++ reference mechanism. * Refer to the official for more information on this powerful * extension. */ static sxi32 VmRefObjInsert(ph7_vm *pVm,VmRefObj *pRef) { sxu32 nBucket; if( pVm->nRefUsed * 3 >= pVm->nRefSize ){ VmRefObj **apNew; sxu32 nNew; /* Allocate a larger table */ nNew = pVm->nRefSize << 1; apNew = (VmRefObj **)SyMemBackendAlloc(&pVm->sAllocator,sizeof(VmRefObj *) * nNew); if( apNew ){ VmRefObj *pEntry = pVm->pRefList; sxu32 n; /* Zero the structure */ SyZero((void *)apNew,nNew * sizeof(VmRefObj *)); /* Rehash all referenced entries */ for( n = 0 ; n < pVm->nRefUsed ; ++n ){ /* Remove old collision links */ pEntry->pNextCollide = pEntry->pPrevCollide = 0; /* Point to the appropriate bucket */ nBucket = VmRefHash(pEntry->nIdx) & (nNew - 1); /* Insert the entry */ pEntry->pNextCollide = apNew[nBucket]; if( apNew[nBucket] ){ apNew[nBucket]->pPrevCollide = pEntry; } apNew[nBucket] = pEntry; /* Point to the next entry */ pEntry = pEntry->pNext; } /* Release the old table */ SyMemBackendFree(&pVm->sAllocator,pVm->apRefObj); /* Install the new one */ pVm->apRefObj = apNew; pVm->nRefSize = nNew; } } /* Point to the appropriate bucket */ nBucket = VmRefHash(pRef->nIdx) & (pVm->nRefSize - 1); /* Insert the entry */ pRef->pNextCollide = pVm->apRefObj[nBucket]; if( pVm->apRefObj[nBucket] ){ pVm->apRefObj[nBucket]->pPrevCollide = pRef; } pVm->apRefObj[nBucket] = pRef; MACRO_LD_PUSH(pVm->pRefList,pRef); pVm->nRefUsed++; return SXRET_OK; } /* * Destroy a memory object [i.e: a variable] and remove it from * the reference table. * This function is invoked when the user perform an unset * call [i.e: unset($var); ]. * The implementation of the reference mechanism in the PH7 engine * differ greatly from the one used by the zend engine. That is, * the reference implementation is consistent,solid and it's * behavior resemble the C++ reference mechanism. * Refer to the official for more information on this powerful * extension. */ static sxi32 VmRefObjUnlink(ph7_vm *pVm,VmRefObj *pRef) { ph7_hashmap_node **apNode; SyHashEntry **apEntry; sxu32 n; /* Point to the reference table */ apNode = (ph7_hashmap_node **)SySetBasePtr(&pRef->aArrEntries); apEntry = (SyHashEntry **)SySetBasePtr(&pRef->aReference); /* Unlink the entry from the reference table */ for( n = 0 ; n < SySetUsed(&pRef->aReference) ; n++ ){ if( apEntry[n] ){ SyHashDeleteEntry2(apEntry[n]); } } for(n = 0 ; n < SySetUsed(&pRef->aArrEntries) ; ++n ){ if( apNode[n] ){ PH7_HashmapUnlinkNode(apNode[n],FALSE); } } if( pRef->pPrevCollide ){ pRef->pPrevCollide->pNextCollide = pRef->pNextCollide; }else{ pVm->apRefObj[VmRefHash(pRef->nIdx) & (pVm->nRefSize - 1)] = pRef->pNextCollide; } if( pRef->pNextCollide ){ pRef->pNextCollide->pPrevCollide = pRef->pPrevCollide; } MACRO_LD_REMOVE(pVm->pRefList,pRef); /* Release the node */ SySetRelease(&pRef->aReference); SySetRelease(&pRef->aArrEntries); SyMemBackendPoolFree(&pVm->sAllocator,pRef); pVm->nRefUsed--; return SXRET_OK; } /* * Install a memory object [i.e: a variable] in the reference table. * The implementation of the reference mechanism in the PH7 engine * differ greatly from the one used by the zend engine. That is, * the reference implementation is consistent,solid and it's * behavior resemble the C++ reference mechanism. * Refer to the official for more information on this powerful * extension. */ PH7_PRIVATE sxi32 PH7_VmRefObjInstall( ph7_vm *pVm, /* Target VM */ sxu32 nIdx, /* Memory object index in the global object pool */ SyHashEntry *pEntry, /* Hash entry of this object */ ph7_hashmap_node *pMapEntry, /* != NULL if the memory object is an array entry */ sxi32 iFlags /* Control flags */ ) { VmFrame *pFrame = pVm->pFrame; VmRefObj *pRef; /* Check if the referenced object already exists */ pRef = VmRefObjExtract(&(*pVm),nIdx); if( pRef == 0 ){ /* Create a new entry */ pRef = VmNewRefObj(&(*pVm),nIdx); if( pRef == 0 ){ return SXERR_MEM; } pRef->iFlags = iFlags; /* Install the entry */ VmRefObjInsert(&(*pVm),pRef); } while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } if( pFrame->pParent != 0 && pEntry ){ VmSlot sRef; /* Local frame,record referenced entry so that it can * be deleted when we leave this frame. */ sRef.nIdx = nIdx; sRef.pUserData = pEntry; if( SXRET_OK != SySetPut(&pFrame->sRef,(const void *)&sRef)) { pEntry = 0; /* Do not record this entry */ } } if( pEntry ){ /* Address of the hash-entry */ SySetPut(&pRef->aReference,(const void *)&pEntry); } if( pMapEntry ){ /* Address of the hashmap node [i.e: Array entry] */ SySetPut(&pRef->aArrEntries,(const void *)&pMapEntry); } return SXRET_OK; } /* * Remove a memory object [i.e: a variable] from the reference table. * The implementation of the reference mechanism in the PH7 engine * differ greatly from the one used by the zend engine. That is, * the reference implementation is consistent,solid and it's * behavior resemble the C++ reference mechanism. * Refer to the official for more information on this powerful * extension. */ PH7_PRIVATE sxi32 PH7_VmRefObjRemove( ph7_vm *pVm, /* Target VM */ sxu32 nIdx, /* Memory object index in the global object pool */ SyHashEntry *pEntry, /* Hash entry of this object */ ph7_hashmap_node *pMapEntry /* != NULL if the memory object is an array entry */ ) { VmRefObj *pRef; sxu32 n; /* Check if the referenced object already exists */ pRef = VmRefObjExtract(&(*pVm),nIdx); if( pRef == 0 ){ /* Not such entry */ return SXERR_NOTFOUND; } /* Remove the desired entry */ if( pEntry ){ SyHashEntry **apEntry; apEntry = (SyHashEntry **)SySetBasePtr(&pRef->aReference); for( n = 0 ; n < SySetUsed(&pRef->aReference) ; n++ ){ if( apEntry[n] == pEntry ){ /* Nullify the entry */ apEntry[n] = 0; /* * NOTE: * In future releases,think to add a free pool of entries,so that * we avoid wasting spaces. */ } } } if( pMapEntry ){ ph7_hashmap_node **apNode; apNode = (ph7_hashmap_node **)SySetBasePtr(&pRef->aArrEntries); for(n = 0 ; n < SySetUsed(&pRef->aArrEntries) ; n++ ){ if( apNode[n] == pMapEntry ){ /* nullify the entry */ apNode[n] = 0; } } } return SXRET_OK; } #ifndef PH7_DISABLE_BUILTIN_FUNC /* * Extract the IO stream device associated with a given scheme. * Return a pointer to an instance of ph7_io_stream when the scheme * have an associated IO stream registered with it. NULL otherwise. * If no scheme:// is avalilable then the file:// scheme is assumed. * For more information on how to register IO stream devices,please * refer to the official documentation. */ PH7_PRIVATE const ph7_io_stream * PH7_VmGetStreamDevice( ph7_vm *pVm, /* Target VM */ const char **pzDevice, /* Full path,URI,... */ int nByte /* *pzDevice length*/ ) { const char *zIn,*zEnd,*zCur,*zNext; ph7_io_stream **apStream,*pStream; SyString sDev,sCur; sxu32 n,nEntry; int rc; /* Check if a scheme [i.e: file://,http://,zip://...] is available */ zNext = zCur = zIn = *pzDevice; zEnd = &zIn[nByte]; while( zIn < zEnd ){ if( zIn < &zEnd[-3]/*://*/ && zIn[0] == ':' && zIn[1] == '/' && zIn[2] == '/' ){ /* Got one */ zNext = &zIn[sizeof("://")-1]; break; } /* Advance the cursor */ zIn++; } if( zIn >= zEnd ){ /* No such scheme,return the default stream */ return pVm->pDefStream; } SyStringInitFromBuf(&sDev,zCur,zIn-zCur); /* Remove leading and trailing white spaces */ SyStringFullTrim(&sDev); /* Perform a linear lookup on the installed stream devices */ apStream = (ph7_io_stream **)SySetBasePtr(&pVm->aIOstream); nEntry = SySetUsed(&pVm->aIOstream); for( n = 0 ; n < nEntry ; n++ ){ pStream = apStream[n]; SyStringInitFromBuf(&sCur,pStream->zName,SyStrlen(pStream->zName)); /* Perfrom a case-insensitive comparison */ rc = SyStringCmp(&sDev,&sCur,SyStrnicmp); if( rc == 0 ){ /* Stream device found */ *pzDevice = zNext; return pStream; } } /* No such stream,return NULL */ return 0; } #endif /* PH7_DISABLE_BUILTIN_FUNC */ /* * Section: * HTTP/URI related routines. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * URI Parser: Split an URI into components [i.e: Host,Path,Query,...]. * URI syntax: [method:/][/[user[:pwd]@]host[:port]/][document] * This almost, but not quite, RFC1738 URI syntax. * This routine is not a validator,it does not check for validity * nor decode URI parts,the only thing this routine does is splitting * the input to its fields. * Upper layer are responsible of decoding and validating URI parts. * On success,this function populate the "SyhttpUri" structure passed * as the first argument. Otherwise SXERR_* is returned when a malformed * input is encountered. */ static sxi32 VmHttpSplitURI(SyhttpUri *pOut,const char *zUri,sxu32 nLen) { const char *zEnd = &zUri[nLen]; sxu8 bHostOnly = FALSE; sxu8 bIPv6 = FALSE ; const char *zCur; SyString *pComp; sxu32 nPos = 0; sxi32 rc; /* Zero the structure first */ SyZero(pOut,sizeof(SyhttpUri)); /* Remove leading and trailing white spaces */ SyStringInitFromBuf(&pOut->sRaw,zUri,nLen); SyStringFullTrim(&pOut->sRaw); /* Find the first '/' separator */ rc = SyByteFind(zUri,(sxu32)(zEnd - zUri),'/',&nPos); if( rc != SXRET_OK ){ /* Assume a host name only */ zCur = zEnd; bHostOnly = TRUE; goto ProcessHost; } zCur = &zUri[nPos]; if( zUri != zCur && zCur[-1] == ':' ){ /* Extract a scheme: * Not that we can get an invalid scheme here. * Fortunately the caller can discard any URI by comparing this scheme with its * registered schemes and will report the error as soon as his comparison function * fail. */ pComp = &pOut->sScheme; SyStringInitFromBuf(pComp,zUri,(sxu32)(zCur - zUri - 1)); SyStringLeftTrim(pComp); } if( zCur[1] != '/' ){ if( zCur == zUri || zCur[-1] == ':' ){ /* No authority */ goto PathSplit; } /* There is something here , we will assume its an authority * and someone has forgot the two prefix slashes "//", * sooner or later we will detect if we are dealing with a malicious * user or not,but now assume we are dealing with an authority * and let the caller handle all the validation process. */ goto ProcessHost; } zUri = &zCur[2]; zCur = zEnd; rc = SyByteFind(zUri,(sxu32)(zEnd - zUri),'/',&nPos); if( rc == SXRET_OK ){ zCur = &zUri[nPos]; } ProcessHost: /* Extract user information if present */ rc = SyByteFind(zUri,(sxu32)(zCur - zUri),'@',&nPos); if( rc == SXRET_OK ){ if( nPos > 0 ){ sxu32 nPassOfft; /* Password offset */ pComp = &pOut->sUser; SyStringInitFromBuf(pComp,zUri,nPos); /* Extract the password if available */ rc = SyByteFind(zUri,(sxu32)(zCur - zUri),':',&nPassOfft); if( rc == SXRET_OK && nPassOfft < nPos){ pComp->nByte = nPassOfft; pComp = &pOut->sPass; pComp->zString = &zUri[nPassOfft+sizeof(char)]; pComp->nByte = nPos - nPassOfft - 1; } /* Update the cursor */ zUri = &zUri[nPos+1]; }else{ zUri++; } } pComp = &pOut->sHost; while( zUri < zCur && SyisSpace(zUri[0])){ zUri++; } SyStringInitFromBuf(pComp,zUri,(sxu32)(zCur - zUri)); if( pComp->zString[0] == '[' ){ /* An IPv6 Address: Make a simple naive test */ zUri++; pComp->zString++; pComp->nByte = 0; while( ((unsigned char)zUri[0] < 0xc0 && SyisHex(zUri[0])) || zUri[0] == ':' ){ zUri++; pComp->nByte++; } if( zUri[0] != ']' ){ return SXERR_CORRUPT; /* Malformed IPv6 address */ } zUri++; bIPv6 = TRUE; } /* Extract a port number if available */ rc = SyByteFind(zUri,(sxu32)(zCur - zUri),':',&nPos); if( rc == SXRET_OK ){ if( bIPv6 == FALSE ){ pComp->nByte = (sxu32)(&zUri[nPos] - zUri); } pComp = &pOut->sPort; SyStringInitFromBuf(pComp,&zUri[nPos+1],(sxu32)(zCur - &zUri[nPos+1])); } if( bHostOnly == TRUE ){ return SXRET_OK; } PathSplit: zUri = zCur; pComp = &pOut->sPath; SyStringInitFromBuf(pComp,zUri,(sxu32)(zEnd-zUri)); if( pComp->nByte == 0 ){ return SXRET_OK; /* Empty path */ } if( SXRET_OK == SyByteFind(zUri,(sxu32)(zEnd-zUri),'?',&nPos) ){ pComp->nByte = nPos; /* Update path length */ pComp = &pOut->sQuery; SyStringInitFromBuf(pComp,&zUri[nPos+1],(sxu32)(zEnd-&zUri[nPos+1])); } if( SXRET_OK == SyByteFind(zUri,(sxu32)(zEnd-zUri),'#',&nPos) ){ /* Update path or query length */ if( pComp == &pOut->sPath ){ pComp->nByte = nPos; }else{ if( &zUri[nPos] < (char *)SyStringData(pComp) ){ /* Malformed syntax : Query must be present before fragment */ return SXERR_SYNTAX; } pComp->nByte -= (sxu32)(zEnd - &zUri[nPos]); } pComp = &pOut->sFragment; SyStringInitFromBuf(pComp,&zUri[nPos+1],(sxu32)(zEnd-&zUri[nPos+1])) } return SXRET_OK; } /* * Extract a single line from a raw HTTP request. * Return SXRET_OK on success,SXERR_EOF when end of input * and SXERR_MORE when more input is needed. */ static sxi32 VmGetNextLine(SyString *pCursor,SyString *pCurrent) { const char *zIn; sxu32 nPos; /* Jump leading white spaces */ SyStringLeftTrim(pCursor); if( pCursor->nByte < 1 ){ SyStringInitFromBuf(pCurrent,0,0); return SXERR_EOF; /* End of input */ } zIn = SyStringData(pCursor); if( SXRET_OK != SyByteListFind(pCursor->zString,pCursor->nByte,"\r\n",&nPos) ){ /* Line not found,tell the caller to read more input from source */ SyStringDupPtr(pCurrent,pCursor); return SXERR_MORE; } pCurrent->zString = zIn; pCurrent->nByte = nPos; /* advance the cursor so we can call this routine again */ pCursor->zString = &zIn[nPos]; pCursor->nByte -= nPos; return SXRET_OK; } /* * Split a single MIME header into a name value pair. * This function return SXRET_OK,SXERR_CONTINUE on success. * Otherwise SXERR_NEXT is returned when a malformed header * is encountered. * Note: This function handle also mult-line headers. */ static sxi32 VmHttpProcessOneHeader(SyhttpHeader *pHdr,SyhttpHeader *pLast,const char *zLine,sxu32 nLen) { SyString *pName; sxu32 nPos; sxi32 rc; if( nLen < 1 ){ return SXERR_NEXT; } /* Check for multi-line header */ if( pLast && (zLine[-1] == ' ' || zLine[-1] == '\t') ){ SyString *pTmp = &pLast->sValue; SyStringFullTrim(pTmp); if( pTmp->nByte == 0 ){ SyStringInitFromBuf(pTmp,zLine,nLen); }else{ /* Update header value length */ pTmp->nByte = (sxu32)(&zLine[nLen] - pTmp->zString); } /* Simply tell the caller to reset its states and get another line */ return SXERR_CONTINUE; } /* Split the header */ pName = &pHdr->sName; rc = SyByteFind(zLine,nLen,':',&nPos); if(rc != SXRET_OK ){ return SXERR_NEXT; /* Malformed header;Check the next entry */ } SyStringInitFromBuf(pName,zLine,nPos); SyStringFullTrim(pName); /* Extract a header value */ SyStringInitFromBuf(&pHdr->sValue,&zLine[nPos + 1],nLen - nPos - 1); /* Remove leading and trailing whitespaces */ SyStringFullTrim(&pHdr->sValue); return SXRET_OK; } /* * Extract all MIME headers associated with a HTTP request. * After processing the first line of a HTTP request,the following * routine is called in order to extract MIME headers. * This function return SXRET_OK on success,SXERR_MORE when it needs * more inputs. * Note: Any malformed header is simply discarded. */ static sxi32 VmHttpExtractHeaders(SyString *pRequest,SySet *pOut) { SyhttpHeader *pLast = 0; SyString sCurrent; SyhttpHeader sHdr; sxu8 bEol; sxi32 rc; if( SySetUsed(pOut) > 0 ){ pLast = (SyhttpHeader *)SySetAt(pOut,SySetUsed(pOut)-1); } bEol = FALSE; for(;;){ SyZero(&sHdr,sizeof(SyhttpHeader)); /* Extract a single line from the raw HTTP request */ rc = VmGetNextLine(pRequest,&sCurrent); if(rc != SXRET_OK ){ if( sCurrent.nByte < 1 ){ break; } bEol = TRUE; } /* Process the header */ if( SXRET_OK == VmHttpProcessOneHeader(&sHdr,pLast,sCurrent.zString,sCurrent.nByte)){ if( SXRET_OK != SySetPut(pOut,(const void *)&sHdr) ){ break; } /* Retrieve the last parsed header so we can handle multi-line header * in case we face one of them. */ pLast = (SyhttpHeader *)SySetPeek(pOut); } if( bEol ){ break; } } /* for(;;) */ return SXRET_OK; } /* * Process the first line of a HTTP request. * This routine perform the following operations * 1) Extract the HTTP method. * 2) Split the request URI to it's fields [ie: host,path,query,...]. * 3) Extract the HTTP protocol version. */ static sxi32 VmHttpProcessFirstLine( SyString *pRequest, /* Raw HTTP request */ sxi32 *pMethod, /* OUT: HTTP method */ SyhttpUri *pUri, /* OUT: Parse of the URI */ sxi32 *pProto /* OUT: HTTP protocol */ ) { static const char *azMethods[] = { "get","post","head","put"}; static const sxi32 aMethods[] = { HTTP_METHOD_GET,HTTP_METHOD_POST,HTTP_METHOD_HEAD,HTTP_METHOD_PUT}; const char *zIn,*zEnd,*zPtr; SyString sLine; sxu32 nLen; sxi32 rc; /* Extract the first line and update the pointer */ rc = VmGetNextLine(pRequest,&sLine); if( rc != SXRET_OK ){ return rc; } if ( sLine.nByte < 1 ){ /* Empty HTTP request */ return SXERR_EMPTY; } /* Delimit the line and ignore trailing and leading white spaces */ zIn = sLine.zString; zEnd = &zIn[sLine.nByte]; while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ zIn++; } /* Extract the HTTP method */ zPtr = zIn; while( zIn < zEnd && !SyisSpace(zIn[0]) ){ zIn++; } *pMethod = HTTP_METHOD_OTHR; if( zIn > zPtr ){ sxu32 i; nLen = (sxu32)(zIn-zPtr); for( i = 0 ; i < SX_ARRAYSIZE(azMethods) ; ++i ){ if( SyStrnicmp(azMethods[i],zPtr,nLen) == 0 ){ *pMethod = aMethods[i]; break; } } } /* Jump trailing white spaces */ while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ zIn++; } /* Extract the request URI */ zPtr = zIn; while( zIn < zEnd && !SyisSpace(zIn[0]) ){ zIn++; } if( zIn > zPtr ){ nLen = (sxu32)(zIn-zPtr); /* Split raw URI to it's fields */ VmHttpSplitURI(pUri,zPtr,nLen); } /* Jump trailing white spaces */ while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ zIn++; } /* Extract the HTTP version */ zPtr = zIn; while( zIn < zEnd && !SyisSpace(zIn[0]) ){ zIn++; } *pProto = HTTP_PROTO_11; /* HTTP/1.1 */ rc = 1; if( zIn > zPtr ){ rc = SyStrnicmp(zPtr,"http/1.0",(sxu32)(zIn-zPtr)); } if( !rc ){ *pProto = HTTP_PROTO_10; /* HTTP/1.0 */ } return SXRET_OK; } /* * Tokenize,decode and split a raw query encoded as: "x-www-form-urlencoded" * into a name value pair. * Note that this encoding is implicit in GET based requests. * After the tokenization process,register the decoded queries * in the $_GET/$_POST/$_REQUEST superglobals arrays. */ static sxi32 VmHttpSplitEncodedQuery( ph7_vm *pVm, /* Target VM */ SyString *pQuery, /* Raw query to decode */ SyBlob *pWorker, /* Working buffer */ int is_post /* TRUE if we are dealing with a POST request */ ) { const char *zEnd = &pQuery->zString[pQuery->nByte]; const char *zIn = pQuery->zString; ph7_value *pGet,*pRequest; SyString sName,sValue; const char *zPtr; sxu32 nBlobOfft; /* Extract superglobals */ if( is_post ){ /* $_POST superglobal */ pGet = VmExtractSuper(&(*pVm),"_POST",sizeof("_POST")-1); }else{ /* $_GET superglobal */ pGet = VmExtractSuper(&(*pVm),"_GET",sizeof("_GET")-1); } pRequest = VmExtractSuper(&(*pVm),"_REQUEST",sizeof("_REQUEST")-1); /* Split up the raw query */ for(;;){ /* Jump leading white spaces */ while(zIn < zEnd && SyisSpace(zIn[0]) ){ zIn++; } if( zIn >= zEnd ){ break; } zPtr = zIn; while( zPtr < zEnd && zPtr[0] != '=' && zPtr[0] != '&' && zPtr[0] != ';' ){ zPtr++; } /* Reset the working buffer */ SyBlobReset(pWorker); /* Decode the entry */ SyUriDecode(zIn,(sxu32)(zPtr-zIn),PH7_VmBlobConsumer,pWorker,TRUE); /* Save the entry */ sName.nByte = SyBlobLength(pWorker); sValue.zString = 0; sValue.nByte = 0; if( zPtr < zEnd && zPtr[0] == '=' ){ zPtr++; zIn = zPtr; /* Store field value */ while( zPtr < zEnd && zPtr[0] != '&' && zPtr[0] != ';' ){ zPtr++; } if( zPtr > zIn ){ /* Decode the value */ nBlobOfft = SyBlobLength(pWorker); SyUriDecode(zIn,(sxu32)(zPtr-zIn),PH7_VmBlobConsumer,pWorker,TRUE); sValue.zString = (const char *)SyBlobDataAt(pWorker,nBlobOfft); sValue.nByte = SyBlobLength(pWorker) - nBlobOfft; } /* Synchronize pointers */ zIn = zPtr; } sName.zString = (const char *)SyBlobData(pWorker); /* Install the decoded query in the $_GET/$_REQUEST array */ if( pGet && (pGet->iFlags & MEMOBJ_HASHMAP) ){ VmHashmapInsert((ph7_hashmap *)pGet->x.pOther, sName.zString,(int)sName.nByte, sValue.zString,(int)sValue.nByte ); } if( pRequest && (pRequest->iFlags & MEMOBJ_HASHMAP) ){ VmHashmapInsert((ph7_hashmap *)pRequest->x.pOther, sName.zString,(int)sName.nByte, sValue.zString,(int)sValue.nByte ); } /* Advance the pointer */ zIn = &zPtr[1]; } /* All done*/ return SXRET_OK; } /* * Extract MIME header value from the given set. * Return header value on success. NULL otherwise. */ static SyString * VmHttpExtractHeaderValue(SySet *pSet,const char *zMime,sxu32 nByte) { SyhttpHeader *aMime,*pMime; SyString sMime; sxu32 n; SyStringInitFromBuf(&sMime,zMime,nByte); /* Point to the MIME entries */ aMime = (SyhttpHeader *)SySetBasePtr(pSet); /* Perform the lookup */ for( n = 0 ; n < SySetUsed(pSet) ; ++n ){ pMime = &aMime[n]; if( SyStringCmp(&sMime,&pMime->sName,SyStrnicmp) == 0 ){ /* Header found,return it's associated value */ return &pMime->sValue; } } /* No such MIME header */ return 0; } /* * Tokenize and decode a raw "Cookie:" MIME header into a name value pair * and insert it's fields [i.e name,value] in the $_COOKIE superglobal. */ static sxi32 VmHttpPorcessCookie(ph7_vm *pVm,SyBlob *pWorker,const char *zIn,sxu32 nByte) { const char *zPtr,*zDelimiter,*zEnd = &zIn[nByte]; SyString sName,sValue; ph7_value *pCookie; sxu32 nOfft; /* Make sure the $_COOKIE superglobal is available */ pCookie = VmExtractSuper(&(*pVm),"_COOKIE",sizeof("_COOKIE")-1); if( pCookie == 0 || (pCookie->iFlags & MEMOBJ_HASHMAP) == 0 ){ /* $_COOKIE superglobal not available */ return SXERR_NOTFOUND; } for(;;){ /* Jump leading white spaces */ while( zIn < zEnd && SyisSpace(zIn[0]) ){ zIn++; } if( zIn >= zEnd ){ break; } /* Reset the working buffer */ SyBlobReset(pWorker); zDelimiter = zIn; /* Delimit the name[=value]; pair */ while( zDelimiter < zEnd && zDelimiter[0] != ';' ){ zDelimiter++; } zPtr = zIn; while( zPtr < zDelimiter && zPtr[0] != '=' ){ zPtr++; } /* Decode the cookie */ SyUriDecode(zIn,(sxu32)(zPtr-zIn),PH7_VmBlobConsumer,pWorker,TRUE); sName.nByte = SyBlobLength(pWorker); zPtr++; sValue.zString = 0; sValue.nByte = 0; if( zPtr < zDelimiter ){ /* Got a Cookie value */ nOfft = SyBlobLength(pWorker); SyUriDecode(zPtr,(sxu32)(zDelimiter-zPtr),PH7_VmBlobConsumer,pWorker,TRUE); SyStringInitFromBuf(&sValue,SyBlobDataAt(pWorker,nOfft),SyBlobLength(pWorker)-nOfft); } /* Synchronize pointers */ zIn = &zDelimiter[1]; /* Perform the insertion */ sName.zString = (const char *)SyBlobData(pWorker); VmHashmapInsert((ph7_hashmap *)pCookie->x.pOther, sName.zString,(int)sName.nByte, sValue.zString,(int)sValue.nByte ); } return SXRET_OK; } /* * Process a full HTTP request and populate the appropriate arrays * such as $_SERVER,$_GET,$_POST,$_COOKIE,$_REQUEST,... with the information * extracted from the raw HTTP request. As an extension Symisc introduced * the $_HEADER array which hold a copy of the processed HTTP MIME headers * and their associated values. [i.e: $_HEADER['Server'],$_HEADER['User-Agent'],...]. * This function return SXRET_OK on success. Any other return value indicates * a malformed HTTP request. */ static sxi32 VmHttpProcessRequest(ph7_vm *pVm,const char *zRequest,int nByte) { SyString *pName,*pValue,sRequest; /* Raw HTTP request */ ph7_value *pHeaderArray; /* $_HEADER superglobal (Symisc eXtension to the PHP specification)*/ SyhttpHeader *pHeader; /* MIME header */ SyhttpUri sUri; /* Parse of the raw URI*/ SyBlob sWorker; /* General purpose working buffer */ SySet sHeader; /* MIME headers set */ sxi32 iMethod; /* HTTP method [i.e: GET,POST,HEAD...]*/ sxi32 iVer; /* HTTP protocol version */ sxi32 rc; SyStringInitFromBuf(&sRequest,zRequest,nByte); SySetInit(&sHeader,&pVm->sAllocator,sizeof(SyhttpHeader)); SyBlobInit(&sWorker,&pVm->sAllocator); /* Ignore leading and trailing white spaces*/ SyStringFullTrim(&sRequest); /* Process the first line */ rc = VmHttpProcessFirstLine(&sRequest,&iMethod,&sUri,&iVer); if( rc != SXRET_OK ){ return rc; } /* Process MIME headers */ VmHttpExtractHeaders(&sRequest,&sHeader); /* * Setup $_SERVER environments */ /* 'SERVER_PROTOCOL': Name and revision of the information protocol via which the page was requested */ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "SERVER_PROTOCOL", iVer == HTTP_PROTO_10 ? "HTTP/1.0" : "HTTP/1.1", sizeof("HTTP/1.1")-1 ); /* 'REQUEST_METHOD': Which request method was used to access the page */ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "REQUEST_METHOD", iMethod == HTTP_METHOD_GET ? "GET" : (iMethod == HTTP_METHOD_POST ? "POST": (iMethod == HTTP_METHOD_PUT ? "PUT" : (iMethod == HTTP_METHOD_HEAD ? "HEAD" : "OTHER"))), -1 /* Compute attribute length automatically */ ); if( SyStringLength(&sUri.sQuery) > 0 && iMethod == HTTP_METHOD_GET ){ pValue = &sUri.sQuery; /* 'QUERY_STRING': The query string, if any, via which the page was accessed */ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "QUERY_STRING", pValue->zString, pValue->nByte ); /* Decoded the raw query */ VmHttpSplitEncodedQuery(&(*pVm),pValue,&sWorker,FALSE); } /* REQUEST_URI: The URI which was given in order to access this page; for instance, '/index.html' */ pValue = &sUri.sRaw; ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "REQUEST_URI", pValue->zString, pValue->nByte ); /* * 'PATH_INFO' * 'ORIG_PATH_INFO' * Contains any client-provided pathname information trailing the actual script filename but preceding * the query string, if available. For instance, if the current script was accessed via the URL * http://www.example.com/php/path_info.php/some/stuff?foo=bar, then $_SERVER['PATH_INFO'] would contain * /some/stuff. */ pValue = &sUri.sPath; ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "PATH_INFO", pValue->zString, pValue->nByte ); ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "ORIG_PATH_INFO", pValue->zString, pValue->nByte ); /* 'HTTP_ACCEPT': Contents of the Accept: header from the current request, if there is one */ pValue = VmHttpExtractHeaderValue(&sHeader,"Accept",sizeof("Accept")-1); if( pValue ){ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "HTTP_ACCEPT", pValue->zString, pValue->nByte ); } /* 'HTTP_ACCEPT_CHARSET': Contents of the Accept-Charset: header from the current request, if there is one. */ pValue = VmHttpExtractHeaderValue(&sHeader,"Accept-Charset",sizeof("Accept-Charset")-1); if( pValue ){ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "HTTP_ACCEPT_CHARSET", pValue->zString, pValue->nByte ); } /* 'HTTP_ACCEPT_ENCODING': Contents of the Accept-Encoding: header from the current request, if there is one. */ pValue = VmHttpExtractHeaderValue(&sHeader,"Accept-Encoding",sizeof("Accept-Encoding")-1); if( pValue ){ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "HTTP_ACCEPT_ENCODING", pValue->zString, pValue->nByte ); } /* 'HTTP_ACCEPT_LANGUAGE': Contents of the Accept-Language: header from the current request, if there is one */ pValue = VmHttpExtractHeaderValue(&sHeader,"Accept-Language",sizeof("Accept-Language")-1); if( pValue ){ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "HTTP_ACCEPT_LANGUAGE", pValue->zString, pValue->nByte ); } /* 'HTTP_CONNECTION': Contents of the Connection: header from the current request, if there is one. */ pValue = VmHttpExtractHeaderValue(&sHeader,"Connection",sizeof("Connection")-1); if( pValue ){ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "HTTP_CONNECTION", pValue->zString, pValue->nByte ); } /* 'HTTP_HOST': Contents of the Host: header from the current request, if there is one. */ pValue = VmHttpExtractHeaderValue(&sHeader,"Host",sizeof("Host")-1); if( pValue ){ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "HTTP_HOST", pValue->zString, pValue->nByte ); } /* 'HTTP_REFERER': Contents of the Referer: header from the current request, if there is one. */ pValue = VmHttpExtractHeaderValue(&sHeader,"Referer",sizeof("Referer")-1); if( pValue ){ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "HTTP_REFERER", pValue->zString, pValue->nByte ); } /* 'HTTP_USER_AGENT': Contents of the Referer: header from the current request, if there is one. */ pValue = VmHttpExtractHeaderValue(&sHeader,"User-Agent",sizeof("User-Agent")-1); if( pValue ){ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "HTTP_USER_AGENT", pValue->zString, pValue->nByte ); } /* 'PHP_AUTH_DIGEST': When doing Digest HTTP authentication this variable is set to the 'Authorization' * header sent by the client (which you should then use to make the appropriate validation). */ pValue = VmHttpExtractHeaderValue(&sHeader,"Authorization",sizeof("Authorization")-1); if( pValue ){ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "PHP_AUTH_DIGEST", pValue->zString, pValue->nByte ); ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "PHP_AUTH", pValue->zString, pValue->nByte ); } /* Install all clients HTTP headers in the $_HEADER superglobal */ pHeaderArray = VmExtractSuper(&(*pVm),"_HEADER",sizeof("_HEADER")-1); /* Iterate throw the available MIME headers*/ SySetResetCursor(&sHeader); pHeader = 0; /* stupid cc warning */ while( SXRET_OK == SySetGetNextEntry(&sHeader,(void **)&pHeader) ){ pName = &pHeader->sName; pValue = &pHeader->sValue; if( pHeaderArray && (pHeaderArray->iFlags & MEMOBJ_HASHMAP)){ /* Insert the MIME header and it's associated value */ VmHashmapInsert((ph7_hashmap *)pHeaderArray->x.pOther, pName->zString,(int)pName->nByte, pValue->zString,(int)pValue->nByte ); } if( pName->nByte == sizeof("Cookie")-1 && SyStrnicmp(pName->zString,"Cookie",sizeof("Cookie")-1) == 0 && pValue->nByte > 0){ /* Process the name=value pair and insert them in the $_COOKIE superglobal array */ VmHttpPorcessCookie(&(*pVm),&sWorker,pValue->zString,pValue->nByte); } } if( iMethod == HTTP_METHOD_POST ){ /* Extract raw POST data */ pValue = VmHttpExtractHeaderValue(&sHeader,"Content-Type",sizeof("Content-Type") - 1); if( pValue && pValue->nByte >= sizeof("application/x-www-form-urlencoded") - 1 && SyMemcmp("application/x-www-form-urlencoded",pValue->zString,pValue->nByte) == 0 ){ /* Extract POST data length */ pValue = VmHttpExtractHeaderValue(&sHeader,"Content-Length",sizeof("Content-Length") - 1); if( pValue ){ sxi32 iLen = 0; /* POST data length */ SyStrToInt32(pValue->zString,pValue->nByte,(void *)&iLen,0); if( iLen > 0 ){ /* Remove leading and trailing white spaces */ SyStringFullTrim(&sRequest); if( (int)sRequest.nByte > iLen ){ sRequest.nByte = (sxu32)iLen; } /* Decode POST data now */ VmHttpSplitEncodedQuery(&(*pVm),&sRequest,&sWorker,TRUE); } } } } /* All done,clean-up the mess left behind */ SySetRelease(&sHeader); SyBlobRelease(&sWorker); return SXRET_OK; }