Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
269 / 269
100.00% covered (success)
100.00%
23 / 23
CRAP
100.00% covered (success)
100.00%
1 / 1
MethodLinks
100.00% covered (success)
100.00%
269 / 269
100.00% covered (success)
100.00%
23 / 23
140
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 emptySingleton
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getForDim
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
11
 asValueFirstLevel
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 asKeyForForeach
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
6
 withLinksAtDim
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 withKeysLinks
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 asCollapsed
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 asMergedWith
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
11
 withoutShape
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
7
 withAddedOffset
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 asMaybeMovedAtOffset
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 asMovedToKeys
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 asMergedForAssignment
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
8
 normalize
100.00% covered (success)
100.00%
36 / 36
100.00% covered (success)
100.00%
1 / 1
14
 getLinksCollapsing
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 getMethodAndParamTuples
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
7
 isEmpty
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
8
 hasDataForFuncAndParam
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
10
 withFuncAndParam
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
3
 asPreservedTaintednessForFuncParam
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
9
 asTaintednessForBackprop
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
4
 asFilteredForFuncAndParam
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
7
 toString
n/a
0 / 0
n/a
0 / 0
7
 __toString
n/a
0 / 0
n/a
0 / 0
1
1<?php declare( strict_types=1 );
2
3namespace SecurityCheckPlugin;
4
5use ast\Node;
6use Phan\Language\Element\FunctionInterface;
7
8/**
9 * Value object that represents method links.
10 * @todo We might store links inside Taintedness, but the memory usage might skyrocket
11 */
12class MethodLinks {
13    /** @var LinksSet */
14    private $links;
15
16    /** @var self[] */
17    private $dimLinks = [];
18
19    /** @var self|null */
20    private $unknownDimLinks;
21
22    /** @var LinksSet|null */
23    private $keysLinks;
24
25    /**
26     * @param LinksSet|null $links
27     */
28    public function __construct( ?LinksSet $links = null ) {
29        $this->links = $links ?? new LinksSet();
30    }
31
32    /**
33     * @return self
34     */
35    public static function emptySingleton(): self {
36        static $singleton;
37        if ( !$singleton ) {
38            $singleton = new self( new LinksSet );
39        }
40        return $singleton;
41    }
42
43    /**
44     * @note This returns a clone
45     * @param mixed $dim
46     * @param bool $pushOffsets
47     * @return self
48     */
49    public function getForDim( $dim, bool $pushOffsets = true ): self {
50        if ( $this === self::emptySingleton() ) {
51            return $this;
52        }
53        if ( !is_scalar( $dim ) ) {
54            $ret = ( new self( $this->links ) );
55            if ( $pushOffsets ) {
56                $ret = $ret->withAddedOffset( $dim );
57            }
58            if ( $this->unknownDimLinks ) {
59                $ret = $ret->asMergedWith( $this->unknownDimLinks );
60            }
61            foreach ( $this->dimLinks as $links ) {
62                $ret = $ret->asMergedWith( $links );
63            }
64            return $ret;
65        }
66        if ( isset( $this->dimLinks[$dim] ) ) {
67            $ret = ( new self( $this->links ) );
68            if ( $pushOffsets ) {
69                $ret = $ret->withAddedOffset( $dim );
70            }
71            if ( $this->unknownDimLinks ) {
72                $offsetLinks = $this->dimLinks[$dim]->asMergedWith( $this->unknownDimLinks );
73            } else {
74                $offsetLinks = $this->dimLinks[$dim];
75            }
76            return $ret->asMergedWith( $offsetLinks );
77        }
78        if ( $this->unknownDimLinks ) {
79            $ret = clone $this->unknownDimLinks;
80            $ret->links = $ret->links->asMergedWith( $this->links );
81        } else {
82            $ret = new self( $this->links );
83        }
84
85        return $pushOffsets ? $ret->withAddedOffset( $dim ) : $ret;
86    }
87
88    /**
89     * @return self
90     */
91    public function asValueFirstLevel(): self {
92        if ( $this === self::emptySingleton() ) {
93            return $this;
94        }
95        $ret = ( new self( $this->links ) )->withAddedOffset( null );
96        if ( $this->unknownDimLinks ) {
97            $ret = $ret->asMergedWith( $this->unknownDimLinks );
98        }
99        foreach ( $this->dimLinks as $links ) {
100            $ret = $ret->asMergedWith( $links );
101        }
102        return $ret;
103    }
104
105    /**
106     * @return self
107     */
108    public function asKeyForForeach(): self {
109        $emptySingleton = self::emptySingleton();
110        if ( $this === $emptySingleton ) {
111            return $this;
112        }
113
114        $hasBaseLinks = count( $this->links ) !== 0;
115        $hasKeyLinks = $this->keysLinks && count( $this->keysLinks ) !== 0;
116
117        if ( $hasBaseLinks ) {
118            $newLinks = $this->links->asAllMovedToKeys();
119            if ( $hasKeyLinks ) {
120                $newLinks = $newLinks->asMergedWith( $this->keysLinks );
121            }
122        } elseif ( $hasKeyLinks ) {
123            $newLinks = $this->keysLinks;
124        } else {
125            return $emptySingleton;
126        }
127
128        return new self( $newLinks );
129    }
130
131    /**
132     * @param mixed $dim
133     * @param MethodLinks $links
134     * @return self
135     */
136    public function withLinksAtDim( $dim, self $links ): self {