PHP Chronofix

PHP

Contents

Introduction

This self-contained library is intended to aid in parsing dates and time values regardless of incoming format, and output the result to a uniform format of choice.

The simple fact is that client side inputs are notoriously unreliable. Even when enforcing inputs to choose or sanitize dates, you may not end up with uniform results across browsers. Or even the SAME browser. Google Chrome for instance, will send a different format depending upon seconds input. Even if you enforce seconds using the step attribute, Chrome will not send seconds as part of the date time string if the user chooses a seconds value of 00.

Unless you choose a value of 01 or greater, Chrome simply won’t include seconds.

However, should you choose a seconds value of 01 or more, then Chrome WILL include the seconds when posting. Same browser. Same version. Same field attributes. Yet entirely different formats depending on perfectly valid user inputs.

You could choose to use text fields and invoke heavy JavaScript date/time pickers, but then you harm the mobile experience, where seamless date choosing is already implemented. If you choose a dynamic solution that looks for support of date time fields, then you regain the mobile experience, but you’re right back to dealing with unreliable formatting.

In a nutshell, if you don’t perform server side validation, you may wind up with undefined behavior from whatever code relies on the input. Databases are particularly finicky. Go ahead and send client side dates directly to an RDMS. Your results will be the same as Forrest Gump’s chocolate box.

Unfortunately, most forms of server side validation are themselves rather persnickety. In the above example, a validator expecting seconds will reject input from Chrome, and several mobile browsers as well. This is where my date and time library comes into play. By leveraging a layered approach and PHP’s string to time, we can accept most any valid time string, verify it, and output in a single uniform format for use.

Use

Simple – Object is initialized with default settings:

// Initialize object with default settings.
$dc_time 		= new dc\Chronofix();

Advanced – Initializing a settings object first, and applying it to the main object.

// Initialize settings object, and use it
// to set date format to Year-Month-Day only.
$dc_time_settings = new dc\Config()
(); $dc_time_settings->set_format('Y-m-d'); // Initialize time library with settings. $dc_time = new dc\Chronofix($dc_time_settings); 

Once initialized the following functions are exposed. All examples assume default settings.

Settings Object

Again be aware settings are entirely optional.

echo $settings->get_format();
// Outputs Y-m-d H:i:s

Returns the format string currently in use.

$string = 'Y-m-d H:i:s';

$settings->set_format($string);

Replaces format in use with $string.

Time Object

$object = $time->get_settings();

Returns a reference to settings object in use by time object. By accessing the current settings object, you can modify settings at runtime.

echo $time->get_time();

// Outputs 2017-02-15 12:00:00

Returns the current date/time value as a string.

$string = '2017-02-15 12:00:00';

echo $time->is_valid($string);

// Outputs TRUE if valid, or FALSE.

For internal use, but exposed for possible utility purposes. Evaluates $string against current date/time format. If the formats match, TRUE is returned. Otherwise FALSE is returned.

echo $time->sanitize();

// Outputs 2017-02-15 12:00:00

Evaluates the current date/time value and attempts to convert its format to the current format setting. Leverages a combination of php’s strtotime and date object to handle nearly any format of dates and times. Outputs newly formatted date/time string, or NULL on failure.

$object = new dc\Config();

$time->set_settings($object);

Replaces current settings object with $object.

$string = '2017-02-15 12:00:00';

$time->set_time($string);

Replaces the current date/time string with $string. You will need to do this before performing any other operations.

Source

Full source available on Github.

Dynamic Stored Procedure Call Generation

PHP

Scripting experiment. Inspector Blair page layout is currently static code. I would like to upgrade to a content managed layout. One of the first challenges is overcoming the diverse array of forms and subsequent data calls. Dynamic queries are NOT acceptable. Instead I’ll need to find a way to create dynamic inputs for stored procedures that can be driven from a forms database.

This experiment was to test a simple array generator. Normally saving to the database is handled by a set of class calls as in this excerpt:

case RECORD_NAV_COMMANDS::SAVE:
	
	// Stop errors in case someone tries a direct command link.
	if($obj_navigation_rec->get_command() != RECORD_NAV_COMMANDS::SAVE) break;
							
	// Save the record. Saving main record is straight forward. We’ll run the populate method on our 
	// main data object which will gather up post values. Then we can run a query to merge the values into 
	// database table. We’ll then get the id from saved record (since we are using a surrogate key, the ID
	// should remain static unless this is a brand new record). 
	
	// If necessary we will then save any sub records (see each for details).
	
	// Finally, we redirect to the current page using the freshly acquired id. That will ensure we have 
	// always an up to date ID for our forms and navigation system.			

	// Populate the object from post values.			
	$_main_data->populate_from_request();
	
	// --Sub data: Role.
	$_obj_data_sub_request = new class_account_role_data();
	$_obj_data_sub_request->populate_from_request();

	// Let's get account info from the active directory system. We'll need to put
	// names int our own database so we can control ordering of output.
	$account_lookup = new class_access_lookup();
	$account_lookup->lookup($_main_data->get_account());

	// Call update stored procedure.
	$query->set_sql('{call account_update(@id			= ?,
											@log_update_by	= ?, 
											@log_update_ip 	= ?,										 
											@account 		= ?,
											@department 	= ?,
											@details		= ?,
											@name_f			= ?,
											@name_l			= ?,
											@name_m			= ?,
											@sub_role_xml	= ?)}');
											
	$params = array(array('<root><row id="'.$_main_data->get_id().'"/></root>', 		SQLSRV_PARAM_IN),
				array($access_obj->get_id(), 				SQLSRV_PARAM_IN),
				array($access_obj->get_ip(), 			SQLSRV_PARAM_IN),
				array($_main_data->get_account(), 		SQLSRV_PARAM_IN),						
				array($_main_data->get_department(),	SQLSRV_PARAM_IN),						
				array($_main_data->get_details(), 		SQLSRV_PARAM_IN),
				array($account_lookup->get_account_data()->get_name_f(), SQLSRV_PARAM_IN),
				array($account_lookup->get_account_data()->get_name_l(), SQLSRV_PARAM_IN),
				array($account_lookup->get_account_data()->get_name_m(), SQLSRV_PARAM_IN),
				array($_obj_data_sub_request->xml(), 	SQLSRV_PARAM_IN));
	
	//var_dump($params);
	//exit;
	
	$query->set_params($params);			
	$query->query();
	
	// Repopulate main data object with results from merge query.
	$query->get_line_params()->set_class_name('blair_class_account_data');
	$_main_data = $query->get_line_object();
	
	// Now that save operation has completed, reload page using ID from
	// database. This ensures the ID is always up to date, even with a new
	// or copied record.
	header('Location: '.$_SERVER['PHP_SELF'].'?id='.$_main_data->get_id());
	
	break;

 

Before we can begin to control the above calls dynamically, we’ll need to break the call down and see if we can assemble the sql string. Here we will concentrate on building the SQL string.

The form parts and column names they send data too will likely be stored in a sub-table of the forms database, and output as a linked list. We need to use those column names in a call string for sending or retrieving data. This simple experiment uses a keyed array to simulate the list we might get and see if we can concatenate a usable stored procedure call string.

$_main_data->populate_from_request();
            
// --Sub data: Role.
$_obj_data_sub_request = new class_account_role_data();
$_obj_data_sub_request->populate_from_request();

// Let's get account info from the active directory system. We'll need to put
// names int our own database so we can control ordering of output.
$account_lookup = new class_access_lookup();
$account_lookup->lookup($_main_data->get_account());

$save_row['id']                = '<root><row id="'.$_main_data->get_id().'"/></root>';
$save_row['log_update_by']    = $access_obj->get_id();
$save_row['log_update_ip']     = $access_obj->get_ip();
$save_row['account']         = $_main_data->get_account();
$save_row['department']        = $_main_data->get_department();
$save_row['name_f']         = $account_lookup->get_account_data()->get_name_f();
$save_row['name_l']         = $account_lookup->get_account_data()->get_name_l();
$save_row['name_m']         = $account_lookup->get_account_data()->get_name_m();    
$save_row['sub_role_xml']     = $_obj_data_sub_request->xml();        

$sql_str = '{call account_update(@';
$sql_str .= implode(' = ?, @', array_keys($save_row));
$sql_str .= ')}';
echo $sql_str;

//

Obviously this alone won’t be enough, but the resulting output looks quite promising:

{call account_update(@id = ?, @log_update_by = ?, @log_update_ip = ?, @account = ?, @department = ?, @name_f = ?, @name_l = ?, @name_m = ?, @sub_role_xml)}

 

 

PHP Directory Scan

PHP

Introduction

This function will scan directories and return keyed arrays of file attributes matching a user provided filter string. Perfect for image, documents, and other sorts of content delivery where a naming convention is known but the directory contents are often appended or otherwise in flux.

Example

Let’s assume we need to locate a series of .pdf newsletters. Occasionally these letters are uploaded to the web server with a big endian date based naming convention.

The documents we need might be part of a larger container with many other items.

Since we know each file begins with “bio_newsletter_”, we can use that as our search string, like this:

$directory 			= '/docs/pdf/';
$filter				= 'bio_newsletter*/';
$attribute			= 'name';
$descending_order 	= TRUE;

$files = directory_scan($directory, $filter, $attribute, $descending_order);

The function will then rummage through our target directory, and return an array with any matched files, giving you an output that looks something like this:

 
Key Value
/docs/pdf/bio_newsletter_2015_09.pdf /docs/pdf/bio_newsletter_2015_09.pdf
/docs/pdf/bio_newsletter_2015_05.pdf /docs/pdf/bio_newsletter_2015_05.pdf
/docs/pdf/bio_newsletter_2015_04.pdf /docs/pdf/bio_newsletter_2015_04.pdf

 

This might look redundant, but that’s because keys are always populated with file name to allow extraction of values by name later, and in this case we are looking specifically for the file name. There is an option of returning one of several attributes, which are reflected in the value.

If the directory does not exist or isn’t readable, the function will return NULL.

Source

// Caskey, Damon V.
// 2012-03-19
//
// Scan a directory for files matching filter
// and return an array of matches.
//
// $directory: 		Directory to scan.
// $filter:		Filter string.
// $attribute:		File attribute to acquire. See here for 
// 			list of available attributes: http://php.net/manual/en/function.stat.php
// $order_descending:	FALSE (default) = Order by file name ascending. 
//			TRUE = Order by file name descending. 
function directory_scan($directory, $filter, $attribute = 'name', $order_descending = FALSE)
{	
    $result 			= NULL;	// Final result.
    $directory_handle 	= NULL; 	// Directory object handle.
	$directory_valid	= FALSE;	// If directory is accessible.
	$stat				= array();	// Attribute array.
	
	// Validate directory.
	$directory_valid = is_readable($directory);
	
	// If the directory is valid, open it
	// and get the object handle.
	if($directory_valid)
	{
		$directory_handle = opendir($directory);
	}
	
	// Do we have a directory handle?
	if($directory_handle) 
	{
		// Scan all items in directory
		// and populate result array with 
		// the attribute of those with
		// names matching our search pattern.
        do 
		{
			// Get first/next item name in the 
			// directory handle.
			$file_name = readdir($directory_handle);
			
			
            if (preg_match($filter, $file_name)) 
			{
                $stat = stat($directory.'/'.$file_name);
				
				// If requested attribute is name, then
				// just pass on the name with directory.
				// Otherwise, pass the requested attribute.
				if($attribute == 'name')
				{
					$result[$file_name] = $file_name;
				}
				else
				{
					$result[$file_name] = $stat[$attribute];
				}
            }
			
        }
		while($file_name !== FALSE);
        
		// Close the directory object.
		closedir($directory_handle);
        
		// Sort the array as requested.
		if ($order_descending)
		{
            arsort($result);
        }
        else
		{
            asort($result);
        }
    }
	
	// Return resulting array.
    return $result;
}

 

A word of caution – directory scanning is simple and effective, but doesn’t scale so well. A few hundred files is fine, but once you start breaching the thousands it’s probably time to break your directory structure down a bit, or consider a RDMS solution.

Until next time!

DC

Bootstrap Remote File Models

Introduction

Opening a model is well documented for the bootstrap framework, assuming the model’s contents are located within the same document. However, if the model target is a remote file the process becomes slightly more nebulous. After some experimenting and Stack Overflow research I have compiled the following steps to do so.

Link

The link for opening a model must still target a container element of some sort, typically a DIV id. You may also choose to target the container by class. For remote files, you need to include a href as well, targeting the remote file location.

<a href="model_document.php" data-target="#model_div_container_id" data-toggle="modal">Open Model</a>

Model Container

Next you need to prepare the calling document (the same document that contains model link) with a model container. This is exactly the same as with a normal model, except you are only adding the model’s container elements, not its content.

<div class="modal fade" id="#model_div_container_id">
    <div class="modal-dialog">
        <div class="modal-content">
        	<!-- Model content area - leave this empty.-->
        </div>
    </div>
</div>

Model Document (Content)

The last step is to prepare your remote document. Add whatever content you wish. The only special caveat to consider is you do NOT add the model container elements to this document. You only add the content. The content itself may include its own container elements, formatting, and so forth. In the example below, the model is a simple list, and therefore only the markup for the list is included.

<ul>	
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
</ul>

You’ve probably already guessed what’s going on here. When the model link is clicked, the remote document’s contents are inserted into the calling document’s target element. Bootstrap Model Documentation (extracted 2017-01-23):

If a remote URL is provided, content will be loaded one time via jQuery’s load method and injected into the .modal-content div. If you’re using the data-api, you may alternatively use the href attribute to specify the remote source.

This is why you must include a target element in the calling document, and conversely NOT include model containers in the remote document. Hope this helps. Special thanks to Buzinas from Stack Overflow.

Until next time!

DC

Quick Tip – PHP Enumeration

PHP

Looking for native enumeration with PHP? Sadly you are out of luck. PHP simply does not support enumeration out of the box. Fortunately though you do have some options. Each one has its attributes and shortcomings based upon your needs.

  • splEnum: This is an experimental PHP extension that will provide enumeration that’s as close to native support as you’re going to get. Because it requires installing additional files to work, it is not a good solution unless you have full server control. Personally I avoid this approach at all costs. I prefer clean installs of PHP. Also, depending on extensions makes your code non portable.
  • Custom class: There are a lot of great enumeration classes out there, or you could always write one from scratch. Marijan Šuflaj has written an excellent version available for use here. The primary disadvantage is complexity. Even using a pre-made class the instantiation and function calls are sometimes just not worth it when all you need is a straight forward list of enumerated values.
  • The third option: Leverage abstract classes to create an encapsulated list. This is the simplest route and what I shall be demonstrating here.

 

Creating a value list really is a simple affair with PHP. All you need is to craft an abstract class and define a group of constants within it. Abstract classes cannot be instantiated as objects, but their members are immediately available for referencing.

Here is an example for calendar months.

// Set up a list of months.
abstract class MONTH
{
	const
		JANUARY		= 1,
		FEBRUARY	= 2,
		MARCH		= 3,
		APRIL		= 4,
		MAY		    = 5,
		JUNE		= 6,
		JULY		= 7,
		AUGUST		= 8,
		SEPTEMBER	= 9,
		OCTOBER		= 10,
		NOVEMBER	= 11,
		DECEMBER	= 12;
}

// Now echo a value.
echo MONTH::APRIL;

That’s really all there is to it! Sure, this isn’t true enumeration. You must still key in values. Plus there’s no error checking or type hinting for switch operations and similar ilk as you’d find in a compiled language. What you do get is a simple list with encapsulation and list type hinting from the IDE. All with minimal code. Keeping the global namespace clean alone is a fantastic benefit.

A few keystrokes and your IDE springs to action.

A few keystrokes and your IDE springs to action.

For most rudimentary enumeration needs this should prove more than sufficient, at least until the fine folks at PHP step up and provide us with the real thing.

Until next time!
DC

PHP Object Oriented Request Data

PHP

This article assumes LAMP or other PHP based web layout, and basic proficiency with Object Oriented PHP scripting.

Have you ever found yourself wanting to use OOP methods in PHP but found it frustrating due to the rather bolted on nature of object support? In particular, dealing with outside data arrays like $_POST and $_GET?

Since the advent of OOP support in PHP, it doesn’t take long to get spoiled by the type hinting and name space advantages OOP offers (I’ll leave the other benefits to be debated by the ever on going Procedural vs OOP soldiers still at war out there).

The problems come when you have to start processing uncontrolled outside data, and as mentioned above, one of the biggest offenders is request data from the client. The moment this occurs – pretty much the moment you build any website, your nice clean OOP concept goes right out the window. Type hinting and automated error checking quickly follow suit. How do we fix this? Let’s start with our goals:

  • Type hinting. Without type hinting, powerful IDEs like Dreamweaver are little more than very expensive text editors.
  • Deal with missing variables. Even with validation, there are times, perhaps by design when some incoming variables might not have a value assigned.
  • Avoiding repetition. Re typing array keys over and over isn’t just inconvenient. You’re practically begging to insert hard to locate typo bugs in the code.

 

Now with those objectives in mind, it’s easy to see the first two could be solved by grouping our variables into an object. The real question is how to get them there and avoid repetitious code.

