Refactoring code for namespaces appliance

php.internals

Martin Alterisio

18 years ago
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. 1) find the class definition and reduce the class name to namespace/class 2) add namespace declaration on top of file 3) replace class name in definition with short name 4) find all function calls (not method calls) which are not keywords (array, isset, unset, list, etc) and prefix them with :: 5) 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) 6) find static method calls with variables used as class name, and mark them for user handled refactoring 7) 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

Paul Biggar

18 years ago
Hi Martin, On Dec 21, 2007 8:30 PM, Martin Alterisio <malterisio777@gmail.com> wrote:
> 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. > > 1) find the class definition and reduce the class name to namespace/class > 2) add namespace declaration on top of file > 3) replace class name in definition with short name
So far this would be trivial.
> 4) 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.
> 5) 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.
> 6) 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.
> 7) 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

Martin Alterisio

18 years ago
2007/12/21, Paul Biggar <paul.biggar@gmail.com>:
> > Hi Martin, > > On Dec 21, 2007 8:30 PM, Martin Alterisio <malterisio777@gmail.com> wrote: > > 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.
> 4) 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 ::
> 6) 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.
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

Paul Biggar

18 years ago
On Dec 22, 2007 12:02 AM, Martin Alterisio <malterisio777@gmail.com> wrote:
> 2007/12/21, Paul Biggar <paul.biggar@gmail.com>: > > > > 4) 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.
> > > 6) 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. > > 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