9

As you can see below I have a super class (Article) and two sub classes. I want each of the sub classes to have a static array that shall hold all it's objects.

abstract class Article
{
    public static $articles = array(); // Variable for storing all the objects of each sub-class.

    public function add_Object_To_Array()
    {       
        array_push(self::$articles, $this);
    }
}

class Report extends Article{}
class Interview extends Article{}

-Making two Report objects and adding them to their array:

$tmp = new Report();
$tmp->add_Object_To_Array();

$tmp = new Report();
$tmp->add_Object_To_Array();

-Making two Interview objects and adding them to their array:

$tmp = new Interview();
$tmp->add_Object_To_Array();

$tmp = new Interview();
$tmp->add_Object_To_Array();

print_r(Report::$articles);
print_r(Interview::$articles);

-The above script spits out the two arays:

Array
(
    [0] => Report Object()

    [1] => Report Object()

    [2] => Interview Object()

    [3] => Interview Object()   
)
Array
(
    [0] => Report Object()

    [1] => Report Object()

    [2] => Interview Object()

    [3] => Interview Object()    
)

Which looks pretty similar if you ask me, but the first one should only contain Reports, and the second one only Interviews.

1. It seems that there is only one array, why is it only one array?
2. I have a static container of objects in the same class, is this bad coding? (Any suggestions?)

I'm pretty new to php, but have a background from java.

6
  • abstract class Article is missing its closing }. $this in that context refers to abstract class Article. How will you push that on to the Array? Commented Jul 13, 2013 at 18:11
  • Thanks, but I thought: $tmp = new Report(); $tmp->add_Object_To_Array(); referred to the the "Report object's function"? (OOP thinking). I'm aware of the fact that there can be no objects of the abstract class . Commented Jul 13, 2013 at 18:25
  • use static::$articles rather than self::$articles Commented Jul 13, 2013 at 18:27
  • 1
    Thanks, but I already tried that, it didn't work.. Commented Jul 13, 2013 at 18:34
  • @AlexP static:: is required, but it's only part of the solution. You also need to define the variables separately. Commented Jul 13, 2013 at 18:39

3 Answers 3

18

Everything is going into only one array for two reasons:

  1. The $articles property is only defined in the Article class.

    Static class properties do not get inherited the same way you might expect if you're used to non-static properties. While they are available to the child classes, they're still referencing a single variable on the parent class - resulting in the behavior you're seeing here where both child classes are sharing the same array.

    The only way to prevent this is to define a separate array in each of your child classes, like this:

    class Report extends Article {
        public static $articles = array();
    }
    class Interview extends Article {
        public static $articles = array();
    }
    

    This actually makes sense if you think of the static variable declarations as code that gets run when the class is defined. Creating a static variable and assigning an empty array to it happens when the Article class is defined. It doesn't happen again when the Interview and Report classes are defined. There's only one time that an empty array is getting assigned - there's only one shared variable.

  2. You're using self in your add_Object_To_Array() method instead of static.

    • self:: refers to the class it is defined in, so since your add_Object_To_Array() method is defined in the Article class, it'll refer to the Article::$articles array.

    • static:: Is available starting in PHP 5.3, and refers to the class it is called on. This is known as Late Static Binding, and will result in add_Object_To_Array() referring to either Report::$articles or Interview::$articles depending on the type of the object you're calling it on.

    This code will reference the two arrays that we declared in the first step:

    public function add_Object_To_Array() {
        array_push(static::$articles, $this);
    }
    
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much! Great answer, both educating and problem-solving! :)
3

I thought I'd present an alternate solution that changes your design slightly, but doesn't require static definitions in each subclass. Depending on your usage, this may or may not be a better option than the first solution.

This solution uses get_class() to find the type of the object we're storing, and then stores it in the parent class - in a sub-array keyed by classname:

abstract class Article
{
    public static $articles = array();

    public function add_Object_To_Array()
    {
        // get the actual class of the current object
        $class = get_class($this);
        // define an empty subarray for this class if we haven't seen it before
        if (!isset(self::$articles[$class])) {
            self::$articles[$class] = array();
        }
        // add this object to the appropriate subarray
        array_push(self::$articles[$class], $this);
    }
}
class Report extends Article{}
class Interview extends Article{}

The above code does not use late static binding, so it'll work with any PHP version, instead of just PHP 5.3+.

When you run this version with your original example, you'll get output like this:

Array
(
    [Report] => Array
        (
            [0] => Report Object
                (
                )

            [1] => Report Object
                (
                )

        )

    [Interview] => Array
        (
            [0] => Interview Object
                (
                )

            [1] => Interview Object
                (
                )

        )

)

If you do have PHP 5.3 or later, you can extend this even more and use the get_called_class() function to define a getInstances() static method (in the Article class) that works like this:

public static function getInstances()
{
    $class = get_called_class();
    // return isset(self::$articles[$class]) ? self::$articles[$class] : array();
    if (isset(self::$articles[$class])) {
        return self::$articles[$class];
    } else {
        return array();
    }
}

Then you can call this method in your example like this:

print_r(Report::getInstances());
print_r(Interview::getInstances());

2 Comments

nice! I didn't quite catch your extended method, more precisely return isset(self::$articles[$class]) ? self::$articles[$class] : array();, I'm not very familiar with this type of if-statement. Does it return an empty array if the statement is false?
@MathiasCiarlo I'm using the ternary operator there. I've rewritten it to a clearer but essentially equivalent version.
1

For anyone looking for an alternative solution, apart the above good ones. You can use PHP's late static binding (static::), but you also need to define the variables separately in each subclass (this will make them independent).

You can use this:

abstract class Article
{
    public static $articles = array();

    public function add_Object_To_Array()
    {
        array_push(static::$articles, $this);
    }
}

class Report extends Article
{
    public static $articles = array();
}

class Interview extends Article
{
    public static $articles = array();
}

Then when calling add_Object_To_Array() from each subclass, it will store the articles in separate places.

See Late Static Bindings

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.