Never been to DZone Snippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

About this user

Stephen Martindale http://blue-wildebeest.blogspot.com

« Newer Snippets
Older Snippets »
Showing 1-4 of 4 total  RSS 

Space-Separated Tag Parser

Here is a function that accepts a string containing tags and returns an array of extracted tags. (Updated to ignore duplicates)
/**
 * Parses a String of Tags
 *
 * Tags are space delimited. Either single or double quotes mark a phrase.
 * Odd quotes will cause everything on their right to reflect as one single
 * tag or phrase. All white-space within a phrase is converted to single
 * space characters. Quotes burried within tags are ignored! Duplicate tags
 * are ignored, even duplicate phrases that are equivalent.
 *
 * Returns an array of tags.
 */
function ParseTagString($sTagString)
{
	$arTags = array();		// Array of Output
	$cPhraseQuote = null;	// Record of the quote that opened the current phrase
	$sPhrase = null;		// Temp storage for the current phrase we are building
	
	// Define some constants
	static $sTokens = " \r\n\t";	// Space, Return, Newline, Tab
	static $sQuotes = "'\"";		// Single and Double Quotes
	
	// Start the State Machine
	do
	{
		// Get the next token, which may be the first
		$sToken = isset($sToken)? strtok($sTokens) : strtok($sTagString, $sTokens);
		
		// Are there more tokens?
		if ($sToken === false)
		{
			// Ensure that the last phrase is marked as ended
			$cPhraseQuote = null;
		}
		else
		{		
			// Are we within a phrase or not?
			if ($cPhraseQuote !== null)
			{
				// Will the current token end the phrase?
				if (substr($sToken, -1, 1) === $cPhraseQuote)
				{
					// Trim the last character and add to the current phrase, with a single leading space if necessary
					if (strlen($sToken) > 1) $sPhrase .= ((strlen($sPhrase) > 0)? ' ' : null) . substr($sToken, 0, -1);
					$cPhraseQuote = null;
				}
				else
				{
					// If not, add the token to the phrase, with a single leading space if necessary
					$sPhrase .= ((strlen($sPhrase) > 0)? ' ' : null) . $sToken;
				}
			}
			else
			{
				// Will the current token start a phrase?
				if (strpos($sQuotes, $sToken[0]) !== false)
				{
					// Will the current token end the phrase?
					if ((strlen($sToken) > 1) && ($sToken[0] === substr($sToken, -1, 1)))
					{
						// The current token begins AND ends the phrase, trim the quotes
						$sPhrase = substr($sToken, 1, -1);
					}
					else
					{
						// Remove the leading quote
						$sPhrase = substr($sToken, 1);
						$cPhraseQuote = $sToken[0];
					}
				}
				else
					$sPhrase = $sToken;
			}
		}
		
		// If, at this point, we are not within a phrase, the prepared phrase is complete and can be added to the array
		if (($cPhraseQuote === null) && ($sPhrase != null))
		{
			$sPhrase = strtolower($sPhrase);
			if (!in_array($sPhrase, $arTags)) $arTags[] = $sPhrase;
			$sPhrase = null;
		}
	}
	while ($sToken !== false);	// Stop when we receive FALSE from strtok()
	return $arTags;
}


The string can be recreated from the array with the use of this reverse function:
/**
 * Reverses ParseTagString()
 */
function CreateTagString($arTags)
{
	// Prepare each tag to be imploded
	for ($i = 0; $i < sizeof($arTags); $i++)
	{
		// Record findings
		$bContainsWhitespace = false;	// Was whitespace found?
		$cRequiredQuote = '"';			// Use double-quote by default
		$cLastChar = null;
	
		// Search the tag
		for ($j = 0; $j < strlen($arTags[$i]); $j++)
		{
			$c = $arTags[$i][$j];
			
			// If the current character is a space
			if ($c === ' ')
			{
				$bContainsWhitespace = true;
				
				// If the previous char was a double quote, we require single quotes round our phrase
				if ($cLastChar === '"')
				{
					$cRequiredQuote = "'";
					break;	// There is no more point in continuing our search, we cant handle double-mixed quotes
				}
			}
			
			// Record this char as the last char
			$cLastChar = $c;
		}
		
		// Quote if necessary
		if ($bContainsWhitespace) $arTags[$i] = $cRequiredQuote . $arTags[$i] . $cRequiredQuote;
	}
	return implode(' ', $arTags);
}


