Configurare Zend_Mail usando application.ini

zfmailIn un precedente articolo ho parlato di come scrivere un’application resource per Zend Framework per utilizzare il plugin ZFDebug. In questo articolo vedremo come utilizzare lo stesso meccanismo per un’operazione più semplice ma di utilizzo molto più frequente, ovvero configurare il default transport per l’invio di messaggi di posta elettronica tramite il componente Zend_Mail.

Normalmente per configurare l’invio dei messaggi si utilizza il seguente codice (ad esempio nel bootstrap):

$config = array('auth' => 'login',
                'username' => 'myusername',
                'password' => 'password');

$transport = new Zend_Mail_Transport_Smtp('mail.server.com', $config);
Zend_Mail::setDefaultTransport($transport);

Per liberarci di questo codice in ogni applicazione che utilizzi la classe Zend_Mail ho scritto una classe Application Resource da inserire nella propria libreria. Il codice della classe è piuttosto semplice

/**
 * Resource for creating smtp transports
 *
 * @uses       Zend_Application_Resource_ResourceAbstract
 * @category   Zend
 * @package    Zend_Application
 * @subpackage Resource
 * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zenit_Application_Resource_Smtp extends Zend_Application_Resource_ResourceAbstract {
	/**
	 * Transport
	 *
	 * @var Zend_Mail_Transport_Abstract
	 */
	protected $_transport = null;
	
	/**
	 * Parameters to use
	 *
	 * @var array
	 */
	protected $_params = array ();
	
	/**
	 * Server name
	 *
	 * @var string
	 */
	protected $_server = '127.0.0.1';
	
	/**
	 * @var string
	 */
	protected $_transportClass;
	
	/**
	 * Set the adapter params
	 *
	 * @param $params array
	 */
	public function setParams(array $params) {
		$this->_params = $params;
		return $this;
	}
	
	/**
	 * Validates the hostname provided as server
	 *
	 * @param string $server
	 */
	public function setServer($server) {
		if (Zend_Validate::is($server, 'Hostname')) {
			$this->_server = $server;
		} else {
			throw new Zend_Mail_Exception("Invalid server name $server");
		}
	}
	
	/**
	 * Returns the hostname for the server
	 *
	 * @return string
	 */
	public function getServer() {
		return $this->_server;
	}
	
	/**
	 * Transport parameters
	 *
	 * @return array
	 */
	public function getParams() {
		return $this->_params;
	}
	
	/**
	 * Determine the type of transport to use
	 *
	 * @param string $class
	 * @throws Zend_Mail_Transport_Exception if the given class doesn't exists
	 */
	public function setTransport($class) {
		if (false === strpos($class, '_')) {
			// allow short names such as smtp
			$class = 'Zend_Mail_Transport_' . ucfirst($class);
		}
		if (!class_exists($class)) {
			throw new Zend_Mail_Transport_Exception(
					"Unexistant transport class $class");
		}
		$this->_transportClass = $class;
	}
	
	/**
	 * Return the transport class to use
	 *
	 * @return null|Zend_Mail_Transport_Abstract
	 */
	public function getTransport() {
		if (null == $this->_transport && $this->_transportClass) {
			$class = $this->_transportClass;
			// instantiate class
			switch ($class) {
				case 'Zend_Mail_Transport_Smtp':
				case 'Zenit_Mail_Transport_Smtp':
					$transport = new $class($this->getServer(), 
							$this->getParams());
					break;
				case 'Zend_Mail_Transport_Sendmail':
				default:
					$transport = new $class();
					break;
			}
			// check transport type
			if (!$transport instanceof Zend_Mail_Transport_Abstract) {
				throw new Zend_Mail_Transport_Exception(
						"Class $class is not a valid Zend_Mail_Transport_Abstract");
			}
			// set transport as default transport
			Zend_Mail::setDefaultTransport($transport);
			// assign it to local field
			$this->_transport = $transport;
		}
		return $this->_transport;
	}
	
	/**
	 * Defined by Zend_Application_Resource_Resource
	 *
	 * @return null|Zend_Mail_Transport_Abstract
	 */
	public function init() {
		return $this->getTransport();
	}
}

Come nel caso precedente, è sufficiente inserire questa classe nel proprio include_path e cambiare/registrare il namespace della libreria (nel mio caso Zenit).

