IDE static analysis for dynamically instantiated classes - php

I'm creating a class from variables (in a controlled environment) like this:
$controller = new $controllerClassName();
But my IDE doesn't know what type of class is. So, I'd like to know if there is a way to identify the class (maybe I could create an interface).
This is what I want to do:
$controller = (InterfaceController) new $controllerClassName();
Edit: I'm using PhpStorm IDE
Solution:
As yivi suggests, I've used PHP-Doc annotations, so now it's working:
$controller = new $controllerClassName();
/** #var MyInterface $controller */
Note that "MyInterface" could be a class or an interface.
Also, I've tried to execute the following code, but it didn't work. It seems that the annotation must be in the same block of the variable:
/**
* #param string $var1
* #param string $var2
* ...
* #var MyInterface $controller
*/
function thisIsAFunction($var1, $var2, ...) {
...
$controller = new $controllerClassName(); // <- Class still unknown
...
}

You don't specify your ide, but in most competent PHP IDEs you can use PHP-DOC annotations to specify type and help with static-analysis.
E.g.:
/** #var SomeClass $someClass */
$someClass->thisCouldBeAutoCompleted();
In your case, since you are instantiating your class dynamically for some reason, you probably should (as you said) use an interface which is implemented by the classes you are liable to instantiate, or an parent class for the family.
So maybe:
$painter = new $painterImplementingClass();
/** #var PaintInterface $painter $painter*/
$painter->line($point1, $point2, $color);
// auto-completion should work for Pencil, Pen, Brush and other
// classes that implement PaintInterface
Or
$vehicle = new $vehicleClass();
/** #var AbstractVehicle $vehicle */
$vehicle->accelerate($acceleration, $time);
// auto-completion and static analysis should work for Car, Bicycle,
// Boat and other classes that extend AbstractVehicle

Perhaps you need to tune your IDE for a little.
Or to make it clear for your IDE you can use namespaces which will point to your new $controllerClassName();
You can read more about namespaces here

Related

Code completion of my classes in netbeans 8

How do I enable code completion for clases and methods I create in code for in netbeans 8.
Thanks,
Dion
You really should google this kinda thing, but I'm in the mood to answer a question :-)
Anyway, most IDE's read your code in pretty much the same way. I can't attest to NetBeans specifically (I use PHPStorm), but the general idea is to make sure you add docblocks to your classes, methods etc. The IDE reads these and can then provide decent code-completion.
<?php
namespace App;
/**
* Class MyClass
* Does some stuff
* #package App
*/
class MyClass extends SomeOtherClass
{
/**
* This is my var
* #var string
*/
public $myVar = 'some val';
/**
* This is my method
* #param string $yourString
* #return SomethingElse
*/
public function myMethod ($yourString)
{
$this->myVar = $yourString;
return new SomethingElse($this->myVar);
}
}
Have a look at the PHPdoc site for the tag syntax. Most IDE's will also have a way of generating this for you as well.

How to prevent PhpStorm from showing an Expected... warning when using PHPUnit mocks?

When mocking an interface in PHPUnit, PhpStorm complains when it's used as parameter for a type-hinted function.
Example
interface InterfaceA{
}
class ClassA{
public function foo(InterfaceA $foo){}
}
class PhpStormTest extends PHPUnit_Framework_TestCase
{
public function testFoo(){
$mock = $this->getMock("InterfaceA");
$a = new ClassA();
$a->foo($mock);
}
}
On $a->foo($mock); PhpStorm underlines $mock with the warning Expected InterfaceA, got PHPUnit_Framework_MockObject_MockObject
Image
I guess it's happening because PHPUnit creates the mock a runtime and PhpStorm cannot know that it's actually implementing the interface.
I found a workaround to this problem in the Jetbrain blog at PhpStorm Type Inference and Mocking Frameworks. The important part:
By default, PhpStorm is capable of figuring out the available methods
on the mock object. However, it only displays those for PHPUnit’s
PHPUnit_Framework_MockObject_MockObject class. Fortunately, we can
solve this by instructing PhpStorm to infer type information from
other classes as well, by using a simple docblock comment.
So to make the warning disappear, we need to add /** #var InterfaceA */ /** #var InterfaceA|PHPUnit_Framework_MockObject_MockObject */ (cudos to Supericy) to let PhpStorm know our mock actually implements InterfaceA:
interface InterfaceA{
}
class ClassA{
public function foo(InterfaceA $foo){}
}
class PhpStormTest extends PHPUnit_Framework_TestCase
{
public function testFoo(){
/** #var InterfaceA|PHPUnit_Framework_MockObject_MockObject */
$mock = $this->getMock("InterfaceA");
$a = new ClassA();
$a->foo($mock);
}
}
This bugged me for some time, hope it helps someone :)
Edit
Since PHPUnit_Framework_MockObject_MockObject is really ugly to type, you can abbreviate it via MOOMOO and let PHPStorms auto-complete do the rest:
Another plugin I have used for this is the Dynamic Return Type Plugin, it lets you configure return types of methods in a very dynamic way (the example is to have better type information from Mocks).
Another, less verbose but possibly riskier, approach can be to wrap the call to getMock() with your own function and mark that with #return mixed:
/**
*
* #return mixed
*/
public function myGetMock($str)
{
return $this->getMock($str);
}
Calling this method instead of $this->getMock() will make the warning disappear.

Symfony2 and avoiding overly verbose code

