http://daniberg.com
Programming PHP, 2nd Edition
Rasmus Lerdorf, Kevin Tatroe & Peter MacIntyre
Index 
Chapter 05
Chapter 06
Page 156 - Class Person
Page 161 - Serialization
Page 135 - Example 5-3. Sorting arrays  
back

Reading the example I find one thing worth mentioning: declaring vars in PHP. From time to time I have to deal with code written by other programmers. It's not rare to deal with sloppy or naive code.

So, think about the poor soul who will struggle with your code in the future and do us all a huge favor. Set the directives error_reporting = E_ALL and register_globals = Off in your php.ini file.

The directive Register globals now comes off by default. That's good!

On the other hand the NOTICE flag comes disabled in the error_reporting directive. It'll avoid giving tips on how your code works in the server. That's also good! But...

Turning it ON will prevent you to fall into situations like referring to undefined vars or being out of boundaries in arrays. It will help you debug and build cleaner code on your development environment.

Ok, so if you run the script as print in the book you'll see the following warnings:

Undefined variable: submitted in ...path... on line 19
Undefined variable: submitted in ...path... on line 47
Where ...path... is the path to the file on your system.

The variable $submitted is not defined the first time you run the script. So, as rule of thumb, vars should be declared before being referred. Since the form is set with the post method, you should refer to $submitted as $_POST['submitted']. The same applies to all vars inside the form.

Here's the complete modified code:

<?php
if(!isset($_POST['submitted'])) { $_POST['submitted'] = ''; }

function user_sort($a, $b) 
{	
  // smarts is all-important, so sort it first
  if($b == 'smarts') {
    return 1;
  }
  else if($a == 'smarts') {
		return -1;
  }
	
  return ($a == $b) ? 0 : (($a < $b) ? -1 : 1);
}

$values = array(
  'name' => 'Buzz Lightyear',
  'email_address' => 'buzz@startcommand.gal',
  'age' => 32,
  'smarts' => 'some');
								
if($_POST['submitted']) {
  if($_POST['sort_type'] == 'usort' || $_POST['sort_type'] == 'uksort' || $_POST['sort_type'] == 'uasort') {
    $_POST['sort_type']($values, 'user_sort');
  }
  else {
    $_POST['sort_type']($values);
  }
}
?>

<form action="example-5-3-modified.php" method="post">
  <p>
    <input type="radio" name="sort_type" value="sort" checked="checked" />Standard sort<br />
    <input type="radio" name="sort_type" value="rsort" />Reverse sort<br />
    <input type="radio" name="sort_type" value="usort" />User-defined sort<br />
    <input type="radio" name="sort_type" value="ksort" />Key sort<br />
    <input type="radio" name="sort_type" value="krsort" />Reverse key sort<br />
    <input type="radio" name="sort_type" value="uksort" />User-defined key sort<br />
    <input type="radio" name="sort_type" value="asort" />Value sort<br />
    <input type="radio" name="sort_type" value="arsort" />Reverse value sort<br />
    <input type="radio" name="sort_type" value="uasort" />User-defined value sort<br />
  </p>
	
  <p align="center">
    <input type="submit" value="Sort" name="submitted" />
  </p>
	
  <p>
    Values <?= $_POST['submitted'] ? 'sorted by '.$_POST['sort_type'] : "unsorted"; ?>:
  </p>
	
  <ul>
    <?
    foreach($values as $key=>$value) {
      echo "<li><b>$key</b>: $value</li>";
    }
    ?>
  </ul>
</form>
Page 152 - Constructor Example  
back

Constructors - Just a typo in the constructor. The code should be

...
function __constructor($name, $age)
...
Page 152 - Constructor Parent Class  
back

The code demonstrates that in PHP you must explicitly call the constructor of a base class. I have modified the code a little bit.

1. instead of the keyword var your code will be cleaner and easier to debug if you make use of public, protected and private. It's always a good idea to apply the rule of less privilege possible for both methods and properties.

2. The constructors for both classes make use of the magic function __construct() and the access modifier keyword public.

4. Finally, if we take the advice from page 150:

"Using parent:: centralizes the knowledge of the parent class in the extends clause".

We replace $this->Person for parent::__construct.

Here's the modified code:

<?php
class Person {
	protected $name, $address, $age;
	
	public function __construct($name, $address, $age) {
		
		$this->name = $name;
		$this->address = $address;
		$this->age = $age;
	}
}


class Employee extends Person {
	protected $position, $salary;
	