For a working example let’s assume we have an incoming form post with the following fields:

  • First Name
  • Last Name
  • E-mail
  • Phone

 

We’ll use the HTML5 required attribute and JavaScript validation to make sure most of the fields are filled out, but we want “Phone” to be optional. This is all well and good, but as any experienced developer is aware, you can never, EVER trust client side validation. So even without an optionally blank field, we’re going to have to ensure missing variables are initialized on post.

Let’s try a couple of solutions and evaluate them vs. our criteria. For this article, we’re going to use $_REQUEST. If you aren’t familiar, $_REQUEST is a handy single command that acquires all values you’d normally get from $_GET, $_POST and $_COOKIE.

 Standard Object

// Initialize standard object with request array.
$request = object($_REQUEST);

// Do stuff...

 

On the surface this is a beautifully elegant solution. The PHP Standard Object initializes with $_REQUEST in a single line, and any $_REQUEST vars are now available as data members.

Unfortunately, this method has two serious drawbacks.

  1. Firstly, Standard Object has no methods for our members. All we’ve really accomplished is shifting a bunch of data from one global space to another with no validation or protection. Might be quick and easy, but it’s nothing close to an object oriented solution and will inevitably bite us in the butt at some point.
  2. Any missing $_REQUEST vars stay missing. The Standard Object can’t include what isn’t there to begin with. To solve that issue you might try this:
// If any post vars are missing, populate them with NULL.
if(!isset($_REQUEST['name_f']))     $_REQUEST['name_f']     = NULL;
if(!isset($_REQUEST['name_l']))     $_REQUEST['name_l']     = NULL;
if(!isset($_REQUEST['mail']))       $_REQUEST['mail']       = NULL;
if(!isset($_REQUEST['phone']))      $_REQUEST['phone']      = NULL;

