PHP 8.0: CurlHandle class objects replace curl handlers


One of the long-term goals in PHP is to convert resource type to appropriate class objects. In PHP 8, the Curl functionality is transformed into class object based resources.

Resource to Object Migration
PHP is gradually phasing out all resource types with class objects, and this migration is one step of the Resource to Object Migration plan.

Init functions return objects

Curl init functions curl_init, curl_multi_init, and curl_share_init functions returned PHP resources prior to PHP 8. It was not possible enforce typing in those functions, or any other user-land functionality that used them.

From PHP 8, these functions return objects. You still have to use init functions to create these objects; it is not allowed to new CurlInit() (or any other objects for that matter).

curl_init() now returns a \CurlHandle object

Prior to PHP 8, when you call curl_init(), it returned a PHP resource of type Curl.

From PHP 8 and forward, curl_init function returns an instance of \CurlHandle class.

- /**
-  * @return resource|false 
-  */
- function curl_init() {}
+ function curl_init(): \CurlHandle|false {}
final class CurlHandle {}

curl_multi_init() now returns a CurlMultiHandle object

Similar to curl_init() in PHP 8, curl_multi_init() now returns a \CurlMultiHandle object instead of a resource with type curl_multi.

- /**
-  * @return resource 
-  */
- function curl_multi_init() {}
+ function curl_multi_init(): \CurlMultiHandle {}
final class CurlMultiHandle {}

curl_share_init() now returns a \CurlShareHandle object

curl_share_init() now returns a \CurlShareHandle object instead of a resource with type curl_share.

- /**
-  * @return resource
-  */
- function curl_share_init() {}
+ function curl_share_init(): \CurlShareHandle {}
final class CurlShareHandle {}

All these newly added classes, CurlHandle, CurlMultiHandle, and CurlShareHandle follow resource-to-object semantics.

  • Directly instantiating objects is not allowed. For example, new CurlHandle will result in an error:

    new CurlHandle();
    new CurlMultiHandle();
    new CurlShareHandle();
    Cannot directly construct CurlHandle, use curl_init() instead in ... on line ...
    Cannot directly construct CurlMultiHandle, use curl_init() instead in ... on line ...
    Cannot directly construct CurlShareHandle, use curl_share_init() instead in ... on line ...
  • All classes are declared final. Extending them is not allowed:

    class Foo extends CurlHandle {}
    Class Foo may not inherit from final class (CurlHandle) in ... on line ...
  • No dynamic properties are allowed. Attempting to set a dynamic property will result in an error:

    $ch = curl_init();
    $ch->foo = 'Bar';
    Cannot create dynamic property CurlHandle::$foo in ... on line ...

All curl_* functions accept Curl* objects instead of resources

PHP does not have a resourcetype, so it was not possible to enforce a parameter or return type for any of the curl_ functions. This conveniently makes this resource to Curl* object transform not introduce backwards-incompatibilities.

is_resource() returns false on Curl* objects

Because the Curl init function return values are standard PHP objects, is_resource function returns false which is the correct bahavior for that function.

This might break your existing code because it is common to check if the curl_init() call was successful with a is_resource($handle) call.

Note that in PHP 8 and in old versions, curl_(multi_|share_)init() functions return false on failed resource creation.

To make sure the code is compatible with both PHP 8 and older versions, you can change is_resource($handle) calls with $handle !== false calls.

  $handle = \curl_init();
- if (!\is_resource($handle)) {
+ if ($handle === false) {
    throw new \Exception();

curl_close no longer closes the resource

Because Curl resources are now objects, Curl handles are closed when the object is no longer referenced, or explicitly destroyed.

You can still call curl_close($handle), but this function no longer has any effect.

In your existing code, you can explicitly unset() the CurlHandle object. PHP will automatically do it following standard object garbage collection mechanisms.

  $handle = curl_init();
  // ... use it ... 
+ unset($handle);

Backwards compatibility impact

Calling is_resource() on Curl init function return values will now return false, which breaks existing code. For cross-version compatibility, always use $handle === false which will correctly evaluate in all PHP versions.

PHP has no resource type declared in a way that you can enforce it in user-land code. This mitigates any possible typed functions.

Furthermore, curl_close no longer effectively closes the Curl handler. Because the handlers are now objects, the resources will be destroyed during garbage collection, unless the handler is explicitly closed with an unset() call. This unset() call can be used in older PHP versions without breaking the functionality. See the example in curl_close section above

It is not possible to backport this functionality to older PHP versions. This means that if you start to enforce CurlHandle, CurlShareHandle, and CurlShareHandle types, that code will not work in older PHP versions.