Completed
Push — master ( 2f10c4...363bc6 )
by Peter
06:39
created

CompositionIterator::iterate()   D

Complexity

Conditions 9
Paths 11

Size

Total Lines 33
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 9

Importance

Changes 0
Metric Value
dl 0
loc 33
ccs 16
cts 16
cp 1
rs 4.909
c 0
b 0
f 0
cc 9
eloc 15
nc 11
nop 1
crap 9
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: peter
5
 * Date: 07.03.18
6
 * Time: 17:46
7
 */
8
9
namespace Maslosoft\Mangan\Helpers;
10
11
12
use Countable;
13
use Iterator;
14
use Maslosoft\Addendum\Interfaces\AnnotatedInterface;
15
use Maslosoft\Addendum\Utilities\ClassChecker;
16
use Maslosoft\Mangan\Mangan;
17
use Maslosoft\Mangan\Meta\ManganMeta;
18
19
/**
20
 * Iterate over composition of documents.
21
 *
22
 * NOTE: This will include only AnnotatedInterface instances.
23
 *
24
 * @package Maslosoft\Mangan\Helpers
25
 */
26
class CompositionIterator implements Iterator, Countable
27
{
28
	private $model = null;
29
30
	private $direct = false;
31
32
	private $types = [];
33
34
	/**
35
	 * Models holder
36
	 * @var null|AnnotatedInterface[]
37
	 */
38
	private $models = null;
39
40
	private $pointer = 0;
41
42 16
	public function __construct(AnnotatedInterface $model)
43
	{
44 16
		$this->model = $model;
45 16
	}
46
47
	/**
48
	 * Limit results to only direct descendants.
49
	 * @return $this
50
	 */
51 3
	public function direct()
52
	{
53 3
		$this->direct = true;
54 3
		return $this;
55
	}
56
57
	/**
58
	 * Limit results to only to the type provided.
59
	 *
60
	 * The `$type` should be class or interface name
61
	 * or object instance.
62
	 *
63
	 * Repeated calls will add types uniquely.
64
	 *
65
	 * @param $type string|object
66
	 * @param $include boolean Whether to include this type or skip
67
	 * @return $this
68
	 */
69 14
	public function ofType($type, $include = true)
70
	{
71 14
		if (is_object($type))
72
		{
73
			$type = get_class($type);
74
		}
75 14
		assert(is_string($type));
76 14
		assert(ClassChecker::exists($type));
77 14
		$this->types[$type] = $include;
78 14
		return $this;
79
	}
80
81 16
	private function init()
82
	{
83 16
		if (null === $this->models)
84
		{
85 16
			$this->models = [];
86 16
			$this->iterate($this->model);
87
		}
88 14
	}
89
90 16
	private function iterate($model)
91
	{
92 16
		foreach (ManganMeta::create($model)->fields() as $name => $meta)
93
		{
94 16
			if (is_array($model->$name))
95
			{
96 9
				foreach ($model->$name as $child)
97
				{
98 9
					if ($this->doInclude($child))
99
					{
100 6
						$this->models[] = $child;
101
					}
102 9
					if($this->recurse())
103
					{
104 9
						$this->iterate($child);
105
					}
106
				}
107 7
				continue;
108
			}
109 16
			if ($this->skip($model->$name))
110
			{
111 16
				continue;
112
			}
113 4
			if ($this->doInclude($model->$name))
114
			{
115 4
				$this->models[] = $model->$name;
116
			}
117 4
			if($this->recurse())
118
			{
119 4
				$this->iterate($model->$name);
120
			}
121
		}
122 14
	}
123
124 16
	private function skip($model)
125
	{
126
		// Non-object
127 16
		if (!is_object($model))
128
		{
129 16
			return true;
130
		}
131
		// Skip if not annotated
132 8
		if (!$model instanceof AnnotatedInterface)
133
		{
134 4
			return true;
135
		}
136 4
		return false;
137
	}
138
139
	/**
140
	 * Whether to include `$model` in result
141
	 * @param $model
142
	 * @return bool
143
	 */
144 9
	private function doInclude($model)
145
	{
146
		// Don't skip if no types
147 9
		if (empty($this->types))
148
		{
149 4
			return true;
150
		}
151
152
		// Include if is_a type
153 7
		foreach ($this->types as $type => $include)
154
		{
155 7
			if (is_a($model, $type))
156
			{
157 7
				return $include;
158
			}
159
		}
160 4
		return false;
161
	}
162
163 9
	private function recurse()
164
	{
165 9
		return !$this->direct;
166
	}
167
168 6
	public function current()
169
	{
170 6
		$this->init();
171 6
		return $this->models[$this->pointer];
172
	}
173
174 6
	public function next()
175
	{
176 6
		$this->init();
177 6
		++$this->pointer;
178 6
	}
179
180
	public function key()
181
	{
182
		$this->init();
183
		return $this->pointer;
184
	}
185
186 14
	public function valid()
187
	{
188 14
		$this->init();
189 14
		return isset($this->models[$this->pointer]);
190
	}
191
192 16
	public function rewind()
193
	{
194 16
		$this->init();
195 14
		$this->pointer = 0;
196 14
	}
197
198 5
	public function count()
199
	{
200 5
		$this->init();
201 5
		return count($this->models);
202
	}
203
204
205
}