<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
// +----------------------------------------------------------------------+
// | Copyright (c) 2004 Jon Wood                                          |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 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: Jon Wood <jon@jellybob.co.uk>                               |
// +----------------------------------------------------------------------+
//
// $Id: DB.php,v 1.3 2004/07/07 16:05:30 jellybob Exp $

/**
 * Require the core class.
 */
require_once('Auth/PrefManager2/Container.php');

/**
 * Require PEAR MDB2 for data management.
 */
require_once('MDB2.php');

/**
 * The PEAR MDB2 container for Auth_PrefManager2
 *
 * @author Jon Wood <jon@jellybob.co.uk>
 * @package Auth_PrefManager2
 * @category Authentication
 */
class Auth_PrefManager2_Container_MDB2 extends Auth_PrefManager2_Container
{
    
/**
     * The MDB2 object being used for data access.
     * 
     * @access private
     * @var MDB2
     */
    
var $_db null;
       
    function 
Auth_PrefManager2_Container_DB($options = array())
    {
        
$this->Auth_PrefManager2_Container($options);
        return 
$this->_connect();
    }
    
    
/**
     * Sets a value with the container.
     *
     * @param string $owner The owner to set the preference for.
     * @param string $preference The name of the preference to set.
     * @param string $application The application to set for.
     * @return bool Success/failure
     * @access protected
     * @abstract
     */
    
function _set($owner$preference$value$application)
    {
        if (
$this->_exists($owner$preference$application)) {
            
// If the preference already exists update its value.
            
$query sprintf('UPDATE %s SET %s=%s WHERE %s=%s AND %s=%s AND %s=%s',
                             
$this->_options['table'],
                             
$this->_options['value_column'],
                             
$this->_db->quote($value'text'),
                             
$this->_options['owner_column'],
                             
$this->_db->quote($owner'text'),
                             
$this->_options['preference_column'],
                             
$this->_db->quote($preference'text'),
                             
$this->_options['application_column'],
                             
$this->_db->quote($application'text'));
        } else {
            
// Otherwise insert a new row.
            
$query sprintf('INSERT INTO %s (%s, %s, %s, %s) VALUES(%s, %s, %s, %s)',
                             
$this->_options['table'],
                             
$this->_options['value_column'],
                             
$this->_options['owner_column'],
                             
$this->_options['preference_column'],
                             
$this->_options['application_column'],
                             
$this->_db->quote($value'text'),
                             
$this->_db->quote($owner'text'),
                             
$this->_db->quote($preference'text'),
                             
$this->_db->quote($application'text'));
        }
        
        
$result $this->_runExec($query);
        
        if (!
is_null($result)) {
            return 
true;
        }
        
        return 
false;
    }
    
    
/**
     * Gets a value from the container.
     *
     * @param string $owner The owner to set the preference for.
     * @param string $preference The name of the preference to set.
     * @param mixed $value The value to set the preference to.
     * @param string $application The application to set for.
     * @return mixed|null The value, or null if none is set.
     * @access protected
     * @abstract
     */
    
function _get($owner$preference$application)
    {
        if (
$this->_exists($owner$preference$application)) {
            
// If the preference already exists update its value.
            
$query sprintf('SELECT %s FROM %s WHERE %s=%s AND %s=%s AND %s=%s',
                             
$this->_options['value_column'],
                             
$this->_options['table'],
                             
$this->_options['owner_column'],
                             
$this->_db->quote($owner'text'),
                             
$this->_options['preference_column'],
                             
$this->_db->quote($preference'text'),
                             
$this->_options['application_column'],
                             
$this->_db->quote($application'text'));
        } else {
            return 
null;
        }
        
        
$result $this->_runQuery($query);
        
        if (!
PEAR::isError($result)) {
            return 
$result->fetchOne();
        }
        
        return 
null;
    }
    
    
/**
     * Deletes a value from the container.
     *
     * @param string $owner The owner to delete the preference for.
     * @param string $preference The name of the preference to delete.
     * @param string $application The application to delete from.
     * @return bool Success/failure
     * @access protected
     * @abstract
     */
    
function _delete($owner$preference$application)
    {
        if (
$this->_exists($owner$preference$application)) {
            
// If the preference already exists update its value.
            
$query sprintf('DELETE FROM %s WHERE %s=%s AND %s=%s AND %s=%s',
                             
$this->_options['table'],
                             
$this->_options['owner_column'],
                             
$this->_db->quote($owner'text'),
                             
$this->_options['preference_column'],
                             
$this->_db->quote($preference'text'),
                             
$this->_options['application_column'],
                             
$this->_db->quote($application'text'));
        } else {
            
// Should this be returning true, since the value is no longer
            // there, or false, because no delete has been done?
            
return true;
        }
        
        
$result $this->_runExec($query);
        
        return !
PEAR::isError($result);
    }
    
    
/**
     * Checks if the specified preference exists in the data container.
     *
     * Returns null if an error occurs.
     *
     * @param string $owner The owner to delete the preference for.
     * @param string $preference The name of the preference to delete.
     * @param string $application The application to delete from.
     * @return bool Does the pref exist?
     * @access protected
     * @abstract
     */
    
function _exists($owner$preference$application)
    {
        
$query sprintf('SELECT COUNT(%s) FROM %s WHERE %s=%s AND %s=%s AND %s=%s',
                         
$this->_options['owner_column'],
                         
$this->_options['table'],
                         
$this->_options['owner_column'],
                         
$this->_db->quote($owner'text'),
                         
$this->_options['preference_column'],
                         
$this->_db->quote($preference'text'),
                         
$this->_options['application_column'],
                         
$this->_db->quote($application'text'));
                         echo 
$query "<br />";
        
        
$result $this->_runQuery($query);
        if (!
PEAR::isError($result)) {
            return (bool)
$result->fetchOne();
        }
        
        return 
null;
    }
    
    
/**
     * Connects to the DSN provided in the options array.
     *
     * @return bool Success/failure
     * @throws AUTH_PREFMANAGER2_DB_CONNECTION_FAILED
     * @access private
     */
    
function _connect()
    {
        
$db =& MDB2::connect($this->_options['dsn']);
        
        if (
PEAR::isError($db)) {
            
$this->_throwError(AUTH_PREFMANAGER2_DB_CONNECT_FAILED'error', array('dsn' => $this->_options['dsn']), $db);
            return 
false;
        }
        
        
$this->_db $db;
        return 
true;
    }
    
    
/**
     * Runs a query on the database.
     *
     * Returns null on error.
     *
     * @param string $query The query to run.
     * @return integer|PEAR_Error The result for the query.
     * @throws AUTH_PREFMANAGER2_DB_QUERY_FAILED
     * @access private
     * @todo Improve the connection handling here.
     */
    
function &_runExec($query)
    {
        if (!
MDB2::isConnection($this->_db)) {
            
$this->_connect();
        }
        
        if (!
MDB2::isConnection($this->_db)) {
            
$result $this->_db->exec($query);
            if (
PEAR::isError($result)) {
                
$this->_throwError(AUTH_PREFMANAGER2_DB_QUERY_FAILED'error', array('query' => $query), $result);
                return 
null;
            }
            
            return 
$result;
        }
        
        return 
null;
    }
    
    
/**
     * Runs a query on the database.
     *
     * Returns null on error.
     *
     * @param string $query The query to run.
     * @return MDB2_Result|PEAR_Error The result for the query.
     * @throws AUTH_PREFMANAGER2_DB_QUERY_FAILED
     * @access private
     * @todo Improve the connection handling here.
     */
    
function &_runQuery($query)
    {
        if (!
MDB2::isConnection($this->_db)) {
            
$this->_connect();
        }
        
        if (!
MDB2::isConnection($this->_db)) {
            
$result $this->_db->query($query);
            if (
PEAR::isError($result)) {
                
$this->_throwError(AUTH_PREFMANAGER2_DB_QUERY_FAILED'error', array('query' => $query), $result);
                return 
null;
            }
            
            return 
$result;
        }
        
        return 
null;
    }
    
    
/**
     * Reads the options array, and sets default values for anything
     * which isn't set.
     *
     * @param array $options An array of options.
     * @return void
     * @access protected
     */
    
function _parseOptions($options)
    {
        if (!isset(
$options['table'])) {
            
$options['table'] = 'preferences';
        }
        
        if (!isset(
$options['owner_column'])) {
            
$options['owner_column'] = 'owner';
        }
        
        if (!isset(
$options['application_column'])) {
            
$options['application_column'] = 'application';
        }
        
        if (!isset(
$options['preference_column'])) {
            
$options['preference_column'] = 'name';
        }
        
        if (!isset(
$options['value_column'])) {
            
$options['value_column'] = 'value';
        }
        
        if (!isset(
$options['dsn'])) {
            
$this->_throwError(AUTH_PREFMANAGER2_DB_NO_DSN);
        }
        
        
parent::_parseOptions($options);
    }
}
?>