Paul M. Jones

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

Quick Hits: Savant Forum, Akismet

Two quick hits: first, by popular request, I've started a web-forum for Savant at http://phpsavant.com/forum; it uses Vanilla. Second, because of a recent spate of spam attacks on my blog comments (1000 or more per day), I've activated Akismet; in 24 hours it has captured 3900 spam comments and trackbacks. Makes life a lot easier, although it'd be nice if that was the default spam-catcher for Wordpress.


Solar 0.19.0 released

(Solar, the simple object library and application repository for PHP5, is both a library and framework for developing web applications in PHP5.)

The single biggest change in this release is in the license; we have moved from LGPL to New BSD.

The main additions in this release are project-support related.

  • The Solar_Docs_Apiref class will read an entire Solar-style file hierarchy and parse the inline documentation into an array; you can then build a writer of your own to take that array and generate documentation files from it. (This is how I build the API reference documentation for the Solar web site.)
  • The related Solar_Docs_Phpdoc class will parse a PHPDoc comment block into its summary, narrative, and technical portions. While nowhere near as robust or full-featured as PHPDocumentor, I think the internals of Solar_Docs_Phpdoc are much easier to understand. It doesn't support inline tags, but most of the important block tags are recognized and parsed appropriately (@var, @param, @return, @throws, etc).
  • Solar_Docs_Apiref also makes use of the new Solar_Class_Map, which parses a Solar-style file hierarchy and returns an array of class-name keys with file-name values.
  • There is a new Solar_Log class, with adapters for file-based logging (good for production), echo-based logging (good for development), and multiple log support.

There's one big change in Solar_View: instead of custom helper locations being defined by paths, they are now defined by class names. This means your custom helpers no longer need to be named 'Solar_View_Helper_*'; you can now name them as is proper for their location in the file system. For example, if your helper classes are in the "Vendor/App/Helper/*" directory, you now name each helper class "Vendor_App_Helper_*". This makes helper class names consistent with the rest of Solar.

In line with this, you no longer use Solar_View::addHelperPath(),
getHelperPath(), or setHelperPath(); instead, you addHelperClass(),
getHelperClass(), and setHelperClass(). The class-based methods work
just the same as the path-based methods, except you specify class
name prefixes instead of path prefixes. For example, if you used to
"addHelperPath('Vendor/App/Helper')", you would now "addHelperClass
('Vendor_App_Helper')". Then helpers of that class will be used
before the Solar_View_Helper classes are used.

Also in line with this, the Solar_View 'helper_path' config key has
been renamed 'helper_class', and a new Solar_Controller_Page config
key 'helper_class' has been added so that page controllers know where
helpers are (this is to support view helpers attached to layouts).

Finally, Solar_Test_Suite usage has changed a bit. Instead of
setting a 'sub' key to specify that you want to run a sub-series of
class tests, you specify that same value when calling
Solar_Test_Suite::run($sub).

Future Plans

The next release will concentrate on feature requests, and (sorry!)
another class reorganization so as to be more consistent with pattern-
based names, and to reduce the depth of some parts of the hierarchy.

None of the reorganizational changes should require more than a
simple search-and-replace on the existing code base when complete.
For example, Solar_Sql_Driver will become Solar_Sql_Adapter,
Solar_Cache_File will become Solar_Cache_Adapter_File, and so on.

The User subclasses (Auth, Role, Access) will come to the top of the
hierarchy, so Solar_User_Auth will become Solar_Auth, and the
Solar_User_Auth_Driver files will become Solar_Auth_Adapter files.

There are other change ideas too; you can see those in the todo file
here.


Database access problems

Right after I posted last, there was a database hiccup at my hoster. They've reset it now. Sorry for the site access delays.


"Do Your Best" Is Not A Plan

My buddy Bolivar Shagnasty described a problem he was having with his manager (Dirk Karkles). At their annual review, Dirk wasn't presenting any measurable goals for Bolivar's performance. "I just want you to do your best," said Dirk. "As long as you do your best, I'll be satisfied." Bolivar wasn't happy with this statement, but was unsure why. After all, shouldn't everyone try to do his best?

