Solar 0.2.0 Released

Solar is a simple object library and application repository for PHP5 under E_STRICT. It takes hints from PEAR and Horde, and bears some superficial resemblance to Ruby on Rails for its MVC application directory structure.

API documentation is becoming voluminous, and is bundled with the package now, along with an example Solar.config.php file.

The change notes for this release are:

* WARNING: This is a public development release, and is not
    yet stable.

* API documentation is now distributed with Solar
    installations; this accounts for 90% or more of the
    package size.  Look in your PEAR docs/ directory for the
    new files.

* Added a Solar.config.php example to documentation.  Look
    for it in your PEAR docs/ directory.

* Added Solar_Template class (is an extended and customized
    Savant3 class).  Savant3 is now distributed in the
    Template/ directory.

* Solar_App:

    * Converted $action_src, $action_var, and
        $action_default to array keys ($action['src'],
        $action['var'], $action['default']).

    * Added a $view property (is a reference to the shared
        object called 'template'; i.e., a Solar_Template
        object).  Sets up the views directory
        (template_path) automatically, and sets up the added
        paths for user-developed themes automatically.

    * Calling view() now returns the output of the named
        named view, not the path to the file for that view.

    * Calling model() now returns a new instance of the
        named model, instead of the path to the file for
        that model.

* Converted Solar_App_Bugs and Solar_App_Bookmarks to use
    the new and different Solar_App features for $action,
    $view, and view().

* Solar_Cache_File:

    * Now uses '.serial' as the filename extension for
        serialized files no matter what

    * No more prefixing of file names

    * Config values are transferred to protected properties
        so they cannot be changed after instantiation

    * Creates the cache directory if it does not already
        exist.

    * Always hashes the cache entry key (prevents directory
        traversals)

* Solar_Cache_Mem renamed to Solar_Cache_Memcache.

* Solar_App_Bookmarks: if you try to QuickMark a URI that
    you already have bookmarked, you are redirected to that
    entry in your bookmarks (instead of it being stored more
    than once).

* Solar_Sql_Driver: now uses lazy connections. it connects
    at exec() time instead of __construct() time.  This puts
    the speed hit of connecting to the database until the
    last possible instant, which means merely instantiating
    an Sql object should not slow you down.

My Yawp Article Is Published

This month’s International PHP Magazine has my article on Yawp; woohoo! 🙂

Yawp (for PHP4 and non-strict PHP5) is a single PEAR-compliant class that encapsulates a number of other PEAR classes, and ties them all together with a simple configuration file. When you use Yawp as the base of your application, you get…

    * A single easy-to-edit config file
    * Automated authentication processing
    * Automatic creation of common objects:
          o Database abstraction layer
          o Disk cache
          o Composite logger
          o Benchmark timer
          o Variable dumper
    * Safe accessor methods for PATH_INFO, GET, POST, and configuration values

Be sure the visit the Yawp website for full API and user documentation.

How is Solar different from PEAR?

Regarding the latest release of Solar, Atrus asked: “How is this significantly different from PEAR?”

It’s a good question. Let me outline some of what I think are the major differences. Please note that with maybe one exception, none of this is intended as criticism of PEAR, only as description. You can figure out for yourself where the criticism lies.

  1. As Atrus noted, Solar is built from the ground up for E_STRICT compliance in PHP5, which makes it incompatible with PHP4.
  2. Having said that, Solar is very much like PEAR in concept, in that one of its goals is to be a class library providing common web application functions: error handling, user authentication, querying SQL sources, OR mangement, and so on. However, Solar differs from PEAR in execution of that concept:
    • All class constructors have identical parameters (a single associative array).
    • Almost all Solar classes are extended from a single base class. The base class is relatively light and provides common functionality such as error generation and localization string handling. This means that all Solar classes have localization built in from the start. (Some classes are not extended from the base class; typically, these classes are collections of static methods.)
    • The classes are much more consistent with each other because they are the product of one coder (your humble servant).
    • The code is better-commented, easier to read, simpler, and more straightforward than most of what is found in PEAR.
  3. Solar is quite different from PEAR in that it is also a framework.
    • Yawp was my one attempt at building a straightforward and comprehensible foundation for rapid application development out of PEAR components. While Yawp is moderately successful at this, the inconsistency between PEAR classes makes it necessary to custom-code the “glue” between those classes so they can interoperate. With Solar, the classes are all so similar in implementation that it is much easier to make them interoperate.
    • As a framework, Solar has a consistent and extensible configuration mechanism that is identical for each class in the system; PEAR does not have such a mechanism.
    • Solar provides built-in methods for safe retrieval of user-provided variables and configuration values; PEAR does not.
  4. Solar provides mid-level independent application components out of the box (these are called Solar Cells). For example, there is a universal tagging mechanism, a combined comments/trackback storage system, a bug-tracking entity, and so on. Combining these components with a little procedural glue (e.g. in a controller action) gets you a lot of good functionality in a much less time. PEAR has no such unified component mechanism that I know of (happy to be proved wrong here).
  5. Solar comes with useful applications out-of-the-box (a bug tracker and a del.icio.us-like bookmark system) with more on the way, such as blog and wiki. With the one exception of phpDocumentor, PEAR does not. You might say that the “AR” in Solar is actually true. 😉 In this way, Solar might be more like Horde than PEAR.
  6. The communities are very different. PEAR is a mishmash of developers with their own goals and desires, driven by a voting system for new packages, herded like cats by an unelected Group that prefers to force collaboration under a rule of seniority, and with various (probably unfixable) flaws in the social norms; in other words, it is much like elitist democracy or mob rule under appointed oligarchy. Solar is more like beneficent dictatorship where the main rule is “be nice.” 😉
  7. Last but not least: PEAR has been in place for a few years now, and Solar is very young (first release was only 2 months ago). If you want to get in on the ground floor of a growing community of PHP5 developers and make a difference in a new project, this might be the place for you. Sign up for the mailing list at the Solar home page: http://solarphp.com.

