Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
 
3
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
4
 
5
// LICENSE AGREEMENT. If folded, press za here to unfold and read license {{{
6
 
7
/**
8
* +-----------------------------------------------------------------------------+
9
* | Copyright (c) 2004 Sérgio Gonçalves Carvalho                                |
10
* +-----------------------------------------------------------------------------+
11
* | This file is part of XML_RPC2.                                              |
12
* |                                                                             |
13
* | XML_RPC2 is free software; you can redistribute it and/or modify            |
14
* | it under the terms of the GNU Lesser General Public License as published by |
15
* | the Free Software Foundation; either version 2.1 of the License, or         |
16
* | (at your option) any later version.                                         |
17
* |                                                                             |
18
* | XML_RPC2 is distributed in the hope that it will be useful,                 |
19
* | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
20
* | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
21
* | GNU Lesser General Public License for more details.                         |
22
* |                                                                             |
23
* | You should have received a copy of the GNU Lesser General Public License    |
24
* | along with XML_RPC2; if not, write to the Free Software                     |
25
* | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                    |
26
* | 02111-1307 USA                                                              |
27
* +-----------------------------------------------------------------------------+
28
* | Author: Sérgio Carvalho <sergio.carvalho@portugalmail.com>                  |
29
* +-----------------------------------------------------------------------------+
30
*
31
* @category   XML
32
* @package    XML_RPC2
33
* @author     Fabien MARTY <fab@php.net>
34
* @copyright  2005-2006 Fabien MARTY
35
* @license    http://www.gnu.org/copyleft/lesser.html  LGPL License 2.1
36
* @version    CVS: $Id: CachedServer.php 308701 2011-02-26 01:33:54Z sergiosgc $
37
* @link       http://pear.php.net/package/XML_RPC2
38
*/
39
 
40
// }}}
41
 
42
// dependencies {{{
43
require_once('Cache/Lite.php');
44
// }}}
45
 
46
/**
47
 * XML_RPC "cached server" class.
48
 *
49
 * @category   XML
50
 * @package    XML_RPC2
51
 * @author     Fabien MARTY <fab@php.net>
52
 * @copyright  2005-2006 Fabien MARTY
53
 * @license    http://www.gnu.org/copyleft/lesser.html  LGPL License 2.1
54
 * @link       http://pear.php.net/package/XML_RPC2
55
 */