Fatto ciò per configurare il trasporto da usare usando il file application.ini è sufficiente inserire queste righe:

; smtp stuff
resources.smtp.transport = "smtp"
resources.smtp.server = "smtp.example.org"
resources.smtp.params.auth = "login"
resources.smtp.params.username = "myusername"
resources.smtp.params.password = "mypassword"

Et voilà, il gioco è fatto. Ora si può usare in qualsiasi punto della propria applicazione la classe Zend_Mail per l’invio di messaggi ed il metodo di invio usato sarà quello configurato tramite il file application.ini.

È possibile usare anche classi personalizzate come transport, è sufficiente che tali classi estendano la classe Zend_Mail_Transport_Abstract e che prevedano un costruttore vuoto (in alternativa è necessario personalizzare il metodo getTransport per adattarlo alle proprie esigenze). Per utilizzare una classe personalizzata è sufficiente passare il nome completo della classe come valore dell’opzione resources.smtp.transport, mentre per le classi predefinite si può anche usare il nome breve della classe senza il namespace completo (nell’esempio “smtp”).

Se il server smtp non necessita di autenticazione per l’invio è sufficiente rimuovere le relative righe dal file di configurazione (4-6).

Per quanto mi riguarda attualmente sto utilizzando come transport l’estensione della classe Zend_Mail_Transport_Smtp consigliatami in questo thread del forum di Zend_Framework, in quanto la classe originale soffre di un memory leak nel momento in cui si invia un numero consistente di email nello stesso script, a causa di un log interno che viene salvato in una stringa in memoria. Non appena verrà rilasciato l’aggiornamento che includa la correzione discussa qui, tornerò ad usare la classe ufficiale (di conseguenza la linea case 'Zenit_Mail_Transport_Smtp': nel metodo getTransport verrà rimossa dal codice).

