I have a PHP core with an abstract class AppBase which use a trait Uninstall.
To force developper to implement a static function to delete some options inside the main class MyApp, the AppBase implements an interface with a static function 'delete_options()'.
AppBase
abstract class AppBase implements iUninstall{
use Uninstall;
}
Uninstall
trait Uninstall{
public static function uninstall(){
//Do some general stuff
self::delete_options();
}
}
iUninstall
interface iUninstall {
public static function delete_options();
}
MyApp
include_once "core/iUninstall.php";
include_once "core/Uninstall.php";
include_once "core/AppBase.php";
class MyApp extends AppBase{
public static function delete_options() {
delete_option( "first-option" );
delete_option( "second-option" );
}
}
My problem is I got this error:
PHP Fatal error: Uncaught Error: Cannot call abstract method iUninstall::delete_options() in Uninstall.php
I can see the trait Uninstall must be attached to AppBase to use delete_options so there is a matter in my OOP architecture.
How can I resolve this ?
First off, you should have gotten a fatal error about AppBase having an abstract method delete_options() while not being an abstract class. So, you need to make AppBase an abstract class. (But perhaps you had just forgotten to copy that into your example.)
Then, in Uninstall::uninstall() you need to use static instead of self (to utilize late static binding).
So, to wrap it up:
trait Uninstall {
public static function uninstall(){
// static instead of self
static::delete_options();
}
}
interface iUninstall {
public static function delete_options();
}
// abstract class instead of class
abstract class AppBase implements iUninstall{
use Uninstall;
}
class MyApp extends AppBase {
public static function delete_options() {
echo 'deleting';
}
}
MyApp::uninstall();
/* result:
deleting
*/
Or... you could just implement delete_options() as a (stub) method in AppBase, but there was no indication in your question that that was your original intent.
view online parsed # eval.in
Related
I have an abstract database class named as:
abstract class database {
protected $value;
}
I created another abstract class
abstract class my_database extends database {
public function set_value($value) {
$this->value = $value;
}
}
When I try to use it:
$my_db = new my_database();
I get error:
Fatal error: Cannot instantiate abstract class my_database in ...
What I try to do is: The abstract class database has a protected $value and I would like to create a wrapper class, to be able to change the protected value (temporarily).
How can I do that?
EDIT1: unfortunately earlier, when I tried without abstract my_database, I got the errors:
- abstract methods and must therefore be declared abstract or implemented
- Abstract function cannot contain body
EDIT2:
After taking out the abstract word completely from my_database, I got the following error:
Fatal error: Class my_database contains 32 abstract methods and must
therefore be declared abstract or implement the remaining methods
How can I fix this?
Classes defined as abstract may not be instantiated, and any class that contains at least one abstract method must also be abstract. You can read about this in PHP's documentation here: link
Here's an example.
There is an abstract class (note that abstract methods don't have body - they CAN'T have body - it's just a signature):
abstract class AbstractClass
{
// Force Extending class to define this method
abstract protected function getValue();
abstract protected function prefixValue($prefix);
// Common method. It will be available for all children - they don't have to declare it again.
public function printOut() {
print $this->getValue() . "\n";
}
}
Extend your abstract class with a class like this (note that all abstract methods MUST be defined in concrete class):
class ConcreteClass1 extends AbstractClass
{
protected function getValue() {
return "ConcreteClass1";
}
public function prefixValue($prefix) {
return "{$prefix}ConcreteClass1";
}
}
Then you can create instance of ConcreteClass1:
$class1 = new ConcreteClass1;
Your class should not be abstract:
class my_database extends database {
public function set_value($value) {
$this->value = $value;
}
}
In OOP, abstract class can't be instanciated juste it can be extended.
Is it OK (good practice OOP wise) to make a class implement a certain interface so that a trait being used in that class can access the classes functions.
I found a SO question a few days ago with an answer explaining that traits shouldn't use methods/things from the class it's being used in. What If I made a class implement an interface so it had to have those functions that the trait uses from the class? Would that be OK. I'm taking an OOP class in university next year, so I only learned what OOP I did from the internet, in case this is a bad question. :p
So here's the idea to clarify (in PHP)
trait MyTrait {
public function foo() {
return $this->bar(); // bar is in the class the trait is to be used in
}
}
class MyClass implements MyTraitCompatible {
public function bar() {
return "BAR!";
}
}
interface MyTraitCompatible {
public function bar();
}
Also, is there anyway to enforce that a class needs to implement MyTraitCompatible to use MyTrait?
Edit: (My actual goal is to have one function used in two classes that both extend another class (Eloquent) and would be completely identical but the function would not be used in all classes extending Eloquent - this is one way I thought of doing it.)
One option is that your trait could check that the class using it implements the interface you expect. Here's an example in the constructor method:
trait MyTrait {
public function __construct() {
if (!in_array('MyTraitCompatible', class_implements($this, false))) {
throw new Exception('To use this trait you must implement MyTraitCompatible!');
}
}
public function foo() {
return $this->bar(); // bar is in the class the trait is to be used in
}
}
A valid class would be:
class MyClass implements MyTraitCompatible {
use MyTrait;
public function bar() {
return "BAR!";
}
}
An invalid class would be:
class InvalidClass {
use MyTrait;
public function baz() {
return "I don't think so buddy.";
}
}
Obviously if the class using this trait has a constructor already then this would conflict. There isn't a pretty way to avoid this since the class using the trait will have precedence over the trait and would just override it. One option is that you could define a check method in the trait and call it from the methods in the trait to check compatibility, but it's not ideal:
trait MyTrait {
protected function compatible() {
if (!in_array('MyTraitCompatible', class_implements($this, false))) {
throw new Exception('To use this trait you must implement MyTraitCompatible!');
}
var_dump('Passed the test!');
}
public function foo() {
$this->compatible();
return $this->bar(); // bar is in the class the trait is to be used in
}
}
You could also replace that compatible() method with the __call() magic method - but again, you might run into conflicts if you have one defined elsewhere.
I got a problem with abstract class. Currently i use abstract class runnable to determine what my API need to work.
abstract class runnable {
abstract protected function __setConfig();
abstract public function __run(api $api);
public function run(api $api){
$this->__setConfig();
$this->__run($api);
}
}
I haven't got problem with this, but when i miss one method i got this fatal error:
Fatal error: Class MyClass contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (runnable::__setConfig)
I would like know how i can test if runnable's conditions are met before instanciate MyClass, i have tried with ReflectionClass::isInstantiable but i get again this error...
$reflectionClass = new ReflectionClass($appName);
if($reflectionClass->IsInstantiable()) {
// OK
} else {
// Cutom error
}
How i can make it properly?
Thank you for your help.
You can't, as soon you extend your class to an abstract it will throw an error if it doesn't contain's the methods ( even before initiate the class ).
You can't do it with reflection because is for reflection alone as the name states.
You can try to hack it with runkit , it should be the only way to do what you want.
But in the end i don't really know why you should need to do this, why don't you just extend it to a normal class instead of a abstract one? ( if you don't really care about it )
For this case you might create an Interface something like that:
interface runnableInterface {
public function run(api $api);
public function setConfig();
}
abstract class runnableAbstract implement runnableInterface {
public function run(api $api){
$this->setConfig();
$this->run($api);
}
}
class runnable extends runnableAbstract implement runnableInterface {
public function setConfig() {
// do something here
};
public function run(api $api){
$this->setConfig();
$this->run($api);
}
}
Also you can read Object Interfaces and Class Abstraction
If I have an abstract class like this:
abstract class MyApp
{
public function init()
{
$this->stuff = $this->getStuff();
}
public function getStuff()
{
return new BlueStuff();
}
}
And then I have a class that extends from this abstract class like this:
class MyExtendedClass extends MyApp
{
public function init()
{
parent::init();
}
public function getStuff()
{
return new RedStuff();
}
}
If I do:
$myobj = new MyExtendedClass();
$myobj->init();
Why does the method getStuff from the child class get called? Isn't $this in the context of the abstract class? If so, shouldn't the method of the abstract class get called?
Thanks!
New answer
In PHP you can use subclasses as if all methods in the parent class that don't exist in the subclass have been copied to the subclass. So your example would be the same as:
class MyExtendedClass extends MyApp {
public function init() {
$this->stuff = $this->getStuff();
}
public function getStuff() {
return new RedStuff();
}
}
Just think of the subclass as having all code of the parent class and you're normally all right. There is one exception to this rule: properties. A private property of a class can only be accessed by that class, subclasses can't access the private properties of parent classes. In order to do that you need to change the private property into a protected property.
Original answer
Abstract classes in PHP are just like regular classes with one big difference: they can't get initiated. This means you can't do new AbstractClass();.
Since they work exactly like regular classes for everything else, this also is the case for extending classes. This means that PHP first tries to find the method in the initiated class, and only looks in the abstract classes if it doesn't exist.
So in your example this would mean that the getStuff() method from MyExtendedClass is called. Furthermore, this means you can leave out the init() method in MyExtendedClass.
Having the following class hierarchy:
class TheParent{
public function parse(){
$this->validate();
}
}
class TheChild extends TheParent{
private function validate(){
echo 'Valid!!';
}
}
$child= new TheChild();
$child->parse();
What is the sequence of steps in which this is going to work?
The problem is when I ran that code it gave the following error:
Fatal error: Call to private method TheChild::validate() from context 'TheParent' on line 4
Since TheChild inherits from TheParent shouldn't $this called in parse() be referring to the instance of $child, so validate() will be visible to parse()?
Note:
After doing some research I found that the solution to this problem would either make the validate() function protected according to this comment in the PHP manual, although I don't fully understand why it is working in this case.
The second solution is to create an abstract protected method validate() in the parent and override it in the child (which will be redundant) to the first solution as protected methods of a child can be accessed from the parent?!!
Can someone please explain how the inheritance works in this case?
Other posters already pointed out that the mehods need to be protected in order to access them.
I think you should change one more thing in your code. Your base class parent relies on a method that is defined in a child class. That is bad programming. Change your code like this:
abstract class TheParent{
public function parse(){
$this->validate();
}
abstract function validate();
}
class TheChild extends TheParent{
protected function validate(){
echo 'Valid!!';
}
}
$child= new TheChild();
$child->parse();
creating an abstract function ensures that the child class will definitely have the function validate because all abstract functions of an abstract class must be implemented for inheriting from such a class
Your idea of inheritence is correct, just not the visibility.
Protected can be used by the class and inherited and parent classes, private can only be used in the actual class it was defined.
Private can only be accessed by the class which defines, neither parent nor children classes.
Use protected instead:
class TheParent{
public function parse(){
$this->validate();
}
}
class TheChild extends TheParent{
protected function validate(){
echo 'Valid!!';
}
}
$child= new TheChild();
$child->parse();
FROM PHP DOC
Visibility from other objects
Objects of the same type will have access to each others private and protected members even though they are not the same instances. This is because the implementation specific details are already known when inside those objects.
Private can only be accessed by the class which defines or Same object type Example
class TheChild {
public function parse(TheChild $new) {
$this->validate();
$new->validate(); // <------------ Calling Private Method of $new
}
private function validate() {
echo 'Valid!!';
}
}
$child = new TheChild();
$child->parse(new TheChild());
Output
Valid!!Valid!!