Memcache拡張モジュールを透過的に扱えるパッチを書いてみた。
PHPなんだから透過的にmemcachedとか扱えないとまずいでしょうということで。
要はphp.iniに
# 以下の書式が利用可能 # hostname # hostname:tcpport # hostname:tcpport:udpport # [hostname] # [hostname]:tcpport # [hostname]:tcpport:udpport memcache.auto_connect_hosts = 10.1.4.1, 10.1.4.2:11211, [::::::10.1.4.3]::11211
と書いておくと、
<?php $_MEMCACHE['test'] = 'test'; // Memcache::set() 相当 var_dump($_MEMCACHE['test']); // Memcache::get() 相当 ?>
のように、オートグローバル変数 $_MEMCACHE を操作した際に自動的に接続を行ってmemcachedにアクセスできるという話。
以下より memcache-3.0.2.tar.gz を落としてきて、パッチを当ててからビルドしてください。
http://pecl.php.net/package/memcache
追記:
$_MEMCACHE の中身はどうなってるんだという話。var_dump($_MEMCACHE);してみると分かるとおり、単なるMemcacheオブジェクトで、 配列アクセス「[]」がオーバーロードされているだけです。
diff -ur memcache-3.0.2~/memcache.c memcache-3.0.2/memcache.c --- memcache-3.0.2~/memcache.c 2008-09-12 05:03:23.000000000 +0900 +++ memcache-3.0.2/memcache.c 2008-11-19 10:37:50.000000000 +0900 @@ -123,6 +123,32 @@ }; /* }}} */ +/* {{{ mmc_object_handlers */ +static zend_object_handlers mmc_object_handlers = { + ZEND_OBJECTS_STORE_HANDLERS, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +}; +/* }}} */ + #ifdef COMPILE_DL_MEMCACHE ZEND_GET_MODULE(memcache) #endif @@ -224,6 +250,7 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("memcache.allow_failover", "1", PHP_INI_ALL, OnUpdateLong, allow_failover, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.max_failover_attempts", "20", PHP_INI_ALL, OnUpdateFailoverAttempts, max_failover_attempts, zend_memcache_globals, memcache_globals) + STD_PHP_INI_ENTRY("memcache.auto_connect_hosts", "localhost:11211", PHP_INI_ALL, OnUpdateString, auto_connect_hosts, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.default_port", "11211", PHP_INI_ALL, OnUpdateLong, default_port, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.chunk_size", "32768", PHP_INI_ALL, OnUpdateChunkSize, chunk_size, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.protocol", "ascii", PHP_INI_ALL, OnUpdateProtocol, protocol, zend_memcache_globals, memcache_globals) @@ -244,6 +271,9 @@ static void _mmc_server_list_dtor(zend_rsrc_list_entry * TSRMLS_DC); static void php_mmc_set_failure_callback(mmc_pool_t *, zval *, zval * TSRMLS_DC); static void php_mmc_failure_callback(mmc_pool_t *, mmc_t *, void * TSRMLS_DC); +static zend_bool _php_mmc_auto_global_cb(char *name, uint name_len TSRMLS_DC); +static zval *_php_mmc_read_dimension(zval *object, zval *offset, int type TSRMLS_DC); +static void _php_mmc_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC); /* }}} */ /* {{{ php_memcache_init_globals() @@ -260,7 +290,10 @@ PHP_MINIT_FUNCTION(memcache) { zend_class_entry ce; - + mmc_object_handlers = *zend_get_std_object_handlers(); + mmc_object_handlers.read_dimension = _php_mmc_read_dimension; + mmc_object_handlers.write_dimension = _php_mmc_write_dimension; + INIT_CLASS_ENTRY(ce, "MemcachePool", php_memcache_pool_class_functions); memcache_pool_ce = zend_register_internal_class(&ce TSRMLS_CC); @@ -286,6 +319,8 @@ REGISTER_LONG_CONSTANT("MEMCACHE_HAVE_SESSION", 0, CONST_CS | CONST_PERSISTENT); #endif + zend_register_auto_global("_MEMCACHE", sizeof("_MEMCACHE") - 1, _php_mmc_auto_global_cb); + return SUCCESS; } /* }}} */ @@ -523,7 +558,7 @@ long value = 1, defval = 0, exptime = 0; mmc_request_t *request; void *value_handler_param[3]; - + if (mmc_object == NULL) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Oz|lll", &mmc_object, memcache_pool_ce, &keys, &value, &defval, &exptime) == FAILURE) { return; @@ -986,6 +1021,238 @@ } /* }}} */ +static zend_bool _php_mmc_auto_global_cb(char *name, uint name_len TSRMLS_DC) /* {{{ */ +{ + zval *mmc_object = NULL; + if (!MEMCACHE_G(auto_connect_hosts)) { + return FALSE; + } + { + int list_id; + zend_object_value ov; + mmc_pool_t *pool = mmc_pool_new(TSRMLS_C); + pool->failure_callback_param = &php_mmc_failure_callback; + pool->failure_callback = &php_mmc_failure_callback; + + ALLOC_INIT_ZVAL(mmc_object); + object_init_ex(mmc_object, memcache_ce); + list_id = zend_list_insert(pool, le_memcache_pool); + add_property_resource(mmc_object, "connection", list_id); + Z_OBJ_HT_P(mmc_object) = &mmc_object_handlers; + } + { + const char *host; + int host_len; + int tcp_port; + int udp_port; + { + const char *p, *pt; + p = MEMCACHE_G(auto_connect_hosts); + for (;;) { + while (*p != '\0' && isspace(*(unsigned char *)p)) { + ++p; + } + if (*p == '\0') { + break; + } + if (*p == '[') { + host = ++p; + while (*p != '\0' && *p != ']') { + ++p; + } + if (*p == '\0') { + pt = host; + } else { + pt = p; + ++p; + while (*p != '\0' && isspace(*(unsigned char *)p)) { + ++p; + } + if (*p != '\0' && *p != ':' && *p != ',') { + pt = host; + } + } + } else { + host = p; + while (*p != '\0' && *p != ':' && *p != ',') { + ++p; + } + pt = p; + while (pt > host && isspace(*(unsigned char *)(pt - 1))) { + --pt; + } + } + host_len = pt - host; + tcp_port = MEMCACHE_G(default_port); + udp_port = 0; + if (*p == ':') { + ++p; + while (*p != '\0' && isspace(*(unsigned char *)p)) { + ++p; + } + if (*p != '\0') { + const char *tcp_port_str = p; + while (*p != '\0' && *p != ':' && *p != ',') { + ++p; + } + pt = p; + while (pt > tcp_port_str && isspace(*(unsigned char *)(pt - 1))) { + --pt; + } + if (pt != tcp_port_str) { + errno = 0; + tcp_port = strtol(tcp_port_str, NULL, 10); + switch (errno) { + case EINVAL: case ERANGE: + tcp_port = -1; + break; + } + } + } + if (*p == ':') { + ++p; + while (*p != '\0' && isspace(*(unsigned char *)p)) { + ++p; + } + if (*p != '\0') { + const char *udp_port_str = p; + while (*p != '\0' && *p != ',') { + ++p; + } + pt = p; + while (pt > udp_port_str && isspace(*(unsigned char *)(pt - 1))) { + --pt; + } + if (pt != udp_port_str) { + errno = 0; + udp_port = strtol(udp_port_str, NULL, 10); + switch (errno) { + case EINVAL: case ERANGE: + udp_port = -1; + break; + } + } + if (*p == ',') { + ++p; + } + } + } else if (*p == ',') { + ++p; + } + } else if (*p == ',') { + ++p; + } + if (host_len == 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "memcache.auto_connect_hosts contains an invalid host name"); + goto fail; + } + if (tcp_port < 0 || udp_port < 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "memcache.auto_connect_hosts contains an invalid port number"); + goto fail; + } + + if (!php_mmc_pool_addserver(mmc_object, host, host_len, tcp_port, 0, 1, 1, MMC_DEFAULT_TIMEOUT, MMC_DEFAULT_RETRY, 1, NULL TSRMLS_CC)) { + goto fail; + } + } + } + } + zend_hash_update(&EG(symbol_table), "_MEMCACHE", sizeof("_MEMCACHE"), &mmc_object, sizeof(zval *), NULL); + return 0; +fail: + if (mmc_object) { + zval_ptr_dtor(&mmc_object); + } + return 1; +} +/* }}} */ + +static zval *_php_mmc_read_dimension(zval *mmc_object, zval *offset, int type TSRMLS_DC) /* {{{ */ +{ + mmc_pool_t *pool; + void *value_handler_param[3], *failover_handler_param[2]; + zval *return_value; + if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC) || !pool->num_servers) { + return NULL; + } + + ALLOC_INIT_ZVAL(return_value); + ZVAL_FALSE(return_value); + + value_handler_param[0] = return_value; + value_handler_param[1] = NULL; + value_handler_param[2] = NULL; + + { + mmc_request_t *request; + + /* allocate request */ + request = mmc_pool_request_get( + pool, MMC_PROTO_UDP, + mmc_value_handler_single, value_handler_param, + mmc_pool_failover_handler, NULL TSRMLS_CC); + + if (mmc_prepare_key(offset, request->key, &(request->key_len)) != MMC_OK) { + mmc_pool_release(pool, request); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid key"); + zval_ptr_dtor(&return_value); + return NULL; + } + + pool->protocol->get(request, MMC_OP_GET, offset, request->key, request->key_len); + + /* schedule request */ + if (mmc_pool_schedule_key(pool, request->key, request->key_len, request, 1 TSRMLS_CC) != MMC_OK) { + zval_ptr_dtor(&return_value); + return NULL; + } + } + + /* execute all requests */ + mmc_pool_run(pool TSRMLS_CC); + + return return_value; +} +/* }}} */ + +static void _php_mmc_write_dimension(zval *mmc_object, zval *offset, zval *value TSRMLS_DC) /* {{{ */ +{ + mmc_pool_t *pool; + mmc_request_t *request; + zval return_value; + + if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC) || !pool->num_servers) { + return; + } + + { + mmc_request_t *request; + /* allocate request */ + request = mmc_pool_request(pool, MMC_PROTO_TCP, mmc_stored_handler, &return_value, mmc_pool_failover_handler, NULL TSRMLS_CC); + + if (mmc_prepare_key(offset, request->key, &(request->key_len)) != MMC_OK) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid key"); + mmc_pool_release(pool, request); + return; + } + + /* assemble command */ + if (pool->protocol->store(pool, request, MMC_OP_SET, request->key, request->key_len, 0, 0, 0, value TSRMLS_CC) != MMC_OK) { + mmc_pool_release(pool, request); + return; + } + + /* schedule request */ + if (mmc_pool_schedule_key(pool, request->key, request->key_len, request, MEMCACHE_G(redundancy) TSRMLS_CC) != MMC_OK) { + return; + } + } + + /* execute all requests */ + mmc_pool_run(pool TSRMLS_CC); +} +/* }}} */ + /* ---------------- module functions ---------------- */ diff -ur memcache-3.0.2~/memcache_pool.h memcache-3.0.2/memcache_pool.h --- memcache-3.0.2~/memcache_pool.h 2008-09-12 05:03:23.000000000 +0900 +++ memcache-3.0.2/memcache_pool.h 2008-11-18 09:43:24.000000000 +0900 @@ -348,6 +348,7 @@ long max_failover_attempts; long redundancy; long session_redundancy; + char* auto_connect_hosts; ZEND_END_MODULE_GLOBALS(memcache) #ifdef ZTS