Avant même qu'il ne soit fini, voici la première version pre-beta-alpha-0.0.1 (soyons fou et proposons des trucs en beta comme Gmail).

Prérequis et note diverses :

  • le script ne fonctionne que avec PHP5
  • un certain nombre de variables sont à configurer au début du fichier pour que ça fonctionne sur d'autres configurations (voir les commentaires)
  • le paquet PEAR::DB est nécessaire
  • les components Zend_Mail et Zend_Mime sont nécessaires
  • la base de données peut-être autre chose que Mysql (voir les possibilités de Pear::DB)

Pour tester l'envoi sur une seule adresse mail :

# sendnews -e -t

Pour remettre le flag "envoye" à zéro dans la table (après un envoi réussi) :

# sendnews -r

Pour envoyer :

# sendnews -e

Pour visualiser l'aide :

# sendnews --help

Il reste pas mal de chose à faire pour que ce système d'envoi de newsletter soit quand même un peu plus complet :

  • gérer les bounces, c'est à dire les retours sur les adresses non valides (c'est un "gros" boulot)
  • vérifier le mode silencieux (pour lancer le programme dans un cron)
  • activer les erreurs et le log qui va avec
  • utiliser PDO de PHP5 à la place de PEAR::DB
  • faire un paquet tout en un avec Zend_Mail et Zend_Mime.

Enfin le code !

Vous pouvez également télécharger directement le script

#!/usr/bin/php
<?php

// on supprime la limite d'execution d'un script php
set_time_limit(0);

/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
 * Envoi d'une lettre d'information
 *
 * PHP version 5
 *
 * LICENSE: Ce programme est un logiciel libre distribue sous licence GNU/GPL
 *
 * @author     Yves Tannier <yves_chez_grafactory.net>
 * @copyright  2006 Yves Tannier
 * @license    http://www.gnu.org/copyleft/lesser.html  LGPL License 2.1
 * @version    0.1.0
 * @link       http://www.grafactory.net
 */


/**
 * librairie Pear::DB
 */

require_once 'DB.php';

/**
 * librairie Zend_Mail
 */

require_once 'Zend/Mail.php';

$host = "localhost"; // nom hote de la BDD
$user = "user"; // utilisateur
$pass = "pass"; // mot de passetheatre
$bdd  = "base"; // base de donnees
$type = "mysql"; // type de bdd

// expediteur
$sender = "xxx@xxx.tld";
$sender_name = "John Doe";

// adresse email du test par defaut
$defaut_send = "xxx@xxx.tld";

// sujet
$subject = "Lettre hebdomadaire";

// pour les clients qui ne lise pas le html.
$msg_text = "Si vous ne pouvez pas lire correctement ce message rendz-vous a l'adresse suivante...";

// repertoire de stockage des messages Html
$dir_html = "/home/web/newsletter/date/";

// table
$table_users = "newsletter_abonnes";

// champs de la table
$table_fields = array('id'     => 'id',
                      'email'  => 'email');

// champ flag envoi OK
$field_send = "envoye";

// instruction WHERE si besoin (peut être vid)
$where_more = " AND thea=1 ";

// serveur smtp
$smtp_server = "localhost";

// nombre d'envoi avant la pause
$per_send = 100;

// duree de la pause en seconde
$pause_time = 15;

// aide contextuelle
$help_string = "Parametres :
                -s : mode silencieux (inactif)
                -e : lancer l'envoi
                -r : remettre le flag envoye a null
                -t : envoyer un message de test
                -l : voir les erreurs (inactif)
                -h, --help : cette aide...

"
;

// recuperer les parametres passe en CLI
foreach($_SERVER['argv'] as $param) {
    switch($param) {
      case '-s' :
        $silent = true;
        break;
      case '-e' :
        $execute = true;
        break;
      case '-r' :
        $renew = true;
        break;
      case '-t' :
        $testsend = true;
        break;
      case '-l' :
        $viewlog = true;
        break;
      case '-h' :
      case '--help' :
        echo $help_string;
        exit();
      case $_SERVER['SCRIPT_NAME'] :
        unset ($_SERVER['argv'][0]);
        break;
      default :
        echo "
Parametre inconnu : "
.$param."

"
;
        exit();
    }
}

if (empty($_SERVER['argv'])) {
    echo "
Veuillez preciser au moins un parametre

"
;
    echo $help_string;
}

