]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - classes/Memcached_DataObject.php
Debug check to track down live error -- wrong data type sometimes being sent down...
[quix0rs-gnu-social.git] / classes / Memcached_DataObject.php
1 <?php
2 /*
3  * StatusNet - the 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('STATUSNET') && !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         if (is_object($cls) || is_object($j) || is_object($v)) {
97             $e = new Exception();
98             common_log(LOG_ERR, __METHOD__ . ' object in param: ' .
99                 str_replace("\n", " ", $e->getTraceAsString()));
100         }
101         return common_cache_key(strtolower($cls).':'.$k.':'.$v);
102     }
103
104     static function getcached($cls, $k, $v) {
105         $c = Memcached_DataObject::memcache();
106         if (!$c) {
107             return false;
108         } else {
109             return $c->get(Memcached_DataObject::cacheKey($cls, $k, $v));
110         }
111     }
112
113     function keyTypes()
114     {
115         global $_DB_DATAOBJECT;
116         if (!isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"])) {
117             $this->databaseStructure();
118
119         }
120         return $_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"];
121     }
122
123     function encache()
124     {
125         $c = $this->memcache();
126         if (!$c) {
127             return false;
128         } else {
129             $pkey = array();
130             $pval = array();
131             $types = $this->keyTypes();
132             ksort($types);
133             foreach ($types as $key => $type) {
134                 if ($type == 'K') {
135                     $pkey[] = $key;
136                     $pval[] = $this->$key;
137                 } else {
138                     $c->set($this->cacheKey($this->tableName(), $key, $this->$key), $this);
139                 }
140             }
141             # XXX: should work for both compound and scalar pkeys
142             $pvals = implode(',', $pval);
143             $pkeys = implode(',', $pkey);
144             $c->set($this->cacheKey($this->tableName(), $pkeys, $pvals), $this);
145         }
146     }
147
148     function decache()
149     {
150         $c = $this->memcache();
151         if (!$c) {
152             return false;
153         } else {
154             $pkey = array();
155             $pval = array();
156             $types = $this->keyTypes();
157             ksort($types);
158             foreach ($types as $key => $type) {
159                 if ($type == 'K') {
160                     $pkey[] = $key;
161                     $pval[] = $this->$key;
162                 } else {
163                     $c->delete($this->cacheKey($this->tableName(), $key, $this->$key));
164                 }
165             }
166             # should work for both compound and scalar pkeys
167             # XXX: comma works for now but may not be safe separator for future keys
168             $pvals = implode(',', $pval);
169             $pkeys = implode(',', $pkey);
170             $c->delete($this->cacheKey($this->tableName(), $pkeys, $pvals));
171         }
172     }
173
174     function multicache($cls, $kv)
175     {
176         ksort($kv);
177         $c = Memcached_DataObject::memcache();
178         if (!$c) {
179             return false;
180         } else {
181             $pkeys = implode(',', array_keys($kv));
182             $pvals = implode(',', array_values($kv));
183             return $c->get(Memcached_DataObject::cacheKey($cls, $pkeys, $pvals));
184         }
185     }
186
187     function getSearchEngine($table)
188     {
189         require_once INSTALLDIR.'/lib/search_engines.php';
190         static $search_engine;
191         if (!isset($search_engine)) {
192             if (Event::handle('GetSearchEngine', array($this, $table, &$search_engine))) {
193                 if ('mysql' === common_config('db', 'type')) {
194                     $type = common_config('search', 'type');
195                     if ($type == 'like') {
196                         $search_engine = new MySQLLikeSearch($this, $table);
197                     } else if ($type == 'fulltext') {
198                         $search_engine = new MySQLSearch($this, $table);
199                     } else {
200                         throw new ServerException('Unknown search type: ' . $type);
201                     }
202                 } else {
203                     $search_engine = new PGSearch($this, $table);
204                 }
205             }
206         }
207         return $search_engine;
208     }
209
210     static function cachedQuery($cls, $qry, $expiry=3600)
211     {
212         $c = Memcached_DataObject::memcache();
213         if (!$c) {
214             $inst = new $cls();
215             $inst->query($qry);
216             return $inst;
217         }
218         $key_part = common_keyize($cls).':'.md5($qry);
219         $ckey = common_cache_key($key_part);
220         $stored = $c->get($ckey);
221         if ($stored) {
222             return new ArrayWrapper($stored);
223         }
224
225         $inst = new $cls();
226         $inst->query($qry);
227         $cached = array();
228         while ($inst->fetch()) {
229             $cached[] = clone($inst);
230         }
231         $inst->free();
232         $c->set($ckey, $cached, MEMCACHE_COMPRESSED, $expiry);
233         return new ArrayWrapper($cached);
234     }
235
236     // We overload so that 'SET NAMES "utf8"' is called for
237     // each connection
238
239     function _connect()
240     {
241         global $_DB_DATAOBJECT;
242
243         $sum = $this->_getDbDsnMD5();
244
245         if (!empty($_DB_DATAOBJECT['CONNECTIONS'][$sum]) &&
246             !PEAR::isError($_DB_DATAOBJECT['CONNECTIONS'][$sum])) {
247             $exists = true;
248         } else {
249             $exists = false;
250        }
251
252         $result = parent::_connect();
253
254         if ($result && !$exists) {
255             $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
256             if (common_config('db', 'type') == 'mysql' &&
257                 common_config('db', 'utf8')) {
258                 $conn = $DB->connection;
259                 if (!empty($conn)) {
260                     if ($DB instanceof DB_mysqli) {
261                         mysqli_set_charset($conn, 'utf8');
262                     } else if ($DB instanceof DB_mysql) {
263                         mysql_set_charset('utf8', $conn);
264                     }
265                 }
266             }
267         }
268
269         return $result;
270     }
271
272     // XXX: largely cadged from DB_DataObject
273
274     function _getDbDsnMD5()
275     {
276         if ($this->_database_dsn_md5) {
277             return $this->_database_dsn_md5;
278         }
279
280         $dsn = $this->_getDbDsn();
281
282         if (is_string($dsn)) {
283             $sum = md5($dsn);
284         } else {
285             /// support array based dsn's
286             $sum = md5(serialize($dsn));
287         }
288
289         return $sum;
290     }
291
292     function _getDbDsn()
293     {
294         global $_DB_DATAOBJECT;
295
296         if (empty($_DB_DATAOBJECT['CONFIG'])) {
297             DB_DataObject::_loadConfig();
298         }
299
300         $options = &$_DB_DATAOBJECT['CONFIG'];
301
302         // if the databse dsn dis defined in the object..
303
304         $dsn = isset($this->_database_dsn) ? $this->_database_dsn : null;
305
306         if (!$dsn) {
307
308             if (!$this->_database) {
309                 $this->_database = isset($options["table_{$this->__table}"]) ? $options["table_{$this->__table}"] : null;
310             }
311
312             if ($this->_database && !empty($options["database_{$this->_database}"]))  {
313                 $dsn = $options["database_{$this->_database}"];
314             } else if (!empty($options['database'])) {
315                 $dsn = $options['database'];
316             }
317         }
318
319         if (!$dsn) {
320             throw new Exception("No database name / dsn found anywhere");
321         }
322
323         return $dsn;
324     }
325 }