Depending on the specific situation, average developers are often 10% to 20% less efficient than excellent developers. Good developers are more efficient because they have extensive experience and good programming habits. Bad programming habits will affect efficiency. This article helps you become a better programmer by demonstrating some good programming habits.
These good programming habits not only increase efficiency, but also allow you to write code that is easy to maintain throughout the life cycle of your application. The code you write may require a lot of maintenance; application maintenance is a significant expense. Developing good programming habits can improve design quality (such as modularity), making the code easier to understand and therefore easier to maintain, while also reducing maintenance costs.
Bad programming habits will cause code defects, making it difficult to maintain and modify, and are likely to introduce other defects when modifying. The following are 5 good programming habits that can help PHP code avoid these pitfalls:
◆Use good naming.
◆Divide into smaller parts.
◆Add comments to the code.
◆Handle error conditions.
◆Do not use copy and paste.
These habits are detailed below:
Use Good Naming
Using good naming is the most important programming habit because descriptive names make code easier to read and understand. Whether the code is easy to understand depends on whether it can be maintained in the future. Even if the code is uncommented, it will greatly facilitate future changes if it is easy to understand. The goal of this habit is to make the code you write as easy to read and understand as a book.
Bad Habit: Vague or Meaningless Names
The code in Listing 1 contains variable names that are too short, illegible abbreviations, and method names that do not reflect the functionality of the method. If the method name gives the impression that it is supposed to do one thing, but in fact it does something else, this will cause serious problems because it will be misleading.
Listing 1. Bad habits: vague or meaningless names
<?php
function getNBDay($d){
switch($d) {
case 5:
case 6:
case 7:
return 1;
default:
return ($d + 1);
}
}
$day = 5;
$nextDay = getNBDay($day);
echo ("Next day is: " . $nextDay . "n");
?>
Good Practice: Descriptive and Concise Names
The code in Listing 2 demonstrates good programming practices. The new method names are highly descriptive and reflect the purpose of the method. Likewise, the changed variable names are more descriptive. The only variable that remains the shortest is $i, which in this listing is a loop variable. Although many people frown upon using names that are too short, it is acceptable (and even beneficial) to use them in loop variables because it clearly indicates the function of the code.
Listing 2. Good practice: descriptive and concise names
<?php
define ('MONDAY', 1);
define ('TUESDAY', 2);
define ('WEDNESDAY', 3);
define ('THURSDAY', 4);
define ('FRIDAY', 5);
define ('SATURDAY', 6);
define ('SUNDAY', 7);
/*
*
* @param $dayOfWeek
* @return int Day of week, with 1 being Monday and so on.
*/
function findNextBusinessDay($dayOfWeek){
$nextBusinessDay = $dayOfWeek;
switch($dayOfWeek) {
case FRIDAY:
case SATURDAY:
case SUNDAY:
$nextBusinessDay = MONDAY;
break;
default:
$nextBusinessDay += 1;
break;
}
return $nextBusinessDay;
}
$day = FRIDAY;
$nextBusDay = findNextBusinessDay($day);
echo ("Next day is:" . $nextBusDay . "n");
?>
We encourage you to break large conditions into a method, and then name the method with a name that describes the condition. This technique can improve the readability of the code and make the condition concrete so that it can be extracted and even reused. It's also easy to update methods if conditions change. Because a method has a meaningful name, it reflects the purpose of the code and makes the code easier to read.
in smaller parts
before continuing with programming. If you continue to program while solving an urgent problem, the function will become longer and longer. In the long run, this isn't a problem, but you'll want to remember to go back and refactor it into smaller pieces.
Refactoring is a good idea, but you should make a habit of writing shorter, more focused code. Short methods can be read in one window and are easy to understand. If a method is too long to be read in one window, it becomes difficult to follow because you can't quickly follow the entire idea from start to finish.
When building methods, you should get into the habit of having each method do just one thing. This is a good habit because: first, if a method only does one thing, then it is more likely to be reused; second, such a method is easy to test; third, such a method is easy to understand and change.
Bad Habit: Overly Long Methods (Do Too Many Things)
Listing 3 shows a very long function that has many problems. It does a lot of things, so it's not compact enough. It's also easier to read, debug, and test. Things it does include iterating over a file, building a list, assigning values to each object, performing calculations, and more.
Listing 3. Bad habit: too long function
<?php
function writeRssFeed($user)
{
// Get the DB connection information
// look up the user's preferences...
$link = mysql_connect('mysql_host', 'mysql_user', 'mysql_password')
OR die(mysql_error());
// Query
$perfsQuery = sprintf("SELECT max_stories FROM user_perfs WHERE user= '%s'",
mysql_real_escape_string($user));
$result = mysql_query($query, $link);
$max_stories = 25; // default it to 25;
if ($row = mysql_fetch_assoc($result)) {
$max_stories = $row['max_stories'];
}
// go get my data
$perfsQuery = sprintf("SELECT * FROM stories WHERE post_date = '%s'",
mysql_real_escape_string());
$result = mysql_query($query, $link);
$feed = "<rss version="2.0">" .
"<channel>" .
"<title>My Great Feed</title>" .
"<link> http://www.example.com/feed.xml </link>" .
"<description>The best feed in the world</description>" .
"<language>en-us</language>" .
"<pubDate>Tue, 20 Oct 2008 10:00:00 GMT</pubDate>" .
"<lastBuildDate>Tue, 20 Oct 2008 10:00:00 GMT</lastBuildDate>" .
"<docs> http://www.example.com/rss </docs>" .
"<generator>MyFeed Generator</generator>" .
"<managingEditor> [email protected] </managingEditor>" .
"<webMaster> [email protected] </webMaster>" .
"<ttl>5</ttl>";
// build the feed...
while ($row = mysql_fetch_assoc($result)) {
$title = $row['title'];
$link = $row['link'];
$description = $row['description'];
$date = $row['date'];
$guid = $row['guid'];
$feed .= "<item>";
$feed .= "<title>" . $title . "</title>";
$feed .= "<link>" . $link . "</link>";
$feed .= "<description> " . $description . "</description>";
$feed .= "<pubDate>" . $date . "</pubDate>";
$feed .= "<guid>" . $guid . "</guid>";
$feed .= "</item>";
}
$feed .= "</rss";
// write the feed out to the server...
echo($feed);
}
?>If you write a few more methods like this, maintenance will become a real problem.
Good Habit: Manageable, Function-Specific Method
List 4 Rewrite the original method into a more compact, readable method. In this example, a long method is broken down into several short methods, and each short method is responsible for one thing. Such code is very helpful for future reuse and testing.
Listing 4. Good practice: easy-to-manage, function-specific approach
<?php
function createRssHeader()
{
return "<rss version="2.0">" .
"<channel>" .
"<title>My Great Feed</title>" .
"<link> http://www.example.com/feed.xml </link>" .
"<description>The best feed in the world</description>" .
"<language>en-us</language>" .
"<pubDate>Tue, 20 Oct 2008 10:00:00 GMT</pubDate>" .
"<lastBuildDate>Tue, 20 Oct 2008 10:00:00 GMT</lastBuildDate>" .
"<docs> http://www.example.com/rss </docs>" .
"<generator>MyFeed Generator</generator>" .
"<managingEditor> [email protected] </managingEditor>" .
"<webMaster> [email protected] </webMaster>" .
"<ttl>5</ttl>";
}
function createRssFooter()
{
return "</channel></rss>";
}
function createRssItem($title, $link, $desc, $date, $guid)
{
$item .= "<item>";
$item .= "<title>" . $title . "</title>";
$item .= "<link>" . $link . "</link>";
$item .= "<description> " . $description . "</description>";
$item .= "<pubDate>" . $date . "</pubDate>";
$item .= "<guid>" . $guid . "</guid>";
$item .= "</item>";
return $item;
}
function getUserMaxStories($db_link, $default)
{
$perfsQuery = sprintf("SELECT max_stories FROM user_perfs WHERE user= '%s'",
mysql_real_escape_string($user));
$result = mysql_query($perfsQuery, $db_link);
$max_stories = $default;
if ($row = mysql_fetch_assoc($result)) {
$max_stories = $row['max_stories'];
}
return $max_stories;
}
function writeRssFeed($user)
{
//Get the DB connection information
$settings = parse_ini_file("rss_server.ini");
// look up the user's preferences...
$link = mysql_connect($settings['db_host'], $settings['user'],
$settings['password']) OR die(mysql_error());
$max_stories = getUserMaxStories($link, 25);
//go get my data
$newsQuery = sprintf("SELECT * FROM stories WHERE post_date = '%s'",
mysql_real_escape_string(time()));
$result = mysql_query($newsQuery, $link);
$feed = createRssHeader();
$i = 0;
// build the feed...
while ($row = mysql_fetch_assoc($result)) {
if ($i < $max_stories) {
$title = $row['title'];
$link = $row['link'];
$description = $row['description'];
$date = $row['date'];
$guid = $row['guid'];
$feed .= createRssItem($title, $link, $description, $date, $guid);
$i++;
} else {
break;
}
}
mysql_close($link);
$feed .= createRssFooter();
// write the feed out to the server...
echo($feed);
}
?> There are also limitations to splitting long methods into short methods, and excessive splitting will be counterproductive. Therefore, do not abuse this good habit. Breaking code into large chunks can make reading as difficult as not breaking up long code.
Adding Comments to Your Code
Adding good comments to your code can sometimes seem as difficult as writing the code. It's not easy to know what to annotate because we often tend to annotate what the code is currently doing. It's a good idea to comment the purpose of your code. In the function's less obvious header block, the reader is told about the method's inputs and outputs, as well as the method's original goal.
It's common to comment out what the code is currently doing, but this is unnecessary. If the code is complex and you have to comment out what it is currently doing, this will suggest that you should rewrite the code to make it easier to understand. Learn to use good names and shorter methods to make your code more readable without providing comments explaining their purpose.
Bad Habit: Over- or Under-Commenting Functions
The comments in Listing 5 only tell the reader what the code is doing—it's iterating through a loop or adding a number. But it ignores why it does its current job. This leaves the people maintaining the code without knowing whether the code can be changed safely (without introducing new defects).
Listing 5. Bad habit: too many or not enough function comments
<?php
class ResultMessage
{
private $severity;
private $message;
public function __construct($sev, $msg)
{
$this->severity = $sev;
$this->message = $msg;
}
public function getSeverity()
{
return $this->severity;
}
public function setSeverity($severity)
{
$this->severity = $severity;
}
public function getMessage()
{
return $this->message;
}
public function setMessage($msg)
{
$this->message = $msg;
}
}
function cntMsgs($messages)
{
$n = 0;
/* iterate through the messages... */
foreach($messages as $m) {
if ($m->getSeverity() == 'Error') {
$n++; // add one to the result;
}
}
return $n;
}
$messages = array(new ResultMessage("Error", "This is an error!"),
new ResultMessage("Warning", "This is a warning!"),
new ResultMessage("Error", "This is another error!"));
$errs = cntMsgs($messages);
echo("There are " . $errs . " errors in the result.n");
?>
Good practice: Annotated functions and classes
The comments in Listing 6 tell the reader about classes and methods purpose. This comment explains why the code is doing its current job, which can be useful when maintaining the code in the future. Code may need to be modified as conditions change, but modifications are easy if the purpose of the code is easily understood.
Listing 6. Good practice: annotated functions and classes
<?php
/**
* The ResultMessage class holds a message that can be returned
* as a result of a process. The message has a severity and
* message.
*
* @author nagood
*
*/
class ResultMessage
{
private $severity;
private $message;
/**
* Constructor for the ResultMessage that allows you to assign
* severity and message.
* @param $sev See {@link getSeverity()}
* @param $msg
* @return unknown_type
*/
public function __construct($sev, $msg)
{
$this->severity = $sev;
$this->message = $msg;
}
/**
* Returns the severity of the message. Should be one
* "Information", "Warning", or "Error".
* @return string Message severity
*/
public function getSeverity()
{
return $this->severity;
}
/**
* Sets the severity of the message
* @param $severity
* @return void
*/
public function setSeverity($severity)
{
$this->severity = $severity;
}
public function getMessage()
{
return $this->message;
}
public function setMessage($msg)
{
$this->message = $msg;
}
}
/*
* Counts the messages with the given severity in the array
* of messages.
*
* @param $messages An array of ResultMessage
* @return int Count of messages with a severity of "Error"
*/
function countErrors($messages)
{
$matchingCount = 0;
foreach($messages as $m) {
if ($m->getSeverity() == "Error") {
$matchingCount++;
}
}
return $matchingCount;
}
$messages = array(new ResultMessage("Error", "This is an error!"),
new ResultMessage("Warning", "This is a warning!"),
new ResultMessage("Error", "This is another error!"));
$errs = countErrors($messages);
echo("There are " . $errs . " errors in the result.n");
?>
Handling Errors
As a general rule of thumb, if you want to write robust applications, follow the 80/20 rule for error handling: 80% of your code is used to handle exceptions and validation, and 20% of your code is used to do the actual work. This is often done when coding the basic logic (happy-path) of a program. This means writing code that works under the hood, that all data is available and all conditions are as expected. Such code can be fragile during the life cycle of the application. At the other extreme, it takes a lot of time to write code for conditions you've never encountered before.
This habit requires you to write enough error handling code, rather than writing code to deal with all errors, so that the code never completes.
Bad Habits: No Error Handling at All The code
in Listing 7 demonstrates two bad habits. First, the input parameters are not checked, even though it is known that parameters in certain states will cause exceptions in the method. Second, the code calls a method that may throw an exception but does not handle the exception. When a problem occurs, the author of the code or the person maintaining the code can only guess at the source of the problem.
Listing 7. Bad habit: not handling error conditions
<?php
// Get the actual name of the
function convertDayOfWeekToName($day)
{
$dayNames = array(
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday");
return $dayNames[$day];
}
echo("The name of the 0 day is: " . convertDayOfWeekToName(0) . "n");
echo("The name of the 10 day is: " . convertDayOfWeekToName(10) . "n");
echo("The name of the 'orange' day is: " . convertDayOfWeekToName('orange') . "n");
?>
Good Practice: Handling Exceptions
Listing 8 demonstrates throwing and handling exceptions in a meaningful way. Additional error handling not only makes the code more robust, it also improves the readability of the code, making it easier to understand. The way exceptions are handled is a good indication of the original author's intent when writing the method.
Listing 8. Good practice: handling exceptions
<?php
/**
* This is the exception thrown if the day of the week is invalid.
* @author nagood
*
*/
class InvalidDayOfWeekException extends Exception { }
class InvalidDayFormatException extends Exception { }
/**
* Gets the name of the day given the day in the week. Will
* return an error if the value supplied is out of range.
*
* @param $day
* @return unknown_type
*/
function convertDayOfWeekToName($day)
{
if (! is_numeric($day)) {
throw new InvalidDayFormatException('The value '' . $day . '' is an ' .
'invalid format for a day of week.');
}
if (($day > 6) || ($day < 0)) {
throw new InvalidDayOfWeekException('The day number '' . $day . '' is an ' .
'invalid day of the week. Expecting 0-6.');
}
$dayNames = array(
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday");
return $dayNames[$day];
}
echo("The name of the 0 day is: " . convertDayOfWeekToName(0) . "n");
try {
echo("The name of the 10 day is: " . convertDayOfWeekToName(10) . "n");
} catch (InvalidDayOfWeekException $e) {
echo ("Encountered error while trying to convert value: " . $e->getMessage() . "n");
}
try {
echo("The name of the 'orange' day is: " . convertDayOfWeekToName('orange') . "n");
} catch (InvalidDayFormatException $e) {
echo ("Encountered error while trying to convert value: " . $e->getMessage() . "n");
}
?>
Although checking parameters is a confirmation - if you require parameters to be in a certain state, it will be helpful to the people using the method - you should check them and throw meaningful exceptions:
◆ Handle exceptions as closely as possible The issues that arise are closely related.
◆Dedicated handling of each exception.
Don’t use copy-paste
You can copy-paste code from elsewhere into your code editor, but there are pros and cons to doing so. The good thing is that copying code from an example or template can avoid many mistakes. The downside is that this easily leads to a lot of similar programming methods.
Be careful not to copy and paste code from one part of the application to another. If you're this way, stop this bad habit and consider rewriting this code to be reusable. Generally speaking, putting code in one place makes it easier to maintain later because it only needs to be changed in one place.
Bad habits: Similar code snippets
in Listing 9 show several methods that are almost identical, but with different values. There are tools that can help find copied and pasted code (see Resources).
Listing 9. Bad habits: similar code snippets
<?php
/**
* Counts the number of messages found in the array of
* ResultMessage with the getSeverity() value of "Error"
*
* @param $messages An array of ResultMessage
* @return unknown_type
*/
function countErrors($messages)
{
$matchingCount = 0;
foreach($messages as $m) {
if ($m->getSeverity() == "Error") {
$matchingCount++;
}
}
return $matchingCount;
}
/**
* Counts the number of messages found in the array of
* ResultMessage with the getSeverity() value of "Warning"
*
* @param $messages An array of ResultMessage
* @return unknown_type
*/
function countWarnings($messages)
{
$matchingCount = 0;
foreach($messages as $m) {
if ($m->getSeverity() == "Warning") {
$matchingCount++;
}
}
return $matchingCount;
}
/**
* Counts the number of messages found in the array of
* ResultMessage with the getSeverity() value of "Information"
*
* @param $messages An array of ResultMessage
* @return unknown_type
*/
function countInformation($messages)
{
$matchingCount = 0;
foreach($messages as $m) {
if ($m->getSeverity() == "Information") {
$matchingCount++;
}
}
return $matchingCount;
}
$messages = array(new ResultMessage("Error", "This is an error!"),
new ResultMessage("Warning", "This is a warning!"),
new ResultMessage("Error", "This is another error!"));
$errs = countErrors($messages);
echo("There are " . $errs . " errors in the result.n");
?>
Good Practice: Reusable Functions with Parameters
Listing 10 shows the modified code, which puts the copied code into a method. Another method has also been changed and it now delegates tasks to the new method. Building a common approach takes time to design, and doing so allows you to stop and think, rather than instinctively copying and pasting. But the time invested in a common approach will pay off when changes are necessary.
Listing 10. Good practice: reusable functions with parameters
<?php
/*
* Counts the messages with the given severity in the array
* of messages.
*
* @param $messages An array of ResultMessage
* @return int Count of messages matching $withSeverity
*/
function countMessages($messages, $withSeverity)
{
$matchingCount = 0;
foreach($messages as $m) {
if ($m->getSeverity() == $withSeverity) {
$matchingCount++;
}
}
return $matchingCount;
}
/**
* Counts the number of messages found in the array of
* ResultMessage with the getSeverity() value of "Error"
*
* @param $messages An array of ResultMessage
* @return unknown_type
*/
function countErrors($messages)
{
return countMessages($messages, "Errors");
}
/**
* Counts the number of messages found in the array of
* ResultMessage with the getSeverity() value of "Warning"
*
* @param $messages An array of ResultMessage
* @return unknown_type
*/
function countWarnings($messages)
{
return countMessages($messages, "Warning");
}
/**
* Counts the number of messages found in the array of
* ResultMessage with the getSeverity() value of "Warning"
*
* @param $messages An array of ResultMessage
* @return unknown_type
*/
function countInformation($messages)
{
return countMessages($messages, "Information");
}
$messages = array(new ResultMessage("Error", "This is an error!"),
new ResultMessage("Warning", "This is a warning!"),
new ResultMessage("Error", "This is another error!"));
$errs = countErrors($messages);
echo("There are " . $errs . " errors in the result.n");
?>
Conclusion
If you develop the good habits discussed in this article when writing PHP code, you You will be able to build code that is easy to read, understand, and maintain. Maintainable code built this way will reduce the risk of debugging, fixing, and extending the code.
Using good names and shorter methods improves code readability. The purpose of commenting code is to facilitate code understanding and expansion. Handling errors appropriately makes your code more robust. Finally, stop using copy-paste to keep your code clean and improve reusability.