Passed
Push — master ( 65709d...74d483 )
by bader
04:24 queued 10s
created

Visits::recordedIp()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 10
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Awssat\Visits;
4
5
use Awssat\Visits\Traits\{Lists, Periods, Record, Setters};
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Support\Arr;
8
use Illuminate\Support\Str;
9
use Jaybizzle\CrawlerDetect\CrawlerDetect;
10
11
class Visits
12
{
13
    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...
14
15
    /**
16
     * @var mixed
17
     */
18
    protected $ipSeconds;
19
    /**
20
     * @var null
21
     */
22
    protected $subject;
23
    /**
24
     * @var bool|mixed
25
     */
26
    protected $fresh = false;
27
    /**
28
     * @var null|string
29
     */
30
    protected $country = null;
31
    /**
32
     * @var null|string
33
     */
34
    protected $referer = null;
35
    /**
36
     * @var null|string
37
     */
38
    protected $operatingSystem = null;
39
    /**
40
     * @var null|string
41
     */
42
    protected $language = null;
43
    /**
44
     * @var mixed
45
     */
46
    protected $periods;
47
    /**
48
     * @var Keys
49
     */
50
    protected $keys;
51
52
    /**
53
     * @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...
54
     */
55
    protected $connection;
56
57
    /**
58
     * @var boolean
59
     */
60
    public $ignoreCrawlers = false;
61
    /**
62
     * @var array
63
     */
64
    public $globalIgnore = [];
65
66
    /**
67
     * @param \Illuminate\Database\Eloquent\Model $subject any model
68
     * @param string $tag use only if you want to use visits on multiple models
69
     */
70
    public function __construct($subject = null, $tag = 'visits')
71
    {
72
        $config = config('visits');
73
74
        $this->connection = $this->determineConnection($config['engine'] ?? 'redis')
75
                                ->connect($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

75
                                ->/** @scrutinizer ignore-call */ connect($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...
76
                                ->setPrefix($config['keys_prefix'] ?? $config['redis_keys_prefix'] ?? 'visits');
77
78
        if(! $this->connection) {
79
            return;
80
        }
81
82
        $this->periods = $config['periods'];
83
        $this->ipSeconds = $config['remember_ip'];
84
        $this->fresh = $config['always_fresh'];
85
        $this->ignoreCrawlers = $config['ignore_crawlers'];
86
        $this->globalIgnore = $config['global_ignore'];
87
        $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...
88
        $this->keys = new Keys($subject, preg_replace('/[^a-z0-9_]/i', '', $tag));
89
90
        $this->periodsSync();
91
    }
92
93
    protected function determineConnection($name)
94
    {
95
        $connections = [
96
            'redis' => \Awssat\Visits\DataEngines\RedisEngine::class,
97
            'eloquent' => \Awssat\Visits\DataEngines\EloquentEngine::class
98
        ];
99
100
        if(! array_key_exists($name, $connections)) {
101
            throw new Exception("(Laravel-Visits) The selected engine `{$name}` is not supported! Please correct this issue from config/visits.php.");
0 ignored issues
show
Bug introduced by
The type Awssat\Visits\Exception was not found. Did you mean Exception? If so, make sure to prefix the type with \.
Loading history...
102
        }
103
104
        return app($connections[$name]);
105
    }
106
107
    /**
108
     * @param $subject
109
     * @return self
110
     */
111
    public function by($subject)
112
    {
113
        if($subject instanceof Model) {
114
            $this->keys->append($this->keys->modelName($subject), $subject->{$subject->getKeyName()});
115
        } else if (is_array($subject)) {
116
            $this->keys->append(array_keys($subject)[0], Arr::first($subject));
117
        }
118
119
        return $this;
120
    }
121
122
    /**
123
     * Reset methods
124
     *
125
     * @param $method
126
     * @param string $args
127
     * @return \Awssat\Visits\Reset
128
     */
129
    public function reset($method = 'visits', $args = '')
130
    {
131
        return new Reset($this, $method, $args);
132
    }
133
134
    /**
135
     * Check for the ip is has been recorded before
136
     * @return bool
137
     */
138
    public function recordedIp()
139
    {
140
        if(! $this->connection->exists($this->keys->ip(request()->ip()))) {
141
            $this->connection->set($this->keys->ip(request()->ip()), true);
142
            $this->connection->setExpiration($this->keys->ip(request()->ip()), $this->ipSeconds);
143
144
            return false;
145
        }
146
147
        return true;
148
    }
149
150
    /**
151
     * Get visits of model incount(stance.
152
     * @return mixed
153
     */
154
    public function count()
155
    {
156
        if ($this->country) {
157
            return $this->connection->get($this->keys->visits."_countries:{$this->keys->id}", $this->country);
158
        } else if ($this->referer) {
159
            return $this->connection->get($this->keys->visits."_referers:{$this->keys->id}", $this->referer);
160
        } else if ($this->operatingSystem) {
161
            return $this->connection->get($this->keys->visits."_OSes:{$this->keys->id}", $this->operatingSystem);
162
        } else if ($this->language) {
163
            return $this->connection->get($this->keys->visits."_languages:{$this->keys->id}", $this->language);
164
        }
165
166
        return intval(
167
            $this->keys->instanceOfModel
168
                    ? $this->connection->get($this->keys->visits, $this->keys->id)
169
                    : $this->connection->get($this->keys->visitsTotal())
170
        );
171
    }
172
173
    /**
174
     * @return integer time left in seconds
175
     */
176
    public function timeLeft()
177
    {
178
        return $this->connection->timeLeft($this->keys->visits);
179
    }
180
181
    /**
182
     * @return integer time left in seconds
183
     */
184
    public function ipTimeLeft()
185
    {
186
        return $this->connection->timeLeft($this->keys->ip(request()->ip()));
187
    }
188
189
    protected function isCrawler()
190
    {
191
        return $this->ignoreCrawlers && app(CrawlerDetect::class)->isCrawler();
192
    }
193
194
    /**
195
     * @param int $inc value to increment
196
     * @param bool $force force increment, skip time limit
197
     * @param array $ignore to ignore recording visits of periods, country, refer, language and operatingSystem. pass them on this array.
198
     */
199
    public function increment($inc = 1, $force = false, $ignore = [])
200
    {
201
        if ($force || (!$this->isCrawler() && !$this->recordedIp())) {
202
            $this->connection->increment($this->keys->visits, $inc, $this->keys->id);
203
            $this->connection->increment($this->keys->visitsTotal(), $inc);
204
205
            if(is_array($this->globalIgnore) && sizeof($this->globalIgnore) > 0) {
206
                $ignore = array_merge($ignore, $this->globalIgnore);
207
            }
208
209
            //NOTE: $$method is parameter also .. ($periods,$country,$refer)
210
            foreach (['country', 'refer', 'periods', 'operatingSystem', 'language'] as $method) {
211
                if(! in_array($method, $ignore))  {
212
                    $this->{'record'.Str::studly($method)}($inc);
213
                }
214
            }
215
        }
216
    }
217
218
    /**
219
     * @param int $inc
220
     * @param array $ignore to ignore recording visits like country, periods ...
221
     */
222
    public function forceIncrement($inc = 1, $ignore = [])
223
    {
224
        $this->increment($inc, true, $ignore);
225
    }
226
227
    /**
228
     * Decrement a new/old subject to the cache cache.
229
     *
230
     * @param int $dec
231
     * @param array $ignore to ignore recording visits like country, periods ...
232
     */
233
    public function decrement($dec = 1, $force = false, $ignore = [])
234
    {
235
        $this->increment(-$dec, $force, $ignore);
236
    }
237
238
    /**
239
     * @param int $dec
240
     * @param array $ignore to ignore recording visits like country, periods ...
241
     */
242
    public function forceDecrement($dec = 1, $ignore = [])
243
    {
244
        $this->decrement($dec, true, $ignore);
245
    }
246
247
    /**
248
     * @param $period
249
     * @param int $time
250
     * @return bool
251
     */
252
    public function expireAt($period, $time)
253
    {
254
        $periodKey = $this->keys->period($period);
255
        return $this->connection->setExpiration($periodKey, $time);
256
    }
257
}
258