1<?php
2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
4/**
5 * DNS Library for handling lookups and updates.
6 *
7 * PHP Version 5
8 *
9 * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>.
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 *
16 *   * Redistributions of source code must retain the above copyright
17 *     notice, this list of conditions and the following disclaimer.
18 *
19 *   * Redistributions in binary form must reproduce the above copyright
20 *     notice, this list of conditions and the following disclaimer in
21 *     the documentation and/or other materials provided with the
22 *     distribution.
23 *
24 *   * Neither the name of Mike Pultz nor the names of his contributors
25 *     may be used to endorse or promote products derived from this
26 *     software without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
31 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
32 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
33 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
34 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
35 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
36 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
38 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
39 * POSSIBILITY OF SUCH DAMAGE.
40 *
41 * @category  Networking
42 * @package   Net_DNS2
43 * @author    Mike Pultz <mike@mikepultz.com>
44 * @copyright 2010 Mike Pultz <mike@mikepultz.com>
45 * @license   http://www.opensource.org/licenses/bsd-license.php  BSD License
46 * @version   SVN: $Id: Shm.php 160 2012-07-18 03:57:32Z mike.pultz $
47 * @link      http://pear.php.net/package/Net_DNS2
48 * @since     File available since Release 1.1.0
49 *
50 */
51
52/**
53 * Shared Memory-based caching for the Net_DNS2_Cache class
54 *
55 * @category Networking
56 * @package  Net_DNS2
57 * @author   Mike Pultz <mike@mikepultz.com>
58 * @license  http://www.opensource.org/licenses/bsd-license.php  BSD License
59 * @link     http://pear.php.net/package/Net_DNS2
60 * @see      Net_DNS2_Packet
61 *
62 */
63class Net_DNS2_Cache_Shm extends Net_DNS2_Cache
64{
65    /*
66     * resource id of the shared memory cache
67     */
68    private $_cache_id = false;
69
70    /*
71     * the IPC key
72     */
73    private $_cache_file_tok = -1;
74
75    /**
76     * open a cache object
77     *
78     * @param string  $cache_file path to a file to use for cache storage
79     * @param integer $size       the size of the shared memory segment to create
80     * @param string  $serializer the name of the cache serialize to use
81     *
82     * @throws Net_DNS2_Exception
83     * @access public
84     * @return void
85     *
86     */
87    public function open($cache_file, $size, $serializer)
88    {
89        $this->cache_size       = $size;
90        $this->cache_file       = $cache_file;
91        $this->cache_serializer = $serializer;
92
93        //
94        // make sure the file exists first
95        //
96        if (!file_exists($cache_file)) {
97
98            if (file_put_contents($cache_file, '') === false) {
99
100                throw new Net_DNS2_Exception(
101                    'failed to create empty SHM file: ' . $cache_file,
102                    Net_DNS2_Lookups::E_CACHE_SHM_FILE
103                );
104            }
105        }
106
107        //
108        // convert the filename to a IPC key
109        //
110        $this->_cache_file_tok = ftok($cache_file, 't');
111        if ($this->_cache_file_tok == -1) {
112
113            throw new Net_DNS2_Exception(
114                'failed on ftok() file: ' . $this->_cache_file_tok,
115                Net_DNS2_Lookups::E_CACHE_SHM_FILE
116            );
117        }
118
119        //
120        // try to open an existing cache; if it doesn't exist, then there's no
121        // cache, and nothing to do.
122        //
123        $this->_cache_id = @shmop_open($this->_cache_file_tok, 'w', 0, 0);
124        if ($this->_cache_id !== false) {
125
126            //
127            // this returns the size allocated, and not the size used, but it's
128            // still a good check to make sure there's space allocated.
129            //
130            $allocated = shmop_size($this->_cache_id);
131            if ($allocated > 0) {
132
133                //
134                // read the data from the shared memory segment
135                //
136                $data = trim(shmop_read($this->_cache_id, 0, $allocated));
137                if ( ($data !== false) && (strlen($data) > 0) ) {
138
139                    //
140                    // unserialize and store the data
141                    //
142                    $decoded = null;
143
144                    if ($this->cache_serializer == 'json') {
145
146                        $decoded = json_decode($data, true);
147                    } else {
148
149                        $decoded = unserialize($data);
150                    }
151
152                    if (is_array($decoded) == true) {
153
154                        $this->cache_data = $decoded;
155                    } else {
156
157                        $this->cache_data = array();
158                    }
159
160                    //
161                    // call clean to clean up old entries
162                    //
163                    $this->clean();
164                }
165            }
166        }
167    }
168
169    /**
170     * Destructor
171     *
172     * @access public
173     *
174     */
175    public function __destruct()
176    {
177        //
178        // if there's no cache file set, then there's nothing to do
179        //
180        if (strlen($this->cache_file) == 0) {
181            return;
182        }
183
184        $fp = fopen($this->cache_file, 'r');
185        if ($fp !== false) {
186
187            //
188            // lock the file
189            //
190            flock($fp, LOCK_EX);
191
192            //
193            // check to see if we have an open shm segment
194            //
195            if ($this->_cache_id === false) {
196
197                //
198                // try opening it again, incase it was created by another
199                // process in the mean time
200                //
201                $this->_cache_id = @shmop_open(
202                    $this->_cache_file_tok, 'w', 0, 0
203                );
204                if ($this->_cache_id === false) {
205
206                    //
207                    // otherwise, create it.
208                    //
209                    $this->_cache_id = @shmop_open(
210                        $this->_cache_file_tok, 'c', 0, $this->cache_size
211                    );
212                }
213            }
214
215            //
216            // get the size allocated to the segment
217            //
218            $allocated = shmop_size($this->_cache_id);
219
220            //
221            // read the contents
222            //
223            $data = trim(shmop_read($this->_cache_id, 0, $allocated));
224
225            //
226            // if there was some data
227            //
228            if ( ($data !== false) && (strlen($data) > 0) ) {
229
230                //
231                // unserialize and store the data
232                //
233                $c = $this->cache_data;
234
235                $decoded = null;
236
237                if ($this->cache_serializer == 'json') {
238
239                    $decoded = json_decode($data, true);
240                } else {
241
242                    $decoded = unserialize($data);
243                }
244
245                if (is_array($decoded) == true) {
246
247                    $this->cache_data = array_merge($c, $decoded);
248                }
249            }
250
251            //
252            // delete the segment
253            //
254            shmop_delete($this->_cache_id);
255
256            //
257            // clean the data
258            //
259            $this->clean();
260
261            //
262            // clean up and write the data
263            //
264            $data = $this->resize();
265            if (!is_null($data)) {
266
267                //
268                // re-create segment
269                //
270                $this->_cache_id = @shmop_open(
271                    $this->_cache_file_tok, 'c', 0644, $this->cache_size
272                );
273                if ($this->_cache_id === false) {
274                    return;
275                }
276
277                $o = shmop_write($this->_cache_id, $data, 0);
278            }
279
280            //
281            // close the segment
282            //
283            shmop_close($this->_cache_id);
284
285            //
286            // unlock
287            //
288            flock($fp, LOCK_UN);
289
290            //
291            // close the file
292            //
293            fclose($fp);
294        }
295    }
296};
297
298/*
299 * Local variables:
300 * tab-width: 4
301 * c-basic-offset: 4
302 * c-hanging-comment-ender-p: nil
303 * End:
304 */
305?>
306