public function handle($app_username, $app_tenant)
	{
		if ($this->check($app_username, $app_tenant) and 		// We have tokens ...
			$this->test($app_username, $app_tenant))			// ... and they are valid
		{
			// They are already logged in, send them on to exchange data
			header('Location: ' . $this->_that_url);
			exit;
		}
		else
		{
			if (isset($_GET['oauth_token']))
			{
				// We're in the middle of an OAuth token session

				/*
				$arr = mysql_fetch_array(mysql_query("
					SELECT
						*
					FROM
						quickbooks_oauth
					WHERE
						oauth_request_token = '" . $_REQUEST['oauth_token'] . "' "));
				*/

				if ($arr = $this->_driver->oauthRequestResolve($_GET['oauth_token']))
				{
					$info = $this->_getAccessToken(
						$arr['oauth_request_token'],
						$arr['oauth_request_token_secret'],
						$_GET['oauth_verifier']);

					//print('got back [' . $info . ']');
					//print_r($info);
					//exit;

					if ($info)
					{
						/*
						mysql_query("
							UPDATE
								quickbooks_oauth
							SET
								oauth_access_token = '" . $info['oauth_token'] . "',
								oauth_access_token_secret = '" . $info['oauth_token_secret'] . "',
								qb_realm = '" . $_REQUEST['realmId'] . "',
								qb_flavor = '" . $_REQUEST['dataSource'] . "'
							WHERE
								quickbooks_oauth_id = " . $arr['quickbooks_oauth_id']);
						*/

						$this->_driver->oauthAccessWrite(
							$this->_key,
							$arr['oauth_request_token'],
							$info['oauth_token'],
							$info['oauth_token_secret'],
							$_GET['realmId'],
							$_GET['dataSource']);

						//print_r($_REQUEST);
						//exit;
						//print_r($info);

						//print('authd now, go here exchange_data.php');
						header('Location: ' . $this->_that_url);
						exit;
					}
					else
					{
						// Something went wrong when fetching the user token...?
						print('something went wrong fetching user token');
					}
				}
				else
				{
					print('something went wrong... invalid oauth token?');
				}
			}
			else
			{
				$auth_url = $this->_getAuthenticateURL($app_username, $app_tenant, $this->_this_url);

				// Forward them to the auth page
				header('Location: ' . $auth_url);
				exit;
			}
		}

		return true;
	}


public function widgetMenu($app_username, $app_tenant)
	{
		$token = null;
		$secret = null;

		if ($creds = $this->load($app_username, $app_tenant))
		{
			return $this->_request(
				QuickBooks_IPP_OAuth::METHOD_GET,
				QuickBooks_IPP_IntuitAnywhere::URL_APP_MENU,
				array(),
				$creds['oauth_access_token'],
				$creds['oauth_access_token_secret']);
		}

		return '';
	}

public function widgetMenu ( $ app_username , $ app_tenant ) { $ token = null ; $ secret = null ; if ( $ creds = $ this -> load ( $ app_username , $ app_tenant ) ) { return $ this -> _request ( QuickBooks_IPP_OAuth :: METHOD_GET , QuickBooks_IPP_IntuitAnywhere :: URL_APP_MENU , array ( ) , $ creds [ 'oauth_access_token' ] , $ creds [ 'oauth_access_token_secret' ] ) ; } return '' ; }

This function returns the html for displaying the "Blue Dot" menu

As per Intuit's recommendation, your app should call this function before the user clicks the
blue dot menu and cache it. This will improve the user's experience and prevent unnecessary API
calls to Intuit's web service. See:
https://ipp.developer.intuit.com/0010_Intuit_Partner_Platform/0025_Intuit_Anywhere/1000_Getting_Started_With_IA/0500_Add_IA_Widgets/3000_Blue_Dot_Menu/Menu_Proxy_Code

Example usage:
// Your app should read from cache here if possible before calling widgetMenu()
$html = $object->widgetMenu($app_username, $app_tenant);
if (strlen($html)) {
// Your app should write to cache here if possible
print $html;
exit;
}

@param string $app_username
@param string $app_tenant
@return html string

This function returns the html for displaying the Blue Dot menu


static public function mapSchemaToSQLDefinition($xml, &$tables)
	{
		$Parser = new QuickBooks_XML_Parser($xml);

		$errnum = 0;
		$errmsg = '';
		$tmp = $Parser->parse($errnum, $errmsg);

		$tmp = $tmp->children();
		$base = current($tmp);

		$tmp = $base->children();
		$rs = next($tmp);

		foreach ($rs->children() as $qbxml)
		{
			QuickBooks_SQL_Schema::_transform('', $qbxml, $tables);
		}

		/*
		while (count($subtables) > 0)
		{
			$node = array_shift($subtables);

			$subsubtables = array();
			$tables[] = QuickBooks_SQL_Schema::_transform('', $node, $subsubtables);

			$subtables = array_merge($subtables, $subsubtables);
		}
		*/

		// The code below tries to guess as a good set of indexes to use for
		//	any database tables we've generated from the schema. The code looks
		//	at all of the fields in the table and if any of them are *ListID or
		//	*TxnID it makes them indexes.

		// This is a list of field names that will *always* be assigned
		//	indexes, regardless of what table they are in
		$always_index_fields = array(
			'qbsql_external_id',
			'Name',
			'FullName',
			'EntityType',
			'TxnType',
			'Email',
			//'Phone',
			'IsActive',
			'RefNumber',
			//'Address_City',
			//'Address_State',
			'Address_Country',
			//'Address_PostalCode',
			//'BillAddress_City',
			//'BillAddress_State',
			'BillAddress_Country',
			//'BillAddress_PostalCode',
			//'ShipAddress_City',
			//'ShipAddress_State',
			'ShipAddress_Country',
			//'ShipAddress_PostalCode',
			'CompanyName',
			//'FirstName',
			'LastName',
			//'Contact',
			'TxnDate',
			'IsPaid',
			'IsPending',
			'IsManuallyClosed',
			'IsFullyReceived',
			'IsToBePrinted',
			'IsToBeEmailed',
			'IsFullyInvoiced',
			//'IsFinanceCharge',
			);

		// This is a list of table.field names that will be assigned indexes
		$always_index_tablefields = array(
			//'Account.AccountType',
			);

		/*
		'*FullName',
		'*ListID',
		'*TxnID',
		'*EntityType',
		'*TxnType',
		'*LineID',
		*/

		foreach ($tables as $table => $tabledef)
		{
			$uniques = array();
			$indexes = array();

			foreach ($tabledef[1] as $field => $fielddef)
			{
				if ($field == 'ListID' or 		// Unique keys
					$field == 'TxnID' or
					$field == 'Name')
				{
					// We can't apply indexes to TEXT columns, so we need to
					//	check and make sure the column isn't of type TEXT
					//	before we decide to use this as an index

					if ($fielddef[0] != QUICKBOOKS_DRIVER_SQL_TEXT)
					{
						$uniques[] = $field;
					}
				}
				else if (substr($field, -6, 6) == 'ListID' or 		// Other things we should index for performance
					substr($field, -5, 5) == 'TxnID' or
					substr($field, -6, 6) == 'LineID' or
					in_array($field, $always_index_fields) or
					in_array($table . '.' . $field, $always_index_tablefields))
				{
					// We can't apply indexes to TEXT columns, so we need to
					//	check and make sure the column isn't of type TEXT
					//	before we decide to use this as an index

					if ($fielddef[0] != QUICKBOOKS_DRIVER_SQL_TEXT)
					{
						$indexes[] = $field;
					}
				}
			}

			//print_r($indexes);
			//print_r($uniques);

			$tables[$table][3] = $indexes;
			$tables[$table][4] = $uniques;
		}

		return true;
	}

Take a qbXML schema and transform that schema to an SQL schema definition

@param string $xml			The XML string to transform
@param array $tables			An array of... erm... something?
@return boolean


static protected function _transform($curpath, $node, &$tables)
	{
		print('' . $curpath . '   node: ' . $node->name() . "\n");

		$table = '';
		$field = '';

		$this_sql = array();
		$other_sql = array();
		QuickBooks_SQL_Schema::mapToSchema($curpath . ' ' . $node->name(), QUICKBOOKS_SQL_SCHEMA_MAP_TO_SQL, $this_sql, $other_sql);

		foreach (array_merge(array( $this_sql ), $other_sql) as $sql)
		{
			$table = $sql[0];
			$field = $sql[1];

			/*
			if (!$sql[0] or !$sql[1])
			{
				print('		table for node: ' . $sql[0] . "\n");
				print('		field for node: ' . $sql[1] . "\n");
			}
			else
			{
				print("\n");
			}
			*/

			if ($table)
			{
				if (!isset($tables[$table]))
				{
					$tables[$table] = array(
						0 => $table,
						1 => array(),		// fields
						2 => null, 			// primary key
						3 => array(), 		// other keys
						4 => array(  ), 		// uniques
						);
				}
			}

			if ($table and $field)
			{
				if (!isset($tables[$table][1][$field]))
				{
					$tables[$table][1][$field] = QuickBooks_SQL_Schema::mapFieldToSQLDefinition($table, $field, $node->data());
				}
			}
		}

		if ($node->childCount())
		{
			foreach ($node->children() as $child)
			{
				QuickBooks_SQL_Schema::_transform($curpath . ' ' . $node->name(), $child, $tables);
			}
		}

		return true;
	}

Transform an XML document into an SQL schema

@param string $curpath
@param QuickBooks_XML_Node $node
@param array $tables
@return


public function set($key, $value, $cast = true)
	{
		if (is_array($value))
		{
			$this->_object[$key] = $value;
		}
		else
		{
			//print('set(' . $key . ', ' . $value . ', ' . $cast . ')' . "\n");

			if ($cast and $value != '__EMPTY__')
			{
				$value = QuickBooks_Cast::cast($this->object(), $key, $value, true, false);
			}

			//print('	setting [' . $key . '] to value {' . $value . '}' . "\n");

			$this->_object[$key] = $value;
		}

		return true;
	}

Set a value within the object

@param string $key
@param string $value
@return boolean


public function get($key, $default = null)
	{
		if (isset($this->_object[$key]))
		{
			return $this->_object[$key];
		}

		return $default;
	}

public function get ( $ key , $ default = null ) { if ( isset ( $ this -> _object [ $ key ] ) ) { return $ this -> _object [ $ key ] ; } return $ default ; }

Get a value from the object

@param string $key		The key to fetch the value for
@param mixed $default	If there is no value set for the given key, this will be returned
@return mixed			The value fetched


public function setFullNameType($fullname_key, $name_key, $parent_key, $value)
	{
		if (false !== strpos($value, ':'))
		{
			if ($name_key and $parent_key)
			{
				// This covers the case where we are setting FullName, which
				//	needs to be broken up into:
				//		Name
				//		ParentRef FullName

				$explode = explode(':', $value);
				$name = end($explode);
				$parent = implode(':', array_slice($explode, 0, -1));

				$this->set($name_key, $name);
				$this->set($parent_key, $parent);

				// Build the parent name from the newly set Name and ParentRef (need to fetch because they might have been casted/truncate)
				$value = $this->get($parent_key) . ':' . $this->get($name_key);
			}
			else
			{
				// This covers the case where we are setting
				//	CustomerType_FullName, there is no separate parent element,
				//	so we just set the whole chunk

				;
			}
		}
		else
		{
			$this->set($name_key, $value);

			// Fetch the Name (need to fetch because they might have been casted/truncate)
			$value = $this->get($name_key);
		}

		$this->set($fullname_key, $value);
	}

Set a FullName field

@param string $fullname_key
@param string $name_key
@param string $parent_key
@param string $value
@return void


public function setBooleanType($key, $value)
	{
		//print('setting BooleanType [' . $key . '] to ' . $value . "\n");

		if ($value == 'true' or $value === 1 or $value === true)
		{
			//print("\t" . ' set to TRUE' . "\n");
			return $this->set($key, 'true');
		}

		//print("\t" . ' set to FALSE' . "\n");
		return $this->set($key, 'false');
	}

Set a boolean value

@param string $key
@param mixed $value
@return boolean


public function setDateType($key, $date, $dont_allow_19691231 = true)
	{
		if ($date == '1969-12-31' and $dont_allow_19691231)
		{
			return false;
		}

		if (!strlen($date) or
			$date == '0')
		{
			return false;
		}

		// 1228241458		vs.		19830102
		//if (ereg('^[[:digit:]]+$', $date) and strlen($date) > 8)
		if (ctype_digit($date) and strlen($date) > 8)
		{
			// It's a unix timestamp (seconds since unix epoch, conver to string)
			$date = date('Y-m-d', $date);
		}

		return $this->set($key, date('Y-m-d', strtotime($date)));
	}

Set a date

@param string $key		The key for where to store the date
@param mixed $date		The date value (accepts anything www.php.net/strtotime can convert or unix timestamps)
@return boolean


public function getDateType($key, $format = 'Y-m-d')
	{
		if (!strlen($format))
		{
			$format = 'Y-m-d';
		}

		if ($this->exists($key) and $this->get($key))
		{
			return date($format, strtotime($this->get($key)));
		}

		return null;
	}

public function getDateType ( $ key , $ format = 'Y-m-d' ) { if ( ! strlen ( $ format ) ) { $ format = 'Y-m-d' ; } if ( $ this -> exists ( $ key ) and $ this -> get ( $ key ) ) { return date ( $ format , strtotime ( $ this -> get ( $ key ) ) ) ; } return null ; }

Get a date value

@param string $key		Get a date value
@param string $format	The format (any format from www.php.net/date)
@return string


public function remove($key)
	{
		if (isset($this->_object[$key]))
		{
			unset($this->_object[$key]);
			return true;
		}

		return false;
	}

public function remove ( $ key ) { if ( isset ( $ this -> _object [ $ key ] ) ) { unset ( $ this -> _object [ $ key ] ) ; return true ; } return false ; }

Removes a key from this object

@param string $key
@return boolean


