source: trunk/FACT++/www/smartfact/log/Browscap.php@ 16885

Last change on this file since 16885 was 14142, checked in by tbretz, 12 years ago
Browscap.php from https://github.com/GaretJax/phpbrowscap (will need update from time to time)
File size: 26.6 KB
Line 
1<?php
2
3namespace phpbrowscap;
4
5use \Exception as BaseException;
6
7/**
8 * Browscap.ini parsing class with caching and update capabilities
9 *
10 * PHP version 5
11 *
12 * Copyright (c) 2006-2012 Jonathan Stoppani
13 *
14 * Permission is hereby granted, free of charge, to any person obtaining a
15 * copy of this software and associated documentation files (the "Software"),
16 * to deal in the Software without restriction, including without limitation
17 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 * and/or sell copies of the Software, and to permit persons to whom the
19 * Software is furnished to do so, subject to the following conditions:
20 *
21 * The above copyright notice and this permission notice shall be included
22 * in all copies or substantial portions of the Software.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30 * THE SOFTWARE.
31 *
32 * @package Browscap
33 * @author Jonathan Stoppani <jonathan@stoppani.name>
34 * @author Vítor Brandão <noisebleed@noiselabs.org>
35 * @copyright Copyright (c) 2006-2012 Jonathan Stoppani
36 * @version 1.0
37 * @license http://www.opensource.org/licenses/MIT MIT License
38 * @link https://github.com/GaretJax/phpbrowscap/
39 */
40class Browscap
41{
42 /**
43 * Current version of the class.
44 */
45 const VERSION = '1.0';
46
47 /**
48 * Different ways to access remote and local files.
49 *
50 * UPDATE_FOPEN: Uses the fopen url wrapper (use file_get_contents).
51 * UPDATE_FSOCKOPEN: Uses the socket functions (fsockopen).
52 * UPDATE_CURL: Uses the cURL extension.
53 * UPDATE_LOCAL: Updates from a local file (file_get_contents).
54 */
55 const UPDATE_FOPEN = 'URL-wrapper';
56 const UPDATE_FSOCKOPEN = 'socket';
57 const UPDATE_CURL = 'cURL';
58 const UPDATE_LOCAL = 'local';
59
60 /**
61 * Options for regex patterns.
62 *
63 * REGEX_DELIMITER: Delimiter of all the regex patterns in the whole class.
64 * REGEX_MODIFIERS: Regex modifiers.
65 */
66 const REGEX_DELIMITER = '@';
67 const REGEX_MODIFIERS = 'i';
68
69 /**
70 * The values to quote in the ini file
71 */
72 const VALUES_TO_QUOTE = 'Browser|Parent';
73
74 /**
75 * Definitions of the function used by the uasort() function to order the
76 * userAgents array.
77 *
78 * ORDER_FUNC_ARGS: Arguments that the function will take.
79 * ORDER_FUNC_LOGIC: Internal logic of the function.
80 */
81 const ORDER_FUNC_ARGS = '$a, $b';
82 const ORDER_FUNC_LOGIC = '$a=strlen($a);$b=strlen($b);return$a==$b?0:($a<$b?1:-1);';
83
84 /**
85 * The headers to be sent for checking the version and requesting the file.
86 */
87 const REQUEST_HEADERS = "GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: %s\r\nConnection: Close\r\n\r\n";
88
89 /**
90 * Options for auto update capabilities
91 *
92 * $remoteVerUrl: The location to use to check out if a new version of the
93 * browscap.ini file is available.
94 * $remoteIniUrl: The location from which download the ini file.
95 * The placeholder for the file should be represented by a %s.
96 * $timeout: The timeout for the requests.
97 * $updateInterval: The update interval in seconds.
98 * $errorInterval: The next update interval in seconds in case of an error.
99 * $doAutoUpdate: Flag to disable the automatic interval based update.
100 * $updateMethod: The method to use to update the file, has to be a value of
101 * an UPDATE_* constant, null or false.
102 */
103 public $remoteIniUrl = 'http://browsers.garykeith.com/stream.asp?BrowsCapINI';
104 public $remoteVerUrl = 'http://browsers.garykeith.com/versions/version-date.asp';
105 public $timeout = 5;
106 public $updateInterval = 432000; // 5 days
107 public $errorInterval = 7200; // 2 hours
108 public $doAutoUpdate = true;
109 public $updateMethod = null;
110
111 /**
112 * The path of the local version of the browscap.ini file from which to
113 * update (to be set only if used).
114 *
115 * @var string
116 */
117 public $localFile = null;
118
119 /**
120 * The useragent to include in the requests made by the class during the
121 * update process.
122 *
123 * @var string
124 */
125 public $userAgent = 'Browser Capabilities Project - PHP Browscap/%v %m';
126
127 /**
128 * Flag to enable only lowercase indexes in the result.
129 * The cache has to be rebuilt in order to apply this option.
130 *
131 * @var bool
132 */
133 public $lowercase = false;
134
135 /**
136 * Flag to enable/disable silent error management.
137 * In case of an error during the update process the class returns an empty
138 * array/object if the update process can't take place and the browscap.ini
139 * file does not exist.
140 *
141 * @var bool
142 */
143 public $silent = false;
144
145 /**
146 * Where to store the cached PHP arrays.
147 *
148 * @var string
149 */
150 public $cacheFilename = 'cache.php';
151
152 /**
153 * Where to store the downloaded ini file.
154 *
155 * @var string
156 */
157 public $iniFilename = 'browscap.ini';
158
159 /**
160 * Path to the cache directory
161 *
162 * @var string
163 */
164 public $cacheDir = null;
165
166 /**
167 * Flag to be set to true after loading the cache
168 *
169 * @var bool
170 */
171 protected $_cacheLoaded = false;
172
173 /**
174 * Where to store the value of the included PHP cache file
175 *
176 * @var array
177 */
178 protected $_userAgents = array();
179 protected $_browsers = array();
180 protected $_patterns = array();
181 protected $_properties = array();
182
183 /**
184 * An associative array of associative arrays in the format
185 * `$arr['wrapper']['option'] = $value` passed to stream_context_create()
186 * when building a stream resource.
187 *
188 * Proxy settings are stored in this variable.
189 *
190 * @see http://www.php.net/manual/en/function.stream-context-create.php
191 *
192 * @var array
193 */
194 protected $_streamContextOptions = array();
195
196 /**
197 * A valid context resource created with stream_context_create().
198 *
199 * @see http://www.php.net/manual/en/function.stream-context-create.php
200 *
201 * @var resource
202 */
203 protected $_streamContext = null;
204
205 /**
206 * Constructor class, checks for the existence of (and loads) the cache and
207 * if needed updated the definitions
208 *
209 * @param string $cache_dir
210 */
211 public function __construct($cache_dir)
212 {
213 // has to be set to reach E_STRICT compatibility, does not affect system/app settings
214 date_default_timezone_set(date_default_timezone_get());
215
216 if (!isset($cache_dir)) {
217 throw new Exception(
218 'You have to provide a path to read/store the browscap cache file'
219 );
220 }
221
222 $old_cache_dir = $cache_dir;
223 $cache_dir = realpath($cache_dir);
224
225 if (false === $cache_dir) {
226 throw new Exception(
227 sprintf('The cache path %s is invalid. Are you sure that it exists and that you have permission to access it?', $old_cache_dir)
228 );
229 }
230
231 // Is the cache dir really the directory or is it directly the file?
232 if (substr($cache_dir, -4) === '.php') {
233 $this->cacheFilename = basename($cache_dir);
234 $this->cacheDir = dirname($cache_dir);
235 } else {
236 $this->cacheDir = $cache_dir;
237 }
238
239 $this->cacheDir .= DIRECTORY_SEPARATOR;
240 }
241
242 /**
243 * Gets the information about the browser by User Agent
244 *
245 * @param string $user_agent the user agent string
246 * @param bool $return_array whether return an array or an object
247 * @throws Exception
248 * @return stdObject the object containing the browsers details. Array if
249 * $return_array is set to true.
250 */
251 public function getBrowser($user_agent = null, $return_array = false)
252 {
253 // Load the cache at the first request
254 if (!$this->_cacheLoaded) {
255 $cache_file = $this->cacheDir . $this->cacheFilename;
256 $ini_file = $this->cacheDir . $this->iniFilename;
257
258 // Set the interval only if needed
259 if ($this->doAutoUpdate && file_exists($ini_file)) {
260 $interval = time() - filemtime($ini_file);
261 } else {
262 $interval = 0;
263 }
264
265 // Find out if the cache needs to be updated
266 if (!file_exists($cache_file) || !file_exists($ini_file) || ($interval > $this->updateInterval)) {
267 try {
268 $this->updateCache();
269 } catch (Exception $e) {
270 if (file_exists($ini_file)) {
271 // Adjust the filemtime to the $errorInterval
272 touch($ini_file, time() - $this->updateInterval + $this->errorInterval);
273 } elseif ($this->silent) {
274 // Return an array if silent mode is active and the ini db doesn't exsist
275 return array();
276 }
277
278 if (!$this->silent) {
279 throw $e;
280 }
281 }
282 }
283
284 $this->_loadCache($cache_file);
285 }
286
287 // Automatically detect the useragent
288 if (!isset($user_agent)) {
289 if (isset($_SERVER['HTTP_USER_AGENT'])) {
290 $user_agent = $_SERVER['HTTP_USER_AGENT'];
291 } else {
292 $user_agent = '';
293 }
294 }
295
296 $browser = array();
297 foreach ($this->_patterns as $key => $pattern) {
298 if (preg_match($pattern . 'i', $user_agent)) {
299 $browser = array(
300 $user_agent, // Original useragent
301 trim(strtolower($pattern), self::REGEX_DELIMITER),
302 $this->_userAgents[$key]
303 );
304
305 $browser = $value = $browser + $this->_browsers[$key];
306
307 while (array_key_exists(3, $value) && $value[3]) {
308 $value = $this->_browsers[$value[3]];
309 $browser += $value;
310 }
311
312 if (!empty($browser[3])) {
313 $browser[3] = $this->_userAgents[$browser[3]];
314 }
315
316 break;
317 }
318 }
319
320 // Add the keys for each property
321 $array = array();
322 foreach ($browser as $key => $value) {
323 if ($value === 'true') {
324 $value = true;
325 } elseif ($value === 'false') {
326 $value = false;
327 }
328 $array[$this->_properties[$key]] = $value;
329 }
330
331 return $return_array ? $array : (object) $array;
332 }
333
334 /**
335 * Load (auto-set) proxy settings from environment variables.
336 */
337 public function autodetectProxySettings()
338 {
339 $wrappers = array('http', 'https', 'ftp');
340
341 foreach ($wrappers as $wrapper) {
342 $url = getenv($wrapper.'_proxy');
343 if (!empty($url)) {
344 $params = array_merge(array(
345 'port' => null,
346 'user' => null,
347 'pass' => null,
348 ), parse_url($url));
349 $this->addProxySettings($params['host'], $params['port'], $wrapper, $params['user'], $params['pass']);
350 }
351 }
352 }
353
354 /**
355 * Add proxy settings to the stream context array.
356 *
357 * @param string $server Proxy server/host
358 * @param int $port Port
359 * @param string $wrapper Wrapper: "http", "https", "ftp", others...
360 * @param string $username Username (when requiring authentication)
361 * @param string $password Password (when requiring authentication)
362 *
363 * @return Browscap
364 */
365 public function addProxySettings($server, $port = 3128, $wrapper = 'http', $username = null, $password = null)
366 {
367 $settings = array($wrapper => array(
368 'proxy' => sprintf('tcp://%s:%d', $server, $port),
369 'request_fulluri' => true,
370 ));
371
372 // Proxy authentication (optional)
373 if (isset($username) && isset($password)) {
374 $settings[$wrapper]['header'] = 'Proxy-Authorization: Basic '.base64_encode($username.':'.$password);
375 }
376
377 // Add these new settings to the stream context options array
378 $this->_streamContextOptions = array_merge(
379 $this->_streamContextOptions,
380 $settings
381 );
382
383 /* Return $this so we can chain addProxySettings() calls like this:
384 * $browscap->
385 * addProxySettings('http')->
386 * addProxySettings('https')->
387 * addProxySettings('ftp');
388 */
389 return $this;
390 }
391
392 /**
393 * Clear proxy settings from the stream context options array.
394 *
395 * @param string $wrapper Remove settings from this wrapper only
396 *
397 * @return array Wrappers cleared
398 */
399 public function clearProxySettings($wrapper = null)
400 {
401 $wrappers = isset($wrapper) ? array($wrappers) : array_keys($this->_streamContextOptions);
402
403 $affectedProtocols = array();
404 $options = array('proxy', 'request_fulluri', 'header');
405 foreach ($wrappers as $wrapper) {
406
407 // remove wrapper options related to proxy settings
408 if (isset($this->_streamContextOptions[$wrapper]['proxy'])) {
409 foreach ($options as $option){
410 unset($this->_streamContextOptions[$wrapper][$option]);
411 }
412
413 // remove wrapper entry if there are no other options left
414 if (empty($this->_streamContextOptions[$wrapper])) {
415 unset($this->_streamContextOptions[$wrapper]);
416 }
417
418 $clearedWrappers[] = $wrapper;
419 }
420 }
421
422 return $clearedWrappers;
423 }
424
425 /**
426 * Returns the array of stream context options.
427 *
428 * @return array
429 */
430 public function getStreamContextOptions()
431 {
432 return $this->_streamContextOptions;
433 }
434
435 /**
436 * Parses the ini file and updates the cache files
437 *
438 * @return bool whether the file was correctly written to the disk
439 */
440 public function updateCache()
441 {
442 $ini_path = $this->cacheDir . $this->iniFilename;
443 $cache_path = $this->cacheDir . $this->cacheFilename;
444
445 // Choose the right url
446 if ($this->_getUpdateMethod() == self::UPDATE_LOCAL) {
447 $url = $this->localFile;
448 } else {
449 $url = $this->remoteIniUrl;
450 }
451
452 $this->_getRemoteIniFile($url, $ini_path);
453
454 if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
455 $browsers = parse_ini_file($ini_path, true, INI_SCANNER_RAW);
456 } else {
457 $browsers = parse_ini_file($ini_path, true);
458 }
459
460 array_shift($browsers);
461
462 $this->_properties = array_keys($browsers['DefaultProperties']);
463 array_unshift(
464 $this->_properties,
465 'browser_name',
466 'browser_name_regex',
467 'browser_name_pattern',
468 'Parent'
469 );
470
471 $this->_userAgents = array_keys($browsers);
472 usort(
473 $this->_userAgents,
474 create_function(self::ORDER_FUNC_ARGS, self::ORDER_FUNC_LOGIC)
475 );
476
477 $user_agents_keys = array_flip($this->_userAgents);
478 $properties_keys = array_flip($this->_properties);
479
480 $search = array('\*', '\?');
481 $replace = array('.*', '.');
482
483 foreach ($this->_userAgents as $user_agent) {
484 $pattern = preg_quote($user_agent, self::REGEX_DELIMITER);
485 $this->_patterns[] = self::REGEX_DELIMITER
486 . '^'
487 . str_replace($search, $replace, $pattern)
488 . '$'
489 . self::REGEX_DELIMITER;
490
491 if (!empty($browsers[$user_agent]['Parent'])) {
492 $parent = $browsers[$user_agent]['Parent'];
493 $browsers[$user_agent]['Parent'] = $user_agents_keys[$parent];
494 }
495
496 foreach ($browsers[$user_agent] as $key => $value) {
497 $key = $properties_keys[$key] . ".0";
498 $browser[$key] = $value;
499 }
500
501 $this->_browsers[] = $browser;
502 unset($browser);
503 }
504 unset($user_agents_keys, $properties_keys, $browsers);
505
506 // Save the keys lowercased if needed
507 if ($this->lowercase) {
508 $this->_properties = array_map('strtolower', $this->_properties);
509 }
510
511 // Get the whole PHP code
512 $cache = $this->_buildCache();
513
514 // Save and return
515 return (bool) file_put_contents($cache_path, $cache, LOCK_EX);
516 }
517
518 /**
519 * Loads the cache into object's properties
520 *
521 * @return void
522 */
523 protected function _loadCache($cache_file)
524 {
525 require $cache_file;
526
527 $this->_browsers = $browsers;
528 $this->_userAgents = $userAgents;
529 $this->_patterns = $patterns;
530 $this->_properties = $properties;
531
532 $this->_cacheLoaded = true;
533 }
534
535 /**
536 * Parses the array to cache and creates the PHP string to write to disk
537 *
538 * @return string the PHP string to save into the cache file
539 */
540 protected function _buildCache()
541 {
542 $cacheTpl = "<?php\n\$properties=%s;\n\$browsers=%s;\n\$userAgents=%s;\n\$patterns=%s;\n";
543
544 $propertiesArray = $this->_array2string($this->_properties);
545 $patternsArray = $this->_array2string($this->_patterns);
546 $userAgentsArray = $this->_array2string($this->_userAgents);
547 $browsersArray = $this->_array2string($this->_browsers);
548
549 return sprintf(
550 $cacheTpl,
551 $propertiesArray,
552 $browsersArray,
553 $userAgentsArray,
554 $patternsArray
555 );
556 }
557
558 /**
559 * Lazy getter for the stream context resource.
560 *
561 * @return resource
562 */
563 protected function _getStreamContext($recreate = false)
564 {
565 if (!isset($this->_streamContext) || true === $recreate) {
566 $this->_streamContext = stream_context_create($this->_streamContextOptions);
567 }
568
569 return $this->_streamContext;
570 }
571
572 /**
573 * Updates the local copy of the ini file (by version checking) and adapts
574 * his syntax to the PHP ini parser
575 *
576 * @param string $url the url of the remote server
577 * @param string $path the path of the ini file to update
578 * @throws Exception
579 * @return bool if the ini file was updated
580 */
581 protected function _getRemoteIniFile($url, $path)
582 {
583 // Check version
584 if (file_exists($path) && filesize($path)) {
585 $local_tmstp = filemtime($path);
586
587 if ($this->_getUpdateMethod() == self::UPDATE_LOCAL) {
588 $remote_tmstp = $this->_getLocalMTime();
589 } else {
590 $remote_tmstp = $this->_getRemoteMTime();
591 }
592
593 if ($remote_tmstp < $local_tmstp) {
594 // No update needed, return
595 touch($path);
596
597 return false;
598 }
599 }
600
601 // Get updated .ini file
602 $browscap = $this->_getRemoteData($url);
603
604
605 $browscap = explode("\n", $browscap);
606
607 $pattern = self::REGEX_DELIMITER
608 . '('
609 . self::VALUES_TO_QUOTE
610 . ')="?([^"]*)"?$'
611 . self::REGEX_DELIMITER;
612
613
614 // Ok, lets read the file
615 $content = '';
616 foreach ($browscap as $subject) {
617 $subject = trim($subject);
618 $content .= preg_replace($pattern, '$1="$2"', $subject) . "\n";
619 }
620
621 if ($url != $path) {
622 if (!file_put_contents($path, $content)) {
623 throw new Exception("Could not write .ini content to $path");
624 }
625 }
626
627 return true;
628 }
629
630 /**
631 * Gets the remote ini file update timestamp
632 *
633 * @throws Exception
634 * @return int the remote modification timestamp
635 */
636 protected function _getRemoteMTime()
637 {
638 $remote_datetime = $this->_getRemoteData($this->remoteVerUrl);
639 $remote_tmstp = strtotime($remote_datetime);
640
641 if (!$remote_tmstp) {
642 throw new Exception("Bad datetime format from {$this->remoteVerUrl}");
643 }
644
645 return $remote_tmstp;
646 }
647
648 /**
649 * Gets the local ini file update timestamp
650 *
651 * @throws Exception
652 * @return int the local modification timestamp
653 */
654 protected function _getLocalMTime()
655 {
656 if (!is_readable($this->localFile) || !is_file($this->localFile)) {
657 throw new Exception("Local file is not readable");
658 }
659
660 return filemtime($this->localFile);
661 }
662
663 /**
664 * Converts the given array to the PHP string which represent it.
665 * This method optimizes the PHP code and the output differs form the
666 * var_export one as the internal PHP function does not strip whitespace or
667 * convert strings to numbers.
668 *
669 * @param array $array the array to parse and convert
670 * @return string the array parsed into a PHP string
671 */
672 protected function _array2string($array)
673 {
674 $strings = array();
675
676 foreach ($array as $key => $value) {
677 if (is_int($key)) {
678 $key = '';
679 } elseif (ctype_digit((string) $key) || strpos($key, '.0')) {
680 $key = intval($key) . '=>' ;
681 } else {
682 $key = "'" . str_replace("'", "\'", $key) . "'=>" ;
683 }
684
685 if (is_array($value)) {
686 $value = $this->_array2string($value);
687 } elseif (ctype_digit((string) $value)) {
688 $value = intval($value);
689 } else {
690 $value = "'" . str_replace("'", "\'", $value) . "'";
691 }
692
693 $strings[] = $key . $value;
694 }
695
696 return 'array(' . implode(',', $strings) . ')';
697 }
698
699 /**
700 * Checks for the various possibilities offered by the current configuration
701 * of PHP to retrieve external HTTP data
702 *
703 * @return string the name of function to use to retrieve the file
704 */
705 protected function _getUpdateMethod()
706 {
707 // Caches the result
708 if ($this->updateMethod === null) {
709 if ($this->localFile !== null) {
710 $this->updateMethod = self::UPDATE_LOCAL;
711 } elseif (ini_get('allow_url_fopen') && function_exists('file_get_contents')) {
712 $this->updateMethod = self::UPDATE_FOPEN;
713 } elseif (function_exists('fsockopen')) {
714 $this->updateMethod = self::UPDATE_FSOCKOPEN;
715 } elseif (extension_loaded('curl')) {
716 $this->updateMethod = self::UPDATE_CURL;
717 } else {
718 $this->updateMethod = false;
719 }
720 }
721
722 return $this->updateMethod;
723 }
724
725 /**
726 * Retrieve the data identified by the URL
727 *
728 * @param string $url the url of the data
729 * @throws Exception
730 * @return string the retrieved data
731 */
732 protected function _getRemoteData($url)
733 {
734 ini_set('user_agent', $this->_getUserAgent());
735
736 switch ($this->_getUpdateMethod()) {
737 case self::UPDATE_LOCAL:
738 $file = file_get_contents($url);
739
740 if ($file !== false) {
741 return $file;
742 } else {
743 throw new Exception('Cannot open the local file');
744 }
745 case self::UPDATE_FOPEN:
746 // include proxy settings in the file_get_contents() call
747 $context = $this->_getStreamContext();
748 $file = file_get_contents($url, false, $context);
749
750 if ($file !== false) {
751 return $file;
752 } // else try with the next possibility (break omitted)
753 case self::UPDATE_FSOCKOPEN:
754 $remote_url = parse_url($url);
755 $remote_handler = fsockopen($remote_url['host'], 80, $c, $e, $this->timeout);
756
757 if ($remote_handler) {
758 stream_set_timeout($remote_handler, $this->timeout);
759
760 if (isset($remote_url['query'])) {
761 $remote_url['path'] .= '?' . $remote_url['query'];
762 }
763
764 $out = sprintf(
765 self::REQUEST_HEADERS,
766 $remote_url['path'],
767 $remote_url['host'],
768 $this->_getUserAgent()
769 );
770
771 fwrite($remote_handler, $out);
772
773 $response = fgets($remote_handler);
774 if (strpos($response, '200 OK') !== false) {
775 $file = '';
776 while (!feof($remote_handler)) {
777 $file .= fgets($remote_handler);
778 }
779
780 $file = str_replace("\r\n", "\n", $file);
781 $file = explode("\n\n", $file);
782 array_shift($file);
783
784 $file = implode("\n\n", $file);
785
786 fclose($remote_handler);
787
788 return $file;
789 }
790 } // else try with the next possibility
791 case self::UPDATE_CURL:
792 $ch = curl_init($url);
793
794 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
795 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->timeout);
796 curl_setopt($ch, CURLOPT_USERAGENT, $this->_getUserAgent());
797
798 $file = curl_exec($ch);
799
800 curl_close($ch);
801
802 if ($file !== false) {
803 return $file;
804 } // else try with the next possibility
805 case false:
806 throw new Exception('Your server can\'t connect to external resources. Please update the file manually.');
807 }
808 }
809
810 /**
811 * Format the useragent string to be used in the remote requests made by the
812 * class during the update process.
813 *
814 * @return string the formatted user agent
815 */
816 protected function _getUserAgent()
817 {
818 $ua = str_replace('%v', self::VERSION, $this->userAgent);
819 $ua = str_replace('%m', $this->_getUpdateMethod(), $ua);
820
821 return $ua;
822 }
823}
824
825/**
826 * Browscap.ini parsing class exception
827 *
828 * @package Browscap
829 * @author Jonathan Stoppani <jonathan@stoppani.name>
830 * @copyright Copyright (c) 2006-2012 Jonathan Stoppani
831 * @version 1.0
832 * @license http://www.opensource.org/licenses/MIT MIT License
833 * @link https://github.com/GaretJax/phpbrowscap/*/
834class Exception extends BaseException
835{}
Note: See TracBrowser for help on using the repository browser.