<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Stut.net &#187; PHP</title>
	<atom:link href="http://stut.net/tag/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://stut.net</link>
	<description>Ramblings of a random software engineer</description>
	<lastBuildDate>Thu, 08 Jul 2010 10:42:58 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Twitorfit</title>
		<link>http://stut.net/2008/12/17/twitorfit/</link>
		<comments>http://stut.net/2008/12/17/twitorfit/#comments</comments>
		<pubDate>Wed, 17 Dec 2008 10:39:13 +0000</pubDate>
		<dc:creator>Stuart</dc:creator>
				<category><![CDATA[Cool]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[twitorfit]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://stut.net/blog/?p=311</guid>
		<description><![CDATA[My latest project went live on Monday this week. It&#8217;s another Twitter-based toy along the same lines as hotornot but it uses your Twitter account and profile picture. Since launch it&#8217;s proved very successful and has spread quickly due to its viral nature. The site is called Twitorfit and was the brainchild of a Twitter-based [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_jade" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fstut.net%252F2008%252F12%252F17%252Ftwitorfit%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Twitorfit%22%20%7D);"></div>
<p>My latest project went live on Monday this week. It&#8217;s another <a href="http://twitter.com/stut">Twitter</a>-based toy along the same lines as <a title="Are you hot or not?" href="http://www.hotornot.com/">hotornot</a> but it uses your Twitter account and profile picture. Since launch it&#8217;s proved very successful and has spread quickly due to its viral nature.</p>
<p>The site is called <a title="Are you a twit or fit?" href="http://www.twitorfit.com/">Twitorfit</a> and was the brainchild of a Twitter-based conversation between <a title="What is this tech?" href="http://www.nickhalstead.com/">Nick Halstead</a> of <a title="Your news, your views" href="http://fav.or.it/">fav.or.it</a> and <a title="Huddle" href="http://www.huddle.net/">Andy McLoughlin</a> and <a title="Girl About Web" href="http://girlaboutweb.com/">Zuzanna Pasierbinska</a> from <a title="Huddle: Online Project Management, Group Collaboration and Document Sharing" href="http://www.huddle.net/">Huddle</a>. You can read the full story on the <a title="Twitorfit - the story" href="http://blog.twitorfit.com/2008/12/15/twitorfit-–-the-story/">Twitorfit blog</a>.</p>
<p style="text-align: center;"><a style="margin-right: 2em;" title="Twtiorfit Screenshot by Stuart Dallas, on Flickr" href="http://www.flickr.com/photos/stuartdallas/3114896083/"><img src="http://farm4.static.flickr.com/3048/3114896083_0c58fbb68a_m.jpg" alt="Twtiorfit Screenshot" width="240" height="234" /></a><a title="Twitorfit Top 10 Screenshot by Stuart Dallas, on Flickr" href="http://www.flickr.com/photos/stuartdallas/3115725548/"><img src="http://farm4.static.flickr.com/3287/3115725548_7a9bbfd516_m.jpg" alt="Twitorfit Top 10 Screenshot" width="150" height="240" /></a></p>
<p style="text-align: center;"><em>Rate users then see the top 10 &#8220;twits&#8221; and &#8220;fits&#8221;</em></p>
<p>Development of the site took a little under 20 man-hours in total from concept to launch including several rewrites of the core architecture. I&#8217;d like to thank <a title="Proton Gun" href="http://www.protongun.com/">Daniel Saxil-Nielsen</a>, the designer at fav.or.it, for sorting out the design and <a title="Alex Forrow on Twitter" href="http://twitter.com/alexforrow">Alex Forrow</a>, their sysadmin, for sorting out the live server. They both responded to my requests quickly and efficiently leaving me able to concentrate on getting the code done.</p>
<p>I&#8217;m hoping to do another post with a bit more detail about how Twitorfit works, and the specific techniques I used to ensure it would stand up to the initial peak in traffic that this type of project tends to get. For now I&#8217;ll just say that it&#8217;s written in <a title="PHP" href="http://www.php.net/">PHP5</a>, uses a <a href="http://www.mysql.com/">MySQL database</a> and <a title="Memcached" href="http://www.danga.com/memcached/">Memcache</a>. It&#8217;s running on <a title="Sun Microsystems" href="http://www.sun.com/">Sun</a> hardware provided by the <a title="Sun Startup Essentials" href="http://uk.sun.com/startupessentials/">Startup Essentials program</a>.</p>
<p>The site currently asks for your Twitter username and password when you log in and/or register which is less than ideal, but until they <a title="OAuth on the Twitter API Issue Tracker" href="http://code.google.com/p/twitter-api/issues/detail?id=2">release their much-awaited OAuth implementation</a> it&#8217;s the most user-friendly way to authenticate Twitter users. It does not store your password and aside from login verification and the optional tweet when you register it does not make any requests against the Twitter API as you.</p>
<p>In summary it&#8217;s been a fun little project to work on, and the feedback so far has been fantastic. If you&#8217;re not on Twitter yet you should be &#8211; <a title="Sign up for Twitter" href="https://twitter.com/signup">get your account here</a>, and when you&#8217;re done be sure to <a title="Are you a twit or fit?" href="http://www.twitorfit.com/">register on Twitorfit and get rating</a>!</p>
<div id="crp_related"><h3>If you liked this you might also like these:</h3><ul><li><a href="http://stut.net/2008/01/28/tweetmeme-launch/" rel="bookmark" class="crp_title">Tweetmeme Launch</a></li><li><a href="http://stut.net/2008/04/04/twitter-public-timeline/" rel="bookmark" class="crp_title">Twitter public timeline</a></li><li><a href="http://stut.net/2009/01/24/oauth-and-twitter-realistic-expectations/" rel="bookmark" class="crp_title">OAuth and Twitter: Realistic expectations</a></li><li><a href="http://stut.net/2008/05/23/has-that-website-gone-titsup/" rel="bookmark" class="crp_title">Has that website gone TitsUp?</a></li><li><a href="http://stut.net/2008/04/28/recent-ads-from-freeads-classifieds-on-twitter/" rel="bookmark" class="crp_title">Recent ads from Freeads Classifieds on Twitter</a></li></ul></div>
]]></content:encoded>
			<wfw:commentRss>http://stut.net/2008/12/17/twitorfit/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>PHP Models</title>
		<link>http://stut.net/2008/06/08/php-models-2/</link>
		<comments>http://stut.net/2008/06/08/php-models-2/#comments</comments>
		<pubDate>Sun, 08 Jun 2008 20:42:27 +0000</pubDate>
		<dc:creator>Stuart</dc:creator>
				<category><![CDATA[Misc]]></category>
		<category><![CDATA[activerecord]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://stut.net/blog/?p=220</guid>
		<description><![CDATA[I wanted an object-oriented way of accessing a database that strikes a good balance between abstracting the details of SQL escaping, insert or update, etc and going too far to the point where the benefits are drowned out by the abstraction. In this article I present the system I am currently using. It does most [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_jade" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fstut.net%252F2008%252F06%252F08%252Fphp-models-2%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22PHP%20Models%22%20%7D);"></div>
<p>I wanted an object-oriented way of accessing a database that strikes a good balance between abstracting the details of SQL escaping, insert or update, etc and going too far to the point where the benefits are drowned out by the abstraction. In this article I present the system I am currently using. It does most of what I wanted but certainly has potential for further improvement.</p>
<p>The main reason this post exists is due to a request from someone on the PHP-General list. This code is not intended to be bug-free or extensively tested or indeed anything. Treat it as you would any other experimental code.<span id="more-220"></span></p>
<h2>Design</h2>
<p>The basic design of this system is based around a base class called <em>Table</em>. Fundamentally the <em>Table</em> class wraps a database table row. When using a system like this it is important to keep in mind what the memory implications are, but more on that later.</p>
<p>When an instance of a subclass of the <em>Table</em> class is created, it gets the table definition from either the database or a definition cache. The cache is file-based and is considerably quicker than getting the definition from the database every time. The definition for any given table is loaded only once per request. The only important implication of this is that if the table definition changes you need to delete the cache file to force it to be loaded from the database again.</p>
<p>One last thing&#8230; the class relies upon the primary key to perform updates, so it&#8217;s vital that any tables you want to use with this system have one.</p>
<p>Ok, nuff talk, let&#8217;s get to the code!</p>
<h2>Implementation</h2>
<p>Probably the easiest thing to do is to go through the two classes piece by piece. Rather than describe the code and force you to match up my ramblings with a separate file of code, I&#8217;ve commented the source liberally. Let&#8217;s start with the <em>Table</em> class.</p>
<pre name="code" class="php">&lt;?php
 // Table: An ActiveRecord-style DB abstraction class
 // Copyright (c) 2005-2006 Stuart Dallas
 // Released into the public domain with absolutely no warranty, explicit, implied or otherwise
 // Use at your own risk!! 

// If you do use this code please drop an email to code@stut.net and let me know why <img src='http://stut.net/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' />
 // Patches, comments, suggestions, questions, etc are welcomed

// The MODEL_CACHE_DIR is where the table definitions get cached. This can be anywhere you like, defaults to cache/
 // in the same location as this file. Obviously the web user (usually nobody or www) needs to have write access to
 // this directory.
 define('MODEL_CACHE_DIR', dirname(__FILE__).'/cache/');
 // Make sure the directory exists, or try to create it if not
 if (!file_exists(MODEL_CACHE_DIR))
 	mkdir(MODEL_CACHE_DIR) or die('Failed to create model cache directory on line '.__LINE__.' of '.__FILE__);

class Table
 {
 	protected $table = '';					// What table does this map to
 	protected $idfield = 'id';				// If there is an ID field, but it's not called id, override this
 	protected $readonly = false;			// Access to this table is read only
 	protected $data = false;				// Internal array of row data
 	protected $dirty = false;				// Is this row dirty?
 	protected $disregarddirty = false;		// Do we care that it's dirty?
 	protected $isnew = true;				// Is it a new row?
 	protected $customfieldtypes = array();	// Overrides of the default field types (for automatic form creation)

protected static $fields = array();		// Per-request cache of the table definitions

// Init is called by the constructors of subclasses
 	// It is responsible for loading the table definition
 	public function Init($classname, $conditions = '', $order = '')
 	{
 		// Have we been called statically or dynamically?
 		// Either way we need an instance to work with
 		if (isset($this))
 			$obj = &amp;$this;
 		else
 			$obj = new $classname();

// The table variable should be set by the subclass, if not use the class name
 		if ($obj-&gt;table == '')
 			$obj-&gt;table = strtolower($classname);

// Only load the field defs if this model has not been used yet in this request
 		if (!isset(self::$fields[$obj-&gt;table]))
 		{
 			// Build the base filename of the cache files for this table
 			$cachefilename = MODEL_CACHE_DIR.$obj-&gt;table;

// Does the field definition cache file exist? If not we need to build it
 			if (!file_exists($cachefilename.'.fields'))
 			{
 				// Initialise the cache (stored statically in the Table class)
 				self::$fields[$obj-&gt;table]['fields'] = array();
 				self::$fields[$obj-&gt;table]['autonumber'] = array();
 				self::$fields[$obj-&gt;table]['primarykey'] = array();

// Get the field definitions
 				$query = mysql_query('show columns from '.$obj-&gt;table);
 				while ($row = mysql_fetch_assoc($query))
 				{
 					// We store auto_number fields separately for use when creating new rows
 					if (strpos($row['Extra'], 'auto_increment') !== false)
 						self::$fields[$obj-&gt;table]['autonumber'][] = $row['Field'];
 					else
 						self::$fields[$obj-&gt;table]['fields'][$row['Field']] = $row;
 				}

// Get the primary key fields
 				$query = mysql_query('show indexes from '.$obj-&gt;table);
 				while ($row = mysql_fetch_assoc($query))
 				{
 					if ($row['Key_name'] == 'PRIMARY')
 						self::$fields[$obj-&gt;table]['primarykey'][] = $row['Column_name'];
 				}

// Now save the definition in files in the model cache directory
 				file_put_contents($cachefilename.'.fields', serialize(self::$fields[$obj-&gt;table]['fields']));
 				file_put_contents($cachefilename.'.autonumber', serialize(self::$fields[$obj-&gt;table]['autonumber']));
 				file_put_contents($cachefilename.'.primarykey', serialize(self::$fields[$obj-&gt;table]['primarykey']));
 			}
 			else
 			{
 				// The cache files exist, load them
 				self::$fields[$obj-&gt;table]['fields'] = unserialize(file_get_contents($cachefilename.'.fields'));
 				self::$fields[$obj-&gt;table]['autonumber'] = unserialize(file_get_contents($cachefilename.'.autonumber'));
 				self::$fields[$obj-&gt;table]['primarykey'] = unserialize(file_get_contents($cachefilename.'.primarykey'));
 			}
 		}

// Did we get given conditions?
 		if (strlen($conditions) &gt; 0)
 		{
 			// If it's numeric, use it as an ID and build a where clause. This relies upon the idfield member variable that
 			// should be overridden in subclasses if the ID field is not named 'id'
 			if (is_numeric($conditions))
 			{
 				$conditions = $this-&gt;idfield.' = '.Table::Escape($conditions);
 			}

// Try to find a row matching the conditions
 			if ($obj-&gt;FindFirst($conditions, $order) === false)
 			{
 				// Couldn't find one, reset this object so it's an unsaved new row
 				$obj-&gt;Clear();
 			}
 		}
 		else
 		{
 			// No conditions, it's an unsaved new row
 			if($obj-&gt;data === false) $obj-&gt;Clear();
 		}
 	}

// The destructor does nothing more than a sanity check to see if this row is dirty (i.e. contains unsaved data), in which
 	// case it raises an error. The disregarddirty flag can be used to disable this where destroying a dirty object is expected
 	public function __destruct()
 	{
 		if ($this-&gt;dirty &amp;&amp; !$this-&gt;disregarddirty)
 			trigger_error(get_class($this).'.__destruct: Dirty object being released');
 	}

// Ahh, magic functions!!
 	// This one allows you to use OO syntax to access fields, e.g. $obj-&gt;id will get you the id field
 	public function __get($field)
 	{
 		return (isset($this-&gt;data[$field]) ? $this-&gt;data[$field] : '');
 	}

// The other half of __get is __set. This method is called if you do something like $obj-&gt;id = 100
 	public function __set($field, $value)
 	{
 		// If we're read only then changing the data is not allowed
 		if ($this-&gt;readonly)
 		{
 			trigger_error(get_class($this).'.__set: Attempt to modify a readonly object');
 		}
 		// If the row is not new then the primary key is read only
 		elseif (!$this-&gt;isnew and in_array($field, self::$fields[$this-&gt;table]['primarykey']))
 		{
 			trigger_error(get_class($this).'.__set: Attempt to set primary key field "'.$field.'"');
 		}
 		// Now we check to make sure the attribute being set actually exists (i.e. is in the table fields or is already in the
 		// data array)
 		elseif (isset(self::$fields[$this-&gt;table]['fields'][$field]) or isset($this-&gt;data[$field]))
 		{
 			if (!isset($this-&gt;data[$field]) or $this-&gt;data[$field] != $value)
 				$this-&gt;dirty = true;
 			$this-&gt;data[$field] = $value;
 		}
 		// If we get here then we don't know anything about the field being set - that's an error that is!
 		else
 		{
 			trigger_error(get_class($this).'.__set: Unknown field "'.$field.'"');
 		}
 	}

// LoadFromArray does what it says on the tin. It fills in the data for this record from an array
 	private function LoadFromArray($arr)
 	{
 		$this-&gt;data = array();

// Only allow fields we know about, ignore anything else
 		foreach (array_keys(self::$fields[$this-&gt;table]['fields']) as $field)
 			if (isset($arr[$field]))
 				$this-&gt;data[$field] = $arr[$field];
 		foreach (self::$fields[$this-&gt;table]['autonumber'] as $field)
 			if (isset($arr[$field]))
 				$this-&gt;data[$field] = $arr[$field];

// At first glance these might seem wrong, but this method is used when reading &gt; 1 row (see the FindAll method) and since it's
 		// a private method we know the data source will always be the database, so it's not new and it's not dirty
 		$this-&gt;dirty = false;
 		$this-&gt;isnew = false;

// Call table-specific translation
 		$this-&gt;AfterLoad();
 	}

// Call this function to disable the error generated when a dirty object is destructed
 	public function DisregardDirty()
 	{
 		$this-&gt;disregarddirty = true;
 	}

// Clear the object of data.
 	protected function Clear()
 	{
 		$this-&gt;data = array();
 	}

// Stubs for table-specific stuff
 	protected function AfterLoad() { }
 	protected function BeforeSave() { }
 	protected function AfterDelete($result) { }
 	protected function BeforeDelete() { }

// GetFieldType first looks in the customfieldtypes array to see if the subclass has overridden it before returning the
 	// field type from the table definition. If we don't know anything about the field, assume it's a string
 	public function GetFieldType($field)
 	{
 		if (isset($this-&gt;customfieldtypes[$field]))
 			return $this-&gt;customfieldtypes[$field];
 		if (isset(self::$fields[$this-&gt;table]['fields'][$field]['Type']))
 			return self::$fields[$this-&gt;table]['fields'][$field]['Type'];
 		return 'string';
 	}

// GetValueFromDB will re-fetch a single value from the DB - useful for flags, locks, etc
 	public function GetValueFromDB($field)
 	{
 		$retval = $this-&gt;GetValuesFromDB(array($field));
 		return $retval[$field];
 	}

// GetValuesFromDB will get a given set of fields from this row in the table
 	// Note that it does not update the internal data
 	public function GetValuesFromDB($fieldlist = array())
 	{
 		$retval = false;
 		if (!$this-&gt;isnew and count($fieldlist) &gt; 0)
 		{
 			$sql = 'select '.implode(',', $fieldlist).' from '.$this-&gt;table.' where '.$this-&gt;PrimaryKeyWhere();
 			$query = mysql_query($sql);
 			if ($query !== false and mysql_num_rows($query) == 1)
 			{
 				$row = mysql_fetch_assoc($query);
 				$retval = array();
 				foreach ($row as $key =&gt; $val)
 					$retval[$key] = $val;
 			}
 		}
 		return $retval;
 	}

// GetFieldList will produce an array containing the table definition
 	public function GetFieldList($pkey = true, $normal = true)
 	{
 		$retval = array();
 		if ($pkey) $retval['primarykey'] = self::$fields[$this-&gt;table]['primarykey'];
 		if ($normal) $retval['normal'] = self::$fields[$this-&gt;table]['fields'];
 		return $retval;
 	}

// Reload will update this object from the table
 	// Reloading a new row (duh!!) or reloading a dirty object will raise errors
 	public function Reload($ignoredirty = false)
 	{
 		if ($this-&gt;isnew)
 		{
 			trigger_error(get_class($this).'.Reload: Attempted to reload a new object');
 		}
 		else
 		{
 			if (!$ignoredirty and !$this-&gt;disregarddirty and $this-&gt;dirty)
 				trigger_error(get_class($this).'.Reload: Reloading a dirty object - changes lost');
 			// Reload from DB
 			$this-&gt;FindFirst($this-&gt;PrimaryKeyWhere());
 		}
 	}

// CreateInsertSQL is a helper function used to build insert statements
 	private function CreateInsertSQL($setfields)
 	{
 		return 'insert into '.$this-&gt;table.' set '.implode(', ', $setfields);
 	}

// Save this row
 	public function Save()
 	{
 		$retval = false;

// Can't save a read only row
 		if ($this-&gt;readonly)
 		{
 			trigger_error(get_class($this).'.Save: Attempt to save a readonly object');
 		}
 		// Are we dirty?
 		elseif ($this-&gt;dirty)
 		{
 			// Call table-specific translation
 			$this-&gt;BeforeSave();

// Get the fields
 			$setfields = array();
 			foreach (array_keys(self::$fields[$this-&gt;table]['fields']) as $field)
 			{
 				if (isset($this-&gt;data[$field]))
 					$setfields[$field] = $field.' = '.self::Escape($this-&gt;data[$field]);
 			}

// Is this a new row?
 			if ($this-&gt;isnew)
 			{
 				// Insert a new row
 				$sql = $this-&gt;CreateInsertSQL($setfields);
 			}
 			else
 			{
 				// Update existing row
 				$sql = 'update '.$this-&gt;table.' set '.implode(', ', $setfields).' where '.$this-&gt;PrimaryKeyWhere();
 			}

$result = mysql_query($sql);
 			if ($result === false)
 			{
 				// Something bad happened!
 				trigger_error(get_class($this).'.Save: Failed to save object - '.mysql_error());
 				$retval = false;
 			}
 			else
 			{
 				// Saved successfully, we're now clean
 				$this-&gt;dirty = false;
 				$retval = true;

// If this was a new row we need to grab the auto_number'd id
 				if ($this-&gt;isnew)
 				{
 					$this-&gt;data[$this-&gt;idfield] = mysql_insert_id();
 					// We're no longer a new row
 					$this-&gt;isnew = false;
 				}
 			}

// Call table-specific stuff
 			$this-&gt;AfterLoad();
 		}
 		else
 		{
 			// Not dirty, call it a success!
 			$retval = true;
 		}

return $retval;
 	}

// Delete will delete this row from the table
 	public function Delete()
 	{
 		$retval = false;
 		// Can't delete a new row, it doesn't actually exist yet!
 		if ($this-&gt;isnew)
 		{
 			trigger_error(get_class($this).'.Delete: Attempted to delete a new object');
 		}
 		else
 		{
 			// Call table-specific stuff
 			$this-&gt;BeforeDelete();
 			// Do the delete
 			$sql = 'delete from '.$this-&gt;table.' where '.$this-&gt;PrimaryKeyWhere();
 			$retval = mysql_query($sql);
 			// Call table-specific stuff
 			$this-&gt;AfterDelete($retval);
 		}
 		return $retval;
 	}

// PrimaryKeyWhere builds a where clause from the fields in the primary key
 	// This effectively produces a where clause that will retrieve the row this object is representing
 	public function PrimaryKeyWhere()
 	{
 		$wherefields = array();
 		foreach (self::$fields[$this-&gt;table]['primarykey'] as $field)
 			$wherefields[] = $field.' = '.self::Escape($this-&gt;data[$field]);
 		return '('.implode(' and ', $wherefields).')';
 	}

// FindAll takes a set of conditions in the form of a where clause, a limit clause and an order specification,
 	// builds a query from them and executes it. The rows returned are used to create an array of objects which is
 	// then returned
 	// Note that this method can only be called on subclasses - it cannot be called directly on the Table class
 	public function FindAll($conditions = '', $limit = '', $order = '')
 	{
 		$sql = 'select * from '.$this-&gt;table;
 		if (strlen($conditions) &gt; 0)
 			$sql .= ' where '.$conditions;

if (strlen($order) &gt; 0)
 			$sql .= ' order by '.$order;

if (strlen($limit) &gt; 0)
 			$sql .= ' limit '.$limit;

$query = mysql_query($sql);

// Errors will currently cause an error to be raised. This may not be ideal for your application, you may need
 		// to change how these are handled
 		if (!$query)
 			trigger_error('MySQL error in '.get_class($this).'::FindAll: '.mysql_error());

// If the query got no rows return an empty array
 		if (mysql_num_rows($query) == 0)
 			return array();

$classname = get_class($this);
 		$retval = array();
 		while ($row = mysql_fetch_assoc($query))
 		{
 			$obj = new $classname();
 			$obj-&gt;LoadFromArray($row);
 			$retval[] = $obj;
 		}
 		return $retval;
 	}

// FindFirst does the same as FindAll but only gets a single row and returns a single object
 	public function FindFirst($conditions = '', $order = '')
 	{
 		$sql = 'select * from '.$this-&gt;table;
 		if (strlen($conditions) &gt; 0)
 			$sql .= ' where '.$conditions;

if (strlen($order) &gt; 0)
 			$sql .= ' order by '.$order;

$sql .= ' limit 1';

$query = mysql_query($sql);

// Errors will currently cause an error to be raised. This may not be ideal for your application, you may need
 		// to change how these are handled
 		if (!$query)
 			trigger_error('MySQL error in '.get_class($this).'::FindFirst: '.mysql_error());

// If the query got no rows return false rather than a new empty object
 		if (mysql_num_rows($query) == 0)
 			return false;

$row = mysql_fetch_assoc($query);
 		$this-&gt;LoadFromArray($row);

return $this;
 	}

// Paged is similar to FindAll but it will get a certain range of rows given a page number and the number of rows
 	// on each page. Returns an array where [0] is an array of rows, [1] is the current page number (in case it was
 	// adjusted) and [2] is the total number of pages available
 	public function Paged($page = 1, $perpage = 10, $conditions = '', $order = '')
 	{
 		// Get the total count
 		$sql = 'select count(1) from '.$this-&gt;table;
 		if (strlen($conditions) &gt; 0)
 			$sql .= ' where '.$conditions;
 		$query = mysql_query($sql);

// Query failed, raise an error
 		if (!$query)
 			trigger_error('MySQL error in '.get_class($this).'::Paged: '.mysql_error());

// Because it's a count query this should never happen, but handle nicely just in case
 		if (mysql_num_rows($query) == 0)
 			return array(array(), 1, 1);

$row = mysql_fetch_array($query);
 		$rowcount = $row[0];
 		// Return if there are no matching rows
 		if ($rowcount == 0)
 			return array(array(), 1, 1);

// Make sure the requested page number is in range
 		$totalpages = ceil($rowcount / $perpage);
 		if ($page &gt; $totalpages) $page = $totalpages;
 		if ($page &lt; 1) $page = 1;

// Build the query
 		$sql = 'select * from '.$this-&gt;table;
 		if (strlen($conditions) &gt; 0)
 			$sql .= ' where '.$conditions;

if (strlen($order) &gt; 0)
 			$sql .= ' order by '.$order;

$sql .= ' limit '.(($page-1) * $perpage).','.$perpage;

$query = mysql_query($sql);

// Query failed, raise an error
 		if (!$query)
 			trigger_error('MySQL error in '.get_class($this).'::Paged: '.mysql_error());

// No rows returned, this shouldn't be possible but handle nicely just in case
 		if (mysql_num_rows($query) == 0)
 			return array(array(), 1, 1);

// Create the object array
 		$classname = get_class($this);
 		$retval = array();
 		while ($row = mysql_fetch_assoc($query))
 		{
 			$obj = new $classname();
 			$obj-&gt;LoadFromArray($row);
 			$retval[] = $obj;
 		}

// Return the rows, the page number and the number of pages
 		return array($retval, $page, $totalpages);
 	}

// Count returns the number of rows that match a condition
 	public function Count($conditions)
 	{
 		$sql = 'select count(*) as count from '.$this-&gt;table;
 		if (strlen($conditions) &gt; 0)
 			$sql .= ' where '.$conditions;

$query = mysql_query($sql);
 		if (!$query or mysql_num_rows($query) == 0)
 			return false;

$row = mysql_fetch_assoc($query);
 		return $row['count'];
 	}

// Is this row new?
 	public function IsNew()
 	{
 		return $this-&gt;isnew;
 	}

//////////////////////////////
 	// Static utility functions //
 	//////////////////////////////

// Escape should be used to escape all values used in conditions
 	static public function Escape($var)
 	{
 		return '"'.mysql_real_escape_string($var).'"';
 	}

// MakeLike returns a like query for a given var and val
 	static public function MakeLike($var, $val)
 	{
 		return '(`'.$var.'` like "%'.mysql_real_escape_string($val).'%")';
 	}

// MakeLikes takes an array of vars and a single val and returns a set of likes combined by op
 	static public function MakeLikes($vars, $val, $op = 'or')
 	{
 		$likes = array();
 		foreach ($vars as $var)
 			$likes[] = self::MakeLike($var, $val);
 		return '('.implode(' '.$op.' ', $likes).')';
 	}

// Lock and Unlock wrap the table locking system
 	static protected function Lock($tables = false)
 	{
 		if ($tables === false)
 			trigger_error("Table::Lock called without a table to lock");

if (!is_array($tables))
 			$tables = array($tables);
 		return mysql_query('lock tables `'.implode('`,`', $tables).'`');
 	}
 	static protected function Unlock()
 	{
 		return mysql_query('unlock tables');
 	}

// GetSingleValue will return the first value of the first row returned by the provided SQL
 	// Caller should make sure it's only getting one field and one row
 	static public function &amp; GetSingleValue($sql)
 	{
 		$query = mysql_query($sql);
 		if ($query === false)
 		{
 			trigger_error('Query failed: '.mysql_error(), E_USER_ERROR);
 			exit;
 		}
 		if (mysql_num_rows($query) == 0)
 			return false;
 		$retval = mysql_fetch_array($query);
 		mysql_free_result($query);
 		return $retval[0];
 	}

// GetSingle will return an associative array containing the first row returned by the provided SQL
 	// Caller should make sure it's only getting one row
 	static public function &amp; GetSingle($sql)
 	{
 		$query = mysql_query($sql);
 		if ($query === false)
 		{
 			trigger_error('Query failed: '.mysql_error(), E_USER_ERROR);
 			exit;
 		}
 		if (mysql_num_rows($query) == 0)
 			return array();
 		$retval = mysql_fetch_assoc($query);
 		mysql_free_result($query);
 		return $retval;
 	}

// GetMultiple will return an array of associative arrays containing every row returned by the provided SQL
 	// Be careful not to get too many rows with this method - it loads them all into memory!!
 	static public function &amp; GetMultiple($sql)
 	{
 		$query = mysql_query($sql);
 		if ($query === false)
 		{
 			trigger_error('Query failed: '.mysql_error(), E_USER_ERROR);
 			exit;
 		}
 		$retval = array();
 		if (mysql_num_rows($query) &gt; 0)
 		{
 			while ($row = mysql_fetch_assoc($query))
 			$retval[] = $row;
 		}
 		mysql_free_result($query);
 		return $retval;
 	}

// GetSingleColumn will return an array containing the first field of each row returned by the provided SQL
 	// Be careful not to get too many rows with this method - it loads them all into memory
 	static public function &amp; GetSingleColumn($sql)
 	{
 		$query = mysql_query($sql);
 		if ($query === false)
 		{
 			trigger_error('Query failed: '.mysql_error(), E_USER_ERROR);
 			exit;
 		}
 		$retval = array();
 		if (mysql_num_rows($query) &gt; 0)
 		{
 			while ($row = mysql_fetch_array($query))
 			$retval[] = $row[0];
 		}
 		mysql_free_result($query);
 		return $retval;
 	}

// Modify is intended to execute a SQL statement that will make a change (insert, update, alter, etc)
 	static public function Modify($sql)
 	{
 		$query = mysql_query($sql);
 		if ($query === false)
 			return false;
 		return true;
 	}

// ModifyWithAutonumber is the same as Modify but returns the autonumber ID
 	static public function ModifyWithAutonumber($sql)
 	{
 		$query = mysql_query($sql);
 		if ($query === false)
 			return false;
 		return mysql_insert_id();
 	}
 }</pre>
<p>And that concludes the <em>Table</em> class, I hope you enjoyed the ride. Seriously though, it&#8217;s not too complicated and it really does make working with small numbers of rows a lot easier.</p>
<h4>A quick mention of resource usage</h4>
<p>Before we get on to the example subclass I just wanted to mention the resource implications of this class. Clearly this method of accessing a database uses more memory and be a bit more CPU-intensive than simply using the MySQL functions where they are needed. However, for me at least, the benefits far outweigh the costs. And from what I&#8217;ve seen the costs are fairly minimal anyway.</p>
<p>The key thing is to be a bit careful about what the code you&#8217;re writing will actually do. Is it going to retrieve 10,000 rows meaning it will instantiate 10,000 objects? If so then you&#8217;re better off using another method. If, on the other hand, you&#8217;re retrieving a single row that will be stored in the session and may get changed occasionally during its lifetime, this is absolutely worth the minimal cost in efficiency. I&#8217;ll go into an example of this type of usage for the <em>Account</em> class in the next section, and I&#8217;ll explain how it helps with that type of situation.</p>
<p>Something I have tried to avoid is abstracting the database too much. This class is not meant to make it easy to switch between database systems. While it would be relatively trivial to convert it to use MSSQL or PostgreSQL, some work would be needed anywhere a limit clause has been used, or a MySQL-specific feature has been used in some conditions. But it&#8217;s ok since that was not my aim. There are plenty of other database abstraction projects out there, and from my experience each one sucks just as much as the others. But I digress.</p>
<p>I was trying to explain, via a lengthy detour, why you won&#8217;t find Open, Next and Close methods in this class. It&#8217;s not what I was trying to do. If I have a situation where those methods would be needed I&#8217;d prefer to use mysql_(p)connect, mysql_fetch_assoc and mysql_close rather than waste time trying to replace something perfectly adequate for the rare times I&#8217;d need them.</p>
<p>Right, mini-rant over. On to the account class.</p>
<pre name="code" class="php">&lt;?php
 // Model: Account 

// Pull in the table definition
 require_once('table.class.php');

class Account extends Table
 {
 	// The table we're mapping to is called accounts
 	protected $table = 'accounts';
 	// We have a number of fields we want to treat differently
 	// Note that these definitions don't affect how the data is treated, it just changes what is reported
 	// by the GetFieldType method in the Table class - this was added to allow automatic generation of
 	// forms
 	// An array indicates something akin to an enumeration where the actual value used can be defined
 	// differently (look at ACM in AccountType)
 	protected $customfieldtypes = array('Status' =&gt; array('Created', 'Active', 'Expired', 'Deleted'),
 										'Created' =&gt; 'date',
 										'Expires' =&gt; 'date',
 										'AccountType' =&gt; array('Full', 'Demo', 'ACM' =&gt; 'acm'),
 										);

// AfterLoad is called whenever the Table class completes loading of the data
 	// It can be used to...
 	protected function AfterLoad()
 	{
 		// ...enforce consistency...
 		if (isset($this-&gt;data['AccountType']))
 		{
 			// If ACM, the accounttype should be lowercase
 			if (strtolower($this-&gt;data['AccountType']) == 'acm')
 				$this-&gt;data['AccountType'] = 'acm';
 		}

// ...present data to consumers in a different format to that which is stored in the table...
 		if (isset($this-&gt;data['Created']) and $this-&gt;data['Created'] &gt; 0)
 			$this-&gt;data['Created'] = date('Y-m-d', $this-&gt;data['Created']);

if (isset($this-&gt;data['Expires']) and $this-&gt;data['Expires'] &gt; 0)
 			$this-&gt;data['Expires'] = date('Y-m-d', $this-&gt;data['Expires']);

// ...including complex types...
 		if (isset($this-&gt;data['OtherInfo']))
 			$this-&gt;data['OtherInfo'] = unserialize($this-&gt;data['OtherInfo']);

// ...and it can be used to create pseudo fields that do not exist in the table but may exist in other tables
 		$this-&gt;data['projects'] = array();
 	}

// BeforeSave is called by the Table class right before it saves the data back to the table
 	// It has the reverse purpose of AfterLoad, so you can...
 	protected function BeforeSave()
 	{
 		// ...store complex types...
 		if (isset($this-&gt;data['OtherInfo']) and is_array($this-&gt;data['OtherInfo']))
 			$this-&gt;data['OtherInfo'] = serialize($this-&gt;data['OtherInfo']);

// ...convert data to a format suitable for storage in a table...
 		if (isset($this-&gt;data['Expires']) and strlen($this-&gt;data['Expires']) &gt; 0 and $this-&gt;data['Expires'] != 0)
 			$this-&gt;data['Expires'] = strtotime($this-&gt;data['Expires']);

// ...and forcing default values in new rows
 		if ($this-&gt;isnew)
 		{
 			$this-&gt;data['Created'] = time();
 			if (!isset($this-&gt;data['Status'])) $this-&gt;data['Status'] = 'Created';
 		}
 		else
 		{
 			if (isset($this-&gt;data['Created']) and strlen($this-&gt;data['Created']) &gt; 0 and $this-&gt;data['Created'] != 0)
 				$this-&gt;data['Created'] = strtotime($this-&gt;data['Created']);
 		}

// Note the absense of any reference to the projects variable
 		// This is fine since the Save method of the Table class uses its own internal list of fields to decide which parts of
 		// the data to save to the table
 	}

// In addition to overriding methods in the Table class, we can create methods that are specific to this particular table
 	// For example, this method will return true only if the AccountType is set to acm (Account Manager)
 	public function IsACM()
 	{
 		return (isset($this-&gt;data['AccountType']) and $this-&gt;data['AccountType'] == 'acm');
 	}

// Methods can also return related rows
 	// For example, GetACM will return an Account object representing this objects Account Manager
 	public function &amp; GetACM()
 	{
 		$retval = false;
 		if ($this-&gt;data['ACM'] &gt; 0)
 		{
 			$retval = new Account('id = '.$this-&gt;data['ACM']);
 		}
 		return $retval;
 	}

// In true OO tradition, any method in this class should be related to an Account
 	// For example, IsExpired will return true if the account has expired...
 	public function IsExpired()
 	{
 		return (isset($this-&gt;data['Expires']) and $this-&gt;data['Expires'] != 0 and strtotime($this-&gt;data['Expires']) &lt; time());
 	}

// ...MarkDeleted will set the status of the Account and save it back to the database...
 	public function MarkDeleted()
 	{
 		$this-&gt;Status = 'Deleted';
 		// Note that calling Save will cause BeforeSave to be called, the data will then be saved and finally AfterLoad will be called
 		// This means that the member variable named data will be reset, along with any custom fields put in by AfterLoad
 		return $this-&gt;Save();
 	}

// ...performing actions like resetting the account password...
 	public function ResetPassword($password = '')
 	{
 		// If no password was given, generate a random one
 		$pwd = $password;
 		if (strlen($pwd) == 0)
 			$pwd = generatePassword(); // Function source omitted since it's irrelevant

// Save it
 		$this-&gt;Password = $pwd;
 		if (!$this-&gt;Save())
 			return false;

if ($password == '')
 		{
 			// Password was random, email it to the address held in the account
 			if (strlen($this-&gt;data['EmailAddress']) &gt; 0)
 				mail($this-&gt;data['EmailAddress'], 'Password Updated', 'Your new password is: '.$pwd, "From: Support &lt;support@example.com&gt;", '-fsupport@example.com');
 		}

return true;
 	}

// ...getting or counting other records...
 	public function ACM_GetAccounts($countonly = false)
 	{
 		if ($countonly)
 		{
 			return $this-&gt;Count('ACM = '.self::Escape($this-&gt;data['id']));
 		}
 		else
 		{
 			return $this-&gt;FindAll('ACM = '.self::Escape($this-&gt;data['id']), '', 'name asc');
 		}
 	}

// ...ensuring that deletions get propogated to dependent data...
 	public function Destroy()
 	{
 		$accounts = $this-&gt;ACM_GetAccounts();
 		foreach ($accounts as $account)
 		{
 			$result = $account-&gt;Destroy();
 			if ($result !== true)
 				return $result;
 		}

// Call destroy on all projects first - Project is another class derived from Table
 		$tmp = new Project();
 		$projects = $tmp-&gt;FindAll('accountid = '.self::Escape($this-&gt;data['id']));
 		foreach ($projects as $project)
 		{
 			$result = $project-&gt;Destroy();
 			if ($result !== true)
 				return $result;
 		}
 		// Then delete this account
 		$result = $this-&gt;Delete();
 		if ($result !== true)
 			return 'Failed to delete account '.$this-&gt;data['id'];
 		return true;
 	}

// ...other functions omitted for clarity

// You can also define static methods
 	// Account::Current() will get you an account object from the session (see the next method, Login)
 	static public function &amp; Current()
 	{
 		$retval = false;
 		if (isset($_SESSION['account']))
 			$retval = $_SESSION['account'];
 		return $retval;
 	}

// The Login method takes an email address and a password and tries to log the user in
 	// If login is successful it stores the Account object in the session so it can be retrieved by the Current method
 	static public function Login($email, $password)
 	{
 		$account = new Account();
 		if ($account-&gt;FindFirst('EmailAddress = '.Table::Escape($email).' and Password = '.Table::Escape($password)) !== false)
 		{
 			$_SESSION['account'] = &amp;$account;
 			return true;
 		}
 		return false;
 	}

// Static functions can also be used to return multiple rows
 	// This one will return an array of objects containing the Account Manager records
 	static public function &amp; GetACMs()
 	{
 		$account = new Account();
 		return $account-&gt;FindAll('AccountType = '.Table::Escape('acm'), '', 'name asc');
 	}

// The rest is exactly the same for every class that derives from Table - absolutely nothing needs changing from class to class,
 	// but it's vital that these exist - see the next bit of the article for an explanation

// The constructor takes conditions and order and passes them, along with the class name, to the Init method
 	public function __construct($conditions = '', $order = '') { $this-&gt;Init(__CLASS__, $conditions, $order); }
 	// The magic __wakeup method is called when an object is read from serialised data (e.g. the session). It also calls the Init
 	// method but just with the class name
 	public function __wakeup() { $this-&gt;Init(__CLASS__); }
 }</pre>
<p>Ok, so there are a couple of oddities in there that need further explanation, and they both stem from the same problem. The PHP 5 implementation of objects means that inherited methods have an identity crisis. Say you have class A and class B, which extends A. In class A you have a method defined called WhoAmI which returns the name of the class. One possible implementation would use __CLASS__, another possibility would be the get_class function. You may also go as far as to pass $this into get_class. Let&#8217;s try an example&#8230;</p>
<pre name="code" class="php"> class A
 {
 	public function WhoAmI_1()
 	{
 		return __CLASS__;
 	} 

public function WhoAmI_2()
 	{
 		return get_class();
 	}

public function WhoAmI_3()
 	{
 		return get_class(\$this);
 	}
 }

class B extends A
 {
 }

print A::WhoAmI_1();
 print B::WhoAmI_1();

print A::WhoAmI_2();
 print B::WhoAmI_2();</pre>
<p>Now, any sane and reasonable person with a basic knowledge of OOP would expect this to print &#8220;ABAB&#8221;. Yeah, I wish!! What you actually get is &#8220;AAAA&#8221;. Grrrrrrr!!</p>
<p>Now there are good reasons for why PHP does this, and from what I understand fixing it is a big job. I also understand that it&#8217;s been done in PHP 6 but is unlikely to make it into the 5.x branch, which is a shame.</p>
<p>So how do we get around that. Well, you probably noticed the WhoAmI_3 method in the above example that wasn&#8217;t used. Obviously you can&#8217;t use that method statically since it uses <strong>$this</strong>, so let&#8217;s add a few lines to it so we can try it out.</p>
<pre name="code" class="php">$a = new A();
$b = new B();
print $a-&gt;WhoAmI_3();
print $b-&gt;WhoAmI_3();</pre>
<p>Perhaps unsurprisingly this gives us what we need: &#8220;AAAAAB&#8221;. But hang on a second, we had to create an instance of the object to do that. Not good. But unfortunately it&#8217;s the only way around it that I can find.</p>
<p>Hmm, you may be wondering what the heck I&#8217;m going on about. Look back at the source for account.class.php, around line 206 is a good example. This is the GetACMs method which is expected to return an array of Account objects representing the Account Manager rows. Notice how it creates a temporary instance if the Account class, calls FindAll on it and throws it away. Hopefully that&#8217;s clear now.</p>
<p>While this is a major <span class="definition" title="Pain in the Arse">PitA</span> it&#8217;s not the end of the world. And despite several people telling me it&#8217;s a waste of resources and makes any other gains worthless, I still firmly believe that it&#8217;s a very small price to pay for the convenience this system provides. Also, the only workaround I&#8217;ve found that avoids having to create a temporary object is to repeat the code for FindAll in every derived class, and that&#8217;s something I&#8217;m not prepared to do. I&#8217;d rather lose a few microseconds of time and a few bytes of memory than have to do that. It&#8217;s bad enough having to remember to put the __construct and __wakeup lines into each one. Which leads me nicely on to those.</p>
<p>For the same reason that the temporary object is required, the __construct and __wakeup methods are required to be duplicated in each derived class. Daft though it may seem, but if you remove the __construct method from the Account class, the code <strong>new Account($id)</strong> would actually try to create a Table object which would have no way of knowing what table to use. Another <span class="definition" title="Pain in the Arse">PitA</span>, but not so bad as long as you remember to copy those two lines into every class that derives from <em>Table</em>.</p>
<p>Hopefully, with the arrival of PHP 6 I should be able to modify these classes to work the way I had hoped they would.</p>
<div id="crp_related"><h3>If you liked this you might also like these:</h3><ul><li><a href="http://stut.net/2008/07/20/mysql-sessions/" rel="bookmark" class="crp_title">MySQL Sessions</a></li><li><a href="http://stut.net/2008/10/16/snippet-cookie-class-for-php/" rel="bookmark" class="crp_title">Snippet: Cookie class for PHP</a></li><li><a href="http://stut.net/2008/10/28/snippet-simple-templates-with-php/" rel="bookmark" class="crp_title">Snippet: Simple templates with PHP</a></li><li><a href="http://stut.net/2008/10/20/snippet-singletons-with-php/" rel="bookmark" class="crp_title">Snippet: Singletons with PHP</a></li><li><a href="http://stut.net/2008/06/08/where-are-these-backslashes-coming-from/" rel="bookmark" class="crp_title">Where are these backslashes coming from?</a></li></ul></div>
]]></content:encoded>
			<wfw:commentRss>http://stut.net/2008/06/08/php-models-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Where are these backslashes coming from?</title>
		<link>http://stut.net/2008/06/08/where-are-these-backslashes-coming-from/</link>
		<comments>http://stut.net/2008/06/08/where-are-these-backslashes-coming-from/#comments</comments>
		<pubDate>Sun, 08 Jun 2008 20:08:14 +0000</pubDate>
		<dc:creator>Stuart</dc:creator>
				<category><![CDATA[Misc]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://stut.net/blog/?p=218</guid>
		<description><![CDATA[Are you seeing backslashes (\) being inserted before quotes in the data you&#8217;re using? Have you &#8220;solved&#8221; the problem using stripslashes? Do you want to know where these are coming from and how to stop it? Of course you do&#8230; read on! What&#8217;s causing it? There is a configuration option called magic_quotes_gpc that is, for [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_jade" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fstut.net%252F2008%252F06%252F08%252Fwhere-are-these-backslashes-coming-from%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Where%20are%20these%20backslashes%20coming%20from%3F%22%20%7D);"></div>
<p>Are you seeing backslashes (\) being inserted before quotes in the data you&#8217;re using? Have you &#8220;solved&#8221; the problem using <a href="http://php.net/stripslashes">stripslashes</a>? Do you want to know where these are coming from and how to stop it? Of course you do&#8230; read on!<br />
<span id="more-218"></span></p>
<h2>What&#8217;s causing it?</h2>
<p>There is a configuration option called <a href="http://php.net/info#ini.magic-quotes-gpc">magic_quotes_gpc</a> that is, for historic reasons, <em>on</em> by default. It&#8217;s this option that&#8217;s causing the backslashes. It effectively runs the <a href="http://php.net/addslashes">addslashes</a> function on all GET, POST and COOKIE data.</p>
<p>The reason for this is that many years ago this was the recommended way to escape incoming data before sending it to a SQL database. Having it done automatically could be seen to be useful. Personally I hate it &#8211; I&#8217;d rather know what&#8217;s happening to the data I&#8217;m dealing with and not rely on the server being configured in a certain way.</p>
<h2>How do I stop it?</h2>
<p>The simple answer is to turn magic_quotes_gpc off. Unfortunately not everyone has the luxury of being able to do that so the following chunk of code can be placed at the top of any file to check for and undo the addslashes on the GET, POST and COOKIE superglobals. This is pretty-much required to write run-anywhere PHP scripts.</p>
<pre name="code" class="php">if (get_magic_quotes_gpc()) {
  function stripslashes_array($array) {
    return  is_array($array)
           ?
            array_map('stripslashes_array', $array)
           :
            stripslashes($array);
  }  

  $_COOKIE = stripslashes_array($_COOKIE);
  $_FILES = stripslashes_array($_FILES);
  $_GET = stripslashes_array($_GET);
  $_POST = stripslashes_array($_POST);
  $_REQUEST = stripslashes_array($_REQUEST);
}</pre>
<p>Rather than placing this in every file I&#8217;d recommend putting it in a separate file that you include at the top of each file. Alternatively you could use the auto_prepend_file php.ini directive to include it for all scripts.</p>
<div id="crp_related"><h3>If you liked this you might also like these:</h3><ul><li><a href="http://stut.net/2008/10/28/snippet-simple-templates-with-php/" rel="bookmark" class="crp_title">Snippet: Simple templates with PHP</a></li><li><a href="http://stut.net/2008/07/20/mysql-sessions/" rel="bookmark" class="crp_title">MySQL Sessions</a></li><li><a href="http://stut.net/2007/10/05/links-for-2007-10-04/" rel="bookmark" class="crp_title">links for 2007-10-04</a></li><li><a href="http://stut.net/2008/07/26/sessionless-sessions-2/" rel="bookmark" class="crp_title">Sessionless Sessions</a></li><li><a href="http://stut.net/2008/10/16/snippet-cookie-class-for-php/" rel="bookmark" class="crp_title">Snippet: Cookie class for PHP</a></li></ul></div>
]]></content:encoded>
			<wfw:commentRss>http://stut.net/2008/06/08/where-are-these-backslashes-coming-from/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Validating domains and email addresses</title>
		<link>http://stut.net/2008/06/08/validating-domains-and-email-addresses/</link>
		<comments>http://stut.net/2008/06/08/validating-domains-and-email-addresses/#comments</comments>
		<pubDate>Sun, 08 Jun 2008 19:56:35 +0000</pubDate>
		<dc:creator>Stuart</dc:creator>
				<category><![CDATA[Misc]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://stut.net/blog/?p=217</guid>
		<description><![CDATA[This is a very common situation. You&#8217;re taking input from the user, including their email address. You want to make sure that they&#8217;re not feeding you a load of crap, so you want to validate their email address. The best way to do this is with a regular expression, but it&#8217;s not a simple task. Cal Henderson (of [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_jade" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fstut.net%252F2008%252F06%252F08%252Fvalidating-domains-and-email-addresses%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Validating%20domains%20and%20email%20addresses%22%20%7D);"></div>
<p>This is a very common situation. You&#8217;re taking input from the user, including their email address. You want to make sure that they&#8217;re not feeding you a load of crap, so you want to validate their email address. The best way to do this is with a regular expression, but it&#8217;s not a simple task.</p>
<p><a href="http://iamcal.com/">Cal Henderson</a> (of <a href="http://flickr.com/">Flickr</a> fame) wrote an excellent article a little while ago where he wrote a regular expression against the specification document that defines these things. As Cal points out, that specification is RFC822. Now this potentially has its problems because it was written in 1982 and the rules regarding valid characters in domain names have changed since then, but as far as I can tell his solution has then covered.</p>
<p>Check out his article: <a href="http://iamcal.com/publish/articles/php/parsing_email/">http://iamcal.com/publish/articles/php/parsing_email/</a></p>
<p>Hopefully Cal won&#8217;t mind if I reproduce the end result of his work here&#8230;</p>
<pre name="code" class="php">function is_valid_email_address($email)
{
$qtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]';
$dtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]';
$atom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c'.
 	'\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+';
$quoted_pair = '\\x5c[\\x00-\\x7f]';
$domain_literal = "\\x5b($dtext|$quoted_pair)*\\x5d";
$quoted_string = "\\x22($qtext|$quoted_pair)*\\x22";
$domain_ref = $atom;
$sub_domain = "($domain_ref|$domain_literal)";
$word = "($atom|$quoted_string)";
$domain = "$sub_domain(\\x2e$sub_domain)*";
$local_part = "$word(\\x2e$word)*";
$addr_spec = "$local_part\\x40$domain";
return preg_match("!^$addr_spec$!", $email) ? 1 : 0;
}</pre>
<p>For a recent project I needed a function to just validate a domain name, so I extracted the relevant parts and created the following function&#8230;</p>
<pre name="code" class="php">function is_valid_domain($domainname)
{
$dtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]';
$atom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c'.
 	'\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+';
$quoted_pair = '\\x5c[\\x00-\\x7f]';
$domain_literal = "\\x5b($dtext|$quoted_pair)*\\x5d"; 

$domain_ref = $atom;
$sub_domain = "($domain_ref|$domain_literal)";
$domain = "$sub_domain(\\x2e$sub_domain)*";
return preg_match("/^$domain$/i", $domainname) ? true : false;
}</pre>
<div id="crp_related"><h3>If you liked this you might also like these:</h3><ul><li><a href="http://stut.net/2008/10/16/snippet-cookie-class-for-php/" rel="bookmark" class="crp_title">Snippet: Cookie class for PHP</a></li><li><a href="http://stut.net/2008/02/11/outage/" rel="bookmark" class="crp_title">Outage</a></li><li><a href="http://stut.net/2010/07/06/twitter-related-domain-names-for-sale/" rel="bookmark" class="crp_title">Twitter-related domain names for sale</a></li><li><a href="http://stut.net/2008/05/23/has-that-website-gone-titsup/" rel="bookmark" class="crp_title">Has that website gone TitsUp?</a></li><li><a href="http://stut.net/2006/11/05/php-models/" rel="bookmark" class="crp_title">PHP Models</a></li></ul></div>
]]></content:encoded>
			<wfw:commentRss>http://stut.net/2008/06/08/validating-domains-and-email-addresses/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Sessionless Sessions</title>
		<link>http://stut.net/2008/03/11/sessionless-sessions/</link>
		<comments>http://stut.net/2008/03/11/sessionless-sessions/#comments</comments>
		<pubDate>Tue, 11 Mar 2008 12:27:33 +0000</pubDate>
		<dc:creator>Stuart</dc:creator>
				<category><![CDATA[Misc]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[session]]></category>

		<guid isPermaLink="false">http://stut.net/blog/2008/03/11/sessionless-sessions/</guid>
		<description><![CDATA[A little while ago I mentioned in a post on the PHP-General mailing list that I&#8217;d implemented a way to persist data between page requests without requiring server-side storage. This raised a number of questions which I answered without giving too much away. A few weeks later Jochem Maas asked for a more detailed explanation. [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_jade" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fstut.net%252F2008%252F03%252F11%252Fsessionless-sessions%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Sessionless%20Sessions%22%20%7D);"></div>
<p>A little while ago I mentioned in a post on the PHP-General mailing list that I&#8217;d implemented a way to persist data between page requests without requiring server-side storage. This raised a number of questions which I answered without giving too much away.</p>
<p>A few weeks later <a href="http://iamjochem.com/" title="The website of Jochem Maas">Jochem Maas</a> <a href="http://marc.info/?l=php-general&amp;m=120463909026488&amp;w=2" title="maintaining [user] state without a session">asked for a more detailed explanation</a>. I had already started writing an article for this site explaining the details but since it&#8217;s a low priority I hadn&#8217;t finished it yet. I have now.</p>
<p><a href="http://stut.net/articles/sessionless_sessions.html" title="Sessionless Sessions">Sessionless Sessions</a> is a somewhat confusing title but I hope I&#8217;ve explained what I mean clearly within the article As always comments are welcome.</p>
<div id="crp_related"><h3>If you liked this you might also like these:</h3><ul><li><a href="http://stut.net/2006/11/05/php-models/" rel="bookmark" class="crp_title">PHP Models</a></li><li><a href="http://stut.net/2007/08/06/inconsiderate-shared-hosting-customers/" rel="bookmark" class="crp_title">Inconsiderate shared hosting customers</a></li><li><a href="http://stut.net/2006/12/11/compile-delphi-with-speechless/" rel="bookmark" class="crp_title">Compile Delphi with Speechless</a></li><li><a href="http://stut.net/2008/07/26/sessionless-sessions-2/" rel="bookmark" class="crp_title">Sessionless Sessions</a></li><li><a href="http://stut.net/2008/07/20/mysql-sessions/" rel="bookmark" class="crp_title">MySQL Sessions</a></li></ul></div>
]]></content:encoded>
			<wfw:commentRss>http://stut.net/2008/03/11/sessionless-sessions/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
