Completed
Push — master ( 2a1d1e...a0a921 )
by Ondrej
01:40
created

VotingAssembly::strategyDenyUnlessAllowed()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 13
Code Lines 7

Duplication

Lines 13
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 13
loc 13
ccs 0
cts 9
cp 0
rs 9.2
c 0
b 0
f 0
cc 4
eloc 7
nc 4
nop 2
crap 20
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
			case Strategy::DENY_UNLESS_ALLOWED:
82
				return $this->strategyDenyUnlessAllowed($votingSubject, $votingContext);
83
84
			default:
85
				throw new InvalidVotingResultException('Unable to decide on result, invalid strategy: '.$this->strategy);
86
		}
87
	}
88
89
90
	/**
91
	 * @param \SpareParts\Overseer\Voter\IVotingSubject $votingSubject
92
	 * @param \SpareParts\Overseer\Identity\IVotingContext $votingContext
93
	 * @return \SpareParts\Overseer\IVotingResult
94
	 * @throws \SpareParts\Overseer\InvalidVotingResultException
95
	 */
96
	private function strategyFirstVoteDecides(IVotingSubject $votingSubject, IVotingContext $votingContext)
97
	{
98
		foreach ($this->voters as $name => $voter) {
99
			if (($result = $voter->vote($votingSubject, $votingContext)) !== null) {
100
				return $this->prepareResult($name, $result);
101
			}
102
		}
103
		throw new InvalidVotingResultException('Voting assembly did not decide on any result!');
104
	}
105
106
107
	/**
108
	 * @param \SpareParts\Overseer\Voter\IVotingSubject $votingSubject
109
	 * @param \SpareParts\Overseer\Identity\IVotingContext $votingContext
110
	 * @return \SpareParts\Overseer\IVotingResult
111
	 */
112 View Code Duplication
	private function strategyAllowUnlessDenied($votingSubject, $votingContext)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
113
	{
114
		foreach ($this->voters as $name => $voter) {
115
			if (($result = $voter->vote($votingSubject, $votingContext)) !== null) {
116
				$vote = $this->prepareResult($name, $result);
117
				// at least one voter denied access
118
				if (!$vote->isAllowed()) {
119
					return $vote;
120
				}
121
			}
122
		}
123
		return new VotingResult(IVotingResult::ALLOW);
124
	}
125
126
127
	/**
128
	 * @param \SpareParts\Overseer\Voter\IVotingSubject $votingSubject
129
	 * @param \SpareParts\Overseer\Identity\IVotingContext $votingContext
130
	 * @return \SpareParts\Overseer\IVotingResult
131
	 */
132 View Code Duplication
	private function strategyDenyUnlessAllowed($votingSubject, $votingContext)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
133
	{
134
		foreach ($this->voters as $name => $voter) {
135
			if (($result = $voter->vote($votingSubject, $votingContext)) !== null) {
136
				$vote = $this->prepareResult($name, $result);
137
				// at least one voter allowed access
138
				if ($vote->isAllowed()) {
139
					return $vote;
140
				}
141
			}
142
		}
143
		return new VotingResult(IVotingResult::DENY);
144
	}
145
146
147
	/**
148
	 * @param string $voterName
149
	 * @param string|IVotingResult $partialResult
150
	 * @return \SpareParts\Overseer\IVotingResult|null
151
	 * @throws \SpareParts\Overseer\InvalidVotingResultException
152
	 */
153
	protected function prepareResult($voterName, $partialResult)
154
	{
155
		if (is_null($partialResult)) {
156
			return null;
157
		}
158
		if (is_string($partialResult)) {
159
			return new VotingResult($partialResult, $voterName);
160
		}
161
		if (!($partialResult instanceof IVotingResult)) {
162
			throw new InvalidVotingResultException('Expected bool or IVotingResult, got '.(string)$partialResult);
163
		}
164
		return $partialResult;
165
	}
166
167
168
	/**
169
	 * @param string $actionName
170
	 * @param \SpareParts\Overseer\Voter\IVotingSubject $subject
171
	 * @param \SpareParts\Overseer\Identity\IVotingContext $context
172
	 * @return bool
173
	 */
174 4
	public function canVoteOn($actionName, IVotingSubject $subject, IVotingContext $context)
175
	{
176 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...
177 1
			return false;
178
		}
179
180 3
		if ($subject->getVotingSubjectName() === $this->subjectName && $actionName === $this->actionName) {
181 1
			return true;
182
		}
183 2
		return false;
184
	}
185
}
186