A PHP library for making HTTP requests with browser impersonation. This library uses curl-impersonate to mimic various browsers' network signatures, making it useful for accessing websites that may detect and block automated requests.
Install via Composer:
composer require hamaadraza/php-impersonate- PHP 8.0 or higher
<?php
require 'vendor/autoload.php';
use Raza\PHPImpersonate\PHPImpersonate;
// Simple GET request
$response = PHPImpersonate::get('https://example.com');
echo $response->body();
// POST request with data
$response = PHPImpersonate::post('https://example.com/api', [
'username' => 'johndoe',
'email' => 'john@example.com'
]);
// Check the response
if ($response->isSuccess()) {
$data = $response->json();
echo "User created with ID: " . $data['id'];
} else {
echo "Error: " . $response->status();
}The library provides convenient static methods for making requests:
// GET request with optional headers and timeout
PHPImpersonate::get(string $url, array $headers = [], int $timeout = 30): Response
// POST request with optional data, headers and timeout
PHPImpersonate::post(string $url, ?array $data = null, array $headers = [], int $timeout = 30): Response
// PUT request with optional data, headers and timeout
PHPImpersonate::put(string $url, ?array $data = null, array $headers = [], int $timeout = 30): Response
// PATCH request with optional data, headers and timeout
PHPImpersonate::patch(string $url, ?array $data = null, array $headers = [], int $timeout = 30): Response
// DELETE request with optional headers and timeout
PHPImpersonate::delete(string $url, array $headers = [], int $timeout = 30): Response
// HEAD request with optional headers and timeout
PHPImpersonate::head(string $url, array $headers = [], int $timeout = 30): ResponseYou can also create an instance of the client for more configuration options:
// Create a client with specific browser and timeout
$client = new PHPImpersonate('chrome107', 30);
// Instance methods
$client->sendGet(string $url, array $headers = []): Response
$client->sendPost(string $url, ?array $data = null, array $headers = []): Response
$client->sendPut(string $url, ?array $data = null, array $headers = []): Response
$client->sendPatch(string $url, ?array $data = null, array $headers = []): Response
$client->sendDelete(string $url, array $headers = []): Response
$client->sendHead(string $url, array $headers = []): Response
// Generic send method
$client->send(Request $request): ResponseThe Response class provides several methods for working with HTTP responses:
// Get the HTTP status code
$response->status(): int
// Get the response body as string
$response->body(): string
// Check if the response was successful (status code 200-299)
$response->isSuccess(): bool
// Parse the response body as JSON (throws \JsonException on failure)
$response->json(bool $associative = true, int $depth = 512, int $flags = 0): mixed
// Check whether a header is present (case-insensitive)
$response->hasHeader(string $name): bool
// Get the first value of a header (case-insensitive), or $default when absent
$response->header(string $name, ?string $default = null): ?string
// Get ALL values of a header — use this for Set-Cookie and other repeatable headers
$response->headerAll(string $name): string[]
// Get all headers as a map of name → list of values
$response->headers(): array<string, string[]>
// Serialise to a plain array
$response->toArray(): array
// Dump response details to a string (for logging)
$response->dump(): string
// Print response details and return self (for debugging)
$response->debug(): ResponseMost headers have a single value, so header() is all you need:
$contentType = $response->header('Content-Type'); // 'application/json'
$etag = $response->header('ETag', 'none'); // fallback to 'none'
if ($response->hasHeader('X-Rate-Limit-Remaining')) {
$remaining = $response->header('X-Rate-Limit-Remaining');
}Some headers are legitimately repeated by the server — most commonly Set-Cookie.
Per RFC 6265 §4.1.1, cookie values
must not be folded into a single comma-separated string, so header('Set-Cookie') would
silently drop all but the first cookie. Use headerAll() instead:
$cookies = $response->headerAll('Set-Cookie');
// ['sessionid=abc; Path=/; HttpOnly', 'csrftoken=xyz; Path=/; SameSite=Lax']
foreach ($cookies as $cookie) {
echo $cookie . "\n";
}headers() returns the full map when you need to inspect everything at once:
$allHeaders = $response->headers();
// [
// 'Content-Type' => ['application/json'],
// 'Set-Cookie' => ['sessionid=abc; Path=/; HttpOnly', 'csrftoken=xyz; Path=/'],
// 'Cache-Control'=> ['no-cache, no-store'],
// ]
foreach ($response->headers() as $name => $values) {
// $values is always an array, even for single-value headers
foreach ($values as $value) {
echo "$name: $value\n";
}
}Key point: Each header name maps to an array of values (string[]), not a single string. This correctly handles HTTP responses where headers like Set-Cookie can appear multiple times.
PHP-Impersonate supports mimicking various browsers:
chrome99_android(default)chrome99chrome100chrome101chrome104chrome107chrome110chrome116chrome119chrome120chrome123chrome124chrome131chrome131_androidchrome133achrome136edge99edge101firefox133firefox135safari153safari155safari170safari172_iossafari180safari180_iossafari184safari184_iossafari260safari260_iostor145
Example:
// Create a client that mimics Firefox
$client = new PHPImpersonate('firefox135');
$response = $client->sendGet('https://example.com');You can configure request timeouts:
// Set a 5-second timeout for this request
$response = PHPImpersonate::get('https://example.com', [], 5);
// Or when creating a client instance
$client = new PHPImpersonate('chrome107', 10); // 10-second timeoutYou can route requests through a proxy server using the curlOptions parameter:
use Raza\PHPImpersonate\PHPImpersonate;
$client = new PHPImpersonate(
browser: 'chrome136',
timeout: 30,
curlOptions: [
'proxy' => 'http://127.0.0.1:8080', // HTTP proxy
'proxy-user' => 'user:password', // optional authentication
]
);
$response = $client->sendGet('https://api.ipify.org?format=json');
echo $response->body();The following proxy-related curl options are supported:
| Option | Description | Example |
|---|---|---|
proxy |
Proxy server address | 'http://127.0.0.1:8080' or 'http://proxy.example.com:3128' |
proxy-user |
Proxy authentication credentials | 'username:password' |
You can also use SOCKS proxies by specifying the protocol:
$client = new PHPImpersonate(
browser: 'chrome136',
timeout: 30,
curlOptions: [
'proxy' => 'socks5://127.0.0.1:1080', // SOCKS5 proxy
]
);For one-off requests with a proxy, create an instance and use the instance methods:
$client = new PHPImpersonate(
browser: 'chrome136',
timeout: 30,
curlOptions: [
'proxy' => 'http://proxy.example.com:8080',
'proxy-user' => 'user:pass',
]
);
// GET request through proxy
$response = $client->sendGet('https://example.com');
// POST request through proxy
$response = $client->sendPost('https://example.com/api', [
'key' => 'value'
]);// Data will be automatically converted to JSON with correct Content-Type
$data = [
'title' => 'New Post',
'body' => 'This is the content',
'userId' => 1
];
$response = PHPImpersonate::post(
'https://jsonplaceholder.typicode.com/posts',
$data,
['Content-Type' => 'application/json']
);
$post = $response->json();
echo "Created post with ID: {$post['id']}\n";try {
$response = PHPImpersonate::get('https://example.com/nonexistent', [], 5);
if (!$response->isSuccess()) {
echo "Error: HTTP {$response->status()}\n";
echo $response->body();
}
} catch (\Raza\PHPImpersonate\Exception\RequestException $e) {
echo "Request failed: " . $e->getMessage();
}PHP-Impersonate supports sending data in different formats:
By default, data is sent as form data (application/x-www-form-urlencoded):
// This will be sent as form data
$response = PHPImpersonate::post('https://example.com/api', [
'username' => 'johndoe',
'email' => 'john@example.com'
]);
// Explicitly specify form data
$response = PHPImpersonate::post('https://example.com/api',
[
'username' => 'johndoe',
'email' => 'john@example.com'
],
['Content-Type' => 'application/x-www-form-urlencoded']
);You can send data as JSON by specifying the Content-Type header:
// Send data as JSON
$response = PHPImpersonate::post('https://example.com/api',
[
'username' => 'johndoe',
'email' => 'john@example.com'
],
['Content-Type' => 'application/json']
);For PUT and PATCH requests, JSON is used as the default format.
Run the test suite:
composer testThis project is licensed under the MIT License - see the LICENSE file for details.