Skip to content

Commit e52aa8d

Browse files
committed
Merge pull request GaretJax#16 from noisebleed/noiselabs-proxy
Proxy support when fetching remote files
2 parents 8e6968c + 1551d9c commit e52aa8d

File tree

8 files changed

+401
-1
lines changed

8 files changed

+401
-1
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*~

.travis.yml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
language: php
2+
3+
php:
4+
- 5.3
5+
- 5.4

README.markdown

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Browser Capabilities PHP Project
33

44
_Hacking around with PHP to have a better solution than `get_browser()`_
55

6+
[![Build Status](https://secure.travis-ci.org/GaretJax/phpbrowscap.png?branch=master)](http://travis-ci.org/GaretJax/phpbrowscap)
67

78
Introduction
89
------------

phpunit.xml.dist

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<phpunit bootstrap="tests/bootstrap.php" colors="true">
4+
<testsuites>
5+
<testsuite name="phpbrowscap Test Suite">
6+
<directory>tests/phpbrowscap/</directory>
7+
</testsuite>
8+
</testsuites>
9+
10+
<filter>
11+
<whitelist>
12+
<directory suffix=".php">src/phpbrowscap/</directory>
13+
</whitelist>
14+
</filter>
15+
</phpunit>

src/phpbrowscap/Browscap.php

+141-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
*
3232
* @package Browscap
3333
* @author Jonathan Stoppani <[email protected]>
34+
* @author Vítor Brandão <[email protected]>
3435
* @copyright Copyright (c) 2006-2012 Jonathan Stoppani
3536
* @version 1.0
3637
* @license http://www.opensource.org/licenses/MIT MIT License
@@ -179,6 +180,28 @@ class Browscap
179180
protected $_patterns = array();
180181
protected $_properties = array();
181182

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+
182205
/**
183206
* Constructor class, checks for the existence of (and loads) the cache and
184207
* if needed updated the definitions
@@ -308,6 +331,107 @@ public function getBrowser($user_agent = null, $return_array = false)
308331
return $return_array ? $array : (object) $array;
309332
}
310333

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+
311435
/**
312436
* Parses the ini file and updates the cache files
313437
*
@@ -431,6 +555,20 @@ protected function _buildCache()
431555
);
432556
}
433557

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+
434572
/**
435573
* Updates the local copy of the ini file (by version checking) and adapts
436574
* his syntax to the PHP ini parser
@@ -605,7 +743,9 @@ protected function _getRemoteData($url)
605743
throw new Exception('Cannot open the local file');
606744
}
607745
case self::UPDATE_FOPEN:
608-
$file = file_get_contents($url);
746+
// include proxy settings in the file_get_contents() call
747+
$context = $this->_getStreamContext();
748+
$file = file_get_contents($url, false, $context);
609749

610750
if ($file !== false) {
611751
return $file;

tests/bootstrap.php

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
/**
4+
* Browscap.ini parsing class with caching and update capabilities
5+
*
6+
* PHP version 5
7+
*
8+
* Copyright (c) 2006-2012 Jonathan Stoppani
9+
*
10+
* Permission is hereby granted, free of charge, to any person obtaining a
11+
* copy of this software and associated documentation files (the "Software"),
12+
* to deal in the Software without restriction, including without limitation
13+
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
14+
* and/or sell copies of the Software, and to permit persons to whom the
15+
* Software is furnished to do so, subject to the following conditions:
16+
*
17+
* The above copyright notice and this permission notice shall be included
18+
* in all copies or substantial portions of the Software.
19+
*
20+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21+
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26+
* THE SOFTWARE.
27+
*
28+
* @package Browscap
29+
* @author Vítor Brandão <[email protected]>
30+
* @copyright Copyright (c) 2006-2012 Jonathan Stoppani
31+
* @version 1.0
32+
* @license http://www.opensource.org/licenses/MIT MIT License
33+
* @link https://github.com/GaretJax/phpbrowscap/
34+
*/
35+
36+
require_once __DIR__.'/phpbrowscap/TestCase.php';
37+
38+
spl_autoload_register(function($class)
39+
{
40+
$file = __DIR__.'/../src/'.strtr($class, '\\', '/').'.php';
41+
if (file_exists($file)) {
42+
require $file;
43+
return true;
44+
}
45+
});

tests/phpbrowscap/BrowscapTest.php

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
3+
namespace phpbrowscap;
4+
5+
use phpbrowscap\Browscap;
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 Vítor Brandão <[email protected]>
34+
* @copyright Copyright (c) 2006-2012 Jonathan Stoppani
35+
* @version 1.0
36+
* @license http://www.opensource.org/licenses/MIT MIT License
37+
* @link https://github.com/GaretJax/phpbrowscap/
38+
*/
39+
class BrowscapTest extends TestCase
40+
{
41+
public function testProxyAutoDetection()
42+
{
43+
$browscap = $this->createBrowscap();
44+
45+
putenv('http_proxy=http://proxy.example.com:3128');
46+
putenv('https_proxy=http://proxy.example.com:3128');
47+
putenv('ftp_proxy=http://proxy.example.com:3128');
48+
49+
$browscap->autodetectProxySettings();
50+
$options = $browscap->getStreamContextOptions();
51+
52+
$this->assertEquals($options['http']['proxy'], 'tcp://proxy.example.com:3128');
53+
$this->assertTrue($options['http']['request_fulluri']);
54+
55+
$this->assertEquals($options['https']['proxy'], 'tcp://proxy.example.com:3128');
56+
$this->assertTrue($options['https']['request_fulluri']);
57+
58+
$this->assertEquals($options['ftp']['proxy'], 'tcp://proxy.example.com:3128');
59+
$this->assertTrue($options['ftp']['request_fulluri']);
60+
}
61+
62+
public function testAddProxySettings()
63+
{
64+
$browscap = $this->createBrowscap();
65+
66+
$browscap->addProxySettings('proxy.example.com', 3128, 'http');
67+
$options = $browscap->getStreamContextOptions();
68+
69+
$this->assertEquals($options['http']['proxy'], 'tcp://proxy.example.com:3128');
70+
$this->assertTrue($options['http']['request_fulluri']);
71+
}
72+
73+
public function testClearProxySettings()
74+
{
75+
$browscap = $this->createBrowscap();
76+
77+
$browscap->addProxySettings('proxy.example.com', 3128, 'http');
78+
$options = $browscap->getStreamContextOptions();
79+
80+
$this->assertEquals($options['http']['proxy'], 'tcp://proxy.example.com:3128');
81+
$this->assertTrue($options['http']['request_fulluri']);
82+
83+
$clearedWrappers = $browscap->clearProxySettings();
84+
$options = $browscap->getStreamContextOptions();
85+
86+
$this->assertEmpty($options);
87+
$this->assertEquals($clearedWrappers, array('http'));
88+
}
89+
90+
public function testGetStreamContext()
91+
{
92+
$cacheDir = $this->createCacheDir();
93+
$browscap = new BrowscapForTest($cacheDir);
94+
95+
$browscap->addProxySettings('proxy.example.com', 3128, 'http');
96+
97+
$resource = $browscap->getStreamContext();
98+
99+
$this->assertTrue(is_resource($resource));
100+
}
101+
}
102+
103+
class BrowscapForTest extends Browscap
104+
{
105+
public function getStreamContext($recreate = false)
106+
{
107+
return $this->_getStreamContext($recreate);
108+
}
109+
}

0 commit comments

Comments
 (0)