Hi.
In languages like Java you can cast objects at runtime with the "usual" cast syntax ClassA a = (ClassA) new ClassB(); This syntax is not recognized in php, but sometimes runtime casts are necessary when the system can't properly figure out the types.
One instance where this is extra painful is the (arguably bad) service locator pattern. Often you have some "module" hierarchy where every manager/worker class extends some interface or abstract class and the best you can do with the "getService" method on the service locator is to return that interface... but it is never the correct type!
Example code:
class Service { }
class A extends Service { }
class B extends Service { }
class Code {
public function main() {
$service = $this->getService('A'); // returns 'Service'
$this->test($service); // call fails to check even if $service is actually 'A'
}
/**
* @return Service superclass
*/
public function getService($type) { return new $type(); }
public function test(A $a) { }
}
I propose an extension to syntax like this
$super = /*(A)*/ $this->getService('A');
The "commented cast" will alter the type of the following expression, in this case the method call.
This is unsafe (the sevice returned might not be A and it is impossible to know), so in case of e.g. Java a warning is generated by the compiler which has to be suppressed manually. Here it is not required because this is a deliberate annotation and so the programmer probably knows why they put it in.
This is very helpful on older projects littered with explicit calls to service locators or containers (I happen to work on such a codebase) and would allow tons of type checking which is simply impossible now.
I've tried to hack a bit on Scope.php's method getType to add the support and it doesn't seem that difficult (basically check any Expr to see if it has a comment attached with this particular syntax and then return the type directly), but I'm failing to figure out how to do namespace/uses resolution so that I don't have to always provide the full type.
Here is the code I've put in and it works, but as you can see I check for MethodCall explicitly (which is only one of many Exprs). This is because php-parser attaches the comment to all subnodes and not just the parent node, which in this case would for example also cast $this to A and then complain A has no such method as test). There is an issue opened for that: nikic/PHP-Parser#253.
if ($node instanceof Expr\MethodCall && isset($node->getAttributes()['comments'])) {
$name = $node->getAttributes()['comments'][0]->getText();
if (preg_match('#/\*\(.*?\)\*/#', $name)) {
$type = substr($name, 3, -3);
// this is not correct, I guess there is some "resolver" from string to type
return new ObjectType($type, false);
}
}
I will happily hack on this if someone can provide guidance on how to resolve the types/namespaces/renames.
Hi.
In languages like Java you can cast objects at runtime with the "usual" cast syntax
ClassA a = (ClassA) new ClassB();This syntax is not recognized in php, but sometimes runtime casts are necessary when the system can't properly figure out the types.One instance where this is extra painful is the (arguably bad) service locator pattern. Often you have some "module" hierarchy where every manager/worker class extends some interface or abstract class and the best you can do with the "getService" method on the service locator is to return that interface... but it is never the correct type!
Example code:
I propose an extension to syntax like this
The "commented cast" will alter the type of the following expression, in this case the method call.
This is unsafe (the sevice returned might not be A and it is impossible to know), so in case of e.g. Java a warning is generated by the compiler which has to be suppressed manually. Here it is not required because this is a deliberate annotation and so the programmer probably knows why they put it in.
This is very helpful on older projects littered with explicit calls to service locators or containers (I happen to work on such a codebase) and would allow tons of type checking which is simply impossible now.
I've tried to hack a bit on
Scope.php's methodgetTypeto add the support and it doesn't seem that difficult (basically check any Expr to see if it has a comment attached with this particular syntax and then return the type directly), but I'm failing to figure out how to do namespace/uses resolution so that I don't have to always provide the full type.Here is the code I've put in and it works, but as you can see I check for
MethodCallexplicitly (which is only one of manyExprs). This is because php-parser attaches the comment to all subnodes and not just the parent node, which in this case would for example also cast$thistoAand then complainAhas no such method astest). There is an issue opened for that: nikic/PHP-Parser#253.I will happily hack on this if someone can provide guidance on how to resolve the types/namespaces/renames.