views:

173

answers:

5

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');
}
+2  A: 

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)

Tom Haigh
A: 

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.

Paul Dixon
+1  A: 

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...
Greg
they already inherit from a base class, but eval is evil (yes we know =) ), i'm interest in a possible cleaner way
avastreg
A: 

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.
n3rd
+2  A: 

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.

Justin Johnson
interesting point, but i think i'll do it simpler, always using Reflection. "Registering" classes is a good idea.
avastreg