Paul M. Jones

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

Savant3-3.0.0dev1

The first development release of Savant version 3 is ready for download. (Savant is a template system for PHP that uses PHP itself as the template markup language; it supports plugins and output filters, as well as optional compilers.) The following is a link to the PEAR package tarball.

http://phpsavant.com/Savant3-3.0.0dev1.tgz

The release rate for Savant has been very high (three major versions in 18 months). This is because had the bad luck (or poor foresight) to start Savant2 on the cusp of the PHP5 release, so it had to handle both PHP4 and PHP5. With any luck, Savant3 should be the last major version number until PHP6.

Some change notes follow, but they are not a replacement for examining the source code. As development progresses, I'll write up real documentation.

CHANGE NOTES:

Savant2 code was 31793 bytes with comments; Savant3 is 23728 bytes with comments. This is not due to reduced comments. ;-)

PHP5 only, uses public/protected (no private because we want to allow extension) and runs clean under E_ALL|E_STRICT error reporting.

Real unit tests using the .phpt framework. The 'form' plugin test fails becuase the test is not complete yet, but all others pass.

Reduced number of default plugins, mostly from the form element suite. The plugin 'modify' has been renamed to 'safe' but I'm not sure that will remain the case.

In Savant2, you were not supposed to assign a variable with a name starting in "_". In Savant3, there no more restrictions on what you can name an assigned variable; any allowed PHP name is OK.

In Savant2, configuration properties were prefixed with underscore. In Savant3, config properties are stored as a static array inside the conf() method. This has the benefit of keeping all configs away from the assigned variable space (i.e., the object properties). Access only through public getters and setters (which are nominally dumb, but in this case are necessary).

The assign() and assignRef() methods work just like always, but return true/false instead of error objects.

Variable extraction is still allowed. Initially I disliked the idea, but the new configuration scheme allows a very simple and elegant extraction using extract() and get_object_vars($this).

Restricted directories are always on (i.e., Savant2::setRestrict(true) is always the case).

Filters are now simply callbacks; you can specify a function, static class method, or object instance method. The callback should take one parameter, the buffered template output.

Filters are now applied through the native PHP ob_start() callback system, so you can do more complex stuff like ob_gzhandler. However, it makes filters harder to debug, as they occur within the ob_start() process.

Compilers are now simply callbacks; you can specify a function, static class method, or object instance method. The callback should take two parameters, the path to the source template and a reference to the calling Savant object.

No more methods like clear() or getVars(); you can access all assigned variables as public properties of the template object.

No more loading or unloading of plugins or filters; all happens automatically. Once a plugin is instantiated, you cannot un-instantiate it.

Plugins always get a reference to the calling Savant object, just like in Savant1.

Plugin classes must use the method name as their "main" method instead of plugin(). E.g., the 'form' plugin uses form() instead of plugin() as the main method. This is because E_STRICT forces you to have the exact same number of parameters when extending a class.

Plugins are activated via the magic method __call(); thus, no more plugin() method per se. Plugin instance are stored in a static array inside __call(); they are not available to the rest of the object.

All plugins return instead of echo; to display plugin output, use "echo $this->pluginName()". This is because of magic method __toString() requirements. Thus, no more splugin() method.

You can call display() and fetch() the way you did in Savant2, but you can also "echo $Savant;" as the equivalent of display().

The following old methods are rougly congruent to these new method names (this is only a partial list).

    Savant2::loadTemplate() ~= Savant3::template()
    Savant2::setError()     ~= Savant3::setErrorType()
    Savant2::loadPlugin()   ~= Savant3::setPluginConf()
    Savant2::loadFilter()   ~= Savant3::addFilters()
    Savant2::plugin('xxxx') ~= Savant3::xxxx()

Savant: Why Plugins?

A good question from the Savant-Talk list today. The quote is from the questioner, and the remainder is my response (almost verbatim from the list).

But could You PLS explain me shortly the main idea of plugins? I mean I can not understand that there is a need to create for example any html form fields by php... I still see the idea of templates as separation of business logic from display logic. From my point of view such example form fields shoud be still in template but not generated by php. So maybe such ones should be provided quite so as a templates or something like that. Only the values should come to them.

Well my wonders goes farther - why to use ANY php or savant function INSIDE template...(?)

