List Info

Thread: Zend_Factory/Zend_Loader addition suggestion




Zend_Factory/Zend_Loader addition suggestion
user name
2008-01-17 15:20:46
Hi guys,

I'm currently working on a project to implement Zend Framework controller and model testing and specification. I'm looking for opinions on whether this is feasible - the main problem I have is that directly instantiating objects within controllers makes it impossible for any testing suite from isolating the controller action from it's carrying objects. Without requiring some perverse parameter acrobatics or requiring configuration heavy injectors like Pico or Phemto a simple solution (I think) is to enable a simplified object loader of a few dozen lines to be added for use.

This could be added either as a separate class Zend_Factory, or appended to Zend_Loader (which seems appropriate). I've copied the current sample code and unit tests below. In use, it would require a simple call of:

Zend_Factory::create('Zend_Mail');

Then a unit test or BDD spec could mock this object result (before the create() call of course) using:

Zend_Factory::replaceClass('Zend_Mail', new ZMail_Mock);

Here's the suggested source code and unit tests. Any comments or criticisms most welcome - not sure if it's worth an actual proposal given it's size but it's of immense value to my ongoing work. If you prefer online viewing it's at: http://svn.astrumfutura.org/zendframework/trunk/library/Proposed/Zend/Factory.php

Zend/Factory.php:

<?php

require_once 'Zend/Loader.php';

require_once 'Zend/Registry.php';

/**
 * Abstract object instantiation with minimal setup and allow for replacement
 * of created object return values by preceding unit tests or BDD specifications
 *
 */
class Zend_Factory
{

 ; &nbsp; /**
   ;  * Registry instance for holding replacement objects
&nbsp; &nbsp;  *
 &nbsp; &nbsp; * var Zend_Registry
 &nbsp;   */
 &nbsp;  protected static $_registry = null;

&nbsp; &nbsp; /**
   ;  * Create a new object based on the referenced class name and construction
 &nbsp; &nbsp; * parameters. If a registered replacement object exists, this will be
 &nbsp;   * returned instead.
&nbsp;   ; *
 &nbsp; &nbsp; * param object $className
 &nbsp; &nbsp; * param array $constructionParams
&nbsp;   ; * return object
&nbsp; &nbsp;  */
 &nbsp;  public static function create($className, array $constructionParams = null)
 &nbsp;  {
 &nbsp; &nbsp;   ; if (!class_exists($className, true)) {
 &nbsp; &nbsp;   ; &nbsp; &nbsp; Zend_Loader::loadClass($className);
 &nbsp;   ; &nbsp; }
 &nbsp; &nbsp;   ; if (isset(self::$_registry->$className)) {
 &nbsp; &nbsp;   ; &nbsp; &nbsp; return self::$_registry->$className;
 &nbsp; &nbsp; &nbsp;  }
 &nbsp; &nbsp;   ; if ($constructionParams !== null) {
 &nbsp; &nbsp;   ; &nbsp; &nbsp; $refClass = new ReflectionClass($className);
&nbsp; &nbsp; &nbsp;   ; &nbsp;  $createdObject = $refClass->newInstanceArgs($constructionParams);
&nbsp; &nbsp;   ;  } else {
 &nbsp; &nbsp;   ; &nbsp; &nbsp; $createdObject = new $className;
 &nbsp; &nbsp; &nbsp;  }
 &nbsp; &nbsp;   ; return $createdObject;
 ; &nbsp; }

