Passed
Pull Request — master (#82)
by Mark
07:57
created

QueryListener::getSessionKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Socialblue\LaravelQueryAdviser\DataListener;
4
5
use Illuminate\Database\Events\QueryExecuted;
6
use Illuminate\Support\Facades\Cache;
7
use Illuminate\Support\Facades\Log;
8
use Socialblue\LaravelQueryAdviser\Helper\QueryBuilderHelper;
9
10
class QueryListener {
11
12
    public static function listen(QueryExecuted $query) {
13
        if (config('laravel-query-adviser.enable_query_logging') === false) {
14
            return;
15
        }
16
17
        $sessionKey = self::getSessionKey();
18
19
        if (empty($sessionKey)) {
20
            return;
21
        }
22
23
        $url = url()->current();
24
        if (strpos($url, '/query-adviser') !== false) {
25
            return;
26
        }
27
        $time = time();
28
        $referer = request()->headers->get('referer');
29
30
        $data = self::getFromCache($time, $sessionKey);
31
32
        $possibleTraces = self::formatPossibleTraces(self::getPossibleTraces());
33
34
        self::putToCache(
35
            self::formatData($query, $data, $time, $possibleTraces, $url, $referer),
36
            $sessionKey
37
        );
38
    }
39
40
    /**
41
     * @return array
42
     */
43
    protected static function getPossibleTraces(): array
44
    {
45
        $traces = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 25);
46
        krsort($traces);
47
48
        $possibleTraces = array_filter($traces,
49
            static function ($trace) {
50
                return isset($trace['file']) &&
51
                    isset($trace['object']) &&
52
                    strpos($trace['file'], base_path('vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php')) !== false;
53
            }
54
        );
55
56
        $currentPossibleTrace = current($possibleTraces);
57
58
        if (!empty($currentPossibleTrace)) {
59
            $calledBy = $traces[key($possibleTraces) +1];
60
            $currentPossibleTrace['file'] = $calledBy['file'];
61
            $currentPossibleTrace['line'] = $calledBy['line'];
62
            $currentPossibleTrace['function'] = $calledBy['function'];
63
            return [$currentPossibleTrace];
64
        }
65
66
        return $possibleTraces;
67
    }
68
69
    /**
70
     * @param possibleTraces
71
     * @return mixed
72
     */
73
    protected static function formatPossibleTraces($possibleTraces)
74
    {
75
        array_walk($possibleTraces, static function (&$trace) {
76
            if (method_exists($trace['object'], 'getModel')) {
77
                $a = $trace['object']->getModel();
78
                if (is_string($a)) {
79
                    $trace['model'] = $a;
80
                } else {
81
                    $trace['model'] = get_class($a);
82
                }
83
            }
84
            unset($trace['object']);
85
            unset($trace['args']);
86
        });
87
        return $possibleTraces;
88
    }
89
90
    /**
91
     * @param QueryExecuted $query
92
     * @param array $data
93
     * @param int $time
94
     * @param $possibleTraces
95
     * @param string $url
96
     * @param string|null $referer
97
     * @return array
98
     */
99
    protected static function formatData(QueryExecuted $query, array $data, int $time, $possibleTraces, string $url, $referer = ''): array
100
    {
101
        $key = count($data[$time]);
102
103
        $data[$time][$key] = [
104
            'time'      => $time,
105
            'timeKey'   => $key,
106
            'backtrace' => $possibleTraces,
107
            'sql'       => QueryBuilderHelper::combineQueryAndBindings($query->sql, $query->bindings),
108
            'rawSql'    => $query->sql,
109
            'bindings'  => $query->bindings,
110
            'queryTime' => $query->time,
111
            'url'       => empty($url) ? '/' : $url,
112
            'referer'   => empty($referer) ? '/' : $referer,
113
        ];
114
        return $data;
115
    }
116
117
    /**
118
     * @param array $data
119
     * @param $sessionKey
120
     * @return array
121
     */
122
    protected static function putToCache(array $data, $sessionKey): array
123
    {
124
        if (count($data) > config('laravel-query-adviser.cache.max_entries')) {
125
            array_shift($data);
126
        }
127
128
        Cache::tags(['laravel-query-adviser-sessions'])->put(
129
            $sessionKey,
130
            $data,
131
            config('laravel-query-adviser.cache.ttl')
132
        );
133
134
        return $data;
135
    }
136
137
    /**
138
     * @param int $time
139
     * @param $sessionKey
140
     * @return array|mixed
141
     */
142
    protected static function getFromCache(int $time, $sessionKey)
143
    {
144
145
        $data = Cache::tags(['laravel-query-adviser-sessions'])->get($sessionKey, []);
146
        if (!is_array($data)) {
0 ignored issues
show
introduced by
The condition is_array($data) is always true.
Loading history...
147
            $data = [];
148
        }
149
150
        if (!isset($data[$time])) {
151
            $data[$time] = [];
152
        }
153
154
        return $data;
155
    }
156
157
    /**
158
     * @return \Illuminate\Config\Repository|mixed
159
     */
160
    protected static function getSessionKey()
161
    {
162
        return Cache::get(config('laravel-query-adviser.cache.session_id'), false);
163
    }
164
}
165