Completed
Push — master ( 4632b0...98cfe8 )
by Guillermo
36:11
created

PlayersCollection   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 198
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
wmc 32
lcom 1
cbo 2
dl 0
loc 198
rs 9.6
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A addPlayer() 0 6 2
A addCouple() 0 9 3
A players() 0 4 1
A shufflePlayers() 0 4 1
A player() 0 8 2
A excludePlayers() 0 11 4
A areExclude() 0 10 3
A countExcludePlayers() 0 4 1
A isDuplicatePlayer() 0 8 2
A shuffleAssoc() 0 12 3
A forceShuffle() 0 11 4
A current() 0 4 1
A next() 0 4 1
A key() 0 4 1
A valid() 0 4 1
A rewind() 0 4 1
A count() 0 4 1
1
<?php
2
3
namespace SecretSanta;
4
5
use SecretSanta\Exceptions\PlayersCollectionException;
6
7
/**
8
 * Class PlayersCollection
9
 * @package SecretSanta
10
 */
11
class PlayersCollection implements \Iterator, \Countable
12
{
13
    /** @var Player[] */
14
    private $players = [];
15
    /** @var array  */
16
    private $excludePlayers = [];
17
18
    /**
19
     * @param Player $player
20
     * @throws PlayersCollectionException
21
     */
22
    public function addPlayer(Player $player)
23
    {
24
        if (!$this->isDuplicatePlayer($player)) {
25
            $this->players[$player->id()] = $player;
26
        }
27
    }
28
29
    /**
30
     * @param Player $player
31
     * @param Player $couple
32
     */
33
    public function addCouple(Player $player, Player $couple)
34
    {
35
        if (!$this->isDuplicatePlayer($player) && !$this->isDuplicatePlayer($couple)) {
36
            $this->players[$player->id()] = $player;
37
            $this->players[$couple->id()] = $couple;
38
39
            $this->excludePlayers($player, $couple);
40
        }
41
    }
42
43
    /**
44
     * @return Player[]
45
     */
46
    public function players()
47
    {
48
        return $this->players;
49
    }
50
51
    /**
52
     * @return Player[]
53
     */
54
    public function shufflePlayers()
55
    {
56
        return $this->shuffleAssoc($this->players);
57
    }
58
59
    /**
60
     * @param string $id
61
     * @return Player
62
     * @throws PlayersCollectionException
63
     */
64
    public function player($id)
65
    {
66
        if (!isset($this->players[$id])) {
67
            throw new PlayersCollectionException("Player {$id} not found");
68
        }
69
70
        return $this->players[$id];
71
    }
72
73
74
    /**
75
     * @param Player[] ...$players
76
     */
77
    private function excludePlayers(...$players)
78
    {
79
        foreach ($players as $mainPlayer) {
80
            foreach ($players as $player) {
81
                if ($mainPlayer->id() == $player->id()){
0 ignored issues
show
Bug introduced by
The method id cannot be called on $mainPlayer (of type array<integer,object<SecretSanta\Player>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
Bug introduced by
The method id cannot be called on $player (of type array<integer,object<SecretSanta\Player>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
82
                    continue;
83
                }
84
                $this->excludePlayers[$mainPlayer->id()][] = $player->id();
0 ignored issues
show
Bug introduced by
The method id cannot be called on $mainPlayer (of type array<integer,object<SecretSanta\Player>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
Bug introduced by
The method id cannot be called on $player (of type array<integer,object<SecretSanta\Player>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
85
            }
86
        }
87
    }
88
89
    /**
90
     * @param Player $player
91
     * @param Player $player2
92
     * @return bool
93
     */
94
    public function areExclude(Player $player, Player $player2)
95
    {
96
        if (array_key_exists($player->id(), $this->excludePlayers)
97
            && in_array($player2->id(), $this->excludePlayers[$player->id()])
98
        ) {
99
            return true;
100
        }
101
102
        return false;
103
    }
104
105
    /**
106
     * @return int
107
     */
108
    public function countExcludePlayers()
109
    {
110
        return count($this->excludePlayers);
111
    }
112
113
114
    /**
115
     * @param Player $player
116
     * @return bool
117
     * @throws PlayersCollectionException
118
     */
119
    private function isDuplicatePlayer(Player $player)
120
    {
121
        if (array_key_exists($player->id(), $this->players)) {
122
            throw  new PlayersCollectionException('Duplicate player: '.$player->email());
123
        }
124
125
        return false;
126
    }
127
128
    /**
129
     * @param array $list
130
     * @return array
131
     */
132
    private function shuffleAssoc($list)
133
    {
134
        if (!is_array($list)) return $list;
135
136
        $keys = $this->forceShuffle(array_keys($list));
137
        $random = [];
138
        foreach ($keys as $key) {
139
            $random[$key] = $list[$key];
140
        }
141
142
        return $random;
143
    }
144
145
    /**
146
     * @param array $list
147
     * @return array
148
     */
149
    private function forceShuffle($list)
150
    {
151
        if (!is_array($list) || count($list) < 2) return $list;
152
153
        $shuffleList = $list;
154
        while ($shuffleList == $list) {
155
            shuffle($shuffleList);
156
        }
157
158
        return $shuffleList;
159
    }
160
161
    /**
162
     * @return Player
163
     */
164
    public function current()
165
    {
166
        return current($this->players);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression current($this->players); of type SecretSanta\Player|false adds false to the return on line 166 which is incompatible with the return type documented by SecretSanta\PlayersCollection::current of type SecretSanta\Player. It seems like you forgot to handle an error condition.
Loading history...
167
    }
168
169
    /**
170
     * @return void Any returned value is ignored.
171
     */
172
    public function next()
173
    {
174
        next($this->players);
175
    }
176
177
    /**
178
     * @return int
179
     */
180
    public function key()
181
    {
182
        return key($this->players);
183
    }
184
185
    /**
186
     * @return bool
187
     */
188
    public function valid()
189
    {
190
        return ($this->current() !== false);
191
    }
192
193
    /**
194
     * @return void Any returned value is ignored.
195
     */
196
    public function rewind()
197
    {
198
        reset($this->players);
199
    }
200
201
    /**
202
     * @return int
203
     */
204
    public function count()
205
    {
206
        return count($this->players);
207
    }
208
}