store[$key])) return ''; else return $this->store[$key]; } public function set($key, $value){ $this->store[$key] = $value; } } class file_store { private $store = array(); function __construct() { $tmp_dir = sys_get_temp_dir(); $this->filename = "$tmp_dir/bablic_snippet"; if(file_exists($this->filename)){ $str = file_get_contents($this->filename); $this->store = json_decode($str, true); } } public function get($key){ if (empty($this->store[$key])) return ''; else return $this->store[$key]; } public function set($key, $value){ $this->store[$key] = $value; $file = fopen($this->filename, "w"); if($file){ $str = json_encode($this->store); fwrite($file, $str); fclose($file); } } } class BablicSDK { public $site_id = ''; private $save_flag = true; private $done = false; private $subdir = false; private $subdir_base = ''; private $url = ''; private $nocache = false; private $nourl = false; public $access_token = ''; private $channel_id = ''; private $version = ''; private $snippet = ''; private $meta = ''; public $trial_started = false; private $_body = ''; private $pos = 0; private $timestamp = 0; private $use_snippet_url = false; private $dont_add_sub_dir_base = false; private $bablic_base = 'https://www.bablic.com'; private $bablic_seo_base = 'http://seo.bablic.com'; private $_locale = ''; private $folders = array(); function __construct($options) { if (empty($options['channel_id'])){ $options['channel_id'] = 'php'; } if(!empty($options['test']) && $options['test']){ $this->bablic_base = 'http://staging.bablic.com'; $this->bablic_seo_base = 'http://staging.bablic.com'; } $this->channel_id = $options['channel_id']; if(!empty($options['store'])) $this->store = $options['store']; else $this->store = new file_store(); if ($this->store->get('site_id') != '') $this->get_data_from_store(); if(!empty($options['site_id'])){ $this->site_id = $options['site_id']; if($this->store->get('site_id') != $this->site_id) $this->get_site_from_bablic(); else if(!empty($_SERVER['HTTP_X_BABLIC_REFRESH'])) $this->get_site_from_bablic(); } if($this->site_id){ try{ header('x-bablic-id: '.$this->site_id); } catch(Exception $e){ } } if($this->site_id && (empty($this->timestamp) || ((time() - $this->timestamp) > 12000))){ $this->timestamp = time(); $this->get_site_from_bablic(); } if(!empty($options['subdir']) && $this->meta){ $meta = json_decode($this->meta, true); $locale_keys = $meta['localeKeys']; if(count($locale_keys) > 0){ $this->subdir = $options['subdir']; if(!empty($options['subdir_base'])) $this->subdir_base = $options['subdir_base']; } } if(isset($options['use_snippet_url'])) $this->use_snippet_url = true; if (!empty($options['folders'])) { $this->folders = $options['folders']; } if(isset($options['dont_add_sub_dir_base'])) $this->dont_add_sub_dir_base = true; } private function getFolder($locale) { foreach ($this->folders as $folder => $l) { if ($l == $locale) { return $folder; } } $locale = substr($locale, 0, 2); foreach ($this->folders as $folder => $l) { if (substr($l, 0, 2) == $locale) { return $folder; } } return ''; } private function getLocaleFromFolder($folderLocale, $locales) { foreach ($locales as $l) { if ($l == $folderLocale) { return $l; } } $folderLocale = substr($folderLocale, 0, 2); foreach ($locales as $l) { if (substr($l, 0, 2) == $folderLocale) { return $l; } } return $folderLocale; } private function save_data_to_store(){ $this->store->set('meta', $this->meta); $this->store->set('access_token', $this->access_token); $this->store->set('version', $this->version); $this->store->set('trial_started', $this->trial_started?'1':''); $this->store->set('snippet', $this->snippet); $this->store->set('site_id', $this->site_id); $this->store->set('time',$this->timestamp); } public function clear_data(){ $this->site_id = ''; $this->version = ''; $this->trial_started = false; $this->snippet = ''; $this->meta = ''; $this->access_token = ''; $this->save_data_to_store(); } private function get_data_from_store() { $this->site_id = $this->store->get('site_id'); $this->version = $this->store->get('version'); $this->trial_started = $this->store->get('trial_started') == '1'; $this->meta = $this->store->get('meta'); $this->snippet = $this->store->get('snippet'); $this->access_token = $this->store->get('access_token'); $this->timestamp = $this->store->get('time'); } public function set_site($site,$callback=''){ if(empty($site['id'])) die('No site id'); $this->site_id = $site['id']; if($this->access_token == '') $this->access_token = isset($site['access_token']) ? $site['access_token'] : ''; $this->get_site_from_bablic(); if($callback == '') return; $url = $this->bablic_base . "/api/v1/site/".$site['id']."?access_token=$this->access_token&channel_id=$this->channel_id"; $payload = array( 'callback' => $callback ); $ch = curl_init($url); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($ch, CURLOPT_HEADER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-type: application/json","X-HTTP-Method-Override:PUT","Expect:")); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); $result = curl_exec($ch); $result = json_decode($result, true); if (!empty($result['error'])) { return array("error" => "Bablic returned error"); } } public function create_site($options) { $url = $this->bablic_base . "/api/v1/site?channel_id=$this->channel_id"; $payload = array( 'url' => $options['site_url'], 'email'=> isset($options['email']) ? $options['email'] : '', 'original' => isset($options['original_locale']) ? $options['original_locale'] : '', 'callback' => isset($options['callback']) ? $options['callback'] : '', ); $ch = curl_init($url); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($ch, CURLOPT_HEADER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-type: application/json","Expect:")); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); $result = curl_exec($ch); $result = json_decode($result, true); if (!empty($result['error'])) { return array("error" => "Bablic returned error"); } $this->access_token = $result['access_token']; $this->site_id = $result['id']; $this->snippet = $this->use_snippet_url ? $result['snippetURL'] : $result['snippet']; $this->version = $result['version']; $this->trial_started = false; $this->meta = json_encode($result['meta']); $this->save_data_to_store(); } public function get_site_from_bablic() { $url = $this->bablic_base . "/api/v1/site/$this->site_id?access_token=$this->access_token&channel_id=$this->channel_id"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $result = curl_exec($ch); $result = json_decode($result, true); if (!empty($result['error'])) { if(!empty($result['error']['code']) && $result['error']['code'] == 410){ $this->clear_data(); $this->save_data_to_store(); return array("error" => "Site removed"); } return array("error" => "Bablic returned error"); } if(!empty($result['access_token'])) $this->access_token = $result['access_token']; $this->site_id = $result['id']; $this->snippet = $this->use_snippet_url ? $result['snippetURL'] : $result['snippet']; $this->version = $result['version']; $this->trial_started = $result['trialStarted']; $this->meta = json_encode($result['meta']); $this->timestamp = time(); $this->save_data_to_store(); } public function refresh_site(){ $this->get_site_from_bablic(); } public function clear_cache(){ $tmp_dir = sys_get_temp_dir(); $folder = "$tmp_dir/bablic_cache"; if (!file_exists($folder)){ return; } array_map('unlink', glob("$folder/*")); } public function get_meta() { return $this->meta; } public function get_site(){ return array ( "meta" => $this->meta, "access_token" => $this->access_token, "site_id" => $this->site_id, "version" => $this->version, "trial_started" => $this->trial_started, "snippet" => $this->snippet ); } public function get_snippet($async) { $snippet = $this->snippet; if($async) { $snippet = str_replace(''.$snippet; } return $snippet; } public function get_bablic_top(){ return ''.$this->get_alt_tags().$this->get_snippet($this->get_locale() == $this->get_original()) . ''; } public function bablic_top(){ echo ''; $this->alt_tags(); echo $this->get_snippet($this->get_locale() == $this->get_original()); echo ''; } public function get_alt_tags(){ $meta = json_decode($this->meta, true); $locale_keys = $meta['localeKeys']; $locale = $this->get_locale(); $url = $_SERVER['REQUEST_URI']; $str = ''; if(is_array($locale_keys)){ foreach( $locale_keys as $alt){ if($alt != $locale) { $parts = explode('_', $alt); $iso = sizeof($parts) > 1 ? $parts[0].'-'.strtoupper($parts[1]) : $parts[0]; array_push($res, array($this->get_link($alt, $url), $iso)); } } if($locale != $meta['original']) $str .= ''; } return $str; } public function alt_tags(){ $meta = json_decode($this->meta, true); $locale_keys = $meta['localeKeys']; $locale = $this->get_locale(); $url = $_SERVER['REQUEST_URI']; if(is_array($locale_keys)){ foreach( $locale_keys as $alt){ if($alt != $locale) { $parts = explode('_', $alt); $iso = sizeof($parts) > 1 ? $parts[0].'-'.strtoupper($parts[1]) : $parts[0]; echo ''; } } if($locale != $meta['original']) echo ''; } } private function get_all_headers() { $headers = array(); foreach($_SERVER as $key => $value) { if (substr($key, 0, 5) != 'HTTP_') { continue; } $header = str_replace(' ', '-', ucwords(str_replace('_', ' ', strtolower(substr($key, 5))))); $headers[$header] = $value; } return $headers; } public function detect_locale_from_header() { $headers = $this->get_all_headers(); if(!isset($headers['Accept-Language'])) return false; $lang = explode(',', $headers['Accept-Language']); if (!empty($lang)) return $lang[0]; return false; } public function detect_locale_from_cookie($allowed_keys) { if (!empty($_COOKIE) && !empty($_COOKIE['bab_locale']) && !empty($allowed_keys)){ $cookie_locale = $_COOKIE['bab_locale']; $match = false; foreach ($allowed_keys as &$value) { if ($value === $cookie_locale) $match = true; if (!$match) if (substr($value,0,2) === substr($cookie_locale,0,2)) $match = true; } if ($match) return $cookie_locale; } return false; } public function get_link($locale, $url) { if($url[0] != '/' && $url[0] != 'h') return $url; $parsed = parse_url($url); $scheme = isset($parsed['scheme']) ? $parsed['scheme'] . '://' : ''; $host = isset($parsed['host']) ? $parsed['host'] : ''; $port = isset($parsed['port']) ? ':' . $parsed['port'] : ''; $path = isset($parsed['path']) ? $parsed['path'] : '/'; $query = isset($parsed['query']) ? '?' . $parsed['query'] : ''; $fragment = isset($parsed['fragment']) ? '#' . $parsed['fragment'] : ''; $meta = json_decode($this->meta, true); $link = 'javascript:void(0);'; $localeDetection = $meta['localeDetection']; if($this->subdir) $localeDetection = 'subdir'; if($localeDetection == 'custom' && empty($meta['customUrls'])) $localeDetection = 'querystring'; switch($localeDetection){ case 'custom': $custom_url = $meta['customUrls'][$locale]; if ($custom_url) { $scheme = $scheme == '' ? 'http://' : $scheme; if(strpos($custom_url, '/') !== false){ // custom contains querystring if(strpos($custom_url, '?') !== false) return $scheme.$custom_url.$fragment; // custom contains path return $scheme.$custom_url.$query.$fragment; } // custom is only domain return $scheme.$custom_url.$path.$query.$fragment; } break; case 'querystring': $query_locale = ''; if(!isset($parsed['query'])) return $scheme.$host.$port.$path.'?locale='.$locale.$fragment; $output = array(); parse_str(substr($query,1),$output); $output['locale'] = $locale; $query = http_build_query($output); return $scheme.$host.$port.$path.'?'.$query.$fragment; case 'subdir': if ($this->subdir_base != '') { $path = preg_replace('/^(?:'.preg_quote($this->subdir_base, '/').')?\//', '/', $path); } $folder_keys = array_keys($this->folders); $prefix = ''; if (sizeof($folder_keys) > 0) { $prefix = '/'.$this->getFolder($locale); $locale_keys = $folder_keys; } else{ $locale_keys = $meta['localeKeys']; if($locale != $meta['original']) $prefix = '/'.$locale; } $locale_regex = '('.implode('|', $locale_keys).')'; $path = preg_replace('/^\/?'.$locale_regex.'\//', '/', $path); return $scheme.$host.$port.$this->subdir_base.$prefix.$path.$query.$fragment; case 'hash': $fragment = '#locale_'.$locale; return $scheme.$host.$port.$path.$query.$fragment; } return $url; } public function get_original(){ if($this->meta == '') return null; $meta = json_decode($this->meta, true); return $meta['original']; } public function get_locales(){ if($this->meta == '') return array(); $meta = json_decode($this->meta, true); return $meta['localeKeys']; } private function get_locale_inner($is_bot){ if(!empty($_SERVER['HTTP_BABLIC_LOCALE'])) return $_SERVER['HTTP_BABLIC_LOCALE']; if($this->meta == '') return ''; $meta = json_decode($this->meta, true); $auto = $meta['autoDetect']; $default = $meta['default']; $custom_urls = $meta['customUrls']; $locale_keys = $meta['localeKeys']; if(!$locale_keys) $locale_keys = array(); array_push($locale_keys, $meta['original']); $locale_detection = $meta['localeDetection']; if($this->subdir) $locale_detection = 'subdir'; $detected = ''; if(!$is_bot && $auto && !empty($locale_keys)){ $detected_lang = $this->detect_locale_from_header(); $normalized_lang = strtolower(str_replace('-','_',$detected_lang)); foreach ($locale_keys as &$value) { if ($value === $normalized_lang){ $detected = $value; break; } if (substr($value,0,2) === substr($normalized_lang,0,2)){ $detected = $value; break; } } } $from_cookie = $is_bot ? false : $this->detect_locale_from_cookie($locale_keys); $url = $this->get_current_url(); $parsed_url = parse_url($url); switch ($locale_detection) { case 'querystring': if ((!empty($_GET)) && (!empty($_GET['locale']))) return $_GET['locale']; else if ($from_cookie) return $from_cookie; else if ($detected) return $detected; return $default; case 'subdir': $path = $parsed_url['path']; preg_match('/^(?:'.preg_quote($this->subdir_base, '/').")?(\/(\w\w(_\w\w)?))(?:\/|$)/", $path, $matches); if ($matches) { if (!empty($this->folders[$matches[2]])) { $folder = $this->folders[$matches[2]]; array_push($locale_keys, $meta['original']); return $this->getLocaleFromFolder($folder, $locale_keys); } return $matches[2]; } if ($from_cookie) { return $default; } if ($detected) { return $detected; } return $default; case 'custom': foreach ($custom_urls as $key => $value) { $pattern = $this->create_domain_regex($value); if (preg_match($pattern, $url, $matches)) return $key; } return $default; default: return $from_cookie; } return; } public function get_locale() { if($this->_locale != '') return $this->_locale; $locale = $this->get_locale_inner(false); $this->_locale = $locale; return $locale; } private function create_domain_regex($str){ $new_str = preg_replace("/([.?+^$[\]\\\/(){}|-])/", "\\\\$1", $str); return '/'.preg_replace("/\*/",'.*', $new_str).'/'; } public function editor_url() { return $this->bablic_base . "/channels/editor?site=$this->site_id&access_token=$this->access_token"; } public function remove_site(){ $url = $this->bablic_base . "/api/v1/site/$this->site_id?access_token=$this->access_token&channel_id=$this->channel_id"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE"); $result = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $this->clear_data(); curl_close($ch); } public function handle_request($options=array()) { if($this->site_id == '') return; if (($this->is_bot() == false) && (empty($options['debug']) || $options['debug'] == false)) return; if (!empty($options['url'])) $this->url = $options['url']; else $this->url = $this->get_current_url(); if ($this->ignorable($this->url)) return; if (!empty($options['nocache']) && $options['nocache'] == true) $this->nocache = true; if (!empty($options['nourl']) && $options['nourl'] == true) $this->nourl = true; if($this->meta){ $meta = json_decode($this->meta, true); $default = $meta['default']; $locale = $this->get_locale_inner(true); if ($default == $locale) return; $this->get_html_for_url($this->url); } else { $this->get_html_for_url($this->url); } } private function is_bot() { if(empty($_SERVER['HTTP_USER_AGENT'])) return false; $is_bot = '/bot|crawler|baiduspider|80legs|google|facebook|twitter|seo/i'; if(preg_match($is_bot, $_SERVER['HTTP_USER_AGENT'], $matches)) return true; return false; } private function get_current_url() { $protocol = 'http'; if ($_SERVER['SERVER_PORT'] == 443 || (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on')) $protocol .= 's'; $host = $_SERVER['HTTP_HOST']; $uri = $_SERVER['REQUEST_URI']; return "$protocol://$host$uri"; } public function ignorable($url) { $filename_tester = "/\.(js|css|jpg|jpeg|png|mp3|avi|mpeg|bmp|wav|pdf|doc|xml|docx|xlsx|xls|json|kml|svg|eot|woff|woff2)/"; return preg_match($filename_tester, $url, $matches); } public function process_buffer($buffer) { $headers = headers_list(); $rcode = 200; if(function_exists('http_response_code')) $rcode = http_response_code(); else $rcode = (isset($GLOBALS['http_response_code']) ? $GLOBALS['http_response_code'] : 200); if ($rcode < 200 || $rcode >= 300) return $buffer; if ($this->ignorable($this->get_current_url())) return $buffer; foreach ($headers as &$value) { $html_found = 0; $contenttype_found = 0; $html_found = strpos($value, "text/html;"); $contenttype_found = strpos($value, "Content-Type"); if ($html_found === false){ // do nothing }else { break; } } if (($html_found === false)&&($contenttype_found === 0)) return $buffer; $html = ob_get_contents(); $url = $this->url; $response = $this->send_to_bablic($url, $html); return $response; } private function filename_from_url($url) { return md5($url); } private function full_path_from_url($url) { $tmp_dir = sys_get_temp_dir(); $folder = "$tmp_dir/bablic_cache"; if (!file_exists($folder)){ mkdir($folder); } $filename = $this->filename_from_url($url); return "$folder/$filename"; } public function write_buffer($ch,$fp,$len){ $data = substr($this->_body, $this->pos, $len); // increment $pos $this->pos += strlen($data); // return the data to send in the request return $data; } private function send_to_bablic($url, $html) { $nourl_param = $this->nourl ? "&nourl=1" : ""; $bablic_url = $this->bablic_seo_base . "/api/engine/seo?site=$this->site_id".$nourl_param."&url=".urlencode($url).($this->subdir ? "&ld=subdir" : "").($this->subdir_base ? "&sdb=" .urlencode($this->subdir_base) : ""); $bablic_url .= '&folders='.urlencode(json_encode($this->folders)); if ($this->dont_add_sub_dir_base) $bablic_url .= '&dasdb=1'; $curl = curl_init($bablic_url); $length = strlen($html); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($curl, CURLOPT_HEADER, false); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-type: text/html", "Content-Length: $length","Expect:")); curl_setopt($curl, CURLOPT_POST, true); $this->_body = $html; $this->pos = 0; curl_setopt($curl, CURLOPT_READFUNCTION, array(&$this,'write_buffer')); $response = curl_exec($curl); $status = curl_getinfo($curl, CURLINFO_HTTP_CODE); if (($status != 200 ) && ($status != 301)) { return $html; } curl_close($curl); $this->save_html($response, $this->full_path_from_url($url)); return $response; } private function save_html($content, $filename) { $file = fopen($filename, "w") or die("Unable to open file!"); fwrite($file, $content); fclose($file); } public function noop() { return ''; } public function get_html_for_url($url) { $cached_file = $this->read_from_cache($this->full_path_from_url($url)); if ($cached_file){ exit; return; } ob_start(array(&$this, "process_buffer")); return; } private function read_from_cache($filename) { if ($this->nocache == true) return false; try{ $html_file = file_exists($filename); if ($html_file) { $file_modified = filemtime($filename); $now = time(); $validity = ($now - (2*24*60*60) < $file_modified); if ($validity === false) return false; readfile($filename); return true; } else { return false; } } catch(Exception $e){ return false; } } } ?>