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!!!
Related
I'm learning OOP PHP. I want to call a method from another class to a new class.
For just a example:
<?php
class Aclass {
function aMethod($input)
{
echo 'Hello a world ';
}
}
?>
And i want to call the method aMethod from the class 'Aclass' into the new class.
<?php
class Bclass {
//calling the method here?
}
?>
i tried extending , still not working for me.
Thanks.
In your class Bclass you should create some functions. In case below you are creating a new instance of Aclass and then using function aMethod.
Example
<?php
class Bclass {
public function __construct() {
$a = new Aclass();
$a->aMethod("some_text");
}
}
?>
Other way is extend Bclass. In this case your class Bclass extends everything what's in Aclass so you can use it just with $this.
Example
<?php
class Bclass extends Aclass {
public function __construct() {
$this->aMethod("some_text");
}
}
?>
Also your function aMethod in Aclass should have public or protected visibility. Public if you create an instance, protected if you extends. More informations can be found in manuals at the end.
Example
<?php
class Aclass {
public function aMethod($input) // protected if you will extend this class
{
echo 'Hello a world ';
}
}
?>
You can of course use both methods not only in __construct but also in other functions.
Manuals
PHP: Visibility
PHP: Constructors and Destructors
For this I'd use dependency injection. Which is just a fancy way of saying "sending an object of the A class when creating B".
In other words, something like this:
class typeA {
public function __construct () {};
public function test () {
return 'Test string';
}
}
class typeB {
protected $testObj;
public function __construct (typeA $testCase) {
$this->testObj = $testCase;
}
public function getTest () {
return $this->testObj->test ();
}
}
$a = new typeA ();
$b = new typeB ($a);
echo $b->getTest ();
Constructors are meant to be used to create an object that's ready to be used, which is why I've just stored the dependency inside the typeB object itself. Then, in the getTest() method I invoke the test() method of the object I'm depending upon, in order to get the needed data from it.
Doing it in this manner will allow you to write flexible OOP code, which can easily be expanded and extended as you require. Hiding the dependencies inside the constructors, by creating objects there, creates a hidden and hard dependency. Something which makes it a lot harder, if not down right impossible, to properly leverage the extensible nature of the class-based designs.
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
Take the following example:
class A implements Serializable {
serialize() {}
}
class B extends A {
serialize() {}
}
Class A is a persistant but minimal class used on every page. class B is temporary admin only (used on a settings screen) class which populates members by reading files.
I need to serialize the object and store in the database twice, once for regular pages, and the second (with a limited life) for the admin page.
$instance = new B(); // and populate
$data = serialize( $instance );
This will always call the over-ridden method. Is there any way I could cast $instance to type A so that I can call on class A's serialize method?
It's possible by creating a closure, Looks following snippet for demonstration
<?php
interface Greeting
{
public function hello();
}
class A implements Greeting
{
public function hello()
{
echo "Say hello from A\n";
}
}
class B extends A
{
public function hello()
{
echo "Say hello from B\n";
}
}
$b = new B();
$closure = function() {
return parent::hello();
};
$closure = $closure->bindTo($b, 'B');
$closure(); // Say hello from A
$b->hello(); // Say hello from B
The answer is no, you cannot. The children redeclare the parent functionality of the method and completely override it. For this, static methods would be required.
class TopParent
{
protected function foo()
{
$this->bar();
}
private function bar()
{
echo 'Bar';
}
}
class MidParent extends TopParent
{
protected function foo()
{
$this->midMethod();
parent::foo();
}
public function midMethod()
{
echo 'Mid';
}
public function generalMethod()
{
echo 'General';
}
}
Now the question is if I have a class, that extends MidParent because I need to call
class Target extends MidParent
{
//How to override this method to return TopParent::foo(); ?
protected function foo()
{
}
}
So I need to do this:
$mid = new MidParent();
$mid->foo(); // MidBar
$taget = new Target();
$target->generalMethod(); // General
$target->foo(); // Bar
UPDATE
Top parent is ActiveRecord class, mid is my model object. I want to use model in yii ConsoleApplication. I use 'user' module in this model, and console app doesn't support this module. So I need to override method afterFind, where user module is called. So the Target class is the class that overrides some methods from model which uses some modules that console application doesn't support.
Try this (http://php.net/manual/en/language.oop5.final.php - not allow to overriding in the childrens):
final protected function foo()
{
$this->midMethod();
parent::foo();
}
in class MidParent and the class Target can't overrides this method.
Directly - you can't. This is how OOP works.
You can do it by a little redesign, e.g. in MidParent add method:
protected function parentFoo()
{
parent::foo();
}
and in Target:
public function foo()
{
$this->parentFoo();
}
But, again, this is only a workaround to solve your question and not a solution.
Actually, you can do this like this way with Reflection::getParentClass():
class Foo
{
public function test($x, $y)
{
echo(sprintf('I am test of Foo with %s, %s'.PHP_EOL, $x, $y));
}
}
class Bar extends Foo
{
public function test()
{
echo('I am test of Bar'.PHP_EOL);
parent::test();
}
}
class Baz extends Bar
{
public function test()
{
$class = new ReflectionClass(get_class($this));
return call_user_func_array(
[$class->getParentClass()->getParentClass()->getName(), 'test'],
func_get_args()
);
}
}
$obj = new Baz();
$obj->test('bee', 'feo'); //I am test of Foo with bee, feo
-but this is an architecture smell in any case. If you need something like this, that should tell you: you're doing something wrong. I don't want to recommend anyone to use this way, but since it's possible - here it is.
#AnatoliyGusarov, your question is interesting and in a sense you can achieve what you desire using yii and php advances features like Traits and Traits in Yii.
Given that it depends on what version of php you are using.However in yii you can achieve this by behaviors and check this SOQ.
In a nutshell you have to use language advanced features or YII framework features to come around this kind of issues,but that boils down to actual requirements
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.