Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 81 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Example code for using proxy servers in different programming languages. Current
* Python
* JavaScript / Node.js
* Ruby
* PHP

## Python Proxy Examples

Expand Down Expand Up @@ -119,8 +120,87 @@ node javascript/run_tests.js axios got

## Ruby Proxy Examples

These examples use [Bundler](https://bundler.io/). Install Ruby development headers and libcurl first so native extensions can compile (Debian/Ubuntu: `ruby-dev` and `libcurl4-openssl-dev`; Fedora: `ruby-devel` and `libcurl-devel`).

```bash
cd ruby
bundle install
```

**Running examples:**

```bash
# Required: set your proxy URL
export PROXY_URL='http://user:pass@proxy.example.com:8080'

# Optional: target URL (default: https://api.ipify.org?format=json)
export TEST_URL='https://httpbin.org/ip'

# Optional: print one response header
export RESPONSE_HEADER='X-ProxyMesh-IP'

# Single example (from ruby/)
bundle exec ruby faraday-proxy.rb

# All examples as tests
bundle exec ruby run_tests.rb

# Specific examples
bundle exec ruby run_tests.rb faraday typhoeus
```

**Examples:**

| Library | Example | Description |
|---------|---------|-------------|
| [Net::HTTP](https://docs.ruby-lang.org/en/master/Net/HTTP.html) (stdlib) | [net-http-proxy.rb](ruby/net-http-proxy.rb) | Low-level HTTP with proxy (`Net::HTTP.new` + proxy host/port/user/pass) |
| [Faraday](https://lostisland.github.io/faraday/) | [faraday-proxy.rb](ruby/faraday-proxy.rb) | Middleware-style client; `Faraday.new(proxy: url)` |
| [HTTParty](https://github.com/jnunemaker/httparty) | [httparty-proxy.rb](ruby/httparty-proxy.rb) | Simple API; `http_proxyaddr` / `http_proxyport` / credentials |
| [HTTP.rb](https://github.com/httprb/http) | [http-rb-proxy.rb](ruby/http-rb-proxy.rb) | Lightweight DSL; proxy via `HTTP.via(host, port, user, pass)` |
| [RestClient](https://github.com/rest-client/rest-client) | [rest-client-proxy.rb](ruby/rest-client-proxy.rb) | Simple REST API; proxy via `RestClient.proxy = url` |
| [Typhoeus](https://github.com/typhoeus/typhoeus) | [typhoeus-proxy.rb](ruby/typhoeus-proxy.rb) | libcurl via Ethon; `proxy:` URL on the request |
| [Excon](https://github.com/excon/excon) | [excon-proxy.rb](ruby/excon-proxy.rb) | Fast client; `Excon.get(url, proxy: url)` |
| [HTTPClient](https://github.com/nahi/httpclient) | [httpclient-proxy.rb](ruby/httpclient-proxy.rb) | LWP-like client; pass full proxy URL to `HTTPClient.new` |
| [Mechanize](https://github.com/sparklemotion/mechanize) | [mechanize-proxy.rb](ruby/mechanize-proxy.rb) | Crawling / forms; `set_proxy(host, port, user, password)` |
| [Nokogiri](https://nokogiri.org/) | [nokogiri-proxy.rb](ruby/nokogiri-proxy.rb) | Parse HTML after a proxied `Net::HTTP` fetch |

Libraries above are actively maintained on RubyGems (releases within the last year as of early 2026). Like most high-level Ruby HTTP clients, they do not expose custom headers on the HTTPS `CONNECT` tunnel to the proxy or proxy response headers; for ProxyMesh-style custom proxy headers, lower-level clients or a dedicated helper library may be required.

## PHP Proxy Examples

**Installation:**

```bash
cd php
composer install
```

**Running Examples:**

```bash
# Required: Set your proxy URL
export PROXY_URL='http://user:pass@proxy.example.com:8080'

# Run a single example
php php/guzzle_proxy.php

# Run all examples as tests
php php/run_tests.php
```

**Examples:**

| Library | Example | Description |
|---------|---------|-------------|
| [cURL](https://www.php.net/manual/en/book.curl.php) | [curl_proxy.php](php/curl_proxy.php) | PHP's built-in HTTP client (libcurl) |
| [Guzzle](https://docs.guzzlephp.org/) | [guzzle_proxy.php](php/guzzle_proxy.php) | Most popular PHP HTTP client |
| [Symfony HttpClient](https://symfony.com/doc/current/http_client.html) | [symfony_http_client_proxy.php](php/symfony_http_client_proxy.php) | Modern PSR-18 HTTP client |
| [Buzz](https://github.com/kriswallsmith/Buzz) | [buzz_proxy.php](php/buzz_proxy.php) | Simple PSR-18 HTTP client |
| [PHP Streams](https://www.php.net/manual/en/book.stream.php) | [streams_proxy.php](php/streams_proxy.php) | Built-in PHP streams (file_get_contents) |
| [Amp HTTP](https://amphp.org/http-client) | [amphp_proxy.php](php/amphp_proxy.php) | Async HTTP client |

> **Note:** See [php-proxy-headers](https://github.com/proxymeshai/php-proxy-headers) for extensions that add custom proxy header support.

These examples use [Bundler](https://bundler.io/). Install Ruby development headers and libcurl first so native extensions can compile (Debian/Ubuntu: `ruby-dev` and `libcurl4-openssl-dev`; Fedora: `ruby-devel` and `libcurl-devel`).

```bash
Expand Down Expand Up @@ -189,7 +269,7 @@ More examples and language-specific proxy-header tooling:

## Contributing

Contributions are welcome for all supported languages in this repository (Python, JavaScript, and Ruby), as well as new language examples.
Contributions are welcome for all supported languages in this repository (Python, JavaScript, Ruby, and PHP), as well as new language examples.

When opening a Pull Request:

Expand Down
52 changes: 52 additions & 0 deletions php/amphp_proxy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/usr/bin/env php
<?php
/**
* Amp HTTP Client with proxy example.
*
* Configuration via environment variables:
* PROXY_URL - Proxy URL (required), e.g., http://user:pass@proxy:8080
* TEST_URL - URL to request (default: https://api.ipify.org?format=json)
*
* Amp HTTP Client is an async HTTP client for PHP. It supports proxies
* but does NOT support custom CONNECT headers or reading proxy response headers.
*/

require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/common.php';

use Amp\Http\Client\HttpClientBuilder;
use Amp\Http\Client\Request;
use Amp\Http\Client\Connection\DefaultConnectionFactory;
use Amp\Http\Client\Connection\UnlimitedConnectionPool;
use Amp\Http\Tunnel\Http1TunnelConnector;

$proxyUrl = get_proxy_url();

$testUrl = getenv('TEST_URL') ?: 'https://api.ipify.org?format=json';

$parsedProxy = parse_url($proxyUrl);
$proxyHost = $parsedProxy['host'];
$proxyPort = $parsedProxy['port'] ?? 8080;

try {
$tunnelHeaders = [];
if (isset($parsedProxy['user'])) {
$credentials = $parsedProxy['user'] . ':' . ($parsedProxy['pass'] ?? '');
$tunnelHeaders['Proxy-Authorization'] = 'Basic ' . base64_encode($credentials);
}
$connector = new Http1TunnelConnector("{$proxyHost}:{$proxyPort}", $tunnelHeaders);
$pool = new UnlimitedConnectionPool(new DefaultConnectionFactory($connector));

$client = (new HttpClientBuilder())
->usingPool($pool)
->build();

$request = new Request($testUrl);
$response = $client->request($request);

echo "Status: " . $response->getStatus() . "\n";
echo "Body: " . $response->getBody()->buffer() . "\n";
} catch (Exception $e) {
fwrite(STDERR, "Error: " . $e->getMessage() . "\n");
exit(1);
}
41 changes: 41 additions & 0 deletions php/buzz_proxy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env php
<?php
/**
* Buzz with proxy example.
*
* Configuration via environment variables:
* PROXY_URL - Proxy URL (required), e.g., http://user:pass@proxy:8080
* TEST_URL - URL to request (default: https://api.ipify.org?format=json)
*
* Buzz is a simple PSR-18 HTTP client. It supports proxies but does NOT
* support custom CONNECT headers or reading proxy response headers.
*/

require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/common.php';

use Buzz\Browser;
use Buzz\Client\Curl;
use Nyholm\Psr7\Factory\Psr17Factory;

$proxyUrl = get_proxy_url();

$testUrl = getenv('TEST_URL') ?: 'https://api.ipify.org?format=json';

try {
$psr17Factory = new Psr17Factory();

$client = new Curl($psr17Factory, [
'proxy' => $proxyUrl,
'timeout' => 30,
]);

$browser = new Browser($client, $psr17Factory);
$response = $browser->get($testUrl);

echo "Status: " . $response->getStatusCode() . "\n";
echo "Body: " . $response->getBody() . "\n";
} catch (Exception $e) {
fwrite(STDERR, "Error: " . $e->getMessage() . "\n");
exit(1);
}
56 changes: 56 additions & 0 deletions php/common.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

/**
* Shared helpers for PHP proxy examples.
*/

function normalize_proxy_url(string $value): string
{
$trimmed = trim($value);
if ($trimmed === '') {
return '';
}

if (!preg_match('/^[a-z][a-z0-9+\-.]*:\/\//i', $trimmed)) {
$trimmed = "http://{$trimmed}";
}

$parts = parse_url($trimmed);
if (!is_array($parts) || empty($parts['host'])) {
return '';
}

$scheme = $parts['scheme'] ?? 'http';
$host = $parts['host'];
$port = $parts['port'] ?? 31280;
$user = $parts['user'] ?? null;
$pass = $parts['pass'] ?? null;

$auth = '';
if ($user !== null) {
$auth = rawurlencode($user);
if ($pass !== null) {
$auth .= ':' . rawurlencode($pass);
}
$auth .= '@';
}

return sprintf('%s://%s%s:%d', $scheme, $auth, $host, $port);
}

function get_proxy_url(): string
{
$raw = getenv('PROXY_URL') ?: getenv('HTTPS_PROXY');
if (!$raw) {
fwrite(STDERR, "Error: Set PROXY_URL environment variable\n");
exit(1);
}

$normalized = normalize_proxy_url($raw);
if ($normalized === '') {
fwrite(STDERR, "Error: Invalid PROXY_URL value\n");
exit(1);
}

return $normalized;
}
24 changes: 24 additions & 0 deletions php/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "proxymesh/proxy-examples-php",
"description": "PHP proxy usage examples",
"type": "project",
"license": "MIT",
"require": {
"php": ">=8.1",
"guzzlehttp/guzzle": "^7.8",
"symfony/http-client": "^7.0",
"php-http/guzzle7-adapter": "^1.0",
"nyholm/psr7": "^1.8",
"kriswallsmith/buzz": "^1.2",
"amphp/http-client": "^5.0",
"amphp/http-tunnel": "^2.0"
},
"autoload": {
"psr-4": {
"ProxyExamples\\": "src/"
}
},
"scripts": {
"test": "php run_tests.php"
}
}
Loading