To test the whole system, use the following array of test cases:
$arTestInputs = array(
	"this test ensures that words are correctly split",
	"in this test \"phrases\" and \"multi-word phrases\" are tested",
	"this test shows the behaviour if an \"odd quote is detected",
	"this test shows that 'different quotes' work too",
	"but mixed quotes fail: \"test phrase' does not stop on the quote",
	"which can be usefull in some cases where \"the systems' requirements\" state that it is necessary",
	"quotes need not be attached to \" their phrase \"",
	"embedded\"quotes are ignored!",
	"this is also usefull and demonstrates the system's coolness",
	"redundant   white-space is   removed from \"  tags    and phrases\"",
	"\"\"double quotes\"\" will result in single quotes!",
	"remember that 'double-quotes\" may be nested within single quotes'",
	"TaGs ArE NOT case SENsITiVE!",
	"a duplicate tag will be removed from the tag list",
	"even a \" complex phrase\" that is equivalent to another 'compleX   PHrASe   '"
);

foreach ($arTestInputs as $sTest)
{
	print ("<pre>$sTest</pre>");
	print "<pre>";
	print_r (ParseTagString($sTest));
	print "</pre>";
	print "<pre>";
	print CreateTagString(ParseTagString($sTest));
	print "</pre>";
	print "<hr />";
}


2006-03-09 0.1.0 - 0.2.0 Duplicate phrases are now ignored.

--
Version 0.2.0 - 2006-03-09
STEM: The STEM Cells of PHP
This work is licensed under a Creative Commons Attribution-ShareAlike 2.5 License
http://creativecommons.org/licenses/by-sa/2.5/

Converting old PHP Errors into Exceptions

The PHP5 Exception class will not work correctly. It would reflect an incorrect line number and file name and not record the 'errcontext' value associated with PHP errors. To solve this problem, we must extend the Exception class.
/**
 * This exception behaves like a "old school" PHP Error
 */
class STEM_ErrorException extends Exception
{
	/**
	 * The PHP Error Context
	 *
	 * The fifth parameter is optional, errcontext, which is an array that points to the active symbol table at the point the error occurred. In other words, errcontext  will contain an array of every variable that existed in the scope the error was triggered in. User error handler must not modify error context.
	 */
	private $m_arContext;

	/**
	 * Constructor
	 */
	public function __construct($vMessage, $vCode, $vFile, $vLine, $arContext = null)
	{
		parent::__construct($vMessage, $vCode);
		
		$this->file = $vFile;
		$this->line = $vLine;
		
		$this->m_arContext = $arContext;
	}
}


We need to define a function to handle errors. We also want to write two helper functions that will set and restore the error handler to our function. To do this, we write three methods in an abstract class.
/**
 * STEM Error Handler
 *
 * Registers Itself as a PHP Error Handler and proceeds to convert all
 * native "old school" PHP errors into new PHP5 Exceptions.
 *
 * Call STEM_ErrorHandler::Initialize(); before your try blocks and
 * STEM_ErrorHandler::Uninitialize(); afterwards.
 */
abstract class STEM_ErrorHandler
{
	/**
	 * Encapsulates set_error_handler()
	 */
	public static function Initialize()
	{
		set_error_handler(array("STEM_ErrorHandler", "HandleError"));
	}
	
	/**
	 * Encapsulates restore_error_handler()
	 */
	public static function Uninitialize()
	{
		restore_error_handler();
	}
	
	/**
	 * Handles PHP Errors
	 */
	public static function HandleError($errno, $errstr, $errfile, $errline, $errcontext)
	{
		throw new STEM_ErrorException($errstr, $errno, $errfile, $errline, $errcontext);
	}
}


To test it, we create a file that triggers errors. Before triggering the first error, we call Initialize(). We then Uninitialize() the error handler and trigger the error again to check that our error handler has been removed. It is important to check the line numbers mentioned in the message that this prints!
STEM_ErrorHandler::Initialize();

try
{
	trigger_error("Hello World!");
}
catch (Exception $e)
{
	print $e;
}

STEM_ErrorHandler::Uninitialize();
print "<hr />";

try
{
	trigger_error("Hello World!");
}
catch (Exception $e)
{
	print $e;
}


--
Version 0.1.0 - 2006-02-14
STEM: The STEM Cells of PHP
This work is licensed under a Creative Commons Attribution-ShareAlike 2.5 License
http://creativecommons.org/licenses/by-sa/2.5/

Random Password Generator

This is a complete, working, random password generator for PHP. It allows the implementor to customize the character sets that the password is generated from.

To configure the generator, create the following configuration array. It is an array of arrays where each element array defines the characters in the pool and the minimum and maximum number of these characters that must appear in the result password. Each member array is given a single character token that identifies it.
// Configuration definitions, move to config.php
$CONFIG['security']['password_generator'] = array(
	"C" => array('characters' => 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 'minimum' => 4, 'maximum' => 6),
	"S" => array('characters' => "!@()-_=+?*^&", 'minimum' => 1, 'maximum' => 2),
	"N" => array('characters' => '1234567890', 'minimum' => 2, 'maximum' => 2)
);


