Location: PHPKode > projects > PHP Authentication using Uma > uma/DataObject.class.php
<?
/**
 * This is my Object class for data abstractions. I wrote this because 
 * I got sick of writing the same code over and over again. It works well 
 * for me, but that doesn't mean it will work well for you. I have made
 * a few assumptions and have listed them here:
 * You are using my Connection class or have implemented your own 
 * Connection class with the same methods and functionality.
 * You have your database designed with each table containing a 
 * PRIMARY_KEY that is an int. I always call this column 'id'
 * but you can tell the Object class which column is your primary key 
 * by calling setPrimaryKeyElement.
 * You do not modify any of the member variables and use the methods 
 * provided to modify the data in the member variables.
 * I can't think of any reason for not modifying them, but I just don't 
 * think you should because it's bad practice and future releases may have 
 * a problem with you directly modifying them.
		
 * Caveats: This Object class only really represents on table row in a 
 * database. It gets one row from the database and doesn't deal with 
 * objects that may span multiple tables. However, you could override 
 * all the methods in this class get fancier. I choose to approach 
 * this type of problem by overriding the get() method in my implementation
 * class. Since I setup an extension of this class for every table 
 * in the database and only want to deal with data as objects, simply 
 * overriding the get() method works well for me. In my get() method 
 * I use the foriegn key to return an object for whatever data I am after. 
 * For example:
 *	User extends Object and user has data in a Permissions 
 * 	table that we will want to ge able to access as well.
 * 	I override the get() in my User class that returns an array of 
 *	Permission objects when a program calls get("permission"). 
 *	This is a bad example and I am still working on the implementation.
 * @author  Keith Vance (hide@address.com) February 2002
 * Copyright 2002 Vance Consulting LLC
 */

include_once("Connection.class.php");
include_once("config.php");

class DataObject extends Connection {
    var $autocommit;
    var $_logging;
    var $_logging_fp;
    var $_verify;
    var $_verify_err;
    var $_verified;
    var $_class;
    var $_object;
    var $_elements;
    var $_object_type;
    var $_id;
    var $_pkey;
    var $_ini;
    
    /**
     * Constructor.
     *
     * @param String $primary_key Database primary key column name
     * @param String [$type] This is more of name than a type. Currently
     * this usually corresponds to the name of the table.
     * @param Int [$id] This is the primary key for record to load when
     * constructing this object.
     * @param String [$datasource] You could override the config.php 
     * DS_NAME value here if you want to specify the data source name
     * as something different from what is set in the config.php file
     * @author Keith Vance <hide@address.com>
     */
    function DataObject($primary_key, $type = false, $id = 0, 
			$datasource = DS_NAME) {
	$debug = FALSE;
	// Not yet doing anything with this.
	if (phpversion() >= "4.2") {
	    $this->_ini = ini_get_all();
	}
	
	$this->autocommit = AUTO_COMMIT;
	$this->_elements = FALSE;
	$this->_logging = FALSE;
	$this->_verify = FALSE;
	$this->_verified = TRUE;
	$this->_verify_err = TRUE;
	$this->_pkey = FALSE;
			
	if (!$type) new FatalError(NO_OBJECT_TYPE, __LINE__, "Object");

	$this->_pkey = $primary_key;
	$this->_object_type = $type;
	if (!$this->_dbhost) {
	    $this->_dbhost = DS_HOST;
	}

	if (!$this->_database) {
	    $this->_database = DS_NAME;
	}

	if (!$this->_dbuser) {
	    $this->_dbuser = DS_USER;
	}

	if (!$this->_dbpassword) {
	    $this->_dbpassword = DS_PASSWD;
	}
	
	if ($debug) {
	    echo "db host: " . $this->_dbhost . "<br>\n";
	    echo "database: " . $this->_database . "<br>\n";
	    echo "db user: " . $this->_dbuser . "<br>\n";
	}
			
	parent::Connection();
	if (!$this->live()) new FatalError(CONNECT_FAILED, __LINE__, 
					   $this->_class);
	
	// First load up the _elements member with all the column names
	$this->_elements = $this->getTableData($this->_object_type);
	if (count($this->_elements) == 0) {
	    return new Error(RECORD_NOT_FOUND, __LINE__, $this->_class);
	}
			
	// If id is set to something other than 0, try and get the 
	// record from the database and load up the object.
	if ($id > 0) {
	    $sql = "select * from " . $this->_object_type . 
		" where (" . $this->_pkey . " = '$id')";
	    if (!$this->live()) {
		$this->connect();
		if (!$this->live()) {
		    new FatalError(CONNECT_FAILED, __LINE__, $this->_class);
		}
	    }
				
	    $this->query($sql);
	    
	    if ($this->numRows() == 0) {
		return new Error(RECORD_NOT_FOUND, __LINE__, $this->_class);
	    } elseif ($this->numRows() > 1) {
		new FatalError(DUPLICATE_RECORD_FOUND, __LINE__, 
			       $this->_class);
	    }
				
	    $this->next();
	    if ($debug) echo "Loading DataObject<br>\n";
	    for ($i = 0, $j = count($this->_elements); $i < $j; $i++) {
		if ($debug) {
		    echo "element -> " . $this->_elements[$i] . 
			" value -> " . 	$this->_record[$this->_elements[$i]] . 
			"<br>\n";
		}
		$this->_object[$this->_elements[$i]] = 
		    $this->_record[$this->_elements[$i]];
	    }
	    
	    $this->_id = $id;
	} else {
	    for ($i = 0, $j = count($this->_elements); $i < $j; $i++) {
		$this->_object[$this->_elements[$i]] = "";
	    }
	}
    }
		
