Latest Articles

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.

Never Use $_GET Again

{ 2 Comments }

Matt Butcher's new blog entry, Never Use $_GET Again, discusses using the filter_input function that is available in php 5.2+ to remedy concerns about input validation.  Its an elegantly simple approach that solves a busload of problems.  This is worth reading and I think is going to not only be worth implementing but also worth retrofitting to your existing applications.

Toggle Table Rows with jQuery, no plug-in required

{ 2 Comments }

Last week I encountered a situation with one of our web apps that required a toggle-able table row. Essentially the existing table was populated with information from four different tables. In addition to several joins there were some nested queries, and it was killing the MySQL server.

So, I decided to pull the information from the nested queries out of the rows and use a hidden table row below each existing row where I would show the formerly nested information using an ajax call to retrieve. Removing the nested queries significantly improved query performance.

The biggest problem in this was creating a table row that toggles. The native toggle() jquery function didn't work at first, and this is because a table row's display attribute is "table-row", and not "block" as toggle() resets to.

I found this jQuery plugin by Janko at Warp Speed for doing exactly what I need. It is simple and works quite well with the minimum necessary amount of code. This is accomplished by using jQuery calls to add an anonymous click function to the even rows. However, there are some drawbacks. First, if you observe the demo, notice that a click anywhere in the parent row triggers the toggle.  This is great unless you have a link in the parent row.  What to do if you have, say, a table of employees and you want a link to their full record in the parent row?

Quite unintentionally, after fiddling with jQuery for a couple of hours I came up with a solution.  Only after I tested did I realize I had Janko's plugin commented out.  I was able to confirm my solution works with no plugin required.  Rather than using the odd/even selectors in jQuery, I have used row ID numbers as you will see in the demo.  To exempt the cell with the link from the anonymous click function, I have added the clickable class to each cell instead.  If you don't want this extra layer of complexity, just strip out all the parent() business from the JavaScript.

Please help yourself to the code and let me know what you think. View the demo...

P.S. In case you are wondering why I have an empty third row for each record:  This is to preserve the jQuery-based alternating table row highlighting that uses the jQuery even selector.  Since Janko's solution also relies on this method, it was wiping out my table striping.

Gerrymandered Code: Extending Zend_Registry to globalize session vars

{ 14 Comments }

In my most recent post I talked about putting my toes in the Zend Framework water with stand-alone usage of Zend_Cache. This has been a great experience, and since writing the post I've been updating my main application with more caching anywhere I can see a benefit.

In the past couple of days I waded a little deeper into ZF, installing Zend_Registry (also stand-alone) and using it to globalize configuration variables that I was previously loading into $_SESSION in order to access throughout my scripts and inside classes without having to pass them explicitly.

I've been using $_SESSION like this for a couple of years, and I've known the whole time it was a band-aid approach to solve the problem. Such an approach is not an ideal "design pattern." Though it passes my baseline rule--it works--there are downsides. The two most obvious are that 1) there's nothing to stop me from accidentally overwriting a session var inside some script or method, and 2) there's no sense loading up the session handler with stuff that really shouldn't be in there.

Zend_Registry is a very lightweight module (one file), that is actually an extension to the native ArrayObject PHP5 class. Any variable, object, or scalar you load into Zend_Registry instantly becomes available globally throughout your script. Let me prevent confusion by saying there is no persistence across page loads--this is just for within each request--but the upside is any scope issues are swept away in a very elegant manner. Hint: Combine Zend_Registry with Zend_Cache to achieve persistence.

The Registry design pattern is not unique to ZF, you can even write your own Registry class if you care to.  I'm still not fluent in OOP-speak, but I understand Zend_Registry is implemented by default as a Singleton.  I know there's a lot of trashing of the Singleton pattern out there in programmer land.  However, I don't get too hung up on such metaphysical debates...not to say there isn't merit in the argument, but for me and my work it is not a practical concern.

I refactoring my main app with Zend_Registry in place of my rat's nest of $_SESSION['config']['foo'] and such...which is mostly just things like email addresses for the system admin (me) and other global variables that may come in handy in any script or method.

As with my previous ZF article, I'll leave the details to the fine folks at Zend (see Zend_Registry documentation here), but the implementation of Zend_Registry in this fashion can be exactly this simple:

Zend_Registry::set('admin_email', 'chris.renner@company.net ' );

And then later in your script (or function or object method or wherever) you return the value by calling:

Zend_Registry::get('admin_email' );

Wow, how elegant! You can also access the registry as an object or as an array. For my needs, the static setter/getter methods are perfect.