Bolivar is an excellent worker, but Dirk Karkles would always find something unsatisfactory with Bolivar's performance. "Is this really your best?" he would ask. "Well, yes," Bolivar would say, "but I can tell you're not happy with it. What level of performance on this task would make you happy?"

Dirk's answer was, "I just want you to do your best. I don't want to limit you by setting goals for you. For example, if I set a goal for you of '30', and you're capable of doing '50', then I've cheated you of '20' by setting too low a goal. You don't want me to cheat you of your best performance, do you?"

* * *

As personal philosophy, "do your best" is hard to beat, but as a management plan, it sucks. A manager cannot externally measure when you've attained "your best" in anything but the most strictly-defined activties (like a "personal best" in sports). The developer manager who uses "do your best" as goal direction is being lazy by avoiding the hard work of defining his expectations, and is setting his workers up for failure.

Here are some reasons "do your best" is a poor management plan:

  • It's not measurable.
  • A worker's "best" is a contingent target; it depends on how you marshal your limited resources (time, energy, attention, etc). If you do your absolute top best on one task, it reduces the amount of resources available for another task, so you have not done your "best" on everything. This means there is always something for the manager to point at and say "you're not doing your best."
  • Even assuming that it is a legitimate goal, there is no way to exceed the expectation of "your best." This means doing your absolute top best at all times is only "breaking even" as far as the manager is concerned. This is demoralizing to a worker; every single time he misses even a small thing, he gets picked-at for not doing his best. This leads to lower performance, since the goal of "your best" can never be truly met at all times.
  • It is lazy and greedy on the part of the manager. His job is to meet measurable goals, not to wring workers dry. The line about "then I've cheated you of '20'" above is an example of this.

Does this mean that all management expectations must be measurably defined explicitly in advance? Of course not; there is necessarily a lot of give-and-take in the implicit relationships and expectations of a particular organizational culture. But these implicit expectations are behavioral and interpersonal guides, not management goals. Goals by definition must be measurable; that way, you know when you've achieved them.

The lesson for managers here is: you need to do the hard work of defining your expectations in a measurable way; if you can't do that, don't be surprised if your workers are performing at the level you want. "Do your best" is philosophy, not planning.


Developers and Managers

Shortly, I will post the first of what I expect to be an occasional series of observations on management practice in the development world. Although the series is not PHP-specific, it will be in the context of developer-managers, so I'm categorizing it under both PHP and management.

I hold an undergraduate business degree, have contributed to various management papers and studies, have been an entrepreneur, have rejoiced under good management and suffered under bad management, and have been both a good manager and bad manager. I think these are sufficient qualifications for my opinion having some basis in reality.

To protect the guilty (including myself ;-) I've invented a composite character I'll call "Dirk Karkles" to act the part of the poor or misguided manager; any resemblance to persons living or dead is purely coincidental. Similarly, I'll be using the composite character "Bolivar Shagnasty" from time to time as the developer or other target of Dirk's policies and practices.

My catchphrase for this series is "Don't be a Dirk." :-)


Solar 0.18.0 released, and more about testing

I released Solar version 0.18.0 over the weekend. The biggest change is in the testing methodology (yes, again ;-).

A couple of weeks ago I had an IM-chat with Matthew about testing, and he related the merits of a PHPUnit-style approach, versus the PHPT approach I have blogged about previously.

After having used both, I can say that the procedural PHPT testing style is easier to approach for someone new to testing, and is great for smaller or standalone projects, but it doesn't seem to "scale up" well for larger class structures. The object-oriented testing style used by PHPUnit and SimpleTest requires more inital setup work, but "scales up" much better in larger projects.

I used PHPUnit while working at Zend, but frankly it seemed kind of magical to me; I just didn't get the way it worked internally, especially setting up test suites. PHPUnit is a robust and highly configurable piece of software, but it does seem a bit impenetrable. This may be due to the fact that PHPUnit is ideologically derived from similar Java projects, and I am just not a Java kind of guy.

So in a spirit of meet-in-the-middle, I coded up a pair of Solar-based unit-test classes to take over from Solar_Test_Assert and .phpt, then converted a couple of existing .phpt tests to the new technique. It worked like a champ. After adding a Solar_Test_Suite class to scan a base directory for test cases and generate TAP-compliant output, I converted all the remaining .phpt tests to the new object-oriented testing style. The new test suites run much faster, and it's easier to extend an abstract test-class than it is to copy and modify a bunch of .phpt files.