    /**
     * Checks to see if a record exists with the $attribute and $value
     * specified. This is convenient if you wish to find out if a
     * user exists with the username ($attribute) of 'keith' ($value).
     * 
     * @param String $attribute to query against.
     * @param String $value of attribute quering against
     * @return Boolean
     * @author Keith Vance <hide@address.com>
     */
    function exists($attribute, $value) {
	if (!$this->live()) {
	    $this->connect();
	    if (!$this->live()) {
		new FatalError(CONNECT_FAILED, __LINE__, $this->_class);
	    }
	}
			
	$sql = "select * from " . $this->_object_type . 
	    " where $attribute='$value'";
	$this->query($sql);
	if ($this->numRows() > 0) {
	    return TRUE;
	} else {
	    return FALSE;
	}
    }
		
    /**
     * Returns TRUE if verifying is enabled.
     *
     * @return Boolean
     * @author Keith Vance <hide@address.com>
     */ 
    function isVerifyOn() {
	return $this->_verify;
    }
    
    /**
     * Turn verification on or off.
     *
     * @param Boolean TRUE to turn on verification, FALSE to disable
     * @author Keith Vance <hide@address.com>
     */
    function verify($boo) {
	if ($boo) $this->_verify_err = array();
	$this->_verify = $boo;
    }
	
    /**
     * Reset the verification error array.
     *
     * @author Keith Vance <hide@address.com>
     */
    function resetVerificationError() {
	$this->_verify_err = array();
    }

    /**
     * Set a verification error.
     *
     * @param String $name Verification error name, key in Hash
     * @param String $msg Error message for verification error.
     * @author Keith Vance <hide@address.com>
     */
    function setVerificationError($name, $msg) {
	$this->_verify_err[$name][] = $msg;
    }
	
    /**
     * Checks to see if verification is enabled.
     *
     * @return Boolean
     * @author Keith Vance <hide@address.com>
     */
    function isVerified() {
	return empty($this->_verify_err);
    }
    
    /**
     * Get a Verification error from the _verify_err Hash
     *
     * @param Boolean [$reset] Defaults to FALSE, but if set to TRUE
     * the verification errors are cleared.
     * @author Keith Vance <hide@address.com>
     */
    function getVerificationError($reset = FALSE) {
	if ($reset) reset($this->_verify_err);
	$ret =  current($this->_verify_err);
	next($this->_verify_err);
	return $ret;
    }

    /**
     * Get the verification error hash
     *
     * @return Hash Verification error hash the keys are the name of error
     * and the value is the error message.
     * @author Keith Vance <hide@address.com>
     */
    function getVerificationErrors() {
	return $this->_verify_err;
    }
    
    /**
     * Turns logging on or off.
     * 
     * @param Boolean [$boo] Defaults is FALSE, if TRUE turn on logging
     * and attempt to create log dir with 2775 permissions.
     * @author Keith Vance <hide@address.com>
     */
    function logging($boo = FALSE) {
	if ($boo) {
	    @mkdir("log", 2775);
	    $this->_logging_fp = @fopen("log/" . $this->_class . ".log", "w");
	    @fwrite($this->_logging_fp, date(DATE_FORMAT) . 
		    " Logging Stopped\n");
	} else {
	    if ($this->_logging_fp) {
		@fwrite($this->_logging_fp, date(DATE_FORMAT) . 
			" Logging Stopped\n");
		@fclose($this->_logging_fp);
	    }
	}
	$this->_logging = $boo;
    }
    