// Initialize standard object with post array.
$post = object(&lt;a title=&quot;$_REQUEST&quot; href=&quot;http://php.net/manual/en/reserved.variables.request.php&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;$_REQUEST&lt;/em&gt;&lt;/a&gt;);

// Do stuff...

 

With this technique we insert blank values into $_REQUEST before initializing Standard Object, and solve the missing variable issue. Unfortunately we’ve exchanged one problem for another: Now we have to repeat every key twice. Lastly, we’ve also done nothing about type hinting. Conclusion? Not with the hassle.

Custom Class, Populate Members Individually In Constructor

class request
{
    private
        $name_f    = NULL, // First name.
        $name_l = NULL, // Last name.
        $mail    = NULL, // Email.
        $phone  = NULL; // Phone number.
    
    // Class constructor
    public function __construct()
    {
        $this-&gt;populate_members();
    }
    
    private function populate_members()
    {
        if(isset($_REQUEST['name_f']))    $this-&gt;name_f    = $_REQUEST['name_f'];
        if(isset($_REQUEST['name_l']))    $this-&gt;name_l    = $_REQUEST['name_l'];
        if(isset($_REQUEST['mail']))      $this-&gt;mail      = $_REQUEST['mail'];
        if(isset($_REQUEST['phone']))     $this-&gt;phone     = $_REQUEST['phone'];
    }
    
    // Accessors
    public function get_name_f()
    {
        $return $this-&gt;name_f;
    }
    ...
    
    // Mutators 
    public function set_name_f($value)
    {
        $this-&gt;name_f = $value;
    }
    ...
}

// Initialize class object.
$post = new post();

// Do stuff...

 

This is a little better. By writing our own class and running a function that populates piecemeal in the constructor, we get type hinting from our IDE and all of our variable instantiate work is packaged up. One problem remains – we still have to type every identifier three times at minimum. That’s a bug just waiting to happen. It’s a good start though, so let’s build on it a bit with the method below:

Custom Class, Dynamic Population

class request
{
    private
        $name_f    = NULL, // First name.
        $name_l = NULL, // Last name.
        $mail    = NULL, // Email.
        $phone  = NULL; // Phone number.
    
    // Class constructor
    public function __construct()
    {
        $this-&gt;populate_members();
    }
    
    private function populate_members()
    {
        // Iterate through each class variable.
        foreach($this as $key =&gt; $value)
        {            
            // If we can find a matching a post var with key matching
            // key of current object var, set object var to the post value.
            if(isset($_POST[$key]))
            {                    
                $this-&gt;$key = $_POST[$key];                                   
            }
        } 
    }
    
    // Accessors
    public function get_name_f()
    {
        $return $this-&gt;name_f;
    }
    ...
    
    // Mutators 
    public function set_name_f($value)
    {
        $this-&gt;name_f = $value;
    }
    ...
}

// Initialize object.
$post = new post();

// Do stuff...

 

Now we’re in business! This might look a bit complex at first glance, but it’s really the simplest of all attempts thus far. More importantly it provides everything we asked for and then some.

Let’s look closer at the populate_members() method and see what’s going on:

foreach($this as $key =&gt; $value)

 

PHP allows us to loop through all the data members of an object just as if it were an array (this has other uses elsewhere, more on that in another article). We can even key the data member’s name as a string just as if it were an array key, and that’s what we’re leveraging for a dynamic data member population. FYI, we really don’t care about $value here, it’s just a necessary filler syntax.

if(isset($_REQUEST[$key]))
{                    
    $this-&gt;$key = $_POST[$key];                                   
}

 

Now in each loop we use the value of $key as an string array key for $_POST and evaluate it with isset(). If isset() returns TRUE, we know the value exists. All that’s needed is to populate the corresponding data member with that $_POST value. Again, PHP helps us out; it is perfectly legal to pass string variables as a data member id at run time.

Once the loop is completed, all $_REQUEST keys that have a matching data member will have been located and the data members populated. Any missing $_REQUEST values are simply ignored; we already established a default value for their corresponding data members.

At this point all conditions have been met. Your IDE will provide type hinting and error checking. Variables are only entered once as data member names. Missing $_REQUEST values are dealt with elegantly. No helper code outside of the class is needed. Best of all, this concept isn’t limited to $_REQUEST at all. Any array array is also fair game.

We could stop right here, but unfortunately there’s just one little flaw remaining. Elegant as it is, this solution fully bypasses the object’s mutator methods, and in the process throws best practice data validation right out the window. There’s nothing stopping you from putting some data validation into the loop (or ignoring best practice), but with just a little more front end work, you can leverage your mutators and keep all of the data validation encapsulated.

Custom Class, Dynamic Mutation

 

class request
{
    private
        $name_f    = NULL, // First name.
        $name_l = NULL, // Last name.
        $mail    = NULL, // Email.
        $phone  = NULL; // Phone number.
    
    // Class constructor
    public function __construct()
    {
        $this-&gt;populate_members();
    }
    
    private function populate_members()
    {
        // Interate through each class variable.
	foreach($this as $key =&gt; $value) 
	{			
		// If we can find a matching a post var with key matching
		// key of current object var, set object var to the post value. 
		if(isset($_REQUEST[$key]))
		{
			// Add 'set_' prefix so member name is now a mutator method name.
			$method = 'set_'.$key;
				
			// If a mutator method by the current name exists, run it and
			// pass current request value. 
			if(method_exists($this, $method)=== TRUE)
			{										
				$this-&gt;$method($_REQUEST[$key]);
			}
		}
	} 
    }
    
    // Accessors
    public function get_name_f()
    {
        $return $this-&gt;name_f;
    }
    ...
    
    // Mutators 
    public function set_name_f($value)
    {
        $this-&gt;name_f = $value;
    }
    ...
}

 

This looks and works very similar to the above method, but instead of populating members directly, values are routed through the object’s mutator methods. This does double duty of encapsulating validation and providing a secondary gateway to control just which members you want populated at all.

The secret is that PHP allows you to reference methods by string at run time just it it does members. First, we use a bit of string manipulation to get a member name from our $_REQUEST name. I use a prefix of “set_” for my mutators, so it’s a simple matter of tacking that on to a placeholder variable.

// Add 'set_' prefix so member name is now a mutator method name.
$method = 'set_'.$key;

Next, we will need to make sure the method exists. PHP will immediately halt the script and throw a fatal error if an attempt is made to call non-existent methods, so this check is vital. If the check passes, we use the string to call out mutate method and pass it our $_REQUEST value. Notice how the member’s signature is NOT part of the string – treat it exactly as you would any hard coded member’s signature.

// If a mutator method by the current name exists, run it and
// pass current request value. 
if(method_exists($this, $method)=== TRUE)
{										
	$this-&gt;$method($_REQUEST[$key]);
}

Now we truly are ready. All of our $_REQUEST data is packaged into a fully encapsulated object with elegant evaluation and gateways. Our IDE will now provide us with type hinting and error checking, plus all of the code is reusable and double keying has been kept to the bare minimum. No more hidden typos, mystery variables, and annoying PHP notice errors!

Give it a try, season to taste and enjoy.

Until next time!
DC

Lists and PHP Range

PHP

This article assumes LAMP or other PHP based web layout, and basic proficiency with PHP scripting.

Imagine you’ve just finished one of those simple but massive and often tedious alphabetical link lists or tables all of us in the web business are called upon to assemble at some point. You know the customer will appreciate the convenience of anchors and bookmark links. On the surface it would appear you have the less than fun choices of cobbling together a cumbersome loop to generate your links, or to type and double check each one by hand. Fortunately as of PHP version 4.0 there is a better option: Range.

Range is an oft overlooked and very useful function that will accept two bookends and return an array containing the in between values in order (bookends included).

For an example, to get the set of letter bookmark links at the top of this page, we could do it the hard way:

<a href="#A">A</a>
<a href="#B">B</a>
<a href="#C">C</a>

We could also write some home grown functions, probably involving a nested loop or two. But why do either when we could get the same result like this?

$markup = NULL;     // List markup.
$letter = NULL;     // Letter placeholder.
$range  = array();  // Range array.
 
// Get range array, A to Z.
$range = range('A', 'Z');
 
// Loop range array.
foreach ($range as $letter)
{
    // Build link list markup.
    $markup .= '<a href="#'.$letter.'">'.$letter.'</a> '; 
} 
// Output markup 
echo $list; 

Simple, elegant, reusable, and best of all it’s far less prone to human error. Either the whole list works or none of it will, in which case you will know right away without the need to check each link individually.

Try your own experiments with range; it is quite versatile and extremely powerful in the right hands. It’s nothing you couldn’t do piecemeal or with custom solutions, but will most certainly simplify the process – freeing up your efforts for far more worthy causes.

Until next time!
DC

Page – Main

<?php require_once($_SERVER['DOCUMENT_ROOT']."/libraries/php/classes/config.php"); //Basic configuration file. ?>


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>UK - Environmental Health And Safety</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<link rel="stylesheet" href="libraries/css/style.css" type="text/css" />
<link rel="stylesheet" href="libraries/css/print.css" type="text/css" media="print" />
</head>

<body>

<div id="container">
	<div id="mainNavigation">
		<?php include($cDocroot."libraries/includes/inc_mainnav_0001.php"); ?>
	</div>
	<div id="subContainer">
		<?php include("a_banner_0001.php"); ?>
		<div id="subNavigation">
		 	<?php include("a_subnav_0001.php"); ?> 
		</div>
		<div id="content">
			<p class="header">Welcome</p>
		  <p>Welcome to the University of Kentucky's Environmental Health And Safety Division. UK safety begins with you!</p>
		  <p class="sub_header">OUR MISSION</p>
		  <p>The EHS Division supports   the University's teaching, research, and public service mission by   promoting a safe, healthful, clean, and accessible campus environment.</p>
		  <p>The Division's   programs are intended to provide safe and healthy conditions for work   and study, protect the environment, and comply with applicable laws and   regulations.  The Division serves the University community by providing   technical services, education and training, periodic audits, and   compliance assistance.</p>
		 
	      <?php include($cDocroot."libraries/includes/inc_updates_0001.php"); ?>
	  </div>       
	</div>    
		<div id="sidePanel">		
			<?php include($cDocroot."a_sidepanel_0001.php"); ?>		
		</div>
	<div id="footer">
		<?php include($cDocroot."libraries/includes/inc_footer_0001.php"); ?>		
	</div>
</div>

<div id="footerPad">
<?php include($cDocroot."libraries/includes/inc_footerpad_0001.php"); ?>
</div>
</body>
</html>

Class – Utility

<?php 

class class_utility {

	/*
	Utility
	Damon Vaughn Caskey
	2013-01-09
	
	Miscellaneous utility functions.
	*/
	
	public function utl_color_alternation($i, $cColorEven="#DDDDFF", $cColorOdd="#CECEFF")
	{		
		/*
		back_alt_0001 - https://www.caskeys.com/dc/?p=4900
		Damon Vaughn Caskey
		2012-10-18
		
		Output alternating background colors for even/odd rows in tables or other types of layouts.
		
		$i:				Current row/location/count. 
		$cColorEven:	Color to output if $i is an even number.
		$cColorEven:	Color to output if $i is an odd number.
		*/
		
		if($i%2)				//Even number?
		{				
			return $cColorEven;	//Return even color.
		}
		else
		{	
			return $cColorOdd;	//Return odd color.
		}
	}
	
	public function utl_get_get($cID, $cDefault=NULL)
	{
		/*
		utl_get_get
		Damon Vaughn Caskey
		2013-01-01
		
		Wrapper to obtain get value.
		
		$cID:		Index.
		$cDefault:	Default on not set.
		*/
		
		return $this->utl_validate_isset($_GET[$cID], $cDefault);
	}
	
	public function utl_get_post($cID, $cDefault=NULL)
	{
		/*
		utl_get_post
		Damon Vaughn Caskey
		2013-01-01
		
		Wrapper to obtain post value.
		
		$cID:		Index.
		$cDefault:	Default on not set.
		*/
		//echo $_POST[$cID];
		
		return $this->utl_validate_isset($_POST[$cID], $cDefault);
	}
	
	public function utl_get_server_value($cID, $cDefault=NULL)
	{
		/*
		utl_get_server_value
		Damon Vaughn Caskey
		2013-01-01
		
		Wrapper to obtain server value.
		
		$cID:		Index.
		$cDefault:	Default on not set.
		*/
		
		return $this->utl_validate_isset($_SERVER[$cID], $cDefault);
	}
	
	public function utl_str_to_array($cList=NULL, $cDel=",")
	{	
		/*
		str_to_array
		Damon Vaughn Caskey
		2013-01-09
		
		Break string into indexed array with no spaces.
		
		$cList:	List array to break up.
		$cDel:	Delimiter.
		*/
	
		/*
		If list is populated remove spaces and break into array.
		*/
		if($cList)									//List populated?								
		{
			$cList = str_replace (" ", "", $cList);	//Remove spaces.
			$cList = explode($cDel, $cList);		//Break into array.
		}
		
		/*
		Return end result.
		*/
		return $cList;
	}
	
	public function utl_redirect($cURL=NULL)
	{	
		/*
		utl_redirect
		Damon Vaughn Caskey
		2013-01-09
		
		Send header that redirects client to new page.
		
		$cURL:	Target address.
		*/

		/*
		If headers haven't been sent, redirect user to an error page. Otherwise we'll just have to die and settle for a plain text message.
		*/
		
		if(headers_sent())
		{ 
			/*
			Good coding will always avoid attempting to resend headers, but let's make sure to catch them here before PHP throws a nasty error.
			*/			
		}
		else
		{			
			header('Location: '.$cURL);
			return TRUE;
		}
		
		/*
		Return end result.
		*/
		return FALSE;
	}
	
	public function utl_validate_email(&$cValue, $cDefault=NULL)
	{
		/*
		utl_validate_email
		Damon Vaughn Caskey
		2013_01_01
		
		Validate email variable.
		
		$cValue:	Email value.
		$cDefault:	Default on fail.
		*/
				
		if(filter_var($cValue, FILTER_VALIDATE_EMAIL)) 
		{			
			list($user,$domaine)=explode("@", $cValue, 2);
			
			if(!checkdnsrr($domaine, "MX")&& !checkdnsrr($domaine, "A"))
			{
				/*
				Bad domain.
				*/
				$cValue = FALSE;
			}
			else 
			{
				//Domain OK.
			}
		}
		else 
		{		
			/*
			Bad address.
			*/	
			$cValue = FALSE;
		} 
		
		if($cValue == FALSE)
		{
			$cValue = $cDefault;
		}
		
		return $cValue;
	}
	
	public function utl_validate_ip(&$cValue, $cDefault=NULL)
	{
		/*
		utl_validate_ip
		Damon Vaughn Caskey
		2013-01-01
		
		Validate ip variable.
		
		$cValue:	ip value.
		$cDefault:	Default on fail.
		*/
		
		$cValue = filter_var($cValue, FILTER_VALIDATE_IP);		
		
		if($cValue == FALSE)
		{
			$cValue = $cDefault;
		}
		
		return $cValue;
	}
	
	public function utl_validate_isset(&$cValue, $cDefault=NULL)
	{
		/*
		utl_validate_isset
		Damon Vaughn Caskey
		2013-01-01
		
		Return default if variable is not set.
		
		$cValue:	Value.
		$cDefault:	Default on not set.
		*/
		
		if(!isset($cValue))
		{
			$cValue = $cDefault;
		}
		
		return $cValue;
	}
	
	public function utl_if_exists($cValue, $cValPrefix=NULL, $cValCadence=NULL, $cAlt=NULL)
	{
		/*
		utl_
		Damon Vaughn Caskey
		2013-01-01
		
		Return self if self has a value, or $cAlt if $cValue is empty or null. Reduces need to retype and 
		potentiality mix up variable names or array keys twice in common "echo <X> if it has a value" situations.

		Preconditions:
			$cValue: Value to test and return if it exists.
			$cValPrefix: Add to front of $cValue on return.
			$cValCadence: Add to end of $cValue on return.
			$cAlt: Value to return if $cValue is NULL.
		*/
		
		/* Vaiables */
		$cReturn = $cAlt;	//Final value to return.
		
		/* Value exists? */
		if($cValue)
		{
			/* Tack on additions and return value. */			
			$cReturn = $cValPrefix.$cValue.$cValCadence;
		}
		
		return $cReturn;		
	}
}
?>



Class – Training

<?php

class class_training
{    

	/*
	constants
	Damon Vaughn Caskey
	2012_12_18
	
	Global constants. 
	*/		
		
	private	$oDB		= NULL;	//Class object: Database.
	private $oDBA		= NULL;	//Class object: Database (answers).
	private $oSes		= NULL;	//Class object: Session.
	private	$oUtl		= NULL;	//Class object: Filter.
	private	$oErr		= NULL;	//Class object: Error.
	private $oFrm		= NULL;	//Class object: Forms. 		
	private $cQuery		= NULL;
	private $cParams	= NULL;
	private $cTVarsTD	= NULL;	//Class variable ID array.
	private $cTVars		= NULL;
	
	function __construct($oDep)
	{
		/*
		Constructor
		Damon Vaughn Caskey
		2012_12_29
		
		Class constructor.
		*/
		
		/* Import object dependencies. */
		$this->oSes	= $oDep['Ses'];
		$this->oDB 	= $oDep['DB'];
		$this->oFrm	= $oDep['Frm'];
		$this->oFil	= $oDep['Fil'];
		$this->oErr	= $oDep['Err'];
		$this->oDBA	= new class_db(array('Utl' => $oDep['Utl'], 'Err' => $oDep['Err']));
				
		/* Verify object dependencies. */
		if(!$this->oDBA)	trigger_error("Missing object dependency: Database (Ans).", E_USER_ERROR);
		if(!$this->oDB)		trigger_error("Missing object dependency: Database.", E_USER_ERROR);
		if(!$this->oSes)	trigger_error("Missing object dependency: Session.", E_USER_ERROR);	
		if(!$this->oFrm)	trigger_error("Missing object dependency: Forms.", E_USER_ERROR);	
		if(!$this->oFil)	trigger_error("Missing object dependency: Filter.", E_USER_ERROR);		
		if(!$this->oErr)	trigger_error("Missing object dependency: Error.", E_USER_ERROR);		
	}
	
	public function training_quiz_grade($cQuizID)
	{
		$cQuestionCount = NULL;
		$cQuestionRight = NULL;	
		$val			= NULL;	
		$iQueCnt		= 0;
		$cResponse		= NULL;
		$cQuizGradeStr	= NULL;
		$ans			= 0;
		$cPercentage	= 0;
								
		$cQuestionCount = $this->oSes->session_get('quiz_question_count');
		$cQuestionRight = $this->oSes->session_get('quiz_answer_right');
		
		$cQuestionRight = str_replace (" ", "", $cQuestionRight);	//Remove spaces.
		$cQuestionRight = explode(",", $cQuestionRight);			//Break into array.
		
		foreach($cQuestionRight as $val)				//Loop array collection.
		{		
			/* Increment counter. */
			$iQueCnt++;					
			
			/* Get question response */
			$cResponse = $this->oFil->utl_get_post("Q".$iQueCnt);
							
			$cQuizGradeStr.= "Question ".$iQueCnt." response: ";	
			
			if (!$cResponse)
			{
				$cQuizGradeStr.= "No answer.<br/>";
			}
			else				
			{
				if ($cResponse == $val)
				{
					$cQuizGradeStr.= $cResponse." - Correct.<br/>";
					$ans++;
				}
				else
				{
					$cQuizGradeStr.= $cResponse." - Incorrect.<br/>";					
				}	
			}							
		}
		
		$cPercentage	= round(($ans / $iQueCnt)*100);
			
		$cQuizGradeStr.= "<br />You answered ".$ans. " of ".$iQueCnt. " correctly (".$cPercentage."%).<br /><br />";
					
		$array["ans"] 			= $ans;
		$array["percentage"] 	= $cPercentage;
        $array["text"] 			= $cQuizGradeStr;
        			
		return $array;		
		
	}
		
	public function training_quiz_questions($cQuizID, $cOrder=NULL, $cQuantity=NULL)
	{
		/*
		training_quiz_questions
		Damon V. Caskey
		2011_11_29
		~2012_12_23: DB Class.
		
		Populate question list and possible answers from database from Quiz ID.
		
		$cQuizID:	Quiz ID to get quiz questions from. 
		*/		
  					
		$cQuizStr 			= NULL;								//Final output string to be placed into page.
		$QuestionID			= NULL;								//Current question ID in question loop.	
		$cQuestionCount		= 0;								//Include ID into question header.		
		$cQuestionRight		= NULL;								
				
		if(!$cOrder)
		{
			$cOrder = NULL;	//Order in table.	
		}
		else if($cOrder == 1)
		{
			$cOrder = "ORDER BY	question_order";	//User specified order.
		}
		else if($cOrder == 2)
		{
			$cOrder = "ORDER BY	newid()";	//Random order	
		}
		
		$cQuantity = $cQuantity ? "TOP ".$cQuantity : NULL;
		
		/* Construct questions query string. */
		$this->cQuery = "SELECT "
			.$cQuantity
			." *
			FROM 		tbl_class_train_questions
			WHERE		fk_tbl_class_train_parameters_guid_id = ? "
			.$cOrder;	
		
		/* Apply parameters. */
		$this->cParams = array(&$cQuizID);
		
		/* Execute questions query. */
		$this->oDB->db_basic_select($this->cQuery, $this->cParams);
		
		/* Construct answers query string. */
		$this->cQuery = "SELECT 
			* 
			FROM 	tbl_class_train_answers
			WHERE	fk_tbl_class_train_questions_guid_id = ?
			ORDER BY answer_order, value";
		
		/* Apply parameters. */
		$this->cParams = array(&$QuestionID);
		
		while($this->oDB->db_line())
		{
			$QuestionID = $this->oDB->cDBLine["guid_id"];
			
			/*	Get answer set matching current question ID. */
			$this->cParams = array(&$QuestionID);
			
			/*Record answer array. First element will be blank. ( ", A, C, B, A, etc.") */
			if($cQuestionRight)
			{
				$cQuestionRight .= ", " .$this->oDB->cDBLine["right_answer"];
			}
			else
			{
				$cQuestionRight = $this->oDB->cDBLine["right_answer"];
			}
			
			/* Build question string. */
			$cQuizStr 	.="<p><span class='TrainQuestionHeader'>"
						."Question ".++$cQuestionCount			
						."</span><br />"
						."<span class='TrainQuestionText'>"			
						.$this->oDB->cDBLine["question"]		
						."</span><br />";			
			
			/* Execute answers query. */
			$this->oDBA->db_basic_select($this->cQuery, $this->cParams);
			
			while ($this->oDBA->db_line())
    		{			
				$cQuizStr	.=	"<input type='radio' name='Q"					
							.$cQuestionCount							
							."' value='"	
							.$this->oDBA->cDBLine["value"]	
							."' />"
							."<span class='TrainQuestionAnswerHeader'>"									
							.$this->oDBA->cDBLine["value"]
							.")</span> "
							."<span class='TrainQuestionAnswerText'>"										
							.$this->oDBA->cDBLine["text"]
							."</span><br />";							
			}						
		}			
		
		if(!$cQuizStr)
		{
			$cQuizStr = "<h2><span class='alert'>No questions available.</span></h2>";
		}
		
		$this->oSes->session_set('quiz_answer_right', $cQuestionRight);
		$this->oSes->session_set('quiz_question_count', $cQuestionCount);
				
		return $cQuizStr;
	}
	
	public function training_quiz_questions_setup($cQuizID, $cOrder=NULL, $cQuantity=NULL)
	{		
		/*
		class_quiz_questions_0003
		Damon V. Caskey
		2012_10_04
		
		Create form to modifiy questions and answers.
		
		$cQuizID:	Quiz ID to get quiz questions from. 
		*/		
  			
		$cQuizStr 			= NULL;								//Final output string to be placed into page.
		$cQuery				= NULL;								//Query string.
		$cParams			= NULL;								//Parameter array.
		$oDBA				= NULL;								//Database class object (answers). 
		$QuestionID			= NULL;								//Current question ID in question loop.	
		$cOrderLst			= NULL;								//Droplist values for question order.
		$cAnswerValLst		= NULL;
		
		/* Initialize database class objects. */
		$oDBA	= new class_db(array("Utl" => $oUtl, "Err" => $oErr));					//Database class object (Answers).
		
		/* Prepare answer dropdown list values */
		$cAnswerValLst 		= array_merge(array("-" => "-"), range('A', 'Z'));
				
		/* Construct questions query. */
		$cQuery = "SELECT "
			.$cQuantity
			." *
			FROM 		tbl_class_train_questions
			WHERE		fk_tbl_class_train_parameters_guid_id = ? 
			ORDER BY	question_order";	
						
		/* Apply question parameters. */
		$cParams = array(&$cQuizID);
		
		/* Execute questions query */
		$oDB->db_basic_select($cQuery, $cParams);						
		
		$cQuery = "SELECT 
						* 
						FROM 	tbl_class_train_answers
						WHERE	fk_tbl_class_train_questions_guid_id = ?
						ORDER BY answer_order, value";
		
		/* Apply answer parameters. */
		$cParams = array(&$QuestionID);
		
		/* Prepare question order dropdown list values */
		$cOrderLst = range(1, $oDB->iDBRowCount);
			
		while($oDB->db_line())
		{
			$QuestionID = $oDB->cDBLine["guid_id"];						
			$cAnswers = NULL;					  
												
			$oDBA->db_basic_select($cQuery, $cParams);
			
			while($oDBA->db_line())
			{			
				$cAnswers .= "  
					
					<form name='frm_answer_update' id='frm_answer_update_".$oDBA->cDBLine["guid_id"]."' method='post' action='".$_SERVER['PHP_SELF']."#a_question_".$oDB->cDBLine["question_order"]."'>
						<input type='hidden' name='EditMode' value='1'  />
						<input type='hidden' name='id_guid' value='".$oDBA->cDBLine["guid_id"]."' />
						  <table width='100%' border='0' cellspacing='0' cellpadding='2' bgcolor='#DDDDFF'>
							<tr>
							  <td width='10%'>
								  <select name='frm_lst_answer_val' id='frm_lst_answer_val_".$oDBA->cDBLine["guid_id"]."'>"
									.$this->oFrm->forms_select_options($cAnswerValLst, NULL, $oDBA->cDBLine["value"], FALSE).
								  "</select>
							  </td>
							  <td width='70%'><textarea name='frm_ta_answer_text' id='frm_ta_answer_text_".$oDBA->cDBLine["guid_id"]."' cols='35' rows='1'>".$oDBA->cDBLine["text"]."</textarea></td>
							  <td width='10%' align='center'><input type='image' src='/media/image/icon_save_0001.png' name='frm_btn_ans_save' id='frm_btn_save_ans_".$oDBA->cDBLine["guid_id"]."' value='Save' /></td>
							  <td width='10%' align='center'><input type='image' src='/media/image/icon_delete_0001.png' name='frm_btn_ans_delete' id='frm_btn_delete_ans_".$oDBA->cDBLine["guid_id"]."' value='Delete' /></td>
							</tr>
						  </table>
					</form><br />";									
										
			}				  
		  
			$cAnswers .= "<div id='new_answer_".$oDB->cDBLine["guid_id"]."'>

		  	<form name='frm_answer_new' id='frm_answer_new_".$oDB->cDBLine["guid_id"]."' method='post' action='".$_SERVER['PHP_SELF']."#a_question_".$oDB->cDBLine["question_order"]."'>
					<input type='hidden' name='EditMode' value='1'  />
					<input type='hidden' name='id_guid' value='".$oDB->cDBLine["guid_id"]."' />
					  <table width='100%' border='0' cellspacing='0' cellpadding='2' bgcolor='#DDDDFF'>
						<tr>
						  <td width='10%'>                          
							  <select name='frm_lst_answer_val' id='frm_lst_answer_val_".$oDB->cDBLine["guid_id"]."' >"
							   .$this->oFrm->forms_select_options($cAnswerValLst, NULL, "-", FALSE). 
							  "</select>
						  </td>
						  <td width='70%'><textarea name='frm_ta_answer_text' id='frm_ta_answer_text_".$oDB->cDBLine["guid_id"]."' cols='35' rows='1'></textarea></td>
						  <td width='20%' align='center'><input type='image' src='/media/image/icon_save_0001.png' name='frm_btn_ans_add' id='frm_btn_ans_add_".$oDB->cDBLine["guid_id"]."' value='Add' /></td>
						</tr>
					  </table>
				</form></div>";
		  
			  $cQuizStr 	.= "
			  <br />
			  <div id='question_".$oDB->cDBLine["guid_id"]."'>
			  <table width='100%' border='0' cellspacing='0' cellpadding='0' bgcolor='#F0F0FF'>
				<tr>
				  <td>
				  
					  <a name='a_question_".$oDB->cDBLine["question_order"]."' id='a_question_".$oDB->cDBLine["question_order"]."'></a>
					  <form name='frm_question_update' id='frm_question_update_".$oDB->cDBLine["guid_id"]."'  method='post' action='".$_SERVER['PHP_SELF']."#a_question_".$oDB->cDBLine["question_order"]."'>							  
					  <input type='hidden' name='EditMode' value='1'  />
					  <input type='hidden' name='id_guid' value='".$oDB->cDBLine["guid_id"]."' />
						<table width='100%' border='0' cellspacing='0' cellpadding='1' >
						  <tr>
							<th colspan='2'><table width='100%' border='0' cellspacing='0' cellpadding='0'>
							  <tr>
								<td width='77%' align='left'>Question
								  <select name='frm_lst_order' id='frm_lst_order_".$oDB->cDBLine["guid_id"]."'>"
									.$this->oFrm->forms_select_options($cOrderLst, NULL, $oDB->cDBLine["question_order"], FALSE).
								  "</select>
								  
								  </td>
								<td width='11%'><input type='submit' name='frm_btn_que_save' id='frm_btn_que_save_".$oDB->cDBLine["guid_id"]."' value='Save' /></td>
								<td width='12%'><input type='submit' name='frm_btn_que_delete' id='frm_btn_que_delete_".$oDB->cDBLine["guid_id"]."' value='Delete' onclick='return confirmSubmit()' /></td>
							  </tr>
							</table></th>
						  </tr>
						  <tr>
							<td><label for='frm_ta_question_val'>Question Text</label></td>
							<td><textarea name='frm_ta_question_val' id='frm_ta_question_".$oDB->cDBLine["guid_id"]."' cols='45' rows='5'>".$oDB->cDBLine["question"]."</textarea></td>
						  </tr>
						  <tr>
							<td><label for='frm_lst_right_answer'>Correct Response</label></td>
							<td><select name='frm_lst_right_answer' id='frm_lst_right_answer_".$oDB->cDBLine["guid_id"]."' >"
							  .$this->oFrm->forms_select_options($cAnswerValLst, NULL, $oDB->cDBLine["right_answer"], FALSE).
							"</select></td>
						  </tr>
						</table>
					  </form><br />"
					  
					  ."<div id='answers_".$oDB->cDBLine["guid_id"]."'>"
					  .$cAnswers
																 
				  ."</div></td>
				</tr>
			  </table></div>";			
								
		}	
		
			$cOrderLst[$oDB->iDBRowCount+1] = $oDB->iDBRowCount+1; //Add one more to the order list.
		
			$cQuizStr .= "<br />
					  <div id='question_new'>
					  <table width='100%' border='0' cellspacing='0' cellpadding='0' bgcolor='#F0F0FF'>
						<tr>
						  <td>						  
							  <a name='a_question_".($oDB->iDBRowCount+1)."' id='a_question_".($oDB->iDBRowCount+1)."'></a>
							  <form name='frm_question_new' id='frm_question_new'  method='post' action='".$_SERVER['PHP_SELF']."#a_question_".($oDB->iDBRowCount+1)."'>
							  <input type='hidden' name='trigger_type' value='question_add'  />
							  <input type='hidden' name='id_guid' value='".$oDBA->cDBLine["guid_id"]."' />
							  <input type='hidden' name='EditMode' value='1'  />
							  	<table width='100%' border='0' cellspacing='0' cellpadding='1' >
								  <tr>
									<th colspan='2'><table width='100%' border='0' cellspacing='0' cellpadding='0'>
									  <tr>
										<td width='77%' align='left'>Question
										  <select name='frm_lst_order' id='frm_lst_order_".$oDB->cDBLine["guid_id"]."'>"
											.$this->oFrm->forms_select_options($cOrderLst, NULL, ($oDB->iDBRowCount+1), FALSE).
										  "</select>
										  
										  </td>
										<td width='11%'><input type='submit' name='frm_btn_que_add' id='frm_btn_que_add' value='Add' /></td>
									  </tr>
									</table></th>
								  </tr>
								  <tr>
									<td><label for='frm_ta_question_val'>Question Text</label></td>
									<td><textarea name='frm_ta_question_val' id='frm_ta_question_val' cols='45' rows='5'></textarea></td>
								  </tr>
								  <tr>
									<td><label for='frm_lst_right_answer'>Correct Response</label></td>
									<td><select name='frm_lst_right_answer' id='frm_lst_right_answer' >"
									  .$this->oFrm->forms_select_options($cAnswerValLst, NULL, NULL, FALSE).
									"</select></td>
								  </tr>
								</table>
							  </form>
							</td>
						</tr>
					  </table></div>";
		
		if(!$cQuizStr)
		{
			$cQuizStr = "<h2><span class='alert'>No questions available.</span></h2>";
		}
			
		return $cQuizStr;
	}
	
	public function training_class_record($cTrainingParams)
	{		
		/*
		training_class_record_0001
		Damon Vaughn Caskey
		2013-03-27 (Converted to function class_record_0001 include)
		
		Inserts user variables into class participant database.	
		*/
		
		$cQuery		= NULL;	//Query string.
		$cParams	= NULL;	//Parameter array.
		$cClassID	= NULL;	//Class ID.
		$p_id		= NULL;	//Participant ID.
		$listing_id	= NULL;	//Class listing ID.
		
		/* Build query string. */
		$cQuery ="MERGE INTO tbl_class_participant
		USING 
			(SELECT ? AS Search_Col) AS SRC
		ON 
			tbl_class_participant.account = SRC.Search_Col
		WHEN MATCHED THEN
			UPDATE SET
				name_l				= ?,
				name_f				= ?,									
				room				= ?,
				status				= ?,
				phone				= ?,									
				department			= ?,
				supervisor_name_f	= ?,
				supervisor_name_l	= ?
		WHEN NOT MATCHED THEN
			INSERT (account, name_l, name_f, room, status, phone, department, supervisor_name_f, supervisor_name_l)
			VALUES (SRC.Search_Col, ?, ?, ?, ?, ?, ?, ?, ?)
			OUTPUT INSERTED.id_int;";		
		
		/* Apply parameters. */
		$cParams = array(&$cTrainingParams['account'],
						&$cTrainingParams['name_l'],
						&$cTrainingParams['name_f'],
						&$cTrainingParams['room'],
						&$cTrainingParams['status'],
						&$cTrainingParams['phone'],
						&$cTrainingParams['department'],
						&$cTrainingParams['supervisor_name_f'],
						&$cTrainingParams['supervisor_name_l'],
						&$cTrainingParams['name_l'],
						&$cTrainingParams['name_f'],
						&$cTrainingParams['room'],
						&$cTrainingParams['status'],
						&$cTrainingParams['phone'],
						&$cTrainingParams['department'],
						&$cTrainingParams['supervisor_name_f'],
						&$cTrainingParams['supervisor_name_l']);	
		
		/* Execute query. */	
		$this->oDB->db_basic_action($cQuery, $cParams, TRUE);
		
		/* Get ID of created/updated record. */
		$p_id = $this->oDB->cDBLine["id_int"];
		
		/* 	User demographics have now been found or inserted. Now we will deal with class type, instructor and time. */		
		$cQuery = "INSERT INTO	tbl_class
		
								(class_type,
								trainer_id,
								class_date)
					OUTPUT INSERTED.class_id
								VALUES	(?, ?, ?)";	
		
		$cParams = array(&$cTrainingParams['class'],
			&$cTrainingParams['trainer'],
			&$cTrainingParams['taken']);
						
		/* Execute query. */	
		$this->oDB->db_basic_action($cQuery, $cParams, TRUE);
		
		/* Get ID of new record. */		
		$cClassID = $this->oDB->cDBLine["class_id"];
					
		/* Insert newly created id and participant id to class listing table. */		
		$cQuery = "INSERT INTO tbl_class_listing
		
								(participant_id,
								class_id)
					OUTPUT INSERTED.id_int
								VALUES (?, ?)";	
		
		$cParams = array(&$p_id,
						&$cClassID);
						
		/* Execute query. */	
		$this->oDB->db_basic_action($cQuery, $cParams, TRUE);
		
		/* Get ID of new record. */		
		return $this->oDB->cDBLine["id_int"];			  
	}
	
	public function training_vars_get()
	{
		//foreach ($this->cClassVars as $key => $val)
		//{
		//	$this 		
		//}
	}
}



Class – Tables

<?php 

class class_tables {

	/*
	Tables
	Damon Vaughn Caskey
	2013-03-21
	
	Miscellaneous table functions.
	*/
	
	public 	$cMarkup 		= NULL;	//Resulting markup output. Typically a table.
	private $oUtl			= NULL;	//Utility class object.
	
	function __construct($oDep)
	{
		/*
		Constructor
		Damon Vaughn Caskey
		2013_01_21
		
		Class constructor.
		*/
		
		/* Import object dependencies. */
		$this->oUtl = $oDep['Utl'];
						
		/* Verify object dependencies. */
		if(!$this->oUtl)	trigger_error("Missing object dependency: Utility.", E_USER_ERROR);		
	}
	
	public function tables_db_output($oDB, $bRowCount = TRUE, $cFieldSkip = NULL, $cAuxLink = array("Header" => NULL, "Link" => NULL, "Target" => "_blank", "Text" => "Go"), $cAuxLinkFields = NULL, $cRowStyle = NULL)
	{	
		/*
		tables_db_output
		Damon Vaughn Caskey
		2013-03-20
		
		Create complete table markup from database query.
		
		Preconditions:
			Executed database query.
			$oDB: Object with object variables populated by query.
			$bRowCount: True = Display a row count preceding table.
			$cFieldSkip['<fieldname>', ...]: Array of fields from query to skip when creating table.
			$cAuxLink['Link', 'Text', 'Target']: Adds action link to end of table.
				Link: Page name.
				Target: Page target (_new, _blank, etc.)
				Text: Text to display.				
			$cAuxLinkFields['<fieldname>', ...]: Fields to pass as part of action link.
			$cRowStyle[even, odd]: Array to override default alternate row style.
				
		Postconditions:
			Populate and return $cMarkup with table markup string.
		*/
		
		$i						= NULL;	//Working counter.
		$iRowCount 				= NULL;	//Count of records retrieved by query.
		$cOutput				= NULL;	//Output string.
		$cFieldMetadata			= NULL;	//Metadata collection (field name, type, etc.) for database columns.
		$cFieldMetaDataName 	= NULL; //Individual item name from metadata colelction.
		$cFieldMetaDataValue	= NULL;	//Individual item value from metadata collection.
		$iFields				= NULL;	//Field counter/index.
		$cFieldName				= NULL; //Field name array.
		$cName					= NULL;	//Table markup write in: Field name.
		$cValue 				= NULL;	//Table markup write in: Field value.
		$cLink 					= NULL;	//Table markup write in: Action link.
		
		/* Add extra markup if cAuxLink has a value, otherwise leave NULL. " */
		
		if($cAuxLink["Link"] != NULL)
		{			
			$cAuxLink["Link"] .= "?";
			$cAuxLink["Header"] = $cAuxLink["Header"] != NULL ? "<th>".$cAuxLink["Header"]."</th>" : "<th>Action</th>";			
		}
		
		if($bRowCount)
		{
			$iRowCount = $oDB->iDBRowCount; 
			$cOutput .= '<span class="row_count">' .$iRowCount. ' records found.</span><br/><br/>';
		}
		
		$cOutput .= '<div title="Table" class="overflow"><table border="0" cellpadding="5" cellspacing="0" bgcolor="#CCCCCC"><tr>';	
		
		/* Zero counter */
		$i = 0;
		
		/* Loop each column in query result. */	
		foreach($oDB->cDBMeta as $cFieldMetadata)
		{					
			/* Loop coloumn metadata collection. */
			foreach($cFieldMetadata as $cFieldMetaDataName => $cFieldMetaDataValue)
			{			
				/* Column name? */				
				if($cFieldMetaDataName == 'Name')
				{						
					/* Check field skip array before using this field name */
					if(!in_array($cFieldMetaDataValue, $cFieldSkip))
					{						
						/* Output to table header markup and populate name array. */									
						$cOutput .= "<th>".$cFieldMetaDataValue."</th>";	//Populate table header markup.																	
					}
					
					$cFieldName[$i] = $cFieldMetaDataValue;				//Populate Name array.
					
					/* Increment field count. */
					$iFields++;
				}					
			}
			
			/* Increment counter. */
			$i++;							
		}		
					
		$cOutput .= $cAuxLink["Header"];	
				
		/* Output query results as table. */
		while($oDB->db_line(SQLSRV_FETCH_NUMERIC))
		{		
		
			$cLink 		= $cAuxLink["Link"];
			
			/* Increment line counter */
			$iLine++;
			
			/* Insert table row and style. */
			$cOutput .= "<tr bgcolor='".$this->oUtl->utl_color_alternation($iLine)."'>";
											
			for ($i = 0; $i < $iFields; $i++)
			{					
				$cName		= $cFieldName[$i];
				$cValue 	= $oDB->cDBLine[$i];								
			
				/* Check field skip array before using this field. */
				if(!in_array($cName, $cFieldSkip))
				{				
					$cOutput .= "<td>".$cValue."</td>";
				}
				
				if($cLink != NULL)
				{			
					if(in_array($cName, $cAuxLinkFields))
					{
						$cLink .= $cFieldName[$i]."=".$cValue."&";
					}				
				}
			}
			
			if($cLink != NULL)
			{
				$cOutput .= '<td><a href="'.$cLink.'" target="'.$cAuxLink["Target"].'">'.$cAuxLink["Text"].'</a></td>';								
			}
		}
					
		$cOutput .= "</table><br/><br/></div>";
		
		$this->cMarkup = $cOutput;
		
		return $this->cMarkup;
	}		
}
?>



Class – Sessions

<?php

class class_sessions implements SessionHandlerInterface
{    

	/*
	class_sessions
	Damon Vaughn Caskey
	2012_12_10
	
	Override PHP's default session handling to store data in an MSSQL table. 
	*/	
	
	const 	c_iLife 	= 1440;	//Default session time out (in seconds)
	
	private	$oDB 		= NULL;	//Databse class object.
	private $iLife		= NULL;	//Session time out.

	function __construct($oDep, $iLife=self::c_iLife)
	{
		/*
		Constructor
		Damon Vaughn Caskey
		2012_12_29
		
		Class constructor.
		*/		
								
		/* Set class vars. */
		$this->iLife = $iLife;	//Session time out.
		
		/* Import object dependencies. */
		$this->oDB = $oDep['DB'];
				
		/* Verify object dependencies. */
		if(!$this->oDB)	trigger_error("Missing object dependency: Database.", E_USER_ERROR);		
	}
      
   	public function session_set($cID, $cValue=NULL)
	{
		/*
		session_set
		Damon Vaughn Caskey
		2012_12_23
		
		Wrapper to set value of a $_SESSION[] variable.
		
		$cID:		Session variable name/id.
		$cValue:	Value to set.
		*/
		
		$_SESSION[$cID] = $cValue;
	}
	
	public function session_get($cID)
	{
		/*
		session_get
		Damon Vaughn Caskey
		2012_12_23
		
		Wrapper to aquire value in a $_SESSION[] variable.
		
		$cID:	Session variable name/id.
		*/
		
		$cValue	= NULL;	//Value to return.
		
		/* Get session value if any */
		if(isset($_SESSION[$cID]))
		{
			$cValue = $_SESSION[$cID];
		}	
		
		/* Return value. */
		return $cValue;
	}	
   
   	public function open($savePath, $sessionName)
    {	
		/*
		open
		Damon Vaughn Caskey
		2012_12_10
		
		Set database class object for other session functions. Called by PHP to open session.
		
		$savePath: 		Path to locate session file. Unused.
		$sessionName:	Name of session file. Unused.
		*/
					
		/* Return TRUE. */
        return true;
    }

    public function close()
    {	
		/*
		close
		Damon Vaughn Caskey
		2012_12_10
		
		Filler; function is called by PHP to close session.
		*/			
		
		/* Return TRUE. */
        return true;
    }

    public function read($cID)
    {		
        /*
		read
		Damon Vaughn Caskey
		2012_12_10
		
		Locate and read session data from database.
		
		$cID = Session ID.
		*/
	
		$cData 		= NULL; 						//Final output.
		$cQuery 	= NULL;							//Query string.
		$cTime 		= date(constants::c_cDateF);	//Current time.
		$cParams	= NULL;							//Parameter array.						
					 
		/* Build query string. */
		$cQuery = "SELECT session_data 
					FROM tbl_php_sessions 
					WHERE
							session_id = ? 
						AND 
							expire > ?";
		
		/* Apply parameters. */
		$cParams = array(&$cID, &$cTime); 
		
		/* Execute query. */	
		$this->oDB->db_basic_select($cQuery, $cParams);
						
		/* Get result and pass to local var(s). */
		if($this->oDB->rDBResult)
		{
			/* Set line array. */
			$this->oDB->db_line();
			
			/* Get session data. */
			$cData = $this->oDB->cDBLine['session_data'];
		}	
		
		/* Return results. */
		return $cData;
    }

    public function write($cID, $cData)
    {
		/*
		write
		Damon Vaughn Caskey
		2012_12_10
		
		Update or insert session data. Note that only ID, Expire, and Session Data are 
		required. Other data is to aid in debugging.
		
		$cID 	= Session ID.
		$cData	= Session data.
		*/
		
		$cQuery = NULL;	               		//Query string.
		$cTime 	= NULL;						//Current time.
		$cLoc	= $_SERVER["PHP_SELF"];		//Current file.
		$cIP	= $_SERVER['REMOTE_ADDR'];	//Client IP address.
					
		/* Calculate epirire time. */
		$cTime		= date(constants::c_cDateF, time()+$this->iLife);	
		
		/* Ensure IP string is <= 15. Anything over is a MAC or unexpected (and useless) value. */
		$cIP = substr($cIP, 0, 15);
		
		/* Build query string. */
		$cQuery ="MERGE INTO tbl_php_sessions
		USING 
			(SELECT ? AS Search_Col) AS SRC
		ON 
			tbl_php_sessions.session_id = SRC.Search_Col
		WHEN MATCHED THEN
			UPDATE SET
				session_data	= ?,
				expire			= ?,
				source			= ?,
				ip				= ?
		WHEN NOT MATCHED THEN
			INSERT (session_id, session_data, expire, source, ip)
			VALUES (SRC.Search_Col, ?, ?, ?, ?);";		
		
		/* Apply parameters. */
		$cParams = array(&$cID,
				&$cData,
				&$cTime,
				&$cLoc,
				&$cIP,
				&$cData,				
				&$cTime,
				&$cLoc,
				&$cIP);	
		
		/* Execute query. */	
		$this->oDB->db_basic_action($cQuery, $cParams);
		
		/* Return TRUE. */
		return true;
    }

    public function destroy($cID)
    {	
	
		/*
		destroy
		Damon Vaughn Caskey
		2012_12_10
		
		Delete current session.
		
		$cID: Session ID.		 
		*/
				
		$cQuery 	= NULL;	//Query string.
		$cParams	= NULL;	//Parameter array.
		
		/* Build query string. */
		$cQuery		= "DELETE FROM tbl_php_sessions WHERE session_id = ?";
		
		/* Apply parameters. */
		$cParams	= array(&$cID);
		
		/* Execute query. */	
		$this->oDB->db_basic_action($cQuery, $cParams);		
		
		/* Return TRUE. */
		return true;
    }

    public function gc($maxlifetime)
    {
		/*
		gc (Garbage Cleanup)
		Damon Vaughn Caskey
		2012_12_10
		
		Delete expired session data.
		
		$maxlifetime: Expire time. Unused. 
		*/
		
		$cTime		= date(constants::c_cDateF);	//Current time.	
		$cQuery		= NULL;							//Query string.
		$cParams 	= NULL;							//Parameter array.
		
		/* Build query string. */
		$cQuery		= "DELETE FROM tbl_php_sessions WHERE expire < ?";
		
		/* Apply parameters. */
		$cParams	= array(&$cTime);

		/* Execute query. */	
		$this->oDB->db_basic_action($cQuery, $cParams);
	
		/* Return TRUE. */
		return true;
    }
}



Class – Error

<?php

class class_error
{    

	/*
	class_error
	Damon Vaughn Caskey
	2012_12_28
	
	Error handler.
	*/	
	
	const 	c_cDBEHost		= "box406.bluehost.com";	//Error log DB host.
	const 	c_cDBELName		= "caskeysc_uk";			//Error log DB logical name.
	const 	c_cDBEUser		= "caskeysc_ehsinfo";		//Error log DB user.
	const 	c_cDBEPword		= "caskeysc_ehsinfo_user";	//Error log DB password.
	const	c_iETScript		= 0;						//Error type; general script errors.
	const	c_iETDB			= 1;						//Error type; datbase error.
	
	private $cIP			= NULL;						//$_SERVER['REMOTE_ADDR']
	private $cSource		= NULL; 					//$_SERVER['PHP_SELF']
	private $debug			= 0;
	private $oMail			= NULL;						//Class mail hanlder.
	private $oUtl			= NULL;						//Utility functions.
	
	public	$cErrType		= NULL;						//Error number or user type.
	public 	$cErrCode		= NULL;						//Error code.
	public 	$cErrDetail		= NULL;						//Error detail (SQL string, parameters, user defined data...).
	public 	$cErrFile		= NULL;						//File running at error time.
	public	$cErrLine		= NULL; 					//Error line.
	public 	$cErrMsg		= NULL;						//Error message.
	public	$cErrState		= NULL;						//State of server (ex. SQL State).
	public	$cErrTOE		= NULL;						//Time of error.
	public	$cErrVars		= NULL;						//String dump of variables.
	
	public function __construct($oDep, $debug = 0)
    {
        /* Import object dependencies. */
		$this->oMail = $oDep['Mail'];
		$this->oUtl = $oDep['Utl'];
				
		/* Verify object dependencies. */
		if(!$this->oMail)	trigger_error("Missing object dependency: Mail.", E_USER_ERROR);
		if(!$this->oUtl)	trigger_error("Missing object dependency: Utility.", E_USER_ERROR);
		
		$this->debug = $debug;
        set_error_handler(array($this, 'error_handle_start'));
	
		register_shutdown_function(array(&$this, 'error_shutdown'));
	}
	
	public function error_fatal()
	{	
		/*
		error_fatal
		Damon Vaughn Caskey
		2012_12_30
		
		Run final actions before exit on a fatal error.
		*/
		
		/*
		If headers haven't been sent, redirect user to an error page. Otherwise we'll just have to die and settle for a plain text message.
		*/
		if($this->oUtl->utl_redirect("/a_errors/php.php")===FALSE)
		{ 
			die("I'm sorry; it appears an internal error has occurred while processing your request. The webmaster has been alerted and will resolve this issue as soon as possible.");
		}
		
		exit;
			
	}
	
	public function error_handle_start($cCode=NULL, $cMsg=NULL, $cFile=NULL, $cLine=NULL)
	{
		$this->error_handle($cCode, $cMsg, $cFile, $cLine, self::c_iETScript, NULL, NULL);
		
		return true;		
	}
	
	public function error_handle($cCode=NULL, $cMsg=NULL, $cFile=NULL, $cLine=NULL, $cType=self::c_iETScript, $cState=NULL, $cDetail=NULL)
	{
		/*
		error_run
		Damon Vaughn Caskey
		2012_12_28
		
		Run error mail and and log in single call.
				
		$cCode:		Error code/number.
		$cMsg:		Error message.
		$cFile:		PHP generated file name.
		$cLine:		Code line location.
		$cType:		User defined error type.
		$cState:	Server state (mostly for SQL errors).
		$cDetail:	User added detail.
		*/
		
		$iLevel = NULL;
		$value	= NULL;
		$key	= NULL;
		$i		= 0;
				
		$this->cErrTOE		= date(constants::c_cDateF);
		$this->cIP			= $_SERVER['REMOTE_ADDR'];
		$this->cSource		= $_SERVER['PHP_SELF'];
		$this->cErrType		= $cType;
		$this->cErrFile		= $cFile;
		$this->cErrLine		= $cLine;	
		$this->cErrState	= $cState;
		$this->cErrCode		= $cCode;
		$this->cErrMsg		= $cMsg;
		$this->cErrDetail	= $cDetail;
		$this->cErrVars		= NULL;
						
		/*
		If logging in (/authenticate_0001.php) and error is suppressed then exit and do nothing.
		
		LDAP libraries are bugged. EX: ldap_bind throws error 49 on bad password instead of returning FALSE as documented. 
		In PHP this can only be worked around by suppressing the error. Otherwise suppressing errors with @ is bad practice 
		that should be avoided at all costs. Its use will be ignored within any other file.
		*/
		$iLevel = error_reporting();
		
		if (($iLevel == 0 || ($iLevel & $cCode) == 0) && $this->cSource == "/authenticate_0001.php")
		{
			return true;
		}
				
		if($this->cErrCode)
		{
			/*
			Log error to database.
			*/
			//$this->error_log_db();
			
			/*
			If error is any type other than a notice then immediately end script and send an email alert to webmaster.
			*/
			switch ($this->cErrCode)
			{
				case E_USER_ERROR:
				case E_USER_WARNING:
				case E_ERROR:
				case E_CORE_ERROR:
				case E_COMPILE_ERROR:
				default:
					
					if(isset($_GET))
					{
						foreach($_GET as $key => $value)
						{
							$this->cErrVars .= "GET[".$key."]: ".$value." || ";
						}
					}
					
					if(isset($_POST))
					{
						foreach($_POST as $key => $value)
						{
							$this->cErrVars .= "POST[".$key."]: ".$value." || ";
						}
					}
					
					if(isset($_SESSION))
					{
						foreach($_SESSION as $key => $value)
						{
							$this->cErrVars .= "SESSION[".$key."]: ".$value." || ";
						}
					}
		
					$this->error_mail();
					$this->error_fatal();
					break;
				case E_USER_NOTICE:
				case E_NOTICE:
					break;				
			}		
		}
	}
		
   	public function error_log_db()
	{
		/*
		error_db_log
		Damon Vaughn Caskey
		2012_12_28
				
		Attempt to log error detail to database. Self contained to avoid recursive calls to database class.
		*/
				
		$rDBConn		= NULL;	//Connection reference to DB error log.
		$cQuery			= NULL; //Error query string.
		$rDBStatement	= NULL;	//Prepared query reference.
		
		$rDBConn = new mysqli(self::c_cDBEHost, self::c_cDBEUser, self::c_cDBEPword, self::c_cDBELName);
		
		/* If the error log database connection was successful, insert each error to table. */
		if (!$rDBConn->connect_error) 
		{				
			/* Build query string. */ 		
			$cQuery = "INSERT INTO tbl_gen_errors (toe, ip, type, source, file, line, state, code, vars, msg, details) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
			$rDBStatement = $rDBConn->prepare($cQuery);
			
			/* Bind parameters. */
			$rDBStatement->bind_param("sssssssssss", $this->cErrTOE, $this->cIP, $this->cErrType, $this->cSource, $this->cErrFile, $this->cErrLine, $this->cErrState, $this->cErrCode, $this->cErrVars, $this->cErrMsg, $this->cErrDetail);
			
			/* Execute and close query. */ 
			if($rDBStatement != false)
			{							
				$rDBStatement->execute();
				$rDBStatement->close();
			}
			
			/* Close DB connection. */
			$rDBConn->close();			
		}						
		
	}
	
	public function error_mail()
	{
		/*
		error_mail
		Damon Vaughn Caskey
		2012_12_31
		~2012_01_02: Array list upgrade.
		
		Prepare and send an email error alert.
		*/
		
		$cMsg 	= NULL;
		
		$cMsg = array(
			"Time"				=>	$this->cErrTOE,
			"Type"				=>	$this->cErrType,
			"IP"				=>	$this->cIP,
			"Def. Source File"	=>	$this->cSource,
			"Source File"		=>	$this->cErrFile,
			"Line"				=>	$this->cErrLine,
			"State"				=>	$this->cErrState,
			"Code"				=>	$this->cErrCode,
			"Message"			=>	$this->cErrMsg,
			"Variables"			=>	$this->cErrVars,
			"Details"			=>	$this->cErrDetail
		);
				
		$this->oMail->mail_send($cMsg, "Error Report");
	}
			
	public function error_shutdown()
	{
		/*
		error_shutdown
		Damon Vaughn Caskey
		2012_12_31
		
		Shutdown function to capture error types PHP will not normally allow custom error handlers to deal with.
		*/
		
		$cError	= NULL;		//Last error status.
		
		/*
		Get last error status.
		*/
        $cError = error_get_last();
		
		$this->error_handle($cError['type'], $cError['message'], $cError['file'], $cError['line']);
    }	
}


Class – Mail

<?php

class class_mail
{    

	/*
	class_mail - https://www.caskeys.com/dc/?p=5031
	Damon Vaughn Caskey
	2012_12_10
	
	Mail handler. 
	*/	
		
	const	c_bWMAlert	= TRUE;										//Send webmaster a blind copy?
	const	c_cEDefMsg	= "...";									//Default message.
	const	c_cEHead	= "MIME-Version: 1.0 \r\nContent-type: text/html; charset=iso-8859-1\r\n";	//Default email headers.
	const	c_cESubject	= "From EHS Web";							//Default outgoing email subject.
	const	c_cEWMIn	= "dvcask2@uky.edu";						//Default webmaster's incoming email address.
	const	c_cEWMOut	= "ehs_noreply@uky.edu";					//Default address when server sends mail.
				
	public function mail_send($cMsg=self::c_cEDefMsg, $cSubject=self::c_cESubject, $cTo=self::c_cEWMIn, $cFrom=self::c_cEWMOut, $cBcc=NULL, $bWMAlert=self::c_bWMAlert, $cHeader=self::c_cEHead, $cParams=NULL)
	{	
		/*
		mail_send
		Damon Vaughn Caskey
		2012_12_28
		
		Send HTML mail with standard defaults.
		
		$cMsg:		Body of email.
		$cSubject:	Subject line.
		$cTo:		Outgoing address list.
		$cFrom:		Return address.
		$cBcc:		Blind carbon copy address list.
		$bWMAlert:	Send Bcc to webmaster.
		$cHeader:	Header information.
		$cParams:	Optional parameters.
		*/
	
		$cBody = NULL;	//Final sting for message body.
	
		/*
		Insert From address to header.
		*/
		$cHeader .= "From: ".$cFrom. "\r\n";		
		
		/* 
		If Webmaster alert is on, insert address into Bcc and add to header. Otherwise just add Bcc to header as is.
		*/
		if($bWMAlert===TRUE)
		{			
			$cHeader .= "Bcc: ".self::c_cEWMIn. ", ".$cBcc."\r\n";	
		}
		else
		{
			$cHeader .= "Bcc: ".$cBcc."\r\n";
		}
		
		$cHeader .="\r\n";
		
		/*
		If message passed as a key array, break into list and output as table layout.
		*/		
		if (is_array($cMsg))
		{
			/*
			Initial html and table markup.
			*/
			$cBody = "<html>
						<head>
						  <title>".$cSubject."</title>
						</head>
						<body>
						  <h1>".$cSubject."</h1>
						  <table cellpadding='3'>";
			
			/*
			Get each item in array and place into two column table row.
			*/
			foreach($cMsg as $key => $value)
			{			
				$cBody .= "<tr><th>".$key.":</th><td>".$value."</td></tr>";			
			}	
			
			/*
			Add closing markup.
			*/
			$cBody .= "</table>
					</body>
					</html>";	
		}
		else
		{
			/*
			Output message as is.
			*/
			$cBody = $cMsg;
		}
			
		/*
		Run mail function.
		*/
		return mail($cTo, $cSubject, $cBody, $cHeader, $cParams);		
	}	
}

?>

Class – Forms

<?php 

class class_forms {

	/*
	Utility
	Damon Vaughn Caskey
	2013_01_21
	
	Miscellaneous form input functions.
	*/
		
	/* Constants */
	const FORMS_ID_USE_NAME						= NULL;		//Use name to generate ID.
	const FORMS_ITEM_ADDITIONS_NONE				= NULL;		//No manual additions to generated item list.
	const FORMS_LABEL_NONE						= NULL;		//No label for fieldset item.
	const FORMS_LABEL_BLANK						= 1;		//Blank label for fieldset item.
	const FORMS_LABEL_USE_ITEM_KEY				= 2;		//Use the item key of a field for its fieldset label or visible selection.
	const FORMS_LABEL_USE_ITEM_NAME				= 3;		//Use the item name of a field for its fieldset label or visible selection.
	const FORMS_LABEL_USE_ITEM_VALUE			= 4;		//Use the items value of a field for its fieldset label or visible selection.
	const FORMS_LABEL_TYPE_FIELDSET				= 0;		//Fieldset label.
	const FORMS_LABEL_TYPE_INLINE				= 1;		//Label text only; no formatting.
	const FORMS_LEGEND_NONE 					= NULL;		//No legend for fieldset.
	const FORMS_QUERY_PARAMETERS_NONE			= NULL;		//No query parameters.
	const FORMS_TYPE_RADIO						= 0;		//Radio type list.
	const FORMS_TYPE_SELECT						= 1;		//Select type list.
	const FORMS_VALUE_CURRENT_NONE				= NULL;		//Current (last selected) value.
	const FORMS_VALUE_DEFAULT_NONE				= NULL;		//Default selected value.
	const FORMS_VALUE_DEFAULT_NOW				= -1;		//Default to current time for specific field.
	const FORMS_EVENTS_NONE						= NULL;		//No events attached to fieldset item.
	const FORMS_READ_ONLY_OFF					= FALSE;
	const FORMS_READ_ONLY_ON					= TRUE;
		
	public $cItemsList 							= NULL;		//Array of items list for select/radio/etc.
	public $cFormElement						= NULL;		//Arracy to store completed element markup that will be placed in a fieldset.
	public $cFormElementActions					= NULL;		//Array of actions tied to form element (onchange, onclick, etc.).
	public $cFieldset							= NULL;		//Array of completed fieldset markups ready to echo into page.
	public $cFieldsetAddsA						= NULL;		//Array of additional instructions, links, etc. that may be added to fieldset above items.
				
	function __construct($oDep)
	{
		/*
		Constructor
		Damon Vaughn Caskey
		2013-01-21
		
		Class constructor.
		*/
		
		/* Import object dependencies. */
		//$this->oUtl = $oDep['Utl'];
		$this->oDB	= $oDep['DB'];
		//$this->Err	= $oDep['Err'];
				
		/* Verify object dependencies. */
		//if(!$this->oUtl)	trigger_error("Missing object dependency: Utility.", E_USER_ERROR);		
		if(!$this->oDB)		trigger_error("Missing object dependency: Database.", E_USER_ERROR);
		//if(!$this->Err)		trigger_error("Missing object dependency: Errors.", E_USER_ERROR);
	}	
	
	private function forms_events_markup($cEvents=self::FORMS_EVENTS_NONE)
	{
		/*
		forms_events_markup
		Damon Vaughn Caskey
		2013-03-26
		
		Prepare HTML markup string for fieldset form item events (onchange, onclick, etc.).
		*/
		
		if(isset($cEvents))
		{		
			foreach ($cEvents as $cKey => $cValue)
			{
				
				$cEvent .= $cKey.'="'.$cValue.'"';
			}
		}
		return $cEvent;
	}
	
	public function test()
	{
		echo self::FORMS_LABEL_USE_ITEM_VALUE;
	}
	
	private function forms_label_markup($iStyle=self::FORMS_LABEL_NONE, $iType=self::FORMS_LABEL_TYPE_FIELDSET, $cName=NULL, $cID=NULL, $cKey=NULL, $cValue=NULL)
	{
		/*
		forms_label_markup
		Damon Vaughn Caskey
		2013-03-26
		
		Prepare HTML markup string for fieldset form item labels.
		
		Preconditions:
			$cName: 	Name of item.
			$cID:		ID of item.
			$cKey:		Array key of item.
			$cValue: 	Array value of item.
			$iStyle: 	How to arrange label markup.
				self::FORMS_LABEL_USE_ITEM_KEY = The item's Key will be used as a label.
				self::FORMS_LABEL_USE_ITEM_VALUE = The item's Value will be used as label.
				self::FORMS_LABEL_BLANK = The label will be left blank.
				self::FORMS_LABEL_NONE = No label markuop at all.
		
		Postconditions:
			Return finished label markup string.		
		*/
		
		$cClosing = NULL;
		$cLabel = NULL;
		
		if($iType == self::FORMS_LABEL_TYPE_FIELDSET)
		{
			/* Prepare opening label markup. */
			$cLabel = '<div class="'.$cClass['Container_Label'].'"><label for="'.$cID.'">';
			$cClosing = '</label></div>';	
		}							
			
		switch($iStyle)
		{
			case self::FORMS_LABEL_USE_ITEM_KEY:				
				
				$cLabel .= $cKey.$cClosing;									
				break;
		
			case self::FORMS_LABEL_USE_ITEM_NAME:			
				
				$cLabel .= $cName.$cClosing;									
				break;
			
			case self::FORMS_LABEL_USE_ITEM_VALUE:
				
				$cLabel .= $cValue.$cClosing;											
				break;
			
			case self::FORMS_LABEL_BLANK:
			
				$cLabel .= $cClosing;
				break;				
			
			case self::FORMS_LABEL_NONE:
			
				$cLabel = NULL;
				break;
				
			default:
				
				$cLabel .= $iStyle.$cClosing;							
		}
		
		/* Return label markup string. */
		return $cLabel;
	}
	
	public function forms_list_array_from_query($cQuery=NULL, $cParams=array(self::FORMS_QUERY_PARAMETERS_NONE), $cAddsT=self::FORMS_ITEM_ADDITIONS_NONE, $cAddsB=self::FORMS_ITEM_ADDITIONS_NONE){		
		
		/*
		forms_list_array_from_query
		Damon Vaughn Caskey
		2012-12-19
		
		Create list array directly from query results.
		
		$iType:		Select, options, etc.		
		$cQuery:	SQL query string.
		$cParams:	Parameter array.
		$cDefault:	Default selection.
		$cCurrent:	Current selection.
		$bKey:		Use key as item label?
		$cAddsT:	Additional items to place at top of generated list.
		$cAddsB:	Additional items to place at bottom of generated list.
		*/
	
		$i		=	NULL;		//Counter.
		$cList	=	NULL;		//Output string.
		$cKey	=	NULL;
		
		$cList	=	array();
		
		/* Query for list items. */
		$this->oDB->db_basic_select($cQuery, $cParams);
				
		/* Populate list variable. */
		while($this->oDB->db_line(SQLSRV_FETCH_NUMERIC))
		{	
			/* Get key (used for items visible to user). */
			$cKey = $this->oDB->cDBLine[1];
			
			/* If fields exist beyond first key, append to a single key value. */
			if($this->oDB->iDBFCount > 2)
			{	
				/* Start with field #2 (third field) and append to key string until last field is reached. */
				for($i = 2; $i <= ($this->oDB->iDBFCount-1); $i++)
				{
					$cKey .= "; ". $this->oDB->cDBLine[$i];	
				}
			}
			
			/* Populate list array using visible portion of list values as key. */			 
			$cList[$cKey] = $this->oDB->cDBLine[0];									
		}
		
		/* Merge Additions. (As of 5.4 array_merge() will always reorder indexes). */
		if($cAddsT && is_array($cAddsT))
		{	
			$cList = $cAddsT + $cList;	
		}
		
		if($cAddsB && is_array($cAddsB))
		{	
			$cList += $cAddsB;	
		}	
		
		$this->cItemsList = $cList;
		
		/* Output final list value. */
		return $this->cItemsList;
	}
	
	function forms_list_numeric($iType=NULL, $cQuery=NULL, $cParams=NULL, $cDefault=NULL, $cCurrent=NULL, $bKey=self::FORMS_LABEL_USE_ITEM_KEY, $iStart=0, $cAddsT=NULL, $cAddsB=NULL)
	{		
		/*
		forms_list_numeric
		Damon V. Caskey
		2012-12-19		
		
		Generate a simple numeric list with maximum value based number of rows returned by query.
		
		$iType: 	
		$cQuery:	SQL query string.
		$cParams:	Parameter array.
		$cDefault:	Default selection.
		$cCurrent:	Current selection.
		$bKey:		Use key as item label?
		$iStart:	First numeric value of generated list.
		$cAddsT:	Additional items to place at top of generated list.
		$cAddsB:	Additional items to place at bottom of generated list.
		*/
		
		$i		= NULL;	//Counter.
		$cList	= NULL;	//Item list array.					
		
		$cList	= array();
				
		$this->oDB->db_basic_select($cQuery, $cParams);	
								
		for($i = $iStart; $i <= $this->oDB->iDBRowCount; $i++)
		{		
			$cList[$i] = $i;
		}				
		
		/* Merge Additions. (As of 5.4 array_merge() will always reorder indexes). */
		if($cAddsT && is_array($cAddsT))
		{	
			$cList = $cAddsT + $cList;	
		}
		
		if($cAddsB && is_array($cAddsB))
		{	
			$cList += $cAddsB;	
		}
		
		switch ($iType)
		{
			case self::FORMS_TYPE_RADIO:
				//$this->cDLListCode = $this->forms_select_options($cList, $cDefault, $cCurrent, $bKey);
			default:
				$this->cDLListCode = $this->forms_select_options($cList, $cDefault, $cCurrent, $bKey);
		}
		
		return $this->cDLListCode;
	}
		                 
	public function forms_phone($cName=NULL, $cID=self::FORMS_ID_USE_NAME, $cLabel=self::FORMS_LABEL_NONE, $cDefault=array("cc" => "1", "ac" => "859", "lc" => NULL, "pn" => NULL), $cCurrent=self::FORMS_VALUE_CURRENT_NONE, $cClass=NULL, $cEvents=NULL)
	{	
		/*
		forms_phone
		Damon Vaughn Caskey
		2013-01-21
		
		Output form phone input markup.
		*/
	
		$cMarkup 	=	NULL;	//Final markup to echo.
		$cEvent		=	NULL;	//Event string.	
		
		$cMarkup .='<div class="'.$cClass['Container_All'].'">';
				
		/* Set ID to name? */
		$cID = $cID != self::FORMS_ID_USE_NAME ? $cID : $cName;
		
		/* If current value empty or NULL, set "No current" cosntant */
		$cCurrent = $cCurrent ? $cCurrent : self::FORMS_VALUE_CURRENT_NONE;
		
		$cDefault = $cDefault == self::FORMS_VALUE_DEFAULT_NONE ? NULL : $cDefault; 
		
		/* If no current value is available, use default. */
		if(!$cCurrent || $cCurrent == self::FORMS_VALUE_CURRENT_NONE)
		{
			$cCurrent = $cDefault;
		}
				
		/* Parse event actions. */
		$cEvent = $this->forms_events_markup($cEvents);			
		
		/* Prepare label markup. */
		$cMarkup .= $this->forms_label_markup($cLabel, self::FORMS_LABEL_TYPE_FIELDSET, $cName, $cID, $cKey, $cValue);		
		
		$cMarkup .= '<div class="'.$cClass['Container_Item'].'"><input type="text" name="'.$cName.'_cc" id="'.$cID.'_cc" class="'.$cClass.'" value="'.$cDefault['cc'].'" size="1" maxlength="1" />
						  (
							<input type="text" name="'.$cName.'_ac" id="'.$cID.'_ac" class="'.$cClass.'" value="'.$cDefault['ac'].'" size="3" maxlength="3" />
							) 
							<input type="text" name="'.$cName.'_lc" id="'.$cID.'_lc" class="'.$cClass.'" value="'.$cDefault['lc'].'" size="3" maxlength="3" /> 
						  - 
						  <input type="text" name="'.$cName.'_pn" id="'.$cID.'_pn" class="'.$cClass.'" value="'.$cDefault['pn'].'" size="4" maxlength="4" />				  
					</div></div>';		
		/*
		Return end result.
		*/
		return $cMarkup;
	}	
					
	public function forms_radio($cName=NULL, $cID=self::FORMS_ID_USE_NAME, $iLabelStyle=self::FORMS_LABEL_USE_ITEM_KEY, $cList=array(NULL), $cDefault=NULL, $cCurrent=self::FORMS_VALUE_CURRENT_NONE, $cClass=array("Item" => "Standard"), $cEvents=NULL)
	{
		/*
		forms_radio
		Damon Vaughn Caskey
		2013-03-24	
		*/
								
		$cKey		=	NULL;	//Array key.
		$cValue		=	NULL;	//Array value.
		$cChecked	= 	NULL;	//Checked (default/current)?
		$cMarkup 	=	NULL;	//Final markup to echo.
		$cLabel		=	NULL;	//Item label markup.
		$cIDFinal	= 	NULL;	//Final ID inserted into markup.
		$cEvent		=	NULL;	
		
		$cMarkup .='<div class="'.$cClass['Container_All'].'">';
		
		/* Set ID to name? */
		$cID = $cID != self::FORMS_ID_USE_NAME ? $cID : $cName;
		
		/* If current value empty or NULL, set "No current" cosntant */
		$cCurrent = $cCurrent ? $cCurrent : self::FORMS_VALUE_CURRENT_NONE;
		
		/* Parse event actions. */
		$cEvent = $this->forms_events_markup($cEvents);		
		
		foreach ($cList as $cKey => $cValue)
		{				
			/* If $cValue matches $cCurrent, or $cCurrent not provided but $cValue matches $cDefault, add 'checked' to make this the default list item selected. */	
			if ($cCurrent == $cValue || ($cCurrent == self::FORMS_VALUE_CURRENT_NONE && $cValue == $cDefault))
			{
				$cChecked = 'checked="checked"';
			}
			else
			{
				$cChecked = NULL;
			}		
					
			/* IDs must be unique, so we'll combine ID with value. */
			$cIDFinal = $cID."_".$cValue;			
			
			/* Prepare label markup. */
			$cMarkup .= $this->forms_label_markup($iLabelStyle, self::FORMS_LABEL_TYPE_FIELDSET, $cName, $cIDFinal, $cKey, $cValue);			
			
			$cMarkup .= '<div class="'.$cClass['Container_Item'].'"><input type="radio" name="'.$cName.'" id="'.$cIDFinal.'" value="'.$cValue.'" '.$cChecked.' '.$cEvent.' /></div>';			
		}					
		
		$cMarkup .= '</div>';
		
		/*
		Return end result.
		*/
		return $cMarkup;
	
	}
	
	public function forms_select($cName=NULL, $cID=self::FORMS_ID_USE_NAME, $iLabelStyle=self::FORMS_LABEL_USE_ITEM_KEY, $iKeyStyle=self::FORMS_LABEL_USE_ITEM_KEY, $cList=array(NULL => NULL), $cDefault=NULL, $cCurrent=self::FORMS_VALUE_CURRENT_NONE, $cClass=array("Item" => "Standard", "Container_All" => "Show Me"), $cEvents=NULL)
	{
		/*
		forms_radio
		Damon Vaughn Caskey
		2013-03-24	
		*/
								
		$cKey		=	NULL;	//Array key.
		$cValue		=	NULL;	//Array value.
		$cChecked	= 	NULL;	//Checked (default/current)?
		$cMarkup 	=	NULL;	//Final markup to echo.
		$cLabel		=	NULL;	//Item label markup.
		$cEvent		=	NULL;	
		$cItems		=	NULL;	//Select options.
		
		$cMarkup .='<div class="'.$cClass['Container_All'].'">';
		
		/* Set ID to name? */
		$cID = $cID != self::FORMS_ID_USE_NAME ? $cID : $cName;		
		
		/* Parse event actions. */
		$cEvent = $this->forms_events_markup($cEvents);		
		
		if (!is_array($cList))
		{
			$cList = array(NULL => NULL);
		}
		
		foreach ($cList as $cKey => $cValue)
		{				
			/* If $cValue matches $cCurrent, or $cCurrent not provided but $cValue matches $cDefault, add 'checked' to make this the default list item selected. */	
			if ($cCurrent == $cValue || ($cCurrent == self::FORMS_VALUE_CURRENT_NONE && $cValue == $cDefault))
			{
				$cChecked = 'selected';
			}
			else
			{
				$cChecked = NULL;
			}		
					
			/* Prepare label markup. */
			$cLabel = $this->forms_label_markup(self::FORMS_LABEL_USE_ITEM_KEY, self::FORMS_LABEL_TYPE_INLINE, $cName, $cID, $cKey, $cValue);	
			
			$cItems.='<option value="'.$cValue.'" '.$cChecked.'>' . $cLabel . '</option>';			
		}				
		
		
		/* Prepare label markup. */
		$cLabel = $this->forms_label_markup($iLabelStyle, self::FORMS_LABEL_TYPE_FIELDSET, $cName, $cID, $cKey, $cValue);	

		$cMarkup .= $cLabel.'<div class="'.$cClass['Container_Item'].'"><select type="select" name="'.$cName.'" id="'.$cID.'" class="'.$cClass['Item'].'" '.$cEvent.'>'.$cItems.'</select></div></div>';
		
		/*
		Return end result.
		*/
		return $cMarkup;
	
	}
					
	public function forms_text($cName=NULL, $cID=self::FORMS_ID_USE_NAME, $cLabel=self::FORMS_LABEL_NONE, $cDefault=self::FORMS_VALUE_DEFAULT_NONE, $cCurrent=self::FORMS_VALUE_CURRENT_NONE, $cClass=array("Item" => "Standard"), $cEvents=NULL)
	{	
		/*
		forms_text
		Damon Vaughn Caskey
		2013-01-21
		
		Output form text input markup.				
		*/
	
		$cMarkup 	=	NULL;	//Final markup to echo.
		$cEvent		=	NULL;	//Event string.	
		
		$cMarkup .='<div class="'.$cClass['Container_All'].'">';
				
		/* Set ID to name? */
		$cID = $cID != self::FORMS_ID_USE_NAME ? $cID : $cName;
		
		/* If current value empty or NULL, set "No current" cosntant */
		$cCurrent = $cCurrent ? $cCurrent : self::FORMS_VALUE_CURRENT_NONE;
		
		$cDefault = $cDefault == self::FORMS_VALUE_DEFAULT_NONE ? NULL : $cDefault; 
		
		/* If no current value is available, use default. */
		if(!$cCurrent || $cCurrent == self::FORMS_VALUE_CURRENT_NONE)
		{
			$cCurrent = $cDefault;
		}
				
		/* Parse event actions. */
		$cEvent = $this->forms_events_markup($cEvents);			
		
		/* Prepare label markup. */
		$cMarkup .= $this->forms_label_markup($cLabel, self::FORMS_LABEL_TYPE_FIELDSET, $cName, $cID, $cKey, $cValue);
					
		$cMarkup .= '<div class="'.$cClass['Container_Item'].'"><input type="text" name="'.$cName.'" id="'.$cID.'" class="'.$cClass['Item'].'" value="'.$cCurrent.'" '.$cEvent.' /></div></div>';
		
		/*
		Return end result.
		*/
		return $cMarkup;
	}
	
	public function forms_time($cName=NULL, $cID=self::FORMS_ID_USE_NAME, $cLabel=self::FORMS_LABEL_NONE, $cDefault=NULL, $cCurrent=NULL, $cOptions="{dateFormat: 'yy-mm-dd', timeFormat: 'HH:mm:ss', controlType: 'select', changeYear: true, constrainInput: true}", $cFunction="datetimepicker", $bReadOnly=self::FORMS_READ_ONLY_ON, $cClass=NULL, $cEvents=NULL)
	{	
		/*
		forms_time
		Damon Vaughn Caskey
		2013-01-21
		
		Output form date/time input markup.
		*/
	
		$cMarkup 	=	NULL;	//Final markup to echo.
		$cEvent		=	NULL;	//Event string.	
				
		$cMarkup .='<div class="'.$cClass['Container_All'].'">';
				
		/* Set ID to name? */
		$cID = $cID != self::FORMS_ID_USE_NAME ? $cID : $cName;
		
		/* If current value empty or NULL, set "No current" cosntant */
		$cCurrent = $cCurrent ? $cCurrent : self::FORMS_VALUE_CURRENT_NONE;
		
		$cDefault = $cDefault == self::FORMS_VALUE_DEFAULT_NONE ? NULL : $cDefault; 
		
		/* If no current value is available, use default. */
		if(!$cCurrent || $cCurrent == self::FORMS_VALUE_CURRENT_NONE)
		{
			$cCurrent = $cDefault;
		}
		 						
		/* Parse event actions. */
		$cEvent = $this->forms_events_markup($cEvents);			
		
		/* Prepare label markup. */
		$cMarkup .= $this->forms_label_markup($cLabel, self::FORMS_LABEL_TYPE_FIELDSET, $cName, $cID, $cKey, $cValue);		
		
		$cMarkup .= "<script>$(function(){
						$( '#".$cID."' ).".$cFunction."(".$cOptions.");
					});</script>";

		$cMarkup .= "<input type='text' name='".$cName."' id='".$cID."' value='".$cCurrent."'";
		
		if ($bReadOnly==TRUE)
		{
			$cMarkup .= " readonly='readonly'";
		}
		
		$cMarkup .= " /></div>";
		
		/*	Return end result. */
		return $cMarkup;
	}
	
	public function forms_fieldset($cID=NULL, $cLegend=self::FORMS_LEGEND_NONE, $cElements=array(NULL), $cAddOns=array(NULL), $cClass=array("Div"=>"table_row_0", "Fieldset"=>"Standard"))
	{
		/*
		forms_fieldset
		Damon Vaughn Caskey
		2013-03-25
		
		Arrage form input items into a fieldset.
		
		$cID: 		Fieldset ID.
		$cLegend: 	Legend label, if any.
		$cElements:	Markup of field list items (select lists, radio groups, text boxes, etc).
		$cClass:	Style classes.
		*/
		
		$cElementKey	= NULL;	//Single element key.
		$cElementVal	= NULL;	//Single element value.
		
		$cMarkup = '<div class="'.$cClass['Div'].'"><fieldset id="'.$cID.'" class="'.$cClass['Fieldset'].'">';
		
		
		if($cLegend != self::FORMS_LEGEND_NONE)
		{
			$cMarkup .= '<legend>'.$cLegend.'</legend>';	 
		}				
		
		foreach($cAddOns as $cElementKey => $cElementVal)
		{
			$cMarkup .= '<div class="'.$cElementKey.'">'.$cElementVal.'</div>';
		}	
		
		foreach($cElements as $cElementKey => $cElementVal)
		{
			$cMarkup .= '<div class="'.$cElementKey.'">'.$cElementVal.'</div>';
		}		
		
		$cMarkup .= '</fieldset></div>';
		
		$this->cFieldset[$cID] = $cMarkup;
		
		return $this->cFieldset[$cID];
	}
		
}
?>

