Link to home
Start Free TrialLog in
Avatar of rgb192
rgb192Flag for United States of America

asked on

non static property array being populated

Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

The word "static" seems to have many meanings in PHP.  There is a procedural meaning associated with variables that hold their values across multiple function calls.  And there are the two OOP meanings.  Hopefully these links will disambiguate:
http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static
http://php.net/manual/en/language.oop5.static.php
http://php.net/manual/en/language.oop5.late-static-bindings.php
Avatar of rgb192

ASKER

https://www.experts-exchange.com/questions/28340946/array-of-multiple-employees-or-an-array-of-1-employee-information.html?anchorAnswerId=39789958#a39789958

How can

line 28 $this->_employees[] = $e;

be able to hold values from multiple instances.


and could you show me a similar example please.
See code below.  There are two classes defined here - Department and Employee.  In the Department class, line 13, part of the constructor that is run at the time a Department object is created from this class, says this:

$this->_employees = array();

And that statement creates an empty array in the object property named _employees.

The standard PHP notation to add a data element to the end of an array is to use the paired square brackets without any array key inside them.  Look for "creating/modifying with square bracket" on this page:
http://php.net/manual/en/language.types.array.php

Once the constructor has been run, the _employees array exists, but is empty.  It follows that we might want to add some employees using the addEmployee() method.  When we run that code, we find this statement on line 16:

$this->_employees[] = $e;

That takes the $e variable and attaches it to the end of the _employees array.  Each time we call the addEmployee() method we get one more employee added to the array.

/* Class Department.
 *  The class contains two attribute: name and employees[].
 *  The class contains two methods: 
 *  - __construct()
 *  - addEmployee()
 */
class Department 
{
    private $_name;
    private $_employees;
    function __construct($name) {
        $this->_name = $name;
        $this->_employees = array();
    }
    function addEmployee(Employee $e) {
        $this->_employees[] = $e;
        echo "<p>{$e->getName()} has been added to the {$this->_name} department.</p>";
    }
} // End of Department class.

/* Class Employee.
 *  The class contains one attribute: name.
 *  The class contains two methods: 
 *  - __construct()
 *  - getName()
 */
class Employee
{
    private $_name;
    function __construct($name) {
        $this->_name = $name;
    }
    function getName() {
        return $this->_name;
    }
} // End of Employee class.

Open in new window

Avatar of gr8gonzo
I think I misunderstood the wording of the question. I think you -might- be asking how an array works with an object. If I understand correctly, then the answer is the difference between "references" and "values".

When you define a new object like this:

$x = new Employee();

...you are doing two things. You are creating a new Employee object, which PHP holds in its memory. Internally, PHP might simply see it as object #4 or something like that.

You are also creating a variable $x, which is ACTUALLY just a reference to object #4. So $x isn't the actual object itself, it's just the very first reference to object #4.

If you were to then say:

$y = $x;

...then you are creating another reference. PHP sees this and says, "Okay, now $y and $x both point to object #4 that I'm holding in memory."

Now let's say you have an array, and you do this:

$z = new array();
$z[] = $x;

...now PHP has THREE different references to object #4.

So when it comes to objects, PHP isn't copying objects over and over again in memory. You don't have three separate copies of the Employee object. You have one object and three pointers to it.

When you make changes to the object using ANY of the pointers, PHP makes those changes to the object itself.

$x->Foo = "abc";
echo $y; // Will show "abc"
$y->Foo = "def";
echo $x; // Will show "def"
Avatar of rgb192

ASKER

I am understanding better but have more questions.


https://www.experts-exchange.com/questions/28342292/non-static-property-array-being-populated.html?anchorAnswerId=39809338#a39809338

how could you instantiate different objects in this example to use the same employee[] array?

gr8gonzo:
I do not understand how different objects can reference object#1

and what would the class be that holds class->foo property
$x->Foo = "abc";
echo $y; // Will show "abc"
$y->Foo = "def";
echo $x; // Will show "def"
SOLUTION
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
greetings rgb192, , you have not given me very clear or understandable descriptions for what you are after as your result for the code you say you need an example for? ?
I will talk some about what I will guess is the information you may be needing?
For me If I need to "Share" info-data between EVERY object of a Class , I will declare a STATIC property (array in this case) in that Class code. . .

as in the PHP manual -
class Employee {
public static $my_array = array('Foo');

    public function showMy_Array($key) {
        return self::$my_array[$key];
    }
}

