Location: PHPKode > projects > Nzbirc > nzbirc-1.4/modules/store.php
<?php

/**************************************************
 * NZBirc v1
 * Copyright (c) 2006 Harry Bragg
 * tiberious.org
 * Module: store
 **************************************************
 *
 * Full GPL License: <http://www.gnu.org/licenses/gpl.txt>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */

class Net_SmartIRC_module_store extends Net_SmartIRC_module_base
{

	// default module variable
	var $name = 'store';
	var $version = 'v0.3';
	var $description = 'saves variables to an xml file, and loads them again';
	
	/**
	 * Store Module
	 *
	 * STARTCONFIG
	 * @var string filename - Filename to store the variables in
	 * @var string workerFilename - Filename to communicate with the worker thread
	 * @var string folder	- Folder for all the storage variables
	 * ENDCONFIG
	 */  
	var $_config = array(
		'filename' => 'store.xml',
		'workerFilename' => 'worker.xml',
		'folder' => 'store/'
	);
	
	/**
	 * @var XML_Serializer
	 * @access private
	 */
	var $_toXML;
	
	/**
	 * @var XML_UnSerializer
	 * @access private
	 */
	var $_fromXML;
	
	var $_def = array(
		'getRevision' => '/<revision _type="integer">(\d+)<\/revision>/i',
		'getUpdated' => '/<updated _type="integer">(\d+)<\/updated>/i'
	);
	
	function _registerCommands()
	{
	 	global $irc;
	 	
	 	$options = array(
			XML_SERIALIZER_OPTION_INDENT           => '    ',
			XML_SERIALIZER_OPTION_RETURN_RESULT    => true,
			XML_SERIALIZER_OPTION_ATTRIBUTES_KEY   => '_attributes',
			XML_SERIALIZER_OPTION_TYPEHINTS        => true,
			XML_SERIALIZER_OPTION_ROOT_NAME        => 'store',
			XML_SERIALIZER_OPTION_XML_DECL_ENABLED => true,
			XML_SERIALIZER_OPTION_XML_ENCODING     => $irc->config->encoding
		);
	 	
		$this->_toXML = &new XML_Serializer( $options );
		
		$options = array(
			XML_UNSERIALIZER_OPTION_RETURN_RESULT    => true,
			XML_UNSERIALIZER_OPTION_GUESS_TYPES      => true,
			XML_UNSERIALIZER_OPTION_ENCODING_SOURCE  => $irc->config->encoding
		);
		
		$this->_fromXML = &new XML_Unserializer( $options );
		
		// do file/folder checks
		
		if ( !is_dir( $irc->config->userPath.$this->_config['folder'] ) )
		{
			$irc->modDebug( 'store', 'Found no store folder, importing old files and creating new files...', __FILE__, __LINE__ );
			// create directory
			if (mkdir( $irc->config->userPath.$this->_config['folder'], 0755, true ))
			{
				$irc->modDebug( 'store', 'Created folder: '.$irc->config->userPath.$this->_config['folder'], __FILE__, __LINE__ );
			}
			else
			{
				$irc->modDebug( 'store', 'Failed to create folder: '.$irc->config->userPath.$this->_config['folder'], __FILE__, __LINE__ );			
			}
			// import old settings
			if ( file_exists( $this->_config['filename'] ) )
			{
				$this->xmlRead();
				
				rename( $this->_config['filename'], $this->_config['filename'].'.bak' );
			}
			
			if ( file_exists( $this->_config['workerFilename'] ) )
			{
				$this->xmlRead( true );
				
				rename( $this->_config['workerFilename'], $this->_config['workerFilename'].'.bak' );
			}
		}		
	}
	
	function _customClose()
	{
		global $irc;
		
		$irc->unregisterTimeid( $irc->_storeTimerID );
		$irc->unregisterTimeid( $irc->_storeWorkerTimerID );
		if ( isset( $irc->_storeDelayTimerID ) )
		{
			$irc->unregisterTimerid( $irc->_storeDelayTimerID );
			unset( $irc->_storeDelayTimerID );
		}		
	}
	
	/*****************************************************
	 * Class interface instructions
	 *****************************************************/	
	
	/**
	 * Add a variable to the stored list
	 *
	 * @var string $name - name of the variable (inside $irc->)
	 * @return bool
	 * @access public
	 */
	function addVariable( $name )
	{
	 	global $irc;
	 	
		$irc->_storeRevisions[$name] = array(
			'revision' => 0,
			'updated' => 0
		);
		$irc->modDebug('store', 'Added variable $irc->'.$name.' to the store list', __FILE__, __LINE__ );
		
		return $this->xmlVarSync( $name );
	}
	
