Paul M. Jones

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

Automating Release Tasks

I noted earlier today that I've made 5 releases of Solar in 7 days. (Clearly I'm a fan of "release early, release often." ;-) Obviously, I have the Solar-talk subscribers to thank for pointing out bugs and and making functionality requests; these are what drive the need for a release ... thanks, guys. :-)

But what I want to talk about in this entry is the release process itself. With the help of Greg Beaver (indirectly) and Clay Loveless (directly), Solar now has a moderate-length PHP script that handles almost all aspects of the release process automatically. Usage is at the command line; issue "php release.php" for a test run, or "php release.php commit" for a full release-and-commit cycle.

With any luck, the lessons I've learned here will be of use to someone else; with more luck, perhaps someone else will see possible improvements and mention them here. Read on for a narrative of how the script came to be.

First Iteration

The first person I have to thank here is Greg Beaver and his great work on the PEAR packaging and installation tools. Without his stewardship and maintenenance of this wonderful toolset, the Solar release process would be much more difficult than it is.

Even with the PEAR installer, though, building a package XML release file by hand is a terribly tedious exercise. So the second person I have to thank is Clay Loveless: some months ago, he wrote up the first iteration of the auto-packager for Solar using PEAR_PackageFileManager. I added on a bit to it at that time so that we could keep release information (authors, change notes, etc) in a separate location.

The point of keeping the relase info in a separate location was so we could avoid having to modify the packaging script for every release. The script looks for an "info/" directory; in that directory, there is a sub-directory for every release number, and contained therein are a number text and comma-separated values files. The packaging script then uses those files to generate the "header" portions of the PEAR pacakge file. You can see an example "info/" directory here.

Second Iteration

The PEAR_PackageFileManager has a great auto-replacement utility built in. It can look through the source code at installationpackage time and replace certain keywords, such as "@package_version@", with the proper value for that particular installation. (UPDATE: [19:33 CST] Greg Beaver pointed out that addGlobalReplacement() does replace at package-time. Once again, Greg proves he has thought of all the right things. Thanks man.)

But there's a small problem with that for non-PEAR users. Because the replacements happen at installation-time, not at package-time, it means the pearball still has the keywords in place, not the actual values. For methods like Solar::apiVersion() which depend on the automated replacement of "@pacakge_version@" with the correct version number, this is a functionality break for them.

It turns out there's an easy, if somewhat brute-force, solution: copy the original source to a temporary location, replace keywords yourself, and then have PEAR package that new codebase with the keywords pre-replaced. You can see this portion of the code in release.php at the comment noted "prepare package directory".

We copy the original source ("src/") to a packaging directory ("pkg/") and str_replace() keywords in the new "pkg/" directory. When PEAR_PackageFileManager comes into play, it works on the "pkg/" directory instead of the original source. Note also that the pre-replacements happen in documentation files as well (more on that in a bit).

The end result of this is a pearball/tarball that is equally useful to PEAR and non-PEAR installation processes.

Third (and Current) Iteration

So that takes care of the packaging itself. The last issues to handle are mostly administrative. On a full commit cycle, we need to check a few things before rolling the release:

  • Set Subversion keywords on all files
  • Update the wiki files in "docs/apiref/" (these are built with a custom class that applies the PHP5 Reflection API to each class in Solar; more on that in another post)
  • Make sure all pending changes have been committed to Subversion

If those pre-flight checks pass, we then need to actually build the package (see iterations 1 and 2 above). After that:

  • Tag the release using "svn copy"
  • Commit the tagged release to Subversion
  • Extract the documentation files for updating the Solar website

The release.php script splits this into three task sets.

First, the administrative tasks are handled at the top of the script under "status check". This sets keywords, builds docs, etc., and then makes sure that there weren't any changes as a result. If these automated tasks generated changes (as noted by "svn status" returning any ouput at all), the script terminates with a warning to handle outstanding issues.

Second, after the package is built, near the end of the script, we issue a series of "svn" commands to tag the release and commit to the repository.

Finally, we unzip a copy of the pearball and pull out a copy of the docs; these are re-zipped in a separate tarball for upload to the Solar site. We use the packaged docs instead of the source docs so we don't get all the .svn files mixed in with them.

Manual Tasks

There are still some manual tasks to accomplish in a release, but they're easy: upload the pearball to the channel server, upload the docs to the website ... oh, and write an entry about the release on my blog. ;-)


Solar 0.16.1 released

