Programming PHPProgramming PHPSearch this book

6.5. Introspection

Introspection is the ability of a program to examine an object's characteristics, such as its name, parent class (if any), properties, and methods. With introspection, you can write code that operates on any class or object. You don't need to know which methods or properties are defined when you write your code; instead, you can discover that information at runtime, which makes it possible for you to write generic debuggers, serializers, profilers, etc. In this section, we look at the introspective functions provided by PHP.

6.5.1. Examining Classes

To determine whether a class exists, use the class_exists( ) function, which takes in a string and returns a Boolean value. Alternately, you can use the get_declared_classes( ) function, which returns an array of defined classes and checks if the class name is in the returned array:

$yes_no = class_exists(classname);
$classes = get_declared_classes( );

You can get the methods and properties that exist in a class (including those that are inherited from superclasses) using the get_class_methods( ) and get_class_vars( ) functions. These functions take a class name and return an array:

$methods = get_class_methods(classname);
$properties = get_class_vars(classname);

The class name can be a bare word, a quoted string, or a variable containing the class name:

$class = 'Person';
$methods = get_class_methods($class);
$methods = get_class_methods(Person);    // same
$methods = get_class_methods('Person');  // same

The array returned by get_class_methods( ) is a simple list of method names. The associative array returned by get_class_vars( ) maps property names to values and also includes inherited properties. One quirk of get_class_vars( ) is that it returns only properties that have default values; there's no way to discover uninitiailized properties.

Use get_parent_class( ) to find a class's parent class:

$superclass = get_parent_class(classname);

Example 6-1 lists the display_classes( ) function, which displays all currently declared classes and the methods and properties for each.

Example 6-1. Displaying all declared classes

function display_classes ( ) {
  $classes = get_declared_classes( );
  foreach($classes as $class) {
    echo "Showing information about $class<br />";

    echo "$class methods:<br />";
    $methods = get_class_methods($class);
    if(!count($methods)) {
      echo "<i>None</i><br />";
    }
    else {
      foreach($methods as $method) {
        echo "<b>$method</b>( )<br />";
      }
    }

    echo "$class properties:<br />";
    $properties = get_class_vars($class);
    if(!count($properties)) {
      echo "<i>None</i><br />";
    }
    else {
      foreach(array_keys($properties) as $property) {
        echo "<b>\$$property</b><br />";
      }
    }

    echo "<hr />";
  }
}

Figure 6-1 shows the output of the display_classes( ) function.

Figure 6-1

Figure 6-1. Output of display_classes( )

6.5.2. Examining an Object

To get the class to which an object belongs, first make sure it is an object using the is_object( ) function, then get the class with the get_class( ) function:

$yes_no = is_object(var);
$classname = get_class(object);

Before calling a method on an object, you can ensure that it exists using the method_exists( ) function:

$yes_no = method_exists(object, method);

Calling an undefined method triggers a runtime exception.

Just as get_class_vars( ) returns an array of properties for a class, get_object_vars( ) returns an array of properties set in an object:

$array = get_object_vars(object);

And just as get_class_vars( ) returns only those properties with default values, get_object_vars( ) returns only those properties that are set:

class Person {
  var $name;
  var $age;
}
$fred = new Person;
$fred->name = 'Fred';
$props = get_object_vars($fred);    // $props is array('name' => 'Fred');

The get_parent_class( ) function actually accepts either an object or a class name. It returns the name of the parent class, or FALSE if there is no parent class:

class A {}
class B extends A {}
$obj = new B;
echo get_parent_class($obj);           // prints A
echo get_parent_class(B);              // prints A

6.5.3. Sample Introspection Program

Example 6-2 shows a collection of functions that display a reference page of information about an object's properties, methods, and inheritance tree.

Example 6-2. Object introspection functions

// return an array of callable methods (include inherited methods)
function get_methods($object) {
  $methods = get_class_methods(get_class($object));

  if(get_parent_class($object)) {
    $parent_methods = get_class_methods(get_parent_class($object));
    $methods = array_diff($methods, $parent_methods);
  }

  return $methods;
}

// return an array of inherited methods
function get_inherited_methods($object) {
  $methods = get_class_methods(get_class($object));

  if(get_parent_class($object)) {
    $parent_methods = get_class_methods(get_parent_class($object));
    $methods = array_intersect($methods, $parent_methods);
  }
  
  return $methods;
}

// return an array of superclasses
function get_lineage($object) {
  if(get_parent_class($object)) {
    $parent = get_parent_class($object);
    $parent_object = new $parent;
    
    $lineage = get_lineage($parent_object);
    $lineage[] = get_class($object);
  }
  else {
    $lineage = array(get_class($object));
  }

  return $lineage;
}

// return an array of subclasses
function get_child_classes($object) {
  $classes = get_declared_classes( );

  $children = array( );
  foreach($classes as $class) {
    if (substr($class, 0, 2) == '_  _') {
        continue;
    }
    $child = new $class;
    if(get_parent_class($child) == get_class($object)) {
      $children[] = $class;
    }
  }
  
  return $children;
}

// display information on an object
function print_object_info($object) {
  $class = get_class($object);
  echo '<h2>Class</h2>';
  echo "<p>$class</p>";

  echo '<h2>Inheritance</h2>';

  echo '<h3>Parents</h3>';
  $lineage = get_lineage($object);
  array_pop($lineage);
  echo count($lineage) ? ('<p>' . join(' -&gt; ', $lineage) . '</p>')
                       : '<i>None</i>';

  echo '<h3>Children</h3>';
  $children = get_child_classes($object);
  echo '<p>' . (count($children) ? join(', ', $children) 
                                 : '<i>None</i>') . '</p>';

  echo '<h2>Methods</h2>';
  $methods = get_class_methods($class);
  $object_methods = get_methods($object);
  if(!count($methods)) {
    echo "<i>None</i><br />";
  }
  else {
    echo '<p>Inherited methods are in <i>italics</i>.</p>';
    foreach($methods as $method) {
    echo in_array($method, $object_methods) ? "<b>$method</b>( );<br />"
                                            : "<i>$method</i>( );<br />";
    }
  }

  echo '<h2>Properties</h2>';
  $properties = get_class_vars($class);
  if(!count($properties)) {
    echo "<i>None</i><br />";
  }
  else {
    foreach(array_keys($properties) as $property) {
      echo "<b>\$$property</b> = " . $object->$property . '<br />';
    }
  }

  echo '<hr />';
}

Here are some sample classes and objects that exercise the introspection functions from Example 6-2:

class A {
  var $foo = 'foo';
  var $bar = 'bar';
  var $baz = 17.0;
  
  function first_function( ) { }
  function second_function( ) { }
};

class B extends A {
  var $quux = false;
  
  function third_function( ) { }
};

class C extends B {
};

$a = new A;
$a->foo = 'sylvie';
$a->bar = 23;

$b = new B;
$b->foo = 'bruno';
$b->quux = true;

$c = new C;

print_object_info($a);
print_object_info($b);
print_object_info($c);

Figure 6-2 shows the output of this code.

Figure 6-2

Figure 6-2. Object introspection output



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.