 &nbsp;  /**
   ;  * Replace the return value of any call for an instance of the referenced
 &nbsp; &nbsp; * class name with an alternative object.
&nbsp; &nbsp;  *
 &nbsp; &nbsp; * param string $className
 &nbsp; &nbsp; * param object $withObject
 &nbsp; &nbsp; */
 &nbsp;  public static function replaceClass($className, $withObject)
 &nbsp;  {
 &nbsp; &nbsp;   ; if (self::$_registry == null) {
 &nbsp; &nbsp;   ; &nbsp; &nbsp; self::$_registry = new Zend_Registry(array(), ArrayObject::ARRAY_AS_PROPS);
&nbsp; &nbsp;   ;  }
 &nbsp; &nbsp;   ; self::$_registry->$className = $withObject;
 &nbsp;  }
 &nbsp; 
 &nbsp;  /**
   ;  * Clear the registry of replacement objects
&nbsp; &nbsp;  *
 &nbsp; &nbsp; */
 &nbsp;  public static function clearRegistry()
 ; &nbsp; {
 &nbsp; &nbsp;   ; if(isset(self::$_registry)) {
 &nbsp; &nbsp;   ; &nbsp; &nbsp; self::$_registry = null;
&nbsp; &nbsp; &nbsp; &nbsp; }
 &nbsp;  }
}

Unit Tests: FactoryTest.php

<?php
/**
&nbsp;* package&nbsp; &nbsp; Zend_Factory
 * subpackage UnitTests
 */

require_once dirname(dirname(__FILE__)) . '/TestHelper.php';


/** Zend_Factory */
require_once 'Zend/Factory.php';


/** PHPUnit_Framework_TestCase */
require_once 'PHPUnit/Framework/TestCase.php';

// pre included class used in tests
require_once 'Zend/Mail.php';


/**
 * package&nbsp; &nbsp; Zend_Factory
 * subpackage UnitTests
 */
class FactoryTest extends PHPUnit_Framework_TestCase
{

&nbsp; &nbsp; public function testFactoryInstantiatesZendObjectAfterIncluded()
 &nbsp;  {
 &nbsp; &nbsp;   ; $mail = Zend_Factory::create('Zend_Mail');
 &nbsp; &nbsp;   ; $this->assertTrue($mail instanceof Zend_Mail);
 &nbsp;  }

 &nbsp;  public function testFactoryInstantiatesZendObjectBeforeIncluded()
 &nbsp;  {
 &nbsp; &nbsp;   ; $gdata = Zend_Factory::create('Zend_Gdata_Photos_AlbumEntry');
  ; &nbsp; &nbsp;  $this->assertTrue($gdata instanceof Zend_Gdata_Photos_AlbumEntry);
&nbsp;   }

 &nbsp;  public function testFactoryThrowsExceptionIfClassDoesNotExist()
 &nbsp;  {
 &nbsp; &nbsp;   ; try {
 &nbsp; &nbsp;   ; &nbsp;  $mail = Zend_Factory::create('Zend_Foo');
 &nbsp; &nbsp; &nbsp;   ;  $this->fail('Did not throw an expected Exception on non-existent class request');
 &nbsp; &nbsp; &nbsp;  } catch (Zend_Exception $e) {
 &nbsp; &nbsp;   ; }
 &nbsp;  }

 &nbsp;  public function testFactoryInstantiatesUsingConstructParams()
 &nbsp;  {
 &nbsp; &nbsp;   ; $registry = Zend_Factory::create('Zend_Registry', array(array('index'=&gt;'something')));
&nbsp; &nbsp; &nbsp;   $this->assertEquals('something', $registry['index']);
  ;  }

 &nbsp;  public function testFactoryAllowsObjectReplacementForClasses()
 &nbsp;  {
 &nbsp; &nbsp;   ; Zend_Factory::replaceClass('Zend_Registry', new Factory_Foo);
 &nbsp;   ; &nbsp; $class = Zend_Factory::create('Zend_Registry');
&nbsp; &nbsp; &nbsp; &nbsp; $this->assertTrue($class instanceof Factory_Foo);
 &nbsp;  }

 &nbsp;  public function testFactoryChecksOriginalClassExistenceToPreventBlindReplacement()
 &nbsp;  {
 &nbsp; &nbsp;   ; Zend_Factory::replaceClass('Zend_Foo', new Factory_Foo);
 &nbsp;   ; &nbsp; try {
 &nbsp; &nbsp;   ; &nbsp; &nbsp; $class = Zend_Factory::create('Zend_Foo');
 &nbsp; &nbsp; &nbsp;   ; &nbsp; $this->fail('Expected exception since replaced class never existed which means replacement could create blind errors');
  ; &nbsp; &nbsp;  } catch (Zend_Exception $e) {
 &nbsp; &nbsp;   ; }
 &nbsp;  }
 &nbsp; 
 &nbsp;  public function tearDown()
 &nbsp;  {
 &nbsp; &nbsp;   Zend_Factory::clearRegistry();
 &nbsp;  }

}