Solar 0.1.2 released

Busy weekend; lots of MBA coursework completed, and three releases of Solar. This one is a combined bugfix and function enhancement. Change notes are:

* WARNING: This is a public development release, and is not
yet stable.

* Removed notify() method and related properties from
Solar_Cell_Comments, as that method is more properly in
the realm of the application (which may notify in many
different ways, if at all).

* Added setup() method to Solar_App for easier setting of
initial property values in extended classes (e.g., you
can use functions this way).  Added per discussion with
Bhishma Parva (thanks for pointing it out).

* Solar_Uri now automatically urlencodes query and pathinfo
values at export() time.

* Solar_App_Bookmarks now gives the correct backlink in
edit.php view (because of the new Solar_Uri automatic
urlencoding).

* Solar_Cell_Bugs now shows priority options.

* Solar_Cell_Bugs edit form now allows editing of the summary.

Solar 0.1.1 released

Solar is a simple object library and application repository for PHP; Solar applications bear some superficial resemblance to Ruby on Rails. Above all, Solar code is comprehensible and easy to understand, even for the relatively new PHP programmer, because it is exceptionally well-commented and adheres to a simple, straightforward style.

This is a bugfix release. You really should join the Solar mailing list; some good info on setting up Solar.config.php is there.

The changes notes for this release are:

* WARNING: This is a public development release, and is not
yet stable.

* Solar::start() now automatically shares and starts a Solar_User
object ('user') ... this makes sure authentication is always
processed.

* Solar_App::automap() now only attempts scandir() if the
requested directory actually exists.

* Solar_App_Bugs controllers for edit.php and item.php would
try to add the bug summary as the comment subject line,
but the max subject line is 64 chars (the max summary is
255) ... this would cause silent errors and fail to add
the comment.  S_A_Bugs no longer adds a subject line,
which seems to correct the issue.

Going To Cancun!

Well, I just signed up for PHP Tropics conference in Cancun being held by PHP Architect. This is my first PHP conference, and I’m really looking forward to meeting in person some of the people with whom I have corresponded for so long. And goodness knows I need a vacation. 🙂

Solar 0.1.0 Released

Solar is a simple object library and application repository for PHP5. This is a development release made in the middle of the night due to insomnia. 😉

Among other things, there is a second proof-of-concept application included; Solar_App_Bookmarks mimics and extends a lot of del.icio.us functionality. I’m already using it instead of del.icio.us, as I can assign arbitrary ranks to links and then get an RSS feed in that order, instead of descending by timestamp.

Finally, I’ve started an open wiki for documentation (now that I have two good working apps I think it’s time to describe how to install and use them ;-).

The change notes are:

* WARNING: This is a public development release, and is not
  yet stable.

* WARNING: There are database schema changes in this release.

* Fixed bug in Solar::pathinfo(); elements were all off by 1
  (needed to shift off the first returned element, as it
  is always blank).

* In Solar_Sql_Entity::buildSchema(), the 'join' value must
  now be a $schema['rel'] keyword.

* Solar_Sql_Entity::update() now retains primary keys in a
  separate array (instead of unsetting them entirely) and
  restores them after the update process; this will help
  with post-update tasks that need the primary key.

* Solar_Sql_Entity::selectCount() and selectPages() have
  been combined into countPages(), which returns an array
  with both the row count and the page count.

