Bunkermaster's Debugging 101 and BuzzLY's Code Share in the Echo Lounge proved popular, and I thought something similar could be done here. Instead of debugging tips or nifty code snippets, though, I was thinking of providing a place where people could drop off (and critique!) tips for programming "Best Practice". Short subject-line-sized suggestions to write useful code/usable programs, and a blurb about why.

These are about program design and structure, and site usability/accessibility; everyone has their own style and there's no such thing as Best Practice about where to put the parentheses - only shop standards.

Same sort of thing as Debugging 101: [noparse]For tip titles[/noparse], and to kick things off here are a couple or three from me.

1. In instance methods, prefer returning $this to returning void.

Why? Many methods that return void exist to alter the state of the object, or to cause it to perform some sort of I/O. Often, calls to such methods are followed by further alterations or I/O. By having such a method return $this it's easy to chain calls. Instead of writing

$buffer->append($foo);
$buffer->normalize();
$buffer->fill($bar);
$buffer->write();
$buffer->truncate();

you can have

$buffer->append($foo)->normalize()->fill($bar)->write()->truncate();

Sometimes $this wouldn't be appropriate: for example, a shutdown method that makes the object unusable wouldn't yield any benefit by being made to return $this. But try and see if something useful can be returned - something that characterises what the object has just done (such as a collection object's "delete" method(s) returning what was just deleted).

2. Prefer class constants to defined constants.

Instead of

define('SOMECLASS_FOO', 42);
class SomeClass
{

use

class SomeClass
{
	const FOO = 42;

Why?

Automatic namespacing: if the constant value belongs to a particular class, it ought to say so. Personally, I'd've made the DOM extension's "XML" constants all class constants of DOMNode or DOMAttr depending, and the "DOM" class constants of DOMException; but I didn't write the DOM spec 🙂 I'd've preferred DOMNode::ENTITY to XML_ENTITY_NODE and DOMAttr::ID to DOM_ATTRIBUTE_ID.

But the real reason is compile-time parsing and optimisation. The define() function is executed at runtime, so creating the symbol and giving it a value has to wait until then; a class constant is parsed at compile time and an optimising bytecode compiler can create the symbol, give it a value, and substitute that value for the symbol when it appears before the program runs.

Of course, not all defined constants can be turned into class constants: some constants just don't belong to classes (even if you create an interface to contain them), while others can't be determined until runtime (class constants are by definition statically initialised). And then there is the slightly disturbing fact that defined constants can use arbitrary strings for names (using class constants avoids doing this).

Update: From PHP 5.3.0, [man]const[/man] can be used outside of class definitions as well. For groups of related constants, having them in a class groups them together and also provides a place where methods that rely principally on those constants can reside.

3. Give select drop downs quickly-distinguishable names
I got the idea from this one from the search page here, unfortunately. I was tabbing through filling out the keywords and I wanted to search by title. If "Search" had been plain text, followed by a dropdown containing "Entire Posts" and "Titles Only", then I could have reached it by typing 'T'. But as it stands, trying to type the desired dropdown value means typing "Search T".

Yeah, yeah, it's only two options. But it's easy to imagine quite reasonable dropdowns with a couple of dozen entries (the time zone selector on your edit options page, for example).

Of course, the text that appears in a drop down does have to mean something, and it does have to fit everything else. As George Orwell put it, "Break any of these rules sooner than say anything outright barbarous."

    21 days later

    I second that proposal. I have been all over the web looking for a credible source for "best practices". Perhaps I just haven't caught the spirit of open source, but for a noob developer best practices are a gold mine. Otherwise, you just have to figure it out - and a year later you get a clue and end up kicking yourself for your previous code but you don't have time to go back and fix all of it. I'd rather do it right the first time.

    On #3, I totally agree. There's nothing more frustrating than trying to fill out a long web form (i.e.- to register for a site), and the drop-downs don't have intuitive key shortcuts. My biggest pet peeve is when I'm asked to select my profession or industry and it doesn't list anything under "Computers" or "Software", but it has 5 selections under "Technical".

      Weedpacket;10904673 wrote:

      ...
      2. Prefer class constants to defined constants.
      ...

      You can even have interface constants, shared by any class that implements that interface.

      interface MyConstants
      {
         const FOO = 'Hello';
         const BAR = 'World';
      }
      
      class Fubar implements MyConstants
      {
         public function hello()
         {
            echo self::FOO . ', ' . self::BAR . '!';
         }
      }
      
      $test = new Fubar();
      $test->hello();
      
        ixalmida;10907748 wrote:

        My biggest pet peeve is when I'm asked to select my profession or industry and it doesn't list anything under "Computers" or "Software", but it has 5 selections under "Technical".

        For me one of the nominees for "biggest peeve" would be options that are indented with n spaces instead of being styled that way (e.g., the "Type of bug" field in http://bugs.php.net/report.php). I know the problem is GD related and I know that's what the option is listed as, but I can't just type 'gd'.

        Problem is, while I'd like to suggest

        <html>
        <style type="text/css">
        #indentedselect option.level1 { padding-left:1em; }
        #indentedselect option.level2 { padding-left:2em; }
        </style>
        </head><body>
        <select id='indentedselect'>
        <option class='level0'>a</option>
        <option class='level1'>a.a</option>
        <option class='level1'>a.b</option>
        <option class='level2'>a.b.a</option>
        <option class='level2'>a.b.b</option>
        <option class='level2'>a.b.c</option>
        <option class='level0'>b</option>
        <option class='level1'>b.a</option>
        <option class='level1'>b.b</option>
        </select></body></html>

        that doesn't work (as written) in IE7, and I'm not looking forward to the hacks that would probably be needed to fake it there.

          20 days later

          Don't use 0 as an primary key (ID number).(aka. 3, 2, 1, Clear!)

          C gives a special meaning to memory location zero: it calls it NULL. It's handy if you're being expected to pass a pointer but you don't have a pointer to pass: you pass NULL. The program will go all funny if you actually try to use location zero.

          By analogy, by not allowing Record 0 to exist in your tables gives you an ID number that says "Not a record" without having to go out of type by using a NULL.

          Since a primary key is not supposed to ever be NULL, some other value should be used in contexts where the absence of a legitimate ID number is denoted. In cases where such an absence might be common or irrelevant, it's tedious to have to handle it as a special case; and where it's rare, then one special case is as good as any other. Consistently using 0 for this special case makes sense both semantically and syntactically.

          Columns that may or may not contain references to a primary key in another table can be declared NOT NULL (the optimiser likes this, and it allows more stringent checking of insertions and updates), and queries can be simplified because they can say "WHERE fk IN (0,56,33,12)" instead of "WHERE fk IN (56,33,12) OR fk IS NULL".

          "COUNT(fk) WHERE foo!='bar'" won't count any records where fk is NULL; it will count them if those records used 0 instead - which is probably more like what you're after with that query.

          On the application side, the fact that 0 is an integer just like legitimate keys also allows simplification: if you allowed Record 0 to exist, then any time a function using ID numbers received an empty value, it would have to check the type: is it NULL or is it 0? One represents a record, the other doesn't. Merry havoc could ensue if one is mistaken for the other. More havoc could ensure if NULL were to be cast to 0 for some reason (whether directly or, say via strval).

          In the ongoing war over whether or not NULLs in databases are Good Things or Bad Things, this is one small campaign where I think avoiding them is desirable: don't use 0 as a primary key value in any record; in a foreign key relationship, use 0 instead of NULL to indicate the absence of a key; and in applications, use 0 to mean "the ID of a nonexistent record".

            Good stuff WP! Nice examples you and Nog provided. Stuff like this is definitely golden for users improving their coding skills!

            I second this 'Best Practices' idea as well. I'm actually inclined to suggest perhaps having either
            a) a new and separate 'Best Practices' forum or
            b) a Child forum of the same name branched off of Code Critique.

            Personally, I'm not sure I like the idea of lumping both regular code critique and best practice threads together under one forum roof to-to-speak. Just my opinion of course. But an absolutely fantastic idea.. Having short, sweet, clean well written snippets with simple explanations behind it will serve us very well indeed!

              Write a Reply...