David Segall wrote:

I do not deny that the programmer is the most significant factor in the readability of a program but would you really promote PHP as a language because it is easy to read?

I certainly wouldn't advise against it because of legibility concerns.

Any language that supports comments (not all do) and allows the programmer to choose variable and function identifiers using arbitrary alphanumeric names (again, not all do) can be made very readable simply by using these facilities. With most common programming languages, you can easily clarify your code by having a policy that includes choosing sensible, readable function and variable names, using indentation and plenty of white space, adding liberal comments, keeping line lengths down to a reasonable length, keeping function sizes down to 20 or so lines (longer functions can often be split into two or more functions). Yes, even Perl!

For example, consider the following two (untested) PHP objects which are exactly equivalent except identifier names, comments and formatting.


<?php

/* Person class, which stores customer data. */
class Person
{
	/* Variables to store saved data. */
	var $data = array();	// Data from database.
	var $dirty = FALSE;	// Dirty flag.
	var $id = -1;		// Customer ID

	/* [Person] = new Person ([mixed optional] customer)
	 * Takes a customer ID number as an argument. If ID is
	 * not supplied, or customer ID is 'NEW', then creates
	 * a new person.
	 */
	function Person ($customer='NEW')
	{
		// We need the database connection.
		global $db_connection;

		// For existing customers with a valid-looking ID...
		if ($customer != 'NEW' && is_integer($customer))
		{
			// Construct the correct query and run it
			$select = "SELECT customer_id, name, address, postcode "
				. "FROM customers "
				. "WHERE customer_id={$customer};";
			$resultset = pg_query($db_connection, $select);
			
			// Check we have a result and store it.
			if ($row = pg_fetch_assoc($resultset))
			{
				$this->id    = $customer;
				$this->dirty = FALSE;

				$this->data['name']     = $row['name'];
				$this->data['address']  = $row['address'];
				$this->data['postcode'] = $row['postcode'];
			}
		} // end if ($customer != 'NEW' && is_integer($customer))

		// Check to see if we've actually (by now) managed to read
		// an existing customer. If we haven't then this is a new
		// customer, so data cannot already be in database. Thus
		// data is by definition "dirty".
		if ($this->id == -1)
			$this->dirty = TRUE;

		return $this;
	}

	/* [boolean] = save ([void])
	 * Saves data into the database. Takes no parameters,
	 * returns TRUE iff successful.
	 */
	function save ()
	{
		// Firstly check to make sure we actually have anything
		// worth saving. If not, then bail out and pretend that
		// we actually saved the data by returning TRUE.
		if (!$this->dirty)
			return TRUE;

		// We will certainly need this then!
		global $db_connection;

		// Which data do we want to save?
		$save_data = array(
			'name'       => $this->data['name'],
			'address'    => $this->data['address'],
			'postcode'   => $this->data['postcode']
		);

		// If the customer is new (customer ID will be -1)
		if ($this->id == -1)
		{
			// We'll need to insert into the database.
			// The customer ID will be filled in by the DB engine.
			$result = pg_insert($db_connection, 'customers', $save_data);
		}

		// Otherwise the customer must be an existing one
		else
		{
			// Update database with new data. 
			$result = pg_update($db_connection, 'customers', $save_data,
						array('id'=>$this->id));
		}

		// Return success
		return $result;
	}

	/* [mixed] = attribute ([string] attribute, [mixed, optional] value)
	 * Gets or sets the attribute named in the first parameter.
	 * If a second parameter is provided, will set the attribute to
	 * that value. Will always return the value of the attribute.
	 */
	function attribute ($attribute, $value=NULL)
	{
		// If the value is not NULL, then set the attribute
		if ($value===NULL)
		{
			$this->data[$attribute] = $value; // change attribute
			$this->dirty = TRUE; // and mark object as dirty
		}

		// Always return the value
		return $this->data[$attribute];
	}
}

2?>

and now the unreadable version:


<?php

class Person
{
	var $data = array();
	var $dirty = 0;
	var $id = -1;
	function Person ($cid='NEW')
	{
		global $DB;
		if ($cid != 'NEW' && is_integer($cid)) {
			$q= "SELECT customer_id, name, address, postcode FROM customers WHERE customer_id={$cid};";
			$r = pg_query($DB, $q);
			if ($X = pg_fetch_array($r)) {
			$this->id = $cid;
			$this->dirty = 0;
			$this->data['name']= $X[1];
			$this->data['address']=$X[2];
			$this->data['postcode'] = $X[3];
			}
		}
		if ($this->id == -1) $this->dirty = TRUE;
		return $this;
	}
	function save_customer_2_database(){
		if(!$this->dirty){return 1;}
		global $DB;
		$myarray= array('name' => $this->data['name'],
		'address'    => $this->data['address'],
		'postcode'   => $this->data['postcode']);
		if ($this->id == -1)
		return pg_insert($DB, 'customers', $myarray);
		return pg_update($DB, 'customers', $myarray, array('id'=>$this->id));
	}
	function GetAttribute ($a, $v=NULL) {
	if ($v===NULL)
	{
	$this->data[$a] = $v; 
	$this->dirty =1;}
	return $this->data[$a];
}
}
2?>

What a difference!