Public feed #722268

Jemand

public text v1 · immutable
Raw Edit
·published 2007-10-01 21:40 UTC
rendered paste body
<?php
// $Id: cache.inc,v 1.5 2006/11/10 07:26:27 drumm Exp $

/**
 * Return data from the persistent cache.
 *
 * @param $key
 *   The cache ID of the data to retrieve.
 * @param $table
 *   The table $table to store the data in. Valid core values are 'cache_filter',
 *   'cache_menu', 'cache_page', or 'cache' for the default cache.
 */
function cache_get($key, $table = 'cache') {
  global $user;

  // Garbage collection necessary when enforcing a minimum cache lifetime
  $cache_flush = variable_get('cache_flush', 0);
  if ($cache_flush && ($cache_flush + variable_get('cache_lifetime', 0) <= time())) {
    // Time to flush old cache data
    db_query("DELETE FROM {%s} WHERE expire != %d AND expire <= %d", $table, CACHE_PERMANENT, $cache_flush);
    variable_set('cache_flush', 0);
  }

  $cache = db_fetch_object(db_query("SELECT data, created, headers, expire FROM {%s} WHERE cid = '%s'", $table, $key));
  if (isset($cache->data)) {
    // If the data is permanent or we're not enforcing a minimum cache lifetime
    // always return the cached data.
    if ($cache->expire == CACHE_PERMANENT || !variable_get('cache_lifetime', 0)) {
      $cache->data = db_decode_blob($cache->data);
    }
    // If enforcing a minimum cache lifetime, validate that the data is
    // currently valid for this user before we return it by making sure the
    // cache entry was created before the timestamp in the current session's
    // cache timer. The cache variable is loaded into the $user object by
    // sess_read() in session.inc.
    else {
      if ($user->cache > $cache->created) {
        // This cache data is too old and thus not valid for us, ignore it.
        return 0;
      }
      else {
        $cache->data = db_decode_blob($cache->data);
      }
    }
    return $cache;
  }
  return 0;
}

/**
 * Store data in the persistent cache.
 *
 * The persistent cache is split up into four database
 * tables. Contributed modules can add additional tables.
 *
 * 'cache_page': This table stores generated pages for anonymous
 * users. This is the only table affected by the page cache setting on
 * the administrator panel.
 *
 * 'cache_menu': Stores the cachable part of the users' menus.
 *
 * 'cache_filter': Stores filtered pieces of content. This table is
 * periodically cleared of stale entries by cron.
 *
 * 'cache': Generic cache storage table.
 *
 * The reasons for having several tables are as follows:
 *
 * - smaller tables allow for faster selects and inserts
 * - we try to put fast changing cache items and rather static
 *   ones into different tables. The effect is that only the fast
 *   changing tables will need a lot of writes to disk. The more
 *   static tables will also be better cachable with MySQL's query cache
 *
 * @param $cid
 *   The cache ID of the data to store.
 * @param $table
 *   The table $table to store the data in. Valid core values are 'cache_filter',
 *   'cache_menu', 'cache_page', or 'cache'.
 * @param $data
 *   The data to store in the cache. Complex data types must be serialized first.
 * @param $expire
 *   One of the following values:
 *   - CACHE_PERMANENT: Indicates that the item should never be removed unless
 *     explicitly told to using cache_clear_all() with a cache ID.
 *   - CACHE_TEMPORARY: Indicates that the item should be removed at the next
 *     general cache wipe.
 *   - A Unix timestamp: Indicates that the item should be kept at least until
 *     the given time, after which it behaves like CACHE_TEMPORARY.
 * @param $headers
 *   A string containing HTTP header information for cached pages.
 */
function cache_set($cid, $table = 'cache', $data, $expire = CACHE_PERMANENT, $headers = NULL) {
  db_lock_table($table);
  db_query("UPDATE {%s} SET data = %b, created = %d, expire = %d, headers = '%s' WHERE cid = '%s'", $table, $data, time(), $expire, $headers, $cid);
  if (!db_affected_rows()) {
    @db_query("INSERT INTO {%s} (cid, data, created, expire, headers) VALUES ('%s', %b, %d, %d, '%s')", $table, $cid, $data, time(), $expire, $headers);
  }
  db_unlock_tables();
}

