1<?php 2/** 3 * Copyright since 2007 PrestaShop SA and Contributors 4 * PrestaShop is an International Registered Trademark & Property of PrestaShop SA 5 * 6 * NOTICE OF LICENSE 7 * 8 * This source file is subject to the Open Software License (OSL 3.0) 9 * that is bundled with this package in the file LICENSE.md. 10 * It is also available through the world-wide-web at this URL: 11 * https://opensource.org/licenses/OSL-3.0 12 * If you did not receive a copy of the license and are unable to 13 * obtain it through the world-wide-web, please send an email 14 * to license@prestashop.com so we can send you a copy immediately. 15 * 16 * DISCLAIMER 17 * 18 * Do not edit or add to this file if you wish to upgrade PrestaShop to newer 19 * versions in the future. If you wish to customize PrestaShop for your 20 * needs please refer to https://devdocs.prestashop.com/ for more information. 21 * 22 * @author PrestaShop SA and Contributors <contact@prestashop.com> 23 * @copyright Since 2007 PrestaShop SA and Contributors 24 * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) 25 */ 26 27/** 28 * Class ConnectionCore. 29 */ 30class ConnectionCore extends ObjectModel 31{ 32 /** @var int */ 33 public $id_guest; 34 35 /** @var int */ 36 public $id_page; 37 38 /** @var string */ 39 public $ip_address; 40 41 /** @var string */ 42 public $http_referer; 43 44 /** @var int */ 45 public $id_shop; 46 47 /** @var int */ 48 public $id_shop_group; 49 50 /** @var string */ 51 public $date_add; 52 53 /** 54 * @see ObjectModel::$definition 55 */ 56 public static $definition = [ 57 'table' => 'connections', 58 'primary' => 'id_connections', 59 'fields' => [ 60 'id_guest' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true], 61 'id_page' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true], 62 'ip_address' => ['type' => self::TYPE_INT, 'validate' => 'isInt'], 63 'http_referer' => ['type' => self::TYPE_STRING, 'validate' => 'isAbsoluteUrl'], 64 'id_shop' => ['type' => self::TYPE_INT, 'required' => true], 65 'id_shop_group' => ['type' => self::TYPE_INT, 'required' => true], 66 'date_add' => ['type' => self::TYPE_DATE, 'validate' => 'isDate'], 67 ], 68 ]; 69 70 /** 71 * @see ObjectModel::getFields() 72 * 73 * @return array 74 */ 75 public function getFields() 76 { 77 if (!$this->id_shop_group) { 78 $this->id_shop_group = Context::getContext()->shop->id_shop_group; 79 } 80 81 $fields = parent::getFields(); 82 83 return $fields; 84 } 85 86 /** 87 * @param Cookie $cookie 88 * @param bool $full 89 * 90 * @return array 91 */ 92 public static function setPageConnection($cookie, $full = true) 93 { 94 $idPage = false; 95 // The connection is created if it does not exist yet and we get the current page id 96 if (!isset($cookie->id_connections) || !isset($_SERVER['HTTP_REFERER']) || strstr($_SERVER['HTTP_REFERER'], Tools::getHttpHost(false, false) . '/') === false) { 97 $idPage = Connection::setNewConnection($cookie); 98 } 99 // If we do not track the pages, no need to get the page id 100 if (!Configuration::get('PS_STATSDATA_PAGESVIEWS') && !Configuration::get('PS_STATSDATA_CUSTOMER_PAGESVIEWS')) { 101 return []; 102 } 103 if (!$idPage) { 104 $idPage = Page::getCurrentId(); 105 } 106 // If we do not track the page views by customer, the id_page is the only information needed 107 if (!Configuration::get('PS_STATSDATA_CUSTOMER_PAGESVIEWS')) { 108 return ['id_page' => $idPage]; 109 } 110 111 // The ending time will be updated by an ajax request when the guest will close the page 112 $timeStart = date('Y-m-d H:i:s'); 113 Db::getInstance()->insert( 114 'connections_page', 115 [ 116 'id_connections' => (int) $cookie->id_connections, 117 'id_page' => (int) $idPage, 118 'time_start' => $timeStart, 119 ], 120 false, 121 true, 122 Db::INSERT_IGNORE 123 ); 124 125 // This array is serialized and used by the ajax request to identify the page 126 return [ 127 'id_connections' => (int) $cookie->id_connections, 128 'id_page' => (int) $idPage, 129 'time_start' => $timeStart, 130 ]; 131 } 132 133 /** 134 * @param Cookie $cookie 135 * 136 * @return int|bool Connection ID 137 * `false` if failure 138 */ 139 public static function setNewConnection($cookie) 140 { 141 if (isset($_SERVER['HTTP_USER_AGENT']) 142 && preg_match('/BotLink|ahoy|AlkalineBOT|anthill|appie|arale|araneo|AraybOt|ariadne|arks|ATN_Worldwide|Atomz|bbot|Bjaaland|Ukonline|borg\-bot\/0\.9|boxseabot|bspider|calif|christcrawler|CMC\/0\.01|combine|confuzzledbot|CoolBot|cosmos|Internet Cruiser Robot|cusco|cyberspyder|cydralspider|desertrealm, desert realm|digger|DIIbot|grabber|downloadexpress|DragonBot|dwcp|ecollector|ebiness|elfinbot|esculapio|esther|fastcrawler|FDSE|FELIX IDE|ESI|fido|H�m�h�kki|KIT\-Fireball|fouineur|Freecrawl|gammaSpider|gazz|gcreep|golem|googlebot|griffon|Gromit|gulliver|gulper|hambot|havIndex|hotwired|htdig|iajabot|INGRID\/0\.1|Informant|InfoSpiders|inspectorwww|irobot|Iron33|JBot|jcrawler|Teoma|Jeeves|jobo|image\.kapsi\.net|KDD\-Explorer|ko_yappo_robot|label\-grabber|larbin|legs|Linkidator|linkwalker|Lockon|logo_gif_crawler|marvin|mattie|mediafox|MerzScope|NEC\-MeshExplorer|MindCrawler|udmsearch|moget|Motor|msnbot|muncher|muninn|MuscatFerret|MwdSearch|sharp\-info\-agent|WebMechanic|NetScoop|newscan\-online|ObjectsSearch|Occam|Orbsearch\/1\.0|packrat|pageboy|ParaSite|patric|pegasus|perlcrawler|phpdig|piltdownman|Pimptrain|pjspider|PlumtreeWebAccessor|PortalBSpider|psbot|Getterrobo\-Plus|Raven|RHCS|RixBot|roadrunner|Robbie|robi|RoboCrawl|robofox|Scooter|Search\-AU|searchprocess|Senrigan|Shagseeker|sift|SimBot|Site Valet|skymob|SLCrawler\/2\.0|slurp|ESI|snooper|solbot|speedy|spider_monkey|SpiderBot\/1\.0|spiderline|nil|suke|http:\/\/www\.sygol\.com|tach_bw|TechBOT|templeton|titin|topiclink|UdmSearch|urlck|Valkyrie libwww\-perl|verticrawl|Victoria|void\-bot|Voyager|VWbot_K|crawlpaper|wapspider|WebBandit\/1\.0|webcatcher|T\-H\-U\-N\-D\-E\-R\-S\-T\-O\-N\-E|WebMoose|webquest|webreaper|webs|webspider|WebWalker|wget|winona|whowhere|wlm|WOLP|WWWC|none|XGET|Nederland\.zoek|AISearchBot|woriobot|NetSeer|Nutch|YandexBot/i', $_SERVER['HTTP_USER_AGENT'])) { 143 // This is a bot : don't log connection 144 return false; 145 } 146 147 // A new connection is created if the guest made no actions during 30 minutes 148 $sql = 'SELECT SQL_NO_CACHE `id_guest` 149 FROM `' . _DB_PREFIX_ . 'connections` 150 WHERE `id_guest` = ' . (int) $cookie->id_guest . ' 151 AND `date_add` > \'' . pSQL(date('Y-m-d H:i:00', time() - 1800)) . '\' 152 ' . Shop::addSqlRestriction(Shop::SHARE_CUSTOMER) . ' 153 ORDER BY `date_add` DESC'; 154 $result = Db::getInstance()->getRow($sql, false); 155 if (empty($result['id_guest']) && (int) $cookie->id_guest) { 156 // The old connections details are removed from the database in order to spare some memory 157 Connection::cleanConnectionsPages(); 158 159 $referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''; 160 $arrayUrl = parse_url($referer); 161 if (!isset($arrayUrl['host']) || preg_replace('/^www./', '', $arrayUrl['host']) == preg_replace('/^www./', '', Tools::getHttpHost(false, false))) { 162 $referer = ''; 163 } 164 $connection = new Connection(); 165 $connection->id_guest = (int) $cookie->id_guest; 166 $connection->id_page = Page::getCurrentId(); 167 $connection->ip_address = Tools::getRemoteAddr() ? (int) ip2long(Tools::getRemoteAddr()) : ''; 168 $connection->id_shop = Context::getContext()->shop->id; 169 $connection->id_shop_group = Context::getContext()->shop->id_shop_group; 170 $connection->date_add = $cookie->date_add; 171 if (Validate::isAbsoluteUrl($referer)) { 172 $connection->http_referer = substr($referer, 0, 254); 173 } 174 $connection->add(); 175 $cookie->id_connections = $connection->id; 176 177 return $connection->id_page; 178 } 179 180 return false; 181 } 182 183 /** 184 * @param int $idConnections 185 * @param int $idPage 186 * @param string $timeStart 187 * @param int $time 188 */ 189 public static function setPageTime($idConnections, $idPage, $timeStart, $time) 190 { 191 if (!Validate::isUnsignedId($idConnections) 192 || !Validate::isUnsignedId($idPage) 193 || !Validate::isDate($timeStart)) { 194 return; 195 } 196 197 // Limited to 5 minutes because more than 5 minutes is considered as an error 198 if ($time > 300000) { 199 $time = 300000; 200 } 201 Db::getInstance()->execute(' 202 UPDATE `' . _DB_PREFIX_ . 'connections_page` 203 SET `time_end` = `time_start` + INTERVAL ' . (int) ($time / 1000) . ' SECOND 204 WHERE `id_connections` = ' . (int) $idConnections . ' 205 AND `id_page` = ' . (int) $idPage . ' 206 AND `time_start` = \'' . pSQL($timeStart) . '\''); 207 } 208 209 /** 210 * Clean connections page. 211 */ 212 public static function cleanConnectionsPages() 213 { 214 $period = Configuration::get('PS_STATS_OLD_CONNECT_AUTO_CLEAN'); 215 216 if ($period === 'week') { 217 $interval = '1 WEEK'; 218 } elseif ($period === 'month') { 219 $interval = '1 MONTH'; 220 } elseif ($period === 'year') { 221 $interval = '1 YEAR'; 222 } else { 223 return; 224 } 225 226 if ($interval != null) { 227 // Records of connections details older than the beginning of the specified interval are deleted 228 Db::getInstance()->execute(' 229 DELETE FROM `' . _DB_PREFIX_ . 'connections_page` 230 WHERE time_start < LAST_DAY(DATE_SUB(NOW(), INTERVAL ' . $interval . '))'); 231 } 232 } 233} 234