class Factory_Foo
{
}

Best regards,
Paddy
 ;
Pádraic Brady

http://blog.astrumfutura.com
http://www.patternsforphp.com
OpenID Europe Foundation Member-Subscriber

Re: Zend_Factory/Zend_Loader addition suggestion
user name
2008-01-17 17:17:08
Have you seen my proposal, Zend_Di? I needed something similar and I developed that component. I've&nbsp;using it for quite some time now. The problem with Pico and Phemto is that you need to adapt your API to their ;injectors, this doesn't happen with Zend_Di, it adapts to your API. 
 
The problem with Pico is that it's way to heavy, and Phemto is way to simple and only supports CI.
 
Check out the proposal:
 
The code you posted is similar to the one found in Zend_Di_Component_Factory
 
Cheers,
Fede
&nbsp;
----- Original Message -----
Sent: Thursday, January 17, 2008 9:20 PM
Subject: [fw-general] Zend_Factory/Zend_Loader addition suggestion

Hi guys,

I'm currently working on a project to implement Zend Framework controller and model testing and specification. I'm looking for opinions on whether this is feasible - the main problem I have is that directly instantiating objects within controllers makes it impossible for any testing suite from isolating the controller action from it's carrying objects. Without requiring some perverse parameter acrobatics or requiring configuration heavy injectors like Pico or Phemto a simple solution (I think) is to enable a simplified object loader of a few dozen lines to be added for use.

This could be added either as a separate class Zend_Factory, or appended to Zend_Loader (which seems appropriate). I've copied the current sample code and unit tests below. In use, it would require a simple call of:

Zend_Factory::create('Zend_Mail');

Then a unit test or BDD spec could mock this object result (before the create() call of course) using:

Zend_Factory::replaceClass('Zend_Mail', new ZMail_Mock);

Here's the suggested source code and unit tests. Any comments or criticisms most welcome - not sure if it's worth an actual proposal given it's size but it's of immense value to my ongoing work. If you prefer online viewing it's at: http://svn.astrumfutura.org/zendframework/trunk/library/Proposed/Zend/Factory.php

Zend/Factory.php:

<?php

require_once 'Zend/Loader.php';

require_once 'Zend/Registry.php';

/**
 * Abstract object instantiation with minimal setup and allow for replacement
 * of created object return values by preceding unit tests or BDD specifications
 *
 */
class Zend_Factory
{

 ; &nbsp; /**
   ;  * Registry instance for holding replacement objects
&nbsp; &nbsp;  *
 &nbsp; &nbsp; * var Zend_Registry
 &nbsp;   */
 &nbsp;  protected static $_registry = null;

&nbsp; &nbsp; /**
   ;  * Create a new object based on the referenced class name and construction
 &nbsp; &nbsp; * parameters. If a registered replacement object exists, this will be
 &nbsp;   * returned instead.
&nbsp;   ; *
 &nbsp; &nbsp; * param object $className
 &nbsp; &nbsp; * param array $constructionParams
&nbsp;   ; * return object
&nbsp; &nbsp;  */
 &nbsp;  public static function create($className, array $constructionParams = null)
 &nbsp;  {
 &nbsp; &nbsp;   ; if (!class_exists($className, true)) {
 &nbsp; &nbsp;   ; &nbsp; &nbsp; Zend_Loader::loadClass($className);
 &nbsp;   ; &nbsp; }
 &nbsp; &nbsp;   ; if (isset(self::$_registry->$className)) {
 &nbsp; &nbsp;   ; &nbsp; &nbsp; return self::$_registry->$className;
 &nbsp; &nbsp; &nbsp;  }
 &nbsp; &nbsp;   ; if ($constructionParams !== null) {
 &nbsp; &nbsp;   ; &nbsp; &nbsp; $refClass = new ReflectionClass($className);
&nbsp; &nbsp; &nbsp;   ; &nbsp;  $createdObject = $refClass->newInstanceArgs($constructionParams);
&nbsp; &nbsp;   ;  } else {
 &nbsp; &nbsp;   ; &nbsp; &nbsp; $createdObject = new $className;
 &nbsp; &nbsp; &nbsp;  }
 &nbsp; &nbsp;   ; return $createdObject;
 ; &nbsp; }