    /**
     * Write to log file.
     *
     * @param String $page The page which caused the error.
     * @param $tring $msg The message to log
     * @param Int [$line] The line number where the error occured.
     * @param Boolean [$notifiy] Send email notification of log entry.
     * @author Keith Vance <hide@address.com>
     */
    function log($page, $msg, $line = 0, $notify = FALSE) {
	if (!$this->isLoggingOn()) {
	    return;
	}
	$text = date(DATE_FORMAT) . " $page $msg [$line]\n";
	if ($notify) {
	    mail(ERROR_MESSAGE_RECPT, "DataObject using " . 
		 $this->_class . " class Error", $text);
	}
	@fwrite($this->_logging_fp, $text);
	@fflush($this->_logging_fp);
    }
    
    /**
     * Determine if logging is on or off.
     * 
     * @return Boolean
     * @author Keith Vance <hide@address.com>
     */
    function isLoggingOn() {
	return $this->_logging;
    }
    
    /**
     * This sets the primary key for the database this object is working
     * with. I use only one column as the primary and I always call it 'id',
     * some people may not call it 'id' and that's fine 'cause you can 
     * set it here.
     * However I don't think this will work if you are making your primary key
     * for your table something more complicated like a combination of columns.
     * @author Keith Vance 2.20.02 11:23pm
     */
    function setPrimaryKeyElement($key) {
	$this->_pkey = $key;
    }
	
    function getPKey() {
	return $this->getPrimaryKeyElement();
    }
    /**
     * Gets the primary key.
     * @author Keith Vance 2.20.02 11:23pm
     */
    function getPrimaryKeyElement() {
	if ($this->_pkey == "") {
	    new FatalError(NO_PRIMARY_KEY, __LINE__, $this->_class);
	}
	return $this->_pkey;
    }
		
    /**
     * Returns the element from this object.
     * Elements are the same as column names and are 
     * in fact generated by getting a list of columns
     * from the database.
     * This allows you to reference the column names to retrieve
     * any piece of data you wish about a given object.
     * @param String $element name of element to begotten
     * @return String element you wish to get or FALSE if element is not 
     * defined
     * @author Keith Vance 2.20.02 10:00pm
     */
    function get($element) {
	if (isset($this->_object[$element])) {
	    return stripslashes($this->_object[$element]);
	} else {
	    return FALSE;
	}
    }
		
    /**
     * Returns this object hash.
     *
     * @return Hash
     * @author Keith Vance (hide@address.com) 03.24.02
     */
    function getObject() {
	return $this->_object;
    }
		
    /**
     * Returns the array of elements.
     * The elements array is derived from getting
     * a list of all columns in the table.
     *
     * @return Array
     * @author Keith Vance 2.20.02 10:04pm
     */
    function getAllElements() {
	return $this->_elements;
    }
    
    /**
     * Sets the specified element with the specified value.
     *
     * @param String $element Element in object to set.
     * @param String $value Value to set element to.
     * @author Keith Vance 2.20.02 10:05pm
     */
    function set($element, $value) {
	if (get_magic_quotes_gpc() || get_magic_quotes_runtime()) {
	    $this->_object[$element] = $value;
	} else {
	    $this->_object[$element] = addslashes($value);
	}
	
	if (SET_LAST_MODIFIED) { 
	    $this->_object[LAST_MODIFIED] = time();
	}
    }
		
    /**
     * Sets all the elements and values.
     * $values is a hash where the keys are the 
     * element names and the values are the values.
     * If you put element names in the array key that don't
     * exist in the table you will either get an error you save.
     * This method overwrites any data in this object and if you
     * call save after this everything dump in here will be written
     * to the database, unless you have specified invalid data then 
     * and error will ocurr when trying to save.
     * 
     * @param Hash
     * @author Keith Vance 2.20.02 10:41pm
     */
    function setAll($values) {
	if (is_array($values)) {
	    foreach ($values as $key=>$val) {
		$this->set($key, $val);
	    }
	} else {
	    return FALSE;
	}
	return TRUE;
    }
		
