Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

Proxy support when fetching remote files #16

Merged
merged 4 commits into from Mar 23, 2012
View
@@ -0,0 +1 @@
+*~
View
@@ -0,0 +1,5 @@
+language: php
+
+php:
+ - 5.3
+ - 5.4
View
@@ -3,6 +3,7 @@ Browser Capabilities PHP Project
_Hacking around with PHP to have a better solution than `get_browser()`_
+[![Build Status](https://secure.travis-ci.org/GaretJax/phpbrowscap.png?branch=master)](http://travis-ci.org/GaretJax/phpbrowscap)
Introduction
------------
View
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit bootstrap="tests/bootstrap.php" colors="true">
+ <testsuites>
+ <testsuite name="phpbrowscap Test Suite">
+ <directory>tests/phpbrowscap/</directory>
+ </testsuite>
+ </testsuites>
+
+ <filter>
+ <whitelist>
+ <directory suffix=".php">src/phpbrowscap/</directory>
+ </whitelist>
+ </filter>
+</phpunit>
@@ -31,6 +31,7 @@
*
* @package Browscap
* @author Jonathan Stoppani <jonathan@stoppani.name>
+ * @author Vítor Brandão <noisebleed@noiselabs.org>
* @copyright Copyright (c) 2006-2012 Jonathan Stoppani
* @version 1.0
* @license http://www.opensource.org/licenses/MIT MIT License
@@ -180,6 +181,28 @@ class Browscap
protected $_properties = array();
/**
+ * An associative array of associative arrays in the format
+ * `$arr['wrapper']['option'] = $value` passed to stream_context_create()
+ * when building a stream resource.
+ *
+ * Proxy settings are stored in this variable.
+ *
+ * @see http://www.php.net/manual/en/function.stream-context-create.php
+ *
+ * @var array
+ */
+ protected $_streamContextOptions = array();
+
+ /**
+ * A valid context resource created with stream_context_create().
+ *
+ * @see http://www.php.net/manual/en/function.stream-context-create.php
+ *
+ * @var resource
+ */
+ protected $_streamContext = null;
+
+ /**
* Constructor class, checks for the existence of (and loads) the cache and
* if needed updated the definitions
*
@@ -309,6 +332,107 @@ public function getBrowser($user_agent = null, $return_array = false)
}
/**
+ * Load (auto-set) proxy settings from environment variables.
+ */
+ public function autodetectProxySettings()
+ {
+ $wrappers = array('http', 'https', 'ftp');
+
+ foreach ($wrappers as $wrapper) {
+ $url = getenv($wrapper.'_proxy');
+ if (!empty($url)) {
+ $params = array_merge(array(
+ 'port' => null,
+ 'user' => null,
+ 'pass' => null,
+ ), parse_url($url));
+ $this->addProxySettings($params['host'], $params['port'], $wrapper, $params['user'], $params['pass']);
+ }
+ }
+ }
+
+ /**
+ * Add proxy settings to the stream context array.
+ *
+ * @param string $server Proxy server/host
+ * @param int $port Port
+ * @param string $wrapper Wrapper: "http", "https", "ftp", others...
+ * @param string $username Username (when requiring authentication)
+ * @param string $password Password (when requiring authentication)
+ *
+ * @return Browscap
+ */
+ public function addProxySettings($server, $port = 3128, $wrapper = 'http', $username = null, $password = null)
+ {
+ $settings = array($wrapper => array(
+ 'proxy' => sprintf('tcp://%s:%d', $server, $port),
+ 'request_fulluri' => true,
+ ));
+
+ // Proxy authentication (optional)
+ if (isset($username) && isset($password)) {
+ $settings[$wrapper]['header'] = 'Proxy-Authorization: Basic '.base64_encode($username.':'.$password);
+ }
+
+ // Add these new settings to the stream context options array
+ $this->_streamContextOptions = array_merge(
+ $this->_streamContextOptions,
+ $settings
+ );
+
+ /* Return $this so we can chain addProxySettings() calls like this:
+ * $browscap->
+ * addProxySettings('http')->
+ * addProxySettings('https')->
+ * addProxySettings('ftp');
+ */
+ return $this;
+ }
+
+ /**
+ * Clear proxy settings from the stream context options array.
+ *
+ * @param string $wrapper Remove settings from this wrapper only
+ *
+ * @return array Wrappers cleared
+ */
+ public function clearProxySettings($wrapper = null)
+ {
+ $wrappers = isset($wrapper) ? array($wrappers) : array_keys($this->_streamContextOptions);
+
+ $affectedProtocols = array();
+ $options = array('proxy', 'request_fulluri', 'header');
+ foreach ($wrappers as $wrapper) {
+
+ // remove wrapper options related to proxy settings
+ if (isset($this->_streamContextOptions[$wrapper]['proxy'])) {
+ foreach ($options as $option){
+ unset($this->_streamContextOptions[$wrapper][$option]);
+ }
+
+ // remove wrapper entry if there are no other options left
+ if (empty($this->_streamContextOptions[$wrapper])) {
+ unset($this->_streamContextOptions[$wrapper]);
+ }
+
+ $clearedWrappers[] = $wrapper;
+ }
+ }
+
+ return $clearedWrappers;
+ }
+
+ /**
+ * Returns the array of stream context options.
+ *
+ * @return array
+ */
+ public function getStreamContextOptions()
+ {
+ return $this->_streamContextOptions;
+ }
+
+ /**
* Parses the ini file and updates the cache files
*
* @return bool whether the file was correctly written to the disk
@@ -432,6 +556,20 @@ protected function _buildCache()
}
/**
+ * Lazy getter for the stream context resource.
+ *
+ * @return resource
+ */
+ protected function _getStreamContext($recreate = false)
+ {
+ if (!isset($this->_streamContext) || true === $recreate) {
+ $this->_streamContext = stream_context_create($this->_streamContextOptions);
+ }
+
+ return $this->_streamContext;
+ }
+
+ /**
* Updates the local copy of the ini file (by version checking) and adapts
* his syntax to the PHP ini parser
*
@@ -605,7 +743,9 @@ protected function _getRemoteData($url)
throw new Exception('Cannot open the local file');
}
case self::UPDATE_FOPEN:
- $file = file_get_contents($url);
+ // include proxy settings in the file_get_contents() call
+ $context = $this->_getStreamContext();
+ $file = file_get_contents($url, false, $context);
if ($file !== false) {
return $file;
View
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * Browscap.ini parsing class with caching and update capabilities
+ *
+ * PHP version 5
+ *
+ * Copyright (c) 2006-2012 Jonathan Stoppani
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @package Browscap
+ * @author Vítor Brandão <noisebleed@noiselabs.org>
+ * @copyright Copyright (c) 2006-2012 Jonathan Stoppani
+ * @version 1.0
+ * @license http://www.opensource.org/licenses/MIT MIT License
+ * @link https://github.com/GaretJax/phpbrowscap/
+ */
+
+require_once __DIR__.'/phpbrowscap/TestCase.php';
+
+spl_autoload_register(function($class)
+{
+ $file = __DIR__.'/../src/'.strtr($class, '\\', '/').'.php';
+ if (file_exists($file)) {
+ require $file;
+ return true;
+ }
+});
@@ -0,0 +1,109 @@
+<?php
+
+namespace phpbrowscap;
+
+use phpbrowscap\Browscap;
+
+/**
+ * Browscap.ini parsing class with caching and update capabilities
+ *
+ * PHP version 5
+ *
+ * Copyright (c) 2006-2012 Jonathan Stoppani
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @package Browscap
+ * @author Vítor Brandão <noisebleed@noiselabs.org>
+ * @copyright Copyright (c) 2006-2012 Jonathan Stoppani
+ * @version 1.0
+ * @license http://www.opensource.org/licenses/MIT MIT License
+ * @link https://github.com/GaretJax/phpbrowscap/
+ */
+class BrowscapTest extends TestCase
+{
+ public function testProxyAutoDetection()
+ {
+ $browscap = $this->createBrowscap();
+
+ putenv('http_proxy=http://proxy.example.com:3128');
+ putenv('https_proxy=http://proxy.example.com:3128');
+ putenv('ftp_proxy=http://proxy.example.com:3128');
+
+ $browscap->autodetectProxySettings();
+ $options = $browscap->getStreamContextOptions();
+
+ $this->assertEquals($options['http']['proxy'], 'tcp://proxy.example.com:3128');
+ $this->assertTrue($options['http']['request_fulluri']);
+
+ $this->assertEquals($options['https']['proxy'], 'tcp://proxy.example.com:3128');
+ $this->assertTrue($options['https']['request_fulluri']);
+
+ $this->assertEquals($options['ftp']['proxy'], 'tcp://proxy.example.com:3128');
+ $this->assertTrue($options['ftp']['request_fulluri']);
+ }
+
+ public function testAddProxySettings()
+ {
+ $browscap = $this->createBrowscap();
+
+ $browscap->addProxySettings('proxy.example.com', 3128, 'http');
+ $options = $browscap->getStreamContextOptions();
+
+ $this->assertEquals($options['http']['proxy'], 'tcp://proxy.example.com:3128');
+ $this->assertTrue($options['http']['request_fulluri']);
+ }
+
+ public function testClearProxySettings()
+ {
+ $browscap = $this->createBrowscap();
+
+ $browscap->addProxySettings('proxy.example.com', 3128, 'http');
+ $options = $browscap->getStreamContextOptions();
+
+ $this->assertEquals($options['http']['proxy'], 'tcp://proxy.example.com:3128');
+ $this->assertTrue($options['http']['request_fulluri']);
+
+ $clearedWrappers = $browscap->clearProxySettings();
+ $options = $browscap->getStreamContextOptions();
+
+ $this->assertEmpty($options);
+ $this->assertEquals($clearedWrappers, array('http'));
+ }
+
+ public function testGetStreamContext()
+ {
+ $cacheDir = $this->createCacheDir();
+ $browscap = new BrowscapForTest($cacheDir);
+
+ $browscap->addProxySettings('proxy.example.com', 3128, 'http');
+
+ $resource = $browscap->getStreamContext();
+
+ $this->assertTrue(is_resource($resource));
+ }
+}
+
+class BrowscapForTest extends Browscap
+{
+ public function getStreamContext($recreate = false)
+ {
+ return $this->_getStreamContext($recreate);
+ }
+}
Oops, something went wrong.