Class – Config

<?php	
	
	/*
	Configuration file. This should be added to all PHP scripts to set up commonly used includes, functions, objects, variables and so on.
	*/
	
	$iReqTime 	= $_SERVER['REQUEST_TIME_FLOAT'];
	$cDocroot 	= NULL; //Document root.	
	$oDB		= NULL;	//Database class object.
	$oErr		= NULL;	//Error class object.
	$oMail		= NULL;	//E-Mail handler class object.
	$oSes		= NULL;	//Session handler class object.
	$oUtl		= NULL;	//Utility class object.
	$oFrm		= NULL;	//Forms class object.
		
	/* Get needed includes. */
	require_once("access.php");		//Account based access.
	require_once("constants.php");	//Global constants.
	require_once("database.php");	//Database handler.
	require_once("forms.php");		//Forms handler.
	require_once("error.php");		//Error handler.
	require_once("mail.php");		//Mail handler.
	require_once("sessions.php");	//Session handler.
	require_once("tables.php");		//Table handler.
	require_once("utility.php");	//Utility functions.	
		
	/* Initialize class objects */
	$oUtl	= new class_utility();														//Utility functions.
	$oMail	= new class_mail();															//E-Mail handler.
	$oErr 	= new class_error(array("Mail" => $oMail, "Utl" => $oUtl));					//Error handler.
	$oDB	= new class_db(array("Utl" => $oUtl, "Err" => $oErr));						//Database handler.
	$oSes	= new class_sessions(array("DB" => $oDB));									//Session handler.	
	$oAcc	= new class_access(array("DB" => $oDB, "Ses" => $oSes, "Utl" => $oUtl));	//Account based access.
	$oTbl 	= new class_tables(array("Utl"=>$oUtl));									//Tables handler.
	$oFrm 	= new class_forms(array("DB"=>$oDB));										//Forms handler.
		
	$cDocroot = $oUtl->utl_get_server_value('DOCUMENT_ROOT')."/";
	
	/* Replace default session handler. */
	session_set_save_handler($oSes, true);
	
	/* If session not started, initialize. */
	if(session_status()==PHP_SESSION_NONE)
	{					
		session_start();
	}