In most of my controllers, I need to get a reference to one or more of my custom entity repositories, so naturally, I do this a lot:
/** #var $repo MyFirstEntityRepository */
$repo1 = $this->getDoctrine()->getManager()->getRepository('MyNamespaceMyBundle:MyFirstEntity');
/** #var $repo MySecondEntityRepository */
$repo2 = $this->getDoctrine()->getManager()->getRepository('MyNamespaceMyBundle:MySecondEntity');
/** #var $repo MyThirdEntityRepository */
$repo3 = $this->getDoctrine()->getManager()->getRepository('MyNamespaceMyBundle:MyThirdEntity');
My question is: if I have a bunch of different Entities that I need a repository reference for, is it good practice to create a bunch of corresponding get[EntityName]Repository methods in some sort of BaseController which all other controllers could inherit from?
The refactored controller code would be more like:
$repo1 = $this->getMyFirstEntityRepository();
$repo2 = $this->getMySecondEntityRepository();
$repo3 = $this->getMyThirdEntityRepository();
Which would play nicely with IDE autocompletion and type inference as well.
Is this good practice? Or does it violate some sort of standard? Does it make the code any less "loosely coupled"?
How about this?
$em = $this->getDoctrine()->getManager();
/** #var $repo MyFirstEntityRepository */
$repo1 = $em->getRepository('MyNamespaceMyBundle:MyFirstEntity');
/** #var $repo MySecondEntityRepository */
$repo2 = $em->getRepository('MyNamespaceMyBundle:MySecondEntity');
/** #var $repo MyThirdEntityRepository */
$repo3 = $em->getRepository('MyNamespaceMyBundle:MyThirdEntity');
Seems to me declaring variable $em solves all the DRY violations...
I'd suggest the Model Manager approach. You could then, in turn, use the JMSDiExtraBundle to make instantiation even easier.
Note: If you're using Symfony 2.2, then you probably already have the JMSDiExtraBundle installed, since it was part of the standard distribution in that version.

Autocompletion for ZF2 view helpers in PhpStorm

Does anyone know if PHPStorm has some builtin support for view helper autocompletion or a possibility to write a plugin for it.
I don't want to use inline var definitions for this as this would be cumbersome to do if I use a lot of view helpers
$this->inlineScript()-> //I want some autocomplete here.
$this->translate('some translation')-> //Please give me autocompletion
If I use var definitions it will end up as something like this, but it will really clutter up my view:
/* #var $inlineScript \Zend\View\Helper\InlineScript */
$inlineScript = $this->inlineScript();
$inlineScript-> //Now I have autocompletion goodness
/* #var $translate \Zend\I18n\View\Helper\Translate */
$translate = $this->translate();
$translate('some translation')-> //Now I have autocompletion goodness
NOTE I'm posting my method discussed in the comments as answer.
To typehint non-existing methods, the syntax is as following:
/**
* #method \Zend\Mvc\Controller\Plugin\Url url(string $route = null, array $params = null)
*/
class MyClass
{
}
This allows us to use have a type-hint for method url on any variable recognized as MyClass:
/* #var $a \MyClass */
$a->// typehint!
You need such a "fake" class and then start your view scripts with:
/* #var $this \MyFakeClass */
That will give you type-hints on $this within your view script.
You could ideally open a pull request against https://github.com/zendframework/zf2 with something similar to https://github.com/zendframework/zf2/pull/3438

Preserving auto-completion abilities with Symfony2 Dependency Injection

I'm using PHP Storm as my IDE, but I believe that other IDE's such as Netbeans will have the same issue as I'll explain below.
When using a framework like Symfony2, we have the wonderful world of Dependency Injection added. So objects can simply be instantiated using code like the following snippet:
$myThingy = $this->get('some_cool_service');
This is very handy, as objects are already configured beforehand. The one problem is, that auto-completion breaks entirely in basically any PHP IDE, as the IDE does not know what type the get() method is returning.
Is there a way to preserve auto-completion? Would creating for example an extension of Controller be the answer? For example:
class MyController extends Controller {
/**
* #return \MyNamespace\CoolService
*/
public getSomeCoolService() {
return new CoolService();
}
}
and then for application controllers, specify MyController as the base class instead of Controller?
What about using a Factory class, or any other possible methods?
It is more involving, but you can still do this with eclipse PDT:
$myThingy = $this->get('some_cool_service');
/* #var $myThingy \MyNamespace\CoolService */
UPDATE:
The example on this page shows you may also use the other way round with phpStorm:
$myThingy = $this->get('some_cool_service');
/* #var \MyNamespace\CoolService $myThingy */
You could define private properties in your controllers
class MyController extends Controller
{
/**
* #var \Namespace\To\SomeCoolService;
*/
private $my_service;
public function myAction()
{
$this->my_service = $this->get('some_cool_service');
/**
* enjoy your autocompletion :)
*/
}
}
I use base Controller class for bundle. You need to annotate the return in method. At least that works on Eclipse.
/**
* Gets SomeCoolService
*
* #return \Namespace\To\SomeCoolService
*/
protected function getSomeCoolService()
{
return $this->get('some_cool_service');
}
I don't like /*var ... */, because it gets too much into code.
I don't like private properties, because you can wrongly assume that services are already loaded.
I use Komodo Studio, and tagging variables with #var, even inside methods, preserves auto completion for me.
namespace MyProject\MyBundle\Controller;
use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\HttpFoundation\Request;
class WelcomeController extends ContainerAware
{
public function indexAction()
{
/*#var Request*/$request = $this->container->get('request');
$request->[autocomplete hint list appears here]
}
}
working with netbeans IDE 7.1.2 PHP

Categories