PhpStorm Generic Classes not working at all - php

This is all happening for me in PhpStorm 2022.2.1
I've been trying to set up a class in PHP that supports Generics, but to no avail. No matter what I tried, PhpStorm always type hints as mixed. Here is a pair of classes as an example:
/** #template T */
class MyGenericClass
{
/** #var T */
public $param;
/** #param T $param */
public function __construct($param)
{
$this->param = $param;
}
/** #return T */
public function getParam()
{
return $this->param;
}
}
class Test
{
public static function test(): void
{
$generic = new MyGenericClass(1);
$a = $generic->getParam();
$b = $generic->param;
}
}
I would expect $generic->getParams() and $generic->param to be hint typed as int, but both of these show up as mixed. I also tried adding a #var MyGenericClass<int> $generic, but it does not help, and PhpStorm warns that it's already inferred from source code.
I also tried to set this up with arrays, and I even tried using Symfony ArrayCollections that are supposed to work - passing an array of integers and calling first() on a Symfony ArrayCollection is hint typed as mixed too.
On the other hand, function scoped templates work just fine. If I make a function like
/**
* #template T
* #param class-string<T> $param
* #return T
*/
public static function test2(string $param)
{
return new $param();
}
the return type is hinted as mixed|T, and when passing an actual class string, it does correctly hint for result functions, although that seems to also be the case even if I get rid of templates. In a similar case, I had hint show up as the actual passed type, not sure why it's showing up as T instead of the Class string passed to it?
I'm not sure whether I'm missing something, or maybe something is messed up with my configuration?

Related

PhpStorm - Inspections about NULL value

Question about this example:
<?php declare(strict_types=1);
class SomeTestClass
{
/** #var string|null */
private $name;
/** #var string */
private $sth;
public function getName(): ?string
{
return $this->name;
}
public function setSomething(string $value): void
{
$this->sth = $value;
}
public function something(): void
{
$this->setSomething($this->getName());
}
}
Why PhpStorm 2018.3.5 is not informing me about wrong code in something() related to using nullable value as a method argument which should not be nullable? I'm asking about this because when $name property will be null, then I would have an error in my app.
Version with
public function something(): void
{
/** #var string|null $name */
$name = $this->getName();
$this->setSomething($name);
}
also does not work.
PhpStorm is not very strict when analysing code. For example, when injecting property values through constructor, it does not look at declared property type when autocompleting methods and properties, but rather at the type that was injected into constructor.
For very strict analysis, try out PHPStan. Unlike PhpStorm, you can easily add it to your CI build. See the resulting error for your example.
I think PhpStorm inspection is broken with instance variables type annotations.
If I use directly:
$this->setSomething(null);
I get the type error.
However, if I directly assign null to $this->sth, I get no error at all:
$this->sth = null;
Basically, don't rely on variable type annotations to be correctly inspected with strict_types=1

Declare an object as a specific class in php

Is there an opportunity, that will allow it to declare a variable in php as a specific object, so that IDEs like Netbeans can detect that Object and suggest me the possible methods and variable in that class?
What I know, is that it works with function like this example:
public static function add(\Event $element) {
//The $element variable is now declared, that it have to be an Event object
}
In the theory, my question looks like this:
\Event $events = EventContainer::getAll();
But unfortunately that wouldn't work.
In eclipse I usually use this pattern:
class Foo {
/**
* #var $barArray \Baz\Bar[]
*/
protected $barArray;
/**
* #var $bar \Baz\Bar
*/
protected $bar
/**
* #return \Baz\Bar
*/
public function getBar()
{
return $this->bar;
}
/**
* #return \Baz\Bar[]
*/
public function getAllBar()
{
return $this->barArray;
}
}
That way eclipse knows by the javadoc what to use in autocomplete. It might work the same in other editors.

How can I use PHPDoc to type-hint the parameters of a Callable?

