Latest Articles

OMG, PHP is Losing to Ruby, Python?

{ 7 Comments }

Sorry for the hyperbolic headline, that's just to get your attention. Now that I have you here, lets have a serious discussion.

The TIOBE Index was just updated with the February programming language rankings, and the PHP community seems to be a mild panic about dropping two spots in the rankings (from 3rd to 5th) and now falling behind Python (4th) on the list. The Python and Ruby camps are pretty happy with their results, and many seem vindicated as the results seem to have only amplified the PHP bashing out there.

Being the contrarian that I am, I could not just take the TIOBE results at face value.  Sure it shows PHP search traffic decreasing relative to the other terms, but what are we actually measuring here.

Are we measuring the installed base? Are we measuring the number of programmers/users?  Are we measuring the number of applications?  The answers are no, no and no.  TIOBE measures search engine traffic, period.  TIOBE's methodology is limited to the volume of searches for $language . "programming".  For example, they would have pulled search volume for "Java programming", "PHP programming", "Ruby programming" and so on.

To be sure, this approach is consistent across the languages, but there's a significant amount of measurement error here if our goal is to determine the popularity of each language.

I went to Google Trends to look at search results of PHP against Ruby and Python, and yes indeed there is an alarming drop in PHP search volume going back to 2004 (see below).  However, the Ruby and Python search volume is by comparison nearly off the chart scale except for the blips in the last few months for Ruby.  We'll have to wait and see if the Ruby spike is a trend or an anomoly, but looking at the historical data we can say the observable trend is zero growth in Ruby or PHP "popularity" as measured by TIOBE's methodology.

Google Trends_ php, python, ruby

Search volume for PHP, Python and Ruby Since 2004

This observation doesn't change the alarming drop in PHP search volume, but what good is this metric in a vacuum?  The following chart shows search volume for Java, C++ and PHP respectively.  Notice a trend?

Google Trends_ java, php, c++

Search volume for Java, C++ and PHP since 2004

