Paul M. Jones

Don't listen to the crowd, they say "jump."

Quick Wrapup: php|works 2007

This is waaay late, but better than never. ;-)

I didn't get to attend too many talks this time around, mostly because I spent my time practicing and editing my presentations the-day-of. I did get to all three of the talks from Jeff Moore (valuable stuff), as well Ed Finkler's PhpSecInfo presentation (it looks like a very useful security tool).

I also got to speak (too briefly) with a couple of fellow framework enthusiasts: Dustin Whittle of Symfony, and Felix Geisendörfer of Cake.

Felix made it to my benchmarking talk and gave valuable feedback on the methodology and results. Dustin did not attend; I'm sure he was preparing for his own presentation in the following hour. I also got to speak (again too briefly) with Chris Jones (no relation), who had very nice things to say about my performance.

I wish I'd had a Code Igniter devotee at that one, but Ed Finkler did make it to my project organization talk as a representative user of CI.

I've not seen this linked prominently anywhere, but you can get all the slides from php|works 2007 (well, all the ones that the presenters turned in ;-) here: http://works.phparch.com/c/p/works_live,slides

Finally, while I did get to hang around with all the "cool kids" and presenters (Sara Golemon, Chris Shiflett, Sebastian Bergmann, Andrei Zmievski, Cal Evans, Megan Nelson, Ben Ramsey, Derick Rethans, Clay Loveless, and everyone else whose names escape me right now), what I *really* enjoyed was meeting the attendees and hearing about the work they're doing with PHP. That's always the high point of these conferences for me. :-)


Hired On At OmniTI

As my family and some of my friends are aware, I have accepted an invitation from Chris Shiflett to work as "Senior Developer" at OmniTI. I'll be starting there next week. Woohoo!





php|works 2007 Teaser: Framework Benchmarks!

I'm giving two talks at php|works this year, and it turns out that they complement each other quite nicely. In a way, they are both about organization and architecture.

The first one is literally about how to organize your PHP project. (Yes, that link does show you all the slides for the talk – but you get a lot more info by attending. [Update: I have modified the presentation rather thoroughly, so those slides are no longer fully representative of the talk.]) Easy one-liner: "Project Planning In One Lesson."

The second one dovetails nicely from "how to organize" to "how to measure your architectural decisions". This talk is based on lessons from my benchmarking experiments combined with an extended riff on something Laura Thomson said about those experiments:

That, by the way, is an excellent, excellent article that displays a good methodology for researching design decisions.

(Thanks for the multiple compliments, Laura. :-)

An Attendance Request

I would really like it if at least one experienced developer on each of the frameworks (Cake, Code Igniter, Prado, Symfony, Zend) could attend the benchmarking talk and critique it. I'm pretty sure there's going to be a lot of questions and discussion during and after the presentation about each of the projects. Any takers?

About The Benchmarking Talk

Easy one-liner: "Statistical Histories for Fun and Profit." And I do mean profit: the better your apps runs on a given stack, the more eyeballs you get on your ads (or, the more users you can handle).

  • First, I will talk briefly about why you should do benchmarking on your "whole app" (as opposed to profiling sub-components), give a short example on benching an application, and go from there to talk about discovering the limits imposed on you by the various portions of the server + software stack.
  • The second half of the talk is about how (and why) to benchmark a framework, and how to fairly compare different frameworks to each other on a level playing field.
  • At the end, I'll give new benchmark numbers on Cake, Solar, Symfony, and Zend. Bonus: I include Code Igniter and Prado, too! But probably not in the context the Code Igniter guys want. You'll have to attend the talk to see what I mean. ;-)

A couple of slides from the talk:

Framework Benchmarking

Compare Like With Like


Solar: From Blog to Docs

Quick note: the blog entries of the past few days have been converted to manual entries at the Solar website.

The translation went pretty quickly, because I wrote the original blog entries using Solar_Markdown, which converted them to HTML for the blog. Then I did some quick edits and re-converted them using Solar_Markdown_Wiki (which is the markup format for all the class API and manual entries at the Solar website).


The Stenhouse CSS Framework and Solar

Solar comes with a reference application, Solar_App_Bookmarks. As I outlined in an earlier post about views and layouts in Solar, the bookmarks application is extended from Solar_App_Base. The Base package holds all the layouts, and those layouts are what I want to talk about in this entry.

The Stenhouse CSS Framework