/* classe Zend_Mail modifiee pour ajouter
 * la methode de remise a zero du header 'To'
 */

class MyZend_Mail extends Zend_Mail
{
    public function removeAddTo()
    {
        unset($this->_headers[To]);
        array_splice($this->_recipients,0);
    }
}

// connection a la BDD
$dsn = "$type://$user:$pass@$host/$bdd";
$db = DB::connect($dsn);
$db->setFetchMode(DB_FETCHMODE_ASSOC);

// envoi
if ($execute) {
           
    // par cours du répertoire pour le dernier fichier place
    $dir = scandir($dir_html);
    $files = array();
    foreach ( $dir as $file ) {
        if (!preg_match('/^\./', $file)) {
           $files['name'][0] = $file;
           $files['date'][0] = filemtime($dir_html.$file);
        }
    }

    $defaut_file = $files['name'][0];

    // on recupere le nom du fichier HTML
    if(!$silent) {
        fwrite(STDOUT, "Entrez le nom precis du fichier html (defaut : ".$defaut_file.") : ");
        $file_name = trim(fgets(STDIN));
    }

    if (empty($file_name)) {
       $file_name = $defaut_file;
    }
   
    // le mail en html depuis la page html
    $handle = @fopen($dir_html.$file_name, "r");
    if ($handle) {
       while (!feof($handle)) {
         $msg_html .= fgets($handle, 4096);
       }
       fclose($handle);
    }
    else {
        echo "Le fichier html du message n'a pas ete trouve ou n'a pas pu etre ouvert
"
;
        exit();
    }

       
    require_once 'Zend/Mail/Transport/Smtp.php';
    $tr = new Zend_Mail_Transport_Smtp($smtp_server);
   
    // construction du message
    $mail = new MyZend_Mail();
    $mail->setBodyText($msg_text);
    $mail->setBodyHtml($msg_html);
    $mail->setFrom($sender, $sender_name);
    $mail->setSubject($subject);

    // on connecte
    $tr->connect();
       
    // si ce n'est pas un test
    if (!$testsend) {
       
        // requete
        $res = $db->query("SELECT ".$table_fields['id'].",".$table_fields['email']." FROM ".$table_users." WHERE 1 ".$where_more." AND ".$field_send."=0");
       
        // incrementation
        $i = 0;
   
        //  boucle
        while($row = $res->fetchRow()) {
           
            // a qui envoye ?
            $mail->addTo($row[$table_fields['email']]);

            // la pause (deconnection/reconnection)
            if (($i % $per_send) == 0 && $i>0) {
               
                $tr->disconnect();

                if (!$silent) {
                    echo "==============================> Pause au niveau ".$i."
"
;
                    for($s=0;$s<$pause_time;$s++) {
                        echo ".";
                        sleep(1);
                    }
                    echo "
"
;
                }
               
                $tr->connect();

            }
           
            // envoi (manque le retour d'erreur)
            try {

                $mail->send();
                $up = $db->autoExecute($table_users, array($field_send=>1), DB_AUTOQUERY_UPDATE,$table_fields['id'].'='.$row['id']);
               
                if (!$silent) {
                    echo "-> Envoi en cours a ".$row[$table_fields['email']]." ".$row[$table_fields['id']]."
"
;
                }

            }
            catch(Zend_Mail_Exception $e) {

                echo $e->getTrace()."
"
;
            }

            // on supprime le mail dans les headers
            $mail->removeAddTo($row[$table_fields['email']]);

            $i++;

        }

    }

    // sinon on test l'envoi
    else {

        if(!$silent) {

            fwrite(STDOUT, "Entrez l'adresse de test (par defaut : ".$defaut_send.") : ");
            $defaut_name = trim(fgets(STDIN));

            if (!empty($defaut_name)) {
                $defaut_send = $defaut_name;
            }
           
        }

        // a qui envoye ?
        $mail->addTo($defaut_send);
       
        // envoi du message
        $mail->send();
       
    }

    echo "Envoi termine !
"
;

}

// on remet le flag d'envoi a 0
if($renew) {
   
    $renew = $db->autoExecute($table_users, array($field_send=>0), DB_AUTOQUERY_UPDATE);

    if (!$silent) {
        echo "-> Le flag d'envoi a bien ete reinitialise
"
;
    }
   
}
?>