简体   繁体   中英

How to handle nested (recursive iterator) dynamics objects in PHP?

I have a foreach that instances several kind of classes and this must be nested. They all extends of the same Abstract class.

I don't need to handle any specific case but all the cases.

How should I handle this nesting? I was thinking about defining a function in the class that does the foreach, or maybe in the Parent class, that have all this cases defined inside.

Example 1

<?php
class Parent
{
    private $childs = [];
    function nest($child)
    {
        $this->childs[] = $child;
    }
}

// There is different types of A that are nested according to its type.
$A1 = class A extends Parent { $type = 1 }
$A2 = class A extends Parent { $type = 2 }
$B = class B extends Parent {}
$A3 = class A extends Parent { $type = 2 }
$C = class C extends Parent {}

// foreach ([$A1, $A2, $B, $A3, $C])
// $A1->nest($A2);

But $A1 can not be nested, $A2 can only be nested by $A1 , $B can be nested by $A1 and $A2 but should be nested by $A2 because it's the previous object that can nest it, $A3 should be nested to $A1 , etc.

// expected output of the example
object(A)#1 (2) {
  ["type"]=>
  int(1)
  ["childs":"Parent":private]=>
  array(2) {
    [0]=>
    object(A)#2 (2) {
      ["type"]=>
      int(2)
      ["childs":"Parent":private]=>
      array(1) {
        [0]=>
        object(B)#3 (2) {
          ["childs":"Parent":private]=>
          array(0) {
          }
        }
      }
    }
    [1]=>
    object(A)#4 (2) {
      ["type"]=>
      int(2)
      ["childs":"Parent":private]=>
      array(1) {
        [0]=>
        object(C)#5 (1) {
          ["childs":"Parent":private]=>
          array(0) {
          }
        }
      }
    }
  }
}

Example 2

<?php
$numbers = [1, 2, 3, 2, 4];

class Number
{
    private $biggers = [];
    public function addBigger($number)
    {
        $this->biggers[] = $number;
    }
}

class One extends Number{ private $value = 1; }
class Two extends Number{ private $value = 2; }
class Three extends Number{ private $value = 3; }
class Four extends Number{ private $value = 4; }

foreach ($numbers as $number) {
    // Algorithm
}

Expected output:

object(One)#1 (2) {
["value":"One":private]=>
  int(1)
  ["biggers":"Number":private]=>
  array(2) {
    [0]=>
    object(Two)#2 (2) {
    ["value":"Two":private]=>
      int(2)
      ["biggers":"Number":private]=>
      array(1) {
        [0]=>
        object(Three)#3 (2) {
        ["value":"Three":private]=>
          int(3)
          ["biggers":"Number":private]=>
          array(0) {
          }
        }
      }
    }
    [1]=>
    object(Two)#4 (2) {
    ["value":"Two":private]=>
      int(2)
      ["biggers":"Number":private]=>
      array(1) {
    [0]=>
        object(Four)#5 (2) {
        ["value":"Four":private]=>
          int(4)
          ["biggers":"Number":private]=>
          array(0) {
          }
        }
      }
    }
  }
}

What you need is a RecursiveIterator .

You have to implement your own RecursiveIterator :

class RecursiveContainerIterator extends IteratorIterator implements RecursiveIterator {
    public function getChildren() {
        if (is_array($this->current())) {
            return new RecursiveArrayIterator($this->current());
        } else if ($this->current() instanceof IteratorAggregate) {
            return $this->current()->getIterator();
        } else {
            throw new InvalidArgumentException('');
        }
    }

    public function hasChildren() {
        return is_array($this->current()) || $this->current() instanceof Traversable;
    }
}

class Container implements IteratorAggregate {
    private $children;
    public function __construct() {
        $this->children = $children = new ArrayObject();
    }

    public function addChild($child) {
        $this->children->append($child);
    }

    public function getChildren() {
        return $this->children;
    }

    public function getIterator() {
        return new RecursiveContainerIterator($this->children);
    }
}

$parent = new Container();
$child1 = new Container();
$child2 = new Container();
$grandChild1 = new Container();
$grandChild2 = new Container();
$grandChild3 = new Container();
$grandChild4 = new Container();

$parent->addChild($child1);
$parent->addChild($child2);
$parent->addChild(array(5, 6));

$child1->addChild($grandChild1);
$child1->addChild($grandChild2);

$child2->addChild($grandChild3);
$child2->addChild($grandChild4);

$grandChild1->addChild(1);
$grandChild2->addChild(2);
$grandChild3->addChild(3);
$grandChild3->addChild(4);

foreach (new RecursiveIteratorIterator($parent, RecursiveIteratorIterator::LEAVES_ONLY) as $child) {
    var_dump($child);
}

Will output:

 int(1) int(2) int(3) int(4) int(5) int(6) 

If you need to handle the object itself and not only the leaves, you can use the RecursiveIteratorIterator::SELF_FIRST flag.

You mean something like that:

$objectArray[] = $A1;
$objectArray[] = $A2;
$objectArray[] = $B;
$objectArray[] = $A3;
$objectArray[] = $C;

foreach($objectArray as $obj){
   if(isset($obj->type)){
   //type is defined
  }else{

  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM