I have a main class and two extended classes:
class Main {
public $foo;
}
class A extends Main {
public function setVar()
{
$foo = "test";
}
}
class B extends Main {
public function getVar()
{
return $this->foo;
}
}
$A = new A;
$B = new B;
$A->setVar();
echo "result: ".$B->getVar();
But result ($B->getVar()) stays empty. I am clearly missing something simple here... Besides that, is this the way to go splitting long classes up in relevant subclasses?
change:
class A extends Main {
public function setVar()
{
$foo = "test";
}
}
to:
class A extends Main {
public function setVar()
{
$this->foo = "test";
}
}
Notice the $this keyword
Example of using Dependency Injection (DI) to share an instance of Main between to different class instances rather than trying to use inheritence
class Main {
public $foo;
}
class A {
protected $main;
public function setVar($data)
{
$this->main->foo = $data;
}
public function __construct(Main $main)
{
$this->main = $main;
}
}
class B extends Main {
protected $main;
public function getVar()
{
return $this->main->foo;
}
public function __construct(Main $main)
{
$this->main = $main;
}
}
$M = new Main;
$A = new A($M);
$B = new B($M);
$A->setVar("test");
echo "result: ".$B->getVar();
To further clarify things, it may help to think it in this way. Let's say your Main is a class that stands for a vehicle (and hence we rename it Vehicle), so class means that Vehicle defines the characteristics of a veicle but not a particular one to do operations with (we need an instance for that).
Let's translate also your class A in Car and B in Truck: therefore they are specializations of your main class that, along with possessing every characteristic of a generic Vehicle, extend the concept of "vehicle" and point out specific behaviours and properties of Cars in general, and Trucks in general, but - and that's the point - don't reference a particular car or truck. We need instances for that.
class Vehicle
{
public $numberOfTires;
}
class Car extends Vehicle
{
function smashMe()
{
echo "Oops!";
}
}
class Truck extends Vehicle
{
function smashMe()
{
echo "More Oops!";
}
}
From this point we can define particular instances (your Porsche, my Camaro... different cars) on which we may perform operations (calling methods) and set properties.
$genericVehicle = new Vehicle;
$myPorsche = new Car;
$yourCamaro = new Car;
$hisTruck = new Truck;
$herTruck = new Truck;
But every instance remains independent from the other.
Both are different instances of the the same class.
Operations done on one instance, does not apply on any of the others (That's kinda the point of OOP).
To do what you want, You can have B extend A, and then perform both operations on a single instance of the B class.
Related
I'm trying to covert old code that uses functions to use classes. Some of the old code has options only available if an advanced option is set. In the base class I have put all of the functions (methods). If the function is available as an advanced option it is overriden in the second class. If not, it should say not available in the base class. The problem is that I can't figure out how to call one class or the other, short of putting in a bunch of if's, of course.
The original function would look like this
function Testclass() {
if (advanced_enabled)
return 'Do advacned stuff<br>';
else
return 'Do base stuff<br>';
}
Here are my classes:
class A {
public function Testclass() {
return 'in base class<br>';
}
public function SomeBaseCode() {
}
}
class B {
public function Testclass() {
return 'in advanced class<br>';
}
}
If I do this:
$a = new A();
echo 'base '.$a->Testclass();
$b = new B();
echo 'base '.$b->Testclass();
The output is
in base class
in advanced class
What I'm wanting to do is have the advanced class used if present. But the base class has to be present because it has methods always available. I can do this
$a = new A();
echo 'base '.$a->Testclass();
if (advanced_enabled) {
$b = new B();
echo 'base '.$b->Testclass();
}
But that gives me two different class variables and I would have to edit a lot of code to check each. I'm fairly new to classes so maybe I am missing some basic idea. Is there a way to do this?
Your advanced class B needs to extend from the base class A.
class A {
public function Testclass() {
return 'in base class<br>';
}
public function SomeBaseCode() {
}
}
class B extends A {
public function Testclass() {
return 'in advanced class<br>';
}
}
Instantiation is based on the advanced_enabled flag.
$a = advanced_enabled ? new A() : new B();
$a->SomeBaseCode();
$a->Testclass();
Instead of directly creating the class using new, use a Factory to do this for you. This factory can be a different class, or a simple function. If you use any PHP framework, you're probably using dependency injection, which can take care of this for you.
If you do not want to re-define all methods (i.e. some methods in B should be exactly the same as in A), you can use the extend keyword to have class B inherit all methods from class A you did not override. This principle is called object inheritance.
In pseudo-PHP, it would look a bit like this:
// Define classes (interface is optional, but recommended)
interface someInterface {
public function testClass();
public function someOtherFunction();
}
class simpleVersion implements someInterface {
public function testClass() { /* ... */ }
public function someOtherFunction() { /* ... */ }
}
class advancedVersion extends simpleVersion {
public function testClass() { /* ... */ }
// someOtherFunction is not defined here. but is still usable!
}
// Create the factory responsible for instantiating the class
function createVersion() {
return $advancedEnabled ? new advancedVersion(); : new simpleVersion();
}
// Create the class instance (dynamically) and use it
$class = createVersion(); // $class is now either simpleVersion or advancedVersion.
$class->testClass();
Also, if you want a class B method to do something 'advanced', while also keeping the functionality of class A, you can use the special parent::something(); call to 'copy' the functionality of the base class:
class Shouter() {
public function shout($text) {
return strtoupper($text);
}
}
class LoudShouter() extends Shouter {
public function shout($text) {
return parent::shout($text) . '!!!';
}
}
$text = 'Hello World';
$shouter1 = new Shouter();
$shouter2 = new LoudShouter();
var_dump(
$text,
$shouter1->shout($text),
$shouter2->shout($text)
);
// Output:
// -> Hello World
// -> HELLO WORLD
// -> HELLO WORLD!!!
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'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.
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.
I stumbled across a very wired error in php:
class A {
public $var = "test";
public function __construct() {
$this->var = "test2";
$b = new B;
$b->method();
}
}
class B extends A {
public function method() {
$c = new C;
$c->method();
}
}
class C extends B {
public function method() {
echo $this->var;
}
}
$a = new A;
I get the output "test", but I do not know why, cause the variable var should be overwritten in Class A.
If I output $var in Class A it says "test2", if I output it in Class B it says "test"…
The code on your question won't work because of the circular references (eg: $b = new B in A's constructor), which will cause PHP to run out of memory. You really shouldn't be instantiating children classes in a parent class.
That being said, by what you are describing, it sounds like you are defining a constructor in B, which overrides the parent constructor. In PHP children classes don't implicitly call the parent constructor (unlike in languages like Java).
So, it just inherits the original value for $var (ie: "test"), which is never changed. If you are overriding __construct() in B, you'll have to explicitly call the parent constructor, like:
class B extends A {
public function __construct() {
parent::__construct();
}
}
And that should give you "test2" when you do something like:
$b = new B;
echo $b->var;
See this demo: http://ideone.com/Q9Bp8
What is the best way to have 3 classes, where the third and second can access variables of the first class?
The answer is, it depends on what you are doing. It sounds like you are not understanding how OOP works, which is a bigger problem. In general you only use inheritance when the children classes could reuse code from the parent class, and/or there is some sort of is-a or has-a relationship.
If your classes don't fit this model, just make the 3 classes independent, and hold a reference to the first class in your other classes. For example:
class A {
public $n = 0;
public function change($n) {
$this->n = $n;
}
}
class B {
public function __construct($a) {
$this->my_a = $a;
}
public function get() {
return $this->my_a->n;
}
}
$a = new A();
$b = new B($a):
echo $b->get(); // 0
$a->change(10);
echo $b->get(); // 10
See this demo: http://codepad.org/xL1Dzs0W