How can i use code completion with fluent interface? - php

I wonder how to use autocompletion with inherited class. For exemple i have this kind of code :
<?php
class A {
/**
* #return $this
*/
function a(){
return $this;
}
/**
* #return $this
*/
function b(){
return $this;
}
}
class B extends A{
function c() {
}
}
$object = new b();
$object->a()->b()->c();
?>
When i try to navigate with ctrl+click i can find a and b function but how can i reach c?
Thanks.

You have to use the correct PHPDoc style documentation for Eclipse to add autocompletion. In your #return Statement you have to indicate the actual type (name of your class) returned, not the variable:
<?php
class A {
/**
* #return A
*/
function a(){
return $this;
}
/**
* #return A
*/
function b(){
return $this;
}
}
class B extends A{
/**
* #return B
*/
function c() {
}
}
$object = new B();
$object->a()->b()->c();
?>
Now in your example the problem is, that it won't really work with the subclass, because the documentation says that you e.g. for $object->a() return an instance of class A. Therefore autocomplete won't show the methods from class B (you can call them though).

Related

PHPDoc: document parent class method that always yields itself or descendant class?

Consider this code:
class ParentClass {
public static function generate($className = __CLASS__){
if(!$className) return new self();
else return new $className();
}
}
class ChildClass extends ParentClass {
/**
* #param string $className
* #return ChildClass
*/
public static function generate($className = __CLASS__) {
return parent::generate($className);
}
}
var_dump($ChildClass::generate()); // object(ChildClass)[1]
ChildClass::generate() returns an instance of ChildClass wherever I use it because I never provide a $className argument. Problem is that my IDE gives me a warning about the parent::generate() call not matching the documented return type:
I would like to make this warning go away by adding documentation to the parent method. I could do:
#return ParentClass | ChildClass
Adding this to the parent method works but that's not practical because there are many dozen children classes, and there could be many more in the future. I have tried both of the following:
#return static
#return $className
but that hasn't made the warning go away. Is there a PHPDoc approved way to indicate that the calling child class will always be returned? Or -- more accurately -- that a class of type $className will be returned? If not, is there a way that works even with just my IDE? (PhpStorm 2017.2)
Update
#gogaz comment below got me to think: it would be enough if a PHPDoc #return could indicate something like self | descendants
You can document parent class method and then just inherit that on a child class method.
class ParentClass
{
/**
* #param string $className
* #return ChildClass
*/
public static function generate($className = __CLASS__){
if(!$className) return new self();
else return new $className();
}
}
class ChildClass extends ParentClass
{
/**
* #inheritdoc
*/
public static function generate($className = __CLASS__) {
return parent::generate($className);
}
}
I hope that this will solve your problem.
UPDATE:
Another way to do this is by using interface that all classes will implement, so you could expect that your generate method will return some class that implements that interface.
/**
* ...
* #return MyCustomInterface
*/
If this doesn't solve your issue... Then, you can set return type as "mixed", this will suppress warning and it won't lie :) because your generate method can return any class you specify via argument...
/**
* ...
* #return mixed
*/
I've run across something similar (PHPStorm is more anal than PHP is on the subject). Unless there's some reason you HAVE to have it be specific to the child class, I would do this
class ParentClass {
/**
* #param string $className
* #return ParentClass
*/
public static function generate($className = __CLASS__){
if(!$className) return new self();
else return new $className();
}
}
Remember, your ChildClass is still an instance of ParentClass

Php typehinting in called method of object, created in base class

I have the following situation:
class Main {
function get() {
return new Query();
}
}
class Order extends Main {
public $id;
public $name;
}
class Query {
/**
* #return Main
*/
function execute() {
/// some code here
return $parsedObject;
}
}
When I use this code to execute() and get Order objects (as parsed object), I'm writing this:
$order = new Order();
$result = $order->get()->execute();
$result->id; /// typehint not works, cuz $result is type of Main, not more concrete type Order
So my question is - is there any way to pass a type of base (abstract class, interface) class implementation to method, that is called from that base class, for getting beautiful typehint of created object. Cuz i can create User, that extends Main class, with his own fields. But calling new User()->get()->excute() will give me the same result - an object of type Main
You can annotate just about anything, here is your code with everything annotated.
/**
* Class Main
*/
class Main {
/**
* #return Query
*/
function get() {
return new Query();
}
}
/**
* Class Order
*/
class Order
extends Main {
/** #var int */
public $id;
/** #var string */
public $name;
}
/**
* Class Query
*/
class Query {
/**
* #return Main
*/
function execute() {
/// some code here
return $parsedObject;
}
}

How to tell PHP Storm that the function return static or another type

class A {
/**
* #return static|bool
*/
public static function build()
{
if (/**/) {
return new static;
}
return false;
}
}
class B extends A {}
$o = B::build();
PHP Storm does not understand that there is the B instance in $o.
If I leave just the static without second type in return annotation, all is right.
At the moment PhpStorm does not correctly understand #return static|bool -- only #return static on its own is working right now.
https://youtrack.jetbrains.com/issue/WI-23435 -- watch this and related ticket (star/vote/comment) to get notified on progress.
Partial workaround: type hint that variable ($o in your case) via inlline PHPDoc, e.g.
/** #var B $o */
$o = B::build();
a workaround, not very elegant:
class A {
protected $class_name;
public static function build()
{
return $class_name::static_variable;
}
}
class B extends A {
protected $class_name="B";
}
$o = B::build();

phpdoc #var in php file

i have next php code
<?php
class A {
public function foo() {
}
}
/**
* #var A $a
*/
$a->
I want to make my ide autocomplete $a-> correct, and show me that there is only one available method foo in $a. There is no any string like $a = new A();
$a instantiated in another place and handled by autoloader.
The following syntax works fine in eclipse
/* #var $a A */
$a->
Note that I switched parameters order.
For some reason PDT in Eclipse swaps the order of the #var parameters. This syntax works:
<?php
class A {
public function foo() {
}
}
/**
* #var $a A
*/
$a->
I'm using a variant of eloquent that autopopulates variables and the autohinting utterly fails in my eclipse, wether I place it above, under it, single line, multi line comments.
I did find a way in which it does work for me.
class Foo extends Model {
public function beforeSave() {
$bar = $this->bar;
foreach($bar as $baz) {
$baz-> // <-- this works now \o/
}
}
/**
* #return \Foo\Baz\Models\Bar
*/
public function getBar() {
return $this->bar;
}
}

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.

Categories