/**
 *
 * Expire data from the cache. If called without arguments, expirable
 * entries will be cleared from the cache_page table.
 *
 * @param $cid
 *   If set, the cache ID to delete. Otherwise, all cache entries that can
 *   expire are deleted.
 *
 * @param $table
 *   If set, the table $table to delete from. Mandatory
 *   argument if $cid is set.
 *
 * @param $wildcard
 *   If set to TRUE, the $cid is treated as a substring
 *   to match rather than a complete ID. The match is a right hand
 *   match. If '*' is given as $cid, the table $table will be emptied.
 */
function cache_clear_all($cid = NULL, $table = NULL, $wildcard = FALSE) {
  global $user;

  if (!isset($cid) && !isset($table)) {
    cache_clear_all(NULL, 'cache_page');
    return;
  }

  if (empty($cid)) {
    if (variable_get('cache_lifetime', 0)) {
      // We store the time in the current user's $user->cache variable which
      // will be saved into the sessions table by sess_write(). We then
      // simulate that the cache was flushed for this user by not returning
      // cached data that was cached before the timestamp.
      $user->cache = time();

      $cache_flush = variable_get('cache_flush', 0);
      if ($cache_flush == 0) {
        // This is the first request to clear the cache, start a timer.
        variable_set('cache_flush', time());
      }
      else if (time() > ($cache_flush + variable_get('cache_lifetime', 0))) {
        // Clear the cache for everyone, cache_flush_delay seconds have
        // passed since the first request to clear the cache.
        db_query("DELETE FROM {%s} WHERE expire != %d AND expire < %d", $table, CACHE_PERMANENT, time());
        variable_set('cache_flush', 0);
      }
    }
    else {
      // No minimum cache lifetime, flush all temporary cache entries now.
      db_query("DELETE FROM {%s} WHERE expire != %d AND expire < %d", $table, CACHE_PERMANENT, time());
    }
  }
  else {
    if ($wildcard) {
      if ($cid == '*') {
        db_query("TRUNCATE TABLE {%s}", $table);
      }
      else {
        db_query("DELETE FROM {%s} WHERE cid LIKE '%s%%'", $table, $cid);
      }
    }
    else {
      db_query("DELETE FROM {%s} WHERE cid = '%s'", $table, $cid);
    }
  }
}


======================

<?php
// +----------------------------------------------------------------------+
// | PEAR :: Cache                                                        |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group                                |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license,       |
// | that is bundled with this package in the file LICENSE, and is        |
// | available at through the world-wide-web at                           |
// | http://www.php.net/license/2_02.txt.                                 |
// | If you did not receive a copy of the PHP license and are unable to   |
// | obtain it through the world-wide-web, please send a note to          |
// | license@php.net so we can mail you a copy immediately.               |
// +----------------------------------------------------------------------+
// | Authors: Ulf Wendel <ulf.wendel@phpdoc.de>                           |
// |          Sebastian Bergmann <sb@sebastian-bergmann.de>               |
// |          Chuck Hagenbuch <chuck@horde.org>                           |
// +----------------------------------------------------------------------+
//
// $Id: db.php,v 1.8 2005/01/26 09:42:30 dufuz Exp $

require_once 'DB.php';
require_once 'Cache/Container.php';

/**
* PEAR/DB Cache Container.
*
* WARNING: Other systems might or might not support certain datatypes of
* the tables shown. As far as I know there's no large binary
* type in SQL-92 or SQL-99. Postgres seems to lack any
* BLOB or TEXT type, for MS-SQL you could use IMAGE, don't know
* about other databases. Please add sugestions for other databases to
* the inline docs.
*
* The field 'changed' has no meaning for the Cache itself. It's just there
* because it's a good idea to have an automatically updated timestamp
* field for debugging in all of your tables.
*
* For _MySQL_ you need this DB table:
*
* CREATE TABLE cache (
*   id          CHAR(32) NOT null DEFAULT '',
*   cachegroup  VARCHAR(127) NOT null DEFAULT '',
*   cachedata   BLOB NOT null DEFAULT '',
*   userdata    VARCHAR(255) NOT null DEFAULT '',
*   expires     INT(9) NOT null DEFAULT 0,
*
*   changed     TIMESTAMP(14) NOT null,
*
*   INDEX (expires),
*   PRIMARY KEY (id, cachegroup)
* )
*
* @author   Sebastian Bergmann <sb@sebastian-bergmann.de>
* @version  $Id: db.php,v 1.8 2005/01/26 09:42:30 dufuz Exp $
* @package  Cache
*/
class Cache_Container_db extends Cache_Container
{

    /**
    * Name of the DB table to store caching data
    *
    * @see  Cache_Container_file::$filename_prefix
    */
    var $cache_table = '';

    /**
    * PEAR DB dsn to use.
    *
    * @var  string
    */
    var $dsn = '';

    /**
    * PEAR DB object
    *
    * @var  object PEAR_DB
    */
    var $db;

    function Cache_Container_db($options)
    {
        if (!is_array($options) || !isset($options['dsn'])) {
            return new Cache_Error('No dsn specified!', __FILE__, __LINE__);
        }

        $this->setOptions($options,  array_merge($this->allowed_options, array('dsn', 'cache_table')));

        if (!$this->dsn) {
            return new Cache_Error('No dsn specified!', __FILE__, __LINE__);
        }
        $this->db = DB::connect($this->dsn, true);
        if (PEAR::isError($this->db)) {
            return new Cache_Error('DB::connect failed: ' . DB::errorMessage($this->db), __FILE__, __LINE__);
        }
        $this->db->setFetchMode(DB_FETCHMODE_ASSOC);
    }

    function fetch($id, $group)
    {
        $query = sprintf("SELECT cachedata, userdata, expires FROM %s WHERE id = '%s' AND cachegroup = '%s'",
                         $this->cache_table,
                         addslashes($id),
                         addslashes($group)
                         );

        $res = $this->db->query($query);

        if (PEAR::isError($res)) {
            return new Cache_Error('DB::query failed: ' . DB::errorMessage($res), __FILE__, __LINE__);
        }
        $row = $res->fetchRow();
        if (is_array($row)) {
            $data = array($row['expires'], $this->decode($row['cachedata']), $row['userdata']);
        } else {
            $data = array(null, null, null);
        }
        // last used required by the garbage collection
        // WARNING: might be MySQL specific
        $query = sprintf("UPDATE %s SET changed = (NOW() + 0) WHERE id = '%s' AND cachegroup = '%s'",
                            $this->cache_table,
                            addslashes($id),
                            addslashes($group)
                          );

        $res = $this->db->query($query);

        if (PEAR::isError($res)) {
            return new Cache_Error('DB::query failed: ' . DB::errorMessage($res), __FILE__, __LINE__);
        }
        return $data;
    }

    /**
    * Stores a dataset.
    *
    * WARNING: we use the SQL command REPLACE INTO this might be
    * MySQL specific. As MySQL is very popular the method should
    * work fine for 95% of you.
    */
    function save($id, $data, $expires, $group, $userdata)
    {
        $this->flushPreload($id, $group);

        $query = sprintf("REPLACE INTO %s (userdata, cachedata, expires, id, cachegroup) VALUES ('%s', '%s', %d, '%s', '%s')",
                         $this->cache_table,
                         addslashes($userdata),
                         addslashes($this->encode($data)),
                          $this->getExpiresAbsolute($expires) ,
                         addslashes($id),
                         addslashes($group)
                        );

        $res = $this->db->query($query);

        if (PEAR::isError($res)) {
            return new Cache_Error('DB::query failed: ' . DB::errorMessage($res) , __FILE__, __LINE__);
        }
    }

    function remove($id, $group)
    {
        $this->flushPreload($id, $group);

        $query = sprintf("DELETE FROM %s WHERE id = '%s' and cachegroup = '%s'",
                         $this->cache_table,
                         addslashes($id),
                         addslashes($group)
                        );

        $res = $this->db->query($query);

        if (PEAR::isError($res)) {
            return new Cache_Error('DB::query failed: ' . DB::errorMessage($res), __FILE__, __LINE__);
        }
    }

    function flush($group = '')
    {
        $this->flushPreload();

         if ($group) {
            $query = sprintf("DELETE FROM %s WHERE cachegroup = '%s'", $this->cache_table, addslashes($group));
        } else {
            $query = sprintf("DELETE FROM %s", $this->cache_table);
        }

        $res = $this->db->query($query);

        if (PEAR::isError($res))
            return new Cache_Error('DB::query failed: ' . DB::errorMessage($res), __FILE__, __LINE__);
    }

    function idExists($id, $group)
    {
        $query = sprintf("SELECT id FROM %s WHERE ID = '%s' AND cachegroup = '%s'",
                         $this->cache_table,
                         addslashes($id),
                         addslashes($group)
                        );

        $res = $this->db->query($query);

        if (PEAR::isError($res)) {
            return new Cache_Error('DB::query failed: ' . DB::errorMessage($res), __FILE__, __LINE__);
        }
        $row = $res->fetchRow();

        if (is_array($row)) {
            return true;
        }
        return false;
    }

    function garbageCollection($maxlifetime)
    {
        $this->flushPreload();

        $query = sprintf('DELETE FROM %s WHERE (expires <= %d AND expires > 0) OR changed <= %d',
                         $this->cache_table,
                         time(),
                         time() - $maxlifetime
                       );

        $res = $this->db->query($query);

        $query = sprintf('select sum(length(cachedata)) as CacheSize from %s',
                         $this->cache_table
                       );
        $cachesize = $this->db->GetOne($query);
        if (PEAR::isError($cachesize)) {
            return new Cache_Error('DB::query failed: ' . DB::errorMessage($cachesize), __FILE__, __LINE__);
        }

        //if cache is to big.
        if ($cachesize > $this->highwater) {
            //find the lowwater mark.
            $query = sprintf('select length(cachedata) as size, changed from %s order by changed DESC',
                                     $this->cache_table
                       );
            $res = $this->db->query($query);
            if (PEAR::isError($res)) {
                return new Cache_Error('DB::query failed: ' . DB::errorMessage($res), __FILE__, __LINE__);
            }

            $numrows = $this->db->numRows($res);
            $keep_size = 0;
            while ($keep_size < $this->lowwater && $numrows--) {
                $entry = $res->fetchRow(DB_FETCHMODE_ASSOC);
                $keep_size += $entry['size'];
            }

            //delete all entries, which were changed before the "lowwwater mark"
            $query = sprintf('delete from %s where changed <= '.($entry['changed'] ? $entry['changed'] : 0),
                                     $this->cache_table
                                   );
            $res = $this->db->query($query);
        }

        if (PEAR::isError($res)) {
            return new Cache_Error('DB::query failed: ' . DB::errorMessage($res), __FILE__, __LINE__);
        }
    }

}
?>


=================


