When dealing with inheritance in PHP I found some lack of knowledge, mainly about constructors and private properties.
Let's take this code as example:
<?php
class Module
{
public $type;
public function __construct($type)
{
$this->type = $type;
}
}
class BModule extends Module
{
}
class CModule extends BModule
{
}
class A
{
private $module;
public function __construct(Module $module)
{
echo 'Set module for '.__CLASS__.' to '.$module->type . PHP_EOL;
echo "<br>";
$this->module = $module;
}
public function getModule()
{
echo "I (as " . __CLASS__ . ") have a module of type " . $this->module->type;
return $this->module->type;
}
}
class B extends A
{
}
$m = new Module('base-module');
$bm = new BModule('bi-module');
echo "<br>--------A---------<br>";
$a = new A($m);
echo "<br>A is of type " . $a->getModule();
echo "<br>--------B---------<br>";
$b = new B($bm);
echo "<br>B is of type " . $b->getModule();
Some questions:
shouldn't B construction call the constructor in the context of B? (and so I would expect it to fail cause it didn't inherited the private property $module)
or PHP would simply call the A constructor, using/referencing methods and properties from A? (including the private ones)
I can pass to $b either a Module or a BModule object; this is because BModule is a child of Module. Is PHP checking some inheritance chain (checking the parents) of the passed object when verifying the type hinting?
so can I pass to the constructor either an object of type Module or BModule or CModule?
And this is another example:
<?php
class a
{
private $a;
protected $a_copy;
public function __construct($a_value)
{
$this->a = $a_value;
$this->a_copy = $this->a;
}
public function getA()
{
return $this->a;
}
public function getCopyA()
{
return $this->a;
}
}
class b extends a
{
}
$a = new a('value for a');
$b = new b('value for b');
echo "<br>-----A-----<br>";
echo $a->getA()."<br>";
echo $a->getCopyA()."<br>";
echo "<br>-----B-----<br>";
echo $b->getA()." (I would expect to have no access to \$a)<br>";
echo $b->getCopyA()."<br>";
Being the property $a private, I would expect to not be able to access it or do anything with it from the class b.
It is a little bit non-sense for my actual understanding.
This is expected functionality, although B inherits all of the methods of A, they're not called in the context of B, they're called in the context of A. So the A constructor is called. This means that functions defined in A can access A's properties even when the object is extended. Methods defined in A cannot access properties of B however, which appears to be your understanding.
So to shortly answer your questions:
No, functions are always called in the context of where they are defined, not from where they are called.
PHP will check all the way down the inheritance chain to see if it's correct. Any child of a Class can be assumed to have the same functions. So if B extends A, You can use either B or A as a parameter when it's type-hinted to A, but only use B if it's type-hinted to B
Ad 1: No, the context of the called method is where the method (in this case the constructor) is declared. If the context would be class B then anyone could break your class simply by extending it.
Take a look at this example:
class A
{
private $module;
public function __construct(Module $module)
{
echo 'Set module for '.__CLASS__.' to '.$module->type . PHP_EOL;
echo "<br>";
$this->module = $module;
}
}
class B extends A
{
public function __construct()
{
parent::__construct(new Module()); // call the parent (which is A)
}
}
This illustrates how the scope of A::__construct() is actually the A class.
Ad 2: Yes, each object that is an instance of sub class can be used in place of the super class. This is why you should write your classes so that they can be substituted when the static typing requires a super class. For more information on this subject see the Liskov substitution principle.
As for the last example: again there is no code in sub class that would operate on private members of the super class. All code operates from within the super class context. So there is no problem here.
There would be a problem if you tried to overload methods of super class and use its private members like this:
class b extends a
{
public function getA()
{
return $this->a . "_suffix"; // error
}
}
In such case you must depend on the implementation of getA() method in the super class:
class b extends a
{
public function getA()
{
return parent::getA() . "_suffix"; // ok, we are depending on the super class implementation
}
}
Related
I have created an helper class:
abstract class Format{
public static function format_array_id_value($result){
$array = [];
foreach($result as $val){
extract($val);
$array[$id] = urldecode($val);
}
return $array;
}
}
I am requiring the containing file at the start of the application. I am attemping to call it from another class method as Format::format_array_id_value($result). I am receiving the error "Fatal error: Class 'Format' not found in Expense.php on line 22".
Am I not thinking about abstract classes correctly? Must I instantiate the class in order to use this helper method?
Thank you. :)
Daniela, there's no reason to use this as an abstract class, it's not its purpose.
As stated in documentation
Classes defined as abstract cannot be instantiated, and any class that contains at least one abstract method must also be abstract. Methods defined as abstract simply declare the method's signature - they cannot define the implementation.
Instead, use a common class
<?php
class FormatHelper
{
public static function formatArrayIdValue($result, $id)
{
$array = [];
foreach ($result as $val) {
extract($val);
$array[$id] = urldecode($val);
}
return $array;
}
}
and make sure you include it in your script:
<?php
require_once('FormatHelper.php');
print_r(
FormatHelper::formatArrayIdValue(['foo bar baz'], 'foo')
);
// output:
// Array ( [foo] => foo bar baz )
And the proper usage of abstract classes is:
<?php
abstract class AbstractFormatHelper
{
// note that abstract method has no body
abstract public static function requiredMethodToBeImplementedInChildClass();
}
class FormatHelper extends AbstractFormatHelper
{
public static function requiredMethodToBeImplementedInChildClass()
{
return 'Implemented!';
}
public static function formatArrayIdValue($result, $id)
{
$array = [];
foreach ($result as $val) {
extract($val);
$array[$id] = urldecode($val);
}
return $array;
}
}
Conclusions & suggestions
Operators
Use :: operator for accessing static methods.
MyClass::myStaticMethod();
Use -> operator for accessing methods of object.
$obj = new MyClass();
$obj->myNonStaticMethod();
Inheritance with abstract
As a Rule of thumb (ROT) abstract class is defined as abstract and contains at least one abstract method. That means that the main purpose of abstract classes is allowing its inheritance,
Although as Jeto pointed as a side effect if the class is defined as abstract, even if it doesn't contain any non_static methods it cannot be instantiated, however, this is not purpose for using abstract classes. Instead, you should use abstract classes only in case when you want to force developers (maybe yourself) for creating methods in their classes which extends your abstract class.
analyse this sample:
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
class MyClass
{
public static function myStaticMethod()
{
return ' this is myStaticMethod()';
}
public function myNonStaticMethod()
{
return 'this is myNonStaticMethod()';
}
}
abstract class AbstractFormatHelper
{
abstract public static function requiredMethodToBeImplementedInChildClass();
abstract protected static function otherProtectedAbstractMethod();
// final abstract protected static function otherProtectedFinalAbstractMethod(); // you even can't declare it
public static function foo()
{
return "foo() doesn't need to be implemented";
}
protected static function bar()
{
return "this doesn't need to be implemented but will be not accessible if you won't change it to public";
}
final protected static function baz()
{
return "baz() is final PROTECTED, you can NOT override it in the child, but you still can access it in child by getter ";
}
final private static function boo()
{
return 'boo() is final PRIVATE but and you can NOT access it in child by getter ';
}
}
class FormatHelper extends AbstractFormatHelper
{
public static function requiredMethodToBeImplementedInChildClass()
{
return 'Implemented!';
}
public static function otherProtectedAbstractMethod()
{
return 'erProtectedAbstractMethod() implemented';
}
public static function foo()
{
return parent::foo() . " but foo() can be implemented";
}
public static function bar()
{
return 'method bar() which was protected now can be public OR protected NOT private (see below)';
}
// That wouldn't work with private
// private static function bar()
// {
// return parent::bar() . ' I wanted to override it in child as a private but it is impossible';
// }
public static function getBaz()
{
return self::baz(); // you can still access it in child class, cause is protected
}
// public static function getBoo()
// {
// return self::boo(); // it won't work ass parent boo is private
// }
}
echo '<pre> OPERATORS' . PHP_EOL . PHP_EOL;
// operands
echo MyClass::myStaticMethod() . PHP_EOL;
echo MyClass::myNonStaticMethod() . PHP_EOL; // Warning In PHP 7, calling non-static methods statically is deprecated
$obj = new MyClass();
echo $obj->myStaticMethod() . PHP_EOL; // wouldn't use that
echo $obj::myStaticMethod() . PHP_EOL;
echo $obj->myNonStaticMethod() . PHP_EOL;
echo $obj::myNonStaticMethod() . PHP_EOL; // Warning In PHP 7, calling non-static methods statically is deprecated
echo PHP_EOL . PHP_EOL;
echo 'INHERITANCE WITH `abstract`' . PHP_EOL . PHP_EOL;
echo FormatHelper::foo() . PHP_EOL;
echo FormatHelper::bar() . PHP_EOL;
// echo FormatHelper::baz() . PHP_EOL; // you cannot access it as it's protected and final
echo FormatHelper::getBaz() . PHP_EOL;
Namespaces
Although this tip exceeds boundaries of this answer consider using namespaces in the future, especially, when your project will grow, so with proper autoload you will be able to use classes without requiring them each time like.
<?php
require_once('autoloader.php');
print_r(\Your\Namespace\FormatHelper::formatArrayIdValue(['foo bar baz'], 'foo');
print_r(\Other\Namespace\OtherHelper::format('foo');
print_r(\Quite\Other\Something\SomeHelper::someMethod());
Conventions
note, for the convention I renamed the classes and methods names, also added $id param as it was missing.
In the end, I figured the autoloader was not configured correctly, thus not loading the abstract Format class. Although I am still uncertain why manually requiring this file did not work, fixing the autoloader did solve it. Now onto the next set of errors.
Thank you lads for all of your input. :)
There are several questions about weird behaviour when a child has an implementation of a private parent method like in the following example:
class A {
private function toOverridePrivate() {
echo "private toOverridePrivate A\n";
}
public function callInA() {
$this->toOverridePrivate();
echo "\n";
}
}
class B extends A {
private function toOverridePrivate() {
echo "private toOverridePrivate B\n";
}
public function callInB() {
$this->toOverridePrivate();
echo "\n";
}
}
$a = new A;
$b = new B;
$a->callInA(); // private toOverridePrivate A
$b->callInA(); // private toOverridePrivate A
$b->callInB(); // private toOverridePrivate B
When calling $b->callPrintsInA(), As implementation of toOverridePrivate is called, because B::toOverridePrivate is not accessible from A.
In summary it can be said, that the method must be accessible from the scope it is called, therefore the following fails:
class A {
public function callInA() {
$this->toOverridePrivate();
echo "\n";
}
}
class B extends A {
private function toOverridePrivate() {
echo "private toOverridePrivate B\n";
}
}
$b = new B;
$b->callInA(); // ERROR: Call to private method B::toOverridePrivate() from context 'A'
In the last example toOverridePrivate is not accessible in the scope of A, despite the fact that $this is in fact an object of type B.
But one aspect which varies between answers is, if having another implementation of a private parent method in a child class in overriding. In the most popular question I could find "Strange behavior when overriding private methods" the accepted answer says it is overriding...
Now if you override the private method, its new scope will not be A, it will be B
...while the second most voted answer states that...
A private method is not overridable
... because it is unkown outside of its class scope.
The last statement is questionable, because declaring a private method final makes overriding it not possible with the exact message that it can not be overridden:
class A {
final private function someMethod() { }
}
class B extends A {
// Fatal error: Cannot override final method A::someMethod()
private function someMethod() { }
}
So...
Does a child class override a private parent method, if it has an implementation of that method? If this isn't overriding, what vocabulary would you use to describe it?
The child class simply has no knowledge of any private methods from parent class. Any such method in the scope of child class is undefined. It can't be overridden if it is not defined in the first place.
If the method is public it is visible to everyone and any code can call it. When the method is protected in is only known to the class itself and any of its descendants. When a method is private it is only known in the scope of this class. To any other class this method is simply undefined.
You can define another method with the same name in the child class, but it is not overriding anything. You could still call it overriding, but it would not make much sense. Overriding means that if you do not redefine the method in the child class it will call the definition from parent class. With private methods it is not possible because they are not accessible from the child class.
Consider this example:
class A {
private function toOverridePrivate() {
echo "private toOverridePrivate A\n";
}
}
class B extends A {
public function toOverridePrivate() {
parent::toOverridePrivate();
}
}
$o = new B;
$o->toOverridePrivate();
Fatal error: Uncaught Error: Call to private method A::toOverridePrivate() from context 'B'
Whether the class B defines method toOverridePrivate or not, it makes no difference, because the method toOverridePrivate from A is always inaccessible.
Is there any way to define abstract class properties in PHP?
abstract class Foo_Abstract {
abstract public $tablename;
}
class Foo extends Foo_Abstract {
//Foo must 'implement' $property
public $tablename = 'users';
}
There is no such thing as defining a property.
You can only declare properties because they are containers of data reserved in memory on initialization.
A function on the other hand can be declared (types, name, parameters) without being defined (function body missing) and thus, can be made abstract.
"Abstract" only indicates that something was declared but not defined and therefore before using it, you need to define it or it becomes useless.
No, there is no way to enforce that with the compiler, you'd have to use run-time checks (say, in the constructor) for the $tablename variable, e.g.:
class Foo_Abstract {
public final function __construct(/*whatever*/) {
if(!isset($this->tablename))
throw new LogicException(get_class($this) . ' must have a $tablename');
}
}
To enforce this for all derived classes of Foo_Abstract you would have to make Foo_Abstract's constructor final, preventing overriding.
You could declare an abstract getter instead:
abstract class Foo_Abstract {
abstract public function get_tablename();
}
class Foo extends Foo_Abstract {
protected $tablename = 'tablename';
public function get_tablename() {
return $this->tablename;
}
}
Depending on the context of the property, if I want to force declaration of an abstract class property in an extended class, I like to use a constant with the static keyword for the property in the abstract object constructor or setter/getter methods. You can optionally use final to prevent the method from being overridden in extended classes.
Example: https://3v4l.org/WH5Xl
abstract class AbstractFoo
{
public $bar;
final public function __construct()
{
$this->bar = static::BAR;
}
}
class Foo extends AbstractFoo
{
//const BAR = 'foobar'; //uncomment to prevent exception
}
$foo = new Foo();
//Fatal Error: Undefined class constant 'BAR'
However, the extended class overrides the parent class properties and methods if redefined.
For example; if a property is declared as protected in the parent and redefined as public in the extended class, the resulting property is public. Otherwise, if the property is declared private in the parent it will remain private and not available to the extended class.
http://www.php.net//manual/en/language.oop5.static.php
As stated above, there is no such exact definition.
I, however, use this simple workaround to force the child class to define the "abstract" property:
abstract class Father
{
public $name;
abstract protected function setName(); // now every child class must declare this
// function and thus declare the property
public function __construct()
{
$this->setName();
}
}
class Son extends Father
{
protected function setName()
{
$this->name = "son";
}
function __construct(){
parent::__construct();
}
}
The need for abstract properties can indicate design problems. While many of answers implement kind of Template method pattern and it works, it always looks kind of strange.
Let's take a look at the original example:
abstract class Foo_Abstract {
abstract public $tablename;
}
class Foo extends Foo_Abstract {
//Foo must 'implement' $property
public $tablename = 'users';
}
To mark something abstract is to indicate it a must-have thing. Well, a must-have value (in this case) is a required dependency, so it should be passed to the constructor during instantiation:
class Table
{
private $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function name(): string
{
return $this->name;
}
}
Then if you actually want a more concrete named class you can inherit like so:
final class UsersTable extends Table
{
public function __construct()
{
parent::__construct('users');
}
}
This can be useful if you use DI container and have to pass different tables for different objects.
I've asked myself the same question today, and I'd like to add my two cents.
The reason we would like abstract properties is to make sure that subclasses define them and throw exceptions when they don't. In my specific case, I needed something that could work with statically.
Ideally I would like something like this:
abstract class A {
abstract protected static $prop;
}
class B extends A {
protected static $prop = 'B prop'; // $prop defined, B loads successfully
}
class C extends A {
// throws an exception when loading C for the first time because $prop
// is not defined.
}
I ended up with this implementation
abstract class A
{
// no $prop definition in A!
public static final function getProp()
{
return static::$prop;
}
}
class B extends A
{
protected static $prop = 'B prop';
}
class C extends A
{
}
As you can see, in A I don't define $prop, but I use it in a static getter. Therefore, the following code works
B::getProp();
// => 'B prop'
$b = new B();
$b->getProp();
// => 'B prop'
In C, on the other hand, I don't define $prop, so I get exceptions:
C::getProp();
// => Exception!
$c = new C();
$c->getProp();
// => Exception!
I must call the getProp() method to get the exception and I can't get it on class loading, but it is quite close to the desired behavior, at least in my case.
I define getProp() as final to avoid that some smart guy (aka myself in 6 months) is tempted to do
class D extends A {
public static function getProp() {
// really smart
}
}
D::getProp();
// => no exception...
As you could have found out by just testing your code:
Fatal error: Properties cannot be declared abstract in ... on line 3
No, there is not. Properties cannot be declared abstract in PHP.
However you can implement a getter/setter function abstract, this might be what you're looking for.
Properties aren't implemented (especially public properties), they just exist (or not):
$foo = new Foo;
$foo->publicProperty = 'Bar';
PHP 7 makes it quite a bit easier for making abstract "properties". Just as above, you will make them by creating abstract functions, but with PHP 7 you can define the return type for that function, which makes things a lot easier when you're building a base class that anyone can extend.
<?php
abstract class FooBase {
abstract public function FooProp(): string;
abstract public function BarProp(): BarClass;
public function foo() {
return $this->FooProp();
}
public function bar() {
return $this->BarProp()->name();
}
}
class BarClass {
public function name() {
return 'Bar!';
}
}
class FooClass extends FooBase {
public function FooProp(): string {
return 'Foo!';
}
public function BarProp(): BarClass {
// This would not work:
// return 'not working';
// But this will!
return new BarClass();
}
}
$test = new FooClass();
echo $test->foo() . PHP_EOL;
echo $test->bar() . PHP_EOL;
if tablename value will never change during the object's lifetime, following will be a simple yet safe implementation.
abstract class Foo_Abstract {
abstract protected function getTablename();
public function showTableName()
{
echo 'my table name is '.$this->getTablename();
}
}
class Foo extends Foo_Abstract {
//Foo must 'implement' getTablename()
protected function getTablename()
{
return 'users';
}
}
the key here is that the string value 'users' is specified and returned directly in getTablename() in child class implementation. The function mimics a "readonly" property.
This is fairly similar to a solution posted earlier on which uses an additional variable. I also like Marco's solution though it can be a bit more complicated.
Just define the property in the base class without assigning it a (default) value.
Getting the property value without redefining it with a default value or assigning it a value will throw an Error.
<?php
class Base {
protected string $name;
public function i_am() : string {
return $this->name;
}
}
class Wrong extends Base {
...
}
class Good extends Base {
protected string $name = 'Somebody';
}
$test = new Good();
echo $test->i_am(), '<br>'; // Will show "Nobody"
$test = new Wrong();
echo $test->i_am(), '<br>'; // Will throw an Error:
// Error: Typed property Base::$name must not be accessed before initialization in ....
?>
You can define a static property in an abstract class.
<?php
abstract class Foo {
private static $bar = "1234";
public static function func() {
echo self::$bar;
}
}
Foo::func(); // It will be printed 1234
Too late to answer the question, but you may use the difference between self and static as follows
<?php
class A { // Base Class
protected static $name = 'ClassA';
public static function getSelfName() {
return self::$name;
}
public static function getStaticName() {
return static::$name;
}
}
class B extends A {
protected static $name = 'ClassB';
}
echo A::getSelfName(); // ClassA
echo A::getStaticName(); // ClassA
echo B::getSelfName(); // ClassA
echo B::getStaticName(); // ClassB
I'd like to implement the following using a private constructor.
The problem is that get_class() returns ParentBase; eventhough; get_called_class() returns ChildClass.
How can I have __construct() be called from the calling class context instead of the base class context?
There will be many child classes, so I only want one shared factory method, and I also want to make sure a child cannot be extended (so that it cannot be created with the new keyword).
Seems like there should be a way of making ChildClass::createObject() work with a private ChildClass constructor and a public ParentBase factory method.
<?php
class ParentBase
{
public static function createObject()
{
echo get_class() . "<br/>"; // prints ParentBase
echo get_called_class() . "<br/>"; // prints ChildClass
return new static();
}
}
class ChildClass extends ParentBase
{
private $greeting = "bye";
private function __construct()
{
$this->greeting = "hi";
}
public function greet()
{
echo $this->greeting;
}
}
$child = ChildClass::createObject();
$child->greet();
The output from the above is:
ParentBase
ChildClass
Fatal error: Call to private ChildClass::__construct() from context 'ParentBase'
Protected contstructor works:
http://codepad.viper-7.com/sCgJwA
Private constructor doesn't:
http://codepad.viper-7.com/YBs7Iz
That is an expected behavior createObject(); is a function of ParentBase, So it will return ParentBase from get_class() but, it was called from ChildClass So, it will return ChildClass from get_called_class().
And about the constructor, since the constructor is assigned as private, you restrict the object creation from within the class only. By making it protected, now Parent Class can create the object of ChildClass
Probable solution would be to override, the createObject() class in the ChildClass itself.
class ChildClass extends ParentBase
{
public static function createObject()
{
echo get_class() . "<br/>";
echo get_called_class() . "<br/>";
return new static();
}
}
Or, you could make the constructor protected, then you will make the constructor accessible to parent classes and restrict any sub classes of child classes making it final, thus making it accessible from parent class only.
The child constructor must be protected or public to my knowledge. I ran into a similar issue for a different problem, I was attempting to access a private property.
But for some reason your question "Can I call a private child constructor from a base factory method?" does not reflect your code so I suggest you edit that as I am troubling myself over how to answer this.
If this is PHP 5.4......
After playing around with some other things and reading up on PHP OOP. It looks like Traits can do this.
I like the use Trait notataion, which in this case makes it obvious that you should use the Factory to instantiate the class.
Using a Trait is advantageous in that it the Factory Trait can be shared across multiple Class hierarchies that do not have common lineages:
<?php
// NOTE: traits are only avaialable in ** PHP 5.4 **
trait Factory
{
public static function createObject()
{
echo get_class() . "<br/>"; // prints ChildClass
echo get_called_class() . "<br/>"; // prints ChildClass
return new static();
}
}
class ChildClass
{
use Factory;
private $greeting = "bye";
private function __construct()
{
$this->greeting = "hi";
}
public function greet()
{
echo $this->greeting;
}
}
$child = ChildClass::createObject();
$child->greet();
Working Code Example
Is there any way to define abstract class properties in PHP?
abstract class Foo_Abstract {
abstract public $tablename;
}
class Foo extends Foo_Abstract {
//Foo must 'implement' $property
public $tablename = 'users';
}
There is no such thing as defining a property.
You can only declare properties because they are containers of data reserved in memory on initialization.
A function on the other hand can be declared (types, name, parameters) without being defined (function body missing) and thus, can be made abstract.
"Abstract" only indicates that something was declared but not defined and therefore before using it, you need to define it or it becomes useless.
No, there is no way to enforce that with the compiler, you'd have to use run-time checks (say, in the constructor) for the $tablename variable, e.g.:
class Foo_Abstract {
public final function __construct(/*whatever*/) {
if(!isset($this->tablename))
throw new LogicException(get_class($this) . ' must have a $tablename');
}
}
To enforce this for all derived classes of Foo_Abstract you would have to make Foo_Abstract's constructor final, preventing overriding.
You could declare an abstract getter instead:
abstract class Foo_Abstract {
abstract public function get_tablename();
}
class Foo extends Foo_Abstract {
protected $tablename = 'tablename';
public function get_tablename() {
return $this->tablename;
}
}
Depending on the context of the property, if I want to force declaration of an abstract class property in an extended class, I like to use a constant with the static keyword for the property in the abstract object constructor or setter/getter methods. You can optionally use final to prevent the method from being overridden in extended classes.
Example: https://3v4l.org/WH5Xl
abstract class AbstractFoo
{
public $bar;
final public function __construct()
{
$this->bar = static::BAR;
}
}
class Foo extends AbstractFoo
{
//const BAR = 'foobar'; //uncomment to prevent exception
}
$foo = new Foo();
//Fatal Error: Undefined class constant 'BAR'
However, the extended class overrides the parent class properties and methods if redefined.
For example; if a property is declared as protected in the parent and redefined as public in the extended class, the resulting property is public. Otherwise, if the property is declared private in the parent it will remain private and not available to the extended class.
http://www.php.net//manual/en/language.oop5.static.php
As stated above, there is no such exact definition.
I, however, use this simple workaround to force the child class to define the "abstract" property:
abstract class Father
{
public $name;
abstract protected function setName(); // now every child class must declare this
// function and thus declare the property
public function __construct()
{
$this->setName();
}
}
class Son extends Father
{
protected function setName()
{
$this->name = "son";
}
function __construct(){
parent::__construct();
}
}
The need for abstract properties can indicate design problems. While many of answers implement kind of Template method pattern and it works, it always looks kind of strange.
Let's take a look at the original example:
abstract class Foo_Abstract {
abstract public $tablename;
}
class Foo extends Foo_Abstract {
//Foo must 'implement' $property
public $tablename = 'users';
}
To mark something abstract is to indicate it a must-have thing. Well, a must-have value (in this case) is a required dependency, so it should be passed to the constructor during instantiation:
class Table
{
private $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function name(): string
{
return $this->name;
}
}
Then if you actually want a more concrete named class you can inherit like so:
final class UsersTable extends Table
{
public function __construct()
{
parent::__construct('users');
}
}
This can be useful if you use DI container and have to pass different tables for different objects.
I've asked myself the same question today, and I'd like to add my two cents.
The reason we would like abstract properties is to make sure that subclasses define them and throw exceptions when they don't. In my specific case, I needed something that could work with statically.
Ideally I would like something like this:
abstract class A {
abstract protected static $prop;
}
class B extends A {
protected static $prop = 'B prop'; // $prop defined, B loads successfully
}
class C extends A {
// throws an exception when loading C for the first time because $prop
// is not defined.
}
I ended up with this implementation
abstract class A
{
// no $prop definition in A!
public static final function getProp()
{
return static::$prop;
}
}
class B extends A
{
protected static $prop = 'B prop';
}
class C extends A
{
}
As you can see, in A I don't define $prop, but I use it in a static getter. Therefore, the following code works
B::getProp();
// => 'B prop'
$b = new B();
$b->getProp();
// => 'B prop'
In C, on the other hand, I don't define $prop, so I get exceptions:
C::getProp();
// => Exception!
$c = new C();
$c->getProp();
// => Exception!
I must call the getProp() method to get the exception and I can't get it on class loading, but it is quite close to the desired behavior, at least in my case.
I define getProp() as final to avoid that some smart guy (aka myself in 6 months) is tempted to do
class D extends A {
public static function getProp() {
// really smart
}
}
D::getProp();
// => no exception...
As you could have found out by just testing your code:
Fatal error: Properties cannot be declared abstract in ... on line 3
No, there is not. Properties cannot be declared abstract in PHP.
However you can implement a getter/setter function abstract, this might be what you're looking for.
Properties aren't implemented (especially public properties), they just exist (or not):
$foo = new Foo;
$foo->publicProperty = 'Bar';
PHP 7 makes it quite a bit easier for making abstract "properties". Just as above, you will make them by creating abstract functions, but with PHP 7 you can define the return type for that function, which makes things a lot easier when you're building a base class that anyone can extend.
<?php
abstract class FooBase {
abstract public function FooProp(): string;
abstract public function BarProp(): BarClass;
public function foo() {
return $this->FooProp();
}
public function bar() {
return $this->BarProp()->name();
}
}
class BarClass {
public function name() {
return 'Bar!';
}
}
class FooClass extends FooBase {
public function FooProp(): string {
return 'Foo!';
}
public function BarProp(): BarClass {
// This would not work:
// return 'not working';
// But this will!
return new BarClass();
}
}
$test = new FooClass();
echo $test->foo() . PHP_EOL;
echo $test->bar() . PHP_EOL;
if tablename value will never change during the object's lifetime, following will be a simple yet safe implementation.
abstract class Foo_Abstract {
abstract protected function getTablename();
public function showTableName()
{
echo 'my table name is '.$this->getTablename();
}
}
class Foo extends Foo_Abstract {
//Foo must 'implement' getTablename()
protected function getTablename()
{
return 'users';
}
}
the key here is that the string value 'users' is specified and returned directly in getTablename() in child class implementation. The function mimics a "readonly" property.
This is fairly similar to a solution posted earlier on which uses an additional variable. I also like Marco's solution though it can be a bit more complicated.
Just define the property in the base class without assigning it a (default) value.
Getting the property value without redefining it with a default value or assigning it a value will throw an Error.
<?php
class Base {
protected string $name;
public function i_am() : string {
return $this->name;
}
}
class Wrong extends Base {
...
}
class Good extends Base {
protected string $name = 'Somebody';
}
$test = new Good();
echo $test->i_am(), '<br>'; // Will show "Nobody"
$test = new Wrong();
echo $test->i_am(), '<br>'; // Will throw an Error:
// Error: Typed property Base::$name must not be accessed before initialization in ....
?>
You can define a static property in an abstract class.
<?php
abstract class Foo {
private static $bar = "1234";
public static function func() {
echo self::$bar;
}
}
Foo::func(); // It will be printed 1234
Too late to answer the question, but you may use the difference between self and static as follows
<?php
class A { // Base Class
protected static $name = 'ClassA';
public static function getSelfName() {
return self::$name;
}
public static function getStaticName() {
return static::$name;
}
}
class B extends A {
protected static $name = 'ClassB';
}
echo A::getSelfName(); // ClassA
echo A::getStaticName(); // ClassA
echo B::getSelfName(); // ClassA
echo B::getStaticName(); // ClassB