	/**
	 * Set an item in a variable (All store variables are assumed to be arrays)
	 *
	 * @var string $name - Name of the variable
	 * @var mixed $data  - Data to send
	 * @var string $key  - If a specfic key is required, use that key
	 * @return bool
	 * @access public
	 */	 
	function setVariable( $name, $data, $key = false )
	{
	 	global $irc;
	 	 
		// check the variable
		if ( isset( $irc->_storeRevisions[$name] ) ) 
		{
			$this->xmlVarSync( $name );
			
		 	$ref = &$irc->$name;
			// check key
			if ( $key !== false )
				$ref[$key] = $data;
			else
				$ref = $data;
			
			$irc->_storeRevisions[$name]['revision']++;
			$irc->_storeRevisions[$name]['updated'] = time();

			$irc->modDebug( 'store', 'Added data to variable: $irc->'.$name, __FILE__, __LINE__ );
			
			$this->xmlVarSync( $name );
		}
		else
		{
			$irc->modDebug( 'store', 'Failed to add data to variable: $irc->'.$name.' (variable does not exist)', __FILE__, __LINE__ );
			return false;
		}
	}

	/**
	 * Unset an item in a variable (All store variables are assumed to be arrays)
	 *
	 * @var string $name - Name of the variable
	 * @var string $key  - If a specfic key is required, use that key
	 * @return bool
	 * @access public
	 */	
	function unsetVariable( $name, $key = false )
	{
	 	global $irc;
	 	
		if ( isset( $irc->_storeRevisions[$name] ) )
		{
			$this->xmlVarSync( $name );
			
			$ref = &$irc->$name;
			
			// check key
			if ( $key !== false )
				unset( $ref[$key] );
			else
				unset( $ref );
			
			$irc->_storeRevisions[$name]['revision']++;
			$irc->_storeRevisions[$name]['updated'] = time();
			
			$irc->modDebug( 'store', sprintf( 'Unset %s variable: $irc->%s', ( ( $key )? $key.' from':''), $name ), __FILE__, __LINE__ );
			
			$this->xmlVarSync( $name );
			return true;
		}
		else
		{
			$irc->modDebug( 'store', 'Failed to remove data to variable: $irc->'.$name.' (variable does not exist)', __FILE__, __LINE__ );
			return false;
		}
	}
	
	/**
	 * Updated a variable manually
	 *
	 * @var string $name
	 * @return void
	 * @access public
	 */
	function manualUpdate( $name, $new = false )
	{
	 	global $irc;
		
		if ( is_array( $name ) )
		{			
			foreach( $name as $var )
			{				
				if ( $new )
				{
					$irc->_storeRevisions[$var]['revision']++;
					$irc->_storeRevisions[$var]['updated'] = time();
				}
				
				$this->xmlVarSync( $var );
			}
		}
		else
		{
			if ( $new )
			{
				$irc->_storeRevisions[$name]['revision']++;
				$irc->_storeRevisions[$name]['updated'] = time();
			}
				
			$this->xmlVarSync( $name );
		}
	}
	