	public function __construct($name, $address, $age, $position, $salary) {
		
		parent::__construct($name, $address, $age);
		$this->position = $position;
		$this->salary = $salary;
	}
	
	public function __toString() {
		return $this->name;
	}
}

?>
Page 153 - Examining Classes  
back

The section about Introspection - Examining Classes presents the functions get_class_methods() and get_class_vars().

The author presents 3 ways to pass the classname parameter to get_class_methods() function. The problem we see here is the second example:

$methods = get_class_methods(Person);

The code is not appropriate since it will throw an E_NOTICE. The function expects a string or an object as param and the PHP interpreter will look for the constant Person which does not exist. So, you should really stick to the 'Person' in this case.

The examples in this section only make use of the old PHP4 syntax. Keywords public, protected and private are not present.

Since you're a PHP5 whiz and public, protected and private are regular spells to you both functions will only return vars and methods set as public.

One side effect is that if you're examining a class, you'll fetch NULL values for your vars. Member vars will be initialized in the constructor -- after an object is instantiated.

The following piece of code illustrates this behavior:

<?php

class A
{
  var $var1 = 1;
  public $var2;
  protected $var3;
  private $var4;
  
  public function __construct()
  {
    $this->var2 = 2;
    $this->var3 = 3;
    $this->var4 = 4;
  }
  
  function appear() {}
  public function appearPublic() {}
  protected function hiddenProtected() {}
  private function hiddenPrivate() {}
  
}

// examining the class
$aVars = get_class_vars('A');
var_dump($aVars);

echo '<br />';

$aMethods = get_class_methods('A');
var_dump($aMethods);

echo '<br />';

// examining the object
$hA = new A();

$aVars = get_object_vars($hA);
var_dump($aVars);

?>
Page 156 - Class Person  
back

There's a small typo in the first line of page 156. The correct statement is:

$fred = new Person;
Page 156 - Introspection - Example 06  
back

The first function presented in example 6-2 holds a wrong comment. The function get_methods() return an array of callable methods EXCLUDING inherited methods. The comment should be replaced with:

// return an array of callable methods (exclude inherited methods)

Depending on how is set your php environment you can run into trouble with the function get_child_classes(). The problem is in the statement:

$child = new $class;
What if the class expects some params? So, to avoid instantiating classes you can take advantage of Reflection. Here's an alternative version of the function get_child_classes():
function get_child_classes($object)
{
	$classes = get_declared_classes();
	
	$children = array();
	foreach($classes as $class)
	{
		// better approach
		$hRef = new ReflectionClass($class);
		if($hRef->isSubclassOf(get_class($object)))
			$children[] = $class;
	}
	
	return $children;
}

Page 161 - Serialization  
back

The only problem I see with the Serialization example is that it needs the directive register_globals set to on. Since we now have this directive set to off as default it's better to follow the trend.

The code below displays the modified version of the file front.php

<?php
include_once('ex-6-3-log-inc.php');
session_start();
?>

<html>
<head>
	<title>Front Page</title>
</head>
<body>

<?php 
$l = '';
$now = strftime("%c");

// register globals == off approach

if(!isset($_SESSION['l']))
{
	$l = new Log("/tmp/persistent_log");
	
	$l->write("Created $now");
	echo("Created session and persistent log object.<p>");
	
}

else
{
	$l = unserialize($_SESSION['l']);
}

$l->write("Viewed first page $now");
echo "The log contains:<p>";
echo nl2br($l->read());

// should be last command since it cleans up file resource
$_SESSION['l'] = serialize($l);
?>

<a href="ex-6-5-next.php">Move to the next page</a>

</body>
</html>

There are only 3 things worth mentioning. First note that the var $l is declared in the beginning and out of the {}. Although PHP won't complain it won't hurt coding with scope in mind. Second, we're using $_SESSION instead of session_register. Finally, we're calling serialize in the end of the code.

The modified version next.php is below. This time the only change is the use of $_SESSION to access $l. Ok, now you can also press the Back button on your browser.

Please, note that I have changed the filenames.

<?php
include_once('ex-6-3-log-inc.php');
session_start();
?>

<html>
<head><title>Next Page</title></head>
<body>

<?php 
$l = unserialize($_SESSION['l']);

$now = strftime("%c");
$l->write("Viewed page 2 at $now");

echo "The log contains:<p>";
echo nl2br($l->read());
?>

</body>
</html>



Website and all code by Dani Berg
Comments, suggestions and questions:
dani@daniberg.com
© Copyright 2004-2008. All rights reserved. DANIBERG.