1: <?php
2: /**
3: * Abstraction of the IMAP4rev1 search criteria (see RFC 3501 [6.4.4]).
4: * Allows translation between abstracted search criteria and a generated IMAP
5: * search criteria string suitable for sending to a remote IMAP server.
6: *
7: * Copyright 2008-2012 Horde LLC (http://www.horde.org/)
8: *
9: * See the enclosed file COPYING for license information (LGPL). If you
10: * did not receive this file, see http://www.horde.org/licenses/lgpl21.
11: *
12: * @author Michael Slusarz <slusarz@horde.org>
13: * @category Horde
14: * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
15: * @package Imap_Client
16: */
17: class Horde_Imap_Client_Search_Query implements Serializable
18: {
19: /* Serialized version. */
20: const VERSION = 3;
21:
22: /* Constants for dateSearch() */
23: const DATE_BEFORE = 'BEFORE';
24: const DATE_ON = 'ON';
25: const DATE_SINCE = 'SINCE';
26:
27: /* Constants for intervalSearch() */
28: const INTERVAL_OLDER = 'OLDER';
29: const INTERVAL_YOUNGER = 'YOUNGER';
30:
31: /**
32: * The charset of the search strings. All text strings must be in
33: * this charset. By default, this is 'US-ASCII' (see RFC 3501 [6.4.4]).
34: *
35: * @var string
36: */
37: protected $_charset = null;
38:
39: /**
40: * The list of search params.
41: *
42: * @var array
43: */
44: protected $_search = array();
45:
46: /**
47: * Temp array used when building search string.
48: *
49: * @var array
50: */
51: protected $_temp = array();
52:
53: /**
54: * String representation: The IMAP search string.
55: */
56: public function __toString()
57: {
58: $utils = new Horde_Imap_Client_Utils();
59: $res = $this->build(null);
60: return trim($utils->parseCommandArray($res['query']));
61: }
62:
63: /**
64: * Sets the charset of the search text.
65: *
66: * @param string $charset The charset to use for the search.
67: * @param callback $callback A callback function to run on all text
68: * values when the charset changes. It must
69: * accept three parameters: the text, the old
70: * charset (will be null if no charset was
71: * previously given), and the new charset. It
72: * should return the converted text value.
73: */
74: public function charset($charset, $callback = null)
75: {
76: $oldcharset = $this->_charset;
77: $this->_charset = strtoupper($charset);
78: if (is_null($callback) || ($oldcharset == $this->_charset)) {
79: return;
80: }
81:
82: foreach (array('header', 'text') as $item) {
83: if (isset($this->_search[$item])) {
84: foreach (array_keys($this->_search[$item]) as $key) {
85: $this->_search[$item][$key]['text'] = call_user_func_array($callback, array($this->_search[$item][$key]['text'], $oldcharset, $this->_charset));
86: }
87: }
88: }
89: }
90:
91: /**
92: * Builds an IMAP4rev1 compliant search string.
93: *
94: * @param array $exts The list of extensions supported by the server.
95: * This determines whether certain criteria can be
96: * used, and determines whether workarounds are used
97: * for other criteria. In the format returned by
98: * Horde_Imap_Client_Base::capability(). If this value
99: * is null, all extensions are assumed to be
100: * available.
101: *
102: * @return array An array with these elements:
103: * - charset: (string) The charset of the search string. If null, no
104: * text strings appear in query.
105: * - exts: (array) The list of IMAP extensions used to create the
106: * string.
107: * - imap4: (boolean) True if the search uses IMAP4 criteria (as opposed
108: * to IMAP2 search criteria).
109: * - query: (array) The IMAP search string.
110: *
111: * @throws Horde_Imap_Client_Exception_NoSupportExtension
112: */
113: public function build($exts = array())
114: {
115: $this->_temp = array(
116: 'cmds' => array(),
117: 'exts' => $exts,
118: 'exts_used' => array(),
119: 'imap4' => false
120: );
121: $cmds = &$this->_temp['cmds'];
122: $charset = null;
123: $exts_used = &$this->_temp['exts_used'];
124: $imap4 = &$this->_temp['imap4'];
125: $ptr = &$this->_search;
126:
127: if (isset($ptr['new'])) {
128: $this->_addFuzzy(!empty($ptr['newfuzzy']));
129: if ($ptr['new']) {
130: $cmds[] = 'NEW';
131: unset($ptr['flag']['UNSEEN']);
132: } else {
133: $cmds[] = 'OLD';
134: }
135: unset($ptr['flag']['RECENT']);
136: }
137:
138: if (!empty($ptr['flag'])) {
139: foreach ($ptr['flag'] as $key => $val) {
140: if ($key == 'draft') {
141: // DRAFT flag was not in IMAP2
142: $imap4 = true;
143: }
144:
145: $this->_addFuzzy(!empty($val['fuzzy']));
146:
147: $tmp = '';
148: if (empty($val['set'])) {
149: // This is a 'NOT' search. All system flags but \Recent
150: // have 'UN' equivalents.
151: if ($key == 'RECENT') {
152: $cmds[] = 'NOT';
153: // NOT searches were not in IMAP2
154: $imap4 = true;
155: } else {
156: $tmp = 'UN';
157: }
158: }
159:
160: if ($val['type'] == 'keyword') {
161: $cmds[] = $tmp . 'KEYWORD';
162: $cmds[] = array('t' => Horde_Imap_Client::DATA_ATOM, 'v' => $key);
163: } else {
164: $cmds[] = $tmp . $key;
165: }
166: }
167: }
168:
169: if (!empty($ptr['header'])) {
170: /* The list of 'system' headers that have a specific search
171: * query. */
172: $systemheaders = array(
173: 'BCC', 'CC', 'FROM', 'SUBJECT', 'TO'
174: );
175:
176: foreach ($ptr['header'] as $val) {
177: $this->_addFuzzy(!empty($val['fuzzy']));
178:
179: if (!empty($val['not'])) {
180: $cmds[] = 'NOT';
181: // NOT searches were not in IMAP2
182: $imap4 = true;
183: }
184:
185: if (in_array($val['header'], $systemheaders)) {
186: $cmds[] = $val['header'];
187: } else {
188: // HEADER searches were not in IMAP2
189: $cmds[] = 'HEADER';
190: $cmds[] = array('t' => Horde_Imap_Client::DATA_ASTRING, 'v' => $val['header']);
191: $imap4 = true;
192: }
193: $cmds[] = array('t' => Horde_Imap_Client::DATA_ASTRING, 'v' => isset($val['text']) ? $val['text'] : '');
194: $charset = is_null($this->_charset)
195: ? 'US-ASCII'
196: : $this->_charset;
197: }
198: }
199:
200: if (!empty($ptr['text'])) {
201: foreach ($ptr['text'] as $val) {
202: $this->_addFuzzy(!empty($val['fuzzy']));
203:
204: if (!empty($val['not'])) {
205: $cmds[] = 'NOT';
206: // NOT searches were not in IMAP2
207: $imap4 = true;
208: }
209: $cmds[] = $val['type'];
210: $cmds[] = array('t' => Horde_Imap_Client::DATA_ASTRING, 'v' => $val['text']);
211: if (is_null($charset)) {
212: $charset = is_null($this->_charset)
213: ? 'US-ASCII'
214: : $this->_charset;
215: }
216: }
217: }
218:
219: if (!empty($ptr['size'])) {
220: foreach ($ptr['size'] as $key => $val) {
221: $this->_addFuzzy(!empty($val['fuzzy']));
222: if (!empty($val['not'])) {
223: $cmds[] = 'NOT';
224: }
225: $cmds[] = $key;
226: $cmds[] = array('t' => Horde_Imap_Client::DATA_NUMBER, 'v' => $val['size']);
227: // LARGER/SMALLER searches were not in IMAP2
228: $imap4 = true;
229: }
230: }
231:
232: if (isset($ptr['ids']) &&
233: (count($ptr['ids']['ids']) || $ptr['ids']['ids']->all)) {
234: $this->_addFuzzy(!empty($val['fuzzy']));
235: if (!empty($ptr['ids']['not'])) {
236: $cmds[] = 'NOT';
237: }
238: if (!$ptr['ids']['ids']->sequence) {
239: $cmds[] = 'UID';
240: }
241: $cmds[] = $ptr['ids']['ids']->all
242: ? '1:*'
243: : strval($ptr['ids']['ids']);
244:
245: // ID searches were not in IMAP2
246: $imap4 = true;
247: }
248:
249: if (!empty($ptr['date'])) {
250: foreach ($ptr['date'] as $val) {
251: $this->_addFuzzy(!empty($val['fuzzy']));
252:
253: if (!empty($val['not'])) {
254: $cmds[] = 'NOT';
255: // NOT searches were not in IMAP2
256: $imap4 = true;
257: }
258:
259: if (empty($val['header'])) {
260: $cmds[] = $val['range'];
261: } else {
262: $cmds[] = 'SENT' . $val['range'];
263: // 'SENT*' searches were not in IMAP2
264: $imap4 = true;
265: }
266: $cmds[] = $val['date'];
267: }
268: }
269:
270: if (!empty($ptr['within'])) {
271: if (is_null($exts) || isset($exts['WITHIN'])) {
272: $exts_used[] = 'WITHIN';
273: $imap4 = true;
274: }
275:
276: foreach ($ptr['within'] as $key => $val) {
277: $this->_addFuzzy(!empty($val['fuzzy']));
278: if (!empty($val['not'])) {
279: $cmds[] = 'NOT';
280: // NOT searches were not in IMAP2
281: $imap4 = true;
282: }
283:
284: if (is_null($exts) || isset($exts['WITHIN'])) {
285: $cmds[] = $key;
286: $cmds[] = array('t' => Horde_Imap_Client::DATA_NUMBER, 'v' => $val['interval']);
287: } else {
288: // This workaround is only accurate to within 1 day, due
289: // to limitations with the IMAP4rev1 search commands.
290: $date = new DateTime('now -' . $val['interval'] . ' seconds');
291: $cmds[] = ($key == self::INTERVAL_OLDER)
292: ? self::DATE_BEFORE
293: : self::DATE_SINCE;
294: $cmds[] = $date->format('d-M-Y');
295: }
296: }
297: }
298:
299: if (!empty($ptr['modseq'])) {
300: if (!is_null($exts) && !isset($exts['CONDSTORE'])) {
301: throw new Horde_Imap_Client_Exception_NoSupportExtension('IMAP Server does not support CONDSTORE.');
302: }
303:
304: $exts_used[] = 'CONDSTORE';
305: $imap4 = true;
306:
307: $this->_addFuzzy(!empty($ptr['modseq']['fuzzy']));
308:
309: if (!empty($ptr['modseq']['not'])) {
310: $cmds[] = 'NOT';
311: }
312: $cmds[] = 'MODSEQ';
313: if (!is_null($ptr['modseq']['name'])) {
314: $cmds[] = array('t' => Horde_Imap_Client::DATA_STRING, 'v' => $ptr['modseq']['name']);
315: $cmds[] = $ptr['modseq']['type'];
316: }
317: $cmds[] = array('t' => Horde_Imap_Client::DATA_NUMBER, 'v' => $ptr['modseq']['value']);
318: }
319:
320: if (isset($ptr['prevsearch'])) {
321: if (!is_null($exts) && !isset($exts['SEARCHRES'])) {
322: throw new Horde_Imap_Client_Exception_NoSupportExtension('IMAP Server does not support SEARCHRES.');
323: }
324:
325: $exts_used[] = 'SEARCHRES';
326: $imap4 = true;
327:
328: $this->_addFuzzy(!empty($ptr['prevsearchfuzzy']));
329:
330: if (!$ptr['prevsearch']) {
331: $cmds[] = 'NOT';
332: }
333: $cmds[] = '$';
334: }
335:
336: // Add AND'ed queries
337: if (!empty($ptr['and'])) {
338: foreach ($ptr['and'] as $val) {
339: $ret = $val->build();
340: $cmds = array_merge($cmds, $ret['query']);
341: }
342: }
343:
344: // Add OR'ed queries
345: if (!empty($ptr['or'])) {
346: foreach ($ptr['or'] as $val) {
347: // OR queries were not in IMAP 2
348: $imap4 = true;
349:
350: $ret = $val->build();
351:
352: // First OR'd query
353: if (empty($cmds)) {
354: $cmds = array($ret['query']);
355: } else {
356: $cmds = array_merge(array(
357: 'OR',
358: $ret['query']
359: ), $cmds);
360: }
361: }
362: }
363:
364: // Default search is 'ALL'
365: if (empty($cmds)) {
366: $cmds[] = 'ALL';
367: }
368:
369: return array(
370: 'charset' => $charset,
371: 'exts' => array_keys(array_flip($exts_used)),
372: 'imap4' => $imap4,
373: 'query' => $cmds
374: );
375: }
376:
377: /**
378: * Adds fuzzy modifier to search keys.
379: *
380: * @param boolean $add Add the fuzzy modifier?
381: *
382: * @throws Horde_Imap_Client_Exception_NoSupport_Extension
383: */
384: protected function _addFuzzy($add)
385: {
386: if ($add) {
387: if (!isset($this->_temp['exts']['SEARCH']) ||
388: !in_array('FUZZY', $this->_temp['exts']['SEARCH'])) {
389: throw new Horde_Imap_Client_Exception_NoSupportExtension('IMAP Server does not support SEARCH=FUZZY.');
390: }
391: $this->_temp['cmds'][] = 'FUZZY';
392: $this->_temp['exts_used'][] = 'SEARCH=FUZZY';
393: $this->_temp['imap4'] = true;
394: }
395: }
396:
397: /**
398: * Search for a flag/keywords.
399: *
400: * @param string $name The flag or keyword name.
401: * @param boolean $set If true, search for messages that have the flag
402: * set. If false, search for messages that do not
403: * have the flag set.
404: * @param array $opts Additional options:
405: * - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
406: * MUST support RFC 6203.
407: */
408: public function flag($name, $set = true, array $opts = array())
409: {
410: $name = strtoupper(ltrim($name, '\\'));
411: if (!isset($this->_search['flag'])) {
412: $this->_search['flag'] = array();
413: }
414:
415: /* The list of defined system flags (see RFC 3501 [2.3.2]). */
416: $systemflags = array(
417: 'ANSWERED', 'DELETED', 'DRAFT', 'FLAGGED', 'RECENT', 'SEEN'
418: );
419:
420: $this->_search['flag'][$name] = array_filter(array(
421: 'fuzzy' => !empty($opts['fuzzy']),
422: 'set' => $set,
423: 'type' => in_array($name, $systemflags) ? 'flag' : 'keyword'
424: ));
425: }
426:
427: /**
428: * Determines if flags are a part of the search.
429: *
430: * @return boolean True if search query involves flags.
431: */
432: public function flagSearch()
433: {
434: return !empty($this->_search['flag']);
435: }
436:
437: /**
438: * Search for either new messages (messages that have the '\Recent' flag
439: * but not the '\Seen' flag) or old messages (messages that do not have
440: * the '\Recent' flag). If new messages are searched, this will clear
441: * any '\Recent' or '\Unseen' flag searches. If old messages are searched,
442: * this will clear any '\Recent' flag search.
443: *
444: * @param boolean $newmsgs If true, searches for new messages. Else,
445: * search for old messages.
446: * @param array $opts Additional options:
447: * - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
448: * MUST support RFC 6203.
449: */
450: public function newMsgs($newmsgs = true, array $opts = array())
451: {
452: $this->_search['new'] = $newmsgs;
453: if (!empty($opts['fuzzy'])) {
454: $this->_search['newfuzzy'] = true;
455: }
456: }
457:
458: /**
459: * Search for text in the header of a message.
460: *
461: * @param string $header The header field.
462: * @param string $text The search text.
463: * @param boolean $not If true, do a 'NOT' search of $text.
464: * @param array $opts Additional options:
465: * - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
466: * MUST support RFC 6203.
467: */
468: public function headerText($header, $text, $not = false,
469: array $opts = array())
470: {
471: if (!isset($this->_search['header'])) {
472: $this->_search['header'] = array();
473: }
474: $this->_search['header'][] = array_filter(array(
475: 'fuzzy' => !empty($opts['fuzzy']),
476: 'header' => strtoupper($header),
477: 'text' => $text,
478: 'not' => $not
479: ));
480: }
481:
482: /**
483: * Search for text in either the entire message, or just the body.
484: *
485: * @param string $text The search text.
486: * @param string $bodyonly If true, only search in the body of the
487: * message. If false, also search in the headers.
488: * @param boolean $not If true, do a 'NOT' search of $text.
489: * @param array $opts Additional options:
490: * - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
491: * MUST support RFC 6203.
492: */
493: public function text($text, $bodyonly = true, $not = false,
494: array $opts = array())
495: {
496: if (!isset($this->_search['text'])) {
497: $this->_search['text'] = array();
498: }
499:
500: $this->_search['text'][] = array_filter(array(
501: 'fuzzy' => !empty($opts['fuzzy']),
502: 'not' => $not,
503: 'text' => $text,
504: 'type' => $bodyonly ? 'BODY' : 'TEXT'
505: ));
506: }
507:
508: /**
509: * Search for messages smaller/larger than a certain size.
510: *
511: * @param integer $size The size (in bytes).
512: * @param boolean $larger Search for messages larger than $size?
513: * @param boolean $not If true, do a 'NOT' search of $text.
514: * @param array $opts Additional options:
515: * - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
516: * MUST support RFC 6203.
517: */
518: public function size($size, $larger = false, $not = false,
519: array $opts = array())
520: {
521: if (!isset($this->_search['size'])) {
522: $this->_search['size'] = array();
523: }
524: $this->_search['size'][$larger ? 'LARGER' : 'SMALLER'] = array_filter(array(
525: 'fuzzy' => !empty($opts['fuzzy']),
526: 'not' => $not,
527: 'size' => (float)$size
528: ));
529: }
530:
531: /**
532: * Search for messages within a given ID sequence range. Only one message
533: * range can be specified per query.
534: *
535: * @param Horde_Imap_Client_Ids $ids The list of IDs to search.
536: * @param boolean $not If true, do a 'NOT' search of the
537: * IDs.
538: * @param array $opts Additional options:
539: * - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
540: * MUST support RFC 6203.
541: */
542: public function ids(Horde_Imap_Client_Ids $ids, $not = false,
543: array $opts = array())
544: {
545: $this->_search['ids'] = array_filter(array(
546: 'fuzzy' => !empty($opts['fuzzy']),
547: 'ids' => $ids,
548: 'not' => $not
549: ));
550: }
551:
552: /**
553: * Search for messages within a date range.
554: *
555: * @param mixed $date DateTime or Horde_Date object.
556: * @param string $range Either:
557: * - Horde_Imap_Client_Search_Query::DATE_BEFORE
558: * - Horde_Imap_Client_Search_Query::DATE_ON
559: * - Horde_Imap_Client_Search_Query::DATE_SINCE
560: * @param boolean $header If true, search using the date in the message
561: * headers. If false, search using the internal
562: * IMAP date (usually arrival time).
563: * @param boolean $not If true, do a 'NOT' search of the range.
564: * @param array $opts Additional options:
565: * - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
566: * MUST support RFC 6203.
567: */
568: public function dateSearch($date, $range, $header = true, $not = false,
569: array $opts = array())
570: {
571: if (!isset($this->_search['date'])) {
572: $this->_search['date'] = array();
573: }
574: $this->_search['date'][] = array_filter(array(
575: 'date' => $date->format('d-M-Y'),
576: 'fuzzy' => !empty($opts['fuzzy']),
577: 'header' => $header,
578: 'range' => $range,
579: 'not' => $not
580: ));
581: }
582:
583: /**
584: * Search for messages within a given interval. Only one interval of each
585: * type can be specified per search query. If the IMAP server supports
586: * the WITHIN extension (RFC 5032), it will be used. Otherwise, the
587: * search query will be dynamically created using IMAP4rev1 search
588: * terms.
589: *
590: * @param integer $interval Seconds from the present.
591: * @param string $range Either:
592: * - Horde_Imap_Client_Search_Query::INTERVAL_OLDER
593: * - Horde_Imap_Client_Search_Query::INTERVAL_YOUNGER
594: * @param boolean $not If true, do a 'NOT' search.
595: * @param array $opts Additional options:
596: * - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
597: * MUST support RFC 6203.
598: */
599: public function intervalSearch($interval, $range, $not = false,
600: array $opts = array())
601: {
602: if (!isset($this->_search['within'])) {
603: $this->_search['within'] = array();
604: }
605: $this->_search['within'][$range] = array(
606: 'fuzzy' => !empty($opts['fuzzy']),
607: 'interval' => $interval,
608: 'not' => $not
609: );
610: }
611:
612: /**
613: * AND queries - the contents of this query will be AND'ed (in its
614: * entirety) with the contents of each of the queries passed in. All
615: * AND'd queries must share the same charset as this query.
616: *
617: * @param array $queries An array of queries to AND with this one. Each
618: * query is a Horde_Imap_Client_Search_Query
619: * object.
620: */
621: public function andSearch($queries)
622: {
623: if (!isset($this->_search['and'])) {
624: $this->_search['and'] = array();
625: }
626: $this->_search['and'] = array_merge($this->_search['and'], $queries);
627: }
628:
629: /**
630: * OR a query - the contents of this query will be OR'ed (in its entirety)
631: * with the contents of each of the queries passed in. All OR'd queries
632: * must share the same charset as this query. All contents of any single
633: * query will be AND'ed together.
634: *
635: * @param array $queries An array of queries to OR with this one. Each
636: * query is a Horde_Imap_Client_Search_Query
637: * object.
638: */
639: public function orSearch($queries)
640: {
641: if (!isset($this->_search['or'])) {
642: $this->_search['or'] = array();
643: }
644: $this->_search['or'] = array_merge($this->_search['or'], $queries);
645: }
646:
647: /**
648: * Search for messages modified since a specific moment. The IMAP server
649: * must support the CONDSTORE extension (RFC 4551) for this query to be
650: * used.
651: *
652: * @param integer $value The mod-sequence value.
653: * @param string $name The entry-name string.
654: * @param string $type Either 'shared', 'priv', or 'all'. Defaults to
655: * 'all'
656: * @param boolean $not If true, do a 'NOT' search.
657: * @param array $opts Additional options:
658: * - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
659: * MUST support RFC 6203.
660: */
661: public function modseq($value, $name = null, $type = null, $not = false,
662: array $opts = array())
663: {
664: if (!is_null($type)) {
665: $type = strtolower($type);
666: if (!in_array($type, array('shared', 'priv', 'all'))) {
667: $type = 'all';
668: }
669: }
670:
671: $this->_search['modseq'] = array_filter(array(
672: 'fuzzy' => !empty($opts['fuzzy']),
673: 'name' => $name,
674: 'not' => $not,
675: 'type' => (!is_null($name) && is_null($type)) ? 'all' : $type,
676: 'value' => $value
677: ));
678: }
679:
680: /**
681: * Use the results from the previous SEARCH command. The IMAP server must
682: * support the SEARCHRES extension (RFC 5182) for this query to be used.
683: *
684: * @param boolean $not If true, don't match the previous query.
685: * @param array $opts Additional options:
686: * - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
687: * MUST support RFC 6203.
688: */
689: public function previousSearch($not = false, array $opts = array())
690: {
691: $this->_search['prevsearch'] = $not;
692: if (!empty($opts['fuzzy'])) {
693: $this->_search['prevsearchfuzzy'] = true;
694: }
695: }
696:
697: /* Serializable methods. */
698:
699: /**
700: * Serialization.
701: *
702: * @return string Serialized data.
703: */
704: public function serialize()
705: {
706: $data = array(
707: // Serialized data ID.
708: self::VERSION,
709: $this->_search
710: );
711:
712: if (!is_null($this->_charset)) {
713: $data[] = $this->_charset;
714: }
715:
716: return serialize($data);
717: }
718:
719: /**
720: * Unserialization.
721: *
722: * @param string $data Serialized data.
723: *
724: * @throws Exception
725: */
726: public function unserialize($data)
727: {
728: $data = @unserialize($data);
729: if (!is_array($data) ||
730: !isset($data[0]) ||
731: ($data[0] != self::VERSION)) {
732: throw new Exception('Cache version change');
733: }
734:
735: $this->_search = $data[1];
736: if (isset($data[2])) {
737: $this->_charset = $data[2];
738: }
739: }
740:
741: }
742: