<?php
/**
* This file implements the LinkCache class.
*
* This file is part of the evoCore framework - {@link http://evocore.net/}
* See also {@link https://github.com/b2evolution/b2evolution}.
*
* @license GNU GPL v2 - {@link http://b2evolution.net/about/gnu-gpl-license}
*
* @copyright (c)2003-2020 by Francois Planque - {@link http://fplanque.com/}
*
* @package evocore
*/
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
load_class( '_core/model/dataobjects/_dataobjectcache.class.php', 'DataObjectCache' );
/**
* LinkCache Class
*
* @package evocore
*/
class LinkCache extends DataObjectCache
{
/**
* Cache for item -> array of object references
* @access private
* @var array
*/
var $cache_item = array();
/**
* @access private
* @var array Remember full loads
*/
var $loaded_cache_item = array();
/**
* Cache for comment -> array of object references
* @access private
* @var array
*/
var $cache_comment = array();
/**
* @access private
* @var array Remember full loads
*/
var $loaded_cache_comment = array();
/**
* Cache for user -> array of object references
* @access private
* @var array
*/
var $cache_user = array();
/**
* @access private
* @var array Remember full loads
*/
var $loaded_cache_user = array();
/**
* Cache for email campaigns -> array of object references
* @access private
* @var array
*/
var $cache_emailcampaign = array();
/**
* @access private
* @var array Remember full loads
*/
var $loaded_cache_emailcampaign = array();
/**
* Cache for messages -> array of object references
* @access private
* @var array
*/
var $cache_message = array();
/**
* @access private
* @var array Remember full loads
*/
var $loaded_cache_message = array();
/**
* Cache for temporary objects -> array of object references
* @access private
* @var array
*/
var $cache_temporary = array();
/**
* @access private
* @var array Remember full loads
*/
var $loaded_cache_temporary = array();
/**
* Cache for file -> array of object references
* @access private
* @var array
*/
var $cache_file = array();
/**
* @access private
* @var array Remember full loads
*/
var $loaded_cache_file = array();
/**
* Constructor
*/
function __construct()
{
parent::__construct( 'Link', false, 'T_links', 'link_', 'link_ID' );
}
/**
* Add a dataobject to the cache
*
* @param object Link
* @param string Type of cached object ( '' - empty value to cache depend on link owner type, 'file' - to cache file )
* @return boolean TRUE on success
*/
function add( $Obj, $cache_type = '' )
{
if( ( !isset($Obj->ID) ) || ( $Obj->ID == 0 ) )
{ // The object is not cachable, becuase it has no valid ID
return false;
}
// If the object wasn't already cached and is valid:
$this->cache[$Obj->ID] = $Obj;
if( $cache_type == 'file' )
{ // Cache by File ID
$this->cache_file[$Obj->file_ID][$Obj->ID] = $Obj;
return true;
}
// Cache by Item, Comment or User, we need to get the owner
$LinkOwner = & $Obj->get_LinkOwner();
if( $LinkOwner == NULL )
{ // LinkOwner is not valid
return false;
}
$link_object_ID = $LinkOwner->get_ID();
switch( $LinkOwner->type )
{
case 'item': // cache indexed by Item ID
$this->cache_item[$link_object_ID][$Obj->ID] = $Obj;
break;
case 'comment': // cache indexed by Comment ID
$this->cache_comment[$link_object_ID][$Obj->ID] = $Obj;
break;
case 'user': // cache indexed by User ID
$this->cache_user[$link_object_ID][$Obj->ID] = $Obj;
break;
case 'emailcampaign': // cache indexed by EmailCampaign ID
$this->cache_emailcampaign[$link_object_ID][$Obj->ID] = & $Obj;
break;
case 'message': // cache indexed by Message ID
$this->cache_message[$link_object_ID][$Obj->ID] = & $Obj;
break;
case 'temporary': // cache indexed by Temporary ID
$this->cache_temporary[$link_object_ID][$Obj->ID] = & $Obj;
break;
default:
return false;
}
return true;
}
/**
* Remove object from the cache
*
* @param object Link
* @param string Type of cached object ( '' - empty value to cache depend on link owner type, 'file' - to cache file )
* @return boolean TRUE on success
*/
function remove( & $Obj, $cache_type = '' )
{
if( ( !isset($Obj->ID) ) || ( $Obj->ID == 0 ) )
{ // The object is not cachable, so it can't be in the cache
return false;
}
$this->remove_by_ID( $Obj->ID );
if( ( $cache_type == 'file' ) && ( isset( $this->cache_file[$Obj->file_ID][$Obj->ID] ) ) )
{ // Cache by File ID
unset( $this->cache_file[$Obj->file_ID][$Obj->ID] );
return true;
}
// Cache by Item, Comment or User, we need to get the owner
$LinkOwner = & $Obj->get_LinkOwner();
if( $LinkOwner == NULL )
{ // LinkOwner is not valid
return false;
}
$link_object_ID = $LinkOwner->get_ID();
switch( $LinkOwner->type )
{
case 'item':
if( isset( $this->cache_item[$link_object_ID][$Obj->ID] ) )
{
unset( $this->cache_item[$link_object_ID][$Obj->ID] );
return true;
}
break;
case 'comment':
if( isset( $this->cache_comment[$link_object_ID][$Obj->ID] ) )
{
unset( $this->cache_comment[$link_object_ID][$Obj->ID] );
return true;
}
break;
case 'user':
if( isset( $this->cache_user[$link_object_ID][$Obj->ID] ) )
{
unset( $this->cache_user[$link_object_ID][$Obj->ID] );
return true;
}
break;
case 'emailcampaign':
if( isset( $this->cache_emailcampaign[$link_object_ID][$Obj->ID] ) )
{
unset( $this->cache_emailcampaign[$link_object_ID][$Obj->ID] );
return true;
}
break;
case 'message':
if( isset( $this->cache_message[$link_object_ID][$Obj->ID] ) )
{
unset( $this->cache_message[$link_object_ID][$Obj->ID] );
return true;
}
break;
case 'temporary':
if( isset( $this->cache_temporary[$link_object_ID][$Obj->ID] ) )
{
unset( $this->cache_temporary[$link_object_ID][$Obj->ID] );
return true;
}
break;
case 'file':
default: // Invalid cache type
break;
}
// Object was not in the cache
return false;
}
/**
* Returns links for a given Item
*
* Loads if necessary
*
* @todo dh> does not get used anywhere (yet)?
*
* @param integer item ID to load links for
* @return array of refs to Link objects
*/
function & get_by_item_ID( $item_ID )
{
// Make sure links are loaded:
$this->load_by_item_ID( $item_ID );
return $this->cache_item[$item_ID];
}
/**
* Returns links for a given Comment
*
* Loads if necessary
*
* @param integer comment ID to load links for
* @return array of refs to Link objects
*/
function & get_by_comment_ID( $comment_ID )
{
// Make sure links are loaded:
$this->load_by_comment_ID( $comment_ID );
return $this->cache_comment[$comment_ID];
}
/**
* Returns links for a given User
*
* Loads if necessary
*
* @param integer user ID to load links for
* @return array of refs to Link objects
*/
function & get_by_user_ID( $user_ID )
{
// Make sure links are loaded:
$this->load_by_user_ID( $user_ID );
return $this->cache_user[$user_ID];
}
/**
* Returns links for a given Email Campaign
*
* Loads if necessary
*
* @param integer Email campaign ID to load links for
* @return array of refs to Link objects
*/
function & get_by_emailcampaign_ID( $emailcampaign_ID )
{
// Make sure links are loaded:
$this->load_by_emailcampaign_ID( $emailcampaign_ID );
return $this->cache_emailcampaign[ $emailcampaign_ID ];
}
/**
* Returns links for a given Message
*
* Loads if necessary
*
* @param integer Message ID to load links for
* @return array of refs to Link objects
*/
function & get_by_message_ID( $message_ID )
{
// Make sure links are loaded:
$this->load_by_message_ID( $message_ID );
return $this->cache_message[ $message_ID ];
}
/**
* Returns links for a given temporary object
*
* Loads if necessary
*
* @param integer Temporary ID to load links for
* @return array of refs to Link objects
*/
function & get_by_temporary_ID( $temporary_ID )
{
// Make sure links are loaded:
$this->load_by_temporary_ID( $temporary_ID );
return $this->cache_temporary[ $temporary_ID ];
}
/**
* Returns links for a given File
*
* Loads if necessary
*
* @param integer file ID to load links for
* @return array of refs to Link objects
*/
function & get_by_file_ID( $file_ID )
{
// Make sure links are loaded:
$this->load_by_file_ID( $file_ID );
return $this->cache_file[$file_ID];
}
/**
* Load a set of Links by the given link type into the cache.
*
* @param SQL SQL object
* @param string Cache type
*/
function load_type_by_sql( $SQL, $cache_type )
{
global $DB;
$links = $DB->get_results( $SQL );
// Load linked files into the FileCache
$this->load_linked_files( $links );
foreach( $links as $row )
{ // Cache each matching object:
$this->add( new Link( $row ), $cache_type );
}
}
/**
* Load links for a given Item
*
* Optimization: If the Item happens to be in the current MainList, Links for the whole MainList will be cached.
*
* @todo cache Link targets before letting the Link constructor handle it
*
* @param integer item ID to load links for
*/
function load_by_item_ID( $item_ID )
{
global $DB, $Debuglog, $MainList, $ItemList;
if( isset( $this->loaded_cache_item[$item_ID] ) )
{
$Debuglog->add( "Already loaded <strong>$this->objtype(Item #$item_ID)</strong> into cache", 'dataobjects' );
return false;
}
// Check if this Item is part of the MainList
if( $MainList || $ItemList )
{
$prefetch_IDs = array();
if( $MainList )
{
$prefetch_IDs = array_merge($prefetch_IDs, $MainList->get_page_ID_array());
}
if( $ItemList )
{
$prefetch_IDs = array_merge($prefetch_IDs, $ItemList->get_page_ID_array());
}
$Debuglog->add( "Loading <strong>$this->objtype(Item #$item_ID)</strong> into cache as part of MainList/ItemList...");
$this->load_by_item_list( $prefetch_IDs );
}
else
{ // NO, load Links for this single Item:
// Remember this special load:
$this->cache_item[$item_ID] = array();
$this->loaded_cache_item[$item_ID] = true;
$Debuglog->add( "Loading <strong>$this->objtype(Item #$item_ID)</strong> into cache", 'dataobjects' );
$SQL = new SQL( 'Get the links by item ID #'.$item_ID );
$SQL->SELECT( '*' );
$SQL->FROM( 'T_links' );
$SQL->WHERE( 'link_itm_ID = '.$DB->quote( $item_ID ) );
$SQL->ORDER_BY( 'link_order' );
$SQL->append( 'FOR UPDATE' ); // fp: we specify FOR UPDATE because we need to lock all changes to the link_order column.
// fp: Note: FOR UPDATE won't do anything if we're not in a transaction (which is good)
$this->load_type_by_sql( $SQL, 'item' );
}
return true;
}
/**
* Load links for a given Comment
*
* @param integer comment ID to load links for
*/
function load_by_comment_ID( $comment_ID )
{
global $DB, $Debuglog;
if( isset( $this->loaded_cache_comment[$comment_ID] ) )
{
$Debuglog->add( "Already loaded <strong>$this->objtype(Comment #$comment_ID)</strong> into cache", 'dataobjects' );
return false;
}
// Remember this special load:
$this->cache_comment[$comment_ID] = array();
$this->loaded_cache_comment[$comment_ID] = true;
$Debuglog->add( "Loading <strong>$this->objtype(Comment #$comment_ID)</strong> into cache", 'dataobjects' );
$SQL = new SQL( 'Get the links by comment ID #'.$comment_ID );
$SQL->SELECT( '*' );
$SQL->FROM( 'T_links' );
$SQL->WHERE( 'link_cmt_ID = '.$DB->quote( $comment_ID ) );
$SQL->ORDER_BY( 'link_order' );
$this->load_type_by_sql( $SQL, 'comment' );
return true;
}
/**
* Load links for a given User
*
* @param integer user ID to load links for
*/
function load_by_user_ID( $user_ID )
{
global $DB, $Debuglog;
if( isset( $this->loaded_cache_user[$user_ID] ) )
{
$Debuglog->add( "Already loaded <strong>$this->objtype(User #$user_ID)</strong> into cache", 'dataobjects' );
return false;
}
// Remember this special load:
$this->cache_user[$user_ID] = array();
$this->loaded_cache_user[$user_ID] = true;
$Debuglog->add( "Loading <strong>$this->objtype(User #$user_ID)</strong> into cache", 'dataobjects' );
$SQL = new SQL( 'Get the links by user ID #'.$user_ID );
$SQL->SELECT( '*' );
$SQL->FROM( 'T_links' );
$SQL->WHERE( 'link_usr_ID = '.$DB->quote( $user_ID ) );
$SQL->ORDER_BY( 'link_order' );
$this->load_type_by_sql( $SQL, 'user' );
return true;
}
/**
* Load links for a given Email Campaign
*
* @param integer Email campaign ID to load links for
*/
function load_by_emailcampaign_ID( $emailcampaign_ID )
{
global $DB, $Debuglog;
if( isset( $this->loaded_cache_emailcampaign[ $emailcampaign_ID ] ) )
{
$Debuglog->add( "Already loaded <strong>$this->objtype(EmailCampaign #$emailcampaign_ID)</strong> into cache", 'dataobjects' );
return false;
}
// Remember this special load:
$this->cache_emailcampaign[ $emailcampaign_ID ] = array();
$this->loaded_cache_emailcampaign[ $emailcampaign_ID ] = true;
$Debuglog->add( "Loading <strong>$this->objtype(EmailCampaign #$emailcampaign_ID)</strong> into cache", 'dataobjects' );
$SQL = new SQL( 'Get the links by email campaign ID #'.$emailcampaign_ID );
$SQL->SELECT( '*' );
$SQL->FROM( 'T_links' );
$SQL->WHERE( 'link_ecmp_ID = '.$DB->quote( $emailcampaign_ID ) );
$SQL->ORDER_BY( 'link_order' );
$this->load_type_by_sql( $SQL, 'emailcampaign' );
return true;
}
/**
* Load links for a given Email Campaign
*
* @param integer Message ID to load links for
*/
function load_by_message_ID( $message_ID )
{
global $DB, $Debuglog;
if( isset( $this->loaded_cache_message[ $message_ID ] ) )
{
$Debuglog->add( "Already loaded <strong>$this->objtype(Message #$message_ID)</strong> into cache", 'dataobjects' );
return false;
}
// Remember this special load:
$this->cache_message[ $message_ID ] = array();
$this->loaded_cache_message[ $message_ID ] = true;
$Debuglog->add( "Loading <strong>$this->objtype(Message #$message_ID)</strong> into cache", 'dataobjects' );
$SQL = new SQL( 'Get the links by message ID #'.$message_ID );
$SQL->SELECT( '*' );
$SQL->FROM( 'T_links' );
$SQL->WHERE( 'link_msg_ID = '.$DB->quote( $message_ID ) );
$SQL->ORDER_BY( 'link_order' );
$this->load_type_by_sql( $SQL, 'message' );
return true;
}
/**
* Load links for a given Temporary ID object
*
* @param integer Temporary ID to load links for
*/
function load_by_temporary_ID( $temporary_ID )
{
global $DB, $Debuglog;
if( isset( $this->loaded_cache_temporary[ $temporary_ID ] ) )
{
$Debuglog->add( "Already loaded <strong>$this->objtype(Temporary #$temporary_ID)</strong> into cache", 'dataobjects' );
return false;
}
// Remember this special load:
$this->cache_temporary[ $temporary_ID ] = array();
$this->loaded_cache_temporary[ $temporary_ID ] = true;
$Debuglog->add( "Loading <strong>$this->objtype(Temporary #$temporary_ID)</strong> into cache", 'dataobjects' );
$SQL = new SQL( 'Get the links by temporary ID #'.$temporary_ID );
$SQL->SELECT( '*' );
$SQL->FROM( 'T_links' );
$SQL->WHERE( 'link_tmp_ID = '.$DB->quote( $temporary_ID ) );
$SQL->ORDER_BY( 'link_order' );
$this->load_type_by_sql( $SQL, 'temporary' );
return true;
}
/**
* Load links for a given Item list
*
* @todo cache Link targets before letting the Link constructor handle it
*
* @param array of of item IDs to load links for
*/
function load_by_item_list( $itemIDarray )
{
global $DB, $Debuglog;
if( empty( $itemIDarray ) )
{ // Nothing to load:
return false;
}
$item_list = implode( ',', $itemIDarray );
$Debuglog->add( "Loading <strong>$this->objtype(Items #$item_list)</strong> into cache", 'dataobjects' );
// For each item in list...
foreach( $itemIDarray as $item_ID )
{ // Remember this special load:
$this->cache_item[$item_ID] = array();
$this->loaded_cache_item[$item_ID] = true;
}
$SQL = new SQL( 'Get the links by item IDs: '.$item_list );
$SQL->SELECT( '*' );
$SQL->FROM( 'T_links' );
$SQL->WHERE( 'link_itm_ID IN ('.$item_list.')' );
$SQL->ORDER_BY( 'link_itm_ID, link_order' );
$this->load_type_by_sql( $SQL, 'item' );
return true;
}
/**
* Load links for a given Comment list
*
* @todo cache Link targets before letting the Link constructor handle it
*
* @param array Comment IDs to load links for
*/
function load_by_comment_list( $comment_IDs )
{
global $DB, $Debuglog;
if( empty( $comment_IDs ) )
{ // Nothing to load:
return;
}
$comment_list = implode( ',', $comment_IDs );
$Debuglog->add( "Loading <strong>$this->objtype(Comments #$comment_list)</strong> into cache", 'dataobjects' );
foreach( $comment_IDs as $comment_ID )
{ // Remember this special load for each comment in list:
$this->cache_comment[ $comment_ID ] = array();
$this->loaded_cache_comment[ $comment_ID ] = true;
}
$SQL = new SQL( 'Get the links by comment IDs: '.$comment_list );
$SQL->SELECT( '*' );
$SQL->FROM( 'T_links' );
$SQL->WHERE( 'link_cmt_ID IN ( '.$comment_list.' )' );
$SQL->ORDER_BY( 'link_cmt_ID, link_order' );
$this->load_type_by_sql( $SQL, 'comment' );
}
/**
* Load links for a given File
*
* @param integer file ID to load links for
*/
function load_by_file_ID( $file_ID )
{
global $DB, $Debuglog;
if( isset( $this->loaded_cache_file[$file_ID] ) )
{
$Debuglog->add( "Already loaded <strong>$this->objtype(File #$file_ID)</strong> into cache", 'dataobjects' );
return false;
}
// Remember this special load:
$this->cache_file[$file_ID] = array();
$this->loaded_cache_file[$file_ID] = true;
$Debuglog->add( "Loading <strong>$this->objtype(File #$file_ID)</strong> into cache", 'dataobjects' );
$SQL = new SQL( 'Get the links by file ID #'.$file_ID );
$SQL->SELECT( '*' );
$SQL->FROM( 'T_links' );
$SQL->WHERE( 'link_file_ID = '.$DB->quote( $file_ID ) );
$SQL->ORDER_BY( 'link_ID' );
$links = $DB->get_results( $SQL );
foreach( $links as $row )
{ // Cache each matching object:
$this->add( new Link( $row ), 'file' );
}
return true;
}
/**
* Load required Files into the FileCache before Link class constructor will be called.
* It's imporatnt to load all Files with one query instead of loading the one by one
*
* private function
*
* @param array link rows
*/
function load_linked_files( & $link_rows )
{
if( empty( $link_rows ) )
{ // There are nothing to load
return;
}
// Collect required file Ids
$link_file_ids = array();
foreach( $link_rows as $row )
{
if( ! empty( $row->link_file_ID ) )
{ // Skip a link with worn file ID:
$link_file_ids[] = $row->link_file_ID;
}
}
if( ! empty( $link_file_ids ) )
{ // Load required Files into FileCache:
$FileCache = & get_FileCache();
$FileCache->load_list( $link_file_ids );
}
}
/**
* Clear the cache **extensively**
*
* @param boolean Keep copy of cache in case we try to re instantiate previous object
* @param string What to clear: 'all', 'user', 'item', 'comment', 'emailcampaign', 'message', 'file'
* @param integer ID of the clearing object
*/
function clear( $keep_shadow = false, $object = 'all', $object_ID = 0 )
{
parent::clear( $keep_shadow );
switch( $object )
{
case 'all':
// Clear all cached objects
$this->cache_item = array();
$this->loaded_cache_item = array();
$this->cache_comment = array();
$this->loaded_cache_comment = array();
$this->cache_user = array();
$this->loaded_cache_user = array();
$this->loaded_cache_emailcampaign = array();
$this->loaded_cache_message = array();
$this->cache_file = array();
$this->loaded_cache_file = array();
break;
case 'item':
case 'comment':
case 'user':
case 'emailcampaign':
case 'message':
case 'temporary':
case 'file':
// Clear only the selected type of objects
if( empty( $object_ID ) )
{ // Clear all cached objects of this type
$this->{'cache_'.$object} = array();
$this->{'loaded_cache_'.$object} = array();
}
else
{ // Clear a cache only one object
unset( $this->{'cache_'.$object}[ $object_ID ] );
unset( $this->{'loaded_cache_'.$object}[ $object_ID ] );
}
break;
}
}
}
?>