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);
Related
I coded a bunch of classes extending an abstract class in PHP. The abstract class has variables as well as the class which extends the abstract class.
I would like to create a method inside the abstract class, which return all the class variables of the child classes but don't have to be recoded in every subclass.
This snippet works fine in a subclass in order to get all variables, the ones from the abstract class and the other classes:
get_class_vars(get_class($this))
However, if I move this snippet to the abstract class, it doesnt work. Here's what I did:
public function test($test)
{
var_dump(get_class($test));
var_dump(get_class_vars(get_class($test)));
}
This code returns the class name of the passed class correctly, but the get_class_vars() does only return the variables of the abstract class, no matter which class is passed here.
What did I do wrong here?
<?php
abstract class Entity
{
protected int $top;
public function test()
{
var_dump(get_called_class());
var_dump(get_class_vars(get_called_class()));
}
}
class Sub extends Entity
{
public String $test; // CHANGED FROM PRIVATE TO PUBLIC!
}
$test = new Sub();
$test->test();
I found the solution - it was a "private" issue. The variable in the subclass needs to be at least a protected variable in order to be seen from the top class.
I just wrote code like this:
<?php
class test
{
// Force Extending class to define this method
abstract protected function getValue();
abstract protected function prefixValue($prefix);
// Common method
public function printOut() {
print $this->getValue() . "\n";
}
}
class testabs extends test{
public function getValue()
{
}
public function prefixValue($f)
{
}
}
$obj = new testabs();
?>
When I run this code, I received the error below:
Fatal error: Class test contains 2 abstract methods and must therefore be declared abstract or implement the remaining methods (test::getValue, test::prefixValue) in C:\wamp64\www\study\abstract.php on line 12
I understand the first part of this error. I changed the class test to abstract and the error is gone, but the or part i can't understand.
If you are going to add abstract methods, then you will need to make the class abstract as well. That way, the class cannot be instantiated- only non-abstract sub-classes can be.
The method visibility (refer to the second sub-section Method Visiblilty) is not the same in the sub-class. Depending on whether you want the methods to be called by code outside of sub-classes, you can declare the (abstract) methods in class test with visibility public, or else declare the sub-class methods also with visibility protected.
And note the second paragraph from the Class Abstraction page, which explains this:
When inheriting from an abstract class, all methods marked abstract in the parent's class declaration must be defined by the child; additionally, these methods must be defined with the same (or a less restricted) visibility. For example, if the abstract method is defined as protected, the function implementation must be defined as either protected or public, but not private
<?php
abstract class test{
// Force Extending class to define this method
abstract protected function getValue();
abstract protected function prefixValue($prefix);
// Common method
public function printOut() {
print $this->getValue() . "\n";
}
}
class testabs extends test{
protected function getValue()
{
}
/**
* this method can be called from other methods with this class
* or sub-classes, but not called directly by code outside of this class
**/
protected function prefixValue($f)
{
}
}
$obj = new testabs();
// this method cannot be called here because its visibility is protected
$obj->prefixValues();// Fatal Error
?>
The key technical differences between an abstract class and an interface are:
Abstract classes can have constants, members, method stubs (methods without a body) and defined methods, whereas interfaces can only have constants and methods stubs.
Methods and members of an abstract class can be defined with any visibility, whereas all methods of an interface must be defined as public (they are defined public by default).
When inheriting an abstract class, a concrete child class must define the abstract methods, whereas an abstract class can extend another abstract class and abstract methods from the parent class don't have to be defined.
Similarly, an interface extending another interface is not responsible for implementing methods from the parent interface. This is because interfaces cannot define any implementation.
A child class can only extend a single class (abstract or concrete), whereas an interface can extend or a class can implement multiple other interfaces.
A child class can define abstract methods with the same or less restrictive visibility, whereas a class implementing an interface must define the methods with the exact same visibility (public).
Your class has abstract functions but is not declared as abstract, so you have two choices. Either declare the class as abstract or provide an implementation of the abstract functions.
The first option (which you tried) allows the class to exist and be used by a concrete subclass that implements the functions. The second option means that the class is fully defined and can be used as is.
When your class has abstract methods it has to be declared abstract too.
So the following is correct:
<?php
abstract class test
{
// Force Extending class to define this method
abstract protected function getValue();
abstract protected function prefixValue($prefix);
// Common method
public function printOut() {
print $this->getValue() . "\n";
}
}
I'm trying to place a trait inside a class called Page. I also need to rename a trait function so that it doesn't clash with an existing class function. I thought I did all this successfully however I get an error that points to the wrong location?!
Call to undefined function App\Pages\Models\myTraitDefaultScope()
I've also tried: MyTrait\defaultScope($query) instead of trying to rename the conflicting function. But I then get the following error:
Call to undefined function App\MyTrait\defaultScope()
Below is the trait and class contained in separate files.
<?php
namespace App;
use Illuminate\Support\Facades\Auth;
trait MyTrait{
public function defaultScope($query){
return $query->where('active', '1')
}
}
.
<?php namespace Modules\Pages\Models;
use Illuminate\Database\Eloquent\Model;
use App\MyTrait;
class Page extends Model {
use MyTrait{
MyTrait::defaultScope as myTraitDefaultScope;
}
public function defaultScope($query){
return myTraitDefaultScope($query);
}
}
I'm not all that awesome at this so please don't shoot if I've got something badly wrong :)
When you 'use' a trait in your class, the class inherits all the methods and properties of the trait, like if it was extending an abstract class or an interface
So, this method of MyTrait:
public function defaultScope($query){
return $query->where('active', '1')
}
will be inherited by your Page class
As you have aliased this method as: myTraitDefaultScope, to call the method you should call it in the same way you would call every other method of the Page class:
public function defaultScope($query){
//call the method of the class
return $this->myTraitDefaultScope($query);
}
As you're using trait. So it points to the current or parent class. Thus, calling any method should be like $this->method($params); syntax.
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.
Are constructors inherited or do they belong to the class they are defined in? I only have seen examples with constructors of subclasses which call superclass' constructors. This is my current code, which can give some hint about what's going on. (I will change the code according to your replies. If I can use the constructor of the superclass, I won't define a constructor for each subclass and call superclass' constructor from each.
abstract class view
{
public $vieverid;
function __construct($viewerid) {
$this->viewer = $viewerid;
}
}
class viewactor extends view{
function __construct($viewerid) {
$this->viewerid = $viewerid;
}
According to my understanding, PHP doesn't auto-call parent's constructor if child constructor is defined. Otherwise it does.
In child constructor you have to call parent's constructor manually.
abstract class view
{
public $vieverid;
function __construct($viewerid) {
$this->viewer = $viewerid;
}
}
class viewactor extends view{
function __construct($viewerid) {
parent::__construct($viewerid); // manual call
// do your stuff here...
$this->viewerid = $viewerid;
}
parent::__construct(params); use for calling superclass constructor
PHP4
PHP doesn't call constructors of the base class automatically from a
constructor of a derived class. It is your responsibility to propagate
the call to constructors upstream where appropriate.
PHP5
PHP doesn't call constructors of the base class if new constructor defined.
If you define a constructor for derived class
It is your responsibility to propagate
the call to constructors upstream where appropriate.
parent::__construct(params)
Constructors
abstract class view
{
public $vieverid;
function __construct($viewerid) {
$this->vieverid= $viewerid;
}
}
class viewactor extends view{
function __construct($viewerid) {
parent::__construct($viewerid);
// Extra code if you want
}
}
class viewactor_construct extends view{
// Works in PHP5
}
Parent constructors are not called implicitly if the child class defines a constructor. In order to run a parent constructor, a call to parent::__construct() within the child constructor is required.
See here