Has anyone tried refactoring a "real world" application/library to use
namespaces?
I've been pondering about how to algorithmically/mechanically prepare a non
namespaced code for namespaces appliance. This would be a first step which
will just prepare the code for namespaces in a safe way, but not to profit
from aliases created by use. The latest will have to be implemented manually
(IMHO).
I've come up with the following, for an organization model of a class per
file.
Please review.
- find the class definition and reduce the class name to namespace/class
- add namespace declaration on top of file
- replace class name in definition with short name
- find all function calls (not method calls) which are not keywords (array,
isset, unset, list, etc) and prefix them with :: - find all class names (static method calls (no self:: nor parent::), new,
catch, type hints, instanceof, extends, implements) and:
5a) replace separator used (possibly: _) with namespace separator (::)
5b) IF the class has no namespace (ie global namespace) prefix the class
name with ::
5c) IF the namespace of the class starts with the same namespace found in
(1), remove it or prefix it with namespace::
(although this is not necessary) - find static method calls with variables used as class name, and mark them
for user handled refactoring - find internal functions that use callables (call_user_func, array_map,
array_filter, etc) and mark them for user handled refactoring
Later on, a methodical way of adding use statements for reducing the class
names used, can be implemented. Although this might be more complicate than
it seems if the method has to be careful to avoid creating possible name
clashes.
If anyone has a comment on this, I'll be more than grateful if you share.
Best Regards,
Martin Alterisio
Hi Martin,
I've been pondering about how to algorithmically/mechanically prepare a non
namespaced code for namespaces appliance. This would be a first step which
will just prepare the code for namespaces in a safe way, but not to profit
from aliases created by use. The latest will have to be implemented manually
(IMHO).
phc (www.phpcompiler.org) would be suitable for this purpose. It has
good support for nearly all of the features you need.
I've come up with the following, for an organization model of a class per
file.
Please review.
- find the class definition and reduce the class name to namespace/class
- add namespace declaration on top of file
- replace class name in definition with short name
So far this would be trivial.
- find all function calls (not method calls) which are not keywords (array,
isset, unset, list, etc) and prefix them with ::
list, array etc wouldnt be confused with functions. Functions are just
methods without target. You'd need a short list of functions such as
empty, unset etc to avoid.
- find all class names (static method calls (no self:: nor parent::), new,
catch, type hints, instanceof, extends, implements) and:
All of these are trivial.
5a) replace separator used (possibly: _) with namespace separator (::)
5b) IF the class has no namespace (ie global namespace) prefix the class
name with ::
5c) IF the namespace of the class starts with the same namespace found in
(1), remove it or prefix it with namespace::
(although this is not necessary)
This would be easy too.
- find static method calls with variables used as class name, and mark them
for user handled refactoring
I'm not sure why this couldnt be done automatically, but finding
static method calls is also easy. You could add comments to mark these
fairly easily.
- find internal functions that use callables (call_user_func, array_map,
array_filter, etc) and mark them for user handled refactoring
A lot of this could be automated, but there would of course be some
which would require marking.
Later on, a methodical way of adding use statements for reducing the class
names used, can be implemented. Although this might be more complicate than
it seems if the method has to be careful to avoid creating possible name
clashes.If anyone has a comment on this, I'll be more than grateful if you share.
If you need any advice, on this, please let me know.
Paul
--
Paul Biggar
paul.biggar@gmail.com
2007/12/21, Paul Biggar paul.biggar@gmail.com:
Hi Martin,
I've been pondering about how to algorithmically/mechanically prepare a
non
namespaced code for namespaces appliance. This would be a first step
which
will just prepare the code for namespaces in a safe way, but not to
profit
from aliases created by use. The latest will have to be implemented
manually
(IMHO).phc (www.phpcompiler.org) would be suitable for this purpose. It has
good support for nearly all of the features you need.
Thank you, that looks like it's the tool for the job.
- find all function calls (not method calls) which are not keywords
(array,isset, unset, list, etc) and prefix them with ::
list, array etc wouldnt be confused with functions. Functions are just
methods without target. You'd need a short list of functions such as
empty, unset etc to avoid.
I didn't quite understand your point here. If you can elaborate a little
more.
Take into consideration that the reason here for the distinction between
function calls which are actually keywords are due to the fact that the
following wouldn't be valid:
if (::isset($var)) {
Because isset is actually a keyword.
And to ensure that the code is ported safely to code which uses namespaces,
the function calls which point to globals or internal functions, should be
prefixed with ::
- find static method calls with variables used as class name, and mark
themfor user handled refactoring
I'm not sure why this couldnt be done automatically, but finding
static method calls is also easy. You could add comments to mark these
fairly easily.
Maybe some workaround can be found to str_replace the namespace separator,
but I think this wouldn't be optimal and possibly harm code readability and
maintainability. Consider a factory method:
<?php
class Factory {
public static function create($what, $arguments) {
switch ($what) {
case 'bananas':
$class = 'Food_Fruit_Banana';
break;
...
}
return $class::create($arguments);
}
}
?>
An automated refactoring would be tempted to do:
<?php
class Factory {
public static function create($what, $arguments) {
switch ($what) {
case 'bananas':
$class = 'Food_Fruit_Banana';
break;
...
}
$temp = str_replace('_', '::', $class);
return $temp::create($arguments);
}
}
?>
Which I think is not the proper way to refactorize this code.
This would be, IMHO, the right way to refactorize this code:
<?php
class Factory {
public static function create($what, $arguments) {
switch ($what) {
case 'bananas':
$class = 'Food::Fruit::Banana';
break;
...
}
return $class::create($arguments);
}
}
?>
Considering the variants and possible uses of this syntax, I'll say it's
better to leave this job to the coder rather than to an automated job.
Thank you for your response.
Best Regards,
Martin Alterisio
2007/12/21, Paul Biggar paul.biggar@gmail.com:
- find all function calls (not method calls) which are not keywords
(array,
isset, unset, list, etc) and prefix them with ::list, array etc wouldnt be confused with functions. Functions are just
methods without target. You'd need a short list of functions such as
empty, unset etc to avoid.
Because isset is actually a keyword.
Right, the phc grammar doesnt differentiate between built-ins and
functions, even though the PHP grammar has rules for each. You just
need a list of the built-ins, such as isset, etc. You'll see when you
start working with phc, but the point I was making was that this isnt
hard.
- find static method calls with variables used as class name, and mark
them
for user handled refactoringI'm not sure why this couldnt be done automatically, but finding
static method calls is also easy. You could add comments to mark these
fairly easily.Maybe some workaround can be found to str_replace the namespace separator,
but I think this wouldn't be optimal and possibly harm code readability and
maintainability. Consider a factory method:<?php
class Factory {
public static function create($what, $arguments) {
switch ($what) {
case 'bananas':
$class = 'Food_Fruit_Banana';
break;
...
}
return $class::create($arguments);
}
}
?>An automated refactoring would be tempted to do:
<?php
class Factory {
public static function create($what, $arguments) {
switch ($what) {
case 'bananas':
$class = 'Food_Fruit_Banana';
break;
...
}
$temp = str_replace('_', '::', $class);
return $temp::create($arguments);
}
}
?>Which I think is not the proper way to refactorize this code.
This would be, IMHO, the right way to refactorize this code:<?php
class Factory {
public static function create($what, $arguments) {
switch ($what) {
case 'bananas':
$class = 'Food::Fruit::Banana';
break;
...
}
return $class::create($arguments);
}
}
?>Considering the variants and possible uses of this syntax, I'll say it's
better to leave this job to the coder rather than to an automated job.
True, as I'm sorry to say that we dont have the facilities to
automatically match the definition of variables to where they are
used.
However, once you have a list of class_names, a global replace on all
strings may be all you need.
Paul
--
Paul Biggar
paul.biggar@gmail.com