From e33cdbfdfdcbb7fcbacb808ef4507e2eb4db34ca Mon Sep 17 00:00:00 2001 From: WeboAp Date: Sat, 6 Jun 2015 20:20:17 -0400 Subject: [PATCH] error #4 fix --- .gitignore | 4 + .travis.yml | 12 + README.md | 122 ++++++++++ composer.json | 27 +++ phpunit.xml | 18 ++ src/Facades/VisitorFacade.php | 14 ++ src/Ip.php | 53 +++++ src/Services/Cache/CacheClass.php | 45 ++++ src/Services/Cache/CacheInterface.php | 11 + src/Services/Geo/GeoInterface.php | 9 + src/Services/Geo/MaxMind.php | 66 ++++++ src/Services/Validation/Checker.php | 49 ++++ .../Validation/ValidationInterface.php | 9 + src/Services/Validation/Validator.php | 23 ++ src/Storage/QbVisitorRepository.php | 123 ++++++++++ src/Storage/VisitorInterface.php | 25 ++ src/Visitor.php | 222 ++++++++++++++++++ src/VisitorServiceProvider.php | 132 +++++++++++ src/config/visitor.php | 22 ++ src/migrations/.gitkeep | 0 ...4_02_09_225721_create_visitor_registry.php | 35 +++ 21 files changed, 1021 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 README.md create mode 100644 composer.json create mode 100644 phpunit.xml create mode 100644 src/Facades/VisitorFacade.php create mode 100644 src/Ip.php create mode 100644 src/Services/Cache/CacheClass.php create mode 100644 src/Services/Cache/CacheInterface.php create mode 100644 src/Services/Geo/GeoInterface.php create mode 100644 src/Services/Geo/MaxMind.php create mode 100644 src/Services/Validation/Checker.php create mode 100644 src/Services/Validation/ValidationInterface.php create mode 100644 src/Services/Validation/Validator.php create mode 100644 src/Storage/QbVisitorRepository.php create mode 100644 src/Storage/VisitorInterface.php create mode 100644 src/Visitor.php create mode 100644 src/VisitorServiceProvider.php create mode 100644 src/config/visitor.php create mode 100644 src/migrations/.gitkeep create mode 100644 src/migrations/2014_02_09_225721_create_visitor_registry.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2c1fc0c --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/vendor +composer.phar +composer.lock +.DS_Store \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..0a1c1cb --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + +before_script: + - curl -s http://getcomposer.org/installer | php + - php composer.phar install --dev + +script: phpunit \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..63adcf5 --- /dev/null +++ b/README.md @@ -0,0 +1,122 @@ +#Visitor +============== + +Register your visitors, Page hists, and count for Laravel 5 + +for laravel 4 use ver v1.0.0 + +### Installation + + +The recommended way to install Winput is through composer. + +## Step 1 + +Just add to `composer.json` file: + +``` json +{ + "require": { + "weboap/visitor": "dev-master" + } +} +``` + +then run +``` php +php composer.phar update +``` + +## Step 2 + +Add +``` php +'Weboap\Visitor\VisitorServiceProvider' +``` + +to the list of service providers in app/config/app.php + +## Step 3 + +Migrate the Visitor Table +Run + +``` php +php artisan vendor:publish +``` +then + +``` php +php artisan migrate +``` +to migrate visitor table + +the config.php will be copied to /config at the same time + +``` php +/config/visitor.php +``` + +costumize it accordinly + + + +## Step 5 (Optional) + +Visit +http://dev.maxmind.com/geoip/geoip2/geolite2/ + +download GeoLite2-City.mmdb + +place it in (create the geo directory) + +``` php +storage/geo/ +``` +or where ever you want just adjust the package config to reflect the new location, +it's used to geo locate visitors + + + + +### Usage + + + +``` php + + +Visitor::log(); //log in db visitor ip, geo location, hit counter + + +Visitor::get(); +Visitor::get( $ip ); //fetch ip record + + + +Visitor::forget( $ip ); //delete ip from log + + +Visitor::has( $ip ); // checkk if visitor ip exist in log + + +Visitor::count() // return count of all site registred unique visitors + + +Visitor::all(); // all records + + +Visitor::clicks(); //total of all clicks + + +Visitor::range($date_start, $date_end); // visitors count in a date range; + + +``` +###Credits +This product Uses GeoLite2 data created by MaxMind, whenever available. + +Enjoy! + + + diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..0f73408 --- /dev/null +++ b/composer.json @@ -0,0 +1,27 @@ +{ + "name": "weboap/visitor", + "description": "log your visitors in db, page hits, and generate visit counter for Laravel 5", + "keywords": ["framework", "laravel","visitors", "counter", "log", "hits", "clicks"], + "license": "MIT", + "authors": [ + { + "name": "WeboAp", + "email": "weboap@gmail.com" + } + ], + "require": { + "php": ">=5.4.0", + "illuminate/support": "~5.0", + "jalle19/php-whitelist-check": "1.0.4", + "geoip2/geoip2": "2.1.1" + }, + "autoload": { + "psr-4": { + "Weboap\\Visitor\\": "src" + } + }, + "minimum-stability": "dev", + "require-dev": { + "phpspec/phpspec": "~2.1" + } +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..e89ac6d --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,18 @@ + + + + + ./tests/ + + + \ No newline at end of file diff --git a/src/Facades/VisitorFacade.php b/src/Facades/VisitorFacade.php new file mode 100644 index 0000000..f374efb --- /dev/null +++ b/src/Facades/VisitorFacade.php @@ -0,0 +1,14 @@ +request = $request; + $this->validators = $validators; + + } + + public function get() + { + $ip = $this->request->getClientIp(); + + if($ip == '::1') { + $ip = '127.0.0.1'; + } + + return $ip; + + } + + public function isValid( $ip = null ) + { + if( ! isset( $ip ) ) + { + return false; + } + + foreach ($this->validators as $validator) + { + if( ! $validator->validate( $ip ) ) return false; + } + + return true; + } + + + + + +} \ No newline at end of file diff --git a/src/Services/Cache/CacheClass.php b/src/Services/Cache/CacheClass.php new file mode 100644 index 0000000..52f42a4 --- /dev/null +++ b/src/Services/Cache/CacheClass.php @@ -0,0 +1,45 @@ +cache = $cache; + + } + + + + + public function destroy( $key ) + { + $this->cache->forget( $key ); + } + + + public function rememberForever( $key, $data ) + { + + return $this->cache->rememberForever( $key, function() use ( $data ) + { + return $data; + }); + } + + + + + + + + + + +} \ No newline at end of file diff --git a/src/Services/Cache/CacheInterface.php b/src/Services/Cache/CacheInterface.php new file mode 100644 index 0000000..2b8b1a0 --- /dev/null +++ b/src/Services/Cache/CacheInterface.php @@ -0,0 +1,11 @@ +config = $config; + $this->ip = $ip; + } + + + + public function locate() + { + // + $ip = $this->ip->get(); + $db = $this->config->get('visitor.maxmind_db_path'); + + if( !is_string($db) || ! file_exists( $db )|| ! $this->ip->isValid( $ip ) ) return []; + + + $this->reader = new Reader( $db ); + + try{ + + $record = $this->reader->city( $ip ); + + return [ + 'country_code' => $record->country->isoCode, + 'country_name' => $record->country->name, + 'state_code' => $record->mostSpecificSubdivision->isoCode, + 'state' => $record->mostSpecificSubdivision->name, + 'city' => $record->city->name, + 'postale_code' => $record->postal->code + + ]; + + } + catch (AddressNotFoundException $e) { + + return []; + }; + + } + + + + + + +} + diff --git a/src/Services/Validation/Checker.php b/src/Services/Validation/Checker.php new file mode 100644 index 0000000..787e261 --- /dev/null +++ b/src/Services/Validation/Checker.php @@ -0,0 +1,49 @@ +checker = $checker; + $this->config = $config; + + } + + public function validate( $ip ) + { + $list = $this->config->get('visitor::ignored'); + + if(! is_array($list)) $list = array(); + + try { + $this->checker->whitelist( $list ); + } + catch (InvalidArgumentException $e) { + + throw new InvalidArgumentException('invalid definition encountered in white list!'); + }; + /** + *if ip is in the ignored list return false mean dont register + * if ip is not in ignored list return true mean register + **/ + return ! $this->checker->check( $ip ); + + } + + + + + +} + + + +class InvalidArgumentException extends Exception {} \ No newline at end of file diff --git a/src/Services/Validation/ValidationInterface.php b/src/Services/Validation/ValidationInterface.php new file mode 100644 index 0000000..8a190f5 --- /dev/null +++ b/src/Services/Validation/ValidationInterface.php @@ -0,0 +1,9 @@ +_is_ip4( $ip ) || $this->_is_ipv6( $ip ) ) return true; + } + + + private function _is_ip4( $ip ) + { + return filter_var($ip, FILTER_VALIDATE_IP) !== false; + } + + private function _is_ipv6( $ip ) + { + return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)!== false; + } + +} diff --git a/src/Storage/QbVisitorRepository.php b/src/Storage/QbVisitorRepository.php new file mode 100644 index 0000000..cbeaae9 --- /dev/null +++ b/src/Storage/QbVisitorRepository.php @@ -0,0 +1,123 @@ +config = $config; + $this->db = $db; + $this->cache = $cache; +} + + +public function setTable($table) +{ + $this->tableName = $table; +} + +public function getTable() +{ + return isset( $this->tableName )? $this->tableName : $this->config->get('visitor.table'); + +} + +public function create( array $data) +{ + return $this->db->table( $this->getTable() )->insert($data); + +} + +public function get($ip) +{ + return $this->db->table( $this->getTable() )->whereIp( $ip )->first(); +} + +public function update( $ip, array $data) +{ + return $this->db->table( $this->getTable() )->whereIp( $ip )->update( $data ); +} + +public function delete( $ip ) +{ + return $this->db->table( $this->getTable() )->whereIp( $ip )->delete(); + +} + +public function all() +{ + // Query the database and cache it forever + + return $this->cache->rememberForever( $this->tableName , $this->db->table( $this->getTable() )->get() ); + + +} + + + +public function count( $ip = null ) +{ + if( ! isset( $ip ) ) + { + return $this->db->table( $this->getTable() )->count(); + } + else + { + return $this->db->table( $this->getTable() )->whereIp( $ip )->count(); + } +} + + +public function increment( $ip ) +{ + $this->db->table( $this->getTable() )->whereIp( $ip )->increment('clicks'); +} + +public function clicksSum() +{ + return $this->db->table( $this->getTable() )->sum('clicks'); + +} + +public function range($start, $end) +{ + return $this->db->table( $this->getTable() )->whereBetween('created_at', array($start, $end))->count(); +} + + + +} \ No newline at end of file diff --git a/src/Storage/VisitorInterface.php b/src/Storage/VisitorInterface.php new file mode 100644 index 0000000..77f8584 --- /dev/null +++ b/src/Storage/VisitorInterface.php @@ -0,0 +1,25 @@ +storage = $storage; + $this->geo = $geo; + $this->ip = $ip; + $this->config = $config; + $this->cache = $cache; + + + $this->tableName = $this->config->get('visitor.table'); + + } + + + /** + * @param null $ip + * @return null + */ + public function get( $ip = null ) + { + if( ! isset( $ip ) ) + { + $ip = $this->ip->get(); + } + + if( $this->ip->isValid( $ip ) ) + { + return $this->storage->get( $ip ); + } + + return null; + } + + + /** + * + */ + public function log() + { + + $ip = $this->ip->get(); + + if( ! $this->ip->isValid( $ip ) ) return; + + + if( $this->has( $ip ) ) + { + //ip already exist in db. + $this->storage->increment( $ip ); + + } + else + { + $geo = $this->geo->locate( $ip ); + + $country = array_key_exists('country_code', $geo) ? $geo['country_code'] : null; + + + //ip doesnt exist in db + $data = array( + 'ip' => $ip, + 'country' => $country, + 'clicks' => 1, + 'updated_at' => c::now(), + 'created_at' => c::now() + ); + $this->storage->create( $data ); + + } + + // Clear the database cache + $this->cache->destroy( $this->tableName ); + + + + } + + + /** + * @param $ip + */ + public function forget( $ip ) + { + if( ! $this->ip->isValid( $ip ) ) return; + + //delete the ip from db + $this->storage->delete( $ip ); + + // Clear the database cache + $this->cache->destroy( $this->tableName ); + + + } + + + /** + * @param $ip + * @return bool + */ + public function has( $ip ) + { + if( ! $this->ip->isValid( $ip ) ) return false; + + return $this->count( $ip ) > 0 ; + + } + + /** + * @param null $ip + * @return mixed + */ + public function count( $ip = null) + { + //if ip null then return count of all visits + return $this->storage->count( $ip ); + } + + /** + * @return mixed + */ + public function all() + { + return $this->storage->all(); + } + + /** + * @return mixed + */ + public function clicks() + { + return $this->storage->clicksSum(); + } + + /** + * @param $start + * @param $end + * @return mixed + */ + public function range($start, $end) + { + $start = date( 'Y-m-d H:i:s', strtotime( $start )); + $end = date( 'Y-m-d 23:59:59', strtotime( $end )); + + + return $this->storage->range($start, $end); + } + + + + + + + + +} + diff --git a/src/VisitorServiceProvider.php b/src/VisitorServiceProvider.php new file mode 100644 index 0000000..95c9889 --- /dev/null +++ b/src/VisitorServiceProvider.php @@ -0,0 +1,132 @@ +publishes([ + realpath(__DIR__.'/migrations') => base_path('/database/migrations') ], + 'migrations'); + + $this->publishes([ + __DIR__.'/config/visitor.php' => config_path('visitor.php'), + ]); + + + } + + /** + * Register the service provider. + * + * @return void + */ + public function register() + { + $this->registerBindings(); + + $this->RegisterIp(); + + $this->RegisterVisitor(); + + $this->RegisterBooting(); + } + + public function RegisterVisitor() + { + $this->app->singleton('visitor', function($app) + { + + + return new Visitor( + $app->make('Weboap\Visitor\Storage\VisitorInterface'), + $app->make('Weboap\Visitor\Services\Geo\GeoInterface'), + $app['ip'], + $app->make('config'), + $app->make('Weboap\Visitor\Services\Cache\CacheInterface') + + ); + }); + + $this->app->bind('Weboap\Visitor\Visitor', function($app) { + return $app['visitor']; + }); + + } + + + public function RegisterIp() + { + $this->app->singleton('ip', function($app) + { + return new Ip( + $app->make('request'), + array( + $app->make('Weboap\Visitor\Services\Validation\Validator'), + $app->make('Weboap\Visitor\Services\Validation\Checker') + ) + + ); + }); + + } + + + + + public function registerBooting() + { + $this->app->booting(function() + { + $loader = \Illuminate\Foundation\AliasLoader::getInstance(); + $loader->alias('Visitor', 'Weboap\Visitor\Facades\VisitorFacade'); + + + }); + } + + + + protected function registerBindings() + { + $this->app->singleton( + 'Weboap\Visitor\Storage\VisitorInterface', + 'Weboap\Visitor\Storage\QbVisitorRepository' + ); + + $this->app->singleton( + 'Weboap\Visitor\Services\Geo\GeoInterface', + 'Weboap\Visitor\Services\Geo\MaxMind' + ); + + $this->app->singleton( + 'Weboap\Visitor\Services\Cache\CacheInterface', + 'Weboap\Visitor\Services\Cache\CacheClass' + ); + } + + + + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides() + { + return array('visitor'); + } + +} diff --git a/src/config/visitor.php b/src/config/visitor.php new file mode 100644 index 0000000..a33a2e3 --- /dev/null +++ b/src/config/visitor.php @@ -0,0 +1,22 @@ + 'visitor_registry', + + 'ignored' => array( + //'172.16.10.0/24', + '192.168.10.0/24', + '10.0.3.1', + '10.0.0.0/16', + '2001:14b8:100:934b::3:1', + '2001:14b8:100:934b::/64', + // '*.example.com', + 'localhost' + ), + + 'maxmind_db_path' => storage_path().'/geo/GeoLite2-City.mmdb', + + + + ]; \ No newline at end of file diff --git a/src/migrations/.gitkeep b/src/migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/migrations/2014_02_09_225721_create_visitor_registry.php b/src/migrations/2014_02_09_225721_create_visitor_registry.php new file mode 100644 index 0000000..80b2125 --- /dev/null +++ b/src/migrations/2014_02_09_225721_create_visitor_registry.php @@ -0,0 +1,35 @@ +increments('id'); + $table->string('ip', 32); + $table->string('country', 4)->nullable(); + $table->integer('clicks')->unsigned()->default(0); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('visitor_registry'); + } + +}