PhpStorm, Laravel and autocompletition - php

I have a method run() in TreeGen class:
class TreeGen implements TreeGenerable{
public function run()
{
...
$this->pushEmptyGroupsToTree($numFighters);
...
}
}
Thing is $this could be one of the 4 Subclasses of tree gen, and all those method implement different versions of pushEmptyGroupsToTree.
But PhpStorm is only looking for pushEmptyGroupsToTree in TreeGen class and off course, I don't have it defined in the super class, so it doesn't detect it.
Is there a way to make him recognize subclass methods?

IDE behaves correctly. You either need to declare this class abstract, or declare abstract method here or in superclass.
UPDATE
Method run of TreeGen could be called from instance of Treegen
$treeGen = new TreeGen;
$treeGen->run();
and that will cause Call to undefined method error.
If TreeGen is not supposed to be called directly it should be abstract.

If you dont want TreeGreen to be used directly, declare it abstract as such:
abstract class TreeGen implements TreeGenerable {
// ...
If you need the child classes to have that method declared, declare it as abstract in your superclass:
abstract class TreeGen implements TreeGenerable
{
// ...
abstract public function pushEmptyGroupsToTree($numFighters);
}
Now you cant extend TreeGen without implementing pushEmptyGroupsToTree as well

For this case you must use PHPDoc and type hinting
And don`t forget about:
ide-helper:generate Generate a new IDE Helper file.
ide-helper:meta Generate metadata for PhpStorm
ide-helper:models Generate autocompletion for models

Related

Why can you override the initial value of a property defined in a trait that is used in a parent class?

The PHP docs says the following about overriding trait properties:
If a trait defines a property then a class can not define a property
with the same name unless it is compatible (same visibility and
initial value), otherwise a fatal error is issued.
However, when you use a trait in an abstract class, then you can override the properties defined in the trait in a class extending that abstract class:
<?php
trait PropertyTrait
{
public $prop = 'default';
}
abstract class A
{
use PropertyTrait;
}
class B extends A
{
public $prop = 'overridden';
public function write()
{
echo $this->prop;
}
}
$b = new B();
$b->write(); // outputs "overridden"
Live demo
The code above works, but I can't find any reference about it in the documentation. Is this an intended feature?
Because for all intents and purposes B is not using PropertyTrait. That's used by A to compose the abstract class.
B has no visibility of what traits A is using. If you were to execute class_uses on B, you'd get an empty array. Docs, and example.
Since B is not using any traits, the class is free to override any inherited properties.
The fact that A is an abstract class has no bearing on this. The same behaviour would happen with any class that extended a class that was composed using traits.

For an abstract function, should the phpdoc be defined for the abstract function, the implemented function, or both?

Is there a correct or standard way to do this?
For example:
AbstractFoo.php:
abstract class AbstractFoo {
/**
* Does the phpdoc go here?
*/
public function fooFunction();
}
Foo.php:
class Foo extends AbstractFoo {
/**
* Or does the phpdoc go here?
*/
public function fooFunction(){
// some implementation
}
}
Consider the following issues:
If you put the phpdoc in the abstract FooFunction(), and control + click $foo->FooFunction(), you will end up at the Foo class' FooFunction(), which is undocumented, and not immediately obvious that it's implementing an abstract function. And if it was obvious, there's no easy way to get to the phpdoc in the abstract function, without
Determining what class the object was
Locating the abstract class it is inheriting
Doing a "find" on the function name in that abstract class
If you put the phpdoc only in the children functions, then you're faced with duplicating phpdocs across the children functions of n number of inheriting classes.
If you put the phpdoc in both, the abstract function's phpdoc becomes purposeless, unless a child class did not have a phpdoc, wanting to inherit the abstract's phpdoc. But even in this case, again, there's no easy way to navigate to that abstract phpdoc.
You should document an interface or abstract implementation.
Otherwise, use #inheritdoc/document your implementation of an interface method.

PHPStorm 9 inspection of class inheritance works unexpectable

I'm facing the following issue in PHPStorm 9:
Say I have an interface FieldInterface that has some methods:
namespace Acme;
interface FieldInterface {
public function methodA();
public function methodB();
}
then I have an abstract class that implements base functionality of the interface. That abstract class has the user to implement certain methods, let's say it's methodB in our example:
namespace Acme;
abstract class AbstractField implements FieldInterface {
public function methodA() {
// implement methodA
}
public abstract function methodB(); // have the user implement it
}
And finally I have some ready-to-use class StringField:
namespace Acme;
class StringField extends AbstractField {
public function methodB() {
// implement methodB
}
}
At this point everything's going well. But if I add new method in the FieldInterface, PHPStorm does not say that anything is wrong with AbstractField while it's obvious that I should add public abstract function newMethod(); in there. However, it spots the error in StringField class instead.
It could be understood from the point that abstract classes are made for the purpose of extention, but usually you extend the abstract class rather than implement underlying interface. The whole meaning of making abstract class is to save user's time for implementing the interface. So why PHPStorm forces me to implement interface in concrete class rather than forcing me to implement it in abstract class that is explicitly implements the interface.
So I wonder if it is a bug in PHPStorm, or maybe it's done on purpose. Either way, is there any workaround?
That's how it should be, showing an error in the abstract class would be wrong.
In fact, public abstract function methodB(); is redundant because the abstract class already "inherits" this abstract method from the interface as it does not implement it.
The only workaround is to make AbstractField not abstract.

How to override a trait's method in abstract class?

I'm stuck into a problem with traits I can't solve on my own.
I have classes extending an abstract class (in my case these are several controller classes and an abstract class Controller, the used framework won't be important here, since this is a general PHP question…) that uses traits. I'd like to override a method defined in one of the traits. This only works as long as I define the method in my sub-classes but not in my abstract class.
So, this one works perfectly:
class MyController extends Controller
{
use AnyTrait;
public function anyMethodFromAnyTrait()
{
// override AnyTrait::anyMethodFromAnyTrait()
}
}
I also know how to call the anyMethodFromAnyTrait method from AnyTrait by using as.
class MyController extends Controller
{
use AnyTrait { AnyTrait::anyMethodFromAnyTrait as method }
public function anyMethodFromAnyTrait()
{
// invoke AnyTrait::anyMethodFromAnyTrait()
$this->method();
}
}
Both work like a charm.
But my problem is a bit different.
When using the trait and defining the method in my abstract class I am not able to override the trait's method.
Assume the following controller class:
class MyController extends Controller
{
public function anyAction()
{
// let's see what happens…
$this->anyMethodFromAnyTrait();
}
}
…and the abstract one that's extended by MyController:
abstract class Controller
{
use AnyTrait
public function anyMethodFromAnyTrait()
{
// do something different than AnyTrait
}
}
…And this is what's not working at all. Whenever I call $this->anyMethodFromAnyTrait() within MyController the trait's method as implememented in AnyTrait will be invoked. The same named method in my abstract Controller will be ignored.
Therefore I only can override a trait's method in a concrete sub-class but not in an abstract class that is extended by that sub-class.
So the method definitions in traits get a higher priority by PHP than the same method definitions in abstract classes.
Do you know any workaround for that behaviour?
One workaround would be to use the traits ONLY in the subclasses.
PHP always prefers the trait methods over the "local" ones.
The reason why it works in subclasses is, that the trait method of the superclass is extended, not the trait usage itself.

Force child classes to override a particular function in PHP

I am creating a reporting library in PHP and developed an abstract class named ReportView. This will provide the basic functionality of a report like Generating header and footer, create parameter form.
There will be another function named generate_report in this class. Currently it is empty in abstract class as at this level we do not know the contents of report. Further it includes a render function which calls this generate_report function and sends output to browser.
So I need whenever a child class inherits from ReportView it must implement
the generate_report method otherwise PHP must give error. Is there any keyword or method through which we can enforce implemetation of a specific function.
Do the following:
abstract class ReportView {
abstract protected function generate_report();
// snip ...
}
class Report extends ReportView {
protected function generate_report() { /* snip */ }
}
Any class that extends ReportView and is not abstract must implement generate_report (and any other abstract function in its super classes for that matter).
Sounds like you’d be better off creating an interface, which would enforce you to define those methods in classes that then implement this interface.
<?php
interface ReportInterface {
public function generate();
}
class MyReportClass implements ReportInterface {
}
Instantiating MyReportClass here will throw a fatal error, telling you generate() has not been implemented.
Edit: You can also create abstract classes that implement this interface. You can have your abstract class contain any methods all extending classes need, and have your interface define any methods you need to be defined by extending classes.
You need to declare the method as abstract as well (and don't give it a method body), otherwise the derived classes will not be forced to implement it.
Alternatively, you could implement the method but have it just throw an Exception (not sure why you would want to do this).
Lastly, if all the methods in your base class are "abstract" (do not have bodies) then you can make the class into an Interface instead.

Categories