Hi Mark,

This would be a nice idea for a generalized component that would apply to
all classes, but unfortunately you can't do it.

For example, this doesn't work:

$object = new Zend_Delegate::getClassName('Zend_Example');

It has to be in two steps:

$class  = Zend_Delegate::getClassName('Zend_Example');
$object = new $class;

And it's downright impossible for static classes unless you use
call_user_func() or call_user_func_array():

$class = Zend_Delegate::getClassName('Zend_Example');
call_user_func(array($class, 'method')); // Works
$class::method(); // Syntax error

Furthermore, because PHP doesn't have late static binding (in 5.2.1, at
least), you can't say "self::$property = $xyz".  You must use accessors
for all properties and have to go through call_user_func[_array]() to
ensure that the proper classes are being used.

In short, it would be great to have, but limitations in PHP itself prevent
it from being anything close to easy or efficient.

-Matt

On Wed, March 21, 2007 7:35 am, Mark Gibson wrote:
> As I mentioned in the response to 'Zend_Db_Select::distinct', I have
> an idea, here it is:
>
> A common pattern for classes that instantiate objects of another class,
> and wish to allow to user to declare an alternative implementation
> or subclass for the instantiated object.
>
> This provides a consistent interface for setting alternative classes,
> and a possibly a single implementation, reducing overall class sizes.
>
> I've demonstrated this using the Zend_Db_Table_Abstract class, as
> at present it allow you to select an alternative class for rows
> and rowsets. This pattern could be applied to any part of the
> Zend Framework that requires this behaviour.
>
> Ultimately this functionality could be factored into a base class
> and/or helper class.
>
> Example:
>
> class Zend_Db_Table_Abstract
> {
>      const ROW_CLASS = 'rowClass';
>      const ROWSET_CLASS = 'rowsetClass';
>
>      // Defines restrictions on the superclass of an object
>      protected $_roles = array(
>          self::ROW_CLASS    => true,
>          self::ROWSET_CLASS => 'Zend_Db_Table_Rowset'
>      );
>
>      // The default classes to be used
>      protected $_classes = array(
>          self::ROW_CLASS    => 'Zend_Db_Table_Row',
>          self::ROWSET_CLASS => 'Zend_Db_Table_Rowset'
>      );
>
>      public function setClass($role, $classname)
>      {
>          if (!isset($this->_roles[$role])) {
>              // throw exception - unknown role
>          }
>
>          // Ensure the class actually exists
>          Zend_Loader::loadClass($classname);
>
>          if (is_string($this->_roles[$role])) {
>              if (!is_subclass_of($classname, $this->_roles[$role])) {
>                  // throw exception - the alternative class must subclass
>                  // or implement the interface declared in $this->_roles
>              }
>          } else {
>              // $this->_roles[$role] can just be set to true instead of a
>              // string if strict inheritance is not a requirement
>
>              $this->_classes[$role] = $classname;
>          }
>      }
>
>      public function getClass($role)
>      {
>          return $this->_classes[$role];
>      }
>
>      public function instantiate($role)
>      {
>          $class = $this->getClass($role);
>          return new $class();
>
>          // If arguments need to be passed on instantiation, then
>          // just perform the above directly, with the added args.
>      }
> }
>
>
> Example usage:
>
> $table->setClass(Zend_Db_Table_Abstract::ROW_CLASS, 'MyTableRow');
>
> Additionally a global registry or configuration could be used to declare
> alternatives:
>
> Zend_???::setClass('Zend_Db_Table_Abstract', 'rowClass', 'MyTableRow');
>
> or declared in an ini file:
>
> [classes]
> Zend_Db_Table_Abstract.rowClass = MyTableRow
>
>
> Any thoughts?
> Is it worth turning into a proposal?
>
> - Mark.

Reply via email to