Completed
Push — master ( c7f388...af3ede )
by Ondrej
01:42
created

VotingAssembly   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 141
Duplicated Lines 7.09 %

Coupling/Cohesion

Components 2
Dependencies 4

Test Coverage

Coverage 30.23%

Importance

Changes 0
Metric Value
wmc 19
lcom 2
cbo 4
dl 10
loc 141
ccs 13
cts 43
cp 0.3023
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 1
A commenceVote() 0 14 3
A strategyFirstVoteDecides() 5 9 3
A prepareResult() 0 13 4
A strategyAllowUnlessDenied() 5 9 3
B canVoteOn() 0 11 5

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
namespace SpareParts\Overseer\Assembly;
3
4
5
use SpareParts\Overseer\Identity\IVotingContext;
6
use SpareParts\Overseer\IVotingResult;
7
use SpareParts\Overseer\Strategy;
8
use SpareParts\Overseer\InvalidVotingResultException;
9
use SpareParts\Overseer\Voter\IVoter;
10
use SpareParts\Overseer\Voter\IVotingSubject;
11
use SpareParts\Overseer\VotingResult;
12
13
class VotingAssembly implements IVotingAssembly
14
{
15
16
	/**
17
	 * @var string
18
	 */
19
	private $strategy;
20
21
	/**
22
	 * @var IVoter[]
23
	 */
24
	private $voters;
25
26
	/**
27
	 * @var string
28
	 */
29
	private $subjectName;
30
31
	/**
32
	 * @var string
33
	 */
34
	private $actionName;
35
36
	/**
37
	 * @var null|string
38
	 */
39
	private $contextClassname;
40
41
42
	/**
43
	 * VotingAssembly constructor.
44
	 * @param string $subjectName
45
	 * @param string $actionName
46
	 * @param string $strategy
47
	 * @param \SpareParts\Overseer\Voter\IVoter[] $voters
48
	 * @param string|null $contextClassname If present, this assembly will require specific context class
49
	 */
50 4
	public function __construct(
51
		$subjectName,
52
		$actionName,
53
		$strategy,
54
		array $voters,
55
		$contextClassname = IVotingContext::class
56
	) {
57 4
		$this->strategy = $strategy;
58 4
		$this->voters = $voters;
59 4
		$this->subjectName = $subjectName;
60 4
		$this->actionName = $actionName;
61 4
		$this->contextClassname = $contextClassname;
62 4
	}
63
64
65
	/**
66
	 * @param \SpareParts\Overseer\Voter\IVotingSubject $votingSubject
67
	 * @param \SpareParts\Overseer\Identity\IVotingContext $votingContext
68
	 * @return null|\SpareParts\Overseer\IVotingResult
69
	 * @throws \SpareParts\Overseer\InvalidVotingResultException
70
	 */
71
	public function commenceVote(IVotingSubject $votingSubject, IVotingContext $votingContext)
72
	{
73
		$result = null;
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
74
		switch ($this->strategy) {
75
			case Strategy::FIRST_VOTE_DECIDES:
76
				return $this->strategyFirstVoteDecides($votingSubject, $votingContext);
77
78
			case Strategy::ALLOW_UNLESS_DENIED:
79
				return $this->strategyAllowUnlessDenied($votingSubject, $votingContext);
80
81
			default:
82
				throw new InvalidVotingResultException('Unable to decide on result, invalid strategy: '.$this->strategy);
83
		}
84
	}
85
86
87
	/**
88
	 * @param \SpareParts\Overseer\Voter\IVotingSubject $votingSubject
89
	 * @param \SpareParts\Overseer\Identity\IVotingContext $votingContext
90
	 * @return \SpareParts\Overseer\IVotingResult
91
	 * @throws \SpareParts\Overseer\InvalidVotingResultException
92
	 */
93
	private function strategyFirstVoteDecides(IVotingSubject $votingSubject, IVotingContext $votingContext)
94
	{
95 View Code Duplication
		foreach ($this->voters as $name => $voter) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
96
			if (($result = $voter->vote($votingSubject, $votingContext)) !== null) {
97
				return $this->prepareResult($name, $result);
98
			}
99
		}
100
		throw new InvalidVotingResultException('Voting assembly did not decide on any result!');
101
	}
102
103
104
	/**
105
	 * @param string $voterName
106
	 * @param string|IVotingResult $partialResult
107
	 * @return \SpareParts\Overseer\VotingResult|null
108
	 * @throws \SpareParts\Overseer\InvalidVotingResultException
109
	 */
110
	protected function prepareResult($voterName, $partialResult)
111
	{
112
		if (is_null($partialResult)) {
113
			return null;
114
		}
115
		if (is_string($partialResult)) {
116
			return new VotingResult($partialResult, $voterName);
117
		}
118
		if (!($partialResult instanceof IVotingResult)) {
119
			throw new InvalidVotingResultException('Expected bool or IVotingResult, got '.(string)$partialResult);
120
		}
121
		return $partialResult;
122
	}
123
124
125
	private function strategyAllowUnlessDenied($votingSubject, $votingContext)
126
	{
127 View Code Duplication
		foreach ($this->voters as $name => $voter) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
128
			if (($result = $voter->vote($votingSubject, $votingContext)) !== null) {
129
				return $this->prepareResult($name, $result);
130
			}
131
		}
132
		throw new InvalidVotingResultException('Voting assembly did not decide on any result!');
133
	}
134
135
136
	/**
137
	 * @param string $actionName
138
	 * @param \SpareParts\Overseer\Voter\IVotingSubject $subject
139
	 * @param \SpareParts\Overseer\Identity\IVotingContext $context
140
	 * @return bool
141
	 */
142 4
	public function canVoteOn($actionName, IVotingSubject $subject, IVotingContext $context)
143
	{
144 4
		if ($this->contextClassname && !($context instanceof $this->contextClassname)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->contextClassname of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
145 1
			return false;
146
		}
147
148 3
		if ($subject->getVotingSubjectName() === $this->subjectName && $actionName === $this->actionName) {
149 1
			return true;
150
		}
151 2
		return false;
152
	}
153
}
154