├── CREDITS ├── docker ├── host.conf └── start.sh ├── config.m4 ├── tests ├── 005.phpt ├── 058.phpt ├── memcache.sh ├── 016.phpt ├── 008.phpt ├── 013.phpt ├── pecl16536.phpt ├── pecl16442.phpt ├── 028.phpt ├── pecl17566.phpt ├── 018.phpt ├── 014.phpt ├── pecl77900.phpt ├── 017.phpt ├── 006.phpt ├── githubbug13.phpt ├── 015.phpt ├── 007.phpt ├── 037.phpt ├── 012.phpt ├── 004.phpt ├── pecl11221.phpt ├── 047a.phpt ├── 054.phpt ├── githubbug53.phpt ├── 009.phpt ├── 030.phpt ├── 011.phpt ├── 051.phpt ├── 010.phpt ├── 052.phpt ├── 026.phpt ├── pecl17518.phpt ├── 049.phpt ├── 050.phpt ├── pecl63142.phpt ├── 048.phpt ├── 035.phpt ├── 044b.phpt ├── 055.phpt ├── 039.phpt ├── 001b.phpt ├── 001.phpt ├── pecl63272.phpt ├── 019a.phpt ├── 038.phpt ├── 027.phpt ├── 019b.phpt ├── 024.phpt ├── 045.phpt ├── 100.phpt ├── 053.phpt ├── 022.phpt ├── 021.phpt ├── 041.phpt ├── bug73539.phpt ├── 042.phpt ├── 057.phpt ├── 003.phpt ├── 100a.phpt ├── 025.phpt ├── 100bphpt ├── 022a.phpt ├── 022b.phpt ├── 020.phpt ├── 100c.phpt ├── 002.phpt ├── 024a.phpt ├── 024b.phpt ├── 029.phpt ├── 040.phpt ├── 047.phpt ├── 036b.phpt ├── 036.phpt ├── redundancy_test.phpt ├── 056.phpt ├── 034.phpt ├── 019.phpt ├── 046.phpt ├── 043.phpt ├── 023.phpt ├── 044.phpt ├── 027a.phpt ├── 027b.phpt ├── 032.phpt ├── 031.phpt ├── connect.inc └── 033.phpt ├── Vagrantfile ├── Dockerfile ├── example.php ├── .gitignore ├── composer.json ├── config.w32 ├── .github └── workflows │ └── config.yml ├── src ├── memcache_queue.h ├── memcache_standard_hash.c ├── memcache_queue.c ├── php_memcache.h ├── memcache_consistent_hash.c ├── memcache_ascii_protocol.c ├── memcache_session.c └── memcache_pool.h ├── LICENSE ├── config9.m4 ├── README └── profile.php /CREDITS: -------------------------------------------------------------------------------- 1 | Antony Dovgal, Mikael Johansson 2 | -------------------------------------------------------------------------------- /docker/host.conf: -------------------------------------------------------------------------------- 1 | order hosts,bind 2 | multi off 3 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id$ 2 | dnl this file is required by phpize 3 | 4 | sinclude(config9.m4) 5 | -------------------------------------------------------------------------------- /tests/005.phpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/websupport-sk/pecl-memcache/HEAD/tests/005.phpt -------------------------------------------------------------------------------- /tests/058.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->findServer() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | findServer('test_key'); 11 | var_dump($result); 12 | 13 | ?> 14 | --EXPECTF-- 15 | string(%d) "%s:%d" -------------------------------------------------------------------------------- /tests/memcache.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # basic script to launch memcached for testing 4 | memcached -p11211 -U11211 -d 5 | memcached -p11212 -U11212 -d 6 | 7 | # if this fails, make sure your user has permissions 8 | # to write to /var/run/memcached 9 | memcached -s /var/run/memcached/memcached.sock -d 10 | -------------------------------------------------------------------------------- /tests/016.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->delete() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | set('test', 'test'); 11 | 12 | $memcache->delete('test', 10); 13 | 14 | /* the item should be still there */ 15 | $memcache->delete('test'); 16 | 17 | echo "Done\n"; 18 | 19 | ?> 20 | --EXPECT-- 21 | Done 22 | -------------------------------------------------------------------------------- /tests/008.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache_delete() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 20 | --EXPECT-- 21 | Done 22 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | VAGRANTFILE_API_VERSION = '2' 5 | 6 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 7 | config.vm.box = 'ubuntu/bionic64' 8 | 9 | config.vm.provider :virtualbox do |vb| 10 | vb.name = 'ext-memcache-dev' 11 | vb.memory = 1024 12 | vb.cpus = 2 13 | end 14 | 15 | config.vm.provision 'docker' 16 | 17 | end 18 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG PHP_IMAGE=php:8.2 2 | FROM $PHP_IMAGE 3 | 4 | RUN docker-php-ext-configure pcntl --enable-pcntl \ 5 | && docker-php-ext-install -j$(nproc) pcntl 6 | 7 | RUN apt-get update && apt-get install -y \ 8 | git \ 9 | zlib1g-dev \ 10 | memcached ; 11 | 12 | COPY docker/host.conf /etc/host.conf 13 | 14 | # ENV LOCAL_DEV 1 15 | # ADD . /usr/src/pecl-memcache 16 | COPY docker/start.sh / 17 | CMD ["/start.sh"] 18 | -------------------------------------------------------------------------------- /tests/013.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->set() & memcache->get() with strange keys 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | set($key, $var, false, 10); 14 | $result = $memcache->get($key); 15 | 16 | var_dump($result); 17 | 18 | ?> 19 | --EXPECT-- 20 | string(4) "test" 21 | -------------------------------------------------------------------------------- /tests/pecl16536.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | PECL bug #16536 (Weight of 0 causes SegFault on memcache_add_server) 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | addServer($host, $port, true, 0)); 12 | var_dump(@$mmc->set('TEST_KEY', 'test_value')); 13 | 14 | echo "Done\n"; 15 | ?> 16 | --EXPECTF-- 17 | bool(false) 18 | bool(false) 19 | Done 20 | -------------------------------------------------------------------------------- /tests/pecl16442.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | PECL bug #16442 (memcache_set fail with integer value) 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 16 | --EXPECTF-- 17 | int(1) 18 | -------------------------------------------------------------------------------- /tests/028.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->addServer() and memcache->close() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | addServer($host, $port, true, 50); 15 | $result2 = $memcache2->close(); 16 | 17 | var_dump($result1); 18 | var_dump($result2); 19 | 20 | ?> 21 | --EXPECTF-- 22 | bool(true) 23 | bool(true) 24 | -------------------------------------------------------------------------------- /tests/pecl17566.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | PECL bug #17566 (3.0.4 cache delete bug) 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | connect($host, $port); 12 | $m1->set("test", "123"); 13 | $m1->delete("test"); 14 | 15 | $m2 = new Memcache; 16 | $m2->connect($host, $port); 17 | var_dump($m2->get("test")); 18 | 19 | echo "Done\n"; 20 | ?> 21 | --EXPECTF-- 22 | bool(false) 23 | Done 24 | -------------------------------------------------------------------------------- /tests/018.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache_set() & memcache_add() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 20 | --EXPECT-- 21 | bool(true) 22 | bool(false) 23 | -------------------------------------------------------------------------------- /tests/014.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->increment() & memcache->decrement() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | set('new_test', 5); 11 | 12 | $result1 = $memcache->increment('new_test'); 13 | $result2 = $memcache->decrement('new_test'); 14 | $result3 = $memcache->get('new_test'); 15 | 16 | var_dump($result1); 17 | var_dump($result2); 18 | var_dump($result3); 19 | 20 | ?> 21 | --EXPECT-- 22 | int(6) 23 | int(5) 24 | int(5) 25 | -------------------------------------------------------------------------------- /tests/pecl77900.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | PECL Bug #77900 (Memcache session handling, zend_mm_heap corrupted) 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 20 | bool(true) 21 | } -------------------------------------------------------------------------------- /tests/017.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Memcache class should be inheritable 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | connect($host, $port); 18 | $t->foo(); 19 | 20 | var_dump($t); 21 | 22 | echo "Done\n"; 23 | ?> 24 | --EXPECTF-- 25 | foo 26 | object(test)%s%d) { 27 | ["connection"]=> 28 | resource(%d) of type (memcache connection) 29 | } 30 | Done 31 | -------------------------------------------------------------------------------- /tests/006.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache_increment() & memcache_decrement() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 21 | --EXPECT-- 22 | int(6) 23 | int(5) 24 | int(5) 25 | -------------------------------------------------------------------------------- /tests/githubbug13.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | session_regenerate_id() should not cause fatal error 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 17 | --EXPECTF-- 18 | bool(true) -------------------------------------------------------------------------------- /tests/015.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->getVersion() & memcache->getStats() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | getVersion(); 11 | var_dump($version); 12 | 13 | $stats = $memcache->getStats(); 14 | if (ini_get('memcache.protocol') == 'binary') { 15 | var_dump($stats === false); 16 | var_dump(1); 17 | } 18 | else { 19 | var_dump(count($stats) > 10); 20 | var_dump(count($stats)); 21 | } 22 | 23 | ?> 24 | --EXPECTF-- 25 | string(%d) "%s" 26 | bool(true) 27 | int(%d) 28 | -------------------------------------------------------------------------------- /tests/007.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache_get_version() & memcache_get_stats() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 10); 20 | var_dump(count($stats)); 21 | } 22 | 23 | ?> 24 | --EXPECTF-- 25 | string(%d) "%s" 26 | bool(true) 27 | int(%d) 28 | -------------------------------------------------------------------------------- /tests/037.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ini_set('memcache.hash_strategy') 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 20 | --EXPECTF-- 21 | string(8) "standard" 22 | string(10) "consistent" 23 | string(10) "consistent" -------------------------------------------------------------------------------- /example.php: -------------------------------------------------------------------------------- 1 | set("str_key", "String to store in memcached"); 7 | $memcache->set("num_key", 123); 8 | 9 | $object = new StdClass; 10 | $object->attribute = 'test'; 11 | $memcache->set("obj_key", $object); 12 | 13 | $array = Array('assoc'=>123, 345, 567); 14 | $memcache->set("arr_key", $array); 15 | 16 | var_dump($memcache->get('str_key')); 17 | var_dump($memcache->get('num_key')); 18 | var_dump($memcache->get('obj_key')); 19 | } 20 | else { 21 | echo "Connection to memcached failed"; 22 | } 23 | ?> 24 | 25 | -------------------------------------------------------------------------------- /tests/012.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache_add() & memcache_replace() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | plain_attribute = 'value'; 12 | $var->array_attribute = Array('test1', 'test2'); 13 | 14 | $result1 = $memcache->add('non_existing_test_key2', $var, false, 2); 15 | $result2 = $memcache->replace('non_existing_test_key2', $var, false, 10); 16 | 17 | var_dump($result1); 18 | var_dump($result2); 19 | 20 | $memcache->delete('non_existing_test_key2'); 21 | 22 | ?> 23 | --EXPECT-- 24 | bool(true) 25 | bool(true) 26 | -------------------------------------------------------------------------------- /tests/004.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache_add() & memcache_replace() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | plain_attribute = 'value'; 12 | $var->array_attribute = Array('test1', 'test2'); 13 | 14 | $result1 = memcache_add($memcache, 'non_existing_test_key', $var, false, 1); 15 | $result2 = memcache_replace($memcache, 'non_existing_test_key', $var, false, 10); 16 | 17 | var_dump($result1); 18 | var_dump($result2); 19 | 20 | memcache_delete($memcache, 'non_existing_test_key'); 21 | 22 | ?> 23 | --EXPECT-- 24 | bool(true) 25 | bool(true) 26 | -------------------------------------------------------------------------------- /tests/pecl11221.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | PECL bug #11221 (Double free when returning cached object with __sleep) 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | set('block', $block)); 20 | 21 | return $block; 22 | } 23 | 24 | $cache = new memcache; 25 | $cache->connect($host, $port); 26 | 27 | cache_failure(); 28 | 29 | echo "Done\n"; 30 | ?> 31 | --EXPECTF-- 32 | bool(true) 33 | Done 34 | -------------------------------------------------------------------------------- /tests/047a.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->get() with flags (PHP 5 only) 3 | --SKIPIF-- 4 | 5 | =')) die('skip requires PHP 5 '); ?> 6 | --FILE-- 7 | set('test_key1', 'test1', $flag1); 14 | 15 | $result1 = $memcache->get('test_key1', null); 16 | var_dump($result1); 17 | 18 | // Test procedural 19 | $result1 = memcache_get($memcache, 'test_key1', null); 20 | var_dump($result1); 21 | 22 | ?> 23 | --EXPECT-- 24 | string(5) "test1" 25 | string(5) "test1" 26 | -------------------------------------------------------------------------------- /tests/054.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->set() with value larger than MTU 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | setCompressThreshold(1000000, 1.0); 12 | 13 | $value = str_repeat('a', 64 * 1024); 14 | $result = $memcache->set('test_key', $value, 0, 2); 15 | var_dump($result); 16 | 17 | $result2 = $memcache->get('test_key'); 18 | var_dump(strlen($result2)); 19 | 20 | ?> 21 | --EXPECTF-- 22 | bool(true) 23 | int(65536) -------------------------------------------------------------------------------- /tests/githubbug53.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Module shouldn't crash on failed serialization 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | set('foobar', $oFoo); 17 | 18 | --EXPECTF-- 19 | Warning: MemcachePool::set(): Failed to serialize value in %s on line %d 20 | 21 | Fatal error: Uncaught Exception: fail in %s:%d 22 | Stack trace: 23 | #0 [internal function]: foo->__sleep() 24 | #1 %s(%d): MemcachePool->set('foobar', Object(foo)) 25 | #2 {main} 26 | thrown in %s on line %d 27 | -------------------------------------------------------------------------------- /tests/009.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->set() method 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | plain_attribute = 'value'; 12 | $var->array_attribute = Array('test1', 'test2'); 13 | 14 | $result = $memcache->set('test_key', $var, false, 10); 15 | var_dump($result); 16 | 17 | // Should generate a "SERVER_ERROR: out of memory" 18 | $var = str_repeat('a', 1500000); 19 | $memcache->setCompressThreshold(0); 20 | $result = @$memcache->set('test_key', $var, false, 10); 21 | var_dump($result); 22 | 23 | echo "Done\n"; 24 | 25 | ?> 26 | --EXPECT-- 27 | bool(true) 28 | bool(false) 29 | Done 30 | -------------------------------------------------------------------------------- /tests/030.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ini_set("memcache.chunk_size") 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 26 | --EXPECTF-- 27 | string(%d) "%d" 28 | string(5) "16384" 29 | string(5) "32768" 30 | bool(false) 31 | bool(false) 32 | -------------------------------------------------------------------------------- /tests/011.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->set()/memcache->get() using compression 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | plain_attribute = 'value'; 12 | $var->array_attribute = Array('test1', 'test2'); 13 | 14 | $memcache->set('test_key', $var, MEMCACHE_COMPRESSED, 10); 15 | 16 | $result = $memcache->get('test_key'); 17 | 18 | var_dump($result); 19 | 20 | ?> 21 | --EXPECTF-- 22 | object(stdClass)%s2) { 23 | ["plain_attribute"]=> 24 | string(5) "value" 25 | ["array_attribute"]=> 26 | array(2) { 27 | [0]=> 28 | string(5) "test1" 29 | [1]=> 30 | string(5) "test2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/051.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->set() with duplicate keys 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | set('test_key', array('test')); 11 | var_dump($result); 12 | 13 | $result = $memcache->set('test_key2', 'test2'); 14 | var_dump($result); 15 | 16 | $result = $memcache->get(array('test_key', 'test_key')); 17 | var_dump($result); 18 | 19 | $result = $memcache->get(array('test_key2', 'test_key2')); 20 | var_dump($result); 21 | 22 | ?> 23 | --EXPECT-- 24 | bool(true) 25 | bool(true) 26 | array(1) { 27 | ["test_key"]=> 28 | array(1) { 29 | [0]=> 30 | string(4) "test" 31 | } 32 | } 33 | array(1) { 34 | ["test_key2"]=> 35 | string(5) "test2" 36 | } 37 | -------------------------------------------------------------------------------- /tests/010.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->get() function 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | plain_attribute = 'value'; 12 | $value->array_attribute = array('test1', 'test2'); 13 | $memcache->set('test_key', $value); 14 | 15 | $result = $memcache->get('test_key'); 16 | var_dump($result); 17 | 18 | $result = $memcache->get(array('unset_test_key', 'unset_test_key1')); 19 | var_dump($result); 20 | 21 | ?> 22 | --EXPECTF-- 23 | object(stdClass)%s2) { 24 | ["plain_attribute"]=> 25 | string(5) "value" 26 | ["array_attribute"]=> 27 | array(2) { 28 | [0]=> 29 | string(5) "test1" 30 | [1]=> 31 | string(5) "test2" 32 | } 33 | } 34 | array(0) { 35 | } 36 | -------------------------------------------------------------------------------- /tests/052.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->connect() and memcache->close() in loop 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | connect($host, $port); 15 | 16 | $key = 'test_key'; 17 | $memcache->set($key, 'test'); 18 | $memcache->close(); 19 | 20 | for ($i=0; $i<$count; $i++) { 21 | $memcache->connect($host, $port); 22 | $result = $memcache->get($key); 23 | 24 | if (!$result) { 25 | printf('Failed to fetch value for iteration %d', $i); 26 | } 27 | 28 | $memcache->close(); 29 | } 30 | 31 | $end = time(); 32 | if (($end - $start) < 2) { 33 | echo "true"; 34 | } else { 35 | echo "false"; 36 | } 37 | 38 | ?> 39 | --EXPECT-- 40 | true 41 | -------------------------------------------------------------------------------- /tests/026.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->delete() with load balancing 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | addServer($host2, $port2); 12 | 13 | $result1 = $memcache->set('delete_fail_key1', $var, false, 1); 14 | $result2 = $memcache->delete('delete_fail_key1'); 15 | $result3 = $memcache->delete('delete_fail_key2'); 16 | 17 | var_dump($result1); 18 | var_dump($result2); 19 | var_dump($result3); 20 | 21 | $memcache = new Memcache(); 22 | $memcache->addServer($nonExistingHost, $nonExistingPort); 23 | $result4 = @$memcache->delete('delete_fail_key1'); 24 | 25 | var_dump($result4); 26 | 27 | ?> 28 | --EXPECT-- 29 | bool(true) 30 | bool(true) 31 | bool(false) 32 | bool(false) 33 | -------------------------------------------------------------------------------- /tests/pecl17518.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | PECL bug #17518 (Strange behavior in increment on non integer and after) 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | connect('localhost', 11211); 12 | 13 | $m->delete('a'); 14 | $m->delete('b'); 15 | $m->delete('c'); 16 | 17 | var_dump($m->increment('a', 42)); 18 | var_dump($m->get('a')); 19 | 20 | $m->set('b', 'bar'); 21 | var_dump($m->increment('b', 42)); 22 | var_dump($m->get('b')); 23 | 24 | $m->set('c', 1); 25 | var_dump($m->increment('c', 42)); 26 | var_dump($m->get('c')); 27 | 28 | --EXPECTF-- 29 | bool(false) 30 | bool(false) 31 | 32 | Notice: MemcachePool::increment(): Server localhost (tcp 11211, udp 0) failed with: %s (%d) in %s 33 | bool(false) 34 | string(3) "bar" 35 | int(43) 36 | int(43) 37 | -------------------------------------------------------------------------------- /tests/049.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->append(), memcache->prepend() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | set('test_key', 'a'); 11 | $result2 = $memcache->get('test_key'); 12 | var_dump($result1); 13 | var_dump($result2); 14 | 15 | $result1 = $memcache->append('test_key', 'b'); 16 | $result2 = $memcache->get('test_key'); 17 | var_dump($result1); 18 | var_dump($result2); 19 | 20 | $result1 = $memcache->prepend('test_key', 'c'); 21 | $result2 = $memcache->get('test_key'); 22 | var_dump($result1); 23 | var_dump($result2); 24 | 25 | ?> 26 | --EXPECT-- 27 | bool(true) 28 | string(1) "a" 29 | bool(true) 30 | string(2) "ab" 31 | bool(true) 32 | string(3) "cab" -------------------------------------------------------------------------------- /tests/050.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | failure callback throws exception 3 | --SKIPIF-- 4 | = 5.0.0'); ?> 5 | --FILE-- 6 | addServer($nonExistingHost, $nonExistingPort, false, 1, 1, 15, true, '_callback_server_failure'); 17 | $result = 'Test1'; 18 | 19 | try { 20 | $result = @$memcache->set('test_key', 'test-032-01'); 21 | } 22 | catch (Exception $e) { 23 | var_dump($e->getMessage()); 24 | } 25 | 26 | var_dump($result); 27 | 28 | ?> 29 | --EXPECTF-- 30 | string(%d) "%s" 31 | string(5) "Test2" 32 | string(5) "Test1" 33 | -------------------------------------------------------------------------------- /tests/pecl63142.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | PECL bug #63142 (memcache 3.0.7 segfaults with object (un)serialization) 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ;fix me later 7 | report_memleaks=0 8 | --FILE-- 9 | obj = $obj; 15 | $memcache = new Memcache; 16 | $memcache->connect('127.0.0.1', 11211); 17 | $memcache->set('x', $obj, false, 300); 18 | $x = $memcache->get('x'); echo "."; 19 | $x = $memcache->get('x'); echo "."; 20 | $x = $memcache->get('x'); echo "."; 21 | $x = $memcache->get('x'); echo "."; 22 | $x = $memcache->get('x'); echo "."; 23 | $x = $memcache->get('x'); echo "."; 24 | $x = $memcache->get('x'); echo "."; 25 | $x = $memcache->get('x'); echo "."; 26 | $x = $memcache->get('x'); echo ".\n"; 27 | 28 | echo "Done\n"; 29 | 30 | ?> 31 | --EXPECTF-- 32 | ......... 33 | Done 34 | -------------------------------------------------------------------------------- /tests/048.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->get(), set() with CAS 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | set('test_key', 'a'); 11 | var_dump($result); 12 | 13 | $cas = null; 14 | $flags = null; 15 | $result = $memcache->get('test_key', $flags, $cas); 16 | var_dump($result); 17 | var_dump($cas); 18 | 19 | $result1 = $memcache->cas('test_key', 'b', 0, 0, $cas + 1); 20 | $result2 = $memcache->get('test_key'); 21 | var_dump($result1); 22 | var_dump($result2); 23 | 24 | $result1 = $memcache->cas('test_key', 'b', 0, 0, $cas); 25 | $result2 = $memcache->get('test_key'); 26 | var_dump($result1); 27 | var_dump($result2); 28 | 29 | ?> 30 | --EXPECTF-- 31 | bool(true) 32 | string(1) "a" 33 | int(%s%d) 34 | bool(false) 35 | string(1) "a" 36 | bool(true) 37 | string(1) "b" 38 | -------------------------------------------------------------------------------- /tests/035.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache::connect() with unix domain socket 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | addServer($domainsocket, 0); 12 | var_dump($result); 13 | 14 | $result1 = $memcache->set('test_key', 'abc'); 15 | $result2 = $memcache->get('test_key'); 16 | var_dump($result1); 17 | var_dump($result2); 18 | 19 | $memcache = new Memcache(); 20 | $result = $memcache->connect($domainsocket, 0); 21 | var_dump($result); 22 | 23 | $memcache = memcache_connect($domainsocket, null); 24 | var_dump($result); 25 | 26 | ?> 27 | --EXPECTF-- 28 | bool(true) 29 | bool(true) 30 | string(3) "abc" 31 | bool(true) 32 | bool(true) -------------------------------------------------------------------------------- /tests/044b.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ini_set('memcache.session_redundancy') 3 | --SKIPIF-- 4 | 7 | 8 | --FILE-- 9 | delete($balanceKey1); 19 | $memcache2->delete($balanceKey1); 20 | 21 | // Test set 22 | session_id($balanceKey1); 23 | session_start(); 24 | $_SESSION['key'] = 'Test1'; 25 | session_write_close(); 26 | 27 | $result1 = $memcache1->get($balanceKey1); 28 | $result2 = $memcache2->get($balanceKey1); 29 | var_dump($result1); 30 | var_dump($result2); 31 | ?> 32 | --EXPECTF-- 33 | string(16) "key%sTest1%s" 34 | string(16) "key%sTest1%s" 35 | -------------------------------------------------------------------------------- /tests/055.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->set()/get() datatype preservation 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | set('test_key', '1'); 11 | $result = $memcache->get('test_key'); 12 | var_dump($result); 13 | 14 | $memcache->set('test_key', 1); 15 | $result = $memcache->get('test_key'); 16 | var_dump($result); 17 | 18 | $memcache->set('test_key', true); 19 | $result = $memcache->get('test_key'); 20 | var_dump($result); 21 | 22 | $memcache->set('test_key', false); 23 | $result = $memcache->get('test_key'); 24 | var_dump($result); 25 | 26 | $memcache->set('test_key', 1.1); 27 | $result = $memcache->get('test_key'); 28 | var_dump($result); 29 | 30 | $memcache->set('test_key', '1', 0x10); 31 | 32 | ?> 33 | --EXPECTF-- 34 | string(1) "1" 35 | int(1) 36 | bool(true) 37 | bool(false) 38 | float(1.1) 39 | 40 | Warning: MemcachePool::set(): The lowest two bytes of the flags %s -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | 34 | # Tests-generated stuff 35 | tests/*.diff 36 | tests/*.php 37 | tests/*.out 38 | tests/*.sh 39 | tests/*.log 40 | tests/*.exp 41 | 42 | *.fragments 43 | Makefile* 44 | autom4te* 45 | aclocal* 46 | .libs/ 47 | acinclude.m4 48 | build/ 49 | config.guess 50 | config.h 51 | config.h.in 52 | config.log 53 | config.nice 54 | config.status 55 | config.sub 56 | configure 57 | configure.in 58 | install-sh 59 | libtool 60 | ltmain.sh 61 | missing 62 | mkinstalldirs 63 | run-tests.php 64 | fails.log 65 | configure.ac 66 | *.code-workspace 67 | .vscode/* 68 | .idea/ -------------------------------------------------------------------------------- /tests/039.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->get() over multi-datagram UDP 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | set($key.$i, $key.'_value_'.$i, 0, 10); 16 | $keys[] = $key.$i; 17 | } 18 | 19 | $memcacheudp = new MemcachePool(); 20 | $memcacheudp->addServer($host, $nonExistingPort, $udpPort); 21 | 22 | $result1 = $memcacheudp->get($keys); 23 | ksort($result1); 24 | 25 | var_dump(count($result1)); 26 | var_dump(count($result1) == count($keys)); 27 | var_dump(reset($result1)); 28 | 29 | $result2 = array_keys($result1); 30 | var_dump(reset($result2)); 31 | 32 | ?> 33 | --EXPECTF-- 34 | int(%d) 35 | bool(true) 36 | string(16) "test_key_value_0" 37 | string(9) "test_key0" -------------------------------------------------------------------------------- /docker/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export CFLAGS="-fstack-protector-strong -O2" 4 | export CPPFLAGS="${CFLAGS}" 5 | export LDFLAGS="-Wl,-O1 -Wl,--hash-style=both" 6 | 7 | # Build extension 8 | set -eux 9 | cd /usr/src 10 | 11 | git clone https://github.com/websupport-sk/pecl-memcache.git 12 | 13 | cd pecl-memcache; 14 | phpize 15 | ./configure 16 | make -j"$(nproc)" 17 | 18 | # Spawn memcached for tests 19 | echo "Starting memcached... " 20 | mkdir -p /var/run/memcached 21 | chown memcache:memcache /var/run/memcached 22 | /usr/bin/memcached -m 64 -u memcache -s /var/run/memcached/memcached.sock -d 23 | /usr/bin/memcached -m 64 -u memcache -U 11211 -l 127.0.0.1 -p 11211 -d 24 | /usr/bin/memcached -m 64 -u memcache -U 11212 -l 127.0.0.1 -p 11212 -d 25 | 26 | # Let's start tests 27 | cd /usr/src/pecl-memcache 28 | export NO_INTERACTION=1 29 | export TEST_PHP_ARGS="--show-diff --keep-all -w fails.log" 30 | 31 | MEMCACHE_PROTOCOL=ascii make test 32 | MEMCACHE_PROTOCOL=binary make test 33 | -------------------------------------------------------------------------------- /tests/001b.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache_set() function 3 | --SKIPIF-- 4 | = 80000) 6 | die('skip php prior to 8 only'); 7 | include 'connect.inc'; 8 | --FILE-- 9 | plain_attribute = 'value'; 15 | $value->array_attribute = array('test1', 'test2'); 16 | memcache_set($memcache, 'test_key', $value, false, 10); 17 | 18 | $key = 123; 19 | $value = 123; 20 | memcache_set($memcache, $key, $value, false, 10); 21 | 22 | var_dump($key); 23 | var_dump($value); 24 | 25 | try { 26 | memcache_set($memcache, $key); 27 | } catch (ArgumentCountError $e) { 28 | echo "{$e->getMessage()}\n"; 29 | } 30 | 31 | try { 32 | $memcache->set($key); 33 | } catch (ArgumentCountError $e) { 34 | echo "{$e->getMessage()}\n"; 35 | } 36 | 37 | echo "Done\n"; 38 | 39 | ?> 40 | --EXPECTF-- 41 | int(123) 42 | int(123) 43 | 44 | Warning: %s parameter%s 45 | 46 | Warning: %s parameter%s 47 | Done -------------------------------------------------------------------------------- /tests/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache_set() function 3 | --SKIPIF-- 4 | plain_attribute = 'value'; 15 | $value->array_attribute = array('test1', 'test2'); 16 | memcache_set($memcache, 'test_key', $value, false, 10); 17 | 18 | $key = 123; 19 | $value = 123; 20 | memcache_set($memcache, $key, $value, false, 10); 21 | 22 | var_dump($key); 23 | var_dump($value); 24 | 25 | try { 26 | memcache_set($memcache, $key); 27 | } catch (ArgumentCountError $e) { 28 | echo "{$e->getMessage()}\n"; 29 | } 30 | 31 | try { 32 | $memcache->set($key); 33 | } catch (ArgumentCountError $e) { 34 | echo "{$e->getMessage()}\n"; 35 | } 36 | 37 | echo "Done\n"; 38 | 39 | ?> 40 | --EXPECTF-- 41 | int(123) 42 | int(123) 43 | Wrong parameter count for memcache_set() 44 | Wrong parameter count for MemcachePool::set() 45 | Done -------------------------------------------------------------------------------- /tests/pecl63272.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | PECL bug #63272 (Explicitly reserve range of flags in php_memcache.h so application code can use) 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | addServer($host, $port); 19 | 20 | foreach ($user_flags as $user_flag) { 21 | $m->set("testkey", "testvalue", $user_flag); 22 | $m->set("testkey_compressed", "testvalue", $user_flag | MEMCACHE_COMPRESSED); 23 | 24 | $getflags = 0; 25 | var_dump($m->get("testkey", $getflags)); 26 | var_dump($m->get("testkey_compressed", $getflags)); 27 | } 28 | 29 | echo "Done\n"; 30 | ?> 31 | --EXPECT-- 32 | string(9) "testvalue" 33 | string(9) "testvalue" 34 | string(9) "testvalue" 35 | string(9) "testvalue" 36 | string(9) "testvalue" 37 | string(9) "testvalue" 38 | string(9) "testvalue" 39 | string(9) "testvalue" 40 | Done 41 | -------------------------------------------------------------------------------- /tests/019a.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache_add_server() 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | getMessage()}\n"; 20 | } 21 | 22 | try { 23 | var_dump(memcache_add_server($memcache, new stdclass, array())); 24 | } catch (TypeError $e) { 25 | echo "{$e->getMessage()}\n"; 26 | } 27 | 28 | echo "Done\n"; 29 | 30 | ?> 31 | --EXPECTF-- 32 | bool(true) 33 | bool(true) 34 | memcache_add_server(): Argument #1 ($memcache) must be of type Memcache, stdClass given 35 | memcache_add_server(): Argument #2 ($host) must be of type string, stdClass given 36 | Done 37 | -------------------------------------------------------------------------------- /tests/038.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->get() over UDP 3 | --SKIPIF-- 4 | skip known bug 5 | 6 | --FILE-- 7 | set('test_key1', 'abc', 0, 10); 12 | var_dump($result1); 13 | 14 | $result2 = $memcache->set('test_key2', 'def', 0, 10); 15 | var_dump($result2); 16 | 17 | $memcacheudp = new MemcachePool(); 18 | $memcacheudp->addServer($host, $nonExistingPort, $udpPort); 19 | 20 | $result3 = $memcacheudp->get(array('test_key1', 'test_key2')); 21 | var_dump($result3); 22 | 23 | $result4 = $memcacheudp->get(array('test_key1')); 24 | var_dump($result4); 25 | 26 | $result5 = $memcacheudp->get('test_key2'); 27 | var_dump($result5); 28 | 29 | ?> 30 | --EXPECT-- 31 | bool(true) 32 | bool(true) 33 | array(2) { 34 | ["test_key1"]=> 35 | string(3) "abc" 36 | ["test_key2"]=> 37 | string(3) "def" 38 | } 39 | array(1) { 40 | ["test_key1"]=> 41 | string(3) "abc" 42 | } 43 | string(3) "def" -------------------------------------------------------------------------------- /tests/027.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->setCompressThreshold() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | setCompressThreshold(10000); 12 | 13 | $result1 = $memcache->set('non_existing_test_key', $var, 0, 1); 14 | $result2 = $memcache->get('non_existing_test_key'); 15 | 16 | var_dump($result1); 17 | var_dump(strlen($result2)); 18 | 19 | $memcache->setCompressThreshold(10000, 0); 20 | $result3 = $memcache->set('non_existing_test_key', $var, 0, 1); 21 | $memcache->setCompressThreshold(10000, 1); 22 | $result4 = $memcache->set('non_existing_test_key', $var, 0, 1); 23 | 24 | var_dump($result3); 25 | var_dump($result4); 26 | 27 | $result5 = $memcache->set('non_existing_test_key', 'abc', MEMCACHE_COMPRESSED, 1); 28 | $result6 = $memcache->get('non_existing_test_key'); 29 | 30 | var_dump($result5); 31 | var_dump($result6); 32 | 33 | ?> 34 | --EXPECT-- 35 | bool(true) 36 | int(15000) 37 | bool(true) 38 | bool(true) 39 | bool(true) 40 | string(3) "abc" 41 | -------------------------------------------------------------------------------- /tests/019b.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache_add_server() 3 | --SKIPIF-- 4 | = 80000) 6 | die('skip php prior to 8 only'); 7 | include 'connect.inc'; if (!isset($host2)) die('skip $host2 not set'); ?> 8 | --FILE-- 9 | getMessage()}\n"; 20 | } 21 | 22 | try { 23 | var_dump(memcache_add_server($memcache, new stdclass, array())); 24 | } catch (TypeError $e) { 25 | echo "{$e->getMessage()}\n"; 26 | } 27 | 28 | echo "Done\n"; 29 | 30 | ?> 31 | --EXPECTF-- 32 | bool(true) 33 | bool(true) 34 | Argument 1 passed to memcache_add_server() must be an instance of MemcachePool, instance of stdClass given 35 | 36 | Warning: memcache_add_server() expects parameter 2 to be string, object given in %s on line %d 37 | NULL 38 | Done -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "websupport-sk/pecl-memcache", 3 | "type": "php-ext", 4 | "license": "PHP-3.0", 5 | "description": "PHP Extension - Memcache module with support of newer PHP 7.x and PHP 8.x", 6 | "require": { 7 | "php": ">= 8.0.0" 8 | }, 9 | "php-ext": { 10 | "extension-name": "memcache", 11 | "configure-options": [ 12 | { 13 | "name": "enable-memcache", 14 | "description": "Enable memcache support" 15 | }, 16 | { 17 | "name": "disable-memcache-session", 18 | "description": "Disable memcache session handler support" 19 | }, 20 | { 21 | "name": "with-zlib-dir", 22 | "description": "Set the path to ZLIB install prefix.", 23 | "needs-value": true 24 | }, 25 | { 26 | "name": "enable-debug", 27 | "description": "compile with debugging symbols" 28 | } 29 | ] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/024.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->close(), memcache->get() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | addServer($host2, $port2); 11 | $memcache->addServer($nonExistingHost, $nonExistingPort); 12 | 13 | $result1 = $memcache->close(); 14 | var_dump($result1); 15 | 16 | $memcache = new Memcache(); 17 | $result2 = $memcache->connect($host, $port); 18 | $result3 = $memcache->set('test_key', 'test', false, 1); 19 | $result4 = $memcache->close(); 20 | 21 | // This will fail since all servers have been removed 22 | $result5 = $memcache->get('test_key'); 23 | 24 | // Reconnect server 25 | $result6 = $memcache->connect($host, $port); 26 | $result7 = $memcache->get('test_key'); 27 | 28 | var_dump($result2); 29 | var_dump($result3); 30 | var_dump($result4); 31 | var_dump($result5); 32 | var_dump($result6); 33 | var_dump($result7); 34 | 35 | ?> 36 | --EXPECT-- 37 | bool(true) 38 | bool(true) 39 | bool(true) 40 | bool(true) 41 | bool(false) 42 | bool(true) 43 | string(4) "test" 44 | -------------------------------------------------------------------------------- /tests/045.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Nested get's in __wakeup() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | result = $memcache->get('_test_key3'); 16 | var_dump($this->result); 17 | } 18 | } 19 | 20 | $a1 = new testclass(); 21 | 22 | $memcache->set('_test_key1', $a1); 23 | $memcache->set('_test_key2', array(123)); 24 | $memcache->set('_test_key3', 'test3'); 25 | 26 | $a2 = $memcache->get('_test_key1'); 27 | var_dump($a2); 28 | 29 | $result = $memcache->get(array('_test_key1', '_test_key2')); 30 | if (is_array($result)) 31 | ksort($result); 32 | var_dump($result); 33 | 34 | ?> 35 | --EXPECTF-- 36 | string(5) "test3" 37 | object(testclass)%s { 38 | ["result"]=> 39 | string(5) "test3" 40 | } 41 | string(5) "test3" 42 | array(2) { 43 | ["_test_key1"]=> 44 | object(testclass)%s { 45 | ["result"]=> 46 | string(5) "test3" 47 | } 48 | ["_test_key2"]=> 49 | array(1) { 50 | [0]=> 51 | int(123) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/100.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->flush() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | addServer($host, $port); 17 | 18 | $result1 = $memcache->set('test_key', 'abc'); 19 | $result2 = $memcache->get('test_key'); 20 | var_dump($result1); 21 | var_dump($result2); 22 | 23 | // Test partly failing flush 24 | $memcache = new Memcache(); 25 | $memcache->addServer($host, $port); 26 | $memcache->addServer($nonExistingHost, $nonExistingPort); 27 | 28 | $result = @$memcache->flush(); 29 | var_dump($result); 30 | 31 | // Test failing flush 32 | $memcache = new Memcache(); 33 | $memcache->addServer($nonExistingHost, $nonExistingPort); 34 | 35 | $result = @$memcache->flush(); 36 | var_dump($result); 37 | 38 | ?> 39 | --EXPECT-- 40 | bool(true) 41 | string(3) "abc" 42 | bool(false) 43 | bool(false) 44 | -------------------------------------------------------------------------------- /tests/053.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ini_set('session.save_handler') with unix domain socket 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | connect($domainsocket, 0); 12 | 13 | $session_save_path = ", $domainsocket:0?weight=1, "; 14 | ini_set('session.save_handler', 'memcache'); 15 | ini_set('memcache.session_save_path', $session_save_path); 16 | 17 | $result1 = session_start(); 18 | $id = session_id(); 19 | 20 | $_SESSION['_test_key'] = 'Test'; 21 | 22 | $result2 = $memcache->get($id); 23 | session_write_close(); 24 | $result3 = $memcache->get($id); 25 | 26 | // Test destroy 27 | session_start(); 28 | $result4 = session_destroy(); 29 | $result5 = $memcache->get($id); 30 | 31 | var_dump($result1); 32 | var_dump($id); 33 | var_dump($result2); 34 | var_dump($result3); 35 | var_dump($result4); 36 | var_dump($result5); 37 | 38 | ?> 39 | --EXPECTF-- 40 | bool(true) 41 | string(%d) "%s" 42 | bool(false) 43 | string(%d) "%s" 44 | bool(true) 45 | bool(false) 46 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // $Id$ 2 | // vim:ft=javascript 3 | 4 | ARG_ENABLE("memcache", "memcache support", "no"); 5 | 6 | if (PHP_MEMCACHE != "no") { 7 | if (!PHP_ZLIB_SHARED || CHECK_LIB("zlib_a.lib", "memcache", PHP_MEMCACHE)) { 8 | 9 | var dll = get_define('PHPDLL'); 10 | var old_conf_dir = configure_module_dirname; 11 | 12 | if (dll.match(/^php[78]/) != null) { 13 | configure_module_dirname = configure_module_dirname + "\\src"; 14 | } else if (dll.match(/^php5/) != null) { 15 | ERROR("PHP 7.x required for pecl-php-memcache ver 4+. Use pecl-php-meachce ver 3.x for PHP 5.x."); 16 | } else { 17 | ERROR("Cannot determine PHP version from:'" + dll + "'"); 18 | } 19 | 20 | EXTENSION("memcache", " \ 21 | memcache.c \ 22 | memcache_ascii_protocol.c \ 23 | memcache_binary_protocol.c \ 24 | memcache_consistent_hash.c \ 25 | memcache_pool.c \ 26 | memcache_queue.c \ 27 | memcache_session.c \ 28 | memcache_standard_hash.c"); 29 | 30 | configure_module_dirname = old_conf_dir; 31 | 32 | AC_DEFINE('HAVE_MEMCACHE', 1, 'Have memcache support'); 33 | ADD_FLAG("CFLAGS_MEMCACHE", "/D HAVE_MEMCACHE_SESSION=1"); 34 | } else { 35 | WARNING("memcache not enabled; libraries and headers not found"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/022.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->getExtendedStats() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | addServer($nonExistingHost, $nonExistingPort); 12 | $memcache->addServer($host, $port); 13 | $memcache->addServer($host2, $port2); 14 | 15 | $result1 = @$memcache->getStats(); 16 | $result2 = @$memcache->getExtendedStats(); 17 | $result3 = @$memcache->getExtendedStats(); 18 | 19 | var_dump($result1['pid']); 20 | 21 | var_dump(count($result2)); 22 | var_dump($result2["$host:$port"]['pid']); 23 | var_dump($result2["$host2:$port2"]['pid']); 24 | var_dump($result2["$nonExistingHost:$nonExistingPort"]); 25 | 26 | var_dump(count($result3)); 27 | var_dump($result3["$host:$port"]['pid']); 28 | var_dump($result3["$host2:$port2"]['pid']); 29 | var_dump($result3["$nonExistingHost:$nonExistingPort"]); 30 | 31 | ?> 32 | --EXPECTF-- 33 | string(%d) "%d" 34 | int(3) 35 | string(%d) "%d" 36 | string(%d) "%d" 37 | bool(false) 38 | int(3) 39 | string(%d) "%d" 40 | string(%d) "%d" 41 | bool(false) 42 | -------------------------------------------------------------------------------- /tests/021.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->set()/memcache->get() with failover 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | addServer($host, $port); 15 | $memcache->addServer($nonExistingHost, $nonExistingPort, false, 1, 1, -1); 16 | 17 | $result1 = @$memcache->set($balanceKey1, $var1, false, 1); 18 | $result2 = $memcache->set($balanceKey2, $var2, false, 1); 19 | $result3 = $memcache->get($balanceKey1); 20 | $result4 = $memcache->get(array($balanceKey1, $balanceKey2)); 21 | 22 | var_dump($result1); 23 | var_dump($result2); 24 | var_dump($result3); 25 | var_dump(is_array($result4) ? array_values($result4) : $result4); 26 | 27 | $memcache = new Memcache(); 28 | $memcache->addServer($nonExistingHost, $nonExistingPort); 29 | 30 | $result5 = @$memcache->set($balanceKey1, $var1, false, 1); 31 | $result6 = @$memcache->get($balanceKey1); 32 | 33 | var_dump($result5); 34 | var_dump($result6); 35 | 36 | ?> 37 | --EXPECT-- 38 | bool(true) 39 | bool(true) 40 | string(5) "test1" 41 | array(2) { 42 | [0]=> 43 | string(5) "test1" 44 | [1]=> 45 | string(5) "test2" 46 | } 47 | bool(false) 48 | bool(false) 49 | -------------------------------------------------------------------------------- /tests/041.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->delete() with multiple keys 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | set($balanceKey1, 'abc'); 12 | $memcache->set($balanceKey2, 'def'); 13 | 14 | $result1 = $memcache->get($balanceKey1); 15 | $result2 = $memcache->get($balanceKey2); 16 | var_dump($result1); 17 | var_dump($result2); 18 | 19 | $result = $memcache->delete(array($balanceKey1, $balanceKey2)); 20 | var_dump($result); 21 | 22 | $result1 = $memcache->get($balanceKey1); 23 | $result2 = $memcache->get($balanceKey2); 24 | var_dump($result1); 25 | var_dump($result2); 26 | 27 | print "\n"; 28 | 29 | $key = 'test_key'; 30 | $values = array(); 31 | 32 | for ($i=0; $i<250; $i++) { 33 | $values[$key.$i] = $key.'_value_'.$i; 34 | } 35 | 36 | $result1 = $memcache->set($values, null, 0, 10); 37 | var_dump($result1); 38 | 39 | $result2 = memcache_delete($memcache, array_keys($values)); 40 | var_dump($result1); 41 | 42 | $result3 = $memcache->get(array_keys($values)); 43 | var_dump($result3); 44 | 45 | ?> 46 | --EXPECT-- 47 | string(3) "abc" 48 | string(3) "def" 49 | bool(true) 50 | bool(false) 51 | bool(false) 52 | 53 | bool(true) 54 | bool(true) 55 | array(0) { 56 | } 57 | -------------------------------------------------------------------------------- /.github/workflows/config.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | on: [push, pull_request] 3 | jobs: 4 | ubuntu: 5 | strategy: 6 | matrix: 7 | version: ["8.0", "8.1", "8.2"] 8 | runs-on: ubuntu-latest 9 | timeout-minutes: 5 10 | steps: 11 | - name: Checkout pecl-memcache extension 12 | uses: actions/checkout@v2 13 | - name: Setup PHP 14 | uses: shivammathur/setup-php@v2 15 | with: 16 | php-version: ${{matrix.version}} 17 | - name: Install dependencies 18 | run: | 19 | sudo apt-get update 20 | sudo apt-get install zlib1g-dev memcached 21 | sudo systemctl stop memcached 22 | - name: Start memcached daemons 23 | run: | 24 | mkdir -p /var/run/memcached 25 | sudo chmod a+w /var/run/memcached 26 | /usr/bin/memcached -m 64 -s /var/run/memcached/memcached.sock -d 27 | /usr/bin/memcached -m 64 -U 11211 -l 127.0.0.1 -p 11211 -d 28 | /usr/bin/memcached -m 64 -U 11212 -l 127.0.0.1 -p 11212 -d 29 | - name: phpize 30 | run: phpize 31 | - name: configure 32 | run: ./configure 33 | - name: make 34 | run: make 35 | - name: test 36 | run: | 37 | MEMCACHE_PROTOCOL=ascii make test TESTS="--show-diff" 38 | MEMCACHE_PROTOCOL=binary make test TESTS="--show-diff" 39 | -------------------------------------------------------------------------------- /tests/bug73539.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache multi host save path function 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | 45 | --EXPECTF-- 46 | array(1) { 47 | ["bof.test"]=> 48 | int(42) 49 | } 50 | array(1) { 51 | ["bof.test"]=> 52 | int(42) 53 | } 54 | array(1) { 55 | ["bof.test"]=> 56 | int(42) 57 | } 58 | Done 59 | -------------------------------------------------------------------------------- /tests/042.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->set() with multiple values 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | set($balanceKey1, ''); 12 | $memcache->set($balanceKey2, ''); 13 | 14 | $result1 = $memcache->get($balanceKey1); 15 | $result2 = $memcache->get($balanceKey2); 16 | var_dump($result1); 17 | var_dump($result2); 18 | 19 | $values = array( 20 | $balanceKey1 => 'abc', 21 | $balanceKey2 => 'def'); 22 | 23 | $result = $memcache->set($values); 24 | var_dump($result); 25 | 26 | $result1 = $memcache->get($balanceKey1); 27 | $result2 = $memcache->get($balanceKey2); 28 | var_dump($result1); 29 | var_dump($result2); 30 | 31 | print "\n"; 32 | 33 | $key = 'test_key'; 34 | $values = array(); 35 | 36 | for ($i=0; $i<250; $i++) { 37 | $values[$key.$i] = $key.'_value_'.$i; 38 | } 39 | 40 | $memcache->delete(array_keys($values)); 41 | 42 | $result1 = memcache_set($memcache, $values, null, 0, 10); 43 | var_dump($result1); 44 | 45 | $result2 = $memcache->get(array_keys($values)); 46 | ksort($result2); 47 | 48 | var_dump(count($result2)); 49 | var_dump(count($result2) == count($values)); 50 | var_dump(reset($result2)); 51 | 52 | ?> 53 | --EXPECT-- 54 | string(0) "" 55 | string(0) "" 56 | bool(true) 57 | string(3) "abc" 58 | string(3) "def" 59 | 60 | bool(true) 61 | int(250) 62 | bool(true) 63 | string(16) "test_key_value_0" -------------------------------------------------------------------------------- /tests/057.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | session locking 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 52 | --EXPECT-- 53 | array(1) { 54 | ["counter"]=> 55 | int(50) 56 | } 57 | -------------------------------------------------------------------------------- /tests/003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache_set()/memcache_get() using compression 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | plain_attribute = 'value'; 12 | $var->array_attribute = Array('test1', 'test2'); 13 | 14 | memcache_set($memcache, 'test_key', $var, MEMCACHE_COMPRESSED, 10); 15 | memcache_set($memcache, 'test_key1', $var, MEMCACHE_COMPRESSED, 10); 16 | 17 | $result = memcache_get($memcache, 'test_key'); 18 | 19 | var_dump($result); 20 | 21 | $result = memcache_get($memcache, Array('test_key', 'test_key1')); 22 | 23 | var_dump($result); 24 | 25 | ?> 26 | --EXPECTF-- 27 | object(stdClass)%s2) { 28 | ["plain_attribute"]=> 29 | string(5) "value" 30 | ["array_attribute"]=> 31 | array(2) { 32 | [0]=> 33 | string(5) "test1" 34 | [1]=> 35 | string(5) "test2" 36 | } 37 | } 38 | array(2) { 39 | ["test_key"]=> 40 | object(stdClass)%s2) { 41 | ["plain_attribute"]=> 42 | string(5) "value" 43 | ["array_attribute"]=> 44 | array(2) { 45 | [0]=> 46 | string(5) "test1" 47 | [1]=> 48 | string(5) "test2" 49 | } 50 | } 51 | ["test_key1"]=> 52 | object(stdClass)%s2) { 53 | ["plain_attribute"]=> 54 | string(5) "value" 55 | ["array_attribute"]=> 56 | array(2) { 57 | [0]=> 58 | string(5) "test1" 59 | [1]=> 60 | string(5) "test2" 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tests/100a.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache_flush() 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | addServer($nonExistingHost, $nonExistingPort); 27 | 28 | $result = @memcache_flush($memcache2); 29 | var_dump($result); 30 | 31 | memcache_close($memcache); 32 | var_dump(memcache_flush($memcache)); 33 | try { 34 | var_dump(memcache_flush(new stdClass)); 35 | } catch (TypeError $e) { 36 | echo "{$e->getMessage()}\n"; 37 | } 38 | try { 39 | var_dump(memcache_flush('')); 40 | } catch (TypeError $e) { 41 | echo "{$e->getMessage()}\n"; 42 | } 43 | 44 | echo "Done\n"; 45 | 46 | ?> 47 | --EXPECTF-- 48 | bool(true) 49 | bool(false) 50 | bool(false) 51 | bool(true) 52 | memcache_flush(): Argument #1 ($memcache) must be of type MemcachePool, stdClass given 53 | memcache_flush(): Argument #1 ($memcache) must be of type MemcachePool, string given 54 | Done 55 | -------------------------------------------------------------------------------- /tests/025.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->increment() with load balancing 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | addServer($host, $port); 15 | $memcache->addServer($host2, $port2); 16 | 17 | $memcache1 = memcache_connect($host, $port); 18 | $memcache2 = memcache_connect($host2, $port2); 19 | 20 | $memcache1->set($balanceKey1, '', false, 2); 21 | $memcache1->set($balanceKey2, '', false, 2); 22 | $memcache2->set($balanceKey1, '', false, 2); 23 | $memcache2->set($balanceKey2, '', false, 2); 24 | 25 | $result1 = $memcache->set($balanceKey1, $var1, false, 2); // hashes to host2 26 | $result2 = $memcache->set($balanceKey2, $var2, false, 2); // hashes to host1 27 | $result3 = $memcache->increment($balanceKey1); 28 | $result4 = $memcache->increment($balanceKey2); 29 | 30 | var_dump($result1); 31 | var_dump($result2); 32 | var_dump($result3); 33 | var_dump($result4); 34 | 35 | $result5 = $memcache1->get($balanceKey1); 36 | $result6 = $memcache1->get($balanceKey2); 37 | $result7 = $memcache2->get($balanceKey1); 38 | $result8 = $memcache2->get($balanceKey2); 39 | 40 | var_dump($result5); 41 | var_dump($result6); 42 | var_dump($result7); 43 | var_dump($result8); 44 | 45 | ?> 46 | --EXPECT-- 47 | bool(true) 48 | bool(true) 49 | int(11) 50 | int(21) 51 | string(0) "" 52 | int(21) 53 | int(11) 54 | string(0) "" 55 | -------------------------------------------------------------------------------- /tests/100bphpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache_flush() 3 | --SKIPIF-- 4 | = 80000) 6 | die('skip php prior to 8 only'); 7 | include 'connect.inc'; ?> 8 | --FILE-- 9 | addServer($nonExistingHost, $nonExistingPort); 27 | 28 | $result = @memcache_flush($memcache2); 29 | var_dump($result); 30 | 31 | memcache_close($memcache); 32 | var_dump(memcache_flush($memcache)); 33 | try { 34 | var_dump(memcache_flush(new stdClass)); 35 | } catch (TypeError $e) { 36 | echo "{$e->getMessage()}\n"; 37 | } 38 | try { 39 | var_dump(memcache_flush('')); 40 | } catch (TypeError $e) { 41 | echo "{$e->getMessage()}\n"; 42 | } 43 | 44 | echo "Done\n"; 45 | 46 | ?> 47 | --EXPECTF-- 48 | bool(true) 49 | bool(false) 50 | bool(false) 51 | bool(true) 52 | Argument 1 passed to memcache_flush() must be an instance of MemcachePool, instance of stdClass given 53 | Argument 1 passed to memcache_flush() must be an instance of MemcachePool, string given 54 | Done 55 | -------------------------------------------------------------------------------- /tests/022a.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache_get_extended_stats() 3 | --SKIPIF-- 4 | getMessage()}\n"; 24 | } 25 | try { 26 | var_dump(memcache_get_extended_stats(new stdClass)); 27 | } catch (TypeError $e) { 28 | echo "{$e->getMessage()}\n"; 29 | } 30 | 31 | var_dump($result1['pid']); 32 | 33 | var_dump(count($result2)); 34 | var_dump($result2["$host:$port"]['pid']); 35 | var_dump($result2["$host2:$port2"]['pid']); 36 | var_dump($result2["$nonExistingHost:$nonExistingPort"]); 37 | 38 | ?> 39 | --EXPECTF-- 40 | memcache_get_extended_stats(): Argument #1 ($memcache) must be of type MemcachePool, array given 41 | memcache_get_extended_stats(): Argument #1 ($memcache) must be of type MemcachePool, stdClass given 42 | string(%d) "%d" 43 | int(3) 44 | string(%d) "%d" 45 | string(%d) "%d" 46 | bool(false) 47 | -------------------------------------------------------------------------------- /tests/022b.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache_get_extended_stats() 3 | --SKIPIF-- 4 | = 80000) 6 | die('skip php prior to 8 only'); 7 | include 'connect.inc'; if (!isset($host2)) die('skip $host2 not set'); if (ini_get('memcache.protocol') == 'binary') die('skip binary protocol does not support stats'); 8 | --FILE-- 9 | getMessage()}\n"; 24 | } 25 | try { 26 | var_dump(memcache_get_extended_stats(new stdClass)); 27 | } catch (TypeError $e) { 28 | echo "{$e->getMessage()}\n"; 29 | } 30 | 31 | var_dump($result1['pid']); 32 | 33 | var_dump(count($result2)); 34 | var_dump($result2["$host:$port"]['pid']); 35 | var_dump($result2["$host2:$port2"]['pid']); 36 | var_dump($result2["$nonExistingHost:$nonExistingPort"]); 37 | 38 | ?> 39 | --EXPECTF-- 40 | Argument 1 passed to memcache_get_extended_stats() must be an instance of MemcachePool, array given 41 | Argument 1 passed to memcache_get_extended_stats() must be an instance of MemcachePool, instance of stdClass given 42 | string(%d) "%d" 43 | int(3) 44 | string(%d) "%d" 45 | string(%d) "%d" 46 | bool(false) -------------------------------------------------------------------------------- /tests/020.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->set()/memcache->get() with multiple keys and load balancing 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | set($balanceKey1, '', false, 2); 18 | $memcache1->set($balanceKey2, '', false, 2); 19 | $memcache2->set($balanceKey1, '', false, 2); 20 | $memcache2->set($balanceKey2, '', false, 2); 21 | 22 | $result1 = $memcache->set($balanceKey1, $var1, false, 2); // hashes to $host2 23 | $result2 = $memcache->set($balanceKey2, $var2, false, 2); // hashes to $host1 24 | $result3 = $memcache->get(array($balanceKey1, $balanceKey2)); 25 | 26 | if (is_array($result3)) 27 | sort($result3); 28 | 29 | var_dump($result1); 30 | var_dump($result2); 31 | var_dump($result3); 32 | 33 | $result4 = $memcache1->get($balanceKey1); // return ""; key1 is at $host2 34 | $result5 = $memcache1->get($balanceKey2); 35 | $result6 = $memcache2->get($balanceKey1); 36 | $result7 = $memcache2->get($balanceKey2); // return ""; key2 is at $host1 37 | 38 | var_dump($result4); 39 | var_dump($result5); 40 | var_dump($result6); 41 | var_dump($result7); 42 | 43 | ?> 44 | --EXPECT-- 45 | bool(true) 46 | bool(true) 47 | array(2) { 48 | [0]=> 49 | string(5) "test1" 50 | [1]=> 51 | string(5) "test2" 52 | } 53 | string(0) "" 54 | string(5) "test2" 55 | string(5) "test1" 56 | string(0) "" 57 | -------------------------------------------------------------------------------- /tests/100c.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->flush() with time in future 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | addServer($host, $port); 17 | 18 | $result1 = $memcache->set('test_key', 'abc'); 19 | $result2 = $memcache->get('test_key'); 20 | var_dump($result1); 21 | var_dump($result2); 22 | 23 | $result = $memcache->flush(time()+3); 24 | var_dump($result); 25 | 26 | sleep(1); 27 | 28 | $result = $memcache->get('test_key'); 29 | var_dump($result); 30 | 31 | sleep(3); 32 | 33 | $result = $memcache->get('test_key'); 34 | var_dump($result); 35 | 36 | // Test partly failing flush 37 | $memcache = new Memcache(); 38 | $memcache->addServer($host, $port); 39 | $memcache->addServer($nonExistingHost, $nonExistingPort); 40 | 41 | $result = @$memcache->flush(); 42 | var_dump($result); 43 | 44 | // Test failing flush 45 | $memcache = new Memcache(); 46 | $memcache->addServer($nonExistingHost, $nonExistingPort); 47 | 48 | $result = @$memcache->flush(); 49 | var_dump($result); 50 | 51 | ?> 52 | --EXPECT-- 53 | bool(true) 54 | string(3) "abc" 55 | bool(true) 56 | string(3) "abc" 57 | bool(false) 58 | bool(false) 59 | bool(false) 60 | -------------------------------------------------------------------------------- /tests/002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache_get() function 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | plain_attribute = 'value'; 12 | $var->array_attribute = Array('test1', 'test2'); 13 | 14 | $result = memcache_set($memcache, 'test_key', $var); 15 | var_dump($result); 16 | 17 | $result = memcache_set($memcache, 'test_key1', $var); 18 | var_dump($result); 19 | 20 | $result = memcache_get($memcache, 'test_key'); 21 | var_dump($result); 22 | 23 | $result = memcache_get($memcache, array('test_key', 'test_key1')); 24 | var_dump($result); 25 | 26 | $result = memcache_get($memcache, array('unset_test_key', 'unset_test_key1')); 27 | var_dump($result); 28 | 29 | ?> 30 | --EXPECTF-- 31 | bool(true) 32 | bool(true) 33 | object(stdClass)%s2) { 34 | ["plain_attribute"]=> 35 | string(5) "value" 36 | ["array_attribute"]=> 37 | array(2) { 38 | [0]=> 39 | string(5) "test1" 40 | [1]=> 41 | string(5) "test2" 42 | } 43 | } 44 | array(2) { 45 | ["test_key"]=> 46 | object(stdClass)%s2) { 47 | ["plain_attribute"]=> 48 | string(5) "value" 49 | ["array_attribute"]=> 50 | array(2) { 51 | [0]=> 52 | string(5) "test1" 53 | [1]=> 54 | string(5) "test2" 55 | } 56 | } 57 | ["test_key1"]=> 58 | object(stdClass)%s2) { 59 | ["plain_attribute"]=> 60 | string(5) "value" 61 | ["array_attribute"]=> 62 | array(2) { 63 | [0]=> 64 | string(5) "test1" 65 | [1]=> 66 | string(5) "test2" 67 | } 68 | } 69 | } 70 | array(0) { 71 | } -------------------------------------------------------------------------------- /tests/024a.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache_close(), memcache_get() 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | connect($host, $port); 21 | $result3 = memcache_set($memcache, 'test_key', 'test', false, 1); 22 | $result4 = memcache_close($memcache); 23 | 24 | // This will fail since all servers have been removed 25 | $result5 = memcache_get($memcache, 'test_key'); 26 | 27 | var_dump($result2); 28 | var_dump($result3); 29 | var_dump($result4); 30 | var_dump($result5); 31 | 32 | 33 | var_dump(memcache_close($memcache)); 34 | var_dump(memcache_close($memcache)); 35 | try { 36 | var_dump(memcache_close(new stdClass)); 37 | } catch (TypeError $e) { 38 | echo "{$e->getMessage()}\n"; 39 | } 40 | try { 41 | var_dump(memcache_close("")); 42 | } catch (TypeError $e) { 43 | echo "{$e->getMessage()}\n"; 44 | } 45 | 46 | echo "Done\n"; 47 | 48 | ?> 49 | --EXPECTF-- 50 | bool(true) 51 | bool(true) 52 | bool(true) 53 | bool(true) 54 | bool(false) 55 | bool(true) 56 | bool(true) 57 | memcache_close(): Argument #1 ($memcache) must be of type MemcachePool, stdClass given 58 | memcache_close(): Argument #1 ($memcache) must be of type MemcachePool, string given 59 | Done 60 | -------------------------------------------------------------------------------- /tests/024b.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache_close(), memcache_get() 3 | --SKIPIF-- 4 | = 80000) 6 | die('skip php prior to 8 only'); 7 | include 'connect.inc'; if (!isset($host2)) die('skip $host2 not set'); ?> 8 | --FILE-- 9 | connect($host, $port); 21 | $result3 = memcache_set($memcache, 'test_key', 'test', false, 1); 22 | $result4 = memcache_close($memcache); 23 | 24 | // This will fail since all servers have been removed 25 | $result5 = memcache_get($memcache, 'test_key'); 26 | 27 | var_dump($result2); 28 | var_dump($result3); 29 | var_dump($result4); 30 | var_dump($result5); 31 | 32 | 33 | var_dump(memcache_close($memcache)); 34 | var_dump(memcache_close($memcache)); 35 | try { 36 | var_dump(memcache_close(new stdClass)); 37 | } catch (TypeError $e) { 38 | echo "{$e->getMessage()}\n"; 39 | } 40 | try { 41 | var_dump(memcache_close("")); 42 | } catch (TypeError $e) { 43 | echo "{$e->getMessage()}\n"; 44 | } 45 | 46 | echo "Done\n"; 47 | 48 | ?> 49 | --EXPECTF-- 50 | bool(true) 51 | bool(true) 52 | bool(true) 53 | bool(true) 54 | bool(false) 55 | bool(true) 56 | bool(true) 57 | Argument 1 passed to memcache_close() must be an instance of MemcachePool, instance of stdClass given 58 | Argument 1 passed to memcache_close() must be an instance of MemcachePool, string given 59 | Done 60 | -------------------------------------------------------------------------------- /tests/029.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ini_set("memcache.allow_failover") 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | addServer($host, $port); 17 | $memcache->addServer($nonExistingHost, $nonExistingPort); 18 | 19 | $result1 = @$memcache->set($balanceKey1, $var1, false, 1); // hashes to $host2 20 | $result2 = $memcache->set($balanceKey2, $var2, false, 1); // hashes to $host1 21 | $result3 = $memcache->get($balanceKey1); 22 | $result4 = $memcache->get($balanceKey2); 23 | 24 | var_dump($result1); 25 | var_dump($result2); 26 | var_dump($result3); 27 | var_dump($result4); 28 | 29 | $result = $memcache->get(array($balanceKey1, $balanceKey2)); 30 | if (is_array($result)) 31 | sort($result); 32 | var_dump($result); 33 | 34 | ini_set('memcache.allow_failover', 0); 35 | 36 | $result1 = $memcache->get($balanceKey1); 37 | $result2 = $memcache->get($balanceKey2); 38 | 39 | var_dump($result1); 40 | var_dump($result2); 41 | 42 | $result = $memcache->get(array($balanceKey1, $balanceKey2)); 43 | if (is_array($result)) 44 | sort($result); 45 | var_dump($result); 46 | 47 | $result = @ini_set('memcache.allow_failover', "abc"); 48 | var_dump($result); 49 | 50 | ?> 51 | --EXPECTF-- 52 | bool(true) 53 | bool(true) 54 | string(5) "test1" 55 | string(5) "test2" 56 | array(2) { 57 | [0]=> 58 | string(5) "test1" 59 | [1]=> 60 | string(5) "test2" 61 | } 62 | bool(false) 63 | string(5) "test2" 64 | array(1) { 65 | [0]=> 66 | string(5) "test2" 67 | } 68 | string(1) "0" 69 | -------------------------------------------------------------------------------- /tests/040.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->increment()/decrement() with multiple keys 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | set($balanceKey1, 1, 0, 10); 12 | $memcache->set($balanceKey2, 2, 0, 10); 13 | 14 | $result = $memcache->increment(array($balanceKey1, $balanceKey2), 1); 15 | asort($result); 16 | var_dump($result); 17 | 18 | $result1 = $memcache->get($balanceKey1); 19 | $result2 = $memcache->get($balanceKey2); 20 | var_dump($result1); 21 | var_dump($result2); 22 | 23 | $result = $memcache->decrement(array($balanceKey1, $balanceKey2), 1); 24 | asort($result); 25 | var_dump($result); 26 | 27 | $result = memcache_increment($memcache, array($balanceKey1, $balanceKey2), 1); 28 | asort($result); 29 | var_dump($result); 30 | 31 | $result = memcache_decrement($memcache, array($balanceKey1, $balanceKey2), 1); 32 | asort($result); 33 | var_dump($result); 34 | 35 | $result = $memcache->increment(array()); 36 | var_dump($result); 37 | 38 | $result = $memcache->increment(array('unset_test_key', 'unset_test_key1')); 39 | asort($result); 40 | var_dump($result); 41 | 42 | ?> 43 | --EXPECTF-- 44 | array(2) { 45 | ["%s"]=> 46 | int(2) 47 | ["%s"]=> 48 | int(3) 49 | } 50 | int(2) 51 | int(3) 52 | array(2) { 53 | ["%s"]=> 54 | int(1) 55 | ["%s"]=> 56 | int(2) 57 | } 58 | array(2) { 59 | ["%s"]=> 60 | int(2) 61 | ["%s"]=> 62 | int(3) 63 | } 64 | array(2) { 65 | ["%s"]=> 66 | int(1) 67 | ["%s"]=> 68 | int(2) 69 | } 70 | array(0) { 71 | } 72 | array(2) { 73 | ["%s"]=> 74 | bool(false) 75 | ["%s"]=> 76 | bool(false) 77 | } 78 | -------------------------------------------------------------------------------- /tests/047.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->get() with flags 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | set('test_key1', 'test1', $flag1); 14 | $memcache->set('test_key2', 'test2', $flag2); 15 | 16 | // Test OO 17 | $result1 = null; 18 | $result2 = $memcache->get('test_key1', $result1); 19 | 20 | var_dump($result1); 21 | var_dump($result2); 22 | 23 | $result3 = null; 24 | $result4 = $memcache->get(array('test_key1', 'test_key2'), $result3); 25 | 26 | if (is_array($result3)) 27 | ksort($result3); 28 | if (is_array($result4)) 29 | ksort($result4); 30 | 31 | var_dump($result3); 32 | var_dump($result4); 33 | 34 | // Test procedural 35 | $result1 = null; 36 | $result2 = memcache_get($memcache, 'test_key1', $result1); 37 | 38 | var_dump($result1); 39 | var_dump($result2); 40 | 41 | $result3 = null; 42 | $result4 = memcache_get($memcache, array('test_key1', 'test_key2'), $result3); 43 | 44 | if (is_array($result3)) 45 | ksort($result3); 46 | if (is_array($result4)) 47 | ksort($result4); 48 | 49 | var_dump($result3); 50 | var_dump($result4); 51 | 52 | ?> 53 | --EXPECT-- 54 | int(65536) 55 | string(5) "test1" 56 | array(2) { 57 | ["test_key1"]=> 58 | int(65536) 59 | ["test_key2"]=> 60 | int(131072) 61 | } 62 | array(2) { 63 | ["test_key1"]=> 64 | string(5) "test1" 65 | ["test_key2"]=> 66 | string(5) "test2" 67 | } 68 | int(65536) 69 | string(5) "test1" 70 | array(2) { 71 | ["test_key1"]=> 72 | int(65536) 73 | ["test_key2"]=> 74 | int(131072) 75 | } 76 | array(2) { 77 | ["test_key1"]=> 78 | string(5) "test1" 79 | ["test_key2"]=> 80 | string(5) "test2" 81 | } 82 | -------------------------------------------------------------------------------- /tests/036b.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ini_set('session.save_path') 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | get($id); 21 | session_write_close(); 22 | $result3 = $memcache->get($id); 23 | 24 | // Test destroy 25 | $result4 = session_start(); 26 | $result5 = session_destroy(); 27 | $result6 = $memcache->get($id); 28 | 29 | // Test large session 30 | $session_save_path = "tcp://$host:$port"; 31 | session_save_path($session_save_path); 32 | 33 | session_start(); 34 | $largeval = str_repeat('a', 1024*2048); 35 | $_SESSION['_test_key']= $largeval; 36 | session_write_close(); 37 | 38 | // test large cookie lifetime 39 | ini_set('session.gc_maxlifetime', 1209600); 40 | $result7 = session_start(); 41 | $id = session_id(); 42 | $_SESSION['_test_key'] = 'Test'; 43 | $result8 = $memcache->get($id); 44 | session_write_close(); 45 | $result9 = $memcache->get($id); 46 | 47 | 48 | var_dump($result1); 49 | var_dump($id); 50 | var_dump($result2); 51 | var_dump($result3); 52 | var_dump($result4); 53 | var_dump($result5); 54 | var_dump($result6); 55 | var_dump($result7); 56 | var_dump($result8); 57 | var_dump($result9); 58 | 59 | ?> 60 | --EXPECTF-- 61 | bool(true) 62 | string(%d) "%s" 63 | bool(false) 64 | string(%d) "%s" 65 | bool(true) 66 | bool(true) 67 | bool(false) 68 | bool(true) 69 | string(%d) "%s" 70 | string(%d) "%s" 71 | -------------------------------------------------------------------------------- /tests/036.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ini_set('session.save_handler') 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | get($id); 21 | session_write_close(); 22 | $result3 = $memcache->get($id); 23 | 24 | // Test destroy 25 | $result4 = session_start(); 26 | $result5 = session_destroy(); 27 | $result6 = $memcache->get($id); 28 | 29 | // Test large session 30 | $session_save_path = "tcp://$host:$port"; 31 | ini_set('memcache.session_save_path', $session_save_path); 32 | 33 | session_start(); 34 | $largeval = str_repeat('a', 1024*2048); 35 | $_SESSION['_test_key']= $largeval; 36 | session_write_close(); 37 | 38 | // test large cookie lifetime 39 | ini_set('session.gc_maxlifetime', 1209600); 40 | $result7 = session_start(); 41 | $id = session_id(); 42 | $_SESSION['_test_key'] = 'Test'; 43 | $result8 = $memcache->get($id); 44 | session_write_close(); 45 | $result9 = $memcache->get($id); 46 | 47 | 48 | var_dump($result1); 49 | var_dump($id); 50 | var_dump($result2); 51 | var_dump($result3); 52 | var_dump($result4); 53 | var_dump($result5); 54 | var_dump($result6); 55 | var_dump($result7); 56 | var_dump($result8); 57 | var_dump($result9); 58 | 59 | ?> 60 | --EXPECTF-- 61 | bool(true) 62 | string(%d) "%s" 63 | bool(false) 64 | string(%d) "%s" 65 | bool(true) 66 | bool(true) 67 | bool(false) 68 | bool(true) 69 | string(%d) "%s" 70 | string(%d) "%s" 71 | -------------------------------------------------------------------------------- /tests/redundancy_test.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | redundancy test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | flush(); 34 | 35 | $pid = pcntl_fork(); 36 | if (!$pid) { 37 | // In child process 38 | session_id($sid); 39 | session_start(); 40 | if (!isset($_SESSION['counter'])) 41 | $_SESSION['counter'] = 0; 42 | $_SESSION['counter'] += 1; 43 | session_write_close(); 44 | 45 | exit(0); 46 | } 47 | pcntl_waitpid($pid, $status); 48 | 49 | $memcache2->flush(); 50 | 51 | $pid = pcntl_fork(); 52 | if (!$pid) { 53 | // In child process 54 | session_id($sid); 55 | session_start(); 56 | if (!isset($_SESSION['counter'])) 57 | $_SESSION['counter'] = 0; 58 | $_SESSION['counter'] += 1; 59 | session_write_close(); 60 | 61 | exit(0); 62 | } 63 | pcntl_waitpid($pid, $status); 64 | 65 | 66 | session_id($sid); 67 | session_start(); 68 | var_dump($_SESSION); 69 | 70 | ?> 71 | --EXPECT-- 72 | array(1) { 73 | ["counter"]=> 74 | int(3) 75 | } 76 | -------------------------------------------------------------------------------- /tests/056.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->addServer() with microsecond timeout 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | addServer($unreachableHost, $unreachablePort, false, 1, 0.1); 18 | 19 | $t1 = microtime_float(); 20 | $memcache->set('test_key', '1'); 21 | $t2 = microtime_float(); 22 | 23 | $t = $t2 - $t1; 24 | var_dump($t); 25 | var_dump($t > 0.01 && $t < 0.2); 26 | 27 | $memcache = new MemcachePool(); 28 | $memcache->addServer($unreachableHost, $unreachablePort, 0, true, 1, 0.1); 29 | 30 | $t1 = microtime_float(); 31 | $memcache->set('test_key', '1'); 32 | $t2 = microtime_float(); 33 | 34 | $t = $t2 - $t1; 35 | var_dump($t); 36 | var_dump($t > 0.01 && $t < 0.2); 37 | 38 | $memcache = new MemcachePool(); 39 | $t1 = microtime_float(); 40 | $memcache->connect($unreachableHost, $unreachablePort, 0, false, 1, 0.1); 41 | $t2 = microtime_float(); 42 | 43 | $t = $t2 - $t1; 44 | var_dump($t); 45 | var_dump($t > 0.01 && $t < 0.2); 46 | 47 | $memcache = new MemcachePool(); 48 | $memcache->addServer($unreachableHost, $unreachablePort, 0, true, 1, 1); 49 | $memcache->setServerParams($unreachableHost, $unreachablePort, 0.1); 50 | 51 | $t1 = microtime_float(); 52 | $memcache->set('test_key', '1'); 53 | $t2 = microtime_float(); 54 | 55 | $t = $t2 - $t1; 56 | var_dump($t); 57 | var_dump($t > 0.01 && $t < 0.2); 58 | 59 | ?> 60 | --EXPECTF-- 61 | Notice: MemcachePool::set(): %s 62 | float(0.%d) 63 | bool(true) 64 | 65 | Notice: MemcachePool::set(): %s 66 | float(0.%d) 67 | bool(true) 68 | 69 | Notice: MemcachePool::connect(): %s 70 | 71 | Warning: MemcachePool::connect(): %s 72 | float(0.%d) 73 | bool(true) 74 | 75 | Notice: MemcachePool::set(): %s 76 | float(0.%d) 77 | bool(true) 78 | -------------------------------------------------------------------------------- /tests/034.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->getStats() with arguments 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | set('test_key', 'abc'); 11 | var_dump($result); 12 | 13 | $result = $memcache->getStats(); 14 | var_dump($result['pid']); 15 | $version = $result['version']; 16 | 17 | $result = $memcache->getStats('abc'); 18 | var_dump($result); 19 | 20 | $result = $memcache->getStats('reset'); 21 | var_dump($result); 22 | 23 | // malloc was removed in memcached 1.4.0 24 | if ($version >= '1.4.0') { 25 | var_dump("0"); 26 | } else { 27 | $result = $memcache->getStats('malloc'); 28 | var_dump($result['arena_size']); 29 | } 30 | 31 | $result = $memcache->getStats('slabs'); 32 | var_dump($result[key($result)]['chunk_size']); 33 | var_dump($result[key($result)]['free_chunks_end']); 34 | $slab = key($result); 35 | 36 | $result = $memcache->getStats('cachedump', $slab, 10); 37 | var_dump($result[key($result)][0]); 38 | var_dump($result[key($result)][1]); 39 | 40 | $result = $memcache->getStats('items'); 41 | var_dump($result['items'][$slab]['number']); 42 | 43 | //$result = $memcache->getStats('sizes'); 44 | //var_dump($result['64']); 45 | 46 | $result = $memcache->getExtendedStats('abc'); 47 | // adding "@" to suppress new behaviour in PHP 7.4+, see: https://wiki.php.net/rfc/notice-for-non-valid-array-container 48 | @var_dump($result["$host:$port"]); 49 | 50 | $result = $memcache->getExtendedStats('items'); 51 | var_dump(isset($result["$host:$port"]['items'])); 52 | 53 | ?> 54 | --EXPECTF-- 55 | bool(true) 56 | string(%d) "%d" 57 | 58 | Warning: %s: Invalid stats type %s 59 | bool(false) 60 | bool(true) 61 | string(%d) "%d" 62 | string(%d) "%d" 63 | string(%d) "%d" 64 | string(%d) "%d" 65 | string(%d) "%d" 66 | string(%d) "%d" 67 | 68 | Warning: %s: Invalid stats type %s 69 | NULL 70 | bool(true) 71 | -------------------------------------------------------------------------------- /tests/019.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->addServer() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | set('test_key', 'test'); 14 | $result2 = @$memcache->get('test_key'); 15 | 16 | var_dump($result1); 17 | var_dump($result2); 18 | 19 | $result1 = $memcache->addServer($host, $port, true, 1, 1, 15); 20 | $result2 = $memcache->connect($host2, $port2); 21 | 22 | $result3 = $memcache->set('non_existing_test_key', $var, false, 1); 23 | $result4 = $memcache->get('non_existing_test_key'); 24 | $result5 = $memcache->getExtendedStats(); 25 | 26 | var_dump($result1); 27 | var_dump($result2); 28 | var_dump($result3); 29 | var_dump($result4); 30 | var_dump(count($result5)); 31 | 32 | // Test default port 33 | $memcache = new Memcache(); 34 | 35 | ini_set('memcache.default_port', $port); 36 | $result1 = $memcache->addServer($host); 37 | 38 | ini_set('memcache.default_port', $port2); 39 | $result2 = $memcache->connect($host2); 40 | 41 | $result3 = $memcache->set($balanceKey1, 'a'); 42 | $result4 = $memcache->set($balanceKey2, 'b'); 43 | 44 | var_dump($result1); 45 | var_dump($result2); 46 | var_dump($result3); 47 | var_dump($result4); 48 | 49 | // Test empty connection 50 | $memcache = new Memcache(); 51 | $memcache->set('test_key', '1'); 52 | 53 | // Test bad connection id 54 | $memcache = new Memcache(); 55 | $memcache->connect($host, $port); 56 | var_dump($memcache->connection); 57 | 58 | $memcache->connection = true; 59 | $memcache->set('test_key', '1'); 60 | 61 | ?> 62 | --EXPECTF-- 63 | bool(false) 64 | bool(false) 65 | bool(true) 66 | bool(true) 67 | bool(true) 68 | string(4) "test" 69 | int(2) 70 | bool(true) 71 | bool(true) 72 | bool(true) 73 | bool(true) 74 | 75 | Warning: %s::set(): No servers added %s 76 | resource(%d) of type (memcache connection) 77 | 78 | Warning: %s::set(): Invalid %s member variable %s 79 | -------------------------------------------------------------------------------- /tests/046.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | hash strategies and functions 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | $functions) { 17 | foreach ($functions as $function => $keys) { 18 | ini_set('memcache.hash_strategy', $strategy); 19 | ini_set('memcache.hash_function', $function); 20 | list ($balanceKey1, $balanceKey2) = $keys; 21 | 22 | print "\n$strategy:$function\n"; 23 | 24 | $memcache = new Memcache(); 25 | $memcache->addServer($host, $port); 26 | $memcache->addServer($host2, $port2); 27 | 28 | $memcache1->set($balanceKey1, '', false, 2); 29 | $memcache1->set($balanceKey2, '', false, 2); 30 | $memcache2->set($balanceKey1, '', false, 2); 31 | $memcache2->set($balanceKey2, '', false, 2); 32 | 33 | $memcache->set($balanceKey1, $var1, false, 2); // hashes to $host2 34 | $memcache->set($balanceKey2, $var2, false, 2); // hashes to $host1 35 | 36 | $result4 = $memcache1->get($balanceKey1); // return false; key1 is at $host2 37 | $result5 = $memcache1->get($balanceKey2); 38 | $result6 = $memcache2->get($balanceKey1); 39 | $result7 = $memcache2->get($balanceKey2); // return false; key2 is at $host1 40 | 41 | var_dump($result4); 42 | var_dump($result5); 43 | var_dump($result6); 44 | var_dump($result7); 45 | } 46 | } 47 | 48 | ?> 49 | --EXPECT-- 50 | 51 | consistent:crc32 52 | string(0) "" 53 | string(5) "test2" 54 | string(5) "test1" 55 | string(0) "" 56 | 57 | consistent:fnv 58 | string(0) "" 59 | string(5) "test2" 60 | string(5) "test1" 61 | string(0) "" 62 | 63 | standard:crc32 64 | string(0) "" 65 | string(5) "test2" 66 | string(5) "test1" 67 | string(0) "" 68 | 69 | standard:fnv 70 | string(0) "" 71 | string(5) "test2" 72 | string(5) "test1" 73 | string(0) "" 74 | -------------------------------------------------------------------------------- /tests/043.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ini_set('memcache.redundancy') 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | delete($balanceKey1); 16 | $memcache2->delete($balanceKey1); 17 | 18 | $result = $memcache->get($balanceKey1); 19 | var_dump($result); 20 | 21 | $memcache->set($balanceKey1, 'Test1'); 22 | 23 | $result1 = $memcache1->get($balanceKey1); 24 | $result2 = $memcache2->get($balanceKey1); 25 | var_dump($result1); 26 | var_dump($result2); 27 | 28 | ini_set('memcache.redundancy', 10); 29 | $memcache = test_connect_pool(); 30 | 31 | // Test set 32 | $memcache->set($balanceKey1, 'Test2'); 33 | 34 | $result1 = $memcache1->get($balanceKey1); 35 | $result2 = $memcache2->get($balanceKey1); 36 | var_dump($result1); 37 | var_dump($result2); 38 | 39 | // Test increment 40 | $memcache->set($balanceKey1, '1'); 41 | $memcache->increment($balanceKey1); 42 | 43 | $result1 = $memcache1->get($balanceKey1); 44 | $result2 = $memcache2->get($balanceKey1); 45 | var_dump($result1); 46 | var_dump($result2); 47 | 48 | // Test delete 49 | $memcache->delete($balanceKey1); 50 | 51 | $result1 = $memcache1->get($balanceKey1); 52 | $result2 = $memcache2->get($balanceKey1); 53 | var_dump($result1); 54 | var_dump($result2); 55 | 56 | // Test invalid values 57 | ini_set('memcache.redundancy', 0); 58 | ini_set('memcache.redundancy', -1); 59 | ini_set('memcache.redundancy', 'Test'); 60 | 61 | ?> 62 | --EXPECTF-- 63 | bool(false) 64 | bool(false) 65 | string(5) "Test1" 66 | string(5) "Test2" 67 | string(5) "Test2" 68 | string(1) "2" 69 | string(1) "2" 70 | bool(false) 71 | bool(false) 72 | 73 | Warning: ini_set(): memcache.redundancy must be a positive integer ('0' given) in %s 74 | 75 | Warning: ini_set(): memcache.redundancy must be a positive integer ('-1' given) in %s 76 | 77 | Warning: ini_set(): memcache.redundancy must be a positive integer ('Test' given) in %s 78 | -------------------------------------------------------------------------------- /tests/023.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->delete() with load balancing 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | addServer($host, $port); 15 | $memcache->addServer($host2, $port2); 16 | 17 | $memcache1 = memcache_connect($host, $port); 18 | $memcache2 = memcache_connect($host2, $port2); 19 | 20 | $memcache1->set($balanceKey1, '', 0, 10); 21 | $memcache1->set($balanceKey2, '', 0, 10); 22 | $memcache2->set($balanceKey1, '', 0, 10); 23 | $memcache2->set($balanceKey2, '', 0, 10); 24 | 25 | $result1 = $memcache->set($balanceKey1, $var1, 0, 10); // hashes to host2 26 | $result2 = $memcache->set($balanceKey2, $var2, 0, 10); // hashes to host1 27 | $result3 = $memcache->get($balanceKey1); 28 | $result4 = $memcache->get($balanceKey2); 29 | 30 | var_dump($result1); 31 | var_dump($result2); 32 | var_dump($result3); 33 | var_dump($result4); 34 | print "\r\n"; 35 | 36 | $result5 = $memcache1->get($balanceKey1); 37 | $result6 = $memcache1->get($balanceKey2); 38 | $result7 = $memcache2->get($balanceKey1); 39 | $result8 = $memcache2->get($balanceKey2); 40 | 41 | var_dump($result5); 42 | var_dump($result6); 43 | var_dump($result7); 44 | var_dump($result8); 45 | print "\r\n"; 46 | 47 | $result9 = $memcache->delete($balanceKey1); 48 | $result10 = $memcache->get($balanceKey1); 49 | $result11 = $memcache2->get($balanceKey1); 50 | 51 | var_dump($result9); 52 | var_dump($result10); 53 | var_dump($result11); 54 | 55 | $result12 = $memcache->delete($balanceKey2); 56 | $result13 = $memcache->get($balanceKey2); 57 | $result14 = $memcache1->get($balanceKey2); 58 | 59 | var_dump($result12); 60 | var_dump($result13); 61 | var_dump($result14); 62 | 63 | ?> 64 | --EXPECT-- 65 | bool(true) 66 | bool(true) 67 | string(5) "test1" 68 | string(5) "test2" 69 | 70 | string(0) "" 71 | string(5) "test2" 72 | string(5) "test1" 73 | string(0) "" 74 | 75 | bool(true) 76 | bool(false) 77 | bool(false) 78 | bool(true) 79 | bool(false) 80 | bool(false) 81 | -------------------------------------------------------------------------------- /tests/044.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ini_set('memcache.session_redundancy') 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | delete($balanceKey1); 21 | $memcache1->delete($balanceKey2); 22 | $memcache2->delete($balanceKey1); 23 | $memcache2->delete($balanceKey2); 24 | 25 | // Test set 26 | session_id($balanceKey1); 27 | session_start(); 28 | $_SESSION['key'] = 'Test1'; 29 | session_write_close(); 30 | 31 | $result1 = $memcache1->get($balanceKey1); 32 | $result2 = $memcache2->get($balanceKey1); 33 | var_dump($result1); 34 | var_dump($result2); 35 | 36 | // Test delete 37 | session_id($balanceKey1); 38 | session_start(); 39 | session_destroy(); 40 | 41 | $result1 = $memcache1->get($balanceKey1); 42 | $result2 = $memcache2->get($balanceKey1); 43 | var_dump($result1); 44 | var_dump($result2); 45 | 46 | // Test lost session on server1 47 | session_id($balanceKey1); 48 | session_start(); 49 | $_SESSION['key'] = 'Test2'; 50 | session_write_close(); 51 | unset($_SESSION['key']); 52 | 53 | $result = $memcache1->delete($balanceKey1); 54 | var_dump($result); 55 | 56 | session_id($balanceKey1); 57 | @session_start(); 58 | var_dump($_SESSION); 59 | session_write_close(); 60 | 61 | // Test lost session on server2 62 | session_id($balanceKey2); 63 | session_start(); 64 | $_SESSION['key'] = 'Test3'; 65 | session_write_close(); 66 | unset($_SESSION['key']); 67 | 68 | $result = $memcache2->delete($balanceKey1); 69 | var_dump($result); 70 | 71 | session_id($balanceKey2); 72 | session_start(); 73 | var_dump($_SESSION); 74 | session_write_close(); 75 | ob_flush(); 76 | ?> 77 | --EXPECTF-- 78 | string(16) "key%sTest1%s" 79 | string(16) "key%sTest1%s" 80 | bool(false) 81 | bool(false) 82 | bool(true) 83 | array(1) { 84 | ["key"]=> 85 | string(5) "Test2" 86 | } 87 | bool(true) 88 | array(1) { 89 | ["key"]=> 90 | string(5) "Test3" 91 | } 92 | -------------------------------------------------------------------------------- /tests/027a.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache_set_compress_threshold() 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | getMessage()}\n"; 40 | } 41 | var_dump(memcache_set_compress_threshold($memcache, -1, -1)); 42 | var_dump(memcache_set_compress_threshold($memcache, 1, -1)); 43 | var_dump(memcache_set_compress_threshold($memcache, -1, 1)); 44 | try { 45 | var_dump(memcache_set_compress_threshold(new stdClass, 1, 1)); 46 | } catch (TypeError $e) { 47 | echo "{$e->getMessage()}\n"; 48 | } 49 | 50 | echo "Done\n"; 51 | 52 | ?> 53 | --EXPECTF-- 54 | bool(true) 55 | int(15000) 56 | bool(true) 57 | bool(true) 58 | bool(true) 59 | string(3) "abc" 60 | memcache_set_compress_threshold(): Argument #1 ($memcache) must be of type MemcachePool, array given 61 | 62 | Warning: memcache_set_compress_threshold()%s threshold must be a positive integer in %s on line %d 63 | bool(false) 64 | 65 | Warning: memcache_set_compress_threshold()%s min_savings must be a float in the 0..1 range in %s on line %d 66 | bool(false) 67 | 68 | Warning: memcache_set_compress_threshold()%s threshold must be a positive integer in %s on line %d 69 | bool(false) 70 | memcache_set_compress_threshold(): Argument #1 ($memcache) must be of type MemcachePool, stdClass given 71 | Done 72 | -------------------------------------------------------------------------------- /tests/027b.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache_set_compress_threshold() 3 | --SKIPIF-- 4 | = 80000) 6 | die('skip php prior to 8 only'); 7 | include 'connect.inc'; ?> 8 | --FILE-- 9 | getMessage()}\n"; 40 | } 41 | var_dump(memcache_set_compress_threshold($memcache, -1, -1)); 42 | var_dump(memcache_set_compress_threshold($memcache, 1, -1)); 43 | var_dump(memcache_set_compress_threshold($memcache, -1, 1)); 44 | try { 45 | var_dump(memcache_set_compress_threshold(new stdClass, 1, 1)); 46 | } catch (TypeError $e) { 47 | echo "{$e->getMessage()}\n"; 48 | } 49 | 50 | echo "Done\n"; 51 | 52 | ?> 53 | --EXPECTF-- 54 | bool(true) 55 | int(15000) 56 | bool(true) 57 | bool(true) 58 | bool(true) 59 | string(3) "abc" 60 | Argument 1 passed to memcache_set_compress_threshold() must be an instance of MemcachePool, array given 61 | 62 | Warning: memcache_set_compress_threshold()%s threshold must be a positive integer in %s on line %d 63 | bool(false) 64 | 65 | Warning: memcache_set_compress_threshold()%s min_savings must be a float in the 0..1 range in %s on line %d 66 | bool(false) 67 | 68 | Warning: memcache_set_compress_threshold()%s threshold must be a positive integer in %s on line %d 69 | bool(false) 70 | Argument 1 passed to memcache_set_compress_threshold() must be an instance of MemcachePool, instance of stdClass given 71 | Done 72 | -------------------------------------------------------------------------------- /tests/032.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->getServerStatus(), memcache->setServerParams() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | addServer($host, $port, false); 13 | $memcache->addServer($host2, $port2, false); 14 | 15 | $memcache1 = memcache_connect($host, $port); 16 | $memcache2 = memcache_pconnect($host2, $port2); 17 | 18 | $memcache1->set($balanceKey1, '', false, 2); 19 | $memcache1->set($balanceKey2, '', false, 2); 20 | $memcache2->set($balanceKey1, '', false, 2); 21 | $memcache2->set($balanceKey2, '', false, 2); 22 | 23 | $result1 = $memcache->set($balanceKey1, 'test-030-01', false, 2); // hashes to $host2 24 | $result2 = $memcache->set($balanceKey2, 'test-030-02', false, 2); // hashes to $host1 25 | 26 | var_dump($result1); 27 | var_dump($result2); 28 | 29 | $result4 = $memcache1->get($balanceKey1); // return false; key1 is at $host2 30 | $result5 = $memcache1->get($balanceKey2); 31 | $result6 = $memcache2->get($balanceKey1); 32 | $result7 = $memcache2->get($balanceKey2); // return false; key2 is at $host1 33 | 34 | var_dump($result4); 35 | var_dump($result5); 36 | var_dump($result6); 37 | var_dump($result7); 38 | 39 | // Add server in failed mode 40 | $memcache = new Memcache(); 41 | $memcache->addServer($host, $port, false); 42 | $memcache->addServer($host2, $port2, false, 1, 1, -1, false); 43 | 44 | $result8 = $memcache->getServerStatus($host, $port); 45 | $result9 = $memcache->getServerStatus($host2, $port2); 46 | 47 | var_dump($result8); 48 | var_dump($result9); 49 | 50 | // Hot-add failed server 51 | $result10 = $memcache->setServerParams($host2, $port2, 1, 15, true); 52 | $result11 = $memcache->getServerStatus($host2, $port2); 53 | 54 | var_dump($result10); 55 | var_dump($result11); 56 | 57 | $result12 = $memcache->set($balanceKey1, 'test-030-03', false, 1); // hashes to $host2 58 | $result13 = $memcache2->get($balanceKey1); // key should be at $host2 (reconnected $host2) 59 | 60 | var_dump($result12); 61 | var_dump($result13); 62 | 63 | ?> 64 | --EXPECT-- 65 | bool(true) 66 | bool(true) 67 | string(0) "" 68 | string(11) "test-030-02" 69 | string(11) "test-030-01" 70 | string(0) "" 71 | int(1) 72 | int(0) 73 | bool(true) 74 | int(1) 75 | bool(true) 76 | string(11) "test-030-03" 77 | -------------------------------------------------------------------------------- /tests/031.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->addServer() adding server in failed mode 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | addServer($host, $port, false); 13 | $memcache->addServer($host2, $port2, false); 14 | 15 | $memcache1 = memcache_connect($host, $port); 16 | $memcache2 = memcache_connect($host2, $port2); 17 | 18 | $memcache1->set($balanceKey1, '', false, 2); 19 | $memcache1->set($balanceKey1, '', false, 2); 20 | $memcache2->set($balanceKey1, '', false, 2); 21 | $memcache2->set($balanceKey2, '', false, 2); 22 | 23 | $result1 = $memcache->set($balanceKey1, 'test-030-01', false, 2); // hashes to $host2 24 | $result2 = $memcache->set($balanceKey2, 'test-030-02', false, 2); // hashes to $host1 25 | 26 | var_dump($result1); 27 | var_dump($result2); 28 | 29 | $result4 = $memcache1->get($balanceKey1); // return false; key1 is at $host2 30 | $result5 = $memcache1->get($balanceKey2); 31 | $result6 = $memcache2->get($balanceKey1); 32 | $result7 = $memcache2->get($balanceKey2); // return false; key2 is at $host1 33 | 34 | var_dump($result4); 35 | var_dump($result5); 36 | var_dump($result6); 37 | var_dump($result7); 38 | 39 | // Add server in failed mode 40 | $memcache = new Memcache(); 41 | $memcache->addServer($host, $port, false); 42 | $memcache->addServer($host2, $port2, false, 1, 1, -1, false); 43 | 44 | $result8 = $memcache->set($balanceKey1, 'test-030-03', false, 1); // hashes to $host2 45 | $result9 = $memcache->set($balanceKey2, 'test-030-04', false, 1); // hashes to $host1 46 | 47 | var_dump($result8); 48 | var_dump($result9); 49 | 50 | $result10 = $memcache1->get($balanceKey1); // key should be $host1 (failed over from $host2) 51 | $result11 = $memcache1->get($balanceKey2); // key should always be at $host1 52 | 53 | var_dump($result10); 54 | var_dump($result11); 55 | 56 | // Single server in failed mode 57 | function _callback_server_failure($host, $port) { 58 | print "_callback_server_failure()\n"; 59 | } 60 | 61 | $memcache = new Memcache(); 62 | $memcache->addServer($host, $port, true, 1, 1, -1, false, '_callback_server_failure'); 63 | $result13 = $memcache->add('test_key', 'Test'); 64 | $result14 = $memcache->get('test_key'); 65 | 66 | var_dump($result13); 67 | var_dump($result14); 68 | 69 | ?> 70 | --EXPECT-- 71 | bool(true) 72 | bool(true) 73 | string(0) "" 74 | string(11) "test-030-02" 75 | string(11) "test-030-01" 76 | string(0) "" 77 | bool(true) 78 | bool(true) 79 | string(11) "test-030-03" 80 | string(11) "test-030-04" 81 | bool(false) 82 | bool(false) 83 | -------------------------------------------------------------------------------- /src/memcache_queue.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2007 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.0 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_0.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 | | Authors: Antony Dovgal | 16 | | Mikael Johansson | 17 | +----------------------------------------------------------------------+ 18 | */ 19 | 20 | /* $Id$ */ 21 | 22 | #ifndef MEMCACHE_QUEUE_H_ 23 | #define MEMCACHE_QUEUE_H_ 24 | 25 | /* request / server stack */ 26 | #define MMC_QUEUE_PREALLOC 25 27 | 28 | typedef struct mmc_queue { 29 | void **items; /* items on queue */ 30 | int alloc; /* allocated size */ 31 | int head; /* head index in ring buffer */ 32 | int tail; /* tail index in ring buffer */ 33 | int len; 34 | } mmc_queue_t; 35 | 36 | #define mmc_queue_release(q) ZEND_SECURE_ZERO((q), sizeof(*(q))) 37 | #define mmc_queue_reset(q) (q)->len = (q)->head = (q)->tail = 0 38 | #define mmc_queue_item(q, i) ((q)->tail + (i) < (q)->alloc ? (q)->items[(q)->tail + (i)] : (q)->items[(i) - ((q)->alloc - (q)->tail)]) 39 | 40 | #ifdef PHP_WIN32 41 | #define MMC_QUEUE_INLINE 42 | #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L 43 | /* see https://gcc.gnu.org/gcc-5/porting_to.html */ 44 | #define MMC_QUEUE_INLINE extern inline 45 | #else 46 | #define MMC_QUEUE_INLINE inline 47 | #endif 48 | 49 | void mmc_queue_push(mmc_queue_t *, void *); 50 | void *mmc_queue_pop(mmc_queue_t *); 51 | int mmc_queue_contains(mmc_queue_t *, void *); 52 | void mmc_queue_free(mmc_queue_t *); 53 | void mmc_queue_copy(mmc_queue_t *, mmc_queue_t *); 54 | void mmc_queue_remove(mmc_queue_t *, void *); 55 | 56 | #endif /*MEMCACHE_QUEUE_H_*/ 57 | 58 | /* 59 | * Local variables: 60 | * tab-width: 4 61 | * c-basic-offset: 4 62 | * End: 63 | * vim600: noet sw=4 ts=4 fdm=marker 64 | * vim<600: noet sw=4 ts=4 65 | */ 66 | -------------------------------------------------------------------------------- /tests/connect.inc: -------------------------------------------------------------------------------- 1 | array( 51 | 'crc32' => array('key1_abc', 'key2_abcde'), 52 | 'fnv' => array('key1_a', 'key2_2534534'), 53 | ), 54 | 'standard' => array( 55 | 'crc32' => array('load_test_key1', 'load_test_key2'), 56 | 'fnv' => array('key1_ab', 'key2_a'), 57 | ), 58 | ); 59 | 60 | $strat = strtolower(ini_get('memcache.hash_strategy')); 61 | $func = strtolower(ini_get('memcache.hash_function')); 62 | list ($balanceKey1, $balanceKey2) = $balanceKeys[$strat][$func]; 63 | 64 | if (!isset($udpPort)) 65 | $udpPort = 0; 66 | if (!isset($udpPort2)) 67 | $udpPort2 = 0; 68 | 69 | $memcache = memcache_connect($host, $port); 70 | 71 | function test_connect1() { 72 | global $host, $port, $udpPort; 73 | $memcache = new MemcachePool(); 74 | $memcache->connect($host, $port, isset($udpPort) ? $udpPort : 0); 75 | return $memcache; 76 | } 77 | 78 | function test_connect2() { 79 | global $host2, $port2, $udpPort2; 80 | $memcache = new MemcachePool(); 81 | $memcache->connect($host2, $port2, isset($udpPort2) ? $udpPort2 : 0); 82 | return $memcache; 83 | } 84 | 85 | function test_connect_pool() { 86 | global $host, $port, $udpPort, $host2, $port2, $udpPort2; 87 | $memcache = new MemcachePool(); 88 | $memcache->addServer($host, $port, isset($udpPort) ? $udpPort : 0); 89 | $memcache->addServer($host2, $port2, isset($udpPort2) ? $udpPort2 : 0); 90 | return $memcache; 91 | } 92 | 93 | if (!$memcache) { 94 | die('skip Connection to memcached failed'); 95 | } 96 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------- 2 | The PHP License, Version 3.0 3 | Copyright (c) 1999 - 2005 The PHP Group. All rights reserved. 4 | -------------------------------------------------------------------- 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, is permitted provided that the following conditions 8 | are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in 15 | the documentation and/or other materials provided with the 16 | distribution. 17 | 18 | 3. The name "PHP" must not be used to endorse or promote products 19 | derived from this software without prior written permission. For 20 | written permission, please contact group@php.net. 21 | 22 | 4. Products derived from this software may not be called "PHP", nor 23 | may "PHP" appear in their name, without prior written permission 24 | from group@php.net. You may indicate that your software works in 25 | conjunction with PHP by saying "Foo for PHP" instead of calling 26 | it "PHP Foo" or "phpfoo" 27 | 28 | 5. The PHP Group may publish revised and/or new versions of the 29 | license from time to time. Each version will be given a 30 | distinguishing version number. 31 | Once covered code has been published under a particular version 32 | of the license, you may always continue to use it under the terms 33 | of that version. You may also choose to use such covered code 34 | under the terms of any subsequent version of the license 35 | published by the PHP Group. No one other than the PHP Group has 36 | the right to modify the terms applicable to covered code created 37 | under this License. 38 | 39 | 6. Redistributions of any form whatsoever must retain the following 40 | acknowledgment: 41 | "This product includes PHP, freely available from 42 | ". 43 | 44 | THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND 45 | ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 46 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 47 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP 48 | DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 49 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 50 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 51 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 53 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 54 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 55 | OF THE POSSIBILITY OF SUCH DAMAGE. 56 | 57 | -------------------------------------------------------------------- 58 | 59 | This software consists of voluntary contributions made by many 60 | individuals on behalf of the PHP Group. 61 | 62 | The PHP Group can be contacted via Email at group@php.net. 63 | 64 | For more information on the PHP Group and the PHP project, 65 | please see . 66 | 67 | This product includes the Zend Engine, freely available at 68 | . 69 | -------------------------------------------------------------------------------- /src/memcache_standard_hash.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2007 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.0 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_0.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 | | Authors: Antony Dovgal | 16 | | Mikael Johansson | 17 | +----------------------------------------------------------------------+ 18 | */ 19 | 20 | /* $Id$ */ 21 | 22 | #ifdef HAVE_CONFIG_H 23 | #include "config.h" 24 | #endif 25 | 26 | #include "php.h" 27 | #include "php_memcache.h" 28 | 29 | ZEND_EXTERN_MODULE_GLOBALS(memcache) 30 | 31 | typedef struct mmc_standard_state { 32 | int num_servers; 33 | mmc_t **buckets; 34 | int num_buckets; 35 | mmc_hash_function_t *hash; 36 | } mmc_standard_state_t; 37 | 38 | void *mmc_standard_create_state(mmc_hash_function_t *hash) /* {{{ */ 39 | { 40 | mmc_standard_state_t *state = emalloc(sizeof(mmc_standard_state_t)); 41 | ZEND_SECURE_ZERO(state, sizeof(mmc_standard_state_t)); 42 | state->hash = hash; 43 | return state; 44 | } 45 | /* }}} */ 46 | 47 | void mmc_standard_free_state(void *s) /* {{{ */ 48 | { 49 | mmc_standard_state_t *state = s; 50 | if (state != NULL) { 51 | if (state->buckets != NULL) { 52 | efree(state->buckets); 53 | } 54 | efree(state); 55 | } 56 | } 57 | /* }}} */ 58 | 59 | mmc_t *mmc_standard_find_server(void *s, const char *key, unsigned int key_len) /* {{{ */ 60 | { 61 | mmc_standard_state_t *state = s; 62 | 63 | if (state->num_servers > 1) { 64 | /* "new-style" hash */ 65 | unsigned int hash = (mmc_hash(state->hash, key, key_len) >> 16) & 0x7fff; 66 | return state->buckets[(hash ? hash : 1) % state->num_buckets]; 67 | } 68 | 69 | return state->buckets[0]; 70 | } 71 | /* }}} */ 72 | 73 | void mmc_standard_add_server(void *s, mmc_t *mmc, unsigned int weight) /* {{{ */ 74 | { 75 | mmc_standard_state_t *state = s; 76 | int i; 77 | 78 | /* add weight number of buckets for this server */ 79 | state->buckets = erealloc(state->buckets, sizeof(*state->buckets) * (state->num_buckets + weight)); 80 | 81 | for (i=0; ibuckets[state->num_buckets + i] = mmc; 83 | } 84 | 85 | state->num_buckets += weight; 86 | state->num_servers++; 87 | } 88 | /* }}} */ 89 | 90 | mmc_hash_strategy_t mmc_standard_hash = { 91 | mmc_standard_create_state, 92 | mmc_standard_free_state, 93 | mmc_standard_find_server, 94 | mmc_standard_add_server 95 | }; 96 | 97 | /* 98 | * Local variables: 99 | * tab-width: 4 100 | * c-basic-offset: 4 101 | * End: 102 | * vim600: noet sw=4 ts=4 fdm=marker 103 | * vim<600: noet sw=4 ts=4 104 | */ 105 | -------------------------------------------------------------------------------- /src/memcache_queue.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2004 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.0 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_0.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 | | Authors: Antony Dovgal | 16 | | Mikael Johansson | 17 | +----------------------------------------------------------------------+ 18 | */ 19 | 20 | /* $Id$ */ 21 | 22 | #ifdef HAVE_CONFIG_H 23 | #include "config.h" 24 | #endif 25 | 26 | #include "php.h" 27 | #include "memcache_queue.h" 28 | 29 | MMC_QUEUE_INLINE void mmc_queue_push(mmc_queue_t *queue, void *ptr) { 30 | if (mmc_queue_contains(queue, ptr)) return; 31 | 32 | if (queue->len >= queue->alloc) { 33 | int increase = 1 + MMC_QUEUE_PREALLOC; 34 | queue->alloc += increase; 35 | queue->items = erealloc(queue->items, sizeof(*queue->items) * queue->alloc); 36 | 37 | /* move tail segment downwards */ 38 | if (queue->head < queue->tail) { 39 | memmove(queue->items + queue->tail + increase, queue->items + queue->tail, (queue->alloc - queue->tail - increase) * sizeof(*queue->items)); 40 | queue->tail += increase; 41 | } 42 | } 43 | 44 | if (queue->len) { 45 | queue->head++; 46 | 47 | if (queue->head >= queue->alloc) { 48 | queue->head = 0; 49 | } 50 | } 51 | 52 | queue->items[queue->head] = ptr; 53 | queue->len++; 54 | } 55 | 56 | MMC_QUEUE_INLINE void *mmc_queue_pop(mmc_queue_t *queue) { 57 | if (queue->len) { 58 | void *ptr; 59 | 60 | ptr = queue->items[queue->tail]; 61 | queue->len--; 62 | 63 | if (queue->len) { 64 | queue->tail++; 65 | 66 | if (queue->tail >= queue->alloc) { 67 | queue->tail = 0; 68 | } 69 | } 70 | 71 | return ptr; 72 | } 73 | return NULL; 74 | } 75 | 76 | MMC_QUEUE_INLINE int mmc_queue_contains(mmc_queue_t *queue, void *ptr) { 77 | if (queue != NULL) { 78 | int i; 79 | 80 | for (i=0; i < queue->len; i++) { 81 | if (mmc_queue_item(queue, i) == ptr) { 82 | return 1; 83 | } 84 | } 85 | } 86 | 87 | return 0; 88 | } 89 | 90 | MMC_QUEUE_INLINE void mmc_queue_free(mmc_queue_t *queue) { 91 | if (queue->items != NULL) { 92 | efree(queue->items); 93 | } 94 | ZEND_SECURE_ZERO(queue, sizeof(*queue)); 95 | } 96 | 97 | MMC_QUEUE_INLINE void mmc_queue_copy(mmc_queue_t *target, mmc_queue_t *source) { 98 | if (target->alloc != source->alloc) { 99 | target->alloc = source->alloc; 100 | target->items = erealloc(target->items, sizeof(*target->items) * target->alloc); 101 | } 102 | 103 | memcpy(target->items, source->items, sizeof(*source->items) * source->alloc); 104 | target->head = source->head; 105 | target->tail = source->tail; 106 | target->len = source->len; 107 | } 108 | 109 | MMC_QUEUE_INLINE void mmc_queue_remove(mmc_queue_t *queue, void *ptr) { 110 | void *item; 111 | mmc_queue_t original = *queue; 112 | mmc_queue_release(queue); 113 | 114 | while ((item = mmc_queue_pop(&original)) != NULL) { 115 | if (item != ptr) { 116 | mmc_queue_push(queue, item); 117 | } 118 | } 119 | 120 | mmc_queue_free(&original); 121 | } 122 | 123 | /* 124 | * Local variables: 125 | * tab-width: 4 126 | * c-basic-offset: 4 127 | * End: 128 | * vim600: noet sw=4 ts=4 fdm=marker 129 | * vim<600: noet sw=4 ts=4 130 | */ 131 | -------------------------------------------------------------------------------- /src/php_memcache.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2007 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.0 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_0.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 | | Authors: Antony Dovgal | 16 | | Mikael Johansson | 17 | +----------------------------------------------------------------------+ 18 | */ 19 | 20 | /* $Id$ */ 21 | 22 | #ifndef PHP_MEMCACHE_H 23 | #define PHP_MEMCACHE_H 24 | 25 | extern zend_module_entry memcache_module_entry; 26 | #define phpext_memcache_ptr &memcache_module_entry 27 | 28 | #ifdef PHP_WIN32 29 | #define PHP_MEMCACHE_API __declspec(dllexport) 30 | #else 31 | #define PHP_MEMCACHE_API 32 | #endif 33 | 34 | #include "memcache_pool.h" 35 | 36 | PHP_RINIT_FUNCTION(memcache); 37 | PHP_MINIT_FUNCTION(memcache); 38 | PHP_MSHUTDOWN_FUNCTION(memcache); 39 | PHP_MINFO_FUNCTION(memcache); 40 | 41 | PHP_NAMED_FUNCTION(zif_memcache_pool_connect); 42 | PHP_NAMED_FUNCTION(zif_memcache_pool_addserver); 43 | PHP_NAMED_FUNCTION(zif_memcache_pool_findserver); 44 | 45 | PHP_FUNCTION(memcache_connect); 46 | PHP_FUNCTION(memcache_pconnect); 47 | PHP_FUNCTION(memcache_add_server); 48 | PHP_FUNCTION(memcache_set_server_params); 49 | PHP_FUNCTION(memcache_set_failure_callback); 50 | PHP_FUNCTION(memcache_get_server_status); 51 | PHP_FUNCTION(memcache_get_version); 52 | PHP_FUNCTION(memcache_add); 53 | PHP_FUNCTION(memcache_set); 54 | PHP_FUNCTION(memcache_replace); 55 | PHP_FUNCTION(memcache_cas); 56 | PHP_FUNCTION(memcache_append); 57 | PHP_FUNCTION(memcache_prepend); 58 | PHP_FUNCTION(memcache_get); 59 | PHP_FUNCTION(memcache_delete); 60 | PHP_FUNCTION(memcache_debug); 61 | PHP_FUNCTION(memcache_get_stats); 62 | PHP_FUNCTION(memcache_get_extended_stats); 63 | PHP_FUNCTION(memcache_set_compress_threshold); 64 | PHP_FUNCTION(memcache_increment); 65 | PHP_FUNCTION(memcache_decrement); 66 | PHP_FUNCTION(memcache_close); 67 | PHP_FUNCTION(memcache_flush); 68 | PHP_FUNCTION(memcache_set_sasl_auth_data); 69 | 70 | #define PHP_MEMCACHE_VERSION "8.2" 71 | 72 | #define MMC_DEFAULT_TIMEOUT 1 /* seconds */ 73 | #define MMC_DEFAULT_RETRY 15 /* retry failed server after x seconds */ 74 | #define MMC_DEFAULT_CACHEDUMP_LIMIT 100 /* number of entries */ 75 | 76 | #define MEMCACHE_IS_CALLABLE(cb_zv, flags, cb_sp) zend_is_callable((cb_zv), (flags), (cb_sp)) 77 | #define MEMCACHE_LIST_INSERT(list, val) zend_list_insert((list), (val)) 78 | 79 | /* internal functions */ 80 | mmc_t *mmc_find_persistent(const char *, int, unsigned short, unsigned short, double, int); 81 | int mmc_value_handler_single(const char *, unsigned int, zval *, unsigned int, unsigned long, void *); 82 | int mmc_value_handler_multi(const char *, unsigned int, zval *, unsigned int, unsigned long, void *); 83 | int mmc_stored_handler(mmc_t *, mmc_request_t *, int, const char *, unsigned int, void *); 84 | int mmc_numeric_response_handler(mmc_t *, mmc_request_t *, int, const char *, unsigned int, void *); 85 | 86 | /* session handler struct */ 87 | #if HAVE_MEMCACHE_SESSION 88 | #include "ext/session/php_session.h" 89 | 90 | extern ps_module ps_mod_memcache; 91 | #define ps_memcache_ptr &ps_mod_memcache 92 | 93 | PS_FUNCS(memcache); 94 | #endif 95 | 96 | #endif /* PHP_MEMCACHE_H */ 97 | 98 | /* 99 | * Local variables: 100 | * tab-width: 4 101 | * c-basic-offset: 4 102 | * End: 103 | * vim600: noet sw=4 ts=4 fdm=marker 104 | * vim<600: noet sw=4 ts=4 105 | */ 106 | -------------------------------------------------------------------------------- /tests/033.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | memcache->addServer() with server failure callback 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | setServerParams($host, $tcp_port, 1, -1, false, '_callback_server_failure'); 19 | } 20 | 21 | // Test function callback using addServer() 22 | $memcache = new Memcache(); 23 | $memcache->addServer($nonExistingHost, $nonExistingPort, false, 1, 1, 15, true, '_callback_server_failure'); 24 | 25 | $result1 = $memcache->set('test_key', 'test-032-01'); 26 | var_dump($result1); 27 | 28 | class MemcachedFailureHandler { 29 | function _callback_server_failure($host, $tcp_port, $udp_port, $error, $errnum) { 30 | var_dump($host); 31 | var_dump($tcp_port); 32 | var_dump($udp_port); 33 | var_dump($error); 34 | var_dump($errnum); 35 | } 36 | } 37 | 38 | // Test OO callback using setServerParams() 39 | $memcache = new Memcache(); 40 | $memcache->addServer($nonExistingHost, $nonExistingPort, false); 41 | $result2 = $memcache->setServerParams($nonExistingHost, $nonExistingPort, 1, 15, true, 42 | array(new MemcachedFailureHandler(), '_callback_server_failure')); 43 | var_dump($result2); 44 | 45 | $result3 = $memcache->set('test_key', 'test-032-01'); 46 | var_dump($result3); 47 | 48 | // Test giving non-existing callback to addServer() 49 | $memcache = new Memcache(); 50 | $result4 = @$memcache->addServer($nonExistingHost, $nonExistingPort, false, 1, 1, 15, true, 'non_existing_user_function'); 51 | var_dump($result4); 52 | 53 | // Test giving non-existing callback to setServerParams() 54 | $memcache = new Memcache(); 55 | $memcache->addServer($nonExistingHost, $nonExistingPort, false); 56 | $result5 = @$memcache->setServerParams($nonExistingHost, $nonExistingPort, 1, 15, true, 'non_existing_user_function'); 57 | var_dump($result5); 58 | 59 | // Test self-referencing callback 60 | class MyMemcache extends Memcache { 61 | function _callback_server_failure($host, $tcp_port, $udp_port, $error, $errnum) { 62 | var_dump($host); 63 | } 64 | } 65 | $memcache = new MyMemcache(); 66 | $memcache->addServer($nonExistingHost, $nonExistingPort, false); 67 | $result6 = $memcache->setServerParams($nonExistingHost, $nonExistingPort, 1, 15, true, 68 | array($memcache, '_callback_server_failure')); 69 | $result7 = @$memcache->set('test_key', 'test-032-01'); 70 | 71 | var_dump($result6); 72 | var_dump($result7); 73 | 74 | // Test resetting callback to null 75 | $memcache = new Memcache(); 76 | $memcache->addServer($nonExistingHost, $nonExistingPort, false, 1, 1, 15, true, '_callback_server_failure'); 77 | $result8 = $memcache->setServerParams($nonExistingHost, $nonExistingPort, 1, 15, true, null); 78 | $result9 = @$memcache->set('test_key', 'test-032-01'); 79 | 80 | var_dump($result8); 81 | var_dump($result9); 82 | 83 | // Test MemcachePool::setFailureCallback() 84 | function _callback_server_failure2($host, $tcp_port, $udp_port, $error, $errnum) { 85 | var_dump($error); 86 | } 87 | 88 | $memcache = new MemcachePool(); 89 | $memcache->addServer($nonExistingHost, $nonExistingPort); 90 | $result8 = $memcache->setFailureCallback('_callback_server_failure2'); 91 | $result9 = @$memcache->set('test_key', 'test-032-01'); 92 | 93 | var_dump($result8); 94 | var_dump($result9); 95 | 96 | $result8 = $memcache->setFailureCallback(null); 97 | $result9 = @$memcache->set('test_key', 'test-032-01'); 98 | 99 | var_dump($result8); 100 | var_dump($result9); 101 | 102 | // Test refcount 103 | function test_connect() { 104 | global $mc, $nonExistingHost, $nonExistingPort; 105 | $mc = new Memcache(); 106 | $mc->addServer($nonExistingHost, $nonExistingPort, false, 1, 1, 15, true, '_callback_server_failure2'); 107 | } 108 | 109 | test_connect(); 110 | $result10 = $mc->set('test_key', 'test-032-02'); 111 | var_dump($result10); 112 | 113 | ?> 114 | --EXPECTF-- 115 | string(%d) "%s" 116 | int(%d) 117 | int(%d) 118 | string(%d) "Connection %s" 119 | int(%d) 120 | bool(false) 121 | bool(true) 122 | string(%d) "%s" 123 | int(%d) 124 | int(%d) 125 | string(%d) "Connection %s" 126 | int(%d) 127 | bool(false) 128 | bool(false) 129 | bool(false) 130 | string(%d) "%s" 131 | bool(true) 132 | bool(false) 133 | bool(true) 134 | bool(false) 135 | string(%d) "%s" 136 | bool(true) 137 | bool(false) 138 | bool(true) 139 | bool(false) 140 | string(%d) "Connection %s" 141 | bool(false) 142 | -------------------------------------------------------------------------------- /config9.m4: -------------------------------------------------------------------------------- 1 | dnl 2 | dnl $Id$ 3 | dnl 4 | 5 | PHP_ARG_ENABLE(memcache, whether to enable memcache support, 6 | [ --enable-memcache Enable memcache support]) 7 | 8 | PHP_ARG_ENABLE(memcache-session, whether to enable memcache session handler support, 9 | [ --disable-memcache-session Disable memcache session handler support], yes, no) 10 | 11 | if test -z "$PHP_ZLIB_DIR"; then 12 | PHP_ARG_WITH(zlib-dir, for the location of ZLIB, 13 | [ --with-zlib-dir[=DIR] memcache: Set the path to ZLIB install prefix.], no, no) 14 | fi 15 | 16 | if test -z "$PHP_DEBUG"; then 17 | AC_ARG_ENABLE(debug, 18 | [ --enable-debug compile with debugging symbols],[ 19 | PHP_DEBUG=$enableval 20 | ],[ 21 | PHP_DEBUG=no 22 | ]) 23 | fi 24 | 25 | if test "$PHP_MEMCACHE" != "no"; then 26 | 27 | if test "$PHP_ZLIB_DIR" != "no" && test "$PHP_ZLIB_DIR" != "yes"; then 28 | if test -f "$PHP_ZLIB_DIR/include/zlib/zlib.h"; then 29 | PHP_ZLIB_DIR="$PHP_ZLIB_DIR" 30 | PHP_ZLIB_INCDIR="$PHP_ZLIB_DIR/include/zlib" 31 | elif test -f "$PHP_ZLIB_DIR/include/zlib.h"; then 32 | PHP_ZLIB_DIR="$PHP_ZLIB_DIR" 33 | PHP_ZLIB_INCDIR="$PHP_ZLIB_DIR/include" 34 | else 35 | AC_MSG_ERROR([Can't find ZLIB headers under "$PHP_ZLIB_DIR"]) 36 | fi 37 | else 38 | for i in /usr/local /usr; do 39 | if test -f "$i/include/zlib/zlib.h"; then 40 | PHP_ZLIB_DIR="$i" 41 | PHP_ZLIB_INCDIR="$i/include/zlib" 42 | elif test -f "$i/include/zlib.h"; then 43 | PHP_ZLIB_DIR="$i" 44 | PHP_ZLIB_INCDIR="$i/include" 45 | fi 46 | done 47 | fi 48 | 49 | dnl # zlib 50 | AC_MSG_CHECKING([for the location of zlib]) 51 | if test "$PHP_ZLIB_DIR" = "no"; then 52 | AC_MSG_ERROR([memcache support requires ZLIB. Use --with-zlib-dir= to specify prefix where ZLIB include and library are located]) 53 | else 54 | AC_MSG_RESULT([$PHP_ZLIB_DIR]) 55 | if test "z$PHP_LIBDIR" != "z"; then 56 | dnl PHP5+ 57 | PHP_ADD_LIBRARY_WITH_PATH(z, $PHP_ZLIB_DIR/$PHP_LIBDIR, MEMCACHE_SHARED_LIBADD) 58 | else 59 | dnl PHP4 60 | PHP_ADD_LIBRARY_WITH_PATH(z, $PHP_ZLIB_DIR/lib, MEMCACHE_SHARED_LIBADD) 61 | fi 62 | PHP_ADD_INCLUDE($PHP_ZLIB_INCDIR) 63 | PHP_SUBST(MEMCACHE_SHARED_LIBADD) 64 | fi 65 | 66 | AC_MSG_CHECKING(PHP version) 67 | if test -d $abs_srcdir/src ; then 68 | dnl # only when for PECL, not for PHP 69 | export OLD_CPPFLAGS="$CPPFLAGS" 70 | export CPPFLAGS="$CPPFLAGS $INCLUDES" 71 | AC_TRY_COMPILE([#include ], [ 72 | #if PHP_MAJOR_VERSION < 7 73 | #error "PHP < 7" 74 | #endif 75 | ], [ 76 | subdir=src 77 | AC_MSG_RESULT([PHP 7.x]) 78 | ], 79 | AC_MSG_ERROR([PHP 7.x required for pecl-php-memcache ver 4+. Use pecl-php-memcache ver 3.x for PHP 5.x.]) 80 | ) 81 | export CPPFLAGS="$OLD_CPPFLAGS" 82 | else 83 | AC_MSG_ERROR([unknown]) 84 | fi 85 | 86 | if test "$PHP_MEMCACHE_SESSION" != "no"; then 87 | AC_MSG_CHECKING([for session includes]) 88 | session_inc_path="" 89 | 90 | if test -f "$abs_srcdir/include/php/ext/session/php_session.h"; then 91 | session_inc_path="$abs_srcdir/include/php" 92 | elif test -f "$abs_srcdir/ext/session/php_session.h"; then 93 | session_inc_path="$abs_srcdir" 94 | elif test -f "$phpincludedir/ext/session/php_session.h"; then 95 | session_inc_path="$phpincludedir" 96 | else 97 | for i in php php4 php5 php6; do 98 | if test -f "$prefix/include/$i/ext/session/php_session.h"; then 99 | session_inc_path="$prefix/include/$i" 100 | fi 101 | done 102 | fi 103 | 104 | if test "$session_inc_path" = ""; then 105 | AC_MSG_ERROR([Cannot find php_session.h]) 106 | else 107 | AC_MSG_RESULT([$session_inc_path]) 108 | fi 109 | fi 110 | 111 | SOURCES_EX="memcache.c memcache_pool.c memcache_queue.c memcache_ascii_protocol.c memcache_binary_protocol.c memcache_standard_hash.c memcache_consistent_hash.c" 112 | SESSION_SOURCES_EX="memcache_session.c" 113 | 114 | SOURCES=`echo "$subdir/$SOURCES_EX" |sed "s:[ ]: $subdir/:g"` 115 | SESSION_SOURCES=`echo "$subdir/$SESSION_SOURCES_EX" |sed "s:[ ]: $subdir/:g"` 116 | 117 | AC_MSG_CHECKING([for memcache session support]) 118 | if test "$PHP_MEMCACHE_SESSION" != "no"; then 119 | AC_MSG_RESULT([enabled]) 120 | AC_DEFINE(HAVE_MEMCACHE_SESSION,1,[Whether memcache session handler is enabled]) 121 | AC_DEFINE(HAVE_MEMCACHE,1,[Whether you want memcache support]) 122 | PHP_NEW_EXTENSION(memcache, $SOURCES $SESSION_SOURCES, $ext_shared,,-I$session_inc_path) 123 | ifdef([PHP_ADD_EXTENSION_DEP], 124 | [ 125 | PHP_ADD_EXTENSION_DEP(memcache, session) 126 | ]) 127 | else 128 | AC_MSG_RESULT([disabled]) 129 | AC_DEFINE(HAVE_MEMCACHE,1,[Whether you want memcache support]) 130 | PHP_NEW_EXTENSION(memcache, $SOURCES, $ext_shared) 131 | fi 132 | 133 | if test ! -d $subdir; then 134 | mkdir $subdir 135 | fi 136 | 137 | dnl this is needed to build the extension with phpize and -Wall 138 | 139 | if test "$PHP_DEBUG" = "yes"; then 140 | CFLAGS="$CFLAGS -Wall" 141 | fi 142 | 143 | fi 144 | -------------------------------------------------------------------------------- /src/memcache_consistent_hash.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2007 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.0 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_0.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 | | Authors: Antony Dovgal | 16 | | Mikael Johansson | 17 | +----------------------------------------------------------------------+ 18 | */ 19 | 20 | /* $Id$ */ 21 | 22 | #ifdef HAVE_CONFIG_H 23 | #include "config.h" 24 | #endif 25 | 26 | #include 27 | 28 | #include "php.h" 29 | #include "php_memcache.h" 30 | 31 | ZEND_EXTERN_MODULE_GLOBALS(memcache) 32 | 33 | typedef struct mmc_consistent_point { 34 | mmc_t *server; 35 | unsigned int point; 36 | } mmc_consistent_point_t; 37 | 38 | typedef struct mmc_consistent_state { 39 | int num_servers; 40 | mmc_consistent_point_t *points; 41 | int num_points; 42 | mmc_t *buckets[MMC_CONSISTENT_BUCKETS]; 43 | int buckets_populated; 44 | mmc_hash_function_t *hash; 45 | } mmc_consistent_state_t; 46 | 47 | void *mmc_consistent_create_state(mmc_hash_function_t *hash) /* {{{ */ 48 | { 49 | mmc_consistent_state_t *state = emalloc(sizeof(mmc_consistent_state_t)); 50 | ZEND_SECURE_ZERO(state, sizeof(mmc_consistent_state_t)); 51 | state->hash = hash; 52 | return state; 53 | } 54 | /* }}} */ 55 | 56 | void mmc_consistent_free_state(void *s) /* {{{ */ 57 | { 58 | mmc_consistent_state_t *state = s; 59 | if (state != NULL) { 60 | if (state->points != NULL) { 61 | efree(state->points); 62 | } 63 | efree(state); 64 | } 65 | } 66 | /* }}} */ 67 | 68 | static int mmc_consistent_compare(const void *a, const void *b) /* {{{ */ 69 | { 70 | if (((mmc_consistent_point_t *)a)->point < ((mmc_consistent_point_t *)b)->point) { 71 | return -1; 72 | } 73 | if (((mmc_consistent_point_t *)a)->point > ((mmc_consistent_point_t *)b)->point) { 74 | return 1; 75 | } 76 | return 0; 77 | } 78 | /* }}} */ 79 | 80 | static mmc_t *mmc_consistent_find(mmc_consistent_state_t *state, unsigned int point) /* {{{ */ 81 | { 82 | int lo = 0, hi = state->num_points - 1, mid; 83 | 84 | while (1) { 85 | /* point is outside interval or lo >= hi, wrap-around */ 86 | if (point <= state->points[lo].point || point > state->points[hi].point) { 87 | return state->points[lo].server; 88 | } 89 | 90 | /* test middle point */ 91 | mid = lo + (hi - lo) / 2; 92 | 93 | /* perfect match */ 94 | if (point <= state->points[mid].point && point > (mid ? state->points[mid-1].point : 0)) { 95 | return state->points[mid].server; 96 | } 97 | 98 | /* too low, go up */ 99 | if (state->points[mid].point < point) { 100 | lo = mid + 1; 101 | } 102 | else { 103 | hi = mid - 1; 104 | } 105 | } 106 | } 107 | /* }}} */ 108 | 109 | static void mmc_consistent_populate_buckets(mmc_consistent_state_t *state) /* {{{ */ 110 | { 111 | unsigned int i, step = 0xffffffff / MMC_CONSISTENT_BUCKETS; 112 | 113 | qsort((void *)state->points, state->num_points, sizeof(mmc_consistent_point_t), mmc_consistent_compare); 114 | for (i=0; ibuckets[i] = mmc_consistent_find(state, step * i); 116 | } 117 | 118 | state->buckets_populated = 1; 119 | } 120 | /* }}} */ 121 | 122 | mmc_t *mmc_consistent_find_server(void *s, const char *key, unsigned int key_len) /* {{{ */ 123 | { 124 | mmc_consistent_state_t *state = s; 125 | 126 | if (state->num_servers > 1) { 127 | unsigned int hash; 128 | 129 | if (!state->buckets_populated) { 130 | mmc_consistent_populate_buckets(state); 131 | } 132 | 133 | hash = mmc_hash(state->hash, key, key_len); 134 | return state->buckets[hash % MMC_CONSISTENT_BUCKETS]; 135 | } 136 | 137 | return state->points[0].server; 138 | } 139 | /* }}} */ 140 | 141 | void mmc_consistent_add_server(void *s, mmc_t *mmc, unsigned int weight) /* {{{ */ 142 | { 143 | mmc_consistent_state_t *state = s; 144 | int i, key_len, points = weight * MMC_CONSISTENT_POINTS; 145 | unsigned int seed = state->hash->init(), hash; 146 | 147 | /* buffer for "host:port-i\0" */ 148 | char *key = emalloc(strlen(mmc->host) + MAX_LENGTH_OF_LONG * 2 + 3); 149 | key_len = sprintf(key, "%s:%d-", mmc->host, mmc->tcp.port); 150 | seed = state->hash->combine(seed, key, key_len); 151 | 152 | /* add weight * MMC_CONSISTENT_POINTS number of points for this server */ 153 | state->points = erealloc(state->points, sizeof(*state->points) * (state->num_points + points)); 154 | 155 | for (i=0; ihash->finish(state->hash->combine(seed, key, key_len)); 158 | state->points[state->num_points + i].server = mmc; 159 | state->points[state->num_points + i].point = hash; 160 | } 161 | 162 | state->num_points += points; 163 | state->num_servers++; 164 | state->buckets_populated = 0; 165 | 166 | efree(key); 167 | } 168 | /* }}} */ 169 | 170 | mmc_hash_strategy_t mmc_consistent_hash = { 171 | mmc_consistent_create_state, 172 | mmc_consistent_free_state, 173 | mmc_consistent_find_server, 174 | mmc_consistent_add_server 175 | }; 176 | 177 | /* 178 | * Local variables: 179 | * tab-width: 4 180 | * c-basic-offset: 4 181 | * End: 182 | * vim600: noet sw=4 ts=4 fdm=marker 183 | * vim<600: noet sw=4 ts=4 184 | */ 185 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is an official repository for pecl-memcache plugin since 2019. 2 | 3 | This repository contains modified pecl-memcache plugin ported to PHP8, 4 | which was originally developed for the need of hosting company in Slovakia (Websupport.sk). 5 | 6 | The latest release is 8.2 (released: 2023-04-30) with support for PHP 8.0+ (incl. PHP 8.2). 7 | 8 | Please use version 4.0.5.1 (released: 2020-12-19) for PHP 7.x from branch NON_BLOCKING_IO_php7. 9 | 10 | See: https://github.com/websupport-sk/pecl-memcache/releases 11 | See also release on PECL: https://pecl.php.net/package/memcache 12 | 13 | Feel free to use it and post patches. 14 | 15 | 16 | 17 | Original README before 2019: 18 | 19 | memcached module for PHP 20 | ------------------------ 21 | This module requires zlib library, used for on-the-fly data (de)compression. 22 | Also, you'll need memcached to use it =) 23 | 24 | The memcached website is here: 25 | http://www.danga.com/memcached/ 26 | 27 | You will probably need libevent to install memcached: 28 | You can download it here: http://www.monkey.org/~provos/libevent/ 29 | 30 | How to run tests: 31 | 1. sh tests/memcache.sh 32 | 2. TEST_PHP_EXECUTABLE=/usr/local/bin/php php -dextension=modules/memcache.so run-tests.php -d extension=modules/memcache.so 33 | 34 | 35 | New API in 3.0 36 | ------------------------ 37 | 38 | Version 3 introduces a new class "MemcachePool" which implements the new API, the 39 | old class "Memcache" is still retained (but is deprecated) with the same interface 40 | for backwards compatibility. Please note that you need a new memcached version to 41 | use the CAS, default value to increment/decrement, append and prepend, and binary 42 | protocol features. 43 | 44 | New INI directives are available to allow control over protocol, redundancy and hash 45 | strategy selection. These are 46 | 47 | # The binary protocol results in less traffic and is more efficient 48 | # for the client and server to generate/parse 49 | 50 | memcache.protocol = {ascii, binary} # default ascii 51 | 52 | # When enabled the client sends requests to N servers in parallel, resulting in 53 | # a somewhat crude reduncancy or mirroring, suitable when used as a session 54 | # storage. 55 | # 56 | # If data integrity is of greater importance a real replicating memcached 57 | # backend such as "repcached" (http://sourceforge.net/projects/repcached/) is 58 | # recommended 59 | 60 | memcache.redundancy = # default 1 61 | memcache.session_redundancy = # default 2 62 | 63 | # Hash strategy and function selection. The consistent hashing strategy 64 | # is now the default as it allows servers to be added and removed from 65 | # the pool without resulting in all or most keys being re-mapped to 66 | # other server (ie. voiding the cache) 67 | 68 | memcache.hash_strategy = {standard, consistent} # default consistent 69 | memcache.hash_function = {crc32, fnv} # default crc32 70 | 71 | # Compression is enabled by default, the threshold which control the minimum 72 | # string length which triggers compresssion can be changed as 73 | 74 | memcache.compress_threshold = # default 20000 75 | 76 | 77 | The directives are used by the MemcachePool constructor so you can instantiate 78 | several pools with different settings by using ini_set() creativly. For example 79 | 80 | ini_set('memcache.protocol', 'binary'); 81 | 82 | $binarypool = new MemcachePool(); 83 | $binarypool->addServer(...) 84 | 85 | ini_set('memcache.protocol', 'ascii'); 86 | ini_set('memcache.redundancy', '2'); 87 | 88 | $redundantpool = new MemcachePool(); 89 | $redundantpool->addServer(...) 90 | 91 | ini_set('memcache.redundancy', '1'); 92 | 93 | 94 | The new interface looks like 95 | 96 | class MemcachePool() { 97 | bool connect(string host, int tcp_port = 11211, int udp_port = 0, bool persistent = true, int weight = 1, int timeout = 1, int retry_interval = 15) 98 | bool addServer(string host, int tcp_port = 11211, int udp_port = 0, bool persistent = true, int weight = 1, int timeout = 1, int retry_interval = 15, bool status = true) 99 | bool setServerParams(string host, int tcp_port = 11211, int timeout = 1, int retry_interval = 15, bool status = true) 100 | 101 | /** 102 | * Supports fetching flags and CAS values 103 | */ 104 | mixed get(mixed key, mixed &flags = null, mixed &cas = null) 105 | 106 | /** 107 | * Supports multi-set, for example 108 | * $memcache->set(array('key1' => 'val1', 'key2' => 'val1'), null, 0, 60) 109 | */ 110 | bool add(mixed key, mixed var = null, int flag = 0, int exptime = 0) 111 | bool set(mixed key, mixed var = null, int flag = 0, int exptime = 0) 112 | bool replace(mixed key, mixed var = null, int flag = 0, int exptime = 0) 113 | 114 | /** 115 | * Compare-and-Swap, uses the CAS param from MemcachePool::get() 116 | */ 117 | bool cas(mixed key, mixed var = null, int flag = 0, int exptime = 0, int cas = 0) 118 | 119 | /** 120 | * Prepends/appends a value to an existing one 121 | */ 122 | bool append(mixed key, mixed var = null, int flag = 0, int exptime = 0) 123 | bool prepend(mixed key, mixed var = null, int flag = 0, int exptime = 0) 124 | 125 | /** 126 | * Supports multi-key operations, for example 127 | * $memcache->delete(array('key1', 'key2')) 128 | */ 129 | bool delete(mixed key, int exptime = 0) 130 | 131 | /** 132 | * Supports multi-key operations, for example 133 | * $memcache->increment(array('key1', 'key2'), 1, 0, 0) 134 | * 135 | * The new defval (default value) and exptime (expiration time) are used 136 | * if the key doesn't already exist. They must be supplied (even if 0) for 137 | * this to be enabled. 138 | * 139 | * Returns an integer with the new value if key is a string 140 | * Returns an array of integers if the key is an array 141 | */ 142 | mixed increment(mixed key, int value = 1, int defval = 0, int exptime = 0) 143 | mixed decrement(mixed key, int value = 1, int defval = 0, int exptime = 0) 144 | 145 | /** 146 | * Assigns a pool-specific failure callback which will be called when 147 | * a request fails. May be null in order to disable callbacks. The callback 148 | * receive arguments like 149 | * 150 | * function mycallback($host, $tcp_port, $udp_port, $error, $errnum) 151 | * 152 | * Where $host and $error are strings or null, the other params are integers. 153 | */ 154 | bool setFailureCallback(function callback) 155 | 156 | /** 157 | * Locates the server a given would be hashed to 158 | * 159 | * Returns a string "hostname:port" on success 160 | * Returns false on failure such as invalid key 161 | */ 162 | string findServer(string key) 163 | } 164 | 165 | 166 | Maintainers: 167 | Herman J. Radtke III hradtke at php dot net 168 | -------------------------------------------------------------------------------- /profile.php: -------------------------------------------------------------------------------- 1 | $value) 64 | $memcache->set($key, $value); 65 | foreach ($ints as $key => $value) 66 | $memcache->set($key, $value); 67 | 68 | $ts = microtime(true); 69 | 70 | for ($i=0; $i<$reps; $i++) { 71 | $function($memcache); 72 | } 73 | 74 | $ts2 = microtime(true); 75 | 76 | printf('%-15s', sprintf('%u (%.2fs)', round(($count*$reps) / ($ts2-$ts)), round($ts2-$ts,2))); 77 | } 78 | 79 | print "\n"; 80 | } 81 | 82 | print "\n"; 83 | } 84 | 85 | // Create pools to run all tests against 86 | $memcache1 = new Memcache(); 87 | $memcache1->connect($hosts[0][0], $hosts[0][1]); 88 | $memcache1->setCompressThreshold($threshold); 89 | $pools[] = array($memcache1, 'TCP (1 server)'); 90 | 91 | if (class_exists('MemcachePool')) { 92 | $memcache2 = new MemcachePool(); 93 | $memcache2->connect($hosts[0][0], $hosts[0][1], $hosts[0][2], false); 94 | $memcache2->setCompressThreshold($threshold); 95 | $pools[] = array($memcache2, 'UDP (1 server)'); 96 | } 97 | 98 | if (count($hosts) > 1) { 99 | $memcache3 = new Memcache(); 100 | foreach ($hosts as $h) 101 | $memcache3->connect($h[0], $h[1]); 102 | $memcache3->setCompressThreshold($threshold); 103 | $pools[] = array($memcache3, sprintf('TCP (%d servers)', count($hosts))); 104 | 105 | if (class_exists('MemcachePool')) { 106 | $memcache4 = new MemcachePool(); 107 | foreach ($hosts as $h) 108 | $memcache4->connect($h[0], $h[1], $h[2], false); 109 | $memcache4->setCompressThreshold($threshold); 110 | $pools[] = array($memcache4, sprintf('UDP (%d servers)', count($hosts))); 111 | } 112 | } 113 | 114 | // Create values to work with 115 | $values = array(); 116 | $ints = array(); 117 | $objects = array(); 118 | 119 | $key = 'test_key'; 120 | $key2 = 'test_key_int'; 121 | $key3 = 'test_key_obj'; 122 | 123 | for ($i=0; $i<$count; $i++) { 124 | $values[$key.$i] = $key.'_value_'.$i.str_repeat('a', $i*1000); 125 | $ints[$key2.$i] = $i; 126 | $objects[$key3.$i] = new TestClass(new TestClass(new TestClass(str_repeat('a', $i*100)))); 127 | } 128 | 129 | print "Measures are in keys-per-second (higher is better) with number of seconds\nspent in test within parentheses (lower is better).\n\n"; 130 | 131 | // Configuration 132 | printf("memcache.hash_strategy: %s\n", ini_get('memcache.hash_strategy')); 133 | printf("memcache.chunk_size: %u\n", ini_get('memcache.chunk_size')); 134 | printf("memcache.protocol: %s\n", ini_get('memcache.protocol')); 135 | printf("memcache.compress_threshold: %s\n", $threshold); 136 | printf("servers: %d\n", count($hosts)); 137 | printf("iterations: %d\n", $reps); 138 | printf("keys per iteration: %d\n", $count); 139 | print "\n"; 140 | 141 | $ts = time(); 142 | run_tests($pools, $tests1); 143 | run_tests($pools, $tests2); 144 | 145 | if (class_exists('MemcachePool')) 146 | run_tests($pools, $tests3); 147 | 148 | //run_tests($pools, $tests4); 149 | 150 | $ts2 = time(); 151 | 152 | printf("total time: %d minutes, %d seconds\n", floor(($ts2 - $ts)/60), ($ts2 - $ts)%60); 153 | 154 | // Tests 155 | class TestClass { 156 | function __construct($obj = null) { 157 | $this->v1 = str_repeat('abc', 25); 158 | $this->v2 = 123; 159 | $this->v3 = 123.123; 160 | $this->v4 = array('abc', 123); 161 | $this->obj = $obj; 162 | } 163 | } 164 | 165 | function test_set_single($memcache) { 166 | global $values; 167 | foreach ($values as $key => $value) 168 | $memcache->set($key, $value); 169 | } 170 | 171 | function test_set_multi($memcache) { 172 | global $values; 173 | $memcache->set($values); 174 | } 175 | 176 | function test_set_single_obj($memcache) { 177 | global $objects; 178 | foreach ($objects as $key => $value) 179 | $memcache->set($key, $value); 180 | } 181 | 182 | function test_set_multi_obj($memcache) { 183 | global $objects; 184 | $memcache->set($objects); 185 | } 186 | 187 | function test_get_single($memcache) { 188 | global $values; 189 | foreach (array_keys($values) as $key) 190 | $memcache->get($key); 191 | } 192 | 193 | function test_get_multi_obj($memcache) { 194 | global $objects; 195 | $memcache->get(array_keys($objects)); 196 | } 197 | 198 | function test_get_single_obj($memcache) { 199 | global $objects; 200 | foreach (array_keys($objects) as $key) 201 | $memcache->get($key); 202 | } 203 | 204 | function test_get_multi($memcache) { 205 | global $values; 206 | $memcache->get(array_keys($values)); 207 | } 208 | 209 | function test_delete_single($memcache) { 210 | global $values; 211 | foreach (array_keys($values) as $key) 212 | $memcache->delete($key); 213 | } 214 | 215 | function test_delete_multi($memcache) { 216 | global $values; 217 | $memcache->delete(array_keys($values)); 218 | } 219 | 220 | function test_incr_single($memcache) { 221 | global $ints; 222 | foreach (array_keys($ints) as $key) 223 | $memcache->increment($key); 224 | } 225 | 226 | function test_incr_multi($memcache) { 227 | global $ints; 228 | $memcache->increment(array_keys($ints)); 229 | } 230 | 231 | function test_hash_strategy($memcache) { 232 | global $hosts; 233 | $mc = new Memcache(); 234 | foreach ($hosts as $h) 235 | $mc->addServer($h[0], $h[1], false, 100); 236 | $mc->set('test_key', '1'); 237 | } 238 | -------------------------------------------------------------------------------- /src/memcache_ascii_protocol.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2007 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.0 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_0.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 | | Authors: Antony Dovgal | 16 | | Mikael Johansson | 17 | +----------------------------------------------------------------------+ 18 | */ 19 | 20 | /* $Id$ */ 21 | 22 | #ifdef HAVE_CONFIG_H 23 | #include "config.h" 24 | #endif 25 | 26 | #include "memcache_pool.h" 27 | #include "Zend/zend_smart_string.h" 28 | 29 | typedef struct mmc_ascii_request { 30 | mmc_request_t base; /* enable cast to mmc_request_t */ 31 | struct { /* stores value info while the body is being read */ 32 | char key[MMC_MAX_KEY_LEN + 1]; 33 | unsigned int flags; 34 | unsigned long length; 35 | unsigned long cas; /* CAS counter */ 36 | } value; 37 | } mmc_ascii_request_t; 38 | 39 | static int mmc_server_read_value(mmc_t *, mmc_request_t *); 40 | 41 | static int mmc_stream_get_line(mmc_stream_t *io, char **line) /* 42 | attempts to read a line from server, returns the line size or 0 if no complete line was available {{{ */ 43 | { 44 | size_t returned_len = 0; 45 | io->readline(io, io->input.value + io->input.idx, MMC_BUFFER_SIZE - io->input.idx, &returned_len); 46 | io->input.idx += returned_len; 47 | 48 | if (io->input.idx && io->input.value[io->input.idx - 1] == '\n') { 49 | int result = io->input.idx; 50 | *line = io->input.value; 51 | io->input.idx = 0; 52 | return result; 53 | } 54 | 55 | return 0; 56 | } 57 | /* }}} */ 58 | 59 | static int mmc_request_check_response(char *line, int line_len) /* 60 | checks for response status and error codes {{{ */ 61 | { 62 | int response; 63 | 64 | // remove newline and thus prevent passing it to userland 65 | if (line_len >= 2 && line[line_len - 2] == '\r' && line[line_len - 1] == '\n') { 66 | line[line_len - 2] = '\0'; 67 | } 68 | 69 | if (mmc_str_left(line, "OK", line_len, sizeof("OK")-1) || 70 | mmc_str_left(line, "STORED", line_len, sizeof("STORED")-1) || 71 | mmc_str_left(line, "DELETED", line_len, sizeof("DELETED")-1)) 72 | { 73 | response = MMC_OK; 74 | } 75 | else if (mmc_str_left(line, "NOT_FOUND", line_len, sizeof("NOT_FOUND")-1)) { 76 | response = MMC_RESPONSE_NOT_FOUND; 77 | } 78 | else if ( 79 | mmc_str_left(line, "NOT_STORED", line_len, sizeof("NOT_STORED")-1) || 80 | mmc_str_left(line, "EXISTS", line_len, sizeof("EXISTS")-1)) 81 | { 82 | response = MMC_RESPONSE_EXISTS; 83 | } 84 | else if (mmc_str_left(line, "SERVER_ERROR out of memory", line_len, sizeof("SERVER_ERROR out of memory")-1)) { 85 | response = MMC_RESPONSE_OUT_OF_MEMORY; 86 | } 87 | else if (mmc_str_left(line, "SERVER_ERROR object too large", line_len, sizeof("SERVER_ERROR object too large")-1)) { 88 | response = MMC_RESPONSE_TOO_LARGE; 89 | } 90 | else if ( 91 | mmc_str_left(line, "ERROR", line_len, sizeof("ERROR")-1) || 92 | mmc_str_left(line, "SERVER_ERROR", line_len, sizeof("SERVER_ERROR")-1)) 93 | { 94 | response = MMC_RESPONSE_ERROR; 95 | } 96 | else if (mmc_str_left(line, "CLIENT_ERROR", line_len, sizeof("CLIENT_ERROR")-1)) { 97 | response = MMC_RESPONSE_CLIENT_ERROR; 98 | } 99 | else { 100 | response = MMC_RESPONSE_UNKNOWN; 101 | } 102 | 103 | return response; 104 | } 105 | 106 | static int mmc_request_parse_response(mmc_t *mmc, mmc_request_t *request) /* 107 | reads a generic response header and delegates it to response_handler {{{ */ 108 | { 109 | char *line; 110 | int line_len = mmc_stream_get_line(request->io, &line); 111 | 112 | if (line_len > 0) { 113 | int response = mmc_request_check_response(line, line_len); 114 | return request->response_handler(mmc, request, response, line, line_len - (sizeof("\r\n")-1), request->response_handler_param); 115 | } 116 | 117 | return MMC_REQUEST_MORE; 118 | } 119 | /* }}}*/ 120 | 121 | static int mmc_request_parse_mutate(mmc_t *mmc, mmc_request_t *request) /* 122 | reads and parses the response header {{{ */ 123 | { 124 | char *line; 125 | int line_len; 126 | 127 | line_len = mmc_stream_get_line(request->io, &line); 128 | if (line_len > 0) { 129 | long lval; 130 | zval value; 131 | 132 | int response = mmc_request_check_response(line, line_len); 133 | if (response != MMC_RESPONSE_UNKNOWN) { 134 | return request->response_handler(mmc, request, response, line, line_len - (sizeof("\r\n")-1), request->response_handler_param); 135 | } 136 | 137 | if (sscanf(line, "%ld", &lval) < 1) { 138 | return mmc_server_failure(mmc, request->io, "Malformed VALUE header", 0); 139 | } 140 | 141 | ZVAL_LONG(&value, lval); 142 | return request->value_handler(request->key, request->key_len, &value, 0, 0, request->value_handler_param); 143 | } 144 | 145 | return MMC_REQUEST_MORE; 146 | } 147 | /* }}}*/ 148 | 149 | static int mmc_request_parse_value(mmc_t *mmc, mmc_request_t *request) /* 150 | reads and parses the VALUE response header and then reads the value body {{{ */ 151 | { 152 | char *line; 153 | int line_len; 154 | mmc_ascii_request_t *req = (mmc_ascii_request_t *)request; 155 | 156 | line_len = mmc_stream_get_line(request->io, &line); 157 | if (line_len > 0) { 158 | if (mmc_str_left(line, "END", line_len, sizeof("END")-1)) { 159 | return MMC_REQUEST_DONE; 160 | } 161 | 162 | if (sscanf(line, MMC_VALUE_HEADER, req->value.key, &(req->value.flags), &(req->value.length), &(req->value.cas)) < 3) { 163 | return mmc_server_failure(mmc, request->io, "Malformed VALUE header", 0); 164 | } 165 | 166 | /* memory for data + \r\n */ 167 | mmc_buffer_alloc(&(request->readbuf), req->value.length + 2); 168 | 169 | /* allow read_value handler to read the value body */ 170 | request->parse = mmc_server_read_value; 171 | 172 | /* read more, php streams buffer input which must be read if available */ 173 | return MMC_REQUEST_AGAIN; 174 | } 175 | 176 | return MMC_REQUEST_MORE; 177 | } 178 | /* }}}*/ 179 | 180 | static int mmc_server_read_value(mmc_t *mmc, mmc_request_t *request) /* 181 | read the value body into the buffer {{{ */ 182 | { 183 | mmc_ascii_request_t *req = (mmc_ascii_request_t *)request; 184 | request->readbuf.idx += 185 | request->io->read(request->io, request->readbuf.value.c + request->readbuf.idx, req->value.length + 2 - request->readbuf.idx); 186 | 187 | /* done reading? */ 188 | if (request->readbuf.idx >= req->value.length + 2) { 189 | int result; 190 | 191 | /* allow parse_value to read next VALUE or END line */ 192 | request->parse = mmc_request_parse_value; 193 | mmc_buffer_reset(&(request->readbuf)); 194 | 195 | result = mmc_unpack_value( 196 | mmc, request, &(request->readbuf), req->value.key, strlen(req->value.key), 197 | req->value.flags, req->value.cas, req->value.length); 198 | 199 | /* request more data (END line) */ 200 | if (result == MMC_REQUEST_DONE) { 201 | return MMC_REQUEST_AGAIN; 202 | } 203 | 204 | return result; 205 | } 206 | 207 | return MMC_REQUEST_MORE; 208 | } 209 | /* }}}*/ 210 | 211 | static mmc_request_t *mmc_ascii_create_request() /* {{{ */ 212 | { 213 | mmc_ascii_request_t *request = emalloc(sizeof(mmc_ascii_request_t)); 214 | ZEND_SECURE_ZERO(request, sizeof(*request)); 215 | return (mmc_request_t *)request; 216 | } 217 | /* }}} */ 218 | 219 | static void mmc_ascii_clone_request(mmc_request_t *clone, mmc_request_t *request) /* {{{ */ 220 | {} 221 | /* }}} */ 222 | 223 | static void mmc_ascii_reset_request(mmc_request_t *request) /* {{{ */ 224 | { 225 | mmc_ascii_request_t *req = (mmc_ascii_request_t *)request; 226 | req->value.cas = 0; 227 | mmc_request_reset(request); 228 | } 229 | /* }}} */ 230 | 231 | static void mmc_ascii_begin_get(mmc_request_t *request, int op) /* {{{ */ 232 | { 233 | request->parse = mmc_request_parse_value; 234 | 235 | if (op == MMC_OP_GETS) { 236 | smart_string_appendl(&(request->sendbuf.value), "gets", sizeof("gets")-1); 237 | } 238 | else { 239 | smart_string_appendl(&(request->sendbuf.value), "get", sizeof("get")-1); 240 | } 241 | } 242 | /* }}} */ 243 | 244 | static void mmc_ascii_append_get(mmc_request_t *request, zval *zkey, const char *key, unsigned int key_len) /* {{{ */ 245 | { 246 | smart_string_appendl(&(request->sendbuf.value), " ", 1); 247 | smart_string_appendl(&(request->sendbuf.value), key, key_len); 248 | } 249 | /* }}} */ 250 | 251 | static void mmc_ascii_end_get(mmc_request_t *request) /* {{{ */ 252 | { 253 | smart_string_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1); 254 | } 255 | /* }}} */ 256 | 257 | static void mmc_ascii_get(mmc_request_t *request, int op, zval *zkey, const char *key, unsigned int key_len) /* {{{ */ 258 | { 259 | mmc_ascii_begin_get(request, op); 260 | mmc_ascii_append_get(request, zkey, key, key_len); 261 | mmc_ascii_end_get(request); 262 | } 263 | /* }}} */ 264 | 265 | static int mmc_ascii_store( 266 | mmc_pool_t *pool, mmc_request_t *request, int op, const char *key, unsigned int key_len, 267 | unsigned int flags, unsigned int exptime, unsigned long cas, zval *value) /* {{{ */ 268 | { 269 | int status; 270 | mmc_buffer_t buffer; 271 | request->parse = mmc_request_parse_response; 272 | 273 | ZEND_SECURE_ZERO(&buffer, sizeof(buffer)); 274 | status = mmc_pack_value(pool, &buffer, value, &flags); 275 | 276 | if (status != MMC_OK) { 277 | return status; 278 | } 279 | 280 | switch (op) { 281 | case MMC_OP_SET: 282 | smart_string_appendl(&(request->sendbuf.value), "set", sizeof("set")-1); 283 | break; 284 | case MMC_OP_ADD: 285 | smart_string_appendl(&(request->sendbuf.value), "add", sizeof("add")-1); 286 | break; 287 | case MMC_OP_REPLACE: 288 | smart_string_appendl(&(request->sendbuf.value), "replace", sizeof("replace")-1); 289 | break; 290 | case MMC_OP_CAS: 291 | smart_string_appendl(&(request->sendbuf.value), "cas", sizeof("cas")-1); 292 | break; 293 | case MMC_OP_APPEND: 294 | smart_string_appendl(&(request->sendbuf.value), "append", sizeof("append")-1); 295 | break; 296 | case MMC_OP_PREPEND: 297 | smart_string_appendl(&(request->sendbuf.value), "prepend", sizeof("prepend")-1); 298 | break; 299 | default: 300 | return MMC_REQUEST_FAILURE; 301 | } 302 | 303 | smart_string_appendl(&(request->sendbuf.value), " ", 1); 304 | smart_string_appendl(&(request->sendbuf.value), key, key_len); 305 | smart_string_appendl(&(request->sendbuf.value), " ", 1); 306 | smart_string_append_unsigned(&(request->sendbuf.value), flags); 307 | smart_string_appendl(&(request->sendbuf.value), " ", 1); 308 | smart_string_append_unsigned(&(request->sendbuf.value), exptime); 309 | smart_string_appendl(&(request->sendbuf.value), " ", 1); 310 | smart_string_append_unsigned(&(request->sendbuf.value), buffer.value.len); 311 | 312 | if (op == MMC_OP_CAS) { 313 | smart_string_appendl(&(request->sendbuf.value), " ", 1); 314 | smart_string_append_unsigned(&(request->sendbuf.value), cas); 315 | } 316 | 317 | smart_string_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1); 318 | smart_string_appendl(&(request->sendbuf.value), buffer.value.c, buffer.value.len); 319 | smart_string_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1); 320 | 321 | mmc_buffer_free(&buffer); 322 | return MMC_OK; 323 | } 324 | /* }}} */ 325 | 326 | static void mmc_ascii_delete(mmc_request_t *request, const char *key, unsigned int key_len, unsigned int exptime) /* {{{ */ 327 | { 328 | request->parse = mmc_request_parse_response; 329 | 330 | smart_string_appendl(&(request->sendbuf.value), "delete", sizeof("delete")-1); 331 | smart_string_appendl(&(request->sendbuf.value), " ", 1); 332 | smart_string_appendl(&(request->sendbuf.value), key, key_len); 333 | 334 | if (exptime > 0) { 335 | smart_string_appendl(&(request->sendbuf.value), " ", 1); 336 | smart_string_append_unsigned(&(request->sendbuf.value), exptime); 337 | } 338 | 339 | smart_string_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1); 340 | } 341 | /* }}} */ 342 | 343 | static void mmc_ascii_mutate(mmc_request_t *request, zval *zkey, const char *key, unsigned int key_len, long value, long defval, int defval_used, unsigned int exptime) /* {{{ */ 344 | { 345 | request->parse = mmc_request_parse_mutate; 346 | 347 | if (value >= 0) { 348 | smart_string_appendl(&(request->sendbuf.value), "incr", sizeof("incr")-1); 349 | } 350 | else { 351 | smart_string_appendl(&(request->sendbuf.value), "decr", sizeof("decr")-1); 352 | } 353 | 354 | smart_string_appendl(&(request->sendbuf.value), " ", 1); 355 | smart_string_appendl(&(request->sendbuf.value), key, key_len); 356 | smart_string_appendl(&(request->sendbuf.value), " ", 1); 357 | smart_string_append_unsigned(&(request->sendbuf.value), value >= 0 ? value : -value); 358 | smart_string_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1); 359 | } 360 | /* }}} */ 361 | 362 | static void mmc_ascii_flush(mmc_request_t *request, unsigned int exptime) /* {{{ */ 363 | { 364 | request->parse = mmc_request_parse_response; 365 | smart_string_appendl(&(request->sendbuf.value), "flush_all", sizeof("flush_all")-1); 366 | 367 | if (exptime > 0) { 368 | smart_string_appendl(&(request->sendbuf.value), " ", 1); 369 | smart_string_append_unsigned(&(request->sendbuf.value), exptime); 370 | } 371 | 372 | smart_string_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1); 373 | } 374 | /* }}} */ 375 | 376 | static void mmc_ascii_version(mmc_request_t *request) /* {{{ */ 377 | { 378 | request->parse = mmc_request_parse_response; 379 | smart_string_appendl(&(request->sendbuf.value), "version\r\n", sizeof("version\r\n")-1); 380 | } 381 | /* }}} */ 382 | 383 | static void mmc_ascii_stats(mmc_request_t *request, const char *type, long slabid, long limit) /* {{{ */ 384 | { 385 | char *cmd; 386 | unsigned int cmd_len; 387 | request->parse = mmc_request_parse_response; 388 | 389 | if (slabid) { 390 | cmd_len = spprintf(&cmd, 0, "stats %s %ld %ld\r\n", type, slabid, limit); 391 | } 392 | else if (type) { 393 | cmd_len = spprintf(&cmd, 0, "stats %s\r\n", type); 394 | } 395 | else { 396 | cmd_len = spprintf(&cmd, 0, "stats\r\n"); 397 | } 398 | 399 | smart_string_appendl(&(request->sendbuf.value), cmd, cmd_len); 400 | efree(cmd); 401 | } 402 | /* }}} */ 403 | 404 | static void mmc_set_sasl_auth_data(mmc_pool_t *pool, mmc_request_t *request, const char *user, const char *password) /* {{{ */ 405 | { 406 | /* stats not supported */ 407 | } 408 | /* }}} */ 409 | 410 | mmc_protocol_t mmc_ascii_protocol = { 411 | mmc_ascii_create_request, 412 | mmc_ascii_clone_request, 413 | mmc_ascii_reset_request, 414 | mmc_request_free, 415 | mmc_ascii_get, 416 | mmc_ascii_begin_get, 417 | mmc_ascii_append_get, 418 | mmc_ascii_end_get, 419 | mmc_ascii_store, 420 | mmc_ascii_delete, 421 | mmc_ascii_mutate, 422 | mmc_ascii_flush, 423 | mmc_ascii_version, 424 | mmc_ascii_stats, 425 | mmc_set_sasl_auth_data 426 | }; 427 | 428 | /* 429 | * Local variables: 430 | * tab-width: 4 431 | * c-basic-offset: 4 432 | * End: 433 | * vim600: noet sw=4 ts=4 fdm=marker 434 | * vim<600: noet sw=4 ts=4 435 | */ 436 | -------------------------------------------------------------------------------- /src/memcache_session.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2004 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.0 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_0.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 | | Authors: Antony Dovgal | 16 | | Mikael Johansson | 17 | +----------------------------------------------------------------------+ 18 | */ 19 | 20 | /* $Id$ */ 21 | 22 | #ifdef HAVE_CONFIG_H 23 | #include "config.h" 24 | #endif 25 | 26 | #include 27 | #include "php.h" 28 | #include "php_ini.h" 29 | #include "php_variables.h" 30 | 31 | #include "SAPI.h" 32 | #include "Zend/zend_smart_string.h" 33 | #include "ext/standard/url.h" 34 | #include "ext/session/php_session.h" 35 | #ifdef PHP_WIN32 36 | #include "win32/time.h" 37 | #endif 38 | #include "php_memcache.h" 39 | 40 | static int mmc_deleted_handler(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param); 41 | 42 | ZEND_EXTERN_MODULE_GLOBALS(memcache) 43 | 44 | ps_module ps_mod_memcache = { 45 | PS_MOD(memcache) 46 | }; 47 | 48 | /* {{{ PS_OPEN_FUNC 49 | */ 50 | PS_OPEN_FUNC(memcache) 51 | { 52 | mmc_pool_t *pool; 53 | mmc_t *mmc; 54 | 55 | php_url *url; 56 | zval params, *param; 57 | int i, j, path_len; 58 | 59 | const char *path = MEMCACHE_G(session_save_path); 60 | if (!path) { 61 | /* allow to work with standard session.save_path option 62 | and session_save_path function */ 63 | path = save_path; 64 | } 65 | if (!path) { 66 | PS_SET_MOD_DATA(NULL); 67 | ZEND_ASSERT(0 && "open"); 68 | return FAILURE; 69 | } 70 | 71 | pool = mmc_pool_new(); 72 | 73 | for (i=0,path_len=strlen(path); iquery != NULL) { 114 | array_init(¶ms); 115 | /* parse parameters */ 116 | #if PHP_VERSION_ID < 70300 117 | sapi_module.treat_data(PARSE_STRING, estrdup(url->query), ¶ms); 118 | #else 119 | sapi_module.treat_data(PARSE_STRING, estrdup(ZSTR_VAL(url->query)), ¶ms); 120 | #endif 121 | if ((param = zend_hash_str_find(Z_ARRVAL(params), "persistent", sizeof("persistent")-1)) != NULL) { 122 | convert_to_boolean_ex(param); 123 | persistent = Z_TYPE_P(param) == IS_TRUE; 124 | } 125 | 126 | if ((param = zend_hash_str_find(Z_ARRVAL(params), "udp_port", sizeof("udp_port")-1)) != NULL) { 127 | convert_to_long_ex(param); 128 | udp_port = Z_LVAL_P(param); 129 | } 130 | 131 | if ((param = zend_hash_str_find(Z_ARRVAL(params), "weight", sizeof("weight")-1)) != NULL) { 132 | convert_to_long_ex(param); 133 | weight = Z_LVAL_P(param); 134 | } 135 | 136 | if ((param = zend_hash_str_find(Z_ARRVAL(params), "timeout", sizeof("timeout")-1)) != NULL) { 137 | convert_to_double_ex(param); 138 | timeout = Z_DVAL_P(param); 139 | } 140 | 141 | if ((param = zend_hash_str_find(Z_ARRVAL(params), "retry_interval", sizeof("retry_interval")-1)) != NULL) { 142 | convert_to_long_ex(param); 143 | retry_interval = Z_LVAL_P(param); 144 | } 145 | 146 | zval_ptr_dtor(¶ms); 147 | } 148 | #if PHP_VERSION_ID < 70300 149 | if (url->scheme && url->path && !strcmp(url->scheme, "file")) { 150 | char *host; 151 | int host_len = spprintf(&host, 0, "unix://%s", url->path); 152 | #else 153 | if (url->scheme && url->path && !strcmp(ZSTR_VAL(url->scheme), "file")) { 154 | char *host; 155 | int host_len = spprintf(&host, 0, "unix://%s", ZSTR_VAL(url->path)); 156 | #endif 157 | 158 | /* chop off trailing :0 port specifier */ 159 | if (!strcmp(host + host_len - 2, ":0")) { 160 | host_len -= 2; 161 | } 162 | if (persistent) { 163 | mmc = mmc_find_persistent(host, host_len, 0, 0, timeout, retry_interval); 164 | } 165 | else { 166 | mmc = mmc_server_new(host, host_len, 0, 0, 0, timeout, retry_interval); 167 | } 168 | 169 | efree(host); 170 | } 171 | else { 172 | if (url->host == NULL || weight <= 0 || timeout <= 0) { 173 | php_url_free(url); 174 | mmc_pool_free(pool); 175 | PS_SET_MOD_DATA(NULL); 176 | return FAILURE; 177 | } 178 | 179 | #if PHP_VERSION_ID < 70300 180 | if (persistent) { 181 | mmc = mmc_find_persistent(url->host, strlen(url->host), url->port, udp_port, timeout, retry_interval); 182 | } 183 | else { 184 | mmc = mmc_server_new(url->host, strlen(url->host), url->port, udp_port, 0, timeout, retry_interval); 185 | } 186 | #else 187 | if (persistent) { 188 | mmc = mmc_find_persistent(ZSTR_VAL(url->host), ZSTR_LEN(url->host), url->port, udp_port, timeout, retry_interval); 189 | } 190 | else { 191 | mmc = mmc_server_new(ZSTR_VAL(url->host), ZSTR_LEN(url->host), url->port, udp_port, 0, timeout, retry_interval); 192 | } 193 | #endif 194 | 195 | } 196 | 197 | mmc_pool_add(pool, mmc, weight); 198 | php_url_free(url); 199 | } 200 | } 201 | 202 | if (pool->num_servers) { 203 | PS_SET_MOD_DATA(pool); 204 | return SUCCESS; 205 | } 206 | 207 | mmc_pool_free(pool); 208 | PS_SET_MOD_DATA(NULL); 209 | ZEND_ASSERT(0 &&"open"); 210 | return FAILURE; 211 | } 212 | /* }}} */ 213 | 214 | /* {{{ PS_CLOSE_FUNC 215 | */ 216 | PS_CLOSE_FUNC(memcache) 217 | { 218 | mmc_pool_t *pool = PS_GET_MOD_DATA(); 219 | 220 | if (pool) { 221 | mmc_pool_free(pool); 222 | PS_SET_MOD_DATA(NULL); 223 | } 224 | 225 | return SUCCESS; 226 | } 227 | /* }}} */ 228 | 229 | static int php_mmc_session_read_request( 230 | mmc_pool_t *pool, zval *zkey, zval **lockparam, zval *addparam, zval **dataparam, 231 | mmc_request_t **lockreq, mmc_request_t **addreq, mmc_request_t **datareq) /* {{{ */ 232 | { 233 | mmc_request_t *lreq, *areq, *dreq; 234 | zval lockvalue; 235 | 236 | /* increment request which stores the response int using value_handler_single */ 237 | lreq = mmc_pool_request( 238 | pool, MMC_PROTO_TCP, mmc_numeric_response_handler, lockparam[0], 239 | mmc_pool_failover_handler_null, NULL); 240 | lreq->value_handler = mmc_value_handler_single; 241 | lreq->value_handler_param = lockparam; 242 | 243 | /* add request which should fail if lock has been incremented */ 244 | areq = mmc_pool_request( 245 | pool, MMC_PROTO_TCP, mmc_stored_handler, addparam, 246 | mmc_pool_failover_handler_null, NULL); 247 | 248 | /* request to fetch the session data */ 249 | dreq = mmc_pool_request_get( 250 | pool, MMC_PROTO_TCP, mmc_value_handler_single, dataparam, 251 | mmc_pool_failover_handler_null, NULL); 252 | 253 | /* prepare key */ 254 | if (mmc_prepare_key_ex(Z_STRVAL_P(zkey), Z_STRLEN_P(zkey), dreq->key, &(dreq->key_len), MEMCACHE_G(session_key_prefix)) != MMC_OK) { 255 | mmc_pool_release(pool, lreq); 256 | mmc_pool_release(pool, areq); 257 | mmc_pool_release(pool, dreq); 258 | return MMC_REQUEST_FAILURE; 259 | } 260 | 261 | /* append .lock to key */ 262 | memcpy(lreq->key, dreq->key, dreq->key_len); 263 | strcpy(lreq->key + dreq->key_len, ".lock"); 264 | 265 | memcpy(areq->key, dreq->key, dreq->key_len); 266 | strcpy(areq->key + dreq->key_len, ".lock"); 267 | 268 | lreq->key_len = areq->key_len = dreq->key_len + sizeof(".lock")-1; 269 | 270 | /* value for add request */ 271 | ZVAL_LONG(&lockvalue, 1); 272 | 273 | /* build requests */ 274 | pool->protocol->mutate(lreq, zkey, lreq->key, lreq->key_len, 1, 1, 1, MEMCACHE_G(lock_timeout)); 275 | pool->protocol->store(pool, areq, MMC_OP_ADD, areq->key, areq->key_len, 0, MEMCACHE_G(lock_timeout), 0, &lockvalue); 276 | pool->protocol->get(dreq, MMC_OP_GET, zkey, dreq->key, dreq->key_len); 277 | 278 | *lockreq = lreq; 279 | *addreq = areq; 280 | *datareq = dreq; 281 | return MMC_OK; 282 | } 283 | /* }}} */ 284 | 285 | /* {{{ PS_READ_FUNC 286 | */ 287 | PS_READ_FUNC(memcache) 288 | { 289 | *val = ZSTR_EMPTY_ALLOC(); 290 | mmc_pool_t *pool = PS_GET_MOD_DATA(); 291 | 292 | if (pool != NULL) { 293 | zval lockresult, addresult, dataresult, zkey; 294 | zval *lockparam[3]; 295 | zval *dataparam[3]; 296 | 297 | mmc_t *mmc; 298 | mmc_request_t *lockrequest, *addrequest, *datarequest; 299 | mmc_queue_t skip_servers = {0}; 300 | unsigned int last_index = 0, prev_index = 0, timeout = 5000; 301 | long remainingtime = MEMCACHE_G(lock_timeout) * 1000000 * 2; 302 | 303 | lockparam[0] = &lockresult; 304 | lockparam[1] = NULL; 305 | lockparam[2] = NULL; 306 | 307 | dataparam[0] = &dataresult; 308 | dataparam[1] = NULL; 309 | dataparam[2] = NULL; 310 | 311 | ZVAL_STR(&zkey, key); 312 | 313 | do { 314 | /* first request tries to increment lock */ 315 | ZVAL_NULL(&lockresult); 316 | 317 | /* second request tries to add lock, succeeds if lock doesn't exist (not relevant for binary 318 | * protocol where increment takes a default value */ 319 | ZVAL_NULL(&addresult); 320 | 321 | /* third request fetches the data, data is only valid if either of the lock requests succeeded */ 322 | ZVAL_NULL(&dataresult); 323 | 324 | /* create requests */ 325 | if (php_mmc_session_read_request(pool, &zkey, lockparam, &addresult, dataparam, &lockrequest, &addrequest, &datarequest) != MMC_OK) { 326 | return FAILURE; 327 | } 328 | 329 | /* find next server in line */ 330 | prev_index = last_index; 331 | mmc = mmc_pool_find_next(pool, datarequest->key, datarequest->key_len, &skip_servers, &last_index); 332 | 333 | /* schedule the requests */ 334 | if (!mmc_server_valid(mmc) || 335 | mmc_pool_schedule(pool, mmc, lockrequest) != MMC_OK || 336 | /*pool->protocol != &mmc_binary_protocol && */mmc_pool_schedule(pool, mmc, addrequest) != MMC_OK || 337 | mmc_pool_schedule(pool, mmc, datarequest) != MMC_OK) { 338 | mmc_pool_release(pool, lockrequest); 339 | mmc_pool_release(pool, addrequest); 340 | mmc_pool_release(pool, datarequest); 341 | mmc_queue_push(&skip_servers, mmc); 342 | continue; 343 | } 344 | 345 | /* execute requests */ 346 | mmc_pool_run(pool); 347 | 348 | if ((Z_TYPE(lockresult) == IS_LONG && Z_LVAL(lockresult) == 1) || ((Z_TYPE(addresult) == IS_TRUE || Z_TYPE(addresult) == IS_FALSE) && Z_TYPE(addresult) == IS_TRUE)) { 349 | if (Z_TYPE(dataresult) == IS_STRING) { 350 | /* break if successfully locked with existing value */ 351 | mmc_queue_free(&skip_servers); 352 | *val = zend_string_init(Z_STRVAL(dataresult), Z_STRLEN(dataresult), 0); 353 | zval_ptr_dtor(&dataresult); 354 | return SUCCESS; 355 | } 356 | 357 | /* if missing value, skip this server and try next */ 358 | zval_dtor(&dataresult); 359 | mmc_queue_push(&skip_servers, mmc); 360 | 361 | /* if it is the last server in pool and connection was ok return success and empty string due to php70 changes */ 362 | if (skip_servers.len == pool->num_servers && skip_servers.len < MEMCACHE_G(session_redundancy)) { 363 | *val = ZSTR_EMPTY_ALLOC(); 364 | mmc_queue_free(&skip_servers); 365 | return SUCCESS; 366 | } 367 | } 368 | else { 369 | zval_dtor(&dataresult); 370 | 371 | /* if missing lock, back off and retry same server */ 372 | last_index = prev_index; 373 | usleep(timeout); 374 | remainingtime -= timeout; 375 | timeout *= 2; 376 | 377 | /* max value to usleep() is 1 second */ 378 | if (timeout > 1000000) { 379 | timeout = 1000000; 380 | } 381 | } 382 | } while (skip_servers.len < MEMCACHE_G(session_redundancy) && skip_servers.len < pool->num_servers && remainingtime > 0); 383 | 384 | mmc_queue_free(&skip_servers); 385 | zval_dtor(&dataresult); 386 | 387 | return SUCCESS; 388 | } 389 | else 390 | { 391 | return FAILURE; 392 | } 393 | } 394 | /* }}} */ 395 | 396 | /* {{{ PS_WRITE_FUNC 397 | */ 398 | PS_WRITE_FUNC(memcache) 399 | { 400 | mmc_pool_t *pool = PS_GET_MOD_DATA(); 401 | 402 | if (pool != NULL) { 403 | zval lockresult, dataresult, lockvalue, value; 404 | 405 | mmc_t *mmc; 406 | mmc_request_t *lockrequest, *datarequest; 407 | mmc_queue_t skip_servers = {0}; 408 | size_t lifetime = (size_t)time(NULL) + INI_INT("session.gc_maxlifetime"); 409 | unsigned int last_index = 0; 410 | 411 | ZVAL_NULL(&lockresult); 412 | ZVAL_NULL(&dataresult); 413 | 414 | do { 415 | /* allocate requests */ 416 | datarequest = mmc_pool_request( 417 | pool, MMC_PROTO_TCP, mmc_stored_handler, &dataresult, 418 | mmc_pool_failover_handler_null, NULL); 419 | 420 | if (mmc_prepare_key_ex(ZSTR_VAL(key), ZSTR_LEN(key), datarequest->key, &(datarequest->key_len), MEMCACHE_G(session_key_prefix)) != MMC_OK) { 421 | mmc_pool_release(pool, datarequest); 422 | if (lockrequest != NULL) { 423 | mmc_pool_release(pool, lockrequest); 424 | } 425 | break; 426 | } 427 | 428 | /* append .lock to key */ 429 | lockrequest = mmc_pool_request( 430 | pool, MMC_PROTO_TCP, mmc_stored_handler, &lockresult, 431 | mmc_pool_failover_handler_null, NULL); 432 | 433 | memcpy(lockrequest->key, datarequest->key, datarequest->key_len); 434 | strcpy(lockrequest->key + datarequest->key_len, ".lock"); 435 | lockrequest->key_len = datarequest->key_len + sizeof(".lock")-1; 436 | 437 | ZVAL_LONG(&lockvalue, 0); 438 | ZVAL_STR(&value, val); 439 | 440 | /* assemble commands to store data and reset lock */ 441 | if (pool->protocol->store(pool, datarequest, MMC_OP_SET, datarequest->key, datarequest->key_len, 0, lifetime, 0, &value) != MMC_OK || 442 | pool->protocol->store(pool, lockrequest, MMC_OP_SET, lockrequest->key, lockrequest->key_len, 0, MEMCACHE_G(lock_timeout), 0, &lockvalue) != MMC_OK) { 443 | mmc_pool_release(pool, datarequest); 444 | mmc_pool_release(pool, lockrequest); 445 | mmc_queue_push(&skip_servers, mmc); 446 | break; 447 | } 448 | 449 | /* find next server in line */ 450 | mmc = mmc_pool_find_next(pool, datarequest->key, datarequest->key_len, &skip_servers, &last_index); 451 | mmc_queue_push(&skip_servers, mmc); 452 | 453 | if (!mmc_server_valid(mmc) || 454 | mmc_pool_schedule(pool, mmc, datarequest) != MMC_OK || 455 | mmc_pool_schedule(pool, mmc, lockrequest) != MMC_OK) { 456 | mmc_pool_release(pool, datarequest); 457 | mmc_pool_release(pool, lockrequest); 458 | lockrequest = NULL; 459 | continue; 460 | } 461 | } while (skip_servers.len < MEMCACHE_G(session_redundancy) && skip_servers.len < pool->num_servers); 462 | 463 | mmc_queue_free(&skip_servers); 464 | 465 | /* execute requests */ 466 | mmc_pool_run(pool); 467 | 468 | if (Z_TYPE(lockresult) == IS_TRUE && Z_TYPE(dataresult) == IS_TRUE) { 469 | return SUCCESS; 470 | } 471 | } 472 | 473 | return FAILURE; 474 | } 475 | /* }}} */ 476 | 477 | static int mmc_deleted_handler(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param) /* 478 | parses a DELETED response line, param is a zval pointer to store result into {{{ */ 479 | { 480 | if (response == MMC_OK || response == MMC_RESPONSE_NOT_FOUND) { 481 | ZVAL_TRUE((zval *)param); 482 | return MMC_REQUEST_DONE; 483 | } 484 | 485 | if (response == MMC_RESPONSE_CLIENT_ERROR) { 486 | ZVAL_FALSE((zval *)param); 487 | php_error_docref(NULL, E_NOTICE, 488 | "Server %s (tcp %d, udp %d) failed with: %s (%d)", 489 | mmc->host, mmc->tcp.port, 490 | mmc->udp.port, message, response); 491 | 492 | return MMC_REQUEST_DONE; 493 | } 494 | 495 | 496 | return mmc_request_failure(mmc, request->io, message, message_len, 0); 497 | } 498 | /* }}} */ 499 | 500 | /* {{{ PS_DESTROY_FUNC 501 | */ 502 | PS_DESTROY_FUNC(memcache) 503 | { 504 | mmc_pool_t *pool = PS_GET_MOD_DATA(); 505 | 506 | if (pool != NULL) { 507 | zval lockresult, dataresult; 508 | 509 | mmc_t *mmc; 510 | mmc_request_t *lockrequest, *datarequest; 511 | mmc_queue_t skip_servers = {0}; 512 | unsigned int last_index = 0; 513 | 514 | ZVAL_NULL(&lockresult); 515 | ZVAL_NULL(&dataresult); 516 | 517 | do { 518 | /* allocate requests */ 519 | datarequest = mmc_pool_request( 520 | pool, MMC_PROTO_TCP, mmc_deleted_handler, &dataresult, 521 | mmc_pool_failover_handler_null, NULL); 522 | 523 | if (mmc_prepare_key_ex(ZSTR_VAL(key), ZSTR_LEN(key), datarequest->key, &(datarequest->key_len), MEMCACHE_G(session_key_prefix)) != MMC_OK) { 524 | mmc_pool_release(pool, datarequest); 525 | break; 526 | } 527 | 528 | /* append .lock to key */ 529 | lockrequest = mmc_pool_request( 530 | pool, MMC_PROTO_TCP, mmc_deleted_handler, &lockresult, 531 | mmc_pool_failover_handler_null, NULL); 532 | 533 | memcpy(lockrequest->key, datarequest->key, datarequest->key_len); 534 | strcpy(lockrequest->key + datarequest->key_len, ".lock"); 535 | lockrequest->key_len = datarequest->key_len + sizeof(".lock")-1; 536 | 537 | /* assemble commands to store data and reset lock */ 538 | pool->protocol->delete(datarequest, datarequest->key, datarequest->key_len, 0); 539 | pool->protocol->delete(lockrequest, lockrequest->key, lockrequest->key_len, 0); 540 | 541 | /* find next server in line */ 542 | mmc = mmc_pool_find_next(pool, datarequest->key, datarequest->key_len, &skip_servers, &last_index); 543 | mmc_queue_push(&skip_servers, mmc); 544 | 545 | if (!mmc_server_valid(mmc) || 546 | mmc_pool_schedule(pool, mmc, datarequest) != MMC_OK || 547 | mmc_pool_schedule(pool, mmc, lockrequest) != MMC_OK) { 548 | mmc_pool_release(pool, datarequest); 549 | mmc_pool_release(pool, lockrequest); 550 | continue; 551 | } 552 | } while (skip_servers.len < MEMCACHE_G(session_redundancy) && skip_servers.len < pool->num_servers); 553 | 554 | mmc_queue_free(&skip_servers); 555 | 556 | /* execute requests */ 557 | mmc_pool_run(pool); 558 | 559 | if (Z_TYPE(lockresult) == IS_TRUE && Z_TYPE(dataresult) == IS_TRUE) { 560 | return SUCCESS; 561 | } 562 | } 563 | 564 | return FAILURE; 565 | } 566 | /* }}} */ 567 | 568 | /* {{{ PS_GC_FUNC 569 | */ 570 | PS_GC_FUNC(memcache) 571 | { 572 | return SUCCESS; 573 | } 574 | /* }}} */ 575 | 576 | /* 577 | * Local variables: 578 | * tab-width: 4 579 | * c-basic-offset: 4 580 | * End: 581 | * vim600: noet sw=4 ts=4 fdm=marker 582 | * vim<600: noet sw=4 ts=4 583 | */ 584 | 585 | -------------------------------------------------------------------------------- /src/memcache_pool.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2007 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.0 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_0.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 | | Authors: Antony Dovgal | 16 | | Mikael Johansson | 17 | +----------------------------------------------------------------------+ 18 | */ 19 | 20 | /* $Id$ */ 21 | 22 | #ifndef MEMCACHE_POOL_H 23 | #define MEMCACHE_POOL_H 24 | 25 | #ifdef HAVE_CONFIG_H 26 | #include "config.h" 27 | #endif 28 | 29 | #ifdef HAVE_SYS_TYPES_H 30 | #include 31 | #endif 32 | 33 | #ifdef HAVE_SYS_SOCKET_H 34 | #include 35 | #endif 36 | 37 | #ifdef PHP_WIN32 38 | # if defined(_MSC_VER) && _MSC_VER < 1920 39 | # include "win32/php_stdint.h" 40 | # else 41 | # include 42 | # endif 43 | #else 44 | # include 45 | #endif 46 | #include 47 | 48 | #include "php.h" 49 | #include "Zend/zend_smart_string.h" 50 | #include "memcache_queue.h" 51 | 52 | /* 53 | * Mac OS X has no MSG_NOSIGNAL but >= 10.2 comes with SO_NOSIGPIPE which is a setsockopt() option 54 | * and not a send() parameter as MSG_NOSIGNAL. OpenBSD has none of the options so we need to ignore 55 | * SIGPIPE events 56 | */ 57 | #ifndef MSG_NOSIGNAL 58 | #define MSG_NOSIGNAL 0 59 | #endif /*MSG_NOSIGNAL*/ 60 | 61 | /* use lowest byte for flags */ 62 | #define MMC_SERIALIZED 0x0001 63 | #define MMC_COMPRESSED 0x0002 64 | 65 | /* use second lowest byte to indicate data type */ 66 | #define MMC_TYPE_STRING 0x0000 67 | #define MMC_TYPE_BOOL 0x0100 68 | /*#define MMC_TYPE_INT 0x0200*/ 69 | #define MMC_TYPE_LONG 0x0300 70 | /*#define MMC_TYPE_DATE 0x0400*/ 71 | /*#define MMC_TYPE_BYTE 0x0500*/ 72 | /*#define MMC_TYPE_FLOAT 0x0600*/ 73 | #define MMC_TYPE_DOUBLE 0x0700 74 | /*#define MMC_TYPE_BLOB 0x0800*/ 75 | 76 | /* These flags are set here to reserve the for use by users of the pecl-memcache in set and get */ 77 | #define MMC_RESERVED_APPLICATIONDEFINEDFLAG_12 0x10000 /* not used in pecl-memcache code */ 78 | #define MMC_RESERVED_APPLICATIONDEFINEDFLAG_13 0x20000 /* not used in pecl-memcache code */ 79 | #define MMC_RESERVED_APPLICATIONDEFINEDFLAG_14 0x40000 /* not used in pecl-memcache code */ 80 | #define MMC_RESERVED_APPLICATIONDEFINEDFLAG_15 0x80000 /* not used in pecl-memcache code */ 81 | 82 | #define MMC_BUFFER_SIZE 4096 83 | #define MMC_MAX_UDP_LEN 1400 84 | #define MMC_MAX_KEY_LEN 250 85 | #define MMC_VALUE_HEADER "VALUE %250s %u %lu %lu" /* keep in sync with MMC_MAX_KEY_LEN */ 86 | 87 | #define MMC_COMPRESSION_LEVEL Z_DEFAULT_COMPRESSION 88 | #define MMC_DEFAULT_COMPRESS 20000 /* minimum 20k byte values for auto compression to be used */ 89 | #define MMC_DEFAULT_SAVINGS 0.2 /* minimum 20% savings for compression to be used */ 90 | 91 | #define MMC_PROTO_TCP 0 92 | #define MMC_PROTO_UDP 1 93 | 94 | #define MMC_STATUS_FAILED -1 /* server/stream is down */ 95 | #define MMC_STATUS_DISCONNECTED 0 /* stream is disconnected, ie. new connection */ 96 | #define MMC_STATUS_UNKNOWN 1 /* stream is in unknown state, ie. non-validated persistent connection */ 97 | #define MMC_STATUS_CONNECTED 2 /* stream is connected */ 98 | 99 | #define MMC_OK 0 100 | 101 | #define MMC_REQUEST_FAILURE -1 /* operation failed, failover to other server */ 102 | #define MMC_REQUEST_DONE 0 /* ok result, or reading/writing is done */ 103 | #define MMC_REQUEST_MORE 1 /* more data follows in another packet, try select() again */ 104 | #define MMC_REQUEST_AGAIN 2 /* more data follows in this packet, try read/write again */ 105 | #define MMC_REQUEST_RETRY 3 /* retry/reschedule request */ 106 | 107 | #define MMC_RESPONSE_UNKNOWN -2 108 | #define MMC_RESPONSE_ERROR -1 109 | #define MMC_RESPONSE_NOT_FOUND 0x01 /* same as binary protocol */ 110 | #define MMC_RESPONSE_EXISTS 0x02 /* same as binary protocol */ 111 | #define MMC_RESPONSE_TOO_LARGE 0x03 /* same as binary protocol */ 112 | #define MMC_RESPONSE_NOT_STORED 0x05 /* same as binary protocol */ 113 | #define MMC_RESPONSE_CLIENT_ERROR 0x06 114 | #define MMC_RESPONSE_UNKNOWN_CMD 0x81 /* same as binary protocol */ 115 | #define MMC_RESPONSE_OUT_OF_MEMORY 0x82 /* same as binary protocol */ 116 | 117 | #define MMC_STANDARD_HASH 1 118 | #define MMC_CONSISTENT_HASH 2 119 | #define MMC_HASH_CRC32 1 /* CRC32 hash function */ 120 | #define MMC_HASH_FNV1A 2 /* FNV-1a hash function */ 121 | 122 | #define MMC_API_SELECT 1 123 | #define MMC_API_POLL 2 124 | 125 | #define MMC_CONSISTENT_POINTS 160 /* points per server */ 126 | #define MMC_CONSISTENT_BUCKETS 1024 /* number of precomputed buckets, should be power of 2 */ 127 | 128 | /* io buffer */ 129 | typedef struct mmc_buffer { 130 | smart_string value; 131 | unsigned int idx; /* current index */ 132 | } mmc_buffer_t; 133 | 134 | #define mmc_buffer_release(b) ZEND_SECURE_ZERO((b), sizeof(*(b))) 135 | #define mmc_buffer_reset(b) (b)->value.len = (b)->idx = 0 136 | 137 | #ifdef PHP_WIN32 138 | #define MMC_POOL_INLINE 139 | #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L 140 | /* see https://gcc.gnu.org/gcc-5/porting_to.html */ 141 | #define MMC_POOL_INLINE extern inline 142 | #else 143 | #define MMC_POOL_INLINE inline 144 | #endif 145 | 146 | void mmc_buffer_alloc(mmc_buffer_t *, unsigned int); 147 | void mmc_buffer_free(mmc_buffer_t *); 148 | 149 | /* stream handlers */ 150 | typedef struct mmc_stream mmc_stream_t; 151 | 152 | typedef size_t (*mmc_stream_read)(mmc_stream_t *stream, char *buf, size_t count); 153 | typedef char *(*mmc_stream_readline)(mmc_stream_t *stream, char *buf, size_t maxlen, size_t *retlen); 154 | 155 | /* stream container */ 156 | struct mmc_stream { 157 | php_stream *stream; 158 | int fd; /* file descriptor for select() */ 159 | unsigned short port; /* tcp/udp port stream is connected to */ 160 | int chunk_size; /* stream chunk size */ 161 | int status; /* stream status in MMC_STATUS_* status codes */ 162 | long failed; /* the timestamp the stream was marked as failed */ 163 | long retry_interval; /* seconds to wait before automatic reconnect */ 164 | mmc_buffer_t buffer; /* read buffer (when using udp) */ 165 | mmc_stream_read read; /* handles reading from stream */ 166 | mmc_stream_readline readline; /* handles reading lines from stream */ 167 | struct { /* temporary input buffer, see mmc_server_readline() */ 168 | char value[MMC_BUFFER_SIZE]; 169 | int idx; 170 | } input; 171 | }; 172 | 173 | /* request handlers */ 174 | typedef struct mmc mmc_t; 175 | typedef struct mmc_pool mmc_pool_t; 176 | typedef struct mmc_request mmc_request_t; 177 | 178 | typedef int (*mmc_request_reader)(mmc_t *mmc, mmc_request_t *request); 179 | typedef int (*mmc_request_parser)(mmc_t *mmc, mmc_request_t *request); 180 | typedef int (*mmc_request_value_handler)( 181 | const char *key, unsigned int key_len, zval *value, 182 | unsigned int flags, unsigned long cas, void *param); 183 | typedef int (*mmc_request_response_handler)( 184 | mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param); 185 | typedef int (*mmc_request_failover_handler)( 186 | mmc_pool_t *pool, mmc_t *mmc, mmc_request_t *request, void *param); 187 | 188 | void mmc_request_reset(mmc_request_t *); 189 | void mmc_request_free(mmc_request_t *); 190 | 191 | /* server request */ 192 | struct mmc_request { 193 | mmc_stream_t *io; /* stream this request is sending on */ 194 | mmc_buffer_t sendbuf; /* request buffer */ 195 | mmc_buffer_t readbuf; /* response buffer */ 196 | char key[MMC_MAX_KEY_LEN + 1]; /* key buffer to use on failover of single-key requests */ 197 | unsigned int key_len; 198 | unsigned int protocol; /* protocol encoding of request {tcp, udp} */ 199 | mmc_queue_t failed_servers; /* mmc_queue_t, servers this request has failed at */ 200 | unsigned int failed_index; /* last index probed on failure */ 201 | mmc_request_reader read; /* handles reading (and validating datagrams) */ 202 | mmc_request_parser parse; /* called to parse response payload */ 203 | mmc_request_value_handler value_handler; /* called when result value have been parsed */ 204 | void *value_handler_param; 205 | mmc_request_response_handler response_handler; /* called when a non-value response has been received */ 206 | void *response_handler_param; 207 | mmc_request_failover_handler failover_handler; /* called to perform failover of failed request */ 208 | void *failover_handler_param; 209 | struct { 210 | uint16_t reqid; /* id of the request, increasing value set by client */ 211 | uint16_t seqid; /* next expected seqid */ 212 | uint16_t total; /* number of datagrams in response */ 213 | } udp; 214 | }; 215 | 216 | /* udp protocol header */ 217 | typedef struct mmc_udp_header { 218 | uint16_t reqid; /* id of the request, increasing value set by client */ 219 | uint16_t seqid; /* index of this datagram in response */ 220 | uint16_t total; /* number of datagrams in response */ 221 | uint16_t _reserved; 222 | } mmc_udp_header_t; 223 | 224 | /* server */ 225 | struct mmc { 226 | mmc_stream_t tcp; /* main stream, might be tcp or unix socket */ 227 | mmc_stream_t udp; /* auxiliary udp connection */ 228 | mmc_request_t *sendreq; /* currently sending request, NULL if none */ 229 | mmc_request_t *readreq; /* currently reading request, NULL if none */ 230 | mmc_request_t *buildreq; /* request currently being built, NULL if none */ 231 | mmc_queue_t sendqueue; /* mmc_queue_t, requests waiting to be sent */ 232 | mmc_queue_t readqueue; /* mmc_queue_t, requests waiting to be read */ 233 | char *host; 234 | struct timeval timeout; /* network timeout for this server */ 235 | int persistent; 236 | uint16_t reqid; /* next sequential request id */ 237 | char *error; /* last error message */ 238 | int errnum; /* last error code */ 239 | }; 240 | 241 | /* wire protocol */ 242 | #define MMC_ASCII_PROTOCOL 1 243 | #define MMC_BINARY_PROTOCOL 2 244 | 245 | /* same as in binary protocol */ 246 | #define MMC_OP_GET 0x00 247 | #define MMC_OP_SET 0x01 248 | #define MMC_OP_ADD 0x02 249 | #define MMC_OP_REPLACE 0x03 250 | #define MMC_OP_GETS 0x32 251 | #define MMC_OP_CAS 0x33 252 | #define MMC_OP_APPEND 0x34 /* not supported by binary protocol */ 253 | #define MMC_OP_PREPEND 0x35 /* not supported by binary protocol */ 254 | 255 | typedef mmc_request_t * (*mmc_protocol_create_request)(); 256 | typedef void (*mmc_protocol_clone_request)(mmc_request_t *clone, mmc_request_t *request); 257 | typedef void (*mmc_protocol_reset_request)(mmc_request_t *request); 258 | typedef void (*mmc_protocol_free_request)(mmc_request_t *request); 259 | 260 | typedef void (*mmc_protocol_get)(mmc_request_t *request, int op, zval *zkey, const char *key, unsigned int key_len); 261 | typedef void (*mmc_protocol_begin_get)(mmc_request_t *request, int op); 262 | typedef void (*mmc_protocol_append_get)(mmc_request_t *request, zval *zkey, const char *key, unsigned int key_len); 263 | typedef void (*mmc_protocol_end_get)(mmc_request_t *request); 264 | 265 | typedef int (*mmc_protocol_store)( 266 | mmc_pool_t *pool, mmc_request_t *request, int op, const char *key, unsigned int key_len, 267 | unsigned int flags, unsigned int exptime, unsigned long cas, zval *value); 268 | typedef void (*mmc_protocol_delete)(mmc_request_t *request, const char *key, unsigned int key_len, unsigned int exptime); 269 | typedef void (*mmc_protocol_mutate)(mmc_request_t *request, zval *zkey, const char *key, unsigned int key_len, long value, long defval, int defval_used, unsigned int exptime); 270 | 271 | typedef void (*mmc_protocol_flush)(mmc_request_t *request, unsigned int exptime); 272 | typedef void (*mmc_protocol_stats)(mmc_request_t *request, const char *type, long slabid, long limit); 273 | typedef void (*mmc_protocol_version)(mmc_request_t *request); 274 | 275 | typedef void (*mmc_protocol_set_sasl_auth_data)(mmc_pool_t *pool, mmc_request_t *request, const char *user, const char *password); 276 | 277 | typedef struct mmc_protocol { 278 | mmc_protocol_create_request create_request; 279 | mmc_protocol_clone_request clone_request; 280 | mmc_protocol_reset_request reset_request; 281 | mmc_protocol_free_request free_request; 282 | 283 | mmc_protocol_get get; 284 | mmc_protocol_begin_get begin_get; 285 | mmc_protocol_append_get append_get; 286 | mmc_protocol_end_get end_get; 287 | 288 | mmc_protocol_store store; 289 | mmc_protocol_delete delete; 290 | mmc_protocol_mutate mutate; 291 | 292 | mmc_protocol_flush flush; 293 | mmc_protocol_version version; 294 | mmc_protocol_stats stats; 295 | 296 | mmc_protocol_set_sasl_auth_data set_sasl_auth_data; 297 | } mmc_protocol_t; 298 | 299 | extern mmc_protocol_t mmc_ascii_protocol; 300 | extern mmc_protocol_t mmc_binary_protocol; 301 | 302 | /* hashing strategy */ 303 | typedef unsigned int (*mmc_hash_function_init)(); 304 | typedef unsigned int (*mmc_hash_function_combine)(unsigned int seed, const void *key, unsigned int key_len); 305 | typedef unsigned int (*mmc_hash_function_finish)(unsigned int seed); 306 | 307 | typedef struct mmc_hash_function { 308 | mmc_hash_function_init init; 309 | mmc_hash_function_combine combine; 310 | mmc_hash_function_finish finish; 311 | } mmc_hash_function_t; 312 | 313 | extern mmc_hash_function_t mmc_hash_crc32; 314 | extern mmc_hash_function_t mmc_hash_fnv1a; 315 | 316 | #define mmc_hash(hash, key, key_len) ((hash)->finish((hash)->combine((hash)->init(), (key), (key_len)))) 317 | 318 | typedef void * (*mmc_hash_create_state)(mmc_hash_function_t *); 319 | typedef void (*mmc_hash_free_state)(void *state); 320 | typedef mmc_t * (*mmc_hash_find_server)(void *state, const char *key, unsigned int key_len); 321 | typedef void (*mmc_hash_add_server)(void *state, mmc_t *mmc, unsigned int weight); 322 | 323 | typedef struct mmc_hash_strategy { 324 | mmc_hash_create_state create_state; 325 | mmc_hash_free_state free_state; 326 | mmc_hash_find_server find_server; 327 | mmc_hash_add_server add_server; 328 | } mmc_hash_strategy_t; 329 | 330 | extern mmc_hash_strategy_t mmc_standard_hash; 331 | extern mmc_hash_strategy_t mmc_consistent_hash; 332 | 333 | /* 32 bit magic FNV-1a prime and init */ 334 | #define FNV_32_PRIME 0x01000193 335 | #define FNV_32_INIT 0x811c9dc5 336 | 337 | /* failure callback prototype */ 338 | typedef void (*mmc_failure_callback)(mmc_pool_t *pool, mmc_t *mmc, zval *param); 339 | 340 | /* server pool */ 341 | struct mmc_pool { 342 | mmc_t **servers; 343 | int num_servers; 344 | mmc_protocol_t *protocol; /* wire protocol */ 345 | mmc_hash_strategy_t *hash; /* hash strategy */ 346 | void *hash_state; /* strategy specific state */ 347 | fd_set wfds; 348 | fd_set rfds; 349 | #ifndef PHP_WIN32 350 | struct pollfd *pollfds; 351 | int nfds; 352 | #endif 353 | long select_api; 354 | struct timeval timeout; /* smallest timeout for any of the servers */ 355 | int in_select; 356 | mmc_queue_t *sending; /* mmc_queue_t, connections that want to send */ 357 | mmc_queue_t *reading; /* mmc_queue_t, connections that want to read */ 358 | mmc_queue_t _sending1, _sending2; 359 | mmc_queue_t _reading1, _reading2; 360 | mmc_queue_t pending; /* mmc_queue_t, connections that have non-finalized requests */ 361 | mmc_queue_t free_requests; /* mmc_queue_t, request free-list */ 362 | double min_compress_savings; 363 | unsigned int compress_threshold; 364 | mmc_failure_callback failure_callback; /* receives notification when a server fails */ 365 | zval failure_callback_param; 366 | }; 367 | 368 | /* server functions */ 369 | mmc_t *mmc_server_new(const char *, int, unsigned short, unsigned short, int, double, int); 370 | void mmc_server_free(mmc_t *); 371 | void mmc_server_disconnect(mmc_t *mmc, mmc_stream_t *io); 372 | int mmc_server_valid(mmc_t *); 373 | int mmc_server_failure(mmc_t *, mmc_stream_t *, const char *, int); 374 | int mmc_request_failure(mmc_t *, mmc_stream_t *, const char *, unsigned int, int); 375 | 376 | /* pool functions */ 377 | mmc_pool_t *mmc_pool_new(); 378 | void mmc_pool_free(mmc_pool_t *); 379 | void mmc_pool_add(mmc_pool_t *, mmc_t *, unsigned int); 380 | void mmc_pool_close(mmc_pool_t *); 381 | int mmc_pool_open(mmc_pool_t *, mmc_t *, mmc_stream_t *, int); 382 | void mmc_pool_select(mmc_pool_t *); 383 | void mmc_pool_run(mmc_pool_t *); 384 | mmc_t *mmc_pool_find_next(mmc_pool_t *, const char *, unsigned int, mmc_queue_t *, unsigned int *); 385 | mmc_t *mmc_pool_find(mmc_pool_t *, const char *, unsigned int); 386 | int mmc_pool_schedule(mmc_pool_t *, mmc_t *, mmc_request_t *); 387 | int mmc_pool_failover_handler(mmc_pool_t *, mmc_t *, mmc_request_t *, void *); 388 | int mmc_pool_failover_handler_null(mmc_pool_t *, mmc_t *, mmc_request_t *, void *); 389 | 390 | mmc_request_t *mmc_pool_request(mmc_pool_t *, int, 391 | mmc_request_response_handler, void *, mmc_request_failover_handler, void *); 392 | mmc_request_t *mmc_pool_request_get(mmc_pool_t *, int, 393 | mmc_request_value_handler, void *, mmc_request_failover_handler, void *); 394 | 395 | #define mmc_pool_release(p, r) mmc_queue_push(&((p)->free_requests), (r)) 396 | 397 | int mmc_prepare_store( 398 | mmc_pool_t *, mmc_request_t *, const char *, unsigned int, 399 | const char *, unsigned int, unsigned int, unsigned int, zval *); 400 | 401 | int mmc_pool_schedule_key(mmc_pool_t *, const char *, unsigned int, mmc_request_t *, unsigned int); 402 | int mmc_pool_schedule_get(mmc_pool_t *, int, int, zval *, 403 | mmc_request_value_handler, void *, mmc_request_failover_handler, void *, mmc_request_t *); 404 | 405 | /* utility functions */ 406 | int mmc_pack_value(mmc_pool_t *, mmc_buffer_t *, zval *, unsigned int *); 407 | int mmc_unpack_value(mmc_t *, mmc_request_t *, mmc_buffer_t *, const char *, unsigned int, unsigned int, unsigned long, unsigned int); 408 | double timeval_to_double(struct timeval tv); 409 | struct timeval double_to_timeval(double sec); 410 | 411 | int mmc_prepare_key_ex(const char *, unsigned int, char *, unsigned int *, char *); 412 | int mmc_prepare_key(zval *, char *, unsigned int *); 413 | 414 | #define mmc_str_left(h, n, hlen, nlen) ((hlen) >= (nlen) ? memcmp((h), (n), (nlen)) == 0 : 0) 415 | 416 | /* globals */ 417 | ZEND_BEGIN_MODULE_GLOBALS(memcache) 418 | long default_port; 419 | long chunk_size; 420 | long protocol; 421 | long hash_strategy; 422 | long hash_function; 423 | long allow_failover; 424 | long max_failover_attempts; 425 | long redundancy; 426 | long session_redundancy; 427 | long compress_threshold; 428 | long lock_timeout; 429 | long select_api; 430 | char *session_key_prefix; 431 | zend_bool session_prefix_host_key; 432 | zend_bool session_prefix_host_key_remove_www; 433 | zend_bool session_prefix_host_key_remove_subdomain; 434 | char *session_prefix_static_key; 435 | char *session_save_path; 436 | char *key_prefix; 437 | zend_bool prefix_host_key; 438 | zend_bool prefix_host_key_remove_www; 439 | zend_bool prefix_host_key_remove_subdomain; 440 | char *prefix_static_key; 441 | ZEND_END_MODULE_GLOBALS(memcache) 442 | 443 | #ifdef ZTS 444 | #define MEMCACHE_G(v) TSRMG(memcache_globals_id, zend_memcache_globals *, v) 445 | #else 446 | #define MEMCACHE_G(v) (memcache_globals.v) 447 | #endif 448 | 449 | #endif /* MEMCACHE_POOL_H */ 450 | 451 | /* 452 | * Local variables: 453 | * tab-width: 4 454 | * c-basic-offset: 4 455 | * End: 456 | * vim600: noet sw=4 ts=4 fdm=marker 457 | * vim<600: noet sw=4 ts=4 458 | */ 459 | --------------------------------------------------------------------------------