I have a parent class that has a public function that is final. Is it possible to disallow access to this method in a derived class? Other classes that derive from the parent class need to still be able to call it.
class ParentClass {
final public function test() {
}
}
class ChildClass extends ParentClass {
}
class ChildClass2 extends ParentClass {
}
$child = new ChildClass();
$child2 = new ChildClass2();
// make this illegal:
$child->test();
// but not this:
$child2->test();
When you inherit from class it means that derived class is a class that inherits. It means that it has exact the same base functionality that can be expanded, overriden and so on. If it makes no sense that derived class had method that is defined in parent class means that derived class is not a class that it derives.
For real world example let's say you have class FlyingMachine that has method Fly and Move, it would not be logical if you derived class Car from FlyingMachine because cars don't fly, while deriving class AirPlane from mentioned base class is perfectly fine. But it would be logical if you had base class Machine that had method Move and car was derived from this class Car : Machine (car is a machine, right?), FlyingMachine is a machine too so FlyingMachine : Machine is perfectly fine and since air-plane is a flying machine AirPlane : FlyingMachine makes sense.
For your given example this should be something like:
abstract class VeryBaseClass {
public function MethodThatIsLogicalForEveryDerivedClass() {}
}
abstract class BaseClass extends VeryBaseClass {
public function SomeOtherFunctionThatMakesSenseOnlyForSomeClasses() {}
}
class ClassThatShouldNotHaveSpecificFunctionDefinedInBaseClass extends VeryBaseClass{}
class ClassThatShouldHaveSomeOtherMethod extends BaseClass {}
Edit:
If there's a functionality that several derived classes need but for some of them it should be public and for some of them not. You should define in base class this method as protected (derived class can access one but from outside impossible). And in derived class that require that this method was public make another method and call parent's method. E.g.:
abstract class Car { protected function Shift() {} }
class ManualCar extends Car {
public function Shift() { parent::Shift(); }
}
class AutomaticCar extends Car {
//Some implementation of automatic car that uses protected Shift method
}
Little demo
How about checking for that class in the method, and throwing an exception if it's the disallowed class:
class ParentClass {
final public function test() {
if (get_class($this) == "ChildClass")
throw new Exception("ChildClass cannot call test()")
}
}
NB: this is fairly evil
Related
I have the following classes
Abstract class duck
This class have flyBehavoir of type FlyBehavoir
Function to perform flying preformFly()
Function to set flyBehavoir setFlyBrhavoir(FlyBehavoir $flyBehavoir)
Class DonaldDuck extends Duck
in this class, I have a __construct method, inside this constructor, I instantiate new fly behavior FlyWithWings.
The problem is when I need to change flyBehavoir in the runtime via setFlyBrhavoir() method and set it to FlyWithRocket it does not change as long as flyBehavoir is private if I make it public it works fine. how can I do that?
thought that we can change any property in the superclass from the child class, as long as I access this private property vis setter.
below my attempt
<?php
//abstract class that defines what it takes to be a duck
//inside Duck we will have $flyBehavoir object of FlyBehavoir Type
abstract class Duck{
private $flyBehavoir;
public function preformFly(){
$flyBehavoir.fly();
}
public function setFlyBehavoir(FlyBehavoir $flyBehavoir){
$this->flyBehavoir = $flyBehavoir;
}
}
//creating type FlyBehavoir
interface FlyBehavoir{
function fly();
}
//this concrete class of type FlyBehavoir this will provide our ducks with the functionality they need to fly
class FlyWithWings implements FlyBehavoir{
public function fly(){
echo "I am Flying with my own Wings<br>";
}
}
//this concrete class of type FlyBehavoir this will provide our ducks with the functionality they need to fly
class FlyWithRocket implements FlyBehavoir{
public function fly(){
echo "I am the fastest duck ever, see my rockets wings <br>";
}
}
// creating our first duck and given it the ability to fly with wings
class DonaldDuck extends Duck{
public function __construct(){
$this->flyBehavoir = new FlyWithWings;
}
}
$donaldDuck = new DonaldDuck( ) ;
$donaldDuck->flyBehavoir->fly();
//changing behavoir in run time
$donaldDuck->setFlyBehavoir(new FlyWithRocket);
$donaldDuck->flyBehavoir->fly();
Output
I am Flying with my own Wings
I am Flying with my own Wings
A private property is not accessible in child classes.
class DonaldDuck extends Duck {
public function __construct(){
$this->flyBehavoir = new FlyWithWings;
}
}
For all intents and purposes, this class does not formally declare flyBehaviour at all, so $this->flyBehaviour in the constructor creates a new public property. You can see that clearly when var_dumping the object:
object(DonaldDuck)#1 (2) {
["flyBehavoir":"Duck":private]=>
NULL
["flyBehavoir"]=>
object(FlyWithWings)#2 (0) {
}
}
The parent's private property is a) separate, b) private to it and c) null since nobody has set it yet. Otherwise it would also not be possible for you to access $donaldDuck->flyBehavoir->fly() from without the class!
If you have a private property, you need to only let code of the same class act on it:
class DonaldDuck extends Duck {
public function __construct(){
$this->setFlyBehaviour(new FlyWithWings);
}
}
$donaldDuck = new DonaldDuck();
$donaldDuck->setFlyBehavoir(new FlyWithRocket);
$donaldDuck->preformFly();
This works as you expect, since you're using the correctly privileged methods to access the property. If you want to access the property directly in child classes, it needs to be protected (which then won't let you access it from outside the class though, it would have to be public for that).
Is it possible to override an abstract function in PHP 7 with a function in a child class that would narrow down on the accepted argument type?
A word of elaboration - let's assume that I have an abstract method:
abstract public function setTarget(
AbstractDeliveryTarget $deliveryTarget
): Delivery;
Now in one child class, I'd like to override this method's signature in order for it to accept only a specific parameter type (let's assume that EmailDeliveryTarget extends AbstractDeliveryTarget):
public function setTarget(
EmailDeliveryTarget $emailDeliveryTarget
): Delivery;
PHP interpreter would then complain that the signature of the abstract method cannot be altered in the child class. Is there, then, a way to achieve that type-safety in a child class other than some in-method-body type guards?
This is not a technical limitation; what you're asking for doesn't make sense with the principles of OOP.
Your abstract class is a contract; let's define the base class:
class AbstractDeliverer {
abstract public function setTarget(
AbstractDeliveryTarget $deliveryTarget
): Delivery;
}
And some delivery targets
class AbstractDeliveryTarget {}
class EmailDeliveryTarget extends AbstractDeliveryTarget {}
class SMSDeliveryTarget extends AbstractDeliveryTarget {}
Then you can write this somewhere else:
function deliverAll(AbstractDeliverer $d) {
$e = new EmailDeliveryTarget;
$d->setTarget($e);
$s = new SMSDeliveryTarget;
$d->setTarget($s);
}
Because we know that $d is an AbstractDeliverer, we know that passing $e and $s to it should both work. That is the contract guaranteed to us when we made sure our input was of that type.
Now lets see what happens if you extend it the way you wanted:
class EmailOnlyDeliverer extends AbstractDeliverer {
public function setTarget(
EmailDeliveryTarget $emailDeliveryTarget
): Delivery {
/* ... */
}
}
$emailonly = new EmailOnlyDeliverer;
We know that $e instanceOf AbstractDeliverer will be true, because we've inherited, so we know we can safely call our deliverAll method:
deliverAll($emailonly);
The first part of the function is fine, and will effectively run this:
$e = new EmailDeliveryTarget;
$emailonly->setTarget($e);
But then we hit this part:
$s = new SMSDeliveryTarget;
$emailonly->setTarget($s);
Oops! Fatal error! But the contract on AbstractDeliverer told us this was the correct value to pass! What went wrong?
The rule is that a sub-class must accept all inputs that the parent class would accept, but it can accept additional inputs if it wants, something known as "contravariance". (Return types are instead "covariant": the sub-class must never return a value which couldn't be returned from the parent class, but can make a stronger promise that it will only return a subset of those values; I'll leave you to come up with an example of that one yourself).
Looks like no pretty solutions so we can try use interfaces.
<?php
interface DeliveryTarget {}
class AbstractDeliveryTarget implements DeliveryTarget {
}
class EmailDeliveryTarget extends AbstractDeliveryTarget{
}
abstract class Q {
abstract protected function action(DeliveryTarget $class);
}
class Y extends Q {
protected function action(DeliveryTarget $class){}
}
OR
interface DeliveryTargetAction {}
class AbstractDeliveryTarget {
}
class EmailDeliveryTarget extends AbstractDeliveryTarget implements DeliveryTargetAction {
}
abstract class Q {
abstract protected function action(DeliveryTargetAction $class);
}
class Y extends Q {
protected function action(DeliveryTargetAction $class){}
}
Depends on what you want to do.
i have become a bit confused about abstract class ! i have read more of the post written in stackoverflow and another website but i didn't understand ! so i took a look at my book again but i didn't understand it either . so please analyze the code below step by step :
thanks in advance
<?php
abstract class AbstractClass
{
abstract protected function getValue();
public function printOut() {
print $this->getValue();
}
}
class ConcreteClass1 extends AbstractClass
{
protected function getValue() {
return "ConcreteClass1";
}
}
class ConcreteClass2 extends AbstractClass
{
protected function getValue() {
return "ConcreteClass2";
}
}
$class1 = new ConcreteClass1;
$class1->printOut();
$class2 = new ConcreteClass2;
$class2->printOut();
?>
By definition
'An abstract class is a class that is declared abstract —it may or may
not include abstract methods. Abstract classes cannot be instantiated,
but they can be subclassed. An abstract method is a method that is
declared without an implementation'.
If defined an abstract class, you should extend that class with another.
In case of having abstract methods within the abstract class, you should write them in the child class in order to instantiate the child.
Related to the code, that is why when you instantiate the ConcreteClass, the getValue function is 'overwritten' to the pattern, while calling to the printOut method is from the father itself, because It is already written and not overwritten by the child. (See also that method was not abstract, that is why you can also use it from the father class)
Your code is right. Abstact class mean, when you can not make a instance of it. You can not do this:
$abstract = new AbstractClass();
If I have an abstract class like this:
abstract class MyApp
{
public function init()
{
$this->stuff = $this->getStuff();
}
public function getStuff()
{
return new BlueStuff();
}
}
And then I have a class that extends from this abstract class like this:
class MyExtendedClass extends MyApp
{
public function init()
{
parent::init();
}
public function getStuff()
{
return new RedStuff();
}
}
If I do:
$myobj = new MyExtendedClass();
$myobj->init();
Why does the method getStuff from the child class get called? Isn't $this in the context of the abstract class? If so, shouldn't the method of the abstract class get called?
Thanks!
New answer
In PHP you can use subclasses as if all methods in the parent class that don't exist in the subclass have been copied to the subclass. So your example would be the same as:
class MyExtendedClass extends MyApp {
public function init() {
$this->stuff = $this->getStuff();
}
public function getStuff() {
return new RedStuff();
}
}
Just think of the subclass as having all code of the parent class and you're normally all right. There is one exception to this rule: properties. A private property of a class can only be accessed by that class, subclasses can't access the private properties of parent classes. In order to do that you need to change the private property into a protected property.
Original answer
Abstract classes in PHP are just like regular classes with one big difference: they can't get initiated. This means you can't do new AbstractClass();.
Since they work exactly like regular classes for everything else, this also is the case for extending classes. This means that PHP first tries to find the method in the initiated class, and only looks in the abstract classes if it doesn't exist.
So in your example this would mean that the getStuff() method from MyExtendedClass is called. Furthermore, this means you can leave out the init() method in MyExtendedClass.
I mean something like that:
class parentClass {
public function method() {
echo $this->prop;
}
}
class childClass extends parentClass {
public $prop = 5;
}
How can I get a child prop from the parent prop?
Thanks...
Either I don't fully understand what you want or the solution is as trivial as the following code.
class parentClass {
public function method() {
echo $this->prop;
}
}
class childClass extends parentClass {
public $prop = 5;
}
$object = new childClass();
$object->method();
I mean the child class is extending the base class which means it will also inherit all the methods of its parent's class. That makes the whole process of using the parent's class method as simple as calling it from the instance of the child class.
All protected and public members of child classes are visible from within their parent class in PHP, so the example code you provided should work just fine. Quote from the php doc:
Members declared protected can be accessed only within the class
itself and by inherited and parent classes.
But the actual question is: do you really need it?
The proper OO way would be to define a self-contained parent class that expresses something. It should not need to access properties of child classes - this is a so-called code smell. If you really think that you have a case where a similar construct is necessary, you are probably looking for abstract methods, which guarantee that every child class has this property:
abstract class Animal {
public function makeNoise() {
echo $this->getNoiseString();
}
protected abstract function getNoiseString();
}
class Cat extends Animal {
protected function getNoiseString() {
return 'meow';
}
}
//parent
class parentClass {
protected $prop = null;
public function method() {
echo $this->prop;
}
}
//child
class childClass extends parentClass {
protected $prop = 5;
}
Make sure the variable is defined in the parentclass as well. So it will be accessible by the parent.