Netbeans' Find and Replace features made it relatively simple to update my code with the new Zend_Registry calls.  I guess the "web developer" jargon for that would be "I refactored my code base."

Problem solved, right?  Well yes and no.  The pesky need for globalized config vars has been solved, but there is a related problem.  How did I ever get the idea to store config variables in $_SESSION?  Probably the same way most of you reading this did in your newbie days:  I was already using $_SESSION to store actual, legitimate session variables at user login, and then access those variables through my code.

gerrymanderIn addition to being a code jockey, I am a politics junkee and also the owner of a second major in Political Science (I know, its so rare to find someone working in a field that is wholly unrelated to their degree...).  It occurred to me there is an analogy to be made between a  Gerrymandered congressional district and what the pros call "spaghetti code."  I think my amateur-ish use of $_SESSION as a global band-aid amounts to what one might call "Gerrymandered code."  I am hereby coining the phrase!

The obvious solution to replace all the $_SESSION tags littered throughout my code is to simply load $_SESSION into Zend Registry like so:

Zend_Registry::set('session', $_SESSION);

This works, but retrieving session vars proves to be a bit clunky. My reading of the ZF documentation suggests there isn't a way to access the value of an array element in the Registry in a direct fashion. You therefore have to instantiate Zend_Registry every time you need access to a session var. For example...

$registry = new Zend_Registry();
echo $registry['session']['userID'];

I find this a bit clunky to do every time I need to access my session. I also don't care for all the extra overhead caused by generating an object for each one of these cases.

My solution was to extend Zend_Registry with another class that can access an array element inside the registry and return it directly. Here's what I came up with:

/**
 * A simple extension to Zend_Registry to allow direct access to array objects
 * and object properties inside the registry
 * @author Chris Renner http://www.chrisrenner.com
 */
class App_Registry extends Zend_Registry {
 
    /**
     * Constructs a parent Zend_Registry with default
     * ARRAY_AS_PROPS to allow access as an object
     *
     * @param array $array data array
     * @param integer $flags ArrayObject flags
     */
    public function __construct($array = array(), $flags = parent::ARRAY_AS_PROPS) {
        parent::__construct($array, $flags);
    }
 
    /**
     * getter method for directly accessing value of an array element stored in a registry index
     *
     * @param string $index - the array we're accessing
     * @param string $key - the element we want to access
     * @return mixed
     */
    public static function getFromArray($index, $key) {
 
        $array = self::get($index);
 
        if(is_object($array)) {
            $var = $array->$key;
        } elseif(is_array($array)) {
            $var = $array[$key];
        }
 
        return $var;
    }
 
    /**
     * setter method for directly modifying value of an array element stored in a registry index
     *
     * @param string $index - the array we're accessing
     * @param string $key - the element we want to access
     * @param string $val - the value we want to assign
     * @return mixed
     */
    public static function setInArray($index, $key, $val) {
 
        $registry = self::getInstance();
 
        if(is_object($registry[$index])) {
            $registry[$index]->$key = $val;
        } elseif(is_array($registry[$index])) {
            $registry[$index][$key] = $val;
        }
 
    }
 
}

As you can see, there are just two methods. getFromArray returns the value of an array element or object property from the registry. You just need to supply the Zend_Registry index and the key/property name you want back.

setInArray is used to update an existing property or array element. Supply the Zend_Registry index, the property/key name, and the value you want to assign.

So now, if I want to get the userID from the session (We still set the session array into the registry using Zend_Registry::set() as above), I can call a new static method like this:

echo App_Registry::getFromArray('session', 'userID');

...and to change the value I simply call this:

App_Registry::setInArray('session', 'userID', $newValue);

Obviously this is a very rough class.  It could use more polish, like error handling in case we're trying to access something that is neither an object or an array.  This can be used to access elements/properties for any array or object you might pass into the registry (not just session vars).

You might ask why not just use constants to globalize config vars?  You certainly could in a simple application.  If you want to change those vars in the script or store an array, you're out of luck.

I'm curious to see what readers of this post might think, if you have suggestions for improvements or better ways of doing the same thing.  Thanks for reading!

Enhance performance with Zend_Cache

{ No Comment }

1/7/10 Update:  I submitted this post to Zend Developer Zone and they published it! Short Link: http://dz.zend.com/a/11582

So I finally took the Zend Framework plunge a few weeks ago.  No, I didn't start building an application with the framework, but I did start investigating some of the ZF classes for stand-alone implementation in my existing projects.

