memory.lib.php 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. <?php
  2. /* Copyright (C) 2009-2010 Laurent Destailleur <eldy@users.sourceforge.net>
  3. * Copyright (C) 2021 Frédéric France <frederic.france@netlogic.fr>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. * or see https://www.gnu.org/
  18. */
  19. /**
  20. * \file htdocs/core/lib/memory.lib.php
  21. * \brief Set of function for memory/cache management
  22. */
  23. global $shmkeys, $shmoffset;
  24. $shmkeys = array(
  25. 'main' => 1,
  26. 'admin' => 2,
  27. 'dict' => 3,
  28. 'companies' => 4,
  29. 'suppliers' => 5,
  30. 'products' => 6,
  31. 'commercial' => 7,
  32. 'compta' => 8,
  33. 'projects' => 9,
  34. 'cashdesk' => 10,
  35. 'agenda' => 11,
  36. 'bills' => 12,
  37. 'propal' => 13,
  38. 'boxes' => 14,
  39. 'banks' => 15,
  40. 'other' => 16,
  41. 'errors' => 17,
  42. 'members' => 18,
  43. 'ecm' => 19,
  44. 'orders' => 20,
  45. 'users' => 21,
  46. 'help' => 22,
  47. 'stocks' => 23,
  48. 'interventions' => 24,
  49. 'donations' => 25,
  50. 'contracts' => 26,
  51. );
  52. $shmoffset = 1000; // Max number of entries found into a language file. If too low, some entries will be overwritten.
  53. /**
  54. * Save data into a memory area shared by all users, all sessions on server. Note: MAIN_CACHE_COUNT must be set.
  55. *
  56. * @param string $memoryid Memory id of shared area
  57. * @param mixed $data Data to save. It must not be a null value.
  58. * @param int $expire ttl in seconds, 0 never expire
  59. * @return int <0 if KO, 0 if nothing is done, Nb of bytes written if OK
  60. * @see dol_getcache()
  61. */
  62. function dol_setcache($memoryid, $data, $expire = 0)
  63. {
  64. global $conf;
  65. $result = 0;
  66. if (strpos($memoryid, 'count_') === 0) { // The memoryid key start with 'count_...'
  67. if (empty($conf->global->MAIN_CACHE_COUNT)) {
  68. return 0;
  69. }
  70. }
  71. if (isModEnabled('memcached') && class_exists('Memcached')) {
  72. // Using a memcached server
  73. global $dolmemcache;
  74. if (empty($dolmemcache) || !is_object($dolmemcache)) {
  75. $dolmemcache = new Memcached();
  76. $tmparray = explode(':', $conf->global->MEMCACHED_SERVER);
  77. $result = $dolmemcache->addServer($tmparray[0], $tmparray[1] ? $tmparray[1] : 11211);
  78. if (!$result) {
  79. return -1;
  80. }
  81. }
  82. $memoryid = session_name().'_'.$memoryid;
  83. //$dolmemcache->setOption(Memcached::OPT_COMPRESSION, false);
  84. $dolmemcache->add($memoryid, $data, $expire); // This fails if key already exists
  85. $rescode = $dolmemcache->getResultCode();
  86. if ($rescode == 0) {
  87. return is_array($data) ? count($data) : (is_scalar($data) ? strlen($data) : 0);
  88. } else {
  89. return -$rescode;
  90. }
  91. } elseif (isModEnabled('memcached') && class_exists('Memcache')) { // This is a really not reliable cache ! Use Memcached instead.
  92. // Using a memcache server
  93. global $dolmemcache;
  94. if (empty($dolmemcache) || !is_object($dolmemcache)) {
  95. $dolmemcache = new Memcache();
  96. $tmparray = explode(':', $conf->global->MEMCACHED_SERVER);
  97. $result = $dolmemcache->addServer($tmparray[0], $tmparray[1] ? $tmparray[1] : 11211);
  98. if (!$result) {
  99. return -1;
  100. }
  101. }
  102. $memoryid = session_name().'_'.$memoryid;
  103. //$dolmemcache->setOption(Memcached::OPT_COMPRESSION, false);
  104. $result = $dolmemcache->add($memoryid, $data, false, $expire); // This fails if key already exists
  105. if ($result) {
  106. return is_array($data) ? count($data) : (is_scalar($data) ? strlen($data) : 0);
  107. } else {
  108. return -1;
  109. }
  110. } elseif (isset($conf->global->MAIN_OPTIMIZE_SPEED) && ($conf->global->MAIN_OPTIMIZE_SPEED & 0x02)) { // This is a really not reliable cache ! Use Memcached instead.
  111. // Using shmop
  112. $result = dol_setshmop($memoryid, $data, $expire);
  113. } else {
  114. // No intersession cache system available, we use at least the perpage cache
  115. $conf->cache['cachememory_'.$memoryid] = $data;
  116. $result = is_array($data) ? count($data) : (is_scalar($data) ? strlen($data) : 0);
  117. }
  118. return $result;
  119. }
  120. /**
  121. * Read a memory area shared by all users, all sessions on server
  122. *
  123. * @param string $memoryid Memory id of shared area
  124. * @return int|mixed <0 if KO, data if OK, null if not found into cache or no caching feature enabled
  125. * @see dol_setcache()
  126. */
  127. function dol_getcache($memoryid)
  128. {
  129. global $conf;
  130. if (strpos($memoryid, 'count_') === 0) { // The memoryid key start with 'count_...'
  131. if (empty($conf->global->MAIN_CACHE_COUNT)) {
  132. return null;
  133. }
  134. }
  135. // Using a memcached server
  136. if (isModEnabled('memcached') && class_exists('Memcached')) {
  137. global $m;
  138. if (empty($m) || !is_object($m)) {
  139. $m = new Memcached();
  140. $tmparray = explode(':', $conf->global->MEMCACHED_SERVER);
  141. $result = $m->addServer($tmparray[0], $tmparray[1] ? $tmparray[1] : 11211);
  142. if (!$result) {
  143. return -1;
  144. }
  145. }
  146. $memoryid = session_name().'_'.$memoryid;
  147. //$m->setOption(Memcached::OPT_COMPRESSION, false);
  148. //print "Get memoryid=".$memoryid;
  149. $data = $m->get($memoryid);
  150. $rescode = $m->getResultCode();
  151. //print "memoryid=".$memoryid." - rescode=".$rescode." - count(response)=".count($data)."\n<br>";
  152. //var_dump($data);
  153. if ($rescode == 0) {
  154. return $data;
  155. } elseif ($rescode == 16) { // = Memcached::MEMCACHED_NOTFOUND but this constant doe snot exists.
  156. return null;
  157. } else {
  158. return -$rescode;
  159. }
  160. } elseif (isModEnabled('memcached') && class_exists('Memcache')) { // This is a really not reliable cache ! Use Memcached instead.
  161. global $m;
  162. if (empty($m) || !is_object($m)) {
  163. $m = new Memcache();
  164. $tmparray = explode(':', $conf->global->MEMCACHED_SERVER);
  165. $result = $m->addServer($tmparray[0], $tmparray[1] ? $tmparray[1] : 11211);
  166. if (!$result) {
  167. return -1;
  168. }
  169. }
  170. $memoryid = session_name().'_'.$memoryid;
  171. //$m->setOption(Memcached::OPT_COMPRESSION, false);
  172. $data = $m->get($memoryid);
  173. //print "memoryid=".$memoryid." - rescode=".$rescode." - data=".count($data)."\n<br>";
  174. //var_dump($data);
  175. if ($data) {
  176. return $data;
  177. } else {
  178. return null; // There is no way to make a difference between NOTFOUND and error when using Memcache. So do not use it, use Memcached instead.
  179. }
  180. } elseif (isset($conf->global->MAIN_OPTIMIZE_SPEED) && ($conf->global->MAIN_OPTIMIZE_SPEED & 0x02)) { // This is a really not reliable cache ! Use Memcached instead.
  181. // Using shmop
  182. $data = dol_getshmop($memoryid);
  183. return $data;
  184. } else {
  185. // No intersession cache system available, we use at least the perpage cache
  186. if (isset($conf->cache['cachememory_'.$memoryid])) {
  187. return $conf->cache['cachememory_'.$memoryid];
  188. }
  189. }
  190. return null;
  191. }
  192. /**
  193. * Return shared memory address used to store dataset with key memoryid
  194. *
  195. * @param string $memoryid Memory id of shared area ('main', 'agenda', ...)
  196. * @return int <0 if KO, Memoy address of shared memory for key
  197. */
  198. function dol_getshmopaddress($memoryid)
  199. {
  200. global $shmkeys, $shmoffset;
  201. if (empty($shmkeys[$memoryid])) { // No room reserved for thid memoryid, no way to use cache
  202. return 0;
  203. }
  204. return $shmkeys[$memoryid] + $shmoffset;
  205. }
  206. /**
  207. * Return list of contents of all memory area shared
  208. *
  209. * @return array
  210. */
  211. function dol_listshmop()
  212. {
  213. global $shmkeys, $shmoffset;
  214. $resarray = array();
  215. foreach ($shmkeys as $key => $val) {
  216. $result = dol_getshmop($key);
  217. if (!is_numeric($result) || $result > 0) {
  218. $resarray[$key] = $result;
  219. }
  220. }
  221. return $resarray;
  222. }
  223. /**
  224. * Save data into a memory area shared by all users, all sessions on server
  225. *
  226. * @param int $memoryid Memory id of shared area ('main', 'agenda', ...)
  227. * @param string $data Data to save. Must be a not null value.
  228. * @param int $expire ttl in seconds, 0 never expire
  229. * @return int <0 if KO, 0=Caching not available, Nb of bytes written if OK
  230. */
  231. function dol_setshmop($memoryid, $data, $expire)
  232. {
  233. global $shmkeys, $shmoffset;
  234. //print 'dol_setshmop memoryid='.$memoryid."<br>\n";
  235. if (empty($shmkeys[$memoryid]) || !function_exists("shmop_write")) {
  236. return 0;
  237. }
  238. $shmkey = dol_getshmopaddress($memoryid);
  239. if (empty($shmkey)) {
  240. return 0; // No key reserved for this memoryid, we can't cache this memoryid
  241. }
  242. $newdata = serialize($data);
  243. $size = strlen($newdata);
  244. //print 'dol_setshmop memoryid='.$memoryid." shmkey=".$shmkey." newdata=".$size."bytes<br>\n";
  245. $handle = shmop_open($shmkey, 'c', 0644, 6 + $size);
  246. if ($handle) {
  247. $shm_bytes_written1 = shmop_write($handle, str_pad($size, 6), 0);
  248. $shm_bytes_written2 = shmop_write($handle, $newdata, 6);
  249. if (($shm_bytes_written1 + $shm_bytes_written2) != (6 + dol_strlen($newdata))) {
  250. print "Couldn't write the entire length of data\n";
  251. }
  252. shmop_close($handle);
  253. return ($shm_bytes_written1 + $shm_bytes_written2);
  254. } else {
  255. print 'Error in shmop_open for memoryid='.$memoryid.' shmkey='.$shmkey.' 6+size=6+'.$size;
  256. return -1;
  257. }
  258. }
  259. /**
  260. * Read a memory area shared by all users, all sessions on server
  261. *
  262. * @param string $memoryid Memory id of shared area ('main', 'agenda', ...)
  263. * @return int <0 if KO, data if OK, Null if no cache enabled or not found
  264. */
  265. function dol_getshmop($memoryid)
  266. {
  267. global $shmkeys, $shmoffset;
  268. $data = null;
  269. if (empty($shmkeys[$memoryid]) || !function_exists("shmop_open")) {
  270. return null;
  271. }
  272. $shmkey = dol_getshmopaddress($memoryid);
  273. if (empty($shmkey)) {
  274. return null; // No key reserved for this memoryid, we can't cache this memoryid
  275. }
  276. //print 'dol_getshmop memoryid='.$memoryid." shmkey=".$shmkey."<br>\n";
  277. $handle = @shmop_open($shmkey, 'a', 0, 0);
  278. if ($handle) {
  279. $size = trim(shmop_read($handle, 0, 6));
  280. if ($size) {
  281. $data = unserialize(shmop_read($handle, 6, $size));
  282. } else {
  283. return -1;
  284. }
  285. shmop_close($handle);
  286. } else {
  287. return null; // Can't open existing block, so we suppose it was not created, so nothing were cached yet for the memoryid
  288. }
  289. return $data;
  290. }