РЕДАКТИРОВАТЬ: См. Ответ Александра Константинова. Я думал о методе __get magic, который аналогичен, но на самом деле был реализован правильно. Так что вы не можете сделать это без внутренней реализации вашего класса.
EDIT2: внутренняя реализация:
ПРИМЕЧАНИЕ: Вы можете утверждать, что это чисто мастурбация, но в любом случае здесь это звучит так:
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;
}
теперь этот скрипт:
<?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
дает:
test/bar is set
2
test/baz is not set
5
ОРИГИНАЛ следует - это неверно:
Ваша реализация offsetGet
должна возвращать ссылку, чтобы она работала.
public function &offsetGet($name) {
return $this->_arr[$name];
}
Внутренний эквивалент см. здесь .
Поскольку нет аналога get_property_ptr_ptr, вы должны вернуть ссылку (в смысле Z_ISREF) или прокси-объект (см. Обработчик get) в контексте, подобном записи (типы BP_VAR_W, BP_VAR_RW и BP_VAR_UNSET), хотя это не так обязательный. Если read_dimension вызывается в контексте, подобном записи, например, в $ val = & $ obj ['prop'], и вы не возвращаете ни ссылку, ни объект, механизм выдаст уведомление. Очевидно, что для правильной работы этих операций возврата ссылки недостаточно, необходимо, чтобы изменение возвращаемого zval действительно имело некоторый эффект. Обратите внимание, что такие присваивания, как $ obj ['key'] = & $ a, по-прежнему невозможны - для этого нужно, чтобы измерения были фактически сохраняемыми как zval (что может иметь или не иметь место) и два уровня косвенности.
В целом, операции, которые включают запись или отмена субразмера вызова под-свойства, вызывают offsetGet, а не offsetSet, offsetExists или offsetUnset.