NetCurl is a PHP communication library built around one core idea: application code should normally describe what to request, not which low-level transport must perform it.
The maintained 6.1 line can select between built-in communication drivers at runtime. The important compatibility contract is that callers should not need separate business logic for cURL, PHP streams, SOAP, RSS/XML, or registered custom drivers when the same logical request can be represented by more than one transport.
Source and issue tracking:
composer require tornevall/tornelib-php-netcurl:^6.1
Do not infer PHP support only from old README text or old build systems. The actively maintained compatibility information is the current GitHub Actions matrix in the repository.
The old PHP 5.6 compatibility claim is no longer valid for the current 6.1 source.
For a normal request, use NetWrapper and let NetCurl select a usable transport:
use TorneLIB\Module\Network\NetWrapper;
$client = new NetWrapper();
$client->request('https://example.com/api/status');
$body = $client->getBody();
$status = $client->getCode();
$driver = $client->getCurrentWrapperClass(true);
The application does not need to instantiate CurlWrapper or SimpleStreamWrapper itself.
For the built-in drivers, 6.1 currently routes requests in this general order:
SoapClient is available.DataType::RSS_XML is requested.This fallback behavior is part of NetCurl's core design, not an implementation accident.
A typical installation may include cURL, XML and SOAP support:
apt-get install php-curl php-xml php-soap
But generic HTTP communication must not require cURL just to initialize NetCurl. If cURL is missing and PHP streams are usable, NetCurl should be able to route the request through the stream driver instead.
For HTTP/HTTPS stream fallback in 6.1, PHP's URL-aware stream wrappers must be usable. In practice, allow_url_fopen must be enabled for SimpleStreamWrapper to handle remote URLs.
This behavior is now covered by explicit compatibility work because a regression was found where cURL constants were accessed before fallback could occur:
NetWrapper is the normal application-facing choice because it preserves automatic routing and fallback. Direct wrapper access is still useful when the application deliberately guarantees a transport and wants transport-specific behavior.
For example, an environment that guarantees cURL can use CurlWrapper directly:
use TorneLIB\Module\Network\Wrappers\CurlWrapper;
$client = new CurlWrapper();
$client->request('https://example.com/api/status');
$body = $client->getBody();
Doing this intentionally gives up NetWrapper's automatic fallback. Prefer it only when that trade-off is part of the application's design or when diagnosing/testing an individual transport.
Input format and output access are separate concerns.
use TorneLIB\Model\Type\DataType;
use TorneLIB\Model\Type\RequestMethod;
use TorneLIB\Module\Network\NetWrapper;
$client = new NetWrapper();
$client->request(
'https://example.com/api/items',
[
'name' => 'Example',
'enabled' => true,
],
RequestMethod::POST,
DataType::JSON
);
$raw = $client->getBody();
$parsed = $client->getParsed();
$status = $client->getCode();
The request data is encoded as JSON for the transport, while the same completed request can still be inspected as a raw body or through NetCurl's parsed representation.
use TorneLIB\Model\Type\DataType;
use TorneLIB\Model\Type\RequestMethod;
use TorneLIB\Module\Network\NetWrapper;
$client = new NetWrapper();
$client->request(
'https://example.com/api/xml',
'<request><id>123</id></request>',
RequestMethod::POST,
DataType::XML
);
$response = $client->getParsed();
Arrays can also be converted to XML by the configuration layer when XML is the selected request data type.
Use DataType::NORMAL for ordinary HTTP form/query handling:
use TorneLIB\Model\Type\DataType;
use TorneLIB\Model\Type\RequestMethod;
use TorneLIB\Module\Network\NetWrapper;
$client = new NetWrapper();
$client->request(
'https://example.com/api/search',
['query' => 'netcurl'],
RequestMethod::GET,
DataType::NORMAL
);
The same request method is used regardless of whether the selected transport becomes cURL or a stream implementation.
A URL containing ?wsdl or &wsdl is treated as a SOAP/WSDL-style request by NetWrapper.
When PHP's SoapClient is available, NetCurl selects the SOAP wrapper. When SOAP is unavailable, the current 6.1 router can fall back to XML-over-HTTP where that request can be represented safely.
use TorneLIB\Module\Network\NetWrapper;
$client = new NetWrapper();
$client->request('https://example.com/service?wsdl');
$driver = $client->getCurrentWrapperClass(true);
Do not instantiate SoapClientWrapper in ordinary application code merely to choose the transport. Direct wrapper access is intended for cases where transport-specific behavior is deliberately required.
RSS can be selected through DataType::RSS_XML:
use TorneLIB\Model\Type\DataType;
use TorneLIB\Model\Type\RequestMethod;
use TorneLIB\Module\Network\NetWrapper;
$client = new NetWrapper();
$client->request(
'https://example.com/feed.xml',
[],
RequestMethod::GET,
DataType::RSS_XML
);
$feed = $client->getParsed();
Optional feed libraries may provide richer RSS handling, but NetCurl keeps its own fallback path so an optional dependency does not automatically become a hard requirement.
Shared configuration is applied before the selected transport executes the request:
use TorneLIB\Model\Type\AuthType;
use TorneLIB\Module\Network\NetWrapper;
$client = new NetWrapper();
$client
->setHeader('X-Client', 'my-service')
->setAuthentication('username', 'password', AuthType::BASIC)
->setTimeout(15)
->setProxy('proxy.example.net:8080');
$client->request('https://example.com/private-api');
The point of the shared configuration layer is that the caller should not need one authentication/header/timeout setup for cURL and another for streams or SOAP when the selected driver supports the same logical option.
Configuration belongs to the NetWrapper instance. Separate clients can therefore carry different headers, authentication, proxy and timeout state:
$first = new NetWrapper();
$first->setHeader('X-Tenant', 'first');
$second = new NetWrapper();
$second->setHeader('X-Tenant', 'second');
This behavior is being protected with deterministic contract tests before the 6.2 refactor.
NetWrapper::request() also accepts an associative request map. Each entry can carry its own data, method, data type and optional WrapperConfig:
use TorneLIB\Model\Type\DataType;
use TorneLIB\Model\Type\RequestMethod;
use TorneLIB\Module\Network\NetWrapper;
$client = new NetWrapper();
$client->request([
'https://example.com/one' => [
[],
RequestMethod::GET,
DataType::NORMAL,
],
'https://example.com/two' => [
['name' => 'Example'],
RequestMethod::POST,
DataType::JSON,
],
]);
$firstBody = $client->getBody('https://example.com/one');
$secondParsed = $client->getParsed('https://example.com/two');
Multi-request behavior is part of the compatibility contract and is being expanded with deterministic tests rather than relying only on remote integration endpoints.
Applications can register an object implementing NetCurl's WrapperInterface:
$client = new NetWrapper();
$client->register($myDriver, true);
$client->request('custom-or-http-target');
The second argument controls whether registered drivers should be tried before the built-in drivers. With false, built-ins remain preferred and external drivers act as a later fallback.
This is an important extension point and is intended to remain available in 6.2 and 7.0.
Normally the caller should not care, but diagnostics can inspect the selected wrapper:
$client->request('https://example.com/');
printf(
"Driver: %s\n",
$client->getCurrentWrapperClass(true)
);
This is useful for tests, diagnostics and capability verification without making transport selection part of business logic.
MODULE_CURLMODULE_CURL exists in 6.1 only as a compatibility facade for older 6.0-style clients.
Legacy code may still look like this:
use TorneLIB\MODULE_CURL;
$legacy = new MODULE_CURL();
$response = $legacy->doGet('https://example.com/');
For new 6.1 code, prefer NetWrapper:
use TorneLIB\Module\Network\NetWrapper;
$client = new NetWrapper();
$response = $client->request('https://example.com/');
MODULE_CURL is deprecated in 6.1 and planned for removal in 6.2. The migration is intentionally being designed so old calling styles can move forward without losing NetCurl's flexible argument and transport behavior.
6.1 is the compatibility/reference line. Existing behavior is being documented with broader deterministic tests and defects found by those tests are fixed without large architectural changes.
6.2 is the cleanup/rebuild bridge. MODULE_CURL is planned to disappear, but automatic driver selection, flexible request formats, custom drivers, raw/parsed response access and legacy-friendly calling patterns must not disappear with it.
7.0 is planned for PHP 8.3 and newer with typed/model-oriented internals. The modern core must still preserve NetCurl's original strength: callers can use a high-level facade while transports remain replaceable and automatically selected.
The cross-version requirements are tracked in:
The maintained test strategy is split into two kinds of coverage:
Relevant tracking:
For current PHP support, use the repository's GitHub Actions results rather than historical Bamboo, Bitbucket, Confluence or old README claims.