This is a minor bugfix and enhancement release to the Solar library and framework for PHP5. The biggest changes are:

  • Packaging script now properly keeps Solar/App/Public in the main
    library directory (instead of in the data directory) ... reported
    by StR.
  • Solar_View_Helper_Form now uses a 'text' element when an
    unrecognized element type is requested
  • Solar_Controller_Page::_forward() now sets $this->_view properly (previously, was
    not replacing dashes with slashes)

Let's see, that's 5 releases in 7 days; not too shabby. I think it's time to blog about the release process.


Solar 0.16.0 released

Version 0.16.0 of the simple object library and application repository for PHP5 was released yesterday, mostly because I needed to make a minor schema change to one of the "content" tables. Seems the word "position" is reserved in some databases, and I was using that as a column name; the migration script in this release renames it to "pos". There are other minor changes to the Solar_Sql class as well (including more documentation :-).


Solar 0.15.2 released

Just a quick note: fixed two bugs (one in the Solar_Controller_Page::_redirect() method, and one in Solar_App_Bookmarks "user" action for RSS feeds) and released 0.15.2, available now.

Solar is a simple object library and application repository for PHP. It acts as a framework for application development in PHP5, and runs cleanly under E_ALL | E_STRICT error reporting levels.


Solar 0.15.0 alpha released

Yesterday, I uploaded the first non-development (!) release of Solar, version 0.15.0 alpha. I know, "alpha" is only slightly less change-prone than "devel", but it's a move forward and it makes me happy. :-)

