1 | <?php |
||
2 | |||
3 | namespace Awssat\Visits; |
||
4 | |||
5 | use Awssat\Visits\Models\Visit; |
||
6 | use Awssat\Visits\Traits\{Lists, Periods, Record, Setters}; |
||
7 | use Illuminate\Database\Eloquent\Model; |
||
8 | use Illuminate\Support\Arr; |
||
9 | use Illuminate\Support\Str; |
||
10 | use Jaybizzle\CrawlerDetect\CrawlerDetect; |
||
11 | |||
12 | class Visits |
||
13 | { |
||
14 | use Record, Lists, Periods, Setters; |
||
15 | |||
16 | /** |
||
17 | * @var mixed |
||
18 | */ |
||
19 | protected $ipSeconds; |
||
20 | /** |
||
21 | * @var null |
||
22 | */ |
||
23 | protected $subject; |
||
24 | /** |
||
25 | * @var bool|mixed |
||
26 | */ |
||
27 | protected $fresh = false; |
||
28 | /** |
||
29 | * @var null|string |
||
30 | */ |
||
31 | protected $country = null; |
||
32 | /** |
||
33 | * @var null|string |
||
34 | */ |
||
35 | protected $referer = null; |
||
36 | /** |
||
37 | * @var null|string |
||
38 | */ |
||
39 | protected $operatingSystem = null; |
||
40 | /** |
||
41 | * @var null|string |
||
42 | */ |
||
43 | protected $language = null; |
||
44 | /** |
||
45 | * @var mixed |
||
46 | */ |
||
47 | protected $periods; |
||
48 | /** |
||
49 | * @var Keys |
||
50 | */ |
||
51 | protected $keys; |
||
52 | |||
53 | /** |
||
54 | * @var \Awssat\DataEngines\DataEngine |
||
55 | */ |
||
56 | protected $connection; |
||
57 | |||
58 | /** |
||
59 | * @var boolean |
||
60 | */ |
||
61 | public $ignoreCrawlers = false; |
||
62 | /** |
||
63 | * @var array |
||
64 | */ |
||
65 | public $globalIgnore = []; |
||
66 | |||
67 | /** |
||
68 | * @var array |
||
69 | */ |
||
70 | public $config = []; |
||
71 | |||
72 | /** |
||
73 | * @param \Illuminate\Database\Eloquent\Model $subject any model |
||
74 | * @param string $tag use only if you want to use visits on multiple models |
||
75 | */ |
||
76 | public function __construct($subject = null, $tag = 'visits') |
||
77 | { |
||
78 | $this->config = config('visits'); |
||
79 | |||
80 | $this->connection = $this->determineConnection($this->config['engine'] ?? 'redis') |
||
81 | ->connect($this->config['connection']) |
||
82 | ->setPrefix($this->config['keys_prefix'] ?? $this->config['redis_keys_prefix'] ?? 'visits'); |
||
83 | |||
84 | if(! $this->connection) { |
||
85 | return; |
||
86 | } |
||
87 | |||
88 | $this->periods = $this->config['periods']; |
||
89 | $this->ipSeconds = $this->config['remember_ip']; |
||
90 | $this->fresh = $this->config['always_fresh']; |
||
91 | $this->ignoreCrawlers = $this->config['ignore_crawlers']; |
||
92 | $this->globalIgnore = $this->config['global_ignore']; |
||
93 | $this->subject = $subject; |
||
0 ignored issues
–
show
|
|||
94 | $this->keys = new Keys($subject, preg_replace('/[^a-z0-9_]/i', '', $tag)); |
||
95 | |||
96 | if (! empty($this->keys->id)) { |
||
97 | $this->periodsSync(); |
||
98 | } |
||
99 | } |
||
100 | |||
101 | protected function determineConnection($name) |
||
102 | { |
||
103 | $connections = [ |
||
104 | 'redis' => \Awssat\Visits\DataEngines\RedisEngine::class, |
||
105 | 'eloquent' => \Awssat\Visits\DataEngines\EloquentEngine::class |
||
106 | ]; |
||
107 | |||
108 | if(! array_key_exists($name, $connections)) { |
||
109 | throw new \Exception("(Laravel-Visits) The selected engine `{$name}` is not supported! Please correct this issue from config/visits.php."); |
||
110 | } |
||
111 | |||
112 | return app($connections[$name]); |
||
113 | } |
||
114 | |||
115 | /** |
||
116 | * @param $subject |
||
117 | * @return self |
||
118 | */ |
||
119 | public function by($subject) |
||
120 | { |
||
121 | if($subject instanceof Model) { |
||
122 | $this->keys->append($this->keys->modelName($subject), $subject->{$subject->getKeyName()}); |
||
123 | } else if (is_array($subject)) { |
||
124 | $this->keys->append(array_keys($subject)[0], Arr::first($subject)); |
||
125 | } |
||
126 | |||
127 | return $this; |
||
128 | } |
||
129 | |||
130 | /** |
||
131 | * Reset methods |
||
132 | * |
||
133 | * @param $method |
||
134 | * @param string $args |
||
135 | * @return \Awssat\Visits\Reset |
||
136 | */ |
||
137 | public function reset($method = 'visits', $args = '') |
||
138 | { |
||
139 | return new Reset($this, $method, $args); |
||
140 | } |
||
141 | |||
142 | /** |
||
143 | * Check for the ip is has been recorded before |
||
144 | * @return bool |
||
145 | */ |
||
146 | public function recordedIp() |
||
147 | { |
||
148 | if(! $this->connection->exists($this->keys->ip(request()->ip()))) { |
||
149 | $this->connection->set($this->keys->ip(request()->ip()), true); |
||
150 | $this->connection->setExpiration($this->keys->ip(request()->ip()), $this->ipSeconds); |
||
151 | |||
152 | return false; |
||
153 | } |
||
154 | |||
155 | return true; |
||
156 | } |
||
157 | |||
158 | /** |
||
159 | * Get visits of model incount(stance. |
||
160 | * @return mixed |
||
161 | */ |
||
162 | public function count() |
||
163 | { |
||
164 | if ($this->country) { |
||
165 | return $this->connection->get($this->keys->visits."_countries:{$this->keys->id}", $this->country); |
||
166 | } else if ($this->referer) { |
||
167 | return $this->connection->get($this->keys->visits."_referers:{$this->keys->id}", $this->referer); |
||
168 | } else if ($this->operatingSystem) { |
||
169 | return $this->connection->get($this->keys->visits."_OSes:{$this->keys->id}", $this->operatingSystem); |
||
170 | } else if ($this->language) { |
||
171 | return $this->connection->get($this->keys->visits."_languages:{$this->keys->id}", $this->language); |
||
172 | } |
||
173 | |||
174 | return intval( |
||
175 | $this->keys->instanceOfModel |
||
176 | ? $this->connection->get($this->keys->visits, $this->keys->id) |
||
177 | : $this->connection->get($this->keys->visitsTotal()) |
||
178 | ); |
||
179 | } |
||
180 | |||
181 | /** |
||
182 | * @return integer time left in seconds |
||
183 | */ |
||
184 | public function timeLeft() |
||
185 | { |
||
186 | return $this->connection->timeLeft($this->keys->visits); |
||
187 | } |
||
188 | |||
189 | /** |
||
190 | * @return integer time left in seconds |
||
191 | */ |
||
192 | public function ipTimeLeft() |
||
193 | { |
||
194 | return $this->connection->timeLeft($this->keys->ip(request()->ip())); |
||
195 | } |
||
196 | |||
197 | protected function isCrawler() |
||
198 | { |
||
199 | return $this->ignoreCrawlers && app(CrawlerDetect::class)->isCrawler(); |
||
200 | } |
||
201 | |||
202 | /** |
||
203 | * @param int $inc value to increment |
||
204 | * @param bool $force force increment, skip time limit |
||
205 | * @param array $ignore to ignore recording visits of periods, country, refer, language and operatingSystem. pass them on this array. |
||
206 | * @return bool |
||
207 | */ |
||
208 | public function increment($inc = 1, $force = false, $ignore = []) |
||
209 | { |
||
210 | if ($force || (!$this->isCrawler() && !$this->recordedIp())) { |
||
211 | |||
212 | $this->connection->increment($this->keys->visits, $inc, $this->keys->id); |
||
213 | $this->connection->increment($this->keys->visitsTotal(), $inc); |
||
214 | |||
215 | if(is_array($this->globalIgnore) && sizeof($this->globalIgnore) > 0) { |
||
216 | $ignore = array_merge($ignore, $this->globalIgnore); |
||
217 | } |
||
218 | |||
219 | //NOTE: $$method is parameter also .. ($periods,$country,$refer) |
||
220 | foreach (['country', 'refer', 'periods', 'operatingSystem', 'language'] as $method) { |
||
221 | if(! in_array($method, $ignore)) { |
||
222 | $this->{'record'.Str::studly($method)}($inc); |
||
223 | } |
||
224 | } |
||
225 | |||
226 | return true; |
||
227 | } |
||
228 | |||
229 | return false; |
||
230 | } |
||
231 | |||
232 | /** |
||
233 | * @param int $inc |
||
234 | * @param array $ignore to ignore recording visits like country, periods ... |
||
235 | * @return bool |
||
236 | */ |
||
237 | public function forceIncrement($inc = 1, $ignore = []) |
||
238 | { |
||
239 | return $this->increment($inc, true, $ignore); |
||
240 | } |
||
241 | |||
242 | /** |
||
243 | * Decrement a new/old subject to the cache cache. |
||
244 | * |
||
245 | * @param int $dec |
||
246 | * @param array $ignore to ignore recording visits like country, periods ... |
||
247 | * @return bool |
||
248 | */ |
||
249 | public function decrement($dec = 1, $force = false, $ignore = []) |
||
250 | { |
||
251 | return $this->increment(-$dec, $force, $ignore); |
||
252 | } |
||
253 | |||
254 | /** |
||
255 | * @param int $dec |
||
256 | * @param array $ignore to ignore recording visits like country, periods ... |
||
257 | * @return bool |
||
258 | */ |
||
259 | public function forceDecrement($dec = 1, $ignore = []) |
||
260 | { |
||
261 | return $this->decrement($dec, true, $ignore); |
||
262 | } |
||
263 | |||
264 | /** |
||
265 | * @param $period |
||
266 | * @param int $time |
||
267 | * @return bool |
||
268 | */ |
||
269 | public function expireAt($period, $time) |
||
270 | { |
||
271 | $periodKey = $this->keys->period($period); |
||
272 | return $this->connection->setExpiration($periodKey, $time); |
||
273 | } |
||
274 | |||
275 | /** |
||
276 | * To be used with models to integrate relationship with visits model. |
||
277 | * @return \Illuminate\Database\Eloquent\Relations\Relation |
||
278 | */ |
||
279 | public function relation() |
||
280 | { |
||
281 | $prefix = $this->config['keys_prefix'] ?? $this->config['redis_keys_prefix'] ?? 'visits'; |
||
282 | |||
283 | return $this->subject->hasOne(Visit::class, 'secondary_key')->where('primary_key', $prefix.':'.$this->keys->visits); |
||
284 | } |
||
285 | } |
||
286 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.