php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #51284 foreach distroys objects in array
Submitted: 2010-03-12 13:29 UTC Modified: 2010-05-20 10:40 UTC
Votes:1
Avg. Score:5.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:1 (100.0%)
Same OS:1 (100.0%)
From: gerhard at tinned-software dot net Assigned:
Status: Not a bug Package: Class/Object related
PHP Version: Irrelevant OS: MacOSX, Linux, ...
Private report: No CVE-ID: None
 [2010-03-12 13:29 UTC] gerhard at tinned-software dot net
Description:
------------
I have a script which tries to create configured objects of classes. The 
method creating the object of the named class will generate a unique 
identifier to be able to identify the invocation later in the class. This 
unique_id is basically calculated by adding the class name and all 
constructor parameters into a ":"-seperetaed list. For doing this i used 

This all functions well as long as there is not the following very special 
combination of parameters and class-parameterlist. If one of the parameters 
is an object, and this object is passed to the class as reference (&$object).

In such a case, the foreach loop destroys somehow the $parameter array. As 
the $parameter array is not altered in the loop, there is no explanation to 
my way the loop influence the array. 

To prove that the loop is causing this problem i changed the loop from 
foreach() to for(). As you can see in the test script there are 2 methods in 
the OM class. The get_object_OK() uses the for()-loop to generate the ID and
the get_object_NOK() method uses the foreach()-loop to generate it.

With the for()-loop the script runs without any troubles. But with the 
foreach()-loop the script returns an error while invocating the object. This 
error causes the object to be not created.

The error message which can be found in the webserver's / php's logfile is 
the following:
[error] [client 127.0.0.1] PHP Warning:  Invocation of test_obj2's constructor failed in /.../test.php on line 183, referer: http://127.0.0.1:8080/
... The line references to "$obj = $reflection_obj->newInstanceArgs($parameter);" at the get_object_NOK.() function.


Test script:
---------------
class test_obj
{
    var $test = "abc";
    
    function __construct($str)
    {
        $this->test = $str;
    }
    
    function get()
    { 
        echo get_class($this)." - string=".$this->test."<br />\n";
    }
    
    function set($str)
    {
        $test = $str;
    }
}

class test_obj2
{
    var $test = "abc";
    
    function __construct($string1, &$object)
    {
        echo get_class($this)." - string1=$string1<br />\n";
        $object->get();
    }
}





//include_once(dirname(__FILE__).'/object_manager.class.php');
//include_once(dirname(__FILE__).'/element_container.class.php');


//
// Test execution
//
echo "PHP Version: ".phpversion()."<br /><br />\n";
$om = new OM();

$t1 = new test_obj("init-text-object-1");
$t1->get();
echo "Object of class 'Test_CLass' ... Finished.<br />\n";


$t2 = $om->get_object_OK("test_obj2", "object-text-2", $t1);
echo "Object of class 'Test_CLass2' with for() loop ... Finished.<br />\n";

$t2 = $om->get_object_NOK("test_obj2", "object-text-2", $t1);
echo "Object of class 'Test_CLass2' with foreach() loop ... Finished.<br />\n";




//
// A short testing code from a much biger function / Class to 
// demonstrate the behaviour
//
class OM
{
    function get_object_OK()
    {
        $parameter = func_get_args();
        $type = array_shift($parameter);
        
        //
        // create unique identifier for this object creation.
        // Used in the original class to identify the object later in the class.
        //
        $unique_id = "$type:";
        // loop through all parameters
        for($i=0; $i < count($parameter); $i++)
        {
            // if parameter is an object, get only the object name
            if(is_object($parameter[$i]) === TRUE)
            {
                $unique_id .= get_class($parameter[$i]).':';
                continue;
            }
            // get the variable content as string to the identifier-string
            $unique_id .= gettype($parameter[$i]).':';
        }
        
        //
        // Creating the object by name
        //
        $reflection_obj = new ReflectionClass($type);      
        $obj = $reflection_obj->newInstanceArgs($parameter); 
        
        // return the created object
        return $obj;
    }
    
    function get_object_NOK()
    {
        $parameter = func_get_args();
        $type = array_shift($parameter);
        
        //
        // create unique identifier for this object creation.
        // Used in the original class to identify the object later in the class.
        //
        $unique_id = "$type:";
        // loop through all parameters
        foreach($parameter as $key => $param)
        {
            // if parameter is an object, get only the object name
            if(is_object($param) === TRUE)
            {
                $unique_id .= get_class($param).':';
                continue;
            }
            // get the variable content as string to the identifier-string
            $unique_id .= gettype($param).':';
        }
        
        //
        // Creating the object by name
        //
        $reflection_obj = new ReflectionClass($type);      
        $obj = $reflection_obj->newInstanceArgs($parameter); 
        
        // return the created object
        return $obj;
    }
}


Expected result:
----------------
The script should successfully create an object when for() or foreach()-loop 
is used.


Actual result:
--------------
The script works only with for()-loop. When the foreach look is used, the 
following error message is shown.
[error] [client 127.0.0.1] PHP Warning:  Invocation of test_obj2's constructor failed in /.../test.php on line 183, referer: http://127.0.0.1:8080/
... The line references to "$obj = $reflection_obj->newInstanceArgs($parameter);" at the get_object_NOK.() function.


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2010-05-19 15:23 UTC] mike@php.net
-Status: Open +Status: Bogus
 [2010-05-19 15:23 UTC] mike@php.net
I get a Warning: Parameter 2 to test_obj2::__construct() expected to be a reference, value given in /tmp/phpbugs/51284.php on line 128

You lose the ref with func_get_args().
 [2010-05-19 17:45 UTC] gerhard at tinned-software dot net
This comment is simply not true!!!

How can it be that the function call func_get_args() destroys the reference when one of this functions work and the other function does not work? Both functions use this functionb call. But only in the function get_object_NOK() the error message is shown. In the other function, the object is created as expected. 

So please explain me how the function call func_get_args() can cause this?
 [2010-05-20 08:17 UTC] mike@php.net
That's true, sorry. You lose the reference because of foreach; try:

foreach ($parameter as $key => &$param) ...
 [2010-05-20 10:20 UTC] gerhard at tinned-software dot net
So my assumption seems to be correct that it has to do with the foreach loop. 

I will follow your suggestion and try the loop with a & for the second parameter. 

But in general, ... Wouldn't it be an idea to maybe place a line in the documentation for that?
 [2010-05-20 10:40 UTC] gerhard at tinned-software dot net
Thanks, that solved it. Thanks for that.

Its now the second time i commit a bug to php and as the first time it was 
immediately closed as Bogus without having a closer look to it. I don't know 
how much bug reports you have to review every day, but i hope you will have at 
least a closer look next time i report a bug.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Mar 28 19:01:29 2024 UTC