Solar_App_Base comes packaged with the Stenhouse CSS Framework. You should read the whole thing; it's great stuff. (Many thanks to Clay Loveless for introducing me to, and then encouraging me to use, the Stenhouse work.) Here's a short summary of the Stenhouse article:

  1. Stenhouse says there are 6 basic building blocks of most sites: header, footer, main content, sub content, main (site-wide) navigation, and local (section or sub-site) navigation.

  2. Taking accessibilty needs into account, these building blocks needs to set up so that the content pieces are stacked up before the navigation pieces. Stenhouse then works out a single XHTML structure with that in mind.

  3. Finally, Stenhouse provides a series of stylesheet sets that present the elements from that one structure in different ways: 1 or 2 columns of content, mixed with horizonal or vertical main navigation, with or without local navigation. No matter which CSS presentation you use, you don't need to change the XHTML structure at all.

XHTML Scaffold

The basic XHTML structure for the Stenhouse CSS Framework looks like this:

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <title></title>
    </head>
    <body>
        <div id="page">

            <div id="header" class="clearfix">
                <hr />
            </div><!-- end header -->

            <div id="content" class="clearfix">
                <div id="main">

                    <hr />
                </div>

                <div id="sub">
                    <hr />
                </div>

                <div id="local">
                    <hr />
                </div>

                <div id="nav">
                    <hr />
                </div>

            </div><!-- end content -->

            <div id="footer" class="clearfix">
            </div><!-- end footer -->

        </div><!-- end page -->

        <div id="extra1">&nbsp;</div>
        <div id="extra2">&nbsp;</div>

    </body>
</html>

Main Layout Files

In Solar_App_Base, we've split that up into several separate layout template partials, so that your application can override any particular piece of the layout. First, we have the "main" layout templates for the various presentations; each of these corresponds with a Stenhouse CSS stylesheet set:

Solar/
    App/
        Base/
            Layout/
                # main content only
                1col.php

                # main content, with main nav on the left
                navleft-1col.php

                # main content, sub content on the right,
                # and main nav on the left
                navleft-2col.php

                # main content, with main nav at the top
                navtop-1col.php

                # main content, sub content on the right,
                # local nav at the left, and main nav on the top
                navtop-3col.php

                # main content, local nav at the left
                navtop-localleft.php

                # main content, local nav at the right
                navtop-localright.php

                # main content, sub content on the right
                navtop-subright.php

Each of those calls the proper CSS files from the Stenhouse framework to position the <div>s from the XHTML scaffold properly. (After you browse through the related CSS files, you'll see how to build your own CSS presentation groupings.)

So, in any application descended from Solar_App_Base, you can set the $_layout property to one of those layouts:

$this->_layout = 'navtop-3col';

Layout Partials

Each of those main templates is composed of the same partials; the only difference between each layout is the CSS file set. Each partial represents a section of the XHTML structure built by Stenhouse, and each of the elements in the partials can be independently targeted by CSS styling.

_head.php
The <head> section, which adds the title, scripts, styles, and so on.
_body.php

The <body> section, which is composed of the main content as well as ...

_header.php
Contents of <div id="header">, with sub <div>s for "branding" and "search".
_sub.php
Contents of <div id="sub">.
_local.php
Contents of <div id="local">, generally an authentication form (when Solar_Auth is turned on), and then a ul-list of local links.
_nav.php
Contents of <div id="nav">, generally a ul-list of site-wide links.
_footer.php
Contents of <div id="footer">.
_extra1.php
Contents of <div id="extra1">.
_extra2.php
Contents of <div id="extra2">.

I know that looks like a ton of files -- and it is. So why not have all that in just one layout template?

The benefit of having separate files like this is that any particular application can override individual sections of the layout by defining just those files in its own layout directory. The Bookmarks app, for example, overrides just the "_local" layout template to provide a list of links for tags and ordering options.

Solar/
    App/
        Bookmarks/
            Layout/
                _local.php

You can see that layout file here.

Because the Bookmarks app inherits all the other layout templates and partials from Solar_App_Base, it uses those when a custom template is not found. This lets us customize in a relatively fine-grained way.

Now, the Stenhouse CSS Framework may not be for everyone. Developers who want complex grid-based layouts will probably not be happy with the Stenhouse system. One nice thing about Solar is that you can define your own base application, say "Vendor_App_Base", and put your own layout system there. Then all classes extending from Vendor_App_Base will use those layouts instead.

But for the applications I want to distribute with Solar, the Stenhouse CSS Framework has been an excellent fit. My many thanks to Mike Stenhouse for his great work in developing this well-designed and very flexible CSS framework -- even non-designers like me can grasp it relatively quickly, and that is no small achievement.

Update (13:25) -- See also this review of the Stenhouse CSS Framework.


Sending Mail with Solar

Ah, sending mail, the bane of everyone who has ever used the php mail() function for anything remotely non-trivial. There are lots of entries in the “helps you send mail” category in PHP userland:

While each of these will work with Solar, the new Solar_Mail and Solar_Smtp packages work “natively”, in that they support automatic configuration, locale and exception inheritance, and so on. Read on for some examples on how to use them.

A Basic Mail Message

Here’s a short example on how to build a basic message and set the recipients. In this example, I’ll send a quick note to my old friend Bolivar Shagnasty, and give him both a text and HTML version of the message.

Note that the Solar_Mail_Message set*() methods are fluent, so you can chain them together when you find it appropriate. (You can review the full set of Solar_Mail_Message methods if you like.)

$text = <<<TEXT
The quick brown fox jumps over the lazy dog.

Now is the time for all good men to come to the aid of their country.
TEXT;

$html = <<<HTML
<p>The quick brown fox jumps <em>over</em> the lazy dog.</p>

<p><strong>Now</strong> is the time for all good men
to come to the aid of their country.</p>
HTML;

$mail = Solar::factory('Solar_Mail_Message');

$mail->setCharset('utf-8')
     ->setFrom('pmjones@example.com', 'Paul M. Jones')
     ->addTo('boshag@example.com', 'Bolivar Shagnasty')
     ->addCc('nobody@example.net')
     ->setSubject('A Short Test Message')
     ->setText($text)
     ->setHtml($html);

That’s pretty easy … but is it safe?

Headers and Header Injection

Anything that ends up getting sent as a mail header, including addresses and the subject line, is sanitized against header-injection attacks by removing newlines from the header label and value. Let’s say you want to add a new custom header:

$mail = Solar::factory('Solar_Mail_Message');
$mail->setHeader('X-Custom-Header', "FoornrnAn evil message");

Under a less-secure system this might cause the header to be sent as:

X-Custom-Header: Foo

An evil message.

That’s no good – somebody just injected their own message into our email.

With Solar_Mail_Message, when the mail gets sent, that header will go out as:

X-Custom-Header: FooAn evil message

We strip the newlines in header labels and values automatically, so you should be safe against header injections. (If there are other aspects to securing against header injections I would be happy to hear them.)

Attachments

Adding an attachment to an email is pretty easy; you can use a convenience method to attach a file, or you can build it yourself.

Convenience method attachFile():

$file_path = '/path/to/file.pdf';
$mime_type = 'application/pdf';

// this will automatically set the attachment name
// to the basename() of the attached file
$mail = Solar::factory('Solar_Mail_Message');
$mail->attachFile($file_name, $mime_type);

Or you can build and add a MIME part yourself with attachPart():

$file_path = '/path/to/file.pdf';
$mime_type = 'application/pdf';

$part = Solar::factory('Solar_Mail_Message_Part');
$part->setDisposition('attachment');
$part->setFilename(basename($file_name));
$part->setContent(file_get_contents($file_name));
$part->setType($mime_type);

$mail = Solar::factory('Solar_Mail_Message');
$mail->attachPart($part);

Sending The Message

Now that we have a message to send, let’s actually get it out there. To send the message, we need to pick what kind of “transport” we’re going to use, then send the message through that transport.

Solar comes with two Solar_Mail_Transport adapters: the Phpmail one, which uses mail() internally, and an SMTP factory-adapter one, which takes a little more setup but is a lot more robust.

Easy one first: the mail()-based adapter.

$mail = Solar::factory('Solar_Mail_Message');
// .. build the message ...

// build a transport and send the message
$transport = Solar::factory('Solar_Mail_Transport', array(
    'adapter' => 'Solar_Mail_Transport_Adapter_Phpmail'
));

$transport->send($mail);

The SMTP factory-adapter is a little more complicated, but still not too hard. We need to build an SMTP connection object, then tell the SMTP transport adapter to use it, then send the message.

$mail = Solar::factory('Solar_Mail_Message');
// ...

// build an SMTP connection object
$smtp = Solar::factory('Solar_Smtp', array(
    'adapter' => 'Solar_Smtp_Adapter_NoAuth',
    'host'    => 'mail.example.com',
));

// build a transport and send the message
$transport = Solar::factory('Solar_Mail_Transport', array(
    'adapter' => 'Solar_Mail_Transport_Adapter_Smtp',
    'smtp'    => $smtp,
));

$transport->send($mail);

The SMTP connection object is itself a factory, and has adapters for various kinds of authentication:

Transport Dependency-Injection

Let’s say you always use SMTP with plain authentication when sending a message. You can register the transport objects in your bootstrap file or your Solar_Controller_Page::_setup() method so they are available throughout the application:

// build a Solar_Smtp object
$smtp = Solar::factory('Solar_Smtp', array(
    'adapter'  => 'Solar_Smtp_Adapter_PlainAuth',
    'username' => 'pmjones',
    'password' => 'secret',
    'host'     => 'mail.example.com',
));

// register it as 'smtp'
Solar::register('smtp', $smtp);

// build a Solar_Mail_Transport object with the SMTP object injected
$transport = Solar::factory('Solar_Mail_Transport', array(
    'adapter' => 'Solar_Mail_Transport_Adapter_Smtp',
    'smtp'    => 'smtp', // uses the registered 'smtp' object
);

// register the transport as 'mail-transport'
Solar::register('mail-transport', $transport);

Now when you create a new mail message, you can tell it that there’s a transport already available, and call send() directly on the message.

$mail = Solar::factory('Solar_Mail_Message', array(
    'transport' => 'mail-transport',
));

// ... build the message, and then:
$mail->send();

Automation Via Config File Settings

Finally, you can take the configuration of the SMTP, transport, and mail objects entirely out of your logic, and put it all in the Solar config file.

Then you can lazy-load the objects from the registry, and they will automatically have all the right settings. The various dependency objects will be lazy-loaded automatically for you, since dependency injection is a core component of Solar.

In your Solar.config.php file:

// configure SMTP
$config['Solar_Smtp'] = array(
    'adapter'  => 'Solar_Smtp_Adapter_PlainAuth',
    'username' => 'pmjones',
    'password' => 'secret',
    'host'     => 'mail.example.com',
);

// configure mail transport
$config['Solar_Mail_Transport'] = array(
    'adapter' => 'Solar_Mail_Transport_Adapter_Smtp',
    'smtp'    => 'smtp',
);

// tell all mail messages to use the registered 'mail-transport'
$config['Solar_Mail_Message']['transport'] = 'mail-transport';

Then all you have to do in your setup logic is register class names instead of objects …

Solar::register('smtp', 'Solar_Smtp');
Solar::register('mail-transport', 'Solar_Mail_Transport');

… which will cause those objects to be created only at the moment they are first called as depndencies.

Now you can send an email message very simply:

// create and build a message
$mail = Solar::factory('Solar_Mail_Message');
// ...

// send the message; this creates the SMTP and mail-transport objects
// from the config-file settings, and sends the message trough the
// registered transport.
$mail->send();

Using the config-file-centered process instead of the logic-centered process means that you can have all your settings in one place for use throughout your application. You can even change adapters from the config file without changing any of the mail-sending code, as long as you keep the same registry names.

Conclusion

That’s a whirlwind tour of how to send mail in Solar. If you have questions or comments, please feel free to leave them here, or join the Solar mailing list – we’d love to have you around.


Brief Intro to Solar_Http_Request and Response

As I noted in an earlier post, Solar how has classes to represent simple HTTP requests and responses.

Solar_Http_Request

The Solar_Http_Request class represents a standalone HTTP request (i.e., it’s not an HTTP client, just a request). It uses adapters, so you can (in the future) change between a cURL, pecl_http, or sockets adapter – but for now, the only adapter is the streams-based one.

It’s a fluent class, which makes the code a little prettier at times. Here’s a basic GET request that sends “?foo=bar” as the query string.

$request = Solar::factory('Solar_Http_Request');
$request->setUri('http://example.com/index.php')
        ->setContent(array('foo' => 'bar'));

$response = $request->fetch();

(You could also set the query string in the URI if you wanted, but using setContent() does all the esaping for you.)

Here’s the same thing as a POST:

$request = Solar::factory('Solar_Http_Request');

$request->setUri('http://example.com/index.php')
        ->setContent(array('foo' => 'bar'))
        ->setMethod('post');

$response = $request->fetch();

And here’s a request with a lot of customization:

$request = Solar::factory('Solar_Http_Request');

$request->setUri('http://example.com/index.php')
        ->setMethod('post')
        ->setHeader('X-Zim', 'Kneel before Zim!')
        ->setUserAgent('Irken Robot Gir')
        ->setBasicAuth('username', 'password')
        ->setContent(array('foo' => 'bar'));

$response = $request->fetch();

Right now, Solar_Http_Request doesn’t handle file uploads, but that functionality is planned for a future release. However, it does provide SSL support through PHP streams.

Solar_Http_Response

What you get back from the request is a Solar_Http_Response object. You can explore the response by getting the response code, response text, HTTP version, headers, cookies, and content – all pretty standard stuff.

You can also build a new Solar_Http_Response on your own and send that from your application. This has the benefit of giving you relatively good control over headers, response codes, etc., so that you can pass the values around to different parts of your application before sending the final response. (It also sanitizes header values automatically, which can be a nice addition for secure coding practices.)

$response = Solar::factory('Solar_Http_Response');
$response->setResponseCode('200') // auto-sets response text to "Ok"
         ->setHeader('X-Example', 'Custom header value')
         ->setCookie('foo', 'bar') // sets cookie foo=bar
         ->setContent('<p>Hello world!</p>');

$response->display(); // sends sanitized headers and prints the content

Solar_Controller_Page, for example, composes a Solar_Http_Response internally, which it then returns for sending.