├── CREDITS ├── README.markdown ├── common.h ├── config.m4 ├── debian.control ├── debian ├── changelog ├── compat ├── control ├── copyright ├── postinst ├── postrm └── rules ├── igbinary ├── .gitignore ├── COPYING ├── CREDITS ├── ChangeLog ├── EXPERIMENTAL ├── NEWS ├── README ├── config.m4 ├── hash.h ├── hash_function.c ├── hash_function.h ├── hash_si.c ├── igbinary.c ├── igbinary.h ├── igbinary.php ├── igbinary.php.ini ├── igbinary.spec ├── package.xml ├── php_igbinary.h └── tags.sh ├── library.c ├── library.h ├── mkdeb-apache2.sh ├── php_redis.h ├── redis.c ├── redis_session.c ├── redis_session.h ├── serialize.list └── tests ├── TestRedis.php ├── commandsStatus.txt └── executeTests.sh /CREDITS: -------------------------------------------------------------------------------- 1 | Redis client extension for PHP 2 | Alfonso Jimenez 3 | Nasreddine Bouafif 4 | Nicolas Favre-Felix 5 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | PhpRedis 2 | ============= 3 | 4 | THE REPOSITORY HAS MOVED. 5 | ========================= 6 | 7 | Please note that this GitHub repository will no longer be maintained. 8 | The code now lives at [https://github.com/nicolasff/phpredis](https://github.com/nicolasff/phpredis). 9 | 10 | This code has been developed and maintained by [Owlient](http://owlient.eu/) from November 2009 to March 2011. 11 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | #include "php.h" 2 | #include "php_ini.h" 3 | 4 | #ifndef REDIS_COMMON_H 5 | #define REDIS_COMMON_H 6 | 7 | #define redis_sock_name "Redis Socket Buffer" 8 | #define REDIS_SOCK_STATUS_FAILED 0 9 | #define REDIS_SOCK_STATUS_DISCONNECTED 1 10 | #define REDIS_SOCK_STATUS_UNKNOWN 2 11 | #define REDIS_SOCK_STATUS_CONNECTED 3 12 | 13 | #define redis_multi_access_type_name "Redis Multi type access" 14 | 15 | #define _NL "\r\n" 16 | 17 | /* properties */ 18 | #define REDIS_NOT_FOUND 0 19 | #define REDIS_STRING 1 20 | #define REDIS_SET 2 21 | #define REDIS_LIST 3 22 | #define REDIS_ZSET 4 23 | #define REDIS_HASH 5 24 | 25 | /* options */ 26 | #define REDIS_OPT_SERIALIZER 1 27 | #define REDIS_OPT_PREFIX 2 28 | 29 | /* serializers */ 30 | #define REDIS_SERIALIZER_NONE 0 31 | #define REDIS_SERIALIZER_PHP 1 32 | #define REDIS_SERIALIZER_IGBINARY 2 33 | 34 | #define IF_MULTI() if(redis_sock->mode == MULTI) 35 | #define IF_MULTI_OR_ATOMIC() if(redis_sock->mode == MULTI || redis_sock->mode == ATOMIC)\ 36 | 37 | #define IF_MULTI_OR_PIPELINE() if(redis_sock->mode == MULTI || redis_sock->mode == PIPELINE) 38 | #define IF_PIPELINE() if(redis_sock->mode == PIPELINE) 39 | #define IF_NOT_MULTI() if(redis_sock->mode != MULTI) 40 | #define IF_ATOMIC() if(redis_sock->mode == ATOMIC) 41 | #define ELSE_IF_MULTI() else if(redis_sock->mode == MULTI) { \ 42 | if(redis_response_enqueued(redis_sock TSRMLS_CC) == 1) {\ 43 | RETURN_ZVAL(getThis(), 1, 0);\ 44 | } else {\ 45 | RETURN_FALSE;\ 46 | } \ 47 | } 48 | 49 | #define ELSE_IF_PIPELINE() else IF_PIPELINE() { \ 50 | RETURN_ZVAL(getThis(), 1, 0);\ 51 | } 52 | 53 | 54 | #define MULTI_RESPONSE(callback) IF_MULTI_OR_PIPELINE() { \ 55 | fold_item *f1 = malloc(sizeof(fold_item)); \ 56 | f1->fun = (void *)callback; \ 57 | f1->next = NULL; \ 58 | fold_item *current = redis_sock->current;\ 59 | if(current) current->next = f1; \ 60 | redis_sock->current = f1; \ 61 | } 62 | 63 | #define PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len) request_item *tmp; \ 64 | tmp = malloc(sizeof(request_item));\ 65 | tmp->request_str = calloc(cmd_len, 1);\ 66 | memcpy(tmp->request_str, cmd, cmd_len);\ 67 | tmp->request_size = cmd_len;\ 68 | tmp->next = NULL;\ 69 | struct request_item *current_request = redis_sock->pipeline_current; \ 70 | if(current_request) {\ 71 | current_request->next = tmp;\ 72 | } \ 73 | redis_sock->pipeline_current = tmp; \ 74 | if(NULL == redis_sock->pipeline_head) { \ 75 | redis_sock->pipeline_head = redis_sock->pipeline_current;\ 76 | } 77 | 78 | #define SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { \ 79 | efree(cmd); \ 80 | RETURN_FALSE; \ 81 | } 82 | 83 | #define REDIS_SAVE_CALLBACK(callback, closure_context) IF_MULTI_OR_PIPELINE() { \ 84 | fold_item *f1 = malloc(sizeof(fold_item)); \ 85 | f1->fun = (void *)callback; \ 86 | f1->ctx = closure_context; \ 87 | f1->next = NULL; \ 88 | fold_item *current = redis_sock->current;\ 89 | if(current) current->next = f1; \ 90 | redis_sock->current = f1; \ 91 | if(NULL == redis_sock->head) { \ 92 | redis_sock->head = redis_sock->current;\ 93 | }\ 94 | } 95 | 96 | #define REDIS_ELSE_IF_MULTI(function, closure_context) \ 97 | else if(redis_sock->mode == MULTI) { \ 98 | if(redis_response_enqueued(redis_sock TSRMLS_CC) == 1) {\ 99 | REDIS_SAVE_CALLBACK(function, closure_context); \ 100 | RETURN_ZVAL(getThis(), 1, 0);\ 101 | } else {\ 102 | RETURN_FALSE;\ 103 | }\ 104 | } 105 | 106 | #define REDIS_ELSE_IF_PIPELINE(function, closure_context) else IF_PIPELINE() { \ 107 | REDIS_SAVE_CALLBACK(function, closure_context); \ 108 | RETURN_ZVAL(getThis(), 1, 0);\ 109 | } 110 | 111 | #define REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) \ 112 | IF_MULTI_OR_ATOMIC() { \ 113 | SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len); \ 114 | efree(cmd); \ 115 | }\ 116 | IF_PIPELINE() { \ 117 | PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len); \ 118 | efree(cmd); \ 119 | } 120 | 121 | #define REDIS_PROCESS_RESPONSE_CLOSURE(function, closure_context) \ 122 | REDIS_ELSE_IF_MULTI(function, closure_context) \ 123 | REDIS_ELSE_IF_PIPELINE(function, closure_context); 124 | 125 | #define REDIS_PROCESS_RESPONSE(function) REDIS_PROCESS_RESPONSE_CLOSURE(function, NULL) 126 | 127 | typedef enum {ATOMIC, MULTI, PIPELINE} redis_mode; 128 | 129 | typedef struct fold_item { 130 | zval * (*fun)(INTERNAL_FUNCTION_PARAMETERS, void *, ...); 131 | void *ctx; 132 | struct fold_item *next; 133 | } fold_item; 134 | 135 | typedef struct request_item { 136 | char *request_str; 137 | int request_size; /* size_t */ 138 | struct request_item *next; 139 | } request_item; 140 | 141 | /* {{{ struct RedisSock */ 142 | typedef struct { 143 | php_stream *stream; 144 | char *host; 145 | short port; 146 | double timeout; 147 | int failed; 148 | int status; 149 | int persistent; 150 | 151 | int serializer; 152 | 153 | char *prefix; 154 | int prefix_len; 155 | 156 | redis_mode mode; 157 | fold_item *head; 158 | fold_item *current; 159 | 160 | request_item *pipeline_head; 161 | request_item *pipeline_current; 162 | } RedisSock; 163 | /* }}} */ 164 | 165 | void 166 | free_reply_callbacks(zval *z_this, RedisSock *redis_sock); 167 | 168 | #endif 169 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id$ 2 | dnl config.m4 for extension redis 3 | 4 | PHP_ARG_ENABLE(redis, whether to enable redis support, 5 | dnl Make sure that the comment is aligned: 6 | [ --enable-redis Enable redis support]) 7 | 8 | if test "$PHP_REDIS" != "no"; then 9 | 10 | dnl # --with-redis -> check with-path 11 | dnl SEARCH_PATH="/usr/local /usr" # you might want to change this 12 | dnl SEARCH_FOR="/include/redis.h" # you most likely want to change this 13 | dnl if test -r $PHP_REDIS/$SEARCH_FOR; then # path given as parameter 14 | dnl REDIS_DIR=$PHP_REDIS 15 | dnl else # search default path list 16 | dnl AC_MSG_CHECKING([for redis files in default path]) 17 | dnl for i in $SEARCH_PATH ; do 18 | dnl if test -r $i/$SEARCH_FOR; then 19 | dnl REDIS_DIR=$i 20 | dnl AC_MSG_RESULT(found in $i) 21 | dnl fi 22 | dnl done 23 | dnl fi 24 | dnl 25 | dnl if test -z "$REDIS_DIR"; then 26 | dnl AC_MSG_RESULT([not found]) 27 | dnl AC_MSG_ERROR([Please reinstall the redis distribution]) 28 | dnl fi 29 | 30 | dnl # --with-redis -> add include path 31 | dnl PHP_ADD_INCLUDE($REDIS_DIR/include) 32 | 33 | dnl # --with-redis -> check for lib and symbol presence 34 | dnl LIBNAME=redis # you may want to change this 35 | dnl LIBSYMBOL=redis # you most likely want to change this 36 | 37 | dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, 38 | dnl [ 39 | dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $REDIS_DIR/lib, REDIS_SHARED_LIBADD) 40 | dnl AC_DEFINE(HAVE_REDISLIB,1,[ ]) 41 | dnl ],[ 42 | dnl AC_MSG_ERROR([wrong redis lib version or lib not found]) 43 | dnl ],[ 44 | dnl -L$REDIS_DIR/lib -lm -ldl 45 | dnl ]) 46 | dnl 47 | dnl PHP_SUBST(REDIS_SHARED_LIBADD) 48 | 49 | PHP_NEW_EXTENSION(redis, redis.c library.c redis_session.c igbinary/igbinary.c igbinary/hash_si.c igbinary/hash_function.c, $ext_shared) 50 | fi 51 | -------------------------------------------------------------------------------- /debian.control: -------------------------------------------------------------------------------- 1 | Package: phpredis 2 | Version: 1.0 3 | Section: web 4 | Priority: optional 5 | Architecture: all 6 | Essential: no 7 | Depends: 8 | Pre-Depends: 9 | Recommends: php5 10 | Suggests: 11 | Installed-Size: 12 | Maintainer: Nicolas Favre-Felix [n.favre-felix@owlient.eu] 13 | Conflicts: 14 | Replaces: 15 | Provides: phpredis 16 | Description: Redis C extension for PHP5. 17 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | phpredis (2.0.11) unstable; urgency=low 2 | 3 | * Merged with upstream 4 | 5 | -- Simon Effenberg Tue, 30 Nov 2010 09:20:40 +0100 6 | 7 | phpredis (2.0.10-1) unstable; urgency=low 8 | 9 | * Fixed wrong timeout option when setting timeout to lower then 1s. 10 | 11 | -- Simon Effenberg Mon, 08 Nov 2010 12:30:54 +0100 12 | 13 | phpredis (2.0.10) unstable; urgency=low 14 | 15 | * Merged with upstream 16 | 17 | -- Simon Effenberg Fri, 05 Nov 2010 16:07:09 +0100 18 | 19 | phpredis (2.0.8-2) unstable; urgency=low 20 | 21 | * Fixed problem when doing incrBy or decrBy with 1 like incrBy('key', 1). 22 | 23 | -- Simon Effenberg Wed, 27 Oct 2010 13:33:18 +0200 24 | 25 | phpredis (2.0.8-1) unstable; urgency=low 26 | 27 | * Merged with upstream but there seems to be no new version. 28 | * incrBy/decrBy added 29 | 30 | -- Simon Effenberg Wed, 27 Oct 2010 10:36:35 +0200 31 | 32 | phpredis (2.0.8) unstable; urgency=low 33 | 34 | * Merged with upstream 35 | 36 | -- Simon Effenberg Fri, 15 Oct 2010 13:00:32 +0200 37 | 38 | phpredis (2.0.4) unstable; urgency=low 39 | 40 | * Merged with upstream 41 | 42 | -- Simon Effenberg Tue, 05 Oct 2010 09:47:48 +0200 43 | 44 | phpredis (2.0.2) unstable; urgency=low 45 | 46 | * Fixed version number 47 | 48 | -- Nicolas Favre-Felix Thu, 23 Sep 2010 14:28:00 +0100 49 | 50 | phpredis (1.0.0-2) unstable; urgency=low 51 | 52 | * Fix some little debian/* problems. 53 | 54 | -- Simon Effenberg Thu, 23 Sep 2010 11:50:59 +0200 55 | 56 | phpredis (1.0.0-1) unstable; urgency=low 57 | 58 | * Initial release. 59 | 60 | -- Simon Effenberg Wed, 22 Sep 2010 16:04:53 +0200 61 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 7 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: phpredis 2 | Section: web 3 | Priority: optional 4 | Maintainer: Nicolas Favre-Felix 5 | Build-Depends: debhelper (>= 7), php5-dev 6 | Standards-Version: 3.8.0 7 | Homepage: http://github.com/owlient/phpredis 8 | 9 | Package: phpredis 10 | Architecture: any 11 | Depends: ${shlibs:Depends}, ${misc:Depends}, ${php5:Depends} 12 | Recommends: php5 13 | Description: Redis C extension for PHP5. 14 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | This package was debianized by: 2 | 3 | Simon Effenberg on Mon, 22 Sep 2010 14:24:03 +0100 4 | 5 | It was downloaded from: 6 | 7 | http://github.com/owlient/phpredis 8 | 9 | Upstream Author(s): 10 | 11 | Nicolas Favre-Felix 12 | Nasreddine Bouafif 13 | 14 | Copyright: 15 | 16 | 17 | 18 | 19 | License: 20 | 21 | ### SELECT: ### 22 | This package is free software; you can redistribute it and/or modify 23 | it under the terms of the GNU General Public License as published by 24 | the Free Software Foundation; either version 2 of the License, or 25 | (at your option) any later version. 26 | ### OR ### 27 | This package is free software; you can redistribute it and/or modify 28 | it under the terms of the GNU General Public License version 2 as 29 | published by the Free Software Foundation. 30 | ########## 31 | 32 | This package is distributed in the hope that it will be useful, 33 | but WITHOUT ANY WARRANTY; without even the implied warranty of 34 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 35 | GNU General Public License for more details. 36 | 37 | You should have received a copy of the GNU General Public License 38 | along with this package; if not, write to the Free Software 39 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 40 | 41 | On Debian systems, the complete text of the GNU General 42 | Public License can be found in `/usr/share/common-licenses/GPL'. 43 | 44 | The Debian packaging is: 45 | 46 | Copyright C) 2010, Simon Effenberg 47 | 48 | and is licensed under the GPL, see above. 49 | 50 | 51 | # Please also look if there are files or directories which have a 52 | # different copyright/license attached and list them here. 53 | 54 | -------------------------------------------------------------------------------- /debian/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # postinst script for phpredis 3 | # 4 | # see: dh_installdeb(1) 5 | 6 | set -e 7 | 8 | # summary of how this script can be called: 9 | # * `configure' 10 | # * `abort-upgrade' 11 | # * `abort-remove' `in-favour' 12 | # 13 | # * `abort-remove' 14 | # * `abort-deconfigure' `in-favour' 15 | # `removing' 16 | # 17 | # for details, see http://www.debian.org/doc/debian-policy/ or 18 | # the debian-policy package 19 | 20 | case "$1" in 21 | configure) 22 | cat > /etc/php5/conf.d/redis.ini <&2 33 | exit 1 34 | ;; 35 | esac 36 | 37 | # dh_installdeb will replace this with shell code automatically 38 | # generated by other debhelper scripts. 39 | 40 | #DEBHELPER# 41 | 42 | exit 0 43 | -------------------------------------------------------------------------------- /debian/postrm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # postinst script for an Apache virtual host component of plista 3 | # 4 | # see: dh_installdeb(1) 5 | 6 | set -e 7 | 8 | # summary of how this script can be called: 9 | # * `remove' 10 | # * `purge' 11 | # * `upgrade' 12 | # * `failed-upgrade' 13 | # * `abort-install' 14 | # * `abort-install' 15 | # * `abort-upgrade' 16 | # * `disappear' 17 | # 18 | # for details, see http://www.debian.org/doc/debian-policy/ or 19 | # the debian-policy package 20 | 21 | case "$1" in 22 | purge) 23 | if [ -e /etc/php5/conf.d/redis.ini ]; then 24 | rm -f /etc/php5/conf.d/redis.ini 25 | fi 26 | ;; 27 | remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) 28 | ;; 29 | 30 | *) 31 | echo "postrm called with unknown argument \`$1'" >&2 32 | exit 1 33 | ;; 34 | esac 35 | 36 | # dh_installdeb will replace this with shell code automatically 37 | # generated by other debhelper scripts. 38 | 39 | #DEBHELPER# 40 | 41 | exit 0 42 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # Sample debian/rules that uses debhelper. 3 | # This file is public domain software, originally written by Joey Hess. 4 | # 5 | # This version is for packages that are architecture dependent. 6 | 7 | # Uncomment this to turn on verbose mode. 8 | #export DH_VERBOSE=1 9 | 10 | DEB_SRCDIR=$(shell pwd) 11 | INSTALL_DIR=$(DEB_SRCDIR)/debian/$(shell dh_listpackages) 12 | EXTENSION_DIR=`php-config5 --extension-dir` 13 | CFLAGS = -O3 14 | 15 | 16 | build: build-stamp 17 | build-stamp: 18 | dh_testdir 19 | 20 | # Add here commands to compile the package. 21 | 22 | # Because phpize --clean removes all testfiles 23 | # in tests/*.php the svn-buildpackage will fail 24 | # when tests/TestRedis.php was removed. 25 | # So we backup the file: 26 | cd $(DEB_SRCDIR) && mv tests/TestRedis.php tests/TestRedis.php.bak && \ 27 | phpize --clean && mv tests/TestRedis.php.bak tests/TestRedis.php && \ 28 | phpize && \ 29 | ./configure --with-php-config=/usr/bin/php-config5 30 | $(MAKE) -C $(DEB_SRCDIR) 31 | 32 | touch build-stamp 33 | 34 | clean: 35 | dh_testdir 36 | dh_testroot 37 | rm -f build-stamp 38 | 39 | # Add here commands to clean up after the build process. 40 | # See comment in build-stamp why doing the 'mv' 41 | cd $(DEB_SRCDIR) && mv tests/TestRedis.php tests/TestRedis.php.bak && \ 42 | phpize --clean && mv tests/TestRedis.php.bak tests/TestRedis.php 43 | #$(MAKE) -C $(DEB_SRCDIR) clean 44 | #$(MAKE) distclean 45 | 46 | dh_clean 47 | 48 | install: build 49 | dh_testdir 50 | dh_testroot 51 | dh_prep 52 | dh_installdirs 53 | 54 | # Add here commands to install the package into debian/ 55 | $(MAKE) prefix=$(INSTALL_DIR)/usr EXTENSION_DIR=$(INSTALL_DIR)$(EXTENSION_DIR) install 56 | 57 | # Build architecture-independent files here. 58 | binary-indep: build install 59 | # We have nothing to do by default. 60 | 61 | # Build architecture-dependent files here. 62 | binary-arch: build install 63 | dh_testdir 64 | dh_testroot 65 | dh_installchangelogs 66 | dh_installdocs 67 | dh_installexamples 68 | # dh_install 69 | # dh_installmenu 70 | # dh_installdebconf 71 | # dh_installlogrotate 72 | # dh_installemacsen 73 | # dh_installcatalogs 74 | # dh_installpam 75 | # dh_installmime 76 | # dh_installinit 77 | # dh_installcron 78 | # dh_installinfo 79 | # dh_installwm 80 | # dh_installudev 81 | # dh_lintian 82 | # dh_bugfiles 83 | # dh_undocumented 84 | dh_installman 85 | dh_link 86 | dh_strip 87 | dh_compress 88 | dh_fixperms 89 | # dh_perl 90 | # dh_makeshlibs 91 | dh_installdeb 92 | dh_shlibdeps 93 | cd $(DEB_SRCDIR) && \ 94 | echo "php5:Depends=phpapi-`php-config5 --phpapi`, php5-common" >> debian/phpredis.substvars 95 | dh_gencontrol 96 | dh_md5sums 97 | dh_builddeb 98 | 99 | binary: binary-indep binary-arch 100 | .PHONY: build clean binary-indep binary-arch binary install 101 | -------------------------------------------------------------------------------- /igbinary/.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.lo 3 | *.la 4 | .deps 5 | .libs 6 | Makefile 7 | Makefile.fragments 8 | Makefile.global 9 | Makefile.objects 10 | acinclude.m4 11 | aclocal.m4 12 | autom4te.cache 13 | build 14 | config.cache 15 | config.guess 16 | config.h 17 | config.h.in 18 | config.log 19 | config.nice 20 | config.status 21 | config.sub 22 | configure 23 | configure.in 24 | conftest 25 | conftest.c 26 | include 27 | install-sh 28 | libtool 29 | ltmain.sh 30 | missing 31 | mkinstalldirs 32 | modules 33 | scan_makefile_in.awk 34 | *.dsw 35 | *.plg 36 | *.opt 37 | *.ncb 38 | Release 39 | Release_inline 40 | Debug 41 | Release_TS 42 | Release_TSDbg 43 | Release_TS_inline 44 | Debug_TS 45 | run-tests.php 46 | cscope.out 47 | tests/*.out 48 | tests/*.php 49 | tests/*.mem 50 | tests/*.diff 51 | tests/*.log 52 | tests/*.exp 53 | tmp-php.ini 54 | -------------------------------------------------------------------------------- /igbinary/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008 Sulake Dynamoid Oy 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | - Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | - Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | - Neither the name of the 'igbinary' nor the names of its contributors may 15 | be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 28 | THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /igbinary/CREDITS: -------------------------------------------------------------------------------- 1 | igbinary is produced for IRC-Galleria (http://irc-galleria.net, a finnish community site) 2 | by Sulake Dynamoid Oy and its employees. 3 | 4 | - Igbinary 5 | Oleg Grenrus 6 | 7 | - Hash functions 8 | Bob Jenkins 9 | -------------------------------------------------------------------------------- /igbinary/ChangeLog: -------------------------------------------------------------------------------- 1 | 2008-06-29 Oleg Grenrus 2 | * config.m4: fixed CFLAGS 3 | * igbinary.c: do not init object & string hashes if serializing scalars. 4 | unserialize_callback_func support. 5 | igbinary_serialize and igbinary_unserialize for the external use. 6 | * intbinary.h: header for external use 7 | * tests/022.phpt: unserialize_callback_func test 8 | * tests/023.phpt: resource serializes to null test 9 | * tests/002.phpt tests/003.phpt tests/004.phpt tests/005.phpt tests/006.phpt: == -> === 10 | * tests/015b.phpt: pointed out http://bugs.net/bug.php?id=45406 11 | -------------------------------------------------------------------------------- /igbinary/EXPERIMENTAL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owlient/phpredis/90ecd1798da817f7b3f02fd430633d3e5d127654/igbinary/EXPERIMENTAL -------------------------------------------------------------------------------- /igbinary/NEWS: -------------------------------------------------------------------------------- 1 | 1.0.1 2008-07-05 2 | ======== 3 | * unserialize_callback_func support 4 | * slight speedup when serializing scalars 5 | 6 | 1.0.0 2008-06-25 7 | ======== 8 | * Public version 9 | -------------------------------------------------------------------------------- /igbinary/README: -------------------------------------------------------------------------------- 1 | igbinary 2 | ======== 3 | 4 | What it is? 5 | ----------- 6 | 7 | Igbinary is a drop in replacement for the standard php serializer. Instead of 8 | time and space consuming textual representation, igbinary stores php data 9 | structures in a compact binary form. Savings are significant when using 10 | memcached or similar memory based storages for serialized data. 11 | 12 | But where does the name "igbinary" come from? There was once a similar project 13 | called fbinary but it has disappeared from the Internet. Its architecture 14 | wasn't very clean either. IG is an abbreviation for a finnish social networking 15 | site IRC-Galleria (http://irc-galleria.net/) 16 | 17 | Supports: 18 | - PHP5 (tested with 5.2.4 and 5.2.5) 19 | - Same data types as the standard php serializer: 20 | null, bool, int, float, string, array and objects. 21 | - __autoload & unserialize_callback_func 22 | - __sleep & __wakeup 23 | - Serializable -interface 24 | - Data portability between platforms (32/64bit, endianess) 25 | - Tested on Linux amd64, Mac OSX x86 and NetBSD sparc64 26 | 27 | Why to use 28 | ---------- 29 | 30 | Storing complex php data structures like arrays of associative arrays in 31 | serialized form is not very space efficient. Igbinary uses two strategies to 32 | minimize size of the serialized form. 33 | 34 | Strings are stored only once by using a hash table. Arrays of associate arrays 35 | with very verbose keys are stored very compactly. 36 | 37 | Numerical values are stored in the smallest primitive data type available. 38 | 123 = int8_t 39 | 1234 = int16_t 40 | 123456 = int32_t 41 | ... and so on. 42 | 43 | How to use 44 | ---------- 45 | 46 | Add the following lines to your php.ini: 47 | 48 | # Load igbinary extension 49 | extension=igbinary.so 50 | 51 | # Use igbinary as session serializer 52 | session.serialize_handler=igbinary 53 | 54 | .. and in your php code replace serialize and unserialize function calls 55 | with igbinary_serialize and igbinary_unserialize. 56 | 57 | Installing 58 | ---------- 59 | 60 | Note: 61 | Sometimes you may have to substitute phpize with phpize5. 62 | is such cases you will probably want to add "--with-php-config=.../php-config5" as 63 | an option to configure. 64 | 65 | Compiling: 66 | 67 | 1. phpize 68 | 69 | If you use GCC: 70 | 2. ./configure CFLAGS="-O2 -g" --enable-igbinary 71 | 72 | If you use ICC (Intel C Compiler) 73 | 2. ./configure CFLAGS=" -no-prec-div -O3 -xO -unroll2 -g" CC=icc --enable-igbinary 74 | 75 | 3. make 76 | 4. ( make test) 77 | 5. make install 78 | 6. igbinary.so is installed in the default extensions directory 79 | 80 | Bugs & Contributions 81 | -------------------- 82 | 83 | To report bugs, or to contribute fixes and improvements send email to 84 | opensource@dynamoid.com 85 | 86 | Use from other extensions 87 | ------------------------- 88 | 89 | If you are writing own extension and it is possible to use igbinary. 90 | Igbinary installs own header igbinary.h to the ext/igbinary/igbinary.h. 91 | There are just two straighforward functions: igbinary_serialize and igbinary_unserialize. 92 | Look at the igbinary.h for prototypes and more information. 93 | 94 | Use PHP_ADD_EXTENSION_DEP(yourextension, igbinary) in config.m4 if someone wants 95 | compile both of them statically into php. 96 | -------------------------------------------------------------------------------- /igbinary/config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id: config.m4,v 1.7 2008/07/03 17:05:57 phadej Exp $ 2 | dnl config.m4 for extension igbinary 3 | 4 | dnl Comments in this file start with the string 'dnl'. 5 | dnl Remove where necessary. This file will not work 6 | dnl without editing. 7 | 8 | dnl If your extension references something external, use with: 9 | 10 | dnl PHP_ARG_WITH(igbinary, for igbinary support, 11 | dnl Make sure that the comment is aligned: 12 | dnl [ --with-igbinary Include igbinary support]) 13 | 14 | dnl Otherwise use enable: 15 | 16 | PHP_ARG_ENABLE(igbinary, whether to enable igbinary support, 17 | [ --enable-igbinary Enable igbinary support]) 18 | 19 | if test "$PHP_IGBINARY" != "no"; then 20 | AC_CHECK_HEADERS([stdbool.h],, AC_MSG_ERROR([stdbool.h not exists])) 21 | AC_CHECK_HEADERS([stddef.h],, AC_MSG_ERROR([stddef.h not exists])) 22 | AC_CHECK_HEADERS([stdint.h],, AC_MSG_ERROR([stdint.h not exists])) 23 | 24 | AC_CHECK_SIZEOF([long]) 25 | 26 | dnl GCC 27 | AC_MSG_CHECKING(compiler type) 28 | if test ! -z "`$CC --version | grep -i GCC`"; then 29 | AC_MSG_RESULT(gcc) 30 | PHP_IGBINARY_CFLAGS="-Wall -Wpointer-arith -Wmissing-prototypes -Wstrict-prototypes -Wcast-align -Wshadow -Wwrite-strings -Wswitch -Winline -finline-limit=10000 --param large-function-growth=10000 --param inline-unit-growth=10000" 31 | elif test ! -z "`$CC --version | grep -i ICC`"; then 32 | AC_MSG_RESULT(icc) 33 | PHP_IGBINARY_CFLAGS="-no-prec-div -O3 -x0 -unroll2" 34 | else 35 | AC_MSG_RESULT(other) 36 | fi 37 | 38 | PHP_INSTALL_HEADERS([ext/igbinary], [igbinary.h]) 39 | PHP_NEW_EXTENSION(igbinary, igbinary.c hash_si.c hash_function.c, $ext_shared,, $PHP_IGBINARY_CFLAGS) 40 | fi 41 | -------------------------------------------------------------------------------- /igbinary/hash.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Oleg Grenrus 3 | * 4 | * $Id: hash.h,v 1.5 2008/07/01 17:02:18 phadej Exp $ 5 | */ 6 | 7 | #ifndef HASH_H 8 | #define HASH_H 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | /** Key/value pair of hash_si. 15 | * @author Oleg Grenrus 16 | * @see hash_si 17 | */ 18 | struct hash_si_pair 19 | { 20 | char *key; /**< Pointer to key. */ 21 | size_t key_len; /**< Key length. */ 22 | uint32_t value; /**< Value. */ 23 | }; 24 | 25 | /** Hash-array. 26 | * Like c++ map. 27 | * Current implementation uses linear probing. 28 | * @author Oleg Grenrus 29 | */ 30 | struct hash_si { 31 | size_t size; /**< Allocated size of array. */ 32 | size_t used; /**< Used size of array. */ 33 | struct hash_si_pair *data; /**< Pointer to array or pairs of data. */ 34 | }; 35 | 36 | /** Inits hash_si structure. 37 | * @param h pointer to hash_si struct. 38 | * @param size initial size of the hash array. 39 | * @return 0 on success, 1 else. 40 | */ 41 | int hash_si_init (struct hash_si *h, size_t size); 42 | 43 | /** Frees hash_si structure. 44 | * Doesn't call free(h). 45 | * @param h pointer to hash_si struct. 46 | */ 47 | void hash_si_deinit (struct hash_si *h); 48 | 49 | /** Inserts value into hash_si. 50 | * @param h Pointer to hash_si struct. 51 | * @param key Pointer to key. 52 | * @param key_len Key length. 53 | * @param value Value. 54 | * @return 0 on success, 1 or 2 else. 55 | */ 56 | int hash_si_insert (struct hash_si *h, const char *key, size_t key_len, uint32_t value); 57 | 58 | /** Finds value from hash_si. 59 | * Value returned thru value param. 60 | * @param h Pointer to hash_si struct. 61 | * @param key Pointer to key. 62 | * @param key_len Key length. 63 | * @param[out] value Found value. 64 | * @return 0 if found, 1 if not. 65 | */ 66 | int hash_si_find (struct hash_si *h, const char *key, size_t key_len, uint32_t * value); 67 | 68 | /** Remove value from hash_si. 69 | * Removed value is available thru value param. 70 | * @param h Pointer to hash_si struct. 71 | * @param key Pointer to key. 72 | * @param key_len Key length. 73 | * @param[out] value Removed value. 74 | * @return 0 ivalue removed, 1 if not existed. 75 | */ 76 | int hash_si_remove (struct hash_si *h, const char *key, size_t key_len, uint32_t * value); 77 | 78 | /** Travarses hash_si. 79 | * Calls traverse_function on every item. Traverse function should not modify hash 80 | * @param h Pointer to hash_si struct. 81 | * @param traverse_function Function to call on every item of hash_si. 82 | */ 83 | void hash_si_traverse (struct hash_si *h, int (*traverse_function) (const char *key, size_t key_len, uint32_t value)); 84 | 85 | /** Returns size of hash_si. 86 | * @param h Pointer to hash_si struct. 87 | * @return Size of hash_si. 88 | */ 89 | size_t hash_si_size (struct hash_si *h); 90 | 91 | /** Returns capacity of hash_si. 92 | * @param h Pointer to hash_si struct. 93 | * @return Capacity of hash_si. 94 | */ 95 | size_t hash_si_capacity (struct hash_si *h); 96 | 97 | #endif /* HASH_H */ 98 | -------------------------------------------------------------------------------- /igbinary/hash_function.c: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------- 3 | lookup3.c, by Bob Jenkins, May 2006, Public Domain. 4 | */ 5 | 6 | #include /* attempt to define endianness */ 7 | #ifdef linux 8 | # include /* attempt to define endianness */ 9 | #endif 10 | 11 | #include "hash_function.h" 12 | 13 | #define hashsize(n) ((uint32_t)1<<(n)) 14 | #define hashmask(n) (hashsize(n)-1) 15 | #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) 16 | 17 | /* 18 | ------------------------------------------------------------------------------- 19 | mix -- mix 3 32-bit values reversibly. 20 | 21 | This is reversible, so any information in (a,b,c) before mix() is 22 | still in (a,b,c) after mix(). 23 | 24 | If four pairs of (a,b,c) inputs are run through mix(), or through 25 | mix() in reverse, there are at least 32 bits of the output that 26 | are sometimes the same for one pair and different for another pair. 27 | This was tested for: 28 | * pairs that differed by one bit, by two bits, in any combination 29 | of top bits of (a,b,c), or in any combination of bottom bits of 30 | (a,b,c). 31 | * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed 32 | the output delta to a Gray code (a^(a>>1)) so a string of 1's (as 33 | is commonly produced by subtraction) look like a single 1-bit 34 | difference. 35 | * the base values were pseudorandom, all zero but one bit set, or 36 | all zero plus a counter that starts at zero. 37 | 38 | Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that 39 | satisfy this are 40 | 4 6 8 16 19 4 41 | 9 15 3 18 27 15 42 | 14 9 3 7 17 3 43 | Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing 44 | for "differ" defined as + with a one-bit base and a two-bit delta. I 45 | used http://burtleburtle.net/bob/hash/avalanche.html to choose 46 | the operations, constants, and arrangements of the variables. 47 | 48 | This does not achieve avalanche. There are input bits of (a,b,c) 49 | that fail to affect some output bits of (a,b,c), especially of a. The 50 | most thoroughly mixed value is c, but it doesn't really even achieve 51 | avalanche in c. 52 | 53 | This allows some parallelism. Read-after-writes are good at doubling 54 | the number of bits affected, so the goal of mixing pulls in the opposite 55 | direction as the goal of parallelism. I did what I could. Rotates 56 | seem to cost as much as shifts on every machine I could lay my hands 57 | on, and rotates are much kinder to the top and bottom bits, so I used 58 | rotates. 59 | ------------------------------------------------------------------------------- 60 | */ 61 | #define mix(a,b,c) \ 62 | { \ 63 | a -= c; a ^= rot(c, 4); c += b; \ 64 | b -= a; b ^= rot(a, 6); a += c; \ 65 | c -= b; c ^= rot(b, 8); b += a; \ 66 | a -= c; a ^= rot(c,16); c += b; \ 67 | b -= a; b ^= rot(a,19); a += c; \ 68 | c -= b; c ^= rot(b, 4); b += a; \ 69 | } 70 | 71 | /* 72 | ------------------------------------------------------------------------------- 73 | final -- final mixing of 3 32-bit values (a,b,c) into c 74 | 75 | Pairs of (a,b,c) values differing in only a few bits will usually 76 | produce values of c that look totally different. This was tested for 77 | * pairs that differed by one bit, by two bits, in any combination 78 | of top bits of (a,b,c), or in any combination of bottom bits of 79 | (a,b,c). 80 | * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed 81 | the output delta to a Gray code (a^(a>>1)) so a string of 1's (as 82 | is commonly produced by subtraction) look like a single 1-bit 83 | difference. 84 | * the base values were pseudorandom, all zero but one bit set, or 85 | all zero plus a counter that starts at zero. 86 | 87 | These constants passed: 88 | 14 11 25 16 4 14 24 89 | 12 14 25 16 4 14 24 90 | and these came close: 91 | 4 8 15 26 3 22 24 92 | 10 8 15 26 3 22 24 93 | 11 8 15 26 3 22 24 94 | ------------------------------------------------------------------------------- 95 | */ 96 | #define final(a,b,c) \ 97 | { \ 98 | c ^= b; c -= rot(b,14); \ 99 | a ^= c; a -= rot(c,11); \ 100 | b ^= a; b -= rot(a,25); \ 101 | c ^= b; c -= rot(b,16); \ 102 | a ^= c; a -= rot(c,4); \ 103 | b ^= a; b -= rot(a,14); \ 104 | c ^= b; c -= rot(b,24); \ 105 | } 106 | 107 | /* 108 | ------------------------------------------------------------------------------- 109 | hashlittle() -- hash a variable-length key into a 32-bit value 110 | k : the key (the unaligned variable-length array of bytes) 111 | length : the length of the key, counting by bytes 112 | initval : can be any 4-byte value 113 | Returns a 32-bit value. Every bit of the key affects every bit of 114 | the return value. Two keys differing by one or two bits will have 115 | totally different hash values. 116 | 117 | The best hash table sizes are powers of 2. There is no need to do 118 | mod a prime (mod is sooo slow!). If you need less than 32 bits, 119 | use a bitmask. For example, if you need only 10 bits, do 120 | h = (h & hashmask(10)); 121 | In which case, the hash table should have hashsize(10) elements. 122 | 123 | If you are hashing n strings (uint8_t **)k, do it like this: 124 | for (i=0, h=0; i 12) 144 | { 145 | a += k[0]; 146 | a += ((uint32_t)k[1])<<8; 147 | a += ((uint32_t)k[2])<<16; 148 | a += ((uint32_t)k[3])<<24; 149 | b += k[4]; 150 | b += ((uint32_t)k[5])<<8; 151 | b += ((uint32_t)k[6])<<16; 152 | b += ((uint32_t)k[7])<<24; 153 | c += k[8]; 154 | c += ((uint32_t)k[9])<<8; 155 | c += ((uint32_t)k[10])<<16; 156 | c += ((uint32_t)k[11])<<24; 157 | mix(a,b,c); 158 | length -= 12; 159 | k += 12; 160 | } 161 | 162 | /*-------------------------------- last block: affect all 32 bits of (c) */ 163 | switch(length) /* all the case statements fall through */ 164 | { 165 | case 12: c+=((uint32_t)k[11])<<24; 166 | case 11: c+=((uint32_t)k[10])<<16; 167 | case 10: c+=((uint32_t)k[9])<<8; 168 | case 9 : c+=k[8]; 169 | case 8 : b+=((uint32_t)k[7])<<24; 170 | case 7 : b+=((uint32_t)k[6])<<16; 171 | case 6 : b+=((uint32_t)k[5])<<8; 172 | case 5 : b+=k[4]; 173 | case 4 : a+=((uint32_t)k[3])<<24; 174 | case 3 : a+=((uint32_t)k[2])<<16; 175 | case 2 : a+=((uint32_t)k[1])<<8; 176 | case 1 : a+=k[0]; 177 | break; 178 | case 0 : return c; 179 | } 180 | 181 | final(a,b,c); 182 | return c; 183 | } 184 | -------------------------------------------------------------------------------- /igbinary/hash_function.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Oleg Grenrus 3 | * 4 | * $Id: hash_function.h,v 1.4 2008/07/01 17:02:18 phadej Exp $ 5 | */ 6 | 7 | #ifndef HASH_FUNCTION_H 8 | #define HASH_FUNCTION_H 9 | 10 | #include /* defines uint32_t etc */ 11 | 12 | /** 13 | * Hash function 14 | * 15 | * At this moment lookup3 by Bob Jerkins 16 | * 17 | * @param key key 18 | * @param length key length 19 | * @param initval hash init val 20 | * @return hash value of key 21 | * @see http://burtleburtle.net/bob/hash/index.html 22 | * @author Bob Jerkins 23 | */ 24 | uint32_t hash_function(const void *key, size_t length, uint32_t initval); 25 | 26 | #endif /* HASH_FUNCTION_H */ 27 | -------------------------------------------------------------------------------- /igbinary/hash_si.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Oleg Grenrus 3 | * 4 | * $Id: hash_si.c,v 1.5 2008/07/01 17:02:18 phadej Exp $ 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include "hash.h" 14 | #include "hash_function.h" 15 | 16 | /* {{{ nextpow2 */ 17 | /** Next power of 2. 18 | * @param n Integer. 19 | * @return next to n power of 2 . 20 | */ 21 | inline static uint32_t nextpow2(uint32_t n) { 22 | uint32_t m = 1; 23 | while (m < n) { 24 | m = m << 1; 25 | } 26 | 27 | return m; 28 | } 29 | /* }}} */ 30 | /* {{{ hash_si_init */ 31 | int hash_si_init(struct hash_si *h, size_t size) { 32 | size = nextpow2(size); 33 | 34 | h->size = size; 35 | h->used = 0; 36 | h->data = (struct hash_si_pair *) malloc(sizeof(struct hash_si_pair) * size); 37 | if (h->data == NULL) { 38 | return 1; 39 | } 40 | 41 | memset(h->data, 0, sizeof(struct hash_si_pair) * size); 42 | 43 | return 0; 44 | } 45 | /* }}} */ 46 | /* {{{ hash_si_deinit */ 47 | void hash_si_deinit(struct hash_si *h) { 48 | int i; 49 | 50 | for (i = 0; i < h->size; i++) { 51 | if (h->data[i].key != NULL) { 52 | free(h->data[i].key); 53 | } 54 | } 55 | 56 | free(h->data); 57 | 58 | h->size = 0; 59 | h->used = 0; 60 | } 61 | /* }}} */ 62 | /* {{{ _hash_si_find */ 63 | /** Returns index of key, or where it should be. 64 | * @param h Pointer to hash_si struct. 65 | * @param key Pointer to key. 66 | * @param key_len Key length. 67 | * @return index. 68 | */ 69 | inline static size_t _hash_si_find(struct hash_si *h, const char *key, size_t key_len) { 70 | uint32_t hv; 71 | size_t size; 72 | 73 | assert(h != NULL); 74 | 75 | size = h->size; 76 | hv = hash_function(key, key_len, 0) & (h->size-1); 77 | 78 | while (size > 0 && 79 | h->data[hv].key != NULL && 80 | (h->data[hv].key_len != key_len || memcmp(h->data[hv].key, key, key_len) != 0)) { 81 | /* linear prob */ 82 | hv = (hv + 1) & (h->size-1); 83 | size--; 84 | } 85 | 86 | return hv; 87 | } 88 | /* }}} */ 89 | /* {{{ hash_si_remove */ 90 | int hash_si_remove(struct hash_si *h, const char *key, size_t key_len, uint32_t *value) { 91 | uint32_t hv; 92 | uint32_t j, k; 93 | 94 | assert(h != NULL); 95 | 96 | hv = _hash_si_find(h, key, key_len); 97 | 98 | /* dont exists */ 99 | if (h->data[hv].key == NULL) { 100 | return 1; 101 | } 102 | 103 | h->used--; 104 | 105 | free(h->data[hv].key); 106 | 107 | if (value != NULL) 108 | *value = h->data[hv].value; 109 | 110 | j = (hv + 1) & (h->size-1); 111 | while (h->data[j].key != NULL) { 112 | k = hash_function(h->data[j].key, strlen(h->data[j].key), 0) & (h->size-1); 113 | if ((j > hv && (k <= hv || k > j)) || (j < hv && (k <= hv && k > j))) { 114 | h->data[hv].key = h->data[j].key; 115 | h->data[hv].key_len = h->data[j].key_len; 116 | h->data[hv].value = h->data[j].value; 117 | 118 | hv = j; 119 | } 120 | j = (j + 1) & (h->size-1); 121 | } 122 | h->data[hv].key = NULL; 123 | 124 | 125 | return 0; 126 | /* 127 | * loop 128 | * j := (j+1) modulo num_slots 129 | * if slot[j] is unoccupied 130 | * exit loop 131 | * k := hash(slot[j].key) modulo num_slots 132 | * if (j > i and (k <= i or k > j)) or 133 | * (j < i and (k <= i and k > j)) (note 2) 134 | * slot[i] := slot[j] 135 | * i := j 136 | * mark slot[i] as unoccupied 137 | * 138 | * For all records in a cluster, there must be no vacant slots between their natural 139 | * hash position and their current position (else lookups will terminate before finding 140 | * the record). At this point in the pseudocode, i is a vacant slot that might be 141 | * invalidating this property for subsequent records in the cluster. j is such a 142 | * subsequent record. k is the raw hash where the record at j would naturally land in 143 | * the hash table if there were no collisions. This test is asking if the record at j 144 | * is invalidly positioned with respect to the required properties of a cluster now 145 | * that i is vacant. 146 | * 147 | * Another technique for removal is simply to mark the slot as deleted. However 148 | * this eventually requires rebuilding the table simply to remove deleted records. 149 | * The methods above provide O(1) updating and removal of existing records, with 150 | * occasional rebuilding if the high water mark of the table size grows. 151 | */ 152 | } 153 | /* }}} */ 154 | /* {{{ hash_si_rehash */ 155 | /** Rehash/resize hash_si. 156 | * @param h Pointer to hash_si struct. 157 | */ 158 | inline static void hash_si_rehash(struct hash_si *h) { 159 | uint32_t hv; 160 | int i; 161 | struct hash_si newh; 162 | 163 | assert(h != NULL); 164 | 165 | hash_si_init(&newh, h->size * 2); 166 | 167 | for (i = 0; i < h->size; i++) { 168 | if (h->data[i].key != NULL) { 169 | hv = _hash_si_find(&newh, h->data[i].key, h->data[i].key_len); 170 | newh.data[hv].key = h->data[i].key; 171 | newh.data[hv].key_len = h->data[i].key_len; 172 | newh.data[hv].value = h->data[i].value; 173 | } 174 | } 175 | 176 | free(h->data); 177 | h->data = newh.data; 178 | h->size *= 2; 179 | } 180 | /* }}} */ 181 | /* {{{ hash_si_insert */ 182 | int hash_si_insert(struct hash_si *h, const char *key, size_t key_len, uint32_t value) { 183 | uint32_t hv; 184 | 185 | if (h->size / 4 * 3 < h->used + 1) { 186 | hash_si_rehash(h); 187 | } 188 | 189 | hv = _hash_si_find(h, key, key_len); 190 | 191 | if (h->data[hv].key == NULL) { 192 | h->data[hv].key = (char *) malloc(key_len + 1); 193 | if (h->data[hv].key == NULL) { 194 | return 1; 195 | } 196 | memcpy(h->data[hv].key, key, key_len); 197 | h->data[hv].key[key_len] = '\0'; 198 | h->data[hv].key_len = key_len; 199 | 200 | h->used++; 201 | } else { 202 | return 2; 203 | } 204 | 205 | h->data[hv].value = value; 206 | 207 | return 0; 208 | } 209 | /* }}} */ 210 | /* {{{ hash_si_find */ 211 | int hash_si_find(struct hash_si *h, const char *key, size_t key_len, uint32_t *value) { 212 | uint32_t hv; 213 | 214 | assert(h != NULL); 215 | 216 | hv = _hash_si_find(h, key, key_len); 217 | 218 | if (h->data[hv].key == NULL) { 219 | return 1; 220 | } else { 221 | *value = h->data[hv].value; 222 | return 0; 223 | } 224 | } 225 | /* }}} */ 226 | /* {{{ hash_si_traverse */ 227 | void hash_si_traverse(struct hash_si *h, int (*traverse_function) (const char *key, size_t key_len, uint32_t value)) { 228 | int i; 229 | 230 | assert(h != NULL && traverse_function != NULL); 231 | 232 | for (i = 0; i < h->size; i++) { 233 | if (h->data[i].key != NULL && traverse_function(h->data[i].key, h->data[i].key_len, h->data[i].value) != 1) { 234 | return; 235 | } 236 | } 237 | } 238 | /* }}} */ 239 | /* {{{ hash_si_size */ 240 | size_t hash_si_size(struct hash_si *h) { 241 | assert(h != NULL); 242 | 243 | return h->used; 244 | } 245 | /* }}} */ 246 | /* {{{ hash_si_capacity */ 247 | size_t hash_si_capacity(struct hash_si *h) { 248 | assert(h != NULL); 249 | 250 | return h->size; 251 | } 252 | /* }}} */ 253 | 254 | /* 255 | * Local variables: 256 | * tab-width: 2 257 | * c-basic-offset: 4 258 | * End: 259 | * vim600: noet sw=4 ts=4 fdm=marker 260 | * vim<600: noet sw=4 ts=4 261 | */ 262 | -------------------------------------------------------------------------------- /igbinary/igbinary.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Oleg Grenrus 3 | * 4 | * $Id: igbinary.c,v 1.33 2009/03/18 06:44:13 tricky Exp $ 5 | */ 6 | 7 | #ifdef HAVE_CONFIG_H 8 | #include "config.h" 9 | #endif 10 | 11 | #include "php.h" 12 | #include "php_ini.h" 13 | #include "zend_dynamic_array.h" 14 | #include "zend_alloc.h" 15 | #include "ext/standard/info.h" 16 | #include "ext/session/php_session.h" 17 | #include "ext/standard/php_incomplete_class.h" 18 | #include "php_igbinary.h" 19 | 20 | #include "igbinary.h" 21 | 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "hash.h" 30 | 31 | /** Session serializer function prototypes. */ 32 | PS_SERIALIZER_FUNCS(igbinary); 33 | 34 | /* {{{ Types */ 35 | enum igbinary_type { 36 | /* 00 */ igbinary_type_null, /**< Null. */ 37 | 38 | /* 01 */ igbinary_type_ref8, /**< Array reference. */ 39 | /* 02 */ igbinary_type_ref16, /**< Array reference. */ 40 | /* 03 */ igbinary_type_ref32, /**< Array reference. */ 41 | 42 | /* 04 */ igbinary_type_bool_false, /**< Boolean true. */ 43 | /* 05 */ igbinary_type_bool_true, /**< Boolean false. */ 44 | 45 | /* 06 */ igbinary_type_long8p, /**< Long 8bit positive. */ 46 | /* 07 */ igbinary_type_long8n, /**< Long 8bit negative. */ 47 | /* 08 */ igbinary_type_long16p, /**< Long 16bit positive. */ 48 | /* 09 */ igbinary_type_long16n, /**< Long 16bit negative. */ 49 | /* 0a */ igbinary_type_long32p, /**< Long 32bit positive. */ 50 | /* 0b */ igbinary_type_long32n, /**< Long 32bit negative. */ 51 | 52 | /* 0c */ igbinary_type_double, /**< Double. */ 53 | 54 | /* 0d */ igbinary_type_string_empty, /**< Empty string. */ 55 | 56 | /* 0e */ igbinary_type_string_id8, /**< String id. */ 57 | /* 0f */ igbinary_type_string_id16, /**< String id. */ 58 | /* 10 */ igbinary_type_string_id32, /**< String id. */ 59 | 60 | /* 11 */ igbinary_type_string8, /**< String. */ 61 | /* 12 */ igbinary_type_string16, /**< String. */ 62 | /* 13 */ igbinary_type_string32, /**< String. */ 63 | 64 | /* 14 */ igbinary_type_array8, /**< Array. */ 65 | /* 15 */ igbinary_type_array16, /**< Array. */ 66 | /* 16 */ igbinary_type_array32, /**< Array. */ 67 | 68 | /* 17 */ igbinary_type_object8, /**< Object. */ 69 | /* 18 */ igbinary_type_object16, /**< Object. */ 70 | /* 19 */ igbinary_type_object32, /**< Object. */ 71 | 72 | /* 1a */ igbinary_type_object_id8, /**< Object string id. */ 73 | /* 1b */ igbinary_type_object_id16, /**< Object string id. */ 74 | /* 1c */ igbinary_type_object_id32, /**< Object string id. */ 75 | 76 | /* 1d */ igbinary_type_object_ser8, /**< Object serialized data. */ 77 | /* 1e */ igbinary_type_object_ser16, /**< Object serialized data. */ 78 | /* 1f */ igbinary_type_object_ser32, /**< Object serialized data. */ 79 | 80 | /* 20 */ igbinary_type_long64p, /**< Long 64bit positive. */ 81 | /* 21 */ igbinary_type_long64n, /**< Long 64bit negative. */ 82 | 83 | /* 22 */ igbinary_type_objref8, /**< Object reference. */ 84 | /* 23 */ igbinary_type_objref16, /**< Object reference. */ 85 | /* 24 */ igbinary_type_objref32, /**< Object reference. */ 86 | 87 | /* 25 */ igbinary_type_ref, /**< Simple reference */ 88 | }; 89 | 90 | /** Serializer data. 91 | * @author Oleg Grenrus 92 | */ 93 | struct igbinary_serialize_data { 94 | uint8_t *buffer; /**< Buffer. */ 95 | size_t buffer_size; /**< Buffer size. */ 96 | size_t buffer_capacity; /**< Buffer capacity. */ 97 | bool scalar; /**< Serializing scalar. */ 98 | bool compact_strings; /**< Check for duplicate strings. */ 99 | struct hash_si strings; /**< Hash of already serialized strings. */ 100 | struct hash_si objects; /**< Hash of already serialized objects. */ 101 | int error; /**< Error number. Not used. */ 102 | }; 103 | 104 | /** String/len pair for the igbinary_unserializer_data. 105 | * @author Oleg Grenrus 106 | * @see igbinary_unserialize_data. 107 | */ 108 | struct igbinary_unserialize_string_pair { 109 | char *data; /**< Data. */ 110 | size_t len; /**< Data length. */ 111 | }; 112 | 113 | /** Unserializer data. 114 | * @author Oleg Grenrus 115 | */ 116 | struct igbinary_unserialize_data { 117 | uint8_t *buffer; /**< Buffer. */ 118 | size_t buffer_size; /**< Buffer size. */ 119 | size_t buffer_offset; /**< Current read offset. */ 120 | 121 | struct igbinary_unserialize_string_pair *strings; /**< Unserialized strings. */ 122 | size_t strings_count; /**< Unserialized string count. */ 123 | size_t strings_capacity; /**< Unserialized string array capacity. */ 124 | 125 | void **references; /**< Unserialized Arrays/Objects. */ 126 | size_t references_count; /**< Unserialized array/objects count. */ 127 | size_t references_capacity; /**< Unserialized array/object array capacity. */ 128 | 129 | int error; /**< Error number. Not used. */ 130 | smart_str string0_buf; /**< Temporary buffer for strings */ 131 | }; 132 | /* }}} */ 133 | /* {{{ Serializing functions prototypes */ 134 | inline static int igbinary_serialize_data_init(struct igbinary_serialize_data *igsd, bool scalar TSRMLS_DC); 135 | inline static void igbinary_serialize_data_deinit(struct igbinary_serialize_data *igsd TSRMLS_DC); 136 | 137 | inline static int igbinary_serialize_header(struct igbinary_serialize_data *igsd TSRMLS_DC); 138 | 139 | inline static int igbinary_serialize8(struct igbinary_serialize_data *igsd, uint8_t i TSRMLS_DC); 140 | inline static int igbinary_serialize16(struct igbinary_serialize_data *igsd, uint16_t i TSRMLS_DC); 141 | inline static int igbinary_serialize32(struct igbinary_serialize_data *igsd, uint32_t i TSRMLS_DC); 142 | inline static int igbinary_serialize64(struct igbinary_serialize_data *igsd, uint64_t i TSRMLS_DC); 143 | 144 | inline static int igbinary_serialize_null(struct igbinary_serialize_data *igsd TSRMLS_DC); 145 | inline static int igbinary_serialize_bool(struct igbinary_serialize_data *igsd, int b TSRMLS_DC); 146 | inline static int igbinary_serialize_long(struct igbinary_serialize_data *igsd, long l TSRMLS_DC); 147 | inline static int igbinary_serialize_double(struct igbinary_serialize_data *igsd, double d TSRMLS_DC); 148 | inline static int igbinary_serialize_string(struct igbinary_serialize_data *igsd, char *s, size_t len TSRMLS_DC); 149 | inline static int igbinary_serialize_chararray(struct igbinary_serialize_data *igsd, const char *s, size_t len TSRMLS_DC); 150 | 151 | inline static int igbinary_serialize_array(struct igbinary_serialize_data *igsd, zval *z, bool object, bool incomplete_class TSRMLS_DC); 152 | inline static int igbinary_serialize_array_ref(struct igbinary_serialize_data *igsd, zval *z, bool object TSRMLS_DC); 153 | inline static int igbinary_serialize_array_sleep(struct igbinary_serialize_data *igsd, zval *z, HashTable *ht, zend_class_entry *ce, bool incomplete_class TSRMLS_DC); 154 | inline static int igbinary_serialize_object_name(struct igbinary_serialize_data *igsd, const char *name, size_t name_len TSRMLS_DC); 155 | inline static int igbinary_serialize_object(struct igbinary_serialize_data *igsd, zval *z TSRMLS_DC); 156 | 157 | static int igbinary_serialize_zval(struct igbinary_serialize_data *igsd, zval *z TSRMLS_DC); 158 | /* }}} */ 159 | /* {{{ Unserializing functions prototypes */ 160 | inline static int igbinary_unserialize_data_init(struct igbinary_unserialize_data *igsd TSRMLS_DC); 161 | inline static void igbinary_unserialize_data_deinit(struct igbinary_unserialize_data *igsd TSRMLS_DC); 162 | 163 | inline static int igbinary_unserialize_header(struct igbinary_unserialize_data *igsd TSRMLS_DC); 164 | 165 | inline static uint8_t igbinary_unserialize8(struct igbinary_unserialize_data *igsd TSRMLS_DC); 166 | inline static uint16_t igbinary_unserialize16(struct igbinary_unserialize_data *igsd TSRMLS_DC); 167 | inline static uint32_t igbinary_unserialize32(struct igbinary_unserialize_data *igsd TSRMLS_DC); 168 | inline static uint64_t igbinary_unserialize64(struct igbinary_unserialize_data *igsd TSRMLS_DC); 169 | 170 | inline static int igbinary_unserialize_long(struct igbinary_unserialize_data *igsd, enum igbinary_type t, long *ret TSRMLS_DC); 171 | inline static int igbinary_unserialize_double(struct igbinary_unserialize_data *igsd, enum igbinary_type t, double *ret TSRMLS_DC); 172 | inline static int igbinary_unserialize_string(struct igbinary_unserialize_data *igsd, enum igbinary_type t, char **s, size_t *len TSRMLS_DC); 173 | inline static int igbinary_unserialize_chararray(struct igbinary_unserialize_data *igsd, enum igbinary_type t, char **s, size_t *len TSRMLS_DC); 174 | 175 | inline static int igbinary_unserialize_array(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval **z, int object TSRMLS_DC); 176 | inline static int igbinary_unserialize_object(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval **z TSRMLS_DC); 177 | inline static int igbinary_unserialize_object_ser(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval **z, zend_class_entry *ce TSRMLS_DC); 178 | inline static int igbinary_unserialize_ref(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval **z TSRMLS_DC); 179 | 180 | static int igbinary_unserialize_zval(struct igbinary_unserialize_data *igsd, zval **z TSRMLS_DC); 181 | /* }}} */ 182 | /* {{{ igbinary_functions[] */ 183 | 184 | ZEND_DECLARE_MODULE_GLOBALS(igbinary) 185 | 186 | /* {{{ INI entries */ 187 | PHP_INI_BEGIN() 188 | STD_PHP_INI_BOOLEAN("igbinary.compact_strings", "1", PHP_INI_ALL, OnUpdateBool, compact_strings, zend_igbinary_globals, igbinary_globals) 189 | PHP_INI_END() 190 | /* }}} */ 191 | 192 | /* {{{ php_igbinary_init_globals */ 193 | static void php_igbinary_init_globals(zend_igbinary_globals *igbinary_globals) { 194 | igbinary_globals->compact_strings = 1; 195 | } 196 | /* }}} */ 197 | 198 | /* {{{ PHP_MINIT_FUNCTION */ 199 | PHP_MINIT_FUNCTION(igbinary) { 200 | (void) type; 201 | (void) module_number; 202 | ZEND_INIT_MODULE_GLOBALS(igbinary, php_igbinary_init_globals, NULL); 203 | 204 | #if HAVE_PHP_SESSION 205 | php_session_register_serializer("igbinary", 206 | PS_SERIALIZER_ENCODE_NAME(igbinary), 207 | PS_SERIALIZER_DECODE_NAME(igbinary)); 208 | #endif 209 | REGISTER_INI_ENTRIES(); 210 | 211 | return SUCCESS; 212 | } 213 | /* }}} */ 214 | /* {{{ PHP_MSHUTDOWN_FUNCTION */ 215 | PHP_MSHUTDOWN_FUNCTION(igbinary) { 216 | (void) type; 217 | (void) module_number; 218 | 219 | #ifdef ZTS 220 | ts_free_id(igbinary_globals_id); 221 | #endif 222 | 223 | /* 224 | * unregister serializer? 225 | */ 226 | UNREGISTER_INI_ENTRIES(); 227 | 228 | return SUCCESS; 229 | } 230 | /* }}} */ 231 | /* {{{ PHP_MINFO_FUNCTION */ 232 | PHP_MINFO_FUNCTION(igbinary) { 233 | (void) zend_module; 234 | php_info_print_table_start(); 235 | php_info_print_table_row(2, "igbinary support", "enabled"); 236 | php_info_print_table_row(2, "igbinary version", IGBINARY_VERSION); 237 | php_info_print_table_row(2, "igbinary revision", "$Id: igbinary.c,v 1.33 2009/03/18 06:44:13 tricky Exp $"); 238 | php_info_print_table_end(); 239 | 240 | DISPLAY_INI_ENTRIES(); 241 | } 242 | /* }}} */ 243 | /* {{{ int igbinary_serialize(uint8_t**, size_t*, zval*) */ 244 | int igbinary_serialize(uint8_t **ret, size_t *ret_len, zval *z TSRMLS_DC) { 245 | struct igbinary_serialize_data igsd; 246 | 247 | if (igbinary_serialize_data_init(&igsd, Z_TYPE_P(z) != IS_OBJECT && Z_TYPE_P(z) != IS_ARRAY TSRMLS_CC)) { 248 | zend_error(E_WARNING, "igbinary_serialize: cannot init igsd"); 249 | return 1; 250 | } 251 | 252 | if (igbinary_serialize_header(&igsd TSRMLS_CC) != 0) { 253 | igbinary_serialize_data_deinit(&igsd TSRMLS_CC); 254 | return 1; 255 | } 256 | 257 | if (igbinary_serialize_zval(&igsd, z TSRMLS_CC) != 0) { 258 | igbinary_serialize_data_deinit(&igsd TSRMLS_CC); 259 | return 1; 260 | } 261 | 262 | *ret_len = igsd.buffer_size; 263 | *ret = (uint8_t *) emalloc(igsd.buffer_size); 264 | memcpy(*ret, igsd.buffer, igsd.buffer_size); 265 | 266 | igbinary_serialize_data_deinit(&igsd TSRMLS_CC); 267 | 268 | return 0; 269 | } 270 | /* }}} */ 271 | /* {{{ int igbinary_unserialize(const uint8_t *, size_t, zval **) */ 272 | int igbinary_unserialize(const uint8_t *buf, size_t buf_len, zval **z TSRMLS_DC) { 273 | struct igbinary_unserialize_data igsd; 274 | 275 | igbinary_unserialize_data_init(&igsd TSRMLS_CC); 276 | 277 | igsd.buffer = (uint8_t *) buf; 278 | igsd.buffer_size = buf_len; 279 | 280 | if (igbinary_unserialize_header(&igsd TSRMLS_CC)) { 281 | igbinary_unserialize_data_deinit(&igsd TSRMLS_CC); 282 | return 1; 283 | } 284 | 285 | if (igbinary_unserialize_zval(&igsd, z TSRMLS_CC)) { 286 | igbinary_unserialize_data_deinit(&igsd TSRMLS_CC); 287 | return 1; 288 | } 289 | 290 | igbinary_unserialize_data_deinit(&igsd TSRMLS_CC); 291 | 292 | return 0; 293 | } 294 | /* }}} */ 295 | /* {{{ proto string igbinary_unserialize(mixed value) */ 296 | PHP_FUNCTION(igbinary_unserialize) { 297 | (void) return_value_ptr; 298 | (void) this_ptr; 299 | (void) return_value_used; 300 | 301 | char *string; 302 | int string_len; 303 | 304 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &string, &string_len) == FAILURE) { 305 | RETURN_NULL(); 306 | } 307 | 308 | if (string_len <= 0) { 309 | RETURN_NULL(); 310 | } 311 | 312 | if (igbinary_unserialize((uint8_t *) string, string_len, &return_value TSRMLS_CC)) { 313 | RETURN_NULL(); 314 | } 315 | } 316 | /* }}} */ 317 | /* {{{ proto mixed igbinary_serialize(string value) */ 318 | PHP_FUNCTION(igbinary_serialize) { 319 | (void) return_value_ptr; 320 | (void) this_ptr; 321 | (void) return_value_used; 322 | 323 | zval *z; 324 | struct igbinary_serialize_data igsd; 325 | 326 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &z) == FAILURE) { 327 | RETURN_NULL(); 328 | } 329 | 330 | if (igbinary_serialize_data_init(&igsd, Z_TYPE_P(z) != IS_OBJECT && Z_TYPE_P(z) != IS_ARRAY TSRMLS_CC)) { 331 | zend_error(E_WARNING, "igbinary_serialize: cannot init igsd"); 332 | RETURN_NULL(); 333 | } 334 | 335 | igbinary_serialize_header(&igsd TSRMLS_CC); 336 | igbinary_serialize_zval(&igsd, z TSRMLS_CC); 337 | 338 | RETVAL_STRINGL((char *)igsd.buffer, igsd.buffer_size, 1); 339 | 340 | igbinary_serialize_data_deinit(&igsd TSRMLS_CC); 341 | } 342 | /* }}} */ 343 | /* {{{ Serializer encode function */ 344 | PS_SERIALIZER_ENCODE_FUNC(igbinary) 345 | { 346 | struct igbinary_serialize_data igsd; 347 | 348 | if (igbinary_serialize_data_init(&igsd, false TSRMLS_CC)) { 349 | zend_error(E_WARNING, "igbinary_serialize: cannot init igsd"); 350 | return FAILURE; 351 | } 352 | 353 | igbinary_serialize_header(&igsd TSRMLS_CC); 354 | igbinary_serialize_array(&igsd, PS(http_session_vars), false, false TSRMLS_CC); 355 | 356 | if (newlen) 357 | *newlen = igsd.buffer_size; 358 | 359 | *newstr = estrndup((char*)igsd.buffer, igsd.buffer_size); 360 | if (newstr == NULL) { 361 | return FAILURE; 362 | } 363 | 364 | igbinary_serialize_data_deinit(&igsd TSRMLS_CC); 365 | 366 | return SUCCESS; 367 | } 368 | /* }}} */ 369 | /* {{{ Serializer decode function */ 370 | PS_SERIALIZER_DECODE_FUNC(igbinary) { 371 | HashPosition tmp_hash_pos; 372 | HashTable *tmp_hash; 373 | char *key_str; 374 | ulong key_long; 375 | int tmp_int; 376 | uint key_len; 377 | zval *z; 378 | zval **d; 379 | 380 | struct igbinary_unserialize_data igsd; 381 | 382 | if (!val || vallen==0) 383 | return SUCCESS; 384 | 385 | igbinary_unserialize_data_init(&igsd TSRMLS_CC); 386 | 387 | igsd.buffer = (uint8_t *)val; 388 | igsd.buffer_size = vallen; 389 | 390 | if (igbinary_unserialize_header(&igsd TSRMLS_CC)) { 391 | igbinary_unserialize_data_deinit(&igsd TSRMLS_CC); 392 | return FAILURE; 393 | } 394 | 395 | ALLOC_INIT_ZVAL(z); 396 | if (igbinary_unserialize_zval(&igsd, &z TSRMLS_CC)) { 397 | igbinary_unserialize_data_deinit(&igsd TSRMLS_CC); 398 | zval_dtor(z); 399 | FREE_ZVAL(z); 400 | return FAILURE; 401 | } 402 | 403 | igbinary_unserialize_data_deinit(&igsd TSRMLS_CC); 404 | 405 | tmp_hash = HASH_OF(z); 406 | 407 | zend_hash_internal_pointer_reset_ex(tmp_hash, &tmp_hash_pos); 408 | while (zend_hash_get_current_data_ex(tmp_hash, (void *) &d, &tmp_hash_pos) == SUCCESS) { 409 | tmp_int = zend_hash_get_current_key_ex(tmp_hash, &key_str, &key_len, &key_long, 0, &tmp_hash_pos); 410 | 411 | switch (tmp_int) { 412 | case HASH_KEY_IS_LONG: 413 | /* ??? */ 414 | break; 415 | case HASH_KEY_IS_STRING: 416 | php_set_session_var(key_str, key_len-1, *d, NULL TSRMLS_CC); 417 | php_add_session_var(key_str, key_len-1 TSRMLS_CC); 418 | break; 419 | } 420 | zend_hash_move_forward_ex(tmp_hash, &tmp_hash_pos); 421 | } 422 | zval_dtor(z); 423 | FREE_ZVAL(z); 424 | 425 | return SUCCESS; 426 | } 427 | /* }}} */ 428 | /* {{{ igbinary_serialize_data_init */ 429 | /** Inits igbinary_serialize_data. */ 430 | inline static int igbinary_serialize_data_init(struct igbinary_serialize_data *igsd, bool scalar TSRMLS_DC) { 431 | int r = 0; 432 | 433 | igsd->buffer = NULL; 434 | igsd->buffer_size = 0; 435 | igsd->buffer_capacity = 32; 436 | igsd->error = 0; 437 | 438 | igsd->buffer = (uint8_t *) emalloc(igsd->buffer_capacity); 439 | if (igsd->buffer == NULL) { 440 | return 1; 441 | } 442 | 443 | igsd->scalar = scalar; 444 | if (!igsd->scalar) { 445 | hash_si_init(&igsd->strings, 16); 446 | hash_si_init(&igsd->objects, 16); 447 | } 448 | 449 | igsd->compact_strings = (bool)IGBINARY_G(compact_strings); 450 | 451 | return r; 452 | } 453 | /* }}} */ 454 | /* {{{ igbinary_serialize_data_deinit */ 455 | /** Deinits igbinary_serialize_data. */ 456 | inline static void igbinary_serialize_data_deinit(struct igbinary_serialize_data *igsd TSRMLS_DC) { 457 | if (igsd->buffer) { 458 | efree(igsd->buffer); 459 | } 460 | 461 | if (!igsd->scalar) { 462 | hash_si_deinit(&igsd->strings); 463 | hash_si_deinit(&igsd->objects); 464 | } 465 | } 466 | /* }}} */ 467 | /* {{{ igbinary_serialize_header */ 468 | /** Serializes header. */ 469 | inline static int igbinary_serialize_header(struct igbinary_serialize_data *igsd TSRMLS_DC) { 470 | igbinary_serialize32(igsd, IGBINARY_FORMAT_VERSION TSRMLS_CC); /* version */ 471 | 472 | return 0; 473 | } 474 | /* }}} */ 475 | /* {{{ igbinary_serialize_resize */ 476 | /** Expandes igbinary_serialize_data. */ 477 | inline static int igbinary_serialize_resize(struct igbinary_serialize_data *igsd, size_t size TSRMLS_DC) { 478 | if (igsd->buffer_size + size < igsd->buffer_capacity) { 479 | return 0; 480 | } 481 | 482 | while (igsd->buffer_size + size >= igsd->buffer_capacity) { 483 | igsd->buffer_capacity *= 2; 484 | } 485 | 486 | igsd->buffer = (uint8_t *) erealloc(igsd->buffer, igsd->buffer_capacity); 487 | if (igsd->buffer == NULL) 488 | return 1; 489 | 490 | return 0; 491 | } 492 | /* }}} */ 493 | /* {{{ igbinary_serialize8 */ 494 | /** Serialize 8bit value. */ 495 | inline static int igbinary_serialize8(struct igbinary_serialize_data *igsd, uint8_t i TSRMLS_DC) { 496 | if (igbinary_serialize_resize(igsd, 1 TSRMLS_CC)) { 497 | return 1; 498 | } 499 | 500 | igsd->buffer[igsd->buffer_size++] = i; 501 | return 0; 502 | } 503 | /* }}} */ 504 | /* {{{ igbinary_serialize16 */ 505 | /** Serialize 16bit value. */ 506 | inline static int igbinary_serialize16(struct igbinary_serialize_data *igsd, uint16_t i TSRMLS_DC) { 507 | if (igbinary_serialize_resize(igsd, 2 TSRMLS_CC)) { 508 | return 1; 509 | } 510 | 511 | igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 8 & 0xff); 512 | igsd->buffer[igsd->buffer_size++] = (uint8_t) (i & 0xff); 513 | 514 | return 0; 515 | } 516 | /* }}} */ 517 | /* {{{ igbinary_serialize32 */ 518 | /** Serialize 32bit value. */ 519 | inline static int igbinary_serialize32(struct igbinary_serialize_data *igsd, uint32_t i TSRMLS_DC) { 520 | if (igbinary_serialize_resize(igsd, 4 TSRMLS_CC)) { 521 | return 1; 522 | } 523 | 524 | igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 24 & 0xff); 525 | igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 16 & 0xff); 526 | igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 8 & 0xff); 527 | igsd->buffer[igsd->buffer_size++] = (uint8_t) (i & 0xff); 528 | 529 | return 0; 530 | } 531 | /* }}} */ 532 | /* {{{ igbinary_serialize64 */ 533 | /** Serialize 64bit value. */ 534 | inline static int igbinary_serialize64(struct igbinary_serialize_data *igsd, uint64_t i TSRMLS_DC) { 535 | if (igbinary_serialize_resize(igsd, 8 TSRMLS_CC)) { 536 | return 1; 537 | } 538 | 539 | igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 56 & 0xff); 540 | igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 48 & 0xff); 541 | igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 40 & 0xff); 542 | igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 32 & 0xff); 543 | igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 24 & 0xff); 544 | igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 16 & 0xff); 545 | igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 8 & 0xff); 546 | igsd->buffer[igsd->buffer_size++] = (uint8_t) (i & 0xff); 547 | 548 | return 0; 549 | } 550 | /* }}} */ 551 | /* {{{ igbinary_serialize_null */ 552 | /** Serializes null. */ 553 | inline static int igbinary_serialize_null(struct igbinary_serialize_data *igsd TSRMLS_DC) { 554 | return igbinary_serialize8(igsd, igbinary_type_null TSRMLS_CC); 555 | } 556 | /* }}} */ 557 | /* {{{ igbinary_serialize_bool */ 558 | /** Serializes bool. */ 559 | inline static int igbinary_serialize_bool(struct igbinary_serialize_data *igsd, int b TSRMLS_DC) { 560 | return igbinary_serialize8(igsd, (uint8_t) (b ? igbinary_type_bool_true : igbinary_type_bool_false) TSRMLS_CC); 561 | } 562 | /* }}} */ 563 | /* {{{ igbinary_serialize_long */ 564 | /** Serializes long. */ 565 | inline static int igbinary_serialize_long(struct igbinary_serialize_data *igsd, long l TSRMLS_DC) { 566 | long k = l >= 0 ? l : -l; 567 | bool p = l >= 0 ? true : false; 568 | 569 | /* -LONG_MIN is 0 otherwise. */ 570 | if (l == LONG_MIN) { 571 | #if SIZEOF_LONG == 8 572 | igbinary_serialize8(igsd, (uint8_t) igbinary_type_long64n TSRMLS_CC); 573 | igbinary_serialize64(igsd, (uint64_t) 0x8000000000000000 TSRMLS_CC); 574 | #elif SIZEOF_LONG == 4 575 | igbinary_serialize8(igsd, (uint8_t) igbinary_type_long32n TSRMLS_CC); 576 | igbinary_serialize32(igsd, (uint32_t) 0x80000000 TSRMLS_CC); 577 | #else 578 | #error "Strange sizeof(long)." 579 | #endif 580 | return 0; 581 | } 582 | 583 | if (k <= 0xff) { 584 | igbinary_serialize8(igsd, (uint8_t) (p ? igbinary_type_long8p : igbinary_type_long8n) TSRMLS_CC); 585 | igbinary_serialize8(igsd, (uint8_t) k TSRMLS_CC); 586 | } else if (k <= 0xffff) { 587 | igbinary_serialize8(igsd, (uint8_t) (p ? igbinary_type_long16p : igbinary_type_long16n) TSRMLS_CC); 588 | igbinary_serialize16(igsd, (uint16_t) k TSRMLS_CC); 589 | #if SIZEOF_LONG == 8 590 | } else if (k <= 0xffffffff) { 591 | igbinary_serialize8(igsd, (uint8_t) (p ? igbinary_type_long32p : igbinary_type_long32n) TSRMLS_CC); 592 | igbinary_serialize32(igsd, (uint32_t) k TSRMLS_CC); 593 | } else { 594 | igbinary_serialize8(igsd, (uint8_t) (p ? igbinary_type_long64p : igbinary_type_long64n) TSRMLS_CC); 595 | igbinary_serialize64(igsd, (uint64_t) k TSRMLS_CC); 596 | } 597 | #elif SIZEOF_LONG == 4 598 | } else { 599 | igbinary_serialize8(igsd, (uint8_t) (p ? igbinary_type_long32p : igbinary_type_long32n) TSRMLS_CC); 600 | igbinary_serialize32(igsd, (uint32_t) k TSRMLS_CC); 601 | } 602 | #else 603 | #error "Strange sizeof(long)." 604 | #endif 605 | 606 | return 0; 607 | } 608 | /* }}} */ 609 | /* {{{ igbinary_serialize_double */ 610 | /** Serializes double. */ 611 | inline static int igbinary_serialize_double(struct igbinary_serialize_data *igsd, double d TSRMLS_DC) { 612 | igbinary_serialize8(igsd, igbinary_type_double TSRMLS_CC); 613 | 614 | union { 615 | double d; 616 | uint64_t u; 617 | } u; 618 | 619 | u.d = d; 620 | 621 | igbinary_serialize64(igsd, u.u TSRMLS_CC); 622 | 623 | return 0; 624 | } 625 | /* }}} */ 626 | /* {{{ igbinary_serialize_string */ 627 | /** Serializes string. 628 | * Serializes each string once, after first time uses pointers. 629 | */ 630 | inline static int igbinary_serialize_string(struct igbinary_serialize_data *igsd, char *s, size_t len TSRMLS_DC) { 631 | uint32_t t; 632 | uint32_t *i = &t; 633 | 634 | if (len == 0) { 635 | igbinary_serialize8(igsd, igbinary_type_string_empty TSRMLS_CC); 636 | return 0; 637 | } 638 | 639 | if (igsd->scalar || !igsd->compact_strings || hash_si_find(&igsd->strings, s, len, i) == 1) { 640 | if (!igsd->scalar && igsd->compact_strings) { 641 | t = hash_si_size(&igsd->strings); 642 | hash_si_insert(&igsd->strings, s, len, t); 643 | } 644 | 645 | igbinary_serialize_chararray(igsd, s, len TSRMLS_CC); 646 | } else { 647 | if (*i <= 0xff) { 648 | igbinary_serialize8(igsd, (uint8_t) igbinary_type_string_id8 TSRMLS_CC); 649 | igbinary_serialize8(igsd, (uint8_t) *i TSRMLS_CC); 650 | } else if (*i <= 0xffff) { 651 | igbinary_serialize8(igsd, (uint8_t) igbinary_type_string_id16 TSRMLS_CC); 652 | igbinary_serialize16(igsd, (uint16_t) *i TSRMLS_CC); 653 | } else { 654 | igbinary_serialize8(igsd, (uint8_t) igbinary_type_string_id32 TSRMLS_CC); 655 | igbinary_serialize32(igsd, (uint32_t) *i TSRMLS_CC); 656 | } 657 | } 658 | 659 | return 0; 660 | } 661 | /* }}} */ 662 | /* {{{ igbinary_serialize_chararray */ 663 | /** Serializes string data. */ 664 | inline static int igbinary_serialize_chararray(struct igbinary_serialize_data *igsd, const char *s, size_t len TSRMLS_DC) { 665 | if (len <= 0xff) { 666 | igbinary_serialize8(igsd, igbinary_type_string8 TSRMLS_CC); 667 | igbinary_serialize8(igsd, len TSRMLS_CC); 668 | } else if (len <= 0xffff) { 669 | igbinary_serialize8(igsd, igbinary_type_string16 TSRMLS_CC); 670 | igbinary_serialize16(igsd, len TSRMLS_CC); 671 | } else { 672 | igbinary_serialize8(igsd, igbinary_type_string32 TSRMLS_CC); 673 | igbinary_serialize32(igsd, len TSRMLS_CC); 674 | } 675 | 676 | if (igbinary_serialize_resize(igsd, len TSRMLS_CC)) { 677 | return 1; 678 | } 679 | 680 | memcpy(igsd->buffer+igsd->buffer_size, s, len); 681 | igsd->buffer_size += len; 682 | 683 | return 0; 684 | } 685 | /* }}} */ 686 | /* {{{ igbinay_serialize_array */ 687 | /** Serializes array or objects inner properties. */ 688 | inline static int igbinary_serialize_array(struct igbinary_serialize_data *igsd, zval *z, bool object, bool incomplete_class TSRMLS_DC) { 689 | HashTable *h; 690 | HashPosition pos; 691 | size_t n; 692 | zval **d; 693 | 694 | char *key; 695 | uint key_len; 696 | int key_type; 697 | ulong key_index; 698 | 699 | /* hash */ 700 | h = object ? Z_OBJPROP_P(z) : HASH_OF(z); 701 | 702 | /* hash size */ 703 | n = h ? zend_hash_num_elements(h) : 0; 704 | 705 | /* incomplete class magic member */ 706 | if (n > 0 && incomplete_class) { 707 | --n; 708 | } 709 | 710 | if (!object && igbinary_serialize_array_ref(igsd, z, object TSRMLS_CC) == 0) { 711 | return 0; 712 | } 713 | 714 | if (n <= 0xff) { 715 | igbinary_serialize8(igsd, igbinary_type_array8 TSRMLS_CC); 716 | igbinary_serialize8(igsd, n TSRMLS_CC); 717 | } else if (n <= 0xffff) { 718 | igbinary_serialize8(igsd, igbinary_type_array16 TSRMLS_CC); 719 | igbinary_serialize16(igsd, n TSRMLS_CC); 720 | } else { 721 | igbinary_serialize8(igsd, igbinary_type_array32 TSRMLS_CC); 722 | igbinary_serialize32(igsd, n TSRMLS_CC); 723 | } 724 | 725 | if (n == 0) { 726 | return 0; 727 | } 728 | 729 | /* serialize properties. */ 730 | zend_hash_internal_pointer_reset_ex(h, &pos); 731 | for (;; zend_hash_move_forward_ex(h, &pos)) { 732 | key_type = zend_hash_get_current_key_ex(h, &key, &key_len, &key_index, 0, &pos); 733 | 734 | /* last */ 735 | if (key_type == HASH_KEY_NON_EXISTANT) { 736 | break; 737 | } 738 | 739 | /* skip magic member in incomplete classes */ 740 | if (incomplete_class && strcmp(key, MAGIC_MEMBER) == 0) { 741 | continue; 742 | } 743 | 744 | switch (key_type) { 745 | case HASH_KEY_IS_LONG: 746 | igbinary_serialize_long(igsd, key_index TSRMLS_CC); 747 | break; 748 | case HASH_KEY_IS_STRING: 749 | 750 | igbinary_serialize_string(igsd, key, key_len-1 TSRMLS_CC); 751 | break; 752 | default: 753 | zend_error(E_ERROR, "igbinary_serialize_array: key is not string nor array"); 754 | /* not reached */ 755 | return 1; 756 | } 757 | 758 | /* we should still add element even if it's not OK, 759 | * since we already wrote the length of the array before */ 760 | if (zend_hash_get_current_data_ex(h, (void *) &d, &pos) != SUCCESS || d == NULL) { 761 | if (igbinary_serialize_null(igsd TSRMLS_CC)) { 762 | return 1; 763 | } 764 | } else { 765 | if (igbinary_serialize_zval(igsd, *d TSRMLS_CC)) { 766 | return 1; 767 | } 768 | } 769 | 770 | } 771 | 772 | return 0; 773 | } 774 | /* }}} */ 775 | /* {{{ igbinary_serialize_array_ref */ 776 | /** Serializes array reference. */ 777 | inline static int igbinary_serialize_array_ref(struct igbinary_serialize_data *igsd, zval *z, bool object TSRMLS_DC) { 778 | uint32_t t = 0; 779 | uint32_t *i = &t; 780 | union { 781 | zval *z; 782 | struct { 783 | zend_class_entry *ce; 784 | zend_object_handle handle; 785 | } obj; 786 | } key = { 0 }; 787 | 788 | if (object && Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get_class_entry) { 789 | key.obj.ce = Z_OBJCE_P(z); 790 | key.obj.handle = Z_OBJ_HANDLE_P(z); 791 | } else { 792 | key.z = z; 793 | } 794 | 795 | if (hash_si_find(&igsd->objects, (char *)&key, sizeof(key), i) == 1) { 796 | t = hash_si_size(&igsd->objects); 797 | hash_si_insert(&igsd->objects, (char *)&key, sizeof(key), t); 798 | return 1; 799 | } else { 800 | enum igbinary_type type; 801 | if (*i <= 0xff) { 802 | type = object ? igbinary_type_objref8 : igbinary_type_ref8; 803 | igbinary_serialize8(igsd, (uint8_t) type TSRMLS_CC); 804 | igbinary_serialize8(igsd, (uint8_t) *i TSRMLS_CC); 805 | } else if (*i <= 0xffff) { 806 | type = object ? igbinary_type_objref16 : igbinary_type_ref16; 807 | igbinary_serialize8(igsd, (uint8_t) type TSRMLS_CC); 808 | igbinary_serialize16(igsd, (uint16_t) *i TSRMLS_CC); 809 | } else { 810 | type = object ? igbinary_type_objref32 : igbinary_type_ref32; 811 | igbinary_serialize8(igsd, (uint8_t) type TSRMLS_CC); 812 | igbinary_serialize32(igsd, (uint32_t) *i TSRMLS_CC); 813 | } 814 | 815 | return 0; 816 | } 817 | 818 | return 1; 819 | } 820 | /* }}} */ 821 | /* {{{ igbinary_serialize_array_sleep */ 822 | /** Serializes object's properties array with __sleep -function. */ 823 | inline static int igbinary_serialize_array_sleep(struct igbinary_serialize_data *igsd, zval *z, HashTable *h, zend_class_entry *ce, bool incomplete_class TSRMLS_DC) { 824 | HashPosition pos; 825 | size_t n = zend_hash_num_elements(h); 826 | zval **d; 827 | zval **v; 828 | 829 | char *key; 830 | uint key_len; 831 | int key_type; 832 | ulong key_index; 833 | 834 | /* Decrease array size by one, because of magic member (with class name) */ 835 | if (n > 0 && incomplete_class) { 836 | --n; 837 | } 838 | 839 | /* Serialize array id. */ 840 | if (n <= 0xff) { 841 | igbinary_serialize8(igsd, igbinary_type_array8 TSRMLS_CC); 842 | igbinary_serialize8(igsd, n TSRMLS_CC); 843 | } else if (n <= 0xffff) { 844 | igbinary_serialize8(igsd, igbinary_type_array16 TSRMLS_CC); 845 | igbinary_serialize16(igsd, n TSRMLS_CC); 846 | } else { 847 | igbinary_serialize8(igsd, igbinary_type_array32 TSRMLS_CC); 848 | igbinary_serialize32(igsd, n TSRMLS_CC); 849 | } 850 | 851 | if (n == 0) { 852 | return 0; 853 | } 854 | 855 | zend_hash_internal_pointer_reset_ex(h, &pos); 856 | 857 | for (;; zend_hash_move_forward_ex(h, &pos)) { 858 | key_type = zend_hash_get_current_key_ex(h, &key, &key_len, &key_index, 0, &pos); 859 | 860 | /* last */ 861 | if (key_type == HASH_KEY_NON_EXISTANT) { 862 | break; 863 | } 864 | 865 | /* skip magic member in incomplete classes */ 866 | if (incomplete_class && strcmp(key, MAGIC_MEMBER) == 0) { 867 | continue; 868 | } 869 | 870 | if (zend_hash_get_current_data_ex(h, (void *) &d, &pos) != SUCCESS || d == NULL || Z_TYPE_PP(d) != IS_STRING) { 871 | php_error_docref(NULL TSRMLS_CC, E_NOTICE, "__sleep should return an array only " 872 | "containing the names of instance-variables to " 873 | "serialize"); 874 | 875 | /* we should still add element even if it's not OK, 876 | * since we already wrote the length of the array before 877 | * serialize null as key-value pair */ 878 | igbinary_serialize_null(igsd TSRMLS_CC); 879 | } else { 880 | 881 | if (zend_hash_find(Z_OBJPROP_P(z), Z_STRVAL_PP(d), Z_STRLEN_PP(d) + 1, (void *) &v) == SUCCESS) { 882 | igbinary_serialize_string(igsd, Z_STRVAL_PP(d), Z_STRLEN_PP(d) TSRMLS_CC); 883 | igbinary_serialize_zval(igsd, *v TSRMLS_CC); 884 | } else if (ce) { 885 | char *prot_name = NULL; 886 | char *priv_name = NULL; 887 | int prop_name_length; 888 | 889 | do { 890 | /* try private */ 891 | zend_mangle_property_name(&priv_name, &prop_name_length, ce->name, ce->name_length, 892 | Z_STRVAL_PP(d), Z_STRLEN_PP(d), ce->type & ZEND_INTERNAL_CLASS); 893 | if (zend_hash_find(Z_OBJPROP_P(z), priv_name, prop_name_length+1, (void *) &v) == SUCCESS) { 894 | igbinary_serialize_string(igsd, priv_name, prop_name_length TSRMLS_CC); 895 | efree(priv_name); 896 | igbinary_serialize_zval(igsd, *v TSRMLS_CC); 897 | break; 898 | } 899 | efree(priv_name); 900 | 901 | /* try protected */ 902 | zend_mangle_property_name(&prot_name, &prop_name_length, "*", 1, 903 | Z_STRVAL_PP(d), Z_STRLEN_PP(d), ce->type & ZEND_INTERNAL_CLASS); 904 | if (zend_hash_find(Z_OBJPROP_P(z), prot_name, prop_name_length+1, (void *) &v) == SUCCESS) { 905 | igbinary_serialize_string(igsd, prot_name, prop_name_length TSRMLS_CC); 906 | efree(prot_name); 907 | igbinary_serialize_zval(igsd, *v TSRMLS_CC); 908 | break; 909 | } 910 | efree(prot_name); 911 | 912 | /* no win */ 913 | igbinary_serialize_string(igsd, Z_STRVAL_PP(d), Z_STRLEN_PP(d) TSRMLS_CC); 914 | igbinary_serialize_null(igsd TSRMLS_CC); 915 | php_error_docref(NULL TSRMLS_CC, E_NOTICE, "\"%s\" returned as member variable from __sleep() but does not exist", Z_STRVAL_PP(d)); 916 | } while (0); 917 | 918 | } else { 919 | // if all else fails, just serialize the value in anyway. 920 | igbinary_serialize_string(igsd, Z_STRVAL_PP(d), Z_STRLEN_PP(d) TSRMLS_CC); 921 | igbinary_serialize_zval(igsd, *v TSRMLS_CC); 922 | } 923 | } 924 | } 925 | 926 | return 0; 927 | } 928 | /* }}} */ 929 | /* {{{ igbinary_serialize_object_name */ 930 | /** Serialize object name. */ 931 | inline static int igbinary_serialize_object_name(struct igbinary_serialize_data *igsd, const char *class_name, size_t name_len TSRMLS_DC) { 932 | uint32_t t; 933 | uint32_t *i = &t; 934 | 935 | if (hash_si_find(&igsd->strings, class_name, name_len, i) == 1) { 936 | t = hash_si_size(&igsd->strings); 937 | hash_si_insert(&igsd->strings, class_name, name_len, t); 938 | 939 | if (name_len <= 0xff) { 940 | igbinary_serialize8(igsd, (uint8_t) igbinary_type_object8 TSRMLS_CC); 941 | igbinary_serialize8(igsd, (uint8_t) name_len TSRMLS_CC); 942 | } else if (name_len <= 0xffff) { 943 | igbinary_serialize8(igsd, (uint8_t) igbinary_type_object16 TSRMLS_CC); 944 | igbinary_serialize16(igsd, (uint16_t) name_len TSRMLS_CC); 945 | } else { 946 | igbinary_serialize8(igsd, (uint8_t) igbinary_type_object32 TSRMLS_CC); 947 | igbinary_serialize32(igsd, (uint32_t) name_len TSRMLS_CC); 948 | } 949 | 950 | if (igbinary_serialize_resize(igsd, name_len TSRMLS_CC)) { 951 | return 1; 952 | } 953 | 954 | memcpy(igsd->buffer+igsd->buffer_size, class_name, name_len); 955 | igsd->buffer_size += name_len; 956 | } else { 957 | /* already serialized string */ 958 | if (*i <= 0xff) { 959 | igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_id8 TSRMLS_CC); 960 | igbinary_serialize8(igsd, (uint8_t) *i TSRMLS_CC); 961 | } else if (*i <= 0xffff) { 962 | igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_id16 TSRMLS_CC); 963 | igbinary_serialize16(igsd, (uint16_t) *i TSRMLS_CC); 964 | } else { 965 | igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_id32 TSRMLS_CC); 966 | igbinary_serialize32(igsd, (uint32_t) *i TSRMLS_CC); 967 | } 968 | } 969 | 970 | return 0; 971 | } 972 | /* }}} */ 973 | /* {{{ igbinary_serialize_object */ 974 | /** Serialize object. 975 | * @see ext/standard/var.c 976 | * */ 977 | inline static int igbinary_serialize_object(struct igbinary_serialize_data *igsd, zval *z TSRMLS_DC) { 978 | zend_class_entry *ce; 979 | 980 | zval f; 981 | zval *h = NULL; 982 | 983 | int r = 0; 984 | 985 | unsigned char *serialized_data = NULL; 986 | zend_uint serialized_len; 987 | 988 | PHP_CLASS_ATTRIBUTES; 989 | 990 | if (igbinary_serialize_array_ref(igsd, z, true TSRMLS_CC) == 0) { 991 | return r; 992 | } 993 | 994 | ce = Z_OBJCE_P(z); 995 | 996 | /* custom serializer */ 997 | if (ce && ce->serialize != NULL) { 998 | /* TODO: var_hash? */ 999 | if(ce->serialize(z, &serialized_data, &serialized_len, (zend_serialize_data *)NULL TSRMLS_CC) == SUCCESS && !EG(exception)) { 1000 | igbinary_serialize_object_name(igsd, ce->name, ce->name_length TSRMLS_CC); 1001 | 1002 | if (serialized_len <= 0xff) { 1003 | igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_ser8 TSRMLS_CC); 1004 | igbinary_serialize8(igsd, (uint8_t) serialized_len TSRMLS_CC); 1005 | } else if (serialized_len <= 0xffff) { 1006 | igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_ser16 TSRMLS_CC); 1007 | igbinary_serialize16(igsd, (uint16_t) serialized_len TSRMLS_CC); 1008 | } else { 1009 | igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_ser32 TSRMLS_CC); 1010 | igbinary_serialize32(igsd, (uint32_t) serialized_len TSRMLS_CC); 1011 | } 1012 | 1013 | if (igbinary_serialize_resize(igsd, serialized_len TSRMLS_CC)) { 1014 | if (serialized_data) { 1015 | efree(serialized_data); 1016 | } 1017 | r = 1; 1018 | 1019 | return r; 1020 | } 1021 | 1022 | memcpy(igsd->buffer+igsd->buffer_size, serialized_data, serialized_len); 1023 | igsd->buffer_size += serialized_len; 1024 | } else if (EG(exception)) { 1025 | /* exception, return failure */ 1026 | r = 1; 1027 | } else { 1028 | /* Serialization callback failed, assume null output */ 1029 | igbinary_serialize_null(igsd TSRMLS_CC); 1030 | } 1031 | 1032 | if (serialized_data) { 1033 | efree(serialized_data); 1034 | } 1035 | 1036 | return r; 1037 | } 1038 | 1039 | /* serialize class name */ 1040 | PHP_SET_CLASS_ATTRIBUTES(z); 1041 | igbinary_serialize_object_name(igsd, class_name, name_len TSRMLS_CC); 1042 | PHP_CLEANUP_CLASS_ATTRIBUTES(); 1043 | 1044 | if (ce && ce != PHP_IC_ENTRY && zend_hash_exists(&ce->function_table, "__sleep", sizeof("__sleep"))) { 1045 | /* function name string */ 1046 | INIT_PZVAL(&f); 1047 | ZVAL_STRINGL(&f, "__sleep", sizeof("__sleep") - 1, 0); 1048 | 1049 | /* calling z->__sleep */ 1050 | r = call_user_function_ex(CG(function_table), &z, &f, &h, 0, 0, 1, NULL TSRMLS_CC); 1051 | 1052 | if (r == SUCCESS && !EG(exception)) { 1053 | r = 0; 1054 | 1055 | if (h) { 1056 | if (Z_TYPE_P(h) == IS_ARRAY) { 1057 | r = igbinary_serialize_array_sleep(igsd, z, HASH_OF(h), ce, incomplete_class TSRMLS_CC); 1058 | } else { 1059 | php_error_docref(NULL TSRMLS_CC, E_NOTICE, "__sleep should return an array only " 1060 | "containing the names of instance-variables to " 1061 | "serialize"); 1062 | 1063 | /* empty array */ 1064 | igbinary_serialize8(igsd, igbinary_type_array8 TSRMLS_CC); 1065 | r = igbinary_serialize8(igsd, 0 TSRMLS_CC); 1066 | } 1067 | } 1068 | } else { 1069 | r = 1; 1070 | } 1071 | 1072 | /* cleanup */ 1073 | if (h) { 1074 | zval_ptr_dtor(&h); 1075 | } 1076 | 1077 | return r; 1078 | } else { 1079 | return igbinary_serialize_array(igsd, z, true, incomplete_class TSRMLS_CC); 1080 | } 1081 | } 1082 | /* }}} */ 1083 | /* {{{ igbinary_serialize_zval */ 1084 | /** Serialize zval. */ 1085 | static int igbinary_serialize_zval(struct igbinary_serialize_data *igsd, zval *z TSRMLS_DC) { 1086 | if (Z_ISREF_P(z)) { 1087 | igbinary_serialize8(igsd, (uint8_t) igbinary_type_ref TSRMLS_CC); 1088 | } 1089 | switch (Z_TYPE_P(z)) { 1090 | case IS_RESOURCE: 1091 | return igbinary_serialize_null(igsd TSRMLS_CC); 1092 | case IS_OBJECT: 1093 | return igbinary_serialize_object(igsd, z TSRMLS_CC); 1094 | case IS_ARRAY: 1095 | return igbinary_serialize_array(igsd, z, false, false TSRMLS_CC); 1096 | case IS_STRING: 1097 | return igbinary_serialize_string(igsd, Z_STRVAL_P(z), Z_STRLEN_P(z) TSRMLS_CC); 1098 | case IS_LONG: 1099 | return igbinary_serialize_long(igsd, Z_LVAL_P(z) TSRMLS_CC); 1100 | case IS_NULL: 1101 | return igbinary_serialize_null(igsd TSRMLS_CC); 1102 | case IS_BOOL: 1103 | return igbinary_serialize_bool(igsd, Z_LVAL_P(z) ? 1 : 0 TSRMLS_CC); 1104 | case IS_DOUBLE: 1105 | return igbinary_serialize_double(igsd, Z_DVAL_P(z) TSRMLS_CC); 1106 | default: 1107 | zend_error(E_ERROR, "igbinary_serialize_zval: zval has unknown type %d", (int)Z_TYPE_P(z)); 1108 | /* not reached */ 1109 | return 1; 1110 | } 1111 | 1112 | return 0; 1113 | } 1114 | /* }}} */ 1115 | /* {{{ igbinary_unserialize_data_init */ 1116 | /** Inits igbinary_unserialize_data_init. */ 1117 | inline static int igbinary_unserialize_data_init(struct igbinary_unserialize_data *igsd TSRMLS_DC) { 1118 | smart_str empty_str = { 0 }; 1119 | 1120 | igsd->buffer = NULL; 1121 | igsd->buffer_size = 0; 1122 | igsd->buffer_offset = 0; 1123 | 1124 | igsd->strings = NULL; 1125 | igsd->strings_count = 0; 1126 | igsd->strings_capacity = 4; 1127 | igsd->string0_buf = empty_str; 1128 | 1129 | igsd->error = 0; 1130 | igsd->references = NULL; 1131 | igsd->references_count = 0; 1132 | igsd->references_capacity = 4; 1133 | 1134 | igsd->references = (void **) emalloc(sizeof(void *) * igsd->references_capacity); 1135 | if (igsd->references == NULL) { 1136 | return 1; 1137 | } 1138 | 1139 | igsd->strings = (struct igbinary_unserialize_string_pair *) emalloc(sizeof(struct igbinary_unserialize_string_pair) * igsd->strings_capacity); 1140 | if (igsd->strings == NULL) { 1141 | efree(igsd->references); 1142 | return 1; 1143 | } 1144 | 1145 | return 0; 1146 | } 1147 | /* }}} */ 1148 | /* {{{ igbinary_unserialize_data_deinit */ 1149 | /** Deinits igbinary_unserialize_data_init. */ 1150 | inline static void igbinary_unserialize_data_deinit(struct igbinary_unserialize_data *igsd TSRMLS_DC) { 1151 | if (igsd->strings) { 1152 | efree(igsd->strings); 1153 | } 1154 | 1155 | if (igsd->references) { 1156 | efree(igsd->references); 1157 | } 1158 | 1159 | smart_str_free(&igsd->string0_buf); 1160 | 1161 | return; 1162 | } 1163 | /* }}} */ 1164 | /* {{{ igbinary_unserialize_header */ 1165 | /** Unserialize header. Check for version. */ 1166 | inline static int igbinary_unserialize_header(struct igbinary_unserialize_data *igsd TSRMLS_DC) { 1167 | uint32_t version; 1168 | 1169 | if (igsd->buffer_offset + 4 >= igsd->buffer_size) { 1170 | return 1; 1171 | } 1172 | 1173 | version = igbinary_unserialize32(igsd TSRMLS_CC); 1174 | 1175 | if (version != IGBINARY_FORMAT_VERSION) { 1176 | zend_error(E_WARNING, "igbinary_unserialize_header: version mismatch: %u vs %u", (unsigned int) version, (unsigned int) IGBINARY_FORMAT_VERSION); 1177 | return 1; 1178 | } 1179 | 1180 | return 0; 1181 | } 1182 | /* }}} */ 1183 | /* {{{ igbinary_unserialize8 */ 1184 | /** Unserialize 8bit value. */ 1185 | inline static uint8_t igbinary_unserialize8(struct igbinary_unserialize_data *igsd TSRMLS_DC) { 1186 | uint8_t ret = 0; 1187 | ret = igsd->buffer[igsd->buffer_offset++]; 1188 | return ret; 1189 | } 1190 | /* }}} */ 1191 | /* {{{ igbinary_unserialize16 */ 1192 | /** Unserialize 16bit value. */ 1193 | inline static uint16_t igbinary_unserialize16(struct igbinary_unserialize_data *igsd TSRMLS_DC) { 1194 | uint16_t ret = 0; 1195 | ret |= ((uint16_t) igsd->buffer[igsd->buffer_offset++] << 8); 1196 | ret |= ((uint16_t) igsd->buffer[igsd->buffer_offset++] << 0); 1197 | return ret; 1198 | } 1199 | /* }}} */ 1200 | /* {{{ igbinary_unserialize32 */ 1201 | /** Unserialize 32bit value. */ 1202 | inline static uint32_t igbinary_unserialize32(struct igbinary_unserialize_data *igsd TSRMLS_DC) { 1203 | uint32_t ret = 0; 1204 | ret |= ((uint32_t) igsd->buffer[igsd->buffer_offset++] << 24); 1205 | ret |= ((uint32_t) igsd->buffer[igsd->buffer_offset++] << 16); 1206 | ret |= ((uint32_t) igsd->buffer[igsd->buffer_offset++] << 8); 1207 | ret |= ((uint32_t) igsd->buffer[igsd->buffer_offset++] << 0); 1208 | return ret; 1209 | } 1210 | /* }}} */ 1211 | /* {{{ igbinary_unserialize64 */ 1212 | /** Unserialize 64bit value. */ 1213 | inline static uint64_t igbinary_unserialize64(struct igbinary_unserialize_data *igsd TSRMLS_DC) { 1214 | uint64_t ret = 0; 1215 | ret |= ((uint64_t) igsd->buffer[igsd->buffer_offset++] << 56); 1216 | ret |= ((uint64_t) igsd->buffer[igsd->buffer_offset++] << 48); 1217 | ret |= ((uint64_t) igsd->buffer[igsd->buffer_offset++] << 40); 1218 | ret |= ((uint64_t) igsd->buffer[igsd->buffer_offset++] << 32); 1219 | ret |= ((uint64_t) igsd->buffer[igsd->buffer_offset++] << 24); 1220 | ret |= ((uint64_t) igsd->buffer[igsd->buffer_offset++] << 16); 1221 | ret |= ((uint64_t) igsd->buffer[igsd->buffer_offset++] << 8); 1222 | ret |= ((uint64_t) igsd->buffer[igsd->buffer_offset++] << 0); 1223 | return ret; 1224 | } 1225 | /* }}} */ 1226 | /* {{{ igbinary_unserialize_long */ 1227 | /** Unserializes long */ 1228 | inline static int igbinary_unserialize_long(struct igbinary_unserialize_data *igsd, enum igbinary_type t, long *ret TSRMLS_DC) { 1229 | uint32_t tmp32; 1230 | #if SIZEOF_LONG == 8 1231 | uint64_t tmp64; 1232 | #endif 1233 | 1234 | if (t == igbinary_type_long8p || t == igbinary_type_long8n) { 1235 | if (igsd->buffer_offset + 1 > igsd->buffer_size) { 1236 | zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data"); 1237 | return 1; 1238 | } 1239 | 1240 | *ret = (long) (t == igbinary_type_long8n ? -1 : 1) * igbinary_unserialize8(igsd TSRMLS_CC); 1241 | } else if (t == igbinary_type_long16p || t == igbinary_type_long16n) { 1242 | if (igsd->buffer_offset + 2 > igsd->buffer_size) { 1243 | zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data"); 1244 | return 1; 1245 | } 1246 | 1247 | *ret = (long) (t == igbinary_type_long16n ? -1 : 1) * igbinary_unserialize16(igsd TSRMLS_CC); 1248 | } else if (t == igbinary_type_long32p || t == igbinary_type_long32n) { 1249 | if (igsd->buffer_offset + 4 > igsd->buffer_size) { 1250 | zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data"); 1251 | return 1; 1252 | } 1253 | 1254 | /* check for boundaries */ 1255 | tmp32 = igbinary_unserialize32(igsd TSRMLS_CC); 1256 | #if SIZEOF_LONG == 4 1257 | if (tmp32 > 0x80000000 || (tmp32 == 0x80000000 && t == igbinary_type_long32p)) { 1258 | zend_error(E_WARNING, "igbinary_unserialize_long: 64bit long on 32bit platform?"); 1259 | tmp32 = 0; /* t == igbinary_type_long32p ? LONG_MAX : LONG_MIN; */ 1260 | } 1261 | #endif 1262 | *ret = (long) (t == igbinary_type_long32n ? -1 : 1) * tmp32; 1263 | } else if (t == igbinary_type_long64p || t == igbinary_type_long64n) { 1264 | #if SIZEOF_LONG == 8 1265 | if (igsd->buffer_offset + 8 > igsd->buffer_size) { 1266 | zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data"); 1267 | return 1; 1268 | } 1269 | 1270 | /* check for boundaries */ 1271 | tmp64 = igbinary_unserialize64(igsd TSRMLS_CC); 1272 | if (tmp64 > 0x8000000000000000 || (tmp64 == 0x8000000000000000 && t == igbinary_type_long64p)) { 1273 | zend_error(E_WARNING, "igbinary_unserialize_long: too big 64bit long."); 1274 | tmp64 = 0; /* t == igbinary_type_long64p ? LONG_MAX : LONG_MIN */; 1275 | } 1276 | 1277 | *ret = (long) (t == igbinary_type_long64n ? -1 : 1) * tmp64; 1278 | #elif SIZEOF_LONG == 4 1279 | /* can't put 64bit long into 32bit one, placeholder zero */ 1280 | *ret = 0; 1281 | igbinary_unserialize64(igsd TSRMLS_CC); 1282 | zend_error(E_WARNING, "igbinary_unserialize_long: 64bit long on 32bit platform"); 1283 | #else 1284 | #error "Strange sizeof(long)." 1285 | #endif 1286 | } else { 1287 | *ret = 0; 1288 | zend_error(E_WARNING, "igbinary_unserialize_long: unknown type '%02x', position %zu", t, igsd->buffer_offset); 1289 | return 1; 1290 | } 1291 | 1292 | return 0; 1293 | } 1294 | /* }}} */ 1295 | /* {{{ igbinary_unserialize_double */ 1296 | /** Unserializes double. */ 1297 | inline static int igbinary_unserialize_double(struct igbinary_unserialize_data *igsd, enum igbinary_type t, double *ret TSRMLS_DC) { 1298 | (void) t; 1299 | 1300 | if (igsd->buffer_offset + 8 > igsd->buffer_size) { 1301 | zend_error(E_WARNING, "igbinary_unserialize_double: end-of-data"); 1302 | return 1; 1303 | } 1304 | 1305 | union { 1306 | double d; 1307 | uint64_t u; 1308 | } u; 1309 | 1310 | u.u = igbinary_unserialize64(igsd TSRMLS_CC); 1311 | 1312 | *ret = u.d; 1313 | 1314 | return 0; 1315 | } 1316 | /* }}} */ 1317 | /* {{{ igbinary_unserialize_string */ 1318 | /** Unserializes string. Unserializes both actual string or by string id. */ 1319 | inline static int igbinary_unserialize_string(struct igbinary_unserialize_data *igsd, enum igbinary_type t, char **s, size_t *len TSRMLS_DC) { 1320 | size_t i; 1321 | if (t == igbinary_type_string_id8 || t == igbinary_type_object_id8) { 1322 | if (igsd->buffer_offset + 1 > igsd->buffer_size) { 1323 | zend_error(E_WARNING, "igbinary_unserialize_string: end-of-data"); 1324 | return 1; 1325 | } 1326 | i = igbinary_unserialize8(igsd TSRMLS_CC); 1327 | } else if (t == igbinary_type_string_id16 || t == igbinary_type_object_id16) { 1328 | if (igsd->buffer_offset + 2 > igsd->buffer_size) { 1329 | zend_error(E_WARNING, "igbinary_unserialize_string: end-of-data"); 1330 | return 1; 1331 | } 1332 | i = igbinary_unserialize16(igsd TSRMLS_CC); 1333 | } else if (t == igbinary_type_string_id32 || t == igbinary_type_object_id32) { 1334 | if (igsd->buffer_offset + 4 > igsd->buffer_size) { 1335 | zend_error(E_WARNING, "igbinary_unserialize_string: end-of-data"); 1336 | return 1; 1337 | } 1338 | i = igbinary_unserialize32(igsd TSRMLS_CC); 1339 | } else { 1340 | zend_error(E_WARNING, "igbinary_unserialize_string: unknown type '%02x', position %zu", t, igsd->buffer_offset); 1341 | return 1; 1342 | } 1343 | 1344 | if (i >= igsd->strings_count) { 1345 | zend_error(E_WARNING, "igbinary_unserialize_string: string index is out-of-bounds"); 1346 | return 1; 1347 | } 1348 | 1349 | *s = igsd->strings[i].data; 1350 | *len = igsd->strings[i].len; 1351 | 1352 | return 0; 1353 | } 1354 | /* }}} */ 1355 | /* {{{ igbinary_unserialize_chararray */ 1356 | /** Unserializes chararray of string. */ 1357 | inline static int igbinary_unserialize_chararray(struct igbinary_unserialize_data *igsd, enum igbinary_type t, char **s, size_t *len TSRMLS_DC) { 1358 | size_t l; 1359 | 1360 | if (t == igbinary_type_string8 || t == igbinary_type_object8) { 1361 | if (igsd->buffer_offset + 1 > igsd->buffer_size) { 1362 | zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); 1363 | return 1; 1364 | } 1365 | l = igbinary_unserialize8(igsd TSRMLS_CC); 1366 | if (igsd->buffer_offset + l > igsd->buffer_size) { 1367 | zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); 1368 | return 1; 1369 | } 1370 | } else if (t == igbinary_type_string16 || t == igbinary_type_object16) { 1371 | if (igsd->buffer_offset + 2 > igsd->buffer_size) { 1372 | zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); 1373 | return 1; 1374 | } 1375 | l = igbinary_unserialize16(igsd TSRMLS_CC); 1376 | if (igsd->buffer_offset + l > igsd->buffer_size) { 1377 | zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); 1378 | return 1; 1379 | } 1380 | } else if (t == igbinary_type_string32 || t == igbinary_type_object32) { 1381 | if (igsd->buffer_offset + 4 > igsd->buffer_size) { 1382 | zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); 1383 | return 1; 1384 | } 1385 | l = igbinary_unserialize32(igsd TSRMLS_CC); 1386 | if (igsd->buffer_offset + l > igsd->buffer_size) { 1387 | zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); 1388 | return 1; 1389 | } 1390 | } else { 1391 | zend_error(E_WARNING, "igbinary_unserialize_chararray: unknown type '%02x', position %zu", t, igsd->buffer_offset); 1392 | return 1; 1393 | } 1394 | 1395 | if (igsd->strings_count + 1 > igsd->strings_capacity) { 1396 | while (igsd->strings_count + 1 > igsd->strings_capacity) { 1397 | igsd->strings_capacity *= 2; 1398 | } 1399 | 1400 | igsd->strings = (struct igbinary_unserialize_string_pair *) erealloc(igsd->strings, sizeof(struct igbinary_unserialize_string_pair) * igsd->strings_capacity); 1401 | if (igsd->strings == NULL) { 1402 | return 1; 1403 | } 1404 | } 1405 | 1406 | igsd->strings[igsd->strings_count].data = (char *) (igsd->buffer + igsd->buffer_offset); 1407 | igsd->strings[igsd->strings_count].len = l; 1408 | 1409 | igsd->buffer_offset += l; 1410 | 1411 | if (igsd->strings[igsd->strings_count].data == NULL) { 1412 | return 1; 1413 | } 1414 | 1415 | *len = igsd->strings[igsd->strings_count].len; 1416 | *s = igsd->strings[igsd->strings_count].data; 1417 | 1418 | igsd->strings_count += 1; 1419 | 1420 | return 0; 1421 | } 1422 | /* }}} */ 1423 | /* {{{ igbinary_unserialize_array */ 1424 | /** Unserializes array. */ 1425 | inline static int igbinary_unserialize_array(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval **z, int object TSRMLS_DC) { 1426 | size_t n; 1427 | size_t i; 1428 | 1429 | zval *v = NULL; 1430 | /* zval *old_v; */ 1431 | 1432 | char *key; 1433 | size_t key_len = 0; 1434 | long key_index = 0; 1435 | 1436 | enum igbinary_type key_type; 1437 | 1438 | HashTable *h; 1439 | 1440 | if (t == igbinary_type_array8) { 1441 | if (igsd->buffer_offset + 1 > igsd->buffer_size) { 1442 | zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data"); 1443 | return 1; 1444 | } 1445 | n = igbinary_unserialize8(igsd TSRMLS_CC); 1446 | } else if (t == igbinary_type_array16) { 1447 | if (igsd->buffer_offset + 2 > igsd->buffer_size) { 1448 | zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data"); 1449 | return 1; 1450 | } 1451 | n = igbinary_unserialize16(igsd TSRMLS_CC); 1452 | } else if (t == igbinary_type_array32) { 1453 | if (igsd->buffer_offset + 4 > igsd->buffer_size) { 1454 | zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data"); 1455 | return 1; 1456 | } 1457 | n = igbinary_unserialize32(igsd TSRMLS_CC); 1458 | } else { 1459 | zend_error(E_WARNING, "igbinary_unserialize_array: unknown type '%02x', position %zu", t, igsd->buffer_offset); 1460 | return 1; 1461 | } 1462 | 1463 | // n cannot be larger than the number of minimum "objects" in the array 1464 | if (n > igsd->buffer_size - igsd->buffer_offset) { 1465 | zend_error(E_WARNING, "%s: data size %zu smaller that requested array length %zu.", __func__, igsd->buffer_size - igsd->buffer_offset, n); 1466 | return 1; 1467 | } 1468 | 1469 | if (!object) { 1470 | Z_TYPE_PP(z) = IS_ARRAY; 1471 | ALLOC_HASHTABLE(Z_ARRVAL_PP(z)); 1472 | zend_hash_init(Z_ARRVAL_PP(z), n + 1, NULL, ZVAL_PTR_DTOR, 0); 1473 | 1474 | /* references */ 1475 | if (igsd->references_count + 1 >= igsd->references_capacity) { 1476 | while (igsd->references_count + 1 >= igsd->references_capacity) { 1477 | igsd->references_capacity *= 2; 1478 | } 1479 | 1480 | igsd->references = (void **) erealloc(igsd->references, sizeof(void *) * igsd->references_capacity); 1481 | if (igsd->references == NULL) 1482 | return 1; 1483 | } 1484 | 1485 | igsd->references[igsd->references_count++] = (void *) *z; 1486 | } 1487 | 1488 | /* empty array */ 1489 | if (n == 0) { 1490 | return 0; 1491 | } 1492 | 1493 | h = HASH_OF(*z); 1494 | 1495 | for (i = 0; i < n; i++) { 1496 | key = NULL; 1497 | 1498 | if (igsd->buffer_offset + 1 > igsd->buffer_size) { 1499 | zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data"); 1500 | zval_dtor(*z); 1501 | ZVAL_NULL(*z); 1502 | return 1; 1503 | } 1504 | 1505 | key_type = (enum igbinary_type) igbinary_unserialize8(igsd TSRMLS_CC); 1506 | 1507 | switch (key_type) { 1508 | case igbinary_type_long8p: 1509 | case igbinary_type_long8n: 1510 | case igbinary_type_long16p: 1511 | case igbinary_type_long16n: 1512 | case igbinary_type_long32p: 1513 | case igbinary_type_long32n: 1514 | case igbinary_type_long64p: 1515 | case igbinary_type_long64n: 1516 | if (igbinary_unserialize_long(igsd, key_type, &key_index TSRMLS_CC)) { 1517 | zval_dtor(*z); 1518 | ZVAL_NULL(*z); 1519 | return 1; 1520 | } 1521 | break; 1522 | case igbinary_type_string_id8: 1523 | case igbinary_type_string_id16: 1524 | case igbinary_type_string_id32: 1525 | if (igbinary_unserialize_string(igsd, key_type, &key, &key_len TSRMLS_CC)) { 1526 | zval_dtor(*z); 1527 | ZVAL_NULL(*z); 1528 | return 1; 1529 | } 1530 | break; 1531 | case igbinary_type_string8: 1532 | case igbinary_type_string16: 1533 | case igbinary_type_string32: 1534 | if (igbinary_unserialize_chararray(igsd, key_type, &key, &key_len TSRMLS_CC)) { 1535 | zval_dtor(*z); 1536 | ZVAL_NULL(*z); 1537 | return 1; 1538 | } 1539 | break; 1540 | case igbinary_type_string_empty: 1541 | key = ""; 1542 | key_len = 0; 1543 | break; 1544 | case igbinary_type_null: 1545 | continue; 1546 | default: 1547 | zend_error(E_WARNING, "igbinary_unserialize_array: unknown key type '%02x', position %zu", key_type, igsd->buffer_offset); 1548 | zval_dtor(*z); 1549 | ZVAL_NULL(*z); 1550 | return 1; 1551 | } 1552 | 1553 | 1554 | ALLOC_INIT_ZVAL(v); 1555 | if (igbinary_unserialize_zval(igsd, &v TSRMLS_CC)) { 1556 | zval_dtor(*z); 1557 | ZVAL_NULL(*z); 1558 | zval_ptr_dtor(&v); 1559 | return 1; 1560 | } 1561 | 1562 | if (key) { 1563 | /* Keys must include a terminating null. */ 1564 | /* Ensure buffer starts at the beginning. */ 1565 | igsd->string0_buf.len = 0; 1566 | smart_str_appendl(&igsd->string0_buf, key, key_len); 1567 | smart_str_0(&igsd->string0_buf); 1568 | /* 1569 | if (zend_symtable_find(h, key, key_len + 1, (void **)&old_v) == SUCCESS) { 1570 | var_push_dtor(var_hash, old_v); 1571 | } 1572 | */ 1573 | zend_symtable_update(h, igsd->string0_buf.c, igsd->string0_buf.len + 1, &v, sizeof(v), NULL); 1574 | } else { 1575 | /* 1576 | if (zend_hash_index_find(h, key_index, (void **)&old_v) == SUCCESS) { 1577 | var_push_dtor(var_hash, old_v); 1578 | } 1579 | */ 1580 | zend_hash_index_update(h, key_index, &v, sizeof(v), NULL); 1581 | } 1582 | } 1583 | 1584 | return 0; 1585 | } 1586 | /* }}} */ 1587 | /* {{{ igbinary_unserialize_object_ser */ 1588 | /** Unserializes object's property array of objects implementing Serializable -interface. */ 1589 | inline static int igbinary_unserialize_object_ser(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval **z, zend_class_entry *ce TSRMLS_DC) { 1590 | size_t n; 1591 | 1592 | if (ce->unserialize == NULL) { 1593 | zend_error(E_WARNING, "Class %s has no unserializer", ce->name); 1594 | return 1; 1595 | } 1596 | 1597 | if (t == igbinary_type_object_ser8) { 1598 | if (igsd->buffer_offset + 1 > igsd->buffer_size) { 1599 | zend_error(E_WARNING, "igbinary_unserialize_object_ser: end-of-data"); 1600 | return 1; 1601 | } 1602 | n = igbinary_unserialize8(igsd TSRMLS_CC); 1603 | } else if (t == igbinary_type_object_ser16) { 1604 | if (igsd->buffer_offset + 2 > igsd->buffer_size) { 1605 | zend_error(E_WARNING, "igbinary_unserialize_object_ser: end-of-data"); 1606 | return 1; 1607 | } 1608 | n = igbinary_unserialize16(igsd TSRMLS_CC); 1609 | } else if (t == igbinary_type_object_ser32) { 1610 | if (igsd->buffer_offset + 4 > igsd->buffer_size) { 1611 | zend_error(E_WARNING, "igbinary_unserialize_object_ser: end-of-data"); 1612 | return 1; 1613 | } 1614 | n = igbinary_unserialize32(igsd TSRMLS_CC); 1615 | } else { 1616 | zend_error(E_WARNING, "igbinary_unserialize_object_ser: unknown type '%02x', position %zu", t, igsd->buffer_offset); 1617 | return 1; 1618 | } 1619 | 1620 | if (igsd->buffer_offset + n > igsd->buffer_size) { 1621 | zend_error(E_WARNING, "igbinary_unserialize_object_ser: end-of-data"); 1622 | return 1; 1623 | } 1624 | 1625 | if (ce->unserialize(z, ce, (const unsigned char*)(igsd->buffer + igsd->buffer_offset), n, NULL TSRMLS_CC) != SUCCESS) { 1626 | return 1; 1627 | } else if (EG(exception)) { 1628 | return 1; 1629 | } 1630 | 1631 | igsd->buffer_offset += n; 1632 | 1633 | return 0; 1634 | } 1635 | /* }}} */ 1636 | /* {{{ igbinary_unserialize_object */ 1637 | /** Unserialize object. 1638 | * @see ext/standard/var_unserializer.c 1639 | */ 1640 | inline static int igbinary_unserialize_object(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval **z TSRMLS_DC) { 1641 | zend_class_entry *ce; 1642 | zend_class_entry **pce; 1643 | 1644 | zval *h = NULL; 1645 | zval f; 1646 | 1647 | char *name = NULL; 1648 | size_t name_len = 0; 1649 | 1650 | int r; 1651 | 1652 | bool incomplete_class = false; 1653 | 1654 | zval *user_func; 1655 | zval *retval_ptr; 1656 | zval **args[1]; 1657 | zval *arg_func_name; 1658 | 1659 | if (t == igbinary_type_object8 || t == igbinary_type_object16 || t == igbinary_type_object32) { 1660 | if (igbinary_unserialize_chararray(igsd, t, &name, &name_len TSRMLS_CC)) { 1661 | return 1; 1662 | } 1663 | } else if (t == igbinary_type_object_id8 || t == igbinary_type_object_id16 || t == igbinary_type_object_id32) { 1664 | if (igbinary_unserialize_string(igsd, t, &name, &name_len TSRMLS_CC)) { 1665 | return 1; 1666 | } 1667 | } else { 1668 | zend_error(E_WARNING, "igbinary_unserialize_object: unknown object type '%02x', position %zu", t, igsd->buffer_offset); 1669 | return 1; 1670 | } 1671 | 1672 | do { 1673 | /* Try to find class directly */ 1674 | if (zend_lookup_class(name, name_len, &pce TSRMLS_CC) == SUCCESS) { 1675 | ce = *pce; 1676 | break; 1677 | } 1678 | 1679 | /* Check for unserialize callback */ 1680 | if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) { 1681 | incomplete_class = 1; 1682 | ce = PHP_IC_ENTRY; 1683 | break; 1684 | } 1685 | 1686 | /* Call unserialize callback */ 1687 | MAKE_STD_ZVAL(user_func); 1688 | ZVAL_STRING(user_func, PG(unserialize_callback_func), 1); 1689 | args[0] = &arg_func_name; 1690 | MAKE_STD_ZVAL(arg_func_name); 1691 | ZVAL_STRING(arg_func_name, name, 1); 1692 | if (call_user_function_ex(CG(function_table), NULL, user_func, &retval_ptr, 1, args, 0, NULL TSRMLS_CC) != SUCCESS) { 1693 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "defined (%s) but not found", name); 1694 | incomplete_class = 1; 1695 | ce = PHP_IC_ENTRY; 1696 | zval_ptr_dtor(&user_func); 1697 | zval_ptr_dtor(&arg_func_name); 1698 | break; 1699 | } 1700 | if (retval_ptr) { 1701 | zval_ptr_dtor(&retval_ptr); 1702 | } 1703 | 1704 | /* The callback function may have defined the class */ 1705 | if (zend_lookup_class(name, name_len, &pce TSRMLS_CC) == SUCCESS) { 1706 | ce = *pce; 1707 | } else { 1708 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Function %s() hasn't defined the class it was called for", name); 1709 | incomplete_class = true; 1710 | ce = PHP_IC_ENTRY; 1711 | } 1712 | 1713 | zval_ptr_dtor(&user_func); 1714 | zval_ptr_dtor(&arg_func_name); 1715 | } while (0); 1716 | 1717 | /* previous user function call may have raised an exception */ 1718 | if (EG(exception)) { 1719 | return 1; 1720 | } 1721 | 1722 | object_init_ex(*z, ce); 1723 | 1724 | /* reference */ 1725 | if (igsd->references_count + 1 >= igsd->references_capacity) { 1726 | while (igsd->references_count + 1 >= igsd->references_capacity) { 1727 | igsd->references_capacity *= 2; 1728 | } 1729 | 1730 | igsd->references = (void **) erealloc(igsd->references, sizeof(void *) * igsd->references_capacity); 1731 | if (igsd->references == NULL) 1732 | return 1; 1733 | } 1734 | 1735 | igsd->references[igsd->references_count++] = (void *) *z; 1736 | 1737 | /* store incomplete class name */ 1738 | if (incomplete_class) { 1739 | php_store_class_name(*z, name, name_len); 1740 | } 1741 | 1742 | t = (enum igbinary_type) igbinary_unserialize8(igsd TSRMLS_CC); 1743 | switch (t) { 1744 | case igbinary_type_array8: 1745 | case igbinary_type_array16: 1746 | case igbinary_type_array32: 1747 | r = igbinary_unserialize_array(igsd, t, z, 1 TSRMLS_CC); 1748 | break; 1749 | case igbinary_type_object_ser8: 1750 | case igbinary_type_object_ser16: 1751 | case igbinary_type_object_ser32: 1752 | r = igbinary_unserialize_object_ser(igsd, t, z, ce TSRMLS_CC); 1753 | break; 1754 | default: 1755 | zend_error(E_WARNING, "igbinary_unserialize_object: unknown object inner type '%02x', position %zu", t, igsd->buffer_offset); 1756 | return 1; 1757 | } 1758 | 1759 | if (r) { 1760 | return r; 1761 | } 1762 | 1763 | if (Z_OBJCE_PP(z) != PHP_IC_ENTRY && zend_hash_exists(&Z_OBJCE_PP(z)->function_table, "__wakeup", sizeof("__wakeup"))) { 1764 | INIT_PZVAL(&f); 1765 | ZVAL_STRINGL(&f, "__wakeup", sizeof("__wakeup") - 1, 0); 1766 | call_user_function_ex(CG(function_table), z, &f, &h, 0, 0, 1, NULL TSRMLS_CC); 1767 | 1768 | if (h) { 1769 | zval_ptr_dtor(&h); 1770 | } 1771 | 1772 | if (EG(exception)) { 1773 | r = 1; 1774 | } 1775 | } 1776 | 1777 | return r; 1778 | } 1779 | /* }}} */ 1780 | /* {{{ igbinary_unserialize_ref */ 1781 | /** Unserializes array or object by reference. */ 1782 | inline static int igbinary_unserialize_ref(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval **z TSRMLS_DC) { 1783 | size_t n; 1784 | 1785 | if (t == igbinary_type_ref8 || t == igbinary_type_objref8) { 1786 | if (igsd->buffer_offset + 1 > igsd->buffer_size) { 1787 | zend_error(E_WARNING, "igbinary_unserialize_ref: end-of-data"); 1788 | return 1; 1789 | } 1790 | n = igbinary_unserialize8(igsd TSRMLS_CC); 1791 | } else if (t == igbinary_type_ref16 || t == igbinary_type_objref16) { 1792 | if (igsd->buffer_offset + 2 > igsd->buffer_size) { 1793 | zend_error(E_WARNING, "igbinary_unserialize_ref: end-of-data"); 1794 | return 1; 1795 | } 1796 | n = igbinary_unserialize16(igsd TSRMLS_CC); 1797 | } else if (t == igbinary_type_ref32 || t == igbinary_type_objref32) { 1798 | if (igsd->buffer_offset + 4 > igsd->buffer_size) { 1799 | zend_error(E_WARNING, "igbinary_unserialize_ref: end-of-data"); 1800 | return 1; 1801 | } 1802 | n = igbinary_unserialize32(igsd TSRMLS_CC); 1803 | } else { 1804 | zend_error(E_WARNING, "igbinary_unserialize_ref: unknown type '%02x', position %zu", t, igsd->buffer_offset); 1805 | return 1; 1806 | } 1807 | 1808 | if (n >= igsd->references_count) { 1809 | zend_error(E_WARNING, "igbinary_unserialize_ref: invalid reference"); 1810 | return 1; 1811 | } 1812 | 1813 | if (*z != NULL) { 1814 | zval_ptr_dtor(z); 1815 | } 1816 | 1817 | *z = igsd->references[n]; 1818 | Z_ADDREF_PP(z); 1819 | 1820 | if (t == igbinary_type_objref8 || t == igbinary_type_objref16 || t == igbinary_type_objref32) { 1821 | Z_SET_ISREF_TO_PP(z, false); 1822 | } 1823 | 1824 | return 0; 1825 | } 1826 | /* }}} */ 1827 | /* {{{ igbinary_unserialize_zval */ 1828 | /** Unserialize zval. */ 1829 | static int igbinary_unserialize_zval(struct igbinary_unserialize_data *igsd, zval **z TSRMLS_DC) { 1830 | enum igbinary_type t; 1831 | 1832 | long tmp_long; 1833 | double tmp_double; 1834 | char *tmp_chararray; 1835 | size_t tmp_size_t; 1836 | 1837 | if (igsd->buffer_offset + 1 > igsd->buffer_size) { 1838 | zend_error(E_WARNING, "igbinary_unserialize_zval: end-of-data"); 1839 | return 1; 1840 | } 1841 | 1842 | t = (enum igbinary_type) igbinary_unserialize8(igsd TSRMLS_CC); 1843 | 1844 | switch (t) { 1845 | case igbinary_type_ref: 1846 | if (igbinary_unserialize_zval(igsd, z TSRMLS_CC)) { 1847 | return 1; 1848 | } 1849 | Z_SET_ISREF_TO_PP(z, true); 1850 | break; 1851 | case igbinary_type_objref8: 1852 | case igbinary_type_objref16: 1853 | case igbinary_type_objref32: 1854 | case igbinary_type_ref8: 1855 | case igbinary_type_ref16: 1856 | case igbinary_type_ref32: 1857 | if (igbinary_unserialize_ref(igsd, t, z TSRMLS_CC)) { 1858 | return 1; 1859 | } 1860 | break; 1861 | case igbinary_type_object8: 1862 | case igbinary_type_object16: 1863 | case igbinary_type_object32: 1864 | case igbinary_type_object_id8: 1865 | case igbinary_type_object_id16: 1866 | case igbinary_type_object_id32: 1867 | if (igbinary_unserialize_object(igsd, t, z TSRMLS_CC)) { 1868 | return 1; 1869 | } 1870 | break; 1871 | case igbinary_type_array8: 1872 | case igbinary_type_array16: 1873 | case igbinary_type_array32: 1874 | if (igbinary_unserialize_array(igsd, t, z, 0 TSRMLS_CC)) { 1875 | return 1; 1876 | } 1877 | break; 1878 | case igbinary_type_string_empty: 1879 | ZVAL_EMPTY_STRING(*z); 1880 | break; 1881 | case igbinary_type_string_id8: 1882 | case igbinary_type_string_id16: 1883 | case igbinary_type_string_id32: 1884 | if (igbinary_unserialize_string(igsd, t, &tmp_chararray, &tmp_size_t TSRMLS_CC)) { 1885 | return 1; 1886 | } 1887 | ZVAL_STRINGL(*z, tmp_chararray, tmp_size_t, 1); 1888 | break; 1889 | case igbinary_type_string8: 1890 | case igbinary_type_string16: 1891 | case igbinary_type_string32: 1892 | if (igbinary_unserialize_chararray(igsd, t, &tmp_chararray, &tmp_size_t TSRMLS_CC)) { 1893 | return 1; 1894 | } 1895 | ZVAL_STRINGL(*z, tmp_chararray, tmp_size_t, 1); 1896 | break; 1897 | case igbinary_type_long8p: 1898 | case igbinary_type_long8n: 1899 | case igbinary_type_long16p: 1900 | case igbinary_type_long16n: 1901 | case igbinary_type_long32p: 1902 | case igbinary_type_long32n: 1903 | case igbinary_type_long64p: 1904 | case igbinary_type_long64n: 1905 | if (igbinary_unserialize_long(igsd, t, &tmp_long TSRMLS_CC)) { 1906 | return 1; 1907 | } 1908 | ZVAL_LONG(*z, tmp_long); 1909 | break; 1910 | case igbinary_type_null: 1911 | ZVAL_NULL(*z); 1912 | break; 1913 | case igbinary_type_bool_false: 1914 | ZVAL_BOOL(*z, 0); 1915 | break; 1916 | case igbinary_type_bool_true: 1917 | ZVAL_BOOL(*z, 1); 1918 | break; 1919 | case igbinary_type_double: 1920 | if (igbinary_unserialize_double(igsd, t, &tmp_double TSRMLS_CC)) { 1921 | return 1; 1922 | } 1923 | ZVAL_DOUBLE(*z, tmp_double); 1924 | break; 1925 | default: 1926 | zend_error(E_WARNING, "igbinary_unserialize_zval: unknown type '%02x', position %zu", t, igsd->buffer_offset); 1927 | return 1; 1928 | } 1929 | 1930 | return 0; 1931 | } 1932 | /* }}} */ 1933 | 1934 | /* 1935 | * Local variables: 1936 | * tab-width: 2 1937 | * c-basic-offset: 4 1938 | * End: 1939 | * vim600: noet sw=4 ts=4 fdm=marker 1940 | * vim<600: noet sw=4 ts=4 1941 | */ 1942 | -------------------------------------------------------------------------------- /igbinary/igbinary.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Oleg Grenrus 3 | * 4 | * $Id: igbinary.h,v 1.5 2008/07/03 16:43:46 phadej Exp $ 5 | */ 6 | 7 | #ifndef IGBINARY_H 8 | #define IGBINARY_H 9 | 10 | #include 11 | 12 | #include "php.h" 13 | 14 | #define IGBINARY_VERSION "1.0.2" 15 | 16 | /** Serialize zval. 17 | * Return buffer is allocated by this function with emalloc. 18 | * @param[out] ret Return buffer 19 | * @param[out] ret_len Size of return buffer 20 | * @param[in] z Variable to be serialized 21 | * @return 0 on success, 1 elsewhere. 22 | */ 23 | int igbinary_serialize(uint8_t **ret, size_t *ret_len, zval *z TSRMLS_DC); 24 | 25 | /** Unserialize to zval. 26 | * @param[in] buf Buffer with serialized data. 27 | * @param[in] buf_len Buffer length. 28 | * @param[out] z Unserialized zval 29 | * @return 0 on success, 1 elsewhere. 30 | */ 31 | int igbinary_unserialize(const uint8_t *buf, size_t buf_len, zval **z TSRMLS_DC); 32 | 33 | #endif /* IGBINARY_H */ 34 | -------------------------------------------------------------------------------- /igbinary/igbinary.php: -------------------------------------------------------------------------------- 1 | 29 | * @version 1.0.0 30 | * @package igbinary 31 | */ 32 | 33 | /** Generates a storable representation of a value. 34 | * This is useful for storing or passing PHP values around without losing their type and structure. 35 | * To make the serialized string into a PHP value again, use {@link igbinary_unserialize}. 36 | * 37 | * igbinary_serialize() handles all types, except the resource-type. 38 | * You can even serialize() arrays that contain references to itself. 39 | * Circular references inside the array/object you are serialize()ing will also be stored. 40 | * 41 | * If object implements {@link http://www.php.net/~helly/php/ext/spl/interfaceSerializable.html Serializable} -interface, 42 | * PHP will call the member function serialize to get serialized representation of object. 43 | * 44 | * When serializing objects, PHP will attempt to call the member function __sleep prior to serialization. 45 | * This is to allow the object to do any last minute clean-up, etc. prior to being serialized. 46 | * Likewise, when the object is restored using unserialize() the __wakeup member function is called. 47 | * 48 | * @param mixed $value The value to be serialized. 49 | * @return string Returns a string containing a byte-stream representation of value that can be stored anywhere. 50 | * @link http://www.php.net/serialize PHP default serialize 51 | */ 52 | function igbinary_serialize($value); 53 | 54 | /** Creates a PHP value from a stored representation. 55 | * igbinary_unserialize() takes a single serialized variable and converts it back into a PHP value. 56 | * 57 | * If the variable being unserialized is an object, after successfully reconstructing the object 58 | * PHP will automatically attempt to call the __wakeup() member function (if it exists). 59 | * In case the passed string is not unserializeable, NULL is returned and E_WARNING is issued. 60 | * 61 | * @param string $str The serialized string. 62 | * @return mixed The converted value is returned, and can be a boolean, integer, float, string, array or object. 63 | * @link http://www.php.net/manual/en/function.unserialize.php PHP default unserialize 64 | * @link http://www.php.net/~helly/php/ext/spl/interfaceSerializable.html Serializable 65 | */ 66 | function igbinary_unserialize($str); 67 | 68 | ?> 69 | -------------------------------------------------------------------------------- /igbinary/igbinary.php.ini: -------------------------------------------------------------------------------- 1 | [igbinary] 2 | extension=igbinary.so 3 | 4 | ; Enable or disable compacting of duplicate strings 5 | ; The default is On. 6 | ;igbinary.compact_strings=On 7 | -------------------------------------------------------------------------------- /igbinary/igbinary.spec: -------------------------------------------------------------------------------- 1 | # Define version and release number 2 | %define version 1.0.2 3 | %define release 1 4 | 5 | Name: php-igbinary 6 | Version: %{version} 7 | Release: %{release}%{?dist} 8 | Packager: Mikko Koppanen 9 | Summary: PHP igbinary extension 10 | License: PHP Style License (http://opensource.dynamoid.com/#license) 11 | Group: Web/Applications 12 | URL: http://opensource.dynamoid.com/ 13 | # pear package creates .tgz file and the original source was .tar.gz 14 | Source: http://opensource.dynamoid.com/igbinary-%{version}.tgz 15 | Prefix: %{_prefix} 16 | Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root 17 | BuildRequires: php-devel, make, gcc, /usr/bin/phpize 18 | 19 | %description 20 | Igbinary is a drop in replacement for the standard PHP serializer. 21 | Instead of time and space consuming textual representation, 22 | igbinary stores PHP data structures in a compact binary form. 23 | 24 | %prep 25 | %setup -q -n igbinary-%{version} 26 | 27 | %build 28 | /usr/bin/phpize && %configure && %{__make} %{?_smp_mflags} 29 | 30 | # Clean the buildroot so that it does not contain any stuff from previous builds 31 | [ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot} 32 | 33 | # Install the extension 34 | %{__make} install INSTALL_ROOT=%{buildroot} 35 | 36 | # Create the ini location 37 | %{__mkdir} -p %{buildroot}/etc/php.d 38 | 39 | # Preliminary extension ini 40 | echo "extension=igbinary.so" > %{buildroot}/%{_sysconfdir}/php.d/igbinary.ini 41 | 42 | %clean 43 | [ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot} 44 | 45 | %files 46 | %{_libdir}/php/modules/igbinary.so 47 | %{_sysconfdir}/php.d/igbinary.ini 48 | %{_includedir}/php/ext/igbinary/igbinary.h 49 | 50 | %changelog 51 | * Fri Oct 02 2009 Mikko Koppanen 52 | - Initial spec file 53 | -------------------------------------------------------------------------------- /igbinary/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | igbinary 7 | pecl.php.net 8 | igbinary extension 9 | 10 | Igbinary is a drop in replacement for the standard php serializer. Instead of 11 | time and space consuming textual representation, igbinary stores php data 12 | structures in a compact binary form. Savings are significant when using 13 | memcached or similar memory based storages for serialized data. 14 | 15 | 16 | Oleg Grenrus 17 | 18 | oleg.grenrus@dynamoid.com 19 | yes 20 | 21 | 2009-09-02 22 | 23 | 1.0.2 24 | 1.0.2 25 | 26 | 27 | stable 28 | stable 29 | 30 | PHP like license 31 | 32 | - Use Z_ADDREF_PP or Z_ADDREF et al. when increasing refcount. 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 5.2.0 62 | 63 | 64 | 1.4.0b1 65 | 66 | 67 | 68 | igbinary 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /igbinary/php_igbinary.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Oleg Grenrus 3 | * 4 | * $Id: php_igbinary.h,v 1.6 2008/07/03 16:43:46 phadej Exp $ 5 | */ 6 | 7 | #ifndef PHP_IGBINARY_H 8 | #define PHP_IGBINARY_H 9 | 10 | /** Module entry of igbinary. */ 11 | extern zend_module_entry igbinary_module_entry; 12 | #define phpext_igbinary_ptr &igbinary_module_entry 13 | 14 | #ifdef PHP_WIN32 15 | #define PHP_IGBINARY_API __declspec(dllexport) 16 | #else 17 | #define PHP_IGBINARY_API 18 | #endif 19 | 20 | ZEND_BEGIN_MODULE_GLOBALS(igbinary) 21 | zend_bool compact_strings; 22 | ZEND_END_MODULE_GLOBALS(igbinary) 23 | 24 | #ifdef ZTS 25 | #include "TSRM.h" 26 | #endif 27 | 28 | #include "ext/standard/php_smart_str.h" 29 | 30 | /** Module init function. */ 31 | PHP_MINIT_FUNCTION(igbinary); 32 | 33 | /** Module shutdown function. */ 34 | PHP_MSHUTDOWN_FUNCTION(igbinary); 35 | 36 | /** Request init function. */ 37 | PHP_RINIT_FUNCTION(igbinary); 38 | 39 | /** Request shutdown function. */ 40 | PHP_RSHUTDOWN_FUNCTION(igbinary); 41 | 42 | /** Module info function for phpinfo(). */ 43 | PHP_MINFO_FUNCTION(igbinary); 44 | 45 | /** string igbinary_serialize(mixed value). 46 | * Returns the binary serialized value. 47 | */ 48 | PHP_FUNCTION(igbinary_serialize); 49 | 50 | /** mixed igbinary_unserialize(string data). 51 | * Unserializes the given inputstring (value). 52 | */ 53 | PHP_FUNCTION(igbinary_unserialize); 54 | 55 | #ifdef ZTS 56 | #define IGBINARY_G(v) TSRMG(igbinary_globals_id, zend_igbinary_globals *, v) 57 | #else 58 | #define IGBINARY_G(v) (igbinary_globals.v) 59 | #endif 60 | 61 | /** Binary protocol version of igbinary. */ 62 | #define IGBINARY_FORMAT_VERSION 0x00000002 63 | 64 | /** Backport macros from php 5.3 */ 65 | #ifndef Z_ISREF_P 66 | #define Z_ISREF_P(pz) PZVAL_IS_REF(pz) 67 | #endif 68 | 69 | #ifndef Z_ISREF_PP 70 | #define Z_ISREF_PP(ppz) Z_ISREF_P(*(ppz)) 71 | #endif 72 | 73 | #ifndef Z_SET_ISREF_TO_P 74 | #define Z_SET_ISREF_TO_P(pz, isref) (Z_ISREF_P(pz) = (isref)) 75 | #endif 76 | 77 | #ifndef Z_SET_ISREF_TO_PP 78 | #define Z_SET_ISREF_TO_PP(ppz, isref) Z_SET_ISREF_TO_P(*(ppz), isref) 79 | #endif 80 | 81 | #ifndef Z_ADDREF_P 82 | #define Z_ADDREF_P(pz) ZVAL_ADDREF(pz) 83 | #endif 84 | 85 | #ifndef Z_ADDREF_PP 86 | #define Z_ADDREF_PP(ppz) Z_ADDREF_P(*(ppz)) 87 | #endif 88 | #endif /* PHP_IGBINARY_H */ 89 | 90 | 91 | /* 92 | * Local variables: 93 | * tab-width: 2 94 | * c-basic-offset: 0 95 | * End: 96 | * vim600: noet sw=2 ts=2 fdm=marker 97 | * vim<600: noet sw=2 ts=2 98 | */ 99 | -------------------------------------------------------------------------------- /igbinary/tags.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # This generates the tags file for vim or emacs. (vim -t igbinary_serialize8) 4 | # 5 | # Try with "vim -t igbinary_serialize8" 6 | # 7 | 8 | find . -name "*.h" -o -name "*.c" | ctags-exuberant --language-force=c -L - 9 | -------------------------------------------------------------------------------- /library.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "php_network.h" 3 | #include 4 | #include /* TCP_NODELAY */ 5 | #include 6 | #include 7 | #include 8 | 9 | #include "igbinary/igbinary.h" 10 | #include 11 | #include "php_redis.h" 12 | #include "library.h" 13 | #include 14 | 15 | extern zend_class_entry *redis_ce; 16 | extern zend_class_entry *redis_exception_ce; 17 | extern zend_class_entry *spl_ce_RuntimeException; 18 | 19 | PHPAPI void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) { 20 | if (!redis_sock->persistent) { 21 | php_stream_close(redis_sock->stream); 22 | } else { 23 | php_stream_pclose(redis_sock->stream); 24 | } 25 | } 26 | 27 | PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC) 28 | { 29 | 30 | int eof = redis_sock->stream == NULL ? 1 : php_stream_eof(redis_sock->stream); 31 | int count = 0; 32 | while(eof) { 33 | if(count++ == 10) { /* too many failures */ 34 | if(redis_sock->stream) { /* close stream if still here */ 35 | redis_stream_close(redis_sock TSRMLS_CC); 36 | redis_sock->stream = NULL; 37 | redis_sock->mode = ATOMIC; 38 | redis_sock->status = REDIS_SOCK_STATUS_FAILED; 39 | } 40 | zend_throw_exception(redis_exception_ce, "Connection lost", 0 TSRMLS_CC); 41 | return -1; 42 | } 43 | if(redis_sock->stream) { /* close existing stream before reconnecting */ 44 | redis_stream_close(redis_sock TSRMLS_CC); 45 | redis_sock->stream = NULL; 46 | redis_sock->mode = ATOMIC; 47 | } 48 | redis_sock_connect(redis_sock TSRMLS_CC); /* reconnect */ 49 | if(redis_sock->stream) { /* check for EOF again. */ 50 | eof = php_stream_eof(redis_sock->stream); 51 | } 52 | } 53 | return 0; 54 | } 55 | 56 | PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { 57 | char inbuf[1024]; 58 | 59 | if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { 60 | return NULL; 61 | } 62 | 63 | if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { 64 | redis_stream_close(redis_sock TSRMLS_CC); 65 | redis_sock->stream = NULL; 66 | redis_sock->status = REDIS_SOCK_STATUS_FAILED; 67 | redis_sock->mode = ATOMIC; 68 | zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); 69 | return NULL; 70 | } 71 | 72 | if(inbuf[0] != '*') { 73 | return NULL; 74 | } 75 | int numElems = atoi(inbuf+1); 76 | 77 | zval *z_tab; 78 | MAKE_STD_ZVAL(z_tab); 79 | array_init(z_tab); 80 | 81 | redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 82 | redis_sock, z_tab, numElems, 1); 83 | return z_tab; 84 | } 85 | 86 | /** 87 | * redis_sock_read_bulk_reply 88 | */ 89 | PHPAPI char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC) 90 | { 91 | int offset = 0; 92 | size_t got; 93 | 94 | char * reply; 95 | 96 | if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { 97 | return NULL; 98 | } 99 | 100 | if (bytes == -1) { 101 | return NULL; 102 | } else { 103 | reply = emalloc(bytes+1); 104 | 105 | while(offset < bytes) { 106 | got = php_stream_read(redis_sock->stream, reply + offset, bytes-offset); 107 | offset += got; 108 | } 109 | char c; 110 | int i; 111 | for(i = 0; i < 2; i++) { 112 | php_stream_read(redis_sock->stream, &c, 1); 113 | } 114 | } 115 | 116 | reply[bytes] = 0; 117 | return reply; 118 | } 119 | 120 | /** 121 | * redis_sock_read 122 | */ 123 | PHPAPI char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) 124 | { 125 | 126 | char inbuf[1024]; 127 | char *resp = NULL; 128 | 129 | if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { 130 | return NULL; 131 | } 132 | 133 | if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { 134 | redis_stream_close(redis_sock TSRMLS_CC); 135 | redis_sock->stream = NULL; 136 | redis_sock->status = REDIS_SOCK_STATUS_FAILED; 137 | redis_sock->mode = ATOMIC; 138 | zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); 139 | return NULL; 140 | } 141 | 142 | switch(inbuf[0]) { 143 | 144 | case '-': 145 | return NULL; 146 | 147 | case '$': 148 | *buf_len = atoi(inbuf + 1); 149 | resp = redis_sock_read_bulk_reply(redis_sock, *buf_len TSRMLS_CC); 150 | return resp; 151 | 152 | case '+': 153 | case ':': 154 | // Single Line Reply 155 | /* :123\r\n */ 156 | *buf_len = strlen(inbuf) - 2; 157 | if(*buf_len >= 2) { 158 | resp = emalloc(1+*buf_len); 159 | memcpy(resp, inbuf, *buf_len); 160 | resp[*buf_len] = 0; 161 | return resp; 162 | } 163 | 164 | default: 165 | zend_throw_exception_ex( 166 | redis_exception_ce, 167 | 0 TSRMLS_CC, 168 | "protocol error, got '%c' as reply type byte\n", 169 | inbuf[0] 170 | ); 171 | } 172 | 173 | return NULL; 174 | } 175 | 176 | void add_constant_long(zend_class_entry *ce, char *name, int value) { 177 | 178 | zval *constval; 179 | constval = pemalloc(sizeof(zval), 1); 180 | INIT_PZVAL(constval); 181 | ZVAL_LONG(constval, value); 182 | zend_hash_add(&ce->constants_table, name, 1 + strlen(name), 183 | (void*)&constval, sizeof(zval*), NULL); 184 | } 185 | 186 | int 187 | integer_length(int i) { 188 | int sz = 0; 189 | int ci = abs(i); 190 | while (ci>0) { 191 | ci = (ci/10); 192 | sz += 1; 193 | } 194 | if(i == 0) { /* log 0 doesn't make sense. */ 195 | sz = 1; 196 | } else if(i < 0) { /* allow for neg sign as well. */ 197 | sz++; 198 | } 199 | return sz; 200 | } 201 | 202 | int 203 | double_length(double d) { 204 | char *s; 205 | int ret; 206 | s = _php_math_number_format(d, 8, '.', '\x00'); 207 | ret = strlen(s); 208 | efree(s); 209 | return ret; 210 | } 211 | 212 | 213 | int 214 | redis_cmd_format_static(char **ret, char *keyword, char *format, ...) { 215 | 216 | char *p, *s; 217 | va_list ap; 218 | 219 | int total = 0, sz, ret_sz; 220 | int i; 221 | double dbl; 222 | 223 | int stage; /* 0: count & alloc. 1: copy. */ 224 | int elements = strlen(format); 225 | int keyword_len = strlen(keyword); 226 | int header_sz = 1 + integer_length(1 + elements) + 2 /* star + elements + CRLF */ 227 | + 1 + integer_length(keyword_len) + 2 /* dollar + command length + CRLF */ 228 | + keyword_len + 2; /* command + CRLF */ 229 | 230 | for(stage = 0; stage < 2; ++stage) { 231 | va_start(ap, format); 232 | if(stage == 0) { 233 | total = 0; 234 | } else { 235 | total = header_sz; 236 | } 237 | for(p = format; *p; ) { 238 | switch(*p) { 239 | case 's': 240 | s = va_arg(ap, char*); 241 | sz = va_arg(ap, int); 242 | if(stage == 1) { 243 | memcpy((*ret) + total, "$", 1); /* dollar */ 244 | total++; 245 | 246 | sprintf((*ret) + total, "%d", sz); /* size */ 247 | total += integer_length(sz); 248 | 249 | memcpy((*ret) + total, _NL, 2); /* CRLF */ 250 | total += 2; 251 | 252 | memcpy((*ret) + total, s, sz); /* string */ 253 | total += sz; 254 | 255 | memcpy((*ret) + total, _NL, 2); /* CRLF */ 256 | total += 2; 257 | } else { 258 | total += 1 + integer_length(sz) + 2 + sz + 2; 259 | } 260 | break; 261 | 262 | case 'F': 263 | case 'f': 264 | /* use spprintf here */ 265 | dbl = va_arg(ap, double); 266 | sz = double_length(dbl); 267 | char *dbl_str; 268 | if(stage == 1) { 269 | memcpy((*ret) + total, "$", 1); /* dollar */ 270 | total++; 271 | 272 | sprintf((*ret) + total, "%d", sz); /* size */ 273 | total += integer_length(sz); 274 | 275 | memcpy((*ret) + total, _NL, 2); /* CRLF */ 276 | total += 2; 277 | 278 | /* float value */ 279 | dbl_str = _php_math_number_format(dbl, 8, '.', '\x00'); 280 | memcpy((*ret) + total, dbl_str, sz); 281 | efree(dbl_str); 282 | total += sz; 283 | 284 | memcpy((*ret) + total, _NL, 2); /* CRLF */ 285 | total += 2; 286 | } else { 287 | total += 1 + integer_length(sz) + 2 + sz + 2; 288 | } 289 | break; 290 | 291 | case 'i': 292 | case 'd': 293 | i = va_arg(ap, int); 294 | /* compute display size of integer value */ 295 | sz = integer_length(i); 296 | if(stage == 1) { 297 | memcpy((*ret) + total, "$", 1); /* dollar */ 298 | total++; 299 | 300 | sprintf((*ret) + total, "%d", sz); /* size */ 301 | total += integer_length(sz); 302 | 303 | memcpy((*ret) + total, _NL, 2); /* CRLF */ 304 | total += 2; 305 | 306 | sprintf((*ret) + total, "%d", i); /* int */ 307 | total += sz; 308 | 309 | memcpy((*ret) + total, _NL, 2); /* CRLF */ 310 | total += 2; 311 | } else { 312 | total += 1 + integer_length(sz) + 2 + sz + 2; 313 | } 314 | break; 315 | } 316 | p++; 317 | } 318 | if(stage == 0) { 319 | ret_sz = total + header_sz; 320 | (*ret) = emalloc(ret_sz+1); 321 | sprintf(*ret, "*%d" _NL "$%d" _NL "%s" _NL, elements + 1, keyword_len, keyword); 322 | } else { 323 | (*ret)[ret_sz] = 0; 324 | // printf("cmd(%d)=[%s]\n", ret_sz, *ret); 325 | return ret_sz; 326 | } 327 | } 328 | return 0; 329 | } 330 | 331 | /** 332 | * This command behave somehow like printf, except that strings need 2 arguments: 333 | * Their data and their size (strlen). 334 | * Supported formats are: %d, %i, %s 335 | */ 336 | //static /!\ problem with static commands !! 337 | int 338 | redis_cmd_format(char **ret, char *format, ...) { 339 | 340 | char *p, *s; 341 | va_list ap; 342 | 343 | int total = 0, sz, ret_sz; 344 | int i, ci; 345 | double dbl; 346 | char *double_str; 347 | int double_len; 348 | 349 | int stage; /* 0: count & alloc. 1: copy. */ 350 | 351 | for(stage = 0; stage < 2; ++stage) { 352 | va_start(ap, format); 353 | total = 0; 354 | for(p = format; *p; ) { 355 | 356 | if(*p == '%') { 357 | switch(*(p+1)) { 358 | case 's': 359 | s = va_arg(ap, char*); 360 | sz = va_arg(ap, int); 361 | if(stage == 1) { 362 | memcpy((*ret) + total, s, sz); 363 | } 364 | total += sz; 365 | break; 366 | 367 | case 'F': 368 | case 'f': 369 | /* use spprintf here */ 370 | dbl = va_arg(ap, double); 371 | double_len = double_length(dbl); 372 | 373 | if(stage == 1) { 374 | /* float value */ 375 | char *dbl_str = _php_math_number_format(dbl, 8, '.', '\x00'); 376 | memcpy((*ret) + total, dbl_str, sz); 377 | total += sz; 378 | efree(dbl_str); 379 | } 380 | total += double_len; 381 | efree(double_str); 382 | break; 383 | 384 | case 'i': 385 | case 'd': 386 | i = va_arg(ap, int); 387 | /* compute display size of integer value */ 388 | sz = 0; 389 | ci = abs(i); 390 | while (ci>0) { 391 | ci = (ci/10); 392 | sz += 1; 393 | } 394 | if(i == 0) { /* log 0 doesn't make sense. */ 395 | sz = 1; 396 | } else if(i < 0) { /* allow for neg sign as well. */ 397 | sz++; 398 | } 399 | if(stage == 1) { 400 | sprintf((*ret) + total, "%d", i); 401 | } 402 | total += sz; 403 | break; 404 | } 405 | p++; 406 | } else { 407 | if(stage == 1) { 408 | (*ret)[total] = *p; 409 | } 410 | total++; 411 | } 412 | 413 | p++; 414 | } 415 | if(stage == 0) { 416 | ret_sz = total; 417 | (*ret) = emalloc(ret_sz+1); 418 | } else { 419 | (*ret)[ret_sz] = 0; 420 | return ret_sz; 421 | } 422 | } 423 | return 0; 424 | } 425 | 426 | PHPAPI void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { 427 | 428 | char *response; 429 | int response_len; 430 | 431 | if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { 432 | RETURN_FALSE; 433 | } 434 | 435 | double ret = atof(response); 436 | efree(response); 437 | IF_MULTI_OR_PIPELINE() { 438 | add_next_index_double(z_tab, ret); 439 | } else { 440 | RETURN_DOUBLE(ret); 441 | } 442 | } 443 | 444 | PHPAPI void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { 445 | char *response; 446 | int response_len; 447 | 448 | if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { 449 | RETURN_FALSE; 450 | } 451 | 452 | long l; 453 | if (strncmp(response, "+string", 7) == 0) { 454 | l = REDIS_STRING; 455 | } else if (strncmp(response, "+set", 4) == 0){ 456 | l = REDIS_SET; 457 | } else if (strncmp(response, "+list", 5) == 0){ 458 | l = REDIS_LIST; 459 | } else if (strncmp(response, "+zset", 5) == 0){ 460 | l = REDIS_ZSET; 461 | } else if (strncmp(response, "+hash", 5) == 0){ 462 | l = REDIS_HASH; 463 | } else { 464 | l = REDIS_NOT_FOUND; 465 | } 466 | 467 | efree(response); 468 | IF_MULTI_OR_PIPELINE() { 469 | add_next_index_long(z_tab, l); 470 | } else { 471 | RETURN_LONG(l); 472 | } 473 | } 474 | 475 | PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { 476 | char *response; 477 | int response_len; 478 | 479 | if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { 480 | RETURN_FALSE; 481 | } 482 | 483 | zval *z_multi_result; 484 | MAKE_STD_ZVAL(z_multi_result); 485 | array_init(z_multi_result); /* pre-allocate array for multi's results. */ 486 | /* response :: [response_line] 487 | * response_line :: key ':' value CRLF 488 | */ 489 | 490 | char *pos, *cur = response; 491 | while(1) { 492 | char *key, *value, *p; 493 | int is_numeric; 494 | /* key */ 495 | pos = strchr(cur, ':'); 496 | if(pos == NULL) { 497 | break; 498 | } 499 | key = emalloc(pos - cur + 1); 500 | memcpy(key, cur, pos-cur); 501 | key[pos-cur] = 0; 502 | 503 | /* value */ 504 | cur = pos + 1; 505 | pos = strchr(cur, '\r'); 506 | if(pos == NULL) { 507 | break; 508 | } 509 | value = emalloc(pos - cur + 1); 510 | memcpy(value, cur, pos-cur); 511 | value[pos-cur] = 0; 512 | pos += 2; /* \r, \n */ 513 | cur = pos; 514 | 515 | is_numeric = 1; 516 | for(p = value; *p; ++p) { 517 | if(*p < '0' || *p > '9') { 518 | is_numeric = 0; 519 | break; 520 | } 521 | } 522 | 523 | if(is_numeric == 1) { 524 | add_assoc_long(z_multi_result, key, atol(value)); 525 | efree(value); 526 | } else { 527 | add_assoc_string(z_multi_result, key, value, 0); 528 | } 529 | efree(key); 530 | } 531 | efree(response); 532 | 533 | IF_MULTI_OR_PIPELINE() { 534 | add_next_index_zval(z_tab, z_multi_result); 535 | } else { 536 | *return_value = *z_multi_result; 537 | zval_copy_ctor(return_value); 538 | efree(z_multi_result); 539 | } 540 | } 541 | 542 | PHPAPI void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { 543 | 544 | char *response; 545 | int response_len; 546 | char ret; 547 | 548 | if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { 549 | IF_MULTI_OR_PIPELINE() { 550 | add_next_index_bool(z_tab, 0); 551 | return; 552 | } 553 | RETURN_FALSE; 554 | } 555 | ret = response[0]; 556 | efree(response); 557 | 558 | IF_MULTI_OR_PIPELINE() { 559 | if (ret == '+') { 560 | add_next_index_bool(z_tab, 1); 561 | } else { 562 | add_next_index_bool(z_tab, 0); 563 | } 564 | } else { 565 | if (ret == '+') { 566 | RETURN_TRUE; 567 | } else { 568 | RETURN_FALSE; 569 | } 570 | } 571 | } 572 | 573 | PHPAPI void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval * z_tab, void *ctx) { 574 | 575 | char *response; 576 | int response_len; 577 | 578 | if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { 579 | IF_MULTI_OR_PIPELINE() { 580 | add_next_index_bool(z_tab, 0); 581 | return; 582 | } else { 583 | RETURN_FALSE; 584 | } 585 | } 586 | 587 | if(response[0] == ':') { 588 | long long ret = atoll(response + 1); 589 | IF_MULTI_OR_PIPELINE() { 590 | if(ret > (long long)LONG_MAX) { /* overflow */ 591 | add_next_index_stringl(z_tab, response+1, response_len-1, 1); 592 | } else { 593 | efree(response); 594 | add_next_index_long(z_tab, (long)ret); 595 | } 596 | } else { 597 | if(ret > (long long)LONG_MAX) { /* overflow */ 598 | RETURN_STRINGL(response+1, response_len-1, 1); 599 | } else { 600 | efree(response); 601 | RETURN_LONG((long)ret); 602 | } 603 | } 604 | } else { 605 | efree(response); 606 | IF_MULTI_OR_PIPELINE() { 607 | add_next_index_null(z_tab); 608 | } else { 609 | RETURN_FALSE; 610 | } 611 | } 612 | } 613 | 614 | PHPAPI int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int flag) { 615 | 616 | /* 617 | int ret = redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab TSRMLS_CC); 618 | array_zip_values_and_scores(return_value, 0); 619 | */ 620 | 621 | char inbuf[1024]; 622 | 623 | if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { 624 | return -1; 625 | } 626 | if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { 627 | redis_stream_close(redis_sock TSRMLS_CC); 628 | redis_sock->stream = NULL; 629 | redis_sock->stream = NULL; 630 | redis_sock->status = REDIS_SOCK_STATUS_FAILED; 631 | redis_sock->mode = ATOMIC; 632 | zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); 633 | return -1; 634 | } 635 | 636 | if(inbuf[0] != '*') { 637 | return -1; 638 | } 639 | int numElems = atoi(inbuf+1); 640 | zval *z_multi_result; 641 | MAKE_STD_ZVAL(z_multi_result); 642 | array_init(z_multi_result); /* pre-allocate array for multi's results. */ 643 | 644 | redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 645 | redis_sock, z_multi_result, numElems, 1); 646 | 647 | array_zip_values_and_scores(redis_sock, z_multi_result, 0 TSRMLS_CC); 648 | 649 | IF_MULTI_OR_PIPELINE() { 650 | add_next_index_zval(z_tab, z_multi_result); 651 | } else { 652 | *return_value = *z_multi_result; 653 | zval_copy_ctor(return_value); 654 | zval_dtor(z_multi_result); 655 | efree(z_multi_result); 656 | } 657 | 658 | return 0; 659 | } 660 | 661 | PHPAPI int redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { 662 | 663 | return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 1); 664 | } 665 | 666 | PHPAPI int redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { 667 | return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 0); 668 | } 669 | 670 | PHPAPI void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { 671 | 672 | char *response; 673 | int response_len; 674 | char ret; 675 | 676 | if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { 677 | IF_MULTI_OR_PIPELINE() { 678 | add_next_index_bool(z_tab, 0); 679 | return; 680 | } else { 681 | RETURN_FALSE; 682 | } 683 | } 684 | ret = response[1]; 685 | efree(response); 686 | 687 | IF_MULTI_OR_PIPELINE() { 688 | if(ret == '1') { 689 | add_next_index_bool(z_tab, 1); 690 | } else { 691 | add_next_index_bool(z_tab, 0); 692 | } 693 | } else { 694 | if (ret == '1') { 695 | RETURN_TRUE; 696 | } else { 697 | RETURN_FALSE; 698 | } 699 | } 700 | } 701 | 702 | PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { 703 | 704 | char *response; 705 | int response_len; 706 | 707 | if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { 708 | IF_MULTI_OR_PIPELINE() { 709 | add_next_index_bool(z_tab, 0); 710 | return; 711 | } 712 | RETURN_FALSE; 713 | } 714 | IF_MULTI_OR_PIPELINE() { 715 | zval *z = NULL; 716 | if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { 717 | efree(response); 718 | add_next_index_zval(z_tab, z); 719 | } else { 720 | add_next_index_stringl(z_tab, response, response_len, 0); 721 | } 722 | } else { 723 | if(redis_unserialize(redis_sock, response, response_len, &return_value TSRMLS_CC) == 0) { 724 | RETURN_STRINGL(response, response_len, 0); 725 | } else { 726 | efree(response); 727 | } 728 | } 729 | } 730 | 731 | /* like string response, but never unserialized. */ 732 | PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { 733 | 734 | char *response; 735 | int response_len; 736 | 737 | if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { 738 | IF_MULTI_OR_PIPELINE() { 739 | add_next_index_bool(z_tab, 0); 740 | return; 741 | } 742 | RETURN_FALSE; 743 | } 744 | IF_MULTI_OR_PIPELINE() { 745 | zval *z = NULL; 746 | add_next_index_stringl(z_tab, response, response_len, 0); 747 | } else { 748 | RETURN_STRINGL(response, response_len, 0); 749 | } 750 | } 751 | 752 | 753 | /** 754 | * redis_sock_create 755 | */ 756 | PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, 757 | double timeout, int persistent) 758 | { 759 | RedisSock *redis_sock; 760 | 761 | redis_sock = ecalloc(1, sizeof(RedisSock)); 762 | redis_sock->host = ecalloc(host_len + 1, 1); 763 | redis_sock->stream = NULL; 764 | redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; 765 | 766 | redis_sock->persistent = persistent; 767 | 768 | memcpy(redis_sock->host, host, host_len); 769 | redis_sock->host[host_len] = '\0'; 770 | 771 | redis_sock->port = port; 772 | redis_sock->timeout = timeout; 773 | 774 | redis_sock->serializer = REDIS_SERIALIZER_NONE; 775 | redis_sock->mode = ATOMIC; 776 | redis_sock->head = NULL; 777 | redis_sock->current = NULL; 778 | redis_sock->pipeline_head = NULL; 779 | redis_sock->pipeline_current = NULL; 780 | 781 | return redis_sock; 782 | } 783 | 784 | /** 785 | * redis_sock_connect 786 | */ 787 | PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) 788 | { 789 | struct timeval tv, *tv_ptr = NULL; 790 | char *host = NULL, *persistent_id = NULL, *errstr = NULL; 791 | int host_len, err = 0; 792 | 793 | if (redis_sock->stream != NULL) { 794 | redis_sock_disconnect(redis_sock TSRMLS_CC); 795 | } 796 | 797 | tv.tv_sec = (time_t)redis_sock->timeout; 798 | tv.tv_usec = (int)((redis_sock->timeout - tv.tv_sec) * 1000000); 799 | if(tv.tv_sec != 0 || tv.tv_usec != 0) { 800 | tv_ptr = &tv; 801 | } 802 | 803 | if(redis_sock->host[0] == '/' && redis_sock->port < 1) { 804 | host_len = spprintf(&host, 0, "unix://%s", redis_sock->host); 805 | } else { 806 | host_len = spprintf(&host, 0, "%s:%d", redis_sock->host, redis_sock->port); 807 | } 808 | 809 | if (redis_sock->persistent) { 810 | spprintf(&persistent_id, 0, "%s:%f", host, redis_sock->timeout); 811 | } 812 | 813 | redis_sock->stream = php_stream_xport_create(host, host_len, ENFORCE_SAFE_MODE, 814 | STREAM_XPORT_CLIENT 815 | | STREAM_XPORT_CONNECT, 816 | persistent_id, tv_ptr, NULL, &errstr, &err 817 | ); 818 | 819 | if (persistent_id) { 820 | efree(persistent_id); 821 | } 822 | 823 | efree(host); 824 | 825 | if (!redis_sock->stream) { 826 | efree(errstr); 827 | return -1; 828 | } 829 | 830 | /* set TCP_NODELAY */ 831 | php_netstream_data_t *sock = (php_netstream_data_t*)redis_sock->stream->abstract; 832 | int tcp_flag = 1; 833 | setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &tcp_flag, sizeof(int)); 834 | 835 | php_stream_auto_cleanup(redis_sock->stream); 836 | 837 | if(tv.tv_sec != 0) { 838 | php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_READ_TIMEOUT, 839 | 0, &tv); 840 | } 841 | php_stream_set_option(redis_sock->stream, 842 | PHP_STREAM_OPTION_WRITE_BUFFER, 843 | PHP_STREAM_BUFFER_NONE, NULL); 844 | 845 | redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; 846 | 847 | return 0; 848 | } 849 | 850 | /** 851 | * redis_sock_server_open 852 | */ 853 | PHPAPI int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC) 854 | { 855 | int res = -1; 856 | 857 | switch (redis_sock->status) { 858 | case REDIS_SOCK_STATUS_DISCONNECTED: 859 | return redis_sock_connect(redis_sock TSRMLS_CC); 860 | case REDIS_SOCK_STATUS_CONNECTED: 861 | res = 0; 862 | break; 863 | case REDIS_SOCK_STATUS_UNKNOWN: 864 | if (force_connect > 0 && redis_sock_connect(redis_sock TSRMLS_CC) < 0) { 865 | res = -1; 866 | } else { 867 | res = 0; 868 | 869 | redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; 870 | } 871 | break; 872 | } 873 | 874 | return res; 875 | } 876 | 877 | /** 878 | * redis_sock_disconnect 879 | */ 880 | PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC) 881 | { 882 | if (redis_sock == NULL) { 883 | return 1; 884 | } 885 | 886 | if (redis_sock->stream != NULL) { 887 | if (!redis_sock->persistent) { 888 | redis_sock_write(redis_sock, "QUIT", sizeof("QUIT") - 1 TSRMLS_CC); 889 | } 890 | 891 | redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; 892 | if(redis_sock->stream && !redis_sock->persistent) { /* still valid after the write? */ 893 | php_stream_close(redis_sock->stream); 894 | } 895 | redis_sock->stream = NULL; 896 | 897 | return 1; 898 | } 899 | 900 | return 0; 901 | } 902 | 903 | /** 904 | * redis_sock_read_multibulk_reply 905 | */ 906 | PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) 907 | { 908 | char inbuf[1024]; 909 | 910 | if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { 911 | return -1; 912 | } 913 | if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { 914 | redis_stream_close(redis_sock TSRMLS_CC); 915 | redis_sock->stream = NULL; 916 | redis_sock->status = REDIS_SOCK_STATUS_FAILED; 917 | redis_sock->mode = ATOMIC; 918 | zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); 919 | return -1; 920 | } 921 | 922 | if(inbuf[0] != '*') { 923 | return -1; 924 | } 925 | int numElems = atoi(inbuf+1); 926 | zval *z_multi_result; 927 | MAKE_STD_ZVAL(z_multi_result); 928 | array_init(z_multi_result); /* pre-allocate array for multi's results. */ 929 | 930 | redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 931 | redis_sock, z_multi_result, numElems, 1); 932 | 933 | IF_MULTI_OR_PIPELINE() { 934 | add_next_index_zval(z_tab, z_multi_result); 935 | } else { 936 | *return_value = *z_multi_result; 937 | efree(z_multi_result); 938 | } 939 | //zval_copy_ctor(return_value); 940 | return 0; 941 | } 942 | 943 | /** 944 | * Like multibulk reply, but don't touch the values, they won't be compressed. (this is used by HKEYS). 945 | */ 946 | PHPAPI int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) 947 | { 948 | char inbuf[1024]; 949 | 950 | if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { 951 | return -1; 952 | } 953 | if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { 954 | redis_stream_close(redis_sock TSRMLS_CC); 955 | redis_sock->stream = NULL; 956 | redis_sock->status = REDIS_SOCK_STATUS_FAILED; 957 | redis_sock->mode = ATOMIC; 958 | zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); 959 | return -1; 960 | } 961 | 962 | if(inbuf[0] != '*') { 963 | return -1; 964 | } 965 | int numElems = atoi(inbuf+1); 966 | zval *z_multi_result; 967 | MAKE_STD_ZVAL(z_multi_result); 968 | array_init(z_multi_result); /* pre-allocate array for multi's results. */ 969 | 970 | redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 971 | redis_sock, z_multi_result, numElems, 0); 972 | 973 | IF_MULTI_OR_PIPELINE() { 974 | add_next_index_zval(z_tab, z_multi_result); 975 | } else { 976 | *return_value = *z_multi_result; 977 | efree(z_multi_result); 978 | } 979 | //zval_copy_ctor(return_value); 980 | return 0; 981 | } 982 | 983 | PHPAPI int 984 | redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, 985 | zval *z_tab, int numElems, int unwrap_key) 986 | { 987 | char *response; 988 | int response_len; 989 | 990 | while(numElems > 0) { 991 | response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); 992 | if(response != NULL) { 993 | zval *z = NULL; 994 | if(unwrap_key && redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { 995 | efree(response); 996 | add_next_index_zval(z_tab, z); 997 | } else { 998 | add_next_index_stringl(z_tab, response, response_len, 0); 999 | } 1000 | } else { 1001 | add_next_index_bool(z_tab, 0); 1002 | } 1003 | numElems --; 1004 | } 1005 | return 0; 1006 | } 1007 | 1008 | /** 1009 | * redis_sock_read_multibulk_reply_assoc 1010 | */ 1011 | PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) 1012 | { 1013 | char inbuf[1024], *response; 1014 | int response_len; 1015 | 1016 | zval **z_keys = ctx; 1017 | 1018 | if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { 1019 | return -1; 1020 | } 1021 | if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { 1022 | redis_stream_close(redis_sock TSRMLS_CC); 1023 | redis_sock->stream = NULL; 1024 | redis_sock->status = REDIS_SOCK_STATUS_FAILED; 1025 | redis_sock->mode = ATOMIC; 1026 | zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); 1027 | return -1; 1028 | } 1029 | 1030 | if(inbuf[0] != '*') { 1031 | return -1; 1032 | } 1033 | int i, numElems = atoi(inbuf+1); 1034 | zval *z_multi_result; 1035 | MAKE_STD_ZVAL(z_multi_result); 1036 | array_init(z_multi_result); /* pre-allocate array for multi's results. */ 1037 | 1038 | for(i = 0; i < numElems; ++i) { 1039 | response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); 1040 | if(response != NULL) { 1041 | zval *z = NULL; 1042 | if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { 1043 | efree(response); 1044 | add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); 1045 | } else { 1046 | add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len, 1); 1047 | } 1048 | } else { 1049 | add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0); 1050 | } 1051 | zval_dtor(z_keys[i]); 1052 | efree(z_keys[i]); 1053 | } 1054 | efree(z_keys); 1055 | 1056 | IF_MULTI_OR_PIPELINE() { 1057 | add_next_index_zval(z_tab, z_multi_result); 1058 | } else { 1059 | *return_value = *z_multi_result; 1060 | //zval_copy_ctor(return_value); 1061 | efree(z_multi_result); 1062 | } 1063 | return 0; 1064 | } 1065 | 1066 | /** 1067 | * redis_sock_write 1068 | */ 1069 | PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC) 1070 | { 1071 | if(redis_sock && redis_sock->status == REDIS_SOCK_STATUS_DISCONNECTED) { 1072 | zend_throw_exception(redis_exception_ce, "Connection closed", 0 TSRMLS_CC); 1073 | return -1; 1074 | } 1075 | if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { 1076 | return -1; 1077 | } 1078 | return php_stream_write(redis_sock->stream, cmd, sz); 1079 | } 1080 | 1081 | /** 1082 | * redis_free_socket 1083 | */ 1084 | PHPAPI void redis_free_socket(RedisSock *redis_sock) 1085 | { 1086 | if(redis_sock->prefix) { 1087 | efree(redis_sock->prefix); 1088 | } 1089 | efree(redis_sock->host); 1090 | efree(redis_sock); 1091 | } 1092 | 1093 | PHPAPI int 1094 | redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_CC) { 1095 | #if ZEND_MODULE_API_NO >= 20100000 1096 | php_serialize_data_t ht; 1097 | #else 1098 | HashTable ht; 1099 | #endif 1100 | smart_str sstr = {0}; 1101 | zval *z_copy; 1102 | size_t sz; 1103 | uint8_t *val8; 1104 | 1105 | switch(redis_sock->serializer) { 1106 | case REDIS_SERIALIZER_NONE: 1107 | switch(Z_TYPE_P(z)) { 1108 | 1109 | case IS_STRING: 1110 | *val = Z_STRVAL_P(z); 1111 | *val_len = Z_STRLEN_P(z); 1112 | return 0; 1113 | 1114 | case IS_OBJECT: 1115 | MAKE_STD_ZVAL(z_copy); 1116 | ZVAL_STRINGL(z_copy, "Object", 6, 1); 1117 | break; 1118 | 1119 | case IS_ARRAY: 1120 | MAKE_STD_ZVAL(z_copy); 1121 | ZVAL_STRINGL(z_copy, "Array", 5, 1); 1122 | break; 1123 | 1124 | default: /* copy */ 1125 | MAKE_STD_ZVAL(z_copy); 1126 | *z_copy = *z; 1127 | zval_copy_ctor(z_copy); 1128 | break; 1129 | } 1130 | 1131 | /* return string */ 1132 | convert_to_string(z_copy); 1133 | *val = Z_STRVAL_P(z_copy); 1134 | *val_len = Z_STRLEN_P(z_copy); 1135 | efree(z_copy); 1136 | return 1; 1137 | 1138 | case REDIS_SERIALIZER_PHP: 1139 | 1140 | #if ZEND_MODULE_API_NO >= 20100000 1141 | #else 1142 | zend_hash_init(&ht, 10, NULL, NULL, 0); 1143 | #endif 1144 | php_var_serialize(&sstr, &z, &ht TSRMLS_CC); 1145 | *val = sstr.c; 1146 | *val_len = (int)sstr.len; 1147 | #if ZEND_MODULE_API_NO >= 20100000 1148 | #else 1149 | zend_hash_destroy(&ht); 1150 | #endif 1151 | 1152 | return 1; 1153 | 1154 | case REDIS_SERIALIZER_IGBINARY: 1155 | if(igbinary_serialize(&val8, (size_t *)&sz, z TSRMLS_CC) == 0) { /* ok */ 1156 | *val = (char*)val8; 1157 | *val_len = (int)sz; 1158 | return 1; 1159 | } 1160 | return 0; 1161 | } 1162 | return 0; 1163 | } 1164 | 1165 | PHPAPI int 1166 | redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **return_value TSRMLS_CC) { 1167 | 1168 | php_unserialize_data_t var_hash; 1169 | int ret; 1170 | 1171 | switch(redis_sock->serializer) { 1172 | case REDIS_SERIALIZER_NONE: 1173 | return 0; 1174 | 1175 | case REDIS_SERIALIZER_PHP: 1176 | if(!*return_value) { 1177 | MAKE_STD_ZVAL(*return_value); 1178 | } 1179 | memset(&var_hash, 0, sizeof(var_hash)); 1180 | if(!php_var_unserialize(return_value, (const unsigned char**)&val, 1181 | (const unsigned char*)val + val_len, &var_hash TSRMLS_CC)) { 1182 | efree(*return_value); 1183 | ret = 0; 1184 | } else { 1185 | ret = 1; 1186 | } 1187 | var_destroy(&var_hash); 1188 | 1189 | return ret; 1190 | 1191 | case REDIS_SERIALIZER_IGBINARY: 1192 | if(!*return_value) { 1193 | MAKE_STD_ZVAL(*return_value); 1194 | } 1195 | if(igbinary_unserialize((const uint8_t *)val, (size_t)val_len, return_value TSRMLS_CC) == 0) { 1196 | return 1; 1197 | } 1198 | efree(*return_value); 1199 | return 0; 1200 | break; 1201 | } 1202 | return 0; 1203 | } 1204 | 1205 | PHPAPI int 1206 | redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len TSRMLS_CC) { 1207 | if(redis_sock->prefix == NULL || redis_sock->prefix_len == 0) { 1208 | return 0; 1209 | } 1210 | 1211 | int ret_len = redis_sock->prefix_len + *key_len; 1212 | char *ret = ecalloc(1 + ret_len, 1); 1213 | memcpy(ret, redis_sock->prefix, redis_sock->prefix_len); 1214 | memcpy(ret + redis_sock->prefix_len, *key, *key_len); 1215 | 1216 | *key = ret; 1217 | *key_len = ret_len; 1218 | return 1; 1219 | } 1220 | 1221 | /* vim: set tabstop=4 softtabstop=4 noexpandtab shiftwidth=4: */ 1222 | 1223 | -------------------------------------------------------------------------------- /library.h: -------------------------------------------------------------------------------- 1 | void add_constant_long(zend_class_entry *ce, char *name, int value); 2 | int integer_length(int i); 3 | int double_length(double d); 4 | int redis_cmd_format(char **ret, char *format, ...); 5 | int redis_cmd_format_static(char **ret, char *keyword, char *format, ...); 6 | 7 | PHPAPI char * redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC); 8 | 9 | PHPAPI void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); 10 | PHPAPI void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval* z_tab, void *ctx); 11 | PHPAPI void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); 12 | PHPAPI void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); 13 | PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); 14 | PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); 15 | PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); 16 | PHPAPI void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); 17 | PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent); 18 | PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC); 19 | PHPAPI int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC); 20 | PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC); 21 | PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); 22 | PHPAPI char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC); 23 | PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *_z_tab, void *ctx); 24 | PHPAPI int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); 25 | PHPAPI int redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int numElems, int unwrap_key); 26 | PHPAPI int redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); 27 | PHPAPI int redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); 28 | PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); 29 | PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC); 30 | PHPAPI void redis_stream_close(RedisSock *redis_sock TSRMLS_DC); 31 | PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC); 32 | //PHPAPI int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC); 33 | PHPAPI void redis_free_socket(RedisSock *redis_sock); 34 | 35 | PHPAPI int 36 | redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_CC); 37 | PHPAPI int 38 | redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len TSRMLS_CC); 39 | 40 | PHPAPI int 41 | redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **return_value TSRMLS_CC); 42 | -------------------------------------------------------------------------------- /mkdeb-apache2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | phpize 3 | ./configure CFLAGS="-O3" 4 | make clean all 5 | DIR=`php-config --extension-dir | cut -c 2-` 6 | 7 | rm -rf debian 8 | 9 | mkdir -p debian 10 | mkdir -p debian/DEBIAN 11 | mkdir -p debian/$DIR 12 | 13 | cp debian.control debian/DEBIAN/control 14 | 15 | UBUNTU=`uname -v | grep -ci ubuntu` 16 | mkdir -p debian/etc/php5/apache2/conf.d/ 17 | if [ $UBUNTU = "0" ]; then 18 | mkdir -p debian/etc/php5/cli/conf.d/ 19 | fi 20 | 21 | echo "extension=redis.so" >> debian/etc/php5/apache2/conf.d/redis.ini 22 | 23 | if [ $UBUNTU = "0" ]; then 24 | cp debian/etc/php5/apache2/conf.d/redis.ini debian/etc/php5/cli/conf.d/redis.ini 25 | fi 26 | 27 | cp modules/redis.so debian/$DIR 28 | dpkg -b debian phpredis-`uname -m`.deb 29 | rm -rf debian/ 30 | -------------------------------------------------------------------------------- /php_redis.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2009 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Original author: Alfonso Jimenez | 16 | | Maintainer: Nicolas Favre-Felix | 17 | | Maintainer: Nasreddine Bouafif | 18 | +----------------------------------------------------------------------+ 19 | */ 20 | 21 | #include "common.h" 22 | 23 | #ifndef PHP_REDIS_H 24 | #define PHP_REDIS_H 25 | 26 | PHP_METHOD(Redis, __construct); 27 | PHP_METHOD(Redis, connect); 28 | PHP_METHOD(Redis, pconnect); 29 | PHP_METHOD(Redis, close); 30 | PHP_METHOD(Redis, ping); 31 | PHP_METHOD(Redis, get); 32 | PHP_METHOD(Redis, set); 33 | PHP_METHOD(Redis, setex); 34 | PHP_METHOD(Redis, setnx); 35 | PHP_METHOD(Redis, getSet); 36 | PHP_METHOD(Redis, randomKey); 37 | PHP_METHOD(Redis, renameKey); 38 | PHP_METHOD(Redis, renameNx); 39 | PHP_METHOD(Redis, getMultiple); 40 | PHP_METHOD(Redis, exists); 41 | PHP_METHOD(Redis, delete); 42 | PHP_METHOD(Redis, incr); 43 | PHP_METHOD(Redis, incrBy); 44 | PHP_METHOD(Redis, decr); 45 | PHP_METHOD(Redis, decrBy); 46 | PHP_METHOD(Redis, type); 47 | PHP_METHOD(Redis, append); 48 | PHP_METHOD(Redis, getRange); 49 | PHP_METHOD(Redis, setRange); 50 | PHP_METHOD(Redis, getBit); 51 | PHP_METHOD(Redis, setBit); 52 | PHP_METHOD(Redis, strlen); 53 | PHP_METHOD(Redis, getKeys); 54 | PHP_METHOD(Redis, sort); 55 | PHP_METHOD(Redis, sortAsc); 56 | PHP_METHOD(Redis, sortAscAlpha); 57 | PHP_METHOD(Redis, sortDesc); 58 | PHP_METHOD(Redis, sortDescAlpha); 59 | PHP_METHOD(Redis, lPush); 60 | PHP_METHOD(Redis, lPushx); 61 | PHP_METHOD(Redis, rPush); 62 | PHP_METHOD(Redis, rPushx); 63 | PHP_METHOD(Redis, lPop); 64 | PHP_METHOD(Redis, rPop); 65 | PHP_METHOD(Redis, blPop); 66 | PHP_METHOD(Redis, brPop); 67 | PHP_METHOD(Redis, lSize); 68 | PHP_METHOD(Redis, lRemove); 69 | PHP_METHOD(Redis, listTrim); 70 | PHP_METHOD(Redis, lGet); 71 | PHP_METHOD(Redis, lGetRange); 72 | PHP_METHOD(Redis, lSet); 73 | PHP_METHOD(Redis, lInsert); 74 | PHP_METHOD(Redis, sAdd); 75 | PHP_METHOD(Redis, sSize); 76 | PHP_METHOD(Redis, sRemove); 77 | PHP_METHOD(Redis, sMove); 78 | PHP_METHOD(Redis, sPop); 79 | PHP_METHOD(Redis, sRandMember); 80 | PHP_METHOD(Redis, sContains); 81 | PHP_METHOD(Redis, sMembers); 82 | PHP_METHOD(Redis, sInter); 83 | PHP_METHOD(Redis, sInterStore); 84 | PHP_METHOD(Redis, sUnion); 85 | PHP_METHOD(Redis, sUnionStore); 86 | PHP_METHOD(Redis, sDiff); 87 | PHP_METHOD(Redis, sDiffStore); 88 | PHP_METHOD(Redis, setTimeout); 89 | PHP_METHOD(Redis, save); 90 | PHP_METHOD(Redis, bgSave); 91 | PHP_METHOD(Redis, lastSave); 92 | PHP_METHOD(Redis, flushDB); 93 | PHP_METHOD(Redis, flushAll); 94 | PHP_METHOD(Redis, dbSize); 95 | PHP_METHOD(Redis, auth); 96 | PHP_METHOD(Redis, ttl); 97 | PHP_METHOD(Redis, persist); 98 | PHP_METHOD(Redis, info); 99 | PHP_METHOD(Redis, select); 100 | PHP_METHOD(Redis, move); 101 | PHP_METHOD(Redis, zAdd); 102 | PHP_METHOD(Redis, zDelete); 103 | PHP_METHOD(Redis, zRange); 104 | PHP_METHOD(Redis, zReverseRange); 105 | PHP_METHOD(Redis, zRangeByScore); 106 | PHP_METHOD(Redis, zRevRangeByScore); 107 | PHP_METHOD(Redis, zCount); 108 | PHP_METHOD(Redis, zDeleteRangeByScore); 109 | PHP_METHOD(Redis, zDeleteRangeByRank); 110 | PHP_METHOD(Redis, zCard); 111 | PHP_METHOD(Redis, zScore); 112 | PHP_METHOD(Redis, zRank); 113 | PHP_METHOD(Redis, zRevRank); 114 | PHP_METHOD(Redis, zIncrBy); 115 | PHP_METHOD(Redis, zInter); 116 | PHP_METHOD(Redis, zUnion); 117 | PHP_METHOD(Redis, expireAt); 118 | PHP_METHOD(Redis, bgrewriteaof); 119 | PHP_METHOD(Redis, slaveof); 120 | 121 | PHP_METHOD(Redis, mset); 122 | PHP_METHOD(Redis, msetnx); 123 | PHP_METHOD(Redis, rpoplpush); 124 | 125 | PHP_METHOD(Redis, hGet); 126 | PHP_METHOD(Redis, hSet); 127 | PHP_METHOD(Redis, hSetNx); 128 | PHP_METHOD(Redis, hDel); 129 | PHP_METHOD(Redis, hLen); 130 | PHP_METHOD(Redis, hKeys); 131 | PHP_METHOD(Redis, hVals); 132 | PHP_METHOD(Redis, hGetAll); 133 | PHP_METHOD(Redis, hExists); 134 | PHP_METHOD(Redis, hIncrBy); 135 | PHP_METHOD(Redis, hMset); 136 | PHP_METHOD(Redis, hMget); 137 | 138 | PHP_METHOD(Redis, multi); 139 | PHP_METHOD(Redis, discard); 140 | PHP_METHOD(Redis, exec); 141 | PHP_METHOD(Redis, watch); 142 | PHP_METHOD(Redis, unwatch); 143 | 144 | PHP_METHOD(Redis, pipeline); 145 | 146 | PHP_METHOD(Redis, publish); 147 | PHP_METHOD(Redis, subscribe); 148 | PHP_METHOD(Redis, unsubscribe); 149 | 150 | PHP_METHOD(Redis, getOption); 151 | PHP_METHOD(Redis, setOption); 152 | 153 | #ifdef PHP_WIN32 154 | #define PHP_REDIS_API __declspec(dllexport) 155 | #else 156 | #define PHP_REDIS_API 157 | #endif 158 | 159 | #ifdef ZTS 160 | #include "TSRM.h" 161 | #endif 162 | 163 | PHP_MINIT_FUNCTION(redis); 164 | PHP_MSHUTDOWN_FUNCTION(redis); 165 | PHP_RINIT_FUNCTION(redis); 166 | PHP_RSHUTDOWN_FUNCTION(redis); 167 | PHP_MINFO_FUNCTION(redis); 168 | 169 | PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent); 170 | PHPAPI void redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int count); 171 | PHPAPI int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len, 172 | int min_argc, RedisSock **redis_sock, int has_timeout); 173 | PHPAPI void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, int use_alpha); 174 | PHPAPI void generic_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ...); 175 | PHPAPI void generic_empty_long_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ...); 176 | 177 | PHPAPI void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, int use_atof TSRMLS_DC); 178 | PHPAPI int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC); 179 | 180 | PHPAPI int get_flag(zval *object TSRMLS_DC); 181 | PHPAPI void set_flag(zval *object, int new_flag TSRMLS_DC); 182 | 183 | PHPAPI int redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int numElems); 184 | 185 | /* pipeline */ 186 | PHPAPI request_item* get_pipeline_head(zval *object); 187 | PHPAPI void set_pipeline_head(zval *object, request_item *head); 188 | PHPAPI request_item* get_pipeline_current(zval *object); 189 | PHPAPI void set_pipeline_current(zval *object, request_item *current); 190 | 191 | ZEND_BEGIN_MODULE_GLOBALS(redis) 192 | ZEND_END_MODULE_GLOBALS(redis) 193 | 194 | 195 | struct redis_queued_item { 196 | 197 | /* reading function */ 198 | zval * (*fun)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ...); 199 | 200 | char *cmd; 201 | int cmd_len; 202 | 203 | struct redis_queued_item *next; 204 | }; 205 | 206 | extern zend_module_entry redis_module_entry; 207 | #define redis_module_ptr &redis_module_entry 208 | 209 | #define phpext_redis_ptr redis_module_ptr 210 | 211 | #define PHP_REDIS_VERSION "2.1.0" 212 | 213 | #endif 214 | 215 | /* 216 | * Local variables: 217 | * tab-width: 4 218 | * c-basic-offset: 4 219 | * End: 220 | * vim600: noet sw=4 ts=4 fdm=marker 221 | * vim<600: noet sw=4 ts=4 222 | */ 223 | -------------------------------------------------------------------------------- /redis_session.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4 -*- */ 2 | /* 3 | +----------------------------------------------------------------------+ 4 | | PHP Version 5 | 5 | +----------------------------------------------------------------------+ 6 | | Copyright (c) 1997-2009 The PHP Group | 7 | +----------------------------------------------------------------------+ 8 | | This source file is subject to version 3.01 of the PHP license, | 9 | | that is bundled with this package in the file LICENSE, and is | 10 | | available through the world-wide-web at the following url: | 11 | | http://www.php.net/license/3_01.txt | 12 | | If you did not receive a copy of the PHP license and are unable to | 13 | | obtain it through the world-wide-web, please send a note to | 14 | | license@php.net so we can mail you a copy immediately. | 15 | +----------------------------------------------------------------------+ 16 | | Original author: Alfonso Jimenez | 17 | | Maintainer: Nicolas Favre-Felix | 18 | | Maintainer: Nasreddine Bouafif | 19 | +----------------------------------------------------------------------+ 20 | */ 21 | 22 | #ifdef HAVE_CONFIG_H 23 | #include "config.h" 24 | #endif 25 | 26 | #include "common.h" 27 | #include "ext/standard/info.h" 28 | #include "php_redis.h" 29 | #include "redis_session.h" 30 | #include 31 | 32 | #include "library.h" 33 | 34 | #include "php.h" 35 | #include "php_ini.h" 36 | #include "php_variables.h" 37 | #include "SAPI.h" 38 | #include "ext/standard/url.h" 39 | 40 | ps_module ps_mod_redis = { 41 | PS_MOD(redis) 42 | }; 43 | 44 | typedef struct redis_pool_member_ { 45 | 46 | RedisSock *redis_sock; 47 | int weight; 48 | 49 | char *prefix; 50 | size_t prefix_len; 51 | 52 | struct redis_pool_member_ *next; 53 | 54 | } redis_pool_member; 55 | 56 | typedef struct { 57 | 58 | int totalWeight; 59 | int count; 60 | 61 | redis_pool_member *head; 62 | 63 | } redis_pool; 64 | 65 | PHPAPI redis_pool* 66 | redis_pool_new(TSRMLS_D) { 67 | return ecalloc(1, sizeof(redis_pool)); 68 | } 69 | 70 | PHPAPI void 71 | redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight, 72 | char *prefix TSRMLS_DC) { 73 | 74 | redis_pool_member *rpm = ecalloc(1, sizeof(redis_pool_member)); 75 | rpm->redis_sock = redis_sock; 76 | rpm->weight = weight; 77 | rpm->prefix = prefix; 78 | rpm->prefix_len = (prefix?strlen(prefix):0); 79 | 80 | rpm->next = pool->head; 81 | pool->head = rpm; 82 | 83 | pool->totalWeight += weight; 84 | } 85 | 86 | PHPAPI void 87 | redis_pool_free(redis_pool *pool TSRMLS_DC) { 88 | 89 | redis_pool_member *rpm, *next; 90 | rpm = pool->head; 91 | while(rpm) { 92 | next = rpm->next; 93 | redis_sock_disconnect(rpm->redis_sock TSRMLS_CC); 94 | efree(rpm->redis_sock); 95 | if(rpm->prefix) efree(rpm->prefix); 96 | efree(rpm); 97 | rpm = next; 98 | } 99 | efree(pool); 100 | } 101 | 102 | PHPAPI redis_pool_member * 103 | redis_pool_get_sock(redis_pool *pool, const char *key TSRMLS_DC) { 104 | 105 | unsigned int pos, i; 106 | memcpy(&pos, key, sizeof(pos)); 107 | pos %= pool->totalWeight; 108 | 109 | redis_pool_member *rpm = pool->head; 110 | 111 | for(i = 0; i < pool->totalWeight;) { 112 | if(pos >= i && pos < i + rpm->weight) { 113 | redis_sock_server_open(rpm->redis_sock, 0 TSRMLS_CC); 114 | return rpm; 115 | } 116 | i += rpm->weight; 117 | rpm = rpm->next; 118 | } 119 | 120 | return NULL; 121 | } 122 | 123 | /* {{{ PS_OPEN_FUNC 124 | */ 125 | PS_OPEN_FUNC(redis) 126 | { 127 | 128 | php_url *url; 129 | zval *params, **param; 130 | int i, j, path_len; 131 | 132 | redis_pool *pool = redis_pool_new(TSRMLS_C); 133 | 134 | for (i=0,j=0,path_len=strlen(save_path); iquery != NULL) { 174 | MAKE_STD_ZVAL(params); 175 | array_init(params); 176 | 177 | sapi_module.treat_data(PARSE_STRING, estrdup(url->query), params TSRMLS_CC); 178 | 179 | if (zend_hash_find(Z_ARRVAL_P(params), "weight", sizeof("weight"), (void **) ¶m) != FAILURE) { 180 | convert_to_long_ex(param); 181 | weight = Z_LVAL_PP(param); 182 | } 183 | 184 | if (zend_hash_find(Z_ARRVAL_P(params), "timeout", sizeof("timeout"), (void **) ¶m) != FAILURE) { 185 | timeout = atof(Z_STRVAL_PP(param)); 186 | } 187 | if (zend_hash_find(Z_ARRVAL_P(params), "persistent", sizeof("persistent"), (void **) ¶m) != FAILURE) { 188 | persistent = (atol(Z_STRVAL_PP(param)) == 1 ? 1 : 0); 189 | } 190 | if (zend_hash_find(Z_ARRVAL_P(params), "prefix", sizeof("prefix"), (void **) ¶m) != FAILURE) { 191 | prefix = estrndup(Z_STRVAL_PP(param), Z_STRLEN_PP(param)); 192 | } 193 | 194 | /* // not supported yet 195 | if (zend_hash_find(Z_ARRVAL_P(params), "retry_interval", sizeof("retry_interval"), (void **) ¶m) != FAILURE) { 196 | convert_to_long_ex(param); 197 | retry_interval = Z_LVAL_PP(param); 198 | } 199 | */ 200 | 201 | zval_ptr_dtor(¶ms); 202 | } 203 | 204 | if ((url->path == NULL && url->host == NULL) || weight <= 0 || timeout <= 0) { 205 | php_url_free(url); 206 | redis_pool_free(pool TSRMLS_CC); 207 | PS_SET_MOD_DATA(NULL); 208 | return FAILURE; 209 | } 210 | 211 | RedisSock *redis_sock; 212 | if(url->path) { /* unix */ 213 | redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, persistent); 214 | } else { 215 | redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, persistent); 216 | } 217 | redis_pool_add(pool, redis_sock, weight, prefix TSRMLS_CC); 218 | 219 | php_url_free(url); 220 | } 221 | } 222 | 223 | if (pool->head) { 224 | PS_SET_MOD_DATA(pool); 225 | return SUCCESS; 226 | } 227 | 228 | return FAILURE; 229 | } 230 | /* }}} */ 231 | 232 | /* {{{ PS_CLOSE_FUNC 233 | */ 234 | PS_CLOSE_FUNC(redis) 235 | { 236 | redis_pool *pool = PS_GET_MOD_DATA(); 237 | 238 | if(pool){ 239 | redis_pool_free(pool TSRMLS_CC); 240 | PS_SET_MOD_DATA(NULL); 241 | } 242 | return SUCCESS; 243 | } 244 | /* }}} */ 245 | 246 | static char * 247 | redis_session_key(redis_pool_member *rpm, const char *key, int key_len, int *session_len) { 248 | 249 | char *session; 250 | char default_prefix[] = "PHPREDIS_SESSION:"; 251 | char *prefix = default_prefix; 252 | size_t prefix_len = sizeof(default_prefix)-1; 253 | 254 | if(rpm->prefix) { 255 | prefix = rpm->prefix; 256 | prefix_len = rpm->prefix_len; 257 | } 258 | 259 | /* build session key */ 260 | *session_len = key_len + prefix_len; 261 | session = emalloc(*session_len); 262 | memcpy(session, prefix, prefix_len); 263 | memcpy(session + prefix_len, key, key_len); 264 | 265 | return session; 266 | } 267 | 268 | 269 | /* {{{ PS_READ_FUNC 270 | */ 271 | PS_READ_FUNC(redis) 272 | { 273 | char *session, *cmd; 274 | int session_len, cmd_len; 275 | 276 | redis_pool *pool = PS_GET_MOD_DATA(); 277 | redis_pool_member *rpm = redis_pool_get_sock(pool, key TSRMLS_CC); 278 | RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; 279 | if(!rpm || !redis_sock){ 280 | return FAILURE; 281 | } 282 | 283 | /* send GET command */ 284 | session = redis_session_key(rpm, key, strlen(key), &session_len); 285 | cmd_len = redis_cmd_format_static(&cmd, "GET", "s", session, session_len); 286 | efree(session); 287 | if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { 288 | efree(cmd); 289 | return FAILURE; 290 | } 291 | efree(cmd); 292 | 293 | /* read response */ 294 | if ((*val = redis_sock_read(redis_sock, vallen TSRMLS_CC)) == NULL) { 295 | return FAILURE; 296 | } 297 | 298 | return SUCCESS; 299 | } 300 | /* }}} */ 301 | 302 | /* {{{ PS_WRITE_FUNC 303 | */ 304 | PS_WRITE_FUNC(redis) 305 | { 306 | char *cmd, *response, *session; 307 | int cmd_len, response_len, session_len; 308 | 309 | redis_pool *pool = PS_GET_MOD_DATA(); 310 | redis_pool_member *rpm = redis_pool_get_sock(pool, key TSRMLS_CC); 311 | RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; 312 | if(!rpm || !redis_sock){ 313 | return FAILURE; 314 | } 315 | 316 | /* send SET command */ 317 | session = redis_session_key(rpm, key, strlen(key), &session_len); 318 | cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sds", session, session_len, INI_INT("session.gc_maxlifetime"), val, vallen); 319 | efree(session); 320 | if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { 321 | efree(cmd); 322 | return FAILURE; 323 | } 324 | efree(cmd); 325 | 326 | /* read response */ 327 | if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { 328 | return FAILURE; 329 | } 330 | 331 | if(response_len == 3 && strncmp(response, "+OK", 3) == 0) { 332 | efree(response); 333 | return SUCCESS; 334 | } else { 335 | efree(response); 336 | return FAILURE; 337 | } 338 | } 339 | /* }}} */ 340 | 341 | /* {{{ PS_DESTROY_FUNC 342 | */ 343 | PS_DESTROY_FUNC(redis) 344 | { 345 | char *cmd, *response, *session; 346 | int cmd_len, response_len, session_len; 347 | 348 | redis_pool *pool = PS_GET_MOD_DATA(); 349 | redis_pool_member *rpm = redis_pool_get_sock(pool, key TSRMLS_CC); 350 | RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; 351 | if(!rpm || !redis_sock){ 352 | return FAILURE; 353 | } 354 | 355 | /* send DEL command */ 356 | session = redis_session_key(rpm, key, strlen(key), &session_len); 357 | cmd_len = redis_cmd_format_static(&cmd, "DEL", "s", session, session_len); 358 | efree(session); 359 | if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { 360 | efree(cmd); 361 | return FAILURE; 362 | } 363 | efree(cmd); 364 | 365 | /* read response */ 366 | if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { 367 | return FAILURE; 368 | } 369 | 370 | if(response_len == 2 && response[0] == ':' && (response[1] == '0' || response[1] == '1')) { 371 | efree(response); 372 | return SUCCESS; 373 | } else { 374 | efree(response); 375 | return FAILURE; 376 | } 377 | } 378 | /* }}} */ 379 | 380 | /* {{{ PS_GC_FUNC 381 | */ 382 | PS_GC_FUNC(redis) 383 | { 384 | return SUCCESS; 385 | } 386 | /* }}} */ 387 | 388 | /* vim: set tabstop=4 expandtab: */ 389 | 390 | -------------------------------------------------------------------------------- /redis_session.h: -------------------------------------------------------------------------------- 1 | #ifndef REDIS_SESSION_H 2 | #define REDIS_SESSION_H 3 | 4 | #include "ext/session/php_session.h" 5 | 6 | PS_OPEN_FUNC(redis); 7 | PS_CLOSE_FUNC(redis); 8 | PS_READ_FUNC(redis); 9 | PS_WRITE_FUNC(redis); 10 | PS_DESTROY_FUNC(redis); 11 | PS_GC_FUNC(redis); 12 | 13 | 14 | #endif 15 | 16 | -------------------------------------------------------------------------------- /serialize.list: -------------------------------------------------------------------------------- 1 | This file lists which methods support serialization. Only indented methods have been ported. 2 | 3 | get 4 | set 5 | setex 6 | setnx 7 | getSet 8 | getMultiple 9 | append 10 | substr 11 | strlen 12 | lPush 13 | lPushx 14 | rPush 15 | rPushx 16 | lPop 17 | rPop 18 | blPop 19 | brPop 20 | lRemove 21 | lGet 22 | lGetRange 23 | lSet 24 | lInsert 25 | 26 | sAdd 27 | sRemove 28 | sMove 29 | sContains 30 | 31 | zAdd 32 | zDelete 33 | zScore 34 | zRank 35 | zRevRank 36 | zIncrBy 37 | 38 | mset 39 | 40 | hGet 41 | hSet 42 | hGetAll 43 | hExists 44 | hMset 45 | hMget 46 | 47 | publish 48 | subscribe 49 | unsubscribe 50 | -------------------------------------------------------------------------------- /tests/commandsStatus.txt: -------------------------------------------------------------------------------- 1 | 2 | Status : {MULTI, PIPELINE} 3 | Command : Status 4 | example 5 | get : {1, 1} 6 | 7 | 8 | * get {0, 1} 9 | * set {0, 1} 10 | * getset {0, 1} 11 | * incr {0, 1} 12 | * incrby {0, 1} 13 | * decr {0, 1} 14 | * decrby {0, 1} 15 | * rename {0, 1} 16 | * renameNx {0, 1} 17 | * ttl {0, 1} 18 | * ping {0, 1} 19 | * setnx {0, 1} 20 | * exists {0, 1} 21 | * randomkey {0, 1} 22 | * del {0, 1} 23 | * mget {0, 1} 24 | * mset {0, 0} 25 | * msetnx {0, 0} 26 | * expire {0, 1} 27 | * expireat {0, 0} 28 | * info 29 | * multi 30 | * exec 31 | * discard 32 | * auth 33 | * select 34 | * keys 35 | * dbsize 36 | * lastsave 37 | * flushdb 38 | * flushall 39 | * save 40 | * bgsave 41 | * bgrewriteaof 42 | * move 43 | * sort 44 | * type 45 | * append 46 | * substr 47 | * config (get and set, not resetstat) 48 | 49 | * lpush {0, 1} 50 | * rpush {0, 1} 51 | * rpoplpush {0, 1} 52 | * llen {0, 1} 53 | * lpop {0, 1} 54 | * rpop {0, 1} 55 | * blpop 56 | * brpop 57 | * ltrim {0, 1} 58 | * lindex {0, 1} 59 | * lrem {0, 1} 60 | * lset {0, 1} 61 | * lrange {0, 1} 62 | 63 | * sadd 64 | * srem 65 | * sismember 66 | * scard 67 | * spop 68 | * srandmember 69 | * smove 70 | * sinter 71 | * sunion 72 | * sdiff 73 | * sinterstore 74 | * sunionstore 75 | * sdiffstore 76 | 77 | * zadd 78 | * zincrby 79 | * zrem 80 | * zscore 81 | * zrank 82 | * zrevrank 83 | * zrange 84 | * zrevrange 85 | * zcard 86 | * zcount 87 | * zremrangebyrank 88 | * zrangebyscore 89 | * zremrangebyscore 90 | * zunion 91 | * zinter 92 | 93 | * hset 94 | * hget 95 | * hdel 96 | * hexists 97 | * hlen 98 | * hkeys 99 | * hvals 100 | * hgetall 101 | * hincrby 102 | 103 | Functions not implemented 104 | 105 | * subscribe 106 | * unsubscribe 107 | * publish 108 | -------------------------------------------------------------------------------- /tests/executeTests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | phpunit Redis_Test ./TestRedis.php 4 | --------------------------------------------------------------------------------