Location: PHPKode > projects > Obsessive Website Statistics > ows/plugins/00_ows_functions.php
<?php
/*
	$Id: 00_ows_functions.php 109 2007-09-27 04:49:13Z randomperson83 $

	Obsessive Web Statistics
    Copyright (C) 2007 Dustin Spicuzza <hide@address.com>

    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 3 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, see <http://www.gnu.org/licenses/>.
	
	
	If you disable this plugin, a lot of program functionality will 
	break. So.. don't do that.
	
	Really, this shouldn't be a plugin actually. 

*/


class MainFunctions implements iPlugin, iInstallablePlugin, iAnalysisPlugin{

	var $options = null;

	// this should return a unique ID identifying the plugin, should start with an alpha,
	// should use basename instead of just __FILE__ otherwise it could expose path information
	public function getPluginId(){
		return 'p'. md5(basename(__FILE__) . get_class());
	}


	// returns an associative array describing the plugin
	public function getPluginInformation(){
		// automagically increment the revision number :)
		$revision = trim(str_replace('Rev:','',str_replace('$','','$Rev: 109 $')));
		return array(
			'author' => 'Dustin Spicuzza (OWS builtin)',
			'pluginName' => 'Primary functions',
			'version' => "1.0.$revision",
			'description' => 'A lot of important program functionality is contained in this. Bad idea to disable it.',
			'url' => 'http://obsessive.sourceforge.net/'
		);
	}
	
	// this function should return a set of arrays that define the dimensions
	// and attributes that this plugin defines. You should not specify an attribute
	// that another plugin defines. This is not website dependent.
	public function define_dimensions(){
	
		global $cfg;
	
		return array(
			'host' => array(
				'pnode_is' => 'Remote-Host',
				'host' => attribute_defn('varchar',254,16)
				),
			'user' => array(
				'pnode_is' => 'Remote-User', 
				'user' => attribute_defn('varchar',254,16)
				),
			'date' => array(
				'pnode_is' => 'Date',
				'date' => attribute_defn('date',null,true),
				'month' => attribute_defn('int'),
				'day_of_month' => attribute_defn('int'),
				'week' => attribute_defn('int'),
				'day_of_week' => attribute_defn('int'),
				'year' => attribute_defn('int')
				),
			'time' => array(
				'pnode_is' => 'Time',
				'time' => attribute_defn('time',null,true),
				'hour' => attribute_defn('int',null,null,true)
				),
			'method' => array(
				'pnode_is' => 'Request-Method',
				'method' => attribute_defn('varchar',4)
				),
			'request' => array(
				'pnode_is' => 'Request',
				'request' => attribute_defn('text',null,$cfg['indexsz_text']),
				'filename' => attribute_defn('text',null,$cfg['indexsz_text']),
				'is_page' => attribute_defn('boolean',null,true),
				'is_download' => attribute_defn('boolean',null,true),
				'is_image' => attribute_defn('boolean',null,true)
				),
			'protocol' => array(
				'pnode_is' => 'Request-Protocol',
				'protocol' => attribute_defn('varchar',16)
				),
			'status' => array(
				'pnode_is' => 'Status',
				'status' => attribute_defn('int',4,true)
				),
			'bytes' => array(
				'pnode_is' => 'Bytes-Sent',
				'bytes' => 
					($cfg['big_files'] ? 
						attribute_defn('bigint',null,true) : 
						attribute_defn('int',null,true) )
				),
			'referrer' => array(
				'pnode_is' => 'Referrer',
				'referrer' => attribute_defn('text',null,$cfg['indexsz_text']),
				'domain' => attribute_defn('text',null,$cfg['indexsz_text']),
				'is_external' => attribute_defn('boolean',null,true)
				),
			'agent' => array(
				'pnode_is' => 'User-Agent',
				'agent' => attribute_defn('text',null,$cfg['indexsz_text'])
				// is_firefox, is_ie, is_opera.. etc?
				)
		);
	}
	
	/*
		Treat this like a constructor. This is called before all phases of
		analysis, and is only called once per website. It should be used to
		clean up website-specific variables.
	*/
	public function InitializeAnalysis($website){
		$this->options = get_website_options($website);
		return true;
	}