You can see the change notes here, but the major updates are:

  • Solar_Uri has been refactored to be much easier to use, from 16 methods down to 5, with better "action" and "public" support, and it works with mod_rewrite now. (N.b.: Jeff Surgeson noted an issue earlier today with non-mod_rewrite subdirectories, but I've committed a fix and will roll 0.15.1 later today.)
  • Solar_User_Auth now calls session_regenerate_id() on all status changes, and calls to session_start() are no longer silenced.
  • Solar_Filter method names have been made consistent with each other.

Speaking of Solar_Filter, it probably bears comparison with Zend_Filter and Zend_InputFilter, seeing as they were recently reviewed at SitePoint. Solar actually has two separate classes for these functions: Solar_Valid, to check if input matches a particular format, and Solar_Filter, to force input to match a particular format. (Solar_Filter was contributed by Matthew Weier O'Phinney some months ago; thanks, Matthew.)

After Solar 0.15.1 has been out the door a couple more days, I'll be ready to start *adding* functionality instead of adjusting (and breaking, and re-fixing ;-) existing functionality. High on the list of new functions will be:

  • a Solar_User_Access class for standardized application-level access control,
  • integration of the Dojo Toolkit for animations and effects (especially on forms),
  • and a new Solar_App_Todo application (for to-do lists, obviously).

I'll be adding more narrative documentation, and perhaps start blogging about usage, as part of future efforts as well.

UPDATE (12:25 CST) -- Just released Solar 0.15.1 with the subdirectory/mod_rewrite fix.


Solar 0.14.0 Released

Second release in two days! Amazing how "employed" I am, after having quit Zend. ;-)

Solar is a simple object library and application repository for PHP5. It is framework-and-library for web-application development, with an emphasis on distribution, localization, and database portability. You can download it via a PEAR channel and browse the Subversion repository.

Here are some highlights from the change notes:

  • The PEAR installer is still the recommended means of installing and upgrading Solar, but it is no longer required. You can now download the pearball directly, place the files manually, and they'll work just fine.
  • Solar/Layouts has moved to Solar/App/Layouts.
  • There are no more app-specific Public/ directories; instead, all public resources for Solar apps are in Solar/App/Public/, and are broken down by images/, scripts/, and styles/.
  • Solar_Filter has more methods, and the names have changed (see the change notes for full details).
  • Solar_Valid has changed some method names (see the change notes for full details)
  • Solar_Controller_Front::_setup() no longer registers sql, user, and content objects; these registrations have been moved down to the Solar_App page controller to lazy-load them as late as possible.

Of particular note, the Solar_Form form processor, and the related Solar_View_Helper_Form class, combined with the new default.css file, now highlight the feedback messages differently based on the form status (i.e., validation "success" or "failure").

I have also updated the solarphp.com site with the new documentation, and the new doc-reading infrastructure. It's mostly technical API documentation, but there are some actual tutorial pieces in place. Also, for now, this means no more user-comments on the site, but I'll keep the existing ones and put them back once I build a new commenting app.

The next major tasks are to refine the Solar_Sql_Select column deconfliction logic, and/or add Solar_Sql_TableRow and _TableRowset classes, perhaps to replace Solar_Sql_Result.

After that, I'll be ready to move Solar out of "development" to an alpha or even beta status. That will be a happy day. :-)


Solar 0.13.0 Released

Solar is a simple object library and application repository for PHP5. It is framework-and-library for web-application development, with an emphasis on distribution, localization, and database portability. You can download it via a PEAR channel and browse the Subversion repository.

The rest of this entry is taken from the migration notes; be sure the read the change log for full details.

Config Key Changes

I've tried to make config keys consistent and standard across all classes. In particular, note these changes:

  • Keys related to username values now use 'handle' (vs 'username')
  • Keys related to password values now use 'passwd' (vs 'password')
  • Keys related to form submission values now use 'submit' (vs 'op')
  • Keys indicating the name of a driver class are now 'driver' (vs 'class')
  • Keys indicating the configuration array for a driver class are now 'config' (vs 'options')

In particular, these config key changes affect Solar_Sql and Solar_User_*.

You should make sure to change your Solar.config.php array to reflect the new keys; e.g., $config['Solar_Sql']['class'] should become $config['Solar_Sql']['driver'].

Also, you should make sure your authentication forms use the new 'handle

Locale Key Changes

Similarly, I've tried to make locale keys consistent and standard across all classes.

  • 'ERR_*' keys are now reserved for exception and error messages, instead of generic feedback (such as form processing).
  • 'SUBMIT_*' keys take the place of earlier 'OP_*' keys, to indicate a form submission value, or a sub-action value (like 'next' or 'previous')
  • New 'SUCCESS_*' and 'FAILURE_*' keys are for non-exception non-error user feedback, such as in form processing
  • Label keys now use '_SUBMIT', '_HANDLE', and '_PASSWD' in relation to the standard config keys for submissions, usernames, and passwords.

Solar_Filter and Solar_Valid

These classes are no longer static; you have to instantiate them. Also, the custom() methods have been renamed to callback() to more clearly indicate how they work.

URI Generation

The Solar_Uri class has two new config keys, 'action' and 'public', to indicate the base href values for actions and public files.

For example, if your front-controller is at '/~username/solar/front.php', set that as your 'action' config key value. Then, all calls to exportAction() will be prefixed with that value. Similarly, if all your public files (CSS, JavaScript, etc) are at '/~username/public', set that as your 'public' config key value, and all calls to exportPublic() will be prefixed with that value.

These keys make it easy to set defaults that will be used throughout Solar, e.g. in Solar_View helpers for action() and publicHref().

View Processing

Solar_View takes the place of Solar_Template, and is a new "flavor" of Savant3 specifically for Solar integration.

There are new helpers as well, in particular the above-mentioned action() and publicHref() for URI generation, as well as new form and form-element helpers with built-in localization using Solar::locale().

When changing your Solar_Template scripts to Solar_View, note that these changes are needed:

  • Solar_Template => Solar_View
  • $tpl->addPath('template', ...) => $view->addTemplatePath(...)
  • $tpl->addPath('resource', ...) => $view->addHelperPath(...)
  • $this->eprint() => echo $this->escape()
  • $this->locale() => $this->getText() || $this->getTextRaw()
  • $this->eprint($this->locale(...)) => echo $this->getText(...)
  • $this->actionLink() => $this->action()
  • $this->ahref() => $this->anchor()
  • $this->form() is completely different; review the Solar_View_Helper_Form documentation to see all the changes. I'll write more about this later.

Front-Controller

The front controller is much lighter now. All layout processing has been moved into the page controller. Also, the way the front controller maps page requests to page classes has been changed. Previously, you would set up an "_app_class" array that mapped short page names to full class names:

<?php
    $_app_class = array(
        'bookmarks' => 'Solar_App_Bookmarks',
        'hello'     => 'Solar_App_Hello',
        'other'     => 'Vendor_App_Other',
    );
?>

In the spirit of auto-discovery, override, and fallback, the front-controller now uses a simpler array of base class names; the short-page name is appended to the class name to get the proper class.

<?php
    $_classes = array(
        'Solar_App',
        'Vendor_Pages',
        'Another_Vendor_App',
    );
?>

If the front-controller receives a request for the page 'hello', it looks first for 'Another_Vendor_App_Hello', then falls back to 'Vendor_Pages_Hello', and finally defaults to 'Solar_App_Hello'.

This allows you to have your own vendor-specific versions of Solar applications. It also means you don't need to add to the map every time you add a new page controller class; just put your app in one of the noted class directories, and the front-controller will find it automatically.

Page Controller

Conversely, the page-controller has become somewhat more robust. It now handles layout processing by reusing the internal view object, which means that all page-level variables are now available to the layout.

In addition, all Solar_App_* classes extend the new Solar_App class, which provides a series of variables specifically for the layout (e.g., $layout_title, $layout_links, etc).

The technique for indicating what variables should be mapped from the URI into the $_info array has changed. Previously, you would key on the action, and provide an array of variable names:

<?php
    $_action_info = array(
        'read' => array('area', 'page');
    )
?>

This would map a URI of "read/Main/HomePage" to the "list" action, and populate $_info['area'] and $_info['page'].

The problem here is that you couldn't tell the page-controller how to map default action variables. E.g., if $_action_default is 'read', then the URI 'Main/HomePage' should map to the 'read' action, but instead it would map to the 'Main' action (which probably doesn't exist).

