1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Hongliang\Defender; |
4
|
|
|
|
5
|
|
|
use Hongliang\Defender\Voter\VoterInterface; |
6
|
|
|
use Hongliang\Defender\Voter\IpRangeVoter; |
7
|
|
|
use Hongliang\Defender\Voter\UriKeywordVoter; |
8
|
|
|
use Hongliang\Defender\Voter\SpiderVoter; |
9
|
|
|
|
10
|
|
|
class Defender |
11
|
|
|
{ |
12
|
|
|
const NORMAL = 0; |
13
|
|
|
const NOTICE = 1; |
14
|
|
|
const FORBIDDEN = 2; |
15
|
|
|
const DENY = 3; |
16
|
|
|
const REVENGE = 4; |
17
|
|
|
const REVENGE_PERMANENT = 5; |
18
|
|
|
|
19
|
|
|
private $voters = null; |
20
|
|
|
private $redirectUrl = 'http://localhost'; |
21
|
|
|
|
22
|
|
|
public static function defend() |
23
|
|
|
{ |
24
|
|
|
$defender = new self(); |
25
|
|
|
$defender->addVoter(new IpRangeVoter(), self::DENY) |
26
|
|
|
->addVoter(new UriKeywordVoter(), self::FORBIDDEN) |
27
|
|
|
->addVoter(new SpiderVoter(), self::DENY) |
28
|
|
|
->react(); |
29
|
|
|
} |
30
|
|
|
|
31
|
|
|
public function addVoter(VoterInterface $voter, $level = self::FORBIDDEN) |
32
|
|
|
{ |
33
|
|
|
if (null === $this->voters) { |
34
|
|
|
$this->voters = []; |
35
|
|
|
} |
36
|
|
|
$this->voters [] = ['voter' => $voter, 'level' => $level]; |
37
|
|
|
|
38
|
|
|
return $this; |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
public function setRedirectUrl($url) |
42
|
|
|
{ |
43
|
|
|
$this->redirectUrl = $url; |
44
|
|
|
|
45
|
|
|
return $this; |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
public function getRedirectUrl() |
49
|
|
|
{ |
50
|
|
|
return $this->redirectUrl; |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
public function exam() |
54
|
|
|
{ |
55
|
|
|
$level = self::NORMAL; |
56
|
|
|
if (null === $this->voters) { |
57
|
|
|
return $level; |
58
|
|
|
} |
59
|
|
|
$this->sortVoters(); |
60
|
|
|
foreach ($this->voters as $voter) { |
61
|
|
|
if ($voter['voter']->vote()) { |
62
|
|
|
return $voter['level']; |
63
|
|
|
} |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
return $level; |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
private function sortVoters() |
70
|
|
|
{ |
71
|
|
|
if (is_array($this->voters)) { |
72
|
|
|
usort($this->voters, array($this, 'sortVotersFunc')); |
73
|
|
|
} |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
private function sortVotersFunc($a, $b) |
77
|
|
|
{ |
78
|
|
|
return $a['level'] < $b['level']; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
public function react($level = null) |
82
|
|
|
{ |
83
|
|
|
if (null === $level) { |
84
|
|
|
$level = $this->exam(); |
85
|
|
|
} |
86
|
|
|
$exit = false; |
87
|
|
|
switch ($level) { |
88
|
|
View Code Duplication |
case self::REVENGE_PERMANENT: |
89
|
|
|
header('HTTP/1.1 301 Moved Permanently'); |
90
|
|
|
header('Location: '.$this->getRedirectUrl()); |
91
|
|
|
$exit = true; |
92
|
|
|
break; |
93
|
|
View Code Duplication |
case self::REVENGE: |
94
|
|
|
header('HTTP/1.1 302 Moved Temporarily'); |
95
|
|
|
header('Location: '.$this->getRedirectUrl()); |
96
|
|
|
$exit = true; |
97
|
|
|
break; |
98
|
|
|
case self::DENY: |
99
|
|
|
header('HTTP/1.1 500 Internal Server Error'); |
100
|
|
|
$exit = true; |
101
|
|
|
break; |
102
|
|
|
case self::FORBIDDEN: |
103
|
|
|
header('HTTP/1.0 403 Forbidden'); |
104
|
|
|
$exit = true; |
105
|
|
|
break; |
106
|
|
|
case self::NOTICE: |
107
|
|
|
default: |
108
|
|
|
break; |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
if ($exit) { |
112
|
|
|
exit; |
|
|
|
|
113
|
|
|
} |
114
|
|
|
} |
115
|
|
|
} |
116
|
|
|
|
An exit expression should only be used in rare cases. For example, if you write a short command line script.
In most cases however, using an
exit
expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.