Last updated February 9, 2011. Created by solotandem on February 12, 2010.
Log in to edit this page.

There is a strong correlation between the degree of difficulty involved with a change and the usefulness of printing the associated grammar objects. The Grammar Parser provides some handy routines for this purpose, including print_r() and cdp().

To print the grammar object associated with a function call, $item, we can write $item->print_r(). To output this text to the page, we can write "echo $item->print_r()." Because of the potential amount of text to be output, it is often more useful to dump this output to a file. This is easily done with a call to cdp() which will output the text to a file called debug.txt in the subdirectory of the site's files directory specified in the module settings at "/admin/config/development/coder/upgrade/settings." To enable this output, check the "Enable debug output from coder upgrade" box in the module settings.

Example 1: Replace a function call with a variable expression

Among the D7 changes, the function comment_node_url() has been removed. The issue suggests replacing the call with the expression 'comment/' . $comment->cid to build a URL. As mentioned in the beginner examples, the Grammar Parser getFunctionCalls() routine returns a list of function calls. We can use this list to locate each occurrence of comment_node_url() in our code.

Loosely speaking, every function call is a single node of an expression (which is simply a list of nodes). This change involves replacing a single node (holding the function call object) with multiple nodes (the replacement expression) in the grammar representing our code file. To avoid creating these objects in code and setting their properties (which would require us to learn more about the grammar object structure than we want to), we will take advantage of the editing tools in our grammar parser tool box.

Our sample code to upgrade is as follows:

<?php
function example_comment_node_url() {
 
// comment_node_url() -- Change the next line but leave this alone
 
$var = 'xx/' . comment_node_url() . 'yy';
  if (
comment_node_url() == '') {
  }
  return
comment_node_url();
}
?>

To make this upgrade, we will use the 'container' property of the node holding the function call object. The container property is a reference to the expression containing the function call node. In our sample code above, the container expression containing the first occurrence of comment_node_url() is: $var = 'xx/' . comment_node_url() . 'yy'. Using our tools, we:

  • Create the replacement expression object (i.e. a list of nodes).
  • Insert the replacement expression in the container before the function call node.
  • Delete the function call node.

The code to do this follows:

<?php
// Get the array of nodes holding function call objects.
$nodes = $reader->getFunctionCalls();
foreach (
$nodes as &$node) {
 
// Get the function call object.
 
$item = &$node->data;
 
// Make sure this function call has a simple string for its name.
 
if (!is_array($item->name) || !isset($item->name['value']) {
    continue;
  }
  switch (
$item->name['value']) {
    case
'comment_node_url':
     
$temp = $editor->expressionToStatement("'comment/' . \$comment->cid");
     
$node->container->insertListBefore($node, $temp);
     
$node->container->delete($node);
      break;
  }
}
?>

The resulting code is:

<?php
function example_comment_node_url() {
 
// comment_node_url() -- Change the next line but leave this alone
 
$var = 'xx/' . 'comment/' . $comment->cid . 'yy';
  if (
'comment/' . $comment->cid == '') {
  }
  return
'comment/' . $comment->cid;
}
?>

Example 2: Insert a statement before and after a function call and change the function call parameters

In D7, the parameters to filter_formats() have changed. By default (when called with no parameters), this function now returns a list of all text formats on the site, rather than only those formats that the current user has permission to use. To mimic the D6 functionality, we need to use the new $account parameter to retrieve a list of formats for the current user.

Our sample code to upgrade is as follows:

<?php
$formats
= filter_formats($index);
?>

To make this upgrade, we will use the 'parent' property of the function call object, along with some other tools in our editing API. The parent property references the node holding the statement the function call is part of. In our example, the parent node contains the statement '$formats = filter_formats($index).' Using our tools, we:

  • Save the text of the parameter (if any) to the current function call.
  • Set the parameter to the function call to $user.
  • Insert a global statement before the parent statement.
  • Insert a statement after the parent statement that resets the existing assignment variable to the format at the original index (if any).

The code to do this follows:

<?php
// Get the array of nodes holding function call objects.
$nodes = $reader->getFunctionCalls();
foreach (
$nodes as &$node) {
 
// Get the function call object.
 
$item = &$node->data;
 
// Make sure this function call has a simple string for its name.
 
if (!is_array($item->name) || !isset($item->name['value']) {
    continue;
  }
  switch (
$item->name['value']) {
    case
'filter_formats':
     
// Save the parameter to the current function call.
     
$index = $item->printParameter(0);
     
// Set the parameter to $user.
     
$p0 = $editor->expressionToStatement('$user');
     
$item->setParameter(0, $p0);
     
// Insert a global statement before the statement this function call is part of.
     
$statement = $editor->textToStatements('global $user;')->getElement(0);
     
$item->insertStatementBefore($statement);
      if (
$index) {
       
// Insert an assignment statement after the statement this function call is part of.
       
$assignment = $item->parent->data;
       
$assign_variable = $assignment->values->getElement()->getElement()->toString();
       
$statement = $editor->textToStatements("$assign_variable = $assign_variable\[$index\];")->getElement(0);
       
$item->insertStatementAfter($statement);
      }
      break;
  }
}
?>

The resulting code is:

<?php
 
global $user;
 
$formats = filter_formats($user);
 
$formats = $formats[$index];
?>

Looking at the grammar structure for $assignment helps to understand the $assign_variable statement above. The expression $assignment->values->getElement()->getElement() refers to the first item labeled "operand" below. (Note: this listing is produced by echo $assignment->print_r() or cdp($assignment).)

PGPAssignment
(
    [type] => 602
    [values] => PGPList
        (
            [0] => PGPExpression
                (
                    [operand] => PGPOperand
                        (
                            [type] => 309 (T_VARIABLE)
                            [value] => $formats
                        )
                    [assign] => =
                    [operand] => PGPFunctionCall
                        (
                            [type] => 601
                            [name] => Array
                                (
                                    [type] => 307 (T_STRING)
                                    [value] => filter_formats
                                )
                            [noparens] => 0
                            [parameters] => PGPList
                                (
                                    [0] => PGPExpression
                                        (
                                            [operand] => PGPOperand
                                                (
                                                    [type] => 309
                                                    [value] => $user
                                                )
                                        )
                                )
                        )
                )
        )
)

Looking for support? Visit the Drupal.org forums, or join #drupal-support in IRC.