* Solar_User_Auth_Htpasswd now rejects DES-encrypted
  passwords longer than 8 characters due to a limitation
  in crypt().  Have added support for SHA1 (thanks Tomas
  Cox) and APR1-MD5 (thanks Mike Wallner) encrypted
  passwords to offset this.

* Added Solar_User_Auth_Multi to perform authentication
  against multiple fallback sources.

* Solar_User_Role now uses a single role driver class by
  default.

* Added Solar_User_Role_Multi to pull from multiple
  group/role sources.

* Renamed Solar_Cell_Talk to Solar_Cell_Comments for clarity
  (this includes a set of schema changes as well as
  in-place support for trackback and pingback storage).

* Schema changes to Solar_Cell_Bugs.

* Added Solar_Uri as a URI import/manipulate/export tool,
  generally useful for building navigation links.  Works
  with query elements as well as path_info elements.

* Added Solar_Cell_Tags as a shared tag-search resource to
  support ubiquitous tagging across all Solar applications.

* Added Solar_Cell_Bookmarks and Solar_App_Bookmarks for
  bookmark management similar to the del.icio.us service.

* Various locale string additions and modifications.

Htpasswd and crypt() in Solar — fixed!

(Well, sort of fixed.) This post originates from an issue I had with htpasswd files and crypt(); effectively, crypt() only looks at the first 8 characters in a password and validates if they match, regardless of the rest of the password. It turns out this is a known limitation of crypt(); it generated a fair amount of discussion on the pear-dev mailing list.

So while Solar_User_Auth_Htpasswd will still reject passwords longer than 8 characters as a security measure against the default DES crypt() limitation, I have been able to add support for SHA1 and APR1-MD5 encrypted passwords in htpasswd files. This will allow you to use much longer passwords. The new code comes courtesy of two PEAR developers: from a tip by Tomas V. V. Cox for SHA1, and from Mike Wallner’s excellent crypt_apr_md5() method in File_Passwd. Thanks, guys!

(A side note: Apache htpasswd does not use a standard MD5 encryption routine, which is why just calling md5() from PHP was not a viable option.)

Password problems with crypt() and htpasswd files

In working with Solar today, I discovered an issue related to the crypt() function and password files generated by Apache htpasswd. Technically, it’s not a security issue with either of those fine programs, because they do work as documented and intended. However, due to my own ignorance of the limitations of crypt(), I created a security issue of my own; perhaps this post will help others avoid it.

The Solar_User_Auth class is very much like the PEAR Auth class, in that it lets you pick different container or storage types for your username/password authentication. You can use a database table, LDAP, POP or IMAP email account, a .ini file, and more. Similar to LiveUser, Solar_User_Auth also comes with a “Multi” container that lets you specify multiple authentication sources, so you can fall back from one to another automatically.

My problem today was with the “Htpasswd” container for Solar_User_Auth. Apache comes with a utility called “htpasswd” which lets you create a file of usernames and encrypted passwords. The file format is pretty straightforward; each line consists of “username:cryptedpass”. To create a htpasswd file and insert the first username/password combination, you would issue “htpasswd -c /where/you/want/htpasswd.data -a someuser”; it will create the htpasswd.data file and prompt you for the password for “someuser”, encrypting the password with the system crypt() function.

Now here’s the thing about crypt() … effectively, it only looks at the first 8 characters of the password to generate the encrypted hash. (Yes, there are ways to make crypt() use a longer salt, but that’s not pertinent to this particular discussion, as we are only concerned with the way Apache htpasswd uses the crypt() function.)

Thus, if you have a password *longer* than 8 characters, as long as the first 8 characters match, crypt() will call it a valid match. For example, if your password is “password” and is stored as a crypted hash in a htpasswd file, checking against “passwordX” will be exactly the same as checking against “password”, “password123”, and so on; that is, it will be returned as a valid check because the first 8 characters match properly. Similarly, if your password is “longpassword” and you check against only “longpass”, that will be returned as valid, too.

Obviously this is a problem. The solution, at least for Solar_User_Auth_Htpasswd, is that from now on, password checks longer than 8 characters will be rejected automatically as invalid. This sidesteps the problem entirely, even though it does limit users who want to use the Htpasswd driver to passwords of 8 characters or less. The next release of Solar will have this patch in place, and it has already been committed to the Subversion repository for Solar.

Have I missed anything important here? Has anyone else out there run into anything like this? If so, what was your solution?

Update: (2005-04-14) Although I’m retaining the 8-character limit under default DES encryption, I’ve added SHA1 and APR1-MD5 support, which should help a great deal.