| Home | Downloads | Documentation | Plugins | Spinoff Projects | Mailing List |
Tutorial 2: Modifying Tree Nodes Now that we have seen in Tutorial 1
how to inspect the tree, in this tutorial we will look at modifying
the tree. The task we set ourselves is: replace all calls to
The tutorial we develop in this tutorial is available as MySQL2DBX.so in the phc distribution. To see its effect, run phc as follows: phc --run plugins/tutorials/MySQL2DBX.so --dump-php test.php First Attempt
As in the previous tutorial, we are interested in all function calls.
However, now we are interested only in function calls to
method_invocation ::= target method_name params:actual_parameter* ; method_name ::= METHOD_NAME | reflection ; actual_parameter ::= is_ref:"&"? expr ; (The Literals (or “terminal symbols”) do not get
represented by a class called Thus, we arrive at the following first attempt.
#include <phc/Tree_visitor.h>
class MySQL2DBX : public Tree_visitor
{
public:
void post_method_invocation(AST_method_invocation* in)
{
Token_method_name* name;
name = new Token_method_name(new String("mysql_connect"));
if(in->method_name->match(name))
{
in->method_name = new Token_method_name(new String("dbx_connect"));
}
}
};
extern "C" void process_ast(AST_php_script* php_script)
{
MySQL2DBX m2d;
php_script->visit(&m2d);
}
Note: phc uses a garbage
collector, so there is never any need to free objects (you never have
to call Modifying the ParametersUnfortunately, renamingmysql_connect to
dbx_connect is not sufficient, because the parameters to
the two functions differ. According to the PHP manual, the
signatures for both functions are
mysql_connect (server, username, password, new_link, int client_flags) and dbx_connect (module, host, database, username, password, persistent) The Now, in phc, Finally,
We are now ready to write our conversion function:
#include <phc/Tree_visitor.h>
class MySQL2DBX : public Tree_visitor
{
public:
void post_method_invocation(AST_method_invocation* in)
{
AST_actual_parameter_list::iterator pos;
Token_constant_name* module_name;
AST_constant* module_constant;
AST_actual_parameter* param;
Token_method_name* name;
name = new Token_method_name(new String("mysql_connect"));
if(in->method_name->match(name))
{
// Check for too many parameters
if(in->actual_parameters->size() > 3)
{
printf("Error: unable to translate call "
"to mysql_connect on line %ld\n", in->get_line_number());
return;
}
// Modify name
in->method_name = new Token_method_name(new String("dbx_connect"));
// Modify parameters
module_name = new Token_constant_name(new String("DBX_MYSQL"));
module_constant = new AST_constant("%STDLIB%", module_name);
pos = in->actual_parameters->begin();
param = new AST_actual_parameter(false, module_constant);
in->actual_parameters->insert(pos, param); pos++;
/* Skip host */ pos++;
Token_null* null = new Token_null(new String("NULL"));
param = new AST_actual_parameter(false, null);
in->actual_parameters->insert(pos, param);
}
}
};
extern "C" void process_ast(AST_php_script* php_script)
{
MySQL2DBX m2d;
php_script->visit(&m2d);
}
If we apply this transformation to
$link = mysql_connect('host', 'user', 'pass');
We get $link = dbx_connect(DBX_MYSQL, "host", NULL, "user", "pass"); RefactoringA quick note on refactoring. Refactoring is the process of modifying existing programs (PHP scripts), usually to work in new projects or in different setups (for example, with a different database engine). Manual refactoring is laborious and error-prone, so tool-support is a must. Although phc can be used to refactor PHP code as shown in this tutorial, a dedicated refactoring tool for PHP would be easier to use (though of course less flexible). Such a tool can however be built on top of phc. See also the list of Spinoff Projects. What's Next?Tutorial 3 explains how you can modify the structure of the tree, as well as the tree nodes. |
| $LastChangedDate: 2006-09-08 12:24:58 +0100 (Fri, 08 Sep 2006) $. Contents © the authors. |