 &nbsp;  /**
   ;  * Replace the return value of any call for an instance of the referenced
 &nbsp; &nbsp; * class name with an alternative object.
&nbsp; &nbsp;  *
 &nbsp; &nbsp; * param string $className
 &nbsp; &nbsp; * param object $withObject
 &nbsp; &nbsp; */
 &nbsp;  public static function replaceClass($className, $withObject)
 &nbsp;  {
 &nbsp; &nbsp;   ; if (self::$_registry == null) {
 &nbsp; &nbsp;   ; &nbsp; &nbsp; self::$_registry = new Zend_Registry(array(), ArrayObject::ARRAY_AS_PROPS);
&nbsp; &nbsp;   ;  }
 &nbsp; &nbsp;   ; self::$_registry->$className = $withObject;
 &nbsp;  }
 &nbsp; 
 &nbsp;  /**
   ;  * Clear the registry of replacement objects
&nbsp; &nbsp;  *
 &nbsp; &nbsp; */
 &nbsp;  public static function clearRegistry()
 ; &nbsp; {
 &nbsp; &nbsp;   ; if(isset(self::$_registry)) {
 &nbsp; &nbsp;   ; &nbsp; &nbsp; self::$_registry = null;
&nbsp; &nbsp; &nbsp; &nbsp; }
 &nbsp;  }
}

Unit Tests: FactoryTest.php

<?php
/**
&nbsp;* package&nbsp; &nbsp; Zend_Factory
 * subpackage UnitTests
 */

require_once dirname(dirname(__FILE__)) . '/TestHelper.php';


/** Zend_Factory */
require_once 'Zend/Factory.php';


/** PHPUnit_Framework_TestCase */
require_once 'PHPUnit/Framework/TestCase.php';

// pre included class used in tests
require_once 'Zend/Mail.php';


/**
 * package&nbsp; &nbsp; Zend_Factory
 * subpackage UnitTests
 */
class FactoryTest extends PHPUnit_Framework_TestCase
{

&nbsp; &nbsp; public function testFactoryInstantiatesZendObjectAfterIncluded()
 &nbsp;  {
 &nbsp; &nbsp;   ; $mail = Zend_Factory::create('Zend_Mail');
 &nbsp; &nbsp;   ; $this->assertTrue($mail instanceof Zend_Mail);
 &nbsp;  }

 &nbsp;  public function testFactoryInstantiatesZendObjectBeforeIncluded()
 &nbsp;  {
 &nbsp; &nbsp;   ; $gdata = Zend_Factory::create('Zend_Gdata_Photos_AlbumEntry');
  ; &nbsp; &nbsp;  $this->assertTrue($gdata instanceof Zend_Gdata_Photos_AlbumEntry);
&nbsp;   }

 &nbsp;  public function testFactoryThrowsExceptionIfClassDoesNotExist()
 &nbsp;  {
 &nbsp; &nbsp;   ; try {
 &nbsp; &nbsp;   ; &nbsp;  $mail = Zend_Factory::create('Zend_Foo');
 &nbsp; &nbsp; &nbsp;   ;  $this->fail('Did not throw an expected Exception on non-existent class request');
 &nbsp; &nbsp; &nbsp;  } catch (Zend_Exception $e) {
 &nbsp; &nbsp;   ; }
 &nbsp;  }