?>

PHP – E-mail Class

E-mailing is one of the more common and versatile ways for webmasters and their customers to keep abreast of events. Naturally databases and logs are best practice, but as a quick and dirty way to get form submissions, error alerts and so on E-mail is tough to beat. Even with an established database E-mail still has its place as an alert mechanism.

Given the above and the ubiquitous presence of E-mail in general, it only makes sense to simplify the process of sending as much as possible. To that end, this PHP class wraps the PHP Mail function to extend capability in such a way that oft repeated coding and formatting is eliminated. Specific goals are:

  • Eliminate the need to include the webmaster in any “To list”; the webmaster is always sent a Bcc by default.
  • Eliminate need to add headers for HTML layout with an option to remove if needed.
  • Enable the simple organization of those oh so common form submission or list E-mails to be formatted into a table layout without any markup in the initial mail string.
  • Provide defaults so the bare minimum of parameters may be used when calling.

Elements

Requirements

  • PHP 5.3+

Files

Copy the following into your class file.

class class_mail
{    

	/*
	class_mail - https://www.caskeys.com/dc/?p=5031
	Damon Vaughn Caskey
	2012_12_10
	
	Mail handler. 
	*/	
		
	const	c_bWMAlert	= TRUE;										//Send webmaster a blind copy?
	const	c_cEDefMsg	= "...";									//Default message.
	const	c_cEHead	= "MIME-Version: 1.0 \r\nContent-type: text/html; charset=iso-8859-1\r\n";	//Default E-mail headers.
	const	c_cESubject	= "From EHS Web";							//Default outgoing E-mail subject.
	const	c_cEWMIn	= "dvcask2@uky.edu";						//Default webmaster's incoming E-mail address.
	const	c_cEWMOut	= "ehs_noreply@uky.edu";					//Default address when server sends mail.
				
