⌂ Home ▲ Previous: Objects ▼ Next: Anonymous classes
In object-oriented programming, inheritance is the mechanism of basing an object or class upon another object (prototype-based inheritance) or class (class-based inheritance), retaining similar implementation. It is also defined as deriving new classes (sub classes) from existing ones such as super class or base class and then forming them into a hierarchy of classes. In most class-based object-oriented languages like C++, an object created through inheritance, a child object, acquires all the properties and behaviors of the parent object, with the exception of: constructors, destructors, overloaded operators and friend functions of the base class. Inheritance allows programmers to create classes that are built upon existing classes, to specify a new implementation while maintaining the same behaviors (realizing an interface), to reuse code and to independently extend original software via public classes and interfaces. The relationships of objects or classes through inheritance give rise to a directed acyclic graph.
An inherited class is called a subclass of its parent class or super class. The term inheritance is loosely used for both class-based and prototype-based programming, but in narrow use the term is reserved for class-based programming (one class inherits from another), with the corresponding technique in prototype-based programming being instead called delegation (one object delegates to another). Class-modifying inheritance patterns can be pre-defined according to simple network interface parameters such that inter-language compatibility is preserved.
[PHP supports class-based inheritance. – KK]
Inheritance is a well-established programming principle, and PHP makes use of this principle in its object model. This principle will affect the way many classes and objects relate to one another.
For example, when extending a class, the subclass inherits all of the public and protected methods, properties and constants from the parent class. Unless a class overrides those methods, they will retain their original functionality.
This is useful for defining and abstracting functionality, and permits the implementation of additional functionality in similar objects without the need to reimplement all of the shared functionality.
Example: Inheritance example
<?php
class Foo
{
public function printItem($string)
{
echo 'Foo: ' . $string . PHP_EOL;
}
public function printPHP()
{
echo 'PHP is great.' . PHP_EOL;
}
}
class Bar extends Foo
{
public function printItem($string)
{
echo 'Bar: ' . $string . PHP_EOL;
}
}
$foo = new Foo();
$bar = new Bar();
$foo->printItem('baz'); // Output: 'Foo: baz'
$foo->printPHP(); // Output: 'PHP is great'
$bar->printItem('baz'); // Output: 'Bar: baz'
$bar->printPHP(); // Output: 'PHP is great'
?>
Output of the above example in PHP 8.5.0:
Foo: baz
PHP is great.
Bar: baz
PHP is great.
Example: Simple class inheritance
<?php
class SimpleClass
{
function displayVar()
{
echo "Parent class\n";
}
}
class ExtendClass extends SimpleClass
{
// Redefine the parent method
function displayVar()
{
echo "Extending class\n";
parent::displayVar();
}
}
$extended = new ExtendClass();
$extended->displayVar();
?>
The above example will output:
Extending class
Parent class
Example: Class inheritance
<?php
class SomeBaseClass
{
public const SOME_PUBLIC_CONSTANT = 'public constant';
protected const SOME_PROTECTED_CONSTANT = 'protected constant';
private const SOME_PRIVATE_CONSTANT = 'private constant';
public $somePublicProperty = 'public property';
protected $someProtectedProperty = 'protected property';
private $somePrivateProperty = 'private property';
public function somePublicMethod()
{
return 'public method';
}
protected function someProtectedMethod()
{
return 'protected method';
}
private function somePrivateMethod()
{
return 'private method';
}
public function someMethod()
{
print(
'# ' . __METHOD__
. "\n\n* private:\n"
. self::SOME_PRIVATE_CONSTANT . PHP_EOL
. $this->somePrivateProperty . PHP_EOL
. $this->somePrivateMethod() . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedClass extends SomeBaseClass
{
public function otherMethod()
{
$this->someMethod();
print(
'#' . __METHOD__
. "\n\n* protected:\n"
. self::SOME_PROTECTED_CONSTANT . PHP_EOL
. $this->someProtectedProperty . PHP_EOL
. $this->someProtectedMethod() . PHP_EOL
. "\n* public:\n"
. self::SOME_PUBLIC_CONSTANT . PHP_EOL
. $this->somePublicProperty . PHP_EOL
. $this->somePublicMethod() . PHP_EOL
. PHP_EOL
);
}
}
$someObject = new SomeDerivedClass();
print(
"# Global scope:\n\n"
. SomeDerivedClass::SOME_PUBLIC_CONSTANT . PHP_EOL
. $someObject->somePublicProperty . PHP_EOL
. $someObject->somePublicMethod() . PHP_EOL
. PHP_EOL
);
$someObject->otherMethod();
Result (PHP 8.4):
# Global scope:
public constant
public property
public method
# SomeBaseClass::someMethod
* private:
private constant
private property
private method
#SomeDerivedClass::otherMethod
* protected:
protected constant
protected property
protected method
* public:
public constant
public property
public method
Source code: Example
extendskeyword
A class can inherit the constants, methods, and properties of another class by using the keyword extends in the class declaration. It is not possible to extend multiple classes; a class can only inherit from one base class.
Example: Extending class
<?php
class SomeClass
{
public string $someProperty = 'base';
}
class OtherClass extends SomeClass
{
public string $otherProperty = 'derived';
}
$someObject = new OtherClass();
print('Class: ' . get_class($someObject) . PHP_EOL);
print('Base class: ' . get_parent_class($someObject) . PHP_EOL);
print("Base class property: {$someObject->someProperty}\n");
print("Derived class property: {$someObject->otherProperty}\n");
Result (PHP 8.4):
Class: OtherClass
Base class: SomeClass
Base class property: base
Derived class property: derived
Source code: Example
Note:
Unless autoloading is used, the classes must be defined before they are used. If a class extends another, then the parent class must be declared before the child class structure. This rule applies to classes that inherit other classes and interfaces.
Example: Base and derived class
<?php
class Mammal
{
public string $classTaxon = "Mammalia";
}
class Fox extends Mammal
{
public string $speciesTaxon = "Vulpes vulpes";
public string $name;
public function show() : void
{
print("Hi, my name is {$this->name}.\n"
. "Class: {$this->classTaxon}\n"
. "Species: {$this->speciesTaxon}\n"
);
}
}
$foxFerdinand = new Fox();
$foxFerdinand->name = "Ferdinand";
$foxFerdinand->show();
Result (PHP 8.4):
Hi, my name is Ferdinand.
Class: Mammalia
Species: Vulpes vulpes
Source code: Example
Example: Multilevel inheritance
<?php
class Mammal
{
public bool $isDomesticated = false;
public bool $hasTail = false;
public static bool $isMilkFeeded = true;
public static string $classTaxon = "Mammalia";
}
class Fox extends Mammal
{
public string $name;
public static bool $hasFur = true;
public static string $speciesTaxon = "Vulpes vulpes";
public function __construct()
{
$this->hasTail = true;
$this->isDomesticated = false;
}
public function show(): void
{
print("Hi, my name is " . $this->name
. ".\nClass: " . self::$classTaxon
. "\nSpecies: " . self::$speciesTaxon
. "\nAm I milk-feeded as a cub? " . self::$isMilkFeeded
. "\nDo I have tail? " . $this->hasTail
. "\nDo I have fur? " . self::$hasFur
. "\nAm I domesticated? " . $this->isDomesticated
. "\n"
);
}
}
class PetFox extends Fox
{
public function __construct()
{
$this->isDomesticated = true;
}
}
$foxFerdinand = new Fox();
$foxFerdinand->name = "Ferdinand";
$foxFerdinand->show();
print("\n");
$foxAgnes = new PetFox();
$foxAgnes->name = "Agnes";
$foxAgnes->show();
print("\n");
Result (PHP 8.4):
Hi, my name is Ferdinand.
Class: Mammalia
Species: Vulpes vulpes
Am I milk-feeded as a cub? 1
Do I have tail? 1
Do I have fur? 1
Am I domesticated?
Hi, my name is Agnes.
Class: Mammalia
Species: Vulpes vulpes
Am I milk-feeded as a cub? 1
Do I have tail?
Do I have fur? 1
Am I domesticated? 1
Source code: Example
Private methods of a parent class are not accessible to a child class. As a result, child classes may reimplement a private method themselves without regard for normal inheritance rules. Prior to PHP 8.0.0, however, final and static restrictions were applied to private methods. As of PHP 8.0.0, the only private method restriction that is enforced is private final constructors, as that is a common way to “disable” the constructor when using static factory methods instead.
Example: Class inheritance and members visibility
<?php
class SomeBaseClass
{
public const SOME_PUBLIC_CONSTANT = 'public';
protected const SOME_PROTECTED_CONSTANT = 'protected';
private const SOME_PRIVATE_CONSTANT = 'private';
public $somePublicProperty = 'public';
protected $someProtectedProperty = 'protected';
private $somePrivateProperty = 'private';
public function somePublicMethod()
{
return 'public';
}
protected function someProtectedMethod()
{
return 'protected';
}
private function somePrivateMethod()
{
return 'private';
}
public function someMethod()
{
print(
__METHOD__
. "\n* constants:\n"
. self::SOME_PUBLIC_CONSTANT . PHP_EOL
. self::SOME_PROTECTED_CONSTANT . PHP_EOL
. self::SOME_PRIVATE_CONSTANT . PHP_EOL
. "\n* properties:\n"
. $this->somePublicProperty . PHP_EOL
. $this->someProtectedProperty . PHP_EOL
. $this->somePrivateProperty . PHP_EOL
. "\n* methods:\n"
. $this->somePublicMethod() . PHP_EOL
. $this->someProtectedMethod() . PHP_EOL
. $this->somePrivateMethod(). PHP_EOL
. PHP_EOL
);
}
public function anotherMethod(): void
{
print(__METHOD__ . PHP_EOL);
}
}
class SomeDerivedClass extends SomeBaseClass
{
function otherMethod()
{
print(
__METHOD__
. "\n* constants:\n"
. self::SOME_PUBLIC_CONSTANT . PHP_EOL
. self::SOME_PROTECTED_CONSTANT . PHP_EOL
. "\n* properties:\n"
. $this->somePublicProperty . PHP_EOL
. $this->someProtectedProperty . PHP_EOL
. "\n* methods:\n"
. $this->somePublicMethod() . PHP_EOL
. $this->someProtectedMethod() . PHP_EOL
. PHP_EOL
);
}
public function anotherMethod(): void
{
parent::anotherMethod();
print(__METHOD__ . PHP_EOL);
}
}
$someObject = new SomeDerivedClass();
print(
"# Global scope:\n"
. "\n* constants:\n"
. SomeDerivedClass::SOME_PUBLIC_CONSTANT . PHP_EOL
. "\n* properties:\n"
. $someObject->somePublicProperty . PHP_EOL
. "\n* methods:\n"
. $someObject->somePublicMethod() . PHP_EOL
. PHP_EOL
);
$someObject->someMethod();
$someObject->otherMethod();
$someObject->anotherMethod();
print(PHP_EOL);
Result (PHP 8.4):
# Global scope:
* constants:
public
* properties:
public
* methods:
public
SomeBaseClass::someMethod
* constants:
public
protected
private
* properties:
public
protected
private
* methods:
public
protected
private
SomeDerivedClass::otherMethod
* constants:
public
protected
* properties:
public
protected
* methods:
public
protected
SomeBaseClass::anotherMethod
SomeDerivedClass::anotherMethod
Source code: Example
Example: Class members visibility modifiers
<?php
class Mammal
{
public bool $isDomesticated;
protected bool $hasTail;
private bool $isMilkFeeded = true;
private string $classTaxon = "Mammalia";
}
class Fox extends Mammal
{
public string $name;
public function __construct()
{
$this->hasTail = true;
$this->isDomesticated = false;
}
public function show() : void
{
print("Hi, my name is {$this->name}\n"
. "Species: {$this->speciesTaxon}\n"
. "Do I have tail? {$this->hasTail}\n"
. "Do I have fur? {$this->hasFur}\n"
. "Am I domesticated? {$this->isDomesticated}\n\n"
);
}
private bool $hasFur = true;
private string $speciesTaxon = "Vulpes vulpes";
}
class UrbanFox extends Fox
{
public function display() : void
{
print("Hi, my name is {$this->name}\n"
. "Do I have tail? {$this->hasTail}\n"
. "Am I domesticated? {$this->isDomesticated}\n\n"
);
}
}
$foxFerdinand = new Fox();
$foxFerdinand->name = "Ferdinand";
$foxFerdinand->isDomesticated = true;
$foxFerdinand->show();
print("Hi, my name is {$foxFerdinand->name}\n"
. "Am I domesticated? {$foxFerdinand->isDomesticated}\n\n"
);
$foxMelody = new UrbanFox();
$foxMelody->name = "Melody";
$foxMelody->display();
Result (PHP 8.4):
Hi, my name is Ferdinand
Species: Vulpes vulpes
Do I have tail? 1
Do I have fur? 1
Am I domesticated? 1
Hi, my name is Ferdinand
Am I domesticated? 1
Hi, my name is Melody
Do I have tail? 1
Am I domesticated?
Source code: Example
In software systems, encapsulation refers to the bundling of data with the mechanisms or methods that operate on the data. It may also refer to the limiting of direct access to some of that data, such as an object’s components. Essentially, encapsulation prevents external code from being concerned with the internal workings of an object.
Encapsulation allows developers to present a consistent interface that is independent of its internal implementation. As one example, encapsulation can be used to hide the values or state of a structured data object inside a class. This prevents clients from directly accessing this information in a way that could expose hidden implementation details or violate state invariance maintained by the methods.
Encapsulation also encourages programmers to put all the code that is concerned with a certain set of data in the same class, which organizes it for easy comprehension by other programmers. Encapsulation is a technique that encourages decoupling.
All object-oriented programming (OOP) systems support encapsulation, but encapsulation is not unique to OOP. Implementations of abstract data types, modules, and libraries also offer encapsulation. The similarity has been explained by programming language theorists in terms of existential types.
In object-oriented programming languages, and other related fields, encapsulation refers to one of two related but distinct notions, and sometimes to the combination thereof:
- A language mechanism for restricting direct access to some of the object’s components.
- A language construct that facilitates the bundling of data with the methods (or other functions) operating on those data.
Example: Class encapsulation
<?php
class Account
{
public string $login = "";
public string $email = "";
public bool $isActive = false;
public int $id = 0;
protected const CONNECTIONS_MAX_NUMBER = 10;
protected array $connections = [];
private int $connectionsNumber = 0;
public function display(): void
{
print("ID: " . $this->id . "\n"
. "Login: " . $this->login . "\n"
. "E-mail: " . $this->email . "\n"
. "Is active: " . $this->isActive . "\n"
. "Has connections: " . $this->hasConnections() . "\n"
);
}
public function hasConnections(): bool
{
return ($this->connectionsNumber > 0);
}
public function addConnection(int $connectedAccountId): bool
{
if ($this->connectionsNumber == self::CONNECTIONS_MAX_NUMBER) {
return false;
}
array_push($this->connections, $connectedAccountId);
$this->connectionsNumber++;
return true;
}
}
class SocialMediaAccount extends Account
{
public function isFriend(int $checkingAccountId): bool
{
foreach ($this->connections as $connectionAccountId) {
if ($checkingAccountId == $connectionAccountId) {
return true;
}
}
return false;
}
}
$timothy = new SocialMediaAccount();
$timothy->login = "tim";
$timothy->email = "timothy.muppetone@gmail.com";
$timothy->isActive = true;
$timothy->display();
print(PHP_EOL);
$timothy->addConnection(100);
print("Has connections: {$timothy->hasConnections()}\n");
print("Is ID 100 a friend? {$timothy->isFriend(100)}\n");
print("Is ID 200 a friend? {$timothy->isFriend(200)}\n");
print(PHP_EOL);
Result (PHP 8.4):
ID: 0
Login: tim
E-mail: timothy.muppetone@gmail.com
Is active: 1
Has connections:
Has connections: 1
Is ID 100 a friend? 1
Is ID 200 a friend?
Source code: Example
In programming language theory and type theory, polymorphism allows a value or variable to have more than one type and allows a given operation to be performed on values of more than one type.
In object-oriented programming, polymorphism is the provision of one interface to entities of different data types. The concept is borrowed from a principle in biology in which an organism or species can have many different forms or stages.
The most commonly recognized major forms of polymorphism are:
- Ad hoc polymorphism: defines a common interface for an arbitrary set of individually specified types.
- Parametric polymorphism: does not specify concrete types and instead uses abstract symbols that can substitute for any type.
- Subtyping (also called subtype polymorphism or inclusion polymorphism): when a name denotes instances of many different classes related by a common superclass.
Example: Polimorphism
<?php
class Value
{
public float $value = 0;
public string $label;
public function __construct(float $value, string $label = "")
{
$this->value = $value;
$this->label = $label;
}
public function show() : void
{
print($this->label . ": " . $this->value . "\n");
}
}
class Datum extends Value
{
public string $description;
public function __construct(float $value, string $label, string $description = "")
{
$this->value = $value;
$this->label = $label;
$this->description = $description;
}
public function show() : void
{
print(
"Value: " . $this->value
. "\nLabel: " . $this->label
. "\nDescription: " . $this->description . "\n"
);
}
}
function display(Value $value): void
{
$value->show();
}
$temperature = new Datum(24, "Temperature in degree Celsius", "The Central European summer day temperature");
display($temperature);
Result (PHP 8.4):
Value: 24
Label: Temperature in degree Celsius
Description: The Central European summer day temperature
Source code: Example
The inherited constants, methods, and properties can be overridden by redeclaring them with the same name defined in the parent class. However, if the parent class has defined a method or constant as final, they may not be overridden. It is possible to access the overridden [constants, – KK] methods or static properties by referencing them with parent::.
Note: As of PHP 8.1.0, constants may be declared as final.
Example: Class constant overriding with visibility
<?php
class SomeBaseClass
{
public const SOME_PUBLIC_CONSTANT = 'base public';
protected const SOME_PROTECTED_CONSTANT = 'base protected';
private const SOME_PRIVATE_CONSTANT = 'base private';
public function baseContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'self::SOME_PRIVATE_CONSTANT : ' . self::SOME_PRIVATE_CONSTANT . PHP_EOL
// Cannot be called without error in the derived class:
// . 'static::SOME_PRIVATE_CONSTANT : ' . static::SOME_PRIVATE_CONSTANT . PHP_EOL
// Private members are unaccessible in the derived classes.
. "\n* protected:\n\n"
. 'self::SOME_PROTECTED_CONSTANT : ' . self::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'static::SOME_PROTECTED_CONSTANT : ' . static::SOME_PROTECTED_CONSTANT . PHP_EOL
. "\n* public:\n\n"
. 'self::SOME_PUBLIC_CONSTANT : ' . self::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'static::SOME_PUBLIC_CONSTANT : ' . static::SOME_PUBLIC_CONSTANT . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedClass extends SomeBaseClass
{
public function derivedContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. 'parent::SOME_PROTECTED_CONSTANT : ' . parent::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'self::SOME_PROTECTED_CONSTANT : ' . self::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'static::SOME_PROTECTED_CONSTANT : ' . static::SOME_PROTECTED_CONSTANT . PHP_EOL
. "\n* public:\n\n"
. 'parent::SOME_PUBLIC_CONSTANT : ' . parent::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'self::SOME_PUBLIC_CONSTANT : ' . self::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'static::SOME_PUBLIC_CONSTANT : ' . static::SOME_PUBLIC_CONSTANT . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedOverridingClass extends SomeBaseClass
{
public const SOME_PUBLIC_CONSTANT = 'derived public';
protected const SOME_PROTECTED_CONSTANT = 'derived protected';
private const SOME_PRIVATE_CONSTANT = 'derived shadowed private'; // It's not overriding but rather shadowing!
// It's completly new constant - very own constant of the derived class
// because private member of the base class is unaccessible and not visible for the derived class.
public function derivedOverridingContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'self::SOME_PRIVATE_CONSTANT : ' . self::SOME_PRIVATE_CONSTANT . PHP_EOL
// This will be dangerous in case of further inheritance:
. 'static::SOME_PRIVATE_CONSTANT : ' . static::SOME_PRIVATE_CONSTANT . PHP_EOL
. "\n* protected:\n\n"
. 'parent::SOME_PROTECTED_CONSTANT : ' . parent::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'self::SOME_PROTECTED_CONSTANT : ' . self::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'static::SOME_PROTECTED_CONSTANT : ' . static::SOME_PROTECTED_CONSTANT . PHP_EOL
. "\n* public:\n\n"
. 'parent::SOME_PUBLIC_CONSTANT : ' . parent::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'self::SOME_PUBLIC_CONSTANT : ' . self::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'static::SOME_PUBLIC_CONSTANT : ' . static::SOME_PUBLIC_CONSTANT . PHP_EOL
. PHP_EOL
);
}
}
$someObject = new SomeDerivedClass();
print("# SomeDerivedClass:\n\n");
$someObject->baseContext();
$someObject->derivedContext();
$otherObject = new SomeDerivedOverridingClass();
print("# SomeDerivedOverridingClass:\n\n");
$otherObject->baseContext();
$otherObject->derivedOverridingContext();
Result (PHP 8.4):
# SomeDerivedClass:
SomeBaseClass::baseContext
* private:
self::SOME_PRIVATE_CONSTANT : base private
* protected:
self::SOME_PROTECTED_CONSTANT : base protected
static::SOME_PROTECTED_CONSTANT : base protected
* public:
self::SOME_PUBLIC_CONSTANT : base public
static::SOME_PUBLIC_CONSTANT : base public
SomeDerivedClass::derivedContext
* protected:
parent::SOME_PROTECTED_CONSTANT : base protected
self::SOME_PROTECTED_CONSTANT : base protected
static::SOME_PROTECTED_CONSTANT : base protected
* public:
parent::SOME_PUBLIC_CONSTANT : base public
self::SOME_PUBLIC_CONSTANT : base public
static::SOME_PUBLIC_CONSTANT : base public
# SomeDerivedOverridingClass:
SomeBaseClass::baseContext
* private:
self::SOME_PRIVATE_CONSTANT : base private
* protected:
self::SOME_PROTECTED_CONSTANT : base protected
static::SOME_PROTECTED_CONSTANT : derived protected
* public:
self::SOME_PUBLIC_CONSTANT : base public
static::SOME_PUBLIC_CONSTANT : derived public
SomeDerivedOverridingClass::derivedOverridingContext
* private:
self::SOME_PRIVATE_CONSTANT : derived shadowed private
static::SOME_PRIVATE_CONSTANT : derived shadowed private
* protected:
parent::SOME_PROTECTED_CONSTANT : base protected
self::SOME_PROTECTED_CONSTANT : derived protected
static::SOME_PROTECTED_CONSTANT : derived protected
* public:
parent::SOME_PUBLIC_CONSTANT : base public
self::SOME_PUBLIC_CONSTANT : derived public
static::SOME_PUBLIC_CONSTANT : derived public
Source code: Example
Example: Class property overriding with visibility
<?php
class SomeBaseClass
{
public $somePublicProperty = 'base public';
protected $someProtectedProperty = 'base protected';
private $somePrivateProperty = 'base private';
public function baseContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. '$this->somePrivateProperty : ' . $this->somePrivateProperty . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedProperty : ' . $this->someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicProperty : ' . $this->somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedClass extends SomeBaseClass
{
public function derivedContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedProperty : ' . $this->someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicProperty : ' . $this->somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedOverridingClass extends SomeBaseClass
{
public $somePublicProperty = 'derived public';
protected $someProtectedProperty = 'derived protected';
private $somePrivateProperty = 'derived shadowed private'; // It's not overriding but rather shadowing!
// It's completly new property - very own property of the derived class
// because private member of the base class is unaccessible and not visible for the derived class.
public function derivedOverridingContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. '$this->somePrivateProperty : ' . $this->somePrivateProperty . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedProperty : ' . $this->someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicProperty : ' . $this->somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
$someObject = new SomeDerivedClass();
print("# SomeDerivedClass:\n\n");
$someObject->baseContext();
$someObject->derivedContext();
$otherObject = new SomeDerivedOverridingClass();
print("# SomeDerivedOverridingClass:\n\n");
$otherObject->baseContext();
$otherObject->derivedOverridingContext();
Result (PHP 8.4):
# SomeDerivedClass:
SomeBaseClass::baseContext
* private:
$this->somePrivateProperty : base private
* protected:
$this->someProtectedProperty : base protected
* public:
$this->somePublicProperty : base public
SomeDerivedClass::derivedContext
* protected:
$this->someProtectedProperty : base protected
* public:
$this->somePublicProperty : base public
# SomeDerivedOverridingClass:
SomeBaseClass::baseContext
* private:
$this->somePrivateProperty : base private
* protected:
$this->someProtectedProperty : derived protected
* public:
$this->somePublicProperty : derived public
SomeDerivedOverridingClass::derivedOverridingContext
* private:
$this->somePrivateProperty : derived shadowed private
* protected:
$this->someProtectedProperty : derived protected
* public:
$this->somePublicProperty : derived public
Source code: Example
Example: Class static property overriding with visibility
<?php
class SomeBaseClass
{
public static $somePublicProperty = 'base static public';
protected static $someProtectedProperty = 'base static protected';
private static $somePrivateProperty = 'base static private';
public function baseContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'self::$somePrivateProperty : ' . self::$somePrivateProperty . PHP_EOL
// Cannot be called without error in the derived class:
// . 'static::$somePrivateProperty : ' . static::$somePrivateProperty . PHP_EOL
// Private members are unaccessible in the derived classes.
. "\n* protected:\n\n"
. 'self::$someProtectedProperty : ' . self::$someProtectedProperty . PHP_EOL
. 'static::$someProtectedProperty : ' . static::$someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. 'self::$somePublicProperty : ' . self::$somePublicProperty . PHP_EOL
. 'static::$somePublicProperty : ' . static::$somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedClass extends SomeBaseClass
{
public function derivedContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. 'parent::$someProtectedProperty : ' . parent::$someProtectedProperty . PHP_EOL
. 'self::$someProtectedProperty : ' . self::$someProtectedProperty . PHP_EOL
. 'static::$someProtectedProperty : ' . static::$someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. 'parent::$somePublicProperty : ' . parent::$somePublicProperty . PHP_EOL
. 'self::$somePublicProperty : ' . self::$somePublicProperty . PHP_EOL
. 'static::$somePublicProperty : ' . static::$somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedOverridingClass extends SomeBaseClass
{
public static $somePublicProperty = 'derived static public';
protected static $someProtectedProperty = 'derived static protected';
private static $somePrivateProperty = 'derived shadowed static private'; // It's not overriding but rather shadowing!
// It's completly new property - very own property of the derived class
// because private member of the base class is unaccessible and not visible for the derived class.
public function derivedOverridingContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'self::$somePrivateProperty : ' . self::$somePrivateProperty . PHP_EOL
// This will be dangerous in case of further inheritance:
. 'static::$somePrivateProperty : ' . static::$somePrivateProperty . PHP_EOL
. "\n* protected:\n\n"
. 'parent::$someProtectedProperty : ' . parent::$someProtectedProperty . PHP_EOL
. 'self::$someProtectedProperty : ' . self::$someProtectedProperty . PHP_EOL
. 'static::$someProtectedProperty : ' . static::$someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. 'parent::$somePublicProperty : ' . parent::$somePublicProperty . PHP_EOL
. 'self::$somePublicProperty : ' . self::$somePublicProperty . PHP_EOL
. 'static::$somePublicProperty : ' . static::$somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
$someObject = new SomeDerivedClass();
print("# SomeDerivedClass:\n\n");
$someObject->baseContext();
$someObject->derivedContext();
$otherObject = new SomeDerivedOverridingClass();
print("# SomeDerivedOverridingClass:\n\n");
$otherObject->baseContext();
$otherObject->derivedOverridingContext();
Result (PHP 8.4):
# SomeDerivedClass:
SomeBaseClass::baseContext
* private:
self::$somePrivateProperty : base static private
* protected:
self::$someProtectedProperty : base static protected
static::$someProtectedProperty : base static protected
* public:
self::$somePublicProperty : base static public
static::$somePublicProperty : base static public
SomeDerivedClass::derivedContext
* protected:
parent::$someProtectedProperty : base static protected
self::$someProtectedProperty : base static protected
static::$someProtectedProperty : base static protected
* public:
parent::$somePublicProperty : base static public
self::$somePublicProperty : base static public
static::$somePublicProperty : base static public
# SomeDerivedOverridingClass:
SomeBaseClass::baseContext
* private:
self::$somePrivateProperty : base static private
* protected:
self::$someProtectedProperty : base static protected
static::$someProtectedProperty : derived static protected
* public:
self::$somePublicProperty : base static public
static::$somePublicProperty : derived static public
SomeDerivedOverridingClass::derivedOverridingContext
* private:
self::$somePrivateProperty : derived shadowed static private
static::$somePrivateProperty : derived shadowed static private
* protected:
parent::$someProtectedProperty : base static protected
self::$someProtectedProperty : derived static protected
static::$someProtectedProperty : derived static protected
* public:
parent::$somePublicProperty : base static public
self::$somePublicProperty : derived static public
static::$somePublicProperty : derived static public
Source code: Example
Example: Class dynamic method overriding with visibility
<?php
class SomeBaseClass
{
public function somePublicMethod()
{
return 'base public';
}
protected function someProtectedMethod()
{
return 'base protected';
}
private function somePrivateMethod()
{
return 'base private';
}
public function baseContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'self::somePrivateMethod() : ' . self::somePrivateMethod() . PHP_EOL
// Cannot be called without error in the derived class:
// . 'static::somePrivateMethod() : ' . static::somePrivateMethod() . PHP_EOL
// Private members are unaccessible in the derived classes.
. '$this->somePrivateMethod() : ' . $this->somePrivateMethod() . PHP_EOL
. "\n* protected:\n\n"
. 'self::someProtectedMethod() : ' . self::someProtectedMethod() . PHP_EOL
. 'static::someProtectedMethod() : ' . static::someProtectedMethod() . PHP_EOL
. '$this->someProtectedMethod() : ' . $this->someProtectedMethod() . PHP_EOL
. "\n* public:\n\n"
. 'self::somePublicMethod() : ' . self::somePublicMethod() . PHP_EOL
. 'static::somePublicMethod() : ' . static::somePublicMethod() . PHP_EOL
. '$this->somePublicMethod() : ' . $this->somePublicMethod() . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedClass extends SomeBaseClass
{
public function derivedContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. 'parent::someProtectedMethod() : ' . parent::someProtectedMethod() . PHP_EOL
. 'self::someProtectedMethod() : ' . self::someProtectedMethod() . PHP_EOL
. 'static::someProtectedMethod() : ' . static::someProtectedMethod() . PHP_EOL
. '$this->someProtectedMethod() : ' . $this->someProtectedMethod() . PHP_EOL
. "\n* public:\n\n"
. 'parent::somePublicMethod() : ' . parent::somePublicMethod() . PHP_EOL
. 'self::somePublicMethod() : ' . self::somePublicMethod() . PHP_EOL
. 'static::somePublicMethod() : ' . static::somePublicMethod() . PHP_EOL
. '$this->somePublicMethod() : ' . $this->somePublicMethod() . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedOverridingClass extends SomeBaseClass
{
public function somePublicMethod()
{
return 'derived public';
}
protected function someProtectedMethod()
{
return 'derived protected';
}
private function somePrivateMethod() // It's not overriding but rather shadowing!
// It's completly new method - very own method of the derived class
// because private member of the base class is unaccessible and not visible for the derived class.
{
return 'derived shadowed private';
}
public function derivedOverridingContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'self::somePrivateMethod() : ' . self::somePrivateMethod() . PHP_EOL
// This will be dangerous in case of further inheritance:
. 'static::somePrivateMethod() : ' . static::somePrivateMethod() . PHP_EOL
. '$this->somePrivateMethod() : ' . $this->somePrivateMethod() . PHP_EOL
. "\n* protected:\n\n"
. 'parent::someProtectedMethod() : ' . parent::someProtectedMethod() . PHP_EOL
. 'self::someProtectedMethod() : ' . self::someProtectedMethod() . PHP_EOL
. 'static::someProtectedMethod() : ' . static::someProtectedMethod() . PHP_EOL
. '$this->someProtectedMethod() : ' . $this->someProtectedMethod() . PHP_EOL
. "\n* public:\n\n"
. 'parent::somePublicMethod() : ' . parent::somePublicMethod() . PHP_EOL
. 'self::somePublicMethod() : ' . self::somePublicMethod() . PHP_EOL
. 'static::somePublicMethod() : ' . static::somePublicMethod() . PHP_EOL
. '$this->somePublicMethod() : ' . $this->somePublicMethod() . PHP_EOL
. PHP_EOL
);
}
}
$someObject = new SomeDerivedClass();
print("# SomeDerivedClass:\n\n");
$someObject->baseContext();
$someObject->derivedContext();
$otherObject = new SomeDerivedOverridingClass();
print("# SomeDerivedOverridingClass:\n\n");
$otherObject->baseContext();
$otherObject->derivedOverridingContext();
Result (PHP 8.4):
# SomeDerivedClass:
SomeBaseClass::baseContext
* private:
self::somePrivateMethod() : base private
$this->somePrivateMethod() : base private
* protected:
self::someProtectedMethod() : base protected
static::someProtectedMethod() : base protected
$this->someProtectedMethod() : base protected
* public:
self::somePublicMethod() : base public
static::somePublicMethod() : base public
$this->somePublicMethod() : base public
SomeDerivedClass::derivedContext
* protected:
parent::someProtectedMethod() : base protected
self::someProtectedMethod() : base protected
static::someProtectedMethod() : base protected
$this->someProtectedMethod() : base protected
* public:
parent::somePublicMethod() : base public
self::somePublicMethod() : base public
static::somePublicMethod() : base public
$this->somePublicMethod() : base public
# SomeDerivedOverridingClass:
SomeBaseClass::baseContext
* private:
self::somePrivateMethod() : base private
$this->somePrivateMethod() : base private
* protected:
self::someProtectedMethod() : base protected
static::someProtectedMethod() : derived protected
$this->someProtectedMethod() : derived protected
* public:
self::somePublicMethod() : base public
static::somePublicMethod() : derived public
$this->somePublicMethod() : derived public
SomeDerivedOverridingClass::derivedOverridingContext
* private:
self::somePrivateMethod() : derived shadowed private
static::somePrivateMethod() : derived shadowed private
$this->somePrivateMethod() : derived shadowed private
* protected:
parent::someProtectedMethod() : base protected
self::someProtectedMethod() : derived protected
static::someProtectedMethod() : derived protected
$this->someProtectedMethod() : derived protected
* public:
parent::somePublicMethod() : base public
self::somePublicMethod() : derived public
static::somePublicMethod() : derived public
$this->somePublicMethod() : derived public
Source code: Example
<?php
class SomeBaseClass
{
public static function somePublicStaticMethod()
{
return 'base public';
}
protected static function someProtectedStaticMethod()
{
return 'base protected';
}
private static function somePrivateStaticMethod()
{
return 'base private';
}
public function baseContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'self::somePrivateStaticMethod() : ' . self::somePrivateStaticMethod() . PHP_EOL
// Cannot be called without error in the derived class:
// . 'static::somePrivateStaticMethod() : ' . static::somePrivateStaticMethod() . PHP_EOL
// Private members are unaccessible in the derived classes.
. '$this->somePrivateStaticMethod() : ' . $this->somePrivateStaticMethod() . PHP_EOL
. "\n* protected:\n\n"
. 'self::someProtectedStaticMethod() : ' . self::someProtectedStaticMethod() . PHP_EOL
. 'static::someProtectedStaticMethod() : ' . static::someProtectedStaticMethod() . PHP_EOL
. '$this->someProtectedStaticMethod() : ' . $this->someProtectedStaticMethod() . PHP_EOL
. "\n* public:\n\n"
. 'self::somePublicStaticMethod() : ' . self::somePublicStaticMethod() . PHP_EOL
. 'static::somePublicStaticMethod() : ' . static::somePublicStaticMethod() . PHP_EOL
. '$this->somePublicStaticMethod() : ' . $this->somePublicStaticMethod() . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedClass extends SomeBaseClass
{
public function derivedContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. 'parent::someProtectedStaticMethod() : ' . parent::someProtectedStaticMethod() . PHP_EOL
. 'self::someProtectedStaticMethod() : ' . self::someProtectedStaticMethod() . PHP_EOL
. 'static::someProtectedStaticMethod() : ' . static::someProtectedStaticMethod() . PHP_EOL
. '$this->someProtectedStaticMethod() : ' . $this->someProtectedStaticMethod() . PHP_EOL
. "\n* public:\n\n"
. 'parent::somePublicStaticMethod() : ' . parent::somePublicStaticMethod() . PHP_EOL
. 'self::somePublicStaticMethod() : ' . self::somePublicStaticMethod() . PHP_EOL
. 'static::somePublicStaticMethod() : ' . static::somePublicStaticMethod() . PHP_EOL
. '$this->somePublicStaticMethod() : ' . $this->somePublicStaticMethod() . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedOverridingClass extends SomeBaseClass
{
public static function somePublicStaticMethod()
{
return 'derived public';
}
protected static function someProtectedStaticMethod()
{
return 'derived protected';
}
private static function somePrivateStaticMethod()
{
return 'derived shadowed private';
} // It's not overriding but rather shadowing!
// It's completly new method - very own method of the derived class
// because private member of the base class is unaccessible and not visible for the derived class.
public static function derivedOverridingDynamicContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'self::somePrivateStaticMethod() : ' . self::somePrivateStaticMethod() . PHP_EOL
// This will be dangerous in case of further inheritance:
. 'static::somePrivateStaticMethod() : ' . static::somePrivateStaticMethod() . PHP_EOL
. "\n* protected:\n\n"
. 'parent::someProtectedStaticMethod() : ' . parent::someProtectedStaticMethod() . PHP_EOL
. 'self::someProtectedStaticMethod() : ' . self::someProtectedStaticMethod() . PHP_EOL
. 'static::someProtectedStaticMethod() : ' . static::someProtectedStaticMethod() . PHP_EOL
. "\n* public:\n\n"
. 'parent::somePublicStaticMethod() : ' . parent::somePublicStaticMethod() . PHP_EOL
. 'self::somePublicStaticMethod() : ' . self::somePublicStaticMethod() . PHP_EOL
. 'static::somePublicStaticMethod() : ' . static::somePublicStaticMethod() . PHP_EOL
. PHP_EOL
);
}
}
$someObject = new SomeDerivedClass();
print("# SomeDerivedClass:\n\n");
$someObject->baseContext();
$someObject->derivedContext();
$otherObject = new SomeDerivedOverridingClass();
print("# SomeDerivedOverridingClass:\n\n");
$otherObject->baseContext();
$otherObject->derivedOverridingContext();
Result (PHP 8.4):
# SomeDerivedClass:
SomeBaseClass::baseContext
* private:
self::somePrivateMethod() : base private
$this->somePrivateMethod() : base private
* protected:
self::someProtectedMethod() : base protected
static::someProtectedMethod() : base protected
$this->someProtectedMethod() : base protected
* public:
self::somePublicMethod() : base public
static::somePublicMethod() : base public
$this->somePublicMethod() : base public
SomeDerivedClass::derivedContext
* protected:
parent::someProtectedMethod() : base protected
self::someProtectedMethod() : base protected
static::someProtectedMethod() : base protected
$this->someProtectedMethod() : base protected
* public:
parent::somePublicMethod() : base public
self::somePublicMethod() : base public
static::somePublicMethod() : base public
$this->somePublicMethod() : base public
# SomeDerivedOverridingClass:
SomeBaseClass::baseContext
* private:
self::somePrivateMethod() : base private
$this->somePrivateMethod() : base private
* protected:
self::someProtectedMethod() : base protected
static::someProtectedMethod() : derived protected
$this->someProtectedMethod() : derived protected
* public:
self::somePublicMethod() : base public
static::somePublicMethod() : derived public
$this->somePublicMethod() : derived public
SomeDerivedOverridingClass::derivedOverridingContext
* private:
self::somePrivateMethod() : derived shadowed private
static::somePrivateMethod() : derived shadowed private
$this->somePrivateMethod() : derived shadowed private
* protected:
parent::someProtectedMethod() : base protected
self::someProtectedMethod() : derived protected
static::someProtectedMethod() : derived protected
$this->someProtectedMethod() : derived protected
* public:
parent::somePublicMethod() : base public
self::somePublicMethod() : derived public
static::somePublicMethod() : derived public
$this->somePublicMethod() : derived public
Source code: Example
Example: Class inheritance and method overriding
<?php
class SomeBaseClass
{
public function someMethod()
{
print("Some method from base class\n");
}
public function otherMethod()
{
print("Other method from base class\n");
}
}
class SomeDerivedClass extends SomeBaseClass
{
public function otherMethod()
{
parent::otherMethod();
print("Other method from derived class\n");
}
}
$someObject = new SomeDerivedClass();
$someObject->someMethod();
$someObject->otherMethod();
Result (PHP 8.4):
Some method from base class
Other method from base class
Other method from derived class
Source code: Example
A child class may define or redefine individual hooks on a property by redefining the property and just the hooks it wishes to override. A child class may also add hooks to a property that had none. This is essentially the same as if the hooks were methods.
Example: Hook inheritance
<?php
class Point
{
public int $x;
public int $y;
}
class PositivePoint extends Point
{
public int $x {
set {
if ($value < 0) {
throw new \InvalidArgumentException('Too small');
}
$this->x = $value;
}
}
}
?>
Each hook overrides parent implementations independently of each other. If a child class adds hooks, any default value set on the property is removed, and must be redeclared. That is the same consistent with how inheritance works on hook-less properties.
Example: Class property hook overriding with visibility
<?php
class SomeBaseClass
{
public $somePublicProperty = 'base public' {
get => $this->somePublicProperty . ' base hook';
}
protected $someProtectedProperty = 'base protected' {
get => $this->someProtectedProperty . ' base hook';
}
private $somePrivateProperty = 'base private' {
get => $this->somePrivateProperty . ' base hook';
}
public function baseContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. '$this->somePrivateProperty : ' . $this->somePrivateProperty . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedProperty : ' . $this->someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicProperty : ' . $this->somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedClass extends SomeBaseClass
{
public function derivedContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedProperty : ' . $this->someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicProperty : ' . $this->somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedOverridingClass extends SomeBaseClass
{
public $somePublicProperty = 'derived public' {
get => $this->somePublicProperty . ' derived hook';
}
protected $someProtectedProperty = 'derived protected' {
get => $this->someProtectedProperty . ' derived hook';
}
private $somePrivateProperty = 'derived private' {
get => $this->somePrivateProperty . ' derived hook';
} // It's not overriding but rather shadowing!
// It's completly new property hook - very own property hook of the derived class
// because private member of the base class is unaccessible and not visible for the derived class.
public function derivedOverridingContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedProperty : ' . $this->someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicProperty : ' . $this->somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
$someObject = new SomeDerivedClass();
print("# SomeDerivedClass:\n\n");
$someObject->baseContext();
$someObject->derivedContext();
$otherObject = new SomeDerivedOverridingClass();
print("# SomeDerivedOverridingClass:\n\n");
$otherObject->baseContext();
$otherObject->derivedOverridingContext();
Result (PHP 8.4):
# SomeDerivedClass:
SomeBaseClass::baseContext
* private:
$this->somePrivateProperty : base private base hook
* protected:
$this->someProtectedProperty : base protected base hook
* public:
$this->somePublicProperty : base public base hook
SomeDerivedClass::derivedContext
* protected:
$this->someProtectedProperty : base protected base hook
* public:
$this->somePublicProperty : base public base hook
# SomeDerivedOverridingClass:
SomeBaseClass::baseContext
* private:
$this->somePrivateProperty : base private base hook
* protected:
$this->someProtectedProperty : derived protected derived hook
* public:
$this->somePublicProperty : derived public derived hook
SomeDerivedOverridingClass::derivedOverridingContext
* protected:
$this->someProtectedProperty : derived protected derived hook
* public:
$this->somePublicProperty : derived public derived hook
Source code: Example
finalkeyword
The final keyword prevents child classes from overriding a method, property, or constant by prefixing the definition with final. If the class itself is being defined final then it cannot be extended.
Example: Final constants example as of PHP 8.1.0
<?php
class Foo
{
final public const X = "foo";
}
class Bar extends Foo
{
public const X = "bar";
}
// Fatal error: Bar::X cannot override final constant Foo::X
?>
Example: Class final constant
<?php
class SomeBaseClass
{
public const SOME_PUBLIC_CONSTANT = 'base public';
final public const SOME_FINAL_PUBLIC_CONSTANT = 'base final public';
protected const SOME_PROTECTED_CONSTANT = 'base protected';
final protected const SOME_FINAL_PROTECTED_CONSTANT = 'base final protected';
private const SOME_PRIVATE_CONSTANT = 'base private';
public function baseContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'self::SOME_PRIVATE_CONSTANT : ' . self::SOME_PRIVATE_CONSTANT . PHP_EOL
. "\n* protected:\n\n"
. 'self::SOME_PROTECTED_CONSTANT : ' . self::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'self::SOME_FINAL_PROTECTED_CONSTANT : ' . self::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. "\n* public:\n\n"
. 'self::SOME_PUBLIC_CONSTANT : ' . self::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'self::SOME_FINAL_PUBLIC_CONSTANT : ' . self::SOME_FINAL_PUBLIC_CONSTANT . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedClass extends SomeBaseClass
{
public function derivedContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. 'parent::SOME_PROTECTED_CONSTANT : ' . parent::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'self::SOME_PROTECTED_CONSTANT : ' . self::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'parent::SOME_FINAL_PROTECTED_CONSTANT : ' . parent::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. 'self::SOME_FINAL_PROTECTED_CONSTANT : ' . self::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. "\n* public:\n\n"
. 'parent::SOME_PUBLIC_CONSTANT : ' . parent::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'self::SOME_PUBLIC_CONSTANT : ' . self::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'parent::SOME_FINAL_PUBLIC_CONSTANT : ' . parent::SOME_FINAL_PUBLIC_CONSTANT . PHP_EOL
. 'self::SOME_FINAL_PUBLIC_CONSTANT : ' . self::SOME_FINAL_PUBLIC_CONSTANT . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedOverridingClass extends SomeBaseClass
{
public const SOME_PUBLIC_CONSTANT = 'derived public';
protected const SOME_PROTECTED_CONSTANT = 'derived protected';
public function derivedOverridingContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. 'parent::SOME_PROTECTED_CONSTANT : ' . parent::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'self::SOME_PROTECTED_CONSTANT : ' . self::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'parent::SOME_FINAL_PROTECTED_CONSTANT : ' . parent::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. 'self::SOME_FINAL_PROTECTED_CONSTANT : ' . self::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. "\n* public:\n\n"
. 'parent::SOME_PUBLIC_CONSTANT : ' . parent::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'self::SOME_PUBLIC_CONSTANT : ' . self::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'parent::SOME_FINAL_PUBLIC_CONSTANT : ' . parent::SOME_FINAL_PUBLIC_CONSTANT . PHP_EOL
. 'self::SOME_FINAL_PUBLIC_CONSTANT : ' . self::SOME_FINAL_PUBLIC_CONSTANT . PHP_EOL
. PHP_EOL
);
}
}
$someObject = new SomeDerivedClass();
print("# SomeDerivedClass:\n\n");
$someObject->baseContext();
$someObject->derivedContext();
$otherObject = new SomeDerivedOverridingClass();
print("# SomeDerivedOverridingClass:\n\n");
$otherObject->baseContext();
$otherObject->derivedOverridingContext();
Result (PHP 8.4):
# SomeDerivedClass:
SomeBaseClass::baseContext
* private:
self::SOME_PRIVATE_CONSTANT : base private
* protected:
self::SOME_PROTECTED_CONSTANT : base protected
self::SOME_FINAL_PROTECTED_CONSTANT : base final protected
* public:
self::SOME_PUBLIC_CONSTANT : base public
self::SOME_FINAL_PUBLIC_CONSTANT : base final public
SomeDerivedClass::derivedContext
* protected:
parent::SOME_PROTECTED_CONSTANT : base protected
self::SOME_PROTECTED_CONSTANT : base protected
parent::SOME_FINAL_PROTECTED_CONSTANT : base final protected
self::SOME_FINAL_PROTECTED_CONSTANT : base final protected
* public:
parent::SOME_PUBLIC_CONSTANT : base public
self::SOME_PUBLIC_CONSTANT : base public
parent::SOME_FINAL_PUBLIC_CONSTANT : base final public
self::SOME_FINAL_PUBLIC_CONSTANT : base final public
# SomeDerivedOverridingClass:
SomeBaseClass::baseContext
* private:
self::SOME_PRIVATE_CONSTANT : base private
* protected:
self::SOME_PROTECTED_CONSTANT : base protected
self::SOME_FINAL_PROTECTED_CONSTANT : base final protected
* public:
self::SOME_PUBLIC_CONSTANT : base public
self::SOME_FINAL_PUBLIC_CONSTANT : base final public
SomeDerivedOverridingClass::derivedOverridingContext
* protected:
parent::SOME_PROTECTED_CONSTANT : base protected
self::SOME_PROTECTED_CONSTANT : derived protected
parent::SOME_FINAL_PROTECTED_CONSTANT : base final protected
self::SOME_FINAL_PROTECTED_CONSTANT : base final protected
* public:
parent::SOME_PUBLIC_CONSTANT : base public
self::SOME_PUBLIC_CONSTANT : derived public
parent::SOME_FINAL_PUBLIC_CONSTANT : base final public
self::SOME_FINAL_PUBLIC_CONSTANT : base final public
Source code: Example
Note: A property that is declared private(set) is implicitly final.
Example: Final property example as of PHP 8.4.0
<?php
class BaseClass {
final protected string $test;
}
class ChildClass extends BaseClass {
public string $test;
}
// Results in Fatal error: Cannot override final property BaseClass::$test
?>
Example: Class final property
<?php
class SomeBaseClass
{
public $somePublicProperty = 'base public';
final public $someFinalPublicProperty = 'base final public';
protected $someProtectedProperty = 'base protected';
final protected $someFinalProtectedProperty = 'base final protected';
private $somePrivateProperty = 'base private';
public function baseContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. '$this->somePrivateProperty : ' . $this->somePrivateProperty . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedProperty : ' . $this->someProtectedProperty . PHP_EOL
. '$this->someFinalProtectedProperty : ' . $this->someFinalProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicProperty : ' . $this->somePublicProperty . PHP_EOL
. '$this->someFinalPublicProperty : ' . $this->someFinalPublicProperty . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedClass extends SomeBaseClass
{
public function derivedContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedProperty : ' . $this->someProtectedProperty . PHP_EOL
. '$this->someFinalProtectedProperty : ' . $this->someFinalProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicProperty : ' . $this->somePublicProperty . PHP_EOL
. '$this->someFinalPublicProperty : ' . $this->someFinalPublicProperty . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedOverridingClass extends SomeBaseClass
{
public const SOME_PUBLIC_CONSTANT = 'derived public';
protected const SOME_PROTECTED_CONSTANT = 'derived protected';
public function derivedOverridingContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedProperty : ' . $this->someProtectedProperty . PHP_EOL
. '$this->someFinalProtectedProperty : ' . $this->someFinalProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicProperty : ' . $this->somePublicProperty . PHP_EOL
. '$this->someFinalPublicProperty : ' . $this->someFinalPublicProperty . PHP_EOL
. PHP_EOL
);
}
}
$someObject = new SomeDerivedClass();
print("# SomeDerivedClass:\n\n");
$someObject->baseContext();
$someObject->derivedContext();
$otherObject = new SomeDerivedOverridingClass();
print("# SomeDerivedOverridingClass:\n\n");
$otherObject->baseContext();
$otherObject->derivedOverridingContext();
Result (PHP 8.4):
# SomeDerivedClass:
SomeBaseClass::baseContext
* private:
$this->somePrivateProperty : base private
* protected:
$this->someProtectedProperty : base protected
$this->someFinalProtectedProperty : base final protected
* public:
$this->somePublicProperty : base public
$this->someFinalPublicProperty : base final public
SomeDerivedClass::derivedContext
* protected:
$this->someProtectedProperty : base protected
$this->someFinalProtectedProperty : base final protected
* public:
$this->somePublicProperty : base public
$this->someFinalPublicProperty : base final public
# SomeDerivedOverridingClass:
SomeBaseClass::baseContext
* private:
$this->somePrivateProperty : base private
* protected:
$this->someProtectedProperty : base protected
$this->someFinalProtectedProperty : base final protected
* public:
$this->somePublicProperty : base public
$this->someFinalPublicProperty : base final public
SomeDerivedOverridingClass::derivedOverridingContext
* protected:
$this->someProtectedProperty : base protected
$this->someFinalProtectedProperty : base final protected
* public:
$this->somePublicProperty : base public
$this->someFinalPublicProperty : base final public
Source code: Example
Note: As of PHP 8.0.0, private methods may not be declared final except for the constructor.
Example: Final methods example
<?php
class BaseClass {
public function test() {
echo "BaseClass::test() called\n";
}
final public function moreTesting() {
echo "BaseClass::moreTesting() called\n";
}
}
class ChildClass extends BaseClass {
public function moreTesting() {
echo "ChildClass::moreTesting() called\n";
}
}
// Results in Fatal error: Cannot override final method BaseClass::moreTesting()
?>
Example: Class final method
<?php
class SomeBaseClass
{
public function somePublicMethod()
{
return 'base public';
}
final public function someFinalPublicMethod()
{
return 'base final public';
}
protected function someProtectedMethod()
{
return 'base protected';
}
final protected function someFinalProtectedMethod()
{
return 'base final protected';
}
private function somePrivateMethod()
{
return 'base private';
}
public function baseContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. '$this->somePrivateMethod() : ' . $this->somePrivateMethod() . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedMethod() : ' . $this->someProtectedMethod() . PHP_EOL
. '$this->someFinalProtectedMethod() : ' . $this->someFinalProtectedMethod() . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicMethod() : ' . $this->somePublicMethod() . PHP_EOL
. '$this->someFinalPublicMethod() : ' . $this->someFinalPublicMethod() . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedClass extends SomeBaseClass
{
public function derivedContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. 'parent::someProtectedMethod() : ' . parent::someProtectedMethod() . PHP_EOL
. 'self::someProtectedMethod() : ' . self::someProtectedMethod() . PHP_EOL
. 'parent::someFinalProtectedMethod() : ' . parent::someFinalProtectedMethod() . PHP_EOL
. 'self::someFinalProtectedMethod() : ' . self::someFinalProtectedMethod() . PHP_EOL
. "\n* public:\n\n"
. 'parent::somePublicMethod() : ' . parent::somePublicMethod() . PHP_EOL
. 'self::somePublicMethod() : ' . self::somePublicMethod() . PHP_EOL
. 'parent::someFinalPublicMethod() : ' . parent::someFinalPublicMethod() . PHP_EOL
. 'self::someFinalPublicMethod() : ' . self::someFinalPublicMethod() . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedOverridingClass extends SomeBaseClass
{
public function somePublicMethod()
{
return 'derived public';
}
protected function someProtectedMethod()
{
return 'derived protected';
}
public function derivedOverridingContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. 'parent::someProtectedMethod() : ' . parent::someProtectedMethod() . PHP_EOL
. 'self::someProtectedMethod() : ' . self::someProtectedMethod() . PHP_EOL
. 'parent::someFinalProtectedMethod() : ' . parent::someFinalProtectedMethod() . PHP_EOL
. 'self::someFinalProtectedMethod() : ' . self::someFinalProtectedMethod() . PHP_EOL
. "\n* public:\n\n"
. 'parent::somePublicMethod() : ' . parent::somePublicMethod() . PHP_EOL
. 'self::somePublicMethod() : ' . self::somePublicMethod() . PHP_EOL
. 'parent::someFinalPublicMethod() : ' . parent::someFinalPublicMethod() . PHP_EOL
. 'self::someFinalPublicMethod() : ' . self::someFinalPublicMethod() . PHP_EOL
. PHP_EOL
);
}
}
$someObject = new SomeDerivedClass();
print("# SomeDerivedClass:\n\n");
$someObject->baseContext();
$someObject->derivedContext();
$otherObject = new SomeDerivedOverridingClass();
print("# SomeDerivedOverridingClass:\n\n");
$otherObject->baseContext();
$otherObject->derivedOverridingContext();
Result (PHP 8.4):
# SomeDerivedClass:
SomeBaseClass::baseContext
* private:
$this->somePrivateMethod() : base private
* protected:
$this->someProtectedMethod() : base protected
$this->someFinalProtectedMethod() : base final protected
* public:
$this->somePublicMethod() : base public
$this->someFinalPublicMethod() : base final public
SomeDerivedClass::derivedContext
* protected:
parent::someProtectedMethod() : base protected
self::someProtectedMethod() : base protected
parent::someFinalProtectedMethod() : base final protected
self::someFinalProtectedMethod() : base final protected
* public:
parent::somePublicMethod() : base public
self::somePublicMethod() : base public
parent::someFinalPublicMethod() : base final public
self::someFinalPublicMethod() : base final public
# SomeDerivedOverridingClass:
SomeBaseClass::baseContext
* private:
$this->somePrivateMethod() : base private
* protected:
$this->someProtectedMethod() : derived protected
$this->someFinalProtectedMethod() : base final protected
* public:
$this->somePublicMethod() : derived public
$this->someFinalPublicMethod() : base final public
SomeDerivedOverridingClass::derivedOverridingContext
* protected:
parent::someProtectedMethod() : base protected
self::someProtectedMethod() : derived protected
parent::someFinalProtectedMethod() : base final protected
self::someFinalProtectedMethod() : base final protected
* public:
parent::somePublicMethod() : base public
self::somePublicMethod() : derived public
parent::someFinalPublicMethod() : base final public
self::someFinalPublicMethod() : base final public
Source code: Example
Hooks may also be declared final, in which case they may not be overridden.
Example: Final hooks
<?php
class User
{
public string $username {
final set => strtolower($value);
}
}
class Manager extends User
{
public string $username {
// This is allowed
get => strtoupper($this->username);
// But this is NOT allowed, because set is final in the parent.
set => strtoupper($value);
}
}
?>
A property may also be declared final. A final property may not be redeclared by a child class in any way, which precludes altering hooks or widening its access.
Declaring hooks final on a property that is declared final is redundant, and will be silently ignored. This is the same behavior as final methods.
Example: Class final property hook
<?php
class SomeBaseClass
{
public $somePublicProperty = 'base public' {
get => $this->somePublicProperty . ' base hook';
}
public $someFinalPublicProperty = 'base final public' {
final get => $this->someFinalPublicProperty . ' base final hook';
}
protected $someProtectedProperty = 'base protected' {
get => $this->someProtectedProperty . ' base hook';
}
protected $someFinalProtectedProperty = 'base final protected' {
final get => $this->someFinalProtectedProperty . ' base hook';
}
private $somePrivateProperty = 'base private' {
get => $this->somePrivateProperty . ' base hook';
}
public function baseContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. '$this->somePrivateProperty : ' . $this->somePrivateProperty . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedProperty : ' . $this->someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicProperty : ' . $this->somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedClass extends SomeBaseClass
{
public function derivedContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedProperty : ' . $this->someProtectedProperty . PHP_EOL
. '$this->someFinalProtectedProperty : ' . $this->someFinalProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicProperty : ' . $this->somePublicProperty . PHP_EOL
. '$this->someFinalPublicProperty : ' . $this->someFinalPublicProperty . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedOverridingClass extends SomeBaseClass
{
public $somePublicProperty = 'derived public' {
get => $this->somePublicProperty . ' derived hook';
}
protected $someProtectedProperty = 'derived protected' {
get => $this->someProtectedProperty . ' derived hook';
}
public function derivedOverridingContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedProperty : ' . $this->someProtectedProperty . PHP_EOL
. '$this->someFinalProtectedProperty : ' . $this->someFinalProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicProperty : ' . $this->somePublicProperty . PHP_EOL
. '$this->someFinalPublicProperty : ' . $this->someFinalPublicProperty . PHP_EOL
. PHP_EOL
);
}
}
$someObject = new SomeDerivedClass();
print("# SomeDerivedClass:\n\n");
$someObject->baseContext();
$someObject->derivedContext();
$otherObject = new SomeDerivedOverridingClass();
print("# SomeDerivedOverridingClass:\n\n");
$otherObject->baseContext();
$otherObject->derivedOverridingContext();
Result (PHP 8.4):
Class: OtherClass
Base class: SomeClass
Base class property: base
Derived class property: derived
Source code: Example
The visibility of methods, properties and constants can be relaxed, e.g. a protected method can be marked as public, but they cannot be restricted, e.g. marking a public property as private.
Example: Class members overriding and visibility compatibility
<?php
class Association
{
protected array $members = [];
public int $strength = 0;
public function __construct(
public string $name,
protected string $chairman,
) {
$this->affiliate($chairman);
}
protected function affiliate(string $member)
{
$this->members[] = $member;
++$this->strength;
}
}
class Club extends Association
{
public array $members;
public function affiliate(string $member)
{
parent::affiliate($member);
}
}
function communityMeeting(Association $group)
{
print(
"Group name: {$group->name}\n"
. "Group strength: {$group->strength}\n\n"
);
}
$someGroup = new Association('Western Academy Top Graduates', 'Simon Daffodil');
print("# Association:\n\n");
communityMeeting($someGroup);
$otherGroup = new Club('Jotter Hobbyist Pen Club', 'Katy Pernickety');
$otherGroup->affiliate('Doris Frog');
$otherGroup->affiliate('Arthur Carbony');
$otherGroup->affiliate('John Thyme');
$otherGroup->affiliate('Kitty Pranky');
print("# Club:\n\n");
communityMeeting($otherGroup);
print("Members:\n");
foreach($otherGroup->members as $member) {
print($member . PHP_EOL);
}
Result (PHP 8.4):
# Association:
Group name: Western Academy Top Graduates
Group strength: 1
# Club:
Group name: Jotter Hobbyist Pen Club
Group strength: 5
Members:
Katy Pernickety
Doris Frog
Arthur Carbony
John Thyme
Kitty Pranky
Source code: Example
A [method] signature is compatible if it respects the variance rules, makes a mandatory parameter optional, adds only optional new parameters and doesn’t restrict but only relaxes the visibility. This is known as the Liskov Substitution Principle, or LSP for short. The constructor, and private methods are exempt from these signature compatibility rules, and thus won’t emit a fatal error in case of a signature mismatch.
Example: Compatible child methods
<?php
class Base
{
public function foo(int $a) {
echo "Valid\n";
}
}
class Extend1 extends Base
{
function foo(int $a = 5)
{
parent::foo($a);
}
}
class Extend2 extends Base
{
function foo(int $a, $b = 5)
{
parent::foo($a);
}
}
$extended1 = new Extend1();
$extended1->foo();
$extended2 = new Extend2();
$extended2->foo(1);
The above example will output:
Valid
Valid
The following examples demonstrate that a child method which removes a parameter, or makes an optional parameter mandatory, is not compatible with the parent method.
Example: Fatal error when a child method removes a parameter
<?php
class Base
{
public function foo(int $a = 5) {
echo "Valid\n";
}
}
class Extend extends Base
{
function foo()
{
parent::foo(1);
}
}
Output of the above example in PHP 8 is similar to:
Fatal error: Declaration of Extend::foo() must be compatible with Base::foo(int $a = 5) in /in/evtlq on line 13
Example: Fatal error when a child method makes an optional parameter mandatory
<?php
class Base
{
public function foo(int $a = 5) {
echo "Valid\n";
}
}
class Extend extends Base
{
function foo(int $a)
{
parent::foo($a);
}
}
Output of the above example in PHP 8 is similar to:
Fatal error: Declaration of Extend::foo(int $a) must be compatible with Base::foo(int $a = 5) in /in/qJXVC on line 13
Example: Class method overriding and paramenter number compatibility
<?php
class Association
{
protected array $members = [];
public int $strength = 0;
public function __construct(
public string $name,
protected string $chairman,
) {
$this->affiliate($chairman);
}
public function affiliate(string $member)
{
$this->members[] = $member;
++$this->strength;
}
public function display()
{
foreach ($this->members as $member) {
print($member . PHP_EOL);
}
print(PHP_EOL);
}
}
class Club extends Association
{
public function affiliate(string $member, ?int $id = null)
{
parent::affiliate($member);
}
}
function communityMeeting(Association $group, array $newcomers = [])
{
print(
"Group name: {$group->name}\n"
. "Group strength: {$group->strength}\n\n"
);
foreach ($newcomers as $newcomer) {
$group->affiliate($newcomer);
}
}
$someGroup = new Association('Western Academy Top Graduates', 'Simon Daffodil');
print("# Association:\n\n");
communityMeeting($someGroup, ['Karen McLaren', 'North Sloth', 'Ib Vermicelli']);
$someGroup->display();
$otherGroup = new Club('Jotter Hobbyist Pen Club', 'Katy Pernickety');
$otherGroup->affiliate('Doris Frog', 3);
$otherGroup->affiliate('Arthur Carbony', 5);
$otherGroup->affiliate('John Thyme');
$otherGroup->affiliate('Kitty Pranky');
print("# Club:\n\n");
communityMeeting($otherGroup);
$otherGroup->display();
Result (PHP 8.4):
# Association:
Group name: Western Academy Top Graduates
Group strength: 1
Simon Daffodil
Karen McLaren
North Sloth
Ib Vermicelli
# Club:
Group name: Jotter Hobbyist Pen Club
Group strength: 5
Katy Pernickety
Doris Frog
Arthur Carbony
John Thyme
Kitty Pranky
Source code: Example
Exapmle: Class method overriding and parameter requireness compatibility
<?php
class Flower
{
private const STAMP = '*';
public function bloom(int $quantity)
{
$blossoms = '';
for ($i = 0; $i < $quantity; $i++) {
$blossoms .= static::STAMP;
}
return $blossoms;
}
}
class Rose extends Flower
{
protected const STAMP = '@';
public function bloom(int $quantity = 3): string
{
return parent::bloom($quantity);
}
}
class RoseBush extends Rose
{
public function bloom(int $columns = 3, int $rows = 3): string
{
$blossoms = '';
for ($i = 0; $i < $rows; $i++) {
$blossoms .= Rose::bloom($columns);
if ($i < $rows - 1) {
$blossoms .= PHP_EOL;
}
}
return $blossoms;
}
}
function garden(Flower $flower, int $number)
{
print($flower->bloom($number) . PHP_EOL . PHP_EOL);
}
$flower = new Flower();
$rose = new Rose();
$bush = new RoseBush();
garden($flower, 3);
garden($rose, 4);
garden($bush, 5);
Result (PHP 8.4):
***
@@@@
@@@@@
@@@@@
@@@@@
Source code: Example
Warning
Renaming a method’s parameter in a child class is not a signature incompatibility. However, this is discouraged as it will result in a runtime Error if named arguments are used.
Example: Error when using named arguments and parameters were renamed in a child class
<?php
class A {
public function test($foo, $bar) {}
}
class B extends A {
public function test($a, $b) {}
}
$obj = new B;
// Pass parameters according to A::test() contract
$obj->test(foo: "foo", bar: "bar"); // ERROR!
The above example will output something similar to:
Fatal error: Uncaught Error: Unknown named parameter $foo in /in/XaaeN:14
Stack trace:
#0 {main}
thrown in /in/XaaeN on line 14
invariance
By default, properties are neither covariant nor contravariant, hence invariant. That is, their type may not change in a child class at all. The reason for that is “get” operations must be covariant, and “set” operations must be contravariant. The only way for a property to satisfy both requirements is to be invariant.
As of PHP 8.4.0, with the addition of abstract properties (on an interface or abstract class) and virtual properties, it is possible to declare a property that has only a get or set operation. As a result, abstract properties or virtual properties that have only a “get” operation required may be covariant. Similarly, an abstract property or virtual property that has only a “set” operation required may be contravariant.
Once a property has both a get and set operation, however, it is no longer covariant or contravariant for further extension. That is, it is now invariant.
Example: Property type variance
<?php
class Animal {}
class Dog extends Animal {}
class Poodle extends Dog {}
interface PetOwner
{
// Only a get operation is required, so this may be covariant.
public Animal $pet { get; }
}
class DogOwner implements PetOwner
{
// This may be a more restrictive type since the "get" side
// still returns an Animal. However, as a native property
// children of this class may not change the type anymore.
public Dog $pet;
}
class PoodleOwner extends DogOwner
{
// This is NOT ALLOWED, because DogOwner::$pet has both
// get and set operations defined and required.
public Poodle $pet;
}
?>
In PHP 7.2.0, partial contravariance was introduced by removing type restrictions on parameters in a child method. As of PHP 7.4.0, full covariance and contravariance support was added.
Covariance allows a child’s method to return a more specific type than the return type of its parent’s method. Contravariance allows a parameter type to be less specific in a child method, than that of its parent.
A type declaration is considered more specific in the following case:
iterable is changed to array or TraversableA type class is considered less specific if the opposite is true.
When overriding a method, its signature must be compatible with the parent method. Otherwise, a fatal error is emitted, or, prior to PHP 8.0.0, an E_WARNING level error is generated.
Prior to PHP 8.1, most internal classes or methods didn’t declare their return types, and any return type was allowed when extending them.
As of PHP 8.1.0, most internal methods started to “tentatively” declare their return type, in that case the return type of methods should be compatible with the parent being extended; otherwise, a deprecation notice is emitted. Note that lack of an explicit return declaration is also considered a signature mismatch, and thus results in the deprecation notice.
If the return type cannot be declared for an overriding method due to PHP cross-version compatibility concerns, a ReturnTypeWillChange attribute can be added to silence the deprecation notice.
Example: The overriding method does not declare any return type
<?php
class MyDateTime extends DateTime
{
public function modify(string $modifier) { return false; }
}
// "Deprecated: Return type of MyDateTime::modify(string $modifier) should either be compatible with DateTime::modify(string $modifier): DateTime|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice" as of PHP 8.1.0
?>
Example: The overriding method declares a wrong return type
<?php
class MyDateTime extends DateTime
{
public function modify(string $modifier): ?DateTime { return null; }
}
// "Deprecated: Return type of MyDateTime::modify(string $modifier): ?DateTime should either be compatible with DateTime::modify(string $modifier): DateTime|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice" as of PHP 8.1.0
?>
Example: The overriding method declares a wrong return type without a deprecation notice
<?php
class MyDateTime extends DateTime
{
/**
* @return DateTime|false
*/
#[\ReturnTypeWillChange]
public function modify(string $modifier) { return false; }
}
// No notice is triggered
?>
covariance
To illustrate how covariance works, a simple abstract parent class, Animal is created. Animal will be extended by children classes, Cat, and Dog.
<?php
abstract class Animal
{
protected string $name;
public function __construct(string $name)
{
$this->name = $name;
}
abstract public function speak();
}
class Dog extends Animal
{
public function speak()
{
echo $this->name . " barks";
}
}
class Cat extends Animal
{
public function speak()
{
echo $this->name . " meows";
}
}
Note that there aren’t any methods which return values in this example. A few factories will be added which return a new object of class type Animal, Cat, or Dog.
<?php
interface AnimalShelter
{
public function adopt(string $name): Animal;
}
class CatShelter implements AnimalShelter
{
public function adopt(string $name): Cat // instead of returning class type Animal, it can return class type Cat
{
return new Cat($name);
}
}
class DogShelter implements AnimalShelter
{
public function adopt(string $name): Dog // instead of returning class type Animal, it can return class type Dog
{
return new Dog($name);
}
}
$kitty = (new CatShelter)->adopt("Ricky");
$kitty->speak();
echo "\n";
$doggy = (new DogShelter)->adopt("Mavrick");
$doggy->speak();
The above example will output:
Ricky meows
Mavrick barks
contravariance
Continuing with the previous example with the classes Animal, Cat, and Dog, a class called Food and AnimalFood will be included, and a method eat(AnimalFood $food) is added to the Animal abstract class.
<?php
class Food {}
class AnimalFood extends Food {}
abstract class Animal
{
protected string $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function eat(AnimalFood $food)
{
echo $this->name . " eats " . get_class($food);
}
}
In order to see the behavior of contravariance, the eat method is overridden in the Dog class to allow any Food type object. The Cat class remains unchanged.
<?php
class Dog extends Animal
{
public function eat(Food $food) {
echo $this->name . " eats " . get_class($food);
}
}
The next example will show the behavior of contravariance.
<?php
$kitty = (new CatShelter)->adopt("Ricky");
$catFood = new AnimalFood();
$kitty->eat($catFood);
echo "\n";
$doggy = (new DogShelter)->adopt("Mavrick");
$banana = new Food();
$doggy->eat($banana);
The above example will output:
Ricky eats AnimalFood
Mavrick eats Food
But what happens if $kitty tries to eat() the $banana?
$kitty->eat($banana);
The above example will output:
Fatal error: Uncaught TypeError: Argument 1 passed to Animal::eat() must be an instance of AnimalFood, instance of Food given
Example: Class method overriding and types compatibility
<?php
class Adept
{
public $curiosity = "How the time works?";
}
class Craftman extends Adept
{
public $knowledge = "There's a time dilation.";
}
class Master extends Craftman
{
public $wisdom = "GPS clocks need time correction.";
}
class Association
{
function affiliate(Craftman $member)
{
print(
$member->curiosity . PHP_EOL
. $member->knowledge . PHP_EOL . PHP_EOL
);
}
function getGuide(): Craftman
{
return new Craftman();
}
}
class Club extends Association
{
function affiliate(Adept $member)
{
print($member->curiosity . PHP_EOL . PHP_EOL);
}
function getGuide(): Master
{
return new Master();
}
}
function communityMeeting(Association $group)
{
$newMember = new Craftman();
print("Affiliating:\n\n");
$group->affiliate($newMember);
$newGuide = $group->getGuide();
print("Guidance:\n\n");
print(
"Guide curiosity: {$newGuide->curiosity}\n"
. "Guide knowledge: {$newGuide->knowledge}\n\n"
);
}
$someGroup = new Association();
print("# Association:\n\n");
communityMeeting($someGroup);
$otherGroup = new Club();
print("# Club:\n\n");
communityMeeting($otherGroup);
Result (PHP 8.4):
# Association:
Affiliating:
How the time works?
There's a time dilation.
Guidance:
Guide curiosity: How the time works?
Guide knowledge: There's a time dilation.
# Club:
Affiliating:
How the time works?
Guidance:
Guide curiosity: How the time works?
Guide knowledge: There's a time dilation.
Source code: Example
An exception are constructors, whose visibility can be restricted, e.g. a public constructor can be marked as private in a child class.
Unlike other methods, __construct() is exempt from the usual signature compatibility rules when being extended.
Constructors are ordinary methods which are called during the instantiation of their corresponding object. As such, they may define an arbitrary number of arguments, which may be required, may have a type, and may have a default value.
Note:
It is not allowed to override a read-write property with a readonly property or vice versa.
<?php
class A {
public int $prop;
}
class B extends A {
// Illegal: read-write -> readonly
public readonly int $prop;
}
?>
Example: Class constant access with visibility
<?php
class SomeBaseClass
{
public const SOME_PUBLIC_CONSTANT = 'base public';
protected const SOME_PROTECTED_CONSTANT = 'base protected';
private const SOME_PRIVATE_CONSTANT = 'base private';
public static function baseStaticContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'SomeBaseClass::SOME_PRIVATE_CONSTANT : ' . SomeBaseClass::SOME_PRIVATE_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_PRIVATE_CONSTANT : ' . (__CLASS__)::SOME_PRIVATE_CONSTANT . PHP_EOL
. 'self::SOME_PRIVATE_CONSTANT : ' . self::SOME_PRIVATE_CONSTANT . PHP_EOL
// Cannot be called without error in the derived class:
// . 'static::SOME_PRIVATE_CONSTANT : ' . static::SOME_PRIVATE_CONSTANT . PHP_EOL
// Private members are unaccessible in the derived classes.
. "\n* protected:\n\n"
. 'SomeBaseClass::SOME_PROTECTED_CONSTANT : ' . SomeBaseClass::SOME_PROTECTED_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_PROTECTED_CONSTANT : ' . (__CLASS__)::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'self::SOME_PROTECTED_CONSTANT : ' . self::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'static::SOME_PROTECTED_CONSTANT : ' . static::SOME_PROTECTED_CONSTANT . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::SOME_PUBLIC_CONSTANT : ' . SomeBaseClass::SOME_PUBLIC_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_PUBLIC_CONSTANT : ' . (__CLASS__)::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'self::SOME_PUBLIC_CONSTANT : ' . self::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'static::SOME_PUBLIC_CONSTANT : ' . static::SOME_PUBLIC_CONSTANT . PHP_EOL
. PHP_EOL
);
}
public function baseDynamicContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'SomeBaseClass::SOME_PRIVATE_CONSTANT : ' . SomeBaseClass::SOME_PRIVATE_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_PRIVATE_CONSTANT : ' . (__CLASS__)::SOME_PRIVATE_CONSTANT . PHP_EOL
. 'self::SOME_PRIVATE_CONSTANT : ' . self::SOME_PRIVATE_CONSTANT . PHP_EOL
// Cannot be called without error in the derived class:
// . 'static::SOME_PRIVATE_CONSTANT : ' . static::SOME_PRIVATE_CONSTANT . PHP_EOL
// Private members are unaccessible in the derived classes.
. "\n* protected:\n\n"
. 'SomeBaseClass::SOME_PROTECTED_CONSTANT : ' . SomeBaseClass::SOME_PROTECTED_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_PROTECTED_CONSTANT : ' . (__CLASS__)::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'self::SOME_PROTECTED_CONSTANT : ' . self::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'static::SOME_PROTECTED_CONSTANT : ' . static::SOME_PROTECTED_CONSTANT . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::SOME_PUBLIC_CONSTANT : ' . SomeBaseClass::SOME_PUBLIC_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_PUBLIC_CONSTANT : ' . (__CLASS__)::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'self::SOME_PUBLIC_CONSTANT : ' . self::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'static::SOME_PUBLIC_CONSTANT : ' . static::SOME_PUBLIC_CONSTANT . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedClass extends SomeBaseClass
{
public static function derivedStaticContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::SOME_PROTECTED_CONSTANT : ' . SomeBaseClass::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'parent::SOME_PROTECTED_CONSTANT : ' . parent::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'SomeDerivedClass::SOME_PROTECTED_CONSTANT : ' . SomeDerivedClass::SOME_PROTECTED_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_PROTECTED_CONSTANT : ' . (__CLASS__)::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'self::SOME_PROTECTED_CONSTANT : ' . self::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'static::SOME_PROTECTED_CONSTANT : ' . static::SOME_PROTECTED_CONSTANT . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::SOME_PUBLIC_CONSTANT : ' . SomeBaseClass::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'parent::SOME_PUBLIC_CONSTANT : ' . parent::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'SomeDerivedClass::SOME_PUBLIC_CONSTANT : ' . SomeDerivedClass::SOME_PUBLIC_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_PUBLIC_CONSTANT : ' . (__CLASS__)::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'self::SOME_PUBLIC_CONSTANT : ' . self::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'static::SOME_PUBLIC_CONSTANT : ' . static::SOME_PUBLIC_CONSTANT . PHP_EOL
. PHP_EOL
);
}
public function derivedDynamicContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::SOME_PROTECTED_CONSTANT : ' . SomeBaseClass::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'parent::SOME_PROTECTED_CONSTANT : ' . parent::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'SomeDerivedClass::SOME_PROTECTED_CONSTANT : ' . SomeDerivedClass::SOME_PROTECTED_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_PROTECTED_CONSTANT : ' . (__CLASS__)::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'self::SOME_PROTECTED_CONSTANT : ' . self::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'static::SOME_PROTECTED_CONSTANT : ' . static::SOME_PROTECTED_CONSTANT . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::SOME_PUBLIC_CONSTANT : ' . SomeBaseClass::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'parent::SOME_PUBLIC_CONSTANT : ' . parent::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'SomeDerivedClass::SOME_PUBLIC_CONSTANT : ' . SomeDerivedClass::SOME_PUBLIC_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_PUBLIC_CONSTANT : ' . (__CLASS__)::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'self::SOME_PUBLIC_CONSTANT : ' . self::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'static::SOME_PUBLIC_CONSTANT : ' . static::SOME_PUBLIC_CONSTANT . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedOverridingClass extends SomeBaseClass
{
public const SOME_PUBLIC_CONSTANT = 'derived public';
protected const SOME_PROTECTED_CONSTANT = 'derived protected';
private const SOME_PRIVATE_CONSTANT = 'derived shadowed private'; // It's not overriding but rather shadowing!
// It's completly new constant - very own constant of the derived class
// because private member of the base class is unaccessible and not visible for the derived class.
public static function derivedOverridingStaticContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'SomeDerivedOverridingClass::SOME_PRIVATE_CONSTANT : ' . SomeDerivedOverridingClass::SOME_PRIVATE_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_PRIVATE_CONSTANT : ' . (__CLASS__)::SOME_PRIVATE_CONSTANT . PHP_EOL
. 'self::SOME_PRIVATE_CONSTANT : ' . self::SOME_PRIVATE_CONSTANT . PHP_EOL
// This will be dangerous in case of further inheritance:
. 'static::SOME_PRIVATE_CONSTANT : ' . static::SOME_PRIVATE_CONSTANT . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::SOME_PROTECTED_CONSTANT : ' . SomeBaseClass::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'parent::SOME_PROTECTED_CONSTANT : ' . parent::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'SomeDerivedOverridingClass::SOME_PROTECTED_CONSTANT : ' . SomeDerivedOverridingClass::SOME_PROTECTED_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_PROTECTED_CONSTANT : ' . (__CLASS__)::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'self::SOME_PROTECTED_CONSTANT : ' . self::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'static::SOME_PROTECTED_CONSTANT : ' . static::SOME_PROTECTED_CONSTANT . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::SOME_PUBLIC_CONSTANT : ' . SomeBaseClass::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'parent::SOME_PUBLIC_CONSTANT : ' . parent::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'SomeDerivedOverridingClass::SOME_PUBLIC_CONSTANT : ' . SomeDerivedOverridingClass::SOME_PUBLIC_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_PUBLIC_CONSTANT : ' . (__CLASS__)::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'self::SOME_PUBLIC_CONSTANT : ' . self::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'static::SOME_PUBLIC_CONSTANT : ' . static::SOME_PUBLIC_CONSTANT . PHP_EOL
. PHP_EOL
);
}
public function derivedOverridingDynamicContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'SomeDerivedOverridingClass::SOME_PRIVATE_CONSTANT : ' . SomeDerivedOverridingClass::SOME_PRIVATE_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_PRIVATE_CONSTANT : ' . (__CLASS__)::SOME_PRIVATE_CONSTANT . PHP_EOL
. 'self::SOME_PRIVATE_CONSTANT : ' . self::SOME_PRIVATE_CONSTANT . PHP_EOL
// This will be dangerous in case of further inheritance:
. 'static::SOME_PRIVATE_CONSTANT : ' . static::SOME_PRIVATE_CONSTANT . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::SOME_PROTECTED_CONSTANT : ' . SomeBaseClass::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'parent::SOME_PROTECTED_CONSTANT : ' . parent::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'SomeDerivedOverridingClass::SOME_PROTECTED_CONSTANT : ' . SomeDerivedOverridingClass::SOME_PROTECTED_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_PROTECTED_CONSTANT : ' . (__CLASS__)::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'self::SOME_PROTECTED_CONSTANT : ' . self::SOME_PROTECTED_CONSTANT . PHP_EOL
. 'static::SOME_PROTECTED_CONSTANT : ' . static::SOME_PROTECTED_CONSTANT . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::SOME_PUBLIC_CONSTANT : ' . SomeBaseClass::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'parent::SOME_PUBLIC_CONSTANT : ' . parent::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'SomeDerivedOverridingClass::SOME_PUBLIC_CONSTANT : ' . SomeDerivedOverridingClass::SOME_PUBLIC_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_PUBLIC_CONSTANT : ' . (__CLASS__)::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'self::SOME_PUBLIC_CONSTANT : ' . self::SOME_PUBLIC_CONSTANT . PHP_EOL
. 'static::SOME_PUBLIC_CONSTANT : ' . static::SOME_PUBLIC_CONSTANT . PHP_EOL
. PHP_EOL
);
}
}
$someObject = new SomeDerivedClass();
print("# SomeDerivedClass:\n\n");
$someObject->baseStaticContext();
$someObject->baseDynamicContext();
$someObject->derivedStaticContext();
$someObject->derivedDynamicContext();
$otherObject = new SomeDerivedOverridingClass();
print("# SomeDerivedOverridingClass:\n\n");
$otherObject->baseStaticContext();
$otherObject->baseDynamicContext();
$otherObject->derivedOverridingStaticContext();
$otherObject->derivedOverridingDynamicContext();
Result (PHP 8.4):
# SomeDerivedClass:
SomeBaseClass::baseStaticContext
* private:
SomeBaseClass::SOME_PRIVATE_CONSTANT : base private
(__CLASS__)::SOME_PRIVATE_CONSTANT : base private
self::SOME_PRIVATE_CONSTANT : base private
* protected:
SomeBaseClass::SOME_PROTECTED_CONSTANT : base protected
(__CLASS__)::SOME_PROTECTED_CONSTANT : base protected
self::SOME_PROTECTED_CONSTANT : base protected
static::SOME_PROTECTED_CONSTANT : base protected
* public:
SomeBaseClass::SOME_PUBLIC_CONSTANT : base public
(__CLASS__)::SOME_PUBLIC_CONSTANT : base public
self::SOME_PUBLIC_CONSTANT : base public
static::SOME_PUBLIC_CONSTANT : base public
SomeBaseClass::baseDynamicContext
* private:
SomeBaseClass::SOME_PRIVATE_CONSTANT : base private
(__CLASS__)::SOME_PRIVATE_CONSTANT : base private
self::SOME_PRIVATE_CONSTANT : base private
* protected:
SomeBaseClass::SOME_PROTECTED_CONSTANT : base protected
(__CLASS__)::SOME_PROTECTED_CONSTANT : base protected
self::SOME_PROTECTED_CONSTANT : base protected
static::SOME_PROTECTED_CONSTANT : base protected
* public:
SomeBaseClass::SOME_PUBLIC_CONSTANT : base public
(__CLASS__)::SOME_PUBLIC_CONSTANT : base public
self::SOME_PUBLIC_CONSTANT : base public
static::SOME_PUBLIC_CONSTANT : base public
SomeDerivedClass::derivedStaticContext
* protected:
SomeBaseClass::SOME_PROTECTED_CONSTANT : base protected
parent::SOME_PROTECTED_CONSTANT : base protected
SomeDerivedClass::SOME_PROTECTED_CONSTANT : base protected
(__CLASS__)::SOME_PROTECTED_CONSTANT : base protected
self::SOME_PROTECTED_CONSTANT : base protected
static::SOME_PROTECTED_CONSTANT : base protected
* public:
SomeBaseClass::SOME_PUBLIC_CONSTANT : base public
parent::SOME_PUBLIC_CONSTANT : base public
SomeDerivedClass::SOME_PUBLIC_CONSTANT : base public
(__CLASS__)::SOME_PUBLIC_CONSTANT : base public
self::SOME_PUBLIC_CONSTANT : base public
static::SOME_PUBLIC_CONSTANT : base public
SomeDerivedClass::derivedDynamicContext
* protected:
SomeBaseClass::SOME_PROTECTED_CONSTANT : base protected
parent::SOME_PROTECTED_CONSTANT : base protected
SomeDerivedClass::SOME_PROTECTED_CONSTANT : base protected
(__CLASS__)::SOME_PROTECTED_CONSTANT : base protected
self::SOME_PROTECTED_CONSTANT : base protected
static::SOME_PROTECTED_CONSTANT : base protected
* public:
SomeBaseClass::SOME_PUBLIC_CONSTANT : base public
parent::SOME_PUBLIC_CONSTANT : base public
SomeDerivedClass::SOME_PUBLIC_CONSTANT : base public
(__CLASS__)::SOME_PUBLIC_CONSTANT : base public
self::SOME_PUBLIC_CONSTANT : base public
static::SOME_PUBLIC_CONSTANT : base public
# SomeDerivedOverridingClass:
SomeBaseClass::baseStaticContext
* private:
SomeBaseClass::SOME_PRIVATE_CONSTANT : base private
(__CLASS__)::SOME_PRIVATE_CONSTANT : base private
self::SOME_PRIVATE_CONSTANT : base private
* protected:
SomeBaseClass::SOME_PROTECTED_CONSTANT : base protected
(__CLASS__)::SOME_PROTECTED_CONSTANT : base protected
self::SOME_PROTECTED_CONSTANT : base protected
static::SOME_PROTECTED_CONSTANT : derived protected
* public:
SomeBaseClass::SOME_PUBLIC_CONSTANT : base public
(__CLASS__)::SOME_PUBLIC_CONSTANT : base public
self::SOME_PUBLIC_CONSTANT : base public
static::SOME_PUBLIC_CONSTANT : derived public
SomeBaseClass::baseDynamicContext
* private:
SomeBaseClass::SOME_PRIVATE_CONSTANT : base private
(__CLASS__)::SOME_PRIVATE_CONSTANT : base private
self::SOME_PRIVATE_CONSTANT : base private
* protected:
SomeBaseClass::SOME_PROTECTED_CONSTANT : base protected
(__CLASS__)::SOME_PROTECTED_CONSTANT : base protected
self::SOME_PROTECTED_CONSTANT : base protected
static::SOME_PROTECTED_CONSTANT : derived protected
* public:
SomeBaseClass::SOME_PUBLIC_CONSTANT : base public
(__CLASS__)::SOME_PUBLIC_CONSTANT : base public
self::SOME_PUBLIC_CONSTANT : base public
static::SOME_PUBLIC_CONSTANT : derived public
SomeDerivedOverridingClass::derivedOverridingStaticContext
* private:
SomeDerivedOverridingClass::SOME_PRIVATE_CONSTANT : derived shadowed private
(__CLASS__)::SOME_PRIVATE_CONSTANT : derived shadowed private
self::SOME_PRIVATE_CONSTANT : derived shadowed private
static::SOME_PRIVATE_CONSTANT : derived shadowed private
* protected:
SomeBaseClass::SOME_PROTECTED_CONSTANT : base protected
parent::SOME_PROTECTED_CONSTANT : base protected
SomeDerivedOverridingClass::SOME_PROTECTED_CONSTANT : derived protected
(__CLASS__)::SOME_PROTECTED_CONSTANT : derived protected
self::SOME_PROTECTED_CONSTANT : derived protected
static::SOME_PROTECTED_CONSTANT : derived protected
* public:
SomeBaseClass::SOME_PUBLIC_CONSTANT : base public
parent::SOME_PUBLIC_CONSTANT : base public
SomeDerivedOverridingClass::SOME_PUBLIC_CONSTANT : derived public
(__CLASS__)::SOME_PUBLIC_CONSTANT : derived public
self::SOME_PUBLIC_CONSTANT : derived public
static::SOME_PUBLIC_CONSTANT : derived public
SomeDerivedOverridingClass::derivedOverridingDynamicContext
* private:
SomeDerivedOverridingClass::SOME_PRIVATE_CONSTANT : derived shadowed private
(__CLASS__)::SOME_PRIVATE_CONSTANT : derived shadowed private
self::SOME_PRIVATE_CONSTANT : derived shadowed private
static::SOME_PRIVATE_CONSTANT : derived shadowed private
* protected:
SomeBaseClass::SOME_PROTECTED_CONSTANT : base protected
parent::SOME_PROTECTED_CONSTANT : base protected
SomeDerivedOverridingClass::SOME_PROTECTED_CONSTANT : derived protected
(__CLASS__)::SOME_PROTECTED_CONSTANT : derived protected
self::SOME_PROTECTED_CONSTANT : derived protected
static::SOME_PROTECTED_CONSTANT : derived protected
* public:
SomeBaseClass::SOME_PUBLIC_CONSTANT : base public
parent::SOME_PUBLIC_CONSTANT : base public
SomeDerivedOverridingClass::SOME_PUBLIC_CONSTANT : derived public
(__CLASS__)::SOME_PUBLIC_CONSTANT : derived public
self::SOME_PUBLIC_CONSTANT : derived public
static::SOME_PUBLIC_CONSTANT : derived public
Source code: [Example](../../../../examples/code/classes_interfaces_traits/classes/inheritance/class_constant_access_with_visibility.php
Example: Class final constant access with visibility
<?php
class SomeBaseClass
{
final public const SOME_FINAL_PUBLIC_CONSTANT = 'base final public const';
final protected const SOME_FINAL_PROTECTED_CONSTANT = 'base final protected const';
public static function baseStaticContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::SOME_FINAL_PROTECTED_CONSTANT : ' . SomeBaseClass::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_FINAL_PROTECTED_CONSTANT : ' . (__CLASS__)::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. 'self::SOME_FINAL_PROTECTED_CONSTANT : ' . self::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. 'static::SOME_FINAL_PROTECTED_CONSTANT : ' . static::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::SOME_FINAL_PUBLIC_CONSTANT : ' . SomeBaseClass::SOME_FINAL_PUBLIC_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_FINAL_PUBLIC_CONSTANT : ' . (__CLASS__)::SOME_FINAL_PUBLIC_CONSTANT . PHP_EOL
. 'self::SOME_FINAL_PUBLIC_CONSTANT : ' . self::SOME_FINAL_PUBLIC_CONSTANT . PHP_EOL
. 'static::SOME_FINAL_PUBLIC_CONSTANT : ' . static::SOME_FINAL_PUBLIC_CONSTANT . PHP_EOL
. PHP_EOL
);
}
public function baseDynamicContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::SOME_FINAL_PROTECTED_CONSTANT : ' . SomeBaseClass::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_FINAL_PROTECTED_CONSTANT : ' . (__CLASS__)::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. 'self::SOME_FINAL_PROTECTED_CONSTANT : ' . self::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. 'static::SOME_FINAL_PROTECTED_CONSTANT : ' . static::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::SOME_FINAL_PUBLIC_CONSTANT : ' . SomeBaseClass::SOME_FINAL_PUBLIC_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_FINAL_PUBLIC_CONSTANT : ' . (__CLASS__)::SOME_FINAL_PUBLIC_CONSTANT . PHP_EOL
. 'self::SOME_FINAL_PUBLIC_CONSTANT : ' . self::SOME_FINAL_PUBLIC_CONSTANT . PHP_EOL
. 'static::SOME_FINAL_PUBLIC_CONSTANT : ' . static::SOME_FINAL_PUBLIC_CONSTANT . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedClass extends SomeBaseClass
{
public static function derivedStaticContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::SOME_FINAL_PROTECTED_CONSTANT : ' . SomeBaseClass::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. 'parent::SOME_FINAL_PROTECTED_CONSTANT : ' . parent::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. 'SomeDerivedClass::SOME_FINAL_PROTECTED_CONSTANT : ' . SomeDerivedClass::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_FINAL_PROTECTED_CONSTANT : ' . (__CLASS__)::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. 'self::SOME_FINAL_PROTECTED_CONSTANT : ' . self::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. 'static::SOME_FINAL_PROTECTED_CONSTANT : ' . static::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::SOME_FINAL_PUBLIC_CONSTANT : ' . SomeBaseClass::SOME_FINAL_PUBLIC_CONSTANT . PHP_EOL
. 'parent::SOME_FINAL_PUBLIC_CONSTANT : ' . parent::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. 'SomeDerivedClass::SOME_FINAL_PUBLIC_CONSTANT : ' . SomeDerivedClass::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_FINAL_PUBLIC_CONSTANT : ' . (__CLASS__)::SOME_FINAL_PUBLIC_CONSTANT . PHP_EOL
. 'self::SOME_FINAL_PUBLIC_CONSTANT : ' . self::SOME_FINAL_PUBLIC_CONSTANT . PHP_EOL
. 'static::SOME_FINAL_PUBLIC_CONSTANT : ' . static::SOME_FINAL_PUBLIC_CONSTANT . PHP_EOL
. PHP_EOL
);
}
public static function derivedDynamicContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::SOME_FINAL_PROTECTED_CONSTANT : ' . SomeBaseClass::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. 'parent::SOME_FINAL_PROTECTED_CONSTANT : ' . parent::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. 'SomeDerivedClass::SOME_FINAL_PROTECTED_CONSTANT : ' . SomeDerivedClass::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_FINAL_PROTECTED_CONSTANT : ' . (__CLASS__)::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. 'self::SOME_FINAL_PROTECTED_CONSTANT : ' . self::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. 'static::SOME_FINAL_PROTECTED_CONSTANT : ' . static::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::SOME_FINAL_PUBLIC_CONSTANT : ' . SomeBaseClass::SOME_FINAL_PUBLIC_CONSTANT . PHP_EOL
. 'parent::SOME_FINAL_PUBLIC_CONSTANT : ' . parent::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. 'SomeDerivedClass::SOME_FINAL_PUBLIC_CONSTANT : ' . SomeDerivedClass::SOME_FINAL_PROTECTED_CONSTANT . PHP_EOL
. '(__CLASS__)::SOME_FINAL_PUBLIC_CONSTANT : ' . (__CLASS__)::SOME_FINAL_PUBLIC_CONSTANT . PHP_EOL
. 'self::SOME_FINAL_PUBLIC_CONSTANT : ' . self::SOME_FINAL_PUBLIC_CONSTANT . PHP_EOL
. 'static::SOME_FINAL_PUBLIC_CONSTANT : ' . static::SOME_FINAL_PUBLIC_CONSTANT . PHP_EOL
. PHP_EOL
);
}
}
$someObject = new SomeDerivedClass();
print("# SomeDerivedClass:\n\n");
$someObject->baseStaticContext();
$someObject->baseDynamicContext();
$someObject->derivedStaticContext();
$someObject->derivedDynamicContext();
Result (PHP 8.4):
# SomeDerivedClass:
SomeBaseClass::baseStaticContext
* protected:
SomeBaseClass::SOME_FINAL_PROTECTED_CONSTANT : base final protected const
(__CLASS__)::SOME_FINAL_PROTECTED_CONSTANT : base final protected const
self::SOME_FINAL_PROTECTED_CONSTANT : base final protected const
static::SOME_FINAL_PROTECTED_CONSTANT : base final protected const
* public:
SomeBaseClass::SOME_FINAL_PUBLIC_CONSTANT : base final public const
(__CLASS__)::SOME_FINAL_PUBLIC_CONSTANT : base final public const
self::SOME_FINAL_PUBLIC_CONSTANT : base final public const
static::SOME_FINAL_PUBLIC_CONSTANT : base final public const
SomeBaseClass::baseDynamicContext
* protected:
SomeBaseClass::SOME_FINAL_PROTECTED_CONSTANT : base final protected const
(__CLASS__)::SOME_FINAL_PROTECTED_CONSTANT : base final protected const
self::SOME_FINAL_PROTECTED_CONSTANT : base final protected const
static::SOME_FINAL_PROTECTED_CONSTANT : base final protected const
* public:
SomeBaseClass::SOME_FINAL_PUBLIC_CONSTANT : base final public const
(__CLASS__)::SOME_FINAL_PUBLIC_CONSTANT : base final public const
self::SOME_FINAL_PUBLIC_CONSTANT : base final public const
static::SOME_FINAL_PUBLIC_CONSTANT : base final public const
SomeDerivedClass::derivedStaticContext
* protected:
SomeBaseClass::SOME_FINAL_PROTECTED_CONSTANT : base final protected const
parent::SOME_FINAL_PROTECTED_CONSTANT : base final protected const
SomeDerivedClass::SOME_FINAL_PROTECTED_CONSTANT : base final protected const
(__CLASS__)::SOME_FINAL_PROTECTED_CONSTANT : base final protected const
self::SOME_FINAL_PROTECTED_CONSTANT : base final protected const
static::SOME_FINAL_PROTECTED_CONSTANT : base final protected const
* public:
SomeBaseClass::SOME_FINAL_PUBLIC_CONSTANT : base final public const
parent::SOME_FINAL_PUBLIC_CONSTANT : base final protected const
SomeDerivedClass::SOME_FINAL_PUBLIC_CONSTANT : base final protected const
(__CLASS__)::SOME_FINAL_PUBLIC_CONSTANT : base final public const
self::SOME_FINAL_PUBLIC_CONSTANT : base final public const
static::SOME_FINAL_PUBLIC_CONSTANT : base final public const
SomeDerivedClass::derivedDynamicContext
* protected:
SomeBaseClass::SOME_FINAL_PROTECTED_CONSTANT : base final protected const
parent::SOME_FINAL_PROTECTED_CONSTANT : base final protected const
SomeDerivedClass::SOME_FINAL_PROTECTED_CONSTANT : base final protected const
(__CLASS__)::SOME_FINAL_PROTECTED_CONSTANT : base final protected const
self::SOME_FINAL_PROTECTED_CONSTANT : base final protected const
static::SOME_FINAL_PROTECTED_CONSTANT : base final protected const
* public:
SomeBaseClass::SOME_FINAL_PUBLIC_CONSTANT : base final public const
parent::SOME_FINAL_PUBLIC_CONSTANT : base final protected const
SomeDerivedClass::SOME_FINAL_PUBLIC_CONSTANT : base final protected const
(__CLASS__)::SOME_FINAL_PUBLIC_CONSTANT : base final public const
self::SOME_FINAL_PUBLIC_CONSTANT : base final public const
static::SOME_FINAL_PUBLIC_CONSTANT : base final public const
Source code: Example
Example: Class property access with visibility
<?php
class SomeBaseClass
{
public $somePublicProperty = 'base public';
protected $someProtectedProperty = 'base protected';
private $somePrivateProperty = 'base private';
public function baseDynamicContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. '$this->somePrivateProperty : ' . $this->somePrivateProperty . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedProperty : ' . $this->someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicProperty : ' . $this->somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedClass extends SomeBaseClass
{
public function derivedDynamicContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedProperty : ' . $this->someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicProperty : ' . $this->somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedOverridingClass extends SomeBaseClass
{
public $somePublicProperty = 'derived public';
protected $someProtectedProperty = 'derived protected';
private $somePrivateProperty = 'derived shadowed private'; // It's not overriding but rather shadowing!
// It's completly new property - very own property of the derived class
// because private member of the base class is unaccessible and not visible for the derived class.
public function derivedOverridingDynamicContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. '$this->somePrivateProperty : ' . $this->somePrivateProperty . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedProperty : ' . $this->someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicProperty : ' . $this->somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
$someObject = new SomeDerivedClass();
print("# SomeDerivedClass:\n\n");
$someObject->baseDynamicContext();
$someObject->derivedDynamicContext();
$otherObject = new SomeDerivedOverridingClass();
print("# SomeDerivedOverridingClass:\n\n");
$otherObject->baseDynamicContext();
$otherObject->derivedOverridingDynamicContext();
Result (PHP 8.4):
# SomeDerivedClass:
SomeBaseClass::baseDynamicContext
* private:
$this->somePrivateProperty : base private
* protected:
$this->someProtectedProperty : base protected
* public:
$this->somePublicProperty : base public
SomeDerivedClass::derivedDynamicContext
* protected:
$this->someProtectedProperty : base protected
* public:
$this->somePublicProperty : base public
# SomeDerivedOverridingClass:
SomeBaseClass::baseDynamicContext
* private:
$this->somePrivateProperty : base private
* protected:
$this->someProtectedProperty : derived protected
* public:
$this->somePublicProperty : derived public
SomeDerivedOverridingClass::derivedOverridingDynamicContext
* private:
$this->somePrivateProperty : derived shadowed private
* protected:
$this->someProtectedProperty : derived protected
* public:
$this->somePublicProperty : derived public
Source code: Example
Example: Class static property access with visibility
<?php
class SomeBaseClass
{
public static $somePublicProperty = 'base static public';
protected static $someProtectedProperty = 'base static protected';
private static $somePrivateProperty = 'base static private';
public static function baseStaticContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'SomeBaseClass::$somePrivateProperty : ' . SomeBaseClass::$somePrivateProperty . PHP_EOL
. '(__CLASS__)::$somePrivateProperty : ' . (__CLASS__)::$somePrivateProperty . PHP_EOL
. 'self::$somePrivateProperty : ' . self::$somePrivateProperty . PHP_EOL
// Cannot be called without error in the derived class:
// . 'static::$somePrivateProperty : ' . static::$somePrivateProperty . PHP_EOL
// Private members are unaccessible in the derived classes.
. "\n* protected:\n\n"
. 'SomeBaseClass::$someProtectedProperty : ' . SomeBaseClass::$someProtectedProperty . PHP_EOL
. '(__CLASS__)::$someProtectedProperty : ' . (__CLASS__)::$someProtectedProperty . PHP_EOL
. 'self::$someProtectedProperty : ' . self::$someProtectedProperty . PHP_EOL
. 'static::$someProtectedProperty : ' . static::$someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::$somePublicProperty : ' . SomeBaseClass::$somePublicProperty . PHP_EOL
. '(__CLASS__)::$somePublicProperty : ' . (__CLASS__)::$somePublicProperty . PHP_EOL
. 'self::$somePublicProperty : ' . self::$somePublicProperty . PHP_EOL
. 'static::$somePublicProperty : ' . static::$somePublicProperty . PHP_EOL
. PHP_EOL
);
}
public function baseDynamicContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'SomeBaseClass::$somePrivateProperty : ' . SomeBaseClass::$somePrivateProperty . PHP_EOL
. '(__CLASS__)::$somePrivateProperty : ' . (__CLASS__)::$somePrivateProperty . PHP_EOL
. 'self::$somePrivateProperty : ' . self::$somePrivateProperty . PHP_EOL
// Cannot be called without error in the derived class:
// . 'static::$somePrivateProperty : ' . static::$somePrivateProperty . PHP_EOL
// Private members are unaccessible in the derived classes.
. "\n* protected:\n\n"
. 'SomeBaseClass::$someProtectedProperty : ' . SomeBaseClass::$someProtectedProperty . PHP_EOL
. '(__CLASS__)::$someProtectedProperty : ' . (__CLASS__)::$someProtectedProperty . PHP_EOL
. 'self::$someProtectedProperty : ' . self::$someProtectedProperty . PHP_EOL
. 'static::$someProtectedProperty : ' . static::$someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::$somePublicProperty : ' . SomeBaseClass::$somePublicProperty . PHP_EOL
. '(__CLASS__)::$somePublicProperty : ' . (__CLASS__)::$somePublicProperty . PHP_EOL
. 'self::$somePublicProperty : ' . self::$somePublicProperty . PHP_EOL
. 'static::$somePublicProperty : ' . static::$somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedClass extends SomeBaseClass
{
public static function derivedStaticContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::$someProtectedProperty : ' . SomeBaseClass::$someProtectedProperty . PHP_EOL
. 'parent::$someProtectedProperty : ' . parent::$someProtectedProperty . PHP_EOL
. 'SomeDerivedClass::$someProtectedProperty : ' . SomeDerivedClass::$someProtectedProperty . PHP_EOL
. '(__CLASS__)::$someProtectedProperty : ' . (__CLASS__)::$someProtectedProperty . PHP_EOL
. 'self::$someProtectedProperty : ' . self::$someProtectedProperty . PHP_EOL
. 'static::$someProtectedProperty : ' . static::$someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::$somePublicProperty : ' . SomeBaseClass::$somePublicProperty . PHP_EOL
. 'parent::$somePublicProperty : ' . parent::$somePublicProperty . PHP_EOL
. 'SomeDerivedClass::$somePublicProperty : ' . SomeDerivedClass::$somePublicProperty . PHP_EOL
. '(__CLASS__)::$somePublicProperty : ' . (__CLASS__)::$somePublicProperty . PHP_EOL
. 'self::$somePublicProperty : ' . self::$somePublicProperty . PHP_EOL
. 'static::$somePublicProperty : ' . static::$somePublicProperty . PHP_EOL
. PHP_EOL
);
}
public function derivedDynamicContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::$someProtectedProperty : ' . SomeBaseClass::$someProtectedProperty . PHP_EOL
. 'parent::$someProtectedProperty : ' . parent::$someProtectedProperty . PHP_EOL
. 'SomeDerivedClass::$someProtectedProperty : ' . SomeDerivedClass::$someProtectedProperty . PHP_EOL
. '(__CLASS__)::$someProtectedProperty : ' . (__CLASS__)::$someProtectedProperty . PHP_EOL
. 'self::$someProtectedProperty : ' . self::$someProtectedProperty . PHP_EOL
. 'static::$someProtectedProperty : ' . static::$someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::$somePublicProperty : ' . SomeBaseClass::$somePublicProperty . PHP_EOL
. 'parent::$somePublicProperty : ' . parent::$somePublicProperty . PHP_EOL
. 'SomeDerivedClass::$somePublicProperty : ' . SomeDerivedClass::$somePublicProperty . PHP_EOL
. '(__CLASS__)::$somePublicProperty : ' . (__CLASS__)::$somePublicProperty . PHP_EOL
. 'self::$somePublicProperty : ' . self::$somePublicProperty . PHP_EOL
. 'static::$somePublicProperty : ' . static::$somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedOverridingClass extends SomeBaseClass
{
public static $somePublicProperty = 'derived static public';
protected static $someProtectedProperty = 'derived static protected';
private static $somePrivateProperty = 'derived shadowed static private'; // It's not overriding but rather shadowing!
// It's completly new property - very own property of the derived class
// because private member of the base class is unaccessible and not visible for the derived class.
public static function derivedOverridingStaticContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'SomeDerivedOverridingClass::$somePrivateProperty : ' . SomeDerivedOverridingClass::$somePrivateProperty . PHP_EOL
. '(__CLASS__)::$somePrivateProperty : ' . (__CLASS__)::$somePrivateProperty . PHP_EOL
. 'self::$somePrivateProperty : ' . self::$somePrivateProperty . PHP_EOL
// This will be dangerous in case of further inheritance:
. 'static::$somePrivateProperty : ' . static::$somePrivateProperty . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::$someProtectedProperty : ' . SomeBaseClass::$someProtectedProperty . PHP_EOL
. 'parent::$someProtectedProperty : ' . parent::$someProtectedProperty . PHP_EOL
. 'SomeDerivedOverridingClass::$someProtectedProperty : ' . SomeDerivedOverridingClass::$someProtectedProperty . PHP_EOL
. '(__CLASS__)::$someProtectedProperty : ' . (__CLASS__)::$someProtectedProperty . PHP_EOL
. 'self::$someProtectedProperty : ' . self::$someProtectedProperty . PHP_EOL
. 'static::$someProtectedProperty : ' . static::$someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::$somePublicProperty : ' . SomeBaseClass::$somePublicProperty . PHP_EOL
. 'parent::$somePublicProperty : ' . parent::$somePublicProperty . PHP_EOL
. 'SomeDerivedOverridingClass::$somePublicProperty : ' . SomeDerivedOverridingClass::$somePublicProperty . PHP_EOL
. '(__CLASS__)::$somePublicProperty : ' . (__CLASS__)::$somePublicProperty . PHP_EOL
. 'self::$somePublicProperty : ' . self::$somePublicProperty . PHP_EOL
. 'static::$somePublicProperty : ' . static::$somePublicProperty . PHP_EOL
. PHP_EOL
);
}
public function derivedOverridingDynamicContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'SomeDerivedOverridingClass::$somePrivateProperty : ' . SomeDerivedOverridingClass::$somePrivateProperty . PHP_EOL
. '(__CLASS__)::$somePrivateProperty : ' . (__CLASS__)::$somePrivateProperty . PHP_EOL
. 'self::$somePrivateProperty : ' . self::$somePrivateProperty . PHP_EOL
// This will be dangerous in case of further inheritance:
. 'static::$somePrivateProperty : ' . static::$somePrivateProperty . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::$someProtectedProperty : ' . SomeBaseClass::$someProtectedProperty . PHP_EOL
. 'parent::$someProtectedProperty : ' . parent::$someProtectedProperty . PHP_EOL
. 'SomeDerivedOverridingClass::$someProtectedProperty : ' . SomeDerivedOverridingClass::$someProtectedProperty . PHP_EOL
. '(__CLASS__)::$someProtectedProperty : ' . (__CLASS__)::$someProtectedProperty . PHP_EOL
. 'self::$someProtectedProperty : ' . self::$someProtectedProperty . PHP_EOL
. 'static::$someProtectedProperty : ' . static::$someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::$somePublicProperty : ' . SomeBaseClass::$somePublicProperty . PHP_EOL
. 'parent::$somePublicProperty : ' . parent::$somePublicProperty . PHP_EOL
. 'SomeDerivedOverridingClass::$somePublicProperty : ' . SomeDerivedOverridingClass::$somePublicProperty . PHP_EOL
. '(__CLASS__)::$somePublicProperty : ' . (__CLASS__)::$somePublicProperty . PHP_EOL
. 'self::$somePublicProperty : ' . self::$somePublicProperty . PHP_EOL
. 'static::$somePublicProperty : ' . static::$somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
$someObject = new SomeDerivedClass();
print("# SomeDerivedClass:\n\n");
$someObject->baseStaticContext();
$someObject->baseDynamicContext();
$someObject->derivedStaticContext();
$someObject->derivedDynamicContext();
$otherObject = new SomeDerivedOverridingClass();
print("# SomeDerivedOverridingClass:\n\n");
$otherObject->baseStaticContext();
$otherObject->baseDynamicContext();
$otherObject->derivedOverridingStaticContext();
$otherObject->derivedOverridingDynamicContext();
Result (PHP 8.4):
# SomeDerivedClass:
SomeBaseClass::baseStaticContext
* private:
SomeBaseClass::$somePrivateProperty : base static private
(__CLASS__)::$somePrivateProperty : base static private
self::$somePrivateProperty : base static private
* protected:
SomeBaseClass::$someProtectedProperty : base static protected
(__CLASS__)::$someProtectedProperty : base static protected
self::$someProtectedProperty : base static protected
static::$someProtectedProperty : base static protected
* public:
SomeBaseClass::$somePublicProperty : base static public
(__CLASS__)::$somePublicProperty : base static public
self::$somePublicProperty : base static public
static::$somePublicProperty : base static public
SomeBaseClass::baseDynamicContext
* private:
SomeBaseClass::$somePrivateProperty : base static private
(__CLASS__)::$somePrivateProperty : base static private
self::$somePrivateProperty : base static private
* protected:
SomeBaseClass::$someProtectedProperty : base static protected
(__CLASS__)::$someProtectedProperty : base static protected
self::$someProtectedProperty : base static protected
static::$someProtectedProperty : base static protected
* public:
SomeBaseClass::$somePublicProperty : base static public
(__CLASS__)::$somePublicProperty : base static public
self::$somePublicProperty : base static public
static::$somePublicProperty : base static public
SomeDerivedClass::derivedStaticContext
* protected:
SomeBaseClass::$someProtectedProperty : base static protected
parent::$someProtectedProperty : base static protected
SomeDerivedClass::$someProtectedProperty : base static protected
(__CLASS__)::$someProtectedProperty : base static protected
self::$someProtectedProperty : base static protected
static::$someProtectedProperty : base static protected
* public:
SomeBaseClass::$somePublicProperty : base static public
parent::$somePublicProperty : base static public
SomeDerivedClass::$somePublicProperty : base static public
(__CLASS__)::$somePublicProperty : base static public
self::$somePublicProperty : base static public
static::$somePublicProperty : base static public
SomeDerivedClass::derivedDynamicContext
* protected:
SomeBaseClass::$someProtectedProperty : base static protected
parent::$someProtectedProperty : base static protected
SomeDerivedClass::$someProtectedProperty : base static protected
(__CLASS__)::$someProtectedProperty : base static protected
self::$someProtectedProperty : base static protected
static::$someProtectedProperty : base static protected
* public:
SomeBaseClass::$somePublicProperty : base static public
parent::$somePublicProperty : base static public
SomeDerivedClass::$somePublicProperty : base static public
(__CLASS__)::$somePublicProperty : base static public
self::$somePublicProperty : base static public
static::$somePublicProperty : base static public
# SomeDerivedOverridingClass:
SomeBaseClass::baseStaticContext
* private:
SomeBaseClass::$somePrivateProperty : base static private
(__CLASS__)::$somePrivateProperty : base static private
self::$somePrivateProperty : base static private
* protected:
SomeBaseClass::$someProtectedProperty : base static protected
(__CLASS__)::$someProtectedProperty : base static protected
self::$someProtectedProperty : base static protected
static::$someProtectedProperty : derived static protected
* public:
SomeBaseClass::$somePublicProperty : base static public
(__CLASS__)::$somePublicProperty : base static public
self::$somePublicProperty : base static public
static::$somePublicProperty : derived static public
SomeBaseClass::baseDynamicContext
* private:
SomeBaseClass::$somePrivateProperty : base static private
(__CLASS__)::$somePrivateProperty : base static private
self::$somePrivateProperty : base static private
* protected:
SomeBaseClass::$someProtectedProperty : base static protected
(__CLASS__)::$someProtectedProperty : base static protected
self::$someProtectedProperty : base static protected
static::$someProtectedProperty : derived static protected
* public:
SomeBaseClass::$somePublicProperty : base static public
(__CLASS__)::$somePublicProperty : base static public
self::$somePublicProperty : base static public
static::$somePublicProperty : derived static public
SomeDerivedOverridingClass::derivedOverridingStaticContext
* private:
SomeDerivedOverridingClass::$somePrivateProperty : derived shadowed static private
(__CLASS__)::$somePrivateProperty : derived shadowed static private
self::$somePrivateProperty : derived shadowed static private
static::$somePrivateProperty : derived shadowed static private
* protected:
SomeBaseClass::$someProtectedProperty : base static protected
parent::$someProtectedProperty : base static protected
SomeDerivedOverridingClass::$someProtectedProperty : derived static protected
(__CLASS__)::$someProtectedProperty : derived static protected
self::$someProtectedProperty : derived static protected
static::$someProtectedProperty : derived static protected
* public:
SomeBaseClass::$somePublicProperty : base static public
parent::$somePublicProperty : base static public
SomeDerivedOverridingClass::$somePublicProperty : derived static public
(__CLASS__)::$somePublicProperty : derived static public
self::$somePublicProperty : derived static public
static::$somePublicProperty : derived static public
SomeDerivedOverridingClass::derivedOverridingDynamicContext
* private:
SomeDerivedOverridingClass::$somePrivateProperty : derived shadowed static private
(__CLASS__)::$somePrivateProperty : derived shadowed static private
self::$somePrivateProperty : derived shadowed static private
static::$somePrivateProperty : derived shadowed static private
* protected:
SomeBaseClass::$someProtectedProperty : base static protected
parent::$someProtectedProperty : base static protected
SomeDerivedOverridingClass::$someProtectedProperty : derived static protected
(__CLASS__)::$someProtectedProperty : derived static protected
self::$someProtectedProperty : derived static protected
static::$someProtectedProperty : derived static protected
* public:
SomeBaseClass::$somePublicProperty : base static public
parent::$somePublicProperty : base static public
SomeDerivedOverridingClass::$somePublicProperty : derived static public
(__CLASS__)::$somePublicProperty : derived static public
self::$somePublicProperty : derived static public
static::$somePublicProperty : derived static public
Source code: Example
Example: Class readonly property access with visibility
<?php
class SomeBaseClass
{
public function __construct(
public readonly string $somePublicProperty = 'base readonly public',
protected readonly string $someProtectedProperty = 'base readonly protected',
private readonly string $somePrivateProperty = 'base readonly private',
) {
}
public function baseDynamicContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. '$this->somePrivateProperty : ' . $this->somePrivateProperty . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedProperty : ' . $this->someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicProperty : ' . $this->somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedClass extends SomeBaseClass
{
public function derivedDynamicContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedProperty : ' . $this->someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicProperty : ' . $this->somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedOverridingClass extends SomeBaseClass
{
private readonly string $somePrivateProperty; // It's not overriding but rather shadowing!
// It's completly new property - very own property of the derived class
// because private member of the base class is unaccessible and not visible for the derived class.
public function __construct()
{
// It mus be called for initialize properties before reading
// from the derived class object calling base class method.
parent::__construct(
'derived readonly public',
'derived readonly protected',
'derived readonly private',
);
$this->somePrivateProperty = 'derived shadowed readonly private';
}
public function derivedOverridingDynamicContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. '$this->somePrivateProperty : ' . $this->somePrivateProperty . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedProperty : ' . $this->someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicProperty : ' . $this->somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
$someObject = new SomeDerivedClass();
print("# SomeDerivedClass:\n\n");
$someObject->baseDynamicContext();
$someObject->derivedDynamicContext();
$otherObject = new SomeDerivedOverridingClass();
print("# SomeDerivedOverridingClass:\n\n");
$otherObject->baseDynamicContext();
$otherObject->derivedOverridingDynamicContext();
Result (PHP 8.4):
# SomeDerivedClass:
SomeBaseClass::baseDynamicContext
* private:
$this->somePrivateProperty : base readonly private
* protected:
$this->someProtectedProperty : base readonly protected
* public:
$this->somePublicProperty : base readonly public
SomeDerivedClass::derivedDynamicContext
* protected:
$this->someProtectedProperty : base readonly protected
* public:
$this->somePublicProperty : base readonly public
# SomeDerivedOverridingClass:
SomeBaseClass::baseDynamicContext
* private:
$this->somePrivateProperty : derived readonly private
* protected:
$this->someProtectedProperty : derived readonly protected
* public:
$this->somePublicProperty : derived readonly public
SomeDerivedOverridingClass::derivedOverridingDynamicContext
* private:
$this->somePrivateProperty : derived shadowed readonly private
* protected:
$this->someProtectedProperty : derived readonly protected
* public:
$this->somePublicProperty : derived readonly public
Source code: Example
<?php
class SomeBaseClass
{
final public $someFinalPublicProperty = 'base final public';
final protected $someFinalProtectedProperty = 'base final protected';
public function baseDynamicContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. '$this->someFinalProtectedProperty : ' . $this->someFinalProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->someFinalPublicProperty : ' . $this->someFinalPublicProperty . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedClass extends SomeBaseClass
{
public function derivedDynamicContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. '$this->someFinalProtectedProperty : ' . $this->someFinalProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->someFinalPublicProperty : ' . $this->someFinalPublicProperty . PHP_EOL
. PHP_EOL
);
}
}
$someObject = new SomeDerivedClass();
print("# SomeDerivedClass:\n\n");
$someObject->baseDynamicContext();
$someObject->derivedDynamicContext();
Result (PHP 8.4):
# SomeDerivedClass:
SomeBaseClass::baseDynamicContext
* protected:
$this->someFinalProtectedProperty : base final protected
* public:
$this->someFinalPublicProperty : base final public
SomeDerivedClass::derivedDynamicContext
* protected:
$this->someFinalProtectedProperty : base final protected
* public:
$this->someFinalPublicProperty : base final public
Source code: Example
Example: Class method access with visibility
<?php
class SomeBaseClass
{
public function somePublicMethod()
{
return 'base public';
}
protected function someProtectedMethod()
{
return 'base protected';
}
private function somePrivateMethod()
{
return 'base private';
}
public function baseDynamicContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'SomeBaseClass::somePrivateMethod() : ' . SomeBaseClass::somePrivateMethod() . PHP_EOL
. '(__CLASS__)::somePrivateMethod() : ' . (__CLASS__)::somePrivateMethod() . PHP_EOL
. 'self::somePrivateMethod() : ' . self::somePrivateMethod() . PHP_EOL
// Cannot be called without error in the derived class:
// . 'static::somePrivateMethod() : ' . static::somePrivateMethod() . PHP_EOL
// Private members are unaccessible in the derived classes.
. '$this->somePrivateMethod() : ' . $this->somePrivateMethod() . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::someProtectedMethod() : ' . SomeBaseClass::someProtectedMethod() . PHP_EOL
. '(__CLASS__)::someProtectedMethod() : ' . (__CLASS__)::someProtectedMethod() . PHP_EOL
. 'self::someProtectedMethod() : ' . self::someProtectedMethod() . PHP_EOL
. 'static::someProtectedMethod() : ' . static::someProtectedMethod() . PHP_EOL
. '$this->someProtectedMethod() : ' . $this->someProtectedMethod() . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::somePublicMethod() : ' . SomeBaseClass::somePublicMethod() . PHP_EOL
. '(__CLASS__)::somePublicMethod() : ' . (__CLASS__)::somePublicMethod() . PHP_EOL
. 'self::somePublicMethod() : ' . self::somePublicMethod() . PHP_EOL
. 'static::somePublicMethod() : ' . static::somePublicMethod() . PHP_EOL
. '$this->somePublicMethod() : ' . $this->somePublicMethod() . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedClass extends SomeBaseClass
{
public function derivedDynamicContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::someProtectedMethod() : ' . SomeBaseClass::someProtectedMethod() . PHP_EOL
. 'parent::someProtectedMethod() : ' . parent::someProtectedMethod() . PHP_EOL
. 'SomeDerivedClass::someProtectedMethod() : ' . SomeDerivedClass::someProtectedMethod() . PHP_EOL
. '(__CLASS__)::someProtectedMethod() : ' . (__CLASS__)::someProtectedMethod() . PHP_EOL
. 'self::someProtectedMethod() : ' . self::someProtectedMethod() . PHP_EOL
. 'static::someProtectedMethod() : ' . static::someProtectedMethod() . PHP_EOL
. '$this->someProtectedMethod() : ' . $this->someProtectedMethod() . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::somePublicMethod() : ' . SomeBaseClass::somePublicMethod() . PHP_EOL
. 'parent::somePublicMethod() : ' . parent::somePublicMethod() . PHP_EOL
. 'SomeDerivedClass::somePublicMethod() : ' . SomeDerivedClass::somePublicMethod() . PHP_EOL
. '(__CLASS__)::somePublicMethod() : ' . (__CLASS__)::somePublicMethod() . PHP_EOL
. 'self::somePublicMethod() : ' . self::somePublicMethod() . PHP_EOL
. 'static::somePublicMethod() : ' . static::somePublicMethod() . PHP_EOL
. '$this->somePublicMethod() : ' . $this->somePublicMethod() . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedOverridingClass extends SomeBaseClass
{
public function somePublicMethod()
{
return 'derived public';
}
protected function someProtectedMethod()
{
return 'derived protected';
}
private function somePrivateMethod() // It's not overriding but rather shadowing!
// It's completly new method - very own method of the derived class
// because private member of the base class is unaccessible and not visible for the derived class.
{
return 'derived shadowed private';
}
public function derivedOverridingDynamicContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'SomeDerivedOverridingClass::somePrivateMethod() : ' . SomeDerivedOverridingClass::somePrivateMethod() . PHP_EOL
. '(__CLASS__)::somePrivateMethod() : ' . (__CLASS__)::somePrivateMethod() . PHP_EOL
. 'self::somePrivateMethod() : ' . self::somePrivateMethod() . PHP_EOL
// This will be dangerous in case of further inheritance:
. 'static::somePrivateMethod() : ' . static::somePrivateMethod() . PHP_EOL
. '$this->somePrivateMethod() : ' . $this->somePrivateMethod() . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::someProtectedMethod() : ' . SomeBaseClass::someProtectedMethod() . PHP_EOL
. 'parent::someProtectedMethod() : ' . parent::someProtectedMethod() . PHP_EOL
. 'SomeDerivedOverridingClass::someProtectedMethod() : ' . SomeDerivedOverridingClass::someProtectedMethod() . PHP_EOL
. '(__CLASS__)::someProtectedMethod() : ' . (__CLASS__)::someProtectedMethod() . PHP_EOL
. 'self::someProtectedMethod() : ' . self::someProtectedMethod() . PHP_EOL
. 'static::someProtectedMethod() : ' . static::someProtectedMethod() . PHP_EOL
. '$this->someProtectedMethod() : ' . $this->someProtectedMethod() . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::somePublicMethod() : ' . SomeBaseClass::somePublicMethod() . PHP_EOL
. 'parent::somePublicMethod() : ' . parent::somePublicMethod() . PHP_EOL
. 'SomeDerivedOverridingClass::somePublicMethod() : ' . SomeDerivedOverridingClass::somePublicMethod() . PHP_EOL
. '(__CLASS__)::somePublicMethod() : ' . (__CLASS__)::somePublicMethod() . PHP_EOL
. 'self::somePublicMethod() : ' . self::somePublicMethod() . PHP_EOL
. 'static::somePublicMethod() : ' . static::somePublicMethod() . PHP_EOL
. '$this->somePublicMethod() : ' . $this->somePublicMethod() . PHP_EOL
. PHP_EOL
);
}
}
$someObject = new SomeDerivedClass();
print("# SomeDerivedClass:\n\n");
$someObject->baseDynamicContext();
$someObject->derivedDynamicContext();
$otherObject = new SomeDerivedOverridingClass();
print("# SomeDerivedOverridingClass:\n\n");
$otherObject->baseDynamicContext();
$otherObject->derivedOverridingDynamicContext();
Result (PHP 8.4):
# SomeDerivedClass:
SomeBaseClass::baseDynamicContext
* private:
SomeBaseClass::somePrivateMethod() : base private
(__CLASS__)::somePrivateMethod() : base private
self::somePrivateMethod() : base private
$this->somePrivateMethod() : base private
* protected:
SomeBaseClass::someProtectedMethod() : base protected
(__CLASS__)::someProtectedMethod() : base protected
self::someProtectedMethod() : base protected
static::someProtectedMethod() : base protected
$this->someProtectedMethod() : base protected
* public:
SomeBaseClass::somePublicMethod() : base public
(__CLASS__)::somePublicMethod() : base public
self::somePublicMethod() : base public
static::somePublicMethod() : base public
$this->somePublicMethod() : base public
SomeDerivedClass::derivedDynamicContext
* protected:
SomeBaseClass::someProtectedMethod() : base protected
parent::someProtectedMethod() : base protected
SomeDerivedClass::someProtectedMethod() : base protected
(__CLASS__)::someProtectedMethod() : base protected
self::someProtectedMethod() : base protected
static::someProtectedMethod() : base protected
$this->someProtectedMethod() : base protected
* public:
SomeBaseClass::somePublicMethod() : base public
parent::somePublicMethod() : base public
SomeDerivedClass::somePublicMethod() : base public
(__CLASS__)::somePublicMethod() : base public
self::somePublicMethod() : base public
static::somePublicMethod() : base public
$this->somePublicMethod() : base public
# SomeDerivedOverridingClass:
SomeBaseClass::baseDynamicContext
* private:
SomeBaseClass::somePrivateMethod() : base private
(__CLASS__)::somePrivateMethod() : base private
self::somePrivateMethod() : base private
$this->somePrivateMethod() : base private
* protected:
SomeBaseClass::someProtectedMethod() : base protected
(__CLASS__)::someProtectedMethod() : base protected
self::someProtectedMethod() : base protected
static::someProtectedMethod() : derived protected
$this->someProtectedMethod() : derived protected
* public:
SomeBaseClass::somePublicMethod() : base public
(__CLASS__)::somePublicMethod() : base public
self::somePublicMethod() : base public
static::somePublicMethod() : derived public
$this->somePublicMethod() : derived public
SomeDerivedOverridingClass::derivedOverridingDynamicContext
* private:
SomeDerivedOverridingClass::somePrivateMethod() : derived shadowed private
(__CLASS__)::somePrivateMethod() : derived shadowed private
self::somePrivateMethod() : derived shadowed private
static::somePrivateMethod() : derived shadowed private
$this->somePrivateMethod() : derived shadowed private
* protected:
SomeBaseClass::someProtectedMethod() : base protected
parent::someProtectedMethod() : base protected
SomeDerivedOverridingClass::someProtectedMethod() : derived protected
(__CLASS__)::someProtectedMethod() : derived protected
self::someProtectedMethod() : derived protected
static::someProtectedMethod() : derived protected
$this->someProtectedMethod() : derived protected
* public:
SomeBaseClass::somePublicMethod() : base public
parent::somePublicMethod() : base public
SomeDerivedOverridingClass::somePublicMethod() : derived public
(__CLASS__)::somePublicMethod() : derived public
self::somePublicMethod() : derived public
static::somePublicMethod() : derived public
$this->somePublicMethod() : derived public
Source code: Example
Example: Class method access with visibility
<?php
class SomeBaseClass
{
public static function somePublicStaticMethod()
{
return 'base public';
}
protected static function someProtectedStaticMethod()
{
return 'base protected';
}
private static function somePrivateStaticMethod()
{
return 'base private';
}
public static function baseStaticContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'SomeBaseClass::somePrivateStaticMethod() : ' . SomeBaseClass::somePrivateStaticMethod() . PHP_EOL
. '(__CLASS__)::somePrivateStaticMethod() : ' . (__CLASS__)::somePrivateStaticMethod() . PHP_EOL
. 'self::somePrivateStaticMethod() : ' . self::somePrivateStaticMethod() . PHP_EOL
// Cannot be called without error in the derived class:
// . 'static::somePrivateStaticMethod() : ' . static::somePrivateStaticMethod() . PHP_EOL
// Private members are unaccessible in the derived classes.
. "\n* protected:\n\n"
. 'SomeBaseClass::someProtectedStaticMethod() : ' . SomeBaseClass::someProtectedStaticMethod() . PHP_EOL
. '(__CLASS__)::someProtectedStaticMethod() : ' . (__CLASS__)::someProtectedStaticMethod() . PHP_EOL
. 'self::someProtectedStaticMethod() : ' . self::someProtectedStaticMethod() . PHP_EOL
. 'static::someProtectedStaticMethod() : ' . static::someProtectedStaticMethod() . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::somePublicStaticMethod() : ' . SomeBaseClass::somePublicStaticMethod() . PHP_EOL
. '(__CLASS__)::somePublicStaticMethod() : ' . (__CLASS__)::somePublicStaticMethod() . PHP_EOL
. 'self::somePublicStaticMethod() : ' . self::somePublicStaticMethod() . PHP_EOL
. 'static::somePublicStaticMethod() : ' . static::somePublicStaticMethod() . PHP_EOL
. PHP_EOL
);
}
public function baseDynamicContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'SomeBaseClass::somePrivateStaticMethod() : ' . SomeBaseClass::somePrivateStaticMethod() . PHP_EOL
. '(__CLASS__)::somePrivateStaticMethod() : ' . (__CLASS__)::somePrivateStaticMethod() . PHP_EOL
. 'self::somePrivateStaticMethod() : ' . self::somePrivateStaticMethod() . PHP_EOL
// Cannot be called without error in the derived class:
// . 'static::somePrivateStaticMethod() : ' . static::somePrivateStaticMethod() . PHP_EOL
// Private members are unaccessible in the derived classes.
. '$this->somePrivateStaticMethod() : ' . $this->somePrivateStaticMethod() . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::someProtectedStaticMethod() : ' . SomeBaseClass::someProtectedStaticMethod() . PHP_EOL
. '(__CLASS__)::someProtectedStaticMethod() : ' . (__CLASS__)::someProtectedStaticMethod() . PHP_EOL
. 'self::someProtectedStaticMethod() : ' . self::someProtectedStaticMethod() . PHP_EOL
. 'static::someProtectedStaticMethod() : ' . static::someProtectedStaticMethod() . PHP_EOL
. '$this->someProtectedStaticMethod() : ' . $this->someProtectedStaticMethod() . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::somePublicStaticMethod() : ' . SomeBaseClass::somePublicStaticMethod() . PHP_EOL
. '(__CLASS__)::somePublicStaticMethod() : ' . (__CLASS__)::somePublicStaticMethod() . PHP_EOL
. 'self::somePublicStaticMethod() : ' . self::somePublicStaticMethod() . PHP_EOL
. 'static::somePublicStaticMethod() : ' . static::somePublicStaticMethod() . PHP_EOL
. '$this->somePublicStaticMethod() : ' . $this->somePublicStaticMethod() . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedClass extends SomeBaseClass
{
public static function derivedStaticContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::someProtectedStaticMethod() : ' . SomeBaseClass::someProtectedStaticMethod() . PHP_EOL
. 'parent::someProtectedStaticMethod() : ' . parent::someProtectedStaticMethod() . PHP_EOL
. 'SomeDerivedClass::someProtectedStaticMethod() : ' . SomeDerivedClass::someProtectedStaticMethod() . PHP_EOL
. '(__CLASS__)::someProtectedStaticMethod() : ' . (__CLASS__)::someProtectedStaticMethod() . PHP_EOL
. 'self::someProtectedStaticMethod() : ' . self::someProtectedStaticMethod() . PHP_EOL
. 'static::someProtectedStaticMethod() : ' . static::someProtectedStaticMethod() . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::somePublicStaticMethod() : ' . SomeBaseClass::somePublicStaticMethod() . PHP_EOL
. 'parent::somePublicStaticMethod() : ' . parent::somePublicStaticMethod() . PHP_EOL
. 'SomeDerivedClass::somePublicStaticMethod() : ' . SomeDerivedClass::somePublicStaticMethod() . PHP_EOL
. '(__CLASS__)::somePublicStaticMethod() : ' . (__CLASS__)::somePublicStaticMethod() . PHP_EOL
. 'self::somePublicStaticMethod() : ' . self::somePublicStaticMethod() . PHP_EOL
. 'static::somePublicStaticMethod() : ' . static::somePublicStaticMethod() . PHP_EOL
. PHP_EOL
);
}
public function derivedDynamicContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::someProtectedStaticMethod() : ' . SomeBaseClass::someProtectedStaticMethod() . PHP_EOL
. 'parent::someProtectedStaticMethod() : ' . parent::someProtectedStaticMethod() . PHP_EOL
. 'SomeDerivedClass::someProtectedStaticMethod() : ' . SomeDerivedClass::someProtectedStaticMethod() . PHP_EOL
. '(__CLASS__)::someProtectedStaticMethod() : ' . (__CLASS__)::someProtectedStaticMethod() . PHP_EOL
. 'self::someProtectedStaticMethod() : ' . self::someProtectedStaticMethod() . PHP_EOL
. 'static::someProtectedStaticMethod() : ' . static::someProtectedStaticMethod() . PHP_EOL
. '$this->someProtectedStaticMethod() : ' . $this->someProtectedStaticMethod() . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::somePublicStaticMethod() : ' . SomeBaseClass::somePublicStaticMethod() . PHP_EOL
. 'parent::somePublicStaticMethod() : ' . parent::somePublicStaticMethod() . PHP_EOL
. 'SomeDerivedClass::somePublicStaticMethod() : ' . SomeDerivedClass::somePublicStaticMethod() . PHP_EOL
. '(__CLASS__)::somePublicStaticMethod() : ' . (__CLASS__)::somePublicStaticMethod() . PHP_EOL
. 'self::somePublicStaticMethod() : ' . self::somePublicStaticMethod() . PHP_EOL
. 'static::somePublicStaticMethod() : ' . static::somePublicStaticMethod() . PHP_EOL
. '$this->somePublicStaticMethod() : ' . $this->somePublicStaticMethod() . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedOverridingClass extends SomeBaseClass
{
public static function somePublicStaticMethod()
{
return 'derived public';
}
protected static function someProtectedStaticMethod()
{
return 'derived protected';
}
private static function somePrivateStaticMethod()
{
return 'derived shadowed private';
} // It's not overriding but rather shadowing!
// It's completly new method - very own method of the derived class
// because private member of the base class is unaccessible and not visible for the derived class.
public static function derivedOverridingStaticContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'SomeDerivedOverridingClass::somePrivateStaticMethod() : ' . SomeDerivedOverridingClass::somePrivateStaticMethod() . PHP_EOL
. '(__CLASS__)::somePrivateStaticMethod() : ' . (__CLASS__)::somePrivateStaticMethod() . PHP_EOL
. 'self::somePrivateStaticMethod() : ' . self::somePrivateStaticMethod() . PHP_EOL
// This will be dangerous in case of further inheritance:
. 'static::somePrivateStaticMethod() : ' . static::somePrivateStaticMethod() . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::someProtectedStaticMethod() : ' . SomeBaseClass::someProtectedStaticMethod() . PHP_EOL
. 'parent::someProtectedStaticMethod() : ' . parent::someProtectedStaticMethod() . PHP_EOL
. 'SomeDerivedOverridingClass::someProtectedStaticMethod() : ' . SomeDerivedOverridingClass::someProtectedStaticMethod() . PHP_EOL
. '(__CLASS__)::someProtectedStaticMethod() : ' . (__CLASS__)::someProtectedStaticMethod() . PHP_EOL
. 'self::someProtectedStaticMethod() : ' . self::someProtectedStaticMethod() . PHP_EOL
. 'static::someProtectedStaticMethod() : ' . static::someProtectedStaticMethod() . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::somePublicStaticMethod() : ' . SomeBaseClass::somePublicStaticMethod() . PHP_EOL
. 'parent::somePublicStaticMethod() : ' . parent::somePublicStaticMethod() . PHP_EOL
. 'SomeDerivedOverridingClass::somePublicStaticMethod() : ' . SomeDerivedOverridingClass::somePublicStaticMethod() . PHP_EOL
. '(__CLASS__)::somePublicStaticMethod() : ' . (__CLASS__)::somePublicStaticMethod() . PHP_EOL
. 'self::somePublicStaticMethod() : ' . self::somePublicStaticMethod() . PHP_EOL
. 'static::somePublicStaticMethod() : ' . static::somePublicStaticMethod() . PHP_EOL
. PHP_EOL
);
}
public static function derivedOverridingDynamicContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. 'SomeDerivedOverridingClass::somePrivateStaticMethod() : ' . SomeDerivedOverridingClass::somePrivateStaticMethod() . PHP_EOL
. '(__CLASS__)::somePrivateStaticMethod() : ' . (__CLASS__)::somePrivateStaticMethod() . PHP_EOL
. 'self::somePrivateStaticMethod() : ' . self::somePrivateStaticMethod() . PHP_EOL
// This will be dangerous in case of further inheritance:
. 'static::somePrivateStaticMethod() : ' . static::somePrivateStaticMethod() . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::someProtectedStaticMethod() : ' . SomeBaseClass::someProtectedStaticMethod() . PHP_EOL
. 'parent::someProtectedStaticMethod() : ' . parent::someProtectedStaticMethod() . PHP_EOL
. 'SomeDerivedOverridingClass::someProtectedStaticMethod() : ' . SomeDerivedOverridingClass::someProtectedStaticMethod() . PHP_EOL
. '(__CLASS__)::someProtectedStaticMethod() : ' . (__CLASS__)::someProtectedStaticMethod() . PHP_EOL
. 'self::someProtectedStaticMethod() : ' . self::someProtectedStaticMethod() . PHP_EOL
. 'static::someProtectedStaticMethod() : ' . static::someProtectedStaticMethod() . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::somePublicStaticMethod() : ' . SomeBaseClass::somePublicStaticMethod() . PHP_EOL
. 'parent::somePublicStaticMethod() : ' . parent::somePublicStaticMethod() . PHP_EOL
. 'SomeDerivedOverridingClass::somePublicStaticMethod() : ' . SomeDerivedOverridingClass::somePublicStaticMethod() . PHP_EOL
. '(__CLASS__)::somePublicStaticMethod() : ' . (__CLASS__)::somePublicStaticMethod() . PHP_EOL
. 'self::somePublicStaticMethod() : ' . self::somePublicStaticMethod() . PHP_EOL
. 'static::somePublicStaticMethod() : ' . static::somePublicStaticMethod() . PHP_EOL
. PHP_EOL
);
}
}
$someObject = new SomeDerivedClass();
print("# SomeDerivedClass:\n\n");
$someObject->baseStaticContext();
$someObject->baseDynamicContext();
$someObject->derivedStaticContext();
$someObject->derivedDynamicContext();
$otherObject = new SomeDerivedOverridingClass();
print("# SomeDerivedOverridingClass:\n\n");
$otherObject->baseStaticContext();
$otherObject->baseDynamicContext();
$otherObject->derivedOverridingStaticContext();
$otherObject->derivedOverridingDynamicContext();
Result (PHP 8.4):
# SomeDerivedClass:
SomeBaseClass::baseStaticContext
* private:
SomeBaseClass::somePrivateStaticMethod() : base private
(__CLASS__)::somePrivateStaticMethod() : base private
self::somePrivateStaticMethod() : base private
* protected:
SomeBaseClass::someProtectedStaticMethod() : base protected
(__CLASS__)::someProtectedStaticMethod() : base protected
self::someProtectedStaticMethod() : base protected
static::someProtectedStaticMethod() : base protected
* public:
SomeBaseClass::somePublicStaticMethod() : base public
(__CLASS__)::somePublicStaticMethod() : base public
self::somePublicStaticMethod() : base public
static::somePublicStaticMethod() : base public
SomeBaseClass::baseDynamicContext
* private:
SomeBaseClass::somePrivateStaticMethod() : base private
(__CLASS__)::somePrivateStaticMethod() : base private
self::somePrivateStaticMethod() : base private
$this->somePrivateStaticMethod() : base private
* protected:
SomeBaseClass::someProtectedStaticMethod() : base protected
(__CLASS__)::someProtectedStaticMethod() : base protected
self::someProtectedStaticMethod() : base protected
static::someProtectedStaticMethod() : base protected
$this->someProtectedStaticMethod() : base protected
* public:
SomeBaseClass::somePublicStaticMethod() : base public
(__CLASS__)::somePublicStaticMethod() : base public
self::somePublicStaticMethod() : base public
static::somePublicStaticMethod() : base public
$this->somePublicStaticMethod() : base public
SomeDerivedClass::derivedStaticContext
* protected:
SomeBaseClass::someProtectedStaticMethod() : base protected
parent::someProtectedStaticMethod() : base protected
SomeDerivedClass::someProtectedStaticMethod() : base protected
(__CLASS__)::someProtectedStaticMethod() : base protected
self::someProtectedStaticMethod() : base protected
static::someProtectedStaticMethod() : base protected
* public:
SomeBaseClass::somePublicStaticMethod() : base public
parent::somePublicStaticMethod() : base public
SomeDerivedClass::somePublicStaticMethod() : base public
(__CLASS__)::somePublicStaticMethod() : base public
self::somePublicStaticMethod() : base public
static::somePublicStaticMethod() : base public
SomeDerivedClass::derivedDynamicContext
* protected:
SomeBaseClass::someProtectedStaticMethod() : base protected
parent::someProtectedStaticMethod() : base protected
SomeDerivedClass::someProtectedStaticMethod() : base protected
(__CLASS__)::someProtectedStaticMethod() : base protected
self::someProtectedStaticMethod() : base protected
static::someProtectedStaticMethod() : base protected
$this->someProtectedStaticMethod() : base protected
* public:
SomeBaseClass::somePublicStaticMethod() : base public
parent::somePublicStaticMethod() : base public
SomeDerivedClass::somePublicStaticMethod() : base public
(__CLASS__)::somePublicStaticMethod() : base public
self::somePublicStaticMethod() : base public
static::somePublicStaticMethod() : base public
$this->somePublicStaticMethod() : base public
# SomeDerivedOverridingClass:
SomeBaseClass::baseStaticContext
* private:
SomeBaseClass::somePrivateStaticMethod() : base private
(__CLASS__)::somePrivateStaticMethod() : base private
self::somePrivateStaticMethod() : base private
* protected:
SomeBaseClass::someProtectedStaticMethod() : base protected
(__CLASS__)::someProtectedStaticMethod() : base protected
self::someProtectedStaticMethod() : base protected
static::someProtectedStaticMethod() : derived protected
* public:
SomeBaseClass::somePublicStaticMethod() : base public
(__CLASS__)::somePublicStaticMethod() : base public
self::somePublicStaticMethod() : base public
static::somePublicStaticMethod() : derived public
SomeBaseClass::baseDynamicContext
* private:
SomeBaseClass::somePrivateStaticMethod() : base private
(__CLASS__)::somePrivateStaticMethod() : base private
self::somePrivateStaticMethod() : base private
$this->somePrivateStaticMethod() : base private
* protected:
SomeBaseClass::someProtectedStaticMethod() : base protected
(__CLASS__)::someProtectedStaticMethod() : base protected
self::someProtectedStaticMethod() : base protected
static::someProtectedStaticMethod() : derived protected
$this->someProtectedStaticMethod() : derived protected
* public:
SomeBaseClass::somePublicStaticMethod() : base public
(__CLASS__)::somePublicStaticMethod() : base public
self::somePublicStaticMethod() : base public
static::somePublicStaticMethod() : derived public
$this->somePublicStaticMethod() : derived public
SomeDerivedOverridingClass::derivedOverridingStaticContext
* private:
SomeDerivedOverridingClass::somePrivateStaticMethod() : derived shadowed private
(__CLASS__)::somePrivateStaticMethod() : derived shadowed private
self::somePrivateStaticMethod() : derived shadowed private
static::somePrivateStaticMethod() : derived shadowed private
* protected:
SomeBaseClass::someProtectedStaticMethod() : base protected
parent::someProtectedStaticMethod() : base protected
SomeDerivedOverridingClass::someProtectedStaticMethod() : derived protected
(__CLASS__)::someProtectedStaticMethod() : derived protected
self::someProtectedStaticMethod() : derived protected
static::someProtectedStaticMethod() : derived protected
* public:
SomeBaseClass::somePublicStaticMethod() : base public
parent::somePublicStaticMethod() : base public
SomeDerivedOverridingClass::somePublicStaticMethod() : derived public
(__CLASS__)::somePublicStaticMethod() : derived public
self::somePublicStaticMethod() : derived public
static::somePublicStaticMethod() : derived public
SomeDerivedOverridingClass::derivedOverridingDynamicContext
* private:
SomeDerivedOverridingClass::somePrivateStaticMethod() : derived shadowed private
(__CLASS__)::somePrivateStaticMethod() : derived shadowed private
self::somePrivateStaticMethod() : derived shadowed private
static::somePrivateStaticMethod() : derived shadowed private
* protected:
SomeBaseClass::someProtectedStaticMethod() : base protected
parent::someProtectedStaticMethod() : base protected
SomeDerivedOverridingClass::someProtectedStaticMethod() : derived protected
(__CLASS__)::someProtectedStaticMethod() : derived protected
self::someProtectedStaticMethod() : derived protected
static::someProtectedStaticMethod() : derived protected
* public:
SomeBaseClass::somePublicStaticMethod() : base public
parent::somePublicStaticMethod() : base public
SomeDerivedOverridingClass::somePublicStaticMethod() : derived public
(__CLASS__)::somePublicStaticMethod() : derived public
self::somePublicStaticMethod() : derived public
static::somePublicStaticMethod() : derived public
Source code: Example
Example: Class final method access with visibility
<?php
class SomeBaseClass
{
final public function someFinalPublicMethod()
{
return 'base final public';
}
final protected function someFinalProtectedMethod()
{
return 'base final protected';
}
public function baseDynamicContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::someFinalProtectedMethod() : ' . SomeBaseClass::someFinalProtectedMethod() . PHP_EOL
. '(__CLASS__)::someFinalProtectedMethod() : ' . (__CLASS__)::someFinalProtectedMethod() . PHP_EOL
. 'self::someFinalProtectedMethod() : ' . self::someFinalProtectedMethod() . PHP_EOL
. 'static::someFinalProtectedMethod() : ' . static::someFinalProtectedMethod() . PHP_EOL
. '$this->someFinalProtectedMethod() : ' . $this->someFinalProtectedMethod() . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::someFinalPublicMethod() : ' . SomeBaseClass::someFinalPublicMethod() . PHP_EOL
. '(__CLASS__)::someFinalPublicMethod() : ' . (__CLASS__)::someFinalPublicMethod() . PHP_EOL
. 'self::someFinalPublicMethod() : ' . self::someFinalPublicMethod() . PHP_EOL
. 'static::someFinalPublicMethod() : ' . static::someFinalPublicMethod() . PHP_EOL
. '$this->someFinalPublicMethod() : ' . $this->someFinalPublicMethod() . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedClass extends SomeBaseClass
{
public function derivedDynamicContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. 'SomeBaseClass::someFinalProtectedMethod() : ' . SomeBaseClass::someFinalProtectedMethod() . PHP_EOL
. 'parent::someFinalProtectedMethod() : ' . parent::someFinalProtectedMethod() . PHP_EOL
. 'SomeDerivedClass::someFinalProtectedMethod() : ' . SomeDerivedClass::someFinalProtectedMethod() . PHP_EOL
. '(__CLASS__)::someFinalProtectedMethod() : ' . (__CLASS__)::someFinalProtectedMethod() . PHP_EOL
. 'self::someFinalProtectedMethod() : ' . self::someFinalProtectedMethod() . PHP_EOL
. 'static::someFinalProtectedMethod() : ' . static::someFinalProtectedMethod() . PHP_EOL
. '$this->someFinalProtectedMethod() : ' . $this->someFinalProtectedMethod() . PHP_EOL
. "\n* public:\n\n"
. 'SomeBaseClass::someFinalPublicMethod() : ' . SomeBaseClass::someFinalPublicMethod() . PHP_EOL
. 'parent::someFinalPublicMethod() : ' . parent::someFinalPublicMethod() . PHP_EOL
. 'SomeDerivedClass::someFinalPublicMethod() : ' . SomeDerivedClass::someFinalPublicMethod() . PHP_EOL
. '(__CLASS__)::someFinalPublicMethod() : ' . (__CLASS__)::someFinalPublicMethod() . PHP_EOL
. 'self::someFinalPublicMethod() : ' . self::someFinalPublicMethod() . PHP_EOL
. 'static::someFinalPublicMethod() : ' . static::someFinalPublicMethod() . PHP_EOL
. '$this->someFinalPublicMethod() : ' . $this->someFinalPublicMethod() . PHP_EOL
. PHP_EOL
);
}
}
$someObject = new SomeDerivedClass();
print("# SomeDerivedClass:\n\n");
$someObject->baseDynamicContext();
$someObject->derivedDynamicContext();
Result (PHP 8.4):
# SomeDerivedClass:
SomeBaseClass::baseDynamicContext
* protected:
SomeBaseClass::someFinalProtectedMethod() : base final protected
(__CLASS__)::someFinalProtectedMethod() : base final protected
self::someFinalProtectedMethod() : base final protected
static::someFinalProtectedMethod() : base final protected
$this->someFinalProtectedMethod() : base final protected
* public:
SomeBaseClass::someFinalPublicMethod() : base final public
(__CLASS__)::someFinalPublicMethod() : base final public
self::someFinalPublicMethod() : base final public
static::someFinalPublicMethod() : base final public
$this->someFinalPublicMethod() : base final public
SomeDerivedClass::derivedDynamicContext
* protected:
SomeBaseClass::someFinalProtectedMethod() : base final protected
parent::someFinalProtectedMethod() : base final protected
SomeDerivedClass::someFinalProtectedMethod() : base final protected
(__CLASS__)::someFinalProtectedMethod() : base final protected
self::someFinalProtectedMethod() : base final protected
static::someFinalProtectedMethod() : base final protected
$this->someFinalProtectedMethod() : base final protected
* public:
SomeBaseClass::someFinalPublicMethod() : base final public
parent::someFinalPublicMethod() : base final public
SomeDerivedClass::someFinalPublicMethod() : base final public
(__CLASS__)::someFinalPublicMethod() : base final public
self::someFinalPublicMethod() : base final public
static::someFinalPublicMethod() : base final public
$this->someFinalPublicMethod() : base final public
Source code: Example
A hook in a child class may access the parent class’s property using the parent::$prop keyword, followed by the desired hook. For example, parent::$propName::get(). It may be read as “access the prop defined on the parent class, and then run its get operation (or set operation, as appropriate).
If not accessed this way, the parent class’s hook is ignored. This behavior is consistent with how all methods work. This also offers a way to access the parent class’s storage, if any. If there is no hook on the parent property, its default get/set behavior will be used. Hooks may not access any other hook except their own parent on their own property.
The example above could be rewritten as follows, which would allow for the Point class to add its own set hook in the future without issues (in the previous example, a hook added to the parent class would be ignored in the child).
Example: Parent hook access (set)
<?php
class Point
{
public int $x;
public int $y;
}
class PositivePoint extends Point
{
public int $x {
set {
if ($value < 0) {
throw new \InvalidArgumentException('Too small');
}
parent::$x::set($value);
}
}
}
?>
An example of overriding only a get hook could be:
Example: Parent hook access (get)
<?php
class Strings
{
public string $val;
}
class CaseFoldingStrings extends Strings
{
public bool $uppercase = true;
public string $val {
get => $this->uppercase
? strtoupper(parent::$val::get())
: strtolower(parent::$val::get());
}
}
?>
Example: Class property hook access with visibility
<?php
class SomeBaseClass
{
public $somePublicProperty = 'base public' {
get => $this->somePublicProperty . ' base hook';
}
protected $someProtectedProperty = 'base protected' {
get => $this->someProtectedProperty . ' base hook';
}
private $somePrivateProperty = 'base private' {
get => $this->somePrivateProperty . ' base hook';
}
public function baseDynamicContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. '$this->somePrivateProperty : ' . $this->somePrivateProperty . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedProperty : ' . $this->someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicProperty : ' . $this->somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedClass extends SomeBaseClass
{
public function derivedDynamicContext(): void
{
print(
__METHOD__ . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedProperty : ' . $this->someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicProperty : ' . $this->somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
class SomeDerivedOverridingClass extends SomeBaseClass
{
public $somePublicProperty = 'derived public' {
get => parent::$somePublicProperty::get() . ' + ' . $this->somePublicProperty . ' derived hook';
}
protected $someProtectedProperty = 'derived protected' {
get => parent::$someProtectedProperty::get() . ' + ' . $this->someProtectedProperty . ' derived hook';
}
private $somePrivateProperty = 'derived shadowed private' {
get => $this->somePrivateProperty . ' derived shadowed hook';
} // It's not overriding but rather shadowing!
// It's completly new property hook - very own property hook of the derived class
// because private member of the base class is unaccessible and not visible for the derived class.
public function derivedOverridingDynamicContext()
{
print(
__METHOD__ . PHP_EOL
. "\n* private:\n\n"
. '$this->somePrivateProperty : ' . $this->somePrivateProperty . PHP_EOL
. "\n* protected:\n\n"
. '$this->someProtectedProperty : ' . $this->someProtectedProperty . PHP_EOL
. "\n* public:\n\n"
. '$this->somePublicProperty : ' . $this->somePublicProperty . PHP_EOL
. PHP_EOL
);
}
}
$someObject = new SomeDerivedClass();
print("# SomeDerivedClass:\n\n");
$someObject->baseDynamicContext();
$someObject->derivedDynamicContext();
$otherObject = new SomeDerivedOverridingClass();
print("# SomeDerivedOverridingClass:\n\n");
$otherObject->baseDynamicContext();
$otherObject->derivedOverridingDynamicContext();
Result (PHP 8.4):
# SomeDerivedClass:
SomeBaseClass::baseDynamicContext
* private:
$this->somePrivateProperty : base private base hook
* protected:
$this->someProtectedProperty : base protected base hook
* public:
$this->somePublicProperty : base public base hook
SomeDerivedClass::derivedDynamicContext
* protected:
$this->someProtectedProperty : base protected base hook
* public:
$this->somePublicProperty : base public base hook
# SomeDerivedOverridingClass:
SomeBaseClass::baseDynamicContext
* private:
$this->somePrivateProperty : base private base hook
* protected:
$this->someProtectedProperty : derived protected base hook + derived protected derived hook
* public:
$this->somePublicProperty : derived public base hook + derived public derived hook
SomeDerivedOverridingClass::derivedOverridingDynamicContext
* private:
$this->somePrivateProperty : derived shadowed private derived shadowed hook
* protected:
$this->someProtectedProperty : derived protected base hook + derived protected derived hook
* public:
$this->somePublicProperty : derived public base hook + derived public derived hook
Source code: Example
Overloading in PHP provides means to dynamically create properties and methods. These dynamic entities are processed via magic methods one can establish in a class for various action types.
The overloading methods are invoked when interacting with properties or methods that have not been declared or are not visible in the current scope. The rest of this section will use the terms inaccessible properties and inaccessible methods to refer to this combination of declaration and visibility.
All overloading methods must be defined as public.
Note:
None of the arguments of these magic methods can be passed by reference.
Note:
PHP’s interpretation of overloading is different than most object-oriented languages. Overloading traditionally provides the ability to have multiple methods with the same name but different quantities and types of arguments.
public __set(string $name, mixed $value): voidpublic __get(string $name): mixedpublic __isset(string $name): boolpublic __unset(string $name): void__set() is run when writing data to inaccessible (protected or private) or non-existing properties.
__get() is utilized for reading data from inaccessible (protected or private) or non-existing properties.
__isset() is triggered by calling isset() or empty() on inaccessible (protected or private) or non-existing properties.
__unset() is invoked when unset() is used on inaccessible (protected or private) or non-existing properties.
The $name argument is the name of the property being interacted with. The __set() method’s $value argument specifies the value the $name‘ed property should be set to.
Property overloading only works in object context. These magic methods will not be triggered in static context. Therefore these methods should not be declared static. A warning is issued if one of the magic overloading methods is declared static.
Note:
The return value of __set() is ignored because of the way PHP processes the assignment operator. Similarly, __get() is never called when chaining assignments together like this:
$a = $obj->b = 8;
Note:
PHP will not call an overloaded method from within the same overloaded method. That means, for example, writing return $this->foo inside of __get() will return null and raise an E_WARNING if there is no foo property defined, rather than calling __get() a second time. However, overload methods may invoke other overload methods implicitly (such as __set() triggering __get()).
Example: Overloading properties via the __get(), __set(), __isset() and __unset() methods
<?php
class PropertyTest
{
/** Location for overloaded data. */
private $data = array();
/** Overloading not used on declared properties. */
public $declared = 1;
/** Overloading only used on this when accessed outside the class. */
private $hidden = 2;
public function __set($name, $value)
{
echo "Setting '$name' to '$value'\n";
$this->data[$name] = $value;
}
public function __get($name)
{
echo "Getting '$name'\n";
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
$trace = debug_backtrace();
trigger_error(
'Undefined property via __get(): ' . $name .
' in ' . $trace[0]['file'] .
' on line ' . $trace[0]['line'],
E_USER_NOTICE);
return null;
}
public function __isset($name)
{
echo "Is '$name' set?\n";
return isset($this->data[$name]);
}
public function __unset($name)
{
echo "Unsetting '$name'\n";
unset($this->data[$name]);
}
/** Not a magic method, just here for example. */
public function getHidden()
{
return $this->hidden;
}
}
$obj = new PropertyTest;
$obj->a = 1;
echo $obj->a . "\n\n";
var_dump(isset($obj->a));
unset($obj->a);
var_dump(isset($obj->a));
echo "\n";
echo $obj->declared . "\n\n";
echo "Let's experiment with the private property named 'hidden':\n";
echo "Privates are visible inside the class, so __get() not used...\n";
echo $obj->getHidden() . "\n";
echo "Privates not visible outside of class, so __get() is used...\n";
echo $obj->hidden . "\n";
?>
The above example will output:
Setting 'a' to '1'
Getting 'a'
1
Is 'a' set?
bool(true)
Unsetting 'a'
Is 'a' set?
bool(false)
1
Let's experiment with the private property named 'hidden':
Privates are visible inside the class, so __get() not used...
2
Privates not visible outside of class, so __get() is used...
Getting 'hidden'
Notice: Undefined property via __get(): hidden in <file> on line 70 in <file> on line 29
Example: Class property overloading
<?php
class SomeClass
{
private string $secret = 'secret';
private array $data = [];
public function __set(string $propertyName, mixed $propertyValue): void
{
$this->data[$propertyName] = $propertyValue;
}
public function __get(string $propertyName): mixed
{
if (! isset($this->data[$propertyName])) {
return null;
}
return $this->data[$propertyName];
}
public function __isset(string $propertyName): bool
{
return isset($this->data[$propertyName]);
}
public function __unset(string $propertyName): void
{
unset($this->data[$propertyName]);
}
}
$someObject = new SomeClass();
print(
'something exists? ' . (isset($someObject->something) ? 'yes' : 'no') . PHP_EOL
. 'secret exists? ' . (isset($someObject->secret) ? 'yes' : 'no') . PHP_EOL
. PHP_EOL
);
$someObject->something = 'orange';
$someObject->secret = 'lemon';
print(
'something exists? ' . (isset($someObject->something) ? 'yes' : 'no') . PHP_EOL
. 'secret exists? ' . (isset($someObject->secret) ? 'yes' : 'no') . PHP_EOL
. PHP_EOL
. 'something value: ' . $someObject->something . PHP_EOL
. 'secret value: ' . $someObject->secret . PHP_EOL
. PHP_EOL
);
var_dump($someObject);
print(PHP_EOL);
unset($someObject->something);
unset($someObject->secret);
print(
'something exists? ' . (isset($someObject->something) ? 'yes' : 'no') . PHP_EOL
. 'secret exists? ' . (isset($someObject->secret) ? 'yes' : 'no') . PHP_EOL
. PHP_EOL
);
Result (PHP 8.4):
something exists? no
secret exists? no
something exists? yes
secret exists? yes
something value: orange
secret value: lemon
object(SomeClass)#1 (2) {
["secret":"SomeClass":private]=>
string(6) "secret"
["data":"SomeClass":private]=>
array(2) {
["something"]=>
string(6) "orange"
["secret"]=>
string(5) "lemon"
}
}
something exists? no
secret exists? no
Source code: Example
public __call(string $name, array $arguments): mixedpublic static __callStatic(string $name, array $arguments): mixed__call() is triggered when invoking inaccessible methods in an object context.
__callStatic() is triggered when invoking inaccessible methods in a static context.
The $name argument is the name of the method being called. The $arguments argument is an enumerated array containing the parameters passed to the $name‘ed method.
Example: Overloading methods via the __call() and __callStatic() methods
<?php
class MethodTest
{
public function __call($name, $arguments)
{
// Note: value of $name is case sensitive.
echo "Calling object method '$name' "
. implode(', ', $arguments). "\n";
}
public static function __callStatic($name, $arguments)
{
// Note: value of $name is case sensitive.
echo "Calling static method '$name' "
. implode(', ', $arguments). "\n";
}
}
$obj = new MethodTest;
$obj->runTest('in object context');
MethodTest::runTest('in static context');
?>
The above example will output:
Calling object method 'runTest' in object context
Calling static method 'runTest' in static context
Example: Class method overloading
<?php
class SomeClass
{
private array $actions = [];
private const array ACTIONS = [
'multipling' => 'array_product',
];
function __construct()
{
$this->actions = [
'adding' => function($values) {
return array_sum($values);
},
];
}
public function __call(string $methodName, mixed $methodArguments): mixed
{
if (! isset($this->actions[$methodName])) {
return null;
}
return $this->actions[$methodName]($methodArguments);
}
public static function __callStatic(string $methodName, mixed $methodArguments): mixed
{
if (! isset(static::ACTIONS[$methodName])) {
return null;
}
return static::ACTIONS[$methodName]($methodArguments);
}
}
$someObject = new SomeClass();
$result = $someObject->adding(1, 2, 3);
print('1 + 2 + 3 = ' . $result . PHP_EOL . PHP_EOL);
$result = SomeClass::multipling(1, 2, 3);
print('1 * 2 * 3 = ' . $result . PHP_EOL . PHP_EOL);
Result (PHP 8.4):
1 + 2 + 3 = 6
1 * 2 * 3 = 6
Source code: Example
Example: Final class example
<?php
final class BaseClass {
public function test() {
echo "BaseClass::test() called\n";
}
// As the class is already final, the final keyword is redundant
final public function moreTesting() {
echo "BaseClass::moreTesting() called\n";
}
}
class ChildClass extends BaseClass {
}
// Results in Fatal error: Class ChildClass may not inherit from final class (BaseClass)
?>
Example: Final class
<?php
class SomeBaseClass
{
public function someMethod(): string
{
return 'basic';
}
}
final class SomeFinalClass
{
public function someMethod(): string
{
return 'final';
}
}
class SomeDerivedClass extends SomeBaseClass
{
public function someMethod(): string
{
return parent::someMethod() . ' -> derived';
}
}
$someObject = new SomeBaseClass();
print($someObject->someMethod() . PHP_EOL);
$otherObject = new SomeFinalClass();
print($otherObject->someMethod() . PHP_EOL);
$anotherObject = new SomeDerivedClass();
print($anotherObject->someMethod() . PHP_EOL);
Result (PHP 8.4):
basic
final
basic -> derived
Source code: Example
PHP implements a feature called late static bindings which can be used to reference the called class in a context of static inheritance.
More precisely, late static bindings work by storing the class named in the last non-forwarding call. In case of static method calls, this is the class explicitly named (usually the one on the left of the :: operator); in case of non static method calls, it is the class of the object. A forwarding call is a static one that is introduced by self::, parent::, static::, or, if going up in the class hierarchy, forward_static_call(). The function get_called_class() can be used to retrieve a string with the name of the called class and static:: introduces its scope.
This feature was named late static bindings with an internal perspective in mind. Late binding comes from the fact that static:: will not be resolved using the class where the method is defined but it will rather be computed using runtime information. It was also called a static binding as it can be used for (but is not limited to) static method calls.
Example: Static method call
<?php
class SomeClass
{
public static function display()
{
print("Hello, there!\n");
}
}
SomeClass::display();
Result (PHP 8.4):
Hello, there!
Source code: Example
Example: Non-static method call
<?php
class SomeClass
{
public function display()
{
print("Hello, there!\n");
}
}
$someObject = new SomeClass();
$someObject->display();
Result (PHP 8.4):
Hello, there!
Source code: Example
Example: Forwarding method call
<?php
class SomeBaseClass
{
public function display()
{
print("Hello, there!\n");
}
public function base()
{
self::display();
static::display();
}
}
class SomeDerivedClass extends SomeBaseClass
{
public function display()
{
print("Hello, here!\n");
}
public function derived()
{
parent::display();
self::display();
static::display();
}
}
$someObject = new SomeDerivedClass();
$someObject->base();
print(PHP_EOL);
$someObject->derived();
print(PHP_EOL);
Result (PHP 8.4):
Hello, there!
Hello, here!
Hello, there!
Hello, here!
Hello, here!
Source code: Example
Example: Non-forwarding method call
<?php
class SomeBaseClass
{
public function display()
{
print("Hello, there!\n");
}
public function base()
{
$this->display();
}
}
class SomeDerivedClass extends SomeBaseClass
{
public function display()
{
print("Hello, here!\n");
}
public function derived()
{
$this->display();
}
}
$someObject = new SomeDerivedClass();
$someObject->base();
print(PHP_EOL);
$someObject->derived();
print(PHP_EOL);
Result (PHP 8.4):
Hello, here!
Hello, here!
Source code: Example
Example: forward_static_call function
<?php
class SomeClass
{
public static function display()
{
print("Hello, far away!\n");
}
}
class OtherClass extends SomeClass
{
public static function display()
{
print("Hello, there!\n");
}
}
class AnotherClass extends OtherClass
{
public static function display()
{
print("Hello, here!\n");
}
public function method()
{
forward_static_call(['SomeClass', 'display']);
forward_static_call(['OtherClass', 'display']);
forward_static_call([parent::class, 'display']);
forward_static_call(['AnotherClass', 'display']);
forward_static_call([self::class, 'display']);
}
}
$someObject = new AnotherClass();
$someObject->method();
Result (PHP 8.4):
Hello, far away!
Hello, there!
Hello, there!
Hello, here!
Hello, here!
Source code: Example
self::Static references to the current class like self:: or __CLASS__ are resolved using the class in which the function belongs, as in where it was defined:
Example: self:: usage
<?php
class A
{
public static function who()
{
echo __CLASS__;
}
public static function test()
{
self::who();
}
}
class B extends A
{
public static function who()
{
echo __CLASS__;
}
}
B::test();
?>
The above example will output:
A
Late static bindings tries to solve that limitation by introducing a keyword that references the class that was initially called at runtime. Basically, a keyword that would allow referencing B from test() in the previous example. It was decided not to introduce a new keyword but rather use static that was already reserved.
Example: static:: simple usage
<?php
class A
{
public static function who()
{
echo __CLASS__;
}
public static function test()
{
static::who(); // Here comes Late Static Bindings
}
}
class B extends A
{
public static function who()
{
echo __CLASS__;
}
}
B::test();
?>
The above example will output:
B
Note:
In non-static contexts, the called class will be the class of the object instance. Since $this-> will try to call private methods from the same scope, using static:: may give different results. Another difference is that static:: can only refer to static properties.
Example: self:: vs static:: member referencing
<?php
class SomeClass
{
public static function display()
{
print("Hello, there!\n");
}
public function method()
{
self::display();
static::display();
}
}
class OtherClass extends SomeClass
{
public static function display()
{
print("Hello, here!\n");
}
}
$someObject = new OtherClass();
$someObject->method();
Result (PHP 8.4):
Hello, there!
Hello, here!
Source code: Example
Example: static:: usage in a non-static context
<?php
class A
{
private function foo()
{
echo "Success!\n";
}
public function test()
{
$this->foo();
static::foo();
}
}
class B extends A
{
/* foo() will be copied to B, hence its scope will still be A and
* the call be successful */
}
class C extends A
{
private function foo()
{
/* Original method is replaced; the scope of the new one is C */
}
}
$b = new B();
$b->test();
$c = new C();
try {
$c->test();
} catch (Error $e) {
echo $e->getMessage();
}
?>
The above example will output:
Success!
Success!
Success!
Call to private method C::foo() from scope A
Example: static:: vs $this-> member referencing
<?php
class SomeClass
{
public function publicDisplay()
{
print("Hello, public!\n");
}
private function privateDisplay()
{
print("Hello, private!\n");
}
public function method()
{
static::publicDisplay();
$this->publicDisplay();
$this->privateDisplay();
}
}
$someObject = new SomeClass();
$someObject->method();
Result (PHP 8.4):
Hello, public!
Hello, public!
Hello, private!
Source code: Example
Note:
Late static bindings resolution will stop at a fully resolved static call with no fallback. On the other hand, static calls using keywords like parent:: or self:: will forward the calling information.
Example: Forwarding and non-forwarding calls
<?php
class A
{
public static function foo()
{
static::who();
}
public static function who()
{
echo __CLASS__ . "\n";
}
}
class B extends A
{
public static function test()
{
A::foo();
parent::foo();
self::foo();
}
public static function who()
{
echo __CLASS__ . "\n";
}
}
class C extends B
{
public static function who()
{
echo __CLASS__ . "\n";
}
}
C::test();
?>
The above example will output:
A
C
C