    /**
     * Saves this Object to the database.
     * If FatalError occurs application dies hard.
     * If Error occurs the error is thrown back to the
     * calling script, allowing it to deal with it. For instance,
     * DUPLICATE_RECORD error - this may not be a problem but the calling
     * script needs to determine whether it is a problem or not.
     * @author Keith Vance 2.20.02 10:05pm
     */
    function save() {
	$debug = FALSE;
	
	// Make sure the connetion is still and if not fire it up.
	if (!$this->live()) {
	    $this->connect();
	    if (!$this->live()) {
		new FatalError(CONNECT_FAILED, __LINE__, $this->_class);
	    }
	}
				
	if ($this->_object[$this->_pkey] == 0) {
	    // insert data into database.
	    // Build the INSERT sql statement
	    $cols = "";
	    $vals = "";
	    for ($i = 0, $j = count($this->_elements); $i < $j; $i++) {
		// If the element is the PRIMARY_KEY in the database
		// don't attempt to insert it, I am assuming that the
		// primary key is an auto_incrementing int and will be
		// set by the database.
		if ($this->_elements[$i] != $this->getPrimaryKeyElement() &&
		    isset($this->_object[$this->_elements[$i]])) {
		    $cols .= $this->_elements[$i] . ", ";
		    $vals .= "'" . $this->_object[$this->_elements[$i]]."', ";
		}
	    }
	    
	    // whack the trail comma and space from cols and vals, else
	    // we will have invalid sql.
	    $cols = substr($cols, 0, strlen($cols) - 2);
	    $vals = substr($vals, 0, strlen($vals) - 2);
	    $sql = "INSERT INTO " . $this->_object_type . 
		" ($cols) VALUES ($vals)";
	    if ($debug) echo "Object->save():: $sql <br>\n";
	    $this->log($_SERVER['PHP_SELF'], 
		       "save() INSERT sql: $sql", __LINE__);
	    $this->query($sql);
	    // Grab the new PRIMARY KEY for the record just inserted and
	    // stick into the object.
	    $this->set($this->getPrimaryKeyElement(), $this->insertId());
	    // if error is 1062 Duplicate record throw an Error object 
	    // back to the calling script so they can deal with it the way 
	    // they want to.
	    if ($this->_errornumber == 1062) 
		return new Error(DUPLICATE_RECORD, __LINE__, $this->_class);
				
	    if ($this->affectedRows() != 1 || $this->_errornumber != 0) {
		new FatalError(INSERT_FAILED, __LINE__, $this->_class);
	    }
	} else {
	    // update database.
	    $sql = "UPDATE " . $this->_object_type . " SET ";
	    for ($i = 0, $j = count($this->_elements); $i < $j; $i++) {
		if (isset($this->_object[$this->_elements[$i]])) {
		    $sql .= $this->_elements[$i] . " = '" . 
			addslashes($this->_object[$this->_elements[$i]]) . "'";
			$sql .= ", ";
		}
	    }
	    
	    // whack the comma off the end of the $sql variable
	    $sql = substr($sql, 0, (strlen($sql) - 2));
	    $sql .= " WHERE " . $this->_pkey . " = '" . 
		$this->_object[$this->_pkey] . "'";
	    if ($debug) echo "Object->save():: $sql<br>\n";
	    $this->query($sql);
	    if ($this->_errornumber != 0) {
		new FatalError(UPDATE_FAILED, __LINE__, $this->_class);
	    }    
	}
	return TRUE;
    }
		
    /**
     * Deletes this Object from the database.
     * @author Keith Vance2.20.02 10:05pm
     */
    function delete() {
	$debug = false;
	
	// Make sure the connetion is still and if not fire it up.
	if (!$this->live()) {
	    $this->connect();
	    if (!$this->live()) {
		new FatalError(CONNECT_FAILED, __LINE__, $this->_class);
	    }
	}
	
	if ($this->_object[$this->_pkey] > 0) {
	    $sql = "delete from " . $this->_object_type . " where " . 
		$this->_pkey . " = '" . $this->_object[$this->_pkey] . "'";
	    $this->query($sql);
	    if ($this->affectedRows() != 1 || $this->_errornumber != 0) {
		new FatalError(DELETE_FAILED, __LINE__, $this->_class);
	    }
	} else {
	    return false;
	}
	
	$this->_object = false;
	return true;
    }
		