All three of these languages are experiencing significant drops in search volume since 2004 (that's as far back as Google Trends goes).  In fact, Java's decline looks to be twice as bad as PHP's.  Where are the Java developers jumping out of windows?  Does this mean each of these languages is fatally flawed and on its way out to be replaced by up and comers like Ruby and Python.  Of course not.  There's a correlation among the drops in these three languages, and I would hypothesize that there's an external variable that is depressing search volume here.  The alternative explanation is that Java, C++ and PHP are each, by coincidence, experiencing major drops in popularity.  I think that's a far less likely possibility.

I would also wager that in the case of PHP, the proliferation of frameworks mean fewer people are searching Google for "PHP." Instead, we are all busy searching for "Cake PHP", "Symfony" or "Zend Framework".  Google Trends shows fairly low search volume for these three terms, so this is not an answer, but it could be a small contributing factor.

The real question is what's going on with search in general?  Is overall search traffic down this much?  Surely not.  Are people getting their programming language knowledge in other ways?

Ultimately I am reassured as a PHP developer that although something is going on out there, its not hitting just us.  Further, PHP is still in the top 5 on the TIOBE index, and its pretty rare company up there with three of the five being older, more established "enterprise" scale/desktop languages.  We'll have to watch the TIOBE index over the next few months to see if the Python spike is a trend or a blip, so I'm not going to spend a lot of time worrying about it right now.

As technology professionals we can't be afraid of change, so if there is a death knell sounding for PHP, we have to be able to accept it and move on. Having said that, there's no bell tolling yet... I just don't see it.  PHP's only recently begun to see serious enterprise adoption, and the trend is accelerating, not decelerating.  Even if PHP wasn't cool anymore it would be a decade before all this enterprise adoption was undone in favor of other platforms.

I'm all ears if you have any explanations, and please try to back up with data if you can find it.

Update: Kevin Schroeder has now weighed in on this as well.

Minify and Compress all your JavaScript files into One, on the Fly

{ 13 Comments }

As my applications have grown in complexity, I've followed a path probably quite similar to many of you with respect to .js file maintenance. In the beginning I had one js file to include in the site's/app's header, containing just a few basic js functions used across the site.

As my JavaScript codebase grew, I added more .js files, trying to specialize the files and even went through the trouble of including specific files on some pages and not on others. Then I adopted a framework (jQuery in my case) and that is just one more script tag.

At some point I became aware of the minifcation trend for JS and CSS files, and began looking at how much bandwidth I could save per page load by doing so.  Using an online minifier, I began minifying each .js file after every modification.  This became very unmanageable very quickly. I also had to consider the impact of multiple file loads on the browser and how that impacts performance.

I decided to find a way to automate this process.  I stumbled upon the JSmin class PHP class, which is an implementation of Douglas Crockford's JSMin.  The solution would implement JSmin, but with a wrapper class that would read each .js file, minify (and compress if possible), and then output into a single file.  More helpful ideas were found in this blog article at verens.com.

What I came up with accomplishes the following:

- Given an array of .js filenames, reads and minifies each, writes to a single new file.

- Reads file modification date of each file, if none are newer than the auto-generated output file, the process is skipped.

This results in an on-the-fly minifier that only runs when JavaScript code has been modified in any one of the original files.  This makes code deployment simpler....just sync updated js files to the appropriate directory.

I've encountered a couple of negatives which are easily mitigated.  First, in production the process is slow...sometimes 15 seconds. That first user to hit the site after a new js file has been uploaded is going to think the server is down.  Remedy this by uploading at off-peak times and immediately surf to the site yourself, saving an unwitting user the 15 second wait.  Second, I've experienced some kind of funky file collision on occasion which resulted in the minification running on every page load (think 15 second page loads for every page, every time), so when syncing from test to prod I will typically delete the generated file from test first, so prod can then generate its own clean file.

So here's the script:

/**
 * Wrapper class for JSMin javascript minification script
 *
 * based on http://verens.com/archives/2008/05/20/efficient-js-minification-using-php/
 *
 * @author Chris Renner
 */
 
include('JSMin.php');
 
class App_Minifier {
 
    /**
     * Constructor not implemented
     */
    public function __construct() {}
 
    /**
     * Concatenate and minify multiple js files and return filename/path to the merged file
     * @param string $source_dir
     * @param string $cache_dir
     * @param array $scripts
     * @return string
     */
    public static function fetch($source_dir, $cache_dir, $scripts) {
 
        $cache_file = self::get_filename($scripts);
 
        $result = self::compare_files($source_dir, $cache_dir, $scripts, $cache_file);
 
        if(!$result) {
 
            $contents = NULL;
 
            foreach($scripts as $file) {
 
                $contents .= file_get_contents($source_dir . '/' . $file . '.js');
 
            }
 
            // turned off due to performance issues on production 6-9-10
            $code = "";
 
            $minified  = JSMin::minify($contents);
 
            $fp = @fopen($cache_dir . '/' . $cache_file, "w");
            @fwrite($fp, $minified);
            @fclose($fp);
 
	}
 
        return $cache_dir . '/' . $cache_file;
 
    }
 
    /**
     * input array of js file names
     * converts array into string and returns hash of the string
     * as the new filename for the minified js file
     * @param array $scripts
     * @return string
     */
    public static function get_filename($scripts) {
 
        $filename = md5(implode('_', $scripts)) . '.js';
 
        return $filename;
 
    }
 
    /**
     * we're going to compare the modified date of the source files
     * against the hash file if it exists and return true if the hash
     * file is newer and
     * return false if its older or if hash file doesn't exist
     * @param string $source_dir
     * @param string $cache_dir
     * @param array $scripts
     * @param string $cache_file
     * @return boolean
     */
    public static function compare_files($source_dir, $cache_dir, $scripts, $cache_file) {
 
        if(!file_exists($cache_dir . '/' . $cache_file)) {
            return false;
        }
 
        $cache_modified = filemtime($cache_dir . '/' . $cache_file);
 
        foreach($scripts as $source_file) {
 
            $source_modified = filemtime($source_dir . '/' . $source_file . '.js');
 
            if($source_modified > $cache_modified) {
                return false;
            }
 
        }
 
        return true;
 
    }
 
}

And here's how you would call it in your boostrapping file, etc.

// create array of .js filenames to be minified
$scripts = array('jquery', 'jquery.colorbox', 'jquery.livequery', 'jquery.tipsy', 'jquery.validate', 'functions', 'menu', 'childtables', 'datepicker');
 
// call the fetch static method, supplying the source dir, target dir and the scripts array
$scriptfile = App_Minifier::fetch('scripts', 'temp', $scripts);
 
// put the result in a script tag in your html header

Yes I realize that a static class perhaps wasn't the best choice, but it works and it keeps memory usage to a minimum. I'd probably write it differently today, and may yet refactor it to remove the static.

The output $scriptfile will be a .js filename, generated by hashing the concatenation of all the filenames in the scripts array. This permits different combinations of files to produce different output files, if that's something you need.

Also note my comment in fetch() about the gzip feature not being used. This caused problems in my particular environment so I'm not using it, but it may work for some of you and I'd be eager to hear from you if it does. To enable, just change line 50 from

$minified  = JSMin::minify($contents);

to

$minified  = JSMin::minify($code . $contents);

In my specific example I was loading as many as 9 different .js files per page load, totaling 250kb. Now all that JavaScript loads in 1 file measuring 147kb.

Oh, and don't forget to download JSMin.php from github.

Looking forward to PHP Community Conference

{ No Comment }

Eagerly anticipating event details for the 1st PHP Community Conference. A little background is available on Ben Ramsey's blog.

Best of all, its local for me :D

In Development (and life) we should learn to embrace failures…

{ Only 1 Comment }

i'm enjoying my time at ZendCon 2010. I spent all day Monday in Christian Wenz's PHP Certification Bootcamp. This was an extensive overview on the kinds of questions that appear on the PHP 5.3 certification exam as well as some exam strategies and "gotchas" to look out for. My exam was was scheduled for Tuesday night, and as the Bootcamp wore on I became more aware of my knowledge limitations as a PHP developer.

So Tuesday evening came and I went to take the Exam. After the Bootcamp, which was 5.3 focused, I would have liked to switch my exam from PHP 5 to the 5.3 test, but the exam proctor informed me this was locked in from my prior-indicated preference.

To make a long story short, I FAILED. A bit insecure at first, I spent the night processing this now fact and woke up with a healthy perspective.

I remember a Nike ad from 1998 with Michael Jordan where he stated all the shots he's missed in his career, including how many game winners he missed, and he simply said at the end "Failure is how I succeed."

In development as well as life in general, it is important to understand our limitations, our weaknesses, and our opportunities for growth and improvement. If never tested, we can never grow. Fear of failure is simply a fear of growth and improvement.

Other thoughts on this is that being a PHP developer is not my life, its a part of my life. I've been doing this stuff for four years and I had a life before PHP and a life after. I still think the ZCE is important for the community as it lends legitimacy, and it is still a personal goal of mine, but it does help me to remember that faith and family are bigger pieces of my life in a absolute sense, and my professional life is really an expression of my larger self and not the other way around.

I now understand where my weaknesses in PHP are, and therefore where to focus going forward. What failure has provided in this case is information I can now act on.

Anyway, its not typical for me to dump my soul on this blog or anywhere on the interweb, but I figured I'm probably not the only one here at ZendCon that failed the test and therefore probably also not the only one processing the outcome.

Chris

Source and Powerpoint from AJAX Made Easy with jQuery

{ Only 1 Comment }

As promised, attached is the source code and presentation from my presentation/demo yesterday. You'll need a PHP capable web server to run the cities.php file. However, by using a simple txt file with a static JSON string of cities, you could pass the entire city list to your javascript function and do the search filtering there instead of on the server side. Just a thought...

Presentation
Source Code

Sunsetting IE6 on your own

{ No Comment }

Anyone that does front end web development or web design hates Internet Explorer 6.  Why?  First released in 2001, IE6 is dog slow, very buggy, and essentially completely non-standards compliant.

These issues manifest themselves in two primary categories as best I can tell:  javaScript bugs/behaviors, and layout/CSS behaviors or missing features.

So, developers learn to code around these things.  In CSS this means a lot of hacks and html conditional statements to load custom css, js and htc files to fix many CSS-related deficiencies such as the lack of 24-channel PNG transparency.  As to JavaScript, I've found that jQuery cures the need for IE6-specific code, but we continue to see IE6-specific bugs/glitches that are difficult or impossible to isolate, duplicate, or fix.

Unfortunately for enterprise developers, our users seem to be behind the curve when compared to browser adoption. Much of our target audience is still on Windows XP, and depending on IT policies and budget constraints, abandonment of IE6 in the enterprise has lagged behind the consumer segment.

In the enterprise environment I work in, my primary application sees about 200 visits per day across the institution.  About a year ago I began a crusade (no, that is not too strong of a word when the context is IE6) to purge IE6 from the realm of users accessing my web apps.  I had three goals in mind: 1) Reduce userland bugs/glitches and errors caused by IE6, 2) Improve the overall user experience via a superior browser, and 3) Reduce the amount of time I have to spend working around IE6's craptasticness.

