I have an abstract class and I don't know the difference between the two ways of defining the test() function
abstract class Foo {
abstract protected function test();
}
and this
abstract class Foo {
abstract function test();
}
Does it make any difference?
From the PHP manual:
Class methods may be defined as public, private, or protected. Methods declared without any explicit visibility keyword are defined as public.
So the answer is no, they are not the same:
abstract protected function test(); will be accessible only within the class itself and by inheriting and parent classes;
abstract function test(); can be accessed from everywhere.
For compatibility with PHP 4 (where everything is public and there is no way to specify something else), the default visibility of class properties and methods is public.
Because of this,
abstract class Foo {
abstract function test();
}
is the same as:
abstract class Foo {
abstract public function test();
}
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";
}
}
So I have a question about the difference between "when we should declare normal function" and "when we should declare abstract function" in base class. Look at my example.
In the abstract class:
abstract class Birds {
abstract public function fly();
}
class Swallow extends Birds {
public function fly() {
// This function override fly function in Birds class
echo "Implement fly function in Swallow class";
}
}
In the normal class:
class Birds {
public function fly() {
echo "Implement fly function in Birds class";
}
}
class Swallow extends Birds {
public function fly() {
// This function override fly function in Birds class
echo "Implement fly function in Swallow class";
}
}
What you can see. The fly function in Swallow class is inherited by Birds class (in all cases). They are a same thing. So I'm embarrassed and I dont know when we should declare abstract function in base class?
Thanks for your help!
Abstract functions are actually only an interface. E.g. there's no difference in your example between abstract class and if it would be an interface (that's because there's only abstract method).
//abstract class
abstract class Birds {
abstract public function fly();
}
//interface
interface Birds {
public function fly();
}
That's because abstract methods have the same purpose that interface's method. When you somewhere else create a function (or method or a class or another interface etc.), and you will require any Birds instance, you will be sure you have that abstract method avaiable, although it was not implemented in Birds.
public function sendBirdToSpace(Birds $bird) { //no matter what Bird subclass
$bird->fly(); //you're sure this method is avaiable
}
Also usually you will have more than one child class. When it comes to that, it's getting more clear about abstract method.
It's actually pretty simple. Should Birds have a default behaviour implementation of flying? That's all. If every bird should can fly, but there's no default method - make it abstract.
Taken from PHP OOP Class Abstraction:
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.
This is essentially saying that your Swallow class has to inherit (have) the fly() method in it if it extends the Bird class based off of its abstract definition.
The general rule of thumb when harnessing abstract methods is when you want normality among classes.
Take the following for example:
Class Car {
abstract public function make();
abstract public function model();
}
This is our "base" class. Anything that extends from this has to specify the make() & model() methods.
Class Tesla extends Car {
public function make() {}
public function mmodel() {}
}
As you see above, our Tesla class has the required methods within. If you do not include these methods, you'll have PHP errors thrown.
Note
If you're exploring this option of "container" like development, then I'd suggest that you have a good look at PHP OOP Object Interfaces too, well worth it!
I am not asking a typical question about why some code failed, yet I am asking about why it worked.It has worked with me while coding, and I needed it to fail.
Case
a base abstract class with a protected constructor declared abstract
a parent class extends the abstract class with public constructor (Over ridding)
a child class extends the very same abstract class with a protected constructor
abstract class BaseClass {
abstract protected function __construct();
}
class ChildClass extends BaseClass {
protected function __construct(){
echo 'It works';
}
}
class ParentClass extends BaseClass {
public function __construct() {
new ChildClass();
}
}
// $obj = new ChildClass(); // Will result in fatal error. Expected!
$obj = new ParentClass(); // that works!!WHY?
Question
Parent class instantiates child class object, and it works!!
how come it does?
as far as I know,object cannot be instantiated if its constructor declared protected, except only internally or from within any subclasses by inheritance.
The parent class is not a subclass of the child class,it doesn't inherit a dime from it ( yet both extend the same base abstract class), so how come instantiation doesn't fail?
EDIT
This case only happens with an abstract BaseClass that has also an abstract constructor.If BaseClass is concerete, or if its protected constructor is not abstract, instantiation fails as expected.. is it a PHP bug?
For my sanity, I need really an explanation to why PHP behaves this way in this very specific case.
Thanks in advance
Why it works?
Because from inside ParentClass you have granted access to the abstract method from BaseClass. It is this very same abstract method which is called from ChildClass, despite its implementation is defined on itself.
All relies in the difference between a concrete and an abstract method.
You can think like this: an abstract method is a single method with several implementations. On the other hand, each concrete method is a unique method. When it has the same name than its parent, it overrides the parent's one (it does not implement it).
So, when declared abstract, it is always the base class method which is called.
Think about a method declared abstract: Why the signatures of different implementations can't differ? Why can't the child classes declare the method with less visibility?
Anyway, you have just found a very interesting feature. Or, if my understanding above is not correct, and your expected behaviour is the truly expected behaviour, then you have found a bug.
Note: the following was tested with PHP 5.3.8. Other versions may exhibit different behavior.
Since there isn't a formal specification for PHP, there isn't a way of answering this from the point of view of what should happen. The closest we can get is this statement about protected from the PHP manual:
Members declared protected can be accessed only within the class itself and by inherited and parent classes.
Though the member may be overridden in ChildClass (keeping the "protected" specifier), it was originally declared in BaseClass, so it remains visible in descendants of BaseClass.
In direct opposition to this interpretation, compare the behavior for a protected property:
<?php
abstract class BaseClass {
protected $_foo = 'foo';
abstract protected function __construct();
}
class MommasBoy extends BaseClass {
protected $_foo = 'foobar';
protected function __construct(){
echo __METHOD__, "\n";
}
}
class LatchkeyKid extends BaseClass {
public function __construct() {
echo 'In ', __CLASS__, ":\n";
$kid = new MommasBoy();
echo $kid->_foo, "\n";
}
}
$obj = new LatchkeyKid();
Output:
In LatchkeyKid:
MommasBoy::__construct
Fatal error: Cannot access protected property MommasBoy::$_foo in - on line 18
Changing the abstract __construct to a concrete function with an empty implementation gives the desired behavior.
abstract class BaseClass {
protected function __construct() {}
}
However, non-magic methods are visible in relatives, whether or not they're abstract (most magic methods must be public).
<?php
abstract class BaseClass {
abstract protected function abstract_protected();
protected function concrete() {}
}
class MommasBoy extends BaseClass {
/* accessible in relatives */
protected function abstract_protected() {
return __METHOD__;
}
protected function concrete() {
return __METHOD__;
}
}
class LatchkeyKid extends BaseClass {
function abstract_protected() {}
public function __construct() {
echo 'In ', __CLASS__, ":\n";
$kid = new MommasBoy();
echo $kid->abstract_protected(), "\n", $kid->concrete(), "\n";
}
}
$obj = new LatchkeyKid();
Output:
In LatchkeyKid:
MommasBoy::abstract_protected
MommasBoy::concrete
If you ignore the warnings and declare magic methods (other than __construct, __destruct and __clone) as protected, they appear to be accessible in relatives, as with non-magic methods.
Protected __clone and __destruct are not accessible in relatives, whether or not they're abstract. This leads me to believe the behavior of abstract __construct is a bug.
<?php
abstract class BaseClass {
abstract protected function __clone();
}
class MommasBoy extends BaseClass {
protected function __clone() {
echo __METHOD__, "\n";
}
}
class LatchkeyKid extends BaseClass {
public function __construct() {
echo 'In ', __CLASS__, ": \n";
$kid = new MommasBoy();
$kid = clone $kid;
}
public function __clone() {}
}
$obj = new LatchkeyKid();
Output:
In LatchkeyKid:
Fatal error: Call to protected MommasBoy::__clone() from context 'LatchkeyKid' in - on line 16
Access to __clone is enforced in zend_vm_def.h (specifically, ZEND_CLONE opcode handler). This is in addition to access checks for methods, which may be why it has different behavior. However, I don't see special treatment for accessing __destruct, so there's obviously more to it.
Stas Malyshev (hi, Stas!), one of the PHP developers, took a look into __construct, __clone and __destruct and had this to say:
In general, function defined in base class should be accessible to all
[descendents] of that class. The rationale behind it is that if you define
function (even abstract) in your base class, you saying it will be
available to any instance (including extended ones) of this class. So
any descendant of this class can use it.
[...] I checked why ctor behaves differently, and it's because parent ctor
is considered to be prototype for child ctor (with signature
enforcement, etc.) only if it's declared abstract or brought from the
interface. So, by declaring ctor as abstract or making it part of the
interface, you make it part of the contract and thus accessible to all
hierarchy. If you do not do that, ctors are completely unrelated to each
other (this is different for all other non-static methods) and thus
having parent ctor doesn't say anything about child ctor, so parent
ctor's visibility does not carry over. So for ctor is not a bug. [Note: this is similar to J. Bruni's answer.]
I still think it's most probably a bug for __clone and __destruct.
[...]
I've submitted bug #61782 to track the issue with __clone and __destruct.
EDIT: constructors act differenlty... It's expected to work even without abstract classes but I found this test that tests the same case and it looks like it's a technical limitation - the stuff explained below doesn't work with constructors right now.
There's no bug. You need to understand that access attributes work with objects' context. When you extend a class, your class will be able to see methods in BaseClass' context. ChildClass and ParentClass both in BaseClass context, so they can see all BaseClass methods. Why do you need it? For polymorphism:
class BaseClass {
protected function a(){}
}
class ChildClass extends BaseClass {
protected function a(){
echo 'It works';
}
}
class ParentClass extends BaseClass {
public function b(BaseClass $a) {
$a->a();
}
public function a() {
}
}
No matter what child you pass into ParentClass::b() method, you'll be able to access BaseClass methods (including protected, because ParentClass is BaseClass child and children can see protected methods of their parents). The same behaviour applies to constructors and abstract classes.
I wonder if there isn't something buggy w/ the abstract implementation under the hood, or if there is a subtle issue going on that we're missing. Changing BaseClass from abstract to concrete produces the fatal error you're after though (classes renamed for my sanity)
EDIT: I agree w/ what #deceze is saying in his comments, that it is an edge case of abstract implementation and potentially a bug. This is at least a work-around that provides the expected behavior albiet some ugly technique (feigned abstract base class).
class BaseClass
{
protected function __construct()
{
die('Psuedo Abstract function; override in sub-class!');
}
}
class ChildClassComposed extends BaseClass
{
protected function __construct()
{
echo 'It works';
}
}
// Child of BaseClass, Composes ChildClassComposed
class ChildClassComposer extends BaseClass
{
public function __construct()
{
new ChildClassComposed();
}
}
PHP Fatal error: Call to protected ChildClassComposed::__construct()
from context 'ChildClassComposer' in
/Users/quickshiftin/junk-php/change-private-of-another-class.php on
line 46
I've set up an abstract parent class, and a concrete class which extends it. Why can the parent class not call the abstract function?
//foo.php
<?php
abstract class AbstractFoo{
abstract public static function foo();
public static function getFoo(){
return self::foo();//line 5
}
}
class ConcreteFoo extends AbstractFoo{
public static function foo(){
return "bar";
}
}
echo ConcreteFoo::getFoo();
?>
Error:
Fatal error: Cannot call abstract method AbstractFoo::foo() in foo.php on line 5
This is a correct implementation; you should use static, not self, in order to use late static bindings:
abstract class AbstractFoo{
public static function foo() {
throw new RuntimeException("Unimplemented");
}
public static function getFoo(){
return static::foo();
}
}
class ConcreteFoo extends AbstractFoo{
public static function foo(){
return "bar";
}
}
echo ConcreteFoo::getFoo();
gives the expected "bar".
Note that this is not really polymorphism. The static keywork is just resolved into the class from which the static method was called. If you declare an abstract static method, you will receive a strict warning. PHP just copies all static methods from the parent (super) class if they do not exist in the child (sub) class.
You notice that word self?
That is pointing to AbstractClass. Thus it is calling AbstractClass::foo(), not ConcreteClass::foo();
I believe PHP 5.3 will provide late static bindings, but if you are not on that version, self will not refer to an extended class, but the class that the function is located in.
See: http://us.php.net/manual/en/function.get-called-class.php
It's a rule that abstract and static keywords can not be use on a method at the same time.
A method with an abstract keyword means that sub-class must implement it. Adding static to a method of a class allows us to use the method without instantiating it.
So that is why the error occurs.