I'm a bit confused on whether or not this is possible. I've checked a couple of posts here on SO and they don't really explain what I'm looking for.
I have 3 classes. One main class and two classes extending that main class. (see code below). Is it possible to run a method in one of the two extended classes from it's sibling (the other extended class)?
If it's not possible, how can I change my code to accomplish what I'm doing in the example below?
DECLARATION
class A {
public function __construct() {
//do stuff
}
}
class B extends A {
private $classb = array();
public function __construct() {
parent::__construct();
//do stuff
}
public function get($i) {
return $this->classb[$i];
}
public function set($i, $v) {
$this->classb[$i] = $v;
}
}
class C extends A {
public function __construct() {
parent::__construct();
//do stuff
}
public function display_stuff($i) {
echo $this->get($i); //doesn't work
echo parent::get($i); //doesn't work
}
}
USAGE
$b = new B();
$c = new C();
$b->set('stuff', 'somestufftodisplay');
$c->display_stuff('stuff'); // <----- Displays nothing.
Your code shows an additional problem apart from the main question so there are really two answers:
No, you cannot run a method from a sibling class in another sibling class. If you need that, the method should be in the parent class. The same applies to properties.
You cannot use the value of a property from one object in another object, even if they are both of the same class. Setting a property value in one object sets its value only there as different objects can have the same properties with completely different values. If you need to share the value of a property between the objects and also be able to modify it, you should use a static property. In this case you would have to define that in the parent class, see my previous point.
So to make it work, you would need something like
class A {
private static $var = array();
public function get($i) {
return self::$var[$i];
}
public function set($i, $v) {
self::$var[$i] = $v;
}
}
class B extends A {
}
class C extends A {
public function display_stuff($i) {
echo $this->get($i); // works!
}
}
$b = new B();
$c = new C();
$b->set('stuff', 'somestufftodisplay');
$c->display_stuff('stuff');
An example.
Related
My coworker ask me how to dynamically implement methods in a class. What I come up with was strategy pattern. At the first time, I made regular strategy pattern, and at the end I understood it's not good idea to make property call function. Because the child class is controller class whose methods needs to be called directly.
So, I'm trying to implement Package's method to B class directly. But I'm stuck when calling __call function. The function tried to implement works in class B. However, when it's extended the function I saved in B class doesn't work.
<?php
class A {
public $once = 0;
}
class B extends A {
public $methods = [];
public function __construct()
{
//load package
$package = new Package;
//get method names
$methods = get_class_methods($package);
//loop to assign methods to class B instance
foreach($methods as $method)
{
$this->methods[$method] = $this->setFunc($package, $method);
}
}
//I made this function because [$package, $method] was regarded as
// array instead of function when it is assigned to other variable
private function setFunc($package, $methodName)
{
return function() use($package, $methodName)
{
return call_user_func([$package, $methodName]);
};
}
}
//package class
class Package {
public function hello_world(){
return "hello world";
}
}
class C extends B{
public function __construct()
{
parent::__construct();
//assigning functions to class C
foreach($this->methods as $key => $val)
{
//I did it in child class because dynamically produced properties
// weren't recognized
$this->$key = $val;
}
}
//dynamically assigned functions to dynamic properties must be called by
//__call function
public function __call($name, $arguments)
{
//I made this condition because calling function loops infinitely.
if($this->once >= 1)
{
return;
}
$this->once++;
//not working here. nothing shown
return $this->$name();
}
}
$c = new C;
echo $c->hello_world(); //I want to display hello world!
replace return $this->$name(); with call_user_func($this->$name,[]);
or in php7 this works return ($this->$name)();
I have 4 classes, 1 of them is the main class that holds all the general objects.
3 others are situation classes (for the lack of a better word, as they are included in different parts of the script as needed)
I would like to access all three classes' methods and objects from the main class (or at least just the methods) AND access the main class' methods and variables from the other three classes, AND share the access between all the classes, so that I can call any method of any class from any class.
I know I can make the other three classes members of the main class like this:
class Main
{
public $test = 'test';
public function __construct ($classA, $classB, $classC)
{
$this -> classA = $classA;
$this -> classB = $classB;
$this -> classC = $classC;
echo 'Main just got constructed!';
}
public function test()
{
echo $this -> test;
}
}
class A extends Main
{
public function TestA()
{
echo 'In Class A';
}
}
class B extends Main
{
public function __construct()
{
echo 'B just got constructed!';
}
public function TestB()
{
echo 'In Class B';
}
}
class C extends Main
{
public function TestC()
{
echo 'In Class C';
// can we access B from here? probably not.
// would B::TestB() work?
B::TestB();
// yes it does.
// but is there any other way of accessing the method? like $this -> TestB()? apart from extending the B class to C.
// Is there any downside to using this way of accessing methods and variables? what are the cons?
}
}
$A = new A;
$B = new B;
$C = new C;
$Main = new Main ($A, $B, $C);
$C -> TestC();
$Main -> classC -> TestC();
The first issue with this is, extending the Main class to other classes throws warnings because of the missing arguments for __construct() in the Main class:
Warning: Missing argument 1 for Main::__construct()
The other concern is that if I use this method, then all three classes have to be initiated before the Main Class. But My script requires the main class to be executed at the very start as it establishes connections to mysql and sets environment variables etc. Plus it is not necessary that all three classes will be used at the same time, so in some part of the websites, only one class is required and it is pointless to initiate the other classes in those parts. (I have more than 4 classes, this is just to make things simpler). In that case, how do I make the other classes members of Main after it has been initiated?
Another thing, extending Main class thrice in other classes seems to initiate Main class thrice in separately.. which is resetting the main class' objects and they are not the same when accessed with other classes. I found out about that issue when the test code threw "Main just got constructed!" thrice and it was setting the variables to their default values. How to avoid that? I would like the Main class' variables to have same values across all the other classes.. kind of like global variables.
Well, as you have objects "injected" later or before Main instance is created, maybe you want add um method register in Main class. Also, as long as each class must have access to onother (such as linking) you could add an attribute (maybe $main) with instance of Main class.
I think it could be useful:
<?php
class A
{
/**
* #var Main
*/
public $main; # Main class
public function test()
{
#$this->main->aInstance->someMethod();
#$this->main->bInstance->someMethod();
// ...
return __METHOD__;
}
}
class B
{
/**
* #var Main
*/
public $main;
public function test()
{
return __METHOD__;
}
}
class Main
{
public $objectPool = array();
public function __construct(array $objects = array())
{
foreach ($objects as $object) {
$object->main = $this;
# add...
$this->addObject($object);
}
}
public function addObject($object)
{
$this->objectPool[ get_class($object) ] = $object;
}
public function getObject($classname)
{
if (array_key_exists($classname, $this->objectPool)) {
return $this->objectPool[$classname];
}
return null; # Or raise an Excetion?
}
public function test()
{
return get_class($this);
}
}
$obj = array(new A, new B);
$main = new Main($obj);
var_dump($main->getObject('B')->main->test());
var_dump($main->getObject('B')->test());
var_dump($obj[1]->main->getObject('A')->test());
However, I think these objects know much about each (which is not a good idea - bad design, actually).
Here goes the runnable code: http://3v4l.org/Y19Kr
I want to redefine a method, and call my ancestor's version of it, not my parent's.
Here is a short example:
// This class is autogenerated and I am not supposed to modify it.
class myParent extends myGrandparent {
function doSomething() {
doA();
doB();
doC();
parent::doSomething();
}
}
// Here is my code
class myClass extends myParent {
function doSomething() {
// doA(); // I don't want to do A anymore.
// doB(); // Neither B.
doC(); // But I want to keep doing C.
parent::doSomething(); // OOPS!! This does A and B (and C again)!
}
}
How can I call myGrandparent's method directly, instead of myParent's?
I disagree with the "you cannot do this" argument - You can do this with Reflection.
Consider the following class structure:
class A {
function foo() {
echo 'A';
}
}
class B extends A {
function foo() {
parent::foo();
echo 'B';
}
}
class C extends B {
function foo() {
parent::foo();
echo 'C';
}
}
When initialized with this:
$o = new C;
$o->foo();
Will print (as expected, seen in this demo):
ABC
The challenge is to remove the B from the output, effectively only executing A's foo() and C's foo(). So, lets drop into Reflection and grab A's foo() method, and invoke that on C's object. Now consider this alternative definition for C:
class C extends B {
function foo() {
$ref = new ReflectionClass( $this);
$parent = $ref->getParentClass()->getParentClass();
$parent->getMethod( 'foo')->invoke( $this);
echo 'C';
}
}
Now, you'll only get as output (as seen in this demo):
AC
Whether or not this is a "good practice", is up to the OP. I think I've demonstrated that it is possible to "skip" the implementation of B's function and call the grandparent function from the grandchild class.
Not sure what the use cases are, but unless I misunderstand the question/issue (quite possible), you can totally call any arbitrary ancestor (public or protected) method, irrespective of how many times it's been overridden in between, and even the default value of any ancestor member attribute (public or protected), even if that's been overridden too. For example, with the class hierarchy:
Papa > Mama > Baby > Infant, where both the method sayWhat() & instance variable $el are overridden in each descendent class, you can call any ancestor sayWhat method from Infant, and access a different ancestor default attribute value:
class Papa {
protected $el = 'PapaEl';
protected function sayWhat($className = null) {
if (!$className) {
$className = get_class($this);
}
$classVars = get_class_vars($className);
$localEl = $classVars['el'];
echo "<h2>What is PAPA!. El: [$localEl]</h2>";
}
}
class Mama extends Papa {
protected $el = 'MamaEl';
protected function sayWhat() {
echo "<h2>What is MAMA! El: [$this->el]</h2>";
}
}
class Baby extends Mama {
protected $el = 'BabyEl';
protected function sayWhat() {
echo "<h2>What is Lil' Baby!! El: [$this->el]</h2>";
}
}
class Infant extends Baby {
protected $el = 'InfantEl';
protected function sayWhat($className) {
Papa::sayWhat($className);
}
public function mySayWhat($className) {
$this->sayWhat($className);
}
}
$i = new Infant();
$i->mySayWhat('Mama');
Output:
What is PAPA!. El: [MamaEl]
Not sure what value it has, but if someone has the requirement, it seems very doable...
You really can't. You would either need to extend from myGrandParent directly, or you would need to rework the logic in MyParent to provide "pass-through" access to the myGrandParents method. For example, you could make a method on myParent like this:
function doSomethingGrandparent() {
parent::doSomething();
}
and then change your doSomething method in myClass like this:
function doSomething() {
parent::doSomethingGrandparent();
}
The answer is no, you cannot. The children redeclare the parent functionality of the method and completely override it.
I think static methods would be required, since you can't chain the parent:: keyword to work back to a "grandparent" class, so, parent::parent::foo( ) doesn't work.
Suppose both base and son class have a method method_1,
and there's another method method_2 of base.
inside base::method_2,how can I point $this->method_1 to base::method_1 no matter whether $this is a instance of base or son?
If I understand you correctly, you want something like this:
<?php
class base {
public function method1() {
echo "base:method1\n";
}
public function method2() {
if(get_class($this) == 'base') {
$this->method1();
}
else {
parent::method1();
}
echo "base:method2\n";
}
}
class son extends base {
public function method1() {
echo "son:method1\n";
}
}
$obj = new son();
$obj->method2();
where the call to method2 would always use the base version of the method1.
The best way I could do it is as above, but this code won't work since base has no parent. I'm pretty sure what you're trying to do isn't possible.
This is the error you will get:
PHP Fatal error: Cannot access parent:: when current class scope has no parent in
This will do what you want. Props to dbers for the example code (even if his didn't quite work).
<?php
class base {
public function method1() {
echo "base::method1\n";
}
public function method2() {
if (get_parent_class($this) === FALSE) {
echo get_class($this)." has no parent\n";
$this->method1();
} else {
echo get_class($this)." has parent\n";
call_user_func(array(get_parent_class($this), 'method1'));
}
}
}
class son extends base {
public function method1() {
echo "son::method1\n";
}
}
$b = new base();
$b->method2();
$s = new son();
$s->method2();
?>
Outputs:
base has no parent
base::method1
son has parent
base::method1
Make the function private:
<?php
class A
{
public function __construct()
{
$this->foo();
$this->bar();
}
private function foo() { echo "A::foo()\n"; }
public function bar() { echo "A::bar()\n"; }
}
class B extends A
{
public function foo() { echo "B::foo()\n"; }
public function bar() { echo "B::bar()\n"; }
}
new B();
?>
The output is:
A::foo()
B::bar()
Yes it does. It's singular (one parent).
son->method_1 can add to or override ALL of base->method_1 functionality.
son->method_1 can simply add an additional function, and utilize the rest of the functionality of it's parent's instance of method_1
So calling $this->method_1 would use base->method_1, and son->method_1 as long as what you want to use from base isn't overridden in son.
If you call a method that doesn't exist in your subclass, PHP will traverse the class hierarchy until it finds an ancestor class which implements the function you want. This means that if your son class doesn't implement method_2, PHP will automatically look for the nearest ancestor which does. In your case, it will call method_2 of base as you want.
If you are overriding the method_2 in your son class, and you want to do your own implementation of method_2 and also call the base::method_2 implementation then you can use the parent keyword:
class son extends base {
public function method_2() {
parent::method_2();
//do son::method_2()-specific stuff here
}
}
You can't chain parent calls together, so if base was a subclass of GrandparentClass you couldn't do something like this:
parent::parent::method_2(); // trying to call grandparent::method_2
// but this call will fail
But you can directly refer to ancestor classes by name, so this would work:
GrandparentClass::method_2();
And just to take it a little further, there is also a function called class_parents() which returns an array of every ancestor class your class inherits from. This could help if you wanted to go back, say, two ancestors, but you didn't know its specific name for some reason, you could still call the function using eval().
For example, this code would call GrandparentClass::method_2() without directly referencing the class by name in your code:
$parents = class_parents($this);
eval(end($parents) . "::method_2();");
Hope that helps.
HI!
basicly what I ask u to tell me is how to put a "parent" reference into the object
I need it to make example of "extracting method with method object" - one of mostly used refactoring in Java or C#
in Java refering to "parent object" looks like this:
class someClass {
MyObject myObj = new MyObject(this);
}
and thats it :)
but I dont know, how to do the same in PHP
maybe if its imposible you would tell me how you extract your methods out of your classes to new class, that has to do what that method did.
so in other words...
1 - I have class with big and hard to read / refactor method.
2 - I extract that method to new class, giving it fields in place of parameters and method like "execute" - to proced all that this class has to do for me.
3 - I put object of my class to my old function class and I call its method "execute" - so the all logic that was in my_big_method is done.
The best method is inheritance, where you call the parent keyword to access the parent class like so:
class Child extends Father
{
public function __construct()
{
parent::__construct();
}
}
class Father
{
public function __construct()
{
echo "Father says hello";
}
}
new Child();
using the parent keyowrd you can call the constructor like so parent::__construct()
Example: http://codepad.org/kW6dfVMs
if your looking at Injection then you could do something like this.
class Master
{
private $Slave;
public function __construct(Slave $Slave)
{
$this->Slave = $Slave;
}
}
$Master = new Master(new Slave);
if your unsure of the object that should be passed in but you know that it should have a certain interface / set of methods you can get a little more complex and do something like so:
class Master
{
private $Slave;
public function __construct(ISlave $Slave)
{
$this->Slave = $Slave;
}
}
interface ISlave
{
//Declaration of methods
}
class SomeSlaveObject implements ISlave{}
$Master = new Master(new SomeSlaveObject);
Have you tried using the $this keyword?
Easiest way is to pass a reference to the parent object in the constructor, though I may be misunderstanding your goal.
class myClass {
private $parent = false;
function __construct ($parent=false) {
$this->parent = $parent;
}
}
If you are extending a base class, you can use the parent operator:
class otherClass {
function someMethod() {
return 1;
}
}
class myClass extends otherClass {
function aMethod() {
// parent keyword here would refer to "otherClass"
return $this->someMethod();
}
}
Check out the doc on parent: http://php.net/manual/en/keyword.parent.php
PHP version of your example:
class someClass {
public function createObject() {
$myObj = new MyObject($this);
}
}
not much different than java, just less types and more $ signs.
"parent" is a keyword in php, like the java "super" so your question is a little confusing.
I tried with "$this", but "php guy" in my company told me that I cant use $this that is not refering to field or function of current class (that it cant refer to whole class)
my code looks like this:
class to refactor:
class AccountBeforeRefactoring {
// (...)
public function makeTransfer($amount, $destinationAccount){
$transferFee = 1;
if ($amount > 1000){
$transferFee = 1 + $amount * 0.0001;
}
$this->connectToElixir();
// (...)
// (...)
// (...)
$this->debit($amount + $transferFee);
}
}
and it becomes:
class extracted - that was my method:
class TransferMaker {
private $account;
private $amount;
private $destinationAccount;
private $transferFee;
public function __construct($source, $amount, $destinationAccount){
$this->account = $source;
$this->amount = $amount;
$this->destinationAccount = $destinationAccount;
$this->transferFee = 1;
}
public function make(){
if ($this->amount > 1000){
$this->transferFee = 1 + $this->amount * 0.0001;
}
$this->account->connectToElixir();
// (...)
// (...)
// (...)
$this->account->debit($this->amount + $this->transferFee);
}
}
is constructor there made in right way?
and now I need, to make object of MakeTransfer inside my Account class - I tried it this way - is it ok?
class Account {
// (...)
public function makeTransfer($amount, $destinationAccount){
new TransferMaker($this, $amount,
$destinationAccount).make();
}
}
and again - can I just this "$this" just like this? ;)
will my code work? it does compile in Eclipse, but that can mean none.