├── .gitignore ├── .travis.yml ├── README.md ├── composer.json ├── phpunit.xml └── src ├── Facades └── VisitorFacade.php ├── Ip.php ├── Services ├── Cache │ ├── CacheClass.php │ └── CacheInterface.php ├── Geo │ ├── GeoInterface.php │ └── MaxMind.php └── Validation │ ├── Checker.php │ ├── ValidationInterface.php │ └── Validator.php ├── Storage ├── QbVisitorRepository.php └── VisitorInterface.php ├── Visitor.php ├── VisitorServiceProvider.php ├── config └── visitor.php └── migrations ├── .gitkeep └── 2014_02_09_225721_create_visitor_registry.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | composer.lock 4 | .DS_Store -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.3 5 | - 5.4 6 | - 5.5 7 | 8 | before_script: 9 | - curl -s http://getcomposer.org/installer | php 10 | - php composer.phar install --dev 11 | 12 | script: phpunit -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Visitor 2 | ============== 3 | 4 | Register your visitors, Page hits for Laravel 5 5 | 6 | for laravel 4 use ver v1.0.0 7 | 8 | ### Installation 9 | 10 | 11 | The recommended way to install Visitor is through composer. 12 | 13 | ## Step 1 14 | 15 | run 16 | 17 | ``` json 18 | composer require weboap/visitor 19 | ``` 20 | 21 | 22 | ## Step 2 23 | 24 | Add if your laravel version is < 5.5 25 | ``` php 26 | Weboap\Visitor\VisitorServiceProvider::class 27 | ``` 28 | 29 | to the list of service providers in app/config/app.php 30 | 31 | ## Step 3 32 | 33 | Migrate the Visitor Table 34 | Run 35 | 36 | ``` php 37 | php artisan vendor:publish 38 | ``` 39 | then 40 | 41 | ``` php 42 | php artisan migrate 43 | ``` 44 | to migrate visitor table 45 | 46 | the config.php will be copied to /config at the same time 47 | 48 | ``` php 49 | /config/visitor.php 50 | ``` 51 | 52 | costumize it accordinly 53 | 54 | 55 | 56 | ## Step 5 (Optional) 57 | 58 | Visit 59 | http://dev.maxmind.com/geoip/geoip2/geolite2/ 60 | 61 | download GeoLite2-City.mmdb 62 | 63 | place it in (create the geo directory) 64 | 65 | ``` php 66 | storage/geo/ 67 | ``` 68 | or where ever you want just adjust the package config to reflect the new location, 69 | it's used to geo locate visitors 70 | 71 | 72 | 73 | 74 | ### Usage 75 | 76 | 77 | 78 | ``` php 79 | 80 | 81 | Visitor::log(); //log in db visitor ip, geo location, hit counter 82 | 83 | 84 | Visitor::get(); 85 | Visitor::get( $ip ); //fetch ip record 86 | 87 | 88 | 89 | Visitor::forget( $ip ); //delete ip from log 90 | 91 | 92 | Visitor::has( $ip ); // checkk if visitor ip exist in log 93 | 94 | 95 | Visitor::count() // return count of all site registred unique visitors 96 | 97 | 98 | Visitor::all(); // all records as array 99 | 100 | Visitor::all(true); // all records as collection 101 | 102 | 103 | Visitor::clicks(); //total of all clicks 104 | 105 | 106 | Visitor::range($date_start, $date_end); // visitors count in a date range; 107 | 108 | 109 | ``` 110 | ###Credits 111 | This product Uses GeoLite2 data created by MaxMind, whenever available. 112 | 113 | Enjoy! 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weboap/visitor", 3 | "description": "log your visitors in db, page hits, and generate visit counter for Laravel 5", 4 | "keywords": [ 5 | "framework", 6 | "laravel", 7 | "visitors", 8 | "counter", 9 | "log", 10 | "hits", 11 | "clicks" 12 | ], 13 | "license": "MIT", 14 | "authors": [ 15 | { 16 | "name": "WeboAp", 17 | "email": "weboap@gmail.com" 18 | } 19 | ], 20 | "require": { 21 | "php": ">=5.4.0", 22 | "illuminate/support": "^5.0", 23 | "jalle19/php-whitelist-check": "^1.0.6", 24 | "geoip2/geoip2": "^2.4.2" 25 | }, 26 | "autoload": { 27 | "psr-4": { 28 | "Weboap\\Visitor\\": "src" 29 | } 30 | }, 31 | "config": { 32 | "preferred-install": "dist" 33 | }, 34 | "extra": { 35 | "laravel": { 36 | "providers": [ 37 | "Weboap\\Visitor\\VisitorServiceProvider" 38 | ], 39 | "aliases": { 40 | "Visitor": "Weboap\\Visitor\\Facades\\VisitorFacade" 41 | } 42 | } 43 | }, 44 | "minimum-stability": "stable", 45 | "require-dev": { 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | ./tests/ 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Facades/VisitorFacade.php: -------------------------------------------------------------------------------- 1 | request = $request; 31 | $this->validators = $validators; 32 | } 33 | 34 | /** 35 | * @return string 36 | */ 37 | public function get() 38 | { 39 | $ip = $this->request->getClientIp(); 40 | 41 | if ($ip == '::1') { 42 | $ip = '127.0.0.1'; 43 | } 44 | 45 | return $ip; 46 | } 47 | 48 | /** 49 | * @param null $ip 50 | * 51 | * @return bool 52 | */ 53 | public function isValid($ip = null) 54 | { 55 | if (!isset($ip)) { 56 | return false; 57 | } 58 | 59 | foreach ($this->validators as $validator) { 60 | if (!$validator->validate($ip)) { 61 | return false; 62 | } 63 | } 64 | 65 | return true; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Services/Cache/CacheClass.php: -------------------------------------------------------------------------------- 1 | cache = $cache; 25 | } 26 | 27 | /** 28 | * @param $key 29 | */ 30 | public function destroy($key) 31 | { 32 | $this->cache->forget($key); 33 | } 34 | 35 | /** 36 | * @param $key 37 | * @param $data 38 | * 39 | * @return mixed 40 | */ 41 | public function rememberForever($key, $data) 42 | { 43 | return $this->cache->rememberForever($key, function () use ($data) { 44 | return $data; 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Services/Cache/CacheInterface.php: -------------------------------------------------------------------------------- 1 | config = $config; 39 | $this->ip = $ip; 40 | } 41 | 42 | /** 43 | * @return array 44 | */ 45 | public function locate() 46 | { 47 | // 48 | $ip = $this->ip->get(); 49 | $db = $this->config->get('visitor.maxmind_db_path'); 50 | 51 | if (!is_string($db) || !file_exists($db) || !$this->ip->isValid($ip)) { 52 | return []; 53 | } 54 | 55 | $this->reader = new Reader($db); 56 | 57 | try { 58 | $record = $this->reader->city($ip); 59 | 60 | return [ 61 | 'country_code' => $record->country->isoCode, 62 | 'country_name' => $record->country->name, 63 | 'state_code' => $record->mostSpecificSubdivision->isoCode, 64 | 'state' => $record->mostSpecificSubdivision->name, 65 | 'city' => $record->city->name, 66 | 'postale_code' => $record->postal->code, 67 | 68 | ]; 69 | } catch (AddressNotFoundException $e) { 70 | return []; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Services/Validation/Checker.php: -------------------------------------------------------------------------------- 1 | checker = $checker; 33 | $this->config = $config; 34 | } 35 | 36 | /** 37 | * @param $ip 38 | * 39 | * @throws \Weboap\Visitor\Services\Validation\InvalidArgumentException 40 | * 41 | * @return bool 42 | */ 43 | public function validate($ip) 44 | { 45 | $list = $this->config->get('visitor.ignored'); 46 | 47 | if (!is_array($list)) { 48 | $list = []; 49 | } 50 | 51 | try { 52 | $this->checker->whitelist($list); 53 | } catch (InvalidArgumentException $e) { 54 | throw new InvalidArgumentException('invalid definition encountered in white list!'); 55 | } 56 | 57 | /* 58 | *if ip is in the ignored list return false mean dont register 59 | * if ip is not in ignored list return true mean register 60 | **/ 61 | return !$this->checker->check($ip); 62 | } 63 | } 64 | 65 | /** 66 | * Class InvalidArgumentException. 67 | */ 68 | class InvalidArgumentException extends Exception 69 | { 70 | } 71 | -------------------------------------------------------------------------------- /src/Services/Validation/ValidationInterface.php: -------------------------------------------------------------------------------- 1 | _is_ip4($ip) || $this->_is_ipv6($ip)) { 18 | return true; 19 | } 20 | } 21 | 22 | /** 23 | * @param $ip 24 | * 25 | * @return bool 26 | */ 27 | private function _is_ip4($ip) 28 | { 29 | return filter_var($ip, FILTER_VALIDATE_IP) !== false; 30 | } 31 | 32 | /** 33 | * @param $ip 34 | * 35 | * @return bool 36 | */ 37 | private function _is_ipv6($ip) 38 | { 39 | return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Storage/QbVisitorRepository.php: -------------------------------------------------------------------------------- 1 | config = $config; 32 | $this->db = $db; 33 | } 34 | 35 | public function setTable($table) 36 | { 37 | $this->tableName = $table; 38 | } 39 | 40 | public function getTable() 41 | { 42 | return isset($this->tableName) ? $this->tableName : $this->config->get('visitor.table'); 43 | } 44 | 45 | public function create(array $data) 46 | { 47 | return $this->db->table($this->getTable())->insert($data); 48 | } 49 | 50 | public function get($ip) 51 | { 52 | return $this->db->table($this->getTable())->whereIp($ip)->first(); 53 | } 54 | 55 | public function update($ip, array $data) 56 | { 57 | return $this->db->table($this->getTable())->whereIp($ip)->update($data); 58 | } 59 | 60 | public function delete($ip) 61 | { 62 | return $this->db->table($this->getTable())->whereIp($ip)->delete(); 63 | } 64 | 65 | public function all() 66 | { 67 | return $this->db->table($this->getTable())->get(); 68 | } 69 | 70 | public function count($ip = null) 71 | { 72 | if (!isset($ip)) { 73 | return $this->db->table($this->getTable())->count(); 74 | } else { 75 | return $this->db->table($this->getTable())->whereIp($ip)->count(); 76 | } 77 | } 78 | 79 | public function increment($ip) 80 | { 81 | $this->db->table($this->getTable())->whereIp($ip)->increment('clicks'); 82 | } 83 | 84 | public function clicksSum() 85 | { 86 | return $this->db->table($this->getTable())->sum('clicks'); 87 | } 88 | 89 | public function range($start, $end) 90 | { 91 | return $this->db->table($this->getTable())->whereBetween('created_at', [$start, $end])->count(); 92 | } 93 | 94 | /** 95 | * delete all options from db. 96 | * 97 | * @return void 98 | */ 99 | public function clear() 100 | { 101 | $this->db->table($this->getTable())->truncate(); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/Storage/VisitorInterface.php: -------------------------------------------------------------------------------- 1 | storage = $storage; 68 | $this->geo = $geo; 69 | $this->ip = $ip; 70 | $this->cache = $cache; 71 | 72 | $this->collection = new Collection(); 73 | } 74 | 75 | /** 76 | * @param null $ip 77 | * 78 | * @return null 79 | */ 80 | public function get($ip = null) 81 | { 82 | if (!isset($ip)) { 83 | $ip = $this->ip->get(); 84 | } 85 | 86 | if ($this->ip->isValid($ip)) { 87 | return $this->storage->get($ip); 88 | } 89 | } 90 | 91 | public function log() 92 | { 93 | $ip = $this->ip->get(); 94 | 95 | if (!$this->ip->isValid($ip)) { 96 | return; 97 | } 98 | 99 | if ($this->has($ip)) { 100 | //ip already exist in db. 101 | $this->storage->increment($ip); 102 | } else { 103 | $geo = $this->geo->locate($ip); 104 | 105 | $country = array_key_exists('country_code', $geo) ? $geo['country_code'] : null; 106 | 107 | //ip doesnt exist in db 108 | $data = [ 109 | 'ip' => $ip, 110 | 'country' => $country, 111 | 'clicks' => 1, 112 | 'updated_at' => c::now(), 113 | 'created_at' => c::now(), 114 | ]; 115 | $this->storage->create($data); 116 | } 117 | 118 | // Clear the database cache 119 | $this->cache->destroy('weboap.visitor'); 120 | } 121 | 122 | /** 123 | * @param $ip 124 | */ 125 | public function forget($ip) 126 | { 127 | if (!$this->ip->isValid($ip)) { 128 | return; 129 | } 130 | 131 | //delete the ip from db 132 | $this->storage->delete($ip); 133 | 134 | // Clear the database cache 135 | $this->cache->destroy('weboap.visitor'); 136 | } 137 | 138 | /** 139 | * @param $ip 140 | * 141 | * @return bool 142 | */ 143 | public function has($ip) 144 | { 145 | if (!$this->ip->isValid($ip)) { 146 | return false; 147 | } 148 | 149 | return $this->count($ip) > 0; 150 | } 151 | 152 | /** 153 | * @param null $ip 154 | * 155 | * @return mixed 156 | */ 157 | public function count($ip = null) 158 | { 159 | //if ip null then return count of all visits 160 | return $this->storage->count($ip); 161 | } 162 | 163 | /** 164 | * @return mixed 165 | */ 166 | public function all($collection = false) 167 | { 168 | $result = $this->cache->rememberForever('weboap.visitor', $this->storage->all()); 169 | 170 | if ($collection) { 171 | return $this->collection->make($result); 172 | } 173 | 174 | return $result; 175 | } 176 | 177 | /** 178 | * @return mixed 179 | */ 180 | public function clicks() 181 | { 182 | return $this->storage->clicksSum(); 183 | } 184 | 185 | /** 186 | * @param $start 187 | * @param $end 188 | * 189 | * @return mixed 190 | */ 191 | public function range($start, $end) 192 | { 193 | $start = date('Y-m-d H:i:s', strtotime($start)); 194 | $end = date('Y-m-d 23:59:59', strtotime($end)); 195 | 196 | return $this->storage->range($start, $end); 197 | } 198 | 199 | /** 200 | * clear database records / cached results. 201 | * 202 | * @return void 203 | */ 204 | public function clear() 205 | { 206 | //clear database 207 | $this->storage->clear(); 208 | 209 | // clear cached options 210 | $this->cache->destroy('weboap.visitor'); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/VisitorServiceProvider.php: -------------------------------------------------------------------------------- 1 | publishes([ 20 | realpath(__DIR__.'/migrations') => base_path('/database/migrations'), 21 | ], 22 | 'migrations'); 23 | 24 | $this->publishes([ 25 | __DIR__.'/config/visitor.php' => config_path('visitor.php'), 26 | ]); 27 | } 28 | 29 | /** 30 | * Register the service provider. 31 | * 32 | * @return void 33 | */ 34 | public function register() 35 | { 36 | $this->registerBindings(); 37 | 38 | $this->RegisterIp(); 39 | 40 | $this->RegisterVisitor(); 41 | 42 | $this->RegisterBooting(); 43 | } 44 | 45 | public function RegisterVisitor() 46 | { 47 | $this->app->singleton('visitor', function ($app) { 48 | return new Visitor( 49 | $app['Weboap\Visitor\Storage\VisitorInterface'], 50 | $app['Weboap\Visitor\Services\Geo\GeoInterface'], 51 | $app['ip'], 52 | $app['Weboap\Visitor\Services\Cache\CacheInterface'] 53 | 54 | ); 55 | }); 56 | 57 | $this->app->bind('Weboap\Visitor\Visitor', function ($app) { 58 | return $app['visitor']; 59 | }); 60 | } 61 | 62 | public function RegisterIp() 63 | { 64 | $this->app->singleton('ip', function ($app) { 65 | return new Ip( 66 | $app->make('request'), 67 | [ 68 | $app->make('Weboap\Visitor\Services\Validation\Validator'), 69 | $app->make('Weboap\Visitor\Services\Validation\Checker'), 70 | ] 71 | 72 | ); 73 | }); 74 | } 75 | 76 | public function registerBooting() 77 | { 78 | $this->app->booting(function () { 79 | $loader = \Illuminate\Foundation\AliasLoader::getInstance(); 80 | $loader->alias('Visitor', 'Weboap\Visitor\Facades\VisitorFacade'); 81 | }); 82 | } 83 | 84 | protected function registerBindings() 85 | { 86 | $this->app->singleton( 87 | 'Weboap\Visitor\Storage\VisitorInterface', 88 | 'Weboap\Visitor\Storage\QbVisitorRepository' 89 | ); 90 | 91 | $this->app->singleton( 92 | 'Weboap\Visitor\Services\Geo\GeoInterface', 93 | 'Weboap\Visitor\Services\Geo\MaxMind' 94 | ); 95 | 96 | $this->app->singleton( 97 | 'Weboap\Visitor\Services\Cache\CacheInterface', 98 | 'Weboap\Visitor\Services\Cache\CacheClass' 99 | ); 100 | } 101 | 102 | /** 103 | * Get the services provided by the provider. 104 | * 105 | * @return array 106 | */ 107 | public function provides() 108 | { 109 | return ['visitor']; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/config/visitor.php: -------------------------------------------------------------------------------- 1 | 'visitor_registry', 6 | 7 | 'ignored' => [ 8 | '192.168.10.0/24', 9 | '10.0.3.1', 10 | '10.0.0.0/16', 11 | '2001:14b8:100:934b::3:1', 12 | '2001:14b8:100:934b::/64', 13 | // '*.example.com', 14 | 'localhost', 15 | ], 16 | 17 | 'maxmind_db_path' => storage_path().'/geo/GeoLite2-City.mmdb', 18 | 19 | ]; 20 | -------------------------------------------------------------------------------- /src/migrations/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weboAp/Visitor/d8bc4a9f058d91907ce9613bc9b03bf7d8069e38/src/migrations/.gitkeep -------------------------------------------------------------------------------- /src/migrations/2014_02_09_225721_create_visitor_registry.php: -------------------------------------------------------------------------------- 1 | increments('id'); 17 | $table->string('ip', 32); 18 | $table->string('country', 4)->nullable(); 19 | $table->integer('clicks')->unsigned()->default(0); 20 | $table->timestamps(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::drop('visitor_registry'); 32 | } 33 | } 34 | --------------------------------------------------------------------------------