The first ZF class to catch my eye was Zend_Cache, for its obvious performance implications.  The applications I develop and manage are very heavy with database transactions.  Hitting the db every time you need an object is a serious performance bottleneck, and on a shared environment can be troubling to other applications living in the same environment.

I'm going to describe my experiences with Zend_Cache here, but I am not going to bore you with lots of code detail and specifics. The above-linked reference page on the ZF site has more than adequate documentation.

Zend_Cache is incredibly flexible, and allows you to use any one of a number of back end caching methods...i.e. the place you want to store your cached data.  Each of these methods is implemented with an adapter class.  Your choices include file-based caching, sqlite, APC, memcached, Xcache, and ZendPlatform.  There are additional adapters to permit caching in two different methods (TwoLevels) and for methods specific to Zend Server.

The enterprise-shared hosting environment my apps live on includes APC, so I went with that adapter.  My dev environment runs Zend Server CE, which includes an APC-compatible Zend caching extension, so I enabled it through the Zend Server config pane.

There are also a few front end adapters to use, which operate the caching machinery.  Zend_Cache_Core is the base adapter and is, in Zend's words, generic and flexible.  However, for more exotic solutions are available: File, Function, Output and File.

You can cache just about any kind of data that you can store in a variable:  intergers, strings, arrays, serialized stuff, and even objects.  Think about the possible implementations and their performance benefits for just a moment: Object persistence across sessions, recordset caching for speedier searches and paged results, I suppose one could even use this for an alternative session manager.

I already use a vastly simpler file-based caching method for storing serialized arrays for use in various select boxes that occurr frequently across my apps (e.g. "select your department" type stuff).  I may port these over to Zend_Cache later.  Instead, I have implemented Zend_Cache to store objects built from database queries. I am able to manipulate objects across multiple page requests and reduce database requests to only when the object is being modified and saved.

