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

There is a phrase among developers of "eat your own dog food." This phrase means that as you develop a tool, it is a good practice to use that tool in your everyday work environment. When applied to Coder Upgrade, this phrase means modifying the Coder Upgrade API using Coder Upgrade.

The latest example of this practice was the refactoring needed to reflect the change in the array contents returned by PGPReader::getFunctionCalls(). Previously, this function returned an array whose elements were the function call objects in the code file just parsed. After the change, the array elements are the nodes holding the function call objects.

This change to the Grammar Parser API affected about 80 functions in two files of the Coder Upgrade module. These functions needed two changes to accommodate the new return value:

  • The function signature for all hook_upgrade_call_FUNCTION_NAME_alter() routines needed to change from hook($item, ...) to hook($node, ...).
  • Because the exsting hook code was expecting a $item variable, an assignment statement to set this variable needed to be added (i.e. $item = $node->data).

Our sample code to upgrade is as follows:

<?php
function hook_upgrade_call_FUNCTION_NAME_alter(&$item, &$reader) {
 
// Change the function name.
 
$item->name['value'] = 'new_name';
  if (
$item->parameterCount() > 0) {
   
// Delete the first parameter.
   
$item->deleteParameter();
  }
}
?>

The desired code is:

<?php
function hook_upgrade_call_FUNCTION_NAME_alter(&$node, &$reader) {
 
// Get the function call object.
 
$item = &$node->data;
 
// Change the function name.
 
$item->name['value'] = 'new_name';
  if (
$item->parameterCount() > 0) {
   
// Delete the first parameter.
   
$item->deleteParameter();
  }
}
?>

The code to make the changes

To make the changes, I created a Drupal module consisting of 3 files with extensions of info, module and upgrade. The module file consisted of:

<?php
/**
* Implements hook_upgrade_info().
*/
function cu_refactor_upgrade_info() {
 
$upgrade = array(
   
'title' => t('Refactor hook_upgrade_call_FUNCTION_NAME_alter(): change $item parameter to $node and add $item = $node->data to function body'),
  );
  return array(
'cu_refactor' => $upgrade);
}
?>

Because the above hook_upgrade_info() does not specify a 'files' entry, Coder Upgrade looks for the upgrade routines in a file named 'cu_refactor.upgrade.' The upgrade file consisted of:

<?php
/**
* Implements hook_upgrade_file_alter().
*
* Upgrades hook_upgrade_call_FUNCTION_NAME_alter() routines.
* Use this hook as hook_upgrade_hook_HOOK_NAME_alter() will not work due to
* the check of function name prefix against module name.
*
* @param PGPReader $reader
*   The object containing the grammar statements of the file to convert.
*/
function cu_refactor_upgrade_file_alter(&$reader) {
 
cdp("inside " . __FUNCTION__);
 
// Create helper objects.
 
$editor = PGPEditor::getInstance();
 
$nodes = &$reader->getFunctions();
  foreach (
$nodes as &$node) {
   
$item = &$node->data;
    if (!isset(
$item) || !is_object($item) || !is_a($item, 'PGPClass') || $item->type != T_FUNCTION) {
     
// The reference could have been changed in another routine so that it
      // no longer refers to an ojbect.
     
continue;
    }
   
cdp($item->name, '$item->name');
    if (!
is_object($item->name) && strpos($item->name, 'coder_upgrade_upgrade_call_') === 0) {
      if (
$item->getParameter()->toString() == '&$item') {
       
// Change first parameter to $node.
       
$editor->setParameter($item, 0, '&$node');
       
// Insert a statement to reassign the $item variable.
       
$statements = array();
       
$statements[] = '';
       
$statements[] = '// Get the function call object.';
       
$statements[] = '$item = &$node->data;';
       
$body = $item->body;
       
$anchor = $body->search('PGPAssignment', 'values', 0, '$editor', TRUE);
        if (
$anchor) {
         
cdp($anchor->data, '$anchor->data');
         
$statement = $editor->textToStatements(implode("\n", $statements));
         
$body->insertListAfter($anchor, $statement);
        }
        else {
         
cdp('Assignment to $editor not found');
         
array_shift($statements);
         
$statement = $editor->textToStatements(implode("\n", $statements) . "\n\n");
         
$body->insertListBefore($body->first(), $statement);
        }
      }
    }
  }
}
?>

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