Martijn's PHP Coding Blog


November 24th, 2009

Using PHP to upload E-mail to Google App Mail - 2

Using PHP to upload Thunderbird to Google Apps Mail

Google Apps has great benefits for small companies. There is little need to maintain your own IT infrastructure, a simple ADSL line attached to a tiny internal network is sufficient for the majority of tasks.

But how do you move an existing company to Google Apps?

A company I have been working with uses Thunderbird for its e-mail.

Moving should have been straightforward as Google provides their own migration tool : the e-mail uploader.

The development of this program however appears stagnant and its bugs are not being fixed. Strange considering how important Google Apps must be for Google.

For me the problem was that 40-50% of the Thunderbird e-mails in the mail store were not being uploaded because the Google Uploader complained that there was something wrong with the date format.

The bug related to this has been open for over a year.

If I wanted to continue I needed to find my own solution or give up on migrating altogether.

A quick browse through the Zend Framework manual showed that it can read MBox files (Thunderbirds mailbox format) through the Zend_Mail_Storage* class and communicate with Google using the Zend_Gdata* classes.

The exercise left was to bind it all together into a working PHP script that would take my mailboxes and upload them to Google.

Having done this and having succesfully uploaded several multi-Gigabyte sized mailboxes I have uploaded my code (’googlemailappuploader’) to Google Code.

If you find yourself in a similar situation — I hope you find it usefull. Leave me a comment if something is unclear.

November 13th, 2009

Installing Eclipse for PHP development with XDebug for remote debugging - 3

Time to debug by henribergius @ flickr

Time to debug by henribergius @ flickr

This post contains my installation notes on how to setup the Eclipse PHP IDE with support for XDebug remote debugging in a mixed Windows/Ubuntu development environment.

Debugging PHP with XDebug in Eclipse

Debugging PHP with XDebug in Eclipse

One of the great things of writing PHP code in a modern IDE is that you can step through your code, set break points and inspect variables. No more endless print/echo statements, or including your own custom logging code every few lines.

The war to decide the best IDE is far from over. If you would like to write PHP code and are looking for a free IDE (Integrated Development Environment) then both Eclipse PHP and the Netbeans IDE are two very strong candidates. Both are written in JAVA. For this post I concentrate on my own favorite: Eclipse. If you would like to compare several other PHP IDE’s then Davey Shafik has a great table comparing IDE features on his site.

Debugging is done through a remote debugger called XDebug. It ties into the PHP runtime on your server allowing Eclipse to communicate with your scripts / web pages. As soon as you start debugging a web page, Eclipse will call your webserver but also pass a couple of parameters. On seeing these parameters (XDEBUG_SESSION_START/KEY), XDebug will start and connect to Eclipse over a TCP/IP connection. From here on Eclipse can control the execution of the script on the webserver.

My development setup Read the rest of this entry »

November 10th, 2009

The Ultimate PHP5 OOP Class - 1

After reading up on PHP5 object orientation I decided to build a little test class that implemented as many of the OOP features offered by PHP5 as I could find.

Think magic functions, constants, cloning, getters and setters, abstract, final, inheritance, serialization and of course var_export support.

If I missed any concepts feel free to drop a note.

