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