I am trying to inherit a set of different parent class and even not inherit any class but as per some condition. For example, Here is what I would like to do
$choice = 2;
switch($choice) {
case 1:
class child extends parent1
break;
case 2:
class child extends parent2
break;
default:
class child
//extend nothing
break;
}
I think you can figure out what I am trying achieve here.
parent classes
class car { }
Child classes
class ferari extends car { }
class ford extends car { }
grandchild classes
class automaticcar { }
class manualcar { }
Now this grandclasses need to interit a parent as per the value sent from the form using post. Something like this
$brand = $_POST['brand'];
if(isset($brand) && !empty($brand)) {
class automaticcar extends $brand
}
else {
class automaticcar extends car //or even nothing
}
//And then I wish to write the remaining portion of the clas
s
Is your question "Condition based inheritance good?" Then yes it looks necessary in many cases. But I think it would be better to initiate objects conditionally instead of defining extended classes inside condition.
Updates
As far as I understand you want to have different attributes/functions of the child class depending on condition. I faced a similar need/problem in my project. There it was relating to view logic. You may check How is my approach to reuse view logic in my project?
If I understand your problem correctly, then you should have the child classes ready beforehand like this in separate php files:-
child1 class
class1 child extends parent1
child2 class
class2 child extends parent2
And in the condition part do something like:-
$choice = 2;
switch($choice) {
case 1:
include /path/to/child1;
$obj = new child1();
break;
case 2:
include /path/to/child2;
$obj = new child2();
break;
default:
include /path/to/child;
$obj = new child();
//extend nothing
break;
}
The kind of inheritance you are trying to obtain is not attainable using a language which inheritance is based on classes.
The closer solution you can obtain is using a sort of decorator pattern with a bit of magic methods. something like this:
$choice = 2;
switch($choice) {
case 1:
$obj = new child(new parent1());
break;
case 2:
$obj = new child(new parent2());
break;
default:
//extend nothing
$obj = new child();
break;
}
with child being similar to this:
class child {
function __construct($composeObj) {
$this->core = $composeObj;
// wathever you need to iniyialize
}
function __call($name, $params) {
return call_user_func(array($sthis->core, $name), $parameters);
}
// other functions
} // eo child
This solution have some caveats but if you can cope with them (the object does not belongs to the family of the composited object, call_user_func does not pass the parameters by reference) this is a suitable solution.
A better solution is the factory approach suggested by sandeepan but you already refused it.
A Ferrari is not different to a Ford in the properties or methods it supplies. They are both still cars. They just have different values for their attributes. Creating a spezialized subclass shouldn't be necessary for that. Instead try this route:
class Car
{
protected $_manufacturer;
protected $_engine;
protected $_gear;
protected $_price;
protected $_color;
public function __construct($manufacturer, $engine, $gear, $price, $color)
{
// setting properties
}
}
class CarFactory
{
public static function createFerrari()
{
$engine = new Engine(500); // PS
$gear = new Gear(6, 'manual');
return new Car('Ferrari', $engine, $gear, '250000', 'red');
}
public static function createFord()
{
$engine = new Engine(90); // PS
$gear = new Gear(5, 'automatic');
return new Car('Ford', $engine, $gear, '50000', 'black');
}
// other car creation methods ...
}
If you extend a class, you are creating an is-a relationship. The subclass is a specialized parent class. Gear and Engine is nothing a Car is, but something a car has. Whenever you can describe a class to have something, it's a candidate for it's own class or for just being an attribute. Whether it should be it's own class depends on whether the thing encapsulated own unique state and responsibiliy.
First off, I really don't think you understand object oriented principles well enough to ask for this functionality. It's not really needed with the style of OOP that PHP implements.
You are requesting something like conditional mix-ins. It's possible to implement it, but it is a huge kludge and should be avoided. Here's something I put together a while ago when I was just testing some concepts:
<?php
class Mixin
{
private $objects = array();
private $funcs = array();
public function addMixin(Mixable $object)
{
$exported_vars = $object->exportVars();
foreach ($exported_vars as $key => &$ref)
$this->$key = &$ref;
$vars = array();
foreach (array_keys(get_object_vars($this)) as $key)
$vars[$key] = &$this->$key;
$object->importVars($vars);
$this->objects[] = $object;
}
public function __call($method, $args)
{
if (!isset($this->funcs[$method]))
{
$found = false;
foreach ($this->objects as $obj)
{
if (method_exists($obj, $method))
{
$found = true;
$this->funcs[$method] = array($obj, $method);
break;
}
}
if (!$found)
throw new Exception("method doesn't exist");
}
return call_user_func_array($this->funcs[$method], $args);
}
}
class Mixable
{
public function exportVars()
{
$vars = array();
foreach (array_keys(get_object_vars($this)) as $key)
{
$vars[$key] = &$this->$key;
}
return $vars;
}
public function importVars($vars)
{
foreach ($vars as $key => &$ref)
{
$this->$key = &$ref;
}
}
}
?>
You would use it like:
<?php
class Parent1 extends Mixable
{
protected $name = 'Parent 1';
public function p1()
{
print "P1
";
}
}
class Parent2 extends Mixable
{
protected $name = 'Parent 2';
public function p2()
{
print "P2
";
}
}
class Child1 extends Mixin
{
public function whoAmI()
{
print $this->name."
";
}
}
$foo = new Child1();
if (mt_rand(1, 2) == 1)
{
$foo->addMixin(new Parent1());
$foo->p1();
}
else
{
$foo->addMixin(new Parent2());
$foo->p2();
}
$foo->whoAmI();
?>
Please do not try to use the code! Even if it were production ready, it's a terrible concept. I put it here to show you how it would work.
I think what you really should be doing is something more like a Factory pattern: build a CarFactory
class that returns a properly subclassed Car
. (Or you could create a factory method within a Car
class.)
It could be something like Car::get($_POST['brand'])
.
I know I'm almost SIX years late. But just in case someone lands here again, I decided to put it here.
Please note that I do not assume the implicit understanding that the class names Car
, Ferrari
, Ford
etc are vehicles. So as to be able to apply this pattern in a generic fashion.
I'll use either of the following approaches in the decreasing order of preference:
Class alias PHP Manual (PHP 5 >= 5.3.0, PHP 7)
// Assuming $my_brand is the required brand
if (!class_exists($my_brand))
{
// Raise suitable warning here and exit
exit('Brand ' . $my_brand . 'not found.');
}
class_alias($my_brand, 'MyBrand');
class AutomaticCar extends MyBrand
{
}
Conditional stacking of inheritance (Too much work, but useful in a larger size project)
// File: lib/MyBrand/Ferrari.php
class MyBrand extends Ferrari
{
}
// File: lib/MyBrand/Ford.php
class MyBrand extends Ford
{
}
// Then in your main code, assuming $my_brand is the required brand
if (!file_exists('lib/MyBrand/' . $my_brand . '.php'))
{
// Raise suitable warning here and exit
exit('Brand ' . $my_brand . 'not found.');
}
class AutomaticCar extends MyBrand
{
}
Other patterns that I can right now (such as Factory Design and Decorator Pattern) think of would take different routes which doesn't comply with the exact topic. So thats all for now.