protected function _schema($request)
	{
		if (strtolower(substr($request, -2, 2)) != 'rq')
		{
			$request = $request . 'Rq';
		}

		$class = 'QuickBooks_QBXML_Schema_Object_' . $request;
		$file = 'QuickBooks/QBXML/Schema/Object/' . $request . '.php';

		include_once $file;

		if (class_exists($class))
		{
			return new $class();
		}

		return false;
	}

protected function _schema ( $ request ) { if ( strtolower ( substr ( $ request , - 2 , 2 ) ) != 'rq' ) { $ request = $ request . 'Rq' ; } $ class = 'QuickBooks_QBXML_Schema_Object_' . $ request ; $ file = 'QuickBooks/QBXML/Schema/Object/' . $ request . '.php' ; include_once $ file ; if ( class_exists ( $ class ) ) { return new $ class ( ) ; } return false ; }

Get a qbXML schema object for a particular type of request

Schema objects are used to build and validate qbXML requests and the
fields and data types of qbXML elements.

@param string $request		A valid QuickBooks API request (for example: CustomerAddRq, InvoiceQueryRq, CustomerModRq, etc.)
@return QuickBooks_QBXML_Schema_Object


public function asXML($root = null, $parent = null, $object = null)
	{
		if (is_null($root))
		{
			$root = $this->object();
		}

		if (is_null($object))
		{
			$object = $this->_object;
		}

		$Node = new QuickBooks_XML_Node($root);

		foreach ($object as $key => $value)
		{
			if (is_array($value))
			{
				$Node->setChildDataAt($root . ' ' . $key, '', true);

				foreach ($value as $sub)
				{
					//print('printing sub' . "\n");
					//print_r($sub);
					//print($sub->asXML());
					$Node->addChildAt($root, $sub->asXML(null, $root));
				}
			}
			else
			{
				$Node->setChildDataAt($root . ' ' . $key, $value, true);
			}
		}

		//print_r($Node);

		return $Node;
	}

public
function
asXML
(
$
root
=
,
$
parent
=
,
$
object
=
)
{
if
(
is_null
(
$
root
)
)
{
$
root
=
$
this
->
object
(
)
;
}
if
(
is_null
(
$
object
)
)
{
$
object
=
$
this
->
_object
;
}
$
Node
=
new
QuickBooks_XML_Node
(
$
root
)
;
foreach
(
$
object
as
$
key
=>
$
value
)
{
if
(
is_array
(
$
value
)
)
{
$
Node
->
setChildDataAt
(
$
root
.
' '
.
$
key
,
''
,
true
)
;
foreach
(
$
value
as
$
sub
)
{
//print('printing sub' . "\n");
//print_r($sub);
//print($sub->asXML());
$
Node
->
addChildAt
(
$
root
,
$
sub
->
asXML
(
,
$
root
)
)
;
}
}
else
{
$
Node
->
setChildDataAt
(
$
root
.
' '
.
$
key
,
$
value
,
true
)
;
}
}
//print_r($Node);
return
$
Node
;
}

Convert this QuickBooks object to an XML node object representation

@param string $root			The node to use as the root node of the XML node structure
@param string $parent
@return QuickBooks_XML_Node


