newinstance() unfriendly in simple inheritance situations

Swift Mailer is a fantastic library for sending email with php. Discuss this library or ask any questions about it here.

Moderators: Chris Corbyn, General Moderators

Post Reply
gmorehoudh
Forum Commoner
Posts: 50
Joined: Tue Mar 04, 2008 1:49 pm

newinstance() unfriendly in simple inheritance situations

Post by gmorehoudh »

Just spent a good amount of time trying to figure out why a class I created by extending Swift_Mailer in order to wrap the send() function was not working.

Turns out the ::newInstance() call mentioned here (and elsewhere) in the docs -- http://swiftmailer.org/docs/sending-quickref -- returns a Swift_Mailer instance, not my extended class. I simply replaced the instantiation of my class from 'my_class::newInstance($transport)' to 'new my_class($transport)' which is the standard way of doing things in PHP. You may wish to note everywhere in the documentation where it might be relevant that one must either use the standard way of creating new objects, or override newInstance(), when creating classes that extend parts of Swift. It'd be helpful to people who are not familiar with the newInstance() idiom.
josh
DevNet Master
Posts: 4872
Joined: Wed Feb 11, 2004 3:23 pm
Location: Palm beach, Florida

Re: newinstance() unfriendly in simple inheritance situations

Post by josh »

Either that or introduce a configuration parameter to allow the user to substitute a class name
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: newinstance() unfriendly in simple inheritance situations

Post by Chris Corbyn »

This is a problem with PHP and lack of late static binding. PHP 5.3 will allow what you were doing to work correctly.

Everywhere I use newInstance() in Swift Mailer, the "new" keyword can be used too, I just advertise the use of newInstance() for a couple of reasons:

1) It's more likely to scale if I need to change the way the object is created
2) It's the only way you can chain off of the constructor, which I do quite a bit in the documentation.

I'll add something to the docs to mention that the new keyword is also allowed.

On a side note, Swift Mailer is designed so that you shouldn't have to override the send() method and if you're doing this to say, write the mail to a database instead then the "best" course of action is to use composition and write a new Transport. Transports are really simple:

Code: Select all

interface Swift_Transport {
  function start();
  function stop();
  function isStarted();
  function send(Swift_Mime_Message $message);
}
If your intention is to add some logic before or after an extisting transport runs then just wrap it:

Code: Select all

$myTransport = new MyTransport(new Swift_SmtpTransport(...));
 
/* ....
 
Class MyTransport ....
 
  function send(Swift_Mime_Message $message)
  {
    $this->doSomething();
    return $this->_delegate->send($message);
  }
 
...
*/
gmorehoudh
Forum Commoner
Posts: 50
Joined: Tue Mar 04, 2008 1:49 pm

Re: newinstance() unfriendly in simple inheritance situations

Post by gmorehoudh »

Thanks Chris, I will take a look at doing it this way. We are adding logic to divert outbound mail to ourselves if the code is running on one of our development workstations as opposed to the production server.
gmorehoudh
Forum Commoner
Posts: 50
Joined: Tue Mar 04, 2008 1:49 pm

Re: newinstance() unfriendly in simple inheritance situations

Post by gmorehoudh »

Say, what's that _delegate bit in there?
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: newinstance() unfriendly in simple inheritance situations

Post by Chris Corbyn »

gmorehoudh wrote:Say, what's that _delegate bit in there?
The _delegate bit is the transport that the work will be delegated to :) It's another common design pattern. For everything you don't care about or don't want to modify, just pass the invocation along to the delegate. For everything you do care about or want to change the behaviour of, add some custom logic, and possibly call the delegate.

Code: Select all

class MyTransport implements Swift_Transport {
  private $_delegate;
  
  public function __construct(Swift_Transport $delegate) {
    $this->_delegate = $delegate;
  }
  
  public function isStarted() {
    return $this->_delegate->isStarted();
  }
  
  public function start() {
    return $this->_delegate->start();
  }
  
  public function stop() {
    return $this->_delegate->stop();
  }
  
  public function send(Swift_Mime_Message $message) {
    $this->_doSomething(); //Customized behaviour
    return $this->_delegate->send($message); //Delegation
  }
  
  private function _doSomething() {
    echo "Hey, I've customized this send() thingy";
  }
}
 
$delegate = Swift_SmtpTransport::newInstance('localhost', 25);
 
$mailer = new Swift_Mailer(new MyTransport($delegate));
 
 
Post Reply