What's That Noise?! [Ian Kallen's Weblog]

All | LAMP | Music | Java | Ruby | The Agilist | Musings | Commute | Ball
« Previous page | Main | Next page »

20060206 Monday February 06, 2006

PHP Best Practices, Frameworks and Tools

I've annoyed PHP enthusiasts, friends and colleagues alike, with my distaste for PHP. There's nothing intrinsically bad, buggy or poorly performing about PHP per se. It's real simple: a lot of PHP code that I've had to pick up the hood on is a mess and is susceptible to worlds of instability and bugs. The common symptoms I see are mixing business logic, undeclared variables and globals, display code and SQL all scrambled up along with a complete absence of automated tests -- an intractable mess as soon as you want to refactor it. Sorry, my PHP-loving friends, it's nothing personal. I've used PHP longer than most of you. In 1995 or thereabouts it was a refreshing change from Perl CGI's with "print" statements. But now, I frankly don't get all of the zealous passion that PHP proponents have. I'm sure some of the suggestions I've heard ("turn off globals in php.ini", "read Sterling Hughes", "buy PHP 5 Objects, Patterns, and Practice", etc) are all good. I'm sure there are PEAR contributions that are legible and well factored (though, there are those that are not). But all of that misses the point. I'm confident that I or someone else could eventually derive a tool set that meets a rigorous standard for maintainable code. What concerns me are the prevalent practices and establishing best practices. I want to work with the someone else to establish them.

OK. So if it were upto me to establish best practices with PHP, what would I do?

Well, for one, I'd insist on using PHP classes with clear API's. I hate seeing PHP code with a long list of require_once statements, all of which can bring new functions and globals into the current scope. When files are used as grab-bags of functionality, when you're asking yourself "Which included file provided this function or that function? Rewind to read the source code and remember it..." you're in a World of Hurt. Better to define a class, instantiate it or call its static methods. I've been accused of writing PHP code that looks like Java. Well, I'm not sure if that's a disparagement but I think it was intended to be. Thank you very much!

I think PHP has an equivalent to Perl's "use strict" pragma. Gotta have it. Also, I think PHP 5 has exceptions. Gotta have that, too.

I'd use frameworks to encourage a separation of concerns. Either use an existing one or invent one if none of them are upto the task. On my list of things to look into:

The Web Application Component Toolkit project has a somewhat overwhelming list of PHP MVC frameworks. I'm concerned when I see pre-ambles along the lines of "the goal of this project is to port struts to php..." That sounds like a bad idea. Strut's reliance on inheritance and an awkward XML configuration grammar isn't really something to aspire to... I think the Ruby on Rails folks got it right: convention over configuration.

I'd insist on unit testing. I don't know anybody using PhpUnit but I'm willing to be convinced that it's good. Tracing without random writes to error_log (or worse) is also a must-have, proper use of log4php is probably the ticket.

What am I missing? What are the best practices when programming with PHP? Any experts with these topics, come talk to me. Technorati is hiring.

( Feb 06 2006, 09:14:36 PM PST ) Permalink


20050115 Saturday January 15, 2005

Booting linux in single user mode Today, we had to bring up a Debian host in single-user mode and it didn't want to cooperate. Note to self:
Next time LILO doesn't want to stop for a boot prompt, hold down the shift key to force to stop for a prompt.

I don't know if that's a Debian default or something, but it's really annoying that something special is required to get a boot prompt.

( Jan 15 2005, 11:03:22 PM PST ) Permalink


20050103 Monday January 03, 2005

PHP and mod_perl w/mysql breakage on Mac OS X I'm working with code in PHP, Perl and Java that all have to access MySQL. I haven't had any problem using PHP configured "--with-mysql" and mod_perl with DBD::mysql on Linux but on Mac OS X, it just doesn't work.

My setup is: Mac OS X (Panther) with Perl 5.8.1, MySQL (v4.0.17) installed with Fink. Apache (v1.3.31) was compiled with both mod_perl and PHP. When I deployed a mod_perl module that connected to MySQL, the connections always failed with this error:

DBI connect('bjorkdb;port=3306','hamster',...) failed: Protocol mismatch. Server Version = 0 Client Version = 10 at ...
which was really confounding because the connections in other runtime contexts were fine. I double checked to make sure I didn't have multiple libmysqlclient's around or additional Perl installations. All of that checked out.

I recompiled Apache without PHP and everything works great. So my conclusion is that something funky happens on Mac OS X when linking libmysqlclient. Looks like I'll have to keep another set of Apache binaries around should the need to run PHP locally recur, otherwise I'll just stick to the compile that has mod_perl but not PHP.

( Jan 03 2005, 12:01:11 AM PST ) Permalink


20041225 Saturday December 25, 2004

Hasta La Vista, mod_perl - Hola mod_parrot! While I've become more fond of running my application outside of the webserver (i.e. using mod_jk2 to wire Tomcat to Apache is my preferred modus operandi these days), mod_perl will always have a place in my heart; I'm actually writing a mod_perl handler for a simple web application right now.

Well, I'm painfully aware of how mod_perl is getting long in the tooth. So I was pleased to read of mod_parrot. While it's probably going to be a while before I get a chance to mess with Perl 6 and Parrot (or even Python and Parrot), I'm hopeful that mod_parrot will provide high productivity with application with modernized programming facilities in the future.

Oh, by the way, Happy Festivas!

( Dec 25 2004, 05:39:09 PM PST ) Permalink


20041218 Saturday December 18, 2004

PHP's mbstring is a 2 bit thief Normally, PHP's strlen function reports back the number of bytes in a string. When dealing with western characters, that will equal to the number characters as well. However, strange things can happen when PHP's mbstring extension for multi-byte character support is enabled to alias that function.

If you have mbstring.func_overload configured to alias mb_strlen for strlen (i.e. when the 2 bit is flipped), then strlen starts counting characters, not bytes. If you need to count the number of bytes, it's not obvious how you're supposed to do it.

This is how I did it:
In places where I really needed to know the number of bytes, I used a homebrewed function byte_count instead strlen. Here's the function definition for byte_count.

     function byte_count($val) {  
         $len = (function_exists('mb_strlen')) ? 
             mb_strlen($val, 'latin1') :
             strlen($val);
         return $len;
     }

Perl is hokey about it too. The length is supposed to count the number of characters but if you want to force it to count bytes, you need to use the bytes pragma. From the manpage:

           $x = chr(400);
           print "Length is ", length $x, "\n";     # "Length is 1"
           printf "Contents are %vd\n", $x;         # "Contents are 400"
           {
               use bytes;
               print "Length is ", length $x, "\n"; # "Length is 2"
               printf "Contents are %vd\n", $x;     # "Contents are 198.144"
           }

Java is not without it's pickiness but it as least it has byte and char as distinct primitives.

( Dec 18 2004, 12:50:23 AM PST ) Permalink


20041215 Wednesday December 15, 2004

Tool to defeat a DOS against an Apache server farm Throttling the number of requests that come in to a single web server instance isn't rocket science but it requires keeping track of a state shared amongst processes/threads. But how would you efficiently throttle requests that come to a set of servers?

Apparently, there's some Apache goodness available for this now. At least I think it sounds good! Ian Holsman has written mod_ip_count for Apache 2.0. It uses the APR portability layer and memcached for shared state (actually apr_memcache from Paul Querna). This would enable a whole server farm to keep track of request rates from and throttle specific IP addresses.

( Dec 15 2004, 04:24:13 PM PST ) Permalink


20041123 Tuesday November 23, 2004

Adding a file system hierarchy into a CVS module I had a whole bunch of code that needed to added to a CVS repository. Normally with just a new directory or two and a few files, doing "cvs add" for each one is no big deal. But when there's a whole file system hierarchy that can be a real PITA. This is where "cvs import" comes in handy.

I usually only use "cvs import" to create a new CVS module but it can also be used to do a "bulk add." Maybe it's common knowledge for CVS jockies but it's easy to forget about unless oft-used. Here's the scenario:

There, that's a lot easier than individually adding directories and files.

( Nov 23 2004, 11:09:35 AM PST ) Permalink


20041119 Friday November 19, 2004