I have a method that accepts a callback as a parameter. I would like to provide a signature in the PHPDoc for the class method that outlines the parameters for the callback function to be passed to that method so that my IDE (PHPStorm) can produce valid type hints for functions that are passed to my method, or at least someone looking at the code can determine the signature of the callback they're intended to provide.
For example:
class Foo {
public $items = [];
/**
* #param Callable(
* #param ArrayObject $items The list of items that bar() will return
* ) $baz A callback to receive the items
**/
public function bar(Callable $baz) {
$items = new ArrayObject($this->items);
$baz($items);
}
}
The method bar has one parameter, $baz, which is a callback function. Any function passed as an parameter to bar() must accept an ArrayObject as its only parameter.
Ideally, it should be possible to include multiple parameters for the Callable, just like for any other method.
When I write the following code:
$foo = new Foo();
$foo->bar(function(
...I should then receive a parameter list that correctly hints the type (ArrayObject) of the accepted parameter for this function call.
Is such a thing possible? Does PHPStorm or another IDE support it? Is there a recommended/standard way of documenting this even if there is no IDE support?
PHP 7+:
Using an interface for the callable combined with anonymous classes will do the trick. It's not very handy and leads to bit too complex code for the class-consumer, but currently it's the best solution in terms of static code-analysis.
/**
* Interface MyCallableInterface
*/
interface MyCallableInterface{
/**
* #param Bar $bar
*
* #return Bar
*/
public function __invoke(Bar $bar): Bar;
}
/**
* Class Bar
*/
class Bar{
/**
* #var mixed
*/
public $data = null;
}
/**
* Class Foo
*/
class Foo{
/**
* #var Bar
*/
private $bar = null;
/**
* #param MyCallableInterface $fn
*
* #return Foo
*/
public function fooBar(MyCallableInterface $fn): Foo{
$this->bar = $fn(new Bar);
return $this;
}
}
/**
* Usage
*/
(new Foo)->fooBar(new class implements MyCallableInterface{
public function __invoke(Bar $bar): Bar{
$bar->data = [1, 2, 3];
return $bar;
}
});
If you're using PhpStorm it will even auto-generate the __invoke-Method's signature & body within the anonymous class.
I overcame this problem by defining a static function within the class using the callable. That function has its own doc-block and I just refer to it in the method that's requiring my callable using PHPDoc's #see tag.
class Foo
{
/**
* Description of the "bar" callable. Used by {#see baz()}.
*
* #param int $index A 1-based integer.
* #param string $name A non-empty string.
* #return bool
* #see baz()
* #throws \Exception This is a prototype; not meant to be called directly.
*/
public static barCallable($index, $name)
{
throw new \Exception("barCallable prototype called");
}
/**
* Description of the baz() method, using a {#see barCallable()}.
*
* #param callable $bar A non-null {#see barCallable()}.
* #see barCallable()
*/
public function baz(callable $bar)
{
// ...
call_user_func($bar, 1, true);
// ...
}
}
This works well in PhpStorm 10. the Quick Documentation allows to navigate from the method documentation to the prototype documentation easily.
I make my prototype function throw an exception to make it clear it's not meant to be called. I could use a protected or private scope, but then PHPDoc would not always pick the doc-block for documentation generation.
Unfortunately PhpStorm can't track usage of callbacks. It doesn't provide Parameter Info either when using the method requiring a callback, but the callback is at least formally documented.
This approach also has the added benefit of validating a callback definition from the reflection of the prototype at run-time.
It is not possible in PhpStorm by now. I can't even think of other solution which do relatively the same by other means.

php / phpDoc - #return instance of $this class?

How do I mark a method as "returns an instance of the current class" in my phpDoc?
In the following example my IDE (Netbeans) will see that setSomething always returns a foo object.
But that's not true if I extent the object - it'll return $this, which in the second example is a bar object not a foo object.
class foo {
protected $_value = null;
/**
* Set something
*
* #param string $value the value
* #return foo
*/
public function setSomething($value) {
$this->_value = $value;
return $this;
}
}
$foo = new foo();
$out = $foo->setSomething();
So fine - setSomething returns a foo - but in the following example, it returns a bar..:
class bar extends foo {
public function someOtherMethod(){}
}
$bar = new bar();
$out = $bar->setSomething();
$out->someOtherMethod(); // <-- Here, Netbeans will think $out
// is a foo, so doesn't see this other
// method in $out's code-completion
... it'd be great to solve this as for me, code completion is a massive speed-boost.
Anyone got a clever trick, or even better, a proper way to document this with phpDoc?
Update:
As of Netbeans 7.4, the IDE supports #return self, static, and this (http://wiki.netbeans.org/NewAndNoteworthyNB74#Editor_2).
class foo {
protected $_value = null;
/**
* Set something
*
* #param string $value the value
* #return this
*/
public function setSomething($value) {
$this->_value = $value;
return $this;
}
}
class bar extends foo {
public function someOtherMethod(){}
}
Previous Answer:
We have a similar issue with a record iterator's current() method. Since the iterator is extended for many different classes, it doesn't make sense to have a #return $class associated with it. We've used #satrun77's Option 2 before, but I've used #method with some success in Netbeans.
class foo {
protected $_value = null;
/**
* Set something
*
* #param string $value the value
* #return foo
*/
public function setSomething($value) {
$this->_value = $value;
return $this;
}
}
/**
* #method bar setSomething($value)
*/
class bar extends foo {
public function someOtherMethod(){}
}
Thought I'd revisit this Q as I came across a couple of things.
Currently "return $this" isn't supported, but there is a PhpDoc request to add exactly that in v1.5:
http://pear.php.net/bugs/bug.php?id=16223
There's also a request for it in Eclipse PDT:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=276082
Both are relatively old requests. I'm not going to get too excited about this being implemented any time soon, but here goes to hoping :) In the meantime, it seems there is no proper solution to this problem.
!SOLVED! - upgrade to netbeans 9.0 (stable as of July 2018?)
I have been after this for over a year and finally have an open source solution! :)
class Input extends BasicHtml
{
public function someOnlyInputFunc()
{
}
}
class Table extends BasicHtml
{
public function tableOnlyFunc()
{
}
}
abstract class BasicHtml
{
/**
*
* #param array $arrayForNow
* #return $this
*/
public function setStyle( array $arrayForNow )
{
return $this;
}
}
/////driver
$table = new Table();
$input = new Input();
$input->setStyle(array())->//now shows only Input + baseHtml functions
$table->setStyle(array())-> //now shows only Table + baseHtml functions
///note - in 8.0.2 version it shows blank obj drop downs on exact same code.
This also works with traits. As of 11/1/2018 9.0 comes as a big zip (no clean installer for windows, mac?) and you will have to search for adding the php plugings etc BUT IT DOES WORK! Took me about an hour to get it all set. I also have my old 8.x installed and running along side the new 9.0 without issue...so far (just don't run them both at same time). Plugin tip: https://www.reddit.com/r/PHP/comments/9gtaaw/how_to_run_netbeans_9_with_php_support/
Here is 3 work around:
(These are just work around. classes must not be designed and implemented to sue the behavior of an IDE)
Option 1:
make the method someOtherMethod abstract or empty method in foo class
class foo implements ifoo {
protected $_value = null;
/**
* Set something
*
* #param string $value the value
* #return ifoo
*/
public function setSomething($value) {
$this->_value = $value;
return $this;
}
// abstract method or create empty method if you want the method to be
// to be optional
abstract function someOtherMethod();
}
Option 2:
Override the method setSomething in bar class
class bar extends foo {
/**
*
* #param <type> $value
* #return bar
*/
public function setSomething($value) {
return parent::setSomething($value);
}
public function someOtherMethod(){}
}
Option 3:
Use interface
interface ifoo {
public function someOtherMethod(){}
}
class foo {
protected $_value = null;
/**
* Set something
*
* #param string $value the value
* #return ifoo
*/
public function setSomething($value) {
$this->_value = $value;
return $this;
}
}
class bar extends foo implements ifoo {
public function someOtherMethod(){}
}
phpDoc syntax allows for multiple types to be defined by separating them with a | character for the #return tag. When you extend the class foo with class bar you should write a new phpDoc tag that has the proper class for its #return.
If a function returns either foo or bar then you would use #return foo|bar.
However in your case just define #return bar for the overridden function.
Take care.

PHPDoc: #return void necessary?

Is it really necessary do something like this:
/**
* ...
*
* #return void
*/
I have quite a few methods that don't have a return value, and it seems really redundant to put something like this in the comment. Would it be considered bad form to leave it out?
If it makes it clear for the documentation, then leave it in, but it isn't strictly necessary. It's an entirely subjective decision.
Personally, I would leave it out.
EDIT
I stand corrected. After a little googling, the wikipedia page says:
#return [type description] This tag should not be used for constructors or methods defined with a void return type.
The phpdoc.org website says:
#return datatype description
#return datatype1|datatype2 description
The #return tag is used to document the return value of functions or methods. #returns is an alias for #return to support tag formats of other automatic documentors
The datatype should be a valid PHP type (int, string, bool, etc), a class name for the type of object returned, or simply "mixed". If you want to explicitly show multiple possible return types, list them pipe-delimited without spaces (e.g. "#return int|string"). If a class name is used as the datatype in the #return tag, phpDocumentor will automatically create a link to that class's documentation. In addition, if a function returns multiple possible values, separate them using the | character, and phpDocumentor will parse out any class names in the return value. phpDocumentor will display the optional description unmodified.
Sooo... Based on that, I would say leave out the void. It's non-standard, at least.
According to phpDocumentor, #return void is valid:
http://www.phpdoc.org/docs/latest/guides/types.html#keywords
...
this type is commonly only used when defining the return type of
a method or function. The basic definition is that the element
indicated with this type does not contain a value and the user should
not rely on any retrieved value.
For example:
/**
* #return void
*/
function outputHello()
{
echo 'Hello world';
}
In the example above no return statement is specified and thus is the
return value not determined.
Source: http://www.phpdoc.org/docs/latest/for-users/phpdoc/types.html (archived page).
I have to edit my answer because of something I have learned recently.
Using #return void instead of #return null has a very special meaning, consider the following two examples of PHP code.
<?php
/**
* #return void
*/
function return_never() {
echo "foo";
}
/**
* #return null|string
*/
function return_sometimes() {
if ($this->condition()) {
return "foo";
}
}
In the first example PHP will actually return NULL, since PHP always returns NULL. But the returned value is of no use to the caller since it does not say anything about what the function did. IDEs can use the documented information of #return void to indicate the developer that a return values is used which serves no purpose.
<?php
$foo1 = return_never();
$foo2 = return_sometimes();
The first call is senseless since the variable will always contain NULL, the second one might actually contain something. This is becoming even more interesting if we put the function calls into a conditional.
<?php
if (($foo1 = return_never())) {
// Dead code
var_dump($foo1);
}
if (($foo2 = return_sometimes())) {
var_dump($foo2);
}
As you can see, #return void has its use cases and should be used if applicable.
Also note that it is going to be a part of the upcoming PHP PSR-5 standard.[1]
[1] http://www.php-fig.org/psr/
As of php 7.1, void is a valid return type and can be enforced on a function.
I would always add it on the docblock.
Another benefit of writing it, is to differentiate the void methods from the methods that may return anything but don't have a #return entry on the docblock by negligence.
Here is how I understand and use PhpDocumentor annotations:
<?php
/**
* This method always returns string.
* #return string
*/
public function useCase1()
{
return 'foo';
}
/**
* This method returns 2 data types so list them both using pipeline separator.
* #return string|false
*/
public function useCase2()
{
if ($this->foo === 1) {
return 'foo';
}
return false;
}
/**
* This method performs some operation and does not return anything so no return
* annotation is needed.
*/
public function useCase3()
{
$this->doOperation();
$this->doAnotherOperation();
}
/**
* If condition passes method returns void. If condition does not pass it returns
* nothing so I think that specifying the return annotation with void is in space. :)
* #return void
*/
public function useCase4()
{
if ($this->foo === 1) {
$this->doOperation();
return;
}
$this->doAnotherOperation();
}
Personally, I think the big thing missing from this is that documenting a function returns at all is important. Currently standards dont have any documentation for functions that never return....hence a return void is way of saying yes this function does actually return.
Consider this code block
<?php
/**
* #return void
*/
function return_void() {
echo "foo";
}
/**
* #return null|string
*/
function return_sometimes() {
if ($this->condition()) {
return "foo";
}
}
/**
* This function actually doesnt return at all - it kills the script
**/
function noreturn() {
//do somthing then
die(); //or exit()
}
Clearly the use of #return at least indicates the function does return

Categories