	public function mail_send($cMsg=self::c_cEDefMsg, $cSubject=self::c_cESubject, $cTo=self::c_cEWMIn, $cFrom=self::c_cEWMOut, $cBcc=NULL, $bWMAlert=self::c_bWMAlert, $cHeader=self::c_cEHead, $cParams=NULL)
	{	
		/*
		mail_send
		Damon Vaughn Caskey
		2012_12_28
		
		Send HTML mail with standard defaults.
		
		$cMsg:		Body of E-mail.
		$cSubject:	Subject line.
		$cTo:		Outgoing address list.
		$cFrom:		Return address.
		$cBcc:		Blind carbon copy address list.
		$bWMAlert:	Send Bcc to webmaster.
		$cHeader:	Header information.
		$cParams:	Optional parameters.
		*/
	
		$cBody = NULL;	//Final sting for message body.
	
		/*
		Insert From address to header.
		*/
		$cHeader .= "From: ".$cFrom. "\r\n";		
		
		/* 
		If Webmaster alert is on, insert address into Bcc and add to header. Otherwise just add Bcc to header as is.
		*/
		if($bWMAlert===TRUE)
		{			
			$cHeader .= "Bcc: ".self::c_cEWMIn. ", ".$cBcc."\r\n";	
		}
		else
		{
			$cHeader .= "Bcc: ".$cBcc."\r\n";
		}
		
		$cHeader .="\r\n";
		
		/*
		If message passed as a key array, break into list and output as table layout.
		*/		
		if (is_array($cMsg))
		{
			/*
			Initial html and table markup.
			*/
			$cBody = "<html>
						<head>
						  <title>".$cSubject."</title>
						</head>
						<body>
						  <h1>".$cSubject."</h1>
						  <table cellpadding='3'>";
			
			/*
			Get each item in array and place into two column table row.
			*/
			foreach($cMsg as $key => $value)
			{			
				$cBody .= "<tr><th>".$key.":</th><td>".$value."</td></tr>";			
			}	
			
			/*
			Add closing markup.
			*/
			$cBody .= "</table>
					</body>
					</html>";	
		}
		else
		{
			/*
			Output message as is.
			*/
			$cBody = $cMsg;
		}
			
		/*
		Run mail function.
		*/
		return mail($cTo, $cSubject, $cBody, $cHeader, $cParams);		
	}	
}

