-
Notifications
You must be signed in to change notification settings - Fork 0
/
AutoLockCommand.php
207 lines (176 loc) · 4.83 KB
/
AutoLockCommand.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
<?php
/**
* AutoLockCommand class file.
*
* @author XuanXin Zhen <[email protected]>
* @link https://github.com/zhenxxin/yii-auto-lock
* @copyright 2017 by XuanXin Zhen <[email protected]>
* @license see MIT license for detail.
*/
/**
* 一个可以自动锁定的命令行程序。
* 使用方式:
* 1. 继承本类;
* 2. 按照 Yii 框架的方式书写你的 actions。
*
* @property integer $lockMode 锁定模式
*
* @author: XuanXin Zhen <[email protected]>
* @since 0.0.1
*/
class AutoLockCommand extends CConsoleCommand
{
/**
* 需要被排除的 action 列表
* @var array
*/
public $exclude = [];
/**
* 需要被包含的 action 列表
* @var array
*/
public $include = [];
/**
* 锁文件存放目录
* 默认使用框架的 runtime 目录
* @var string
*/
private $runtime;
/**
* 锁文件名
* @var string
*/
private $lockFilename;
/**
* 锁文件
* @var resource
*/
private $lockFile;
/**
* 锁定模式
* @var integer
*/
private $lockMode;
public function __construct($name, CConsoleCommandRunner $runner)
{
parent::__construct($name, $runner);
$this->lockMode = LOCK_EX | LOCK_NB;
$this->runtime = Yii::app()->runtimePath;
}
/**
* 获取当前的锁定模式
* @return integer
*/
public function getLockMode()
{
return $this->lockMode;
}
/**
* 设置锁定模式
* @param $value
* @throws CException
*/
public function setLockMode($value)
{
if (!is_numeric($value)) {
$value = intval($value);
}
if (!is_integer($value)) {
$value = intval($value);
}
// 检查模式的值范围(暂时没有别的办法检查了?)
if ($value < (LOCK_NB & LOCK_UN & LOCK_EX & LOCK_SH) ||
$value > (LOCK_NB | LOCK_UN | LOCK_EX | LOCK_SH)
) {
throw new CException('不支持的值,请确认。');
}
$this->lockMode = $value;
}
/**
* @inheritdoc
* @param string $action
* @param array $params
* @return bool
*/
public function beforeAction($action, $params)
{
if (parent::beforeAction($action, $params)) {
if (!$this->checkLockRequired($action)) {
return true;
}
return $this->lock($action);
}
return false;
}
/**
* @inheritdoc
* @param string $action
* @param array $params
* @param int $exitCode
* @return int
*/
public function afterAction($action, $params, $exitCode = 0)
{
if ($this->checkLockRequired($action)) {
return $this->unlock($action);
}
return parent::afterAction($action, $params, $exitCode);
}
/**
* 创建锁文件,并锁定
* @param $action
* @return bool
*/
private function lock($action)
{
$dir = sprintf('%s/%s', $this->runtime, 'lock');
if (!file_exists($dir)) {
mkdir($dir);
}
$filename = sprintf('%s/%s-%s.lock', $dir, $this->name, $action);
$file = fopen($filename, 'c'); // 'c' 相对于 'w' 方式更适合于需要 咨询锁(advisory lock) 的情况
if (!flock($file, $this->lockMode)) {
return false;
}
// 写入当前进程号,方便 kill
if (fwrite($file, getmypid())) {
$this->lockFile = $file;
$this->lockFilename = $filename;
return true;
}
return false;
}
/**
* 解锁并删除锁文件
* @param $action
* @return bool
*/
private function unlock($action)
{
$filename = $this->lockFilename;
if (!file_exists($filename)) {
return true;
}
return flock($this->lockFile, LOCK_UN) && fclose($this->lockFile) && unlink($filename);
}
/**
* 检查是否需要锁定
* 当 @property $include 为空时,表示全部需要锁定
* @param string $action 被检查的 action 名
* @return bool
*/
private function checkLockRequired($action)
{
$compare = strtolower($action);
$include = array_map(function ($item) {
return strtolower($item);
}, $this->include);
$exclude = array_map(function ($item) {
return strtolower($item);
}, $this->exclude);
if (empty($include)) {
return true;
}
return !in_array($compare, $exclude) || in_array($compare, $include);
}
}