You can leave a response, or trackback from your own site.
  • C4rLoS

    PERDONAMI,
    ma non ho capito dove salvare la classe.

    grazie
    carlo

  • C4rLoS

    Correggimi se ho sbagliato:
    ho rinominato la classe in MyApplication_Resource_Smtp e salvato all’interneto di library:
    – Library
    – MyApplication
    – Resource
    – Smtp.php

    dovrebbe funzionare vero ??
    mi dici come dovrei utilizzare la classe ??

    Grazie.

  • fabio

    Nella situazione che mi dici ci sono altre due condizioni necessarie affinché l’autoloader riconosca la classe:

    • che la directory library sia nell’include path (se hai usato zend tool già lo è, altrimenti puoi registrarla inserendo la riga includePaths.library = APPLICATION_PATH "/library" in application.ini)
    • che tu abbia registrato il namespace MyApplication (con la direttiva autoloadernamespaces.myapp = "MyApplication_")

    Se queste due condizioni sono verificate allora per usare la classe è sufficiente che tu inserisca nel tuo application.ini le seguenti righe con i parametri del tuo mail server nella sezione che preferisci (solitamente basta metterle nella sezione production):
    ; smtp stuff
    resources.smtp.transport = "smtp"
    resources.smtp.server = "smtp.example.org"
    resources.smtp.params.auth = "login"
    resources.smtp.params.username = "myusername"
    resources.smtp.params.password = "mypassword"

    Fammi sapere se hai problemi

  • C4rLoS

    Perfetto.
    Le due condizioni erano già soddisfatte.
    mi sono sfuggite le righe del file di configurazione :P

    Ora voglio mandare un email…utilizzando la funzione preventiviAction().
    quale’ il passo successivo ??

    Ciao

  • fabio

    Bene, quindi se l’applicazione carica senza problemi a questo punto per inviare un’email è sufficiente il seguente codice

    $mail = new Zend_Mail();
    $mail->setFrom('info@example.com', 'Nome visualizzato');
    $mail->setSubject('Oggetto del messaggio');
    $mail->addTo('dest@example.com');
    $mail->setBodyText('Corpo del messaggio');
    $mail->send();

    in qualsiasi punto della tua applicazione.

  • C4rLoS

    mmm..no, l’applicazione non viene caricata.
    ricevo questo msg di errore:

    Fatal error: Uncaught exception ‘Zend_Application_Bootstrap_Exception’ with message ‘Unable to resolve plugin “smtp”; no corresponding plugin with that name’ in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Application\Bootstrap\BootstrapAbstract.php:330 Stack trace: #0 C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Application\Bootstrap\BootstrapAbstract.php(379): Zend_Application_Bootstrap_BootstrapAbstract->getPluginResource(‘smtp’) #1 C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Application\Bootstrap\BootstrapAbstract.php(391): Zend_Application_Bootstrap_BootstrapAbstract->getPluginResources() #2 C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Application\Bootstrap\BootstrapAbstract.php(618): Zend_Application_Bootstrap_BootstrapAbstract->getPluginResourceNames() #3 C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Application\Bootstrap\BootstrapAbstract.php(579): Zend_Application_Bootstrap_BootstrapAbstract->_bootstrap(NULL) #4 C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Application.php(347): Zend_Application_Bootstrap_Bo in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Application\Bootstrap\BootstrapAbstract.php on line 330

    ho salvato la classe smtp.php nel percorso:
    library\MyApplication\Application\Resource

    ciao e grazie

  • C4rLoS

    facendo delle prove, mi risulta che esce qst msg di errore, se in application.ini scrivo:

    resources.smtp.transport = “smtp”
    resources.smtp.server = “smtp.example.org”
    resources.smtp.params.auth = “login”
    resources.smtp.params.username = “myusername”
    resources.smtp.params.password = “mypassword”

    inoltre, utilizzando ZFDebug v1.5 ho notato che la classe MyApplication_Application_Resource_Smtp non viene caricata.

  • fabio

    Chiaro, ho dimenticato di indicarti una direttiva… Devi aggiungere anche questa all’application.ini in modo da indicare a ZF che le tue classi sono usate come application resources:

    pluginPaths.MyApplication_Application_Resource = "MyApplication/Application/Resource"

    Così dovrebbe andare.

  • C4rLoS

    bene..ora non ricevo alcune messaggio di errore, in fase di caricameto.
    ma quando eseguo la funziona… ricevo qst msg di errore:
    Zend_Mail_Protocol_Exception: Impossibile stabilire la connessione. Risposta non corretta della parte connessa dopo l’intervallo di tempo oppure mancata risposta dall’host collegato.
    thrown in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Mail\Protocol\Abstract.php on line 234

    Call Stack
    Zend_Mail_Protocol_Abstract->_connect()
    in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Mail\Protocol\Smtp.php on line 167
    Zend_Mail_Protocol_Smtp->connect()
    in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Mail\Transport\Smtp.php on line 195
    Zend_Mail_Transport_Smtp->_sendMail()
    in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Mail\Transport\Abstract.php on line 348
    Zend_Mail_Transport_Abstract->send()
    in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Mail.php on line 973
    Zend_Mail->send()
    in C:\xampp\htdocs\lavoro\MyWebSite\application\controllers\IndexController.php on line 182
    IndexController->preventivoAction()
    in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Controller\Action.php on line 513
    Zend_Controller_Action->dispatch()
    in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Controller\Dispatcher\Standard.php on line 289
    Zend_Controller_Dispatcher_Standard->dispatch()
    in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Controller\Front.php on line 946
    Zend_Controller_Front->dispatch()
    in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Application\Bootstrap\Bootstrap.php on line 77
    Zend_Application_Bootstrap_Bootstrap->run()
    in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Application.php on line 358
    Zend_Application->run()
    in C:\xampp\htdocs\lavoro\MyWebSite\index.php on line 31

    Domanda: puà essere che sto testando la funziona in locale ???

  • fabio

    Ottimo, manca poco… Dall’errore che ricevi direi che si tratta di un timeout nella connessione al server smtp. Nel file application.ini hai lasciato i dati che ho postato nell’esempio (ovvero smtp.example.com)? In tal caso è quello il problema, i dati li devi sostituire con i dati del tuo server smtp. A seconda di dove farai girare l’applicazione variano, ma in ogni caso per la fase di sviluppo puoi tranquillamente utilizzare il server smtp del tuo provider. In alternativa puoi usare un server smtp di qualche provider italiano, ad esempio se hai un indirizzo di posta di tiscali (o se ne vuoi creare uno per tale scopo) puoi mettere smtp.tiscali.it come server, l’indirizzo che hai creato come username, e la password scelta come password.

  • C4rLoS

    Ke sfista, ho dimenticato di sostiuire i dati :)
    ho lanciato l’applicazione, questa volta ricevo qst msg di errore:

    Zend_Mail_Protocol_Exception: 530 5.7.0 Must issue a STARTTLS command first. c28sm18966336fka.19
    thrown in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Mail\Protocol\Abstract.php on line 378

    Call Stack
    Zend_Mail_Protocol_Abstract->_expect()
    in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Mail\Protocol\Smtp\Auth\Login.php on line 91
    Zend_Mail_Protocol_Smtp_Auth_Login->auth()
    in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Mail\Protocol\Smtp.php on line 217
    Zend_Mail_Protocol_Smtp->helo()
    in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Mail\Transport\Smtp.php on line 196
    Zend_Mail_Transport_Smtp->_sendMail()
    in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Mail\Transport\Abstract.php on line 348
    Zend_Mail_Transport_Abstract->send()
    in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Mail.php on line 973
    Zend_Mail->send()
    in C:\xampp\htdocs\lavoro\MyWebSite\application\controllers\IndexController.php on line 182
    IndexController->preventivoAction()
    in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Controller\Action.php on line 513
    Zend_Controller_Action->dispatch()
    in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Controller\Dispatcher\Standard.php on line 289
    Zend_Controller_Dispatcher_Standard->dispatch()
    in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Controller\Front.php on line 946
    Zend_Controller_Front->dispatch()
    in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Application\Bootstrap\Bootstrap.php on line 77
    Zend_Application_Bootstrap_Bootstrap->run()
    in C:\xampp\htdocs\lavoro\MyWebSite\library\Zend\Application.php on line 358
    Zend_Application->run()
    in C:\xampp\htdocs\lavoro\MyWebSite\index.php on line 31

    questi sono i dati che sto utilizzando:
    resources.smtp.transport = “smtp”
    resources.smtp.server = “smtp.gmail.com”
    resources.smtp.params.auth = “login”
    resources.smtp.params.username = “miousername”
    resources.smtp.params.password = “miapassword”

  • C4rLoS

    Ops’…. sfista => svista :P
    scusatemi.

  • fabio

    Se usi gmail devi abilitare tls altrimenti non ti fa inviare in chiaro, lo puoi fare aggiungendo all’application.ini la seguente riga:

    resources.smtp.params.ssl = "tls"

    oltre alle righe già presenti. Fatto questo sei a posto, appena testato con il mio account gmail per sicurezza, vai tranquillo.

  • C4rLoS

    Problema risolto.
    dopo tanti tentativi sono arrivato ad una conclusione:

    la classe funziona alla perfezione, anche in locale,
    ma non con il server di posta gmail.com ( mi dà problemi)

    ho provato ad utilizzare il server di posta di yahoo e l’email viene inviata correttamente.

    grande, funziona!!
    grazie a te!!!

    Carlo

  • fabio

    Se inserisci la direttiva del mio commento precedente funziona anche con gmail ;-)

    A presto.

  • C4rLoS

    Scusami,
    non avevo letto la tua risposta.

    Ho aggiunto la riga che mi hai consigliato di utilizzare e, ora anche con gmail funziona alla grande.

    Grazie di tutto
    Alla prossima.

    Carlo

  • http://razorblade.netsons.org Sergio

    Ciao Fabio,
    complimenti per l’articolo,
    l’aggiornamento sul memory leak è segnato come rilasciato con la versione 1.10.0, è necessario rimuovere il ‘case’ dalla classe?

    Ciao

  • fabio

    Se usi una versione maggiore della 1.10.0 si, puoi rimuovere l’istruzione dalla classe senza conseguenze, ma tieni presente che lasciarla non fa danni ne introduce errori nella classe. In ogni caso tieni presente che successivamente alla pubblicazione di questo articolo è stata introdotta in Zend Framework una classe che fa esattamente quello che faceva la mia classe, per cui non è più necessario utilizzare la mia estensione.

    La classe in questione è Zend_Application_Resource_Mail e puoi trovare la documentazione ed esempi d’uso nella reference di ZF. L’architettura e l’utilizzo in ogni caso è molto simile alla mia classe per cui anche nei miei progetti ho sostituito la mia implementazione con quella ufficiale.

    Ciao.

Subscribe to RSS Feed