    /**
     * Gets all the Objects from the database.
     * This needs to be implemented in the class that extends this one, 
     * it makes more sense because I want to return an array of objects 
     * which of the correct type so you can call all the methods on 
     * the objects in the array that are available to the specific
     * object not just the ones available here. This may not be the 
     * smartest thing to do but it seems to make sense from my point of view.
     * 
     * @author Keith Vance 2.20.2 10:17pm
     */
    function getAll() {
	new FatalError(METHOD_NOT_IMPLEMENTED, __LINE__, $this->_class);
    }
		
    /** The following methods are for debugging and testing and 
		not intended for any other purpose.
    **/
    function printElements() {
	echo "<br><font color='blue'>print \$this->_elements<br>\n";
	for ($i = 0, $j = count($this->_elements); $i < $j; $i++) {
	    echo $i . " " . $this->_elements[$i] . "<br>\n";
	}
	echo "<br></font><br><br>\n";
    }
    
    function printObject() {
	echo "<br><font color='red'>print \$this->_object<br>\n";
	for ($i = 0, $j = count($this->_object); $i < $j; $i++) {
	    echo "key->" . $this->_elements[$i] . " val->" . $this->_object[$this->_elements[$i]] . "<br>\n";
	}
	echo "<br></font><br><br>\n";
    }

    function scrubText(&$str) {
	$str = strip_tags($str, '<i><b><br>');
	//$str = addslashes($str);
    }

    function dump_var($msg, $var) {
	ob_start();
	echo $msg . "\n";
	print_r($var);
	error_log(ob_get_contents());
	ob_end_clean();
    }

} // end Object class
	
/**
 * This is my generic error handler class for handling Fatal errors 
 * that kill the script when the occur.
 * @author Keith Vance (hide@address.com) 2.20.02
 * Copyright 2002 Vance Consulting LLC
 */
class FatalError {
    var $_debug;	// If debug is TRUE the script will die and 
                        // print an error message
			// If debug is FALSE the script is meta-refreshed 
                        // to error.php and a message is sent to the 
			// webmaster.
    var $_messages;
    var $_code;
		
    function FatalError($code = 0, $line, $class) {
	$this->_debug = TRUE;
	$this->_code = $code;
	$this->_messages[NO_OBJECT_TYPE] = "You need to specify a Object type when you instantiate this class.";
	$this->_messages[NO_OBJECT] = "You are trying to use a non-object where an object is required";
	$this->_messages[RECORD_NOT_FOUND] = "The record you are looking for doesn't exist.";
	$this->_messages[QUERY_FAILED] = "Your query failed.";
	$this->_messages[CONNECT_FAILED] = "Your attempt to connect to the datasource failed.";
	$this->_messages[DUPLICATE_RECORD_FOUND] = "Duplicate record found.";
	$this->_messages[NO_PRIMARY_KEY] = "You need to set which key is your primary key before you can retrieve it.";
	$this->_messages[INSERT_FAILED] = "Data INSERT failed.";
	$this->_messages[UPDATE_FAILED] = "Data UPDATE failed.";
	$this->_messages[DELETE_FAILED] = "Data DELETE faile.";
	$this->_messages[METHOD_NOT_IMPLEMENTED] = "You are called a method that has not been properly implemented.<br>This probably means that you should override the method in your class and provide the logic.";
	if ($debug) {
	    die(sprintf("Error [%d]<br>Class [%s]<br>Line Number [" . $line . "]<br>%s", $this->getCode(), $class, $this->getMessage()));
	} else {
	    mail(ERROR_MESSAGE_RECPT, "Fatal Error " . SITE_NAME, "A fatal error ocurred at " . date(DATE_FORMAT) . " running $class on $line\n");
	    echo "<meta http-equiv='Refresh' content='1'>\n";
	}
    }
    
    function getCode() {
	return $this->_code;
    }
		
    function setCode($c) {
	$this->_code = $c;
    }
		
    function getMessage() {
	return $this->_messages[$this->getCode()];
    }
}	// end FatalError class
	
class Error {
    var $_messages;
    var $_code;
    
    function Error($code = 0, $line, $class) {
	$this->_code = $code;
	$this->_messages['RECORD_NOT_FOUND'] = "The record you are looking for doesn't exist.";
	$this->_messages['DUPLICATE_RECORD'] = "Duplicate record found.";
	$this->_messages['NO_SAVE_VERIFICATION'] = "You need to override this save method in order to use verification.";
    }
		
    function getCode() {
	return $this->_code;
    }
    
    function setCode($c) {
	$this->_code = $c;
    }
    
    function getMessage() {
	return $this->_messages[$this->getCode()];
    }
}
?>
Return current item: PHP Authentication using Uma