BUT you give this condition, which seems confusing to me -
"non static property array being populated"
You also add this - "how could you instantiate different objects in this example to use the same employee[] array?"

Although I kind of think that I understand this last statement of yours, it is not real clear, BECAUSE the - employee[] array - can not be a property of the Object (instance) as in the the class cpde -
$this->_employees = array();
and be shared among ALL of the Class Objects (instances), in the "Usual" Cl;ass code, this _employees property would be a STATIC to be shared among ALL of the Class Objects (instances). . . but for some reason you do not want this.

However, using an Array created OUTSIDE of the Class code, and then Passing that into each new instance by "REFERENCE" will let you do as you have requested, however, this is not really so much a "Class" learning thing, but more of a PHP learning thing, (my view).
     PHP code below
class Department {
protected $_name = '', $_employees = array();

// IMPORTANT, added the $Emp_Array as reference with &
function __construct($name, &$Emp_Array) {
    $this->_name = $name;
    $this->_employees = &$Emp_Array;// add as reference to $this->_employees
    }

function addEmployee(Employee $e) {
// PLEASE NOTICE, here the $this->_employees[] referes to an ARRAY OUTSIDE of any class! !
    $this->_employees[] = $e;
    echo "<p>{$e->getName()} has been added to the {$this->_name} department.</p>";
    }

function listAllEmployees( ) {
// ADDED this function to show you that the $this->_employees is "Shared"
    if (count($this->_employees) < 1) {
        echo 'NO EMPLOYEES IN LIST!<br />';
        return;
        }
    $cnt = count($this->_employees);
    echo 'There are ',$cnt,' Employess in ALL Departments by Names below<br />';
    for($i=0; $i < $cnt; ++$i) {
        echo $this->_employees[$i]->getName(),'<br />';
        }
	}
} // end of Class Department
	
class Employee {
protected $_name = '';

function __construct($name) {
        $this->_name = $name;
    }
    function getName() {
       return $this->_name;
    }
} // End of Employee class.

// // // // // // // END OF CLASS CODE

// first create a normal php array to be shared
$empArray0 = array();
// create several Employee objects
$em1 = new Employee('Start Name HR');
$em2 = new Employee('Name for Second HR');
$em3 = new Employee('Third Employee SL');
$em4 = new Employee('Forth Name SL');

// create two Departments -
$HRdpart = new Department('Human Resources', $empArray0);
$SLdpart = new Department('Sales', $empArray0);
// ADD two employees to each department
$HRdpart->addEmployee($em1);
$HRdpart->addEmployee($em2);
// to Sales
$SLdpart->addEmployee($em3);
$SLdpart->addEmployee($em4);

//list ALL employees in ALL depatrments
$HRdpart->listAllEmployees(); // will show FOUR employees, NOT two

Open in new window

I ran this code on my server and it worked, however this is a VERY poor Class code, for what you say you want, it does NOT have any way to list only the employees in ONE department, there could be changes to code to list just one department employ or ALL employ.
Even though this code works, I would NEVER USE IT! ! BECAUSE it is terrible at Encapsulation for Class properties , , as the  $empArray0  array is outside of any class, I would do with the Class STATIC array property, as this is what the class "static" property was designed to do, to Share values between instances.
ask questions if you need other factors.
gr8gonzo:
I do not understand how different objects can reference object#1

Okay, so let's try some visuals. First, let's say that we have this code:

<?php

// First, a class definition with some default values.
class Foo
{
  public $some_integer = 12345;
  public $some_string = "abc";
  public $some_foo;
}

// Create a few things in memory:
// 1. Object #1, an instance of Foo
// 2. The Values #1 and #2, which are the defaults for Foo.
// 3. The pointer named $a.

$a = new Foo();

// Now create another pointer to object #1.
$b = $a;

// And now let's create another one, this time in an array.
$array = array();
$array["myobject"] = $b; // $a would also work instead of $b

// NOTE: My diagram doesn't break down the array structure, but ultimately, the key of an array is also a pointer.

// Now create another object, Object #2. It also creates the default values and the pointer variable called $c.
$c = new Foo();

// Now make object #1 reference object #2, since the properties of an object, like $some_foo, are also pointers.
$a->some_foo = $c;

// All SIMPLE values like strings and integers are never passed by reference. So if you try to do $var1 = $var2, then you are COPYING the value.

// Create Value #5
$d = 12345;