 &nbsp;  public function testFactoryInstantiatesUsingConstructParams()
 &nbsp;  {
 &nbsp; &nbsp;   ; $registry = Zend_Factory::create('Zend_Registry', array(array('index'=&gt;'something')));
&nbsp; &nbsp; &nbsp;   $this->assertEquals('something', $registry['index']);
  ;  }

 &nbsp;  public function testFactoryAllowsObjectReplacementForClasses()
 &nbsp;  {
 &nbsp; &nbsp;   ; Zend_Factory::replaceClass('Zend_Registry', new Factory_Foo);
 &nbsp;   ; &nbsp; $class = Zend_Factory::create('Zend_Registry');
&nbsp; &nbsp; &nbsp; &nbsp; $this->assertTrue($class instanceof Factory_Foo);
 &nbsp;  }

 &nbsp;  public function testFactoryChecksOriginalClassExistenceToPreventBlindReplacement()
 &nbsp;  {
 &nbsp; &nbsp;   ; Zend_Factory::replaceClass('Zend_Foo', new Factory_Foo);
 &nbsp;   ; &nbsp; try {
 &nbsp; &nbsp;   ; &nbsp; &nbsp; $class = Zend_Factory::create('Zend_Foo');
 &nbsp; &nbsp; &nbsp;   ; &nbsp; $this->fail('Expected exception since replaced class never existed which means replacement could create blind errors');
  ; &nbsp; &nbsp;  } catch (Zend_Exception $e) {
 &nbsp; &nbsp;   ; }
 &nbsp;  }
 &nbsp; 
 &nbsp;  public function tearDown()
 &nbsp;  {
 &nbsp; &nbsp;   Zend_Factory::clearRegistry();
 &nbsp;  }

}

class Factory_Foo
{
}

Best regards,
Paddy
 
Pádraic Brady

http://blog.astrumfutura.com
http://www.patternsforphp.com
OpenID Europe Foundation Member-Subscriber

AW: Zend_Factory/Zend_Loader addition suggestion
user name
2008-01-21 10:21:06
Sounds cool! This would make testing in ZF much easier.

In my opinion the Zend_Factory should somehow be integrated
into Zend_Loader, to integrate easy in auto loaded
environments. Probably Zend_Factory can even be a solution
to replace components in the system without subclassing and
copying a lot of code. But for this it should be possible to
replace classes not only by object but also by other names.

Cheers,
leo
________________________________________
Von: Pádraic Brady [mailto:padraic.bradyyahoo.com]
Gesendet: Donnerstag, 17. Januar 2008 22:21
An: Zend Framework General
Betreff: [fw-general] Zend_Factory/Zend_Loader addition
suggestion

Hi guys,

I'm currently working on a project to implement Zend
Framework controller and model testing and specification.
I'm looking for opinions on whether this is feasible - the
main problem I have is that directly instantiating objects
within controllers makes it impossible for any testing suite
from isolating the controller action from it's carrying
objects. Without requiring some perverse parameter
acrobatics or requiring configuration heavy injectors like
Pico or Phemto a simple solution (I think) is to enable a
simplified object loader of a few dozen lines to be added
for use.

This could be added either as a separate class Zend_Factory,
or appended to Zend_Loader (which seems appropriate). I've
copied the current sample code and unit tests below. In use,
it would require a simple call of:

Zend_Factory::create('Zend_Mail');

Then a unit test or BDD spec could mock this object result
(before the create() call of course) using:

Zend_Factory::replaceClass('Zend_Mail', new ZMail_Mock);

Here's the suggested source code and unit tests. Any
comments or criticisms most welcome - not sure if it's worth
an actual proposal given it's size but it's of immense value
to my ongoing work. If you prefer online viewing it's at: http://svn.astrumfutura.org/zen
dframework/trunk/library/Proposed/Zend/Factory.php

Zend/Factory.php:

<?php

require_once 'Zend/Loader.php';

require_once 'Zend/Registry.php';

/**
 * Abstract object instantiation with minimal setup and
allow for replacement
 * of created object return values by preceding unit tests
or BDD specifications
 *
 */
class Zend_Factory
{

