Condition Based inheritance? - php

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 class

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\n";
}
}
class Parent2 extends Mixable
{
protected $name = 'Parent 2';
public function p2()
{
print "P2\n";
}
}
class Child1 extends Mixin
{
public function whoAmI()
{
print $this->name."\n";
}
}
$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']).

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;
}

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.

Related

Some explanation about calling PHP function in class

I know it is basic php question but I had something that I didn't understand, it is about the return; when I call a function, the bellow code is just an exemple.
Case : In TEST2 when I do my check then I return, that return; do the job and stop the execution of the next code TEST3 Good.
Now in TEST1 I Call a function _checkThat(), and this function do a check then redirect. the problem is it returns shure but the next code will also be executed TEST2 and TEST3 Why ? why when I put the content of that function directly in TEST1 it returns and stop the execution of next code ?
<?php class Some_Class_IndexController extends Some_Other_Controller_Front_Action
{
$name = $this->getRequest()->getParam('name'); //Output: "john"
$email = $this->getRequest()->getParam('email'); //Output: "john#gmail.com"
$phone = $this->getRequest()->getParam('phone'); //Output: 09000000
//TEST1
if(isset($name)) {
$this->_checkThat($name);
}
//TEST2
if(isset($email)) {
if($email === "john#gmail.com") {
//some code to redirect to another page
return;
} else {
//some code to redirect to another page
return;
}
}
//TEST3
if(isset($name)) {
$this->_checkthat();
}
private function _checkThat($name) {
if ($name !== "john") {
//some code to redirect to another page
return;
}
}
}
Other question, Can I use continue; in this case :
if(isset($name)) {
Mage::log('check passed'); //This just write in logs
continue; // or something else
} else if (!isset($name)) {
// some code
}
Although as already mentioned in comments your example code is not correct, your problem here:
if(isset($name)) {
$this->_checkThat($name);
}
is that you do nothing with result returned by _checkThat(). And obviously you should return it:
if(isset($name)) {
return $this->_checkThat($name);
}
As a sidenote, I should mention that naming methods with _ to mark them protected/private is out-of-date practice since php5.
Here is a simple guide.
Inheritance – reusing code the OOP way
Inheritance is a fundamental capability/construct in Object oriented programming where you can use one class, as the base/basis for another class or many other classes.
Why do it?
Doing this allows you to efficiently reuse the code found in your base class.
Say, you wanted to create a new employee class since we can say that employee is a type/kind of `person’, they will share common properties and methods.
Making some sense?
In this type of situation, inheritance can make your code lighter because you are reusing the same code in two different classes. But unlike old-school PHP:
You only have to type the code out once.
The actual code being reused, can be reused in many (unlimited) classes
but it is only typed out in one place conceptually, this is sort-of
like PHP includes().
Take a look at the sample PHP code:
// 'extends' is the keyword that enables inheritance
class employee extends person
{
function __construct($employee_name) {
$this->set_name($employee_name);
}
}
Reusing code with inheritance:
Because the class employee is based on the class person, employee automatically has all the public and protected properties and methods of ‘person’.
Nerd note: Nerds would say that employee is a type of person.
The code:
class employee extends person
{
function __construct($employee_name){
$this->set_name($employee_name);
}
}
Notice how we are able to use set_name() in employee, even though we did not declare that method in the employee class. That is because we already created set_name() in the class person.
Nerd Note: the person class is called (by nerds,) the base class or the parent class because it is the class that the employee is based on. This class hierarchy can become important down the road when your projects become more complex.
Reusing code with inheritance:
As you can see in the code snippet below, we can call get_name on our employee object, courtesy of person.
The code:
<?phpinclude("class_lib.php"); ?>
<?php
// Using our PHP objects in our PHP pages.
$stefan = new person("Stefan Mischook");
echo "Stefan's full name: " . $stefan->get_name();
$james = new employee("Johnny Fingers");
echo "---> " . $james->get_name();
?>
This is a classic example of how OOP can reduce the number of lines of code (don’t have to write the same methods twice) while still keeping your code modular and much easier to maintain.
Overriding methods 1
Sometimes (when using inheritance,) you may need to change how a method works from the base class.
For example, let us say set_name() method in the 'employee' class, had to do something different than what it does in the person class.
You override the person classes version of set_name(), by declaring the same method in employee.
The code snippet:
<?php
class person
{
protected function set_name($new_name) {
if ($new_name != "Jimmy Two Guns") {
$this->name = strtoupper($new_name);
}
}
}
class employee extends person
{
protected function set_name($new_name) {
if ($new_name == "Stefan Sucks") {
$this->name = $new_name;
}
}
}
?>
Notice how set_name() is different in the employee class from the version found in the parent class: person.
<Overriding methods: 2
Sometimes you may need to access your base class’s version of a method you overrode in the derived (sometimes called ‘child’) class.
In our example, we overrode the set_name() method in the employee class. Now I have
used this code:
hperson::set_name($new_name);
to access the parent class’ (person) version of the set_name() method.
The code:
<?php
class person
{
// explicitly adding class properties are optional - but
// is good practice
var $name;
function __construct($persons_name) {
$this->name = $persons_name;
}
public function get_name() {
return $this->name;
}
// protected methods and properties restrict access to
// those elements.
protected function set_name($new_name) {
if ($this->name != "Jimmy Two Guns") {
$this->name = strtoupper($new_name);
}
}
}
// 'extends' is the keyword that enables inheritance
class employee extends person
{
protected function set_name($new_name) {
if ($new_name == "Stefan Sucks") {
$this->name = $new_name;
}
else if ($new_name == "Johnny Fingers") {
person::set_name($new_name);
}
}
function __construct($employee_name)
{
$this->set_name($employee_name);
}
}
?>
Notes: Using the symbol:
::
… allows you to specifically name the class where you want PHP to search for a method:
person::set_name()
… tells PHP to search for set_name() in the person class.
There is also a shortcut if you just want refer to current class’s parent – by using the parent keyword.
The code:
protected function set_name($new_name)
{
if ($new_name == "Stefan Sucks") {
$this->name = $new_name;
}
else if ($new_name == "Johnny Fingers") {
parent::set_name($new_name);
}
}

Instantiating one instance each of parent's class children?

For testing my stuff i would like to automaticaly instantiate one instance each per child of a parent class i name, without calling the class name manually (its too many).
For example:
Class Animal {
public weight;
}
Class Dog extends Animal {
public weight = 20;
}
Class SmallDog extends Dog {
public weight = 18;
}
Class Cat extends Animal {
public weight = 10;
}
function xy(){
$classes = array();
foreach parent->child as child(){
$classes[] = new child()
}
//$classes = [Dog, SmallDog, Cat]
}
Is something like (tm) doable and if so, how ?
As further information, i have an autoload_register that holds each class, but it also holds various classes i dont want to instantiate.
Also, relative to the above example, i have Animal, Dog and Cat all assembled in a single file, lets call it animal.php.
In the code above, i would except to get an instance of anything below Animal, including SmallDog. If its not possible that way, i would be okay with getting Dog and Cat and calling the function with parent being Dog (to get SmallDog instantiated).
thanks for your advice,
This is not really useful in real case scenarios but using #axiac comment you can implement ChildrenResolver class:
class ChildrenResolver
{
protected $defaultClasses = [
0 => 'stdClass',
1 => 'Exception',
2 => 'ErrorException',
// ...
];
protected $declaredClasses;
public function __construct(array $declaredClasses = null)
{
if (is_null($declaredClasses)) {
$declaredClasses = array_diff(
get_declared_classes(),
$this->defaultClasses
);
}
$this->declaredClasses = $declaredClasses;
}
public function getChildClasses($class)
{
return array_filter(
$this->declaredClasses,
function ($declaredClassName) use ($class) {
$declaredClass = new ReflectionClass($declaredClassName);
while(($parent = $declaredClass->getParentClass())) {
if ($parent->name === $class) {
return true;
}
$declaredClass = $parent;
}
return false;
}
);
}
public function getDirectChildClasses($class)
{
return array_filter(
$this->declaredClasses,
function ($declaredClassName) use ($class) {
$declaredClass = new ReflectionClass($declaredClassName);
$parent = $declaredClass->getParentClass();
return $parent ? $parent->name === $class : false;
}
);
}
}
The idea is pretty simple. We loop over declared classes (obtained via get_declared_classes), reflect every class using ReflectionClass and compare parent, which we get from ReflectionClass::getParentClass, with given parent. For the sake of convinience I used array_filter function.
Having this class you can resolve children like this:
$childrenResolver = new ChildrenResolver;
var_dump($childrenResolver->getChildClasses('Animal'));
var_dump($childrenResolver->getDirectChildClasses('Animal'));
var_dump($childrenResolver->getChildClasses('Dog'));
Here is working demo.

OOP approach for wrapping subclasses of database rows

Let's say I want to store dogs in a database table with each dog having its own subclass in PHP.
Basically I want to avoid storing/listing the subclass names in different places in the code. What would be a good OOP approach for that?
abstract class Dog {
protected $data;
public function __construct($data) {
$this->data = $data;
}
public function name() {
return $this->data["name"];
}
abstract public function breed();
}
class GermanShepherd extends Dog {
public function breed() {
return _("German Shepherd");
}
}
class BullDog extends Dog {
public function breed() {
return _("Bulldog");
}
}
Now I have this class that handles groups of objects (i.e. dogs):
class Dogs {
public static function getDogs() {
// ...
$ret = array();
while ($row = mysql_fetch_assoc()) {
switch ($row["type"]) { // I could do this using a lookup array
case "shepherd": $dog = "GermanShepherd"; break;
case "bulldog": $dog = "Bulldog"; break;
}
$ret[] = new $dog($row);
}
return $ret;
}
}
And I would like to use this class to get the dog types in my view (especially for an add dog form), instead of listing the class names:
?><form><select name="type"><?php
foreach (array("GermanShepherd", "Bulldog") as $dog) { // here I would like to do avoid listing the class names again
?><option value="<?=$dog ?>"><?php
$d = new $dog; // actually I can't instantiate the class here because I don't have any data at this point
echo $d->name();
?></option><?php
}
?></select></form><?php
I would like to incorporate this into the Dogs class, something along the lines of this:
class Dogs {
private static $dogs = array(
"shepherd" => "GermanShepherd",
"bulldog" => "Bulldog",
);
public static function getDogs() {
// ...
$ret = array();
while ($row = mysql_fetch_assoc()) {
$dog = self::$dogs[$row["type"]];
$ret[] = new $dog($row);
}
return $ret;
}
public static function getDogTypes() {
return array_values(self::$dogs);
}
}
?><form><select name="type"><?php
foreach (Dogs::getDogTypes() as $dog) {
?><option value="<?=$dog ?>"><?php
$d = new $dog; // here I still need to instantiate the class and I don't have any data to provide it with
echo $d->name();
?></option><?php
}
?></select></form><?php
This would somewhat work so far, but what if I need more class specific information, for example when I have more fields specific to a dog type?
foreach (Dogs::getDogTypes() as $dog) {
$d = new $dog; // instantiate again?
foreach ($d->formFields() as $f) { // I wouldn't do it like this, just putting this here for demonstrative purposes
echo $f;
}
}
I think part of the problem lies in the fact that I need to be able to use my classes with and without database data: Everything seems very reasonable when I have the data from the database table, but I also need the data when I generate the form when creating a new dog.
Thanks for your ideas!
First make use of Interfaces. This will show you that having more specific interfaces (different class methods and properties per subclass) will make you need to deal with them differently in concrete. So they will show you where the deficiencies are and will enable you to streamline your objects into something more re-useable.
As long as your objects are only storing some data, use a data transfer object instead - which is of any "type". So you don't need to deal with type. However, you can use StdClass or Array for that as well if you want to keep it basic. The plus-side is: You don't need to actually write that much code.
In case it's not sufficient (as it will be), only add the code when you need to. Should keep things more simple in the long run then. So start with a basic data transfer object class and build upon it.
So use the classes you write to separate concerns, not to interweave the concerns. Encapsulate what varies, so your code can actually benefit from your design.
I think the static array in your Dogs class is quite an acceptable solution. It doesn't cover for the instantiating problem, but you can fix that with a (static) factory method. To make the instantiating even easier and more scalable, you can make sure the stored strings map to object names somehow:
$dog = 'Dog' . ucfirst( $row['type'] );
$ret[] = new $dog;
I don't think a ->getFormFields() method is a bad idea at all; when the fields differ per Dog type, it would be perfectly valid OO to incorporate that in the object!
What about storing your dogs in a 2D array?
while ($row = mysql_fetch_assoc()) {
switch ($row["type"]) { // I could do this using a lookup array
case "shepherd":
$dog = "GermanShepherd";
break;
case "bulldog":
$dog = "Bulldog";
break;
} // switch
$ret[$row["type"]][] = new $dog($row);
} // while

A PHP design pattern for the model part [PHP Zend Framework]

I have a PHP MVC application using Zend Framework. As presented in the quickstart, I use 3 layers for the model part :
Model (business logic)
Data mapper
Table data gateway (or data access object, i.e. one class per SQL table)
The model is UML designed and totally independent of the DB.
My problem is : I can't have multiple instances of the same "instance/record".
For example : if I get, for example, the user "Chuck Norris" with id=5, this will create a new model instance wich members will be filled by the data mapper (the data mapper query the table data gateway that query the DB). Then, if I change the name to "Duck Norras", don't save it in DB right away, and re-load the same user in another variable, I have "synchronisation" problems... (different instances for the same "record")
Right now, I use the Multiton / Identity Map pattern : like Singleton, but multiple instances indexed by a key (wich is the user ID in our example). But this is complicating my developpement a lot, and my testings too.
How to do it right ?
Identity Map
Edit
In response to this comment:
If I have a "select * from X", how can I skip getting the already loaded records ?
You can't in the query itself, but you can in the logic that loads the rows into entity objects. In pseudo-code:
class Person {}
class PersonMapper {
protected $identity_map = array();
function load($row) {
if (!isset($this->identity_map[$row['id']])) {
$person = new Person();
foreach ($row as $key => $value) {
$person->$key = $value;
}
$this->identity_map[$row['id']] = $person;
}
return $this->identity_map[$row['id']];
}
}
class MappingIterator {
function __construct($resultset, $mapper) {
$this->resultset = $resultset;
$this->mapper = $mapper;
}
function next() {
$row = next($this->resultset);
if ($row) {
return $this->mapper->load($row);
}
}
}
In practice, you'd probably want your MappingIterator to implement Iterator, but I skipped it for brevity.
Keep all loaded model instances in "live model pool". When you load/query a model, first check if it has been already loaded into pool (use primary key or similar concept). If so, return the object (or a reference) from pool. This way all your references point to the same object. My terminology may be incorrect but hopefully you get the idea. Basically the pool acts as a cache between business logic and database.
Multiton
Best option if you want to use a variety of singletons in your project.
<?php
abstract class FactoryAbstract {
protected static $instances = array();
public static function getInstance() {
$className = static::getClassName();
if (!(self::$instances[$className] instanceof $className)) {
self::$instances[$className] = new $className();
}
return self::$instances[$className];
}
public static function removeInstance() {
$className = static::getClassName();
if (array_key_exists($className, self::$instances)) {
unset(self::$instances[$className]);
}
}
final protected static function getClassName() {
return get_called_class();
}
protected function __construct() { }
final protected function __clone() { }
}
abstract class Factory extends FactoryAbstract {
final public static function getInstance() {
return parent::getInstance();
}
final public static function removeInstance() {
parent::removeInstance();
}
}
// using:
class FirstProduct extends Factory {
public $a = [];
}
class SecondProduct extends FirstProduct {
}
FirstProduct::getInstance()->a[] = 1;
SecondProduct::getInstance()->a[] = 2;
FirstProduct::getInstance()->a[] = 3;
SecondProduct::getInstance()->a[] = 4;
print_r(FirstProduct::getInstance()->a);
// array(1, 3)
print_r(SecondProduct::getInstance()->a);
// array(2, 4)

I need to collect an array of classes to call their static variables in php

First thing i want to say that it's not an easy question to explain, so please be patient if it seems confusing.
I have a set of classes like this
class Product {
public static $static_type = 'product';
public static $static_table = 'product_table';
public function __construct($params) { //do some }
}
and then there are the classes News, Events etc
From another class i need to access to those static variables inside these classes in an iterative way. Something like:
//...
if (Product::$static_type) { //do some }
else if (News::$static_type) { //do other }
//...
I want to trasform it in a cycle, like foreach in a way like this (it's not correct but makes sense to my question)
foreach ($classes as $class) {
echo $class::$static_type; //brrrr, just to render the idea :)
}
So i think about a singleton/static class that has a static method returning an array of my classes (not instantiated). Like this:
class Conf {
public function __construct() {
//nothing
}
public static function get_class_array () {
//how to do this???
}
}
and then
foreach (Conf::get_class_array() as $class) {
echo $class::$static_type; //brrrr, just to render the idea :)
}
How i can reach this? I don't want to instantiate Product, News or others in this case.
Edit: eval is evil, i don't want to use it. No tricks with get_declared_class, if there's no way to solve I will use reflection, that i think it's the more elegant way among the mentioned :(.
Edit: in the meantime i'll do the Conf::get_class_array() in this way
public static function get_class_array () {
return array(new ReflectionClass('Prodotto'), new ReflectionClass('News'));
}
and then call it here:
foreach (Conf::get_class_array() as $class) {
echo $class->getStaticPropertyValue('static_type');
}
I don't think you can do this. You could however do one of these:
$properties = get_class_vars('Product');
echo $properties['static_type'];
or
$class = new ReflectionClass('product');
echo $class->getStaticPropertyValue('static_type');
Note that in PHP 5.3 echo $class::$static_type; will work (http://php.net/manual/en/language.oop5.static.php)
Until 5.3.0, you can try this method. Create a container class as you suggested (we'll call it Conf to stick with what you had), and provide two methods for setting and getting applicable classes that you want to iterate over:
<?php
class Conf {
private static $instance;
private $classes = array();
public static function getInstance() {
if ( is_null(self::$instance) ) {
self::$instance = new self();
}
return self::$instance;
}
public function registerClass($className) {
// Use associative index to maintain uniqueness
$this->classes[$className] = $className;
}
public function getRegisteredClasses() {
return $this->classes;
}
}
Some example classes and how to register them:
class X {
public static $a = "catus";
public static $b = "pants";
}
class Y {
public static $a = "apples";
public static $b = "bananers";
}
$conf = Conf::getInstance();
$conf->registerClass("X");
$conf->registerClass("Y");
Now, to access and/or alter the static members, you can do something like the following (using RefelectionClass as tom Haigh pointed out):
$conf = Conf::getInstance();
echo "<pre>";
foreach ( $conf->getRegisteredClasses() as $class ) {
$reflection = new ReflectionClass($class);
echo "<hr/>Class: $class\n";
// Access example
print_r( $reflection->getStaticProperties() );
// Alter example
$reflection->setStaticPropertyValue("a",
$reflection->getStaticPropertyValue("a") . "-modified"
);
print_r( $reflection->getStaticProperties() );
}
If you have a class naming convention like Com_Example_Static_X and Com_Example_Static_Y, you can simplify Conf->getRegisteredClasses() (and even make it a static method if you so desire) by doing as n3rd suggested:
class Conf {
// ....
static public function getMatchingClasses($pattern="/^Com_Example_Static_.+$/") {
$response = array();
foreach ( get_declared_classes() as $className ) {
if ( preg_match($pattern, $className, $m) ) {
$response[] = $className;
}
}
return $response;
}
}
And, of course, update your foreach to:
foreach ( Conf::getMatchingClasses() as $class ) {
// ...
}
Hope that was helpful.
You can use get_declared_classes() to get a list of classes. This will be all class though, not just the ones you've declared.
You should make all your classes inherit from a base class:
class Product extends MyBase {}
Then you can list the classes like this
function get_class_array()
{
$myClasses = array();
foreach (get_declared_classes as $class)
{
if (is_subclass_of($class, 'MyBase'))
$myClasses[] = $class;
}
return $myClasses;
}
Then you can get the data like this:
foreach (get_class_array() as $class)
echo eval("return $class::\$foo;"); // Yes yes, eval is evil, we know...
To get a list of classes, you can use get_declared_classes. Then you'll have to determine which of those classes you want to process.
You could do this by looking for a common base class with is_subclass_of, or using ReflectionClass to see if it has the static member variables you are interested in.
I don't think there's an easy way to do this. Here are a few ideas off the top of my head how you could go about doing this:
Use get_declared_classes() to retrieve a list of all defined classes and check them against your naming scheme (e.g. MyNamespace_*) or whether they implement an interface (e.g. MyStaticEnumerable).
Kinda like the above, but a little more sophisticated: write your on class loader and have it check whether a loaded class is one of ones you want to enumerate. If so, make it known to some manager class.
Check the directory in which the classes are defined to manually enumerate all classes.

Categories