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.
Related
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
I currently have an abstract class which i am extending to other controllers. I have a abstract function within the abstract class which takes the value and places it in the __construct.
abstract class Controller extends BaseController {
abstract public function something();
public function __construct(Request $request) {
if (!is_null($this->something())){
$this->global_constructor_usse = $this->something();
}
}
}
My problem is that, on controllers that don't require this abstract function, I am having to place in the empty function.
class ControllerExample extends Controller {
public function something(){
return 'somethinghere';
}
}
Is there anyway to making the abstract function optional, or have a default value?
class EmptyControllerExample extends Controller {
public function something(){}
}
It is not possible to have a abstract method optional, as it is implied in PHP that all abstract methods must have an implementation.
There are legit use cases for optional abstract methods, yes: event handlers, metadata describers, etc. Unfortunately, you'll need to use regular, non-abstract methods with an empty body, and indicate in PHPDoc that they will do nothing unless extended.
Be wary, though: this can very quickly turn into code smell by diffusing a class responsability with their children. If you're dealing with generic events, you can look into Laravel's own event system, or the Observer pattern instead.
Abstract functions in a parent class, should only be used if its required by your application to implement the following method in all controllers who inherits from it, clearly it is not the case.
In this case i would make a trait. Here you create a trait which can be implemented by the classes who needs it. Notice the use keyword usage, use somethingTrait;
trait SomethingTrait
{
public function something()
{
echo "something called";
}
}
class Controller
{
use SomethingTrait;
public function run()
{
$this->something();
}
}
phpfiddle link
Another aproach could be doing a class inheritance structure, if the controllers you want to implement the methods has something in common. Where you would implement your special method in CrmController, where you still would be able to create shared methods in the abstract controller.
AbstractController
|
CrmController
|
CompanyController
For your question, 'Is there anyway to making the abstract function optional or have a default value?' No, and you are down the wrong path if you are trying to make abstract function optional. Hope my suggestions can help.
I have a class which extends an other class and uses a trait.
class A extends ABase
{
use MyTrait;
//...
}
The extended class (ABase) extends an other class (ABase2), which extends an abstract class (ABase3).
When an A class is created, the trait constructor is executed because there is no constructor defined in A. At the same time, the trait constructor calls a method (configureOptions) which is defined in the trait, but also in the abstract class ABase3 with the same visibility (protected).
trait MyTrait
{
public function __construct($options)
{
//...
$this->configureOptions($resolver);
//...
}
protected function configureOptions($resolver)
{
// Define options
}
}
What I don't understand is that when configureOptions is called from the trait, the method executed is not the method defined in the trait, but the one defined in the abstract class (ABase3).
As PHP docs says: An inherited member from a base class is overridden by a member inserted by a Trait. The precedence order is that members from the current class override Trait methods, which in turn override inherited methods.
Doesn't say nothing about calling methods from the same trait, but shouldn't check first for the methods inside its own class?
Edit:
Not only the abstract class (ABase3) has the configureOptions method, but also the A class, but again, the method called from the trait should call the own class method. Isn't it?
I've got the solution from this answer.
class A extends ABase
{
use MyTrait {
configureOptions as traitConfigureOptions;
}
//...
}
and then, instead of parent::configureOptions($resolver):
$this->traitConfigureOptions($resolver);
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.
If I give the same name to a function in the Model of Codeigniter, that function gets called automatically when I load the model.
//controller
$this->load->model('my_model');
//model
class My_model extends CI_model {
function my_model {
}
}
In this case I don't have to call my model function like this
$this->my_model->my_model();
because loading the model calls the function as well.
Can somebody explain this behaviour? I haven't found anything in docs regarding this.
This is a common concept in Object-Orientated programming. The function is acting as a Constructor. The constructor is called when an instance of the object is created.
In PHP using the __construct() method is the advised way to declare a constructor for the class. However, in PHP 4, a constructor was declared using the class name, so:
For backwards compatibility, if PHP 5 cannot find a __construct()
function for a given class, and the class did not inherit one from a
parent class, it will search for the old-style constructor function,
by the name of the class.
In CodeIgniter, a model is a class. As you don't have a __construct() method in your class, PHP is treating your my_model function as the constructor for the class (as it is the same as the class name).
You may want to the following method to your model. This will stop my_model being treating as a constructor.
function __construct()
{
// Call the Model constructor
parent::__construct();
}
I'd personally avoid calling a method the same name as the class in PHP, as it can lead to this confusion! PHP's docs have some useful information on constructors.