The new page-controller code uses this format for mapping variables ...

<?php
    $_action_info = array(
        'read' => 'area/page',
    );
?>

... and *does* map variables using the default action_info map, so that calls to non-existent actions map properly. Thus, the 'Main/HomePage' URI will map to the 'read' action and populate the 'area' and 'page' $_info keys for you. This should help make your URIs prettier.

Setting default values for info mapping is easier too; for example:

<?php
    $_action_info = array(
        'read' => 'area=Main/page=HomePage',
    );
?>

Public Directories

The Solar/Layout and the Solar/App classes now have a Public/ directory. These are for storing stylesheets, JavaScript files, images, etc. The problem is that, because Solar is outside the webroot, you need to be able to put these files in a place accessible via http.

There are two ways to do this; either way, you need to create a 'public/Solar' directory in your web root. Then in that location, either:

  • Create a symbolic link to the related Solar "Public/" directories, or
  • Copy the related Solar "Public/" directories

The main drawback is that you need to link to (or copy) each Public/ directory; each new app means a new link. I'm going to try to find some way of either collecting all Public/ resources in one logical place, or automating the linking/copying process as part of the installation process.

Next Tasks

I need to update the site itself with the new documentation. After that, I will...

  • Revisit Solar_Filter and Solar_Valid to standardize the method names
  • Make Solar_Sql_Select a "fluent" class

After that, I think I'm going to be able to call the current codebase relatively stable; not release-candidate quality, but certainly preview-release or beta-level.

Of course I need to continue writing documentation and tests, but since this release should be the last major shakeup for a long time, docs should be easier to build and longer-lasting.


Differences Between Solar and Zend Framework

(This is an edited and collated version of an email thread from the solar-talk list.)

Even though (or perhaps because) the Solar project pre-dates the newly-released Zend Framework by about a year, I've had a couple of private emails asking me if I will discontinue Solar, or rewrite Solar on top of the Zend Framework. The short answer to both of these, at least for now, is "No -- Solar is not going away." Especially now that I'm not at Zend any more. ;-)

The long answer is, "Even though Solar and Zend Framework share a similar underlying architecture, Solar has implementation goals that are quite different than Zend Framework; these differences make it reasonable to continue Solar as a separate project."

Here are some of the main similarities between the two projects:

  • An overarching "main" class of static methods to provide common functionality for the remaining library classes, with things like a registry, a class loader, a file-finder on the include path, etc.
  • Significant adherence to PEAR standards for class naming convention and directory structure, where the class "Example_Class_Name" maps to "Example/Class/Name.php".
  • Classes for common web application needs: input validation and filtering; database API abstraction and query generation; URI manipulation; model, view, and controller classes; and so on. Some of these even have similar names, owing to their common adherence to Design Patterns.
  • They're both still under development. ;-)

But there are some significant technical differences between the two projects. Here are some examples just from the overarching "main" Solar class:

  • Solar uses a unified constructor mechanism. All Solar classes are constructed in exactly the same way, using an array of config options as the only constructor parameter. This makes is possible to provide ...
  • A unified configuration mechanism. Basically, you have a single config file that returns a PHP array keyed on class names. Classes can look up their own configuration automatically when constructed, because the __construct() signature is always the same. This also means that Solar can have ...
  • A universal factory method. A call to Solar::factory('Class_Name'), optionally with a factory-time config to override the defaults, will get you a new instance of any Solar-compatible class. This factory mechanism is used for the Solar::registry() (originally called Solar::shared()), and is also used for ...
  • A unified dependency-injector method. In short, you can call Solar::dependency() and it will retrieve a dependency object for you, either from the registry, from a passed object, or by constructing a new object for you on-the-fly. All you need to do is provide the class name and a specification for the dependency.

