1<?php
2// +----------------------------------------------------------------------+
3// | PEAR :: Cache                                                        |
4// +----------------------------------------------------------------------+
5// | Copyright (c) 1997-2003 The PHP Group                                |
6// +----------------------------------------------------------------------+
7// | This source file is subject to version 2.0 of the PHP license,       |
8// | that is bundled with this package in the file LICENSE, and is        |
9// | available at through the world-wide-web at                           |
10// | http://www.php.net/license/2_02.txt.                                 |
11// | If you did not receive a copy of the PHP license and are unable to   |
12// | obtain it through the world-wide-web, please send a note to          |
13// | license@php.net so we can mail you a copy immediately.               |
14// +----------------------------------------------------------------------+
15// | Authors: Ulf Wendel <ulf.wendel@phpdoc.de>                           |
16// +----------------------------------------------------------------------+
17//
18// $Id: msession.php 178289 2005-01-26 09:47:28Z dufuz $
19
20require_once 'Cache/Container.php';
21
22/**
23* Stores cache contents in msessions.
24*
25* WARNING: experimental, untested
26*
27* @author   Ulf Wendel  <ulf.wendel@phpdoc.de>
28* @version  $Id: msession.php 178289 2005-01-26 09:47:28Z dufuz $
29*/
30class Cache_Container_msession extends Cache_Container
31{
32    /**
33    * Length of the Cache-Identifier
34    *
35    * Note that the PEAR-Cache prefixes the ID with an md5() value
36    * of the cache-group. A good value for the id_length
37    * depends on the maximum number of entries per cache group.
38    *
39    * @var  int
40    */
41    var $id_length = 32;
42
43
44    /**
45    * Use msession_uniq to create a unique SID.
46    *
47    * @var  boolean
48    */
49    var $uniq = true;
50
51
52    /**
53    * Establish a connection to a msession server?
54    *
55    * @var  boolean
56    */
57    var $connect = true;
58
59
60    /**
61    * msession host
62    *
63    * @var  string
64    */
65    var $host = null;
66
67
68    /**
69    * msession port
70    *
71    * @var  string
72    */
73    var $port = null;
74
75
76    /**
77    * mesession server connection
78    *
79    * @var  resource msession
80    */
81    var $ms = null;
82
83
84    function Cache_Container_msession($options = '')
85    {
86        if (is_array($options)) {
87            $this->setOptions($options, array_merge($this->allowed_options, array('id_length', 'uniq', 'host', 'port', 'connect')));
88        }
89        if ($connect) {
90            if ($this->host == null) {
91                new Cache_Error('No host specified.', __FILE__, __LINE__);
92            }
93            if ($this->port == null) {
94                new Cache_Error('No port specified.', __FILE__, __LINE__);
95            }
96            if (!($this->ms = msession_connect($this->host, $this->port))) {
97                new Cache_Error('Can not connect to the sever using host "' . $this->host . '" on port "' . $this->port . '"', __FILE__, __LINE__);
98            }
99        }
100
101    } // end func contructor
102
103    function fetch($id, $group)
104    {
105        $id = strtoupper(md5($group)) . $id;
106        $group = msession_get($id, '_pear_cache_data', null);
107
108        if ($data == null) {
109            return array(null, null, null);
110        }
111        return array($data['expires'], $data['cachedata'], $data['userdata']);
112    } // end func fetch
113
114    /**
115    * Stores a dataset.
116    *
117    * WARNING: If you supply userdata it must not contain any linebreaks,
118    * otherwise it will break the filestructure.
119    */
120    function save($id, $cachedata, $expires, $group, $userdata)
121    {
122        $this->flushPreload($id, $group);
123
124        $cachedata      = $this->encode($cachedata);
125        $expires_abs    = $this->getExpiresAbsolute($expires);
126
127        $size = 1 + strlen($cachedata) + strlen($expires_abs) + strlen($userdata) + strlen($group);
128        $size += strlen($size);
129
130        $data = array(
131                    'cachedata' => $cachedata,
132                    'expires'   => $expires_abs,
133                    'userdata'  => $userdata
134                  );
135        $id = strtoupper(md5($group)) . $id;
136
137        msession_lock($id);
138
139        if (!msession_set($id, '_pear_cache', true)) {
140            msession_unlock($id);
141            return new Cache_Error("Can't write cache data.", __FILE__, __LINE__);
142        }
143
144        if (!msession_set($id, '_pear_cache_data', $data)) {
145            msession_unlock($id);
146            return new Cache_Error("Can't write cache data.", __FILE__, __LINE__);
147        }
148
149        if (!msession_set($id, '_pear_cache_group', $group)) {
150            msession_unlock($id);
151            return new Cache_Error("Can't write cache data.", __FILE__, __LINE__);
152        }
153
154        if (!msession_set($id, '_pear_cache_size', $size)) {
155            msession_unlock($id);
156            return new Cache_Error("Can't write cache data.", __FILE__, __LINE__);
157        }
158
159        // let msession do some GC as well
160        // note that msession works different from the PEAR Cache.
161        // msession deletes an entry if it has not been used for n-seconds.
162        // PEAR Cache deletes after n-seconds.
163        if ($expires != 0) {
164            msession_timeout($id, $expires);
165        }
166        msession_unlock($id);
167
168        return true;
169    } // end func save
170
171    function remove($id, $group)
172    {
173        $this->flushPreload($id, $group);
174        return msession_destroy(strtoupper(md5($group)) . $id);
175    } // end func remove
176
177    function flush($group)
178    {
179        $this->flushPreload();
180
181        $sessions = msession_find('_pear_cache_group', $group);
182        if (empty($sessions)) {
183            return 0;
184        }
185
186        foreach ($sessions as $k => $id)
187            messsion_destroy($id);
188
189        return count($sessions);
190    } // end func flush
191
192    function idExists($id, $group)
193    {
194        return (msession_get(strtoupper(md5($group)) . $id, '_pear_cache_group', null) == null) ? false : true;
195    } // end func idExists
196
197    /**
198    * Deletes all expired files.
199    *
200    * Note: garbage collection should cause lot's of network traffic.
201    *
202    * @param    integer Maximum lifetime in seconds of an no longer used/touched entry
203    * @throws   Cache_Error
204    */
205    function garbageCollection($maxlifetime)
206    {
207        $this->flushPreload();
208
209        $sessions = msession_find('_pear_cache', true);
210        if (empty($sessions))
211            return true;
212
213        $total = 0;
214        $entries = array();
215
216        foreach ($sessions as $k => $id) {
217            $data = msession_get($id, '_pear_cache_data', null);
218            if (null == $data) {
219                continue;
220            }
221
222            if ($data['expires'] <= time()) {
223                msession_destroy($id);
224                continue;
225            }
226
227            $size = msession_get($id, '_pear_cache_size', null);
228            $total += $size;
229            $entries[$data['expires']] = array($id, $size);
230        }
231
232        if ($total > $this->highwater) {
233
234            krsort($entries);
235            reset($entries);
236
237            while ($total > $this->lowwater && list($expires, $entry) = each($entries)) {
238                msession_destroy($entry[0]);
239                $total -= $entry[1];
240            }
241
242        }
243
244        return true;
245    } // end func garbageCollection
246
247} // end class file
248?>