1<?php
2/*
3 * This file is part of Object Enumerator.
4 *
5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10
11namespace SebastianBergmann\ObjectEnumerator;
12
13use SebastianBergmann\RecursionContext\Context;
14
15/**
16 * Traverses array structures and object graphs
17 * to enumerate all referenced objects.
18 */
19class Enumerator
20{
21    /**
22     * Returns an array of all objects referenced either
23     * directly or indirectly by a variable.
24     *
25     * @param array|object $variable
26     *
27     * @return object[]
28     */
29    public function enumerate($variable)
30    {
31        if (!is_array($variable) && !is_object($variable)) {
32            throw new InvalidArgumentException;
33        }
34
35        if (isset(func_get_args()[1])) {
36            if (!func_get_args()[1] instanceof Context) {
37                throw new InvalidArgumentException;
38            }
39
40            $processed = func_get_args()[1];
41        } else {
42            $processed = new Context;
43        }
44
45        $objects = [];
46
47        if ($processed->contains($variable)) {
48            return $objects;
49        }
50
51        $array = $variable;
52        $processed->add($variable);
53
54        if (is_array($variable)) {
55            foreach ($array as $element) {
56                if (!is_array($element) && !is_object($element)) {
57                    continue;
58                }
59
60                $objects = array_merge(
61                    $objects,
62                    $this->enumerate($element, $processed)
63                );
64            }
65        } else {
66            $objects[] = $variable;
67            $reflector = new \ReflectionObject($variable);
68
69            foreach ($reflector->getProperties() as $attribute) {
70                $attribute->setAccessible(true);
71
72                try {
73                    $value = $attribute->getValue($variable);
74                } catch (\Throwable $e) {
75                    continue;
76                } catch (\Exception $e) {
77                    continue;
78                }
79
80                if (!is_array($value) && !is_object($value)) {
81                    continue;
82                }
83
84                $objects = array_merge(
85                    $objects,
86                    $this->enumerate($value, $processed)
87                );
88            }
89        }
90
91        return $objects;
92    }
93}
94