I have a parent class like so:
abstract class UiElement
{
protected static ?string $template_name = null;
public function __construct() {
if(static::$template_name == null) {
throw new Exception("static \$template_name has not been set in child class", 1);
}
}
}
now i have a child class like so:
class EmptyContent extends UiElement
{
protected static ?string $template_name = 'empty-content';
public function __construct() {
parent::__construct();
}
}
and call it like so:
$empty = new EmptyContent();
I want to make sure the child class in this case EmptyContent
sets a value other than null when defining this class. So i do the check in the parent's class constructor but this gives me the following error:
Fatal error: Uncaught Exception: static $template_name has not been set in child class in /Applications/MAMP/htdocs/private_projects/Foodio/App/index.php:108 Stack trace: #0 /Applications/MAMP/htdocs/private_projects/Foodio/App/index.php(126): UiElement->__construct() #1 /Applications/MAMP/htdocs/private_projects/Foodio/App/index.php(134): failure->__construct() #2 {main} thrown in
As far as i understand this is due to the fact that the property $template_name
is not yet initialised when the constructor of the parent is fired.
How do i go about doing this?
If more information or clarification is needed let me know so i can add it!
You could create an abstract method instead of a property:
This way you cannot create a child class without the implemented method (forcing a definition). Should you create a child without it:
PHP will throw a fatal error before running your script:
Class EmptyContent contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (UiElement::getTemplateName)
.Now, this comes with its drawbacks. It won't protect you against implementing an empty method or one that returns a wrong type. That kind of stuff will only explode in your face once you use it. You could protect against that by adding a constructor to the child (additionally checking that you don't return an empty string):
in order to force the check on instantiation. This would have to be added on every child (tedious and WET), since you can't handle it on parent level (that would try to call its abstract method and fail before reaching runtime).
There's also another approach you could take, making it a regular (non-abstract) method that throws in the parent:
This will also fail only when you attempt to use the method (not during instantiation) on a child that didn't implement the method, but would be handled in one place, inside the parent. Up to you to weigh the pros and cons.
P.S. Not sure if the method really needs to be static if it's only going to be used internally, but you know your reasoning better than I do.