You can see some code examples here:

The test-runner is very easy to use. If you want to run all tests, just issue "php run.php" from that directory and it will recursively run everything in the Test/* directory. If you want to run a sub-series of tests, pass a class name as the only parameter; e.g., to run the Solar_Uri tests, issue "php run.php Solar_Uri".

Test-runner output is TAP-compliant (which should make Mike Lively happy ;-). Among other things, this means it should be easy (in theory) to hook in to apache-test. TAP output looks something like the following:

bash-2.05$ php run.php Solar_Uri
1..21
ok 1 - Test_Solar_Uri::test__construct
ok 2 - Test_Solar_Uri::test_config
ok 3 - Test_Solar_Uri::testSet
ok 4 - Test_Solar_Uri::testFetch
ok 5 - Test_Solar_Uri::testQuick
ok 6 - Test_Solar_Uri::testSetQuery
ok 7 - Test_Solar_Uri::testSetPath
ok 8 - Test_Solar_Uri_Action::testFetch
ok 9 - Test_Solar_Uri_Action::testQuick
ok 10 - Test_Solar_Uri_Action::test__construct
ok 11 - Test_Solar_Uri_Action::test_config
ok 12 - Test_Solar_Uri_Action::testSet
ok 13 - Test_Solar_Uri_Action::testSetQuery
ok 14 - Test_Solar_Uri_Action::testSetPath
ok 15 - Test_Solar_Uri_Public::testFetch
ok 16 - Test_Solar_Uri_Public::testQuick
ok 17 - Test_Solar_Uri_Public::test__construct
ok 18 - Test_Solar_Uri_Public::test_config
ok 19 - Test_Solar_Uri_Public::testSet
ok 20 - Test_Solar_Uri_Public::testSetQuery
ok 21 - Test_Solar_Uri_Public::testSetPath
21/21 tests, 0 seconds
0 fail, 0 todo, 0 skip, 21 pass
bash-2.05$

There's also a test-maker script that generates a skeleton test class, with a test method for each public method in the target class. Just issue "php make.php My_Class_Name".

And of course, if you write your libraries to Solar standards (which are essentially PEAR standards with a couple of additional points) then you too can use the Solar_Test tools to make your testing life much easier.

Update (11 July 2006): Updated links to point to new SVN directories. Thanks, Clay Loveless.


PHPBlox from Zend?

I just read this article from PHP architect about QEDWiki, and at the end they mention something called PHPBlox. A quick Google search reveals this .swf presentation at Zend Devzone. Anybody know if this is supposed to be a replacement for the Zend IDE, or how it relates to the Zend Framework?


ASP-Style Programming in PHP?

An old buddy of mine wants to start using PHP, but the problem is that he's been working with Microsoft technologies for so long, he doesn't get "the PHP way". His background, for many years, has been with ASP.NET, COM, and those sorts of things.

As it turns out, there's a PHP framework out there that maps well to "the ASP.NET way": Prado.

http://www.pradosoft.com/

They're at a 3.0 release as of a week ago, and won the Zend PHP5 competition last year, so that should speak to the quality of the software. From the page about PRADO we can see they base their concepts on a component model, so it's very like Apache Tapestry, Borland Delphi, and (for the guy who asked me originally) Microsoft ASP:

Most PHP frameworks are trying to establish a loose standard of organizing PHP programming, most preferably the MVC (model-view-controller) model. It is difficult to compare PRADO with these frameworks because they have different focuses. What we can say is, PRADO is more like a high-level language built upon PHP while those MVC frameworks stand for the best programming practices. Both aim to help developers to rapidly complete Web application development. The advantage of PRADO is its rich set of prebuilt powerful components and extreme reusability of the PRADO code, while the advantage of the MVC frameworks is the complete separation of model, view and controller, which greatly facilitates team integration.

I can see that PRADO is very much *not* my style; I don't know a lot of open-source scripter types (Perl/PHP/Python/Ruby) who would really go for this, as it seems over-architected to me. This is not meant as a critical point against PRADO, because it obviously fills the needs for those who like that style of programming. Personally, I don't really want another language on top of PHP (this relates back to why I started Savant; i.e., as a response to Smarty). But if you're used to Microsoft or Java, I can see how this would be an easy way to get on board in the open-source world and start using PHP with your previous non-PHP habits.

Which brings me to a philosophical point: is there a better phrase than "the PHP way" to describe the way we expect PHP apps to be built, or some sort of metaphor that encapsulates the concepts related to "the PHP way"? Not just MVC, because PRADO appears to do that, but some other way of describing succinctly how a PHP app "ought" to be. Maybe there isn't one single description.

Having said that, I think these template notes from the WACT project might be useful hints; see also here. My bet is that the greater majority of PHP/Perl/Python/Ruby programmers are "imperative" guys than "declarative" guys, and maybe that plays into what a proper metaphor might be. Prado/ASP.NET are clearly more declarative, and Savant/Solar/etc are clearly more imperative. Perhaps Less Code is related to the metaphor we want.

I'd be interested to hear comments and feedback, if for no other reason than to compare and contrast the different approaches.

UPDATE (2006-05-04): Interesting analysis from here; note that it is originally from Dec 2005.


Instant Forms from Tables with Solar

I was talking with Matthew this morning, and he asked if I was setting up a methodology to create forms using table definitions. My answer was "yes!".

Here's an example: 2 lines to start Solar, 2 lines to connect to an existing Solar_Sql_Table, 2 lines to create a Solar_Form using the table columns, and 2 lines to display it with Solar_View using the associated form helper. (The "Solar_Model_Nodes" table is part of the existing Solar_Content domain model.)

<?php
// prelims
require 'Solar.php';
Solar::start();

// sql and table connection
Solar::register('sql', 'Solar_Sql');
$table = Solar::factory('Solar_Model_Nodes');

// get a form and load elements from the table
$form = Solar::factory('Solar_Form');
$form->load('Solar_Form_Load_Table', $table);

// display the form
$view = Solar::factory('Solar_View');
echo $view->form($form);

// done!
Solar::stop();
?>

This creates an element for each column and a form-level validation based on the column datatype. Number and string columns get a properly-sized "text" element, CLOBs get a "textarea", and booleans get a checkbox. If the column has its own validation of "inList" or "inKeys", it gets a "select" element of the allowed values. Soon, when I add Ajax-y widgets, date/time/timestamp columns will get date-picker elements.

The $form->load() call can use 'Solar_Form_Load_Xml' too, if you want to define forms using XML. (I don't, but Matthew liked the idea and contributed the code.)

If you only want certain columns, you can pass a list of them as the third param to $form->load():

$cols = array('email', 'subj', 'body');
$form->load('Solar_Form_Load_Table', $table, $cols);

Now, this is not all sweetness and light; the magic requires that you do the work of definining your columns in the Solar_Sql_Table class. Lucky for us, that's pretty easy. More on that in another post.


Solar 0.17.0 Released

This release of Solar includes a minor BC break in how Solar_Sql_Table lets you set up column validation. It also standardizes how validation routines get feedback messages. The change notes are:

* Solar_Sql_Table

    * When specifying a 'valid' key for a column, you now get only
      one validation; if you need more than one validation routine,
      use the 'multiple' method.  Also, you can now specify the
      validation message key you want to use, instead of it being
      forced to 'VALID_COLNAME' (where COLNAME is the column name).
      You can still use a string as the 'valid' key value for simple
      validations.  These changes per discussion with Jeff Surgeson.

    * Uses more Solar_Valid validations internally now

* Solar_Form_Load_Table

    * Modified loading logic to use the new Solar_Sql_Table::$_col
      'valid' key value structure.

* Solar_Valid

    * New method feedback() standardizes Solar_Sql_Table and Solar_Form
      calls where a validation message is returned when data validation
      fails; this is the opposite of all other Solar_Valid methods, in
      that an empty return value indicated success, not failure.

    * New method range() checks a value against min and max range

    * New method rangeLength() checks the length of a value against
      min and max lengths

    * Renamed method inScope() to scope()