Editing Perl with Eclipse and EPIC I've been bouncing between development in Perl and Java a lot this, two languages that I both love and loath at various times. One of the things that I love about developing in Java is using Eclipse. This week I decided to give the Eclipse support for Perl a spin. Well, I'll be saving my love for the Java support.

The only gripe I've heard about Eclipse that I haven't had a good answer for is the absense of Emacs key bindings. Otherwise, what's there not to dig about Eclipse?

Dollar costs
zilch
Support for refactoring
renaming and changing method signatures, moving them around... all with dependent references kept in tact
Extracting interfaces
OK, that falls under refactoring but worthy of its own mention
Source and javadoc stub automation
Spontaneous method stub creation, constructor and accessor creation, javadoc stubs
Test case generation and running
JUnit and ant awareness, yum
Syntactical and semantic error highlighting
Fixing errors early and often is easy cause they're usually obvious

I was hopefull that the EPIC plugin would provide at least some of those things for Perl development. This is what I found:

OK, that's a pretty good start. But some things I wanted like syntax completion (i.e. when editing Java, you can type "for", hit shift-space to pull up options to loop over an array or a Collection and voila: a for loop is materialized. ...so basically all of the goodness you get for Java development is lacking for Perl. Nonetheless, I think it's a promising start. I'll be trying the EPIC updates from time to time as new occasions to develop in Perl present themselves.

In the meantime, you can enjoy the fruits of this week's labor by pulling it off of CPAN; that's where you can get WebService::Technorati. It's also part of the latest release of the Technorati web services SDK. Thanks to David Wheeler for turning me on to Pod::Simple::HTML ...I'm still trying to figure how he gets it to output nice docs from pod, mine didn't come out nearly that purty. Ah well, I guess that'll be part of next week's Perl fun.

( Nov 19 2004, 10:59:25 PM PST ) Permalink


20041117 Wednesday November 17, 2004

XML::Parser on Mac OS X I needed to fiddle with XML::XPath on my powerbook today, it depends on XML::Parser. Complacent with how most unixy things I want to do JFW on Mac OS X, I dropped down to my CPAN shell and typed "install XML::Parser" -- bzzzt!

It turns out that expat is not installed, grrr. So I fired up Fink Commander and had it gimme some expat lovin'. Tried it again -- bzzzt! This is what I did in the CPAN shell

cpan> o conf makepl_arg "EXPATLIBPATH=/sw/lib EXPATINCPATH=/sw/include"
cpan> install XML::Parser
-- ding-ding-ding! We have a winner! XML::Parser installed! Thereafter, XML::XPath JFW'd and I'm on my way.

( Nov 17 2004, 04:53:44 PM PST ) Permalink


20040918 Saturday September 18, 2004

Apache 2.0's poor adoption rate amongst LAMP developers

Although Apache 2.0 has been out for a while now, the risk for mod_perl and PHP based developers is still high.

I just ran into an interview I did with Linux Guru about two years ago. I was relatively upbeat at the time 'cause I expected that the innovations in Apache 2.0 would be sufficiently compelling that it'd drive mod_perl and PHP developers to "get on the bus." Sadly, that hasn't happened.

If you're serving static content or need to wire up an external application server (i.e. Tomcat via mod_jk), then Apache 2.0 is definitely the way to go. But the vast body of mod_perl modules on CPAN that work well with 1.x but don't with 2.0 does not bode well. Thread safe Perl and PHP development is really the key to Apache 2.0's success within that development community, it seems like it'd behoove RedHat, IBM and other vendors who've bet a lot on the open source integration market to spur this development.

( Sep 18 2004, 05:50:24 PM PDT ) Permalink


20040915 Wednesday September 15, 2004

Mason and Maypole

I'm fan of expressing business logic cleanly separated from display logic. It becomes especially important for managing CRUD cycles within an application. In j2ee-land, MVC with struts is the de-facto standard for doing those things and it works pretty well. However, in land o' LAMP, no such standard exists.

I'm currently looking at using Maypole with HTML::Mason. But it looks like (oy!) TMTOWTDI decisions are to be made there:

Which way to go?

( Sep 15 2004, 03:56:41 PM PDT ) Permalink
Comments [1]

20040914 Tuesday September 14, 2004

