1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace bedezign\yii2\audit\models; |
4
|
|
|
|
5
|
|
|
use bedezign\yii2\audit\Audit; |
6
|
|
|
use bedezign\yii2\audit\components\db\ActiveRecord; |
7
|
|
|
use bedezign\yii2\audit\components\Helper; |
8
|
|
|
use Yii; |
9
|
|
|
use yii\db\ActiveQuery; |
10
|
|
|
use yii\db\Expression; |
11
|
|
|
|
12
|
|
|
/** |
13
|
|
|
* AuditEntry |
14
|
|
|
* @package bedezign\yii2\audit\models |
15
|
|
|
* |
16
|
|
|
* @property int $id |
17
|
|
|
* @property string $created |
18
|
|
|
* @property float $duration |
19
|
|
|
* @property int $user_id 0 means anonymous |
20
|
|
|
* @property string $ip |
21
|
|
|
* @property string $route |
22
|
|
|
* @property int $memory_max |
23
|
|
|
* @property string $request_method |
24
|
|
|
* @property string $ajax |
25
|
|
|
* |
26
|
|
|
* @property AuditError[] $linkedErrors |
27
|
|
|
* @property AuditJavascript[] $javascripts |
28
|
|
|
* @property AuditTrail[] $trails |
29
|
|
|
* @property AuditMail[] $mails |
30
|
|
|
* @property AuditData[] $data |
31
|
|
|
*/ |
32
|
|
|
class AuditEntry extends ActiveRecord |
33
|
|
|
{ |
34
|
|
|
/** |
35
|
|
|
* @var bool |
36
|
|
|
*/ |
37
|
|
|
protected $autoSerialize = false; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* @inheritdoc |
41
|
|
|
*/ |
42
|
195 |
|
public static function tableName() |
43
|
|
|
{ |
44
|
195 |
|
return '{{%audit_entry}}'; |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @param bool $initialise |
49
|
|
|
* @return static |
50
|
|
|
*/ |
51
|
117 |
|
public static function create($initialise = true) |
52
|
|
|
{ |
53
|
117 |
|
$entry = new static; |
54
|
|
|
if ($initialise) |
55
|
117 |
|
$entry->record(); |
56
|
|
|
|
57
|
117 |
|
return $entry; |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* Returns all linked AuditError instances |
62
|
|
|
* (Called `linkedErrors()` to avoid confusion with the `getErrors()` method) |
63
|
|
|
* @return ActiveQuery |
64
|
|
|
*/ |
65
|
9 |
|
public function getLinkedErrors() |
66
|
|
|
{ |
67
|
9 |
|
return static::hasMany(AuditError::className(), ['entry_id' => 'id']); |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* Returns all linked AuditTrail instances |
72
|
|
|
* @return ActiveQuery |
73
|
|
|
*/ |
74
|
9 |
|
public function getTrails() |
75
|
|
|
{ |
76
|
9 |
|
return static::hasMany(AuditTrail::className(), ['entry_id' => 'id']); |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* Returns all linked AuditMail instances |
81
|
|
|
* @return ActiveQuery |
82
|
|
|
*/ |
83
|
9 |
|
public function getMails() |
84
|
|
|
{ |
85
|
9 |
|
return static::hasMany(AuditMail::className(), ['entry_id' => 'id']); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* Returns all linked AuditJavascript instances |
90
|
|
|
* @return ActiveQuery |
91
|
|
|
*/ |
92
|
9 |
|
public function getJavascripts() |
93
|
|
|
{ |
94
|
9 |
|
return static::hasMany(AuditJavascript::className(), ['entry_id' => 'id']); |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* Returns all linked data records |
99
|
|
|
* @return ActiveQuery |
100
|
|
|
*/ |
101
|
3 |
|
public function getData() |
102
|
|
|
{ |
103
|
3 |
|
return static::hasMany(AuditData::className(), ['entry_id' => 'id'])->indexBy('type'); |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* Writes a number of associated data records in one go. |
108
|
|
|
* @param $batchData |
109
|
|
|
* @param bool $compact |
110
|
|
|
* @throws \yii\db\Exception |
111
|
|
|
*/ |
112
|
45 |
|
public function addBatchData($batchData, $compact = true) |
113
|
|
|
{ |
114
|
45 |
|
$columns = ['entry_id', 'type', 'created', 'data']; |
115
|
45 |
|
$rows = []; |
116
|
45 |
|
$params = []; |
117
|
45 |
|
$date = date('Y-m-d H:i:s'); |
118
|
|
|
// Some database like postgres depend on the data being escaped correctly. |
119
|
|
|
// PDO can take care of this if you define the field as a LOB (Large OBject), but unfortunately Yii does threat values |
120
|
|
|
// for batch inserts the same way. This code adds a number of literals instead of the actual values |
121
|
|
|
// so that they can be bound right before insert and still get escaped correctly |
122
|
45 |
|
foreach ($batchData as $type => $data) { |
123
|
45 |
|
$param = ':data_' . str_replace('/', '_', $type); |
124
|
45 |
|
$rows[] = [$this->id, $type, $date, new Expression($param)]; |
125
|
45 |
|
$params[$param] = [Helper::serialize($data, $compact), \PDO::PARAM_LOB]; |
126
|
45 |
|
} |
127
|
45 |
|
static::getDb()->createCommand()->batchInsert(AuditData::tableName(), $columns, $rows)->bindValues($params)->execute(); |
128
|
45 |
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* @param $type |
132
|
|
|
* @param $data |
133
|
|
|
* @param bool|true $compact |
134
|
|
|
* @throws \yii\db\Exception |
135
|
|
|
*/ |
136
|
6 |
|
public function addData($type, $data, $compact = true) |
137
|
|
|
{ |
138
|
|
|
// Make sure to mark data as a large object so it gets escaped |
139
|
|
|
$record = [ |
140
|
6 |
|
'entry_id' => $this->id, |
141
|
6 |
|
'type' => $type, |
142
|
6 |
|
'created' => date('Y-m-d H:i:s'), |
143
|
6 |
|
'data' => [Helper::serialize($data, $compact), \PDO::PARAM_LOB], |
144
|
6 |
|
]; |
145
|
6 |
|
static::getDb()->createCommand()->insert(AuditData::tableName(), $record)->execute(); |
146
|
6 |
|
} |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* Records the current application state into the instance. |
150
|
|
|
*/ |
151
|
117 |
|
public function record() |
152
|
|
|
{ |
153
|
117 |
|
$app = Yii::$app; |
154
|
117 |
|
$request = $app->request; |
155
|
|
|
|
156
|
117 |
|
$this->route = $app->requestedAction ? $app->requestedAction->uniqueId : null; |
157
|
117 |
|
if ($request instanceof \yii\web\Request) { |
158
|
117 |
|
$this->user_id = Audit::getInstance()->getUserId(); |
159
|
117 |
|
$this->ip = $this->getUserIP(); |
160
|
117 |
|
$this->ajax = $request->isAjax; |
161
|
117 |
|
$this->request_method = $request->method; |
162
|
117 |
|
} else if ($request instanceof \yii\console\Request) { |
163
|
|
|
$this->request_method = 'CLI'; |
164
|
|
|
} |
165
|
|
|
|
166
|
117 |
|
$this->save(false); |
167
|
117 |
|
} |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* @return bool |
171
|
|
|
*/ |
172
|
75 |
|
public function finalize() |
173
|
|
|
{ |
174
|
75 |
|
$app = Yii::$app; |
175
|
75 |
|
$request = $app->request; |
176
|
|
|
|
177
|
75 |
|
if (!$this->user_id && $request instanceof \yii\web\Request) { |
178
|
75 |
|
$this->user_id = Audit::getInstance()->getUserId(); |
179
|
75 |
|
} |
180
|
|
|
|
181
|
75 |
|
$this->duration = microtime(true) - YII_BEGIN_TIME; |
182
|
75 |
|
$this->memory_max = memory_get_peak_usage(); |
183
|
75 |
|
return $this->save(false, ['duration', 'memory_max', 'user_id']); |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* @return array |
188
|
|
|
*/ |
189
|
15 |
|
public function attributeLabels() |
190
|
|
|
{ |
191
|
|
|
return [ |
192
|
15 |
|
'id' => Yii::t('audit', 'Entry ID'), |
193
|
15 |
|
'created' => Yii::t('audit', 'Created'), |
194
|
15 |
|
'ip' => Yii::t('audit', 'IP'), |
195
|
15 |
|
'duration' => Yii::t('audit', 'Duration'), |
196
|
15 |
|
'user_id' => Yii::t('audit', 'User'), |
197
|
15 |
|
'memory_max' => Yii::t('audit', 'Memory'), |
198
|
15 |
|
'request_method' => Yii::t('audit', 'Request Method'), |
199
|
15 |
|
]; |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* @return bool |
204
|
|
|
*/ |
205
|
|
|
public function hasRelatedData() |
206
|
|
|
{ |
207
|
|
|
if ($this->getLinkedErrors()->count()) { |
208
|
|
|
return true; |
209
|
|
|
} |
210
|
|
|
if ($this->getJavascripts()->count()) { |
211
|
|
|
return true; |
212
|
|
|
} |
213
|
|
|
if ($this->getMails()->count()) { |
214
|
|
|
return true; |
215
|
|
|
} |
216
|
|
|
if ($this->getTrails()->count()) { |
217
|
|
|
return true; |
218
|
|
|
} |
219
|
|
|
return false; |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* @return string |
224
|
|
|
*/ |
225
|
|
|
public function getUserIP() |
226
|
|
|
{ |
227
|
|
|
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { |
228
|
|
|
return current(array_values(array_filter(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])))); |
229
|
|
|
} |
230
|
|
|
return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null; |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
/** |
234
|
|
|
* @param AuditEntry $auditEntry |
|
|
|
|
235
|
|
|
* @return bool|string |
236
|
|
|
*/ |
237
|
|
|
public static function getRequestUrl() |
238
|
|
|
{ |
239
|
|
|
$data = ArrayHelper::getColumn($this->data, 'data'); |
|
|
|
|
240
|
|
|
if (!isset($data['audit/request']) || !is_array($data['audit/request'])) { |
241
|
|
|
return Url::to([$this->route ?: '/'], 'https'); |
242
|
|
|
} |
243
|
|
|
$request = $data['audit/request']; |
244
|
|
|
$route = $this->route ?: (!empty($request['route']) ? $request['route'] : '/'); |
245
|
|
|
return Url::to(ArrayHelper::merge([$route], $request['GET']), 'https'); |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
} |
249
|
|
|
|
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.
Consider the following example. The parameter
$italy
is not defined by the methodfinale(...)
.The most likely cause is that the parameter was removed, but the annotation was not.