I have to give this caveat before continuing further: As my institution has begun deploying Win7 machines, our IE6 usage has plummeted like a brick.  Admittedly this is the biggest single factor in the reduction of IE6 users against my web apps.  However, there is something to be said for encouraging your users to upgrade and warning them that their experience may be poor with IE6.

Step 1 is to determine the user's browser when they hit your site.  On my web app's login page, I use a simple conditional similar to the below example to display a formatted message to the user:

if(eregi('MSIE 6.0', $_SERVER['HTTP_USER_AGENT'])) {
    echo 'You are accessing this site using Internet Explorer 6.0.  We make every reasonable attempt to ensure this site is compatible with IE6, but due to the age and lack of web standards compliance in IE6, you may experience some errors and bugs that are beyond our control.  For best performance, we STRONGLY recommend a more modern browser, such as <a href="http://www.mozilla.org/firefox">Mozilla Firefox</a>, <a href="http://www.apple.com/safari/download">Apple Safari</a>, or <a href="http://www.microsoft.com/windows/Internet-explorer/default.aspx">Microsoft Internet Explorer 8</a>, which are all available for free.  Firefox and Safari are recommended for the best overall experience and performance, though Internet Explorer 7 and 8 are also fully compatible. Less than 7.5% of traffic on this site is from users on IE6.  Therefore, at some point we will make a decision not to support IE6 any longer, as it takes considerable effort to maintain this backward compatibility. PLEASE UPGRADE YOUR BROWSER SOON!';
}