56
class XML_RPC2_CachedServer {
57
 
58
    // {{{ properties
59
 
60
    /**
61
     * cache by default
62
     *
63
     * @var boolean
64
     */
65
    private $_cacheByDefault = true;
66
 
67
    /**
68
     * Cache_Lite object
69
     *
70
     * @var object
71
     */
72
    private $_cacheObject = null;
73
 
74
    /**
75
     * XML_RPC2_Server object (if needed, dynamically built)
76
     *
77
     * @var object
78
     */
79
    private $_serverObject = null;
80
 
81
    /**
82
     * Default cache group for XML_RPC server caching
83
     *
84
     * @var string
85
     */
86
    private $_defaultCacheGroup = 'xml_rpc2_server';
87
 
88
    /**
89
     * callHandler field
90
     *
91
     * The call handler is responsible for executing the server exported methods
92
     *
93
     * @var mixed
94
     */
95
    private $_callHandler = null;
96
 
97
    /**
98
     * either a class name or an object instance
99
     *
100
     * @var mixed
101
     */
102
    private $_callTarget = '';
103
 
104
    /**
105
     * methods prefix
106
     *
107
     * @var string
108
     */
109
    private $_prefix = '';
110
 
111
    /**
112
     * XML_RPC2_Server options
113
     *
114
     * @var array
115
     */
116
    private $_options = array();
117
 
118
    /**
119
     * "cache debug" flag (for debugging the caching process)
120
     *
121
     * @var boolean
122
     */
123
    private $_cacheDebug = false;
124
 
125
    /**
126
     * encoding
127
     *
128
     * @var string
129
     */
130
    private $_encoding = 'utf-8';
131
 
132
    // }}}
133
    // {{{ setCacheOptions()
134
 
135
    /**
136
     * Set options for the caching process
137
     *
138
     * See Cache_Lite constructor for options
139
     * Specific options are 'cachedMethods', 'notCachedMethods', 'cacheByDefault', 'defaultCacheGroup'
140
     * See corresponding properties for more informations
141
     *
142
     * @param array $array
143
     */
144
    private function _setCacheOptions($array)
145
    {
146
        if (isset($array['defaultCacheGroup'])) {
147
            $this->_defaultCacheGroup = $array['defaultCacheGroup'];
148
            unset($array['defaultCacheGroup']); // this is a "non standard" option for Cache_Lite
149
        }
150
        if (isset($array['cacheByDefault'])) {
151
            $this->_cacheByDefault = $array['cacheByDefault'];
152
            unset($array['CacheByDefault']); // this is a "non standard" option for Cache_Lite
153
        }
154
        $array['automaticSerialization'] = false; // datas are already serialized in this class
155
        if (!isset($array['lifetime'])) {
156
            $array['lifetime'] = 3600; // we need a default lifetime
157
        }
158
        $this->_cacheOptions = $array;
159
        $this->_cacheObject = new Cache_Lite($this->_cacheOptions);
160
    }
161
 
162
    // }}}
163
    // {{{ constructor
164
 
165
    /**
166
     * Constructor
167
     *
168
     * @param object $callHandler the call handler will receive a method call for each remote call received.
169
     */
170
    protected function __construct($callTarget, $options = array())
171
    {
172
        if (isset($options['cacheOptions'])) {
173
            $cacheOptions = $options['cacheOptions'];
174
            $this->_setCacheOptions($cacheOptions);
175
            unset($options['cacheOptions']);
176
        }
177
        if (isset($options['cacheDebug'])) {
178
            $this->_cacheDebug = $options['cacheDebug'];
179
            unset($options['cacheDebug']); // 'cacheDebug' is not a standard option for XML/RPC2/Server
180
        }
181
        $this->_options = $options;
182
        $this->_callTarget = $callTarget;
183
        if (isset($this->_options['encoding'])) {
184
            $this->_encoding = $this->_options['encoding'];
185
        }
186
        if (isset($this->_options['prefix'])) {
187
            $this->_prefix = $this->_options['prefix'];
188
        }
189
    }
190
 
191
    // }}}
192
    // {{{ create()
193
 
194
    /**
195
     * "Emulated Factory" method to get the same API than XML_RPC2_Server class
196
     *
197
     * Here, simply returns a new instance of XML_RPC2_CachedServer class
198
     *
199
     * @param mixed $callTarget either a class name or an object instance.
200
     * @param array $options associative array of options
201
     * @return object a server class instance
202
     */
203
    public static function create($callTarget, $options = array())
204
    {
205
        return new XML_RPC2_CachedServer($callTarget, $options);
206
    }
207
 
208
    // }}}
209
    // {{{ handleCall()
210
 
211
    /**
212
     * handle XML_RPC calls
213
     *
214
     */
215
    public function handleCall()
216
    {
217
        $response = $this->getResponse();
218
        $encoding = 'utf-8';
219
        if (isset($this->_options['encoding'])) {
220
            $encoding = $this->_options['encoding'];
221
        }
222
        header('Content-type: text/xml; charset=' . $encoding);
223
        header('Content-length: ' . $this->getContentLength($response));
224
        print $response;
225
    }
226
 
227
    /**
228
     * get the XML response of the XMLRPC server
229
     *
230
     * @return string the XML response
231
     */
232
    public function getResponse()
233
    {
234
        if (isset($GLOBALS['HTTP_RAW_POST_DATA'])) {
235
            $methodName = $this->_parseMethodName($GLOBALS['HTTP_RAW_POST_DATA']);
236
        } else {
237
            $methodName = null;
238
        }
239
        $weCache = $this->_cacheByDefault;
240
        $lifetime = $this->_cacheOptions['lifetime'];
241
        if ($this->_cacheDebug) {
242
            if ($weCache) {
243
                print "CACHE DEBUG : default values  => weCache=true, lifetime=$lifetime\n";
244
            } else {
245
                print "CACHE DEBUG : default values  => weCache=false, lifetime=$lifetime\n";
246
            }
247
        }
248
        if ($methodName) {
249
            // work on reflection API to search for @xmlrpc.caching tags into PHPDOC comments
250
            list($weCache, $lifetime) = $this->_reflectionWork($methodName);
251
            if ($this->_cacheDebug) {
252
                if ($weCache) {
253
                    print "CACHE DEBUG : phpdoc comments => weCache=true, lifetime=$lifetime\n";
254
                } else {
255
                    print "CACHE DEBUG : phpdoc comments => weCache=false, lifetime=$lifetime\n";
256
                }
257
            }
258
        }
259
        if (($weCache) and ($lifetime!=-1)) {
260
            if (isset($GLOBALS['HTTP_RAW_POST_DATA'])) {
261
                $cacheId = $this->_makeCacheId($GLOBALS['HTTP_RAW_POST_DATA']);
262
            } else {
263
                $cacheId = 'norawpostdata';
264
            }
265
            $this->_cacheObject = new Cache_Lite($this->_cacheOptions);
266
            $this->_cacheObject->setLifetime($lifetime);
267
            if ($data = $this->_cacheObject->get($cacheId, $this->_defaultCacheGroup)) {
268
                // cache id hit
269
                if ($this->_cacheDebug) {
270
                    print "CACHE DEBUG : cache is hit !\n";
271
                }
272
            } else {
273
                // cache is not hit
274
                if ($this->_cacheDebug) {
275
                    print "CACHE DEBUG : cache is not hit !\n";
276
                }
277
                $data = $this->_workWithoutCache();
278
                $this->_cacheObject->save($data);
279
            }
280
        } else {
281
            if ($this->_cacheDebug) {
282
                print "CACHE DEBUG : we don't cache !\n";
283
            }
284
            $data = $this->_workWithoutCache();
285
        }
286
        return $data;
287
    }
288
 
289
    // }}}
290
    // {{{ _reflectionWork()
291
 
292
    /**
293
     * Work on reflection API to search for @xmlrpc.caching tags into PHPDOC comments
294
     *
295
     * @param string $methodName method name
296
     * @return array array((boolean) weCache, (int) lifetime) => parameters to use for caching
297
     */
298
    private function _reflectionWork($methodName) {
299
        $weCache = $this->_cacheByDefault;
300
        $lifetime = $this->_cacheOptions['lifetime'];
301
        if (is_string($this->_callTarget)) {
302
            $className = strtolower($this->_callTarget);
303
        } else {
304
            $className = get_class($this->_callTarget);
305
        }
306
        $class = new ReflectionClass($className);
307
        $method = $class->getMethod($methodName);
308
        $docs = explode("\n", $method->getDocComment());
309
        foreach ($docs as $i => $doc) {
310
            $doc = trim($doc, " \r\t/*");
311
            $res = preg_match('/@xmlrpc.caching ([+-]{0,1}[a-zA-Z0-9]*)/', $doc, $results); // TODO : better/faster regexp ?
312
            if ($res>0) {
313
                $value = $results[1];
314
                if (($value=='yes') or ($value=='true') or ($value=='on')) {
315
                    $weCache = true;
316
                } else if (($value=='no') or ($value=='false') or ($value=='off')) {
317
                    $weCache = false;
318
                } else {
319
                    $lifetime = (int) $value;
320
                    if ($lifetime==-1) {
321
                        $weCache = false;
322
                    } else {
323
                        $weCache = true;
324
                    }
325
                }
326
            }
327
         }
328
         return array($weCache, $lifetime);
329
    }
330
 
331
    // }}}
332
    // {{{ _parseMethodName()
333
 
334
    /**
335
     * Parse the method name from the raw XMLRPC client request
336
     *
337
     * NB : the prefix is removed from the method name
338
     *
339
     * @param string $request raw XMLRPC client request
340
     * @return string method name
341
     */
342
    private function _parseMethodName($request)
343
    {
344
        // TODO : change for "simplexml"
345
        $res = preg_match('/<methodName>' . $this->_prefix . '([a-zA-Z0-9\.,\/]*)<\/methodName>/', $request, $results);
346
        if ($res>0) {
347
            return $results[1];
348
        }
349
        return false;
350
    }
351
 
352
    // }}}
353
    // {{{ _workWithoutCache()
354
 
355
    /**
356
     * Do the real stuff if no cache available
357
     *
358
     * @return string the response of the real XML/RPC2 server
359
     */
360
    private function _workWithoutCache()
361
    {
362
        require_once('XML/RPC2/Server.php');
363
        $this->_serverObject = XML_RPC2_Server::create($this->_callTarget, $this->_options);
364
        return $this->_serverObject->getResponse();
365
    }
366
 
367
    // }}}
368
    // {{{ _makeCacheId()
369
 
370
    /**
371
     * make a cache id depending on the raw xmlrpc client request but depending on "environnement" setting too
372
     *
373
     * @param string $raw_request
374
     * @return string cache id
375
     */
376
    private function _makeCacheId($raw_request)
377
    {
378
        return md5($raw_request . serialize($this->_options));
379
    }
380
 
381
    // }}}
382
    // {{{ clean()
383
 
384
    /**
385
     * Clean all the cache
386
     */
387
    public function clean()
388
    {
389
        $this->_cacheObject->clean($this->_defaultCacheGroup, 'ingroup');
390
    }
391
 
392
    // }}}
393
    // {{{ getContentLength()
394
 
395
    /**
396
     * Gets the content legth of a serialized XML-RPC message in bytes
397
     *
398
     * @param string $content the serialized XML-RPC message.
399
     *
400
     * @return integer the content length in bytes.
401
     */
402
    protected function getContentLength($content)
403
    {
404
        if (extension_loaded('mbstring') && (ini_get('mbstring.func_overload') & 2) == 2) {
405
            $length = mb_strlen($content, '8bit');
406
        } else {
407
            $length = strlen((binary)$content);
408
        }
409
 
410
        return $length;
411
    }
412
 
413
    // }}}
414
}
415
 
416
?>