Is it possible in PHP to get the methods of an extended child class in a method declared in the parent class?
Here is a simple (maybe stupid) example:
<?php
class Vehicle{
protected function moveForward(){
// go ahead...
}// moveForward
public function getWhatCanIDo(){
$actions = get_class_methods($this);
return 'I can '.implode(', ', $actions).'<br/>';
}// getWhatCanIDo
}
class Car extends Vehicle{
protected function honk(){
// honk...
}// honk
protected function turnHeadlightsOn(){
// turn headlights on...
}// turnHeadlightsOn
protected function stopEngine(){
// stop the engine
}// stopEngine
}
class Submarine extends Vehicle{
protected function submerge(){
// sink...
}// submerge
protected function ping(){
// ping...
}// ping
protected function fireTorpedos(){
// DESTROY!!!
}// fireTorpedos
protected function stopEngine(){
// stop the engine
}// stopEngine
}
$volvo = new Car();
$uboat = new Submarine();
echo $volvo->getWhatCanIDo();
echo $uboat->getWhatCanIDo();
?>
The output what I expect is:
I can moveForward, getWhatCanIDo, honk, turnHeadlightsOn, stopEngine
I can moveForward, getWhatCanIDo, submerge, ping, fireTorpedos, stopEngine
But instead It returns the the methos of the Vehicle class only, without the methods implemented in the extended class:
I can moveForward, getWhatCanIDo
I can moveForward, getWhatCanIDo
How could I get the extanded class methods?
Additional Infos:
I have to solve this in PHP 5.2.14
The extended classes will have different numbers of methods with different method names so making Vehicle an abstract class wont help, because e.g.: I dont want Submarine to have honk method.
I know I could make the getWhatCanIDo() an abstract method, but I'd like to implement this method "centrally" in the parent class, I dont want to oblige developers to write getWhatCanIDo() method for every extended class (In the future others may join or continue this project, and Its more failsafe to not let them implement this method again and again especially when the method does exactly the same thing.)
You should declare class Vehicle abstract, because it does not really exist and the real vehicles implement it.
Then put whatCanIDo in Car and in Submarine, because you don't ask the vehicle what it can do, you ask the car.
class Car extends Vehicle{
public function getWhatCanIDo(){
$actions = get_class_methods($this);
return 'I can '.implode(', ', $actions).'<br/>';
}// getWhatCanIDo
}
Update:
A yet different approach is to use the standard PHP library ReflectionClass:
$class = new ReflectionClass('Vehicle');
$methods = $class->getMethods();
You need to overload the getWhatCanIDo() function in the Car & Submarine classes. You get the output because the function is executed in the Vehicle class.
Overloading the method causes it to be executed in either the Car or Submarine class.
You could also try get_class_methods(); More at http://www.php.net/manual/en/function.get-class-methods.php
This code is not tested, please, let me know if it works.
class MyBaseClass {
//...
public function getMethods($className) {
return get_class_methods($className);
}
public static function getMethods($myObject) {
return $myObject->getMethods(get_class($myObject));
}
//...
}
class MyInheritedClass {
//...
}
$myBaseObject = new MyBaseClass(/*...*/);
$myInheritedObject = new MyInheritedClass(/*...*/);
echo var_dump(MyBaseClass::getMethods($myBaseObject));
echo var_dump(MyBaseClass::getMethods($myInheritedObject));
Inheritance is not proper tool here. You should use composition. Example:
Have separate objects Runner, Swimmer, WorkWithEngine, Submerge. All off them implements interface with getWhatCanIDo() method.
Create your new Vehiclas by composing them from Types from point one. This object implements interface with getWhatCanIDo() method as well.
$submarine = new Submarine();
$submarine->addAblility(new Swimmer());
$submarine->addAblility(new WorkWithEngine());
$submarine->addAblility(new Submerge());
$submarine->whatCanIDo();
In any case do not use magic like get_class_methods those are constructs for frameworks creators it is not stuff for coding business logic.
Related
I have some issues with OOP. I just started OOP in php and i have some issues.
So i have a question for you, maybe you'll help me.
I have multiple classes (in this case 3)
<?php
//FILE class.NB.php
class NB { //databse manipulations, curls
public $db;
function __construct($db) {
$this->db = $db;
}
public function LoginNB () {
//something here
$this->db->query("UPDATE logins SET login_time = %u", time());
}
}
//FILE class.fn.php
class FN extends NB {
public function deposits () {
$this->LoginNB();
return $this->db->query("SELECT * FROM deposits");
}
public function getUserWihdrawsCompared() {
// AND HERE I WOULD LIKE TO USE the DR's ::usersWithdraws
$users = $this->usersWithdraws();
}
}
//FILE class.dr.php
class DR extends NB {
public function withdraws () {
$this->LoginNB();
return $this->db->query("SELECT * FROM withdraws");
}
public function usersWithdraws() {
$a = $this->db->query("SELECT * FROM user_withdraws");
/*code here*/
return $final_array;
}
public function compare_withdraws_deposits () {
// AND HERE I WOULD LIKE TO USE the FN's ::deposits
$deposit_list = $this->deposits();
/* code here */
return $final_array;
}
}
?>
So my question is, how is possible to use everything in everywhere.
I saw something with traits but i'm not sure, how to use and what exactly to use.
My problems is what i want to user parent's child method in other child with same parent.
But in the end, i would like to use only the parent class for "runing" implementing in other classes if it's possible.
Like:
$NB = new NB($db);
$result = $NB->ShowResults();
Problem: ShowResults() should use both child's methods and child methods used in ShowResults() some times use methods from other child class.
Maybe it's impossible but i would appreciate if you could help me. (even with a confirmation that is not possible)
Thank you.
I think you haven't fully grasped what we mean by "parent" and "child" in OOP, and why they're useful. The purpose of inheritance is not to grant access to the methods of one class in another, or to automatically run multiple implementations of the same thing. Instead, the purpose is to allow code outside the classes to call one of the implementations without needing to know which one.
So, if I have an instance of class NB, I know I can call LoginNB on it. If what I'm passed is actually an instance of class FN, that will still work; class FN will either inherit that method, or reimplement it a different way, but with the same external signature.
However, class NB doesn't know anything about what classes inherit from it, any more than a function knows where else it is called from; the relationship only goes one way.
Second update
I think I've been approaching this problem from the wrong side of the coin. Would I be correct in assuming that I should be making 'First' an abstract class and just finding a way to reference 'Second' and 'Third' at a later time?
Update
Based on some of the feedback, I have added some content to try and clear up what I would like to do. Something similar to this effect.
I know from just looking at the code below that, it is a waste of performance "if" it did work and because it doesn't, know I am approaching the problem from the wrong angle.The end objective isn't all to uncommon at a guess from some of the frameworks I've used.
I'm more trying to base this particular bit of code on the CodeIgniter approach where you can define (what below) is STR_CLASS_NAME in a config file and then at any point through the operation of the program, use it as i have dictated.
STR_CLASS_NAME = 'Second';
class First {
protected $intTestOne = 100;
public function __construct() {
$strClassName = STR_CLASS_NAME;
return new $strClassName();
}
public function TestOne() {
echo $this->intTestOne;
}
protected function TestThreePart() {
return '*Drum ';
}
}
class Second extends First{
/* Override value to know it's working */
protected $intTestOne = 200;
/* Overriding construct to avoid infinite loop */
public function __construct() {}
public function TestTwo() {
echo 'Using method from extended class';
}
public function TestThree() {
echo $this->TestThreePart().'roll*';
}
}
$Test = new First();
$Test->TestOne(); <-- Should echo 200.
$Test->TestTwo(); <-- Should echo 'Using method from extended class'
$Test->TestThree(); <-- Should echo '*Drum roll*'
You may be asking, why do this and not just instantiate Second, well, there are cases when it is slightly different:
STR_CLASS_NAME = 'Third';
class Third extends First{
/* Override value to know it's working */
protected $intTestOne = 300;
/* Overriding construct to avoid infinite loop */
public function __construct() {}
public function TestTwo() {
echo 'Using method from extended class';
}
public function TestThree() {
echo $this->TestThreePart().'snare*';
}
}
$Test = new First();
$Test->TestOne(); <-- Should echo 300.
$Test->TestTwo(); <-- Should echo 'Using method from extended class'
$Test->TestThree(); <-- Should echo '*Drum snare*'
Situation
I have a an abstract class which extends a base class with the actually implementation; in this case a basic DB wrapper.
class DBConnector ()
class DBConnectorMySQLi extends DBConnector()
As you can see, MySQLi is the implementation. Now, dependant upon a value in the configuration process, a constant becomes the class name I wish to use which in this case (as shown below builds DBConnectorMySQLi.
define('STR_DB_INTERFACE', 'MySQLi');
define('DB_CLASS', 'DBConnector'.STR_DB_INTERFACE);
Objective
To have a base class that can be extended to include the implementation
For the code itself not to need know what the name of the implementation actually is
To (in this case) be able to type or use a project accepted common variable to create DBConnectorMySQLi. I.E. $db or something similar. W
Issue
When it comes to actually calling this class, I would like the code to be shown as below. I was wondering whether this is at all possible without the need to add any extra syntax. On a side note, this constant is 100% guaranteed to be defined.
$DBI = new DB_CLASS();
Solution 1
I know it is possible to use a reflection class ( as discussed in THIS QUESTION) and this works via:
$DBI = new ReflectionClass(DB_CLASS);
However, this creates code that is "dirtier" than intended
Solution 2
Start the specific implementation of DBConnectorMySQLi within the constructor function of DBConnector.
define('STR_DB_INTERFACE', 'MySQLi');
define('DB_CLASS', 'DBConnector'.STR_DB_INTERFACE);
class DBConnector() { public function __construct() { $this->objInterface = new DBConnectorMySQLi(); }
class DBConnectorMySQLi()
This however would result in the need to keep on "pushing" variables from one to the other
Any advice is much appreciate
You can use variables when you instantiate a class.
$classname = DB_CLASS;
$DBI = new $classname();
Source: instantiate a class from a variable in PHP?
I've been trying for a long time now to find a correct design using PHP to achieve what I want, but everything I've tried failed and I'm guessing it's probably because I'm not looking from the right angle, so I wish some of you can enlighten me and give me some good advice!
The design might seem a little weird at first, but I assure you it's not because I like to make things complicated. For the sake of simplicity I'm only giving the minimal structure of my problem and not the actual code. It starts with these:
<?php
// ------------------------------------------------------------------------
class Mother_A
{
const _override_1 = 'default';
protected static $_override_2 = array();
public static function method_a()
{
$c = get_called_class();
// Uses $c::_override_1 and $c::$_override_2
}
}
// ------------------------------------------------------------------------
class Mother_B extends Mother_A
{
public function method_b()
{
// Uses self::method_a()
}
}
Class Mother_A defines a static method that uses constants and statics to be overridden by children. This allows to define a generic method (equivalent of a "template" method) in the derived class Mother_B. Neither Mother_A or Mother_B are intended to be instanciated, but Mother_B should not be abstract. This exploits Late Static Binding, which I find very useful btw.
Now comes my problem. I want to define two classes, in n distinct 'situations' (situation 1, situation 2, etc):
<?php
// ------------------------------------------------------------------------
class Child_A_Situation_k extends Mother_A
{
// Uses method_a
}
// ------------------------------------------------------------------------
class Child_B_Situation_k extends Mother_B
{
// Uses method_a and method_b
}
Of course I'm not actually giving these stupid names; both classes have different names in each situation, but both follow the same derivation pattern from Mother_A and Mother_B. However, in each individual case ('situation'), both classes need the exact same constants/static override, and I don't know how to do that without duplicating the override manually in both classes.
I tried many things, but the closest I got was to implement an interface Interface_Situation_k that defined constants and statics for the situation k, and make both children implement this interface. Of course, you can't define statics in an interface, so it failed, but you get the idea. I would have traded the interface for a class, but then there's no multiple inheritance in PHP, so it's not valid either. :/ I'm really stuck, and I can't wait to read a possible solution! Thanks in advance!
this is the best i can do, i don't think there is a way to do it with less code.
Look at the comments inside the code for more info.
Fully working code:
<?php
class Mother_A
{
// you're using '_override_1' as a variable, so its obviously not a constant
// also i made it public for the setSituation function,
// you could keep it protected and use reflections to set it
// but i dont really see a reason for that.
// if you want that, look up how to set private/protected variables
public static $_override_1 = 'default';
public static $_override_2 = array();
public static function method_a()
{
$c = get_called_class();
var_dump($c::$_override_1);
var_dump($c::$_override_2);
// Uses $c::_override_1 and $c::$_override_2
}
public static function setSituation($className)
{
$c = get_called_class();
// iterate through the static properties of $className and $c
// and when the you find properties with the same name, set them
$rBase = new ReflectionClass($c);
$rSituation = new ReflectionClass($className);
$staBase = $rBase->getStaticProperties();
$staSituation = $rSituation->getStaticProperties();
foreach($staSituation as $name => $value)
{
if(isset($staBase[$name])) $c::$$name = $value;
}
}
}
// ------------------------------------------------------------------------
class Mother_B extends Mother_A
{
public function method_b()
{
self::method_a();
}
}
class Situation_k
{
public static $_override_1 = 'k';
public static $_override_2 = array('k','k');
}
class Child_A_Situation_k extends Mother_A { }
Child_A_Situation_k::setSituation('Situation_k');
// This is not as short as writing 'extends Mother_A, Situation_k'
// but i think you wont get it shorter
class Child_B_Situation_k extends Mother_B { }
Child_B_Situation_k::setSituation('Situation_k');
echo '<pre>';
Child_A_Situation_k::method_a();
echo "\n";
Child_B_Situation_k::method_a();
echo "\n";
Child_B_Situation_k::method_b();
echo "\n";
echo '</pre>';
?>
I've run into a problem and I'm not sure if this is just normal behaviour or if I wrote something wrong. I have a method in my base class that applies a global filter to a given class by way of creating a proxy for all new instances of that particular class. The way I planned to go about it is as follows:
Attach static $global_filter (the proxy) to the class I want to be filtered, which extends the base class object
Via my loading mechanism, return the proxy instead of the actual class upon new instantiations (which will mask method calls and apply filters accordingly)
However, I am getting stuck in step 1 and it seems that when I try to assign static $global_filter to the descendent class I want filtered, my base class object also gets the same assignment, which breaks everything else that extends from it.
Please see below for relevant code:
class object {
public static $global_filter;
public function _filterGlobal($class, $method, $callback) {
if ( !is_object($class::$global_filter) ) {
$class::$global_filter = new filterable(null);
# Replace the object being called with the new proxy.
}
var_dump($class);
var_dump($class::$global_filter); // `filterable`
var_dump(\core\blueprint\object::$global_filter); // Returns same as line above
die();
return $class::$global_filter->_add($method, $callback);
}
}
Both $class::$global_filter and \core\blueprint\object::$global_filter (the base class) are returning same instance. Whereas I expected object::$global_filter to be null.
I'm not using late static binding in order to preserve consistency (both single-object filters and global filters are called much in the same way non-statically).
This question seems relevant
Any help will be much appreciated :)
Edit, full example
This would be a concrete class, which extends model which extends object
<?php
use core\blueprint\model;
class modelMock extends model {
protected $schema = array();
public function method($test) {
return $test;
}
}
This would be another object (e.g a controller), which extends object aswell. It applies a filter to all new instances of model
<?php
use core\blueprint\object;
class objectMock extends object {
public function applyFilters() {
$this->_filterGlobal('core\blueprint\model', 'method', function($self, $chain) {
$chain->params[0] = 'new param'; // adjust the paramters
return $chain->next();
});
}
}
when I try to assign static $global_filter to the descendent class I want filtered, my base class object also gets the same assignment
Yes, indeed this happens. A static property in essence is a global variable, constrained within the class's namespace. Running into problems with global variables is often an indication you're not using the best solution.
To solve your problem, you could make the filter a (non-static) property:
$class->$filter = new Whatever();
But as always, there's more roads that lead to Rome, and I would advise you to look for alterative ways to do it.
I don't know if this is a help for you:
class a {
public static $type;
public static function setType($class, $newType) {
$class::$type = $newType;
var_dump($class::$type);
}
}
class b {
public static $type = 'myType';
}
var_dump(b::$type);
a::setType('b', 'yourType');
var_dump(a::$type);
May be you have not defined the static property to the concrete class.
Thanks everyone for you help, I spent some time on it this morning and managed to solve my problem. It's a bit of a workaround but here's how it goes:
public function _filterGlobal($class, $method, $callback) {
if ( !is_object($class::$global_filter[$class]) ) {
$class::$global_filter[$class] = new filterable(null);
# Replace the object being called with the new proxy.
}
return $class::$global_filter[$class]->_add($method, $callback);
}
So basically in order to get unique static variables working in child classes without having to explicitly define them, you can use an array that stores the child's class name as a key and then access these variables via a getter.
This is a follow up question on the following answer : Parent Object in php
class A {
protected function doSomeStuff(){
echo 'a method that all children will need to call';
}
}
class B {
protected $_parent;
public function __construct($parent) {
$this->_parent = $parent;
}
public function doSomeLocalStuff() {
$this->_parent->doSomeStuff(); // Fatal Error
}
}
$a = new A(); // will be used for other children as well.
$b = new B($a);
$b->doSomeLocalStuff();
In the above code, parent object Injection was used, allowing class B to be initialized using a specific instance of class A, but class B wont be able to access class A protected properties or methods (e.g., doSomeStuff()).
But by mixing the above with inheritance, we get the best of both worlds :)
class B extends A {
protected $_parent;
public function __construct($parent) {
$this->_parent = $parent;
}
public function doSomeLocalStuff() {
$this->_parent->doSomeStuff(); // Works :)
}
}
So, is this acceptable ? .. any drawbacks ?
P.S: I'm trying to implement a non-static factory pattern.
Clarification
Consider this, I'm trying to design a class which will be used for calling an external API. We've over 400 different calls, divided into 10 categories (billing, customers, products ... ).
All the 400 calls shares the same parent-url, username/password and some other common properties.
So, instead of putting the 400 method in one big class, I decided to divide them into 10 classes, with a parent class handling common functions (e.g., authentication, url construction, web call ... ), then created a factory pattern, where I can load only needed classes/categories on run-time.
Something like :
$apiCall = new parentPlusFactory();
//contains common methods and a mechanism to load sub-classes
$apiCall->setAPIuserName("user");
$apiCall->setAPIpassword("pass");
$apiCall->useClass('customers')->doSomeCustomerStuff();
$apiCall->useClass('products')->doSomeProductStuff();
That's why I need to share the same parent class instance.
There is no friend keyword in PHP, like in C++. You could check this discussion for a way to implement friend classes.
But do you really need that function to be declared protected?
In general you should favor composition over inheritance. To me your use case sounds like B should not be extending A at all, but instead you should have two separate classes.
Now, PHP 5.4 will have "horizontal reuse", also known as "traits", where it will be possible to "include" a trait into your class.
trait A
{
public function doSomeStuff()
{
echo 'doing some stuff';
}
}
class B
{
use A;
public function doSomeLocalStuff()
{
$this->doSomeStuff();
}
}
class C
{
use A;
public function doSomeLocalStuff()
{
echo 'Doing something completely different here';
}
}
See also PHP manual: traits and PHP 5.4 beta1 released.