Autocompletion for ZF2 view helpers in PhpStorm - php

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

Related

How to use a $this reference inside PHP docblocks

I have a project that utilised MVC where the view file is inherting $this which refers to a view class attached to the controller.
Helper classes have been attached in some of the views and are used like follows:
<?=$this->someHelper->renderSomething()?>
I was hoping to help devs and the IDE out by doing this:
/** #var SomeHelper $this->someHelper */
It's not supported, seemingly. Is there a way to achieve this?
I can only find a workaround at the moment, to declare the helper as a new variable and include a #var statement for that.
It's not possible, you are supposed to type hint the $this instead. If $this is not any concrete class you can type hint, create a fake class/interface instead which will act as a helper to the IDE:
// somewhere outside of your code base, but accessible by the IDE
// use the name of your choice
interface CodeIgniterMvc
{
/**
* #return string
*/
function renderSomething(): string;
/**
* #param array $filter Filtering conditions
* #return \Your\App\Models\User[]
*/
function getUsers(array $filter): array;
}
and in the views:
/** #var $this CodeIgniterMvc **/
Of course include in the repository so every team member can gain such benefits.

IDE static analysis for dynamically instantiated classes

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

PHP dynamic return type hinting

Suppose I have the following PHP function:
/**
* #param string $className
* #param array $parameters
* #return mixed
*/
function getFirstObject($className, $parameters) {
// This uses a Doctrine DQl builder, but it could easily replaced
// by something else. The point is, that this function can return
// instances of many different classes, that do not necessarily
// have common signatures.
$builder = createQueryBuilder()
->select('obj')
->from($className, 'obj');
addParamClausesToBuilder($builder, $parameters, 'obj');
$objects = $builder
->getQuery()
->getResult();
return empty($objects) ? null : array_pop($objects);
}
Basically, the function always returns either an instance of the class specified with the $className parameter or null, if something went wrong. The only catch is, that I do not know the full list of classes this function can return. (at compile time)
Is it possible to get type hinting for the return type of this kind of function?
In Java, I would simply use generics to imply the return type:
static <T> T getOneObject(Class<? extends T> clazz, ParameterStorage parameters) {
...
}
I am aware of the manual type hinting, like
/** #var Foo $foo */
$foo = getOneObject('Foo', $params);
but I would like to have a solution that does not require this boilerplate line.
To elaborate: I am trying to write a wrapper around Doctrine, so that I can easily get the model entities that I want, while encapsulating all the specific usage of the ORM system. I am using PhpStorm.
** edited function to reflect my intended usage. I originally wanted to keep it clean of any specific use case to not bloat the question. Also note, that the actual wrapper is more complex, since I also incorporate model-specific implicit object relations and joins ect.
I use phpdoc #method for this purpose. For example, I create AbstractRepository class which is extend by other Repository classes. Suppose we have AbstractRepository::process(array $results) method whose return type changes according to the class that extends it.
So in sub class:
/**
* #method Car[] process(array $results)
*/
class CarRepo extends AbstractRepository {
//implementation of process() is in the parent class
}
Update 1:
You could also use phpstan/phpstan library. Which is used for static code analyses and you can use it to define generic return types:
/**
* #template T
* #param class-string<T> $className
* #param int $id
* #return T|null
*/
function findEntity(string $className, int $id)
{
// ...
}
This can now be achieved with the IntellJ (IDEA/phpStorm/webStorm) plugin DynamicReturnTypePlugin:
https://github.com/pbyrne84/DynamicReturnTypePlugin
If you use PHPStorm or VSCode (with the extension PHP Intelephense by Ben Mewburn) there is an implementation named metadata where you could specify your own type-hinting based on your code doing the magic inside. So the following should work (as it did on VSCode 1.71.2)
<?php
namespace PHPSTORM_META {
override(\getFirstObject(0), map(['' => '$0']));
}

In the Eclipse PDT, is it possible to configure content assist to look for alternative tags to suggest PHP types?

I'm working on a project that uses the Lithium (http://li3.me/) framework and they document their classes like this:
class Controller extends \lithium\core\Object {
/**
* Contains an instance of the `Request` object with all the details of the HTTP request that
* was dispatched to the controller object. Any parameters captured in routing, such as
* controller or action name are accessible as properties of this object, i.e.
* `$this->request->controller` or `$this->request->action`.
*
* #see lithium\action\Request
* #var object
*/
public $request = null;
I've always used fully qualified class names in the #var and Eclipse seems to do a good job with that for generating content assist. However they seem to document class names using #see tags instead, and content assist is not available. Is there a way to configure PDT to use the information in the #see tag as a class name for the purposes of content assist?
It's not possible without own plugin. #see tag should be used for links only.

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