The GeneratePassword() function uses the configuration array to generate a password. It starts by creating a meta-password, which is a shuffled string of the tokens from the configuration data. After the meta-password is ready, it loops through it and uses each token to choose a character from the pool of available characters defined in the configuration arrays. Once it is done, it returns the result.
function STEM_GeneratePassword()
{
	// Create the meta-password
	$sMetaPassword = "";
	
	global $CONFIG;
	$ahPasswordGenerator = $CONFIG['security']['password_generator'];
	foreach ($ahPasswordGenerator as $cToken => $ahPasswordSeed)
		$sMetaPassword .= str_repeat($cToken, rand($ahPasswordSeed['minimum'], $ahPasswordSeed['maximum']));
		
	$sMetaPassword = str_shuffle($sMetaPassword);
	
	// Create the real password
	$arBuffer = array();
	for ($i = 0; $i < strlen($sMetaPassword); $i ++)
		$arBuffer[] = $ahPasswordGenerator[(string)$sMetaPassword[$i]]['characters'][rand(0, strlen($ahPasswordGenerator[$sMetaPassword[$i]]['characters']) - 1)];

	return implode("", $arBuffer);
}


--
Version 0.1.0 - 2006-02-14
STEM: The STEM Cells of PHP
This work is licensed under a Creative Commons Attribution-ShareAlike 2.5 License
http://creativecommons.org/licenses/by-sa/2.5/

DateTime with PHP and MySql

This script shows, by example, how to convert from a UNIX Timestamp (the returned data type from the PHP time() or mktime() functions) into a string that can be stored in MySql's DATETIME or TIMESTAMP fields. It also shows how to convert back into a UNIX Timestamp. The times are stored in the database as GMT/UTC times, so finally, it shows how to convert from the resulting UNIX timestamp into a string that represents the time with a given GMT/UTC offset.

Create the UNIX Timestamp, using the current system time.
// Create the UNIX Timestamp, using the current system time
$tUnixTime = time();


Convert that UNIX Timestamp into a string, safe for MySql. The string will be in the format CCYY-MM-DD HH:MM:SS and will represent the GMT/UTC time represented by the UNIX Timestamp. Example: 2006-02-10 20:33:55
// Convert that UNIX Timestamp into a string (GMT), safe for MySql
$sGMTMySqlString = gmdate("Y-m-d H:i:s", $tUnixTime);


The string created above is suitable for sending to MySql and storing in a DATETIME or TIMESTAMP field. This snippet does not include the code that would store and retrieve the data.

Parse the String and store the result in a new UNIX Timestamp. Because strtotime() thinks that any string passed to it, that does not specify a timezone, is in the local timezone, according to the settings in your configuration, we append " GMT" to the string that is returned from MySql. This creates a new string that is both compliant with strtotime() and explicitly a GMT value.
// Parse the String into a new UNIX Timestamp
$tParsedTime = strtotime($sGMTMySqlString . " GMT");


Show the two values, in W3C format, for debugging. W3C format dates include the full date, time and timezone information. If we visually or programmatically compare the two values, they should be identical in all aspects.
// Show the Original Time and Parsed Time on the screen, in W3C Format
print "Original Time: " . date(DATE_W3C, $tUnixTime);
print "Parsed Time: " . date(DATE_W3C, $tParsedTime);


The following two lines demonstrate how to take one of these UNIX timestamps and convert it into a user-friendly string, using a time offset. Typically, you would store this time offset in your user's profile.
// Create a String showing the DateTime in CCYY-MM-DD Format in a Specified Zone
$fUTCOffset = +2.00; // GMT/UTC Offset in Hours (2.50 = 2 hours 30 minutes)
print "Time in GMT+2: " . gmdate("Y-m-d H:i:s", $tUnixTime + $fUTCOffset * 3600);


I am not sure whether my idea to use a floating point number to represent the user's time zone is correct. I must check the standard. If the standard is to use a time-span value (2.50 is not 2 hours 30 minutes but 2 hours and 50 minutes, 2.30 is 2 hours and 30 minutes) I will change it. This is a minor change.

I did not know of a PHP function that would convert a GMT/UTC UNIX Timestamp into a string representing a time in a specified zone. All I could find is the date() function which uses the current zone according to the config and the gmdate() zone which returns GMT/UTC. To implement the functionality, I modified the date by adding to it and then converting to GMT/UTC. I do not like this, but it works.

This code makes no attempt to guess whether the current user is affected by DST or not. It simply uses an offset from GMT/UTC. According to my research, DST guessing is dodgy at best and, according to some, soon to be outdated by a change to the DST system in America.

I am not sure, even if I knew how, that I would trust built-in time zone functionality. I would suspect its accuracy for the same reasons I don't use automatic guessing of DST status.

--
Version 0.0.1 - 2006-02-10
Stephen's Modules: DateTime with PHP and MySql
This work is licensed under a Creative Commons Attribution-ShareAlike 2.5 License
http://creativecommons.org/licenses/by-sa/2.5/
« Newer Snippets
Older Snippets »
Showing 1-4 of 4 total  RSS