Now include the file in your script and declare a class object. You are ready to go. Personally I prefer to place common class declarations in a single config file and use injected dependency to avoid multiple objects.

require_once('mail.php');	//Path to mail class.

$oMail = NULL;				//Initialize class object.

$oMail = new class_mail();	//Class object.

Use

Constants

Adjust the constant settings to suit your needs.

const	c_bWMAlert	= TRUE;										//Send webmaster a blind copy?
const	c_cEDefMsg	= "...";									//Default message.
const	c_cEHead	= "MIME-Version: 1.0 \r\nContent-type: text/html; charset=iso-8859-1\r\n";	//Default E-mail headers.
const	c_cESubject	= "From EHS Web";							//Default outgoing E-mail subject.
const	c_cEWMIn	= "dvcask2@uky.edu";						//Default webmaster's incoming E-mail address.
const	c_cEWMOut	= "ehs_noreply@uky.edu";					//Default address when server sends mail.
  • c_bWMAlert: Default option to always send Webmaster a Bcc of the outgoing mail.
  • c_cEDefMsg: Default message text.
  • c_cEHead: Default headers. Included in the class are the headers required to send E-mail with HTML formatting (tables, title, etc.).
  • c_cESubject: Default E-mail subject.
  • c_cEWMIn: Webmaster’s incoming address. By default a Bcc of every mail will be sent to this address.
  • c_cEWMOut: Default “from” address.

Functions

mail_send

Primary (and as of this writing the only) function. Sends an E-mail; includes an optional tabled layout.

public function mail_send($cMsg=self::c_cEDefMsg, $cSubject=self::c_cESubject, $cTo=self::c_cEWMIn, $cFrom=self::c_cEWMOut, $cBcc=NULL, $bWMAlert=self::c_bWMAlert, $cHeader=self::c_cEHead, $cParams=NULL)
	{	
		/*
		mail_send
		Damon Vaughn Caskey
		2012_12_28
		
		Send HTML mail with standard defaults.
		
		$cMsg:		Body of E-mail.
		$cSubject:	Subject line.
		$cTo:		Outgoing address list.
		$cFrom:		Return address.
		$cBcc:		Blind carbon copy address list.
		$bWMAlert:	Send Bcc to webmaster.
		$cHeader:	Header information.
		$cParams:	Optional parameters.
		*/
	
		$cBody = NULL;	//Final sting for message body.
	
		/*
		Insert From address to header.
		*/
		$cHeader .= "From: ".$cFrom. "\r\n";		
		
		/* 
		If Webmaster alert is on, insert address into Bcc and add to header. Otherwise just add Bcc to header as is.
		*/
		if($bWMAlert===TRUE)
		{			
			$cHeader .= "Bcc: ".self::c_cEWMIn. ", ".$cBcc."\r\n";	
		}
		else
		{
			$cHeader .= "Bcc: ".$cBcc."\r\n";
		}
		
		$cHeader .="\r\n";
		
		/*
		If message passed as a key array, break into list and output as table layout.
		*/		
		if (is_array($cMsg))
		{
			/*
			Initial html and table markup.
			*/
			$cBody = "<html>
						<head>
						  <title>".$cSubject."</title>
						</head>
						<body>
						  <h1>".$cSubject."</h1>
						  <table cellpadding='3'>";
			
			/*
			Get each item in array and place into two column table row.
			*/
			foreach($cMsg as $key => $value)
			{			
				$cBody .= "<tr><th>".$key.":</th><td>".$value."</td></tr>";			
			}	
			
			/*
			Add closing markup.
			*/
			$cBody .= "</table>
					</body>
					</html>";	
		}
		else
		{
			/*
			Output message as is.
			*/
			$cBody = $cMsg;
		}
			
		/*
		Run mail function.
		*/
		return mail($cTo, $cSubject, $cBody, $cHeader, $cParams);		
	}

Parameters

Unless noted, parameters are not required. Class defaults will be used for any missing.

  • $cMsg: The message to send (i.e. the body text). If passed as a keyed array, the listed items will be laid out as a table. See below for details.
  • $cSubject: Mail subject.
  • $cTo: Recipient address(s).
  • $cFrom: Sender address.
  • $cBcc: Blind copy address(s).
  • $bWMAlert: Send a blind copy to webmaster.
  • $cHeader: Header options.
  • $cParams: Optional parameters. See PHP Mail for details.

Examples

Send regular E-mail

This call will send a common E-mail to the designated recipients with the automatic webmaster Bcc turned off:

$cMsg         = "Hello world!";
$cSubject    = "Hi";
$cTo        = "someone@somewhere.com, someoneelse@somewhereelse.com";
$cFrom        = "me@myplace.com";

$oMail->mail_send($cMsg, $cSubject, $cTo, $cFrom, NULL, FALSE);
Send table E-mail

A common task for E-mail is to document errors, alerts, form submissions and other types of keyed data that is far easier to read in table format. It’s certainly possible to do this by manually coding table markup into the message, but also repetitive and time consuming.

Instead, you can pass the message as a keyed array; a simple but elegant table from your data will be created and sent to the recipients. The E-mail’s subject will be added as an attention grabbing header just above the table.

$cMsg 	= NULL;

		$cMsg = array(
			"Time"				=>	$this->cErrTOE,
			"Type"				=>	$this->cErrType,
			"IP"				=>	$this->cIP,
			"Def. Source File"	=>	$this->cSource,
			"Source File"		=>	$this->cErrFile,
			"Line"				=>	$this->cErrLine,
			"State"				=>	$this->cErrState,
			"Code"				=>	$this->cErrCode,
			"Message"			=>	$this->cErrMsg,
			"Details"			=>	$this->cErrDetail
		);

$this->oMail->mail_send($cMsg, "Error Report");
Example output:

In this example, an error alert is sent using table layout.

In this example, an error alert is sent using table layout.

PHP – Global Constants

This set of global constants is a dependency for nearly all other functions and classes I have created with PHP. For obvious security reasons some of the actual values may be removed.

class constants
{    

	/*
	constants
	Damon Vaughn Caskey
	2012_12_18
	~2012_12_30: Local constants moved back to individual class files.
	
	Global constants. 
	*/		
	
	/* Basic values */
	const	c_cAdmin		= "";										//Default "all access" user accounts.
	const	c_cDateF		= "Y-m-d H:i:s";							//Default date format.
	const	c_cWMName		= "Damon V. Caskey";						//Default webmaster's name.
	const	c_cWMEmailIn	= "dvcask2@uky.edu";						//Default webmaster's incoming email address.
	const	c_cVFGuid		= "00000000-0000-0000-0000-000000000000";	//False guid.
					
	/* Media & Icons */
	const	c_cIconExcel	= "<img src='/media/image/icon_excel.png' alt='MS Excel document.' title='MS Excel document.' class='iconS'/>";					//MS Excel icon.
	const	c_cIconPDF		= "<img src='/media/image/icon_pdf.png' alt='PDF document.' title='PDF document.' class='iconS' />";							//PDF icon.
	const	c_cIconPPT		= "<img src='/media/image/icon_powerpoint.png' alt='MS Powerpoint document.' title='MS Powerpoint document.' class='iconS' />";	//MS Powerpoint icon.
	const	c_cIconWord		= "<img src='/media/image/icon_word.png' alt='MS Word document.' title='MS Word document.' class='iconS' />";					//MS Word icon.
	const	c_cPathImage	= "/media/image/";							//Default path for image files.	
}

PHP – PIM (PHP IIS MSSQL) Database Class

PHP

Please Note: This class has long been replaced with a truly object oriented solution that utilizes the sqlsrv driver. I am only leaving it here for posterity (and to embarrass myself). Use at your own peril 🙂

 

This PHP class file is designed to take care of day to day database operations for the rare but not unheard of combination of PHP with IIS and MSSQL.

I’m not a fan of reinventing the wheel, but after extensive searching had been unable to find any existing classes that suited the needs of a WIMP combo. Not much of a surprise considering the majority of PHP use falls under the LAMP model, but it is certainly frustrating.

I hope anyone in a similar situation can find this of use. The goals are simple and straight forward:

  • Reduce repetitive code and increase portability. This is an obvious goal – reusable, portable code not only saves time, but cuts down on human error (typos, misplaced values, etc.).
  • Move to an object oriented model. While a function based model can accomplish many of the listed goals here, and an object oriented coding model carries many other advantages, the discussion of which is beyond the scope of this article.
  • Quick implementation. To reduce clutter in pages and functions utilizing the class, class functions should accomplish common tasks with few calls as possible. To accomplish this goal, functions are created as basic building blocks with a cascading operation order in mind. This allows creation of master functions which can in turn run all of the basic building blocks for a common task with a single call.
  • Block SQL injection. This is achieved by designing for and stressing the use of parameters. Used properly this will effectively stops SQL injection attempts cold.
  • Robust error trapping. In the event of an error, as much information as possible should be collected and sent to webmaster. Additionally, an attempt should be made to log collected information into another remote database for easy dissemination.

Elements

Dependencies

Global Constants

The following values are used from global constants list.

class constants
{
	/*
	constants
	Damon Vaughn Caskey
	2012_12_18

	Global constants.
	*/

	/* Basic values */
	const	c_cAdmin		= &quot;&quot;;										//Default &quot;all access&quot; user accounts.
	const	c_cDateF		= &quot;Y-m-d H:i:s&quot;;							//Default date format.
	const	c_cWMName		= &quot;Damon V. Caskey&quot;;						//Default webmaster name.
	const	c_cWMEmailIn	= &quot;&quot;;										//Default webmaster email address (sending mail to webmaster)
	const	c_cWMEmailOut	= &quot;&quot;;										//Default address when server sends mail.
	const	c_cVFGuid		= &quot;00000000-0000-0000-0000-000000000000&quot;;	//False guid.

	/* Database */
	const 	c_cDBHost		= &quot;&quot;;										//Default DB Host.
	const 	c_cDBLName		= &quot;&quot;;										//Default DB logical name.
	const 	c_cDBUser		= &quot;&quot;;										//Default DB user.
	const 	c_cDBPword		= &quot;&quot;;										//Default DB password.
	const 	c_cDBEHost		= &quot;&quot;;										//Error log DB host.
	const 	c_cDBELName		= &quot;&quot;;										//Error log DB logical name.
	const 	c_cDBEUser		= &quot;&quot;;										//Error log DB user.
	const 	c_cEDBPword		= &quot;&quot;;										//Error log DB password.
	const 	c_cDBEEmailSub	= &quot;Database Failure&quot;;						//Error email subject line.
	const 	c_iDBFetchType	= SQLSRV_FETCH_BOTH;						//Default row array fetch type.

