Issues (1)

src/Printo.php (1 issue)

Severity
1
<?php
2
/**
3
 * This file is part of the koriym/printo package.
4
 *
5
 * @license http://opensource.org/licenses/MIT MIT
6
 */
7
namespace Koriym\Printo;
8
9
class Printo
10
{
11
    /**
12
     * Extract object properties
13
     */
14
    const RANGE_PROPERTY = 1;
15
16
    /**
17
     * Extract array
18
     */
19
    const RANGE_ARRAY = 2;
20
21
    /**
22
     * Extract object in array
23
     */
24
    const RANGE_OBJECT_IN_ARRAY = 4;
25
26
    /**
27
     * Only object graph
28
     */
29
    const RANGE_OBJECT_ONLY = 0;
30
31
    /**
32
     * Extract all
33
     */
34
    const RANGE_ALL = 7;
35
    /**
36
     * @var \SplObjectStorage
37
     */
38
    private $objectIdStorage;
39
40
    /**
41
     * @var \SplObjectStorage
42
     */
43
    private $sourceObjectStorage;
44
45
    /**
46
     * @var array
47
     */
48
    private $graph = [
49
        'nodes' => [],
50
        'links' => []
51
    ];
52
53
    /**
54
     * @var int
55
     */
56
    private $nodeIndex = -1;
57
58
    /**
59
     * @var NodeFactoryInterface
60
     */
61
    private $nodeFactory;
62
63
    /**
64
     * @var int
65
     */
66
    private $linkDistance = 100;
67
68
    /**
69
     * @var int
70
     */
71
    private $charge = -300;
72
73
    /**
74
     * Default range is RANGE_PROPERTY | RANGE_ARRAY
75
     *
76
     * @var int
77
     */
78
    private $range = 3;
79
80
    /**
81
     * @var object
82
     */
83
    private $object;
84
85
    /**
86
     * @param object               $object
87
     * @param NodeFactoryInterface $nodeFactory
88
     */
89
    public function __construct($object, ?NodeFactoryInterface $nodeFactory = null)
90
    {
91
        $this->object = $object;
92
        $this->objectIdStorage = new \SplObjectStorage;
93
        $this->sourceObjectStorage = new \SplObjectStorage;
94
        $this->nodeFactory = $nodeFactory ?: new NodeFactory;
95
        if (is_array($this->object)) {
0 ignored issues
show
The condition is_array($this->object) is always false.
Loading history...
96
            $this->linkDistance = 200;
97
        }
98
    }
99
100
    public function __toString()
101
    {
102
        if (is_object($this->object)) {
103
            $this->addObject($this->object);
104
        }
105
        if (is_array($this->object)) {
106
            $this->addArray(0, $this->object);
107
        }
108
109
        $list = json_encode($this->graph);
110
        $linkDistance = $this->linkDistance;
111
        $charge = $this->charge;
112
        $html = require __DIR__ . '/html/default.php';
113
114
        return $html;
115
    }
116
117
    /**
118
     * @param $range
119
     *
120
     * @return $this
121
     */
122
    public function setRange($range)
123
    {
124
        $this->range = $range;
125
126
        return $this;
127
    }
128
129
    /**
130
     * @param $linkDistance
131
     *
132
     * @return $this
133
     */
134
    public function setLinkDistance($linkDistance)
135
    {
136
        $this->linkDistance = $linkDistance;
137
138
        return $this;
139
    }
140
141
    /**
142
     * @param $charge
143
     *
144
     * @return $this
145
     */
146
    public function setCharge($charge)
147
    {
148
        $this->charge = $charge;
149
150
        return $this;
151
    }
152
153
    /**
154
     * @param $object
155
     */
156
    private function addObject($object)
157
    {
158
        $this->sourceObjectStorage->attach($object);
159
        $ref = new \ReflectionObject($object);
160
        $meta = ['file' => $ref->getFileName()];
161
        $sourceIndex = $this->getObjectId($object, $meta);
162
        $props = $ref->getProperties();
163
        foreach ($props as $prop) {
164
            $this->prop($prop, $object, $sourceIndex);
165
        }
166
    }
167
168
    /**
169
     * @param \ReflectionProperty $prop
170
     * @param object              $object
171
     * @param int                 $sourceIndex
172
     */
173
    private function prop(\ReflectionProperty $prop, $object, $sourceIndex)
174
    {
175
        $prop->setAccessible(true);
176
        $value = $prop->getValue($object);
177
        $nonObjectProperty = (! is_object($value) && (! ($this->range & self::RANGE_PROPERTY)));
178
        if ($nonObjectProperty) {
179
            return;
180
        }
181
        /* @var $prop \ReflectionProperty */
182
        $meta = ['prop' => $prop->getName(), 'modifier' => $prop->getModifiers()];
183
        $targetIndex = $this->addGraphLink($sourceIndex, $value, $meta);
184
        if (is_object($value) && ! $this->sourceObjectStorage->contains($value)) {
185
            $this->addObject($value);
186
        }
187
        if (is_array($value)) {
188
            $this->addArray($targetIndex, $value);
189
        }
190
    }
191
192
    /**
193
     * @param int   $sourceIndex
194
     * @param array $array
195
     */
196
    private function addArray($sourceIndex, array $array)
197
    {
198
        if (! ($this->range & self::RANGE_ARRAY)) {
199
            return;
200
        }
201
        foreach ($array as $key => $value) {
202
            if (is_object($value) && ($this->range & self::RANGE_OBJECT_IN_ARRAY)) {
203
                $this->addObject($value);
204
                continue;
205
            }
206
207
            $targetIndex = $this->addGraphLink($sourceIndex, $value, ['key' => $key]);
208
            if (is_array($value) && $value !== $GLOBALS) {
209
                $this->addArray($targetIndex, $value);
210
            }
211
        }
212
    }
213
214
    /**
215
     * @param int   $sourceIndex
216
     * @param mixed $value
217
     * @param array $meta
218
     *
219
     * @return int
220
     */
221
    private function addGraphLink($sourceIndex, $value, array $meta)
222
    {
223
        $targetIndex = $this->getTargetIndex($value, $meta);
224
        $type = $this->getType($value);
225
        $this->graph['links'][] = ['source' => $sourceIndex, 'target' => $targetIndex, 'type' => $type];
226
227
        return $targetIndex;
228
    }
229
230
    /**
231
     * @param $value
232
     *
233
     * @return string
234
     */
235
    private function getType($value)
236
    {
237
        if (is_object($value)) {
238
            return 'object';
239
        }
240
        if (is_array($value)) {
241
            return 'array';
242
        }
243
244
        return 'scalar';
245
    }
246
247
    /**
248
     * @param mixed $value
249
     * @param array $meta
250
     *
251
     * @return int
252
     */
253
    private function getTargetIndex($value, array $meta)
254
    {
255
        if (is_object($value)) {
256
            return $this->getObjectId($value, $meta);
257
        }
258
        $node = $this->nodeFactory->newInstance($value, $meta);
259
        $this->addNode($node);
260
261
        return $this->nodeIndex;
262
    }
263
264
    /**
265
     * @param $object
266
     *
267
     * @return int
268
     */
269
    private function getObjectId($object, array $meta)
270
    {
271
        if ($this->objectIdStorage->contains($object)) {
272
            return (int) $this->objectIdStorage[$object];
273
        }
274
275
        $node = $this->nodeFactory->newInstance($object, $meta);
276
        $index = $this->addNode($node);
277
        $this->objectIdStorage->attach($object, (string) $index);
278
279
        return $index;
280
    }
281
282
    /**
283
     * @param NodeInterface $node
284
     *
285
     * @return int
286
     */
287
    private function addNode(NodeInterface $node)
288
    {
289
        $this->nodeIndex++;
290
        $this->graph['nodes'][] = $node->toArray();
291
292
        return $this->nodeIndex;
293
    }
294
}
295