<?php

    /* The Ultimate PHP5 OOP Class
     *
     * Implements most of PHP5's OOP functionality in a single demonstration
     */

	/* PHP_VERSION_ID is available as of PHP 5.2.7, if our
	 * version is lower than that, then emulate it. Some of the
	 * functionality we test here is only available in 5.3.0 and
	 * higher.
	*/

	if(!defined('PHP_VERSION_ID'))
	{
    	$version = explode('.',PHP_VERSION);
	    define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2]));
	}

    /* Interfaces specify a set of functions that a class MUST
     * implement.
     */

    interface DemoInterface
    {
        public function InterfaceTest();
    }

    /* Abstract classes cannot be instantiated. They define concepts that
     * their derived classes must implement.
     */

    abstract class DemoAbstract
    {

    	/* Constructor
    	 *
    	 */

        public function __construct()
        {
            $this->LogEntry(__CLASS__,__METHOD__);
        }

        /* LogEntry
         *
         * Reports on each methods entry. Defined in the parent class so that
         * we can call it from this constructor as well.
         *
         * @param string	Methodname (from __METHOD__)
         */

        public function LogEntry($methodName)
        {
        	echo "{$methodName} Called" . PHP_EOL;
        }    	

        abstract public function AbstractTest();
    }

    /* The DemoPHP5Class is marked as "final". Because of this selfish act
     * nobody is able to derive from this work and create their own sub class.
     */

    final class DemoPHP5Class extends DemoAbstract implements DemoInterface
    {
        /* Class Constants cannot be changed */
        const VERSION = '1.0';

        /* Private members are only available inside this class */
        private $_className;
        private $_constructorName;
        private $_fileName;
        private $_instanceCount;

        /*  Location for overloaded data. */
        private $_data = array();

        /* As the class is marked final there really is not point in declaring
         * any protected (accessible by derived classes) variables but
         * for good measure we include one.
         */

        protected $_meaningOfLife;

        /* The constructor is labeled as final. Because of this derived
         * classes are unable to implement their own constructor.
         */

        final function __construct()
        {
        	parent::__construct(); 					// Calling the parent constructor

            $this->_className = __CLASS__;          // Magic constants
            $this->_constructorName = __METHOD__;
            $this->_fileName = __FILE__;
            $this->_instanceCount = 0;              // Original, not a clone
            $this->_meaningOfLife = 42;             // No need to say more

			$this->LogEntry(__CLASS__,__METHOD__);
        }

        final function __destruct()
        {
			$this->LogEntry(__METHOD__);
        }

        /* Implements the Abstract members from the DemoAbstract
         * class.
         *
         * @returns string Notification message
         */

        public function AbstractTest()
        {
			$this->LogEntry(__METHOD__);
        }

        /* Implements the DemoInteface
         *
         * @returns string Notification message
         */

        public function InterfaceTest()
        {
			$this->LogEntry(__METHOD__);
        }

        /* Static functions are not part of an instance
         * and can only be called through "classname::staticmethod"
         */

        public static function StaticTest($number)
        {
        	/* Note how LogEntry is NOT static, but we can still call it */
        	/* A warning is generated in STRICT mode					 */
 			parent::LogEntry(__CLASS__,__METHOD__);
        	echo "StaticTest call with ({$number}) succesfull" . PHP_EOL;
        }

        /* Returns the name of the class
         *
         * @returns string Name of the class as discovered by the constructor
         */

        public function GetClassName()
        {
        	$this->LogEntry(__METHOD__);
            return $this->_className;
        }

        /* Set the meaning of life
         *
         * @param   int         Meaning of life
         * @thows   Exception   On invalid input
         * @returns this        Allow for chaining of calls
         */

        public function SetMeaningOfLife($meaningOfLife)
        {
			$this->LogEntry(__METHOD__);

            if (is_int($meaningOfLife))
            {
                $this->_meaningOfLife = $meaningOfLife;
            }
            else
            {
                throw new Exception('Meaning of Life doubted');
            }
            return $this;
        }

        /* Returns the meaning of life
         *
         * @returns int      Meaning of life as a core value
         */

        public function GetMeaningOfLife()
        {
			$this->LogEntry(__METHOD__);
            return $this->_meaningOfLife;
        }

        /* Returns the instance count
         *
         * @returns int 	Instance count
         */

        public function GetInstanceCount()
        {
        	return $this->_instanceCount;
        }

        /* Magic Methods
         *
         * These methods have special meaning inside of PHP5.
         */

        /* Return a string description of the class
         *
         * @returns string  Whatever the class fancies. Usually a description
         */

        public function __toString()
        {
			$this->LogEntry(__METHOD__);
            return '__toString called ; the meaning of life is our domain' . PHP_EOL;
        }

        /* From PHP 5.3.0 its possible to "invoke" a class, using it like
         * a function. Eg. 'DemoPHP5Class(50)'.
         */

        public function __invoke($x)
        {
			$this->LogEntry(__METHOD__);
            echo "__invoke was called with {$x}" . PHP_EOL;
        }

        /* PHP 5 allows the simulation of properties. If a property does not
         * exist when attempting to write the __set method is called giving
         * you an opportunity to handle the variable yourself.
         */

        /* Called when a not-class defined property is set by the user. For
         * example "DemoPHP5Class->FooBar = 500". We add an entry to the
         * $this->_data table.
         *
         * @param   string      A valid name
         * @param   undefined   Any valid PHP value
         * @throws  Exception   Only strings are considered valid names
         */

        public function __set($name,$value)
        {
			$this->LogEntry(__METHOD__);
            if (is_string($name)) {
             $this->_data[$name] = $value;
             return;
            }
            throw new Exception('Invalid Property name');
        }

        /* Called when a not class defined property is retrieved. We
         * attempt to satisfy the called by checking $this->_array.
         *
         * @param    string    A valid name
         * @throws   Exception When the property is not defined
         * @returns  undefined Value of the named property
         */

        public function __get($name)
        {
			$this->LogEntry(__METHOD__);
        	if (is_string($name) &amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp; array_key_exists($name, $this->_data))
                return $this->_data[$name];
            throw new Exception("Attempted to read undefined proporty");
        }

        /* This method is called whenever we try to access an inaccessible
         * property. We check $this->_data to see if a matching key exists.
         *
         * @param   string   A valid string property name
         * @returns bool     True on finding the property in the array
         */

        public function __isset($name)
        {
			$this->LogEntry(__METHOD__);
        	if (is_string($name) &amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp; array_key_exists($name, $this->_data))
                return true;
        }

        /* This method is called when unset is called on an inaccessible
         * class property. We check $this->_data and try to unset it.
         *
         * @param   string      A valid string property name
         * @throws  Exception   The property must exist, mmmm, ok?
         */

        public function __unset($name)
        {
		   $this->LogEntry(__METHOD__);
           if (is_string($name) &amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp; array_key_exists($name, $this->_data))
            unset($this->_data[$name]);
           else
            throw new Exception("Unsetting non-existant property");
        }

        /* When a class is serialized (eg. put away into cold storage)
         * it is checked for a __sleep method. If it exists it is called
         * to allow the class to prepare a list with names of its most important
         * variables for safe storage.
         *
         * @returns array Critical variables that need to be stored
         */

        public function __sleep()
        {
			$this->LogEntry(__METHOD__);
            return array("_meaningOfLife","_className","_constructorName",
                         "_fileName","_data");
        }

        /* On deserialization the class needs to re-initialise.
         * This is a good point to re-establish the database connection and
         * any other critical (but not storable) resources.
         */

        public function __wakeup()
        {
			$this->LogEntry(__METHOD__);
        }

        /* When an object is "cloned" an exact copy is created. This is not
         * always desirable (think resource handles that are now shared by two
         * objects). The __clone magic function is called on the COPY and
         * allows it to initialise any variables it sees fit.
         */

        public function __clone()
        {
			$this->LogEntry(__METHOD__);
        	// Clones know they are copies because only the original
            // object has an _instanceCount value of 0.
            $this->_instanceCount++;
        }

        /* Used by var_export which allows the creation of a PHP evaluable
         * string that would re-create this object.
         *
         * See also: http://www.thoughtlabs.com/2008/02/02/phps-mystical-__set_state-method/
         */

        public static function __set_state($an_array)
        {
        	echo "static::__set_state called" . PHP_EOL;			

			// Create a new class, set its parameters and
			// exit. 

			$tmp = new DemoPHP5Class();
			foreach($an_array as $name => $value)
			 $tmp->$name = $value;

			return $tmp;
        }

    }

    /* Enable strict warnings */
    error_reporting(E_ALL | E_STRICT);

    /* Test Code */
    $demo = new DemoPHP5Class();

    /* Example of a class constant */
    echo "Class version: " . DemoPHP5Class::VERSION . PHP_EOL;  

    /* Test #1:  Abstract method in parent class, simply call it*/
	$demo->AbstractTest();

	/* Test #2:  Call the Interface member */
	$demo->InterfaceTest();

	/* Test #3:  Call of a static member */
	DemoPHP5Class::StaticTest(20);

	/* Test #4:  Call a regular get method  */
	$demo->GetClassName();

	/* Test #5:  Call a regular set method with valid param */
	$demo->SetMeaningOfLife(43);

	/* Test #6:  Call a regular set method with INVALID param */
	try {
		$demo->SetMeaningOfLife(42.5);
	}
	catch (Exception $e) {
		echo "Exception Caught OK" . PHP_EOL;
	}

	/* Test #7: Try to print the object , calling __tostring() */
	echo $demo;

	/* Test #8: Invoke the object , bit like magic really */
	/* 			only available from PHP 5.3.0 up	      */
	if (PHP_VERSION_ID > 50300)
	 $demo('abacradaba');

	/* Test #9: Set an non-existant member of the object, causing
	 * a call to __set, which will emulate/store the value
	 */		 

	$demo->non_existing_member = 256;

	/* Test #10: Confirm that the new member has been set */

	if (isset($demo->non_existing_member))
	 echo 'Confirmed existance of \$demo->non_existing_member' . PHP_EOL;
	else
	 echo 'Failed to verify setting of \$demo->non_existing_member' . PHP_EOL;

	/* Test #11: Retrieve the non-existing member, causing a call
	 * to __get, which will attempt to retrieve it from the internal
	 * storage array.
	 */

	if ($demo->non_existing_member == 256)
		echo "Retrieval of \$demo->non_existing_member succesfull" . PHP_EOL;
	else
		echo "Retrieval of \$demo->non_existing_member FAILED" . PHP_EOL;

	/* Test #12: Remove the member, and test if this was succesfull */

	unset($demo->non_existing_member);
	if (!isset($demo->non_existing_member))
	 	echo 'Confirmed removal of \$demo->non_existing_member' . PHP_EOL;
	else
	 	echo 'Failed to remove \$demo->non_existing_member' . PHP_EOL;

	/* Test #13: Serialization , put the object to sleep and turn it
	 * into a form that we can store away savely.
	 */

	$serialized_data = serialize( $demo );
	$demo2 = unserialize($serialized_data);  

	/* Test #14: Cloning. After cloning the object the clone should
	 * have a higher instance value than the original object because
	 * of the additional changes made by __clone
	 */

	$demo3 = clone $demo;
	if ($demo3->GetInstanceCount() > $demo->GetInstanceCount())
	 	echo "Succesfull cloning; instance count has been increased. " . PHP_EOL;
	else
	 	echo "Failed to clone; instance count is the same. " . PHP_EOL;

	/* Test 15: Var_Export must produce valid PHP code that can be
	 * run through 'eval'. It has trouble with objects , so it needs
	 * a little help from the __set_state magic function
	 */

	eval('$demo4 = ' . var_export($demo, true) . ';');

	/* Remember, we modified the meaning of life to 43 in $demo */
	if ($demo4->GetMeaningOfLife()==$demo->GetMeaningOfLife())
	 echo "Succesfull re-creation of the \$demo class" . PHP_EOL;
	else
	 echo "Failed to recreate. The meaning of life was lost. " . PHP_EOL; 

	/* EOF */

?>

November 9th, 2009

PHP5 : SimpleXML Study Notes - 2

XML Summer School by psd @ flickr

XML Summer School by psd @ flickr

Below are some of the notes I made while preparing for the Zend PHP5 exam. They are a quick memory jog of how SimpleXML works and its limitations.

The SimpleXML extension provides easier access to XML files. It is simpler to use but also much more limited than the DOM XML extension. If your document makes use of XML namespaces (ns:foo) then it is better NOT to use SimpleXML as it will become anything but simple.

SimpleXML only supports XML 1.0. If fed an XML 1.1 document it will show “Unsupported version ‘1.1′” warnings, but will continue and attempt to read the file.

Removing elements is not really supported and the opportunities for modification are limited. SimpleXML is best used for straightforward reading and using of XML files. Read the rest of this entry »