is mod_perl a dying art? Does PHP have a future?

I keep hearing from mercenary recruiters from Amazon about technology jobs requiring mod_perl and HTML::Mason knowledge (I tell 'em "No Thanks But Say Hi To Jon For Me" -- I doubt they ever do) . Hearing that one of the topics of conversation at this year's OSCON was the demise of mod_perl came as quite a surprise.

According to the Journal of jjohn, mod_perl's problem is that it's a CGI enabler (psychobabblisticly: it allows web developers to indulge in Bad Things). jjohn sez...

Now, it's not that mod_perl suck (it doesn't) or that it's not useful in some situations (it is), is just that MOST PEOPLE ARE SIMPLY DOING CGI CRAP. That's right, stupid CGI + HTML is a kind of universal Microsoft Fundation Class that works for programmers of all languages.
He goes on to give PHP a little lovin'
PHP is a terrible language. Perl has long suffered with the albatross of its highly syncretic origin and it's "organic design". However, PHP is a lot worse. It's a kitchen-sink language where crazy things like mysql routines and GD libraries are part of the core language. While objects were around in PHP 4, few PHP systems use OO style. To put a fine point on it, most of the PHP apps I've looked at are poorly written and a bear to debug.

And yet, PHP is frequently a better choice than Perl for web apps.

Besides the close association to the CGI aspersion, the big problem that Perl and mod_perl suffer from is that it's too damn easy to build templating web component frameworks. HTML::Mason, HTML::Embperl, Template Toolkit, HTML::Template, Apache::ASP and so on and so forth ("but wait! there's more..."). How many goddamned many of these do we need? The overlapping similar-but-different functionality borders on Not Invented Here neuroses. And so at a certain point, TMTOWTDI is a liability. As a programming language, PHP suffers from a similar TMTOWTDI blight. For instance, for file path values, there's pathinfo but there's also dirname and basename, which are completely redundant.

So if you're going to use a language and component system that sucks (and they all do), perhaps the thing to do is to use the one that sucks the least. Despite the OO additions to PHP language for PHP 5 (notably absent: real exceptions), it's a tough case to make that PHP sucks less than mod_perl. Maybe the PEAR libraries and the Smarty component system make it a little more usable. Maybe. Perhaps mod_perl's maturity and Perl's general usefulness inside and outside of the web environment is an enduring asset. But I'm not convinced one way or the other. Screw it! Use mono and ASP.Net!

OK, probably not.

In the meantime, I'm looking into combining Maypole and Mason to get a framework together to support the applications that MVC is appropriate for.

( Sep 14 2004, 04:32:31 PM PDT ) Permalink


20040903 Friday September 03, 2004

F*ck PHP, ASP, JSP and XYZP while you're at it

While I can't say that mod_perl sucks more than PHP (nor can I say the same thing about Java and Struts, though it's strengths are narrower). They all suck in one way or another.

At least the PHP fans have a good sense of humor about how much the other guys suck.

Or maybe they're just angry cause Doug kinda orphaned mod_perl 2.0

( Sep 03 2004, 06:59:41 PM PDT ) Permalink
Comments [2]

20040604 Friday June 04, 2004

Database Replication and the old Beta versus VHS thing I've been plugging up holes in some application problems relating to MySQL. It's not my favorite database but then life is full of compromises. I really like FreeBSD better but the sheer momentum around Linux development is impossible to ignore. So I use it and make the best of it. However, if there was a native JVM for FreeBSD that was as upto date as Linux' I'd consider switching back.

But seriously folks, there are a lot of things that are just toyish about MySQL. In the same way that programming language features like Object Oriented Programming shouldn't be sad after thoughts as it is in Perl and PHP, basic database functionality (foreign keys, transactions and subqueries) shouldn't be the "new features" for a database. I've always liked PostgreSQL for its more complete SQL implementation but it just doesn't seem to have the momentum behind it that MySQL does.

