EDIT: Alexander Konstantinov cevaba bakınız. Ben benzer __ olsun sihirli yöntemi, düşünüyordum, ama aslında doğru uygulandığından. Yani sınıfın bir iç uygulama olmadan bunu yapamam.
EDIT2: Dahili uygulama:
NOT: Bu tamamen masturbatory, ama yine de buraya gidiyor iddia olabilir:
static zend_object_handlers object_handlers;
static zend_object_value ce_create_object(zend_class_entry *class_type TSRMLS_DC)
{
zend_object_value zov;
zend_object *zobj;
zobj = emalloc(sizeof *zobj);
zend_object_std_init(zobj, class_type TSRMLS_CC);
zend_hash_copy(zobj->properties, &(class_type->default_properties),
(copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*));
zov.handle = zend_objects_store_put(zobj,
(zend_objects_store_dtor_t) zend_objects_destroy_object,
(zend_objects_free_object_storage_t) zend_objects_free_object_storage,
NULL TSRMLS_CC);
zov.handlers = &object_handlers;
return zov;
}
/* modification of zend_std_read_dimension */
zval *read_dimension(zval *object, zval *offset, int type TSRMLS_DC) /* {{{ */
{
zend_class_entry *ce = Z_OBJCE_P(object);
zval *retval;
void *dummy;
if (zend_hash_find(&ce->function_table, "offsetgetref",
sizeof("offsetgetref"), &dummy) == SUCCESS) {
if(offset == NULL) {
/* [] construct */
ALLOC_INIT_ZVAL(offset);
} else {
SEPARATE_ARG_IF_REF(offset);
}
zend_call_method_with_1_params(&object, ce, NULL, "offsetgetref",
&retval, offset);
zval_ptr_dtor(&offset);
if (!retval) {
if (!EG(exception)) {
/* ought to use php_error_docref* instead */
zend_error(E_ERROR,
"Undefined offset for object of type %s used as array",
ce->name);
}
return 0;
}
/* Undo PZVAL_LOCK() */
Z_DELREF_P(retval);
return retval;
} else {
zend_error(E_ERROR, "Cannot use object of type %s as array", ce->name);
return 0;
}
}
ZEND_MODULE_STARTUP_D(testext)
{
zend_class_entry ce;
zend_class_entry *ce_ptr;
memcpy(&object_handlers, zend_get_std_object_handlers(),
sizeof object_handlers);
object_handlers.read_dimension = read_dimension;
INIT_CLASS_ENTRY(ce, "TestClass", NULL);
ce_ptr = zend_register_internal_class(&ce TSRMLS_CC);
ce_ptr->create_object = ce_create_object;
return SUCCESS;
}
Şimdi bu komut:
<?php
class ArrayTest extends TestClass implements ArrayAccess {
private $_arr = array(
'test' => array(
'bar' => 1,
'baz' => 2
)
);
public function offsetExists($name) {
return isset($this->_arr[$name]);
}
public function offsetSet($name, $value) {
$this->_arr[$name] = $value;
}
public function offsetGet($name) {
throw new RuntimeException("This method should never be called");
}
public function &offsetGetRef($name) {
return $this->_arr[$name];
}
public function offsetUnset($name) {
unset($this->_arr[$name]);
}
}
$arrTest = new ArrayTest();
echo (isset($arrTest['test']['bar'])?"test/bar is set":"error") . "\n";
echo $arrTest['test']['baz']; // Echoes 2
echo "\n";
unset($arrTest['test']['baz']);
echo (isset($arrTest['test']['baz'])?"error":"test/baz is not set") . "\n";
$arrTest['test']['baz'] = 5;
echo $arrTest['test']['baz']; // Echoes 5
verir:
test/bar is set
2
test/baz is not set
5
ORİJİNAL şu - bu yanlıştır:
O iş için offsetGet
uygulama bir başvuru döndürmek gerekir.
public function &offsetGet($name) {
return $this->_arr[$name];
}
Iç eşdeğeri için bkz: here.
Get_property_ptr_ptr hiçbir benzer beri var, bunu zorunlu olmasa da, yazma gibi bağlamlarda veya bir proxy nesnesi (Z_ISREF anlamında) bir başvuru (get işleyici bakınız) (tip BP_VAR_W, BP_VAR_RW ve BP_VAR_UNSET) dönmek gerek. Read_dimension gibi $ val gibi bir yazma gibi bağlamında denir = & ediliyorsa $ Obj ['prop'], ve bir başvuru ne bir nesneyi ne dönmek, motor bir haber yayarlar. Açıkçası, bir referans dönen bu işlemleri düzgün çalışması için yeterli değil, iade zval değiştirerek aslında bazı etkiye sahip olması gerekmektedir. Bu bir aslında zvals (ki veya durum olmayabilir) ve yönlendirme iki seviyeleri olarak depolanabilir olması boyutları gerekir için - örneğin $ obj ['key'] = & $ a kadar atamaları hala mümkün olmadığını unutmayın .
Özetle, bir alt özellik arama offsetGet alt boyut değil offsetSet, offsetExists veya offsetUnset yazarken veya unseting içerir işlemleri.