AuditEntry   A
last analyzed

Complexity

Total Complexity 33

Size/Duplication

Total Lines 216
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 16

Test Coverage

Coverage 84.62%

Importance

Changes 0
Metric Value
dl 0
loc 216
ccs 66
cts 78
cp 0.8462
rs 9.76
c 0
b 0
f 0
wmc 33
lcom 3
cbo 16

15 Methods

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