XCache is a fast, stable PHP opcode cacher that has been proven and is now running on production servers under high load. https://xcache.lighttpd.net/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4185 lines
120 KiB

  1. #if 0
  2. #define XCACHE_DEBUG
  3. #endif
  4. #if 0
  5. #define SHOW_DPRINT
  6. #endif
  7. /* {{{ macros */
  8. #include <stdlib.h>
  9. #include <stdio.h>
  10. #include <string.h>
  11. #include <signal.h>
  12. #include "php.h"
  13. #include "ext/standard/info.h"
  14. #include "ext/standard/md5.h"
  15. #include "ext/standard/php_math.h"
  16. #include "ext/standard/php_string.h"
  17. #include "zend_extensions.h"
  18. #include "SAPI.h"
  19. #include "xcache.h"
  20. #ifdef ZEND_ENGINE_2_1
  21. #include "ext/date/php_date.h"
  22. #endif
  23. #include "submodules/xc_optimizer.h"
  24. #include "submodules/xc_coverager.h"
  25. #include "submodules/xc_disassembler.h"
  26. #include "xcache_globals.h"
  27. #include "xc_processor.h"
  28. #include "xc_const_string.h"
  29. #include "xc_opcode_spec.h"
  30. #include "xc_utils.h"
  31. #include "util/xc_align.h"
  32. #include "util/xc_stack.h"
  33. #include "util/xc_vector.h"
  34. #include "util/xc_trace.h"
  35. #define VAR_ENTRY_EXPIRED(pentry) ((pentry)->ttl && XG(request_time) > (pentry)->ctime + (time_t) (pentry)->ttl)
  36. #define CHECK(x, e) do { if ((x) == NULL) { zend_error(E_ERROR, "XCache: " e); goto err; } } while (0)
  37. #define LOCK(x) xc_lock((x)->lck)
  38. #define UNLOCK(x) xc_unlock((x)->lck)
  39. #define ENTER_LOCK_EX(x) \
  40. xc_lock((x)->lck); \
  41. zend_try { \
  42. do
  43. #define LEAVE_LOCK_EX(x) \
  44. while (0); \
  45. } zend_catch { \
  46. catched = 1; \
  47. } zend_end_try(); \
  48. xc_unlock((x)->lck)
  49. #define ENTER_LOCK(x) do { \
  50. int catched = 0; \
  51. ENTER_LOCK_EX(x)
  52. #define LEAVE_LOCK(x) \
  53. LEAVE_LOCK_EX(x); \
  54. if (catched) { \
  55. zend_bailout(); \
  56. } \
  57. } while(0)
  58. /* }}} */
  59. /* {{{ globals */
  60. static char *xc_shm_scheme = NULL;
  61. static char *xc_mmap_path = NULL;
  62. static char *xc_coredump_dir = NULL;
  63. static zend_bool xc_disable_on_crash = 0;
  64. static xc_hash_t xc_php_hcache = { 0, 0, 0 };
  65. static xc_hash_t xc_php_hentry = { 0, 0, 0 };
  66. static xc_hash_t xc_var_hcache = { 0, 0, 0 };
  67. static xc_hash_t xc_var_hentry = { 0, 0, 0 };
  68. static zend_ulong xc_php_ttl = 0;
  69. static zend_ulong xc_var_maxttl = 0;
  70. enum { xc_deletes_gc_interval = 120 };
  71. static zend_ulong xc_php_gc_interval = 0;
  72. static zend_ulong xc_var_gc_interval = 0;
  73. /* total size */
  74. static zend_ulong xc_php_size = 0;
  75. static zend_ulong xc_var_size = 0;
  76. static xc_shm_t *xc_shm = NULL;
  77. static xc_cache_t **xc_php_caches = NULL;
  78. static xc_cache_t **xc_var_caches = NULL;
  79. static zend_bool xc_initized = 0;
  80. static time_t xc_init_time = 0;
  81. static long unsigned xc_init_instance_id = 0;
  82. #ifdef ZTS
  83. static long unsigned xc_init_instance_subid = 0;
  84. #endif
  85. static zend_compile_file_t *origin_compile_file = NULL;
  86. static zend_compile_file_t *old_compile_file = NULL;
  87. static zend_llist_element *xc_llist_zend_extension = NULL;
  88. static zend_bool xc_test = 0;
  89. static zend_bool xc_readonly_protection = 0;
  90. zend_bool xc_have_op_array_ctor = 0;
  91. static zend_bool xc_module_gotup = 0;
  92. static zend_bool xc_zend_extension_gotup = 0;
  93. static zend_bool xc_zend_extension_faked = 0;
  94. #if !COMPILE_DL_XCACHE
  95. # define zend_extension_entry xcache_zend_extension_entry
  96. #endif
  97. ZEND_DLEXPORT zend_extension zend_extension_entry;
  98. ZEND_DECLARE_MODULE_GLOBALS(xcache)
  99. typedef enum { XC_TYPE_PHP, XC_TYPE_VAR } xc_entry_type_t;
  100. /* }}} */
  101. /* any function in *_unlocked is only safe be called within locked (single thread access) area */
  102. static void xc_php_add_unlocked(xc_cache_t *cache, xc_entry_data_php_t *php) /* {{{ */
  103. {
  104. xc_entry_data_php_t **head = &(cache->phps[php->hvalue]);
  105. php->next = *head;
  106. *head = php;
  107. cache->phps_count ++;
  108. }
  109. /* }}} */
  110. static xc_entry_data_php_t *xc_php_store_unlocked(xc_cache_t *cache, xc_entry_data_php_t *php TSRMLS_DC) /* {{{ */
  111. {
  112. xc_entry_data_php_t *stored_php;
  113. php->hits = 0;
  114. php->refcount = 0;
  115. stored_php = xc_processor_store_xc_entry_data_php_t(cache, php TSRMLS_CC);
  116. if (stored_php) {
  117. xc_php_add_unlocked(cache, stored_php);
  118. return stored_php;
  119. }
  120. else {
  121. cache->ooms ++;
  122. return NULL;
  123. }
  124. }
  125. /* }}} */
  126. static xc_entry_data_php_t *xc_php_find_unlocked(xc_cache_t *cache, xc_entry_data_php_t *php TSRMLS_DC) /* {{{ */
  127. {
  128. xc_entry_data_php_t *p;
  129. for (p = cache->phps[php->hvalue]; p; p = (xc_entry_data_php_t *) p->next) {
  130. if (memcmp(&php->md5.digest, &p->md5.digest, sizeof(php->md5.digest)) == 0) {
  131. p->hits ++;
  132. return p;
  133. }
  134. }
  135. return NULL;
  136. }
  137. /* }}} */
  138. static void xc_php_free_unlocked(xc_cache_t *cache, xc_entry_data_php_t *php) /* {{{ */
  139. {
  140. cache->mem->handlers->free(cache->mem, (xc_entry_data_php_t *)php);
  141. }
  142. /* }}} */
  143. static void xc_php_addref_unlocked(xc_entry_data_php_t *php) /* {{{ */
  144. {
  145. php->refcount ++;
  146. }
  147. /* }}} */
  148. static void xc_php_release_unlocked(xc_cache_t *cache, xc_entry_data_php_t *php) /* {{{ */
  149. {
  150. if (-- php->refcount == 0) {
  151. xc_entry_data_php_t **pp = &(cache->phps[php->hvalue]);
  152. xc_entry_data_php_t *p;
  153. for (p = *pp; p; pp = &(p->next), p = p->next) {
  154. if (memcmp(&php->md5.digest, &p->md5.digest, sizeof(php->md5.digest)) == 0) {
  155. /* unlink */
  156. *pp = p->next;
  157. xc_php_free_unlocked(cache, php);
  158. return;
  159. }
  160. }
  161. assert(0);
  162. }
  163. }
  164. /* }}} */
  165. static inline int xc_entry_equal_unlocked(xc_entry_type_t type, const xc_entry_t *entry1, const xc_entry_t *entry2 TSRMLS_DC) /* {{{ */
  166. {
  167. /* this function isn't required but can be in unlocked */
  168. switch (type) {
  169. case XC_TYPE_PHP:
  170. {
  171. const xc_entry_php_t *php_entry1 = (const xc_entry_php_t *) entry1;
  172. const xc_entry_php_t *php_entry2 = (const xc_entry_php_t *) entry2;
  173. if (php_entry1->file_inode && php_entry2->file_inode) {
  174. zend_bool inodeIsSame = php_entry1->file_inode == php_entry2->file_inode
  175. && php_entry1->file_device == php_entry2->file_device;
  176. if (XG(experimental)) {
  177. /* new experimental behavior: quick check by inode, first */
  178. if (!inodeIsSame) {
  179. return 0;
  180. }
  181. /* and then opened_path compare */
  182. }
  183. else {
  184. /* old behavior: inode check only */
  185. return inodeIsSame;
  186. }
  187. }
  188. }
  189. assert(IS_ABSOLUTE_PATH(entry1->name.str.val, entry1->name.str.len));
  190. assert(IS_ABSOLUTE_PATH(entry2->name.str.val, entry2->name.str.len));
  191. return entry1->name.str.len == entry2->name.str.len
  192. && memcmp(entry1->name.str.val, entry2->name.str.val, entry1->name.str.len + 1) == 0;
  193. case XC_TYPE_VAR:
  194. #ifdef IS_UNICODE
  195. if (entry1->name_type != entry2->name_type) {
  196. return 0;
  197. }
  198. if (entry1->name_type == IS_UNICODE) {
  199. return entry1->name.ustr.len == entry2->name.ustr.len
  200. && memcmp(entry1->name.ustr.val, entry2->name.ustr.val, (entry1->name.ustr.len + 1) * sizeof(UChar)) == 0;
  201. }
  202. #endif
  203. return entry1->name.str.len == entry2->name.str.len
  204. && memcmp(entry1->name.str.val, entry2->name.str.val, entry1->name.str.len + 1) == 0;
  205. break;
  206. default:
  207. assert(0);
  208. }
  209. return 0;
  210. }
  211. /* }}} */
  212. static inline int xc_entry_has_prefix_unlocked(xc_entry_type_t type, xc_entry_t *entry, zval *prefix) /* {{{ */
  213. {
  214. /* this function isn't required but can be in unlocked */
  215. #ifdef IS_UNICODE
  216. if (entry->name_type != prefix->type) {
  217. return 0;
  218. }
  219. if (entry->name_type == IS_UNICODE) {
  220. if (entry->name.ustr.len < Z_USTRLEN_P(prefix)) {
  221. return 0;
  222. }
  223. return memcmp(entry->name.ustr.val, Z_USTRVAL_P(prefix), Z_USTRLEN_P(prefix) * sizeof(UChar)) == 0;
  224. }
  225. #endif
  226. if (prefix->type != IS_STRING) {
  227. return 0;
  228. }
  229. if (entry->name.str.len < Z_STRLEN_P(prefix)) {
  230. return 0;
  231. }
  232. return memcmp(entry->name.str.val, Z_STRVAL_P(prefix), Z_STRLEN_P(prefix)) == 0;
  233. }
  234. /* }}} */
  235. static void xc_entry_add_unlocked(xc_cache_t *cache, xc_hash_value_t entryslotid, xc_entry_t *entry) /* {{{ */
  236. {
  237. xc_entry_t **head = &(cache->entries[entryslotid]);
  238. entry->next = *head;
  239. *head = entry;
  240. cache->entries_count ++;
  241. }
  242. /* }}} */
  243. static xc_entry_t *xc_entry_store_unlocked(xc_entry_type_t type, xc_cache_t *cache, xc_hash_value_t entryslotid, xc_entry_t *entry TSRMLS_DC) /* {{{ */
  244. {
  245. xc_entry_t *stored_entry;
  246. entry->hits = 0;
  247. entry->ctime = XG(request_time);
  248. entry->atime = XG(request_time);
  249. stored_entry = type == XC_TYPE_PHP
  250. ? (xc_entry_t *) xc_processor_store_xc_entry_php_t(cache, (xc_entry_php_t *) entry TSRMLS_CC)
  251. : (xc_entry_t *) xc_processor_store_xc_entry_var_t(cache, (xc_entry_var_t *) entry TSRMLS_CC);
  252. if (stored_entry) {
  253. xc_entry_add_unlocked(cache, entryslotid, stored_entry);
  254. ++cache->updates;
  255. return stored_entry;
  256. }
  257. else {
  258. cache->ooms ++;
  259. return NULL;
  260. }
  261. }
  262. /* }}} */
  263. static xc_entry_php_t *xc_entry_php_store_unlocked(xc_cache_t *cache, xc_hash_value_t entryslotid, xc_entry_php_t *entry_php TSRMLS_DC) /* {{{ */
  264. {
  265. return (xc_entry_php_t *) xc_entry_store_unlocked(XC_TYPE_PHP, cache, entryslotid, (xc_entry_t *) entry_php TSRMLS_CC);
  266. }
  267. /* }}} */
  268. static xc_entry_var_t *xc_entry_var_store_unlocked(xc_cache_t *cache, xc_hash_value_t entryslotid, xc_entry_var_t *entry_var TSRMLS_DC) /* {{{ */
  269. {
  270. return (xc_entry_var_t *) xc_entry_store_unlocked(XC_TYPE_VAR, cache, entryslotid, (xc_entry_t *) entry_var TSRMLS_CC);
  271. }
  272. /* }}} */
  273. static void xc_entry_free_real_unlocked(xc_entry_type_t type, xc_cache_t *cache, volatile xc_entry_t *entry) /* {{{ */
  274. {
  275. if (type == XC_TYPE_PHP) {
  276. xc_php_release_unlocked(cache, ((xc_entry_php_t *) entry)->php);
  277. }
  278. cache->mem->handlers->free(cache->mem, (xc_entry_t *)entry);
  279. }
  280. /* }}} */
  281. static void xc_entry_free_unlocked(xc_entry_type_t type, xc_cache_t *cache, xc_entry_t *entry TSRMLS_DC) /* {{{ */
  282. {
  283. cache->entries_count --;
  284. if ((type == XC_TYPE_PHP ? ((xc_entry_php_t *) entry)->refcount : 0) == 0) {
  285. xc_entry_free_real_unlocked(type, cache, entry);
  286. }
  287. else {
  288. entry->next = cache->deletes;
  289. cache->deletes = entry;
  290. entry->dtime = XG(request_time);
  291. cache->deletes_count ++;
  292. }
  293. return;
  294. }
  295. /* }}} */
  296. static void xc_entry_remove_unlocked(xc_entry_type_t type, xc_cache_t *cache, xc_hash_value_t entryslotid, xc_entry_t *entry TSRMLS_DC) /* {{{ */
  297. {
  298. xc_entry_t **pp = &(cache->entries[entryslotid]);
  299. xc_entry_t *p;
  300. for (p = *pp; p; pp = &(p->next), p = p->next) {
  301. if (xc_entry_equal_unlocked(type, entry, p TSRMLS_CC)) {
  302. /* unlink */
  303. *pp = p->next;
  304. xc_entry_free_unlocked(type, cache, entry TSRMLS_CC);
  305. return;
  306. }
  307. }
  308. assert(0);
  309. }
  310. /* }}} */
  311. static xc_entry_t *xc_entry_find_unlocked(xc_entry_type_t type, xc_cache_t *cache, xc_hash_value_t entryslotid, xc_entry_t *entry TSRMLS_DC) /* {{{ */
  312. {
  313. xc_entry_t *p;
  314. for (p = cache->entries[entryslotid]; p; p = p->next) {
  315. if (xc_entry_equal_unlocked(type, entry, p TSRMLS_CC)) {
  316. zend_bool fresh;
  317. switch (type) {
  318. case XC_TYPE_PHP:
  319. {
  320. xc_entry_php_t *p_php = (xc_entry_php_t *) p;
  321. xc_entry_php_t *entry_php = (xc_entry_php_t *) entry;
  322. fresh = p_php->file_mtime == entry_php->file_mtime && p_php->file_size == entry_php->file_size;
  323. }
  324. break;
  325. case XC_TYPE_VAR:
  326. {
  327. fresh = !VAR_ENTRY_EXPIRED(p);
  328. }
  329. break;
  330. default:
  331. assert(0);
  332. }
  333. if (fresh) {
  334. p->hits ++;
  335. p->atime = XG(request_time);
  336. return p;
  337. }
  338. xc_entry_remove_unlocked(type, cache, entryslotid, p TSRMLS_CC);
  339. return NULL;
  340. }
  341. }
  342. return NULL;
  343. }
  344. /* }}} */
  345. static void xc_entry_hold_php_unlocked(xc_cache_t *cache, xc_entry_php_t *entry TSRMLS_DC) /* {{{ */
  346. {
  347. TRACE("hold %d:%s", entry->file_inode, entry->entry.name.str.val);
  348. entry->refcount ++;
  349. xc_stack_push(&XG(php_holds)[cache->cacheid], (void *)entry);
  350. }
  351. /* }}} */
  352. static inline zend_uint advance_wrapped(zend_uint val, zend_uint count) /* {{{ */
  353. {
  354. if (val + 1 >= count) {
  355. return 0;
  356. }
  357. return val + 1;
  358. }
  359. /* }}} */
  360. static void xc_counters_inc(time_t *curtime, zend_uint *curslot, time_t period, zend_ulong *counters, zend_uint count TSRMLS_DC) /* {{{ */
  361. {
  362. time_t n = XG(request_time) / period;
  363. if (*curtime != n) {
  364. zend_uint target_slot = n % count;
  365. if (n - *curtime > period) {
  366. memset(counters, 0, sizeof(counters[0]) * count);
  367. }
  368. else {
  369. zend_uint slot;
  370. for (slot = advance_wrapped(*curslot, count);
  371. slot != target_slot;
  372. slot = advance_wrapped(slot, count)) {
  373. counters[slot] = 0;
  374. }
  375. counters[target_slot] = 0;
  376. }
  377. *curtime = n;
  378. *curslot = target_slot;
  379. }
  380. counters[*curslot] ++;
  381. }
  382. /* }}} */
  383. static void xc_cache_hit_unlocked(xc_cache_t *cache TSRMLS_DC) /* {{{ */
  384. {
  385. cache->hits ++;
  386. xc_counters_inc(&cache->hits_by_hour_cur_time
  387. , &cache->hits_by_hour_cur_slot, 60 * 60
  388. , cache->hits_by_hour
  389. , sizeof(cache->hits_by_hour) / sizeof(cache->hits_by_hour[0])
  390. TSRMLS_CC);
  391. xc_counters_inc(&cache->hits_by_second_cur_time
  392. , &cache->hits_by_second_cur_slot
  393. , 1
  394. , cache->hits_by_second
  395. , sizeof(cache->hits_by_second) / sizeof(cache->hits_by_second[0])
  396. TSRMLS_CC);
  397. }
  398. /* }}} */
  399. /* helper function that loop through each entry */
  400. #define XC_ENTRY_APPLY_FUNC(name) zend_bool name(xc_entry_t *entry TSRMLS_DC)
  401. typedef XC_ENTRY_APPLY_FUNC((*cache_apply_unlocked_func_t));
  402. static void xc_entry_apply_unlocked(xc_entry_type_t type, xc_cache_t *cache, cache_apply_unlocked_func_t apply_func TSRMLS_DC) /* {{{ */
  403. {
  404. xc_entry_t *p, **pp;
  405. int i, c;
  406. for (i = 0, c = cache->hentry->size; i < c; i ++) {
  407. pp = &(cache->entries[i]);
  408. for (p = *pp; p; p = *pp) {
  409. if (apply_func(p TSRMLS_CC)) {
  410. /* unlink */
  411. *pp = p->next;
  412. xc_entry_free_unlocked(type, cache, p TSRMLS_CC);
  413. }
  414. else {
  415. pp = &(p->next);
  416. }
  417. }
  418. }
  419. }
  420. /* }}} */
  421. #define XC_CACHE_APPLY_FUNC(name) void name(xc_cache_t *cache TSRMLS_DC)
  422. /* call graph:
  423. * xc_gc_expires_php -> xc_gc_expires_one -> xc_entry_apply_unlocked -> xc_gc_expires_php_entry_unlocked
  424. * xc_gc_expires_var -> xc_gc_expires_one -> xc_entry_apply_unlocked -> xc_gc_expires_var_entry_unlocked
  425. */
  426. static XC_ENTRY_APPLY_FUNC(xc_gc_expires_php_entry_unlocked) /* {{{ */
  427. {
  428. TRACE("ttl %lu, %lu %lu", (zend_ulong) XG(request_time), (zend_ulong) entry->atime, xc_php_ttl);
  429. if (XG(request_time) > entry->atime + (time_t) xc_php_ttl) {
  430. return 1;
  431. }
  432. return 0;
  433. }
  434. /* }}} */
  435. static XC_ENTRY_APPLY_FUNC(xc_gc_expires_var_entry_unlocked) /* {{{ */
  436. {
  437. if (VAR_ENTRY_EXPIRED(entry)) {
  438. return 1;
  439. }
  440. return 0;
  441. }
  442. /* }}} */
  443. static void xc_gc_expires_one(xc_entry_type_t type, xc_cache_t *cache, zend_ulong gc_interval, cache_apply_unlocked_func_t apply_func TSRMLS_DC) /* {{{ */
  444. {
  445. TRACE("interval %lu, %lu %lu", (zend_ulong) XG(request_time), (zend_ulong) cache->last_gc_expires, gc_interval);
  446. if (XG(request_time) >= cache->last_gc_expires + (time_t) gc_interval) {
  447. ENTER_LOCK(cache) {
  448. if (XG(request_time) >= cache->last_gc_expires + (time_t) gc_interval) {
  449. cache->last_gc_expires = XG(request_time);
  450. xc_entry_apply_unlocked(type, cache, apply_func TSRMLS_CC);
  451. }
  452. } LEAVE_LOCK(cache);
  453. }
  454. }
  455. /* }}} */
  456. static void xc_gc_expires_php(TSRMLS_D) /* {{{ */
  457. {
  458. int i, c;
  459. if (!xc_php_ttl || !xc_php_gc_interval || !xc_php_caches) {
  460. return;
  461. }
  462. for (i = 0, c = xc_php_hcache.size; i < c; i ++) {
  463. xc_gc_expires_one(XC_TYPE_PHP, xc_php_caches[i], xc_php_gc_interval, xc_gc_expires_php_entry_unlocked TSRMLS_CC);
  464. }
  465. }
  466. /* }}} */
  467. static void xc_gc_expires_var(TSRMLS_D) /* {{{ */
  468. {
  469. int i, c;
  470. if (!xc_var_gc_interval || !xc_var_caches) {
  471. return;
  472. }
  473. for (i = 0, c = xc_var_hcache.size; i < c; i ++) {
  474. xc_gc_expires_one(XC_TYPE_VAR, xc_var_caches[i], xc_var_gc_interval, xc_gc_expires_var_entry_unlocked TSRMLS_CC);
  475. }
  476. }
  477. /* }}} */
  478. static XC_CACHE_APPLY_FUNC(xc_gc_delete_unlocked) /* {{{ */
  479. {
  480. xc_entry_t *p, **pp;
  481. pp = &cache->deletes;
  482. for (p = *pp; p; p = *pp) {
  483. xc_entry_php_t *entry = (xc_entry_php_t *) p;
  484. if (XG(request_time) - p->dtime > 3600) {
  485. entry->refcount = 0;
  486. /* issue warning here */
  487. }
  488. if (entry->refcount == 0) {
  489. /* unlink */
  490. *pp = p->next;
  491. cache->deletes_count --;
  492. xc_entry_free_real_unlocked(XC_TYPE_PHP, cache, p);
  493. }
  494. else {
  495. pp = &(p->next);
  496. }
  497. }
  498. }
  499. /* }}} */
  500. static XC_CACHE_APPLY_FUNC(xc_gc_deletes_one) /* {{{ */
  501. {
  502. if (cache->deletes && XG(request_time) - cache->last_gc_deletes > xc_deletes_gc_interval) {
  503. ENTER_LOCK(cache) {
  504. if (cache->deletes && XG(request_time) - cache->last_gc_deletes > xc_deletes_gc_interval) {
  505. cache->last_gc_deletes = XG(request_time);
  506. xc_gc_delete_unlocked(cache TSRMLS_CC);
  507. }
  508. } LEAVE_LOCK(cache);
  509. }
  510. }
  511. /* }}} */
  512. static void xc_gc_deletes(TSRMLS_D) /* {{{ */
  513. {
  514. int i, c;
  515. if (xc_php_caches) {
  516. for (i = 0, c = xc_php_hcache.size; i < c; i ++) {
  517. xc_gc_deletes_one(xc_php_caches[i] TSRMLS_CC);
  518. }
  519. }
  520. if (xc_var_caches) {
  521. for (i = 0, c = xc_var_hcache.size; i < c; i ++) {
  522. xc_gc_deletes_one(xc_var_caches[i] TSRMLS_CC);
  523. }
  524. }
  525. }
  526. /* }}} */
  527. /* helper functions for user functions */
  528. static void xc_fillinfo_unlocked(int cachetype, xc_cache_t *cache, zval *return_value TSRMLS_DC) /* {{{ */
  529. {
  530. zval *blocks, *hits;
  531. int i;
  532. const xc_block_t *b;
  533. #ifndef NDEBUG
  534. xc_memsize_t avail = 0;
  535. #endif
  536. xc_mem_t *mem = cache->mem;
  537. const xc_mem_handlers_t *handlers = mem->handlers;
  538. zend_ulong interval;
  539. if (cachetype == XC_TYPE_PHP) {
  540. interval = xc_php_ttl ? xc_php_gc_interval : 0;
  541. }
  542. else {
  543. interval = xc_var_gc_interval;
  544. }
  545. add_assoc_long_ex(return_value, ZEND_STRS("slots"), cache->hentry->size);
  546. add_assoc_long_ex(return_value, ZEND_STRS("compiling"), cache->compiling);
  547. add_assoc_long_ex(return_value, ZEND_STRS("updates"), cache->updates);
  548. add_assoc_long_ex(return_value, ZEND_STRS("misses"), cache->updates); /* deprecated */
  549. add_assoc_long_ex(return_value, ZEND_STRS("hits"), cache->hits);
  550. add_assoc_long_ex(return_value, ZEND_STRS("clogs"), cache->clogs);
  551. add_assoc_long_ex(return_value, ZEND_STRS("ooms"), cache->ooms);
  552. add_assoc_long_ex(return_value, ZEND_STRS("errors"), cache->errors);
  553. add_assoc_long_ex(return_value, ZEND_STRS("cached"), cache->entries_count);
  554. add_assoc_long_ex(return_value, ZEND_STRS("deleted"), cache->deletes_count);
  555. if (interval) {
  556. time_t gc = (cache->last_gc_expires + interval) - XG(request_time);
  557. add_assoc_long_ex(return_value, ZEND_STRS("gc"), gc > 0 ? gc : 0);
  558. }
  559. else {
  560. add_assoc_null_ex(return_value, ZEND_STRS("gc"));
  561. }
  562. MAKE_STD_ZVAL(hits);
  563. array_init(hits);
  564. for (i = 0; i < sizeof(cache->hits_by_hour) / sizeof(cache->hits_by_hour[0]); i ++) {
  565. add_next_index_long(hits, (long) cache->hits_by_hour[i]);
  566. }
  567. add_assoc_zval_ex(return_value, ZEND_STRS("hits_by_hour"), hits);
  568. MAKE_STD_ZVAL(hits);
  569. array_init(hits);
  570. for (i = 0; i < sizeof(cache->hits_by_second) / sizeof(cache->hits_by_second[0]); i ++) {
  571. add_next_index_long(hits, (long) cache->hits_by_second[i]);
  572. }
  573. add_assoc_zval_ex(return_value, ZEND_STRS("hits_by_second"), hits);
  574. MAKE_STD_ZVAL(blocks);
  575. array_init(blocks);
  576. add_assoc_long_ex(return_value, ZEND_STRS("size"), handlers->size(mem));
  577. add_assoc_long_ex(return_value, ZEND_STRS("avail"), handlers->avail(mem));
  578. add_assoc_bool_ex(return_value, ZEND_STRS("can_readonly"), xc_readonly_protection);
  579. for (b = handlers->freeblock_first(mem); b; b = handlers->freeblock_next(b)) {
  580. zval *bi;
  581. MAKE_STD_ZVAL(bi);
  582. array_init(bi);
  583. add_assoc_long_ex(bi, ZEND_STRS("size"), handlers->block_size(b));
  584. add_assoc_long_ex(bi, ZEND_STRS("offset"), handlers->block_offset(mem, b));
  585. add_next_index_zval(blocks, bi);
  586. #ifndef NDEBUG
  587. avail += handlers->block_size(b);
  588. #endif
  589. }
  590. add_assoc_zval_ex(return_value, ZEND_STRS("free_blocks"), blocks);
  591. #ifndef NDEBUG
  592. assert(avail == handlers->avail(mem));
  593. #endif
  594. }
  595. /* }}} */
  596. static void xc_fillentry_unlocked(xc_entry_type_t type, const xc_entry_t *entry, xc_hash_value_t entryslotid, int del, zval *list TSRMLS_DC) /* {{{ */
  597. {
  598. zval* ei;
  599. const xc_entry_data_php_t *php;
  600. ALLOC_INIT_ZVAL(ei);
  601. array_init(ei);
  602. add_assoc_long_ex(ei, ZEND_STRS("hits"), entry->hits);
  603. add_assoc_long_ex(ei, ZEND_STRS("ctime"), entry->ctime);
  604. add_assoc_long_ex(ei, ZEND_STRS("atime"), entry->atime);
  605. add_assoc_long_ex(ei, ZEND_STRS("hvalue"), entryslotid);
  606. if (del) {
  607. add_assoc_long_ex(ei, ZEND_STRS("dtime"), entry->dtime);
  608. }
  609. #ifdef IS_UNICODE
  610. do {
  611. zval *zv;
  612. ALLOC_INIT_ZVAL(zv);
  613. switch (entry->name_type) {
  614. case IS_UNICODE:
  615. ZVAL_UNICODEL(zv, entry->name.ustr.val, entry->name.ustr.len, 1);
  616. break;
  617. case IS_STRING:
  618. ZVAL_STRINGL(zv, entry->name.str.val, entry->name.str.len, 1);
  619. break;
  620. default:
  621. assert(0);
  622. }
  623. zv->type = entry->name_type;
  624. add_assoc_zval_ex(ei, ZEND_STRS("name"), zv);
  625. } while (0);
  626. #else
  627. add_assoc_stringl_ex(ei, ZEND_STRS("name"), entry->name.str.val, entry->name.str.len, 1);
  628. #endif
  629. switch (type) {
  630. case XC_TYPE_PHP: {
  631. xc_entry_php_t *entry_php = (xc_entry_php_t *) entry;
  632. php = entry_php->php;
  633. add_assoc_long_ex(ei, ZEND_STRS("size"), entry->size + php->size);
  634. add_assoc_long_ex(ei, ZEND_STRS("refcount"), entry_php->refcount);
  635. add_assoc_long_ex(ei, ZEND_STRS("phprefcount"), php->refcount);
  636. add_assoc_long_ex(ei, ZEND_STRS("file_mtime"), entry_php->file_mtime);
  637. add_assoc_long_ex(ei, ZEND_STRS("file_size"), entry_php->file_size);
  638. add_assoc_long_ex(ei, ZEND_STRS("file_device"), entry_php->file_device);
  639. add_assoc_long_ex(ei, ZEND_STRS("file_inode"), entry_php->file_inode);
  640. #ifdef HAVE_XCACHE_CONSTANT
  641. add_assoc_long_ex(ei, ZEND_STRS("constinfo_cnt"), php->constinfo_cnt);
  642. #endif
  643. add_assoc_long_ex(ei, ZEND_STRS("function_cnt"), php->funcinfo_cnt);
  644. add_assoc_long_ex(ei, ZEND_STRS("class_cnt"), php->classinfo_cnt);
  645. #ifdef ZEND_ENGINE_2_1
  646. add_assoc_long_ex(ei, ZEND_STRS("autoglobal_cnt"),php->autoglobal_cnt);
  647. #endif
  648. break;
  649. }
  650. case XC_TYPE_VAR:
  651. add_assoc_long_ex(ei, ZEND_STRS("refcount"), 0); /* for BC only */
  652. add_assoc_long_ex(ei, ZEND_STRS("size"), entry->size);
  653. break;
  654. default:
  655. assert(0);
  656. }
  657. add_next_index_zval(list, ei);
  658. }
  659. /* }}} */
  660. static void xc_filllist_unlocked(xc_entry_type_t type, xc_cache_t *cache, zval *return_value TSRMLS_DC) /* {{{ */
  661. {
  662. zval* list;
  663. int i, c;
  664. xc_entry_t *e;
  665. ALLOC_INIT_ZVAL(list);
  666. array_init(list);
  667. for (i = 0, c = cache->hentry->size; i < c; i ++) {
  668. for (e = cache->entries[i]; e; e = e->next) {
  669. xc_fillentry_unlocked(type, e, i, 0, list TSRMLS_CC);
  670. }
  671. }
  672. add_assoc_zval(return_value, "cache_list", list);
  673. ALLOC_INIT_ZVAL(list);
  674. array_init(list);
  675. for (e = cache->deletes; e; e = e->next) {
  676. xc_fillentry_unlocked(XC_TYPE_PHP, e, 0, 1, list TSRMLS_CC);
  677. }
  678. add_assoc_zval(return_value, "deleted_list", list);
  679. }
  680. /* }}} */
  681. static zend_op_array *xc_entry_install(xc_entry_php_t *entry_php TSRMLS_DC) /* {{{ */
  682. {
  683. zend_uint i;
  684. xc_entry_data_php_t *p = entry_php->php;
  685. zend_op_array *old_active_op_array = CG(active_op_array);
  686. #ifndef ZEND_ENGINE_2
  687. ALLOCA_FLAG(use_heap)
  688. /* new ptr which is stored inside CG(class_table) */
  689. xc_cest_t **new_cest_ptrs = (xc_cest_t **)my_do_alloca(sizeof(xc_cest_t*) * p->classinfo_cnt, use_heap);
  690. #endif
  691. CG(active_op_array) = p->op_array;
  692. #ifdef HAVE_XCACHE_CONSTANT
  693. /* install constant */
  694. for (i = 0; i < p->constinfo_cnt; i ++) {
  695. xc_constinfo_t *ci = &p->constinfos[i];
  696. xc_install_constant(entry_php->entry.name.str.val, &ci->constant,
  697. UNISW(0, ci->type), ci->key, ci->key_size, ci->h TSRMLS_CC);
  698. }
  699. #endif
  700. /* install function */
  701. for (i = 0; i < p->funcinfo_cnt; i ++) {
  702. xc_funcinfo_t *fi = &p->funcinfos[i];
  703. xc_install_function(entry_php->entry.name.str.val, &fi->func,
  704. UNISW(0, fi->type), fi->key, fi->key_size, fi->h TSRMLS_CC);
  705. }
  706. /* install class */
  707. for (i = 0; i < p->classinfo_cnt; i ++) {
  708. xc_classinfo_t *ci = &p->classinfos[i];
  709. #ifndef ZEND_ENGINE_2
  710. zend_class_entry *ce = CestToCePtr(ci->cest);
  711. /* fix pointer to the be which inside class_table */
  712. if (ce->parent) {
  713. zend_uint class_idx = (/* class_num */ (int) (long) ce->parent) - 1;
  714. assert(class_idx < i);
  715. ci->cest.parent = new_cest_ptrs[class_idx];
  716. }
  717. new_cest_ptrs[i] =
  718. #endif
  719. #ifdef ZEND_COMPILE_DELAYED_BINDING
  720. xc_install_class(entry_php->entry.name.str.val, &ci->cest, -1,
  721. UNISW(0, ci->type), ci->key, ci->key_size, ci->h TSRMLS_CC);
  722. #else
  723. xc_install_class(entry_php->entry.name.str.val, &ci->cest, ci->oplineno,
  724. UNISW(0, ci->type), ci->key, ci->key_size, ci->h TSRMLS_CC);
  725. #endif
  726. }
  727. #ifdef ZEND_ENGINE_2_1
  728. /* trigger auto_globals jit */
  729. for (i = 0; i < p->autoglobal_cnt; i ++) {
  730. xc_autoglobal_t *aginfo = &p->autoglobals[i];
  731. zend_u_is_auto_global(aginfo->type, aginfo->key, aginfo->key_len TSRMLS_CC);
  732. }
  733. #endif
  734. #ifdef XCACHE_ERROR_CACHING
  735. /* restore trigger errors */
  736. for (i = 0; i < p->compilererror_cnt; i ++) {
  737. xc_compilererror_t *error = &p->compilererrors[i];
  738. CG(zend_lineno) = error->lineno;
  739. zend_error(error->type, "%s", error->error);
  740. }
  741. CG(zend_lineno) = 0;
  742. #endif
  743. i = 1;
  744. #ifndef ZEND_ENGINE_2_2
  745. zend_hash_add(&EG(included_files), entry_php->entry.name.str.val, entry_php->entry.name.str.len+1, (void *)&i, sizeof(int), NULL);
  746. #endif
  747. #ifndef ZEND_ENGINE_2
  748. my_free_alloca(new_cest_ptrs, use_heap);
  749. #endif
  750. CG(active_op_array) = old_active_op_array;
  751. return p->op_array;
  752. }
  753. /* }}} */
  754. static inline void xc_entry_unholds_real(xc_stack_t *holds, xc_cache_t **caches, int cachecount TSRMLS_DC) /* {{{ */
  755. {
  756. int i;
  757. xc_stack_t *s;
  758. xc_cache_t *cache;
  759. xc_entry_php_t *entry_php;
  760. for (i = 0; i < cachecount; i ++) {
  761. s = &holds[i];
  762. TRACE("holded %d items", xc_stack_count(s));
  763. if (xc_stack_count(s)) {
  764. cache = caches[i];
  765. ENTER_LOCK(cache) {
  766. while (xc_stack_count(s)) {
  767. entry_php = (xc_entry_php_t *) xc_stack_pop(s);
  768. TRACE("unhold %d:%s", entry_php->file_inode, entry_php->entry.name.str.val);
  769. --entry_php->refcount;
  770. assert(entry_php->refcount >= 0);
  771. }
  772. } LEAVE_LOCK(cache);
  773. }
  774. }
  775. }
  776. /* }}} */
  777. static void xc_entry_unholds(TSRMLS_D) /* {{{ */
  778. {
  779. if (xc_php_caches) {
  780. xc_entry_unholds_real(XG(php_holds), xc_php_caches, xc_php_hcache.size TSRMLS_CC);
  781. }
  782. if (xc_var_caches) {
  783. xc_entry_unholds_real(XG(var_holds), xc_var_caches, xc_var_hcache.size TSRMLS_CC);
  784. }
  785. }
  786. /* }}} */
  787. #define HASH(i) (i)
  788. #define HASH_ZSTR_L(t, s, l) HASH(zend_u_inline_hash_func((t), (s), ((l) + 1) * sizeof(UChar)))
  789. #define HASH_STR_S(s, l) HASH(zend_inline_hash_func((char *) (s), (l)))
  790. #define HASH_STR_L(s, l) HASH_STR_S((s), (l) + 1)
  791. #define HASH_STR(s) HASH_STR_L((s), strlen((s)) + 1)
  792. #define HASH_NUM(n) HASH(n)
  793. static inline xc_hash_value_t xc_hash_fold(xc_hash_value_t hvalue, const xc_hash_t *hasher) /* {{{ fold hash bits as needed */
  794. {
  795. xc_hash_value_t folded = 0;
  796. while (hvalue) {
  797. folded ^= (hvalue & hasher->mask);
  798. hvalue >>= hasher->bits;
  799. }
  800. return folded;
  801. }
  802. /* }}} */
  803. static inline xc_hash_value_t xc_entry_hash_name(xc_entry_t *entry TSRMLS_DC) /* {{{ */
  804. {
  805. return UNISW(NOTHING, UG(unicode) ? HASH_ZSTR_L(entry->name_type, entry->name.uni.val, entry->name.uni.len) :)
  806. HASH_STR_L(entry->name.str.val, entry->name.str.len);
  807. }
  808. /* }}} */
  809. #define xc_entry_hash_var xc_entry_hash_name
  810. static void xc_entry_free_key_php(xc_entry_php_t *entry_php TSRMLS_DC) /* {{{ */
  811. {
  812. #define X_FREE(var) do {\
  813. if (entry_php->var) { \
  814. efree(entry_php->var); \
  815. } \
  816. } while (0)
  817. X_FREE(dirpath);
  818. #ifdef IS_UNICODE
  819. X_FREE(ufilepath);
  820. X_FREE(udirpath);
  821. #endif
  822. #undef X_FREE
  823. }
  824. /* }}} */
  825. static char *xc_expand_url(const char *filepath, char *real_path TSRMLS_DC) /* {{{ */
  826. {
  827. if (strstr(filepath, "://") != NULL) {
  828. size_t filepath_len = strlen(filepath);
  829. size_t copy_len = filepath_len > MAXPATHLEN - 1 ? MAXPATHLEN - 1 : filepath_len;
  830. memcpy(real_path, filepath, filepath_len);
  831. real_path[copy_len] = '\0';
  832. return real_path;
  833. }
  834. return expand_filepath(filepath, real_path TSRMLS_CC);
  835. }
  836. /* }}} */
  837. #define XC_RESOLVE_PATH_CHECKER(name) zend_bool name(const char *filepath, size_t filepath_len, void *data TSRMLS_DC)
  838. typedef XC_RESOLVE_PATH_CHECKER((*xc_resolve_path_checker_func_t));
  839. static zend_bool xc_resolve_path(const char *filepath, char *path_buffer, xc_resolve_path_checker_func_t checker_func, void *data TSRMLS_DC) /* {{{ */
  840. {
  841. char *paths, *path;
  842. char *tokbuf;
  843. size_t path_buffer_len;
  844. int size;
  845. char tokens[] = { DEFAULT_DIR_SEPARATOR, '\0' };
  846. int ret;
  847. ALLOCA_FLAG(use_heap)
  848. #if 0
  849. if ((*filepath == '.' &&
  850. (IS_SLASH(filepath[1]) ||
  851. ((filepath[1] == '.') && IS_SLASH(filepath[2])))) ||
  852. IS_ABSOLUTE_PATH(filepath, strlen(filepath)) ||
  853. !path ||
  854. !*path) {
  855. if (checker_func(path_buffer, path_buffer_len, data TSRMLS_CC)) {
  856. ret = 1;
  857. }
  858. else {
  859. ret = FAILURE;
  860. }
  861. goto finish;
  862. }
  863. #endif
  864. size = strlen(PG(include_path)) + 1;
  865. paths = (char *)my_do_alloca(size, use_heap);
  866. memcpy(paths, PG(include_path), size);
  867. for (path = php_strtok_r(paths, tokens, &tokbuf); path; path = php_strtok_r(NULL, tokens, &tokbuf)) {
  868. path_buffer_len = snprintf(path_buffer, MAXPATHLEN, "%s/%s", path, filepath);
  869. if (path_buffer_len < MAXPATHLEN - 1) {
  870. if (checker_func(path_buffer, path_buffer_len, data TSRMLS_CC)) {
  871. ret = 1;
  872. goto finish;
  873. }
  874. }
  875. }
  876. /* fall back to current directory */
  877. if (zend_is_executing(TSRMLS_C)) {
  878. const char *executed_filename = zend_get_executed_filename(TSRMLS_C);
  879. if (executed_filename && executed_filename[0] && executed_filename[0] != '[') {
  880. size_t filename_len = strlen(filepath);
  881. size_t dirname_len;
  882. for (dirname_len = strlen(executed_filename) - 1; dirname_len > 0; --dirname_len) {
  883. if (IS_SLASH(executed_filename[dirname_len])) {
  884. if (dirname_len + filename_len < MAXPATHLEN - 1) {
  885. ++dirname_len; /* include tailing slash */
  886. memcpy(path_buffer, executed_filename, dirname_len);
  887. memcpy(path_buffer + dirname_len, filepath, filename_len);
  888. path_buffer_len = dirname_len + filename_len;
  889. path_buffer[path_buffer_len] = '\0';
  890. if (checker_func(path_buffer, path_buffer_len, data TSRMLS_CC)) {
  891. ret = 1;
  892. goto finish;
  893. }
  894. }
  895. break;
  896. }
  897. }
  898. }
  899. }
  900. ret = 0;
  901. finish:
  902. my_free_alloca(paths, use_heap);
  903. return ret;
  904. }
  905. /* }}} */
  906. #ifndef ZEND_ENGINE_2_3
  907. static XC_RESOLVE_PATH_CHECKER(xc_stat_file) /* {{{ */
  908. {
  909. return VCWD_STAT(filepath, (struct stat *) data) == 0 ? 1 : 0;
  910. }
  911. /* }}} */
  912. static int xc_resolve_path_stat(const char *filepath, char *path_buffer, struct stat *pbuf TSRMLS_DC) /* {{{ */
  913. {
  914. return xc_resolve_path(filepath, path_buffer, xc_stat_file, (void *) pbuf TSRMLS_CC)
  915. ? SUCCESS
  916. : FAILURE;
  917. }
  918. /* }}} */
  919. #endif
  920. typedef struct xc_compiler_t { /* {{{ */
  921. /* XCache cached compile state */
  922. const char *filename;
  923. size_t filename_len;
  924. const char *opened_path;
  925. char opened_path_buffer[MAXPATHLEN];
  926. xc_entry_hash_t entry_hash;
  927. xc_entry_php_t new_entry;
  928. xc_entry_data_php_t new_php;
  929. } xc_compiler_t;
  930. /* }}} */
  931. typedef struct xc_entry_resolve_path_data_t { /* {{{ */
  932. xc_compiler_t *compiler;
  933. xc_entry_php_t **stored_entry;
  934. } xc_entry_resolve_path_data_t;
  935. /* }}} */
  936. static XC_RESOLVE_PATH_CHECKER(xc_entry_resolve_path_func_unlocked) /* {{{ */
  937. {
  938. xc_entry_resolve_path_data_t *entry_resolve_path_data = (xc_entry_resolve_path_data_t *) data;
  939. xc_compiler_t *compiler = entry_resolve_path_data->compiler;
  940. compiler->new_entry.entry.name.str.val = xc_expand_url(filepath, compiler->opened_path_buffer TSRMLS_CC);
  941. compiler->new_entry.entry.name.str.len = strlen(compiler->new_entry.entry.name.str.val);
  942. *entry_resolve_path_data->stored_entry = (xc_entry_php_t *) xc_entry_find_unlocked(
  943. XC_TYPE_PHP
  944. , xc_php_caches[compiler->entry_hash.cacheid]
  945. , compiler->entry_hash.entryslotid
  946. , (xc_entry_t *) &compiler->new_entry
  947. TSRMLS_CC);
  948. return *entry_resolve_path_data->stored_entry ? 1 : 0;
  949. }
  950. /* }}} */
  951. static int xc_entry_resolve_path_unlocked(xc_compiler_t *compiler, const char *filepath, xc_entry_php_t **stored_entry TSRMLS_DC) /* {{{ */
  952. {
  953. char path_buffer[MAXPATHLEN];
  954. xc_entry_resolve_path_data_t entry_resolve_path_data;
  955. entry_resolve_path_data.compiler = compiler;
  956. entry_resolve_path_data.stored_entry = stored_entry;
  957. return xc_resolve_path(filepath, path_buffer, xc_entry_resolve_path_func_unlocked, (void *) &entry_resolve_path_data TSRMLS_CC)
  958. ? SUCCESS
  959. : FAILURE;
  960. }
  961. /* }}} */
  962. static int xc_entry_php_quick_resolve_opened_path(xc_compiler_t *compiler, struct stat *statbuf TSRMLS_DC) /* {{{ */
  963. {
  964. if (strcmp(SG(request_info).path_translated, compiler->filename) == 0) {
  965. /* sapi has already done this stat() for us */
  966. if (statbuf) {
  967. struct stat *sapi_stat = sapi_get_stat(TSRMLS_C);
  968. if (!sapi_stat) {
  969. goto giveupsapistat;
  970. }
  971. *statbuf = *sapi_stat;
  972. }
  973. compiler->opened_path = xc_expand_url(compiler->filename, compiler->opened_path_buffer TSRMLS_CC);
  974. return SUCCESS;
  975. }
  976. giveupsapistat:
  977. /* absolute path */
  978. if (IS_ABSOLUTE_PATH(compiler->filename, strlen(compiler->filename))) {
  979. if (statbuf && VCWD_STAT(compiler->filename, statbuf) != 0) {
  980. return FAILURE;
  981. }
  982. compiler->opened_path = xc_expand_url(compiler->filename, compiler->opened_path_buffer TSRMLS_CC);
  983. return SUCCESS;
  984. }
  985. /* relative path */
  986. if (*compiler->filename == '.' && (IS_SLASH(compiler->filename[1]) || compiler->filename[1] == '.')) {
  987. const char *ptr = compiler->filename + 1;
  988. if (*ptr == '.') {
  989. while (*(++ptr) == '.');
  990. if (!IS_SLASH(*ptr)) {
  991. return FAILURE;
  992. }
  993. }
  994. if (statbuf && VCWD_STAT(compiler->filename, statbuf) != 0) {
  995. return FAILURE;
  996. }
  997. compiler->opened_path = xc_expand_url(compiler->filename, compiler->opened_path_buffer TSRMLS_CC);
  998. return SUCCESS;
  999. }
  1000. return FAILURE;
  1001. }
  1002. /* }}} */
  1003. static int xc_entry_php_resolve_opened_path(xc_compiler_t *compiler, struct stat *statbuf TSRMLS_DC) /* {{{ */
  1004. {
  1005. if (xc_entry_php_quick_resolve_opened_path(compiler, statbuf TSRMLS_CC) == SUCCESS) {
  1006. /* opened_path resolved */
  1007. return SUCCESS;
  1008. }
  1009. /* fall back to real stat call */
  1010. else {
  1011. #ifdef ZEND_ENGINE_2_3
  1012. char *opened_path = php_resolve_path(compiler->filename, compiler->filename_len, PG(include_path) TSRMLS_CC);
  1013. if (opened_path) {
  1014. strcpy(compiler->opened_path_buffer, opened_path);
  1015. efree(opened_path);
  1016. compiler->opened_path = compiler->opened_path_buffer;
  1017. if (!statbuf || VCWD_STAT(compiler->opened_path, statbuf) == 0) {
  1018. return SUCCESS;
  1019. }
  1020. }
  1021. #else
  1022. char path_buffer[MAXPATHLEN];
  1023. if (xc_resolve_path_stat(compiler->filename, path_buffer, statbuf TSRMLS_CC) == SUCCESS) {
  1024. compiler->opened_path = xc_expand_url(path_buffer, compiler->opened_path_buffer TSRMLS_CC);
  1025. return SUCCESS;
  1026. }
  1027. #endif
  1028. }
  1029. return FAILURE;
  1030. }
  1031. /* }}} */
  1032. static int xc_entry_php_init_key(xc_compiler_t *compiler TSRMLS_DC) /* {{{ */
  1033. {
  1034. if (XG(stat)) {
  1035. struct stat buf;
  1036. time_t delta;
  1037. if (compiler->opened_path) {
  1038. if (VCWD_STAT(compiler->opened_path, &buf) != 0) {
  1039. return FAILURE;
  1040. }
  1041. }
  1042. else {
  1043. if (xc_entry_php_resolve_opened_path(compiler, &buf TSRMLS_CC) != SUCCESS) {
  1044. return FAILURE;
  1045. }
  1046. }
  1047. delta = XG(request_time) - buf.st_mtime;
  1048. if (abs(delta) < 2 && !xc_test) {
  1049. return FAILURE;
  1050. }
  1051. compiler->new_entry.file_mtime = buf.st_mtime;
  1052. compiler->new_entry.file_size = buf.st_size;
  1053. compiler->new_entry.file_device = buf.st_dev;
  1054. compiler->new_entry.file_inode = buf.st_ino;
  1055. }
  1056. else {
  1057. xc_entry_php_quick_resolve_opened_path(compiler, NULL TSRMLS_CC);
  1058. compiler->new_entry.file_mtime = 0;
  1059. compiler->new_entry.file_size = 0;
  1060. compiler->new_entry.file_device = 0;
  1061. compiler->new_entry.file_inode = 0;
  1062. }
  1063. {
  1064. xc_hash_value_t basename_hash_value;
  1065. if (xc_php_hcache.size > 1
  1066. || !compiler->new_entry.file_inode) {
  1067. const char *filename_end = compiler->filename + compiler->filename_len;
  1068. const char *basename = filename_end - 1;
  1069. /* scan till out of basename part */
  1070. while (basename >= compiler->filename && !IS_SLASH(*basename)) {
  1071. --basename;
  1072. }
  1073. /* get back to basename */
  1074. ++basename;
  1075. basename_hash_value = HASH_STR_L(basename, filename_end - basename);
  1076. }
  1077. compiler->entry_hash.cacheid = xc_php_hcache.size > 1 ? xc_hash_fold(basename_hash_value, &xc_php_hcache) : 0;
  1078. compiler->entry_hash.entryslotid = xc_hash_fold(
  1079. compiler->new_entry.file_inode
  1080. ? HASH(compiler->new_entry.file_device + compiler->new_entry.file_inode)
  1081. : basename_hash_value
  1082. , &xc_php_hentry);
  1083. }
  1084. compiler->new_entry.filepath = NULL;
  1085. compiler->new_entry.dirpath = NULL;
  1086. #ifdef IS_UNICODE
  1087. compiler->new_entry.ufilepath = NULL;
  1088. compiler->new_entry.udirpath = NULL;
  1089. #endif
  1090. return SUCCESS;
  1091. }
  1092. /* }}} */
  1093. static inline xc_hash_value_t xc_php_hash_md5(xc_entry_data_php_t *php TSRMLS_DC) /* {{{ */
  1094. {
  1095. return HASH_STR_S(php->md5.digest, sizeof(php->md5.digest));
  1096. }
  1097. /* }}} */
  1098. static int xc_entry_data_php_init_md5(xc_cache_t *cache, xc_compiler_t *compiler TSRMLS_DC) /* {{{ */
  1099. {
  1100. unsigned char buf[1024];
  1101. PHP_MD5_CTX context;
  1102. int n;
  1103. php_stream *stream;
  1104. ulong old_rsid = EG(regular_list).nNextFreeElement;
  1105. stream = php_stream_open_wrapper((char *) compiler->filename, "rb", USE_PATH | REPORT_ERRORS | ENFORCE_SAFE_MODE | STREAM_DISABLE_OPEN_BASEDIR, NULL);
  1106. if (!stream) {
  1107. return FAILURE;
  1108. }
  1109. PHP_MD5Init(&context);
  1110. while ((n = php_stream_read(stream, (char *) buf, sizeof(buf))) > 0) {
  1111. PHP_MD5Update(&context, buf, n);
  1112. }
  1113. PHP_MD5Final((unsigned char *) compiler->new_php.md5.digest, &context);
  1114. php_stream_close(stream);
  1115. if (EG(regular_list).nNextFreeElement == old_rsid + 1) {
  1116. EG(regular_list).nNextFreeElement = old_rsid;
  1117. }
  1118. if (n < 0) {
  1119. return FAILURE;
  1120. }
  1121. compiler->new_php.hvalue = (xc_php_hash_md5(&compiler->new_php TSRMLS_CC) & cache->hphp->mask);
  1122. #ifdef XCACHE_DEBUG
  1123. {
  1124. char md5str[33];
  1125. make_digest(md5str, (unsigned char *) compiler->new_php.md5.digest);
  1126. TRACE("md5 %s", md5str);
  1127. }
  1128. #endif
  1129. return SUCCESS;
  1130. }
  1131. /* }}} */
  1132. static void xc_entry_php_init(xc_entry_php_t *entry_php, const char *filepath TSRMLS_DC) /* {{{*/
  1133. {
  1134. entry_php->filepath = ZEND_24((char *), NOTHING) filepath;
  1135. entry_php->filepath_len = strlen(entry_php->filepath);
  1136. entry_php->dirpath = estrndup(entry_php->filepath, entry_php->filepath_len);
  1137. entry_php->dirpath_len = zend_dirname(entry_php->dirpath, entry_php->filepath_len);
  1138. #ifdef IS_UNICODE
  1139. zend_string_to_unicode(ZEND_U_CONVERTER(UG(runtime_encoding_conv)), &entry_php->ufilepath, &entry_php->ufilepath_len, entry_php->filepath, entry_php->filepath_len TSRMLS_CC);
  1140. zend_string_to_unicode(ZEND_U_CONVERTER(UG(runtime_encoding_conv)), &entry_php->udirpath, &entry_php->udirpath_len, entry_php->dirpath, entry_php->dirpath_len TSRMLS_CC);
  1141. #endif
  1142. }
  1143. /* }}} */
  1144. #ifndef ZEND_COMPILE_DELAYED_BINDING
  1145. static void xc_cache_early_binding_class_cb(zend_op *opline, int oplineno, void *data TSRMLS_DC) /* {{{ */
  1146. {
  1147. char *class_name;
  1148. zend_uint i;
  1149. int class_len;
  1150. xc_cest_t cest;
  1151. xc_entry_data_php_t *php = (xc_entry_data_php_t *) data;
  1152. class_name = Z_OP_CONSTANT(opline->op1).value.str.val;
  1153. class_len = Z_OP_CONSTANT(opline->op1).value.str.len;
  1154. if (zend_hash_find(CG(class_table), class_name, class_len, (void **) &cest) == FAILURE) {
  1155. assert(0);
  1156. }
  1157. TRACE("got ZEND_DECLARE_INHERITED_CLASS: %s", class_name + 1);
  1158. /* let's see which class */
  1159. for (i = 0; i < php->classinfo_cnt; i ++) {
  1160. if (memcmp(ZSTR_S(php->classinfos[i].key), class_name, class_len) == 0) {
  1161. php->classinfos[i].oplineno = oplineno;
  1162. php->have_early_binding = 1;
  1163. break;
  1164. }
  1165. }
  1166. if (i == php->classinfo_cnt) {
  1167. assert(0);
  1168. }
  1169. }
  1170. /* }}} */
  1171. #endif
  1172. /* {{{ Constant Usage */
  1173. #ifdef ZEND_ENGINE_2_4
  1174. # define xcache_literal_is_dir 1
  1175. # define xcache_literal_is_file 2
  1176. #else
  1177. # define xcache_op1_is_file 1
  1178. # define xcache_op1_is_dir 2
  1179. # define xcache_op2_is_file 4
  1180. # define xcache_op2_is_dir 8
  1181. #endif
  1182. typedef struct {
  1183. zend_bool filepath_used;
  1184. zend_bool dirpath_used;
  1185. zend_bool ufilepath_used;
  1186. zend_bool udirpath_used;
  1187. } xc_const_usage_t;
  1188. /* }}} */
  1189. static void xc_collect_op_array_info(xc_compiler_t *compiler, xc_const_usage_t *usage, xc_op_array_info_t *op_array_info, zend_op_array *op_array TSRMLS_DC) /* {{{ */
  1190. {
  1191. #ifdef ZEND_ENGINE_2_4
  1192. int literalindex;
  1193. #else
  1194. zend_uint oplinenum;
  1195. #endif
  1196. xc_vector_t details;
  1197. xc_vector_init(xc_op_array_info_detail_t, &details);
  1198. #define XCACHE_ANALYZE_LITERAL(type) \
  1199. if (zend_binary_strcmp(Z_STRVAL(literal->constant), Z_STRLEN(literal->constant), compiler->new_entry.type##path, compiler->new_entry.type##path_len) == 0) { \
  1200. usage->type##path_used = 1; \
  1201. literalinfo |= xcache_##literal##_is_##type; \
  1202. }
  1203. #define XCACHE_U_ANALYZE_LITERAL(type) \
  1204. if (zend_u_##binary_strcmp(Z_USTRVAL(literal->constant), Z_USTRLEN(literal->constant), compiler->new_entry.u##type##path, compiler->new_entry.u##type##path_len) == 0) { \
  1205. usage->u##type##path_used = 1; \
  1206. literalinfo |= xcache_##literal##_is_##type; \
  1207. }
  1208. #define XCACHE_ANALYZE_OP(type, op) \
  1209. if (zend_binary_strcmp(Z_STRVAL(Z_OP_CONSTANT(opline->op)), Z_STRLEN(Z_OP_CONSTANT(opline->op)), compiler->new_entry.type##path, compiler->new_entry.type##path_len) == 0) { \
  1210. usage->type##path_used = 1; \
  1211. oplineinfo |= xcache_##op##_is_##type; \
  1212. }
  1213. #define XCACHE_U_ANALYZE_OP(type, op) \
  1214. if (zend_u_##binary_strcmp(Z_USTRVAL(Z_OP_CONSTANT(opline->op)), Z_USTRLEN(Z_OP_CONSTANT(opline->op)), compiler->new_entry.u##type##path, compiler->new_entry.u##type##path_len) == 0) { \
  1215. usage->u##type##path_used = 1; \
  1216. oplineinfo |= xcache_##op##_is_##type; \
  1217. }
  1218. #ifdef ZEND_ENGINE_2_4
  1219. for (literalindex = 0; literalindex < op_array->last_literal; literalindex++) {
  1220. zend_literal *literal = &op_array->literals[literalindex];
  1221. zend_uint literalinfo = 0;
  1222. if (Z_TYPE(literal->constant) == IS_STRING) {
  1223. XCACHE_ANALYZE_LITERAL(file)
  1224. else XCACHE_ANALYZE_LITERAL(dir)
  1225. }
  1226. #ifdef IS_UNICODE
  1227. else if (Z_TYPE(literal->constant) == IS_UNICODE) {
  1228. XCACHE_U_ANALYZE_LITERAL(file)
  1229. else XCACHE_U_ANALYZE_LITERAL(dir)
  1230. }
  1231. #endif
  1232. if (literalinfo) {
  1233. xc_op_array_info_detail_t detail;
  1234. detail.index = literalindex;
  1235. detail.info = literalinfo;
  1236. xc_vector_add(xc_op_array_info_detail_t, &details, detail);
  1237. }
  1238. }
  1239. op_array_info->literalinfo_cnt = details.cnt;
  1240. op_array_info->literalinfos = xc_vector_detach(xc_op_array_info_detail_t, &details);
  1241. #else /* ZEND_ENGINE_2_4 */
  1242. for (oplinenum = 0; oplinenum < op_array->last; oplinenum++) {
  1243. zend_op *opline = &op_array->opcodes[oplinenum];
  1244. zend_uint oplineinfo = 0;
  1245. if (Z_OP_TYPE(opline->op1) == IS_CONST) {
  1246. if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_STRING) {
  1247. XCACHE_ANALYZE_OP(file, op1)
  1248. else XCACHE_ANALYZE_OP(dir, op1)
  1249. }
  1250. #ifdef IS_UNICODE
  1251. else if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_UNICODE) {
  1252. XCACHE_U_ANALYZE_OP(file, op1)
  1253. else XCACHE_U_ANALYZE_OP(dir, op1)
  1254. }
  1255. #endif
  1256. }
  1257. if (Z_OP_TYPE(opline->op2) == IS_CONST) {
  1258. if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_STRING) {
  1259. XCACHE_ANALYZE_OP(file, op2)
  1260. else XCACHE_ANALYZE_OP(dir, op2)
  1261. }
  1262. #ifdef IS_UNICODE
  1263. else if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_UNICODE) {
  1264. XCACHE_U_ANALYZE_OP(file, op2)
  1265. else XCACHE_U_ANALYZE_OP(dir, op2)
  1266. }
  1267. #endif
  1268. }
  1269. if (oplineinfo) {
  1270. xc_op_array_info_detail_t detail;
  1271. detail.index = oplinenum;
  1272. detail.info = oplineinfo;
  1273. xc_vector_add(xc_op_array_info_detail_t, &details, detail);
  1274. }
  1275. }
  1276. op_array_info->oplineinfo_cnt = details.cnt;
  1277. op_array_info->oplineinfos = xc_vector_detach(xc_op_array_info_detail_t, &details);
  1278. #endif /* ZEND_ENGINE_2_4 */
  1279. xc_vector_free(xc_op_array_info_detail_t, &details);
  1280. }
  1281. /* }}} */
  1282. void xc_fix_op_array_info(const xc_entry_php_t *entry_php, const xc_entry_data_php_t *php, zend_op_array *op_array, int shallow_copy, const xc_op_array_info_t *op_array_info TSRMLS_DC) /* {{{ */
  1283. {
  1284. #ifdef ZEND_ENGINE_2_4
  1285. zend_uint linteralindex;
  1286. for (linteralindex = 0; linteralindex < op_array_info->literalinfo_cnt; ++linteralindex) {
  1287. int index = op_array_info->literalinfos[linteralindex].index;
  1288. int literalinfo = op_array_info->literalinfos[linteralindex].info;
  1289. zend_literal *literal = &op_array->literals[index];
  1290. if ((literalinfo & xcache_literal_is_file)) {
  1291. if (!shallow_copy) {
  1292. efree(Z_STRVAL(literal->constant));
  1293. }
  1294. if (Z_TYPE(literal->constant) == IS_STRING) {
  1295. assert(entry_php->filepath);
  1296. ZVAL_STRINGL(&literal->constant, entry_php->filepath, entry_php->filepath_len, !shallow_copy);
  1297. TRACE("restored literal constant: %s", entry_php->filepath);
  1298. }
  1299. #ifdef IS_UNICODE
  1300. else if (Z_TYPE(literal->constant) == IS_UNICODE) {
  1301. assert(entry_php->ufilepath);
  1302. ZVAL_UNICODEL(&literal->constant, entry_php->ufilepath, entry_php->ufilepath_len, !shallow_copy);
  1303. }
  1304. #endif
  1305. else {
  1306. assert(0);
  1307. }
  1308. }
  1309. else if ((literalinfo & xcache_literal_is_dir)) {
  1310. if (!shallow_copy) {
  1311. efree(Z_STRVAL(literal->constant));
  1312. }
  1313. if (Z_TYPE(literal->constant) == IS_STRING) {
  1314. assert(entry_php->dirpath);
  1315. TRACE("restored literal constant: %s", entry_php->dirpath);
  1316. ZVAL_STRINGL(&literal->constant, entry_php->dirpath, entry_php->dirpath_len, !shallow_copy);
  1317. }
  1318. #ifdef IS_UNICODE
  1319. else if (Z_TYPE(literal->constant) == IS_UNICODE) {
  1320. assert(!entry_php->udirpath);
  1321. ZVAL_UNICODEL(&literal->constant, entry_php->udirpath, entry_php->udirpath_len, !shallow_copy);
  1322. }
  1323. #endif
  1324. else {
  1325. assert(0);
  1326. }
  1327. }
  1328. }
  1329. #else /* ZEND_ENGINE_2_4 */
  1330. zend_uint oplinenum;
  1331. for (oplinenum = 0; oplinenum < op_array_info->oplineinfo_cnt; ++oplinenum) {
  1332. int oplineno = op_array_info->oplineinfos[oplinenum].index;
  1333. int oplineinfo = op_array_info->oplineinfos[oplinenum].info;
  1334. zend_op *opline = &op_array->opcodes[oplineno];
  1335. if ((oplineinfo & xcache_op1_is_file)) {
  1336. assert(Z_OP_TYPE(opline->op1) == IS_CONST);
  1337. if (!shallow_copy) {
  1338. efree(Z_STRVAL(Z_OP_CONSTANT(opline->op1)));
  1339. }
  1340. if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_STRING) {
  1341. assert(entry_php->filepath);
  1342. ZVAL_STRINGL(&Z_OP_CONSTANT(opline->op1), entry_php->filepath, entry_php->filepath_len, !shallow_copy);
  1343. TRACE("restored op1 constant: %s", entry_php->filepath);
  1344. }
  1345. #ifdef IS_UNICODE
  1346. else if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_UNICODE) {
  1347. assert(entry_php->ufilepath);
  1348. ZVAL_UNICODEL(&Z_OP_CONSTANT(opline->op1), entry_php->ufilepath, entry_php->ufilepath_len, !shallow_copy);
  1349. }
  1350. #endif
  1351. else {
  1352. assert(0);
  1353. }
  1354. }
  1355. else if ((oplineinfo & xcache_op1_is_dir)) {
  1356. assert(Z_OP_TYPE(opline->op1) == IS_CONST);
  1357. if (!shallow_copy) {
  1358. efree(Z_STRVAL(Z_OP_CONSTANT(opline->op1)));
  1359. }
  1360. if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_STRING) {
  1361. assert(entry_php->dirpath);
  1362. TRACE("restored op1 constant: %s", entry_php->dirpath);
  1363. ZVAL_STRINGL(&Z_OP_CONSTANT(opline->op1), entry_php->dirpath, entry_php->dirpath_len, !shallow_copy);
  1364. }
  1365. #ifdef IS_UNICODE
  1366. else if (Z_TYPE(Z_OP_CONSTANT(opline->op1)) == IS_UNICODE) {
  1367. assert(!entry_php->udirpath);
  1368. ZVAL_UNICODEL(&Z_OP_CONSTANT(opline->op1), entry_php->udirpath, entry_php->udirpath_len, !shallow_copy);
  1369. }
  1370. #endif
  1371. else {
  1372. assert(0);
  1373. }
  1374. }
  1375. if ((oplineinfo & xcache_op2_is_file)) {
  1376. assert(Z_OP_TYPE(opline->op2) == IS_CONST);
  1377. if (!shallow_copy) {
  1378. efree(Z_STRVAL(Z_OP_CONSTANT(opline->op2)));
  1379. }
  1380. if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_STRING) {
  1381. assert(entry_php->filepath);
  1382. TRACE("restored op2 constant: %s", entry_php->filepath);
  1383. ZVAL_STRINGL(&Z_OP_CONSTANT(opline->op2), entry_php->filepath, entry_php->filepath_len, !shallow_copy);
  1384. }
  1385. #ifdef IS_UNICODE
  1386. else if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_UNICODE) {
  1387. assert(entry_php->ufilepath);
  1388. ZVAL_UNICODEL(&Z_OP_CONSTANT(opline->op2), entry_php->ufilepath, entry_php->ufilepath_len, !shallow_copy);
  1389. }
  1390. #endif
  1391. else {
  1392. assert(0);
  1393. }
  1394. }
  1395. else if ((oplineinfo & xcache_op2_is_dir)) {
  1396. assert(Z_OP_TYPE(opline->op2) == IS_CONST);
  1397. if (!shallow_copy) {
  1398. efree(Z_STRVAL(Z_OP_CONSTANT(opline->op2)));
  1399. }
  1400. if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_STRING) {
  1401. assert(entry_php->dirpath);
  1402. TRACE("restored op2 constant: %s", entry_php->dirpath);
  1403. ZVAL_STRINGL(&Z_OP_CONSTANT(opline->op2), entry_php->dirpath, entry_php->dirpath_len, !shallow_copy);
  1404. }
  1405. #ifdef IS_UNICODE
  1406. else if (Z_TYPE(Z_OP_CONSTANT(opline->op2)) == IS_UNICODE) {
  1407. assert(entry_php->udirpath);
  1408. ZVAL_UNICODEL(&Z_OP_CONSTANT(opline->op2), entry_php->udirpath, entry_php->udirpath_len, !shallow_copy);
  1409. }
  1410. #endif
  1411. else {
  1412. assert(0);
  1413. }
  1414. }
  1415. }
  1416. #endif /* ZEND_ENGINE_2_4 */
  1417. }
  1418. /* }}} */
  1419. static void xc_free_op_array_info(xc_op_array_info_t *op_array_info TSRMLS_DC) /* {{{ */
  1420. {
  1421. #ifdef ZEND_ENGINE_2_4
  1422. if (op_array_info->literalinfos) {
  1423. efree(op_array_info->literalinfos);
  1424. }
  1425. #else
  1426. if (op_array_info->oplineinfos) {
  1427. efree(op_array_info->oplineinfos);
  1428. }
  1429. #endif
  1430. }
  1431. /* }}} */
  1432. static void xc_free_php(xc_entry_data_php_t *php TSRMLS_DC) /* {{{ */
  1433. {
  1434. zend_uint i;
  1435. if (php->classinfos) {
  1436. for (i = 0; i < php->classinfo_cnt; i ++) {
  1437. xc_classinfo_t *classinfo = &php->classinfos[i];
  1438. zend_uint j;
  1439. for (j = 0; j < classinfo->methodinfo_cnt; j ++) {
  1440. xc_free_op_array_info(&classinfo->methodinfos[j] TSRMLS_CC);
  1441. }
  1442. if (classinfo->methodinfos) {
  1443. efree(classinfo->methodinfos);
  1444. }
  1445. }
  1446. }
  1447. if (php->funcinfos) {
  1448. for (i = 0; i < php->funcinfo_cnt; i ++) {
  1449. xc_free_op_array_info(&php->funcinfos[i].op_array_info TSRMLS_CC);
  1450. }
  1451. }
  1452. xc_free_op_array_info(&php->op_array_info TSRMLS_CC);
  1453. #define X_FREE(var) do {\
  1454. if (php->var) { \
  1455. efree(php->var); \
  1456. } \
  1457. } while (0)
  1458. #ifdef ZEND_ENGINE_2_1
  1459. X_FREE(autoglobals);
  1460. #endif
  1461. X_FREE(classinfos);
  1462. X_FREE(funcinfos);
  1463. #ifdef HAVE_XCACHE_CONSTANT
  1464. X_FREE(constinfos);
  1465. #endif
  1466. #undef X_FREE
  1467. }
  1468. /* }}} */
  1469. static void xc_compile_php(xc_compiler_t *compiler, zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
  1470. {
  1471. zend_uint old_constinfo_cnt, old_funcinfo_cnt, old_classinfo_cnt;
  1472. zend_bool catched = 0;
  1473. /* {{{ compile */
  1474. TRACE("compiling %s", h->opened_path ? h->opened_path : h->filename);
  1475. old_classinfo_cnt = zend_hash_num_elements(CG(class_table));
  1476. old_funcinfo_cnt = zend_hash_num_elements(CG(function_table));
  1477. old_constinfo_cnt = zend_hash_num_elements(EG(zend_constants));
  1478. zend_try {
  1479. compiler->new_php.op_array = old_compile_file(h, type TSRMLS_CC);
  1480. } zend_catch {
  1481. catched = 1;
  1482. } zend_end_try();
  1483. if (catched) {
  1484. goto err_bailout;
  1485. }
  1486. if (compiler->new_php.op_array == NULL) {
  1487. goto err_op_array;
  1488. }
  1489. if (!XG(initial_compile_file_called)) {
  1490. return;
  1491. }
  1492. /* }}} */
  1493. /* {{{ prepare */
  1494. zend_restore_compiled_filename(h->opened_path ? h->opened_path : (char *) h->filename TSRMLS_CC);
  1495. #ifdef HAVE_XCACHE_CONSTANT
  1496. compiler->new_php.constinfo_cnt = zend_hash_num_elements(EG(zend_constants)) - old_constinfo_cnt;
  1497. #endif
  1498. compiler->new_php.funcinfo_cnt = zend_hash_num_elements(CG(function_table)) - old_funcinfo_cnt;
  1499. compiler->new_php.classinfo_cnt = zend_hash_num_elements(CG(class_table)) - old_classinfo_cnt;
  1500. #ifdef ZEND_ENGINE_2_1
  1501. /* {{{ count new_php.autoglobal_cnt */ {
  1502. Bucket *b;
  1503. compiler->new_php.autoglobal_cnt = 0;
  1504. for (b = CG(auto_globals)->pListHead; b != NULL; b = b->pListNext) {
  1505. zend_auto_global *auto_global = (zend_auto_global *) b->pData;
  1506. /* check if actived */
  1507. if (auto_global->auto_global_callback && !auto_global->armed) {
  1508. compiler->new_php.autoglobal_cnt ++;
  1509. }
  1510. }
  1511. }
  1512. /* }}} */
  1513. #endif
  1514. #define X_ALLOC_N(var, cnt) do { \
  1515. if (compiler->new_php.cnt) { \
  1516. ECALLOC_N(compiler->new_php.var, compiler->new_php.cnt); \
  1517. if (!compiler->new_php.var) { \
  1518. goto err_alloc; \
  1519. } \
  1520. } \
  1521. else { \
  1522. compiler->new_php.var = NULL; \
  1523. } \
  1524. } while (0)
  1525. #ifdef HAVE_XCACHE_CONSTANT
  1526. X_ALLOC_N(constinfos, constinfo_cnt);
  1527. #endif
  1528. X_ALLOC_N(funcinfos, funcinfo_cnt);
  1529. X_ALLOC_N(classinfos, classinfo_cnt);
  1530. #ifdef ZEND_ENGINE_2_1
  1531. X_ALLOC_N(autoglobals, autoglobal_cnt);
  1532. #endif
  1533. #undef X_ALLOC
  1534. /* }}} */
  1535. /* {{{ shallow copy, pointers only */ {
  1536. Bucket *b;
  1537. zend_uint i;
  1538. zend_uint j;
  1539. #define COPY_H(vartype, var, cnt, name, datatype) do { \
  1540. for (i = 0, j = 0; b; i ++, b = b->pListNext) { \
  1541. vartype *data = &compiler->new_php.var[j]; \
  1542. \
  1543. if (i < old_##cnt) { \
  1544. continue; \
  1545. } \
  1546. j ++; \
  1547. \
  1548. assert(i < old_##cnt + compiler->new_php.cnt); \
  1549. assert(b->pData); \
  1550. memcpy(&data->name, b->pData, sizeof(datatype)); \
  1551. UNISW(NOTHING, data->type = b->key.type;) \
  1552. if (UNISW(1, b->key.type == IS_STRING)) { \
  1553. ZSTR_S(data->key) = BUCKET_KEY_S(b); \
  1554. } \
  1555. else { \
  1556. ZSTR_U(data->key) = BUCKET_KEY_U(b); \
  1557. } \
  1558. data->key_size = b->nKeyLength; \
  1559. data->h = b->h; \
  1560. } \
  1561. } while(0)
  1562. #ifdef HAVE_XCACHE_CONSTANT
  1563. b = EG(zend_constants)->pListHead; COPY_H(xc_constinfo_t, constinfos, constinfo_cnt, constant, zend_constant);
  1564. #endif
  1565. b = CG(function_table)->pListHead; COPY_H(xc_funcinfo_t, funcinfos, funcinfo_cnt, func, zend_function);
  1566. b = CG(class_table)->pListHead; COPY_H(xc_classinfo_t, classinfos, classinfo_cnt, cest, xc_cest_t);
  1567. #undef COPY_H
  1568. /* for ZE1, cest need to be fixed inside store */
  1569. #ifdef ZEND_ENGINE_2_1
  1570. /* scan for acatived auto globals */
  1571. i = 0;
  1572. for (b = CG(auto_globals)->pListHead; b != NULL; b = b->pListNext) {
  1573. zend_auto_global *auto_global = (zend_auto_global *) b->pData;
  1574. /* check if actived */
  1575. if (auto_global->auto_global_callback && !auto_global->armed) {
  1576. xc_autoglobal_t *data = &compiler->new_php.autoglobals[i];
  1577. assert(i < compiler->new_php.autoglobal_cnt);
  1578. i ++;
  1579. UNISW(NOTHING, data->type = b->key.type;)
  1580. if (UNISW(1, b->key.type == IS_STRING)) {
  1581. ZSTR_S(data->key) = BUCKET_KEY_S(b);
  1582. }
  1583. else {
  1584. ZSTR_U(data->key) = BUCKET_KEY_U(b);
  1585. }
  1586. data->key_len = b->nKeyLength - 1;
  1587. data->h = b->h;
  1588. }
  1589. }
  1590. #endif
  1591. }
  1592. /* }}} */
  1593. /* {{{ collect info for file/dir path */ {
  1594. Bucket *b;
  1595. xc_const_usage_t const_usage;
  1596. unsigned int i;
  1597. xc_entry_php_init(&compiler->new_entry, zend_get_compiled_filename(TSRMLS_C) TSRMLS_CC);
  1598. memset(&const_usage, 0, sizeof(const_usage));
  1599. for (i = 0; i < compiler->new_php.classinfo_cnt; i ++) {
  1600. xc_classinfo_t *classinfo = &compiler->new_php.classinfos[i];
  1601. zend_class_entry *ce = CestToCePtr(classinfo->cest);
  1602. classinfo->methodinfo_cnt = ce->function_table.nTableSize;
  1603. if (classinfo->methodinfo_cnt) {
  1604. int j;
  1605. ECALLOC_N(classinfo->methodinfos, classinfo->methodinfo_cnt);
  1606. if (!classinfo->methodinfos) {
  1607. goto err_alloc;
  1608. }
  1609. for (j = 0, b = ce->function_table.pListHead; b; j ++, b = b->pListNext) {
  1610. xc_collect_op_array_info(compiler, &const_usage, &classinfo->methodinfos[j], (zend_op_array *) b->pData TSRMLS_CC);
  1611. }
  1612. }
  1613. else {
  1614. classinfo->methodinfos = NULL;
  1615. }
  1616. }
  1617. for (i = 0; i < compiler->new_php.funcinfo_cnt; i ++) {
  1618. xc_collect_op_array_info(compiler, &const_usage, &compiler->new_php.funcinfos[i].op_array_info, (zend_op_array *) &compiler->new_php.funcinfos[i].func TSRMLS_CC);
  1619. }
  1620. xc_collect_op_array_info(compiler, &const_usage, &compiler->new_php.op_array_info, compiler->new_php.op_array TSRMLS_CC);
  1621. /* file/dir path free unused */
  1622. #define X_FREE_UNUSED(var) \
  1623. if (!const_usage.var##path_used) { \
  1624. efree(compiler->new_entry.var##path); \
  1625. compiler->new_entry.var##path = NULL; \
  1626. compiler->new_entry.var##path_len = 0; \
  1627. }
  1628. /* filepath is required to restore op_array->filename, so no free filepath here */
  1629. X_FREE_UNUSED(dir)
  1630. #ifdef IS_UNICODE
  1631. X_FREE_UNUSED(ufile)
  1632. X_FREE_UNUSED(udir)
  1633. #endif
  1634. #undef X_FREE_UNUSED
  1635. }
  1636. /* }}} */
  1637. #ifdef XCACHE_ERROR_CACHING
  1638. compiler->new_php.compilererrors = xc_sandbox_compilererrors(TSRMLS_C);
  1639. compiler->new_php.compilererror_cnt = xc_sandbox_compilererror_cnt(TSRMLS_C);
  1640. #endif
  1641. #ifndef ZEND_COMPILE_DELAYED_BINDING
  1642. /* {{{ find inherited classes that should be early-binding */
  1643. compiler->new_php.have_early_binding = 0;
  1644. {
  1645. zend_uint i;
  1646. for (i = 0; i < compiler->new_php.classinfo_cnt; i ++) {
  1647. compiler->new_php.classinfos[i].oplineno = -1;
  1648. }
  1649. }
  1650. xc_undo_pass_two(compiler->new_php.op_array TSRMLS_CC);
  1651. xc_foreach_early_binding_class(compiler->new_php.op_array, xc_cache_early_binding_class_cb, (void *) &compiler->new_php TSRMLS_CC);
  1652. xc_redo_pass_two(compiler->new_php.op_array TSRMLS_CC);
  1653. /* }}} */
  1654. #endif
  1655. return;
  1656. err_alloc:
  1657. xc_free_php(&compiler->new_php TSRMLS_CC);
  1658. err_bailout:
  1659. err_op_array:
  1660. if (catched) {
  1661. zend_bailout();
  1662. }
  1663. }
  1664. /* }}} */
  1665. static zend_op_array *xc_compile_restore(xc_entry_php_t *stored_entry, xc_entry_data_php_t *stored_php TSRMLS_DC) /* {{{ */
  1666. {
  1667. zend_op_array *op_array;
  1668. xc_entry_php_t restored_entry;
  1669. xc_entry_data_php_t restored_php;
  1670. zend_bool catched;
  1671. zend_uint i;
  1672. /* still needed because in zend_language_scanner.l, require()/include() check file_handle.handle.stream.handle */
  1673. i = 1;
  1674. zend_hash_add(&EG(included_files), stored_entry->entry.name.str.val, stored_entry->entry.name.str.len + 1, (void *)&i, sizeof(int), NULL);
  1675. CG(in_compilation) = 1;
  1676. CG(compiled_filename) = stored_entry->entry.name.str.val;
  1677. CG(zend_lineno) = 0;
  1678. TRACE("restoring %d:%s", stored_entry->file_inode, stored_entry->entry.name.str.val);
  1679. xc_processor_restore_xc_entry_php_t(&restored_entry, stored_entry TSRMLS_CC);
  1680. xc_processor_restore_xc_entry_data_php_t(stored_entry, &restored_php, stored_php, xc_readonly_protection TSRMLS_CC);
  1681. restored_entry.php = &restored_php;
  1682. #ifdef SHOW_DPRINT
  1683. xc_dprint(&restored_entry, 0 TSRMLS_CC);
  1684. #endif
  1685. catched = 0;
  1686. zend_try {
  1687. op_array = xc_entry_install(&restored_entry TSRMLS_CC);
  1688. } zend_catch {
  1689. catched = 1;
  1690. } zend_end_try();
  1691. #ifdef HAVE_XCACHE_CONSTANT
  1692. if (restored_php.constinfos) {
  1693. efree(restored_php.constinfos);
  1694. }
  1695. #endif
  1696. if (restored_php.funcinfos) {
  1697. efree(restored_php.funcinfos);
  1698. }
  1699. if (restored_php.classinfos) {
  1700. efree(restored_php.classinfos);
  1701. }
  1702. if (catched) {
  1703. zend_bailout();
  1704. }
  1705. CG(in_compilation) = 0;
  1706. CG(compiled_filename) = NULL;
  1707. TRACE("restored %d:%s", stored_entry->file_inode, stored_entry->entry.name.str.val);
  1708. return op_array;
  1709. }
  1710. /* }}} */
  1711. static zend_op_array *xc_check_initial_compile_file(zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
  1712. {
  1713. XG(initial_compile_file_called) = 1;
  1714. return origin_compile_file(h, type TSRMLS_CC);
  1715. }
  1716. /* }}} */
  1717. typedef struct xc_sandboxed_compiler_t { /* {{{ */
  1718. xc_compiler_t *compiler;
  1719. /* input */
  1720. zend_file_handle *h;
  1721. int type;
  1722. /* sandbox output */
  1723. xc_entry_php_t *stored_entry;
  1724. xc_entry_data_php_t *stored_php;
  1725. } xc_sandboxed_compiler_t; /* {{{ */
  1726. static zend_op_array *xc_compile_file_sandboxed(void *data TSRMLS_DC) /* {{{ */
  1727. {
  1728. xc_sandboxed_compiler_t *sandboxed_compiler = (xc_sandboxed_compiler_t *) data;
  1729. xc_compiler_t *compiler = sandboxed_compiler->compiler;
  1730. zend_bool catched = 0;
  1731. xc_cache_t *cache = xc_php_caches[compiler->entry_hash.cacheid];
  1732. xc_entry_php_t *stored_entry;
  1733. xc_entry_data_php_t *stored_php;
  1734. /* {{{ compile */
  1735. /* make compile inside sandbox */
  1736. #ifdef HAVE_XCACHE_CONSTANT
  1737. compiler->new_php.constinfos = NULL;
  1738. #endif
  1739. compiler->new_php.funcinfos = NULL;
  1740. compiler->new_php.classinfos = NULL;
  1741. #ifdef ZEND_ENGINE_2_1
  1742. compiler->new_php.autoglobals = NULL;
  1743. #endif
  1744. memset(&compiler->new_php.op_array_info, 0, sizeof(compiler->new_php.op_array_info));
  1745. XG(initial_compile_file_called) = 0;
  1746. zend_try {
  1747. compiler->new_php.op_array = NULL;
  1748. xc_compile_php(compiler, sandboxed_compiler->h, sandboxed_compiler->type TSRMLS_CC);
  1749. } zend_catch {
  1750. catched = 1;
  1751. } zend_end_try();
  1752. if (catched
  1753. || !compiler->new_php.op_array /* possible ? */
  1754. || !XG(initial_compile_file_called)) {
  1755. goto err_aftersandbox;
  1756. }
  1757. /* }}} */
  1758. #ifdef SHOW_DPRINT
  1759. compiler->new_entry.php = &compiler->new_php;
  1760. xc_dprint(&compiler->new_entry, 0 TSRMLS_CC);
  1761. #endif
  1762. stored_entry = NULL;
  1763. stored_php = NULL;
  1764. ENTER_LOCK_EX(cache) { /* {{{ php_store/entry_store */
  1765. /* php_store */
  1766. stored_php = xc_php_store_unlocked(cache, &compiler->new_php TSRMLS_CC);
  1767. if (!stored_php) {
  1768. /* error */
  1769. break;
  1770. }
  1771. /* entry_store */
  1772. compiler->new_entry.php = stored_php;
  1773. stored_entry = xc_entry_php_store_unlocked(cache, compiler->entry_hash.entryslotid, &compiler->new_entry TSRMLS_CC);
  1774. if (stored_entry) {
  1775. xc_php_addref_unlocked(stored_php);
  1776. TRACE(" cached %d:%s, holding", compiler->new_entry.file_inode, stored_entry->entry.name.str.val);
  1777. xc_entry_hold_php_unlocked(cache, stored_entry TSRMLS_CC);
  1778. }
  1779. } LEAVE_LOCK_EX(cache);
  1780. /* }}} */
  1781. TRACE("%s", stored_entry ? "stored" : "store failed");
  1782. if (catched || !stored_php) {
  1783. goto err_aftersandbox;
  1784. }
  1785. cache->compiling = 0;
  1786. xc_free_php(&compiler->new_php TSRMLS_CC);
  1787. if (stored_entry) {
  1788. sandboxed_compiler->stored_entry = stored_entry;
  1789. sandboxed_compiler->stored_php = stored_php;
  1790. /* discard newly compiled result, restore from stored one */
  1791. if (compiler->new_php.op_array) {
  1792. #ifdef ZEND_ENGINE_2
  1793. destroy_op_array(compiler->new_php.op_array TSRMLS_CC);
  1794. #else
  1795. destroy_op_array(compiler->new_php.op_array);
  1796. #endif
  1797. efree(compiler->new_php.op_array);
  1798. compiler->new_php.op_array = NULL;
  1799. }
  1800. return NULL;
  1801. }
  1802. else {
  1803. return compiler->new_php.op_array;
  1804. }
  1805. err_aftersandbox:
  1806. xc_free_php(&compiler->new_php TSRMLS_CC);
  1807. cache->compiling = 0;
  1808. if (catched) {
  1809. cache->errors ++;
  1810. zend_bailout();
  1811. }
  1812. return compiler->new_php.op_array;
  1813. } /* }}} */
  1814. static zend_op_array *xc_compile_file_cached(xc_compiler_t *compiler, zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
  1815. {
  1816. /*
  1817. if (clog) {
  1818. return old;
  1819. }
  1820. if (cached_entry = getby entry_hash) {
  1821. php = cached_entry.php;
  1822. php = restore(php);
  1823. return php;
  1824. }
  1825. else {
  1826. if (!(php = getby md5)) {
  1827. if (clog) {
  1828. return old;
  1829. }
  1830. inside_sandbox {
  1831. php = compile;
  1832. entry = create entries[entry];
  1833. }
  1834. }
  1835. entry.php = php;
  1836. return php;
  1837. }
  1838. */
  1839. xc_entry_php_t *stored_entry;
  1840. xc_entry_data_php_t *stored_php;
  1841. zend_bool gaveup = 0;
  1842. zend_bool catched = 0;
  1843. zend_op_array *op_array;
  1844. xc_cache_t *cache = xc_php_caches[compiler->entry_hash.cacheid];
  1845. xc_sandboxed_compiler_t sandboxed_compiler;
  1846. /* stale clogs precheck */
  1847. if (XG(request_time) - cache->compiling < 30) {
  1848. cache->clogs ++;
  1849. return old_compile_file(h, type TSRMLS_CC);
  1850. }
  1851. /* {{{ entry_lookup/hit/md5_init/php_lookup */
  1852. stored_entry = NULL;
  1853. stored_php = NULL;
  1854. ENTER_LOCK_EX(cache) {
  1855. if (!compiler->opened_path && xc_entry_resolve_path_unlocked(compiler, compiler->filename, &stored_entry TSRMLS_CC) == SUCCESS) {
  1856. compiler->opened_path = compiler->new_entry.entry.name.str.val;
  1857. }
  1858. else {
  1859. if (!compiler->opened_path && xc_entry_php_resolve_opened_path(compiler, NULL TSRMLS_CC) != SUCCESS) {
  1860. gaveup = 1;
  1861. break;
  1862. }
  1863. /* finalize name */
  1864. compiler->new_entry.entry.name.str.val = (char *) compiler->opened_path;
  1865. compiler->new_entry.entry.name.str.len = strlen(compiler->new_entry.entry.name.str.val);
  1866. stored_entry = (xc_entry_php_t *) xc_entry_find_unlocked(XC_TYPE_PHP, cache, compiler->entry_hash.entryslotid, (xc_entry_t *) &compiler->new_entry TSRMLS_CC);
  1867. }
  1868. if (stored_entry) {
  1869. xc_cache_hit_unlocked(cache TSRMLS_CC);
  1870. TRACE(" hit %d:%s, holding", compiler->new_entry.file_inode, stored_entry->entry.name.str.val);
  1871. xc_entry_hold_php_unlocked(cache, stored_entry TSRMLS_CC);
  1872. stored_php = stored_entry->php;
  1873. break;
  1874. }
  1875. TRACE("miss entry %d:%s", compiler->new_entry.file_inode, compiler->new_entry.entry.name.str.val);
  1876. if (xc_entry_data_php_init_md5(cache, compiler TSRMLS_CC) != SUCCESS) {
  1877. gaveup = 1;
  1878. break;
  1879. }
  1880. stored_php = xc_php_find_unlocked(cache, &compiler->new_php TSRMLS_CC);
  1881. if (stored_php) {
  1882. compiler->new_entry.php = stored_php;
  1883. xc_entry_php_init(&compiler->new_entry, compiler->opened_path TSRMLS_CC);
  1884. stored_entry = xc_entry_php_store_unlocked(cache, compiler->entry_hash.entryslotid, &compiler->new_entry TSRMLS_CC);
  1885. if (stored_entry) {
  1886. xc_php_addref_unlocked(stored_php);
  1887. TRACE(" cached %d:%s, holding", compiler->new_entry.file_inode, stored_entry->entry.name.str.val);
  1888. xc_entry_hold_php_unlocked(cache, stored_entry TSRMLS_CC);
  1889. }
  1890. else {
  1891. gaveup = 1;
  1892. }
  1893. break;
  1894. }
  1895. if (XG(request_time) - cache->compiling < 30) {
  1896. TRACE("%s", "miss php, but compiling");
  1897. cache->clogs ++;
  1898. gaveup = 1;
  1899. break;
  1900. }
  1901. TRACE("%s", "miss php, going to compile");
  1902. cache->compiling = XG(request_time);
  1903. } LEAVE_LOCK_EX(cache);
  1904. if (catched) {
  1905. cache->compiling = 0;
  1906. zend_bailout();
  1907. }
  1908. /* found entry */
  1909. if (stored_entry && stored_php) {
  1910. zend_llist_add_element(&CG(open_files), h);
  1911. return xc_compile_restore(stored_entry, stored_php TSRMLS_CC);
  1912. }
  1913. /* gaveup */
  1914. if (gaveup) {
  1915. return old_compile_file(h, type TSRMLS_CC);
  1916. }
  1917. /* }}} */
  1918. sandboxed_compiler.compiler = compiler;
  1919. sandboxed_compiler.h = h;
  1920. sandboxed_compiler.type = type;
  1921. sandboxed_compiler.stored_php = NULL;
  1922. sandboxed_compiler.stored_entry = NULL;
  1923. op_array = xc_sandbox(xc_compile_file_sandboxed, (void *) &sandboxed_compiler, h->opened_path ? h->opened_path : h->filename TSRMLS_CC);
  1924. if (sandboxed_compiler.stored_entry) {
  1925. return xc_compile_restore(sandboxed_compiler.stored_entry, sandboxed_compiler.stored_php TSRMLS_CC);
  1926. }
  1927. else {
  1928. return op_array;
  1929. }
  1930. }
  1931. /* }}} */
  1932. static zend_op_array *xc_compile_file(zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
  1933. {
  1934. xc_compiler_t compiler;
  1935. zend_op_array *op_array;
  1936. assert(xc_initized);
  1937. TRACE("xc_compile_file: type=%d name=%s", h->type, h->filename ? h->filename : "NULL");
  1938. if (!XG(cacher)
  1939. || !h->filename
  1940. || !SG(request_info).path_translated
  1941. || strstr(h->filename, "://") != NULL
  1942. #ifdef ZEND_ENGINE_2_3
  1943. /* supported by php_resolve_path */
  1944. || (!XG(stat) && strstr(PG(include_path), "://") != NULL)
  1945. #else
  1946. || strstr(PG(include_path), "://") != NULL
  1947. #endif
  1948. || xc_shm || xc_shm->disabled
  1949. ) {
  1950. TRACE("%s", "cacher not enabled");
  1951. return old_compile_file(h, type TSRMLS_CC);
  1952. }
  1953. /* {{{ entry_init_key */
  1954. compiler.opened_path = h->opened_path;
  1955. compiler.filename = compiler.opened_path ? compiler.opened_path : h->filename;
  1956. compiler.filename_len = strlen(compiler.filename);
  1957. if (xc_entry_php_init_key(&compiler TSRMLS_CC) != SUCCESS) {
  1958. TRACE("failed to init key for %s", compiler.filename);
  1959. return old_compile_file(h, type TSRMLS_CC);
  1960. }
  1961. /* }}} */
  1962. op_array = xc_compile_file_cached(&compiler, h, type TSRMLS_CC);
  1963. xc_entry_free_key_php(&compiler.new_entry TSRMLS_CC);
  1964. return op_array;
  1965. }
  1966. /* }}} */
  1967. /* gdb helper functions, but N/A for coredump */
  1968. int xc_is_rw(const void *p) /* {{{ */
  1969. {
  1970. xc_shm_t *shm;
  1971. size_t i;
  1972. if (xc_php_caches) {
  1973. for (i = 0; i < xc_php_hcache.size; i ++) {
  1974. shm = xc_php_caches[i]->shm;
  1975. if (shm->handlers->is_readwrite(shm, p)) {
  1976. return 1;
  1977. }
  1978. }
  1979. }
  1980. if (xc_var_caches) {
  1981. for (i = 0; i < xc_var_hcache.size; i ++) {
  1982. shm = xc_var_caches[i]->shm;
  1983. if (shm->handlers->is_readwrite(shm, p)) {
  1984. return 1;
  1985. }
  1986. }
  1987. }
  1988. return 0;
  1989. }
  1990. /* }}} */
  1991. int xc_is_ro(const void *p) /* {{{ */
  1992. {
  1993. xc_shm_t *shm;
  1994. size_t i;
  1995. if (xc_php_caches) {
  1996. for (i = 0; i < xc_php_hcache.size; i ++) {
  1997. shm = xc_php_caches[i]->shm;
  1998. if (shm->handlers->is_readonly(shm, p)) {
  1999. return 1;
  2000. }
  2001. }
  2002. }
  2003. if (xc_var_caches) {
  2004. for (i = 0; i < xc_var_hcache.size; i ++) {
  2005. shm = xc_var_caches[i]->shm;
  2006. if (shm->handlers->is_readonly(shm, p)) {
  2007. return 1;
  2008. }
  2009. }
  2010. }
  2011. return 0;
  2012. }
  2013. /* }}} */
  2014. int xc_is_shm(const void *p) /* {{{ */
  2015. {
  2016. return xc_is_ro(p) || xc_is_rw(p);
  2017. }
  2018. /* }}} */
  2019. void xc_gc_add_op_array(xc_gc_op_array_t *gc_op_array TSRMLS_DC) /* {{{ */
  2020. {
  2021. zend_llist_add_element(&XG(gc_op_arrays), (void *) gc_op_array);
  2022. }
  2023. /* }}} */
  2024. static void xc_gc_op_array(void *pDest) /* {{{ */
  2025. {
  2026. xc_gc_op_array_t *op_array = (xc_gc_op_array_t *) pDest;
  2027. zend_uint i;
  2028. #ifdef ZEND_ENGINE_2
  2029. if (op_array->arg_info) {
  2030. for (i = 0; i < op_array->num_args; i++) {
  2031. efree((char *) ZSTR_V(op_array->arg_info[i].name));
  2032. if (ZSTR_V(op_array->arg_info[i].class_name)) {
  2033. efree((char *) ZSTR_V(op_array->arg_info[i].class_name));
  2034. }
  2035. }
  2036. efree(op_array->arg_info);
  2037. }
  2038. #endif
  2039. if (op_array->opcodes) {
  2040. efree(op_array->opcodes);
  2041. }
  2042. }
  2043. /* }}} */
  2044. /* module helper function */
  2045. static int xc_init_constant(int module_number TSRMLS_DC) /* {{{ */
  2046. {
  2047. typedef struct {
  2048. const char *prefix;
  2049. zend_uchar (*getsize)();
  2050. const char *(*get)(zend_uchar i);
  2051. } xc_meminfo_t;
  2052. xc_meminfo_t nameinfos[] = {
  2053. { "", xc_get_op_type_count, xc_get_op_type },
  2054. { "", xc_get_data_type_count, xc_get_data_type },
  2055. { "", xc_get_opcode_count, xc_get_opcode },
  2056. { "OPSPEC_", xc_get_op_spec_count, xc_get_op_spec },
  2057. { NULL, NULL, NULL }
  2058. };
  2059. xc_meminfo_t* p;
  2060. zend_uchar i, count;
  2061. char const_name[96];
  2062. int const_name_len;
  2063. int undefdone = 0;
  2064. for (p = nameinfos; p->getsize; p ++) {
  2065. count = p->getsize();
  2066. for (i = 0; i < count; i ++) {
  2067. const char *name = p->get(i);
  2068. if (!name) continue;
  2069. if (strcmp(name, "UNDEF") == 0) {
  2070. if (undefdone) continue;
  2071. undefdone = 1;
  2072. }
  2073. const_name_len = snprintf(const_name, sizeof(const_name), "XC_%s%s", p->prefix, name);
  2074. zend_register_long_constant(const_name, const_name_len+1, i, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  2075. }
  2076. }
  2077. zend_register_long_constant(ZEND_STRS("XC_SIZEOF_TEMP_VARIABLE"), sizeof(temp_variable), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  2078. zend_register_long_constant(ZEND_STRS("XC_TYPE_PHP"), XC_TYPE_PHP, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  2079. zend_register_long_constant(ZEND_STRS("XC_TYPE_VAR"), XC_TYPE_VAR, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  2080. zend_register_stringl_constant(ZEND_STRS("XCACHE_VERSION"), ZEND_STRL(XCACHE_VERSION), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  2081. zend_register_stringl_constant(ZEND_STRS("XCACHE_MODULES"), ZEND_STRL(XCACHE_MODULES), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  2082. return 0;
  2083. }
  2084. /* }}} */
  2085. static void xc_cache_destroy(xc_cache_t **caches, xc_hash_t *hcache) /* {{{ */
  2086. {
  2087. size_t i;
  2088. xc_cache_t *cache;
  2089. if (!caches) {
  2090. return;
  2091. }
  2092. for (i = 0; i < hcache->size; i ++) {
  2093. cache = caches[i];
  2094. if (cache) {
  2095. if (cache->lck) {
  2096. xc_lock_destroy(cache->lck);
  2097. }
  2098. /* do NOT free
  2099. if (cache->entries) {
  2100. cache->mem->handlers->free(cache->mem, cache->entries);
  2101. }
  2102. cache->mem->handlers->free(cache->mem, cache);
  2103. */
  2104. cache->shm->handlers->memdestroy(cache->mem);
  2105. }
  2106. }
  2107. free(caches);
  2108. }
  2109. /* }}} */
  2110. static xc_cache_t **xc_cache_init(xc_shm_t *shm, xc_hash_t *hcache, xc_hash_t *hentry, xc_hash_t *hphp, xc_shmsize_t shmsize) /* {{{ */
  2111. {
  2112. xc_cache_t **caches = NULL, *cache;
  2113. xc_mem_t *mem;
  2114. time_t now = time(NULL);
  2115. size_t i;
  2116. xc_memsize_t memsize;
  2117. memsize = shmsize / hcache->size;
  2118. /* Don't let it break out of mem after ALIGNed
  2119. * This is important for
  2120. * Simply loop until it fit our need
  2121. */
  2122. while (ALIGN(memsize) * hcache->size > shmsize && ALIGN(memsize) != memsize) {
  2123. if (memsize < ALIGN(1)) {
  2124. CHECK(NULL, "cache too small");
  2125. }
  2126. memsize --;
  2127. }
  2128. CHECK(caches = calloc(hcache->size, sizeof(xc_cache_t *)), "caches OOM");
  2129. for (i = 0; i < hcache->size; i ++) {
  2130. CHECK(mem = shm->handlers->meminit(shm, memsize), "Failed init memory allocator");
  2131. CHECK(cache = mem->handlers->calloc(mem, 1, sizeof(xc_cache_t)), "cache OOM");
  2132. CHECK(cache->entries = mem->handlers->calloc(mem, hentry->size, sizeof(xc_entry_t*)), "entries OOM");
  2133. if (hphp) {
  2134. CHECK(cache->phps= mem->handlers->calloc(mem, hphp->size, sizeof(xc_entry_data_php_t*)), "phps OOM");
  2135. }
  2136. CHECK(cache->lck = xc_lock_init(NULL), "can't create lock");
  2137. cache->hcache = hcache;
  2138. cache->hentry = hentry;
  2139. cache->hphp = hphp;
  2140. cache->shm = shm;
  2141. cache->mem = mem;
  2142. cache->cacheid = i;
  2143. cache->last_gc_deletes = now;
  2144. cache->last_gc_expires = now;
  2145. caches[i] = cache;
  2146. }
  2147. return caches;
  2148. err:
  2149. if (caches) {
  2150. xc_cache_destroy(caches, hcache);
  2151. }
  2152. return NULL;
  2153. }
  2154. /* }}} */
  2155. static void xc_destroy() /* {{{ */
  2156. {
  2157. if (old_compile_file) {
  2158. zend_compile_file = old_compile_file;
  2159. old_compile_file = NULL;
  2160. }
  2161. if (origin_compile_file) {
  2162. zend_compile_file = origin_compile_file;
  2163. origin_compile_file = NULL;
  2164. }
  2165. if (xc_php_caches) {
  2166. xc_cache_destroy(xc_php_caches, &xc_php_hcache);
  2167. xc_php_caches = NULL;
  2168. }
  2169. if (xc_var_caches) {
  2170. xc_cache_destroy(xc_var_caches, &xc_var_hcache);
  2171. xc_var_caches = NULL;
  2172. }
  2173. if (xc_shm) {
  2174. xc_shm_destroy(xc_shm);
  2175. xc_shm = NULL;
  2176. }
  2177. xc_initized = 0;
  2178. }
  2179. /* }}} */
  2180. static int xc_init(int module_number TSRMLS_DC) /* {{{ */
  2181. {
  2182. xc_shmsize_t shmsize = ALIGN(xc_php_size) + ALIGN(xc_var_size);
  2183. xc_php_caches = xc_var_caches = NULL;
  2184. xc_shm = NULL;
  2185. if (shmsize < (size_t) xc_php_size || shmsize < (size_t) xc_var_size) {
  2186. zend_error(E_ERROR, "XCache: neither xcache.size nor xcache.var_size can be negative");
  2187. goto err;
  2188. }
  2189. if (xc_php_size || xc_var_size) {
  2190. CHECK(xc_shm = xc_shm_init(xc_shm_scheme, shmsize, xc_readonly_protection, xc_mmap_path, NULL), "Cannot create shm");
  2191. if (!xc_shm->handlers->can_readonly(xc_shm)) {
  2192. xc_readonly_protection = 0;
  2193. }
  2194. if (xc_php_size) {
  2195. old_compile_file = zend_compile_file;
  2196. zend_compile_file = xc_compile_file;
  2197. CHECK(xc_php_caches = xc_cache_init(xc_shm, &xc_php_hcache, &xc_php_hentry, &xc_php_hentry, xc_php_size), "failed init opcode cache");
  2198. }
  2199. if (xc_var_size) {
  2200. CHECK(xc_var_caches = xc_cache_init(xc_shm, &xc_var_hcache, &xc_var_hentry, NULL, xc_var_size), "failed init variable cache");
  2201. }
  2202. }
  2203. return SUCCESS;
  2204. err:
  2205. if (xc_php_caches || xc_var_caches) {
  2206. xc_destroy();
  2207. /* shm destroied in xc_destroy() */
  2208. }
  2209. else if (xc_shm) {
  2210. xc_destroy();
  2211. xc_shm_destroy(xc_shm);
  2212. xc_shm = NULL;
  2213. }
  2214. return 0;
  2215. }
  2216. /* }}} */
  2217. static void xc_request_init(TSRMLS_D) /* {{{ */
  2218. {
  2219. size_t i;
  2220. if (!XG(internal_table_copied)) {
  2221. zend_function tmp_func;
  2222. xc_cest_t tmp_cest;
  2223. #ifdef HAVE_XCACHE_CONSTANT
  2224. zend_hash_destroy(&XG(internal_constant_table));
  2225. #endif
  2226. zend_hash_destroy(&XG(internal_function_table));
  2227. zend_hash_destroy(&XG(internal_class_table));
  2228. #ifdef HAVE_XCACHE_CONSTANT
  2229. zend_hash_init_ex(&XG(internal_constant_table), 20, NULL, (dtor_func_t) xc_zend_constant_dtor, 1, 0);
  2230. #endif
  2231. zend_hash_init_ex(&XG(internal_function_table), 100, NULL, NULL, 1, 0);
  2232. zend_hash_init_ex(&XG(internal_class_table), 10, NULL, NULL, 1, 0);
  2233. #ifdef HAVE_XCACHE_CONSTANT
  2234. xc_copy_internal_zend_constants(&XG(internal_constant_table), EG(zend_constants));
  2235. #endif
  2236. zend_hash_copy(&XG(internal_function_table), CG(function_table), NULL, &tmp_func, sizeof(tmp_func));
  2237. zend_hash_copy(&XG(internal_class_table), CG(class_table), NULL, &tmp_cest, sizeof(tmp_cest));
  2238. XG(internal_table_copied) = 1;
  2239. }
  2240. if (xc_php_caches && !XG(php_holds)) {
  2241. XG(php_holds) = calloc(xc_php_hcache.size, sizeof(xc_stack_t));
  2242. for (i = 0; i < xc_php_hcache.size; i ++) {
  2243. xc_stack_init(&XG(php_holds[i]));
  2244. }
  2245. }
  2246. if (xc_var_caches && !XG(var_holds)) {
  2247. XG(var_holds) = calloc(xc_var_hcache.size, sizeof(xc_stack_t));
  2248. for (i = 0; i < xc_var_hcache.size; i ++) {
  2249. xc_stack_init(&XG(var_holds[i]));
  2250. }
  2251. }
  2252. #ifdef ZEND_ENGINE_2
  2253. zend_llist_init(&XG(gc_op_arrays), sizeof(xc_gc_op_array_t), xc_gc_op_array, 0);
  2254. #endif
  2255. #if PHP_API_VERSION <= 20041225
  2256. XG(request_time) = time(NULL);
  2257. #else
  2258. XG(request_time) = sapi_get_request_time(TSRMLS_C);
  2259. #endif
  2260. #ifdef HAVE_XCACHE_COVERAGER
  2261. xc_coverager_request_init(TSRMLS_C);
  2262. #endif
  2263. }
  2264. /* }}} */
  2265. static void xc_request_shutdown(TSRMLS_D) /* {{{ */
  2266. {
  2267. if (xc_shm && !xc_shm->disabled) {
  2268. xc_entry_unholds(TSRMLS_C);
  2269. xc_gc_expires_php(TSRMLS_C);
  2270. xc_gc_expires_var(TSRMLS_C);
  2271. xc_gc_deletes(TSRMLS_C);
  2272. }
  2273. #ifdef ZEND_ENGINE_2
  2274. zend_llist_destroy(&XG(gc_op_arrays));
  2275. #endif
  2276. #ifdef HAVE_XCACHE_COVERAGER
  2277. xc_coverager_request_shutdown(TSRMLS_C);
  2278. #endif
  2279. }
  2280. /* }}} */
  2281. /* {{{ PHP_GINIT_FUNCTION(xcache) */
  2282. static
  2283. #ifdef PHP_GINIT_FUNCTION
  2284. PHP_GINIT_FUNCTION(xcache)
  2285. #else
  2286. void xc_init_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
  2287. #endif
  2288. {
  2289. memset(xcache_globals, 0, sizeof(zend_xcache_globals));
  2290. #ifdef HAVE_XCACHE_CONSTANT
  2291. zend_hash_init_ex(&xcache_globals->internal_constant_table, 1, NULL, (dtor_func_t) xc_zend_constant_dtor, 1, 0);
  2292. #endif
  2293. zend_hash_init_ex(&xcache_globals->internal_function_table, 1, NULL, NULL, 1, 0);
  2294. zend_hash_init_ex(&xcache_globals->internal_class_table, 1, NULL, NULL, 1, 0);
  2295. }
  2296. /* }}} */
  2297. /* {{{ PHP_GSHUTDOWN_FUNCTION(xcache) */
  2298. static
  2299. #ifdef PHP_GSHUTDOWN_FUNCTION
  2300. PHP_GSHUTDOWN_FUNCTION(xcache)
  2301. #else
  2302. void xc_shutdown_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
  2303. #endif
  2304. {
  2305. size_t i;
  2306. if (xcache_globals->php_holds != NULL) {
  2307. for (i = 0; i < xc_php_hcache.size; i ++) {
  2308. xc_stack_destroy(&xcache_globals->php_holds[i]);
  2309. }
  2310. free(xcache_globals->php_holds);
  2311. xcache_globals->php_holds = NULL;
  2312. }
  2313. if (xcache_globals->var_holds != NULL) {
  2314. for (i = 0; i < xc_var_hcache.size; i ++) {
  2315. xc_stack_destroy(&xcache_globals->var_holds[i]);
  2316. }
  2317. free(xcache_globals->var_holds);
  2318. xcache_globals->var_holds = NULL;
  2319. }
  2320. if (xcache_globals->internal_table_copied) {
  2321. #ifdef HAVE_XCACHE_CONSTANT
  2322. zend_hash_destroy(&xcache_globals->internal_constant_table);
  2323. #endif
  2324. zend_hash_destroy(&xcache_globals->internal_function_table);
  2325. zend_hash_destroy(&xcache_globals->internal_class_table);
  2326. }
  2327. }
  2328. /* }}} */
  2329. /* user functions */
  2330. static int xcache_admin_auth_check(TSRMLS_D) /* {{{ */
  2331. {
  2332. zval **server = NULL;
  2333. zval **user = NULL;
  2334. zval **pass = NULL;
  2335. char *admin_user = NULL;
  2336. char *admin_pass = NULL;
  2337. HashTable *ht;
  2338. /* auth disabled, nothing to do.. */
  2339. if (!XG(auth_enabled)) {
  2340. return 1;
  2341. }
  2342. if (cfg_get_string("xcache.admin.user", &admin_user) == FAILURE || !admin_user[0]) {
  2343. admin_user = NULL;
  2344. }
  2345. if (cfg_get_string("xcache.admin.pass", &admin_pass) == FAILURE || !admin_pass[0]) {
  2346. admin_pass = NULL;
  2347. }
  2348. if (admin_user == NULL || admin_pass == NULL) {
  2349. php_error_docref(XCACHE_WIKI_URL "/InstallAdministration" TSRMLS_CC, E_ERROR,
  2350. "xcache.admin.user and/or xcache.admin.pass settings is not configured."
  2351. " Make sure you've modified the correct php ini file for your php used in webserver.");
  2352. zend_bailout();
  2353. }
  2354. if (strlen(admin_pass) != 32) {
  2355. php_error_docref(NULL TSRMLS_CC, E_ERROR, "xcache.admin.pass is %lu chars unexpectedly, it is supposed to be the password after md5() which should be 32 chars", (unsigned long) strlen(admin_pass));
  2356. zend_bailout();
  2357. }
  2358. #ifdef ZEND_ENGINE_2_1
  2359. zend_is_auto_global("_SERVER", sizeof("_SERVER") - 1 TSRMLS_CC);
  2360. #endif
  2361. if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &server) != SUCCESS || Z_TYPE_PP(server) != IS_ARRAY) {
  2362. php_error_docref(NULL TSRMLS_CC, E_ERROR, "_SERVER is corrupted");
  2363. zend_bailout();
  2364. }
  2365. ht = HASH_OF((*server));
  2366. if (zend_hash_find(ht, "PHP_AUTH_USER", sizeof("PHP_AUTH_USER"), (void **) &user) == FAILURE) {
  2367. user = NULL;
  2368. }
  2369. else if (Z_TYPE_PP(user) != IS_STRING) {
  2370. user = NULL;
  2371. }
  2372. if (zend_hash_find(ht, "PHP_AUTH_PW", sizeof("PHP_AUTH_PW"), (void **) &pass) == FAILURE) {
  2373. pass = NULL;
  2374. }
  2375. else if (Z_TYPE_PP(pass) != IS_STRING) {
  2376. pass = NULL;
  2377. }
  2378. if (user != NULL && pass != NULL && strcmp(admin_user, Z_STRVAL_PP(user)) == 0) {
  2379. PHP_MD5_CTX context;
  2380. char md5str[33];
  2381. unsigned char digest[16];
  2382. PHP_MD5Init(&context);
  2383. PHP_MD5Update(&context, (unsigned char *) Z_STRVAL_PP(pass), Z_STRLEN_PP(pass));
  2384. PHP_MD5Final(digest, &context);
  2385. md5str[0] = '\0';
  2386. make_digest(md5str, digest);
  2387. if (strcmp(admin_pass, md5str) == 0) {
  2388. return 1;
  2389. }
  2390. }
  2391. #define STR "HTTP/1.0 401 Unauthorized"
  2392. sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
  2393. #undef STR
  2394. #define STR "WWW-authenticate: Basic Realm=\"XCache Administration\""
  2395. sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
  2396. #undef STR
  2397. #define STR "Content-type: text/html; charset=UTF-8"
  2398. sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
  2399. #undef STR
  2400. ZEND_PUTS("<html>\n");
  2401. ZEND_PUTS("<head><title>XCache Authentication Failed</title></head>\n");
  2402. ZEND_PUTS("<body>\n");
  2403. ZEND_PUTS("<h1>XCache Authentication Failed</h1>\n");
  2404. ZEND_PUTS("<p>You're not authorized to access this page due to wrong username and/or password you typed.<br />The following check points is suggested:</p>\n");
  2405. ZEND_PUTS("<ul>\n");
  2406. ZEND_PUTS("<li>Be aware that `Username' and `Password' is case sense. Check capslock status led on your keyboard, and punch left/right Shift keys once for each</li>\n");
  2407. ZEND_PUTS("<li>Make sure the md5 password is generated correctly. You may use <a href=\"mkpassword.php\">mkpassword.php</a></li>\n");
  2408. ZEND_PUTS("<li>Reload browser cache by pressing F5 and/or Ctrl+F5, or simply clear browser cache after you've updated username/password in php ini.</li>\n");
  2409. ZEND_PUTS("</ul>\n");
  2410. ZEND_PUTS("Check <a href=\"" XCACHE_WIKI_URL "/InstallAdministration\">XCache wiki page</a> for more information.\n");
  2411. ZEND_PUTS("</body>\n");
  2412. ZEND_PUTS("</html>\n");
  2413. zend_bailout();
  2414. return 0;
  2415. }
  2416. /* }}} */
  2417. static void xc_clear(long type, xc_cache_t *cache TSRMLS_DC) /* {{{ */
  2418. {
  2419. xc_entry_t *e, *next;
  2420. int entryslotid, c;
  2421. ENTER_LOCK(cache) {
  2422. for (entryslotid = 0, c = cache->hentry->size; entryslotid < c; entryslotid ++) {
  2423. for (e = cache->entries[entryslotid]; e; e = next) {
  2424. next = e->next;
  2425. xc_entry_remove_unlocked(type, cache, entryslotid, e TSRMLS_CC);
  2426. }
  2427. cache->entries[entryslotid] = NULL;
  2428. }
  2429. } LEAVE_LOCK(cache);
  2430. } /* }}} */
  2431. /* {{{ xcache_admin_operate */
  2432. typedef enum { XC_OP_COUNT, XC_OP_INFO, XC_OP_LIST, XC_OP_CLEAR } xcache_op_type;
  2433. static void xcache_admin_operate(xcache_op_type optype, INTERNAL_FUNCTION_PARAMETERS)
  2434. {
  2435. long type;
  2436. int size;
  2437. xc_cache_t **caches, *cache;
  2438. long id = 0;
  2439. xcache_admin_auth_check(TSRMLS_C);
  2440. if (!xc_initized || !xc_shm || xc_shm->disabled) {
  2441. RETURN_NULL();
  2442. }
  2443. switch (optype) {
  2444. case XC_OP_COUNT:
  2445. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type) == FAILURE) {
  2446. return;
  2447. }
  2448. break;
  2449. case XC_OP_CLEAR:
  2450. id = -1;
  2451. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|l", &type, &id) == FAILURE) {
  2452. return;
  2453. }
  2454. break;
  2455. default:
  2456. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &type, &id) == FAILURE) {
  2457. return;
  2458. }
  2459. }
  2460. switch (type) {
  2461. case XC_TYPE_PHP:
  2462. size = xc_php_hcache.size;
  2463. caches = xc_php_caches;
  2464. break;
  2465. case XC_TYPE_VAR:
  2466. size = xc_var_hcache.size;
  2467. caches = xc_var_caches;
  2468. break;
  2469. default:
  2470. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown type %ld", type);
  2471. RETURN_FALSE;
  2472. }
  2473. switch (optype) {
  2474. case XC_OP_COUNT:
  2475. RETURN_LONG(caches ? size : 0)
  2476. break;
  2477. case XC_OP_INFO:
  2478. case XC_OP_LIST:
  2479. if (!caches || id < 0 || id >= size) {
  2480. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
  2481. RETURN_FALSE;
  2482. }
  2483. array_init(return_value);
  2484. cache = caches[id];
  2485. ENTER_LOCK(cache) {
  2486. if (optype == XC_OP_INFO) {
  2487. xc_fillinfo_unlocked(type, cache, return_value TSRMLS_CC);
  2488. }
  2489. else {
  2490. xc_filllist_unlocked(type, cache, return_value TSRMLS_CC);
  2491. }
  2492. } LEAVE_LOCK(cache);
  2493. break;
  2494. case XC_OP_CLEAR:
  2495. if (!caches || id < -1 || id >= size) {
  2496. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
  2497. RETURN_FALSE;
  2498. }
  2499. if (id == -1) {
  2500. for (id = 0; id < size; ++id) {
  2501. xc_clear(type, caches[id] TSRMLS_CC);
  2502. }
  2503. }
  2504. else {
  2505. xc_clear(type, caches[id] TSRMLS_CC);
  2506. }
  2507. xc_gc_deletes(TSRMLS_C);
  2508. break;
  2509. default:
  2510. assert(0);
  2511. }
  2512. }
  2513. /* }}} */
  2514. /* {{{ proto int xcache_count(int type)
  2515. Return count of cache on specified cache type */
  2516. PHP_FUNCTION(xcache_count)
  2517. {
  2518. xcache_admin_operate(XC_OP_COUNT, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  2519. }
  2520. /* }}} */
  2521. /* {{{ proto array xcache_info(int type, int id)
  2522. Get cache info by id on specified cache type */
  2523. PHP_FUNCTION(xcache_info)
  2524. {
  2525. xcache_admin_operate(XC_OP_INFO, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  2526. }
  2527. /* }}} */
  2528. /* {{{ proto array xcache_list(int type, int id)
  2529. Get cache entries list by id on specified cache type */
  2530. PHP_FUNCTION(xcache_list)
  2531. {
  2532. xcache_admin_operate(XC_OP_LIST, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  2533. }
  2534. /* }}} */
  2535. /* {{{ proto array xcache_clear_cache(int type, [ int id = -1 ])
  2536. Clear cache by id on specified cache type */
  2537. PHP_FUNCTION(xcache_clear_cache)
  2538. {
  2539. xcache_admin_operate(XC_OP_CLEAR, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  2540. }
  2541. /* }}} */
  2542. #define VAR_DISABLED_WARNING() do { \
  2543. php_error_docref(NULL TSRMLS_CC, E_WARNING, "XCache var cache was not initialized properly. Check php log for actual reason"); \
  2544. } while (0)
  2545. static int xc_entry_var_init_key(xc_entry_var_t *entry_var, xc_entry_hash_t *entry_hash, zval *name TSRMLS_DC) /* {{{ */
  2546. {
  2547. xc_hash_value_t hv;
  2548. switch (name->type) {
  2549. #ifdef IS_UNICODE
  2550. case IS_UNICODE:
  2551. case IS_STRING:
  2552. #endif
  2553. default:
  2554. #ifdef IS_UNICODE
  2555. convert_to_unicode(name);
  2556. #else
  2557. convert_to_string(name);
  2558. #endif
  2559. }
  2560. #ifdef IS_UNICODE
  2561. entry_var->name_type = name->type;
  2562. #endif
  2563. entry_var->entry.name = name->value;
  2564. hv = xc_entry_hash_var((xc_entry_t *) entry_var TSRMLS_CC);
  2565. entry_hash->cacheid = (hv & xc_var_hcache.mask);
  2566. hv >>= xc_var_hcache.bits;
  2567. entry_hash->entryslotid = (hv & xc_var_hentry.mask);
  2568. return SUCCESS;
  2569. }
  2570. /* }}} */
  2571. /* {{{ proto mixed xcache_get(string name)
  2572. Get cached data by specified name */
  2573. PHP_FUNCTION(xcache_get)
  2574. {
  2575. xc_entry_hash_t entry_hash;
  2576. xc_cache_t *cache;
  2577. xc_entry_var_t entry_var, *stored_entry_var;
  2578. zval *name;
  2579. if (!xc_var_caches || !xc_shm || xc_shm->disabled) {
  2580. VAR_DISABLED_WARNING();
  2581. RETURN_NULL();
  2582. }
  2583. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
  2584. return;
  2585. }
  2586. xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
  2587. cache = xc_var_caches[entry_hash.cacheid];
  2588. ENTER_LOCK(cache) {
  2589. stored_entry_var = (xc_entry_var_t *) xc_entry_find_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) &entry_var TSRMLS_CC);
  2590. if (stored_entry_var) {
  2591. /* return */
  2592. xc_processor_restore_zval(return_value, stored_entry_var->value, stored_entry_var->have_references TSRMLS_CC);
  2593. xc_cache_hit_unlocked(cache TSRMLS_CC);
  2594. }
  2595. else {
  2596. RETVAL_NULL();
  2597. }
  2598. } LEAVE_LOCK(cache);
  2599. }
  2600. /* }}} */
  2601. /* {{{ proto bool xcache_set(string name, mixed value [, int ttl])
  2602. Store data to cache by specified name */
  2603. PHP_FUNCTION(xcache_set)
  2604. {
  2605. xc_entry_hash_t entry_hash;
  2606. xc_cache_t *cache;
  2607. xc_entry_var_t entry_var, *stored_entry_var;
  2608. zval *name;
  2609. zval *value;
  2610. if (!xc_var_caches || !xc_shm || xc_shm->disabled) {
  2611. VAR_DISABLED_WARNING();
  2612. RETURN_NULL();
  2613. }
  2614. entry_var.entry.ttl = XG(var_ttl);
  2615. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|l", &name, &value, &entry_var.entry.ttl) == FAILURE) {
  2616. return;
  2617. }
  2618. if (Z_TYPE_P(value) == IS_OBJECT) {
  2619. php_error_docref(NULL TSRMLS_CC, E_ERROR, "Objects cannot be stored in the variable cache. Use serialize before xcache_set");
  2620. RETURN_NULL();
  2621. }
  2622. /* max ttl */
  2623. if (xc_var_maxttl && (!entry_var.entry.ttl || entry_var.entry.ttl > xc_var_maxttl)) {
  2624. entry_var.entry.ttl = xc_var_maxttl;
  2625. }
  2626. xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
  2627. cache = xc_var_caches[entry_hash.cacheid];
  2628. ENTER_LOCK(cache) {
  2629. stored_entry_var = (xc_entry_var_t *) xc_entry_find_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) &entry_var TSRMLS_CC);
  2630. if (stored_entry_var) {
  2631. xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) stored_entry_var TSRMLS_CC);
  2632. }
  2633. entry_var.value = value;
  2634. RETVAL_BOOL(xc_entry_var_store_unlocked(cache, entry_hash.entryslotid, &entry_var TSRMLS_CC) != NULL ? 1 : 0);
  2635. } LEAVE_LOCK(cache);
  2636. }
  2637. /* }}} */
  2638. /* {{{ proto bool xcache_isset(string name)
  2639. Check if an entry exists in cache by specified name */
  2640. PHP_FUNCTION(xcache_isset)
  2641. {
  2642. xc_entry_hash_t entry_hash;
  2643. xc_cache_t *cache;
  2644. xc_entry_var_t entry_var, *stored_entry_var;
  2645. zval *name;
  2646. if (!xc_var_caches || !xc_shm || xc_shm->disabled) {
  2647. VAR_DISABLED_WARNING();
  2648. RETURN_FALSE;
  2649. }
  2650. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
  2651. return;
  2652. }
  2653. xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
  2654. cache = xc_var_caches[entry_hash.cacheid];
  2655. ENTER_LOCK(cache) {
  2656. stored_entry_var = (xc_entry_var_t *) xc_entry_find_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) &entry_var TSRMLS_CC);
  2657. if (stored_entry_var) {
  2658. xc_cache_hit_unlocked(cache TSRMLS_CC);
  2659. RETVAL_TRUE;
  2660. /* return */
  2661. }
  2662. else {
  2663. RETVAL_FALSE;
  2664. }
  2665. } LEAVE_LOCK(cache);
  2666. }
  2667. /* }}} */
  2668. /* {{{ proto bool xcache_unset(string name)
  2669. Unset existing data in cache by specified name */
  2670. PHP_FUNCTION(xcache_unset)
  2671. {
  2672. xc_entry_hash_t entry_hash;
  2673. xc_cache_t *cache;
  2674. xc_entry_var_t entry_var, *stored_entry_var;
  2675. zval *name;
  2676. if (!xc_var_caches || !xc_shm || xc_shm->disabled) {
  2677. VAR_DISABLED_WARNING();
  2678. RETURN_FALSE;
  2679. }
  2680. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
  2681. return;
  2682. }
  2683. xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
  2684. cache = xc_var_caches[entry_hash.cacheid];
  2685. ENTER_LOCK(cache) {
  2686. stored_entry_var = (xc_entry_var_t *) xc_entry_find_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) &entry_var TSRMLS_CC);
  2687. if (stored_entry_var) {
  2688. xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) stored_entry_var TSRMLS_CC);
  2689. RETVAL_TRUE;
  2690. }
  2691. else {
  2692. RETVAL_FALSE;
  2693. }
  2694. } LEAVE_LOCK(cache);
  2695. }
  2696. /* }}} */
  2697. /* {{{ proto bool xcache_unset_by_prefix(string prefix)
  2698. Unset existing data in cache by specified prefix */
  2699. PHP_FUNCTION(xcache_unset_by_prefix)
  2700. {
  2701. zval *prefix;
  2702. int i, iend;
  2703. if (!xc_var_caches || !xc_shm || xc_shm->disabled) {
  2704. VAR_DISABLED_WARNING();
  2705. RETURN_FALSE;
  2706. }
  2707. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &prefix) == FAILURE) {
  2708. return;
  2709. }
  2710. for (i = 0, iend = xc_var_hcache.size; i < iend; i ++) {
  2711. xc_cache_t *cache = xc_var_caches[i];
  2712. ENTER_LOCK(cache) {
  2713. int entryslotid, jend;
  2714. for (entryslotid = 0, jend = cache->hentry->size; entryslotid < jend; entryslotid ++) {
  2715. xc_entry_t *entry, *next;
  2716. for (entry = cache->entries[entryslotid]; entry; entry = next) {
  2717. next = entry->next;
  2718. if (xc_entry_has_prefix_unlocked(XC_TYPE_VAR, entry, prefix)) {
  2719. xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entryslotid, entry TSRMLS_CC);
  2720. }
  2721. }
  2722. }
  2723. } LEAVE_LOCK(cache);
  2724. }
  2725. }
  2726. /* }}} */
  2727. static inline void xc_var_inc_dec(int inc, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
  2728. {
  2729. xc_entry_hash_t entry_hash;
  2730. xc_cache_t *cache;
  2731. xc_entry_var_t entry_var, *stored_entry_var;
  2732. zval *name;
  2733. long count = 1;
  2734. long value = 0;
  2735. zval oldzval;
  2736. if (!xc_var_caches || !xc_shm || xc_shm->disabled) {
  2737. VAR_DISABLED_WARNING();
  2738. RETURN_NULL();
  2739. }
  2740. entry_var.entry.ttl = XG(var_ttl);
  2741. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ll", &name, &count, &entry_var.entry.ttl) == FAILURE) {
  2742. return;
  2743. }
  2744. /* max ttl */
  2745. if (xc_var_maxttl && (!entry_var.entry.ttl || entry_var.entry.ttl > xc_var_maxttl)) {
  2746. entry_var.entry.ttl = xc_var_maxttl;
  2747. }
  2748. xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
  2749. cache = xc_var_caches[entry_hash.cacheid];
  2750. ENTER_LOCK(cache) {
  2751. stored_entry_var = (xc_entry_var_t *) xc_entry_find_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) &entry_var TSRMLS_CC);
  2752. if (stored_entry_var) {
  2753. TRACE("incdec: got entry_var %s", entry_var.entry.name.str.val);
  2754. /* do it in place */
  2755. if (Z_TYPE_P(stored_entry_var->value) == IS_LONG) {
  2756. zval *zv;
  2757. stored_entry_var->entry.ctime = XG(request_time);
  2758. stored_entry_var->entry.ttl = entry_var.entry.ttl;
  2759. TRACE("%s", "incdec: islong");
  2760. value = Z_LVAL_P(stored_entry_var->value);
  2761. value += (inc == 1 ? count : - count);
  2762. RETVAL_LONG(value);
  2763. zv = (zval *) cache->shm->handlers->to_readwrite(cache->shm, (char *) stored_entry_var->value);
  2764. Z_LVAL_P(zv) = value;
  2765. ++cache->updates;
  2766. break; /* leave lock */
  2767. }
  2768. TRACE("%s", "incdec: notlong");
  2769. xc_processor_restore_zval(&oldzval, stored_entry_var->value, stored_entry_var->have_references TSRMLS_CC);
  2770. convert_to_long(&oldzval);
  2771. value = Z_LVAL(oldzval);
  2772. zval_dtor(&oldzval);
  2773. }
  2774. else {
  2775. TRACE("incdec: %s not found", entry_var.entry.name.str.val);
  2776. }
  2777. value += (inc == 1 ? count : - count);
  2778. RETVAL_LONG(value);
  2779. entry_var.value = return_value;
  2780. if (stored_entry_var) {
  2781. entry_var.entry.atime = stored_entry_var->entry.atime;
  2782. entry_var.entry.ctime = stored_entry_var->entry.ctime;
  2783. entry_var.entry.hits = stored_entry_var->entry.hits;
  2784. xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) stored_entry_var TSRMLS_CC);
  2785. }
  2786. xc_entry_var_store_unlocked(cache, entry_hash.entryslotid, &entry_var TSRMLS_CC);
  2787. } LEAVE_LOCK(cache);
  2788. }
  2789. /* }}} */
  2790. /* {{{ proto int xcache_inc(string name [, int value [, int ttl]])
  2791. Increase an int counter in cache by specified name, create it if not exists */
  2792. PHP_FUNCTION(xcache_inc)
  2793. {
  2794. xc_var_inc_dec(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  2795. }
  2796. /* }}} */
  2797. /* {{{ proto int xcache_dec(string name [, int value [, int ttl]])
  2798. Decrease an int counter in cache by specified name, create it if not exists */
  2799. PHP_FUNCTION(xcache_dec)
  2800. {
  2801. xc_var_inc_dec(-1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  2802. }
  2803. /* }}} */
  2804. /* {{{ proto int xcache_get_refcount(mixed variable)
  2805. XCache internal uses only: Get reference count of variable */
  2806. PHP_FUNCTION(xcache_get_refcount)
  2807. {
  2808. zval *variable;
  2809. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &variable) == FAILURE) {
  2810. RETURN_NULL();
  2811. }
  2812. RETURN_LONG(Z_REFCOUNT(*variable));
  2813. }
  2814. /* }}} */
  2815. /* {{{ proto bool xcache_get_isref(mixed variable)
  2816. XCache internal uses only: Check if variable data is marked referenced */
  2817. ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_get_isref, 0, 0, 1)
  2818. ZEND_ARG_INFO(1, variable)
  2819. ZEND_END_ARG_INFO()
  2820. PHP_FUNCTION(xcache_get_isref)
  2821. {
  2822. zval *variable;
  2823. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &variable) == FAILURE) {
  2824. RETURN_NULL();
  2825. }
  2826. RETURN_BOOL(Z_ISREF(*variable) && Z_REFCOUNT(*variable) >= 3);
  2827. }
  2828. /* }}} */
  2829. #ifdef HAVE_XCACHE_DPRINT
  2830. /* {{{ proto bool xcache_dprint(mixed value)
  2831. Prints variable (or value) internal struct (debug only) */
  2832. PHP_FUNCTION(xcache_dprint)
  2833. {
  2834. zval *value;
  2835. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
  2836. return;
  2837. }
  2838. xc_dprint_zval(value, 0 TSRMLS_CC);
  2839. }
  2840. /* }}} */
  2841. #endif
  2842. /* {{{ proto string xcache_asm(string filename)
  2843. */
  2844. #ifdef HAVE_XCACHE_ASSEMBLER
  2845. PHP_FUNCTION(xcache_asm)
  2846. {
  2847. }
  2848. #endif
  2849. /* }}} */
  2850. #ifdef HAVE_XCACHE_DISASSEMBLER
  2851. /* {{{ proto array xcache_dasm_file(string filename)
  2852. Disassemble file into opcode array by filename */
  2853. PHP_FUNCTION(xcache_dasm_file)
  2854. {
  2855. char *filename;
  2856. int filename_len;
  2857. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) {
  2858. return;
  2859. }
  2860. if (!filename_len) RETURN_FALSE;
  2861. xc_dasm_file(return_value, filename TSRMLS_CC);
  2862. }
  2863. /* }}} */
  2864. /* {{{ proto array xcache_dasm_string(string code)
  2865. Disassemble php code into opcode array */
  2866. PHP_FUNCTION(xcache_dasm_string)
  2867. {
  2868. zval *code;
  2869. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &code) == FAILURE) {
  2870. return;
  2871. }
  2872. xc_dasm_string(return_value, code TSRMLS_CC);
  2873. }
  2874. /* }}} */
  2875. #endif
  2876. /* {{{ proto string xcache_encode(string filename)
  2877. Encode php file into XCache opcode encoded format */
  2878. #ifdef HAVE_XCACHE_ENCODER
  2879. PHP_FUNCTION(xcache_encode)
  2880. {
  2881. }
  2882. #endif
  2883. /* }}} */
  2884. /* {{{ proto bool xcache_decode_file(string filename)
  2885. Decode(load) opcode from XCache encoded format file */
  2886. #ifdef HAVE_XCACHE_DECODER
  2887. PHP_FUNCTION(xcache_decode_file)
  2888. {
  2889. }
  2890. #endif
  2891. /* }}} */
  2892. /* {{{ proto bool xcache_decode_string(string data)
  2893. Decode(load) opcode from XCache encoded format data */
  2894. #ifdef HAVE_XCACHE_DECODER
  2895. PHP_FUNCTION(xcache_decode_string)
  2896. {
  2897. }
  2898. #endif
  2899. /* }}} */
  2900. /* {{{ xc_call_getter */
  2901. typedef const char *(xc_name_getter_t)(zend_uchar type);
  2902. static void xc_call_getter(xc_name_getter_t getter, int count, INTERNAL_FUNCTION_PARAMETERS)
  2903. {
  2904. long spec;
  2905. const char *name;
  2906. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
  2907. return;
  2908. }
  2909. if (spec >= 0 && spec < count) {
  2910. name = getter((zend_uchar) spec);
  2911. if (name) {
  2912. /* RETURN_STRING */
  2913. int len = strlen(name);
  2914. return_value->value.str.len = len;
  2915. return_value->value.str.val = estrndup(name, len);
  2916. return_value->type = IS_STRING;
  2917. return;
  2918. }
  2919. }
  2920. RETURN_NULL();
  2921. }
  2922. /* }}} */
  2923. /* {{{ proto string xcache_get_op_type(int op_type) */
  2924. PHP_FUNCTION(xcache_get_op_type)
  2925. {
  2926. xc_call_getter(xc_get_op_type, xc_get_op_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
  2927. }
  2928. /* }}} */
  2929. /* {{{ proto string xcache_get_data_type(int type) */
  2930. PHP_FUNCTION(xcache_get_data_type)
  2931. {
  2932. xc_call_getter(xc_get_data_type, xc_get_data_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
  2933. }
  2934. /* }}} */
  2935. /* {{{ proto string xcache_get_opcode(int opcode) */
  2936. PHP_FUNCTION(xcache_get_opcode)
  2937. {
  2938. xc_call_getter(xc_get_opcode, xc_get_opcode_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
  2939. }
  2940. /* }}} */
  2941. /* {{{ proto string xcache_get_op_spec(int op_type) */
  2942. PHP_FUNCTION(xcache_get_op_spec)
  2943. {
  2944. xc_call_getter(xc_get_op_spec, xc_get_op_spec_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
  2945. }
  2946. /* }}} */
  2947. /* {{{ proto string xcache_get_opcode_spec(int opcode) */
  2948. PHP_FUNCTION(xcache_get_opcode_spec)
  2949. {
  2950. long spec;
  2951. const xc_opcode_spec_t *opspec;
  2952. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
  2953. return;
  2954. }
  2955. if ((zend_uchar) spec <= xc_get_opcode_spec_count()) {
  2956. opspec = xc_get_opcode_spec((zend_uchar) spec);
  2957. if (opspec) {
  2958. array_init(return_value);
  2959. add_assoc_long_ex(return_value, ZEND_STRS("ext"), opspec->ext);
  2960. add_assoc_long_ex(return_value, ZEND_STRS("op1"), opspec->op1);
  2961. add_assoc_long_ex(return_value, ZEND_STRS("op2"), opspec->op2);
  2962. add_assoc_long_ex(return_value, ZEND_STRS("res"), opspec->res);
  2963. return;
  2964. }
  2965. }
  2966. RETURN_NULL();
  2967. }
  2968. /* }}} */
  2969. /* {{{ proto mixed xcache_get_special_value(zval value)
  2970. XCache internal use only: For decompiler to get static value with type fixed */
  2971. PHP_FUNCTION(xcache_get_special_value)
  2972. {
  2973. zval *value;
  2974. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
  2975. return;
  2976. }
  2977. switch ((Z_TYPE_P(value) & IS_CONSTANT_TYPE_MASK)) {
  2978. case IS_CONSTANT:
  2979. *return_value = *value;
  2980. zval_copy_ctor(return_value);
  2981. return_value->type = UNISW(IS_STRING, UG(unicode) ? IS_UNICODE : IS_STRING);
  2982. break;
  2983. case IS_CONSTANT_ARRAY:
  2984. *return_value = *value;
  2985. zval_copy_ctor(return_value);
  2986. return_value->type = IS_ARRAY;
  2987. break;
  2988. default:
  2989. RETURN_NULL();
  2990. }
  2991. }
  2992. /* }}} */
  2993. /* {{{ proto int xcache_get_type(zval value)
  2994. XCache internal use only for disassembler to get variable type in engine level */
  2995. PHP_FUNCTION(xcache_get_type)
  2996. {
  2997. zval *value;
  2998. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
  2999. return;
  3000. }
  3001. RETURN_LONG(Z_TYPE_P(value));
  3002. }
  3003. /* }}} */
  3004. /* {{{ proto string xcache_coredump(int op_type) */
  3005. PHP_FUNCTION(xcache_coredump)
  3006. {
  3007. if (xc_test) {
  3008. raise(SIGSEGV);
  3009. }
  3010. else {
  3011. php_error_docref(NULL TSRMLS_CC, E_WARNING, "xcache.test must be enabled to test xcache_coredump()");
  3012. }
  3013. }
  3014. /* }}} */
  3015. /* {{{ proto string xcache_is_autoglobal(string name) */
  3016. PHP_FUNCTION(xcache_is_autoglobal)
  3017. {
  3018. zval *name;
  3019. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
  3020. return;
  3021. }
  3022. #ifdef IS_UNICODE
  3023. convert_to_unicode(name);
  3024. #else
  3025. convert_to_string(name);
  3026. #endif
  3027. RETURN_BOOL(zend_u_hash_exists(CG(auto_globals), UG(unicode), Z_STRVAL_P(name), Z_STRLEN_P(name) + 1));
  3028. }
  3029. /* }}} */
  3030. static zend_function_entry xcache_functions[] = /* {{{ */
  3031. {
  3032. PHP_FE(xcache_count, NULL)
  3033. PHP_FE(xcache_info, NULL)
  3034. PHP_FE(xcache_list, NULL)
  3035. PHP_FE(xcache_clear_cache, NULL)
  3036. PHP_FE(xcache_coredump, NULL)
  3037. #ifdef HAVE_XCACHE_ASSEMBLER
  3038. PHP_FE(xcache_asm, NULL)
  3039. #endif
  3040. #ifdef HAVE_XCACHE_DISASSEMBLER
  3041. PHP_FE(xcache_dasm_file, NULL)
  3042. PHP_FE(xcache_dasm_string, NULL)
  3043. #endif
  3044. #ifdef HAVE_XCACHE_ENCODER
  3045. PHP_FE(xcache_encode, NULL)
  3046. #endif
  3047. #ifdef HAVE_XCACHE_DECODER
  3048. PHP_FE(xcache_decode_file, NULL)
  3049. PHP_FE(xcache_decode_string, NULL)
  3050. #endif
  3051. #ifdef HAVE_XCACHE_COVERAGER
  3052. PHP_FE(xcache_coverager_decode, NULL)
  3053. PHP_FE(xcache_coverager_start, NULL)
  3054. PHP_FE(xcache_coverager_stop, NULL)
  3055. PHP_FE(xcache_coverager_get, NULL)
  3056. #endif
  3057. PHP_FE(xcache_get_special_value, NULL)
  3058. PHP_FE(xcache_get_type, NULL)
  3059. PHP_FE(xcache_get_op_type, NULL)
  3060. PHP_FE(xcache_get_data_type, NULL)
  3061. PHP_FE(xcache_get_opcode, NULL)
  3062. PHP_FE(xcache_get_opcode_spec, NULL)
  3063. PHP_FE(xcache_is_autoglobal, NULL)
  3064. PHP_FE(xcache_inc, NULL)
  3065. PHP_FE(xcache_dec, NULL)
  3066. PHP_FE(xcache_get, NULL)
  3067. PHP_FE(xcache_set, NULL)
  3068. PHP_FE(xcache_isset, NULL)
  3069. PHP_FE(xcache_unset, NULL)
  3070. PHP_FE(xcache_unset_by_prefix, NULL)
  3071. PHP_FE(xcache_get_refcount, NULL)
  3072. PHP_FE(xcache_get_isref, arginfo_xcache_get_isref)
  3073. #ifdef HAVE_XCACHE_DPRINT
  3074. PHP_FE(xcache_dprint, NULL)
  3075. #endif
  3076. {NULL, NULL, NULL}
  3077. };
  3078. /* }}} */
  3079. #ifdef ZEND_WIN32
  3080. #include "dbghelp.h"
  3081. typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
  3082. CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
  3083. CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
  3084. CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
  3085. );
  3086. static PTOP_LEVEL_EXCEPTION_FILTER oldFilter = NULL;
  3087. static HMODULE dbghelpModule = NULL;
  3088. static char crash_dumpPath[_MAX_PATH] = { 0 };
  3089. static MINIDUMPWRITEDUMP dbghelp_MiniDumpWriteDump = NULL;
  3090. static LONG WINAPI miniDumperFilter(struct _EXCEPTION_POINTERS *pExceptionInfo) /* {{{ */
  3091. {
  3092. HANDLE fileHandle;
  3093. SetUnhandledExceptionFilter(oldFilter);
  3094. /* create the file */
  3095. fileHandle = CreateFile(crash_dumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  3096. if (fileHandle != INVALID_HANDLE_VALUE) {
  3097. MINIDUMP_EXCEPTION_INFORMATION exceptionInformation;
  3098. BOOL ok;
  3099. exceptionInformation.ThreadId = GetCurrentThreadId();
  3100. exceptionInformation.ExceptionPointers = pExceptionInfo;
  3101. exceptionInformation.ClientPointers = FALSE;
  3102. /* write the dump */
  3103. ok = dbghelp_MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), fileHandle, MiniDumpNormal|MiniDumpWithDataSegs|MiniDumpWithIndirectlyReferencedMemory, &exceptionInformation, NULL, NULL);
  3104. CloseHandle(fileHandle);
  3105. if (ok) {
  3106. zend_error(E_ERROR, "Saved dump file to '%s'", crash_dumpPath);
  3107. return EXCEPTION_EXECUTE_HANDLER;
  3108. }
  3109. else {
  3110. zend_error(E_ERROR, "Failed to save dump file to '%s' (error %d)", crash_dumpPath, GetLastError());
  3111. }
  3112. }
  3113. else {
  3114. zend_error(E_ERROR, "Failed to create dump file '%s' (error %d)", crash_dumpPath, GetLastError());
  3115. }
  3116. return EXCEPTION_CONTINUE_SEARCH;
  3117. }
  3118. /* }}} */
  3119. static void xcache_restore_crash_handler() /* {{{ */
  3120. {
  3121. if (oldFilter) {
  3122. SetUnhandledExceptionFilter(oldFilter);
  3123. oldFilter = NULL;
  3124. }
  3125. }
  3126. /* }}} */
  3127. static void xcache_init_crash_handler() /* {{{ */
  3128. {
  3129. /* firstly see if dbghelp.dll is around and has the function we need
  3130. look next to the EXE first, as the one in System32 might be old
  3131. (e.g. Windows 2000) */
  3132. char dbghelpPath[_MAX_PATH];
  3133. if (GetModuleFileName(NULL, dbghelpPath, _MAX_PATH)) {
  3134. char *slash = strchr(dbghelpPath, '\\');
  3135. if (slash) {
  3136. strcpy(slash + 1, "DBGHELP.DLL");
  3137. dbghelpModule = LoadLibrary(dbghelpPath);
  3138. }
  3139. }
  3140. if (!dbghelpModule) {
  3141. /* load any version we can */
  3142. dbghelpModule = LoadLibrary("DBGHELP.DLL");
  3143. if (!dbghelpModule) {
  3144. zend_error(E_ERROR, "Unable to enable crash dump saver: %s", "DBGHELP.DLL not found");
  3145. return;
  3146. }
  3147. }
  3148. dbghelp_MiniDumpWriteDump = (MINIDUMPWRITEDUMP)GetProcAddress(dbghelpModule, "MiniDumpWriteDump");
  3149. if (!dbghelp_MiniDumpWriteDump) {
  3150. zend_error(E_ERROR, "Unable to enable crash dump saver: %s", "DBGHELP.DLL too old. Get updated dll and put it aside of php_xcache.dll");
  3151. return;
  3152. }
  3153. #ifdef XCACHE_VERSION_REVISION
  3154. #define REVISION "r" XCACHE_VERSION_REVISION
  3155. #else
  3156. #define REVISION ""
  3157. #endif
  3158. sprintf(crash_dumpPath, "%s\\php-%s-xcache-%s%s-%lu-%lu.dmp", xc_coredump_dir, zend_get_module_version("standard"), XCACHE_VERSION, REVISION, (unsigned long) time(NULL), (unsigned long) GetCurrentProcessId());
  3159. #undef REVISION
  3160. oldFilter = SetUnhandledExceptionFilter(&miniDumperFilter);
  3161. }
  3162. /* }}} */
  3163. #else
  3164. /* old signal handlers {{{ */
  3165. typedef void (*xc_sighandler_t)(int);
  3166. #define FOREACH_SIG(sig) static xc_sighandler_t old_##sig##_handler = NULL
  3167. #include "util/xc_foreachcoresig.h"
  3168. #undef FOREACH_SIG
  3169. /* }}} */
  3170. static void xcache_signal_handler(int sig);
  3171. static void xcache_restore_crash_handler() /* {{{ */
  3172. {
  3173. #define FOREACH_SIG(sig) do { \
  3174. if (old_##sig##_handler != xcache_signal_handler) { \
  3175. signal(sig, old_##sig##_handler); \
  3176. } \
  3177. else { \
  3178. signal(sig, SIG_DFL); \
  3179. } \
  3180. } while (0)
  3181. #include "util/xc_foreachcoresig.h"
  3182. #undef FOREACH_SIG
  3183. }
  3184. /* }}} */
  3185. static void xcache_init_crash_handler() /* {{{ */
  3186. {
  3187. #define FOREACH_SIG(sig) \
  3188. old_##sig##_handler = signal(sig, xcache_signal_handler)
  3189. #include "util/xc_foreachcoresig.h"
  3190. #undef FOREACH_SIG
  3191. }
  3192. /* }}} */
  3193. static void xcache_signal_handler(int sig) /* {{{ */
  3194. {
  3195. xcache_restore_crash_handler();
  3196. if (xc_coredump_dir && xc_coredump_dir[0]) {
  3197. if (chdir(xc_coredump_dir) != 0) {
  3198. /* error, but nothing can do about it
  3199. * and should'nt print anything which might SEGV again */
  3200. }
  3201. }
  3202. if (xc_disable_on_crash) {
  3203. xc_disable_on_crash = 0;
  3204. if (xc_shm) {
  3205. xc_shm->disabled = 1;
  3206. }
  3207. }
  3208. raise(sig);
  3209. }
  3210. /* }}} */
  3211. #endif
  3212. /* {{{ PHP_INI */
  3213. static PHP_INI_MH(xc_OnUpdateDummy)
  3214. {
  3215. return SUCCESS;
  3216. }
  3217. static PHP_INI_MH(xc_OnUpdateULong)
  3218. {
  3219. zend_ulong *p = (zend_ulong *) mh_arg1;
  3220. *p = (zend_ulong) atoi(new_value);
  3221. return SUCCESS;
  3222. }
  3223. static PHP_INI_MH(xc_OnUpdateBool)
  3224. {
  3225. zend_bool *p = (zend_bool *)mh_arg1;
  3226. if (strncasecmp("on", new_value, sizeof("on"))) {
  3227. *p = (zend_bool) atoi(new_value);
  3228. }
  3229. else {
  3230. *p = (zend_bool) 1;
  3231. }
  3232. return SUCCESS;
  3233. }
  3234. static PHP_INI_MH(xc_OnUpdateString)
  3235. {
  3236. char **p = (char**)mh_arg1;
  3237. if (*p) {
  3238. pefree(*p, 1);
  3239. }
  3240. *p = pemalloc(strlen(new_value) + 1, 1);
  3241. strcpy(*p, new_value);
  3242. return SUCCESS;
  3243. }
  3244. #ifndef ZEND_ENGINE_2
  3245. #define OnUpdateLong OnUpdateInt
  3246. #endif
  3247. #ifdef ZEND_WIN32
  3248. # define DEFAULT_PATH "xcache"
  3249. #else
  3250. # define DEFAULT_PATH "/dev/zero"
  3251. #endif
  3252. PHP_INI_BEGIN()
  3253. PHP_INI_ENTRY1 ("xcache.mmap_path", DEFAULT_PATH, PHP_INI_SYSTEM, xc_OnUpdateString, &xc_mmap_path)
  3254. PHP_INI_ENTRY1 ("xcache.coredump_directory", "", PHP_INI_SYSTEM, xc_OnUpdateString, &xc_coredump_dir)
  3255. PHP_INI_ENTRY1 ("xcache.disable_on_crash", "0", PHP_INI_SYSTEM, xc_OnUpdateBool, &xc_disable_on_crash)
  3256. PHP_INI_ENTRY1 ("xcache.test", "0", PHP_INI_SYSTEM, xc_OnUpdateBool, &xc_test)
  3257. PHP_INI_ENTRY1 ("xcache.readonly_protection", "0", PHP_INI_SYSTEM, xc_OnUpdateBool, &xc_readonly_protection)
  3258. /* opcode cache */
  3259. PHP_INI_ENTRY1 ("xcache.size", "0", PHP_INI_SYSTEM, xc_OnUpdateDummy, NULL)
  3260. PHP_INI_ENTRY1 ("xcache.count", "1", PHP_INI_SYSTEM, xc_OnUpdateDummy, NULL)
  3261. PHP_INI_ENTRY1 ("xcache.slots", "8K", PHP_INI_SYSTEM, xc_OnUpdateDummy, NULL)
  3262. PHP_INI_ENTRY1 ("xcache.shm_scheme", "mmap", PHP_INI_SYSTEM, xc_OnUpdateString, &xc_shm_scheme)
  3263. PHP_INI_ENTRY1 ("xcache.ttl", "0", PHP_INI_SYSTEM, xc_OnUpdateULong, &xc_php_ttl)
  3264. PHP_INI_ENTRY1 ("xcache.gc_interval", "0", PHP_INI_SYSTEM, xc_OnUpdateULong, &xc_php_gc_interval)
  3265. /* var cache */
  3266. PHP_INI_ENTRY1 ("xcache.var_size", "0", PHP_INI_SYSTEM, xc_OnUpdateDummy, NULL)
  3267. PHP_INI_ENTRY1 ("xcache.var_count", "1", PHP_INI_SYSTEM, xc_OnUpdateDummy, NULL)
  3268. PHP_INI_ENTRY1 ("xcache.var_slots", "8K", PHP_INI_SYSTEM, xc_OnUpdateDummy, NULL)
  3269. PHP_INI_ENTRY1 ("xcache.var_maxttl", "0", PHP_INI_SYSTEM, xc_OnUpdateULong, &xc_var_maxttl)
  3270. PHP_INI_ENTRY1 ("xcache.var_gc_interval", "120", PHP_INI_SYSTEM, xc_OnUpdateULong, &xc_var_gc_interval)
  3271. STD_PHP_INI_BOOLEAN("xcache.cacher", "1", PHP_INI_ALL, OnUpdateBool, cacher, zend_xcache_globals, xcache_globals)
  3272. STD_PHP_INI_BOOLEAN("xcache.stat", "1", PHP_INI_ALL, OnUpdateBool, stat, zend_xcache_globals, xcache_globals)
  3273. STD_PHP_INI_BOOLEAN("xcache.admin.enable_auth", "1", PHP_INI_SYSTEM, OnUpdateBool, auth_enabled, zend_xcache_globals, xcache_globals)
  3274. STD_PHP_INI_BOOLEAN("xcache.experimental", "0", PHP_INI_ALL, OnUpdateBool, experimental, zend_xcache_globals, xcache_globals)
  3275. #ifdef HAVE_XCACHE_OPTIMIZER
  3276. STD_PHP_INI_BOOLEAN("xcache.optimizer", "0", PHP_INI_ALL, OnUpdateBool, optimizer, zend_xcache_globals, xcache_globals)
  3277. #endif
  3278. STD_PHP_INI_ENTRY ("xcache.var_ttl", "0", PHP_INI_ALL, OnUpdateLong, var_ttl, zend_xcache_globals, xcache_globals)
  3279. #ifdef HAVE_XCACHE_COVERAGER
  3280. STD_PHP_INI_BOOLEAN("xcache.coverager" , "0", PHP_INI_ALL, OnUpdateBool, coverager, zend_xcache_globals, xcache_globals)
  3281. PHP_INI_ENTRY1 ("xcache.coveragedump_directory", "", PHP_INI_SYSTEM, xc_OnUpdateDummy, NULL)
  3282. #endif
  3283. PHP_INI_END()
  3284. /* }}} */
  3285. /* {{{ PHP_MINFO_FUNCTION(xcache) */
  3286. static PHP_MINFO_FUNCTION(xcache)
  3287. {
  3288. char buf[100];
  3289. char *ptr;
  3290. int left, len;
  3291. xc_shm_scheme_t *scheme;
  3292. #ifdef HAVE_XCACHE_COVERAGER
  3293. char *covdumpdir;
  3294. #endif
  3295. php_info_print_table_start();
  3296. php_info_print_table_header(2, "XCache Support", "enabled");
  3297. php_info_print_table_row(2, "Version", XCACHE_VERSION);
  3298. #ifdef XCACHE_VERSION_REVISION
  3299. php_info_print_table_row(2, "Revision", "r" XCACHE_VERSION_REVISION);
  3300. #endif
  3301. php_info_print_table_row(2, "Modules Built", XCACHE_MODULES);
  3302. php_info_print_table_row(2, "Readonly Protection", xc_readonly_protection ? "enabled" : "N/A");
  3303. #ifdef ZEND_ENGINE_2_1
  3304. ptr = php_format_date("Y-m-d H:i:s", sizeof("Y-m-d H:i:s") - 1, xc_init_time, 1 TSRMLS_CC);
  3305. php_info_print_table_row(2, "Cache Init Time", ptr);
  3306. efree(ptr);
  3307. #else
  3308. snprintf(buf, sizeof(buf), "%lu", (long unsigned) xc_init_time);
  3309. php_info_print_table_row(2, "Cache Init Time", buf);
  3310. #endif
  3311. #ifdef ZTS
  3312. snprintf(buf, sizeof(buf), "%lu.%lu", xc_init_instance_id, xc_init_instance_subid);
  3313. #else
  3314. snprintf(buf, sizeof(buf), "%lu", xc_init_instance_id);
  3315. #endif
  3316. php_info_print_table_row(2, "Cache Instance Id", buf);
  3317. if (xc_php_size) {
  3318. ptr = _php_math_number_format(xc_php_size, 0, '.', ',');
  3319. snprintf(buf, sizeof(buf), "enabled, %s bytes, %d split(s), with %d slots each", ptr, xc_php_hcache.size, xc_php_hentry.size);
  3320. php_info_print_table_row(2, "Opcode Cache", buf);
  3321. efree(ptr);
  3322. }
  3323. else {
  3324. php_info_print_table_row(2, "Opcode Cache", "disabled");
  3325. }
  3326. if (xc_var_size) {
  3327. ptr = _php_math_number_format(xc_var_size, 0, '.', ',');
  3328. snprintf(buf, sizeof(buf), "enabled, %s bytes, %d split(s), with %d slots each", ptr, xc_var_hcache.size, xc_var_hentry.size);
  3329. php_info_print_table_row(2, "Variable Cache", buf);
  3330. efree(ptr);
  3331. }
  3332. else {
  3333. php_info_print_table_row(2, "Variable Cache", "disabled");
  3334. }
  3335. left = sizeof(buf);
  3336. ptr = buf;
  3337. buf[0] = '\0';
  3338. for (scheme = xc_shm_scheme_first(); scheme; scheme = xc_shm_scheme_next(scheme)) {
  3339. len = snprintf(ptr, left, ptr == buf ? "%s" : ", %s", xc_shm_scheme_name(scheme));
  3340. left -= len;
  3341. ptr += len;
  3342. }
  3343. php_info_print_table_row(2, "Shared Memory Schemes", buf);
  3344. #ifdef HAVE_XCACHE_COVERAGER
  3345. if (cfg_get_string("xcache.coveragedump_directory", &covdumpdir) != SUCCESS || !covdumpdir[0]) {
  3346. covdumpdir = NULL;
  3347. }
  3348. php_info_print_table_row(2, "Coverage Auto Dumper", XG(coverager) && covdumpdir ? "enabled" : "disabled");
  3349. #endif
  3350. php_info_print_table_end();
  3351. DISPLAY_INI_ENTRIES();
  3352. }
  3353. /* }}} */
  3354. /* {{{ extension startup */
  3355. static void xc_zend_extension_register(zend_extension *new_extension, DL_HANDLE handle)
  3356. {
  3357. zend_extension extension;
  3358. extension = *new_extension;
  3359. extension.handle = handle;
  3360. zend_extension_dispatch_message(ZEND_EXTMSG_NEW_EXTENSION, &extension);
  3361. zend_llist_prepend_element(&zend_extensions, &extension);
  3362. TRACE("%s", "registered");
  3363. }
  3364. static zend_llist_element *xc_llist_get_element_by_zend_extension(zend_llist *l, const char *extension_name)
  3365. {
  3366. zend_llist_element *element;
  3367. for (element = zend_extensions.head; element; element = element->next) {
  3368. zend_extension *extension = (zend_extension *) element->data;
  3369. if (!strcmp(extension->name, extension_name)) {
  3370. return element;
  3371. }
  3372. }
  3373. return NULL;
  3374. }
  3375. static void xc_llist_prepend(zend_llist *l, zend_llist_element *element)
  3376. {
  3377. element->next = l->head;
  3378. element->prev = NULL;
  3379. if (l->head) {
  3380. l->head->prev = element;
  3381. }
  3382. else {
  3383. l->tail = element;
  3384. }
  3385. l->head = element;
  3386. ++l->count;
  3387. }
  3388. static void xc_llist_unlink(zend_llist *l, zend_llist_element *element)
  3389. {
  3390. if ((element)->prev) {
  3391. (element)->prev->next = (element)->next;
  3392. }
  3393. else {
  3394. (l)->head = (element)->next;
  3395. }
  3396. if ((element)->next) {
  3397. (element)->next->prev = (element)->prev;
  3398. }
  3399. else {
  3400. (l)->tail = (element)->prev;
  3401. }
  3402. --l->count;
  3403. }
  3404. static int xc_zend_extension_startup(zend_extension *extension)
  3405. {
  3406. if (extension->startup) {
  3407. if (extension->startup(extension) != SUCCESS) {
  3408. return FAILURE;
  3409. }
  3410. }
  3411. return SUCCESS;
  3412. }
  3413. /* }}} */
  3414. static int xc_ptr_compare_func(void *p1, void *p2) /* {{{ */
  3415. {
  3416. return p1 == p2;
  3417. }
  3418. /* }}} */
  3419. static int xc_zend_remove_extension(zend_extension *extension) /* {{{ */
  3420. {
  3421. llist_dtor_func_t dtor;
  3422. assert(extension);
  3423. dtor = zend_extensions.dtor; /* avoid dtor */
  3424. zend_extensions.dtor = NULL;
  3425. zend_llist_del_element(&zend_extensions, extension, xc_ptr_compare_func);
  3426. zend_extensions.dtor = dtor;
  3427. return SUCCESS;
  3428. }
  3429. /* }}} */
  3430. static int xc_config_hash(xc_hash_t *p, char *name, char *default_value) /* {{{ */
  3431. {
  3432. size_t bits, size;
  3433. char *value;
  3434. if (cfg_get_string(name, &value) != SUCCESS) {
  3435. value = default_value;
  3436. }
  3437. p->size = zend_atoi(value, strlen(value));
  3438. for (size = 1, bits = 1; size < p->size; bits ++, size <<= 1) {
  3439. /* empty body */
  3440. }
  3441. p->size = size;
  3442. p->bits = bits;
  3443. p->mask = size - 1;
  3444. return SUCCESS;
  3445. }
  3446. /* }}} */
  3447. static int xc_config_long(zend_ulong *p, char *name, char *default_value) /* {{{ */
  3448. {
  3449. char *value;
  3450. if (cfg_get_string(name, &value) != SUCCESS) {
  3451. value = default_value;
  3452. }
  3453. *p = zend_atol(value, strlen(value));
  3454. return SUCCESS;
  3455. }
  3456. /* }}} */
  3457. /* {{{ PHP_MINIT_FUNCTION(xcache) */
  3458. static PHP_MINIT_FUNCTION(xcache)
  3459. {
  3460. char *env;
  3461. zend_extension *ext;
  3462. zend_llist_position lpos;
  3463. xc_module_gotup = 1;
  3464. if (!xc_zend_extension_gotup) {
  3465. zend_error(E_WARNING, "XCache is designed to be loaded as zend_extension not extension");
  3466. xc_zend_extension_register(&zend_extension_entry, 0);
  3467. xc_zend_extension_startup(&zend_extension_entry);
  3468. xc_zend_extension_faked = 1;
  3469. }
  3470. ext = zend_get_extension("Zend Optimizer");
  3471. if (ext) {
  3472. /* zend_optimizer.optimization_level>0 is not compatible with other cacher, disabling */
  3473. ext->op_array_handler = NULL;
  3474. }
  3475. /* cache if there's an op_array_ctor */
  3476. for (ext = zend_llist_get_first_ex(&zend_extensions, &lpos);
  3477. ext;
  3478. ext = zend_llist_get_next_ex(&zend_extensions, &lpos)) {
  3479. if (ext->op_array_ctor) {
  3480. xc_have_op_array_ctor = 1;
  3481. break;
  3482. }
  3483. }
  3484. #ifndef PHP_GINIT
  3485. ZEND_INIT_MODULE_GLOBALS(xcache, xc_init_globals, xc_shutdown_globals);
  3486. #endif
  3487. REGISTER_INI_ENTRIES();
  3488. /* additional_functions requires PHP 5.3. TODO: find simpler way to do it */
  3489. #ifdef ZEND_ENGINE_2_3
  3490. if (strcmp(sapi_module.name, "cgi-fcgi") == 0 && !sapi_module.additional_functions && !getenv("XCACHE_SKIP_FCGI_WARNING") && !getenv("GATEWAY_INTERFACE")) {
  3491. if ((getenv("PHP_FCGI_CHILDREN") == NULL) || (atoi(getenv("PHP_FCGI_CHILDREN")) < 1)) {
  3492. zend_error(E_WARNING, "PHP_FCGI_CHILDREN should be >= 1 and use 1 group of parent/childs model. Set XCACHE_SKIP_FCGI_WARNING=1 to skip this warning. See " XCACHE_WIKI_URL "/Faq");
  3493. }
  3494. }
  3495. #endif
  3496. xc_config_long(&xc_php_size, "xcache.size", "0");
  3497. xc_config_hash(&xc_php_hcache, "xcache.count", "1");
  3498. xc_config_hash(&xc_php_hentry, "xcache.slots", "8K");
  3499. xc_config_long(&xc_var_size, "xcache.var_size", "0");
  3500. xc_config_hash(&xc_var_hcache, "xcache.var_count", "1");
  3501. xc_config_hash(&xc_var_hentry, "xcache.var_slots", "8K");
  3502. if (strcmp(sapi_module.name, "cli") == 0) {
  3503. if ((env = getenv("XCACHE_TEST")) != NULL) {
  3504. xc_test = atoi(env);
  3505. }
  3506. if (!xc_test) {
  3507. /* disable cache for cli except for testing */
  3508. xc_php_size = xc_var_size = 0;
  3509. }
  3510. }
  3511. if (xc_php_size <= 0) {
  3512. xc_php_size = xc_php_hcache.size = 0;
  3513. }
  3514. if (xc_var_size <= 0) {
  3515. xc_var_size = xc_var_hcache.size = 0;
  3516. }
  3517. if (xc_coredump_dir && xc_coredump_dir[0]) {
  3518. xcache_init_crash_handler();
  3519. }
  3520. xc_init_constant(module_number TSRMLS_CC);
  3521. xc_shm_init_modules();
  3522. if ((xc_php_size || xc_var_size) && xc_mmap_path && xc_mmap_path[0]) {
  3523. if (xc_init(module_number TSRMLS_CC) != SUCCESS) {
  3524. zend_error(E_ERROR, "XCache: Cannot init");
  3525. goto err_init;
  3526. }
  3527. xc_initized = 1;
  3528. xc_init_time = time(NULL);
  3529. #ifdef PHP_WIN32
  3530. xc_init_instance_id = GetCurrentProcessId();
  3531. #else
  3532. xc_init_instance_id = getpid();
  3533. #endif
  3534. #ifdef ZTS
  3535. xc_init_instance_subid = tsrm_thread_id();
  3536. #endif
  3537. }
  3538. xc_util_init(module_number TSRMLS_CC);
  3539. #ifdef HAVE_XCACHE_COVERAGER
  3540. xc_coverager_init(module_number TSRMLS_CC);
  3541. #endif
  3542. return SUCCESS;
  3543. err_init:
  3544. return FAILURE;
  3545. }
  3546. /* }}} */
  3547. /* {{{ PHP_MSHUTDOWN_FUNCTION(xcache) */
  3548. static PHP_MSHUTDOWN_FUNCTION(xcache)
  3549. {
  3550. #ifdef HAVE_XCACHE_COVERAGER
  3551. xc_coverager_destroy();
  3552. #endif
  3553. xc_util_destroy();
  3554. if (xc_initized) {
  3555. xc_destroy();
  3556. }
  3557. if (xc_mmap_path) {
  3558. pefree(xc_mmap_path, 1);
  3559. xc_mmap_path = NULL;
  3560. }
  3561. if (xc_shm_scheme) {
  3562. pefree(xc_shm_scheme, 1);
  3563. xc_shm_scheme = NULL;
  3564. }
  3565. if (xc_coredump_dir && xc_coredump_dir[0]) {
  3566. xcache_restore_crash_handler();
  3567. }
  3568. if (xc_coredump_dir) {
  3569. pefree(xc_coredump_dir, 1);
  3570. xc_coredump_dir = NULL;
  3571. }
  3572. #ifndef PHP_GINIT
  3573. # ifdef ZTS
  3574. ts_free_id(xcache_globals_id);
  3575. # else
  3576. xc_shutdown_globals(&xcache_globals TSRMLS_CC);
  3577. # endif
  3578. #endif
  3579. if (xc_zend_extension_faked) {
  3580. zend_extension *ext = zend_get_extension(XCACHE_NAME);
  3581. if (ext) {
  3582. if (ext->shutdown) {
  3583. ext->shutdown(ext);
  3584. }
  3585. xc_zend_remove_extension(ext);
  3586. }
  3587. }
  3588. UNREGISTER_INI_ENTRIES();
  3589. xc_module_gotup = 0;
  3590. xc_zend_extension_gotup = 0;
  3591. xc_zend_extension_faked = 0;
  3592. return SUCCESS;
  3593. }
  3594. /* }}} */
  3595. /* {{{ PHP_RINIT_FUNCTION(xcache) */
  3596. static PHP_RINIT_FUNCTION(xcache)
  3597. {
  3598. xc_request_init(TSRMLS_C);
  3599. return SUCCESS;
  3600. }
  3601. /* }}} */
  3602. /* {{{ PHP_RSHUTDOWN_FUNCTION(xcache) */
  3603. #ifndef ZEND_ENGINE_2
  3604. static PHP_RSHUTDOWN_FUNCTION(xcache)
  3605. #else
  3606. static ZEND_MODULE_POST_ZEND_DEACTIVATE_D(xcache)
  3607. #endif
  3608. {
  3609. #ifdef ZEND_ENGINE_2
  3610. TSRMLS_FETCH();
  3611. #endif
  3612. xc_request_shutdown(TSRMLS_C);
  3613. return SUCCESS;
  3614. }
  3615. /* }}} */
  3616. /* {{{ module dependencies */
  3617. #if ZEND_MODULE_API_NO >= 20050922
  3618. static zend_module_dep xcache_module_deps[] = {
  3619. ZEND_MOD_REQUIRED("standard")
  3620. ZEND_MOD_CONFLICTS("apc")
  3621. ZEND_MOD_CONFLICTS("eAccelerator")
  3622. ZEND_MOD_CONFLICTS("Turck MMCache")
  3623. {NULL, NULL, NULL}
  3624. };
  3625. #endif
  3626. /* }}} */
  3627. /* {{{ module definition structure */
  3628. zend_module_entry xcache_module_entry = {
  3629. #if ZEND_MODULE_API_NO >= 20050922
  3630. STANDARD_MODULE_HEADER_EX,
  3631. NULL,
  3632. xcache_module_deps,
  3633. #else
  3634. STANDARD_MODULE_HEADER,
  3635. #endif
  3636. XCACHE_NAME,
  3637. xcache_functions,
  3638. PHP_MINIT(xcache),
  3639. PHP_MSHUTDOWN(xcache),
  3640. PHP_RINIT(xcache),
  3641. #ifndef ZEND_ENGINE_2
  3642. PHP_RSHUTDOWN(xcache),
  3643. #else
  3644. NULL,
  3645. #endif
  3646. PHP_MINFO(xcache),
  3647. XCACHE_VERSION,
  3648. #ifdef PHP_GINIT
  3649. PHP_MODULE_GLOBALS(xcache),
  3650. PHP_GINIT(xcache),
  3651. PHP_GSHUTDOWN(xcache),
  3652. #endif
  3653. #ifdef ZEND_ENGINE_2
  3654. ZEND_MODULE_POST_ZEND_DEACTIVATE_N(xcache),
  3655. #else
  3656. NULL,
  3657. NULL,
  3658. #endif
  3659. STANDARD_MODULE_PROPERTIES_EX
  3660. };
  3661. #ifdef COMPILE_DL_XCACHE
  3662. ZEND_GET_MODULE(xcache)
  3663. #endif
  3664. /* }}} */
  3665. static startup_func_t xc_last_ext_startup;
  3666. static int xc_zend_startup_last(zend_extension *extension) /* {{{ */
  3667. {
  3668. zend_extension *ext = zend_get_extension(XCACHE_NAME);
  3669. if (ext) {
  3670. zend_error(E_WARNING, "Module '" XCACHE_NAME "' already loaded");
  3671. }
  3672. /* restore */
  3673. extension->startup = xc_last_ext_startup;
  3674. if (extension->startup) {
  3675. if (extension->startup(extension) != SUCCESS) {
  3676. return FAILURE;
  3677. }
  3678. }
  3679. assert(xc_llist_zend_extension);
  3680. xc_llist_prepend(&zend_extensions, xc_llist_zend_extension);
  3681. if (!xc_module_gotup) {
  3682. return zend_startup_module(&xcache_module_entry);
  3683. }
  3684. return SUCCESS;
  3685. }
  3686. /* }}} */
  3687. ZEND_DLEXPORT int xcache_zend_startup(zend_extension *extension) /* {{{ */
  3688. {
  3689. xc_zend_extension_gotup = 1;
  3690. if (!origin_compile_file) {
  3691. origin_compile_file = zend_compile_file;
  3692. zend_compile_file = xc_check_initial_compile_file;
  3693. }
  3694. if (zend_llist_count(&zend_extensions) > 1) {
  3695. zend_llist_position lpos;
  3696. zend_extension *ext;
  3697. xc_llist_zend_extension = xc_llist_get_element_by_zend_extension(&zend_extensions, XCACHE_NAME);
  3698. if (xc_llist_zend_extension != zend_extensions.head) {
  3699. zend_error(E_WARNING, "XCache must be loaded as the first zend_extension for maximum compatibility");
  3700. }
  3701. /* hide myself */
  3702. xc_llist_unlink(&zend_extensions, xc_llist_zend_extension);
  3703. ext = (zend_extension *) zend_llist_get_last_ex(&zend_extensions, &lpos);
  3704. assert(ext && ext != (zend_extension *) xc_llist_zend_extension->data);
  3705. xc_last_ext_startup = ext->startup;
  3706. ext->startup = xc_zend_startup_last;
  3707. }
  3708. else if (!xc_module_gotup) {
  3709. return zend_startup_module(&xcache_module_entry);
  3710. }
  3711. return SUCCESS;
  3712. }
  3713. /* }}} */
  3714. ZEND_DLEXPORT void xcache_zend_shutdown(zend_extension *extension) /* {{{ */
  3715. {
  3716. /* empty */
  3717. }
  3718. /* }}} */
  3719. ZEND_DLEXPORT void xcache_statement_handler(zend_op_array *op_array) /* {{{ */
  3720. {
  3721. #ifdef HAVE_XCACHE_COVERAGER
  3722. xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_STMT);
  3723. #endif
  3724. }
  3725. /* }}} */
  3726. ZEND_DLEXPORT void xcache_fcall_begin_handler(zend_op_array *op_array) /* {{{ */
  3727. {
  3728. #if 0
  3729. xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_BEGIN);
  3730. #endif
  3731. }
  3732. /* }}} */
  3733. ZEND_DLEXPORT void xcache_fcall_end_handler(zend_op_array *op_array) /* {{{ */
  3734. {
  3735. #if 0
  3736. xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_END);
  3737. #endif
  3738. }
  3739. /* }}} */
  3740. /* {{{ zend extension definition structure */
  3741. ZEND_DLEXPORT zend_extension zend_extension_entry = {
  3742. XCACHE_NAME,
  3743. XCACHE_VERSION,
  3744. XCACHE_AUTHOR,
  3745. XCACHE_URL,
  3746. XCACHE_COPYRIGHT,
  3747. xcache_zend_startup,
  3748. xcache_zend_shutdown,
  3749. NULL, /* activate_func_t */
  3750. NULL, /* deactivate_func_t */
  3751. NULL, /* message_handler_func_t */
  3752. #ifdef HAVE_XCACHE_OPTIMIZER
  3753. xc_optimizer_op_array_handler,
  3754. #else
  3755. NULL, /* op_array_handler_func_t */
  3756. #endif
  3757. xcache_statement_handler,
  3758. xcache_fcall_begin_handler,
  3759. xcache_fcall_end_handler,
  3760. NULL, /* op_array_ctor_func_t */
  3761. NULL, /* op_array_dtor_func_t */
  3762. STANDARD_ZEND_EXTENSION_PROPERTIES
  3763. };
  3764. #ifndef ZEND_EXT_API
  3765. # define ZEND_EXT_API ZEND_DLEXPORT
  3766. #endif
  3767. #if COMPILE_DL_XCACHE
  3768. ZEND_EXTENSION();
  3769. #endif
  3770. /* }}} */