    /**
     * Registry instance for holding replacement objects
     *
     * var Zend_Registry
     */
    protected static $_registry = null;

    /**
     * Create a new object based on the referenced class
name and construction
     * parameters. If a registered replacement object
exists, this will be
     * returned instead.
     *
     * param object $className
     * param array $constructionParams
     * return object
     */
    public static function create($className, array
$constructionParams = null)
    {
        if (!class_exists($className, true)) {
            Zend_Loader::loadClass($className);
        }
        if (isset(self::$_registry->$className)) {
            return self::$_registry->$className;
        }
        if ($constructionParams !== null) {
            $refClass = new ReflectionClass($className);
            $createdObject =
$refClass->newInstanceArgs($constructionParams);
        } else {
            $createdObject = new $className;
        }
        return $createdObject;
    }

    /**
     * Replace the return value of any call for an instance
of the referenced
     * class name with an alternative object.
     *
     * param string $className
     * param object $withObject
     */
    public static function replaceClass($className,
$withObject)
    {
        if (self::$_registry == null) {
            self::$_registry = new Zend_Registry(array(),
ArrayObject::ARRAY_AS_PROPS);
        }
        self::$_registry->$className = $withObject;
    }

    /**
     * Clear the registry of replacement objects
     *
     */
    public static function clearRegistry()
    {
        if(isset(self::$_registry)) {
            self::$_registry = null;
        }
    }
}

Unit Tests: FactoryTest.php

<?php
/**
 * package    Zend_Factory
 * subpackage UnitTests
 */

require_once dirname(dirname(__FILE__)) .
'/TestHelper.php';


/** Zend_Factory */
require_once 'Zend/Factory.php';


/** PHPUnit_Framework_TestCase */
require_once 'PHPUnit/Framework/TestCase.php';

// pre included class used in tests
require_once 'Zend/Mail.php';


/**
 * package    Zend_Factory
 * subpackage UnitTests
 */
class FactoryTest extends PHPUnit_Framework_TestCase
{

    public function
testFactoryInstantiatesZendObjectAfterIncluded()
    {
        $mail = Zend_Factory::create('Zend_Mail');
        $this->assertTrue($mail instanceof Zend_Mail);
    }

    public function
testFactoryInstantiatesZendObjectBeforeIncluded()
    {
        $gdata =
Zend_Factory::create('Zend_Gdata_Photos_AlbumEntry');
        $this->assertTrue($gdata instanceof
Zend_Gdata_Photos_AlbumEntry);
    }

    public function
testFactoryThrowsExceptionIfClassDoesNotExist()
    {
        try {
           $mail = Zend_Factory::create('Zend_Foo');
           $this->fail('Did not throw an expected
Exception on non-existent class request');
        } catch (Zend_Exception $e) {
        }
    }

    public function
testFactoryInstantiatesUsingConstructParams()
    {
        $registry = Zend_Factory::create('Zend_Registry',
array(array('index'=>'something')));
        $this->assertEquals('something',
$registry['index']);
    }

    public function
testFactoryAllowsObjectReplacementForClasses()
    {
        Zend_Factory::replaceClass('Zend_Registry', new
Factory_Foo);
        $class = Zend_Factory::create('Zend_Registry');
        $this->assertTrue($class instanceof
Factory_Foo);
    }

    public function
testFactoryChecksOriginalClassExistenceToPreventBlindReplace
ment()
    {
        Zend_Factory::replaceClass('Zend_Foo', new
Factory_Foo);
        try {
            $class = Zend_Factory::create('Zend_Foo');
            $this->fail('Expected exception since
replaced class never existed which means replacement could
create blind errors');
        } catch (Zend_Exception $e) {
        }
    }

    public function tearDown()
    {
       Zend_Factory::clearRegistry();
    }

}

class Factory_Foo
{
}

Best regards,
Paddy

Pádraic Brady

http://blog.astrumfutura
.com
http://www.patternsforp
hp.com
OpenID Europe Foundation Member-Subscriber


[1-3]

about | contact  Other archives ( Real Estate discussion Medical topics )