// Create Value #6
$e = $d;
?>

Open in new window


Now check out the attached diagram. The blue shapes with rounded corners are pointers, while the grey squares are actual values in memory.

If you're still at a loss, think about this. You, me, Ray, and Slick are all updating the same question on Experts Exchange. The content of the question and the comments are not stored on our computers. The content is all stored in one location on Experts Exchange.

When we want to make an update to this question, we each follow our OWN browser bookmarks (or links in our OWN email). While your bookmark is different than my email link, they both take us to the same question on Experts Exchange. We each have different pointers that take us to the same value.

Questions and comments (the "values") can also point to other questions. For example, in your original question, you have a link to:

https://www.experts-exchange.com/questions/28340946/array-of-multiple-employees-or-an-array-of-1-employee-information.html?anchorAnswerId=39789958#a39789958

You didn't copy the contents of THAT question into THIS question. Instead, you chose to simply add the URL to point to that other question. That's the same way objects referencing objects works. The content of an object can easily point to a value or to another object.
php-references-vs-values.jpg
Avatar of rgb192

ASKER

gr8gonzo
php-references-vs values jpg , foo() (object, array, properties)

and ray
 It's not different objects, but different pointers.

and slick
adding employee to department



Here is my question:

so there is a reference to the value

and if the value changes then so does the reference


I thought if the value was an array then new elements could be added to the array and the value would contain more data

but without a reference then no one knows the value is growing in size
(if a tree falls in a forest and no one is there to hear, does tree still make a sound?)
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of rgb192

ASKER

thanks all

A reference either exists or it doesn't. When you unset() a variable, you are destroying a reference.

A value will only exist in memory as long as there is at least one reference. When the last reference is destroyed, then the value is destroyed, too.
When the last reference is destroyed, then the value is destroyed, too.
That would seem to be correct, but I have not gone into the Zend internals to see if it's actually true.  It might be interesting to test that theory with memory_get_usage() and see if the "destroyed" value amounts to returning the memory or simply making the value unavailable to the PHP script.  This smells like something that could give us a memory leak ;-)
Memory leak occurs but it is very modest.

<?php // demo/memory_get_usage.php

/**
 * Question: After an object is created and some pointers to it are created (and
 * maybe some other stuff) and then the pointers are all unset(), does PHP actually
 * remove the object and return the memory?  Or can this create a memory leak?
 *
 * http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/Q_28342292.html#a39873247
 * http://www.php.net/manual/en/function.memory-get-usage.php
 */
error_reporting(E_ALL);

Class FatObject
{
    public function __construct() { }
    public function bloat($n = 0)
    {
        while ($n > 0)
        {
            $this->array[] = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
            $n--;
        }
    }
}

$old = memory_get_usage();
$x   = new FatObject;
$x->bloat(5000);
$new = memory_get_usage();
$y   = clone($x);
$y->bloat(1);
$cln = memory_get_usage();
unset($x);
$aft = memory_get_usage();
unset($y);
$fin = memory_get_usage();

echo PHP_EOL;
echo "<br>BEFORE BLOAT: $old, AFTER BLOAT: $new, AFTER CLONE: $cln";
echo "<br>AFTER FIRST UNSET(): $aft, AFTER SECOND UNSET(): $fin";

Open in new window

Ray, a couple of notes:

1. You have variables containing the memory usage amounts, so those will also account towards overall memory consumption.

2. PHP allocates more memory than it needs to store values, so it grabs memory space in chunks. When it releases values, it may not completely release all of the memory allocated, but it doesn't mean there's a leak. It just means it will try to reuse that same block if it can. So memory_get_usage() without a parameter will show you the "estimate" of memory usage.

memory_get_usage(true) will show you the physical allocation of memory usage, but it's also misleading sometimes. For example, if you create, bloat, and unset $x, you'll see that memory_get_usage(true) will show you a 100% physical return on memory, but likely between 8 - 64 bytes of "leak" on memory_get_usage(). If you clone $x to $y and then unset them both, the "true" version will show that it didn't release $y, but the no-parameter version will show the released memory. So it's one of those things where you just have to juggle two different memory analysis calls to get an accurate picture of what's happening to memory.
That makes sense.  

There's a little bit more going on under the covers, too.  Try the script as written, then remove line 31 and run it again.  It almost looks like the clone is "put in the queue" but not actually accomplished until there's a mutating reference to the cloned object.  In a way that makes sense, too.