	/* Media &amp; Icons */
	const	c_cIconExcel	= &quot;&quot;;										//MS Excel icon.
	const	c_cIconPDF		= &quot;&quot;;										//PDF icon.
	const	c_cIconPPT		= &quot;&quot;;										//MS Powerpoint icon.
	const	c_cIconWord		= &quot;&quot;;										//MS Word icon.
	const	c_cPathImage	= &quot;&quot;;										//Default path for image files.
}
  • c_cDateF: This is the default format for dates. If you reconfigure this value, make sure it is one that MSSQL date fields will accept; they can get fairly picky.
  • c_cWMEmailIn: This is the webmaster email address. It is where any alerts will be sent, so make sure to include working addresses that won’t flag an alert as spam.
  • c_cWMEmailOut: Address used in the “from” tag of mails sent to webmaster.
  • c_cDBHost: The connection string or IP of your primary database host (or just the one you use most often).
  • c_cDBLName: Your most often used logical database name on your primary database host.
  • c_cDBUser: The user account name to access your primary database host.
  • c_cDBPword: The user account password to access your primary database host.
  • c_cDBEHost: The connection string or IP of on alternate database host to record errors.
  • c_cDBELName: Logical database name where error log table is located on error database host.
  • c_cDBEUser: The user account name to access your error database host.
  • c_cEDBPword: The user account password to access your error database host.
  • c_cDBEEmailSub: The  e-mail subject line of alert mails sent when an error occurs.
  • c_iDBFetchType: The row key fetch type. This determines how the line row array can be accessed. See here for more information.

Next, you will need to initialize the database class in whatever script you would like to use it in. This is fairly simple:

Variables

			
	public	$cDBLine		= NULL;									//Line array.
	public 	$cDBFMeta		= NULL;									//Line metadata array.
	public 	$iDBFCount		= NULL;									//Line field count.
	public	$iDBRowCount	= NULL;									//Table row count.
	public	$iDBRowsAffect	= NULL;									//Number of rows affected by an action query.
	public 	$rDBConn 		= NULL;									//Database connection ID resource.
	public 	$rDBResult		= NULL;									//Database query result ID resource.
	public 	$rDBStatement	= NULL;									//Database prepared query statement ID resource.		

The following public variables are made available upon initialization. These are set by the various function calls and will be the main means of actually extracting values returned from your database.

  • cDBLine: An array of the fields (columns) found in a given table row.
  • cDBFMeta: An array of metadata. This contains information on the table fields themselves (size, name, type, etc.).
  • iDBFCount: Simple count of the number of fields returned from a query.
  • iDBRowCount: How many rows were returned by a select query.
  • iDBRowsAffect: How many rows were affected by an action query.
  • rDBConn: ID of the connection to a database host.
  • rDBResult: ID of the data returned from a query.
  • rDBStatement: ID of a prepared query (SQL string and parameters).

Functions

db_basic_action

function db_basic_action($cQuery, $cParams, $cHost = constants::c_cDBHost, $cDB = constants::c_cDBLName, $cUser = constants::c_cDBUser, $cPword = constants::c_cDBPword)
	{
		/*
		db_basic_action
		Damon Vaughn Caskey
		2012_11_13
		
		Connect and execute an action query with single call.
		
		$cQuery: 	SQL string.
		$cParams:	Parameter array.
		$cHost:		DB Host.
		$cDB:		DB logical name.
		$cUser:		DB user.	
		$cPword:	DB password.
		*/
				
		/* Connect to DB */
		$this-&gt;db_connect($cHost, $cDB, $cUser, $cPword);
		
		/* Execute query. */
		$this-&gt;db_query($cQuery, $cParams);		
		
		/* Set rows affected. */
		$this-&gt;iDBRowsAffect = sqlsrv_rows_affected($this-&gt;rDBResult);		
		
		return $this-&gt;iDBRowsAffect;
	}

This function is designed to run simple action queries with a single call. There is no need to run a separate call to connect, prepare, and query execute. Simply pass the query, parameters and (if necessary) connection values.

Sets and returns iDBRowsAffect.

db_basic_select

function db_basic_select($cQuery, $cParams, $iLine=FALSE, $cHost = constants::c_cDBHost, $cDB = constants::c_cDBLName, $cUser = constants::c_cDBUser, $cPword = constants::c_cDBPword)
	{
		/*
		db_basic_select
		Damon Vaughn Caskey
		2012_11_13
		
		Connect, query and populate common variables from database with a single call.
		
		$cQuery: 	SQL string.
		$cParams:	Parameter array.
		$iLine:		Populate line array with first row?
		$cHost:		DB Host.
		$cDB:		DB logical name.
		$cUser:		DB user.	
		$cPword:	DB password.
		*/
				
		/* Connect to DB */
		$this-&gt;db_connect($cHost, $cDB, $cUser, $cPword);
		
		/* Execute query. */
		$this-&gt;db_query($cQuery, $cParams);
		
		/* Get row count */
		$this-&gt;db_count();
		
		/* Get field count. */
		$this-&gt;db_field_count();
		
		/* Get metadata. */
		$this-&gt;db_field_metadata();	
		
		if($iLine===TRUE)
		{
			$this-&gt;db_line();			
		}
		
		return $this-&gt;cDBLine;
	}

This function is designed to run simple select queries with a single call. There is no need to run a separate call to connect, prepare, and query execute. Simply pass the query, parameters and (if necessary) connection values.

Note the iLine parameter. If this is passed as TRUE, cDBLine will be populated with the first row from your query result. This option is very useful for times when you expect and only need a single row result; one call to this function is all you need. However, you will want to be careful with multiple rows as the row cursor will now be at the next record. This means a subsequent call to db_line() will start at the second record, NOT the first.

Returns cDBLine.

db_close

function db_close()
	{
		/*
		db_close
		Damon Vaughn Caskey
		2012_11_13
		
		Close current conneciton. Normally not needed.
		*/
		
		/* Close DB conneciton. */
		sqlsrv_close($this-&gt;rDBConn);
		
		/* Return TRUE. */
		return TRUE;
	}

Closes connection with rDBConn.

db_connect

function db_connect($cHost = constants::c_cDBHost, $cDB = constants::c_cDBLName, $cUser = constants::c_cDBUser, $cPword = constants::c_cDBPword)
	{		 
		/*
		db_connect
		Damon Vaughn Caskey
		2012_11_13
		
		Connect to database host and store reference to public variable.
		
		$cHost:		DB Host.
		$cDB:		DB logical name.
		$cUser:		DB user.	
		$cPword:	DB password.		
		*/
			
		$db_cred = NULL; //Credentials array.
	
		/* Only bother connecting to DB host if no previous connection is established. */
		if(!$this-&gt;rDBConn)
		{	
			/* Set up credential array. */
			$db_cred = array(&quot;Database&quot;=&gt;$cDB, &quot;UID&quot;=&gt;$cUser, &quot;PWD&quot;=&gt;$cPword);
									
			/* Establish database connection. */
			$this-&gt;rDBConn = sqlsrv_connect($cHost, $db_cred);		
		}
		
		/* False returned. Database connection has failed. */
		if($this-&gt;rDBConn === FALSE)
		{
			/* Stop script and log error. */
			$this-&gt;db_error(&quot;db_connect&quot;, NULL, NULL, TRUE);	
		}	
	}

Opens a connection to database host; defaults to primary host and database using connection pooling.

db_count

function db_count()
	{
		/*
		db_line
		Damon Vaughn Caskey
		2012_11_13
		
		Return number of records from query result.
		*/
		
		/* Get row count. */
		$this-&gt;iDBRowCount = sqlsrv_num_rows($this-&gt;rDBResult);	
		
		$this-&gt;db_error(&quot;db_count&quot;);	
		
		/* Return count. */
		return $this-&gt;iDBRowCount;
	}

Set and return iDBRowCount with record count from last query (rDBResult).

db_error

function db_error($cLocation = NULL, $cSql = NULL, $cParams = NULL, $bFatal = FALSE)
	{
		
		/*
		db_error
		Damon Vaughn Caskey
		2012_06_08
		
		Wrapper for sqlsrv_errors(). Record any errors into an alternate database and send email to webmaster.
		
		$cLocation:	Code location where error trap was called. Aids in dubugging.
		$cSql:		SQL string passed at time of error.
		$cParams:	Parameter array passed at time of error.
		$bFatal:	If error triggers, should process be terminated?
		*/
		
		$cMBody			= NULL;	//Mail message.
		$aErrors		= NULL;	//Errors list array.
		$aError			= NULL;	//Error output array.
		$mysqli			= NULL;	//Connection reference to DB error log.
		$query			= NULL; //Error query string.
		$stmt			= NULL;	//Prepared query reference.
		$val			= NULL;	//Array of error values.
		$cParam			= NULL;	//Individual item from parameter array.
		
		/* Get error collection */
		$aErrors = sqlsrv_errors();
		
		/* Any errors found? */
		if($aErrors)					
		{	
			/* Connect to error log database (obviously this should be an alternate DB from the one that failed). */	
			$mysqli = new mysqli(constants::c_cDBEHost, constants::c_cDBELName, constants::c_cDBEUser, constants::c_cEDBPword);
			
			/* Loop through error collection. */
			foreach($aErrors as $aError)
			{
				/*
				Ignore these codes; they are informational only:
					0:		Cursor type changed.
					5701:	Changed database context.
					5703:	Changed language setting.
				*/
				if($aError['code'] != 0 &amp;&amp; $aError['code'] != 5701 &amp;&amp; $aError['code'] != 5703)
				{
					
					$val[0] = $cLocation;				//Function &amp; Line location. Manually set in execution code to aid debugging; not part of error routine.
					$val[1] = $aError['SQLSTATE'];		//Status of DB host.
					$val[2] = $aError['code'];			//Error code.
					$val[3] = $aError['message'];		//Error message.
					$val[4] = $cSql;					//SQL string (if any) attempting to be executed.
					$val[5] = NULL;						//Parameter array.
					$val[6] = $_SERVER[&quot;PHP_SELF&quot;];		//Calling PHP file.
					$val[7] = $_SERVER['REMOTE_ADDR'];	//Client IP address.
					
					/* Dump parameter array into single string. */
					if(isset($cParams))								//Parameter array passed?
					{
						foreach($cParams as $cParam)				//Loop array collection.
						{
							$val[5] .= $cParam .&quot;, &quot;;				//Add to error parameter string.
						}
					}
					
					/* Build mail body string */
					$cMBody .= 
					&quot;\n Code Location: &quot;.	$val[0]. 
					&quot;\n SQLSTATE: &quot;.		$val[1].
					&quot;\n Code: &quot;.			$val[2].
					&quot;\n Message: &quot;.			$val[3].
					&quot;\n SQL String: &quot;.		$val[4].
					&quot;\n Parameters: &quot;.		$val[5].
					&quot;\n File: &quot;.			$val[6].
					&quot;\n Client IP: &quot;.		$val[7].	
					&quot;\n&quot;;
					
					/* If the error log database connection was successful, insert each error to table. */
					if (!$mysqli-&gt;connect_error) 
					{	
						/* Build query string. */		
						$query = &quot;INSERT INTO tbl_query_errors (codel, state, code, msg, query, params, source, ip) VALUES (?,?,?,?,?,?,?,?)&quot;;
						$stmt = $mysqli-&gt;prepare($query);
						
						/* Bind parameters. */
						$stmt-&gt;bind_param(&quot;ssssssss&quot;, $val[0], $val[1], $val[2], $val[3], $val[4], $val[5], $val[6], $val[7]);
						
						/* Execute and close query. */
						if($stmt != false)
						{							
							/* Execute and close query. */
							$stmt-&gt;execute();
							$stmt-&gt;close();
						}
					}
				}
			}
			
			/* Close DB connection. */
			$mysqli-&gt;close();
			
			/* Send EMail alert. */
			mail(constants::c_cWMEmail, constants::c_cDBEEmailSub, $cMBody, constants::c_cWMEmailOut);
			
			/* If error is fatal, stop PHP execution immediately. */
			if($bFatal)
			{
				die(&quot;A fatal database error has occurred. Please contact the webmaster immediately.&quot;);
			}
			
			return true;
		}
		
		return false;	
	}

Capture, log and return any errors encountered by the various other functions. If errors are found, they will be sent to webmaster via e-mail Additionally an attempt is made to log the errors to another database.

My method is to use the MySQL database in my personal web portal to log various activities from production sites I am responsible for using an INSERT only account. This ensures I am always aware of potential issues and have organized debugging info for problems that arise. For this reason, the error function is written primarily for MySQL. It would be easy enough to alter this for your own needs.

Note the bFatal parameter. If this is set to TRUE, the entire script (i.e. the page being generated) will fail and be replaced with an alert. This is less than user friendly, but in some cases may be a preferable result.

db_execute

function db_execute()
	{
		/*
		db_execute
		Damon Vaughn Caskey
		2012_11_13
		
		Execute prepared query.
		*/
	
		/* Execute statement. */
		$this-&gt;rDBResult = sqlsrv_execute($this-&gt;rDBStatement);
		
		/* Error trapping. */
		$this-&gt;db_error(&quot;db_execute&quot;);
		
		/* Set rows affected. */
		$this-&gt;iDBRowsAffect;
		
		/* Return ID resource. */
		return $this-&gt;rDBResult;		
	}

Executes rDBStatement, then populates and returns rDBResult with the results. Also sets iDBRowsAffect with the number of rows affected by execution. Useful for efficiently running a single action query where only the parameters (if anything) change.

db_field_count

function db_field_count()
	{
		/*
		db_field_count
		Damon Vaughn Caskey
		2012_11_13
		
		Get number of fields from query result.
		*/
		
		/* Get field count. */
		$this-&gt;iDBFCount = sqlsrv_num_fields($this-&gt;rDBResult);
		
		/* Error trapping. */
		$this-&gt;db_error(&quot;db_field_count&quot;);
		
		/* Return field count. */
		return $this-&gt;iDBFCount;
	}

Sets and returns iDBFCount with the number of fields returned by rDBResult.

db_field_metadata

function db_field_metadata()
	{
		/*
		db_field_metadata
		Damon Vaughn Caskey
		2012_11_13
		
		Fetch table rows metadata array (column names, types, etc.).
		*/
		
		/* Get metadata array. */
		$this-&gt;cDBMeta = sqlsrv_field_metadata($this-&gt;rDBResult);
		
		/* Error trapping. */
		$this-&gt;db_error(&quot;db_field_metadata&quot;);
		
		/* Return metadata array. */
		return $this-&gt;cDBMeta;
	}

Sets and returns cDBMeta with field meta data from rDBResult.

db_line

function db_line($iFetchType = constants::c_iDBFetchType)
	{
		/*
		db_line
		Damon Vaughn Caskey
		2012_11_13
		
		Fetch line array from table rows.
		
		$iFetchType: Row key fetch type
		*/
		
		/* Get line array. */
		$this-&gt;cDBLine = sqlsrv_fetch_array($this-&gt;rDBResult, $iFetchType);		
				
		/* Error trapping. */
		$this-&gt;db_error(&quot;db_line&quot;);
		
		/* Return line array. */
		return $this-&gt;cDBLine;
	}

Sets and returns cDBLine with row array data from rDBResult. This is the function you would place inside of a while loop to fetch the entire row list from a record set.

db_prepare

function db_prepare($cQuery, $cParams, $cOptions = array(&quot;Scrollable&quot; =&gt; SQLSRV_CURSOR_STATIC))
	{
		/*
		db_prepare
		Damon Vaughn Caskey
		2012_11_13
		
		Prepare query statement.
		
		$cQuery: 	Basic SQL statement to execute.
		$cParams:	Parameters to pass with query (prevents SQL injection).
		$cOptions:	Options for cursor array, etc.
		*/
	
		/* Execute query. */
		$this-&gt;rDBStatement = sqlsrv_prepare($this-&gt;rDBConn, $cQuery, $cParams, $cOptions);
		
		/* Error trapping. */
		$this-&gt;db_error(&quot;db_prepare&quot;, $cQuery, $cParams);
		
		/* Return query ID resource. */
		return $this-&gt;rDBStatement;		
	}

Prepare a query statement from SQL string and parameters, then place into rDBStatement. Use this function to prepare action queries you need to execute repeatedly.

db_query

function db_query($cQuery, $cParams, $cOptions = array(&quot;Scrollable&quot; =&gt; SQLSRV_CURSOR_STATIC))
	{
		/*
		db_query
		Damon Vaughn Caskey
		2012_11_13
		
		Prepare and execute query.
		
		$cQuery: 	Basic SQL statement to execute.
		$cParams:	Parameters to pass with query (prevents SQL injection).
		$cOptions:	Options for cursor array, etc.
		*/
	
		/* Execute query. */
		$this-&gt;rDBResult = sqlsrv_query($this-&gt;rDBConn, $cQuery, $cParams, $cOptions);
		
		/* Error trapping. */
		$this-&gt;db_error(&quot;db_query&quot;, $cQuery, $cParams);
		
		/* Return query ID resource. */
		return $this-&gt;rDBResult;		
	}

Prepare a query statement from SQL string and parameters, then execute immediately. Populates and returns rDBResult with result. This function is useful when you want to run a single query with no functional add-ons.