# RFC: OOP API for cURL extension

- Status: Inactive
- Authors: Sara Golemon, pollita@php.net
- Date: 2024-02-14
- Version: 0.1
- Original: https://wiki.php.net/rfc/curl-oop
- Page: https://externals.io/rfc/curl-oop
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

## Discussions on the mailing list

- [\[RFC\] OOP API for cURL extension](https://externals.io/message/122371.md) — started on 2024-02-14, 21 messages
- [\[RFC\]\[DISCUSSION\] Object-oriented curl API v2](https://externals.io/message/127758.md) — started on 2025-06-26, 38 messages

---

## Introduction

As part of the [Resource to object conversion project](https://github.com/php/php-tasks/issues/6), the cURL resource types were converted to objects with the release of PHP 8.0 . Currently, those objects are still opaque types for holding and managing the underlying libcurl types, and do not offer any APIs for use by script authors.

## Proposal

This RFC aims to add OOP interfaces to the cURL extension, primarily by aliasing free functions to matching methods in the &#96;CurlHandle&#96;, &#96;CurlMultiHandle&#96;, and &#96;CurlShareHandle&#96; classes. Additionally, this RFC aims to make a handful of changes to the behaviors and return values of some methods in the interest of presenting a more objects-style API.

### Exceptions

Four new exceptions will be introduced. A top level &#96;CurlException&#96; to cover all cURL exception types, plus three specific exceptions as children of &#96;CurlException&#96;: &#96;CurlHandleException&#96;, &#96;CurlMultiException&#96;, and &#96;CurlShareException&#96;.

### CurlHandle

The &#96;CurlHandle&#96; class will be expanded as follows:

```
/**
 * @strict-properties
 * @not-serializable
 */
final class CurlHandle
{
        /**
         * Behaves similarly to `curl_init(?string $uri = null): \CurlHandle`,
         * however this constructor implicitly sets CURLOPT_RETURNTRANSFER == true.
         */
	public function __construct(?string $uri = null) {}

	/** @alias curl_errno */
	public function errno(): int {}

	/** @alias curl_error */
	public function error(): string {}

	/** @alias curl_strerror */
	static public function strerror(int $code): string {}

	/** @alias curl_escape */
	public function escape(string $str): string {}

	/** @alias curl_unescape */
	public function unescape(string $str): string {}

	/**
         * @throws CurlHandleException
         *
         * Similar to `curl_exec(\CurlHandle $ch): string|bool`,
         * but never returns False, since errors are promoted to exceptions.
         */
	public function exec(): string|true {}

	/** @alias curl_pause */
	public function pause(int $flags): int {}

	/** @alias curl_reset */
	public function reset(): void {}

	/** @alias curl_getinfo */
	public function getInfo(?int $option = null): mixed {}

#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */
	/** @alias curl_upkeep */
	public function upkeep(): bool {}
#endif

	/**
	 * Varies from curl_setopt() to allow fluent chaining.
	 * @throws CurlHandleException
	 */
	public function setOpt(int $option, mixed $value): \CurlHandle {}

	/**
	 * Returns self to match setOpt() behavior.
	 * @throws CurlHandleException
	 */
	public function setOptArray(array $option): \CurlHandle {}
}
```

### CurlMultiHandle

The &#96;CurlMultiHandle&#96; class will be expanded as follows:

```
/**
 * @strict-properties
 * @not-serializable
 */
final class CurlMultiHandle
{
	/**
	 * Returns self for fluent calling.
	 * @throws CurlException.
	 */
	public function addHandle(\CurlHandle $handle): \CurlMultiHandle {}

	/**
	 * Returns self for fluent calling.
	 * @throws CurlException.
	 */
	public function removeHandle(\CurlHandle $handle): \CurlMultiHandle {}

	/**
	 * Returns self for fluent calling.
	 * @throws CurlException.
	 */
	public function setOpt(int $option, mixed $value): \CurlMultiHandle {}

	/** @alias curl_multi_errno */
	public function errno(): int {}

	/** @alias curl_multi_error */
	public function error(): ?string {}

	/** @alias curl_multi_strerror */
	public function strerror(int $error_code): ?string {}

	/**
	 * Returns TRUE if still running, FALSE otherwise.
	 * @param int $still_running
	 * @throws CurlException.
	 */
	public function exec(&$still_running): bool {}

	/** @alias curl_multi_getcontent */
	static public function getContent(\CurlHandle $handle): ?string {}

	/**
	 * @alias curl_multi_info_read
	 * @param int $queued_messages
	 * @return array<string, int|object>|false
	 * @refcount 1
	 */
	public function infoRead(&$queued_messages = null): array|false {}

	/** @alias curl_multi_select */
	public function select(float $timeout = 1.0): int {}
}
```

### CurlShareHandle

The &#96;CurlShareHandle&#96; class will be expanded as follows:

```
/**
 * @strict-properties
 * @not-serializable
 */
final class CurlShareHandle
{

	/** @alias curl_share_errno */
	public function errno(): int {}

	/** @alias curl_share_error */
	public function error(): ?string {}

	/** @alias curl_share_strerror */
	static public function strerror(): ?string {}

	/**
	 * Returns self for fluent calling.
	 * @throws CurlException.
	 */
	public function setOpt(int $option, mixed $value): \CurlShareHandle {}
}
```

### New Functions

To make the functional API more consistent (and provide functions for the OOP implementation to alias), the following two additional functions will be added (psuedo-implementation shown):

```
function curl_multi_error(CurlMultiHandle $multi_handle): ?string {
    $errno = curl_multi_errno($multi_handle);
    return $errno ? curl_multi_strerror($errno) ? NULL;
}

function curl_share_error(CurlShareHandle $share_handle): ?string {
    $errno = curl_share_errno($share_handle);
    return $errno ? curl_share_strerror($errno) ? NULL;
}
```

## Backward Incompatible Changes

No breaking changes for script code. Extensions will note small changes in intialization performed by &#96;object\_init\_ex(return\_value, curl\_ce);&#96; as now &#96;curl\_easy\_init()&#96; and other initialization happens in &#96;curl\_object\_create&#96; rather than deferring to constructor/curl\_init().

## Proposed PHP Version(s)

8.4.0

## Unaffected PHP Functionality

Script code written for the original cURL functional API will not be impacted.

## Proposed Voting Choices

Straight yes/no.

## Patches and Tests

Each existing unit test will be reviewed and duplicated as appropriate to cover the OOP version of the API.

\* Work in progress: <https://github.com/php/php-src/pull/13394> Note that as a work in progress this may not 100% match the proposal above until it is complete.

## Implementation

TBD

## References

TBD

## Rejected Features

TBD