Wrap that message with some bold styling to grab the user's attention. Then start watching your browser traffic with Google Analytics.

Once IE9 is out of beta, I'll change the message to include it in the recommended browsers, but for now I want to funnel my users into a browser with some CSS3 support, because I've incorporated a lot of it into my app and I think it improves the user experience a great deal.

What I've seen in the past few months is a steady reduction in IE6 traffic from the mid 30% range this time last year, to 10% a couple of months ago, down to 7.1% this past week. I believe the biggest reduction has been deployment of Win7 desktops, but the incremental drive from 10% down to current levels seems to be due to the above user warning.

The other thing I've been doing is verbally encouraging use of Firefox any time the issue comes up. When I get a call or email about a bug, if I can't replicate it in Safari then it is almost always a browser specific issue, and when asked the user almost always informs me he or she is using IE6.

Once IE6 usage hits 5% I am going to change the warning message to say that IE6 is no longer supported. Of course I'll continue to make accommodations, but I'm not going to go out of my way to make IE6 users comfortable in the application--it just doesn't make sense any longer.

Learning PHP

{ No Comment }

Its quite possible you found this blog while googling around for information on PHP.  If that's true, and you're a newbie trying to learn PHP, I'd like to direct you over to a new post on Six Revisions entitled Learn PHP: Get Started Using PHP by Elias Zerrouq. After a brief history of the language, the article goes on to describe what PHP is, how it works, and the basic steps one must take to begin working with PHP, including installing and/or setting up a development environment, picking a source code editor, and writing your first few lines of code.

Coming Up: My First ZendCon

{ No Comment }

Thanks to a slight relaxation in departmental travel/education funds, I get to go to a conference this fiscal year.  I chose ZendCon, and I'm pretty excited about it.

Unless I find other folks from my institution that are also going, I'll be "flying solo"...an introvert at a geek conference...should be interesting.

Anyway, I'll try to resist my nature and make some new connections.  Mostly I'm just excited about the wide range of subjects and the potential to soak in as much of it as possible.  If you're also headed to ZendCon, drop me a note.

iPhone 4 3G Reception Testing with iOS 4.0.1

{ No Comment }

I've stayed silent on the iPhone 4 antenna "debacle", and I frankly think that although there is a legitimate issue at the heart, it has been overblown by a media eager to find a chink in Mr. Jobs' armor.