First off, use of plugins is not required, of course, so if you don't like them you don't need to use them. This is kind of a weak argument, I know, because the code for plugin support is there whether you like it or not -- in Savant3 this will end up being about 30 lines (with comments it's about 50 lines), which I think is very small for the power you can get from them.

But second, and more importantly, they are convenient time-savers. I completely agree with you when you say you "see the idea of templates as separation of business logic from display logic." To that, I say: a plugin is just an encapsulated, reusable portion of display logic. It gets tiresome to write forms by hand all the time. Instead of *having* to write a select box by looping through options passed in as an assigned variable, and mixing the PHP echo code with the literal HTML, you can use one line in the template to call the 'form' plugin and generate a select box with correct items pre-selected for you.

One argument against plugins is that they are not a necessary component of a template engine; if you want convenience methods, you can instantiate your own output objects and assign them to the template, or write them as global functions, or call them as static class methods. This is a perfectly valid argument. However, to my mind, that kind of thing is a "pattern" of development, and will end up looking nearly the same across all implementations. All that Savant does is take the common aspects of that pattern and codify them for common use so that different developers can collaborate easily and effectively using the same higher-level tool set.

This is not a comprehensive defense of plugins, but I hope this brief essay begins to show why plugins can be useful and worthwhile in a template system.


Savant3 and Unit Testing

I'm starting work on Savant3, which will be PHP5 E_STRICT compliant, and the work is going nicely; I should have an alpha release at the end of the week. This time around, I'm doing real unit tests, and I can now say I am a fan of unit testing. With Savant2, I used "eyeball" testing (described here) but that was no fun at all.

The real problem with unit tests is not writing them; in fact, it's kind of fun trying to figure out how to break the library and then come up with a test for it. No, the trouble was figuring out how to *do* unit tests in the first place. After cursory reviews of SimpleTest and PHPUnit, I was less than enthusiastic; they are their own applications in many ways, and not that intuitive for the new unit-testing initiate (me).

However, the .phpt methodology easy to approach and apply ... once I found it and figured out how to do it. There is some official documentation here (although it does not seem to be widely advertised), and a tutorial-like overview from Aaron Wormus here at the very end of the article.

Between those two pages, and a day spent experimenting, I am now putting together a unit test suite for Savant3. Let me tell you, it is *much* easier to call pear run-tests *.phpt than it is to load the individual Savant2 test pages in a browser and eyeball them for errors. I'm not sure how difficult it would be to apply unit testing to a complex application, but in a library with limited behaviors, it's relatively easy to do and has a high reward-to-effort ratio.

Update (19 Jan, 23:15 CST): You can find the Savant3 CVS repository here.


YaWiki 0.20 released

I have just released YaWiki 0.20. The change notes are:

* Added "create new page" link in the authentication box, so you can create a new page directly instead of having to edit an existing page (requires Javascript)

* Added "edit this page" link in the authentication box, so you don't need to scroll all the way down to end of the page

* Can now specify i18n strings for submit buttons (Save, Cancel, Delete, etc) and scripts will honor them. See the new Yawp.conf-dist.php file for more information. Per request from Heiner Gassen.

* History links to older page versions now bring up the proper version, not the current version. Per note from Lukas Smith.


Free from Windows At Last

As of a couple of days ago, I was able to retire my Windows box completely. My house is now all Macintosh all the time. (Well, except for the Dell laptop I have signed out from the school. ;-)

Until recently, I needed that Windows machine for only one thing: Quicken, my personal finance software. Earlier versions of Quicken for the Mac had been disappointing, so Windows it had to be. However, Quicken 2005 for Mac has worked out just fine; I'm not importing the old Windows data, I'm starting clean, and keeping the old Quicken data as an archive. So goodbye Windows, hello extra desk space, and thank you Jebus.


YaWiki 0.19 alpha released

YaWiki is a wiki-ish CMS, or CMS-ish wiki, primarily for collaborative documentation efforts; it uses Yawp and PEAR as its foundation.

This release adds an enhancement that was removed in earlier versions. The AreaMap page, which allows you to add navigational hints such as tabs and sidebars, now allows you to specify an optional navigation title for the element. This was available in early versions YaWiki, before it supported freelinks; after freelinks became available, navigation element titles were taken from the page title. Due to consistent user feedback, it appears I was unwise to take away the alternate titling, so now it's back. Use a pipe character ("|") on an AreaMap line to indicate the navigation title; e.g., "HomePage | Welcome!" will link to the home page, but the navigation element text will be "Welcome!" regardless of what the HomePage page title is.

You can view the list of changes for this release here.


Blogs As Emergent Journalism

The Belmont Club today talks about bloggers and blogging as an emergent phenomenon made possible by the internet.

The blogosphere is a specific manifestation -- and by no means the only one -- of the networks made possible by the Internet which can be imperfectly compared to the emerging nervous system of a growing organism. Once the software and infrastructure to self-publish was in place, it was natural that analytical cells, or groups of cells would take inputs from other parts of the system and process them. The result was 'instant punditry', which was nothing more than the public exchange of analysis on any subject -- politics, culture and war just happened to be the three most popular. It enabled lawyers to offer opinions on law; military men on things military; scientists on things scientific. And suddenly the journalistic opinion editors found themselves at an increasing disadvantage. While individual bloggers might not have the journalistic experience of the newspaper professionals, they had the inestimable edge of being experts, sometimes the absolute authorities in their respective fields. This is exactly what happened in Memogate. People who had designed Adobe fonts and written desktop publishing programs knew the memos were computer generated and were not going to be overawed by Dan Rather's experts asserting the contrary. They were the real experts and to make an impact they did not have to be correct across a large range of issues. They only had to be right in the one thing they knew best and from that vantage could hammer a mainstream pundit into the dust. Rather's defeat at the hands of Buckhead was not accidental. It was inevitable.

Read the whole thing; Wretchard talks about digital cameras being the sensory apparatus of the internet, and much more.


Yawp 1.0.5 Released

Yawp is a single PEAR-compliant class that encapsulates a number of other PEAR classes, and ties them all together with a simple configuration file. This release provides a minor functionality improvement.

Previously, when a hook script was called, it would be included directly in the calling code (e.g., a 'start' hook would be include()-ed in the middle of the Yawp::start() method; same for login, logout, and authErr hooks). This means it would be possible for the included script to use (and possibly overwrite) variables in the calling Yawp method; while not a security violation, that kind of thing could lead to unexpected behavior, and requires a much closer knowledge of the Yawp internals than should be necessary.

To solve this problem, I have added a method called run() -- all it does is include a file. It doesn't even need a parameter (using one would create a new variable in the scope of the function, and we want *everything* isolated for the included script).

function run() { include func_get_arg(0); }

This has the benefit of executing a script in its own scope, so that the hook script cannot accidentally overwrite variables in the calling Yawp method. All hooks now use the Yawp::run() method instead of include().


Responding to Rare Events

From Belmont Club:

In an abstract way, the information flows surrounding the Tsunami of December 2004 structurally resembled those preceding the Pearl Harbor and September 11 attacks. The raw data announcing the unfolding threat was there, yet the pattern so evident in hindsight was invisible to those who were not looking for it. But if tsunamis and asteroid strikes are rare events, they are comparatively more common than that still rarer object, the unprecedented event: the something that has never happened before. Threats like that can emerge suddenly out of chaotic systems, like WMD terrorism or new viral plagues. Against such events, specific precautions are impossible because no one can prepare for what cannot be foreseen. The real challenge is not so much to create a new dedicated network of staring systems against known threats but to tie current sensors to systems which are capable of cognition. The most valuable survival asset is situational awareness -- the ability to recognize threats you have never seen before and respond in an evolving manner -- and that capability has not yet come to the world as a whole.

And his concluding words: "...the world is not and was never a paradaisal Gaia but a dangerous place filled with peril both natural and man-made. On the days we forget the ocean is there to remind us."


"Universal" College Education Not Necessarily A Good Thing

I love it when other people say what I've been saying (although other people usually say it better). This is from Agoraphilia.

Here are some highlights:

Like it or not, some people are just not college material; they would be better served by vocational or on-the-job training (or by a better high school education than our public schools provide).

Moreover, the attempt to provide universal higher education has the pernicious effect of reducing the value of higher education. Radley Balko explains part of the story: as the supply of people with college degrees rises, the wages of people with college degrees will tend to fall (or, more accurately, not rise as quickly as they otherwise would, since other factors like technological progress tend to drive wages up). But the wage effect is not necessarily a bad thing -- competition is good, even (especially!) among people with desirable skills. My point, at which Radley also hints, is that the incentives created by policies designed to universalize higher education systematically drive down the quality of education.

Why? Three reasons. First, the policies in question typically provide education at far below its real cost. ...

Second, and relatedly, the existence of a large class of weak or unmotivated students changes the incentives of educators. ...

Third, the existence of (near) universal higher education has an undesirable effect on the quality of high school education. Students know, because they are told by their counselors, that a C average is sufficient to get them into a state university. ...

But read the whole thing, really.