1<?php 2/** 3 * EGroupware - eTemplate request object storing the data in the session 4 * 5 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License 6 * @package api 7 * @subpackage etemplate 8 * @link http://www.egroupware.org 9 * @author Ralf Becker <RalfBecker@outdoor-training.de> 10 * @copyright (c) 2007-16 by Ralf Becker <RalfBecker@outdoor-training.de> 11 * @version $Id$ 12 */ 13 14namespace EGroupware\Api\Etemplate\Request; 15 16use EGroupware\Api\Etemplate; 17use EGroupware\Api; 18 19/** 20 * Class to represent the persitent information stored on the server for each eTemplate request 21 * 22 * The information is stored in the users session, which causes the session to constantly grow. 23 * We implement here some garbadge collection to remove old requests. 24 * 25 * The request object should be instancated only via the factory method Api\Etemplate\Request::read($id=null) 26 * 27 * $request = Api\Etemplate\Request::read(); 28 * 29 * // add request data 30 * 31 * $id = $request->id(); 32 * 33 * b) open or modify an existing request: 34 * 35 * if (!($request = Api\Etemplate\Request::read($id))) 36 * { 37 * // request not found 38 * } 39 * 40 * Ajax requests can use this object to open the original request by using the id, they have to transmitt back, 41 * and register further variables, modify the registered ones or delete them AND then update the id, if it changed: 42 * 43 * if (($new_id = $request->id()) != $id) 44 * { 45 * $response->addAssign('etemplate_exec_id','value',$new_id); 46 * } 47 * 48 * For an example look in link_widget::ajax_search() 49 */ 50class Session extends Etemplate\Request 51{ 52 /** 53 * request id 54 * 55 * @var string 56 */ 57 protected $id; 58 59 /** 60 * Private constructor to force the instancation of this class only via it's static factory method read 61 * 62 * @param string|null $id =null 63 */ 64 private function __construct($id=null) 65 { 66 if (!$id) $id = self::request_id(); 67 68 $this->id = $id; 69 70 // hack to quiten IDE Warning for not calling parent::__construct, which we can not! 71 if (false) parent::__construct(); 72 } 73 74 /** 75 * return the id of this request 76 * 77 * @return string 78 */ 79 public function &id() 80 { 81 //error_log(__METHOD__."() id=$this->id"); 82 return $this->id; 83 } 84 85 /** 86 * Factory method to get a new request object or the one for an existing request 87 * 88 * @param string $id =null 89 * @return etemplate_request|boolean the object or false if $id is not found 90 */ 91 static function read($id=null) 92 { 93 $request = new Session($id); 94 95 if (!is_null($id)) 96 { 97 if (!($data = Api\Cache::getSession('etemplate', $id))) 98 { 99 return false; // request not found 100 } 101 $request->data = $data; 102 } 103 //error_log(__METHOD__."(id=$id"); 104 return $request; 105 } 106 107 /** 108 * saves content,readonlys,template-keys, ... via eGW's appsession function 109 * 110 * As a user may open several windows with the same content/template wie generate a location-id from microtime 111 * which is used as location for request to descriminate between the different windows. This location-id 112 * is then saved as a hidden-var in the form. The above mentions session-id has nothing to do / is different 113 * from the session-id which is constant for all windows opened in one session. 114 */ 115 function __destruct() 116 { 117 if ($this->remove_if_not_modified && !$this->data_modified) 118 { 119 //error_log(__METHOD__."() destroying $this->id"); 120 Api\Cache::unsetSession('etemplate', $this->id); 121 } 122 elseif (!$this->destroyed && $this->data_modified) 123 { 124 $this->cleanup(); 125 126 Api\Cache::setSession('etemplate', $this->id, $this->data); 127 } 128 if (!$this->garbage_collection_done) 129 { 130 $this->_php4_request_garbage_collection(); 131 } 132 } 133 134 /** 135 * a little bit of garbage collection for php4 sessions (their size is limited by memory_limit) 136 * 137 * With constant eTemplate use it can grow quite big and lead to unusable sessions (php terminates 138 * before any output with "Allowed memory size of ... exhausted"). 139 * We delete now sessions once used after 10min and sessions never or multiple used after 60min. 140 */ 141 protected function _php4_request_garbage_collection() 142 { 143 // now we are on php4 sessions and do a bit of garbage collection 144 $appsessions =& $_SESSION[Api\Session::EGW_APPSESSION_VAR]['etemplate']; 145 $session_used =& $appsessions['session_used']; 146 147 if ($this->id) 148 { 149 //echo "session_used[$id_used]='".$session_used[$id_used]."'<br/>\n"; 150 ++$session_used[$this->id]; // count the number of times a session got used 151 } 152 $this->garbage_collection_done = true; 153 154 if (count($appsessions) < 20) return; // we dont need to care 155 156 $now = (int) (100 * microtime(true)); // gives precision of 1/100 sec 157 158 foreach(array_keys($appsessions) as $id) 159 { 160 list(,$time) = explode(':',$id); 161 162 if (!$time) continue; // other data, no session 163 164 //echo ++$n.') '.$id.': '.(($now-$time)/100.0)."secs old, used=".$session_used[$id].", size=".strlen($appsessions[$id])."<br>\n"; 165 166 if ($session_used[$id] == 1 && $time < $now - 10*6000 || // session used and older then 10min 167 $time < $now - 30*6000) // session not used and older then 30min 168 { 169 //echo "<p>boetemplate::php4_session_garbage_collection('$id_used'): unsetting session '$id' (now=$now)</p>\n"; 170 unset($appsessions[$id]); 171 unset($session_used[$id]); 172 } 173 } 174 } 175}