ArrayAccess Interface and Multi-dimensional Arrays

  • joebert
  • Fart Bubbles
  • Genius
  • User avatar
  • Posts: 13503
  • Loc: Florida

Post 3+ Months Ago

I'm having a little trouble implementing the PHP ArrayAccess interface when multi-dimensional arrays are involved.

I can get appends to work

Code: [ Select ]
$myobject[] = 'new value';


I can get top-level asignments to work

Code: [ Select ]
$myobject['index'] = 'New Value';


I can access nested values if they're set like so

Code: [ Select ]
$myobject['index'] = array('zero');
echo $myobject['index'][0];
  1. $myobject['index'] = array('zero');
  2. echo $myobject['index'][0];


However if I try assignment to a nested index, it appears to get completely ignored whether the top-level index exists or not.

Code: [ Select ]
$myobject['index']['one'] = 1;


I'm starting to wonder whether the ArrayAccess interface is meant to be implemented directly since while I've been looking I've discovered classes like ArrayObject implement the ArrayAccess interface.

Here's the class with my debugging left in, can anyone see anything wrong with the way I've attempted to implement this ?

Code: [ Select ]
function echo_r($data)
{
    echo sprintf('<pre>%s</pre>', print_r($data, true));
}
 
abstract class identifiable
{
    public $id, $name;
}
 
final class language extends identifiable implements ArrayAccess
{
    public $texts = array();
   
    public function load($ini_file)
    {
        //$this->texts = array_merge_recursive($this->texts, parse_ini_file($ini_file));
        return $this;
    }
 
    public function __construct($id, $name = 'New Language', $ini_file = null)
    {
        return $ini_file ? $this->load($ini_file) : $this;
    }
   
    public function offsetExists($offset)
    {echo "[e:$offset]";
        return isset($this->texts[$offset]);
    }
   
    public function offsetGet($offset)
    {echo "[g:$offset]";
        return $this->texts[$offset];
    }
   
    public function offsetSet($offset, $value)
    {echo "[s:$offset--$value]";
        if(empty($offset))
        {
            $this->texts[] = $value;
            return;
        }
        $this->texts[$offset] = $value;
    }
   
    public function offsetUnset($offset)
    {echo "[u:$offset]";
        unset($this->texts[$offset]);
    }
}
 
 
$lang = new language('test', 'Test Language', 'languages/test.ini');
$lang[] = 'appended';
//$lang['stuff'] = array();
$lang['stuff'][] = 'multi-stuff';
echo_r($lang);
 
///////////////////////////////////////////
[s:--appended][g:stuff]
language Object
(
    [texts] => Array
        (
            [0] => appended
        )
 
    [id] =>
    [name] =>
)
  1. function echo_r($data)
  2. {
  3.     echo sprintf('<pre>%s</pre>', print_r($data, true));
  4. }
  5.  
  6. abstract class identifiable
  7. {
  8.     public $id, $name;
  9. }
  10.  
  11. final class language extends identifiable implements ArrayAccess
  12. {
  13.     public $texts = array();
  14.    
  15.     public function load($ini_file)
  16.     {
  17.         //$this->texts = array_merge_recursive($this->texts, parse_ini_file($ini_file));
  18.         return $this;
  19.     }
  20.  
  21.     public function __construct($id, $name = 'New Language', $ini_file = null)
  22.     {
  23.         return $ini_file ? $this->load($ini_file) : $this;
  24.     }
  25.    
  26.     public function offsetExists($offset)
  27.     {echo "[e:$offset]";
  28.         return isset($this->texts[$offset]);
  29.     }
  30.    
  31.     public function offsetGet($offset)
  32.     {echo "[g:$offset]";
  33.         return $this->texts[$offset];
  34.     }
  35.    
  36.     public function offsetSet($offset, $value)
  37.     {echo "[s:$offset--$value]";
  38.         if(empty($offset))
  39.         {
  40.             $this->texts[] = $value;
  41.             return;
  42.         }
  43.         $this->texts[$offset] = $value;
  44.     }
  45.    
  46.     public function offsetUnset($offset)
  47.     {echo "[u:$offset]";
  48.         unset($this->texts[$offset]);
  49.     }
  50. }
  51.  
  52.  
  53. $lang = new language('test', 'Test Language', 'languages/test.ini');
  54. $lang[] = 'appended';
  55. //$lang['stuff'] = array();
  56. $lang['stuff'][] = 'multi-stuff';
  57. echo_r($lang);
  58.  
  59. ///////////////////////////////////////////
  60. [s:--appended][g:stuff]
  61. language Object
  62. (
  63.     [texts] => Array
  64.         (
  65.             [0] => appended
  66.         )
  67.  
  68.     [id] =>
  69.     [name] =>
  70. )
  • Anonymous
  • Bot
  • No Avatar
  • Posts: ?
  • Loc: Ozzuland
  • Status: Online

Post 3+ Months Ago

  • spork
  • Brewmaster
  • Silver Member
  • User avatar
  • Posts: 6252
  • Loc: Seattle, WA

Post 3+ Months Ago