protected function _initialized()
	{
		$required = array(
			//$this->_mapTableName(QUICKBOOKS_DRIVER_SQL_IDENTTABLE) => false,
			$this->_mapTableName(QUICKBOOKS_DRIVER_SQL_TICKETTABLE) => false,
			$this->_mapTableName(QUICKBOOKS_DRIVER_SQL_USERTABLE) => false,
			$this->_mapTableName(QUICKBOOKS_DRIVER_SQL_RECURTABLE) => false,
			$this->_mapTableName(QUICKBOOKS_DRIVER_SQL_QUEUETABLE) => false,
			$this->_mapTableName(QUICKBOOKS_DRIVER_SQL_LOGTABLE) => false,
			$this->_mapTableName(QUICKBOOKS_DRIVER_SQL_CONFIGTABLE) => false,
			//$this->_mapTableName(QUICKBOOKS_DRIVER_SQL_NOTIFYTABLE) => false,
			//$this->_mapTableName(QUICKBOOKS_DRIVER_SQL_CONNECTIONTABLE) => false,
			);

		$errnum = 0;
		$errmsg = '';
		$res = $this->_query("
			SELECT
				table_name AS name
			FROM
				INFORMATION_SCHEMA.Tables
			WHERE
				TABLE_TYPE = 'BASE TABLE' ", $errnum, $errmsg);
		while ($arr = $this->_fetch($res))
		{
			$table = current($arr);

			if (isset($required[$table]))
			{
				$required[$table] = true;
			}
		}

		foreach ($required as $table => $exists)
		{
			if (!$exists)
			{
				return false;
			}
		}

		return true;
	}

Tell whether or not the SQL driver has been initialized

@return boolean


public function add($Context, $realmID, $Object)
	{
		return parent::_add($Context, $realmID, QuickBooks_IPP_IDS::RESOURCE_DEPARTMENT, $Object);
	}

public function add ( $ Context , $ realmID , $ Object ) { return parent :: _add ( $ Context , $ realmID , QuickBooks_IPP_IDS :: RESOURCE_DEPARTMENT , $ Object ) ; }

Add a new department to QuickBooks

@param QuickBooks_IPP_Context $Context
@param string $realmID
@param QuickBooks_IPP_Object_Department
@return string The new ID of the created department


public function getName()
  {
    if (!$this->exists('Name'))
    {
      if (!is_null($this->getFirstName()) || !is_null($this->getLastName())) {
        $this->setNameAsFirstLast();
      }
    }

    return $this->get('Name');
  }

public function getName ( ) { if ( ! $ this -> exists ( 'Name' ) ) { if ( ! is_null ( $ this -> getFirstName ( ) ) || ! is_null ( $ this -> getLastName ( ) ) ) { $ this -> setNameAsFirstLast ( ) ; } } return $ this -> get ( 'Name' ) ; }

Get the name of this customer

@return string


public function setNameAsFirstLast() {
    $first = $this->getFirstName();
    $last = $this->getLastName();
    if (is_null($first)) { $first = ''; }
    if (is_null($last)) { $last = ''; }

    return $this->set('Name', $first .' '. $last);
  }

public
function
setNameAsFirstLast
(
)
{
$
first
=
$
this
->
getFirstName
(
)
;
$
last
=
$
this
->
getLastName
(
)
;
if
(
is_null
(
$
first
)
)
{
$
first
=
''
;
}
if
(
is_null
(
$
last
)
)
{
$
last
=
''
;
}
return
$
this
->
set
(
'Name'
,
$
first
.
' '
.
$
last
)
;
}

Sets the name as first and last.

@return boolean


protected function _sqlDefaults($config)
	{
		$tmp = array(
			//'only_query',
			//'dont_query',
			'only_import',
			'dont_import',
			'only_add',
			'dont_add',
			'only_modify',
			'dont_modify',
			'only_misc',
			'dont_misc',
			);

		foreach ($tmp as $filter)
		{
			if (empty($config[$filter]) or
				(!empty($config[$filter]) and !is_array($config[$filter])))
			{
				$config[$filter] = array();
			}
		}

		// Any other configuration defaults go here
		$defaults = array(

			);

		return array_merge($defaults, $config);
	}

Apply default options to an array of configuration options

@param array $config
@return array


public function status($user = null, $levels = array())
	{
		$Driver = $this->_driver;

		if (!$user)
		{
			$user = $Driver->authDefault();
		}

		if (!count($levels))
		{
			$levels = array(
				60 * 60 * 12 => array( QuickBooks_Status_Report::STATUS_NOTICE, 'Notice: A connection has not been made in %d days, %d hours and %d minutes.' ),
				60 * 60 * 24 => array( QuickBooks_Status_Report::STATUS_CAUTION, 'Caution: A connection has not been made in %d days, %d hours and %d minutes.' ),
				60 * 60 * 36 => array( QuickBooks_Status_Report::STATUS_WARNING, 'Warning! A connection has not been made in %d days, %d hours and %d minutes.' ),
				60 * 60 * 48 => array( QuickBooks_Status_Report::STATUS_DANGER, 'DANGER! A connection has not been made in %d days, %d hours and %d minutes! Contact support to get this issue resolved!' ),
				);
		}

		if (!isset($levels[0]))
		{
			$levels[0] = array( QuickBooks_Status_Report::STATUS_OK, 'Status is OK. Last connection made %d days, %d hours, and %d minutes ago.' );
		}

		if (!isset($levels[-1]))
		{
			$levels[-1] = array( QuickBooks_Status_Report::STATUS_UNKNOWN, 'Status is unknown.');
		}

		//print_r($levels);

		// Find the status from the ticket table
		$last = $Driver->authLast($user);
		if (is_array($last))
		{
			krsort($levels);

			$ago = time() - strtotime($last[1]);

			$days = floor($ago / (60 * 60 * 24));
			$hours = floor(($ago - ($days * 60 * 60 * 24)) / 60.0 / 60.0);
			$minutes = max(1, floor(($ago - ($days * 60 * 60 * 24) - ($hours * 60 * 60)) / 60.0));

			$retr = null;

			foreach ($levels as $level => $tuple)
			{
				if ($level <= 0)
				{
					continue;
				}

				if ($ago > $level)
				{
					$retr = $tuple;
					break;
				}
			}

			if (!$retr)
			{
				$retr = $levels[0];
			}

			$retr[1] = sprintf($retr[1], $days, $hours, $minutes);

			$retr[] = $last[0];
			$retr[] = $last[1];

			return $retr;
		}

		return $levels[-1];
	}

Get information about the status of a connection to QuickBooks

The returned array will look something like this:
<pre>
Array (
[0] => danger 					// This is a constant, one of the QuickBooks_Status_Report::STATUS_* constants
[1] => ERROR: A connection has not been made in 54 days, 1 hours and 46 minutes! Contact support to get this issue resolved!
[2] => 2010-03-19 12:26:41 		// This is the last time the given user logged in
[3] => 2010-03-19 12:26:41 		// This is the last time the given user performed any action
)
</pre>

@param string $user
@param array $levels
@return array			An array of status information


public function delete($Context, $realmID, $IDType)
	{
		return parent::_delete($Context, $realmID, QuickBooks_IPP_IDS::RESOURCE_PURCHASEORDER, $IDType);
	}

public function delete ( $ Context , $ realmID , $ IDType ) { return parent :: _delete ( $ Context , $ realmID , QuickBooks_IPP_IDS :: RESOURCE_PURCHASEORDER , $ IDType ) ; }

Deletes purchase order.

@param object $Context Context.
@param string $realmID Company Id.
@param string $IDType  Resource.

@return boolean


protected function _read($mixed)
	{
		if (empty($mixed))
		{
			return '';
		}
		else if (is_resource($mixed) and
			get_resource_type($mixed) == 'stream')
		{
			$buffer = '';
			$tmp = '';
			while ($tmp = fread($mixed, 8192))
			{
				$buffer .= $tmp;
			}

			return $buffer;
		}
		else if (substr(trim($mixed), 0, 6) == '{"warn')
		{
			// Intuit has a bug where some of their services return JSON erors
			// instead of XML, so we catch these here...

			return '';
		}
		else if (substr(trim($mixed), 0, 1) != '<')
		{
			return file_get_contents($mixed);
		}

		return $mixed;
	}

Read an open file descriptor, XML file, or string

@param mixed $mixed
@return string

Read an open file descriptor XML file or string


public function load($xml_or_file)
	{
		$xml_or_file = $this->_read($xml_or_file);

		$this->_xml = $xml_or_file;
		return $this->_backend->load($xml_or_file);
	}

public function load ( $ xml_or_file ) { $ xml_or_file = $ this -> _read ( $ xml_or_file ) ; $ this -> _xml = $ xml_or_file ; return $ this -> _backend -> load ( $ xml_or_file ) ; }

Load the XML parser with data from a string or file

@param string $xml_or_file		An XML string or
@return integer


public function parse(&$errnum, &$errmsg)
	{
		if (!strlen($this->_xml))
		{
			$errnum = QuickBooks_XML::ERROR_CONTENT;
			$errmsg = 'No XML content to parse.';
			return false;
		}

		// first, let's remove all of the comments
		if ($this->validate($errnum, $errmsg))
		{
			return $this->_backend->parse($errnum, $errmsg);
		}

		return false;
	}

Parse an XML document into an XML node structure

This function returns either a QuickBooks_XML_Document on success, or false
on failure. You can use the ->validate() method first so you can tell
whether or not the parser will succeed.

@param integer $errnum
@param string $errmsg
@return QuickBooks_XML_Document


public function asQBXML($request, $version = null, $locale = null, $root = null)
	{
		return parent::asQBXML($request, $version, $locale, $root);
	}

public function asQBXML ( $ request , $ version = null , $ locale = null , $ root = null ) { return parent :: asQBXML ( $ request , $ version , $ locale , $ root ) ; }

Convert this object to a valid qbXML request

@param string $request					The type of request to convert this to (examples: CustomerAddRq, CustomerModRq, CustomerQueryRq)
@param boolean $todo_for_empty_elements	A constant, one of: QUICKBOOKS_XML_XML_COMPRESS, QUICKBOOKS_XML_XML_DROP, QUICKBOOKS_XML_XML_PRESERVE
@param string $indent
@param string $root
@return string


static public function e500_notfound($requestID, $user, $action, $ident, $extra, &$err, $xml, $errnum, $errmsg, $config)
	{
		//$requestID, $user, $action, $ident, $extra, $errerr, $xml, $errnum, $errmsg, $this->_callback_config
		// Not found, *still call the callback!*

		/*
		$extra['callbacks'], $method, $action, $ID, $err, $qbxml, $qbobject, $qbres
		*/

		// Get the driver instance
		$Driver = QuickBooks_Driver_Singleton::getInstance();

		if (!isset($extra['callbacks']))
		{
			$extra['callbacks'] = array();
		}

		if (!is_array($extra['callbacks']))
		{
			$extra['callbacks'] = array( $extra['callbacks'] );
		}

		$method = null;
		if (isset($extra['method']))
		{
			$method = $extra['method'];
		}

		$err = '';

		$qbobject = new QuickBooks_Iterator(array());
		$qbres = null;

		foreach ($extra['callbacks'] as $func)
		{
			if (false !== strpos($func, '::') and
				true) // method_exists()) 	// is this safe to do?
			{
				// Callback *static method*

				$tmp = explode('::', $func);

				$return = call_user_func(array( $tmp[0], $tmp[1] ), $method, $action, $ident, $err, $xml, $qbobject, $qbres);
			}
			else if (function_exists($func))
			{
				// Callback *function*

				$return = call_user_func($func, $method, $action, $ident, $err, $xml, $qbobject, $qbres);
			}
			else
			{
				$err = 'Could not call function or method: ' . $func;
				$Driver->log('API: ' . $err, null, QUICKBOOKS_LOG_NORMAL);
				return false;
			}

			if (!$return)
			{
				break;
			}
		}

		if ($err)
		{
			return false;
		}

		return true;
	}

Handle a QuickBooks error indicating that nothing matched a search

For whatever strange reason, instead of returning an empty result set
when you do a search that returns no results, QuickBooks instead returns
an error message. The error message code might be either 1 or 500
depending on what filters you use in your query.

@TODO This should use the QuickBooks_Callbacks class, and not it's own custom callback code

@param string $requestID
@param string $user
@param string $action
@param mixed $ident
@param mixed $extra
@param string $err
@param string $xml
@param integer $errnum
@param string $errmsg
@param array $config
@return boolean


protected function _ticketResolve($ticket)
	{
		static $cache = array();

		if (!$ticket)
		{
			return 0;
		}

		$errnum = 0;
		$errmsg = '';

		if (isset($cache[$ticket]))
		{
			return $cache[$ticket];
		}
		else if ($arr = $this->_fetch($this->_query("
			SELECT
				quickbooks_ticket_id
			FROM
				" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_TICKETTABLE) . "
			WHERE
				ticket = '" . $this->_escape($ticket) . "' ", $errnum, $errmsg, 0, 1)))
		{
			$cache[$ticket] = $arr['quickbooks_ticket_id'];

			return $arr['quickbooks_ticket_id'];
		}

		return 0;
	}

"
			SELECT
				quickbooks_ticket_id
			FROM
				"

Resolve a ticket string back to a ticket ID number

@param string $ticket
@return integer


protected function _configWrite($user, $module, $key, $value, $type, $opts)
	{
		$errnum = 0;
		$errmsg = '';

		if ($arr = $this->_fetch($this->_query("
			SELECT
				quickbooks_config_id
			FROM
				" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_CONFIGTABLE) . "
			WHERE
				qb_username = '" . $this->_escape($user) . "' AND
				module = '" . $this->_escape($module) . "' AND
				cfgkey = '" . $this->_escape($key) . "' ", $errnum, $errmsg, 0, 1)))
		{
			$this->_query("
				UPDATE
					" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_CONFIGTABLE) . "
				SET
					cfgval = '" . $this->_escape($value) . "',
					mod_datetime = '" . date('Y-m-d H:i:s') . "'
				WHERE
					quickbooks_config_id = " . $arr['quickbooks_config_id'], $errnum, $errmsg);
		}
		else
		{
			return $this->_query("
				INSERT INTO
					" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_CONFIGTABLE) . "
				(
					qb_username,
					module,
					cfgkey,
					cfgval,
					cfgtype,
					cfgopts,
					write_datetime,
					mod_datetime
				) VALUES (
					'" . $this->_escape($user) . "',
					'" . $this->_escape($module) . "',
					'" . $this->_escape($key) . "',
					'" . $this->_escape($value) . "',
					'" . $this->_escape($type) . "',
					'" . $this->_escape(serialize($opts)) . "',
					'" . date('Y-m-d H:i:s') . "',
					'" . date('Y-m-d H:i:s') . "'
				) ", $errnum, $errmsg);
		}
	}

"
			SELECT
				quickbooks_config_id
			FROM
				"

"'
				WHERE
					quickbooks_config_id = "

Write a configuration variable to the database

@param string $user		The QuickBooks user this is stored for
@param string $module	The module name this is stored for (free-form text, you make it up, but make it unique! a good habit is to use the __CLASS__ constant)
@param string $key		A key to fetch and store this value by
@param mixed $value		The value
@param string $type
@param array $opts
@return boolean			Success or failure


protected function _configRead($user, $module, $key, &$type, &$opts)
	{
		$errnum = 0;
		$errmsg = '';

		$sql = "
			SELECT
				cfgval,
				cfgtype,
				cfgopts
			FROM
				" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_CONFIGTABLE) . "
			WHERE
				qb_username = '" . $this->_escape($user) . "' AND
				module = '" . $this->_escape($module) . "' AND
				cfgkey = '" . $this->_escape($key) . "' ";

		//print($sql);

		if ($arr = $this->_fetch($this->_query($sql, $errnum, $errmsg, 0, 1)))
		{
			$type = $arr['cfgtype'];
			$opts = $arr['cfgopts'];

			//print_r($arr);

			return $arr['cfgval'];
		}

		$type = null;
		$opts = null;

		return null;
	}

Read configuration information

@param string $user		The username to store this for
@param string $module	The module to store this for
@param string $key		The key to store this by
@param string $type
@param array $opts
@return mixed			The value read from the SQL database


protected function _authCreate($username, $password, $company_file = null, $wait_before_next_update = null, $min_run_every_n_seconds = null)
	{
		$errnum = 0;
		$errmsg = '';

		if (!$this->_count($this->_query("SELECT qb_username FROM " . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_USERTABLE) . " WHERE qb_username = '" . $this->_escape($username) . "' ", $errnum, $errmsg, 0, 1)))
		{
			return $this->_query("
				INSERT INTO
					" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_USERTABLE) . "
				(
					qb_username,
					qb_password,
					qb_company_file,
					qbwc_wait_before_next_update,
					qbwc_min_run_every_n_seconds,
					status,
					write_datetime,
					touch_datetime
				) VALUES (
					'" . $this->_escape($username) . "',
					'" . $this->_escape($this->_hash($password)) . "',
					'" . $this->_escape($company_file) . "',
					" . (int) $wait_before_next_update . ",
					" . (int) $min_run_every_n_seconds . ",
					'" . QUICKBOOKS_USER_ENABLED . "',
					'" . date('Y-m-d H:i:s') . "',
					'" . date('Y-m-d H:i:s') . "'
				) ", $errnum, $errmsg);
		}

		return false;
	}

Create a new user for the SOAP server

@param string $username
@param string $password
@return boolean


protected function _authDefault()
	{
		$errnum = 0;
		$errmsg = '';

		if ($arr = $this->_fetch($this->_query("
			SELECT
				qb_username
			FROM
				" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_USERTABLE) . "
			WHERE
				status = '" . QUICKBOOKS_USER_ENABLED . "' ", $errnum, $errmsg, 0, 1)))
		{
			return $arr['qb_username'];
		}

		return '';
	}

"
			SELECT
				qb_username
			FROM
				"


protected function _authLogin($username, $password, &$company_file, &$wait_before_next_update, &$min_run_every_n_seconds, $override = false)
	{
		$errnum = 0;
		$errmsg = '';

		if ($override) // We still need to make sure that the user exists, even if using external authentication
		{
			$this->authCreate($username, $password);
		}
		else if (strlen(trim($password)) == 0)
		{
			// Blank passwords *always fail*
			return null;
		}
		else if (strlen(trim($password)) == 32 or strlen(trim($password)) == 40)
		{
			// Possible *hack* attempt (they're sending us a random hash hoping it will match one of the hashed passwords)
			return null;
		}

		// Support for plain-text, MD5 (without salt), and SHA1 (without salt) passwords
		$plain_text = $password;
		$plain_md5 = md5($password);
		$plain_sha1 = sha1($password);

		if ($override or
			$arr = $this->_fetch($this->_query("
				SELECT
					*
				FROM
					" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_USERTABLE) . "
				WHERE
					qb_username = '" . $this->_escape($username) . "' AND
					(
						qb_password = '" . $this->_escape($this->_hash($password)) . "' OR
						qb_password = '" . $this->_escape($plain_text) . "' OR
						qb_password = '" . $this->_escape($plain_md5) . "' OR
						qb_password = '" . $this->_escape($plain_sha1) . "'
					) AND
					status = '" . QUICKBOOKS_USER_ENABLED . "' ", $errnum, $errmsg, 0, 1)))
		{
			//$ticket = md5((string) microtime() . $username . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_SALT));
			$ticket = QuickBooks_WebConnector_QWC::GUID(false);

			$this->_query("
				INSERT INTO
					" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_TICKETTABLE) . "
				(
					qb_username,
					ticket,
					ipaddr,
					write_datetime,
					touch_datetime
				) VALUES (
					'" . $this->_escape($username) . "',
					'" . $this->_escape($ticket) . "',
					'" . $_SERVER['REMOTE_ADDR'] . "',
					'" . date('Y-m-d H:i:s') . "',
					'" . date('Y-m-d H:i:s') . "'
				) ", $errnum, $errmsg);

			$this->_query("
				UPDATE
					" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_USERTABLE) . "
				SET
					touch_datetime = '" . date('Y-m-d H:i:s') . "'
				WHERE
					qb_username = '" . $this->_escape($username) . "' ", $errnum, $errmsg);

			if (isset($arr) and
				is_array($arr))		// Might not have this if it's an authenticate override
			{
				$company_file = $arr['qb_company_file'];
				$wait_before_next_update = $arr['qbwc_wait_before_next_update'];
				$min_run_every_n_seconds = $arr['qbwc_min_run_every_n_seconds'];
			}

			return $ticket;
		}

		return null;
	}

Log a user in

@param string $username
@param string $password
@param boolean $override		If this is set to TRUE, a correct password *is not* required
@return string				A session ticket, or an empty string if the login failed


protected function _authCheck($ticket)
	{
		$errnum = 0;
		$errmsg = '';

		if ($arr = $this->_fetch($this->_query("
			SELECT
				quickbooks_ticket_id
			FROM
				" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_TICKETTABLE) . "
			WHERE
				ticket = '" . $this->_escape($ticket) . "' AND
				touch_datetime > '" . date('Y-m-d H:i:s', time() - QUICKBOOKS_TIMEOUT) . "' ", $errnum, $errmsg, 0, 1)))
		{
			$this->_query("
				UPDATE
					" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_TICKETTABLE) . "
				SET
					touch_datetime = '" . date('Y-m-d H:i:s') . "'
				WHERE
					quickbooks_ticket_id = " . $arr['quickbooks_ticket_id'], $errnum, $errmsg);

			return true;
		}

		return false;
	}

"'
				WHERE
					quickbooks_ticket_id = "

Check to see if a log in session is valid

@param string $ticket
@return boolean


protected function _errorLog($ticket, $errnum, $errmsg)
	{
		if ($ticket_id = $this->_ticketResolve($ticket))
		{
			$db_errnum = 0;
			$db_errmsg = '';

			return $this->_query("
				UPDATE
					" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_TICKETTABLE) . "
				 SET
					lasterror_num = '" . $this->_escape($errnum) . "',
					lasterror_msg = '" . $this->_escape(substr($errmsg, 0, 255)) . "'
				WHERE
					quickbooks_ticket_id = " . (int) $ticket_id, $db_errnum, $db_errmsg);
		}

		return false;
	}

Store the last error which occured

@param string $ticket		The session ticket for the session this error occured within
@param string $errnum		The error number
@param string $errmsg		The error message
@return boolean


protected function _recurEnqueue($user, $run_every, $action, $ident, $replace = true, $priority = 0, $extra = null, $qbxml = null)
	{
		$errnum = 0;
		$errmsg = '';

		// By default, it has *never* occured
		$recur_lasttime = (time() - $run_every - 60);

		if ($replace)
		{
			if ($existing = $this->_fetch($this->_query("
					SELECT
						recur_lasttime
					FROM
						" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_RECURTABLE) . "
					WHERE
						qb_username = '" . $this->_escape($user) . "' AND
						qb_action = '" . $this->_escape($action) . "' AND
						ident = '" . $this->_escape($ident) . "' ", $errnum, $errmsg)))
			{
				$this->_query("
					DELETE FROM
						" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_RECURTABLE) . "
					WHERE
						qb_username = '" . $this->_escape($user) . "' AND
						qb_action = '" . $this->_escape($action) . "' AND
						ident = '" . $this->_escape($ident) . "' ", $errnum, $errmsg);

				$recur_lasttime = $existing['recur_lasttime'];
			}
		}

		if ($extra)
		{
			$extra = serialize($extra);
		}

		return $this->_query("
			INSERT INTO
				" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_RECURTABLE) . "
			(
				qb_username,
				qb_action,
				ident,
				extra,
				qbxml,
				priority,
				run_every,
				recur_lasttime,
				enqueue_datetime
			) VALUES (
				'" . $this->_escape($user) . "',
				'" . $this->_escape($action) . "',
				'" . $this->_escape($ident) . "',
				'" . $this->_escape($extra) . "',
				'" . $this->_escape($qbxml) . "',
				" . (int) $priority . ",
				" . (int) $run_every . ",
				" . $recur_lasttime . ",
				'" . date('Y-m-d H:i:s') . "'
			) ", $errnum, $errmsg);
	}

"
					SELECT
						recur_lasttime
					FROM
						"

Register a recurring event for a particular user

@param string $user
@param integer $run_every
@param string $action
@param mixed $ident
@param boolean $replace
@param integer $priority
@param mixed $extra
@return boolean


protected function _recurDequeue($user, $by_priority = false)
	{
		$errnum = 0;
		$errmsg = '';

		$sql = "
			SELECT
				*
			FROM
				" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_RECURTABLE) . "
			WHERE
				qb_username = '" . $this->_escape($user) . "' AND
				recur_lasttime + run_every <= " . time();

		if ($by_priority)
		{
			$sql .= ' ORDER BY priority DESC ';
		}

		if ($arr = $this->_fetch($this->_query($sql . ' ', $errnum, $errmsg, 0, 1)))
		{
			// Update it, so it doesn't get fetched again until it's supposed to
			$errnum = 0;
			$errmsg = '';
			$this->_query("UPDATE " . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_RECURTABLE) . " SET recur_lasttime = " . time() . " WHERE quickbooks_recur_id = " . $arr['quickbooks_recur_id'], $errnum, $errmsg);

			return $arr;
		}

		return false;
	}

"' AND
				recur_lasttime + run_every <= "

Dequeue a recurring even that is schedule to be run

@param string $user
@param boolean $by_priority
@return array


protected function _queueEnqueue($user, $action, $ident, $replace = true, $priority = 0, $extra = null, $qbxml = null)
	{
		$errnum = 0;
		$errmsg = '';

		if ($replace)
		{
			$this->_query("
				DELETE FROM
					" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_QUEUETABLE) . "
				WHERE
					qb_username = '" . $this->_escape($user) . "' AND
					qb_action = '" . $this->_escape($action) . "' AND
					ident = '" . $this->_escape($ident) . "' AND
					qb_status = '" . QUICKBOOKS_STATUS_QUEUED . "' ", $errnum, $errmsg);
		}

		if ($extra)
		{
			$extra = serialize($extra);
		}

		return $this->_query("
			INSERT INTO
				" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_QUEUETABLE) . "
			(
				qb_username,
				qb_action,
				ident,
				extra,
				qbxml,
				priority,
				qb_status,
				enqueue_datetime
			) VALUES (
				'" . $this->_escape($user) . "',
				'" . $this->_escape($action) . "',
				'" . $this->_escape($ident) . "',
				'" . $this->_escape($extra) . "',
				'" . $this->_escape($qbxml) . "',
				" . (int) $priority . ",
				'" . QUICKBOOKS_STATUS_QUEUED . "',
				'" . date('Y-m-d H:i:s') . "'
			) ", $errnum, $errmsg);
	}

Add an item to the queue

@param string $action
@param mixed $ident
@return boolean


protected function _queueProcessing($user)
	{
		$errnum = 0;
		$errmsg = '';

		// Fetch the latest record to be dequeued for this user, and check that it's set with a status of in processing
		$sql = "
			SELECT
				quickbooks_queue_id,
				qb_action,
				ident,
				qb_status,
				dequeue_datetime
			FROM
				" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_QUEUETABLE) . "
			WHERE
				dequeue_datetime IS NOT NULL
			ORDER BY
				dequeue_datetime DESC ";

		$res = $this->_query($sql, $errnum, $errmsg, 0, 1);
		if ($arr = $this->_fetch($res) and
			$arr['qb_status'] == QUICKBOOKS_STATUS_PROCESSING and 					// Make sure this was the last thing we tried to process...
			time() - strtotime($arr['dequeue_datetime']) < QUICKBOOKS_TIMEOUT)		// ... and it occurred during a reasonably recent run
		{
			return $this->_queueGet($user, $arr['quickbooks_queue_id'], QUICKBOOKS_STATUS_PROCESSING);
			//return $this->_queueFetch($user, $arr['qb_action'], $arr['ident'], QUICKBOOKS_STATUS_PROCESSING);
		}

		return false;
	}

Fetch the queue item currently being processed by QuickBooks

@param string $user
@return array


protected function _queueLeft($user, $queued = true)
	{
		$errnum = 0;
		$errmsg = '';

		// SELECT * FROM quickbooks_queue WHERE qb_status = 'q'
		$sql = "
			SELECT
				COUNT(*) AS num_left
			FROM
				" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_QUEUETABLE) . "
			WHERE
				qb_username = '" . $this->_escape($user) . "' ";

		if ($queued)
		{
			$sql .= " AND qb_status = '" . QUICKBOOKS_STATUS_QUEUED . "' ";
		}

		$arr = $this->_fetch($this->_query($sql, $errnum, $errmsg));

		return $arr['num_left'];
	}

"
			SELECT
				COUNT(*) AS num_left
			FROM
				"

Tell how many items are in the queue

@return integer


protected function _queueProcessed($ticket)
	{
		$errnum = 0;
		$errmsg = '';

		if ($arr = $this->_fetch($this->_query("
			SELECT
				processed
			FROM
				" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_TICKETTABLE) . "
			WHERE
				ticket = '" . $this->_escape($ticket) . "' ", $errnum, $errmsg, 0, 1)))
		{
			return $arr['processed'];
		}

		return 0;
	}

Tell how many items have been processed during this session

@param string $ticket
@return integer


protected function _log($msg, $ticket = null, $log_level = QUICKBOOKS_LOG_NORMAL, $cur_log_level = null)
	{
		static $batch = 0;
		/*
		if ($batch == 0)		// Batching needs to be revised, *major* performance hit
		{
			// We store a batch ID so that we can tell which logged messages go with which actual separate HTTP request

			$errnum = 0;
			$errmsg = '';

			if ($arr = $this->_fetch($this->_query("SELECT MAX(batch) AS maxbatch FROM " . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_LOGTABLE) . " ", $errnum, $errmsg)))
			{
				$batch = (int) $arr['maxbatch'];
			}

			$batch++;
		}
		*/

		// Truncate log and queue tables
		$this->_truncate(QUICKBOOKS_DRIVER_SQL_LOGTABLE, $this->_max_log_history);
		$this->_truncate(QUICKBOOKS_DRIVER_SQL_QUEUETABLE, $this->_max_queue_history);
		$this->_truncate(QUICKBOOKS_DRIVER_SQL_TICKETTABLE, $this->_max_ticket_history);

		// Actually insert the log message...
		$errnum = 0;
		$errmsg = '';

		// Make sure the message isn't too long
		$msg = substr($msg, 0, 65534);

		// Log level handling is handled by the QuickBooks_Driver base class (see public method {@link QuickBooks_Driver::log()})
		if ($ticket_id = $this->_ticketResolve($ticket))
		{
			return $this->_query("
				INSERT INTO
					" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_LOGTABLE) . "
				(
					quickbooks_ticket_id,
					batch,
					msg,
					log_datetime
				) VALUES (
					" . $ticket_id . ",
					" . $batch . ",
					'" . $this->_escape($msg) . "',
					'" . date('Y-m-d H:i:s') . "' ) ", $errnum, $errmsg);
		}
		else
		{
			return $this->_query("
				INSERT INTO
					" . $this->_mapTableName(QUICKBOOKS_DRIVER_SQL_LOGTABLE) . "
				(
					batch,
					msg,
					log_datetime
				) VALUES (
					" . $batch . ",
					'" . $this->_escape($msg) . "',
					'" . date('Y-m-d H:i:s') . "' ) ", $errnum, $errmsg);
		}
	}

Write a message to the log file

@param string $msg
@param string $ticket
@param integer $log_level
@return boolean


public function query($sql, &$errnum, &$errmsg, $offset = 0, $limit = null, $vars = array())
	{
		if (is_array($vars)
			and count($vars))
		{
			foreach ($vars as $key => $value)
			{
				$vars[$key] = $this->escape($value);
			}

			array_unshift($vars, $sql);
			$sql = call_user_func_array('sprintf', $vars);
		}

		//print($sql . '<br>');

		return $this->_query($sql, $errnum, $errmsg, $offset, $limit);
	}

public
function
query
(
$
sql
,
&
$
errnum
,
&
$
errmsg
,
$
offset
=
0
,
$
limit
=
,
$
vars
=
array
(
)
)
{
if
(
is_array
(
$
vars
)
and
count
(
$
vars
)
)
{
foreach
(
$
vars
as
$
key
=>
$
value
)
{
$
vars
[
$
key
]
=
$
this
->
escape
(
$
value
)
;
}
array_unshift
(
$
vars
,
$
sql
)
;
$
sql
=
call_user_func_array
(
'sprintf'
,
$
vars
)
;
}
//print($sql . '<br>');
return
$
this
->
_query
(
$
sql
,
$
errnum
,
$
errmsg
,
$
offset
,
$
limit
)
;
}

Execute an SQL query against the database

@param string $sql
@param integer $errnum
@param string $errmsg
@return resource


public function fields($table, $with_field_names_as_keys = false)
	{
		static $cache = array();

		if (isset($cache[$table]))
		{
			return $cache[$table];
		}

		// *Careful* by default it's stored as array( 'field_name' => true, ... )
		$tmp = $this->_fields($table);
		$cache[$table] = array_combine($tmp, array_fill(0, count($tmp), true));

		if ($with_field_names_as_keys)
		{
			return $cache[$table];
		}
		else
		{
			return array_keys($cache[$table]);
		}
	}

Get a list of the fields within an SQL table

@param string $table
@param boolean $with_field_names_as_keys
@return array


public function select($table, $restrict, $order = array(), $offset = null, $limit = null)
	{
		$list = array();

		if (count($restrict))
		{
			$where = array();
			foreach ($restrict as $field => $value)
			{
				$where[] = $field . " = '" . $this->_escape($value) . "' ";
			}

			$where = " WHERE " . implode(' AND ', $where) . " ";
		}
		else
		{
			$where = "";
		}

		$orderby = "";
		if (is_array($order) and
			count($order))
		{
			$orderby = array();

			foreach ($order as $field => $direction)
			{
				$orderby[] = " " . $field . " " . $direction;
			}

			$orderby = " ORDER BY " . implode(', ', $orderby);
		}

		$errnum = 0;
		$errmsg = '';
		if ($res = $this->_query("SELECT * FROM " . $this->_escape($table) . " " . $where . " " . $orderby, $errnum, $errmsg, $offset, $limit))
		{
			while ($arr = $this->_fetch($res))
			{
				$list[] = $arr;
			}
		}

		return $list;
	}

public
function
select
(
$
table
,
$
restrict
,
$
order
=
array
(
)
,
$
offset
=
,
$
limit
=
)
{
$
list
=
array
(
)
;
if
(
count
(
$
restrict
)
)
{
$
where
=
array
(
)
;
foreach
(
$
restrict
as
$
field
=>
$
value
)
{
$
where
[
]
=
$
field
.
" = '"
.
$
this
->
_escape
(
$
value
)
.
"' "
;
}
$
where
=
" WHERE "
.
implode
(
' AND '
,
$
where
)
.
" "
;
}
else
{
$
where
=
""
;
}
$
orderby
=
""
;
if
(
is_array
(
$
order
)
and
count
(
$
order
)
)
{
$
orderby
=
array
(
)
;
foreach
(
$
order
as
$
field
=>
$
direction
)
{
$
orderby
[
]
=
" "
.
$
field
.
" "
.
$
direction
;
}
$
orderby
=
" ORDER BY "
.
implode
(
', '
,
$
orderby
)
;
}
$
errnum
=
0
;
$
errmsg
=
''
;
if
(
$
res
=
$
this
->
_query
(
"SELECT * FROM "
.
$
this
->
_escape
(
$
table
)
.
" "
.
$
where
.
" "
.
$
orderby
,
$
errnum
,
$
errmsg
,
$
offset
,
$
limit
)
)
{
while
(
$
arr
=
$
this
->
_fetch
(
$
res
)
)
{
$
list
[
]
=
$
arr
;
}
}
return
$
list
;
}

Get a list of objects back from the database

@param string $table
@param array $restrict


public function update($table, $object, $where = array(), $resync = true, $discov = null, $derive = true)	// @todo Is that the correct default for $derive?
	{
		$sql = '';
		$set = array();

		if (is_object($object))
		{
			$object = $object->asArray();
		}

		$avail = $this->fields($table, true);		// List of available fields

		// Case folding support
		if ($this->foldsToLower())
		{
			$object = array_change_key_case($object, CASE_LOWER);
		}
		else if ($this->foldsToUpper())
		{
			$object = array_change_key_case($object, CASE_UPPER);
		}

		// Merge by keys to make sure we don't INSERT any fields that don't exist in this schema
		$object = array_intersect_key($object, $avail);

		//
		foreach ($object as $field => $value)
		{
			// Commented out because doing this to very large integers (i.e. ItemRef/FullName is a large integer SKU) causes integer overflow
			/*if (strlen((int) $value) == strlen($value))
			{
				$set[] = $field . ' = ' . (int) $value;
			}
			else
			{*/
			//	$set[] = $field . " = '" . $this->_escape($value) . "' ";
			//}

			if (is_null($value))
			{
				$set[] = $field . " = NULL ";
			}
			else
			{
				$set[] = $field . " = '" . $this->_escape($value) . "' ";
			}
		}

		$wheres = array();
		foreach ($where as $part)
		{
			foreach ($part as $field => $value)
			{
				$wheres[] = $field . " = '" . $this->_escape($value) . "' ";
			}
		}

		$sql = "UPDATE " . $this->_escape($table) . " SET " . implode(', ', $set);

		if ($resync)
		{
			$sql .= ", " . QUICKBOOKS_DRIVER_SQL_FIELD_RESYNC . " = '" . date('Y-m-d H:i:s') . "' ";
		}

		$sql .= " WHERE " . implode(' AND ', $wheres);

		//print($sql);

		$errnum = 0;
		$errmsg = '';
		$return = $this->_query($sql, $errnum, $errmsg);

		if (is_null($discov))
		{
			$discov = $resync;
		}

		if ($discov)
		{
			// Update the discover datetime *IF THE DISCOVER DATETIME IS NULL*
			//	This happens when an AddResponse is received, and we need to
			//	update a record that has just been added to QuickBooks. If we
			//	don't mark it as discovered, then updates to the record will
			//	never be picked up and sent to QuickBooks

			$errnum = 0;
			$errmsg = '';

			$wheres[] = QUICKBOOKS_DRIVER_SQL_FIELD_DISCOVER . " IS NULL ";

			$this->_query("
				UPDATE
					" . $this->_escape($table) . "
				SET
					" . QUICKBOOKS_DRIVER_SQL_FIELD_DISCOVER . " = " . QUICKBOOKS_DRIVER_SQL_FIELD_RESYNC . "
				WHERE
					" . implode(' AND ', $wheres), $errnum, $errmsg);
		}

		return $return;
	}

Update a record in the SQL table

@todo We should make this support only passing a single $where component, instead of an array of array where components
@todo Support the $derive flag

@param string $table			The table to update
@param object $object		An object containing a record to update
@param array $where			An array to use to build the WHERE clause
@param boolean $resync		Update the timestamp field which indicates when we last re-synced with QuickBooks
@param boolean $discov		Update the timestamp field which indicates when this record was discoved in QuickBooks
@param boolean $derive		Update the timestamp field which indicates when we last updated derived fields from QuickBooks
@return boolean


public function interactive($priority = 0, $user = null)
	{
		if ($this->_driver)
		{
			$tmp = array_merge(range('a', 'z'), range(0, 9));
			shuffle($tmp);
			$random = substr(implode('', $tmp), 0, 8);

			/*
			if (!$user)
			{
				$user = $this->_driver->authDefault();
			}
			*/

			if (!$user)
			{
				$user = $this->_user;
			}

			return $this->_driver->queueEnqueue(QUICKBOOKS_INTERACTIVE_MODE, $random, true, $priority, $user);
		}

		return false;
	}

Request to enter "Interactive Mode" with the Web Connector

* This function *does not* work. Please don't use it.

@param integer $priority
@param string $user
@return boolean

Request to enter Interactive Mode with the Web Connector


public function recurring($run_every, $action, $ident = null, $priority = 0, $extra = null, $user = null, $qbxml = null, $replace = true)
	{
		$run_every = QuickBooks_Utilities::intervalToSeconds($run_every);

		if (!strlen($ident))
		{
			$tmp = array_merge(array('a', 'z'), range(0, 9));
			shuffle($tmp);
			$ident = substr(implode('', $tmp), 0, 8);
		}

		if ($this->_driver)
		{
			/*
			if (!$user)
			{
				$user = $this->_driver->authDefault();
			}
			*/

			// Use the default user (provided in __construct) if none is given
			if (!$user)
			{
				$user = $this->_user;
			}

			return $this->_driver->recurEnqueue($user, $run_every, $action, substr($ident, 0, 40), $replace, $priority, $extra, $qbxml);
		}

		return false;
	}

Register a recurring event

Recurring events are actions that get queued up to run once every so
often. So, for instance, if you want to make sure you issue a
CustomerQuery every day, you can register a recurring event that occurs
every day, and then the SOAP server/Web Connector will try to make sure
that it gets run every day.

Note that the SOAP server can't guarantee that an action will occur
every interval *unless* the Web Connector is set to connect at that same
interval or (preferably) more often.

@param mixed $run_every		This can be either an integer number of seconds, or a string like: "15 minutes", "2 days", "hour", etc.
@param string $action		The QuickBooks action you want to execute
@param mixed $ident			An optional ident string (say you wanted to do a CustomerQuery for customer #14 every 15 minutes, you'd pass CustomerQuery for the action, and 14 for the ident)
@param integer $priority		The priority of the action (higher priorities run first)
@param mixed $extra			Any extra data to include for the request handler
@param string $user			The username of the QuickBooks Web Connector user this event should be registered for
@param boolean $replace		Whether or not this should replace any other recurring events with this action/ident
@return boolean


public function size($user = null)
	{
		if ($this->_driver)
		{
			// Use the default user (provided in __construct) if none is given
			if (!$user)
			{
				$user = $this->_user;
			}

			$queued = true;
			return $this->_driver->queueLeft($user, $queued);
		}

		return null;
	}

Tell the number of items currently in the queue

@param string $user		The username of the user to check the queue size for
@return integer			The number of items in the queue


public function getAddress($part = null, $defaults = array())
	{
		if (!is_null($part))
		{
			return $this->get('Address ' . $part);
		}

		return $this->getArray('Address *', $defaults);
	}

public
function
getAddress
(
$
part
=
,
$
defaults
=
array
(
)
)
{
if
(
!
is_null
(
$
part
)
)
{
return
$
this
->
get
(
'Address '
.
$
part
)
;
}
return
$
this
->
getArray
(
'Address *'
,
$
defaults
)
;
}

Get the address

@param string $part			A specific portion of the address to get (i.e. "Addr1" or "State")
@param array $defaults		Default values if a value isn't filled in
@return array				The address


public function authenticate($username, $password, $token)
	{
		$this->_username = $username;
		$this->_password = $password;
		$this->_token = $token;

		$url = 'https://workplace.intuit.com/db/main?act=API_Authenticate';
		$action = 'API_Authenticate';

		$xml = '<?xml version="1.0" encoding="UTF-8" ?>
			<qdbapi>
				<username>' . $username . '</username>
				<password>' . $password . '</password>
				<apptoken>' . $token . '</apptoken>
			</qdbapi>';

		$Context = null;
		$response = $this->_request($Context, QuickBooks_IPP::REQUEST_IPP, $url, $action, $xml);

		if (!$this->_hasErrors($response) and
			$ticket = QuickBooks_XML::extractTagContents('ticket', $response))
		{
			$this->_ticket = $ticket;

			$cookies = array(
				'scache',
				'ptest',
				'stest',
				'luid',
				'TICKET',
				'qbn.ticket',
				'qbn.tkt',
				'qbn.authid',
				'qbn.gauthid',
				'qbn.agentid',
				'iamValidationTime'
				);

			foreach ($cookies as $cookie)
			{
				if ($value = $this->_extractCookie($cookie, $response))
				{
					$this->_cookies[$cookie] = $value;
				}
			}

			return new QuickBooks_IPP_Context($this, $ticket, $token);
		}

		return false;
	}

Authenticate to the IPP web service

IMPORTANT NOTE:
Intuit disallows this method within live applications! You can use it to
test your application, but when you go live you'll need to instead use
a SAML gateway for single-sign-on authentication. Take a look at the
QuickBooks_IPP_Federator class for a working SAML gateway.

@param string $username
@param string $password
@param string $token
@return boolean


public function findById($Context, $realmID)
	{
		$xml = null;

		// WATCH OUT!   We pass in the realmID as ID value
		return parent::_findById($Context, $realmID, QuickBooks_IPP_IDS::RESOURCE_COMPANY, $realmID, $xml);
	}

Get a company by realmID

@param QuickBooks_IPP_Context $Context
@param string $realmID
@return QuickBooks_IPP_Object_Customer	The customer object


public function delete($Context, $realmID, $IDType)
	{
		return parent::_delete($Context, $realmID, QuickBooks_IPP_IDS::RESOURCE_VENDORCREDIT, $IDType);
	}

public function delete ( $ Context , $ realmID , $ IDType ) { return parent :: _delete ( $ Context , $ realmID , QuickBooks_IPP_IDS :: RESOURCE_VENDORCREDIT , $ IDType ) ; }

Deletes vendor credit.

@param object $Context Context.
@param string $realmID Company Id.
@param string $IDType  Resource.

@return boolean


protected function _request($method)
	{
		$start = microtime(true);

		if (!function_exists('curl_init'))
		{
			die('You must have the PHP cURL extension (php.net/curl) enabled to use this (' . QUICKBOOKS_PACKAGE_NAME . ' v' . QUICKBOOKS_PACKAGE_VERSION . ').');
		}

		$this->_log('Using CURL to send request!', QUICKBOOKS_LOG_DEVELOP);
		$return = $this->_requestCurl($method, $errnum, $errmsg);

		if ($errnum)
		{
			$this->_setError($errnum, $errmsg);
		}

		// Calculate and set how long the last HTTP request/response took to make
		$this->_last_duration = microtime(true) - $start;

		return $return;
	}

Make an HTTP request

@param string $method
@return string


public function signOn()
	{
		$this->_setError(QuickBooks_MerchantService::ERROR_OK);

		$xml = '';
		$xml .= '<?xml version="1.0" ?>' . QUICKBOOKS_CRLF;
		$xml .= '<?qbmsxml version="4.1"?>' . QUICKBOOKS_CRLF;
		$xml .= '<QBMSXML>' . QUICKBOOKS_CRLF;
		$xml .= '	<SignonMsgsRq>' . QUICKBOOKS_CRLF;

		if ($this->_certificate)
		{
			$this->_log('Signing on a HOSTED QBMS application.', QUICKBOOKS_LOG_DEBUG);

			$xml .= '		<SignonAppCertRq>' . QUICKBOOKS_CRLF;
			$xml .= '			<ClientDateTime>' . date('Y-m-d\TH:i:s') . '</ClientDateTime>' . QUICKBOOKS_CRLF;
			$xml .= '			<ApplicationLogin>' . $this->_application_login . '</ApplicationLogin>' . QUICKBOOKS_CRLF;
			$xml .= '			<ConnectionTicket>' . $this->_ticket_connection . '</ConnectionTicket>' . QUICKBOOKS_CRLF;
			$xml .= '		</SignonAppCertRq>' . QUICKBOOKS_CRLF;
		}
		else
		{
			$this->_log('Signing on as a DESKTOP QBMS application.', QUICKBOOKS_LOG_DEBUG);

			$xml .= '		<SignonDesktopRq>' . QUICKBOOKS_CRLF;
			$xml .= '			<ClientDateTime>' . date('Y-m-d\TH:i:s') . '</ClientDateTime>' . QUICKBOOKS_CRLF;
			$xml .= '			<ApplicationLogin>' . $this->_application_login . '</ApplicationLogin>' . QUICKBOOKS_CRLF;
			$xml .= '			<ConnectionTicket>' . $this->_ticket_connection . '</ConnectionTicket>' . QUICKBOOKS_CRLF;
			$xml .= '		</SignonDesktopRq>' . QUICKBOOKS_CRLF;
		}

		$xml .= '	</SignonMsgsRq>' . QUICKBOOKS_CRLF;
		$xml .= '</QBMSXML>' . QUICKBOOKS_CRLF;

		$errnum = QuickBooks_MerchantService::ERROR_OK;
		$errmsg = '';

		$response = $this->_request($xml, $errnum, $errmsg);

		if ($errnum)
		{
			$this->_setError(QuickBooks_MerchantService::ERROR_SOCKET, $errnum . ': ' . $errmsg);
			return false;
		}

		$code = $this->_extractAttribute('statusCode', $response);
		$message = $this->_extractAttribute('statusMessage', $response);
		$severity = $this->_extractAttribute('statusSeverity', $response);

		$this->_log('SignOn (initial) response: ' . $severity . '/' . $code . ': ' . $message, QUICKBOOKS_LOG_DEBUG);

		if ($code != QuickBooks_MerchantService::ERROR_OK)
		{
			$this->_setError($code, $message);
			return false;
		}

		if ($ticket = $this->_extractTagContents('SessionTicket', $response))
		{
			$this->_ticket_session = $ticket;

			return true;
		}

		$this->_setError(QuickBooks_MerchantService::ERROR_INTERNAL, 'Could not locate SessionTicket in response.');

		return false;
	}

Sign on to the QBMS service to fetch a session ticket

@return boolean


protected function _transRequestID($type, $Obj, $amount, $force_new_transaction = true)
	{
		$rand = '';
		if ($force_new_transaction)
		{
			$rand = mt_rand() . microtime();
		}

		return md5($type . '-' . serialize($Obj) . '-' . $amount . '-' . $rand);
	}

protected function _transRequestID ( $ type , $ Obj , $ amount , $ force_new_transaction = true ) { $ rand = '' ; if ( $ force_new_transaction ) { $ rand = mt_rand ( ) . microtime ( ) ; } return md5 ( $ type . '-' . serialize ( $ Obj ) . '-' . $ amount . '-' . $ rand ) ; }

Create a unique transaction requestID from a set of parameters

@param string $type
@param object $Obj
@param float $amount
@param boolean $force_new_transaction
@return string


public function refund($Card, $amount, $salestax = null, $comment = null, $is_card_present = false, $is_ecommerce = true, $force_new_transaction = true)
	{
		$this->_setError(QuickBooks_MerchantService::ERROR_OK);
		$this->_log('refund()', QUICKBOOKS_LOG_VERBOSE);

		if (!$this->isSignedOn())
		{
			$this->signOn();

			if ($this->errorNumber())
			{
				return false;
			}
		}

		// Error checking
		if (!($Card instanceof QuickBooks_MerchantService_CreditCard))
		{
			$this->_setError(QuickBooks_MerchantService::ERROR_PARAM, 'refund() expects first parameter to be a Card object, got: ' . print_r($Card, true));
			return false;
		}

		if (!is_numeric($amount))
		{
			$this->_setError(QuickBooks_MerchantService::ERROR_PARAM, 'refund() expects second parameter to be a float, got: ' . print_r($amount, true));
			return false;
		}

		$transRequestID = $this->_transRequestID(QuickBooks_MerchantService::TYPE_REFUND, $Card, $amount, $force_new_transaction);

		$xml = '';
		$xml .= '<?xml version="1.0" encoding="utf-8"?>' . QUICKBOOKS_CRLF;
		$xml .= '<?qbmsxml version="4.1"?>' . QUICKBOOKS_CRLF;
		$xml .= '<QBMSXML>' . QUICKBOOKS_CRLF;
		$xml .= $this->_createSessionXML();
		$xml .= '	<QBMSXMLMsgsRq>' . QUICKBOOKS_CRLF;
		$xml .= '		<CustomerCreditCardRefundRq>' . QUICKBOOKS_CRLF;
		$xml .= '			<TransRequestID>' . $transRequestID . '</TransRequestID>' . QUICKBOOKS_CRLF;

		//                                  $Card, $amount, $salestax, $is_card_present, $is_ecommerce, $is_recurring, $is_mobile, $include_address_data = true, $include_amounts = true, $include_card_number = true, $include_card_cvv = true, $include_card_dates = true
		$xml .= $this->_createCreditCardXML($Card, $amount, $salestax, $is_card_present, $is_ecommerce, false,         null, false,                        true,                    true,                        false);

		//<BatchID >STRTYPE</BatchID> <!-- optional -->

		if ($comment)
		{
			$xml .= '			<Comment>' . substr(QuickBooks_XML::encode($comment), 0, 500) . '</Comment>' . QUICKBOOKS_CRLF;
		}

		$xml .= '		</CustomerCreditCardRefundRq>' . QUICKBOOKS_CRLF;
		$xml .= '	</QBMSXMLMsgsRq>' . QUICKBOOKS_CRLF;
		$xml .= '</QBMSXML>' . QUICKBOOKS_CRLF;

		return $this->_doQBMS(QuickBooks_MerchantService::TYPE_REFUND, 'QBMSXML/QBMSXMLMsgsRs/CustomerCreditCardRefundRs', $xml, $Card);
	}


static public function initialize($dsn = null, $user = null, $config = array(), $return_boolean = true)
	{
		static $instance;
		if (empty($instance))
		{
			if (empty($dsn))
			{
				return false;
			}

			$instance = new QuickBooks_WebConnector_Queue($dsn, $user, $config);
		}

		if ($return_boolean and $instance)
		{
			return true;
		}

		return $instance;
	}

static public function initialize ( $ dsn = null , $ user = null , $ config = array ( ) , $ return_boolean = true ) { static $ instance ; if ( empty ( $ instance ) ) { if ( empty ( $ dsn ) ) { return false ; } $ instance = new QuickBooks_WebConnector_Queue ( $ dsn , $ user , $ config ) ; } if ( $ return_boolean and $ instance ) { return true ; } return $ instance ; }

Initialize the queueing object

@param string $dsn
@param string $user
@param array  $config
@param bool   $return_boolean
@return QuickBooks_WebConnector_Queue


protected function _adapterFactory($adapter, $wsdl, $soap_options, $loglevel)
	{
		$adapter = ucfirst(strtolower($adapter));

		$file = '/QuickBooks/Adapter/Server/' . $adapter . '.php';
		$class = 'QuickBooks_Adapter_Server_' . $adapter;

		QuickBooks_Loader::load($file);

		if (class_exists($class))
		{
			return new $class($wsdl, $soap_options);
		}

		return null;
	}

protected function _adapterFactory ( $ adapter , $ wsdl , $ soap_options , $ loglevel ) { $ adapter = ucfirst ( strtolower ( $ adapter ) ) ; $ file = '/QuickBooks/Adapter/Server/' . $ adapter . '.php' ; $ class = 'QuickBooks_Adapter_Server_' . $ adapter ; QuickBooks_Loader :: load ( $ file ) ; if ( class_exists ( $ class ) ) { return new $ class ( $ wsdl , $ soap_options ) ; } return null ; }

Get an adapter class instance

@param string $adapter
@param string $wsdl
@param array $soap_options
@param integer $loglevel
@return boolean


final protected function _defaults($arr)
	{
		$defaults = array(
			'error_handler' => '',
			'use_builtin_error_handler' => false,
			'time_limit' => 0,
			'log_to_file' => null,
			'log_to_syslog' => null,
			'masking' => true,
			);

		$arr = array_merge($defaults, $arr);

		return $arr;
	}

final protected function _defaults ( $ arr ) { $ defaults = array ( 'error_handler' => '' , 'use_builtin_error_handler' => false , 'time_limit' => 0 , 'log_to_file' => null , 'log_to_syslog' => null , 'masking' => true , ) ; $ arr = array_merge ( $ defaults , $ arr ) ; return $ arr ; }

Merge configurations with the defaults

@param array $arr
@return array


protected function _headers()
	{
		if ($_SERVER['REQUEST_METHOD'] == 'POST')
		{
			header('Content-Type: text/xml');
		}
		else if (isset($_GET['wsdl']) or isset($_GET['WSDL']))
		{
			header('Content-Type: text/xml');
		}
		else
		{
			header('Content-Type: text/plain');
		}

		return true;
	}

protected
function
_headers
(
)
{
if
(
$
_SERVER
[
'REQUEST_METHOD'
]
==
'POST'
)
{
header
(
'Content-Type: text/xml'
)
;
}
else
if
(
isset
(
$
_GET
[
'wsdl'
]
)
or
isset
(
$
_GET
[
'WSDL'
]
)
)
{
header
(
'Content-Type: text/xml'
)
;
}
else
{
header
(
'Content-Type: text/plain'
)
;
}
return
true
;
}

Send the correct HTTP headers for this request

@return boolean


public function action_index()	{
		//Username and password used for the web connector, QWC file, and the QB Framework
		$user = 'QBAPI Username';
		$pass = 'QBAPI Password';

		//Configure the logging level
		$log_level = QUICKBOOKS_LOG_DEVELOP;

		//Pure-PHP SOAP server
		$soapserver = QUICKBOOKS_SOAPSERVER_BUILTIN;

		//we can turn this off
		$handler_options = array(
			'deny_concurrent_logins' => false,
			'deny_reallyfast_logins' => false,
		);

		// The next three params $map, $errmap, and $hooks are callbacks which
		// will be called when certain actions/events/requests/responses occur within
		// the framework

		// Maps inbound requests to functions
		$map = array(
			QUICKBOOKS_ADD_CUSTOMER => array( array($this, '_quickbooks_customer_add_request'), array($this,'_quickbooks_customer_add_response' )),
			QUICKBOOKS_MOD_CUSTOMER => array( array($this, '_quickbooks_customer_mod_request'), array($this, '_quickbooks_customer_mod_response')),
		);

		//Map error handling to functions
		$errmap = array(
			3070 => array($this, '_quickbooks_error_stringtoolong'),
			3140	=> array($this, '_quickbooks_reference_error'),
			'*'	=>	array($this, '_quickbooks_error_handler'),
		);

		//Login success callback
		$hooks = array(
			QuickBooks_WebConnector_Handlers::HOOK_LOGINSUCCESS => array(array($this,'_quickbooks_hook_loginsuccess')),
			);

		//MySQL database name containing the QuickBooks tables is named 'quickbooks' (if the tables don't exist, they'll be created for you)
		$dsn = 'mysql://username:password@host/databasename';

		QuickBooks_WebConnector_Queue_Singleton::initialize($dsn);

		if (!QuickBooks_Utilities::initialized($dsn)) {
			// Initialize creates the neccessary database schema for queueing up requests and logging
			QuickBooks_Utilities::initialize($dsn);

			// This creates a username and password which is used by the Web Connector to authenticate
			QuickBooks_Utilities::createUser($dsn, $user, $pass);

			// Initial test case customer
			$primary_key_of_new_customer = 512;

			// Fire up the Queue
			$Queue = new QuickBooks_WebConnector_Queue($dsn);

			// Drop the directive and the customer into the queue
			$Queue->enqueue(QUICKBOOKS_ADD_CUSTOMER, $primary_key_of_new_customer);

			// Also note the that ->enqueue() method supports some other parameters:
			// 	string $action				The type of action to queue up
			//	mixed $ident = null			Pass in the unique primary key of your record here, so you can pull the data from your application to build a qbXML request in your request handler
			//	$priority = 0				You can assign priorities to requests, higher priorities get run first
			//	$extra = null				Any extra data you want to pass to the request/response handler
			//	$user = null				If you're using multiple usernames, you can pass the username of the user to queue this up for here
			//	$qbxml = null
			//	$replace = true
		}
		//To be used with singleton queue
		$driver_options = array();

		//Callback options, not needed at the moment
		$callback_options = array();

		//nothing needed here at the moment
		$soap_options = array();

		//construct a new instance of the web connector server
		$Server = new QuickBooks_WebConnector_Server($dsn, $map, $errmap, $hooks, $log_level, $soapserver, QUICKBOOKS_WSDL, $soap_options, $handler_options, $driver_options, $callback_options);

		//instruct server to handle responses
		$response = $Server->handle(true, true);
	}

Houses the instance of the soap server and creates the mappings for errors, function callbacks
@author Jayson Lindsley <jay.lindsley@gmail.com>

Houses the instance of the soap server and creates the mappings for errors function callbacks


protected function _requestFactory($request)
	{
		$class = 'QuickBooks_WebConnector_Request_' . ucfirst(strtolower($request));
		$file = '/QuickBooks/WebConnector/Request/' . ucfirst(strtolower($request)) . '.php';

		// Make sure that class gets loaded
		QuickBooks_Loader::load($file, false);

		if (class_exists($class))
		{
			return new $class();
		}

		return false;
	}

protected
function
_requestFactory
(
$
request
)
{
$
class
=
'QuickBooks_WebConnector_Request_'
.
ucfirst
(
strtolower
(
$
request
)
)
;
$
file
=
'/QuickBooks/WebConnector/Request/'
.
ucfirst
(
strtolower
(
$
request
)
)
.
'.php'
;
// Make sure that class gets loaded
QuickBooks_Loader
::
load
(
$
file
,
false
)
;
if
(
class_exists
(
$
class
)
)
{
return
new
$
class
(
)
;
}
return
false
;
}

Create an instance of a request type object

@param string $request
@return QuickBooks_Request


public function handle($raw_http_input)
	{
		// Determine the method, call the correct handler function

		$builtin = QuickBooks_XML::PARSER_BUILTIN;		// The SimpleXML parser has a difference namespace behavior, so force this to use the builtin parser
		$Parser = new QuickBooks_XML_Parser($raw_http_input, $builtin);

		$errnum = 0;
		$errmsg = '';
		if ($Doc = $Parser->parse($errnum, $errmsg))
		{
			//print('parsing...');

			$Root = $Doc->getRoot();

			$Body = $Root->getChildAt('SOAP-ENV:Envelope SOAP-ENV:Body');
			if (!$Body)
			{
				$Body = $Root->getChildAt('soap:Envelope soap:Body');
			}

			$Container = $Body->getChild(0);

			$Request = null;
			$method = '';
			if ($Container)
			{
				$namespace = '';
				$method = $this->_namespace($Container->name(), $namespace);
				$Request = $this->_requestFactory($method);

				foreach ($Container->children() as $Child)
				{
					$namespace = '';
					$member = $this->_namespace($Child->name(), $namespace);

					//$Request->$member = html_entity_decode($Child->data(), ENT_QUOTES);
					$Request->$member = $Child->data();
				}
			}

			//print('method is: ' . $method . "\n");

			$Response = null;
			if (method_exists($this->_class, $method))
			{
				$Response = $this->_class->$method($Request);
			}

			$soap = '<?xml version="1.0" encoding="UTF-8"?>
			<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
			 xmlns:ns1="http://developer.intuit.com/">
				<SOAP-ENV:Body><ns1:' . $method . 'Response>';

			$vars = get_object_vars($Response);

			$soap .= $this->_serialize($vars);

			$soap .= '</ns1:' . $method . 'Response>
			</SOAP-ENV:Body>
			</SOAP-ENV:Envelope>';

			print($soap);
			return true;
		}
		else
		{
			$soap = '';
			$soap .= '<?xml version="1.0" encoding="UTF-8"?>' . QUICKBOOKS_CRLF;
			$soap .= '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">' . QUICKBOOKS_CRLF;
			$soap .= '	<SOAP-ENV:Body>' . QUICKBOOKS_CRLF;
			$soap .= '		<SOAP-ENV:Fault>' . QUICKBOOKS_CRLF;
			$soap .= '			<faultcode>SOAP-ENV:Client</faultcode>' . QUICKBOOKS_CRLF;
			$soap .= '			<faultstring>Bad Request: ' . htmlspecialchars($errnum) . ': ' . htmlspecialchars($errmsg) . '</faultstring>' . QUICKBOOKS_CRLF;
			$soap .= '		</SOAP-ENV:Fault>' . QUICKBOOKS_CRLF;
			$soap .= '	</SOAP-ENV:Body>' . QUICKBOOKS_CRLF;
			$soap .= '</SOAP-ENV:Envelope>' . QUICKBOOKS_CRLF;

			print($soap);
			return false;
		}
	}

Handle a SOAP request

@param string $raw_http_input		The raw incoming SOAP request (HTTP request body)
@return boolean


public function walk($callback)
	{
		//print_r($this->_data);
		//exit;

		foreach ($this->_data as $key => $value)
		{
			if (is_object($value))
			{
				$value->walk($callback);
			}
			else if (is_array($value))
			{
				foreach ($value as $skey => $svalue)
				{
					if (is_object($svalue))
					{
						$svalue->walk($callback);
					}
					else
					{
						$this->_data[$key][$skey] = call_user_func($callback, $this->resource(), $key, $svalue);
					}
				}
			}
			else
			{
				$this->_data[$key] = call_user_func($callback, $this->resource(), $key, $value);
			}
		}
	}

public function walk ( $ callback ) { //print_r($this->_data); //exit; foreach ( $ this -> _data as $ key => $ value ) { if ( is_object ( $ value ) ) { $ value -> walk ( $ callback ) ; } else if ( is_array ( $ value ) ) { foreach ( $ value as $ skey => $ svalue ) { if ( is_object ( $ svalue ) ) { $ svalue -> walk ( $ callback ) ; } else { $ this -> _data [ $ key ] [ $ skey ] = call_user_func ( $ callback , $ this -> resource ( ) , $ key , $ svalue ) ; } } } else { $ this -> _data [ $ key ] = call_user_func ( $ callback , $ this -> resource ( ) , $ key , $ value ) ; } } }

Apply a user-defined function to every single data field in the object

@param string $callback		The callback function

Apply a user - defined function to every single data field in the object


public function charge($Context, $Object_or_token, $amount, $currency = 'USD', $description = '', array $context = array())
	{
		$capture = true;
		return $this->_chargeOrAuth($Context, $Object_or_token, $amount, $currency, $capture, $description, $context);
	}

public function charge ( $ Context , $ Object_or_token , $ amount , $ currency = 'USD' , $ description = '' , array $ context = array ( ) ) { $ capture = true ; return $ this -> _chargeOrAuth ( $ Context , $ Object_or_token , $ amount , $ currency , $ capture , $ description , $ context ) ; }

Charge a credit card

See also:
https://developer.intuit.com/docs/api/payments/charges

@param  [type] $Context         [description]
@param  [type] $Object_or_token [description]
@param  [type] $amount          [description]
@param  string $currency        [description]
@param  string $description     [description]
@param  array  $context         [description]
@return [type]                  [description]


public function getCharge($Context, $id)
	{
		$resp = $this->_http($Context, QuickBooks_Payments::URL_CHARGE . '/' . $id, null);

		$data = json_decode($resp, true);

		$ignore_declines = true;
		if ($this->_handleError($data, $ignore_declines))
		{
			return false;
		}

		return new QuickBooks_Payments_Transaction($data);
	}

public function getCharge ( $ Context , $ id ) { $ resp = $ this -> _http ( $ Context , QuickBooks_Payments :: URL_CHARGE . '/' . $ id , null ) ; $ data = json_decode ( $ resp , true ) ; $ ignore_declines = true ; if ( $ this -> _handleError ( $ data , $ ignore_declines ) ) { return false ; } return new QuickBooks_Payments_Transaction ( $ data ) ; }

Get information about a charge that was made previously

@param  [type] $Context [description]
@param  [type] $id      [description]
@return [type]          [description]


public function getDebit($Context, $id)
	{
		$resp = $this->_http($Context, QuickBooks_Payments::URL_ECHECK . '/' . $id, null);

		$data = json_decode($resp, true);

		$ignore_declines = true;
		if ($this->_handleError($data, $ignore_declines))
		{
			return false;
		}

		return new QuickBooks_Payments_Transaction($data);
	}

public function getDebit ( $ Context , $ id ) { $ resp = $ this -> _http ( $ Context , QuickBooks_Payments :: URL_ECHECK . '/' . $ id , null ) ; $ data = json_decode ( $ resp , true ) ; $ ignore_declines = true ; if ( $ this -> _handleError ( $ data , $ ignore_declines ) ) { return false ; } return new QuickBooks_Payments_Transaction ( $ data ) ; }

Get information about a debit that was made previously

@param  [type] $id [description]
@return [type]     [description]


public function refund($Context, $id, $amount, $context = array())
	{
		$url = str_replace('<id>', $id, QuickBooks_Payments::URL_REFUND);

		$payload = array(
			'amount' => $amount,
			'context' => array(
				'mobile' => false,
				'isEcommerce' => false,
				'recurring' => false,
				),
			);

		$resp = $this->_http($Context, $url, json_encode($payload));

		$data = json_decode($resp, true);

		if ($this->_handleError($data))
		{
			return false;
		}

		return new QuickBooks_Payments_Transaction($data);
	}

public function refund ( $ Context , $ id , $ amount , $ context = array ( ) ) { $ url = str_replace ( '<id>' , $ id , QuickBooks_Payments :: URL_REFUND ) ; $ payload = array ( 'amount' => $ amount , 'context' => array ( 'mobile' => false , 'isEcommerce' => false , 'recurring' => false , ) , ) ; $ resp = $ this -> _http ( $ Context , $ url , json_encode ( $ payload ) ) ; $ data = json_decode ( $ resp , true ) ; if ( $ this -> _handleError ( $ data ) ) { return false ; } return new QuickBooks_Payments_Transaction ( $ data ) ; }

Refund a transaction

@param  [type] $Context [description]
@param  [type] $id      [description]
@param  [type] $amount  [description]
@param  array  $context [description]
@return [type]          [description]


public function storeCard($Context, $id, $Object)
	{
		$id = str_replace(array('{', '}', '-'), '', $id);

		$url = str_replace('<id>', $id, QuickBooks_Payments::URL_CARD);

		if ($Object instanceof QuickBooks_Payments_CreditCard)
		{
			$payload = $Object->toArray();
		}
		else
		{
			$this->_setError();
			return false;
		}

		$resp = $this->_http($Context, $url, json_encode($payload));

		$data = json_decode($resp, true);

		if ($this->_handleError($data))
		{
			return false;
		}

		return QuickBooks_Payments_CreditCard::fromArray($data);
	}

public function storeCard ( $ Context , $ id , $ Object ) { $ id = str_replace ( array ( '{' , '}' , '-' ) , '' , $ id ) ; $ url = str_replace ( '<id>' , $ id , QuickBooks_Payments :: URL_CARD ) ; if ( $ Object instanceof QuickBooks_Payments_CreditCard ) { $ payload = $ Object -> toArray ( ) ; } else { $ this -> _setError ( ) ; return false ; } $ resp = $ this -> _http ( $ Context , $ url , json_encode ( $ payload ) ) ; $ data = json_decode ( $ resp , true ) ; if ( $ this -> _handleError ( $ data ) ) { return false ; } return QuickBooks_Payments_CreditCard :: fromArray ( $ data ) ; }

Store a card via the QuickBooks Payments API

@return QuickBooks_Payments_CreditCard


public function storeCardFromToken($Context, $id, $token)
	{
		$id = str_replace(array('{', '}', '-'), '', $id);

		$url = str_replace('<id>', $id, QuickBooks_Payments::URL_CARD . '/createFromToken');

		$payload = array(
			'value' => $token
			);

		$resp = $this->_http($Context, $url, json_encode($payload));

		$data = json_decode($resp, true);

		if ($this->_handleError($data))
		{
			return false;
		}

		return QuickBooks_Payments_CreditCard::fromArray($data);
	}

public function storeCardFromToken ( $ Context , $ id , $ token ) { $ id = str_replace ( array ( '{' , '}' , '-' ) , '' , $ id ) ; $ url = str_replace ( '<id>' , $ id , QuickBooks_Payments :: URL_CARD . '/createFromToken' ) ; $ payload = array ( 'value' => $ token ) ; $ resp = $ this -> _http ( $ Context , $ url , json_encode ( $ payload ) ) ; $ data = json_decode ( $ resp , true ) ; if ( $ this -> _handleError ( $ data ) ) { return false ; } return QuickBooks_Payments_CreditCard :: fromArray ( $ data ) ; }

Store a card from a token via the QuickBooks Payments API

@return QuickBooks_Payments_CreditCard


public function getCard($Context, $id, $card_id)
	{
		$id = str_replace(array('{', '}', '-'), '', $id);

		$url = str_replace('<id>', $id, QuickBooks_Payments::URL_CARD . '/' . $card_id);

		$resp = $this->_http($Context, $url);

		$data = json_decode($resp, true);

		if ($this->_handleError($data))
		{
			return false;
		}

		return QuickBooks_Payments_CreditCard::fromArray($data);
	}

public function getCard ( $ Context , $ id , $ card_id ) { $ id = str_replace ( array ( '{' , '}' , '-' ) , '' , $ id ) ; $ url = str_replace ( '<id>' , $ id , QuickBooks_Payments :: URL_CARD . '/' . $ card_id ) ; $ resp = $ this -> _http ( $ Context , $ url ) ; $ data = json_decode ( $ resp , true ) ; if ( $ this -> _handleError ( $ data ) ) { return false ; } return QuickBooks_Payments_CreditCard :: fromArray ( $ data ) ; }

Get a card via the QuickBooks Payments API

@return QuickBooks_Payments_CreditCard


public function getCards($Context, $id)
	{
		$id = str_replace(array('{', '}', '-'), '', $id);

		$url = str_replace('<id>', $id, QuickBooks_Payments::URL_CARD);

		$resp = $this->_http($Context, $url, null);

		$data = json_decode($resp, true);

		if ($this->_handleError($data))
		{
			return false;
		}

		$cards = array();

		foreach ($data as $card)
		{
			$cards[] = QuickBooks_Payments_CreditCard::fromArray($card);
		}

		return $cards;
	}

public function getCards ( $ Context , $ id ) { $ id = str_replace ( array ( '{' , '}' , '-' ) , '' , $ id ) ; $ url = str_replace ( '<id>' , $ id , QuickBooks_Payments :: URL_CARD ) ; $ resp = $ this -> _http ( $ Context , $ url , null ) ; $ data = json_decode ( $ resp , true ) ; if ( $ this -> _handleError ( $ data ) ) { return false ; } $ cards = array ( ) ; foreach ( $ data as $ card ) { $ cards [ ] = QuickBooks_Payments_CreditCard :: fromArray ( $ card ) ; } return $ cards ; }

Get all cards associated with a customer via the
QuickBooks Payments API

@return array of QuickBooks_Payments_CreditCard


public function deleteCard($Context, $id, $card_id)
	{
		$id = str_replace(array('{', '}', '-'), '', $id);

		$url = str_replace('<id>', $id, QuickBooks_Payments::URL_CARD . '/' . $card_id);

		$resp = $this->_http($Context, $url, null, 'DELETE');

		$data = json_decode($resp, true);

		if ($this->_handleError($data))
		{
			return false;
		}

		return true;
	}

public function deleteCard ( $ Context , $ id , $ card_id ) { $ id = str_replace ( array ( '{' , '}' , '-' ) , '' , $ id ) ; $ url = str_replace ( '<id>' , $ id , QuickBooks_Payments :: URL_CARD . '/' . $ card_id ) ; $ resp = $ this -> _http ( $ Context , $ url , null , 'DELETE' ) ; $ data = json_decode ( $ resp , true ) ; if ( $ this -> _handleError ( $ data ) ) { return false ; } return true ; }

Delete a card via the QuickBooks Payments API

@return boolean


protected function _handleError($data, $ignore_declines = false)
	{
		if (!$data)
		{
			// Check for 401/other errors
			$info = $this->_last_httpinfo;

			if ($info['http_code'] == QuickBooks_HTTP::HTTP_401)
			{
				$this->_setError($info['http_code'], 'Unauthorized.');
				return true;
			}
			else if ($info['http_code'] == QuickBooks_HTTP::HTTP_404)
			{
				$this->_setError($info['http_code'], 'Not Found.');
				return true;
			}
			else if ($info['http_code'] == QuickBooks_HTTP::HTTP_500)
			{
				$this->_setError($info['http_code'], 'Internal Server Error.');
				return true;
			}
		}

		if (isset($data['errors']))
		{
			$err = array_merge(array(
				'code' => null,
				'message' => null,
				'type' => null,
				'detail' => null,
				'infoLink' => null,
				), $data['errors'][0]);

			$this->_setError($err['code'], $err['message'], $err['type'], $err['detail'], $err['infoLink']);

			return true;
		}

		if (!$ignore_declines)
		{
			if (isset($data['status']) and
				$data['status'] == self::STATUS_DECLINED)
			{
				$this->_setError(self::ERROR_DECLINE, 'This transaction was declined.');

				return true;
			}
		}

		return false;
	}

protected
function
_handleError
(
$
data
,
$
ignore_declines
=
false
)
{
if
(
!
$
data
)
{
// Check for 401/other errors
$
info
=
$
this
->
_last_httpinfo
;
if
(
$
info
[
'http_code'
]
==
QuickBooks_HTTP
::
HTTP_401
)
{
$
this
->
_setError
(
$
info
[
'http_code'
]
,
'Unauthorized.'
)
;
return
true
;
}
else
if
(
$
info
[
'http_code'
]
==
QuickBooks_HTTP
::
HTTP_404
)
{
$
this
->
_setError
(
$
info
[
'http_code'
]
,
'Not Found.'
)
;
return
true
;
}
else
if
(
$
info
[
'http_code'
]
==
QuickBooks_HTTP
::
HTTP_500
)
{
$
this
->
_setError
(
$
info
[
'http_code'
]
,
'Internal Server Error.'
)
;
return
true
;
}
}
if
(
isset
(
$
data
[
'errors'
]
)
)
{
$
err
=
array_merge
(
array
(
'code'
=>
,
'message'
=>
,
'type'
=>
,
'detail'
=>
,
'infoLink'
=>
,
)
,
$
data
[
'errors'
]
[
0
]
)
;
$
this
->
_setError
(
$
err
[
'code'
]
,
$
err
[
'message'
]
,
$
err
[
'type'
]
,
$
err
[
'detail'
]
,
$
err
[
'infoLink'
]
)
;
return
true
;
}
if
(
!
$
ignore_declines
)
{
if
(
isset
(
$
data
[
'status'
]
)
and
$
data
[
'status'
]
==
self
::
STATUS_DECLINED
)
{
$
this
->
_setError
(
self
::
ERROR_DECLINE
,
'This transaction was declined.'
)
;
return
true
;
}
}
return
false
;
}

Handle an error, if set in the returned data

@return boolean

Handle an error if set in the returned data


protected function _http($Context, $url_path, $raw_body = null, $operation = null)
	{
		if($operation !== null)
		{
			$method = $operation;
		}
		else
		{
			$method = 'GET';
			if ($raw_body)
			{
				$method = 'POST';
			}
		}

		$url = $this->_getBaseURL() . $url_path;

		$authcreds = $Context->authcreds();

		$params = array();

		$OAuth = new QuickBooks_IPP_OAuth($this->_oauth_consumer_key, $this->_oauth_consumer_secret);
		$signed = $OAuth->sign($method, $url, $authcreds['oauth_access_token'], $authcreds['oauth_access_token_secret'], $params);

		//print_r($signed);

		//$HTTP = new QuickBooks_HTTP($signed[2]);

		$HTTP = new QuickBooks_HTTP($url);

		$headers = array(
			'Content-Type' => 'application/json',
			'Request-Id' => QuickBooks_Utilities::GUID(),
			'Authorization' => $signed[3],
			);
		$HTTP->setHeaders($headers);

		// Turn on debugging for the HTTP object if it's been enabled in the payment processor
		$HTTP->useDebugMode($this->_debug);

		//
		$HTTP->setRawBody($raw_body);

		$HTTP->verifyHost(false);
		$HTTP->verifyPeer(false);

		if ($method == 'POST')
		{
			$return = $HTTP->POST();
		}
		else if ($method == 'GET')
		{
			$return = $HTTP->GET();
		}
		else if ($method == 'DELETE')
		{
			$return = $HTTP->DELETE();
		}
		else
		{
			$return = null;  // ERROR
		}

		$this->_last_request = $HTTP->lastRequest();
		$this->_last_response = $HTTP->lastResponse();

		//
		$this->log($HTTP->getLog(), QUICKBOOKS_LOG_DEBUG);

		$info = $HTTP->lastInfo();
		$this->_last_httpinfo = $info;

		$errnum = $HTTP->errorNumber();
		$errmsg = $HTTP->errorMessage();

		if ($errnum)
		{
			// An error occurred!
			$this->_setError(QuickBooks_Payments::ERROR_HTTP, $errnum . ': ' . $errmsg);
			return false;
		}

		if ($info['http_code'] == 401)
		{
			$this->_setError(QuickBooks_Payments::ERROR_AUTH, 'Payments return a 401 Unauthorized status.');
			return false;
		}

		// Everything is good, return the data!
		$this->_setError(QuickBooks_Payments::ERROR_OK, '');
		return $return;
	}

Perform a Quickbooks Payments operation via HTTP

@param  $Context
@param  $url_path
@param  $raw_body
@param  $operation
@return boolean


protected static function _mapToQuickBooksID($func, $user, $type, $ID)
	{
		if (strlen($func))
		{
			if (false === strpos($func, '::'))
			{
				return $func($type, $ID);
			}
			else
			{
				$tmp = explode('::', $func);

				return call_user_func(array( $tmp[0], $tmp[1] ), $type, $ID);
			}
		}
		else
		{
			$editsequence = '';
			$extra = null;

			//print('mapping: ' . $user . ', ' . $type . ', ' . $ID . "\n");

			$Driver = QuickBooks_Driver_Singleton::getInstance();
			$ListID_or_TxnID = $Driver->identToQuickBooks($user, $type, $ID, $editsequence, $extra);

			//print('got back: ' . $ListID_or_TxnID . "\n");

			return $ListID_or_TxnID;
		}
	}

Map an application primary key to a QuickBooks ListID or TxnID

@param string $type		The type of object (i.e.: QUICKBOOKS_OBJECT_CUSTOMER, QUICKBOOKS_OBJECT_INVOICE, etc.)
@param mixed $ID			The primary key of the record
@return string			The ListID or TxnID (or NULL if it couldn't be mapped)


static protected function _mapToApplicationID($func, $user, $type, $ListID_or_TxnID)
	{
		if (strlen($func))
		{
			if (false === strpos($func, '::'))
			{
				return $func($type, $ListID_or_TxnID);
			}
			else
			{
				$tmp = explode('::', $func);

				return call_user_func(array( $tmp[0], $tmp[1] ), $type, $ListID_or_TxnID);
			}
		}
		else
		{
			$extra = null;

			$Driver = QuickBooks_Driver_Singleton::getInstance();
			return $Driver->identToApplication($user, $type, $ListID_or_TxnID, $extra);
		}
	}

static protected function _mapToApplicationID ( $ func , $ user , $ type , $ ListID_or_TxnID ) { if ( strlen ( $ func ) ) { if ( false === strpos ( $ func , '::' ) ) { return $ func ( $ type , $ ListID_or_TxnID ) ; } else { $ tmp = explode ( '::' , $ func ) ; return call_user_func ( array ( $ tmp [ 0 ] , $ tmp [ 1 ] ) , $ type , $ ListID_or_TxnID ) ; } } else { $ extra = null ; $ Driver = QuickBooks_Driver_Singleton :: getInstance ( ) ; return $ Driver -> identToApplication ( $ user , $ type , $ ListID_or_TxnID , $ extra ) ; } }

Map a QuickBooks ListID or TxnID to an application primary key

@param string $type					The type of object
@param string $ListID_or_TxnID		The ListID or TxnID of the object within QuickBooks
@return string						The application record primary key


public static function ShipMethodAddRequest($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $version, $locale, $config = array(), $qbxml = null)
	{
		return QuickBooks_Callbacks_API_Callbacks::_doAddRequest($requestID, $user, $action, $ID, $extra, $err, $last_action_time, $last_actionident_time, $version, $locale, $config, $qbxml);
	}

public static function ShipMethodAddRequest ( $ requestID , $ user , $ action , $ ID , $ extra , & $ err , $ last_action_time , $ last_actionident_time , $ version , $ locale , $ config = array ( ) , $ qbxml = null ) { return QuickBooks_Callbacks_API_Callbacks :: _doAddRequest ( $ requestID , $ user , $ action , $ ID , $ extra , $ err , $ last_action_time , $ last_actionident_time , $ version , $ locale , $ config , $ qbxml ) ; }

Pass a request to QuickBooks

@param string $requestID
@param string $user
@param string $action
@param mixed $ID
@param mixed $extra
@param string $err
@param integer $last_action_time
@param integer $last_actionident_time
@param string $version
@param array $locale
@return boolean


static protected function _filterActions($action_to_priority, $only_do, $dont_do, $type)
	{
		$start = microtime(true);
		foreach ($action_to_priority as $action => $priority)
		{
			//print('stepping 1... [' . (microtime(true) - $start) . ']' . "\n");
			$converted = QuickBooks_Utilities::actionToObject($action);
			//print('stepping 2... [' . (microtime(true) - $start) . ']' . "\n");

			if (count($only_do) and
				(false === array_search($action, $only_do) and
				 false === array_search($converted, $only_do)))
			{
				unset($action_to_priority[$action]);
			}

			if (count($dont_do) and
				(false !== array_search($action, $dont_do) or
				false !== array_search($converted, $dont_do)))
			{
				unset($action_to_priority[$action]);
			}
		}
		//print("\n" . 'ending... [' . (microtime(true) - $start) . ']' . "\n\n");

		arsort($action_to_priority);

		//print_r($action_to_priority);

		return $action_to_priority;
	}

Restrict the queueing maps to "only do these actions" and "dont do these actions" maps

@param array $action_to_priority
@param array $only_do
@param array $dont_do
@return array

Restrict the queueing maps to only do these actions and dont do these actions maps


protected static function _requiredVersion($required, $current, $locale = QUICKBOOKS_LOCALE_US, $action = null)
	{
		if ($locale == QUICKBOOKS_LOCALE_US)
		{
			return $current >= $required;
		}

		return true;
	}

protected static function _requiredVersion ( $ required , $ current , $ locale = QUICKBOOKS_LOCALE_US , $ action = null ) { if ( $ locale == QUICKBOOKS_LOCALE_US ) { return $ current >= $ required ; } return true ; }

Returns TRUE if the current version if it's greater than or equal to the required version

* NOTE: *

@param float $required
@param float $current
@return float

Returns TRUE if the current version if it s greater than or equal to the required version


public static function ListDeletedQueryRequest($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $version, $locale, $config = array())
	{
		$xml = '';

		if (!QuickBooks_Callbacks_SQL_Callbacks::_requiredVersion(2.0, $version, $locale, QUICKBOOKS_DEL_LIST))
		{
			return QUICKBOOKS_SKIP;
		}

		$xml .= '<?xml version="1.0" encoding="utf-8"?>
			<?qbxml version="' . QuickBooks_Callbacks_SQL_Callbacks::_version($version, $locale) . '"?>
			<QBXML>
				<QBXMLMsgsRq onError="' . QUICKBOOKS_SERVER_SQL_ON_ERROR . '">
					<ListDeletedQueryRq requestID="' . $requestID . '">
						<ListDelType>Account</ListDelType>
						<ListDelType>BillingRate</ListDelType>
						<ListDelType>Class</ListDelType>
						<ListDelType>Customer</ListDelType>
						<ListDelType>CustomerMsg</ListDelType>
						<ListDelType>CustomerType</ListDelType>
						<ListDelType>DateDrivenTerms</ListDelType>
						<ListDelType>Employee</ListDelType>
						<ListDelType>ItemDiscount</ListDelType>
						<ListDelType>ItemFixedAsset</ListDelType>
						<ListDelType>ItemGroup</ListDelType>
						<ListDelType>ItemInventory</ListDelType>
						<ListDelType>ItemInventoryAssembly</ListDelType>
						<ListDelType>ItemNonInventory</ListDelType>
						<ListDelType>ItemOtherCharge</ListDelType>
						<ListDelType>ItemPayment</ListDelType>
						<ListDelType>ItemSalesTax</ListDelType>
						<ListDelType>ItemSalesTaxGroup</ListDelType>
						<ListDelType>ItemService</ListDelType>
						<ListDelType>ItemSubtotal</ListDelType>
						<ListDelType>JobType</ListDelType>
						<ListDelType>OtherName</ListDelType>
						<ListDelType>PaymentMethod</ListDelType>
						<ListDelType>PayrollItemNonWage</ListDelType>
						<ListDelType>PayrollItemWage</ListDelType>
						<ListDelType>PriceLevel</ListDelType>
						<ListDelType>SalesRep</ListDelType>
						<ListDelType>SalesTaxCode</ListDelType>
						<ListDelType>ShipMethod</ListDelType>
						<ListDelType>StandardTerms</ListDelType>
						<ListDelType>ToDo</ListDelType>
						<ListDelType>UnitOfMeasureSet</ListDelType>
						<ListDelType>Vehicle</ListDelType>
						<ListDelType>Vendor</ListDelType>
						<ListDelType>VendorType</ListDelType>
						' . QuickBooks_Callbacks_SQL_Callbacks::_buildFilter($user, $action, $extra, true) . '
					</ListDeletedQueryRq>
				</QBXMLMsgsRq>
			</QBXML>';

		return $xml;
	}

'"?>
			<QBXML>
				<QBXMLMsgsRq onError="'

'">
					<ListDeletedQueryRq requestID="'

Fetch a list of deleted things from QuickBooks


public static function ListDeletedQueryResponse($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $xml, $idents, $config = array() )
	{
		$Driver = QuickBooks_Driver_Singleton::getInstance();

		$Parser = new QuickBooks_XML_Parser($xml);

		$errnum = 0;
		$errmsg = '';
		$Doc = $Parser->parse($errnum, $errmsg);
		$Root = $Doc->getRoot();

		$List = $Root->getChildAt('QBXML QBXMLMsgsRs ListDeletedQueryRs');

		foreach ($List->children() as $Node)
		{
			$map = array();
			$others = array();

			QuickBooks_SQL_Schema::mapToSchema(trim(QuickBooks_Utilities::objectToXMLElement($Node->getChildDataAt('ListDeletedRet ListDelType'))), QUICKBOOKS_SQL_SCHEMA_MAP_TO_SQL, $map, $others);

			if (isset($map[0]))
			{
				$table = $map[0];

				$data = array(
					'qbsql_flag_deleted' => 1,
					);

				$multipart = array( 'ListID' => $Node->getChildDataAt('ListDeletedRet ListID') );

				$Driver->update(QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $table, $data, array( $multipart ));
			}
		}

		return true;
	}

public
static
function
ListDeletedQueryResponse
(
$
requestID
,
$
user
,
$
action
,
$
ID
,
$
extra
,
&
$
err
,
$
last_action_time
,
$
last_actionident_time
,
$
xml
,
$
idents
,
$
config
=
array
(
)
)
{
$
Driver
=
QuickBooks_Driver_Singleton
::
getInstance
(
)
;
$
Parser
=
new
QuickBooks_XML_Parser
(
$
xml
)
;
$
errnum
=
0
;
$
errmsg
=
''
;
$
Doc
=
$
Parser
->
parse
(
$
errnum
,
$
errmsg
)
;
$
Root
=
$
Doc
->
getRoot
(
)
;
$
List
=
$
Root
->
getChildAt
(
'QBXML QBXMLMsgsRs ListDeletedQueryRs'
)
;
foreach
(
$
List
->
children
(
)
as
$
Node
)
{
$
map
=
array
(
)
;
$
others
=
array
(
)
;
QuickBooks_SQL_Schema
::
mapToSchema
(
trim
(
QuickBooks_Utilities
::
objectToXMLElement
(
$
Node
->
getChildDataAt
(
'ListDeletedRet ListDelType'
)
)
)
,
QUICKBOOKS_SQL_SCHEMA_MAP_TO_SQL
,
$
map
,
$
others
)
;
if
(
isset
(
$
map
[
0
]
)
)
{
$
table
=
$
map
[
0
]
;
$
data
=
array
(
'qbsql_flag_deleted'
=>
1
,
)
;
$
multipart
=
array
(
'ListID'
=>
$
Node
->
getChildDataAt
(
'ListDeletedRet ListID'
)
)
;
$
Driver
->
update
(
QUICKBOOKS_DRIVER_SQL_PREFIX_SQL
.
$
table
,
$
data
,
array
(
$
multipart
)
)
;
}
}
return
true
;
}

Handle a list of deleted items from QuickBooks


public static function TxnVoidRequest($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $version, $locale, $config = array())
	{
		$Driver = QuickBooks_Driver_Singleton::getInstance();

		if ($arr = $Driver->get(QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . strtolower($extra['object']), array( QUICKBOOKS_DRIVER_SQL_FIELD_ID => $ID )))
		{
			$xml = '';
			$xml .= '<?xml version="1.0" encoding="utf-8"?>
			<?qbxml version="' . QuickBooks_Callbacks_SQL_Callbacks::_version($version, $locale) . '"?>
			<QBXML>
				<QBXMLMsgsRq onError="stopOnError">
					<TxnVoidRq requestID="' . $requestID . '">
						<TxnVoidType>' . $extra['object'] . '</TxnVoidType>
						<TxnID>' . $arr['TxnID'] . '</TxnID>
					</TxnVoidRq>
				</QBXMLMsgsRq>
			</QBXML>';

			return $xml;
		}

		return '';
	}