function coder_upgrade_callback_function_calls in Coder 7
Same name and namespace in other branches
- 7.2 coder_upgrade/conversions/other.inc \coder_upgrade_callback_function_calls()
Callback routine for function call changes using grammar parser.
Programmatically changing the source code requires changing the contents of the grammar object (the grammar equivalent of the code represented as a PGPList of statements). A difficult way to do this would be to manually create items (arrays or objects extended from the PGPBase class) and replace the existing grammar items with the new items. This would essntially duplicate the functionality of the PGPReader class.
A simpler approach takes advantage of the grammar parser API which provides helper functions for working with function call objects. Some examples of typical tasks follow.
Print a grammar object
Before making a change, it is a good idea to print the contents of the grammar object to become familiar with its structure and contents. PGP provides a custom print_r function for this purpose. (NOTE: calls to the built-in print_r, var_dump or equivalent routines for this purpose is not recommended as the output may be extremely voluminous.) All of the objects (as opposed to array items) can be printed using the custom print_r routine. An array item can be printed using the built-in print_r routine.
// Print the grammar item (a function call object in this case).
cdp($item
->print_r());
// Print the parameters to the function call.
cdp($item->parameters
->print_r());
// Alternatively, with a parameters object, do this.
$parameters = $item->parameters;
cdp($parameters
->print_r());
Change the name of the function being called
// Change the name of the function being called.
$name['value'] = 'new_function_name';
Get a parameter object and an expression object
// Get a parameter object (PGPExpression).
$param = $item
->getParameter(0);
// Get the first element of the parameter expression (PGPOperand or array).
$element = $param
->getElement(0);
Reorder the parameters
// Save the current parameters to local variables.
$p0 = $item
->getParameter(0);
$p0 = $item
->getParameter(1);
// Swap the parameters.
$item
->setParameter(0, $p1);
$item
->setParameter(1, $p0);
Insert or delete a parameter object
$item
->insertParameter(2, $expression);
$item
->deleteParameter(2);
Set grammar elements from a string of code
As mentioned above, it is not recommended to manually create items (arrays or objects extended from the PGPBase class) and replace the existing grammar items with the new items. A simpler approach is to create the string of code to be inserted and use the PGPEditor class to convert this string to its grammar representation.
// Set a single parameter from a string of code.
$editor
->setParameter($item, 0, $code);
// Set all of the parameters from an array of code strings.
$editor
->setParameters($item, array(
'$form',
'$form_state',
));
// Insert a parameter from a string of code.
$editor
->insertParameter($item, 2, '$langcode = \'en\'');
Get grammar elements as a string of code
// Print a parameter (i.e. convert it from a grammar object to a string).
$parameter = $item
->printParameter(1);
// Get the grammar equivalent of a string of code.
$expression = $editor
->expressionToStatement($string);
Other debug print examples.
cdp($list
->printNode($parent));
cdp($list
->printArray($parent));
cdp($list
->printArray($temp
->getElement(0)));
cdp($list
->printNode($temp
->get(0)));
Parameters
PGPFunctionCall $item: A function call object of the expression or statement.
PGPReader $reader: The object containing the grammar statements of the file to convert.
1 string reference to 'coder_upgrade_callback_function_calls'
- coder_upgrade_convert_function_calls in coder_upgrade/
conversions/ other.inc - Upgrades function calls using grammar parser.
File
- coder_upgrade/
conversions/ other.inc, line 218 - Other conversion routine file for the coder_upgrade module.
Code
function coder_upgrade_callback_function_calls(&$item, &$reader) {
cdp("inside " . __FUNCTION__);
// cdp($item->print_r());
/*
* The $item variable passed to this function is a PGPFunctionCall object.
* When passed here via the coder_upgrade_convert_function_calls routine, the
* variable was an entry in the function calls array filled by the PGPReader
* class (and accessed as $reader->getFunctionCalls()).
*
* The function calls array contains references to PGPFunctionCall objects.
* As these objects are processed, they may change or eliminate other
* references yet to be processed (i.e. if other function call references
* are contained in the current reference). Because of this, we need to
* test whether $item actually refers to a PGPFunctionCall object before
* attempting to do any processing.
*/
if (!isset($item) || !is_a($item, 'PGPFunctionCall')) {
return;
}
// Create helper objects.
$editor = new PGPEditor();
$list = new PGPList();
// Process function call.
$name =& $item->name;
switch ($name['value']) {
case 'actions_synchronize':
// DONE
$count = $item->parameters
->count();
if ($count > 0) {
$item
->deleteParameter();
}
break;
case 'book_toc':
// DONE
// Adjust parameters.
$count = $item->parameters
->count();
if ($count > 2) {
// Switch places.
$p1 = $item
->getParameter(1);
$p2 = $item
->getParameter(2);
$item
->setParameter(1, $p2);
$item
->setParameter(2, $p1);
}
// Remove default parameter.
if ($count == 3) {
$value = $item
->printParameter(2);
cdp("value = {$value}");
if ($value == 'array()') {
$item
->deleteParameter(2);
}
}
break;
case 'check_markup':
// DONE
if ($item->parameters
->count() > 2) {
$editor
->insertParameter($item, 2, '$langcode = \'\' /* TODO Set this variable. */');
}
break;
case '_comment_load':
// DONE
// TODO The comment_wildcard change in hook_menu.
$name['value'] = 'comment_load';
break;
case 'comment_node_url':
// DONE
$parent = $item->parent;
$temp = $editor
->statementsToText($parent);
$from = '@comment_node_url()@';
$to = "'comment/' . \$comment->cid";
$temp = preg_replace($from, $to, $temp);
$temp = $editor
->textToStatements($temp);
$parent->data = $temp
->getElement(0);
break;
case 'comment_validate':
// DONE
$name['value'] = 'comment_form_validate';
$editor
->setParameters($item, array(
'$form',
'$form_state /* TODO Set these variables. */',
));
break;
case 'db_add_field':
// includes/database.pgsql.inc Add a new field to a table.
case 'db_add_index':
// includes/database.pgsql.inc Add an index.
case 'db_add_primary_key':
// includes/database.pgsql.inc Add a primary key.
case 'db_add_unique_key':
// includes/database.pgsql.inc Add a unique key.
case 'db_change_field':
// includes/database.pgsql.inc Change a field definition.
case 'db_create_table':
// includes/database.inc Create a new table from a Drupal table definition.
case 'db_create_table_sql':
// includes/database.pgsql.inc Generate SQL to create a new table from a Drupal schema definition.
case 'db_drop_field':
// includes/database.pgsql.inc Drop a field.
case 'db_drop_index':
// includes/database.pgsql.inc Drop an index.
case 'db_drop_primary_key':
// includes/database.pgsql.inc Drop the primary key.
case 'db_drop_table':
// includes/database.pgsql.inc Drop a table.
case 'db_drop_unique_key':
// includes/database.pgsql.inc Drop a unique key.
case 'db_field_names':
// includes/database.inc Return an array of field names from an array of key/index column specifiers.
case 'db_field_set_default':
// includes/database.pgsql.inc Set the default value for a field.
case 'db_field_set_no_default':
// includes/database.pgsql.inc Set a field to have no default value.
case 'db_rename_table':
// includes/database.pgsql.inc Rename a table.
$item
->deleteParameter();
break;
case 'drupal_add_css':
// DONE
// A similar comment to that in 'drupal_add_js' below applies here.
cdp($item->parameters
->print_r());
$count = $item->parameters
->count();
if ($count < 3) {
$type = trim($item
->printParameter(1), "'\"");
if (in_array($type, array(
'module',
'theme',
))) {
$editor
->setParameter($item, 1, "'file'");
// Could simply delete it.
}
break;
}
$keys = array(
'type',
'media',
'preprocess',
);
$defaults = array(
"'module'",
"'all'",
'TRUE',
);
$string = $editor
->arrayitize($item, 1, $keys, $defaults);
$string = preg_replace('@[\'"]theme[\'"]@', "'file'", $string);
// Could be deleted.
if ($string != 'array()') {
$temp = $editor
->expressionToStatement($string);
$temp
->getElement(0)->multiline = 0;
cdp($temp
->print_r());
$item
->setParameter(1, $temp);
}
break;
case 'drupal_add_js':
// DONE
/*
* With
* drupal_add_js('misc/collapse.js', 'core', 'header', FALSE, TRUE, TRUE);
* we will output
* drupal_add_js('misc/collapse.js', array(
* 'type' => 'core',
* ));
* which is correct, although the function will also accept
* drupal_add_js('misc/collapse.js', 'core');
* The example begs the question why someone would have included all
* the default parameters.
*
* A type of 'core', 'module' or 'theme' all convert to 'file' which is
* the new default. We could add a weight item based on the type?
*/
cdp($item->parameters
->print_r());
$count = $item->parameters
->count();
if ($count < 3) {
$type = trim($item
->printParameter(1), "'\"");
if (in_array($type, array(
'core',
'module',
'theme',
))) {
$editor
->setParameter($item, 1, "'file'");
// Could simply delete it.
}
break;
}
$keys = array(
'type',
'scope',
'defer',
'cache',
'preprocess',
);
$defaults = array(
"'module'",
"'header'",
'FALSE',
'TRUE',
'TRUE',
);
$string = $editor
->arrayitize($item, 1, $keys, $defaults);
$string = preg_replace('@[\'"](core|theme)[\'"]@', "'file'", $string);
// Could be deleted.
if ($string != 'array()') {
$temp = $editor
->expressionToStatement($string);
$temp
->getElement(0)->multiline = 0;
cdp($temp
->print_r());
$item
->setParameter(1, $temp);
}
break;
case 'drupal_clone':
// DONE
$name['value'] = 'clone';
$item->noparens = 1;
break;
case 'drupal_eval':
// DONE
/*
* Examine the statement containing the function call.
* Wrap the containing statement in an "if (module_exists('php'))" block.
* The function call may be the containing statement.
*/
// Set the name of the function call.
$name['value'] = 'php_eval';
// Get the parent = statement (i.e. node) this function call is part of.
$parent = $item->parent;
$temp = $editor
->statementsToText($parent);
$temp = $editor
->textToStatements("if (module_exists('php')) {\n\t{$temp}\n}");
$parent->data = $temp
->getElement(0);
break;
case 'drupal_execute':
// DONE
$name['value'] = 'drupal_form_submit';
break;
case 'drupal_get_content':
// DONE
$name['value'] = 'drupal_get_region_content';
break;
case 'drupal_get_headers':
// DONE
$name['value'] = 'drupal_get_header';
break;
case 'drupal_http_request':
// DONE
cdp($item->parameters
->print_r());
$count = $item->parameters
->count();
if ($count == 1) {
break;
}
$keys = array(
'headers',
'method',
'data',
'max_redirects',
);
$defaults = array(
'xxx_YYY_zzz',
"'GET'",
'NULL',
3,
);
$string = $editor
->arrayitize($item, 1, $keys, $defaults);
$temp = $editor
->expressionToStatement($string);
$temp
->getElement(0)->multiline = 0;
cdp($temp
->print_r());
$item
->setParameter(1, $temp);
break;
case 'drupal_json':
// DONE
$name['value'] = 'drupal_json_output';
break;
case 'drupal_rebuild_code_registry':
// DONE
$name['value'] = 'registry_rebuild';
break;
case 'drupal_rebuild_theme_registry':
// DONE
$name['value'] = 'drupal_theme_rebuild';
break;
case 'drupal_set_content':
// DONE
$name['value'] = 'drupal_add_region_content';
break;
case 'drupal_set_header':
// DONE
// TODO the hook_file_download() changes. Use parser on the entire function.
$temp = $item
->printParameters();
if (strpos($temp, 'Content-Type:') !== FALSE) {
$temp = explode(':', $temp);
foreach ($temp as $key => $string) {
$temp[$key] = "'" . trim($string, "' ") . "'";
}
$editor
->setParameters($item, $temp);
}
elseif (strpos($temp, "\$_SERVER['SERVER_PROTOCOL']") !== FALSE || strpos($temp, '\\$_SERVER["SERVER_PROTOCOL"]') !== FALSE) {
$from = '@\\$_SERVER\\[(\'|")SERVER_PROTOCOL(\'|")\\]\\s*\\.\\s*(\'|")\\s*(.*?)(\'|")@';
$to = "\$3\$4\$3";
$temp = preg_replace($from, $to, $temp);
$editor
->setParameters($item, array(
$temp,
));
}
break;
case 'drupal_set_html_head':
// DONE
$name['value'] = 'drupal_add_html_head';
break;
case 'drupal_set_title':
// DONE
$temp = $item
->printParameters();
cdp("temp = {$temp}");
if (strpos($temp, 'check_plain') !== FALSE) {
// TODO Could check for isset, count of parameters, etc.???
// check_plain could be part of an expression (e.g. concatenation).
$temp = $item
->getParameter(0);
$temp = $temp
->getElement(0);
cdp("Printing the first ");
cdp($temp
->print_r());
$editor
->setParameter($item, 0, $temp
->printParameter(0));
cdp($item
->print_r());
break;
}
$parameters =& $item->parameters;
if ($call =& $parameters
->search($parameters, 'PGPFunctionCall', 'name', 'value', 't')) {
$temp = $call
->toString();
cdp("temp = {$temp}");
if (preg_match('#(\'|")@\\w+(\'|")\\s*=>\\s*#', $temp)) {
$editor
->setParameter($item, 1, 'PASS_THROUGH');
cdp($item
->print_r(0, $item));
}
}
break;
case 'drupal_system_listing':
// DONE
$temp = $item
->printParameter(0);
// Check for type == T_CONSTANT_ENCAPSED_STRING
// Check for a '/' in the mask and use a different mask or delimit the '/' with '\/'.
if ($temp[0] == "'") {
$editor
->setParameter($item, 0, "'/" . substr($temp, 1, -1) . "/'");
}
elseif ($temp[0] == '"') {
$editor
->setParameter($item, 0, '"/' . substr($temp, 1, -1) . '/"');
}
// else if type == T_VARIABLE, find the $mask used in the call and examine its value.
break;
case 'drupal_to_js':
// DONE
$name['value'] = 'drupal_json_encode';
break;
case 'drupal_uninstall_module':
// DONE
$name['value'] = 'drupal_uninstall_modules';
$temp = $item
->printParameters();
$editor
->setParameters($item, array(
'array(' . $temp . ')',
));
break;
case 'file_scan_directory':
// DONE
// TODO Part of http://drupal.org/node/224333#preg_match
// TODO Other changes apply to this function call.
$temp = $item
->printParameter(1);
// Check for type == T_CONSTANT_ENCAPSED_STRING
// Check for a '/' in the mask and use a different mask or delimit the '/' with '\/'.
if ($temp[0] == "'") {
$editor
->setParameter($item, 1, "'/" . substr($temp, 1, -1) . "/'");
}
elseif ($temp[0] == '"') {
$editor
->setParameter($item, 1, '"/' . substr($temp, 1, -1) . '/"');
}
// else if type == T_VARIABLE, find the $mask used in the call and examine its value.
break;
case 'file_set_status':
// DONE
if ($item->parameters
->count() == 2) {
$p0 = $item
->printParameter(0);
$p1 = $item
->printParameter(1);
// Insert statement.
$temp = $editor
->textToStatements("{$p0}->status &= {$p1}");
$parent = $item->parent;
$parent->container
->insertBefore($parent, $temp
->getElement(0));
// Change statement.
$from = $temp1;
$to = "{$p0} = file_save({$p0})";
$temp = str_replace($from, $to, $temp2);
$temp = $editor
->textToStatements($temp);
$parent->data = $temp
->getElement(0);
}
break;
case 'filter_formats':
// DONE
// If has a parameter, then change it to $user and add global statement.
if ($item->parameters
->count() > 0) {
$p0 = $editor
->expressionToStatement('$user');
$item
->setParameter(0, $p0);
// Get the parent = statement (i.e. node) this function call is part of.
$parent =& $item->parent;
// Get the statement list the parent is part of.
$container =& $parent->container;
// Insert a statement.
$statement = $editor
->textToStatements("global \$user;")
->getElement(0);
$container
->insertBefore($parent, $statement, 'global');
}
break;
case 'format_plural':
// DONE
$count = $item->parameters
->count();
if ($count < 5) {
break;
}
$keys = array(
'langcode',
);
$defaults = array(
"'XXX_YYY'",
);
$string = $editor
->arrayitize($item, 4, $keys, $defaults);
$temp = $editor
->expressionToStatement($string);
$temp
->getElement(0)->multiline = 0;
cdp($temp
->print_r());
$item
->setParameter(4, $temp);
break;
case 'function_exists':
// Change was reverted.
// $name['value'] = 'drupal_function_exists';
break;
case 'menu_path_is_external':
// DONE
$name['value'] = 'url_is_external';
break;
case 'module_invoke':
// DONE
// http://drupal.org/node/224333#taxonomy_get_tree
$depth = '$max_depth = NULL /* TODO Set this variable. */';
$count = $item->parameters
->count();
// Confirm this call relates to our topic.
if ($count > 2) {
$p0 = $item
->printParameter(0);
$p1 = $item
->printParameter(1);
if ($p0 != "'taxonomy'" || $p1 != "'get_tree'") {
cdp("FAILED to relate");
break;
}
}
// Adjust parameters.
if ($count > 5) {
// Switch places.
$p4 = $item
->getParameter(4);
$p5 = $item
->getParameter(5);
$item
->setParameter(4, $p5);
$item
->setParameter(5, $p4);
}
elseif ($count > 4) {
// Insert parameter due to change in parameter order.
$editor
->insertParameter($item, 4, $depth);
$count = $item->parameters
->count();
}
$defaults = array(
array(
'NULL',
$depth,
),
'-1',
);
$string = $editor
->removeDefaults($item, 4, $defaults);
break;
case 'module_list':
// DONE
$name['value'] = 'module_implements';
$editor
->setParameters($item, array(
'$hook /* TODO Set this variable. */',
));
break;
case 'module_rebuild_cache':
// DONE
$name['value'] = 'system_rebuild_module_data';
break;
case 'referer_uri':
// DONE
$expression = $editor
->expressionToStatement("\$_SERVER['HTTP_REFERER']");
$item = $expression
->getElement(0);
break;
case 'set_time_limit':
// DONE
$name['value'] = 'drupal_set_time_limit';
break;
case 'system_theme_data':
// DONE
$name['value'] = 'system_rebuild_theme_data';
break;
case 't':
// DONE
$count = $item->parameters
->count();
if ($count < 3) {
break;
}
$keys = array(
'langcode',
);
$defaults = array(
"'XXX_YYY'",
);
$string = $editor
->arrayitize($item, 2, $keys, $defaults);
$temp = $editor
->expressionToStatement($string);
$temp
->getElement(0)->multiline = 0;
cdp($temp
->print_r());
$item
->setParameter(2, $temp);
break;
case 'taxonomy_del_term':
// DONE
$name['value'] = 'taxonomy_term_delete';
break;
case 'taxonomy_del_vocabulary':
// DONE
$name['value'] = 'taxonomy_vocabulary_delete';
break;
case 'taxonomy_get_term':
// DONE
$name['value'] = 'taxonomy_term_load';
break;
case 'taxonomy_get_tree':
// DONE
$depth = '$max_depth = NULL /* TODO Set this variable. */';
$count = $item->parameters
->count();
// Adjust parameters.
if ($count > 3) {
// Switch places.
$p2 = $item
->getParameter(2);
$p3 = $item
->getParameter(3);
$item
->setParameter(2, $p3);
$item
->setParameter(3, $p2);
}
elseif ($count > 2) {
// Insert parameter due to change in parameter order.
$editor
->insertParameter($item, 2, $depth);
$count = $item->parameters
->count();
}
$defaults = array(
array(
'NULL',
$depth,
),
'-1',
);
$string = $editor
->removeDefaults($item, 2, $defaults);
break;
case 'taxonomy_save_term':
// DONE
$name['value'] = 'taxonomy_term_save';
$temp = $item
->printParameters();
$editor
->setParameters($item, array(
'$term /* TODO Term object replaces array ' . $temp . ' */)',
));
break;
case 'taxonomy_save_vocabulary':
// DONE
$name['value'] = 'taxonomy_vocabulary_save';
$temp = $item
->printParameters();
$editor
->setParameters($item, array(
'$vocabulary /* TODO Vocabulary object replaces array ' . $temp . ' */)',
));
break;
case 'theme':
// DONE
/*
* Create a static variable to hold the array of theme registrations.
* Find the theme in the list so we know the names of its parameters.
* Array-itize the parameters.
*
* Find the hook_theme function (using $reader passed to this function)
* and save its contents. This assumes the hook_theme is defined in this
* file!!!
*
* Add helper functions to PGPArray to get the keys and values as arrays.
* TODO Refactor any existing code that does this.
*
* static $themes = array();
*
* $p0 = $item->getParameter(0);
* $theme = $themes[$p0];
* $keys = keys from $theme
* $string = 'array(';
* foreach ($keys as $key) {
* $string .= "'$key' => $p{$i},";
* }
* $string = ');
*/
cdp(__FUNCTION__ . " inside theme case");
global $_coder_upgrade_module_name;
// Find the hook_theme function object.
$function = $editor
->findFunction($reader
->getFunctions(), $_coder_upgrade_module_name . '_theme');
if (is_null($function)) {
return;
}
// Get the keys for the appropriate theme.
$body =& $function->body;
if (!($return = $body
->find(T_RETURN, 'reverse'))) {
clp("ERROR: return statement not found in hook_perm");
return;
}
$value =& $return->value;
$array = $value
->getElement(0);
if (get_class($array) == 'PGPArray') {
if (!($theme = $array
->findValue($item
->getParameter(0)
->toString()))) {
clp("ERROR: theme entry not found in hook_theme");
return;
}
$array = $theme
->getElement(0);
$arguments = $array
->findValue("'arguments'");
$keys = $arguments
->getElement(0)
->keysToArray();
if (empty($keys)) {
break;
}
// Remove the quotes surrounding the keys.
foreach ($keys as $index => $key) {
$keys[$index] = trim($key, "'\"");
}
}
$count = $item->parameters
->count();
if ($count == 1) {
break;
}
$defaults = array_fill(0, $count - 1, "'XXX_YYY'");
$string = $editor
->arrayitize($item, 1, $keys, $defaults);
$temp = $editor
->expressionToStatement($string);
$temp
->getElement(0)->multiline = 0;
$item
->setParameter(1, $temp);
break;
case 'time':
// DONE
$expression = $editor
->expressionToStatement('REQUEST_TIME');
cdp($expression
->print_r());
$item = $expression
->getElement(0);
break;
case 'user_authenticate':
// DONE
$count = $item->parameters
->count();
if ($count == 0) {
$editor
->setParameters($item, array(
'$name',
'$password /* TODO Set these variables */',
));
break;
}
/*
* Two cases:
* - parameter is an array expression: extract values to use as new
* parameters
* - parameter is a variable expression (not an array): assume the
* variable has name and pass as elements
*/
$p0 = $item
->getParameter();
$operand = $p0
->getElement();
$class = get_class($operand);
if ($class == 'PGPOperand') {
// Get the variable name used as the parameter.
$parameter = $item
->getParameter()
->toString();
// Make variable assignments referring to two new parameters.
$assign1 = $editor
->textToStatements('$name = ' . $parameter . "['name']; // TODO Set these variables");
$assign2 = $editor
->textToStatements('$password = ' . $parameter . "['pass'];")
->getElement(0);
cdp($assign1
->print_r());
// Insert the assignments before this statement.
// Get the statement (i.e. node) this function call is part of.
$parent =& $item->parent;
// Get the statement list the parent is part of.
$container =& $parent->container;
// Insert statements.
$container
->insertListBefore($parent, $assign1, 'assignment');
$container
->insertBefore($parent, $assign2, 'assignment');
// Set the parameters on this function call.
$editor
->setParameters($item, array(
'$name',
'$password',
));
}
elseif ($class == 'PGPArray') {
$name = $operand
->findValue("'name'")
->toString();
$password = $operand
->findValue("'pass'")
->toString();
// Set the parameters on this function call.
$editor
->setParameters($item, array(
$name,
$password,
));
}
break;
case 'user_delete':
// DONE
$name['value'] = 'user_cancel';
$editor
->setParameter($item, 2, "\$method = 'user_cancel_block' /* TODO Set this variable */");
break;
}
}