PHP.net wrote:
ArrayObject/ArrayIterator simply implement the ArrayAccess interface which doesn't handle multi dimensional array syntax. As you pointed out already you could have ArrayObject store ArrayObjects itself. Thus the solution is to overwrite ArrayObject.

http://bugs.php.net/bug.php?id=34816

You might be out of luck on this one :|
  • joebert
  • Fart Bubbles
  • Genius
  • User avatar
  • Posts: 13503
  • Loc: Florida

Post 3+ Months Ago

Bummer.
  • Bogey
  • Genius
  • Genius
  • Bogey
  • Posts: 8398
  • Loc: USA

Post 3+ Months Ago

Is this of any help?
  • joebert
  • Fart Bubbles
  • Genius
  • User avatar
  • Posts: 13503
  • Loc: Florida

Post 3+ Months Ago

I'm going to look into the ArrayObject of ArrayObjects. Though I need it to extend identifiable so "instanceof identifiable" works.
  • joebert
  • Fart Bubbles
  • Genius
  • User avatar
  • Posts: 13503
  • Loc: Florida

Post 3+ Months Ago

Decided to abandon the original thought & go with the following code instead since I needed an flexible ini parser anyways.

Code: [ Select ]
abstract class identifiable
{
    public $id, $name;
}

final class language extends identifiable
{
    public $texts = array();
    
    public function load($ini_file)
    {
        $newlines = file($ini_file);
        if($newlines !== false)
        {
            $current_category = '$this->';
            foreach($newlines as &$line)
            {
                if((bool)preg_match('#^([a-z][a-z\d_.]*)\s*=(.+)$#i', $line, $entry))
                {
                    $entry[2] = trim($entry[2], '\'"');
                    eval(sprintf('%s%s="%s";',
                        $current_category,
                        str_replace('.', '->', strtolower($entry[1])),
                        preg_replace('#\{2,}"#', '\"', str_replace('"', '\"', $entry[2]))
                    ));
                }
                else if((bool)preg_match('#^\s*\[([a-z][a-z\d_.]*)\]\s*$#i', $line, $category))
                {
                    $current_category = sprintf('$this->%s->', str_replace('.', '->', strtolower($category[1])));
                }
                unset($line);
            }
        }
        return $this;
    }

    public function __construct($id, $name = 'New Language', $ini_file = null)
    {
        $this->id = $id;
        $this->name = $name;
        return $ini_file ? $this->load($ini_file) : $this;
    }

    public function __set($key, $val)
    {
        $this->texts[strtolower($key)] = $val;
    }
    
    public function &__get($key)
    {
        return $this->texts[strtolower($key)];
    }
}
  1. abstract class identifiable
  2. {
  3.     public $id, $name;
  4. }
  5. final class language extends identifiable
  6. {
  7.     public $texts = array();
  8.     
  9.     public function load($ini_file)
  10.     {
  11.         $newlines = file($ini_file);
  12.         if($newlines !== false)
  13.         {
  14.             $current_category = '$this->';
  15.             foreach($newlines as &$line)
  16.             {
  17.                 if((bool)preg_match('#^([a-z][a-z\d_.]*)\s*=(.+)$#i', $line, $entry))
  18.                 {
  19.                     $entry[2] = trim($entry[2], '\'"');
  20.                     eval(sprintf('%s%s="%s";',
  21.                         $current_category,
  22.                         str_replace('.', '->', strtolower($entry[1])),
  23.                         preg_replace('#\{2,}"#', '\"', str_replace('"', '\"', $entry[2]))
  24.                     ));
  25.                 }
  26.                 else if((bool)preg_match('#^\s*\[([a-z][a-z\d_.]*)\]\s*$#i', $line, $category))
  27.                 {
  28.                     $current_category = sprintf('$this->%s->', str_replace('.', '->', strtolower($category[1])));
  29.                 }
  30.                 unset($line);
  31.             }
  32.         }
  33.         return $this;
  34.     }
  35.     public function __construct($id, $name = 'New Language', $ini_file = null)
  36.     {
  37.         $this->id = $id;
  38.         $this->name = $name;
  39.         return $ini_file ? $this->load($ini_file) : $this;
  40.     }
  41.     public function __set($key, $val)
  42.     {
  43.         $this->texts[strtolower($key)] = $val;
  44.     }
  45.     
  46.     public function &__get($key)
  47.     {
  48.         return $this->texts[strtolower($key)];
  49.     }
  50. }
  • zniko07
  • Born
  • Born
  • zniko07
  • Posts: 1

Post 3+ Months Ago

hi!
i am not sure of this one but if you implement the offsetGet
like this
function offsetGet($offset){
echo 'request offset : ' . $offset . '<br />';
return isset($this->vars[$offset]) ? $this->vars[$offset] : new self;
}

you may get what you wanted.

Post Information

  • Total Posts in this topic: 7 posts
  • Users browsing this forum: No registered users and 124 guests
  • You cannot post new topics in this forum
  • You cannot reply to topics in this forum
  • You cannot edit your posts in this forum
  • You cannot delete your posts in this forum
  • You cannot post attachments in this forum
 
 

© 1998-2014. Ozzu® is a registered trademark of Unmelted, LLC.