1 /*
2   +----------------------------------------------------------------------+
3   | Yet Another Cache                                                    |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2013-2013 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Author: Xinchen Hui <laruence@php.net>                               |
16   +----------------------------------------------------------------------+
17 */
18 
19 /* $Id$ */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <time.h>
26 
27 #include "php.h"
28 #include "php_ini.h"
29 #include "SAPI.h"
30 #include "ext/standard/info.h"
31 #include "Zend/zend_smart_str.h"
32 #include "Zend/zend_exceptions.h"
33 
34 #include "php_yac.h"
35 #include "storage/yac_storage.h"
36 #include "serializer/yac_serializer.h"
37 #ifdef HAVE_FASTLZ_H
38 #include <fastlz.h>
39 #else
40 #include "compressor/fastlz/fastlz.h"
41 #endif
42 
43 zend_class_entry *yac_class_ce;
44 
45 static zend_object_handlers yac_obj_handlers;
46 
47 typedef struct {
48 	unsigned char prefix[YAC_STORAGE_MAX_KEY_LEN];
49 	uint16_t prefix_len;
50 	zend_object std;
51 } yac_object;
52 
53 ZEND_DECLARE_MODULE_GLOBALS(yac);
54 
55 static yac_serializer_t yac_serializer;
56 static yac_unserializer_t yac_unserializer;
57 
58 /** {{{ ARG_INFO
59  */
60 ZEND_BEGIN_ARG_INFO_EX(arginfo_yac_constructor, 0, 0, 0)
61 	ZEND_ARG_INFO(0, prefix)
ZEND_END_ARG_INFO()62 ZEND_END_ARG_INFO()
63 
64 ZEND_BEGIN_ARG_INFO_EX(arginfo_yac_add, 0, 0, 1)
65 	ZEND_ARG_INFO(0, keys)
66 	ZEND_ARG_INFO(0, value)
67 	ZEND_ARG_INFO(0, ttl)
68 ZEND_END_ARG_INFO()
69 
70 ZEND_BEGIN_ARG_INFO_EX(arginfo_yac_get, 0, 0, 1)
71 	ZEND_ARG_INFO(0, keys)
72 ZEND_END_ARG_INFO()
73 
74 ZEND_BEGIN_ARG_INFO_EX(arginfo_yac_delete, 0, 0, 1)
75 	ZEND_ARG_INFO(0, keys)
76 	ZEND_ARG_INFO(0, ttl)
77 ZEND_END_ARG_INFO()
78 
79 ZEND_BEGIN_ARG_INFO_EX(arginfo_yac_void, 0, 0, 0)
80 ZEND_END_ARG_INFO()
81 /* }}} */
82 
83 static PHP_INI_MH(OnChangeKeysMemoryLimit) /* {{{ */ {
84 	if (new_value) {
85 		YAC_G(k_msize) = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value));
86 	}
87 	return SUCCESS;
88 }
89 /* }}} */
90 
PHP_INI_MH(OnChangeValsMemoryLimit)91 static PHP_INI_MH(OnChangeValsMemoryLimit) /* {{{ */ {
92 	if (new_value) {
93 		YAC_G(v_msize) = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value));
94 	}
95 	return SUCCESS;
96 }
97 /* }}} */
98 
PHP_INI_MH(OnChangeCompressThreshold)99 static PHP_INI_MH(OnChangeCompressThreshold) /* {{{ */ {
100 	if (new_value) {
101 		YAC_G(compress_threshold) = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value));
102 		if (YAC_G(compress_threshold) < YAC_MIN_COMPRESS_THRESHOLD) {
103 			YAC_G(compress_threshold) = YAC_MIN_COMPRESS_THRESHOLD;
104 		}
105 	}
106 	return SUCCESS;
107 }
108 /* }}} */
109 
110 /* {{{ PHP_INI
111  */
112 PHP_INI_BEGIN()
113     STD_PHP_INI_BOOLEAN("yac.enable", "1", PHP_INI_SYSTEM, OnUpdateBool, enable, zend_yac_globals, yac_globals)
114     STD_PHP_INI_BOOLEAN("yac.debug", "0", PHP_INI_ALL, OnUpdateBool, debug, zend_yac_globals, yac_globals)
115     STD_PHP_INI_ENTRY("yac.keys_memory_size", "4M", PHP_INI_SYSTEM, OnChangeKeysMemoryLimit, k_msize, zend_yac_globals, yac_globals)
116     STD_PHP_INI_ENTRY("yac.values_memory_size", "64M", PHP_INI_SYSTEM, OnChangeValsMemoryLimit, v_msize, zend_yac_globals, yac_globals)
117     STD_PHP_INI_ENTRY("yac.compress_threshold", "-1", PHP_INI_SYSTEM, OnChangeCompressThreshold, compress_threshold, zend_yac_globals, yac_globals)
118     STD_PHP_INI_ENTRY("yac.enable_cli", "0", PHP_INI_SYSTEM, OnUpdateBool, enable_cli, zend_yac_globals, yac_globals)
119     STD_PHP_INI_ENTRY("yac.serializer", "php", PHP_INI_SYSTEM, OnUpdateString, serializer, zend_yac_globals, yac_globals)
PHP_INI_END()120 PHP_INI_END()
121 /* }}} */
122 
123 #define Z_YACOBJ_P(zv)   (php_yac_fetch_object(Z_OBJ_P(zv)))
124 static inline yac_object *php_yac_fetch_object(zend_object *obj) /* {{{ */ {
125 	return (yac_object *)((char*)(obj) - XtOffsetOf(yac_object, std));
126 }
127 /* }}} */
128 
yac_assemble_key(yac_object * yac,zend_string * name,size_t * len)129 static const char *yac_assemble_key(yac_object *yac, zend_string *name, size_t *len) /* {{{ */ {
130 	const char *key;
131 
132 	if ((ZSTR_LEN(name) + yac->prefix_len) > YAC_STORAGE_MAX_KEY_LEN) {
133 		php_error_docref(NULL, E_WARNING,
134 				"Key '%.*s%s' exceed max key length '%d' bytes",
135 				yac->prefix_len, yac->prefix, ZSTR_VAL(name), YAC_STORAGE_MAX_KEY_LEN);
136 		return NULL;
137 	}
138 
139 	if (yac->prefix_len) {
140 		memcpy(yac->prefix + yac->prefix_len, ZSTR_VAL(name), ZSTR_LEN(name));
141 		key = (const char*)yac->prefix;
142 		*len = yac->prefix_len + ZSTR_LEN(name);
143 	} else {
144 		key = ZSTR_VAL(name);
145 		*len = ZSTR_LEN(name);
146 	}
147 
148 	return key;
149 }
150 /* }}} */
151 
yac_add_impl(yac_object * yac,zend_string * name,zval * value,int ttl,int add)152 static int yac_add_impl(yac_object *yac, zend_string *name, zval *value, int ttl, int add) /* {{{ */ {
153 	int ret = 0, flag = Z_TYPE_P(value);
154 	char *msg;
155 	time_t tv;
156 	const char *key;
157 	size_t key_len;
158 
159 	if ((key = yac_assemble_key(yac, name, &key_len)) == NULL) {
160 		return ret;
161 	}
162 
163 	tv = time(NULL);
164 	switch (Z_TYPE_P(value)) {
165 		case IS_NULL:
166 		case IS_TRUE:
167 		case IS_FALSE:
168 			ret = yac_storage_update(key, key_len, (char *)&flag, sizeof(int), flag, ttl, add, tv);
169 			break;
170 		case IS_LONG:
171 			ret = yac_storage_update(key, key_len, (char *)&Z_LVAL_P(value), sizeof(long), flag, ttl, add, tv);
172 			break;
173 		case IS_DOUBLE:
174 			ret = yac_storage_update(key, key_len, (char *)&Z_DVAL_P(value), sizeof(double), flag, ttl, add, tv);
175 			break;
176 		case IS_STRING:
177 #ifdef IS_CONSTANT
178 		case IS_CONSTANT:
179 #endif
180 			{
181 				if (Z_STRLEN_P(value) > YAC_G(compress_threshold) || Z_STRLEN_P(value) > YAC_STORAGE_MAX_ENTRY_LEN) {
182 					int compressed_len;
183 					char *compressed;
184 
185 					/* if longer than this, then we can not stored the length in flag */
186 					if (Z_STRLEN_P(value) > YAC_ENTRY_MAX_ORIG_LEN) {
187 						php_error_docref(NULL, E_WARNING, "Value is too long(%ld bytes) to be stored", Z_STRLEN_P(value));
188 						return ret;
189 					}
190 
191 					compressed = emalloc(Z_STRLEN_P(value) * 1.05);
192 					compressed_len = fastlz_compress(Z_STRVAL_P(value), Z_STRLEN_P(value), compressed);
193 					if (!compressed_len || compressed_len > Z_STRLEN_P(value)) {
194 						php_error_docref(NULL, E_WARNING, "Compression failed");
195 						efree(compressed);
196 						return ret;
197 					}
198 
199 					if (compressed_len > YAC_STORAGE_MAX_ENTRY_LEN) {
200 						php_error_docref(NULL, E_WARNING, "Value is too long(%ld bytes) to be stored", Z_STRLEN_P(value));
201 						efree(compressed);
202 						return ret;
203 					}
204 
205 					flag |= YAC_ENTRY_COMPRESSED;
206 					flag |= (Z_STRLEN_P(value) << YAC_ENTRY_ORIG_LEN_SHIT);
207 					ret = yac_storage_update(key, key_len, compressed, compressed_len, flag, ttl, add, tv);
208 					efree(compressed);
209 				} else {
210 					ret = yac_storage_update(key, key_len, Z_STRVAL_P(value), Z_STRLEN_P(value), flag, ttl, add, tv);
211 				}
212 			}
213 			break;
214 		case IS_ARRAY:
215 #ifdef IS_CONSTANT_ARRAY
216 		case IS_CONSTANT_ARRAY:
217 #endif
218 		case IS_OBJECT:
219 			{
220 				smart_str buf = {0};
221 
222 				if (yac_serializer(value, &buf, &msg)) {
223 					if (buf.s->len > YAC_G(compress_threshold) || buf.s->len > YAC_STORAGE_MAX_ENTRY_LEN) {
224 						int compressed_len;
225 						char *compressed;
226 
227 						if (buf.s->len > YAC_ENTRY_MAX_ORIG_LEN) {
228 							php_error_docref(NULL, E_WARNING, "Value is too big to be stored");
229 							return ret;
230 						}
231 
232 						compressed = emalloc(buf.s->len * 1.05);
233 						compressed_len = fastlz_compress(ZSTR_VAL(buf.s), ZSTR_LEN(buf.s), compressed);
234 						if (!compressed_len || compressed_len > buf.s->len) {
235 							php_error_docref(NULL, E_WARNING, "Compression failed");
236 							efree(compressed);
237 							return ret;
238 						}
239 
240 						if (compressed_len > YAC_STORAGE_MAX_ENTRY_LEN) {
241 							php_error_docref(NULL, E_WARNING, "Value is too big to be stored");
242 							efree(compressed);
243 							return ret;
244 						}
245 
246 						flag |= YAC_ENTRY_COMPRESSED;
247 						flag |= (buf.s->len << YAC_ENTRY_ORIG_LEN_SHIT);
248 						ret = yac_storage_update(key, key_len, compressed, compressed_len, flag, ttl, add, tv);
249 						efree(compressed);
250 					} else {
251 						ret = yac_storage_update(key, key_len, ZSTR_VAL(buf.s), ZSTR_LEN(buf.s), flag, ttl, add, tv);
252 					}
253 					smart_str_free(&buf);
254 				} else {
255 					php_error_docref(NULL, E_WARNING, "Serialization failed");
256 					smart_str_free(&buf);
257 				}
258 			}
259 			break;
260 		case IS_RESOURCE:
261 			php_error_docref(NULL, E_WARNING, "Type 'IS_RESOURCE' cannot be stored");
262 			break;
263 		default:
264 			php_error_docref(NULL, E_WARNING, "Unsupported valued type to be stored '%d'", flag);
265 			break;
266 	}
267 
268 	return ret;
269 }
270 /* }}} */
271 
yac_add_multi_impl(yac_object * yac,zval * kvs,int ttl,int add)272 static int yac_add_multi_impl(yac_object *yac, zval *kvs, int ttl, int add) /* {{{ */ {
273 	HashTable *ht = Z_ARRVAL_P(kvs);
274 	zend_string *key;
275 	zend_ulong idx;
276 	zval *value;
277 
278 	ZEND_HASH_FOREACH_KEY_VAL(ht, idx, key, value) {
279 		uint32_t should_free = 0;
280 		if (!key) {
281 			key = strpprintf(0, "%lu", idx);
282 			should_free = 1;
283 		}
284 		if (yac_add_impl(yac, key, value, ttl, add)) {
285 			if (should_free) {
286 				zend_string_release(key);
287 			}
288 			continue;
289 		} else {
290 			if (should_free) {
291 				zend_string_release(key);
292 			}
293 			return 0;
294 		}
295 	} ZEND_HASH_FOREACH_END();
296 
297 	return 1;
298 }
299 /* }}} */
300 
yac_get_impl(yac_object * yac,zend_string * name,uint32_t * cas,zval * rv)301 static zval* yac_get_impl(yac_object *yac, zend_string *name, uint32_t *cas, zval *rv) /* {{{ */ {
302 	uint32_t flag, size = 0;
303 	char *data, *msg;
304 	time_t tv;
305 	const char *key;
306 	size_t key_len;
307 
308 	if ((key = yac_assemble_key(yac, name, &key_len)) == NULL) {
309 		return NULL;
310 	}
311 
312 	tv = time(NULL);
313 	if (yac_storage_find(key, key_len, &data, &size, &flag, (int *)cas, tv)) {
314 		switch ((flag & YAC_ENTRY_TYPE_MASK)) {
315 			case IS_NULL:
316 				if (size == sizeof(int)) {
317 					ZVAL_NULL(rv);
318 				}
319 				efree(data);
320 				break;
321 			case IS_TRUE:
322 				if (size == sizeof(int)) {
323 					ZVAL_TRUE(rv);
324 				}
325 				efree(data);
326 				break;
327 			case IS_FALSE:
328 				if (size == sizeof(int)) {
329 					ZVAL_FALSE(rv);
330 				}
331 				efree(data);
332 				break;
333 			case IS_LONG:
334 				if (size == sizeof(long)) {
335 					ZVAL_LONG(rv, *(long*)data);
336 				}
337 				efree(data);
338 				break;
339 			case IS_DOUBLE:
340 				if (size == sizeof(double)) {
341 					ZVAL_DOUBLE(rv, *(double*)data);
342 				}
343 				efree(data);
344 				break;
345 			case IS_STRING:
346 #ifdef IS_CONSTANT
347 			case IS_CONSTANT:
348 #endif
349 				{
350 					if ((flag & YAC_ENTRY_COMPRESSED)) {
351 						size_t orig_len = ((uint32_t)flag >> YAC_ENTRY_ORIG_LEN_SHIT);
352 						char *origin = emalloc(orig_len + 1);
353 						uint32_t length;
354 						length = fastlz_decompress(data, size, origin, orig_len);
355 						if (!length) {
356 							php_error_docref(NULL, E_WARNING, "Decompression failed");
357 							efree(data);
358 							efree(origin);
359 							return NULL;
360 						}
361 						ZVAL_STRINGL(rv, origin, length);
362 						efree(origin);
363 						efree(data);
364 					} else {
365 						ZVAL_STRINGL(rv, data, size);
366 						efree(data);
367 					}
368 				}
369 				break;
370 			case IS_ARRAY:
371 #ifdef IS_CONSTANT_ARRAY
372 			case IS_CONSTANT_ARRAY:
373 #endif
374 			case IS_OBJECT:
375 				{
376 					if ((flag & YAC_ENTRY_COMPRESSED)) {
377 						size_t length, orig_len = ((uint32_t)flag >> YAC_ENTRY_ORIG_LEN_SHIT);
378 						char *origin = emalloc(orig_len + 1);
379 						length = fastlz_decompress(data, size, origin, orig_len);
380 						if (!length) {
381 							php_error_docref(NULL, E_WARNING, "Decompression failed");
382 							efree(data);
383 							efree(origin);
384 							return NULL;
385 						}
386 						efree(data);
387 						data = origin;
388 						size = length;
389 					}
390 					rv = yac_unserializer(data, size, &msg, rv);
391 					efree(data);
392 				}
393 				break;
394 			default:
395 				php_error_docref(NULL, E_WARNING, "Unexpected valued type '%d'", flag);
396 				rv = NULL;
397 				break;
398 		}
399 	} else {
400 		rv = NULL;
401 	}
402 
403 	return rv;
404 }
405 /* }}} */
406 
yac_get_multi_impl(yac_object * yac,zval * keys,zval * cas,zval * rv)407 static zval* yac_get_multi_impl(yac_object *yac, zval *keys, zval *cas, zval *rv) /* {{{ */ {
408 	zval *value;
409 	HashTable *ht = Z_ARRVAL_P(keys);
410 
411 	array_init(rv);
412 
413 	ZEND_HASH_FOREACH_VAL(ht, value) {
414 		uint32_t lcas = 0;
415 		zval *v, tmp;
416 
417 		switch (Z_TYPE_P(value)) {
418 			case IS_STRING:
419 				if ((v = yac_get_impl(yac, Z_STR_P(value), &lcas, &tmp))) {
420 					zend_symtable_update(Z_ARRVAL_P(rv), Z_STR_P(value), v);
421 				} else {
422 					ZVAL_FALSE(&tmp);
423 					zend_symtable_update(Z_ARRVAL_P(rv), Z_STR_P(value), &tmp);
424 				}
425 				continue;
426 			default:
427 				{
428 					zend_string *key = zval_get_string(value);
429 					if ((v = yac_get_impl(yac, key, &lcas, &tmp))) {
430 						zend_symtable_update(Z_ARRVAL_P(rv), key, v);
431 					} else {
432 						ZVAL_FALSE(&tmp);
433 						zend_symtable_update(Z_ARRVAL_P(rv), key, &tmp);
434 					}
435 					zend_string_release(key);
436 				}
437 				continue;
438 		}
439 	} ZEND_HASH_FOREACH_END();
440 
441 	return rv;
442 }
443 /* }}} */
444 
yac_delete_impl(yac_object * yac,zend_string * name,int ttl)445 static int yac_delete_impl(yac_object *yac, zend_string *name, int ttl) /* {{{ */ {
446 	time_t tv = 0;
447 	const char *key;
448 	size_t key_len;
449 
450 	if ((key = yac_assemble_key(yac, name, &key_len)) == NULL) {
451 		return 0;
452 	}
453 
454 	if (ttl) {
455 		tv = (zend_ulong)time(NULL);
456 	}
457 
458 	return yac_storage_delete(key, key_len, ttl, tv);
459 }
460 /* }}} */
461 
yac_delete_multi_impl(yac_object * yac,zval * keys,int ttl)462 static int yac_delete_multi_impl(yac_object *yac, zval *keys, int ttl) /* {{{ */ {
463 	HashTable *ht = Z_ARRVAL_P(keys);
464 	int ret = 1;
465 	zval *value;
466 
467 	ZEND_HASH_FOREACH_VAL(ht, value) {
468 		switch (Z_TYPE_P(value)) {
469 			case IS_STRING:
470 			    ret = ret & yac_delete_impl(yac, Z_STR_P(value), ttl);
471 				continue;
472 			default:
473 				{
474 					zend_string *key = zval_get_string(value);
475 					ret = ret & yac_delete_impl(yac, key, ttl);
476 					zend_string_release(key);
477 				}
478 				continue;
479 		}
480 	} ZEND_HASH_FOREACH_END();
481 
482 	return ret;
483 }
484 /* }}} */
485 
yac_object_new(zend_class_entry * ce)486 static zend_object *yac_object_new(zend_class_entry *ce) /* {{{ */ {
487 	yac_object *yac = emalloc(sizeof(yac_object) + zend_object_properties_size(ce));
488 
489 	if (!YAC_G(enable)) {
490 		zend_throw_exception(NULL, "Yac is not enabled", 0);
491 	}
492 
493 	zend_object_std_init(&yac->std, ce);
494 	yac->std.handlers = &yac_obj_handlers;
495 	yac->prefix_len = 0;
496 
497 
498 	return &yac->std;
499 }
500 /* }}} */
501 
yac_object_free(zend_object * object)502 static void yac_object_free(zend_object *object) /* {{{ */ {
503 	zend_object_std_dtor(object);
504 }
505 /* }}} */
506 
yac_read_property_ptr(zval * zobj,zval * name,int type,void ** cache_slot)507 static zval* yac_read_property_ptr(zval *zobj, zval *name, int type, void **cache_slot) /* {{{ */ {
508 	return &EG(error_zval);
509 }
510 /* }}} */
511 
yac_read_property(zval * zobj,zval * name,int type,void ** cache_slot,zval * rv)512 static zval* yac_read_property(zval *zobj, zval *name, int type, void **cache_slot, zval *rv) /* {{{ */ {
513 	if (UNEXPECTED(type == BP_VAR_RW||type == BP_VAR_W)) {
514 		return &EG(error_zval);
515 	}
516 
517 	if (yac_get_impl(Z_YACOBJ_P(zobj), Z_STR_P(name), NULL, rv)) {
518 		return rv;
519 	}
520 
521 	return &EG(uninitialized_zval);
522 }
523 /* }}} */
524 
yac_write_property(zval * zobj,zval * name,zval * value,void ** cache_slot)525 static YAC_WHANDLER yac_write_property(zval *zobj, zval *name, zval *value, void **cache_slot) /* {{{ */ {
526 	yac_add_impl(Z_YACOBJ_P(zobj), Z_STR_P(name), value, 0, 0);
527     Z_TRY_ADDREF_P(value);
528 
529 	YAC_WHANDLER_RET(value);
530 }
531 /* }}} */
532 
yac_unset_property(zval * zobj,zval * name,void ** cache_slot)533 static void yac_unset_property(zval *zobj, zval *name, void **cache_slot) /* {{{ */ {
534 	yac_delete_impl(Z_YACOBJ_P(zobj), Z_STR_P(name), 0);
535 }
536 /* }}} */
537 
538 /** {{{ proto public Yac::__construct([string $prefix])
539 */
PHP_METHOD(yac,__construct)540 PHP_METHOD(yac, __construct) {
541 	zend_string *prefix = NULL;
542 
543 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S", &prefix) == FAILURE) {
544 		return;
545 	}
546 
547 	if (prefix && ZSTR_LEN(prefix)) {
548 		yac_object *yac;
549 		if (ZSTR_LEN(prefix) > YAC_STORAGE_MAX_KEY_LEN) {
550 			zend_throw_exception_ex(NULL, 0,
551 					"Prefix '%s' exceed max key length '%d' bytes", ZSTR_VAL(prefix), YAC_STORAGE_MAX_KEY_LEN);
552 			return;
553 		}
554 		yac = Z_YACOBJ_P(getThis());
555 		yac->prefix_len = ZSTR_LEN(prefix);
556 		memcpy(yac->prefix, ZSTR_VAL(prefix), ZSTR_LEN(prefix));
557 	}
558 }
559 /* }}} */
560 
561 /** {{{ proto public Yac::add(mixed $keys, mixed $value[, int $ttl])
562 */
PHP_METHOD(yac,add)563 PHP_METHOD(yac, add) {
564 	zend_long ttl = 0;
565 	zval *keys, *value = NULL;
566 	int ret;
567 
568 	switch (ZEND_NUM_ARGS()) {
569 		case 1:
570 			if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &keys) == FAILURE) {
571 				return;
572 			}
573 			break;
574 		case 2:
575 			if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &keys, &value) == FAILURE) {
576 				return;
577 			}
578 			if (Z_TYPE_P(keys) == IS_ARRAY) {
579 				if (Z_TYPE_P(value) == IS_LONG) {
580 					ttl = Z_LVAL_P(value);
581 					value = NULL;
582 				} else {
583 					php_error_docref(NULL, E_WARNING, "ttl parameter must be an integer");
584 					return;
585 				}
586 			}
587 			break;
588 		case 3:
589 			if (zend_parse_parameters(ZEND_NUM_ARGS(), "zzl", &keys, &value, &ttl) == FAILURE) {
590 				return;
591 			}
592 			break;
593 		default:
594 			WRONG_PARAM_COUNT;
595 	}
596 
597 	if (Z_TYPE_P(keys) == IS_ARRAY) {
598 		ret = yac_add_multi_impl(Z_YACOBJ_P(getThis()), keys, ttl, 1);
599 	} else if (Z_TYPE_P(keys) == IS_STRING) {
600 		ret = yac_add_impl(Z_YACOBJ_P(getThis()), Z_STR_P(keys), value, ttl, 1);
601 	} else {
602 		zend_string *key = zval_get_string(keys);
603 		ret = yac_add_impl(Z_YACOBJ_P(getThis()), key, value, ttl, 1);
604 		zend_string_release(key);
605 	}
606 
607 	RETURN_BOOL(ret);
608 }
609 /* }}} */
610 
611 /** {{{ proto public Yac::set(mixed $keys, mixed $value[, int $ttl])
612 */
PHP_METHOD(yac,set)613 PHP_METHOD(yac, set) {
614     zend_long ttl = 0;
615 	zval *keys, *value = NULL;
616 	int ret;
617 
618 	switch (ZEND_NUM_ARGS()) {
619 		case 1:
620 			if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &keys) == FAILURE) {
621 				return;
622 			}
623 			break;
624 		case 2:
625 			if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &keys, &value) == FAILURE) {
626 				return;
627 			}
628 			if (Z_TYPE_P(keys) == IS_ARRAY) {
629 				if (Z_TYPE_P(value) == IS_LONG) {
630 					ttl = Z_LVAL_P(value);
631 					value = NULL;
632 				} else {
633 					php_error_docref(NULL, E_WARNING, "ttl parameter must be an integer");
634 					return;
635 				}
636 			}
637 			break;
638 		case 3:
639 			if (zend_parse_parameters(ZEND_NUM_ARGS(), "zzl", &keys, &value, &ttl) == FAILURE) {
640 				return;
641 			}
642 			break;
643 		default:
644 			WRONG_PARAM_COUNT;
645 	}
646 
647 	if (Z_TYPE_P(keys) == IS_ARRAY) {
648 		ret = yac_add_multi_impl(Z_YACOBJ_P(getThis()), keys, ttl, 0);
649 	} else if (Z_TYPE_P(keys) == IS_STRING) {
650 		ret = yac_add_impl(Z_YACOBJ_P(getThis()), Z_STR_P(keys), value, ttl, 0);
651 	} else {
652 		zend_string *key = zval_get_string(keys);
653 		ret = yac_add_impl(Z_YACOBJ_P(getThis()), key, value, ttl, 0);
654 		zend_string_release(key);
655 	}
656 
657 	RETURN_BOOL(ret);
658 }
659 /* }}} */
660 
661 /** {{{ proto public Yac::get(mixed $keys[, int &$cas])
662 */
PHP_METHOD(yac,get)663 PHP_METHOD(yac, get) {
664 	uint32_t lcas = 0;
665 	zval *ret, *keys, *cas = NULL;
666 
667 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|z", &keys, &cas) == FAILURE) {
668 		return;
669 	}
670 
671 	if (Z_TYPE_P(keys) == IS_ARRAY) {
672 		ret = yac_get_multi_impl(Z_YACOBJ_P(getThis()), keys, cas, return_value);
673 	} else if (Z_TYPE_P(keys) == IS_STRING) {
674 		ret = yac_get_impl(Z_YACOBJ_P(getThis()), Z_STR_P(keys), &lcas, return_value);
675 	} else {
676 		zend_string *key = zval_get_string(keys);
677 		ret = yac_get_impl(Z_YACOBJ_P(getThis()), key, &lcas, return_value);
678 		zend_string_release(key);
679 	}
680 
681 	if (ret == NULL) {
682 		RETURN_FALSE;
683 	}
684 }
685 /* }}} */
686 
687 /** {{{ proto public Yac::delete(mixed $key[, int $delay = 0])
688 */
PHP_METHOD(yac,delete)689 PHP_METHOD(yac, delete) {
690 	zend_long time = 0;
691 	zval *keys;
692 	int ret;
693 
694 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|l", &keys, &time) == FAILURE) {
695 		return;
696 	}
697 
698 	if (Z_TYPE_P(keys) == IS_ARRAY) {
699 		ret = yac_delete_multi_impl(Z_YACOBJ_P(getThis()), keys, time);
700 	} else if (Z_TYPE_P(keys) == IS_STRING) {
701 		ret = yac_delete_impl(Z_YACOBJ_P(getThis()), Z_STR_P(keys), time);
702 	} else {
703 		zend_string *key = zval_get_string(keys);
704 		ret = yac_delete_impl(Z_YACOBJ_P(getThis()), key, time);
705 		zend_string_release(key);
706 	}
707 
708 	RETURN_BOOL(ret);
709 }
710 /* }}} */
711 
712 /** {{{ proto public Yac::flush(void)
713 */
PHP_METHOD(yac,flush)714 PHP_METHOD(yac, flush) {
715 
716 	yac_storage_flush();
717 
718 	RETURN_TRUE;
719 }
720 /* }}} */
721 
722 /** {{{ proto public Yac::info(void)
723 */
PHP_METHOD(yac,info)724 PHP_METHOD(yac, info) {
725 	yac_storage_info *inf;
726 
727 	inf = yac_storage_get_info();
728 
729 	array_init(return_value);
730 
731 	add_assoc_long(return_value, "memory_size", inf->k_msize + inf->v_msize);
732 	add_assoc_long(return_value, "slots_memory_size", inf->k_msize);
733 	add_assoc_long(return_value, "values_memory_size", inf->v_msize);
734 	add_assoc_long(return_value, "segment_size", inf->segment_size);
735 	add_assoc_long(return_value, "segment_num", inf->segments_num);
736 	add_assoc_long(return_value, "miss", inf->miss);
737 	add_assoc_long(return_value, "hits", inf->hits);
738 	add_assoc_long(return_value, "fails", inf->fails);
739 	add_assoc_long(return_value, "kicks", inf->kicks);
740 	add_assoc_long(return_value, "recycles", inf->recycles);
741 	add_assoc_long(return_value, "slots_size", inf->slots_size);
742 	add_assoc_long(return_value, "slots_used", inf->slots_num);
743 
744 	yac_storage_free_info(inf);
745 	return;
746 }
747 /* }}} */
748 
749 /** {{{ proto public Yac::dump(int $limit)
750 */
PHP_METHOD(yac,dump)751 PHP_METHOD(yac, dump) {
752 	long limit = 100;
753 	yac_item_list *list, *l;
754 
755 	array_init(return_value);
756 
757 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &limit) == FAILURE) {
758 		return;
759 	}
760 
761 	list = l = yac_storage_dump(limit);
762 	for (; l; l = l->next) {
763 		zval item;
764 		array_init(&item);
765 		add_assoc_long(&item, "index", l->index);
766 		add_assoc_long(&item, "hash", l->h);
767 		add_assoc_long(&item, "crc", l->crc);
768 		add_assoc_long(&item, "ttl", l->ttl);
769 		add_assoc_long(&item, "k_len", l->k_len);
770 		add_assoc_long(&item, "v_len", l->v_len);
771 		add_assoc_long(&item, "size", l->size);
772 		add_assoc_string(&item, "key", (char*)l->key);
773 		add_next_index_zval(return_value, &item);
774 	}
775 
776 	yac_storage_free_list(list);
777 	return;
778 }
779 /* }}} */
780 
781 #if 0
782 only OO-style APIs is supported now
783 /* {{{{ proto bool yac_add(mixed $keys, mixed $value[, int $ttl])
784  */
785 PHP_FUNCTION(yac_add)
786 {
787 	PHP_MN(yac_add)(INTERNAL_FUNCTION_PARAM_PASSTHRU);
788 }
789 /* }}} */
790 
791 /* {{{ proto bool yac_set(mixed $keys, mixed $value[, int $ttl])
792  */
793 PHP_FUNCTION(yac_set)
794 {
795 	PHP_MN(yac_set)(INTERNAL_FUNCTION_PARAM_PASSTHRU);
796 }
797 /* }}} */
798 
799 /* {{{ proto bool yac_get(mixed $keys[, int &$cas])
800  */
801 PHP_FUNCTION(yac_get)
802 {
803 	PHP_MN(yac_get)(INTERNAL_FUNCTION_PARAM_PASSTHRU);
804 }
805 /* }}} */
806 
807 /* {{{ proto bool yac_delete(mixed $keys[, int $delay = 0])
808  */
809 PHP_FUNCTION(yac_delete)
810 {
811 	PHP_MN(yac_delete)(INTERNAL_FUNCTION_PARAM_PASSTHRU);
812 }
813 /* }}} */
814 
815 /* {{{ proto bool yac_flush(void)
816  */
817 PHP_FUNCTION(yac_flush)
818 {
819 	PHP_MN(yac_flush)(INTERNAL_FUNCTION_PARAM_PASSTHRU);
820 }
821 /* }}} */
822 
823 /* {{{ proto bool yac_info(void)
824  */
825 PHP_FUNCTION(yac_info)
826 {
827 	PHP_MN(yac_info)(INTERNAL_FUNCTION_PARAM_PASSTHRU);
828 }
829 /* }}} */
830 
831 /* {{{ yac_functions[] */
832 zend_function_entry yac_functions[] = {
833 	PHP_FE(yac_add, arginfo_yac_add)
834 	PHP_FE(yac_set, arginfo_yac_add)
835 	PHP_FE(yac_get, arginfo_yac_get)
836 	PHP_FE(yac_delete, arginfo_yac_delete)
837 	PHP_FE(yac_flush, arginfo_yac_void)
838 	PHP_FE(yac_info, arginfo_yac_void)
839 	{NULL, NULL}
840 };
841 /* }}} */
842 #endif
843 
844 /** {{{ yac_methods
845 */
846 zend_function_entry yac_methods[] = {
847 	PHP_ME(yac, __construct, arginfo_yac_constructor, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
848 	PHP_ME(yac, add, arginfo_yac_add, ZEND_ACC_PUBLIC)
849 	PHP_ME(yac, set, arginfo_yac_add, ZEND_ACC_PUBLIC)
850 	PHP_ME(yac, get, arginfo_yac_get, ZEND_ACC_PUBLIC)
851 	PHP_ME(yac, delete, arginfo_yac_delete, ZEND_ACC_PUBLIC)
852 	PHP_ME(yac, flush, arginfo_yac_void, ZEND_ACC_PUBLIC)
853 	PHP_ME(yac, info, arginfo_yac_void, ZEND_ACC_PUBLIC)
854 	PHP_ME(yac, dump, arginfo_yac_void, ZEND_ACC_PUBLIC)
855 	{NULL, NULL, NULL}
856 };
857 /* }}} */
858 
859 /* {{{ PHP_GINIT_FUNCTION
860  */
PHP_GINIT_FUNCTION(yac)861 PHP_GINIT_FUNCTION(yac)
862 {
863 	yac_globals->enable = 1;
864 	yac_globals->k_msize = (4 * 1024 * 1024);
865 	yac_globals->v_msize = (64 * 1024 * 1024);
866 	yac_globals->debug = 0;
867 	yac_globals->compress_threshold = -1;
868 	yac_globals->enable_cli = 0;
869 #ifdef PHP_WIN32
870 	yac_globals->mmap_base = NULL;
871 #endif
872 }
873 /* }}} */
874 
875 /* {{{ PHP_MINIT_FUNCTION
876  */
PHP_MINIT_FUNCTION(yac)877 PHP_MINIT_FUNCTION(yac)
878 {
879 	char *msg;
880 	zend_class_entry ce;
881 
882 	REGISTER_INI_ENTRIES();
883 
884 	if (!YAC_G(enable_cli) && !strcmp(sapi_module.name, "cli")) {
885 		YAC_G(enable) = 0;
886 	}
887 
888 	if (YAC_G(enable)) {
889 		if (!yac_storage_startup(YAC_G(k_msize), YAC_G(v_msize), &msg)) {
890 			php_error(E_ERROR, "Shared memory allocator startup failed at '%s': %s", msg, strerror(errno));
891 			return FAILURE;
892 		}
893 	}
894 
895 	REGISTER_STRINGL_CONSTANT("YAC_VERSION", PHP_YAC_VERSION, 	sizeof(PHP_YAC_VERSION) - 1, 	CONST_PERSISTENT | CONST_CS);
896 	REGISTER_LONG_CONSTANT("YAC_MAX_KEY_LEN", YAC_STORAGE_MAX_KEY_LEN, CONST_PERSISTENT | CONST_CS);
897 	REGISTER_LONG_CONSTANT("YAC_MAX_VALUE_RAW_LEN", YAC_ENTRY_MAX_ORIG_LEN, CONST_PERSISTENT | CONST_CS);
898 	REGISTER_LONG_CONSTANT("YAC_MAX_RAW_COMPRESSED_LEN", YAC_STORAGE_MAX_ENTRY_LEN, CONST_PERSISTENT | CONST_CS);
899 	REGISTER_LONG_CONSTANT("YAC_SERIALIZER_PHP", YAC_SERIALIZER_PHP, CONST_PERSISTENT | CONST_CS);
900 #if YAC_ENABLE_MSGPACK
901 	REGISTER_LONG_CONSTANT("YAC_SERIALIZER_MSGPACK", YAC_SERIALIZER_MSGPACK, CONST_PERSISTENT | CONST_CS);
902 #endif
903 #if YAC_ENABLE_IGBINARY
904 	REGISTER_LONG_CONSTANT("YAC_SERIALIZER_IGBINARY", YAC_SERIALIZER_IGBINARY, CONST_PERSISTENT | CONST_CS);
905 #endif
906 #if YAC_ENABLE_JSON
907 	REGISTER_LONG_CONSTANT("YAC_SERIALIZER_JSON", YAC_SERIALIZER_JSON, CONST_PERSISTENT | CONST_CS);
908 #endif
909 
910 #if YAC_ENABLE_MSGPACK
911 	if (strcmp(YAC_G(serializer), "msgpack") == 0) {
912 		yac_serializer = yac_serializer_msgpack_pack;
913 		yac_unserializer = yac_serializer_msgpack_unpack;
914 		REGISTER_LONG_CONSTANT("YAC_SERIALIZER", YAC_SERIALIZER_MSGPACK, CONST_PERSISTENT | CONST_CS);
915 	} else
916 #endif
917 #if YAC_ENABLE_IGBINARY
918 	if (strcmp(YAC_G(serializer), "igbinary") == 0) {
919 		yac_serializer = yac_serializer_igbinary_pack;
920 		yac_unserializer = yac_serializer_igbinary_unpack;
921 		REGISTER_LONG_CONSTANT("YAC_SERIALIZER", YAC_SERIALIZER_IGBINARY, CONST_PERSISTENT | CONST_CS);
922 	} else
923 #endif
924 #if YAC_ENABLE_JSON
925 	if (strcmp(YAC_G(serializer), "json") == 0) {
926 		yac_serializer = yac_serializer_json_pack;
927 		yac_unserializer = yac_serializer_json_unpack;
928 		REGISTER_LONG_CONSTANT("YAC_SERIALIZER", YAC_SERIALIZER_JSON, CONST_PERSISTENT | CONST_CS);
929 	} else
930 #endif
931 	{
932 		yac_serializer = yac_serializer_php_pack;
933 		yac_unserializer = yac_serializer_php_unpack;
934 		REGISTER_LONG_CONSTANT("YAC_SERIALIZER", YAC_SERIALIZER_PHP, CONST_PERSISTENT | CONST_CS);
935 	}
936 
937 	INIT_CLASS_ENTRY(ce, "Yac", yac_methods);
938 	yac_class_ce = zend_register_internal_class(&ce);
939 	yac_class_ce->ce_flags |= ZEND_ACC_FINAL;
940 	yac_class_ce->create_object = yac_object_new;
941 
942 	memcpy(&yac_obj_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
943 	yac_obj_handlers.offset = XtOffsetOf(yac_object, std);
944 	yac_obj_handlers.free_obj = yac_object_free;
945 	if (YAC_G(enable)) {
946 		yac_obj_handlers.read_property  = yac_read_property;
947 		yac_obj_handlers.write_property = yac_write_property;
948 		yac_obj_handlers.unset_property = yac_unset_property;
949 		yac_obj_handlers.get_property_ptr_ptr = yac_read_property_ptr;
950 	}
951 
952 	return SUCCESS;
953 }
954 /* }}} */
955 
956 /* {{{ PHP_MSHUTDOWN_FUNCTION
957  */
PHP_MSHUTDOWN_FUNCTION(yac)958 PHP_MSHUTDOWN_FUNCTION(yac)
959 {
960 	UNREGISTER_INI_ENTRIES();
961 	if (YAC_G(enable)) {
962 		yac_storage_shutdown();
963 	}
964 	return SUCCESS;
965 }
966 /* }}} */
967 
968 /* {{{ PHP_MINFO_FUNCTION
969  */
PHP_MINFO_FUNCTION(yac)970 PHP_MINFO_FUNCTION(yac)
971 {
972 	smart_str names = {0,};
973 
974 	php_info_print_table_start();
975 	php_info_print_table_header(2, "yac support", "enabled");
976 	php_info_print_table_row(2, "Version", PHP_YAC_VERSION);
977 	php_info_print_table_row(2, "Shared Memory", yac_storage_shared_memory_name());
978 
979 	smart_str_appends(&names, "php");
980 #if YAC_ENABLE_MSGPACK
981 	smart_str_appends(&names, ", msgpack");
982 #endif
983 #if YAC_ENABLE_IGBINARY
984 	smart_str_appends(&names, ", igbinary");
985 #endif
986 #if YAC_ENABLE_JSON
987 	smart_str_appends(&names, ", json");
988 #endif
989 	php_info_print_table_row(2, "Serializer", ZSTR_VAL(names.s));
990 	smart_str_free(&names);
991 
992 	php_info_print_table_end();
993 
994 	DISPLAY_INI_ENTRIES();
995 
996 	if (YAC_G(enable)) {
997 		char buf[64];
998 		yac_storage_info *inf;
999 		inf = yac_storage_get_info();
1000 
1001 		php_info_print_table_start();
1002 		php_info_print_table_colspan_header(2, "Cache info");
1003 		snprintf(buf, sizeof(buf), "%ld", inf->k_msize + inf->v_msize);
1004 		php_info_print_table_row(2, "Total Shared Memory Usage(memory_size)", buf);
1005 		snprintf(buf, sizeof(buf), "%ld", inf->k_msize);
1006 		php_info_print_table_row(2, "Total Shared Memory Usage for keys(keys_memory_size)", buf);
1007 		snprintf(buf, sizeof(buf), "%ld", inf->v_msize);
1008 		php_info_print_table_row(2, "Total Shared Memory Usage for values(values_memory_size)", buf);
1009 		snprintf(buf, sizeof(buf), "%d", inf->segment_size);
1010 		php_info_print_table_row(2, "Size of Shared Memory Segment(segment_size)", buf);
1011 		snprintf(buf, sizeof(buf), "%d", inf->segments_num);
1012 		php_info_print_table_row(2, "Number of Segments (segment_num)", buf);
1013 		snprintf(buf, sizeof(buf), "%d", inf->slots_size);
1014 		php_info_print_table_row(2, "Total Slots Number(slots_size)", buf);
1015 		snprintf(buf, sizeof(buf), "%d", inf->slots_num);
1016 		php_info_print_table_row(2, "Total Used Slots(slots_num)", buf);
1017 		php_info_print_table_end();
1018 
1019 		yac_storage_free_info(inf);
1020 	}
1021 }
1022 /* }}} */
1023 
1024 #ifdef COMPILE_DL_YAC
1025 ZEND_GET_MODULE(yac)
1026 #endif
1027 
1028 static zend_module_dep yac_module_deps[] = {
1029 #if YAC_ENABLE_MSGPACK
1030 	ZEND_MOD_REQUIRED("msgpack")
1031 #endif
1032 #if YAC_ENABLE_IGBINARY
1033 	ZEND_MOD_REQUIRED("igbinary")
1034 #endif
1035 #if YAC_ENABLE_JSON
1036 	ZEND_MOD_REQUIRED("json")
1037 #endif
1038 	{NULL, NULL, NULL, 0}
1039 };
1040 
1041 /* {{{ yac_module_entry
1042  */
1043 zend_module_entry yac_module_entry = {
1044 	STANDARD_MODULE_HEADER_EX,
1045 	NULL,
1046 	yac_module_deps,
1047 	"yac",
1048 	NULL, /* yac_functions, */
1049 	PHP_MINIT(yac),
1050 	PHP_MSHUTDOWN(yac),
1051 	NULL,
1052 	NULL,
1053 	PHP_MINFO(yac),
1054 	PHP_YAC_VERSION,
1055 	PHP_MODULE_GLOBALS(yac),
1056 	PHP_GINIT(yac),
1057 	NULL,
1058 	NULL,
1059 	STANDARD_MODULE_PROPERTIES_EX
1060 };
1061 /* }}} */
1062 
1063 /*
1064  * Local variables:
1065  * tab-width: 4
1066  * c-basic-offset: 4
1067  * End:
1068  * vim600: noet sw=4 ts=4 fdm=marker
1069  * vim<600: noet sw=4 ts=4
1070  */
1071 
1072