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
Related
I've setup an abstract class that must execute methods from another instanciated class.
So I setup an anonymous class that extends my abstract class and in the implemented method, I want to access the methods of that "other instanciated class", but for my IDE to help me to find the correct name of the methods and arguments, I must write somewhere the type.
abstract class blah
{
protected object $reference;
public function __construct(object $reference)
{
$this->reference = $reference;
$this->hello();
}
abstract public function hello();
}
class world
{
public function __construct()
{
new class($this) extends blah {
public function hello()
{
$this->reference->display();
}
};
}
public function display()
{
echo 'hello world';
}
}
new world();
I can't change the type from "object" to something else to allow my IDE to show the methods
public function __construct()
{
new class($this) extends blah {
protected world $reference;
public function hello()
{
$this->reference->display();
}
};
}
this throws
Fatal error: Type of blah#anonymous::$reference must be object (as in class blah)
I could copy the attribute "reference" to another variable and set a typehint to "world", but that's not the best
public function __construct()
{
new class($this) extends blah {
protected world $reference;
public function hello()
{
/** #var world $hello */
$hello = $this->reference;
$hello->display();
}
};
}
what better solution do I have ?
edit: this seems to work, is it the best way ?
new class($this) extends blah {
/** #var world $reference */
protected object $reference;
public function hello()
{
$this->reference->display();
}
};
It's worth understanding why PHP is telling you that you can't change the property type.
Since this is a protected property, it can be read and written by either the parent or child class. For instance, the parent class might have methods like this, which would be inherited by the child class:
public function getReference(): object {
return $this->reference;
}
public function setReference(object $newReference): void {
$this->reference = $newReference;
}
public function setReferenceToDefault(): void {
$this->reference = new SomethingBoring;
}
If you specify world as the type of $reference in a child class, the getReference method will work fine - any instance of world is an object, so the return type is OK. But the setReference() and setReferenceToDefault() methods would fail, because they try to assign something that isn't of type world.
Essentially, by declaring a protected property on the base class, you are setting a contract for all child classes, and PHP is enforcing it for you. The technical term for this is "contravariance": a child class can be more generous in what it accepts, but it can't reject values the parent would accept. The opposite is "covariance", which applies to returning values: the child class can be more specific about what it will return, but can't return something the parent class never would. When something is both input and output, you get both restrictions, so can't vary the type at all.
Since you don't actually use the property on the base class, this restriction isn't actually needed, so in this case one option is simply not to define the property or constructor on the base class. In other cases, you might want to use traits, which are "compiler assisted copy-and-paste": they don't assert any relationship between two classes, but they save you writing the same code more than once.
If you want it only for IDE, then use documentation block to overwrite type declaration (if IDE supports it):
/**
* #property world $reference
*/
new class($this) extends blah {
public function hello()
{
$this->reference->display();
}
};
E.g. PHPStorm, while not supporting that for anonymous class, supports for normal class description:
I've already read Why does PHP 5.2+ disallow abstract static class methods? and How to force an implementation of a protected static function - the second is very similar to my case - but I am still without answer. Basically, I want to assure, that every child of my abstract class has implementation of protected static method, without implementing it as this has no meaning and because of lack of key informations there. Also, it must be static (because caller method is static and it has no context) and protected (so I cannot use interface, and I do not want anyone to call it directly), and it will be called by late static binding. Any ideas?
Dummy code below to illuminate my case:
abstract class BaseClass {
public static function foo() {
// some common stuff
static::bar();
// rest of common stuff
}
public function whoooaaa($condition) {
if ($condition) {
AClass::foo();
} else {
BClass::foo();
}
}
}
class AClass extends BaseClass {
protected static function bar() {
// do something
}
}
class BClass extends BaseClass {
protected static function bar() {
// do something else
}
}
// end somewhere else in my code, two constructions, both used:
AClass::foo();
// ....
$baseClassInheritedInstance->whoooaaa($variableCondition);
My only solution, ugly one, is to implement dummy protected static method in base class and throw a generic exception, so that it must be implemented by inheritance.
You can add a static factory that will fill context for casual objects.
class Factory() {
public static getObject($condition) {
$object = $condition ? new A() : new B();
// you can fill context here and/or use singleton/cache
return $object;
}
}
abstract class Base {
abstract function concreteMethod();
}
class A extends Base {...}
class B extends Base {...}
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
<?php
class A{
//many properties
protected $myProperty1;
protected $myProperty2;
protected $myProperty3;
public function __construct(){
$this->myProperty1='some value';
$this->myProperty2='some value';
$this->myProperty3='some value';
}
public function getProperty1(){
return $this->myProperty1;
}
public function getProperty2(){
return $this->myProperty2;
}
public function getProperty3(){
return $this->myProperty3;
}
//edited: I added some setters, meaning that the object returned from the functions may already have these properties altered
public function setProperty1($p){
$this->myProperty1=$p;
}
public function setProperty2($p){
$this->myProperty2=$p;
}
public function setProperty3($p){
$this->myProperty3=$p;
}
}
class B extends A{
private $myProperty4;
public function __construct(A $a){
$this=$a; //this line has error,it says $this cannot be re-assigned
$this->myProperty4='some value';
}
public function getProperty4(){
return $this->myProperty4;
}
}
//$a = new A();
$a = someClass::getAById(1234); //edited: $a is returned by a function (I cannot modify it)
$b= new B($a); //error
?>
I'd like to create a B's object by passing an A's object to B's constructor, as you can see, I cannot re-assign the $this variable. I am not allowed to modify class A, when there are many properties in A, it'd be tedious for me to do things like this in B's constructor:
public function __construct(A $a){
parent::__construct();
$this->myProperty1=$a->getProperty1();
$this->myProperty2=$a->getProperty2();
$this->myProperty3=$a->getProperty3();
$this->myProperty4='some value';
}
My question is that, how can I safely create an object of class B using an A's object with minimal amount of coding?
class A
{
public $property = 'Foobar';
}
class B extends A
{
public function __construct()
{
echo $this->property; // Foobar
}
}
Am I missing something? It sounds like you're trying to force OOP to do something it's not intended to do, or you're having trouble understanding inheritance.
Every public or protected method and property from class A is available in class B. Either by directly referencing it (as in my example) or by using the parent:: syntax.
EDIT
(Author clarified question)
If class A's properties are accessible, you could use something like the following to copy them down to class B
class B
{
public function __construct()
{
$a = new A(); // Or however A is instantiated
foreach(get_object_vars($a) as $key => $value)
{
$this->$key = $value;
}
}
}
Since B extends A, why not just create B to begin with? If you need to initialize some extra properties, you can over-ride the constructor like this:
class B extends A {
public function __construct(){
parent::__construct(); //calls A's constructor
$this->Bproperty='somevalue';
}
}
If that's not good enough, then you might want to look at Reflection.
In PHP, can I specify an interface to have fields, or are PHP interfaces limited to functions?
<?php
interface IFoo
{
public $field;
public function DoSomething();
public function DoSomethingElse();
}
?>
If not, I realize I can expose a getter as a function in the interface:
public GetField();
You cannot specify members. You have to indicate their presence through getters and setters, just like you did. However, you can specify constants:
interface IFoo
{
const foo = 'bar';
public function DoSomething();
}
See http://www.php.net/manual/en/language.oop5.interfaces.php
Late answer, but to get the functionality wanted here, you might want to consider an abstract class containing your fields. The abstract class would look like this:
abstract class Foo
{
public $member;
}
While you could still have the interface:
interface IFoo
{
public function someFunction();
}
Then you have your child class like this:
class bar extends Foo implements IFoo
{
public function __construct($memberValue = "")
{
// Set the value of the member from the abstract class
$this->member = $memberValue;
}
public function someFunction()
{
// Echo the member from the abstract class
echo $this->member;
}
}
There's an alternative solution for those still curious and interested. :)
Use getter setter. But this might be tedious to implement many getters and setters in many classes, and it clutter class code. And you repeat yourself!
As of PHP 5.4 you can use traits to provide fields and methods to classes, ie:
interface IFoo
{
public function DoSomething();
public function DoSomethingElse();
public function setField($value);
public function getField();
}
trait WithField
{
private $_field;
public function setField($value)
{
$this->_field = $value;
}
public function getField()
{
return $this->field;
}
}
class Bar implements IFoo
{
use WithField;
public function DoSomething()
{
echo $this->getField();
}
public function DoSomethingElse()
{
echo $this->setField('blah');
}
}
This is specially usefull if you have to inherit from some base class and need to implement some interface.
class CooCoo extends Bird implements IFoo
{
use WithField;
public function DoSomething()
{
echo $this->getField();
}
public function DoSomethingElse()
{
echo $this->setField('blah');
}
}
Interfaces are only designed to support methods.
This is because interfaces exist to provide a public API that can then be accessed by other objects.
Publicly accessible properties would actually violate encapsulation of data within the class that implements the interface.
You cannot specify properties in an interface : only methods are allowed (and make sense, as the goal of an interface is to specify an API)
In PHP, trying to define properties in an interface should raise a Fatal Error : this portion of code :
interface A {
public $test;
}
Will give you :
Fatal error: Interfaces may not include member variables in...