The final keyword prevents child classes from overriding a method, property, or constant by prefixing the definition withfinal. If the class itself is being defined final then it cannot be extended.
Example #1 Final methods example
<?php
classBaseClass{
public functiontest() {
echo"BaseClass::test() called\n";
}
final public functionmoreTesting() {
echo"BaseClass::moreTesting() called\n";
}
}
classChildClassextendsBaseClass{
public functionmoreTesting() {
echo"ChildClass::moreTesting() called\n";
}
}
// Results in Fatal error: Cannot override final method BaseClass::moreTesting()
?>Example #2 Final class example
<?php
final classBaseClass{
public functiontest() {
echo"BaseClass::test() called\n";
}
// As the class is already final, the final keyword is redundant
final public functionmoreTesting() {
echo"BaseClass::moreTesting() called\n";
}
}
classChildClassextendsBaseClass{
}
// Results in Fatal error: Class ChildClass may not inherit from final class (BaseClass)
?>Example #3 Final property example as of PHP 8.4.0
<?php
classBaseClass{
final protectedstring $test;
}
classChildClassextendsBaseClass{
publicstring $test;
}
// Results in Fatal error: Cannot override final property BaseClass::$test
?>Example #4 Final constants example as of PHP 8.1.0
<?php
classFoo
{
final public constX="foo";
}
classBarextendsFoo
{
public constX="bar";
}
// Fatal error: Bar::X cannot override final constant Foo::X
?>Note: As of PHP 8.0.0, private methods may not be declared final except for theconstructor.
Note: A property that is declared
private(set)is implicitlyfinal.
Note for Java developers: the 'final' keyword is not used for class constants in PHP. We use the keyword 'const'.http://php.net/manual/en/language.oop5.constants.phpNote that you cannot ovverride final methods even if they are defined as private in parent class.Thus, the following example:<?phpclassparentClass{ final private functionsomeMethod() { }}classchildClassextendsparentClass{ private functionsomeMethod() { }}?>dies with error "Fatal error: Cannot override final method parentClass::someMethod() in ***.php on line 7"Such behaviour looks slight unexpected because in child class we cannot know, which private methods exists in a parent class and vice versa.So, remember that if you defined a private final method, you cannot place method with the same name in child class.@thomas at somewhere dot comThe 'final' keyword is extremely useful. Inheritance is also useful, but can be abused and becomes problematic in large applications. If you ever come across a finalized class or method that you wish to extend, write a decorator instead.<?phpfinal classFoo{ publicmethod doFoo() {// do something useful and return a result}}final classFooDecorator{ private$foo; public function__construct(Foo $foo) {$this->foo=$foo; } public functiondoFoo() {$result=$this->foo->doFoo();// ... customize result ...return$result; }}?>Class constants CAN be “finalised” since PHP8.1. To partly contradict to the most popular user contribution, that was written a long time ago, they were still absolutely right.You can use final methods to replace class constants. The reason for this is you cannot unit test a class constant used in another class in isolation because you cannot mock a constant. Final methods allow you to have the same functionality as a constant while keeping your code loosely coupled.Tight coupling example (bad to use constants):<?phpinterfaceFooInterface{}classFooimplementsFooInterface{ constBAR=1; public function__construct() { }}interfaceBazInterface{ public functiongetFooBar();}// This class cannot be unit tested in isolation because the actual class Foo must also be loaded to get the value of Foo::BARclassBazimplementsBazInterface{ private$foo; public function__construct(FooInterface $foo) {$this->foo=$foo; } public functiongetFooBar() { returnFoo::BAR; }}$foo= newFoo();$baz= newBaz($foo);$bar=$baz->getFooBar();?>Loose coupling example (eliminated constant usage):<?phpinterfaceFooInterface{ public functionbar();}classFooimplementsFooInterface{ public function__construct() { } final public functionbar() { return1; }}interfaceBazInterface{ public functiongetFooBar();}// This class can be unit tested in isolation because class Foo does not need to be loaded by mocking FooInterface and calling the final bar method.classBazimplementsBazInterface{ private$foo; public function__construct(FooInterface $foo) {$this->foo=$foo; } public functiongetFooBar() { return$this->foo->bar(); }}$foo= newFoo();$baz= newBaz($foo);$bar=$baz->getFooBar();?><?phpclassparentClass{ public functionsomeMethod() { }}classchildClassextendsparentClass{ public final functionsomeMethod() { }//override parent function}$class= newchildClass;$class->someMethod();//call the override function in chield class?>imo good to know:<?phpclassBaseClass{ protected static$var='i belong to BaseClass'; public static functiontest() { echo'<hr>'.'i am `'.__METHOD__.'()` and this is my var: `'.self::$var.'`<br>'; } public static functionchangeVar($val) {self::$var=$val; echo'<hr>'.'i am `'.__METHOD__.'()` and i just changed my $var to: `'.self::$var.'`<br>'; } final public static functiondontCopyMe($val) {self::$var=$val; echo'<hr>'.'i am `'.__METHOD__.'()` and i just changed my $var to: `'.self::$var.'`<br>'; }}classChildClassextendsBaseClass{ protected static$var='i belong to ChildClass'; public static functiontest() { echo'<hr>'.'i am `'.__METHOD__.'()` and this is my var: `'.self::$var.'`<br>'.'and this is my parent var: `'.parent::$var.'`'; } public static functionchangeVar($val) {self::$var=$val; echo'<hr>'.'i am `'.__METHOD__.'()` and i just changed my $var to: `'.self::$var.'`<br>'.'but the parent $var is still: `'.parent::$var.'`'; } public static functiondontCopyMe($val)// Fatal error: Cannot override final method BaseClass::dontCopyMe() in ...{self::$var=$val; echo'<hr>'.'i am `'.__METHOD__.'()` and i just changed my $var to: `'.self::$var.'`<br>'; }}BaseClass::test();// i am `BaseClass::test()` and this is my var: `i belong to BaseClass`ChildClass::test();// i am `ChildClass::test()` and this is my var: `i belong to ChildClass` // and this is my parent var: `i belong to BaseClass`ChildClass::changeVar('something new');// i am `ChildClass::changeVar()` and i just changed my $var to: `something new` // but the parent $var is still: `i belong to BaseClass`BaseClass::changeVar('something different');// i am `BaseClass::changeVar()` and i just changed my $var to: `something different`BaseClass::dontCopyMe('a text');// i am `BaseClass::dontCopyMe()` and i just changed my $var to: `a text`ChildClass::dontCopyMe('a text');// Fatal error: Cannot override final method BaseClass::dontCopyMe() in ...?>The use of final keyword is just like that occurs in JavaIn java final has three uses 1) prevent class Inheritance 2) prevent method overriding or redifination of method in subclass 3) and to declare constants But the third point seems to be missing from the PHP I guess, as i am a java developer Currently gaining competence in PHPThe behaviour of FINAL is not as serious as you may think. A little explample:<?phpclassA{ final private functionmethod(){} }classBextendsA{ private functionmethod(){}}?>Normally you would expect some of the following will happen:- An error that final and private keyword cannot be used together- No error as the private visibility says, that a method/var/etc. is only visible within the same classBut what happens is PHP is a little curios: "Cannot override final method A::method()"So its possible to deny method names in subclasses! Don't know if this is a good behavior, but maybe its useful for your purpose.When desiring a special class structure finalizing magic methods maybe helpful.<?phpabstract classA{ final public function__construnct(){ echo"A"; } }classBextendsA{ public function__construct(){ echo"B"; }}$b= newB();// outputs: PHP Fatal error: Cannot override final method a\A::__construct()?>"Note for Java developers: the 'final' keyword is not used for class constants in PHP. We use the keyword 'const'."http://php.net/manual/en/language.oop5.constants.phpThis is more or less true, regardless of the fact that constant (being defined at class level or not) in PHP are only scalar (int, string, etc) while in Java they may be pure object (ex: java.awat.Color.BLACK). The only possible solution of having such kind of constant is :<?phpclassBar{...}classFoo{ public static$FOOBAR; static function__init() { static$init=false; if ($init) throw newException('Constants were already initialized');self::$FOOBAR= newBar();$init=true; }}Foo::__init();?>That said, perhaps it is useless unless PHP automatically calls the __init() method.However, one alternative that could be done in certain case is this :<?phpfunction__autoload($className) { ... requirethe file where theclassis... if (interface_exists($className,false)) return; if (class_exists($className,false)) {$rc= newReflectionClass($className); if (!$rc->hasMethod('__init')) return;$m=$rc->getMethod('__init'); if (!($m->isStatic() &&$m->isPrivate())) { throw newException($className.' __init() method must be private and static !'); }$m->invoke(null); return; } throw newException('Class or interface not found '.$className);}?>This can only work when one class is defined per file, since we are assured that __autoload() will be called to load the file containing the class.eg: test2.php:<?phpclassB{ public static$X; private static function__init() { echo'B',"\n";self::$X= array(1,2); }}classA{ public static$Y; private static function__init() { echo'A',"\n";self::$Y= array(3,4); }}?>test.php:<?phpfunction__autoload($n) { if ($n=='A'||$n=='B') require'test2.php'; ... doour __init()trick...}var_dump(B::$X);// shows B, then array(2) (1, 2)var_dump(A::$Y);// shows NULL.?>