<?php



    /* jpcache_db_connect()

     *

     * Makes connection to the database

     */

    function jpcache_db_connect()

    {

        $GLOBALS["sql_link"] = @mysql_connect($GLOBALS["JPCACHE_DB_HOST"], 

                                              $GLOBALS["JPCACHE_DB_USERNAME"],

                                              $GLOBALS["JPCACHE_DB_PASSWORD"]);

    }

    

    /* jpcache_db_disconnect()

     *

     * Closes connection to the database

     */

    function jpcache_db_disconnect()

    {

        mysql_close($GLOBALS["sql_link"]);

    }

    

    /* jpcache_db_query($query)

     *

     * Executes a given query

     */    

    function jpcache_db_query($query)

    {

        // jpcache_debug("Executing SQL-query $query");

        $ret = @mysql_db_query($GLOBALS["JPCACHE_DB_DATABASE"],

                               $query, 

                               $GLOBALS["sql_link"]);

        return $ret;

    }

    

    /* jpcache_restore()

     *

     * Will try to restore the cachedata from the db.

     */

    function jpcache_restore()

    {

        $res = jpcache_db_query("select GZDATA, DATASIZE, DATACRC from ".

                                    $GLOBALS["JPCACHE_DB_TABLE"].

                                " where CACHEKEY='".

                                    addslashes($GLOBALS["jpcache_key"]).

                                "' and (CACHEEXPIRATION>".

                                    time().

                                " or CACHEEXPIRATION=0)"

                               );

                                

        if ($res && mysql_num_rows($res))

        {

            if ($row = mysql_fetch_array($res))

            {

                // restore data into global scope from found row

                $GLOBALS["jpcachedata_gzdata"]   = $row["GZDATA"];

                $GLOBALS["jpcachedata_datasize"] = $row["DATASIZE"];

                $GLOBALS["jpcachedata_datacrc"]  = $row["DATACRC"];

                return true;

            }

        }

        return false;

    }



    /* jpcache_write()

     *

     * Will (try to) write out the cachedata to the db

     */

    function jpcache_write($gzdata, $datasize, $datacrc) 

    {

        $dbtable = $GLOBALS["JPCACHE_DB_TABLE"];

        

        // XXX: Later on, maybe implement locking mechanism inhere.

        

        // Check if it already exists

        $res = jpcache_db_query("select CACHEEXPIRATION from $dbtable".

                                " where CACHEKEY='".

                                    addslashes($GLOBALS["jpcache_key"]).

                                "'"

                               );

        

        

        if (!$res || mysql_num_rows($res) < 1) 

        {

            // Key not found, so insert

            $res = jpcache_db_query("insert into $dbtable".

                                    " (CACHEKEY, CACHEEXPIRATION, GZDATA,".

                                    " DATASIZE, DATACRC) values ('".

                                        addslashes($GLOBALS["jpcache_key"]).

                                    "',".

                                        (($GLOBALS["JPCACHE_TIME"] != 0) ? 

                                        (time()+$GLOBALS["JPCACHE_TIME"]) : 0).

                                    ",'".

                                        addslashes($gzdata).

                                    "', $datasize, $datacrc)"

                                   );

            // This fails with unique-key violation when another thread has just

            // inserted the same key. Just continue, as the result is (almost) 

            // the same.

        }

        else

        {

            // Key found, so update

            $res = jpcache_db_query("update $dbtable set CACHEEXPIRATION=".

                                        (($GLOBALS["JPCACHE_TIME"] != 0) ?

                                        (time()+$GLOBALS["JPCACHE_TIME"]) : 0).

                                    ", GZDATA='".

                                        addslashes($gzdata).

                                    "', DATASIZE=$datasize, DATACRC=$datacrc where".

                                    " CACHEKEY='".

                                        addslashes($GLOBALS["jpcache_key"]).

                                    "'"

                                   );

            // This might be an update too much, but it shouldn't matter

        }

    }

   

    /* jpcache_do_gc()

     *

     * Performs the actual garbagecollection

     */

    function jpcache_do_gc()

    {

        jpcache_db_query("delete from ".

                            $GLOBALS["JPCACHE_DB_TABLE"].

                         " where CACHEEXPIRATION<=".

                            time().

                         " and CACHEEXPIRATION!=0"

                        );

                        

        // Are we allowed to do an optimize table-call?

        // As noted, first check if this works on your mysql-installation!

        if ($GLOBALS["JPCACHE_OPTIMIZE"])

        {

            jpcache_db_query("OPTIMIZE TABLE ".$GLOBALS["JPCACHE_DB_TABLE"]);                       

        }

    }



    

    /* jpcache_do_start()

     *

     * Additional code that is executed before main jpcache-code kicks in.

     */

    function jpcache_do_start()

    {  

        // Connect to db

        jpcache_db_connect();

    }



    /* jpcache_do_end()

     *

     * Additional code that is executed after caching has been performed,

     * but just before output is returned. No new output can be added!

     */

    function jpcache_do_end()

    {

        // Disconnect from db

        jpcache_db_disconnect();

    }

    

?>

# phpMyAdmin MySQL-Dump

# http://www.htmlwizard.net/phpMyAdmin/

#

# Host: localhost:3300 Database : jpcache



# --------------------------------------------------------

#

# Table structure for table 'CACHEDATA'

#



CREATE TABLE CACHEDATA (

   CACHEKEY varchar(255) NOT NULL,

   CACHEEXPIRATION int(11) NOT NULL,

   GZDATA blob,

   DATASIZE int(11),

   DATACRC int(11),

   PRIMARY KEY (CACHEKEY)

 );