	/**
	 * Syncronise a stored variable with the xml file
	 *
	 * @param string $name - The name of the stored variable
	 * @return boolean - On success/failure
	 */
	function xmlVarSync( $name )
	{
		global $irc;

		$filename = $irc->config->userPath.$this->_config['folder'].$name.'.xml';
		
		$irc->modDebug( 'store', 'xmlVarSync, name: '.$name.', filename: '.$filename, __FILE__, __LINE__, SMARTIRC_DEBUG_MESSAGEPARSER);

		$xmlData = array();
		$rev = 0;
		$upd = 0;
		
		if ( file_exists( $filename ))
		{
			// do some quick revivision checking
			$var = file_get_contents( $filename );
				
			if (preg_match( $this->_def['getRevision'], $var, $match ))
			{
				$rev = $match[1];
			}
			if (preg_match( $this->_def['getUpdated'], $var, $match ))
			{
				$upd = $match[1];
			}
		}
		
		$toWrite = false;
		
		// check revisions
		if ( $rev > 0 )
		{		
			if ( ( $rev > $irc->_storeRevisions[$name]['revision'] ) ||
				 ( ( $rev == $irc->_storeRevisions[$name]['revision'] ) &&
				   ( $upd > $irc->_storeRevisions[$name]['updated'] ) ) )
			{
				$irc->modDebug( 'store', 
					sprintf( 'Updating %s, file is newer, f[rev: %d, upd: %d] l[rev: %d, upd: %d]', 
						$name, $rev, $upd,
						$irc->_storeRevisions[$name]['revision'],
						$irc->_storeRevisions[$name]['updated'] ), 
					__FILE__, __LINE__, SMARTIRC_DEBUG_MESSAGEPARSER );
					
				// if the file's revision is newer than the local revision, read from the file only
				$xmlData = $this->_fromXML->unserialize( $filename, true );			
				if ( PEAR::isError( $xmlData ) )
				{
				   $irc->modDebug( 'store', 'XML Error: '.$xmlData->getMessage(), __FILE__, __LINE__, SMARTIRC_DEBUG_MODULES );
				   $xmlData['revision'] = $irc->_storeRevisions[$name]['revision'];
				   $xmlData['updated'] = $irc->_storeRevisions[$name]['updated'];
				   $xmlData['value'] = $irc->$name;
				}
				else
				{		
					$ref = &$irc->$name;
					$ref = $xmlData['value'];
					$irc->_storeRevisions[$name]['revision'] = $rev;
					$irc->_storeRevisions[$name]['updated'] = $upd;
					
					if ( isset( $ref['lastUpdate'] ) )
					{
						unset( $ref['lastUpdate'] );	
					}					
				}
			}
			else if ( ( $rev < $irc->_storeRevisions[$name]['revision'] ) ||
				      ( ( $rev == $irc->_storeRevisions[$name]['revision'] ) &&
				        ( $upd < $irc->_storeRevisions[$name]['updated'] ) ) )
			{
				$irc->modDebug( 'store', 
					sprintf( 'Updating %s, local is newer, f[rev: %d, upd: %d] l[rev: %d, upd: %d]', 
						$name, $rev, $upd,
						$irc->_storeRevisions[$name]['revision'],
						$irc->_storeRevisions[$name]['updated'] ), 
					__FILE__, __LINE__, SMARTIRC_DEBUG_MESSAGEPARSER );
				
				
				// if the local variable is newer than the files revision, write to the file
				$xmlData['revision'] = $irc->_storeRevisions[$name]['revision'];
				$xmlData['updated'] = $irc->_storeRevisions[$name]['updated'];
				
				if ( isset( $irc->$name ) )
				{
					$xmlData['value'] = $irc->$name;
				}
				else
				{
					$xmlData['value'] = '';	
				}
				$toWrite = true;
			}
			else
			{
				$irc->modDebug( 'store', 
					sprintf( 'Updating %s, everything is the same, f[rev: %d, upd: %d] l[rev: %d, upd: %d]', 
						$name, $rev, $upd,
						$irc->_storeRevisions[$name]['revision'],
						$irc->_storeRevisions[$name]['updated'] ), 
					__FILE__, __LINE__, SMARTIRC_DEBUG_MESSAGEPARSER );
			}
		}
		else
		{
			$irc->modDebug( 'store', 
				sprintf( 'Updating %s, file is empty/blank, f[rev: %d, upd: %d] l[rev: %d, upd: %d]', 
					$name, $rev, $upd,
					$irc->_storeRevisions[$name]['revision'],
					$irc->_storeRevisions[$name]['updated'] ), 
				__FILE__, __LINE__, SMARTIRC_DEBUG_MESSAGEPARSER );
					
			// nothing in the file, lets write one
			$xmlData['revision'] = $irc->_storeRevisions[$name]['revision'];
			$xmlData['updated'] = $irc->_storeRevisions[$name]['updated'];
			if ( isset( $irc->$name ) )
			{
				$xmlData['value'] = $irc->$name;
			}
			else
			{
				$xmlData['value'] = '';	
			}
			$toWrite = true;
		}
		
		if ( $toWrite )
		{
			if ( isset( $xmlData['value']['lastUpdate'] ) )
			{
				unset( $xmlData['value']['lastUpdate'] );
			}
		 	$wxml = $this->_toXML->serialize( $xmlData );
	
			$fp = fopen( $filename, 'w' );
			fwrite( $fp, $wxml );
			fflush( $fp );
			fclose( $fp );
			unset( $wxml, $fp );
		}
		
		unset( $xmlData );
		
		return true;
	}
	
	/**
	 * Sync the variables with the xml file
	 * Checks to see if the file has been updated after the variable
	 * if so, overwrites the variable
	 * otherwise, variable gets written to the file
	 *
	 * @access public
	 */
	function xmlRead( $worker = false )
	{
		global $irc;
		
		$toWrite = array();
		
		$filename = ( $worker )? $this->_config['workerFilename']:$this->_config['filename'];
		
		if ( !file_exists( $filename ) )
		{
			$xmlData = array();
		}
		else
		{						
			$xmlData = $this->_fromXML->unserialize( $filename, true );
			if ( PEAR::isError( $xmlData ) )
			{
			   $irc->modDebug( 'store', 'XML Error: '.$xmlData->getMessage(), __FILE__, __LINE__, SMARTIRC_DEBUG_MODULES );
			   $xmlData = array();
			}
		}
			
		foreach( $xmlData as $name => $value )
		{
			$irc->modDebug( 'store', 'Importing variable: '.$name, __FILE__, __LINE__ );
			
			if ( isset( $value['lastUpdate'] ) )
			{
				unset( $value['lastUpdate'] );
			}
			
			$toWrite['revision'] = 1;
			$toWrite['updated'] = time();
			$toWrite['value'] = $value;
			
			$wxml = $this->_toXML->serialize( $toWrite );
			
			$fname = $irc->config->userPath.$this->_config['folder'].$name.'.xml';

			$fp = fopen( $fname, 'w' );
			fwrite( $fp, $wxml );
			fflush( $fp );
			fclose( $fp );
			
			$irc->modDebug( 'store', 'Wrote file: '.$fname, __FILE__, __LINE__ );
			
			unset( $toWrite, $wxml, $fp );
		}
		
		unset( $xmlData ); // clean up the major memory users
		return true;
	}	
}

?>
Return current item: Nzbirc