1141 lines
39 KiB
C
1141 lines
39 KiB
C
/*
|
|
* 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: oo.c v1.9 FeeBSD 2012-07-17 03:44 devel <chm@symisc.net> $ */
|
|
#include "ph7int.h"
|
|
/*
|
|
* This file implement an Object Oriented (OO) subsystem for the PH7 engine.
|
|
*/
|
|
/*
|
|
* Create an empty class inheritance storage.
|
|
* Return a pointer to a storage (ph7_class_info instance) on success. NULL otherwise.
|
|
*/
|
|
PH7_PRIVATE ph7_class_info *PH7_NewClassInfo(ph7_vm *pVm, const SyString *pName) {
|
|
ph7_class_info *pClassInfo;
|
|
char *zName;
|
|
/* Allocate a new instance */
|
|
pClassInfo = (ph7_class_info *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(ph7_class_info));
|
|
if(pClassInfo == 0) {
|
|
return 0;
|
|
}
|
|
/* Zero the structure */
|
|
SyZero(pClassInfo, sizeof(ph7_class_info));
|
|
/* Duplicate class name */
|
|
zName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte);
|
|
if(zName == 0) {
|
|
SyMemBackendPoolFree(&pVm->sAllocator, pClassInfo);
|
|
return 0;
|
|
}
|
|
/* Initialize the class information storage */
|
|
SyStringInitFromBuf(&pClassInfo->sName, zName, pName->nByte);
|
|
SySetInit(&pClassInfo->sExtends, &pVm->sAllocator, sizeof(SyString));
|
|
SySetInit(&pClassInfo->sImplements, &pVm->sAllocator, sizeof(SyString));
|
|
/* All done */
|
|
return pClassInfo;
|
|
}
|
|
/*
|
|
* Create an empty class.
|
|
* Return a pointer to a raw class (ph7_class instance) on success. NULL otherwise.
|
|
*/
|
|
PH7_PRIVATE ph7_class *PH7_NewRawClass(ph7_vm *pVm, const SyString *pName) {
|
|
ph7_class *pClass;
|
|
char *zName;
|
|
/* Allocate a new instance */
|
|
pClass = (ph7_class *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(ph7_class));
|
|
if(pClass == 0) {
|
|
return 0;
|
|
}
|
|
/* Zero the structure */
|
|
SyZero(pClass, sizeof(ph7_class));
|
|
/* Duplicate class name */
|
|
zName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte);
|
|
if(zName == 0) {
|
|
SyMemBackendPoolFree(&pVm->sAllocator, pClass);
|
|
return 0;
|
|
}
|
|
/* Initialize fields */
|
|
SyStringInitFromBuf(&pClass->sName, zName, pName->nByte);
|
|
SyHashInit(&pClass->hMethod, &pVm->sAllocator, 0, 0);
|
|
SyHashInit(&pClass->hAttr, &pVm->sAllocator, 0, 0);
|
|
SyHashInit(&pClass->hDerived, &pVm->sAllocator, 0, 0);
|
|
SySetInit(&pClass->aInterface, &pVm->sAllocator, sizeof(ph7_class *));
|
|
/* All done */
|
|
return pClass;
|
|
}
|
|
/*
|
|
* Allocate and initialize a new class attribute.
|
|
* Return a pointer to the class attribute on success. NULL otherwise.
|
|
*/
|
|
PH7_PRIVATE ph7_class_attr *PH7_NewClassAttr(ph7_vm *pVm, const SyString *pName, sxu32 nLine, sxi32 iProtection, sxi32 iFlags) {
|
|
ph7_class_attr *pAttr;
|
|
char *zName;
|
|
pAttr = (ph7_class_attr *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(ph7_class_attr));
|
|
if(pAttr == 0) {
|
|
return 0;
|
|
}
|
|
/* Zero the structure */
|
|
SyZero(pAttr, sizeof(ph7_class_attr));
|
|
/* Duplicate attribute name */
|
|
zName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte);
|
|
if(zName == 0) {
|
|
SyMemBackendPoolFree(&pVm->sAllocator, pAttr);
|
|
return 0;
|
|
}
|
|
/* Initialize fields */
|
|
SySetInit(&pAttr->aByteCode, &pVm->sAllocator, sizeof(VmInstr));
|
|
SyStringInitFromBuf(&pAttr->sName, zName, pName->nByte);
|
|
pAttr->iProtection = iProtection;
|
|
pAttr->nIdx = SXU32_HIGH;
|
|
pAttr->iFlags = iFlags;
|
|
pAttr->nLine = nLine;
|
|
return pAttr;
|
|
}
|
|
/*
|
|
* Allocate and initialize a new class method.
|
|
* Return a pointer to the class method on success. NULL otherwise
|
|
* This function associate with the newly created method an automatically generated
|
|
* random unique name.
|
|
*/
|
|
PH7_PRIVATE ph7_class_method *PH7_NewClassMethod(ph7_vm *pVm, ph7_class *pClass, const SyString *pName, sxu32 nLine,
|
|
sxi32 iProtection, sxi32 iFlags, sxi32 iFuncFlags) {
|
|
ph7_class_method *pMeth;
|
|
SyHashEntry *pEntry;
|
|
SyString *pNamePtr;
|
|
char zSalt[10];
|
|
char *zName;
|
|
sxu32 nByte;
|
|
/* Allocate a new class method instance */
|
|
pMeth = (ph7_class_method *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(ph7_class_method));
|
|
if(pMeth == 0) {
|
|
return 0;
|
|
}
|
|
/* Zero the structure */
|
|
SyZero(pMeth, sizeof(ph7_class_method));
|
|
/* Check for an already installed method with the same name */
|
|
pEntry = SyHashGet(&pClass->hMethod, (const void *)pName->zString, pName->nByte);
|
|
if(pEntry == 0) {
|
|
/* Associate an unique VM name to this method */
|
|
nByte = sizeof(zSalt) + pName->nByte + SyStringLength(&pClass->sName) + sizeof(char) * 7/*[[__'\0'*/;
|
|
zName = (char *)SyMemBackendAlloc(&pVm->sAllocator, nByte);
|
|
if(zName == 0) {
|
|
SyMemBackendPoolFree(&pVm->sAllocator, pMeth);
|
|
return 0;
|
|
}
|
|
pNamePtr = &pMeth->sVmName;
|
|
/* Generate a random string */
|
|
PH7_VmRandomString(&(*pVm), zSalt, sizeof(zSalt));
|
|
pNamePtr->nByte = SyBufferFormat(zName, nByte, "[__%z@%z_%.*s]", &pClass->sName, pName, sizeof(zSalt), zSalt);
|
|
pNamePtr->zString = zName;
|
|
} else {
|
|
/* Method is condidate for 'overloading' */
|
|
ph7_class_method *pCurrent = (ph7_class_method *)pEntry->pUserData;
|
|
pNamePtr = &pMeth->sVmName;
|
|
/* Use the same VM name */
|
|
SyStringDupPtr(pNamePtr, &pCurrent->sVmName);
|
|
zName = (char *)pNamePtr->zString;
|
|
}
|
|
/* Initialize method fields */
|
|
pMeth->iProtection = iProtection;
|
|
pMeth->iFlags = iFlags;
|
|
pMeth->nLine = nLine;
|
|
PH7_VmInitFuncState(&(*pVm), &pMeth->sFunc, &zName[sizeof(char) * 4/*[__@*/ + SyStringLength(&pClass->sName)],
|
|
pName->nByte, iFuncFlags | VM_FUNC_CLASS_METHOD, pClass);
|
|
return pMeth;
|
|
}
|
|
/*
|
|
* Check if the given name have a class method associated with it.
|
|
* Return the desired method [i.e: ph7_class_method instance] on success. NULL otherwise.
|
|
*/
|
|
PH7_PRIVATE ph7_class_method *PH7_ClassExtractMethod(ph7_class *pClass, const char *zName, sxu32 nByte) {
|
|
SyHashEntry *pEntry;
|
|
/* Perform a hash lookup */
|
|
pEntry = SyHashGet(&pClass->hMethod, (const void *)zName, nByte);
|
|
if(pEntry == 0) {
|
|
/* No such entry */
|
|
return 0;
|
|
}
|
|
/* Point to the desired method */
|
|
return (ph7_class_method *)pEntry->pUserData;
|
|
}
|
|
/*
|
|
* Check if the given name is a class attribute.
|
|
* Return the desired attribute [i.e: ph7_class_attr instance] on success.NULL otherwise.
|
|
*/
|
|
PH7_PRIVATE ph7_class_attr *PH7_ClassExtractAttribute(ph7_class *pClass, const char *zName, sxu32 nByte) {
|
|
SyHashEntry *pEntry;
|
|
/* Perform a hash lookup */
|
|
pEntry = SyHashGet(&pClass->hAttr, (const void *)zName, nByte);
|
|
if(pEntry == 0) {
|
|
/* No such entry */
|
|
return 0;
|
|
}
|
|
/* Point to the desired method */
|
|
return (ph7_class_attr *)pEntry->pUserData;
|
|
}
|
|
/*
|
|
* Install a class attribute in the corresponding container.
|
|
* Return SXRET_OK on success. Any other return value indicates failure.
|
|
*/
|
|
PH7_PRIVATE sxi32 PH7_ClassInstallAttr(ph7_class *pClass, ph7_class_attr *pAttr) {
|
|
SyString *pName = &pAttr->sName;
|
|
sxi32 rc;
|
|
rc = SyHashInsert(&pClass->hAttr, (const void *)pName->zString, pName->nByte, pAttr);
|
|
return rc;
|
|
}
|
|
/*
|
|
* Install a class method in the corresponding container.
|
|
* Return SXRET_OK on success. Any other return value indicates failure.
|
|
*/
|
|
PH7_PRIVATE sxi32 PH7_ClassInstallMethod(ph7_class *pClass, ph7_class_method *pMeth) {
|
|
SyString *pName = &pMeth->sFunc.sName;
|
|
sxi32 rc;
|
|
rc = SyHashInsert(&pClass->hMethod, (const void *)pName->zString, pName->nByte, pMeth);
|
|
return rc;
|
|
}
|
|
/*
|
|
* Perform an inheritance operation.
|
|
* According to the PHP language reference manual
|
|
* When you extend a class, the subclass inherits all of the public and protected methods
|
|
* from the parent class. Unless a class Overwrites those methods, they will retain their original
|
|
* functionality.
|
|
* This is useful for defining and abstracting functionality, and permits the implementation
|
|
* of additional functionality in similar objects without the need to reimplement all of the shared
|
|
* functionality.
|
|
* Example #1 Inheritance Example
|
|
* <?php
|
|
* class foo
|
|
* {
|
|
* public function printItem($string)
|
|
* {
|
|
* echo 'Foo: ' . $string . PHP_EOL;
|
|
* }
|
|
*
|
|
* public function printPHP()
|
|
* {
|
|
* echo 'PHP is great.' . PHP_EOL;
|
|
* }
|
|
* }
|
|
* class bar extends foo
|
|
* {
|
|
* public function printItem($string)
|
|
* {
|
|
* echo 'Bar: ' . $string . PHP_EOL;
|
|
* }
|
|
* }
|
|
* $foo = new foo();
|
|
* $bar = new bar();
|
|
* $foo->printItem('baz'); // Output: 'Foo: baz'
|
|
* $foo->printPHP(); // Output: 'PHP is great'
|
|
* $bar->printItem('baz'); // Output: 'Bar: baz'
|
|
* $bar->printPHP(); // Output: 'PHP is great'
|
|
*
|
|
* This function return SXRET_OK if the inheritance operation was successfully performed.
|
|
* Any other return value indicates failure and the upper layer must generate an appropriate
|
|
* error message.
|
|
*/
|
|
PH7_PRIVATE sxi32 PH7_ClassInherit(ph7_vm *pVm, ph7_class *pSub, ph7_class *pBase) {
|
|
ph7_class_method *pMeth;
|
|
ph7_class_attr *pAttr;
|
|
SyHashEntry *pEntry;
|
|
SyString *pName;
|
|
sxi32 rc;
|
|
/* Install in the derived hashtable */
|
|
rc = SyHashInsert(&pBase->hDerived, (const void *)SyStringData(&pSub->sName), SyStringLength(&pSub->sName), pSub);
|
|
if(rc != SXRET_OK) {
|
|
return rc;
|
|
}
|
|
/* Copy public/protected attributes from the base class */
|
|
SyHashResetLoopCursor(&pBase->hAttr);
|
|
while((pEntry = SyHashGetNextEntry(&pBase->hAttr)) != 0) {
|
|
/* Make sure the private attributes are not redeclared in the subclass */
|
|
pAttr = (ph7_class_attr *)pEntry->pUserData;
|
|
pName = &pAttr->sName;
|
|
if((pEntry = SyHashGet(&pSub->hAttr, (const void *)pName->zString, pName->nByte)) != 0) {
|
|
if(pAttr->iProtection == PH7_CLASS_PROT_PRIVATE && ((ph7_class_attr *)pEntry->pUserData)->iProtection != PH7_CLASS_PROT_PUBLIC) {
|
|
/* Cannot redeclare private attribute */
|
|
rc = PH7_VmThrowError(pVm, PH7_CTX_ERR, "Private attribute '%z::%z' redeclared inside child class '%z'", &pBase->sName, pName, &pSub->sName);
|
|
if(rc == SXERR_ABORT) {
|
|
return SXERR_ABORT;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
/* Install the attribute */
|
|
if(pAttr->iProtection != PH7_CLASS_PROT_PRIVATE) {
|
|
rc = SyHashInsert(&pSub->hAttr, (const void *)pName->zString, pName->nByte, pAttr);
|
|
if(rc != SXRET_OK) {
|
|
return rc;
|
|
}
|
|
}
|
|
}
|
|
SyHashResetLoopCursor(&pBase->hMethod);
|
|
while((pEntry = SyHashGetNextEntry(&pBase->hMethod)) != 0) {
|
|
/* Make sure the private/final methods are not redeclared in the subclass */
|
|
pMeth = (ph7_class_method *)pEntry->pUserData;
|
|
pName = &pMeth->sFunc.sName;
|
|
if((pEntry = SyHashGet(&pSub->hMethod, (const void *)pName->zString, pName->nByte)) != 0) {
|
|
if(pMeth->iFlags & PH7_CLASS_ATTR_FINAL) {
|
|
/* Cannot Overwrite final method */
|
|
rc = PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot overwrite final method '%z:%z()' inside child class '%z'", &pBase->sName, pName, &pSub->sName);
|
|
if(rc == SXERR_ABORT) {
|
|
return SXERR_ABORT;
|
|
}
|
|
}
|
|
continue;
|
|
} else {
|
|
if(pMeth->iFlags & PH7_CLASS_ATTR_VIRTUAL) {
|
|
/* Virtual method must be defined in the child class */
|
|
rc = PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Virtual method '%z:%z()' must be defined inside child class '%z'", &pBase->sName, pName, &pSub->sName);
|
|
if(rc == SXERR_ABORT) {
|
|
return SXERR_ABORT;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
/* Install the method */
|
|
if(pMeth->iProtection != PH7_CLASS_PROT_PRIVATE) {
|
|
rc = SyHashInsert(&pSub->hMethod, (const void *)pName->zString, pName->nByte, pMeth);
|
|
if(rc != SXRET_OK) {
|
|
return rc;
|
|
}
|
|
}
|
|
}
|
|
/* Mark first inherited class as direct subclass */
|
|
if(!pSub->pBase) {
|
|
pSub->pBase = pBase;
|
|
}
|
|
/* All done */
|
|
return SXRET_OK;
|
|
}
|
|
/*
|
|
* Inherit an object interface from another object interface.
|
|
* According to the PHP language reference manual.
|
|
* Object interfaces allow you to create code which specifies which methods a class
|
|
* must implement, without having to define how these methods are handled.
|
|
* Interfaces are defined using the interface keyword, in the same way as a standard
|
|
* class, but without any of the methods having their contents defined.
|
|
* All methods declared in an interface must be public, this is the nature of an interface.
|
|
*
|
|
* This function return SXRET_OK if the interface inheritance operation was successfully performed.
|
|
* Any other return value indicates failure and the upper layer must generate an appropriate
|
|
* error message.
|
|
*/
|
|
PH7_PRIVATE sxi32 PH7_ClassInterfaceInherit(ph7_class *pSub, ph7_class *pBase) {
|
|
ph7_class_method *pMeth;
|
|
ph7_class_attr *pAttr;
|
|
SyHashEntry *pEntry;
|
|
SyString *pName;
|
|
sxi32 rc;
|
|
/* Install in the derived hashtable */
|
|
SyHashInsert(&pBase->hDerived, (const void *)SyStringData(&pSub->sName), SyStringLength(&pSub->sName), pSub);
|
|
SyHashResetLoopCursor(&pBase->hAttr);
|
|
/* Copy constants */
|
|
while((pEntry = SyHashGetNextEntry(&pBase->hAttr)) != 0) {
|
|
/* Make sure the constants are not redeclared in the subclass */
|
|
pAttr = (ph7_class_attr *)pEntry->pUserData;
|
|
pName = &pAttr->sName;
|
|
if(SyHashGet(&pSub->hAttr, (const void *)pName->zString, pName->nByte) == 0) {
|
|
/* Install the constant in the subclass */
|
|
rc = SyHashInsert(&pSub->hAttr, (const void *)pName->zString, pName->nByte, pAttr);
|
|
if(rc != SXRET_OK) {
|
|
return rc;
|
|
}
|
|
}
|
|
}
|
|
SyHashResetLoopCursor(&pBase->hMethod);
|
|
/* Copy methods signature */
|
|
while((pEntry = SyHashGetNextEntry(&pBase->hMethod)) != 0) {
|
|
/* Make sure the method are not redeclared in the subclass */
|
|
pMeth = (ph7_class_method *)pEntry->pUserData;
|
|
pName = &pMeth->sFunc.sName;
|
|
if(SyHashGet(&pSub->hMethod, (const void *)pName->zString, pName->nByte) == 0) {
|
|
/* Install the method */
|
|
rc = SyHashInsert(&pSub->hMethod, (const void *)pName->zString, pName->nByte, pMeth);
|
|
if(rc != SXRET_OK) {
|
|
return rc;
|
|
}
|
|
}
|
|
}
|
|
/* Mark as subclass */
|
|
pSub->pBase = pBase;
|
|
/* All done */
|
|
return SXRET_OK;
|
|
}
|
|
/*
|
|
* Implements an object interface in the given main class.
|
|
* According to the PHP language reference manual.
|
|
* Object interfaces allow you to create code which specifies which methods a class
|
|
* must implement, without having to define how these methods are handled.
|
|
* Interfaces are defined using the interface keyword, in the same way as a standard
|
|
* class, but without any of the methods having their contents defined.
|
|
* All methods declared in an interface must be public, this is the nature of an interface.
|
|
*
|
|
* This function return SXRET_OK if the interface was successfully implemented.
|
|
* Any other return value indicates failure and the upper layer must generate an appropriate
|
|
* error message.
|
|
*/
|
|
PH7_PRIVATE sxi32 PH7_ClassImplement(ph7_class *pMain, ph7_class *pInterface) {
|
|
ph7_class_attr *pAttr;
|
|
SyHashEntry *pEntry;
|
|
SyString *pName;
|
|
sxi32 rc;
|
|
/* First off,copy all constants declared inside the interface */
|
|
SyHashResetLoopCursor(&pInterface->hAttr);
|
|
while((pEntry = SyHashGetNextEntry(&pInterface->hAttr)) != 0) {
|
|
/* Point to the constant declaration */
|
|
pAttr = (ph7_class_attr *)pEntry->pUserData;
|
|
pName = &pAttr->sName;
|
|
/* Make sure the attribute is not redeclared in the main class */
|
|
if(SyHashGet(&pMain->hAttr, pName->zString, pName->nByte) == 0) {
|
|
/* Install the attribute */
|
|
rc = SyHashInsert(&pMain->hAttr, pName->zString, pName->nByte, pAttr);
|
|
if(rc != SXRET_OK) {
|
|
return rc;
|
|
}
|
|
}
|
|
}
|
|
/* Install in the interface container */
|
|
SySetPut(&pMain->aInterface, (const void *)&pInterface);
|
|
/* TICKET 1433-49/1: Symisc eXtension
|
|
* A class may not implement all declared interface methods,so there
|
|
* is no need for a method installer loop here.
|
|
*/
|
|
return SXRET_OK;
|
|
}
|
|
/*
|
|
* Create a class instance [i.e: Object in the PHP jargon] at run-time.
|
|
* The following function is called when an object is created at run-time
|
|
* typically when the PH7_OP_NEW/PH7_OP_CLONE instructions are executed.
|
|
* Notes on object creation.
|
|
*
|
|
* According to PHP language reference manual.
|
|
* To create an instance of a class, the new keyword must be used. An object will always
|
|
* be created unless the object has a constructor defined that throws an exception on error.
|
|
* Classes should be defined before instantiation (and in some cases this is a requirement).
|
|
* If a string containing the name of a class is used with new, a new instance of that class
|
|
* will be created. If the class is in a namespace, its fully qualified name must be used when
|
|
* doing this.
|
|
* Example #3 Creating an instance
|
|
* <?php
|
|
* $instance = new SimpleClass();
|
|
* // This can also be done with a variable:
|
|
* $className = 'Foo';
|
|
* $instance = new $className(); // Foo()
|
|
* ?>
|
|
* In the class context, it is possible to create a new object by new self and new parent.
|
|
* When assigning an already created instance of a class to a new variable, the new variable
|
|
* will access the same instance as the object that was assigned. This behaviour is the same
|
|
* when passing instances to a function. A copy of an already created object can be made by
|
|
* cloning it.
|
|
* Example #4 Object Assignment
|
|
* <?php
|
|
* class SimpleClass(){
|
|
* public $var;
|
|
* };
|
|
* $instance = new SimpleClass();
|
|
* $assigned = $instance;
|
|
* $reference =& $instance;
|
|
* $instance->var = '$assigned will have this value';
|
|
* $instance = null; // $instance and $reference become null
|
|
* var_dump($instance);
|
|
* var_dump($reference);
|
|
* var_dump($assigned);
|
|
* ?>
|
|
* The above example will output:
|
|
* NULL
|
|
* NULL
|
|
* object(SimpleClass)#1 (1) {
|
|
* ["var"]=>
|
|
* string(30) "$assigned will have this value"
|
|
* }
|
|
* Example #5 Creating new objects
|
|
* <?php
|
|
* class Test
|
|
* {
|
|
* static public function getNew()
|
|
* {
|
|
* return new static;
|
|
* }
|
|
* }
|
|
* class Child extends Test
|
|
* {}
|
|
* $obj1 = new Test();
|
|
* $obj2 = new $obj1;
|
|
* var_dump($obj1 !== $obj2);
|
|
* $obj3 = Test::getNew();
|
|
* var_dump($obj3 instanceof Test);
|
|
* $obj4 = Child::getNew();
|
|
* var_dump($obj4 instanceof Child);
|
|
* ?>
|
|
* The above example will output:
|
|
* bool(true)
|
|
* bool(true)
|
|
* bool(true)
|
|
* Note that Symisc Systems have introduced powerfull extension to
|
|
* OO subsystem. For example a class attribute may have any complex
|
|
* expression associated with it when declaring the attribute unlike
|
|
* the standard PHP engine which would allow a single value.
|
|
* Example:
|
|
* class myClass{
|
|
* public $var = 25<<1+foo()/bar();
|
|
* };
|
|
* Refer to the official documentation for more information.
|
|
*/
|
|
static ph7_class_instance *NewClassInstance(ph7_vm *pVm, ph7_class *pClass) {
|
|
ph7_class_instance *pThis;
|
|
/* Allocate a new instance */
|
|
pThis = (ph7_class_instance *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(ph7_class_instance));
|
|
if(pThis == 0) {
|
|
return 0;
|
|
}
|
|
/* Zero the structure */
|
|
SyZero(pThis, sizeof(ph7_class_instance));
|
|
/* Initialize fields */
|
|
pThis->iRef = 1;
|
|
pThis->pVm = pVm;
|
|
pThis->pClass = pClass;
|
|
SyHashInit(&pThis->hAttr, &pVm->sAllocator, 0, 0);
|
|
return pThis;
|
|
}
|
|
/*
|
|
* Wrapper around the NewClassInstance() function defined above.
|
|
* See the block comment above for more information.
|
|
*/
|
|
PH7_PRIVATE ph7_class_instance *PH7_NewClassInstance(ph7_vm *pVm, ph7_class *pClass) {
|
|
ph7_class_instance *pNew;
|
|
sxi32 rc;
|
|
pNew = NewClassInstance(&(*pVm), &(*pClass));
|
|
if(pNew == 0) {
|
|
return 0;
|
|
}
|
|
/* Associate a private VM frame with this class instance */
|
|
rc = PH7_VmCreateClassInstanceFrame(&(*pVm), pNew);
|
|
if(rc != SXRET_OK) {
|
|
SyMemBackendPoolFree(&pVm->sAllocator, pNew);
|
|
return 0;
|
|
}
|
|
return pNew;
|
|
}
|
|
/*
|
|
* Extract the value of a class instance [i.e: Object in the PHP jargon] attribute.
|
|
* This function never fail.
|
|
*/
|
|
static ph7_value *ExtractClassAttrValue(ph7_vm *pVm, VmClassAttr *pAttr) {
|
|
/* Extract the value */
|
|
ph7_value *pValue;
|
|
pValue = (ph7_value *)SySetAt(&pVm->aMemObj, pAttr->nIdx);
|
|
return pValue;
|
|
}
|
|
/*
|
|
* Perform a clone operation on a class instance [i.e: Object in the PHP jargon].
|
|
* The following function is called when an object is cloned at run-time
|
|
* typically when the PH7_OP_CLONE instruction is executed.
|
|
* Notes on object cloning.
|
|
*
|
|
* According to PHP language reference manual.
|
|
* Creating a copy of an object with fully replicated properties is not always the wanted behavior.
|
|
* A good example of the need for copy constructors. Another example is if your object holds a reference
|
|
* to another object which it uses and when you replicate the parent object you want to create
|
|
* a new instance of this other object so that the replica has its own separate copy.
|
|
* An object copy is created by using the clone keyword (which calls the object's __clone() method if possible).
|
|
* An object's __clone() method cannot be called directly.
|
|
* $copy_of_object = clone $object;
|
|
* When an object is cloned, PHP 5 will perform a shallow copy of all of the object's properties.
|
|
* Any properties that are references to other variables, will remain references.
|
|
* Once the cloning is complete, if a __clone() method is defined, then the newly created object's __clone() method
|
|
* will be called, to allow any necessary properties that need to be changed.
|
|
* Example #1 Cloning an object
|
|
* <?php
|
|
* class SubObject
|
|
* {
|
|
* static $instances = 0;
|
|
* public $instance;
|
|
*
|
|
* public function __construct() {
|
|
* $this->instance = ++self::$instances;
|
|
* }
|
|
*
|
|
* public function __clone() {
|
|
* $this->instance = ++self::$instances;
|
|
* }
|
|
* }
|
|
*
|
|
* class MyCloneable
|
|
* {
|
|
* public $object1;
|
|
* public $object2;
|
|
*
|
|
* function __clone()
|
|
* {
|
|
* // Force a copy of this->object, otherwise
|
|
* // it will point to same object.
|
|
* $this->object1 = clone $this->object1;
|
|
* }
|
|
* }
|
|
* $obj = new MyCloneable();
|
|
* $obj->object1 = new SubObject();
|
|
* $obj->object2 = new SubObject();
|
|
* $obj2 = clone $obj;
|
|
* print("Original Object:\n");
|
|
* print_r($obj);
|
|
* print("Cloned Object:\n");
|
|
* print_r($obj2);
|
|
* ?>
|
|
* The above example will output:
|
|
* Original Object:
|
|
* MyCloneable Object
|
|
* (
|
|
* [object1] => SubObject Object
|
|
* (
|
|
* [instance] => 1
|
|
* )
|
|
*
|
|
* [object2] => SubObject Object
|
|
* (
|
|
* [instance] => 2
|
|
* )
|
|
*
|
|
* )
|
|
* Cloned Object:
|
|
* MyCloneable Object
|
|
* (
|
|
* [object1] => SubObject Object
|
|
* (
|
|
* [instance] => 3
|
|
* )
|
|
*
|
|
* [object2] => SubObject Object
|
|
* (
|
|
* [instance] => 2
|
|
* )
|
|
* )
|
|
*/
|
|
PH7_PRIVATE ph7_class_instance *PH7_CloneClassInstance(ph7_class_instance *pSrc) {
|
|
ph7_class_instance *pClone;
|
|
ph7_class_method *pMethod;
|
|
SyHashEntry *pEntry2;
|
|
SyHashEntry *pEntry;
|
|
ph7_vm *pVm;
|
|
sxi32 rc;
|
|
/* Allocate a new instance */
|
|
pVm = pSrc->pVm;
|
|
pClone = NewClassInstance(pVm, pSrc->pClass);
|
|
if(pClone == 0) {
|
|
return 0;
|
|
}
|
|
/* Associate a private VM frame with this class instance */
|
|
rc = PH7_VmCreateClassInstanceFrame(pVm, pClone);
|
|
if(rc != SXRET_OK) {
|
|
SyMemBackendPoolFree(&pVm->sAllocator, pClone);
|
|
return 0;
|
|
}
|
|
/* Duplicate object values */
|
|
SyHashResetLoopCursor(&pSrc->hAttr);
|
|
SyHashResetLoopCursor(&pClone->hAttr);
|
|
while((pEntry = SyHashGetNextEntry(&pSrc->hAttr)) != 0 && (pEntry2 = SyHashGetNextEntry(&pClone->hAttr)) != 0) {
|
|
VmClassAttr *pSrcAttr = (VmClassAttr *)pEntry->pUserData;
|
|
VmClassAttr *pDestAttr = (VmClassAttr *)pEntry2->pUserData;
|
|
/* Duplicate non-static attribute */
|
|
if((pSrcAttr->pAttr->iFlags & (PH7_CLASS_ATTR_STATIC | PH7_CLASS_ATTR_CONSTANT)) == 0) {
|
|
ph7_value *pvSrc, *pvDest;
|
|
pvSrc = ExtractClassAttrValue(pVm, pSrcAttr);
|
|
pvDest = ExtractClassAttrValue(pVm, pDestAttr);
|
|
if(pvSrc && pvDest) {
|
|
PH7_MemObjStore(pvSrc, pvDest);
|
|
}
|
|
}
|
|
}
|
|
/* call the __clone method on the cloned object if available */
|
|
pMethod = PH7_ClassExtractMethod(pClone->pClass, "__clone", sizeof("__clone") - 1);
|
|
if(pMethod) {
|
|
if(pMethod->iCloneDepth < 16) {
|
|
pMethod->iCloneDepth++;
|
|
PH7_VmCallClassMethod(pVm, pClone, pMethod, 0, 0, 0);
|
|
} else {
|
|
/* Nesting limit reached */
|
|
PH7_VmThrowError(pVm, PH7_CTX_ERR, "Object clone limit reached");
|
|
}
|
|
/* Reset the cursor */
|
|
pMethod->iCloneDepth = 0;
|
|
}
|
|
/* Return the cloned object */
|
|
return pClone;
|
|
}
|
|
#define CLASS_INSTANCE_DESTROYED 0x001 /* Instance is released */
|
|
/*
|
|
* Release a class instance [i.e: Object in the PHP jargon] and invoke any defined destructor.
|
|
* This routine is invoked as soon as there are no other references to a particular
|
|
* class instance.
|
|
*/
|
|
static void PH7_ClassInstanceRelease(ph7_class_instance *pThis) {
|
|
ph7_class_method *pDestr;
|
|
SyHashEntry *pEntry;
|
|
ph7_class *pClass;
|
|
ph7_vm *pVm;
|
|
if(pThis->iFlags & CLASS_INSTANCE_DESTROYED) {
|
|
/*
|
|
* Already destroyed,return immediately.
|
|
* This could happend if someone perform unset($this) in the destructor body.
|
|
*/
|
|
return;
|
|
}
|
|
/* Mark as destroyed */
|
|
pThis->iFlags |= CLASS_INSTANCE_DESTROYED;
|
|
/* Invoke any defined destructor if available */
|
|
pVm = pThis->pVm;
|
|
pClass = pThis->pClass;
|
|
pDestr = PH7_ClassExtractMethod(pClass, "__destruct", sizeof("__destruct") - 1);
|
|
if(pDestr) {
|
|
/* Invoke the destructor */
|
|
pThis->iRef = 2; /* Prevent garbage collection */
|
|
PH7_VmCallClassMethod(pVm, pThis, pDestr, 0, 0, 0);
|
|
}
|
|
/* Release non-static attributes */
|
|
SyHashResetLoopCursor(&pThis->hAttr);
|
|
while((pEntry = SyHashGetNextEntry(&pThis->hAttr)) != 0) {
|
|
VmClassAttr *pVmAttr = (VmClassAttr *)pEntry->pUserData;
|
|
if((pVmAttr->pAttr->iFlags & (PH7_CLASS_ATTR_STATIC | PH7_CLASS_ATTR_CONSTANT)) == 0) {
|
|
PH7_VmUnsetMemObj(pVm, pVmAttr->nIdx, TRUE);
|
|
}
|
|
SyMemBackendPoolFree(&pVm->sAllocator, pVmAttr);
|
|
}
|
|
/* Release the whole structure */
|
|
SyHashRelease(&pThis->hAttr);
|
|
SyMemBackendPoolFree(&pVm->sAllocator, pThis);
|
|
}
|
|
/*
|
|
* Decrement the reference count of a class instance [i.e Object in the PHP jargon].
|
|
* If the reference count reaches zero,release the whole instance.
|
|
*/
|
|
PH7_PRIVATE void PH7_ClassInstanceUnref(ph7_class_instance *pThis) {
|
|
pThis->iRef--;
|
|
if(pThis->iRef < 1) {
|
|
/* No more reference to this instance */
|
|
PH7_ClassInstanceRelease(&(*pThis));
|
|
}
|
|
}
|
|
/*
|
|
* Compare two class instances [i.e: Objects in the PHP jargon]
|
|
* Note on objects comparison:
|
|
* According to the PHP langauge reference manual
|
|
* When using the comparison operator (==), object variables are compared in a simple manner
|
|
* namely: Two object instances are equal if they have the same attributes and values, and are
|
|
* instances of the same class.
|
|
* On the other hand, when using the identity operator (===), object variables are identical
|
|
* if and only if they refer to the same instance of the same class.
|
|
* An example will clarify these rules.
|
|
* Example #1 Example of object comparison
|
|
* <?php
|
|
* function bool2str($bool)
|
|
* {
|
|
* if ($bool === false) {
|
|
* return 'FALSE';
|
|
* } else {
|
|
* return 'TRUE';
|
|
* }
|
|
* }
|
|
* function compareObjects(&$o1, &$o2)
|
|
* {
|
|
* echo 'o1 == o2 : ' . bool2str($o1 == $o2) . "\n";
|
|
* echo 'o1 != o2 : ' . bool2str($o1 != $o2) . "\n";
|
|
* echo 'o1 === o2 : ' . bool2str($o1 === $o2) . "\n";
|
|
* echo 'o1 !== o2 : ' . bool2str($o1 !== $o2) . "\n";
|
|
* }
|
|
* class Flag
|
|
* {
|
|
* public $flag;
|
|
*
|
|
* function Flag($flag = true) {
|
|
* $this->flag = $flag;
|
|
* }
|
|
* }
|
|
*
|
|
* class OtherFlag
|
|
* {
|
|
* public $flag;
|
|
*
|
|
* function OtherFlag($flag = true) {
|
|
* $this->flag = $flag;
|
|
* }
|
|
* }
|
|
*
|
|
* $o = new Flag();
|
|
* $p = new Flag();
|
|
* $q = $o;
|
|
* $r = new OtherFlag();
|
|
*
|
|
* echo "Two instances of the same class\n";
|
|
* compareObjects($o, $p);
|
|
* echo "\nTwo references to the same instance\n";
|
|
* compareObjects($o, $q);
|
|
* echo "\nInstances of two different classes\n";
|
|
* compareObjects($o, $r);
|
|
* ?>
|
|
* The above example will output:
|
|
* Two instances of the same class
|
|
* o1 == o2 : TRUE
|
|
* o1 != o2 : FALSE
|
|
* o1 === o2 : FALSE
|
|
* o1 !== o2 : TRUE
|
|
* Two references to the same instance
|
|
* o1 == o2 : TRUE
|
|
* o1 != o2 : FALSE
|
|
* o1 === o2 : TRUE
|
|
* o1 !== o2 : FALSE
|
|
* Instances of two different classes
|
|
* o1 == o2 : FALSE
|
|
* o1 != o2 : TRUE
|
|
* o1 === o2 : FALSE
|
|
* o1 !== o2 : TRUE
|
|
*
|
|
* This function return 0 if the objects are equals according to the comprison rules defined above.
|
|
* Any other return values indicates difference.
|
|
*/
|
|
PH7_PRIVATE sxi32 PH7_ClassInstanceCmp(ph7_class_instance *pLeft, ph7_class_instance *pRight, int bStrict, int iNest) {
|
|
SyHashEntry *pEntry, *pEntry2;
|
|
ph7_value sV1, sV2;
|
|
sxi32 rc;
|
|
if(iNest > 31) {
|
|
/* Nesting limit reached */
|
|
PH7_VmThrowError(pLeft->pVm, PH7_CTX_ERR, "Nesting limit reached, probably infinite recursion");
|
|
return 1;
|
|
}
|
|
/* Comparison is performed only if the objects are instance of the same class */
|
|
if(pLeft->pClass != pRight->pClass) {
|
|
return 1;
|
|
}
|
|
if(bStrict) {
|
|
/*
|
|
* According to the PHP language reference manual:
|
|
* when using the identity operator (===), object variables
|
|
* are identical if and only if they refer to the same instance
|
|
* of the same class.
|
|
*/
|
|
return !(pLeft == pRight);
|
|
}
|
|
/*
|
|
* Attribute comparison.
|
|
* According to the PHP reference manual:
|
|
* When using the comparison operator (==), object variables are compared
|
|
* in a simple manner, namely: Two object instances are equal if they have
|
|
* the same attributes and values, and are instances of the same class.
|
|
*/
|
|
if(pLeft == pRight) {
|
|
/* Same instance,don't bother processing,object are equals */
|
|
return 0;
|
|
}
|
|
SyHashResetLoopCursor(&pLeft->hAttr);
|
|
SyHashResetLoopCursor(&pRight->hAttr);
|
|
PH7_MemObjInit(pLeft->pVm, &sV1);
|
|
PH7_MemObjInit(pLeft->pVm, &sV2);
|
|
sV1.nIdx = sV2.nIdx = SXU32_HIGH;
|
|
while((pEntry = SyHashGetNextEntry(&pLeft->hAttr)) != 0 && (pEntry2 = SyHashGetNextEntry(&pRight->hAttr)) != 0) {
|
|
VmClassAttr *p1 = (VmClassAttr *)pEntry->pUserData;
|
|
VmClassAttr *p2 = (VmClassAttr *)pEntry2->pUserData;
|
|
/* Compare only non-static attribute */
|
|
if((p1->pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT | PH7_CLASS_ATTR_STATIC)) == 0) {
|
|
ph7_value *pL, *pR;
|
|
pL = ExtractClassAttrValue(pLeft->pVm, p1);
|
|
pR = ExtractClassAttrValue(pRight->pVm, p2);
|
|
if(pL && pR) {
|
|
PH7_MemObjLoad(pL, &sV1);
|
|
PH7_MemObjLoad(pR, &sV2);
|
|
/* Compare the two values now */
|
|
rc = PH7_MemObjCmp(&sV1, &sV2, bStrict, iNest + 1);
|
|
PH7_MemObjRelease(&sV1);
|
|
PH7_MemObjRelease(&sV2);
|
|
if(rc != 0) {
|
|
/* Not equals */
|
|
return rc;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* Object are equals */
|
|
return 0;
|
|
}
|
|
/*
|
|
* Dump a class instance and the store the dump in the BLOB given
|
|
* as the first argument.
|
|
* Note that only non-static/non-constants attribute are dumped.
|
|
* This function is typically invoked when the user issue a call
|
|
* to [var_dump(),var_export(),print_r(),...].
|
|
* This function SXRET_OK on success. Any other return value including
|
|
* SXERR_LIMIT(infinite recursion) indicates failure.
|
|
*/
|
|
PH7_PRIVATE sxi32 PH7_ClassInstanceDump(SyBlob *pOut, ph7_class_instance *pThis, int ShowType, int nTab, int nDepth) {
|
|
SyHashEntry *pEntry;
|
|
ph7_value *pValue;
|
|
sxi32 rc;
|
|
int i;
|
|
if(nDepth > 31) {
|
|
static const char zInfinite[] = "Nesting limit reached: Infinite recursion?";
|
|
/* Nesting limit reached..halt immediately*/
|
|
SyBlobAppend(&(*pOut), zInfinite, sizeof(zInfinite) - 1);
|
|
if(ShowType) {
|
|
SyBlobAppend(&(*pOut), ")", sizeof(char));
|
|
}
|
|
return SXERR_LIMIT;
|
|
}
|
|
rc = SXRET_OK;
|
|
if(!ShowType) {
|
|
SyBlobAppend(&(*pOut), "Object(", sizeof("Object(") - 1);
|
|
}
|
|
/* Append class name */
|
|
SyBlobFormat(&(*pOut), "%z) {", &pThis->pClass->sName);
|
|
#ifdef __WINNT__
|
|
SyBlobAppend(&(*pOut), "\r\n", sizeof("\r\n") - 1);
|
|
#else
|
|
SyBlobAppend(&(*pOut), "\n", sizeof(char));
|
|
#endif
|
|
/* Dump object attributes */
|
|
SyHashResetLoopCursor(&pThis->hAttr);
|
|
while((pEntry = SyHashGetNextEntry(&pThis->hAttr)) != 0) {
|
|
VmClassAttr *pVmAttr = (VmClassAttr *)pEntry->pUserData;
|
|
if((pVmAttr->pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT | PH7_CLASS_ATTR_STATIC)) == 0) {
|
|
/* Dump non-static/constant attribute only */
|
|
for(i = 0 ; i < nTab ; i++) {
|
|
SyBlobAppend(&(*pOut), " ", sizeof(char));
|
|
}
|
|
pValue = ExtractClassAttrValue(pThis->pVm, pVmAttr);
|
|
if(pValue) {
|
|
SyBlobFormat(&(*pOut), "['%z'] =>", &pVmAttr->pAttr->sName);
|
|
#ifdef __WINNT__
|
|
SyBlobAppend(&(*pOut), "\r\n", sizeof("\r\n") - 1);
|
|
#else
|
|
SyBlobAppend(&(*pOut), "\n", sizeof(char));
|
|
#endif
|
|
rc = PH7_MemObjDump(&(*pOut), pValue, ShowType, nTab + 1, nDepth, 0);
|
|
if(rc == SXERR_LIMIT) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for(i = 0 ; i < nTab ; i++) {
|
|
SyBlobAppend(&(*pOut), " ", sizeof(char));
|
|
}
|
|
SyBlobAppend(&(*pOut), "}", sizeof(char));
|
|
return rc;
|
|
}
|
|
/*
|
|
* Call a magic method [i.e: __toString(),__toBool(),__Invoke()...]
|
|
* Return SXRET_OK on successfull call. Any other return value indicates failure.
|
|
* Notes on magic methods.
|
|
* According to the PHP language reference manual.
|
|
* The function names __construct(), __destruct(), __call(), __callStatic()
|
|
* __get(), __toString(), __invoke(), __clone() are magical in PHP classes.
|
|
* You cannot have functions with these names in any of your classes unless
|
|
* you want the magic functionality associated with them.
|
|
* Example of magical methods:
|
|
* __toString()
|
|
* The __toString() method allows a class to decide how it will react when it is treated like
|
|
* a string. For example, what echo $obj; will print. This method must return a string.
|
|
* Example #2 Simple example
|
|
* <?php
|
|
* // Declare a simple class
|
|
* class TestClass
|
|
* {
|
|
* public $foo;
|
|
*
|
|
* public function __construct($foo)
|
|
* {
|
|
* $this->foo = $foo;
|
|
* }
|
|
*
|
|
* public function __toString()
|
|
* {
|
|
* return $this->foo;
|
|
* }
|
|
* }
|
|
* $class = new TestClass('Hello');
|
|
* echo $class;
|
|
* ?>
|
|
* The above example will output:
|
|
* Hello
|
|
*
|
|
* Note that PH7 does not support all the magical method and introduces __toFloat(),__toInt()
|
|
* which have the same behaviour as __toString() but for float and integer types
|
|
* respectively.
|
|
* Refer to the official documentation for more information.
|
|
*/
|
|
PH7_PRIVATE sxi32 PH7_ClassInstanceCallMagicMethod(
|
|
ph7_vm *pVm, /* VM that own all this stuff */
|
|
ph7_class *pClass, /* Target class */
|
|
ph7_class_instance *pThis, /* Target object */
|
|
const char *zMethod, /* Magic method name [i.e: __toString()]*/
|
|
sxu32 nByte, /* zMethod length*/
|
|
const SyString *pAttrName /* Attribute name */
|
|
) {
|
|
ph7_value *apArg[2] = { 0, 0 };
|
|
ph7_class_method *pMeth;
|
|
ph7_value sAttr; /* cc warning */
|
|
sxi32 rc;
|
|
int nArg;
|
|
/* Make sure the magic method is available */
|
|
pMeth = PH7_ClassExtractMethod(&(*pClass), zMethod, nByte);
|
|
if(pMeth == 0) {
|
|
/* No such method,return immediately */
|
|
return SXERR_NOTFOUND;
|
|
}
|
|
nArg = 0;
|
|
/* Copy arguments */
|
|
if(pAttrName) {
|
|
PH7_MemObjInitFromString(pVm, &sAttr, pAttrName);
|
|
sAttr.nIdx = SXU32_HIGH; /* Mark as constant */
|
|
apArg[0] = &sAttr;
|
|
nArg = 1;
|
|
}
|
|
/* Call the magic method now */
|
|
rc = PH7_VmCallClassMethod(pVm, &(*pThis), pMeth, 0, nArg, apArg);
|
|
/* Clean up */
|
|
if(pAttrName) {
|
|
PH7_MemObjRelease(&sAttr);
|
|
}
|
|
return rc;
|
|
}
|
|
/*
|
|
* Extract the value of a class instance [i.e: Object in the PHP jargon].
|
|
* This function is simply a wrapper on ExtractClassAttrValue().
|
|
*/
|
|
PH7_PRIVATE ph7_value *PH7_ClassInstanceExtractAttrValue(ph7_class_instance *pThis, VmClassAttr *pAttr) {
|
|
/* Extract the attribute value */
|
|
ph7_value *pValue;
|
|
pValue = ExtractClassAttrValue(pThis->pVm, pAttr);
|
|
return pValue;
|
|
}
|
|
/*
|
|
* Convert a class instance [i.e: Object in the PHP jargon] into a hashmap [i.e: array in the PHP jargon].
|
|
* Return SXRET_OK on success. Any other value indicates failure.
|
|
* Note on object conversion to array:
|
|
* Acccording to the PHP language reference manual
|
|
* If an object is converted to an array, the result is an array whose elements are the object's properties.
|
|
* The keys are the member variable names.
|
|
*
|
|
* The following example:
|
|
* class Test {
|
|
* public $A = 25<<1; // 50
|
|
* public $c = rand_str(3); // Random string of length 3
|
|
* public $d = rand() & 1023; // Random number between 0..1023
|
|
* }
|
|
* var_dump((array) new Test());
|
|
* Will output:
|
|
* array(3) {
|
|
* [A] =>
|
|
* int(50)
|
|
* [c] =>
|
|
* string(3 'aps')
|
|
* [d] =>
|
|
* int(991)
|
|
* }
|
|
* You have noticed that PH7 allow class attributes [i.e: $a,$c,$d in the example above]
|
|
* have any complex expression (even function calls/anonymous functions) as their default
|
|
* value unlike the standard PHP engine.
|
|
* This is a very powerful feature that you have to look at.
|
|
*/
|
|
PH7_PRIVATE sxi32 PH7_ClassInstanceToHashmap(ph7_class_instance *pThis, ph7_hashmap *pMap) {
|
|
SyHashEntry *pEntry;
|
|
SyString *pAttrName;
|
|
VmClassAttr *pAttr;
|
|
ph7_value *pValue;
|
|
ph7_value sName;
|
|
/* Reset the loop cursor */
|
|
SyHashResetLoopCursor(&pThis->hAttr);
|
|
PH7_MemObjInitFromString(pThis->pVm, &sName, 0);
|
|
while((pEntry = SyHashGetNextEntry(&pThis->hAttr)) != 0) {
|
|
/* Point to the current attribute */
|
|
pAttr = (VmClassAttr *)pEntry->pUserData;
|
|
/* Extract attribute value */
|
|
pValue = ExtractClassAttrValue(pThis->pVm, pAttr);
|
|
if(pValue) {
|
|
/* Build attribute name */
|
|
pAttrName = &pAttr->pAttr->sName;
|
|
PH7_MemObjStringAppend(&sName, pAttrName->zString, pAttrName->nByte);
|
|
/* Perform the insertion */
|
|
PH7_HashmapInsert(pMap, &sName, pValue);
|
|
/* Reset the string cursor */
|
|
SyBlobReset(&sName.sBlob);
|
|
}
|
|
}
|
|
PH7_MemObjRelease(&sName);
|
|
return SXRET_OK;
|
|
}
|
|
/*
|
|
* Iterate throw class attributes and invoke the given callback [i.e: xWalk()] for each
|
|
* retrieved attribute.
|
|
* Note that argument are passed to the callback by copy. That is,any modification to
|
|
* the attribute value in the callback body will not alter the real attribute value.
|
|
* If the callback wishes to abort processing [i.e: it's invocation] it must return
|
|
* a value different from PH7_OK.
|
|
* Refer to [ph7_object_walk()] for more information.
|
|
*/
|
|
PH7_PRIVATE sxi32 PH7_ClassInstanceWalk(
|
|
ph7_class_instance *pThis, /* Target object */
|
|
int (*xWalk)(const char *, ph7_value *, void *), /* Walker callback */
|
|
void *pUserData /* Last argument to xWalk() */
|
|
) {
|
|
SyHashEntry *pEntry; /* Hash entry */
|
|
VmClassAttr *pAttr; /* Pointer to the attribute */
|
|
ph7_value *pValue; /* Attribute value */
|
|
ph7_value sValue; /* Copy of the attribute value */
|
|
int rc;
|
|
/* Reset the loop cursor */
|
|
SyHashResetLoopCursor(&pThis->hAttr);
|
|
PH7_MemObjInit(pThis->pVm, &sValue);
|
|
/* Start the walk process */
|
|
while((pEntry = SyHashGetNextEntry(&pThis->hAttr)) != 0) {
|
|
/* Point to the current attribute */
|
|
pAttr = (VmClassAttr *)pEntry->pUserData;
|
|
/* Extract attribute value */
|
|
pValue = ExtractClassAttrValue(pThis->pVm, pAttr);
|
|
if(pValue) {
|
|
PH7_MemObjLoad(pValue, &sValue);
|
|
/* Invoke the supplied callback */
|
|
rc = xWalk(SyStringData(&pAttr->pAttr->sName), &sValue, pUserData);
|
|
PH7_MemObjRelease(&sValue);
|
|
if(rc != PH7_OK) {
|
|
/* User callback request an operation abort */
|
|
return SXERR_ABORT;
|
|
}
|
|
}
|
|
}
|
|
/* All done */
|
|
return SXRET_OK;
|
|
}
|
|
/*
|
|
* Extract a class attribute value.
|
|
* Return a pointer to the attribute value on success. Otherwise NULL.
|
|
* Note:
|
|
* Access to static and constant attribute is not allowed. That is,the function
|
|
* will return NULL in case someone (host-application code) try to extract
|
|
* a static/constant attribute.
|
|
*/
|
|
PH7_PRIVATE ph7_value *PH7_ClassInstanceFetchAttr(ph7_class_instance *pThis, const SyString *pName) {
|
|
SyHashEntry *pEntry;
|
|
VmClassAttr *pAttr;
|
|
/* Query the attribute hashtable */
|
|
pEntry = SyHashGet(&pThis->hAttr, (const void *)pName->zString, pName->nByte);
|
|
if(pEntry == 0) {
|
|
/* No such attribute */
|
|
return 0;
|
|
}
|
|
/* Point to the class attribute */
|
|
pAttr = (VmClassAttr *)pEntry->pUserData;
|
|
/* Check if we are dealing with a static/constant attribute */
|
|
if(pAttr->pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT | PH7_CLASS_ATTR_STATIC)) {
|
|
/* Access is forbidden */
|
|
return 0;
|
|
}
|
|
/* Return the attribute value */
|
|
return ExtractClassAttrValue(pThis->pVm, pAttr);
|
|
}
|