Solar currently provides other features not yet present in the Zend Framework:

  • built-in localization
  • read-once flashes
  • automated exception generation with localized messages
  • form processing
  • fully-automated form output generation (whole forms with layout, not just form elements)
  • user authentication and role classes

And of course, Solar is licensed under the LGPL (which is FSF/OSI approved).

So what does Zend Framework have that Solar does not?

  • A whole lot of specialty classes. The Zend Feed, Service, and XMLRPC libraries are fantastic, as well as the PDF generator and the Lucene-based search engine in the incubator. These are all excellent work. Solar has nothing to compare with these; I would call them the real gems of the Zend Framework.
  • About a dozen regular individual and corporate developers, and the backing of a real business, to support and drive its development. Solar only has one guy (yours truly) doing regular development, and the good will of a few regular contributors and users (Clay, Matthew, Jean-Eric, Jeff, Stuardo, and others).
  • A great marketing apparatus. Solar, by contrast, has only word-of-mouth and a few blog entries.
  • More automated tests, and more-and-better documentation. These things I really need to work on for Solar; in fact, documentation has been almost the only thing I've been working on for the past few days (more on that in a later post).

Mike Naberezny brings up a great point about the original question of whether or not Solar is going to continue in relation to Zend Framework:

I think that the underlying premise of these questions is very wrong. It is not necessary to choose one over the other -- you are always free to mix and match them at will. That's always been the PHP way.

I can only agree -- it was like when I got emails asking "how is Solar different from PEAR?". The three (PEAR, Solar, Zend Framework) are not really that different architecturally; Solar owes quite a lot to the PEAR standards for class naming, directory hierarchy, coding standards, and so on. Each of the projects has its own dependencies and idiosyncrasies, but if you know how each project works, then you can work on combining pieces from them effectively. The key differences are in the implementations, the little tweaks each makes to the related design patterns, and the society/culture/support-group that grows up around them. (That last part about culture is too often neglected; I like to think the Solar social norms are more approachable for anyone with a learning/mentoring attitude toward programming.)

Having said that, a programmer or team is probably going to pick a core body of code to work from (even if it's internal-only) and then add to it from other sources. I got the idea the questions were asked in this sense; i.e., "Is Solar going away as a core foundation to start from?" not "Should we use it exclusively?" Of course it would make no sense to use Solar or PEAR or Zend Framework exclusively, but each serves as a pretty good base to work from in their own ways.

Limiting yourself to one source of libraries means giving up one of the primary advantages for using PHP and only limits what you can do.

Totally agreed here; there's no reason not to mix pieces from different code bases, especially regarding ZF and Solar (since their architectures are so similar, and since each has things the other does not).


Resigned from Zend

As my family and a few close friends already know, I have quit Zend. While unfortunate, it was necessary.

My sister Amy once told me that, sometimes, "people don't quit their jobs, they quit their managers" and that is definitely the case here. The people I worked with are some of the best I've ever known (Mike, Matthew, Lucas, and everyone else). The work itself was great; although I got to develop some portions of the new Zend Framework, most of my work was internal-only, and I'm happy I was able to be of service to them. Unfortunately, my manager and I just did not work well together. I thought I could adapt myself to his style, but after three months of back-and-forth it wasn't happening. In the end, there was no reasonable solution other than to tender my resignation, which I did last Tuesday, and my last full day was last Wednesday.

So what's next? I've got enough saved to live on comfortably for the next few months, longer if I go into austerity measures. I think I'm going to take some "me" time for the next few weeks. I can catch up on my reading, work on bringing the core of Solar to a full stable release, drive around and see some friends and family, even start blogging more. After that I can start looking for work again; I think I've earned a bit of vacation time. :-)

Update (next day) ... I don't know what to say; the flood of support and good will from everyone has been amazing. I had no idea. I think I've replied personally to everyone who posted a comment or sent an email; if I have not, please accept my apologies, and my thanks for your good wishes. Thank you all so much. :-)


Savant2 Bugfix Release

David Mytton of Olate noted a typo related to setting __autoload() usage in the Savant2 constructor; I have rolled a new release (version 2.4.3) with that fix in place. Thanks, David!

(Savant is a template system for PHP; it uses PHP itself for the template markup, and provides object-oriented plugins and filters. Savant2 is for PHP4 and PHP5, while Savant3 is the PHP5-only and E_STRICT compliant.)