Before I get too far into this let me just say I've become quite the Apple fan over the last three years and even stood in line for 7 hours outside the Apple Store on June 24th to pick up my iPhone 4.

I've been able to replicate the "Death Grip" bar loss on purpose, but I've yet to have it cost me a call either on accident or when purposefully trying to kill a call with it.

I just downloaded the new iOS 4.0.1 update with its modified signal bar algorhythm and decided to do some of my own testing. Granted this is not controlled testing and it is not terribly randomized, so take this test with a grain of salt, just like all other tests that you read on blogs.  I'm not even terribly confident that Consumer Reports' testing was done in the proper environment.

Anyway, I have a generally good 3G signal at my house, owing mostly to being in the Nashville metropolitan area.  The test methodology was to conduct 5 tests using Speedtest.net app, take a photo of the 5th test in each phone position/mode, and then average the 5.  The positions/modes tested were: Wifi (a control), 3G-no hands (control), 3G w/2-fingers, 3G casual hold, and 3G Death Grip.  What follows are the results of the testing along with some pics. All speeds are in kbps.

Wifi

Methodology: Home Wifi network, Airport Extreme Base Station, Comcast Cable Modem, phone on desk, no hands.

Test Download Upload
1 3403 3073
2 3407 3196
3 3637 3178
4 3583 3089
5 3560 3129
Average 3518 3133
SDC10567

Wifi Test #5 (Yeah I know the bars have nothing to do with Wifi...)

3G No Hands

Methodology: 3G, phone on desk, no hands. Full 5 Bars.

Test Download Upload
1 890 897
2 1223 955
3 1351 1257
4 1322 1151
5 689 1273
Average 1095 1107
3G test #5, no hands, 5 Bars

3G test #5, no hands, 5 Bars

3G 2-Fingers

Methodology: 3G, phone on desk, Thumb and pointer finger tightly gripping, bridging antenna gaps on either side of phone. 2 Bars.

Test Download Upload
1 789 297
2 591 218
3 951 229
4 1399 203
5 1093 655
Average 963 320
3G 2-finger test #4, 2 bars

3G 2-finger test #4, 2 bars

3G Casual Hold

Methodology: 3G, phone in hand casually (not tight at all), like I'd be dialing or surfing the web. 3 Bars.

Test Download Upload
1 0 127
2 794 277
3 917 265
4 97 206
5 1119 136
Average 585 202
3G Causal Hold test #5, 3 Bars

3G Causal Hold test #5, 3 Bars

3G Death Grip

Methodology: 3G, phone in hand so tight it hurt my hand for a few minutes (see second photo below), like I'm an Apple-hater trying to score points on my blog. 1 Bar.

Test Download Upload
1 11 52
2 0 0
3 5 0
4 0 76
5 2 0
Average 3.6 25.6
3G Death Grip Test #5, 1 Bar

3G Death Grip Test #5, 1 Bar

My hand after 5 tests of the death grip

My hand after 5 tests of the death grip

Conclusions

Let's just throw out the death grip completely.  Why?  Because I had to hold the phone so tightly it hurt, and no reasonable person is going to hold their phone that tightly to make a call or surf the web or do anything else one does with an iPhone.  Its not a realistic scenario that is likely to occur in the real world.

Having said that, the Casual Hold is entirely valid, and its results were hardly better.  While the average speeds for Casual Hold are way higher than for Death Grip, note the wild inconsistency among the tests in download speeds.  I'm curious to see if this variance continues if one were to conduct a couple dozen tests.  Whatever causes the inconsistency in a casual grip is probably why the problem seems so hard to reproduce for some folks (including me).

I think we can throw out the 2-finger test for the same reason we threw out the Death Grip, no body holds their phone this way. If you did, you'd drop it.  Its a nice way to demonstrate the flaw in a controlled and explicit way, but its not a realistic demonstration of the scenario that causes the problem in actual use.  Having said that, the results are similar to, but more consistent than the Casual Hold.

As for the 3G on the desk with no hands, those results speak for themselves. I won't bother discussion the Wifi results, they are here just for comparison's sake and validation of my methodology.

Overall the casual hold represents a 47% drop in download speed and a 72% drop in upload speed. That's significant enough to notice, but the raw speed is still well above 2G speeds and fast enough one ought to be able to use the phone if in an area with a half-way decent signal to begin with.

I'd also like to note that in both Death Grip and Casual Hold positions I was still able to dial and complete calls.  So one might say looking at data speeds when the issue is call reception is comparing apples to oranges (pun intended). That is a valid criticism.

Clearly the exposed antennas are prone to some attenuation issues. Oddly this seems to only really affect the 3G antenna though.  So yes, its a flaw.  Is it fatal?  I don't really think so.  And judging by the lack of long return lines at Apple stores and continuing high demand, I don't think the majority of consumers are all that upset about this.

Are there folks who's phones are virtually unuseable because of this flaw? Yes, I'm sure there are some, but its hardly approaching even a large minority of iPhone 4 buyers.  Is it a great number as a percent of the whole than had issues with previous models or would have issues with another brand of phone?  Who knows, nobody's sought fit to actually find any real data, this whole thing seems to be one giant anecdotal evidence party.

The media and Apple-haters (is it hate or just jealousy?) are certainly enjoying the brief exposure of Apple's soft underbelly. Apple has gone from counter culture chick to mainstream in just a couple of years.  As other articles have noted, though they are still barely 10% of the PC marketshare, they dominate the all important mindshare.  Everybody loves to hate the big kid on the block, be it Microsoft, Wal-Mart, or GM in years past.  Now that Apple is at the top of the heap and making busloads of money, they should get used to taking shots from the media, competitors and even perhaps formerly allied counter culture.

My guess on the press conference tomorrow is that Steve sends everyone who has already bought an iPhone 4 an Apple Store credit for $30 to be used on a bumper, which actually costs just a buck or two to make.  Some who don't have a reception problem will end up spending the credit on something else, perhaps even a significantly higher priced item.  This remedy actually ends up making Apple more money, much to the chagrin of the Apple haters.  The white iPhones will be announced to have been delayed while a modification (a coating?) is made to the steel band, and future black models will receive the same modification.

MySQL Views just aren’t “there yet”….

{ No Comment }

I am a big fan of Jason Gilmore. His book Beginning PHP and MySQL (link is to latest version, not my actual copy) actually launched my developer career four years ago.

His latest post over on PHPbuilder.com, Refactor Your PHP Site Using MySQL Procedures and Views, has some good information about using views and stored procedures to simplify application logic and improve code maintainability.

I tried to comment to the article on PHPBuilder, but their comment deal is broken, so I thought this would make a decent blog post.

There is no question views and stored procedures are both powerful tools that MySQL went without for quite some time. However, I disagree with Jason to the extent his article fails to mention there is a cost to using these tools.

MySQL's implementation of views has some well documented performance problems. I won't go into them in detail here, you can read an excellent summary on the MySQL Performance Blog in a post by Peter called MySQL view as a performance troublemaker.  The urge to save yourself from writing a complex query can end up costing you and your users down the road when performance issues crop up.

In a vacuum, I completely agree that views and stored procedures should be an essential tool in the developer's toolbox.
However, in reality, MySQL's implementation of views leaves a lot to be desired.  MySQL views have a tremendous amount of overhead associated with them and using a PHP-side join query, no matter how complex, will result in significantly better performance than a MySQL view.
This article has much more to say on View performance in MySQL: http://www.mysqlperformanceblog.com/2007/08/12/mysql-view-as-performance-troublemaker/
I like stored procedures but as with views, PHP can often do many of these transformations and calculations more quickly than the MySQL server can.  I also think you have to be wary of putting too much stuff into stored procedures and creating a confusing architecture if not careful to  heed separation of concerns.

I like stored procedures, but SQL is slow when compared to PHP, and stored procedures will hit the database CPU more than a corresponding function in PHP.  I also think you have to be wary of putting too much stuff into stored procedures and creating a confusing architecture. A good developer always heeds the rule of separation of concerns.  Stuffing so much stuff into the database eventually will defeat the original purpose of simplifying code maintainability, particularly in a team environment.

If we're talking Oracle or another of the more mature RDBMS products out there, there is no question that views should be leveraged as much as possible. While stored procedures are not as big of a performance concern, mis- or over-use can lead to architectural issues. With respect to views,  MySQL just isn't there yet.