	/* 
		This function is called before the round of analysis starts
	
		$ids		An array of all the current ID's for every dimension. If you
					insert new rows into the dimension table, you MUST use and increment
					the ID in the appropriate dimension.
	*/
	public function preAnalysis($website,&$ids){
		return true;
	}

/*
		This function is called for each line grabbed from the logfile. 
		
		$website		The current website being worked on
		
		$dimension  	This parameter defines which dimension is being analyzed
						at the moment. The function may be called multiple times
						for a row with different dimensions as arguments.
		
		$line			This contains an array of data that was retrieved
						from the logfile.
		
		This function should return an item that is the 'primary node' of the
		dimension (defined as an attribute with the same name as the dimension). 
		
		You should return false if you do not define a primary node for that dimension,
		or if there is an error.
		
	*/
	public function getPrimaryNode($website, $dimension, $line){
	
		// this is ugly, but the alternative is uglier..
		switch($dimension){
			case 'host':
				return (strlen($line['Remote-Host']) ? substr($line['Remote-Host'],0,254) : '');
				
			case 'user':
				return (strlen($line['Remote-User']) ? substr($line['Remote-User'],0,254) : '');
				
			case 'date':
				return date('Y-m-d', strtotime(str_replace('/',' ',$line['Date'])));
				
			case 'time':
				return date('H:i:s', strtotime($line['Time']));
				
			case 'method':
				return (strlen($line['Method']) ? substr($line['Request-Method'],0,4) : '');
				
			case 'request':
			
				if (array_key_exists('Request',$line))
					return $line['Request'];
				else if (array_key_exists('Request-Path',$line))
					if (array_key_exists('Query-String',$line))
						return $line['Request-Path'] . ($line['Query-String'] != '-' ? '?' . $line['Query-String'] : '');
					else
						return $line['Request-Path'];
				break;
				
			case 'protocol':
				return (strlen($line['Protocol']) ? substr($line['Request-Protocol'],0,16) : '');
				
			case 'status':
				return intval($line['Status']);
				
			case 'bytes':
				return $line['Bytes-Sent'];
				
			case 'referrer':
				return $line['Referrer'];
			
			case 'agent':
			
				if (array_key_exists('User-Agent',$line))
					return $line['User-Agent'];
				else if (array_key_exists('UD-User-Agent',$line))
					return urldecode($line['UD-User-Agent']);
				break;
		}
		
		return show_error("Invalid dimension passed to plugin\"" . get_class() . "\"");
	}
	
	
	/*
		This function is called for each line grabbed from the logfile. 
		
		$website		The current website being worked on
		
		$dimension  	This parameter defines which dimension is being analyzed
						at the moment. The function may be called multiple times
						for a row with different dimensions as arguments.
		
		$pnode			This contains the primary node of the dimension.
	
		The plugin should only return attributes that are defined in the
		define_dimensions function. This function should return an array 
		in the form of
			
			array('attribute' => 'value', ...)
			
		which defines values to be uploaded to the SQL database. It should NOT return
		the primary node. Please note that the returned values can be cached, so this 
		function may NOT always be called for each row. You should ALWAYS return
		an array with the same keys each time, in the same order.
		
		If you do not define any attributes in the current dimension, or if there is 
		an error, then return false.
	*/
	public function getAttributes($website, $dimension, $pnode){
		
		global $cfg;
	
		// this is ugly, but the alternative is uglier..
		// TODO: some of these preg_match may not need it? use strpos instead?
		switch($dimension){
			case 'date':
				$ts = strtotime($pnode);
				return array(
					'month' => intval(date('m',$ts )),
					'day_of_month' => intval(date('j',$ts )),
					'week' => intval(date('W',$ts)),
					'day_of_week' => intval(date('w',$ts )),
					'year' => intval(date('Y',$ts ))
				);
				
			case 'time':
				$ts = strtotime($pnode);
				return array(
					'hour' => date('H',$ts)
				);
			
			case 'request':
				$filename = substr($pnode ,0,($pos = strpos($pnode,'?')) !== false ? $pos : strlen($pnode));
				
				$pinfo = pathinfo($filename,PATHINFO_EXTENSION);
		
				if ($pinfo == null)
					return array(
						'filename' => $filename,
						'is_page' => true,
						'is_download' => false,
						'is_image' => false
					);
				
				return array(
					'filename' => $filename,
					'is_page' => in_array($pinfo,$cfg['ext_page']) ? true : false,
					'is_download' => in_array($pinfo,$cfg['ext_download']) ? true : false,
					'is_image' => in_array($pinfo,$cfg['ext_image']) ? true : false,
				);

			case 'referrer':

				$url = @parse_url($pnode);
				
				if (is_array($url) && array_key_exists('host',$url))
					return array(
						'domain' => $url['host'],
						'is_external' =>
							(in_array($url['host'],$this->options['aliases']) ? false : true)
					);
				else
					return array(
						'domain' => '',
						'is_external' => true
					);
				
			//case 'agent':
				//return array(
					// is_firefox, is_ie, is_opera.. etc?
				//);
		}
		
		return show_error("Invalid dimension passed to plugin\"" . get_class() . "\"");
	
	}
	
	/* 
		this function is called after the round of analysis is complete. Called many times.
	
		$ids		An array of all the current ID's for every dimension. If you
					insert new rows into the dimension table, you MUST use and increment
					the ID in the appropriate dimension. $ids[$dimensionname] is how it would
					be referenced.
	*/
	public function postAnalysis($website,&$ids){
		return true;
	}
	
	
	

	// This should create tables and/or alter rows. Useful especially for analysis plugins. 
	// Return true if success, false otherwise. It should handle its own transactions.
	// Should NOT create dimensions. Dimensions are created after this function is called.
	// Suggestion: use set_config_var(get_class() . '_installed') for this
	//	
	//	@param 	$website 		Any tables created should be in the form of $website . _keyname						
	function install($website){
	
		global $cfg;
	
		$s_log_table = db_escape_string(str_replace('.','_',$website) . '_config');

	    $sql = array();
		
		// config table
		$sql[] = "CREATE TABLE $s_log_table (
			config_key varchar(254) PRIMARY KEY NOT NULL,
			config_value text NOT NULL
		)" . ($cfg['db_type'] == 'mysql' ? ' ENGINE=InnoDB DEFAULT CHARSET=latin1' : '');
		
		$sql[] = "CREATE INDEX i_config_key ON $s_log_table (config_key(32))";
		
		foreach ($sql as $statement)
			if (!db_is_valid_result(db_query($statement)))
				return false;
		
		return true;
	}
	
	
	// returns true or false. $log_table is NOT escaped
	function isInstalled($website){
		$log_table = db_escape_string(str_replace('.','_',$website));
		return db_has_rows(db_query("SHOW TABLES LIKE '${log_table}_config'"));
	}
	
	// this should remove any information from the database
	function uninstall($website){
		$log_table = db_escape_string(str_replace('.','_',$website));
		return db_is_valid_result(db_query("DROP TABLE ${log_table}_config"));
	}
}


$main_functions = new MainFunctions();
register_plugin('analysis',$main_functions);
register_plugin('installable',$main_functions);

?>
Return current item: Obsessive Website Statistics