The implementation is extremely simple.  Let me demonstrate how to create a cached object. I will assume we already have an instance of the object we want to cache ($this), and I am not providing a complete class below (so don't try to cut and paste the code literally).

// we need a method create a unique ID for the object
// this will be used to identify the cached object when created or when retrieving
// I would suggest using a hash that includes your db unique id
public function create_cache_id($id) {
    $cache_id = md5('myRecord_' . $id);
    return $cache_id;
}
 
// this method will retrieve the object
// $id should be unique id of your database record
public function fetch($id) {
 
    $cache_id = $this->create_cache_id($id);
 
    $frontendOptions = array(
        'lifetime' => 300 ,                 // cached object will expire after this many seconds
        /* this ROCKS because you don't spend time writing code to check if the cache is expired */
        'automatic_serialization' => true   // we'll need this on to store the object, also for an array
    );
 
    // note that for the APC adapter there are no back end options to configure
    // instantiate instance of Zend_Cache
 
    $cache = Zend_Cache::factory('Core', 'APC', $frontendOptions);
 
    if(!($obj = $cache->load($this->cache_id)) ) {
 
        /* if a cache with this ID doesn't exist, then execute your db query and build your object, then we'll add it to cache: */
 
        $this->myQuery($id); // this is just a generic method meant to represent your db transation
 
        $cache->save($this); // adds the object to cache
 
        echo 'this is not from cache'; // diagnostic, comment out later
 
    } else {
 
        /* if the cached object does exist, then we need to re-populate our object properties */
 
	// loop through each element of $obj and add as a property to $this
        foreach($obj as $key => $val) {
            $this->$key = $val;
        }
 
        echo 'this is from cache';  // diagnostic, comment out later
 
    }
 
}

I've grossly oversimplified the external parts of the process such as the parent object class and the db interactions, but this should still give you the idea. Any time you generate your object, you'll automatically check the cache first before querying the database.

You're also going to want a method to clear the cache if your object changes. For example, if your user edits a record using an input form, you're going to want to update the db and the cache. Rather than immediately re-loading the object into cache, I prefer to simply clear it out of cache and wait for the next fetch request to add back into cache. This way you don't have a bunch of objects stored in cache that aren't being used.

Anyway, the approach to clearing the cache is extremely simple. Let's assume you already have a method for processing an update to a database record that underlies your object. To clear the old version of the object from cache, simply call a method like this:

public function clear_cached($id) {
 
       $cache_id = $this->create_cache_id($id);
       $cache = Zend_Cache::factory('Core', 'APC');
       $cache->remove($cache_id);
 
}

It is truly not much more complicated than the above examples, particularly if you already have an existing application structure you want to add Zend_Cache to. I've been pleased with my first experiments into Zend Framework and intend to explore some other components for use in my projects. Hopefully this tutorial will help someone else out there who's been hesitant to buy into the framework craze.

UPDATE: Just thought I'd link to a couple of other great resources on Zend_Cache:

Zend Framework Hidden Gems: Zend_Cache (Zend Developer Zone)

Joey Rivera: Caching using PHP/Zend_Cache and MySQL

Lifehacker: Teach Yourself to How to Code

{ No Comment }

Lifehacker continues to move up my list of favorite sites to visit on a daily basis.  They recently ran a list of top how-to guides from 2009, and included among them is Programming 101: Teach Yourself How to Code.
PHP gets plenty of attention in the section about server-side scripting languages, althought I will note they chose to post the cover of a Python book in the paragraph.

Server-side scripting: Once you're good at making things happen inside a web page, you're going to need to put some dynamic server action behind it—and for that, you'll need to move into a server-side scripting language, like PHP, Python, Perl, or Ruby. For example, to make a web-based contact form that sends an email somewhere based on what a user entered, a server-side script is required. Scripting languages like PHP can talk to a database on your web server as well, so if you want to make a site where users can log in and store information, that's the way to go. Excellent web development site Webmonkey is full of tutorials for various web programming languages. See their PHP Tutorial for Beginners. When you're ready, check out how to use PHP to talk to a database in WebMonkey's PHP and MySQL tutorial. PHP's online documentation and function reference is the best on the web. Each entry (like this one on the strlen function) includes user comments at the bottom which are often as helpful as the documentation itself. (I happen to be partial to PHP, but there are plenty of other server-side scripting languages you might decide to go with instead.)

The author, Gina Trapani, is Lifehacker's founder and, like me,  is a self-taught programmer:

Good coders are a special breed of persistent problem-solvers who are addicted to the small victories that come along a long path of trial and error. Learning how to program is very rewarding, but it can also be a frustrating and solitary experience. If you can, get a buddy to work with you along the way. Getting really good at programming, like anything else, is a matter of sticking with it, trying things out, and getting experience as you go.

New theme for my blog…

{ Only 1 Comment }

I've been paying more attention to web design lately.  Why?  Well I'm definitely not morphing into a designer--I don't have the DNA for it--but rather because design is relevant, even in an enterprise environment.  As I have mentioned before, the interface matters to the end user--perhaps moreso than all the backend code you've spent hours building and that only other coders can truly appreciate.

With that said, I think this blog was getting stale, and although I continue to adore everything Apple, the "Mac" themed blog thing is just a little dated.  After searching Wordpress.org, I stumbled upon MacPress, a very clean and modern Web 2.0-ish theme by the folks at Sizlopedia.  I hope you enjoy the cleaner look, which I happen to think is vastly more readable.  I'll probably tweak here and there in the next few weeks, but I am largely happy with it as-is.

You can download MacPress from Wordpress.org or from Sizlopedia.com.

Smashing Mag: How to Support IE and Still be Cutting Edge

{ No Comment }

Smashing Magazine is more for the web "design" and photoshop crowd, but any developers working with GUI/front ends at all (which is most of us) will find tons of great information there.  Chris Blatnik says that it is the GUI that makes or breaks an app (after all, the users never see the code, no matter how great the developer thinks it is).

Their latest post is about supporting IE on your websites while still utilizing the latest web technologies, such as CSS3.

The payoff:

Remember that the purpose of this post is not to teach you how to hack IE or deal with its quirks or even how to achieve effects by resorting to JavaScript. Rather, it is to explain how we can design and build websites knowing that differences will arise between browsers.

You won’t see people rioting over the lack of rounded corners on Twitter or WordPress; they aren’t even upset by it, because those differences don’t fundamentally break the websites. People can still use the websites and have a good experience. In most cases, they won’t even notice it!

Brandon Savage: He knows what he’s talking about…

{ No Comment }

Via PHPdeveloper.org, I've been following Brandon Savage's blog for a few months.  Most recently, he's posted a pair of excellent articles on productive and useless micro-optimizations.  These are code "enhancements" such as "change all print() statements to echo statements because echo is faster".  Brandon takes a look at several popular micro-enhancements and offers his professional opinion on whether they are truly worth your time or not.

I find Brandon's knowledge of PHP a great resource, and his practical approach is appreciated and it is very evident he develops in the real world rather than in abstract-land. Many of his recent posts have been focused on the beginning php developer.  So if you are a newbie or an intermediate (I consider myself the latter), Brandon's stuff is very much worth your time.