From b40c8d8afe9ff6bbc6db4567fe6b5218fd00694a Mon Sep 17 00:00:00 2001 From: Antony Dovgal Date: Wed, 11 Feb 2004 16:06:39 +0000 Subject: [PATCH] Initial import --- CREDITS | 1 + EXPERIMENTAL | 0 README | 11 + config.m4 | 11 + config.w32 | 9 + example.php | 24 ++ memcache.c | 974 +++++++++++++++++++++++++++++++++++++++++++++++++ package.xml | 39 ++ php_memcache.h | 85 +++++ 9 files changed, 1154 insertions(+) create mode 100644 CREDITS create mode 100644 EXPERIMENTAL create mode 100644 README create mode 100644 config.m4 create mode 100644 config.w32 create mode 100644 example.php create mode 100644 memcache.c create mode 100644 package.xml create mode 100644 php_memcache.h diff --git a/CREDITS b/CREDITS new file mode 100644 index 0000000..e4e76b4 --- /dev/null +++ b/CREDITS @@ -0,0 +1 @@ +Antony Dovgal diff --git a/EXPERIMENTAL b/EXPERIMENTAL new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..d03d5d3 --- /dev/null +++ b/README @@ -0,0 +1,11 @@ +memcached module for PHP +------------------------ +This module doesn't require any libraries or headers to be built. +But you'll need memcached to use it =) + +The memcached website is at: + http://www.danga.com/memcached/ + +You will probably need libevent to install memcached: +You can download it here: http://www.monkey.org/~provos/libevent/ + diff --git a/config.m4 b/config.m4 new file mode 100644 index 0000000..0365a6b --- /dev/null +++ b/config.m4 @@ -0,0 +1,11 @@ +dnl +dnl $Id$ +dnl + +PHP_ARG_ENABLE(memcache, whether to enable memcache support, +[ --enable-memcache Enable memcache support]) + +if test "$PHP_MEMCACHE" != "no"; then + AC_DEFINE(HAVE_MEMCACHE,1,[Whether you want memcache support]) + PHP_NEW_EXTENSION(memcache, memcache.c, $ext_shared) +fi diff --git a/config.w32 b/config.w32 new file mode 100644 index 0000000..8c1315d --- /dev/null +++ b/config.w32 @@ -0,0 +1,9 @@ +// $Id$ +// vim:ft=javascript + +ARG_ENABLE("memcache", "memcache support", "yes"); + +if (PHP_FTP == "yes") { + EXTENSION("memcache", "memcache.c"); + AC_DEFINE('HAVE_MEMCACHE', 1, 'Have memcache support'); +} diff --git a/example.php b/example.php new file mode 100644 index 0000000..c44e533 --- /dev/null +++ b/example.php @@ -0,0 +1,24 @@ +set("str_key", "String to store in memcached"); + $memcache->set("num_key", 123); + + $object = new StdClass; + $object->attribute = 'test'; + $memcache->set("obj_key", $object); + + $array = Array('assoc'=>123, 345, 567); + $memcache->set("arr_key", $array); + + var_dump($memcache->get('str_key')); + var_dump($memcache->get('num_key')); + var_dump($memcache->get('obj_key')); +} +else { + echo "Connection to memcached failed"; +} +?> + diff --git a/memcache.c b/memcache.c new file mode 100644 index 0000000..761fa6e --- /dev/null +++ b/memcache.c @@ -0,0 +1,974 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Antony Dovgal | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +/* + * XXX fix all error messages + */ + +/* TODO + * - should we add compression support ? bzip || gzip ? + * - increment/decrement funcs + * - stats + * - more and better error messages + * - we should probably do a cleanup if some call failed, + * because it can cause further calls to fail + * */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include +#include +#ifdef HAVE_SYS_FILE_H +#include +#endif + +#if HAVE_MEMCACHE + +#include "ext/standard/info.h" +#include "ext/standard/php_string.h" +#include "ext/standard/php_var.h" +#include "ext/standard/php_smart_str.h" +#include "php_network.h" +#include "php_memcache.h" + +/* True global resources - no need for thread safety here */ +static int le_memcache; +static zend_class_entry *memcache_class_entry_ptr; + +#ifdef ZTS +int memcache_globals_id; +#else +PHP_MEMCACHE_API php_memcache_globals memcache_globals; +#endif + +/* {{{ memcache_functions[] + */ +zend_function_entry memcache_functions[] = { + PHP_FE(memcache_connect, NULL) + PHP_FE(memcache_get_version, NULL) + PHP_FE(memcache_add, NULL) + PHP_FE(memcache_set, NULL) + PHP_FE(memcache_replace, NULL) + PHP_FE(memcache_get, NULL) + PHP_FE(memcache_delete, NULL) + PHP_FE(memcache_debug, NULL) + {NULL, NULL, NULL} +}; + +static zend_function_entry php_memcache_class_functions[] = { + PHP_FALIAS(connect, memcache_connect, NULL) + PHP_FALIAS(getversion, memcache_get_version, NULL) + PHP_FALIAS(add, memcache_add, NULL) + PHP_FALIAS(set, memcache_set, NULL) + PHP_FALIAS(replace, memcache_replace, NULL) + PHP_FALIAS(get, memcache_get, NULL) + PHP_FALIAS(delete, memcache_delete, NULL) + {NULL, NULL, NULL} +}; + +/* }}} */ + +/* {{{ memcache_module_entry + */ +zend_module_entry memcache_module_entry = { +#if ZEND_MODULE_API_NO >= 20010901 + STANDARD_MODULE_HEADER, +#endif + "memcache", + memcache_functions, + PHP_MINIT(memcache), + PHP_MSHUTDOWN(memcache), + PHP_RINIT(memcache), /* Replace with NULL if there's nothing to do at request start */ + PHP_RSHUTDOWN(memcache), /* Replace with NULL if there's nothing to do at request end */ + PHP_MINFO(memcache), +#if ZEND_MODULE_API_NO >= 20010901 + "0.1", /* Replace with version number for your extension */ +#endif + STANDARD_MODULE_PROPERTIES +}; +/* }}} */ + +#ifdef COMPILE_DL_MEMCACHE +ZEND_GET_MODULE(memcache) +#endif + +/* {{{ internal function protos */ +static void _mmc_server_list_dtor(zend_rsrc_list_entry * TSRMLS_DC); +static int mmc_get_connection(zval *, mmc_t ** TSRMLS_DC); +static mmc_t* mmc_open(const char *, short, long TSRMLS_DC); +static int mmc_close(mmc_t *); +static int mmc_write(mmc_t *, const char *, int); +static int mmc_readline(mmc_t *); +static int mmc_read(mmc_t *, char *, int); +static char * mmc_get_version(mmc_t *); +static int mmc_str_left(char *, char *, int, int); +static int mmc_sendcmd(mmc_t *, const char *, int); +static int mmc_exec_storage_cmd(mmc_t *, char *, char *, int, int, char *, int); +static int mmc_parse_response(char *, int *, int *); +static int mmc_exec_retrieval_cmd(mmc_t *, char *, char *, int *, char **, int *); +static int mmc_delete(mmc_t *, char *, int); +static void php_mmc_store (INTERNAL_FUNCTION_PARAMETERS, char *); +/* }}} */ + +/* {{{ php_memcache_init_globals() +*/ +static void php_memcache_init_globals(php_memcache_globals *memcache_globals_p TSRMLS_DC) +{ + MEMCACHE_G(debug_mode) = 0; +} +/* }}} */ + +/* {{{ PHP_MINIT_FUNCTION + */ +PHP_MINIT_FUNCTION(memcache) +{ + zend_class_entry memcache_class_entry; + INIT_CLASS_ENTRY(memcache_class_entry, "Memcache", php_memcache_class_functions); + memcache_class_entry_ptr = zend_register_internal_class(&memcache_class_entry TSRMLS_CC); + + le_memcache = zend_register_list_destructors_ex(_mmc_server_list_dtor, NULL, "memcache connection", module_number); + +#ifdef ZTS + ts_allocate_id(&memcache_globals_id, sizeof(php_memcache_globals), (ts_allocate_ctor) php_memcache_init_globals, NULL); +#else + php_memcache_init_globals(&memcache_globals TSRMLS_CC); +#endif + + REGISTER_LONG_CONSTANT("MEMCACHE_SERIALIZED",MMC_SERIALIZED, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("MEMCACHE_COMPRESSED",MMC_COMPRESSED, CONST_CS | CONST_PERSISTENT); + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +PHP_MSHUTDOWN_FUNCTION(memcache) +{ + MEMCACHE_G(debug_mode) = 0; + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_RINIT_FUNCTION + */ +PHP_RINIT_FUNCTION(memcache) +{ + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_RSHUTDOWN_FUNCTION + */ +PHP_RSHUTDOWN_FUNCTION(memcache) +{ + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION + */ +PHP_MINFO_FUNCTION(memcache) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "memcache support", "enabled"); + php_info_print_table_header(2, "Revision", "$Revision$"); + php_info_print_table_end(); +} +/* }}} */ + +/* {{{ _mmc_server_list_dtor() */ +static void _mmc_server_list_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) +{ + mmc_t *mmc = (mmc_t *)rsrc->ptr; + mmc_close(mmc); +} +/* }}} */ + +/* {{{ mmc_debug() */ +static void mmc_debug( const char *format, ...) +{ + TSRMLS_FETCH(); + + if (MEMCACHE_G(debug_mode)) { + char buffer[1024]; + va_list args; + + va_start(args, format); + vsnprintf(buffer, sizeof(buffer)-1, format, args); + va_end(args); + buffer[sizeof(buffer)-1] = '\0'; + php_printf("%s
\n", buffer); + } +} +/* }}} */ + +/* {{{ mmc_get_connection() +*/ +static int mmc_get_connection(zval *id, mmc_t **mmc TSRMLS_DC) +{ + zval **connection; + int resource_type; + + if (zend_hash_find(Z_OBJPROP_P(id), "connection", sizeof("connection"), (void **)&connection) == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot find connection identifier"); + return 0; + } + + *mmc = (mmc_t *) zend_list_find(Z_LVAL_PP(connection), &resource_type); + + if (!*mmc || resource_type != le_memcache) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "connection identifier not found"); + return 0; + } + + return Z_LVAL_PP(connection); +} +/* }}} */ + +/* {{{ mmc_open() */ +static mmc_t* mmc_open(const char *host, short port, long timeout TSRMLS_DC) +{ + mmc_t *mmc; + struct timeval tv; + + if ( !(mmc = emalloc( sizeof(mmc_t) )) ) { + return NULL; + } + + tv.tv_sec = timeout; + tv.tv_usec = 0; + + mmc->stream = php_stream_sock_open_host( host, (unsigned short) port, SOCK_STREAM, 0, 0); + if (mmc->stream == NULL) { + mmc_debug("mmc_open: can't open socket to host"); + efree(mmc); + return NULL; + } + + php_stream_set_option(mmc->stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv); + php_stream_set_option(mmc->stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_NONE, NULL); + + mmc->timeout = timeout; + + if (mmc_get_version(mmc) == NULL) { + mmc_debug("mmc_open: failed to get server's version"); + mmc_close(mmc); + efree(mmc); + return NULL; + } + + mmc->id = zend_list_insert(mmc,le_memcache); + + return mmc; +} +/* }}} */ + +/* {{{ mmc_close() */ +static int mmc_close(mmc_t *mmc) +{ + mmc_debug("mmc_close: closing connection to server"); + + if (mmc == NULL) { + return 0; + } + + if (mmc->stream != NULL) { + mmc_sendcmd(mmc,"quit", 4); + php_stream_close(mmc->stream); + } + + return 1; +} +/* }}} */ + +/* {{{ mmc_write() */ +static int mmc_write(mmc_t *mmc, const char *buf, int len) +{ + int size; + + mmc_debug("mmc_write: sending to server:"); + mmc_debug("mmc_write:---"); + mmc_debug("%s", buf); + mmc_debug("mmc_write:---"); + + size = php_stream_write(mmc->stream, buf, len); + if (size != len) { + mmc_debug("mmc_write: sent length is less, than data length"); + return -1; + } + return len; +} +/* }}} */ + +/* {{{ mmc_readline() */ +static int mmc_readline(mmc_t *mmc) +{ + char *buf; + buf = php_stream_gets(mmc->stream, mmc->inbuf, MMC_BUF_SIZE); + if (buf) { + mmc_debug("mmc_readline: read data:"); + mmc_debug("mmc_readline:---"); + mmc_debug("%s", buf); + mmc_debug("mmc_readline:---"); + return strlen(buf); + } + else { + mmc_debug("mmc_readline: cannot read a line from server"); + return -1; + } +} +/* }}} */ + +/* {{{ mmc_read() */ +static int mmc_read(mmc_t *mmc, char *buf, int len) +{ + int size; + + mmc_debug("mmc_read: reading data from server"); + size = php_stream_read(mmc->stream, buf, len); + buf[size] = '\0'; + + mmc_debug("mmc_read: read %d bytes", size); + mmc_debug("mmc_read:---"); + mmc_debug("%s", buf); + mmc_debug("mmc_read:---"); + + return size; +} +/* }}} */ + +/* {{{ mmc_get_version() */ +static char *mmc_get_version(mmc_t *mmc) +{ + char version[MMC_BUF_SIZE]; + char *version_str; + int len; + + if (mmc_sendcmd(mmc, "version", 7) < 0) { + return NULL; + } + + if ((len = mmc_readline(mmc)) < 0) { + return NULL; + } + + if (mmc_str_left(mmc->inbuf,"VERSION ", len, 8)) { + version_str = estrndup(mmc->inbuf + 8, strlen(mmc->inbuf) - 8 - 2); + return version_str; + } + + mmc_debug("mmc_get_version: data is not valid version string"); + return NULL; +} +/* }}} */ + +/* {{{ mmc_str_left() */ +static int mmc_str_left(char *haystack, char *needle, int haystack_len, int needle_len) +{ + char *found; + + found = php_memnstr(haystack, needle, needle_len, haystack + haystack_len); + if ((found - haystack) == 0) { + return 1; + } + return 0; +} +/* }}} */ + +/* {{{ mmc_sendcmd ()*/ +static int mmc_sendcmd(mmc_t *mmc, const char *cmd, int cmdlen) +{ + if (!mmc || !cmd) { + return -1; + } + + mmc_debug("mmc_sendcmd: sending command '%s'", cmd); + + if (mmc_write(mmc, cmd, cmdlen) != cmdlen) { + mmc_debug("mmc_sendcmd: write failed"); + return -1; + } + + if (mmc_write(mmc, "\r\n", 2) != 2) { + mmc_debug("mmc_sendcmd: write failed"); + return -1; + } + + return 1; +} +/* }}}*/ + +/* {{{ mmc_exec_storage_cmd () */ +static int mmc_exec_storage_cmd(mmc_t *mmc, char *command, char *key, int flags, int expire, char *data, int data_len) +{ + char *real_command, *real_data; + char *flags_tmp, *expire_tmp, *datalen_tmp; + int size, flags_size, expire_size, datalen_size = 0; + int response_buf_size; + + flags_tmp = emalloc(MAX_LENGTH_OF_LONG + 1); + flags_size = sprintf(flags_tmp, "%d", flags); + flags_tmp[flags_size] = '\0'; + + expire_tmp = emalloc(MAX_LENGTH_OF_LONG + 1); + expire_size = sprintf(expire_tmp, "%d", expire); + expire_tmp[expire_size] = '\0'; + + datalen_tmp = emalloc(MAX_LENGTH_OF_LONG + 1); + datalen_size = sprintf(datalen_tmp, "%d", data_len); + datalen_tmp[datalen_size] = '\0'; + + real_command = emalloc( + strlen(command) + + 1 /* space */ + + strlen(key) + + 1 /* space */ + + flags_size + + 1 /* space */ + + expire_size + + 1 /* space */ + + datalen_size + + 1 + ); + + size = sprintf(real_command, "%s ", command); + size += sprintf(real_command + size, "%s ", key); + size += sprintf(real_command + size, "%s ", flags_tmp); + size += sprintf(real_command + size, "%s ", expire_tmp); + size += sprintf(real_command + size, "%s", datalen_tmp); + + real_command [size] = '\0'; + efree(flags_tmp); + efree(expire_tmp); + efree(datalen_tmp); + + mmc_debug("mmc_exec_storage_cmd: store cmd is '%s'", real_command); + + /* tell server, that we're going to store smthng */ + if (mmc_sendcmd(mmc, real_command, size) < 0) { + efree(real_command); + return -1; + } + efree(real_command); + + real_data = emalloc (data_len + 1); + + if (data_len > 0) { + size = sprintf(real_data, "%s", data); + } + else { + size = 0; + } + + real_data[size] = '\0'; + + mmc_debug("mmc_exec_storage_cmd: sending data to server", real_data); + + /* send data */ + if (mmc_sendcmd(mmc, real_data, size) < 0) { + efree(real_data); + return -1; + } + efree(real_data); + + /* get server's response */ + if ((response_buf_size = mmc_readline(mmc)) < 0) { + return -1; + } + + mmc_debug("mmc_exec_storage_cmd: response is '%s'", mmc->inbuf); + + /* stored or not? */ + if(mmc_str_left(mmc->inbuf,"STORED", response_buf_size, 6)) { + return 1; + } + + return -1; +} +/* }}} */ + +/* {{{ mmc_parse_response ()*/ +static int mmc_parse_response(char *response, int *flags, int *value_len) +{ + int i=0, n=0; + int spaces[3]; + int response_len; + + if (!response || (response_len = strlen(response)) <= 0) { + return -1; + } + + mmc_debug("mmc_parse_response: got response '%s'", response); + + for (i = 0; i < response_len; i++) { + if (response[i] == ' ') { + spaces[n] = i; + n++; + if (n == 3) { + break; + } + } + } + + mmc_debug("mmc_parse_response: found %d spaces", n); + + if (n < 3) { + return -1; + } + + *flags = atoi(response + spaces[1]); + *value_len = atoi(response + spaces[2]); + + mmc_debug("mmc_parse_response: 1st space is at %d position", spaces[1]); + mmc_debug("mmc_parse_response: 2nd space is at %d position", spaces[2]); + mmc_debug("mmc_parse_response: flags = %d", *flags); + mmc_debug("mmc_parse_response: value_len = %d ", *value_len); + + return 1; +} + +/* }}} */ + +/* {{{ mmc_exec_retrieval_cmd () */ +static int mmc_exec_retrieval_cmd(mmc_t *mmc, char *command, char *key, int *flags, char **data, int *data_len) +{ + char *real_command, *tmp; + int size, response_buf_size; + + real_command = emalloc( + strlen(command) + + 1 /* space */ + + strlen(key) + + 1 + ); + + size = sprintf(real_command, "%s ", command); + size += sprintf(real_command + size, "%s", key); + + real_command [size] = '\0'; + + mmc_debug("mmc_exec_retrieval_cmd: trying to get '%s'", key); + + /* gimme! =) */ + if (mmc_sendcmd(mmc, real_command, size) < 0) { + efree(real_command); + return -1; + } + efree(real_command); + + /* get server's response */ + if ((response_buf_size = mmc_readline(mmc)) < 0){ + return -1; + } + + mmc_debug("mmc_exec_retrieval_cmd: got response '%s'", mmc->inbuf); + + /* what's this? */ + if(mmc_str_left(mmc->inbuf,"VALUE", response_buf_size, 5) < 0) { + return -1; + } + + tmp = estrdup(mmc->inbuf); + if (mmc_parse_response(tmp, flags, data_len) < 0) { + efree(tmp); + return -1; + } + efree(tmp); + + mmc_debug("mmc_exec_retrieval_cmd: data len is %d bytes", *data_len); + + if (*data_len) { + *data = emalloc(*data_len); + + if ((size = mmc_read(mmc, *data, *data_len)) != *data_len) { + return -1; + } + mmc_debug("mmc_exec_retrieval_cmd: data '%s'", *data); + } + + /* clean those stupid \r\n's */ + mmc_readline(mmc); + + /* read "END" */ + if ((response_buf_size = mmc_readline(mmc)) < 0) { + return -1; + } + + if(mmc_str_left(mmc->inbuf,"END", response_buf_size, 3) < 0) { + return -1; + } + + return 1; +} +/* }}} */ + +/* {{{ mmc_delete () */ +static int mmc_delete(mmc_t *mmc, char *key, int time) +{ + char *real_command, *time_tmp; + int size, response_buf_size; + + time_tmp = emalloc(MAX_LENGTH_OF_LONG + 1); + size = sprintf(time_tmp, "%d", time); + time_tmp[size] = '\0'; + + real_command = emalloc( + 6 /* strlen(delete) */ + + 1 /* space */ + + strlen(key) + + 1 /* space */ + + size + + 1 + ); + + size = sprintf(real_command, "%s ", "delete"); + size += sprintf(real_command + size, "%s ", key); + size += sprintf(real_command + size, "%s", time_tmp); + + real_command [size] = '\0'; + + efree(time_tmp); + + mmc_debug("mmc_delete: trying to delete '%s'", key); + + /* drop it! =) */ + if (mmc_sendcmd(mmc, real_command, size) < 0) { + efree(real_command); + return -1; + } + efree(real_command); + + /* get server's response */ + if ((response_buf_size = mmc_readline(mmc)) < 0){ + return -1; + } + + mmc_debug("mmc_delete: server's response is '%s'", mmc->inbuf); + + /* ok? */ + if(mmc_str_left(mmc->inbuf,"DELETED", response_buf_size, 7)) { + return 1; + } + + if(mmc_str_left(mmc->inbuf,"NOT_FOUND", response_buf_size, 9)) { + /* return 0, if such wasn't found */ + return 0; + } + + /* hmm.. */ + return -1; +} +/* }}} */ + +/* {{{ php_mmc_store ()*/ +static void php_mmc_store (INTERNAL_FUNCTION_PARAMETERS, char *command) +{ + zval *id, **key, **var, **expire; + mmc_t *mmc; + int inx, flags, real_expire, data_len; + char *data, *real_key; + int ac = ZEND_NUM_ARGS(); + + php_serialize_data_t var_hash; + smart_str buf = {0}; + + if ((id = getThis()) != 0) { + if ((inx = mmc_get_connection(id, &mmc TSRMLS_CC)) == 0) { + RETURN_FALSE; + } + if (ac < 2 || ac > 3 || zend_get_parameters_ex(ac, &key, &var, &expire) == FAILURE) { + WRONG_PARAM_COUNT; + } + + convert_to_string_ex(key); + + if (Z_STRLEN_PP(key) > MMC_KEY_MAX_SIZE) { + real_key = estrndup(Z_STRVAL_PP(key), MMC_KEY_MAX_SIZE); + } + else { + real_key = estrdup(Z_STRVAL_PP(key)); + } + + /* default flags & expire */ + flags = 0; + real_expire = 0; + + if (ac > 2) { + convert_to_long_ex(expire); + real_expire = Z_LVAL_PP(expire); + } + + switch (Z_TYPE_PP(var)) { + case IS_STRING: + case IS_LONG: + case IS_DOUBLE: + case IS_BOOL: + convert_to_string_ex(var); + data = Z_STRVAL_PP(var); + data_len = Z_STRLEN_PP(var); + break; + default: + PHP_VAR_SERIALIZE_INIT(var_hash); + php_var_serialize(&buf, var, &var_hash TSRMLS_CC); + PHP_VAR_SERIALIZE_DESTROY(var_hash); + + if (!buf.c) { + /* you're trying to save null or smthing went really wrong */ + RETURN_FALSE; + } + + flags |= MMC_SERIALIZED; + + data = buf.c; + data_len = buf.len; + break; + } + + if (mmc_exec_storage_cmd(mmc, command, real_key, flags, real_expire, data, data_len) > 0) { + efree(real_key); + RETURN_TRUE; + } + efree(real_key); + + RETURN_FALSE; + } + + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "memcache_%s() should not be called like this. Use $memcache->%s() to %s item", command, command, command); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto object memcache_connect( string host, int port [, int timeout ]) + Connects to server and returns Memcache object */ +PHP_FUNCTION(memcache_connect) +{ + zval **host, **port, **timeout; + mmc_t *mmc = NULL; + int timeout_sec = MMC_DEFAULT_TIMEOUT; + int ac = ZEND_NUM_ARGS(); + + if (ac < 2 || ac > 3 || zend_get_parameters_ex(ac, &host, &port, &timeout) == FAILURE) { + WRONG_PARAM_COUNT; + } + + convert_to_string_ex(host); + convert_to_long_ex(port); + + if (ac == 3) { + convert_to_long_ex(timeout); + timeout_sec = Z_LVAL_PP(timeout); + } + + mmc = mmc_open(Z_STRVAL_PP(host), Z_LVAL_PP(port), timeout_sec); + + if (mmc == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't connect to %s:%ld.",Z_STRVAL_PP(host), Z_LVAL_PP(port)); + RETURN_FALSE; + } + + object_init_ex(return_value, memcache_class_entry_ptr); + add_property_resource(return_value, "connection",mmc->id); +} +/* }}} */ + +/* {{{ proto string memcache_get_version( void ) + Returns server's version */ +PHP_FUNCTION(memcache_get_version) +{ + zval *id; + mmc_t *mmc; + int inx; + char *version; + + if ((id = getThis()) != 0) { + if ((inx = mmc_get_connection(id, &mmc TSRMLS_CC)) == 0) { + RETURN_FALSE; + } + + if ( (version = mmc_get_version(mmc)) ) { + RETURN_STRING(version, 1); + } + RETURN_FALSE; + } + + php_error_docref(NULL TSRMLS_CC, E_WARNING, "memcache_get_version() should not be called like this. Use $memcache->getVersion() instead to get server's version"); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto bool memcache_add( string key, mixed var [, int expire ] ) + Adds new item. Item with such key should not exist. */ +PHP_FUNCTION(memcache_add) +{ + php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU,"add"); +} +/* }}} */ + +/* {{{ proto bool memcache_set( string key, mixed var [, int expire ] ) + Sets the value of an item. Item may exist or not */ +PHP_FUNCTION(memcache_set) +{ + php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU,"set"); +} +/* }}} */ + +/* {{{ proto bool memcache_replace( string key, mixed var [, int expire ] ) + Replaces existing item. Returns false if item doesn't exist */ +PHP_FUNCTION(memcache_replace) +{ + php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU,"replace"); +} +/* }}} */ + +/* {{{ proto mixed memcache_get( string key ) + Returns value of existing item or false */ +PHP_FUNCTION(memcache_get) +{ + zval *id, **key; + mmc_t *mmc; + int inx, flags = 0, data_len = 0; + char *data = NULL; + const char *tmp = NULL; + + php_unserialize_data_t var_hash; + + if ((id = getThis()) != 0) { + if ((inx = mmc_get_connection(id, &mmc TSRMLS_CC)) == 0) { + RETURN_FALSE; + } + if (zend_get_parameters_ex(1, &key) == FAILURE) { + WRONG_PARAM_COUNT; + } + + convert_to_string_ex(key); + + if (mmc_exec_retrieval_cmd(mmc, "get", Z_STRVAL_PP(key), &flags, &data, &data_len) > 0) { + + if (!data || !data_len) { + RETURN_EMPTY_STRING(); + } + + tmp = data; + + if (flags & MMC_SERIALIZED) { + PHP_VAR_UNSERIALIZE_INIT(var_hash); + if (!php_var_unserialize(&return_value, &tmp, tmp + data_len, &var_hash TSRMLS_CC)) { + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + zval_dtor(return_value); + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %d of %d bytes", tmp - data, data_len); + RETURN_FALSE; + } + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + } + else { + RETVAL_STRINGL(data, data_len, 0); + } + } + else { + RETURN_FALSE; + } + } + else { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "memcache_get() should not be called like this. Use $memcache->get($key) instead to get item's value"); + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto bool memcache_delete( string key, [ int expire ]) + Deletes existing item */ +PHP_FUNCTION(memcache_delete) +{ + zval *id, **key, **time; + mmc_t *mmc; + int inx, result, real_time; + int ac = ZEND_NUM_ARGS(); + + if ((id = getThis()) != 0) { + if ((inx = mmc_get_connection(id, &mmc TSRMLS_CC)) == 0) { + RETURN_FALSE; + } + if (ac < 1 || ac > 2 || zend_get_parameters_ex(ac, &key, &time) == FAILURE) { + WRONG_PARAM_COUNT; + } + + convert_to_string_ex(key); + real_time = 0; + + if (ac > 1) { + convert_to_long_ex(time); + real_time = Z_LVAL_PP(time); + } + + result = mmc_delete(mmc, Z_STRVAL_PP(key), real_time); + + if (result > 0) { + RETURN_TRUE; + } + else if (result == 0) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "such key doesn't exist"); + RETURN_FALSE; + } + else { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "error while deleting item"); + } + RETURN_FALSE; + } + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "memcache_delete() should not be called like this. Use $memcache->delete($key,$timeout) to delete item"); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto bool memcache_debug( bool onoff ) + Turns on/off internal debugging */ +PHP_FUNCTION(memcache_debug) +{ + zval **onoff; + + if (zend_get_parameters_ex(1, &onoff) == FAILURE) { + WRONG_PARAM_COUNT; + } + + convert_to_long_ex(onoff); + if (Z_LVAL_PP(onoff) != 0) { + MEMCACHE_G(debug_mode) = 1; + } + else { + MEMCACHE_G(debug_mode) = 0; + } + + RETURN_TRUE; +} +/* }}} */ + +#endif /* HAVE_MEMCACHE */ +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/package.xml b/package.xml new file mode 100644 index 0000000..24ef200 --- /dev/null +++ b/package.xml @@ -0,0 +1,39 @@ + + + memcache + memcached extension + + Memcached is a caching daemon designed especially for + dynamic web applications to decrease database load by + storing objects in memory. + This extension allows you to work with memcached through + handy OO interface. + + PHP License + + + tony2001 + Antony Dovgal + tony2001@phpclub.net + + + + 0.1 + 2004-02-08 + alpha + + Initial release in PECL + + + + + CREDITS + README + config.m4 + config.w32 + php_memcache.h + memcache.c + + + +< diff --git a/php_memcache.h b/php_memcache.h new file mode 100644 index 0000000..a82a730 --- /dev/null +++ b/php_memcache.h @@ -0,0 +1,85 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Antony Dovgal | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_MEMCACHE_H +#define PHP_MEMCACHE_H + +extern zend_module_entry memcache_module_entry; +#define phpext_memcache_ptr &memcache_module_entry + +#ifdef PHP_WIN32 +#define PHP_MEMCACHE_API __declspec(dllexport) +#else +#define PHP_MEMCACHE_API +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +PHP_MINIT_FUNCTION(memcache); +PHP_MSHUTDOWN_FUNCTION(memcache); +PHP_RINIT_FUNCTION(memcache); +PHP_RSHUTDOWN_FUNCTION(memcache); +PHP_MINFO_FUNCTION(memcache); + +PHP_FUNCTION(memcache_connect); +PHP_FUNCTION(memcache_get_version); +PHP_FUNCTION(memcache_add); +PHP_FUNCTION(memcache_set); +PHP_FUNCTION(memcache_replace); +PHP_FUNCTION(memcache_get); +PHP_FUNCTION(memcache_delete); +PHP_FUNCTION(memcache_debug); + +#define MMC_BUF_SIZE 4096 +#define MMC_SERIALIZED 1 +#define MMC_COMPRESSED 2 +#define MMC_DEFAULT_TIMEOUT 1 /* seconds */ +#define MMC_KEY_MAX_SIZE 250 /* stoled from memcached sources =) */ + +typedef struct mmc { + int id; + php_stream *stream; + char inbuf[MMC_BUF_SIZE]; + long timeout; +} mmc_t; + +/* our globals */ +typedef struct { + long debug_mode; +} php_memcache_globals; + +#ifdef ZTS +#define MEMCACHE_G(v) TSRMG(memcache_globals_id, zend_memcache_globals *, v) +#else +#define MEMCACHE_G(v) (memcache_globals.v) +#endif + +#endif /* PHP_MEMCACHE_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */