Visits   A
last analyzed

Complexity

Total Complexity 34

Size/Duplication

Total Lines 272
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 79
dl 0
loc 272
rs 9.68
c 0
b 0
f 0
wmc 34

15 Methods

Rating   Name   Duplication   Size   Complexity  
A recordedIp() 0 10 2
A timeLeft() 0 3 1
A __construct() 0 22 3
A isCrawler() 0 3 2
B increment() 0 22 8
A by() 0 9 3
A decrement() 0 3 1
A forceIncrement() 0 3 1
A determineConnection() 0 12 2
A reset() 0 3 1
A ipTimeLeft() 0 3 1
A forceDecrement() 0 3 1
A expireAt() 0 4 1
A relation() 0 5 1
A count() 0 16 6
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;
0 ignored issues
show
introduced by
The trait Awssat\Visits\Traits\Record requires some properties which are not provided by Awssat\Visits\Visits: $iso_code, $id
Loading history...
introduced by
The trait Awssat\Visits\Traits\Lists requires some properties which are not provided by Awssat\Visits\Visits: $id, $primary
Loading history...
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
0 ignored issues
show
Bug introduced by
The type Awssat\DataEngines\DataEngine was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
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'])
0 ignored issues
show
Bug introduced by
The method connect() does not exist on Illuminate\Contracts\Foundation\Application. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

81
                                ->/** @scrutinizer ignore-call */ connect($this->config['connection'])

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
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
Documentation Bug introduced by
It seems like $subject can also be of type Illuminate\Database\Eloquent\Model. However, the property $subject is declared as type null. Maybe add an additional type check?

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 the id property of an instance of the Account 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.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
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