Anyway, my favorite MySQL bug (this is on MySQL 4.0.18) has got to be the silent failure of replication. There are slave database instances whose replication status I assumed were monitorable by doing "SHOW SLAVE STATUS" and observing "Slave_IO_Running" and "Slave_SQL_Running" (each attribute represents a thread that manages the binlog IO and SQL execution on replicated units, respectively) on each of them. Well guess what? That's not sufficient. Both threads can claim to be running and you might even observe the execution positions changing in sync between master and slave. But lo and behold, the real measure of whether or not the MySQL replication is working correctly is to query your application data! For instance, if the timestamps and/or sequences for some key tables are advancing in the master but not the slave, you're hosed. You might need to myisamchk the slave's tables. You might need to simply restart the database slave instance. You might need to ceremonially sacrifice a chicken. Perhaps a little bitch slappin' and sweet whispers will get it going. I dunno. The bottom line is: MySQL might report that it's replication threads are running and its positions are changing but *SURPRISE* your data isn't really updating on the slave! I need replication support for high availability and read concurrency but bugs like that just suck the big one.

So what is to be done? Does MySQL 4.1 fix this crap? Do InnoDB tables replicate more reliably than MyISAM? Is PostgreSQL even an option here? I don't know anybody using PostgreSQL replication. Sometimes when I read the comparisons between MySQL and PostgreSQL, it just makes me wanna throw my hands up in the air and move over to the The Dark Side (AKA Oracle).

Here are some links:

Perhaps citing these out of date sources amounts to FUD. But I don't think so. For me it indicates how much momentum MySQL has... it's not a better technology but the MySQL peoples have managed to leave old critiques behind them and press ahead with after-thought-ish feature additions. VHS won the videotape format wars because it had momentum, not 'cause it's better. I'd be happy to see newer comparisons, contrasts and benchmarks but this is all I've got handy.

( Jun 04 2004, 01:54:11 AM PDT ) Permalink
Comments [2]

20040525 Tuesday May 25, 2004

Class::DBI riddles Sometimes Perl is wonderfully expressive and an extreme productivity tool; sublime, even. Other times, it is an enigma and a riddle rolled into one.

I recently grabbed Class::DBI (v0.96) off of CPAN to help me wire up some simple objects with lots of database attributes. Now, when roughing out objects in Perl, I typically find it quickest to declare a package in the file scope of my working code -- it's like having a runnable test for the package's code write there in fornt of you. Most of the time, that's just fine and dandy -- as the code matures or gets unwieldy, migrating it out into its own .pm module files is fine. But I noticed that with Class::DBI, all kinds of weirdness can ensue if you declare your packages outside of the .pm module file world. I'd seen goofiness with older version complaining about not finding 'db_Main' in the package and yaddada yada. This time around, I tried putting everything in the file scope i.e. so it looks like this:

#!/usr/bin/perl

use Class::DBI;

my @britons = Criminy->retrieve_all;
map { print $_,$/ } @britons;

package Criminy;
use base 'CriminyDBI';

Criminy->table('criminy');
Criminy->columns(All => qw(id foo bar));

package CriminyDBI;
use Class::DBI;
use base 'Class::DBI';
# someday, I'll have a dog and name him Django Hendrix
CriminyDBI->connection('dbi:mysql:test','django','hendrix'); 

But this totally fails...

Criminy can't SELECT 
FROM   criminy
: DBD::mysql::st execute failed: You have an error in your SQL syntax near 
'FROM   criminy
' at line 2 [for Statement "SELECT 
FROM   criminy
"] at /usr/lib/perl5/site_perl/5.8.0/DBIx/ContextualFetch.pm line 51.
 at ./criminy.pl line 5
However if I put each of those packages in their own .pm module file, it's totally happy. I can't explain it (and I really don't want to, it just sucks). Try it out, here's the mysql schema:
CREATE TABLE criminy (
  id int(11) NOT NULL auto_increment,
  foo varchar(32) default NULL,
  bar varchar(32) default NULL,
  PRIMARY KEY  (id)
) TYPE=MyISAM;
INSERT INTO criminy VALUES (1,'Led','Zeppelin');
INSERT INTO criminy VALUES (2,'Black','Sabbath');
INSERT INTO criminy VALUES (3,'Deep','Purple');
Anyway, the short answer: don't take any shortcuts. Declare your packages in their own file and Class::DBI will glide along swimmingly. ( May 25 2004, 11:52:22 PM PDT ) Permalink