Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:73399 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 71272 invoked from network); 25 Mar 2014 01:59:59 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 25 Mar 2014 01:59:59 -0000 Authentication-Results: pb1.pair.com smtp.mail=garyamort@gmail.com; spf=pass; sender-id=pass Authentication-Results: pb1.pair.com header.from=garyamort@gmail.com; sender-id=pass Received-SPF: pass (pb1.pair.com: domain gmail.com designates 209.85.192.47 as permitted sender) X-PHP-List-Original-Sender: garyamort@gmail.com X-Host-Fingerprint: 209.85.192.47 mail-qg0-f47.google.com Received: from [209.85.192.47] ([209.85.192.47:58004] helo=mail-qg0-f47.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 32/E1-60997-F13E0335 for ; Mon, 24 Mar 2014 20:59:59 -0500 Received: by mail-qg0-f47.google.com with SMTP id 63so19277142qgz.6 for ; Mon, 24 Mar 2014 18:59:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=message-id:date:from:user-agent:mime-version:to:subject :content-type:content-transfer-encoding; bh=XgtrQEZjqyarz6sd6q9d3x/Rf7flX8qE15I2PQNKbkw=; b=GwxsrgR7dG5R1GDSz1l2cBWt0yWIB04Ajl45W3UkBa4l5H+E+uQKgT2uuTZK/xzc/U 51rxXOtLZbvWtawlLEEu1Mq5h6aPyYsc65Rl5mtCyVYIxYfOFVuYGbJzZeQfRioIHoCV kaS7JMHcZK5oUUF2Lg+3iOzDyhgvJ24SHPUV0jrNVVaeFdkhYOQcAlj0XySvB/3iI/z/ 4xpJYkJX79m+drRIIjsR5unDAzkRUjmNxB3mDHeZpoW3sd6dB7txS4Z8kHUnWZjkrevL dyiIRfJw7S6dyx7n9Ep9AXTSBx2ztIQTAaJVSoTuw+l6O4H4tGLfAjSRM1get2GHSb8K mTOA== X-Received: by 10.140.27.109 with SMTP id 100mr50041445qgw.14.1395712796790; Mon, 24 Mar 2014 18:59:56 -0700 (PDT) Received: from ?IPv6:2604:2000:1118:4036:62a4:4cff:fea8:603d? ([2604:2000:1118:4036:62a4:4cff:fea8:603d]) by mx.google.com with ESMTPSA id r19sm24983613qac.24.2014.03.24.18.59.56 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Mon, 24 Mar 2014 18:59:56 -0700 (PDT) Message-ID: <5330E319.5060302@gmail.com> Date: Mon, 24 Mar 2014 21:59:53 -0400 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Thunderbird/24.4.0 MIME-Version: 1.0 To: internals@lists.php.net Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Subject: Proposal: Mail Handling, feedback requested From: garyamort@gmail.com (Gary Mort) Current e-mail handling in PHP is dreadful. Almost every framework implements it's own email handling class in order to make sending e-mail reliable, easy to configure, and to implement multiple mail schemes. This means for developers working on multiple frameworks, they can't just call mail($recipient, $subject, $message) - they have to perform convoluted checking to find out what framework they are using, and then use the appropriate api. That was understandable in PHP v4 and in v5 - 5.2. Today though it is a waste of time, PHP internally has implemented a smart, extensible system for generalizing access to system oriented structures. Handlers/Wrappers. By subclassing the appropriate handler class, a PHP application can implement specific functionality to handle specific elements of Session and Stream processing, without having to implement every one of them and without requiring specialized PHP classes to be used directly. http://www.php.net/manual/en/function.session-set-save-handler.php http://www.php.net/manual/en/function.stream-wrapper-register.php http://www.php.net/manual/en/class.sessionhandler.php http://www.php.net/manual/en/class.streamwrapper.php My proposal[which I am willing to work on coding] is to replace the current mail() function with one that will support handlers. Note: This is only to handle HALF of the issues that we deal with when sending e-mail: sending the actual message. It does not attempt to address creating/formatting e-mail messages except in a very general since. My proposed class would be: class MailMessage { /* Properties */ $private array $recipients; $private string $sender; $private string $subject; $private string $body; $private string $header; $private string $additionalParamters; $private array $sentTo; // Mail handler used to send mail, if none specified the current default handler is used $private MailHandler $handler; /* Primary Methods */ /* create a new mail message, constructor parameters mimic current mail() function parameters. Throws exception if there is a problem. It is perfectly valid to create a message and not specify any of the parameters - it is only when the message is SENT that the parameters must be valid */ public MailMessage __construct ( [string $to, string $subject, string $message, string $additional_headers, string $additional_parameters, string $from, MailHandlerInterface $handler] ) /* Send a mail message. Throws exception on problems. If $handler is specified, then that handler is used. If not specified, then the the objects $handler property is used. */ public MailMessage send ([MailHandle $handler ]); /* Utility Methods */ /* Sets appropriate private properties, checks parameters and throwsexceptions when not properly formatted */ public MailMessage setRecipients ( array $recipients ) public MailMessage setSender ( string $sender ); public MailMessage setSubject ( string $$subject ); public MailMessage setBody ( string $body ); public MailMessage setHeader ( string $header ); public MailMessage setAdditionalParameters ( string $additionalParameters ); public MailMessage setHandler( MailHandlerInterface $handler ); public MailMessage setSentTo(); /* Magic method to allow direct setting of sender, subject, body, recipients, and additionalParameters. It is not recommended to allow the setting of mail handler or sentTo in this manner. */ public MailMessage __set(); /* Gets appropriate private properties and returns them either as strings or as arrays */ public mixed getRecipients([string $format = 'string']); public mixed getSubject([string $format = 'string']); public mixed getBody([string $format = 'string']); public mixed getHeader([string $format = 'string']); public mixed getAdditionalParameters([string $format = 'string']); public MailHandlerInterface getHandler(); public array getSentTo(); /* Magic get method for sender, subject, body, recipients, and additionalParameters. It is not recommended to allow retrieval of the mail handler in this manner. Always returns data as an associative array. */ public array __get(); } Sending e-mail in an object oriented manner can be as simple as: $message = new MailMessage($to, $subject, $message); $message->send; Or simply continue to use mail($to, $subject, $message) and it will handle the above transparently. In addition to a MailMessage class, there would also be a MailHandler interface. It only requires 2 methods, send and sendBatch. MailHandlerInterface { /* Methods */ abstract public bool send ( MailMessage $message [, string $address ] ) abstract public bool sendBatch ( MailMessage $message [, array $recipients ] ) } sendBatch at it's simplest would simply loop through each recipient in the message and call send($message, $recipient). It could also be called to send mail only to specific recipients. Handlers are free to use other logic, for example if using Postfix's maildrop function, the message can be saved as a file to a specific directory and Postfix monitors the directory and send's e-mail from it: no need to make costly network connections for every recipient. send is used to deliver a message to a single recipient. If a message has multiple potential recipients, send should throw an exception if the specific recipient is not specified via $address. The send and sendBatch function CAN be used to send a message to an address that is not listed as a recipient. IE this method can be used for transparently copying others for every message the handler sends out. Both send and sendBatch must update the sentTo property of the message to add each recipient a message is actually sent to. This allows mass e-mail situations to add tracking information to embedded images in the message for each recipient. Note, such functionality is beyond the scope of the base interface. Both send and sendBatch MAY check the sentTo property and avoid sending duplicate copies of a message but this is NOT required. Lastly, this would require an additional function: bool mail_handler_register ( MailHandlerInterface $handler [, int $flags = 0 ] ) My goal is that the creation and sending of basic e-mail should be a simple operation. It should be possible for a developer to simply call the mail() function and not be concerned about possible e-mail handling configurations. At the same time, Application Designers and Framework designers should be able to augment basic e-mail delivery with their own custom handling rules. Adding the above functionality to PHP would allow for easy migration - in most cases all they would have to do is add the send and sendBatch methods to their current mail class and make sure to flag the class as implementing the MailHandlerInterface. Then it can be registered to deal with sending or not. Possible additions to the above would be: A MailHandlerClass which can be extended. This way a handler that only wanted to customize the send or sendBatch method could simply inherit the base methods and customize. Adding a createMessage method to the MailHandlerInterface so that the message object that is created by mail($to, $subject, $message) can include custom functionality. Adding a mail_create() function which would return a MailMessage object using the createMessage method from the currently registered MailHandlerInterface. Feedback on changes to the classes to bring things in line with current standards is appreciated. In addition, pointers to the files which currently implement the handler functionality in PHP would be appreciated. I believe I found the functionality, but as I read the code it seems that SessionHandlers and StreamHandlers do not inherit any common code, ie each one is coded individually - so if there is a generic mechanism for registering/using handlers already in the code I am missing I'd rather extend that then write new code. :-) -Gary