]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - classes/Memcached_DataObject.php
change Laconica and Control Yourself to StatusNet in PHP files
[quix0rs-gnu-social.git] / classes / Memcached_DataObject.php
1 <?php
2 /*
3  * StatusNet - a distributed open-source microblogging tool
4  * Copyright (C) 2008, 2009, StatusNet, Inc.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 if (!defined('LACONICA')) { exit(1); }
21
22 require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
23
24 class Memcached_DataObject extends DB_DataObject
25 {
26     function &staticGet($cls, $k, $v=null)
27     {
28         if (is_null($v)) {
29             $v = $k;
30             # XXX: HACK!
31             $i = new $cls;
32             $keys = $i->keys();
33             $k = $keys[0];
34             unset($i);
35         }
36         $i = Memcached_DataObject::getcached($cls, $k, $v);
37         if ($i) {
38             return $i;
39         } else {
40             $i = DB_DataObject::staticGet($cls, $k, $v);
41             if ($i) {
42                 $i->encache();
43             }
44             return $i;
45         }
46     }
47
48     function &pkeyGet($cls, $kv)
49     {
50         $i = Memcached_DataObject::multicache($cls, $kv);
51         if ($i) {
52             return $i;
53         } else {
54             $i = new $cls();
55             foreach ($kv as $k => $v) {
56                 $i->$k = $v;
57             }
58             if ($i->find(true)) {
59                 $i->encache();
60             } else {
61                 $i = null;
62             }
63             return $i;
64         }
65     }
66
67     function insert()
68     {
69         $result = parent::insert();
70         return $result;
71     }
72
73     function update($orig=null)
74     {
75         if (is_object($orig) && $orig instanceof Memcached_DataObject) {
76             $orig->decache(); # might be different keys
77         }
78         $result = parent::update($orig);
79         if ($result) {
80             $this->encache();
81         }
82         return $result;
83     }
84
85     function delete()
86     {
87         $this->decache(); # while we still have the values!
88         return parent::delete();
89     }
90
91     static function memcache() {
92         return common_memcache();
93     }
94
95     static function cacheKey($cls, $k, $v) {
96         return common_cache_key(strtolower($cls).':'.$k.':'.$v);
97     }
98
99     static function getcached($cls, $k, $v) {
100         $c = Memcached_DataObject::memcache();
101         if (!$c) {
102             return false;
103         } else {
104             return $c->get(Memcached_DataObject::cacheKey($cls, $k, $v));
105         }
106     }
107
108     function keyTypes()
109     {
110         global $_DB_DATAOBJECT;
111         if (!isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"])) {
112             $this->databaseStructure();
113
114         }
115         return $_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"];
116     }
117
118     function encache()
119     {
120         $c = $this->memcache();
121         if (!$c) {
122             return false;
123         } else {
124             $pkey = array();
125             $pval = array();
126             $types = $this->keyTypes();
127             ksort($types);
128             foreach ($types as $key => $type) {
129                 if ($type == 'K') {
130                     $pkey[] = $key;
131                     $pval[] = $this->$key;
132                 } else {
133                     $c->set($this->cacheKey($this->tableName(), $key, $this->$key), $this);
134                 }
135             }
136             # XXX: should work for both compound and scalar pkeys
137             $pvals = implode(',', $pval);
138             $pkeys = implode(',', $pkey);
139             $c->set($this->cacheKey($this->tableName(), $pkeys, $pvals), $this);
140         }
141     }
142
143     function decache()
144     {
145         $c = $this->memcache();
146         if (!$c) {
147             return false;
148         } else {
149             $pkey = array();
150             $pval = array();
151             $types = $this->keyTypes();
152             ksort($types);
153             foreach ($types as $key => $type) {
154                 if ($type == 'K') {
155                     $pkey[] = $key;
156                     $pval[] = $this->$key;
157                 } else {
158                     $c->delete($this->cacheKey($this->tableName(), $key, $this->$key));
159                 }
160             }
161             # should work for both compound and scalar pkeys
162             # XXX: comma works for now but may not be safe separator for future keys
163             $pvals = implode(',', $pval);
164             $pkeys = implode(',', $pkey);
165             $c->delete($this->cacheKey($this->tableName(), $pkeys, $pvals));
166         }
167     }
168
169     function multicache($cls, $kv)
170     {
171         ksort($kv);
172         $c = Memcached_DataObject::memcache();
173         if (!$c) {
174             return false;
175         } else {
176             $pkeys = implode(',', array_keys($kv));
177             $pvals = implode(',', array_values($kv));
178             return $c->get(Memcached_DataObject::cacheKey($cls, $pkeys, $pvals));
179         }
180     }
181
182     function getSearchEngine($table)
183     {
184         require_once INSTALLDIR.'/lib/search_engines.php';
185         static $search_engine;
186         if (!isset($search_engine)) {
187                 $connected = false;
188                 if (common_config('sphinx', 'enabled')) {
189                     $search_engine = new SphinxSearch($this, $table);
190                     $connected = $search_engine->is_connected();
191                 }
192
193                 // unable to connect to sphinx' search daemon
194                 if (!$connected) {
195                     if ('mysql' === common_config('db', 'type')) {
196                         $type = common_config('search', 'type');
197                         if ($type == 'like') {
198                             $search_engine = new MySQLLikeSearch($this, $table);
199                         } else if ($type == 'fulltext') {
200                             $search_engine = new MySQLSearch($this, $table);
201                         } else {
202                             throw new ServerException('Unknown search type: ' . $type);
203                         }
204                     } else {
205                         $search_engine = new PGSearch($this, $table);
206                     }
207                 }
208         }
209         return $search_engine;
210     }
211
212     static function cachedQuery($cls, $qry, $expiry=3600)
213     {
214         $c = Memcached_DataObject::memcache();
215         if (!$c) {
216             $inst = new $cls();
217             $inst->query($qry);
218             return $inst;
219         }
220         $key_part = common_keyize($cls).':'.md5($qry);
221         $ckey = common_cache_key($key_part);
222         $stored = $c->get($ckey);
223         if ($stored) {
224             return new ArrayWrapper($stored);
225         }
226
227         $inst = new $cls();
228         $inst->query($qry);
229         $cached = array();
230         while ($inst->fetch()) {
231             $cached[] = clone($inst);
232         }
233         $inst->free();
234         $c->set($ckey, $cached, MEMCACHE_COMPRESSED, $expiry);
235         return new ArrayWrapper($cached);
236     }
237
238     // We overload so that 'SET NAMES "utf8"' is called for
239     // each connection
240
241     function _connect()
242     {
243         global $_DB_DATAOBJECT;
244
245         $sum = $this->_getDbDsnMD5();
246
247         if (!empty($_DB_DATAOBJECT['CONNECTIONS'][$sum]) &&
248             !PEAR::isError($_DB_DATAOBJECT['CONNECTIONS'][$sum])) {
249             $exists = true;
250         } else {
251             $exists = false;
252        }
253
254         $result = parent::_connect();
255
256         if ($result && !$exists) {
257             $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
258             if (common_config('db', 'type') == 'mysql' &&
259                 common_config('db', 'utf8')) {
260                 $conn = $DB->connection;
261                 if (!empty($conn)) {
262                     if ($DB instanceof DB_mysqli) {
263                         mysqli_set_charset($conn, 'utf8');
264                     } else if ($DB instanceof DB_mysql) {
265                         mysql_set_charset('utf8', $conn);
266                     }
267                 }
268             }
269         }
270
271         return $result;
272     }
273
274     // XXX: largely cadged from DB_DataObject
275
276     function _getDbDsnMD5()
277     {
278         if ($this->_database_dsn_md5) {
279             return $this->_database_dsn_md5;
280         }
281
282         $dsn = $this->_getDbDsn();
283
284         if (is_string($dsn)) {
285             $sum = md5($dsn);
286         } else {
287             /// support array based dsn's
288             $sum = md5(serialize($dsn));
289         }
290
291         return $sum;
292     }
293
294     function _getDbDsn()
295     {
296         global $_DB_DATAOBJECT;
297
298         if (empty($_DB_DATAOBJECT['CONFIG'])) {
299             DB_DataObject::_loadConfig();
300         }
301
302         $options = &$_DB_DATAOBJECT['CONFIG'];
303
304         // if the databse dsn dis defined in the object..
305
306         $dsn = isset($this->_database_dsn) ? $this->_database_dsn : null;
307
308         if (!$dsn) {
309
310             if (!$this->_database) {
311                 $this->_database = isset($options["table_{$this->__table}"]) ? $options["table_{$this->__table}"] : null;
312             }
313
314             if ($this->_database && !empty($options["database_{$this->_database}"]))  {
315                 $dsn = $options["database_{$this->_database}"];
316             } else if (!empty($options['database'])) {
317                 $dsn = $options['database'];
318             }
319         }
320
321         if (!$dsn) {
322             throw new Exception("No database name / dsn found anywhere");
323         }
324
325         return $dsn;
326     }
327 }