Issues (34)

src/Graph/AbstractGraph.php (5 issues)

1
<?php
2
3
namespace Graphp\Graph;
4
5
use InvalidArgumentException;
6
use Graphp\GraphInterface;
7
use Graphp\Graph\Specifics\SpecificsInterface;
8
use Graphp\Graph\Specifics\UndirectedSpecifics;
9
use Graphp\Graph\Specifics\DirectedSpecifics;
10
use Graphp\Edge\EdgeInterface;
11
use Graphp\Edge\EdgeSet;
12
use Graphp\Edge\Specifics\EdgeSpecificsInterface;
13
use Graphp\Edge\Specifics\UniformEdgeSpecifics;
14
use Graphp\Edge\Specifics\WeightedEdgeSpecifics;
15
use Graphp\Vertex\VertexInterface;
16
use Graphp\Vertex\VertexSet;
17
use Graphp\Util\SupplierInterface;
18
19
/**
20
 * Class AbstractGraph
21
 *
22
 * @package Graphp\Graph
23
 */
24
class AbstractGraph implements GraphInterface
25
{
26
    /**
27
     * The vertex supplier
28
     *
29
     * @var SupplierInterface
30
     */
31
    private $vertexSupplier;
32
33
    /**
34
     * The edge supplier
35
     *
36
     * @var SupplierInterface
37
     */
38
    private $edgeSupplier;
39
40
    /**
41
     * The graph type
42
     *
43
     * @var GraphTypeInterface
44
     */
45
    private $type;
46
47
    /**
48
     * The graph specifics
49
     *
50
     * @var SpecificsInterface
51
     */
52
    private $specifics;
53
54
    /**
55
     * The edge specifics
56
     *
57
     * @var EdgeSpecificsInterface
58
     */
59
    private $edgeSpecifics;
60
61
    /**
62
     * Construct a new graph
63
     *
64
     * @param SupplierInterface $vertexSupplier - the vertex supplier
65
     * @param SupplierInterface $edgeSupplier - the edge supplier
66
     * @param GraphTypeInterface $type - the graph type
67
     */
68 41
    protected function __construct(
69
        ?SupplierInterface $vertexSupplier = null,
70
        ?SupplierInterface $edgeSupplier = null,
71
        ?GraphTypeInterface $type = null
72
    ) {
73 41
        $this->vertexSupplier = $vertexSupplier;
74 41
        $this->edgeSupplier = $edgeSupplier;
75 41
        $this->type = $type;
76 41
        $this->specifics = $this->createSpecifics($type->isDirected());
0 ignored issues
show
The method isDirected() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

76
        $this->specifics = $this->createSpecifics($type->/** @scrutinizer ignore-call */ isDirected());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
77 41
        $this->edgeSpecifics = $this->createEdgeSpecifics($type->isWeighted());
78 41
    }
79
80
    /**
81
     * Get the graph specifics
82
     *
83
     * @param bool $isDirected - is directed graph?
84
     *
85
     * @return SpecificsInterface
86
     */
87 41
    public function createSpecifics(bool $isDirected): SpecificsInterface
88
    {
89 41
        return $isDirected ? new DirectedSpecifics($this) : new UndirectedSpecifics($this);
90
    }
91
92
    /**
93
     * Get the edge specifics
94
     *
95
     * @param bool $isWeighted - is weighted?
96
     *
97
     * @return EdgeSpecificsInterface
98
     */
99 41
    public function createEdgeSpecifics(bool $isWeighted): EdgeSpecificsInterface
100
    {
101 41
        return $isWeighted ? new WeightedEdgeSpecifics($this) : new UniformEdgeSpecifics($this);
0 ignored issues
show
The call to Graphp\Edge\Specifics\We...pecifics::__construct() has too many arguments starting with $this. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

101
        return $isWeighted ? /** @scrutinizer ignore-call */ new WeightedEdgeSpecifics($this) : new UniformEdgeSpecifics($this);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
The call to Graphp\Edge\Specifics\Un...pecifics::__construct() has too many arguments starting with $this. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

101
        return $isWeighted ? new WeightedEdgeSpecifics($this) : /** @scrutinizer ignore-call */ new UniformEdgeSpecifics($this);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
102
    }
103
104
    /**
105
     * Get all edges connecting the source vertext to the target vertex
106
     *
107
     * @return EdgeSet
108
     */
109 1
    public function getAllEdges(VertexInterface $sourceVertex, VertexInterface $targetVertex): EdgeSet
110
    {
111 1
        return $this->specifics->getAllEdges($sourceVertex, $targetVertex);
112
    }
113
114
    /**
115
     * Get an edge connecting the source vertext to the target vertex
116
     *
117
     * @return null|EdgeInterface
118
     */
119 24
    public function getEdge(VertexInterface $sourceVertex, VertexInterface $targetVertex): ?EdgeInterface
120
    {
121 24
        return $this->specifics->getEdge($sourceVertex, $targetVertex);
122
    }
123
124
    /**
125
     * Get the vertex supplier that the graph uses whenever it needs to create new vertices
126
     *
127
     * @return null|SupplierInterface
128
     */
129 2
    public function getVertexSupplier(): ?SupplierInterface
130
    {
131 2
        return $this->vertexSupplier;
132
    }
133
134
    /**
135
     * Get the edge supplier that the graph uses whenever it needs to create new edges
136
     *
137
     * @return SupplierInterface
138
     */
139 10
    public function getEdgeSupplier(): SupplierInterface
140
    {
141 10
        return $this->edgeSupplier;
142
    }
143
144
    /**
145
     * Get the graph type
146
     *
147
     * @return GraphTypeInterface
148
     */
149 32
    public function getType(): GraphTypeInterface
150
    {
151 32
        return $this->type;
152
    }
153
154
    /**
155
     * Create a new edge in the graph. Return the newly created edge if added to the graph.
156
     *
157
     * @return EdgeInterface
158
     *
159
     * @throws InvalidArgumentException
160
     */
161 29
    public function addEdge(
162
        VertexInterface $sourceVertex,
163
        VertexInterface $targetVertex,
164
        ?EdgeInterface $edge = null
165
    ): ?EdgeInterface {
166 29
        $this->assertVertexExists($sourceVertex);
167 29
        $this->assertVertexExists($targetVertex);
168
        
169 29
        if (!$this->getType()->isAllowingMultipleEdges() && $this->containsEdge($sourceVertex, $targetVertex)) {
170 2
            return null;
171
        }
172
        
173 29
        if (!$this->getType()->isAllowingSelfLoops() && $sourceVertex->equals($targetVertex)) {
174
            throw new InvalidArgumentException("loops are not allowed");
175
        }
176
        
177 29
        $targetEdge = $edge ?? $this->edgeSupplier->get();
178
179 29
        if ($this->edgeSpecifics->add($targetEdge, $sourceVertex, $targetVertex)) {
180 29
            $this->specifics->addEdgeToTouchingVertices($targetEdge);
181 29
            return $targetEdge;
182
        }
183
                
184
        return null;
185
    }
186
187
    /**
188
     * Create and return a new vertex in the graph.
189
     *
190
     * @return null|VertexInterface
191
     */
192 36
    public function addVertex(VertexInterface $vertex): ?VertexInterface
193
    {
194 36
        if (!$this->containsVertex($vertex)) {
195 36
            $this->specifics->addVertex($vertex);
196 36
            return $vertex;
197
        }
198
        
199 5
        return null;
200
    }
201
202
    /**
203
     * Check if the graph contains the given edge, specified either by two vertices or by the edge itself
204
     *
205
     * @param VertexInterface $sourceVertex - the source vertex
206
     * @param VertexInterface $targetVertex - the target vertex
207
     * @param EdgeInterface $edge - the edge
208
     *
209
     * @return bool
210
     */
211 23
    public function containsEdge(
212
        ?VertexInterface $sourceVertex = null,
213
        ?VertexInterface $targetVertex = null,
214
        ?EdgeInterface $edge = null
215
    ): bool {
216 23
        if (!is_null($sourceVertex) && !is_null($targetVertex)) {
217 23
            return !is_null($this->getEdge($sourceVertex, $targetVertex));
218
        }
219 2
        return !is_null($edge) ? $this->edgeSpecifics->containsEdge($edge) : false;
220
    }
221
222
    /**
223
     * Check if the graph contains the given vertex
224
     *
225
     * @return bool
226
     */
227 36
    public function containsVertex(VertexInterface $vertex): bool
228
    {
229 36
        return $this->specifics->getVertexSet()->contains($vertex);
230
    }
231
232
    /**
233
     * Get a set of all edges touching the specified vertex
234
     *
235
     * @param VertexInterface - the vertex
0 ignored issues
show
Documentation Bug introduced by
The doc comment - at position 0 could not be parsed: Unknown type name '-' at position 0 in -.
Loading history...
236
     *
237
     * @return EdgeSet
238
     */
239 7
    public function edgesOf(VertexInterface $vertex): EdgeSet
240
    {
241 7
        $this->assertVertexExists($vertex);
242 7
        return $this->specifics->edgesOf($vertex);
243
    }
244
245
    /**
246
     * Get a set of all edges incoming into the specified vertex
247
     *
248
     * @param VertexInterface $vertex - the vertex
249
     *
250
     * @return EdgeSet
251
     */
252 3
    public function incomingEdgesOf(VertexInterface $vertex): EdgeSet
253
    {
254 3
        $this->assertVertexExists($vertex);
255 3
        return $this->specifics->incomingEdgesOf($vertex);
256
    }
257
258
    /**
259
     * Get a set of all edges outgoing from the specified vertex
260
     *
261
     * @param VertexInterface $vertex - the vertex
262
     *
263
     * @return EdgeSet
264
     */
265 9
    public function outgoingEdgesOf(VertexInterface $vertex): EdgeSet
266
    {
267 9
        $this->assertVertexExists($vertex);
268 9
        return $this->specifics->outgoingEdgesOf($vertex);
269
    }
270
271
    /**
272
     * Remove all edges specified by two vertices or the the list of edges themselves.
273
     * Return true, if graph was changed
274
     *
275
     * @param VertexInterface $vertex - the source vertex
276
     * @param VertexInterface $vertex - the target vertex
277
     * @param EdgeSet $edges - the edges
278
     *
279
     * @return bool
280
     */
281 4
    public function removeAllEdges(
282
        ?VertexInterface $sourceVertex = null,
283
        ?VertexInterface $targetVertex = null,
284
        ?EdgeSet $edges = null
285
    ): bool {
286 4
        $changed = false;
287 4
        if (!is_null($sourceVertex) && !is_null($targetVertex)) {
288 1
            $edge = $this->getEdge($sourceVertex, $targetVertex);
289 1
            if (!is_null($edge)) {
290 1
                $this->specifics->removeEdgeFromTouchingVertices($edge);
291 1
                $this->edgeSpecifics->remove($edge);
292 1
                return true;
293
            }
294
        } else {
295 3
            foreach ($edges as $edge) {
296 1
                if ($this->containsEdge(null, null, $edge)) {
297 1
                    $this->specifics->removeEdgeFromTouchingVertices($edge);
298 1
                    $this->edgeSpecifics->remove($edge);
299 1
                    $changed = true;
300
                }
301
            }
302
        }
303 3
        return $changed;
304
    }
305
306
    /**
307
     * Remove all specified vertices contained in the graph.
308
     * Return true, if graph was changed
309
     *
310
     * @param VertexSet $vertices - the vertices
311
     *
312
     * @return bool
313
     */
314 1
    public function removeAllVertices(VertexSet $vertices): bool
315
    {
316 1
        $changed = false;
317 1
        foreach ($vertices as $vertex) {
318 1
            $changed = $this->removeVertex($vertex);
319
        }
320 1
        return $changed;
321
    }
322
323
    /**
324
     * Remove the specifeid edge from the graph.
325
     * Return the edge, if it was removed
326
     *
327
     * @param VertexInterface $sourceVertex - the source vertex
328
     * @param VertexInterface $targetVertex - the target vertex
329
     * @param EdgeInterface $edge - the edge
330
     *
331
     * @return null|EdgeInterface
332
     */
333 2
    public function removeEdge(
334
        ?VertexInterface $sourceVertex = null,
335
        ?VertexInterface $targetVertex = null,
336
        ?EdgeInterface $edge = null
337
    ): ?EdgeInterface {
338 2
        if (!is_null($sourceVertex) && !is_null($targetVertex)) {
339 2
            $edge = $this->getEdge($sourceVertex, $targetVertex);
340 2
            if (!is_null($edge)) {
341 2
                $this->specifics->removeEdgeFromTouchingVertices($edge);
342 2
                $this->edgeSpecifics->remove($edge);
343 2
                return $edge;
344
            }
345 1
        } elseif (!is_null($edge)) {
346 1
            $this->specifics->removeEdgeFromTouchingVertices($edge);
347 1
            $this->edgeSpecifics->remove($edge);
348 1
            return $edge;
349
        }
350
        return null;
351
    }
352
353
    /**
354
     * Remove the specifeid vertex from the graph.
355
     * Return the true, if it was removed
356
     *
357
     * @param VertexInterface $vertex - the vertex
358
     *
359
     * @return bool
360
     */
361 3
    public function removeVertex(VertexInterface $vertex): bool
362
    {
363 3
        if ($this->containsVertex($vertex)) {
364 3
            $edges = $this->edgesOf($vertex);
365 3
            $this->removeAllEdges(null, null, $edges);
366 3
            $this->specifics->removeVertex($vertex);
367 3
            return true;
368
        }
369 1
        return false;
370
    }
371
372
    /**
373
     * Get the set of edges contained in the graph
374
     *
375
     * @return EdgeSet
376
     */
377 11
    public function edgeSet(): EdgeSet
378
    {
379 11
        return $this->edgeSpecifics->getEdgeSet();
380
    }
381
382
    /**
383
     * Get the set of vertices contained in the graph
384
     *
385
     * @return VertexSet
386
     */
387 14
    public function vertexSet(): VertexSet
388
    {
389 14
        return $this->specifics->getVertexSet();
390
    }
391
392
    /**
393
     * Get the edge source vertex
394
     *
395
     * @param EdgeInterface $edge - the edge
396
     *
397
     * @return VertexInterface
398
     */
399 29
    public function getEdgeSource(EdgeInterface $edge): VertexInterface
400
    {
401 29
        return $this->edgeSpecifics->getEdgeSource($edge);
402
    }
403
404
    /**
405
     * Get the edge target vertex
406
     *
407
     * @param EdgeInterface $edge - the edge
408
     *
409
     * @return VertexInterface
410
     */
411 29
    public function getEdgeTarget(EdgeInterface $edge): VertexInterface
412
    {
413 29
        return $this->edgeSpecifics->getEdgeTarget($edge);
414
    }
415
416
    /**
417
     * Get the edge weight
418
     *
419
     * @param EdgeInterface $edge - the edge
420
     *
421
     * @return null|float
422
     */
423 8
    public function getEdgeWeight(EdgeInterface $edge): ?float
424
    {
425 8
        return $this->edgeSpecifics->getEdgeWeight($edge);
426
    }
427
428
    /**
429
     * Set the edge weight
430
     *
431
     * @param EdgeInterface $edge - the edge
432
     * @param float $weight - the edge weight
433
     */
434 9
    public function setEdgeWeight(EdgeInterface $edge, ?float $weight = null): void
435
    {
436 9
        $this->edgeSpecifics->setEdgeWeight($edge, $weight);
437 9
    }
438
439
    /**
440
     * Assert the the vertex exists
441
     *
442
     * @param VertexInterface - the vertex
0 ignored issues
show
Documentation Bug introduced by
The doc comment - at position 0 could not be parsed: Unknown type name '-' at position 0 in -.
Loading history...
443
     *
444
     * @return bool
445
     *
446
     * @throws InvalidArgumentException
447
     */
448 31
    private function assertVertexExists(VertexInterface $vertex): bool
449
    {
450 31
        if ($this->containsVertex($vertex)) {
451 31
            return true;
452
        } else {
453
            throw new InvalidArgumentException("no such vertex in graph");
454
        }
455
    }
456
}
457