PHP's resource to object transformation
resource
is a special type in PHP that holds a reference to an external resource, hence its name. One of the long-term goals of PHP development is to phase out the use of resource
in favor of standard class objects, that are more familiar with PHP users, supports typing in parameters, return types, and properties, and has a unified approach in handling in PHP internals.
Problems with Resources
Resources lead several back into the history, to PHP 4 era, where classes were not very commonly used, and exceptions were not often used either.
The resource
type is not a type that you can coerce/cast to, or even a type that can be enforced with standard PHP parameter/return/property types. This is the case for all PHP versions, including PHP 8. With PHP 8 supporting scalar types and internal functions with type error support, the lack of resource
type holds back some of the resource
-related functionality to effectively add stricter types.
Internally, it brings challenges to PHP source code to maintain several resource types with its internal state, memory handling, reference counting, and other features which could use standard PHP classes.
Resource to Object Migration
One of the long-term efforts of PHP project is to migrate all resource objects to standard class objects.
Given the long-running history of PHP resource
objects, they are often used extensively in PHP applications. A migration to class objects needs to be as least disruptive as possible.
Maintain Parameter and Return Type consistency
PHP resource
objects do not provide an object-oriented interface, that it contains methods to call. All operations are done using functions.
A function that opens a resource (such as curl_init
) will continue to return a value, but after the migration, it will return a resource class object
instead.
All functions that previously accepted a resource
are then modified to accept the new class object instead.
This approach maintains the majority of backwards-compatibility, because it requires zero modifications on the user's code; the internal code is different, but the API remains consistent.
Restrict Resource Class Objects
The newly migrated resource class objects can be created with the same functions that previously created the resource
objects.
In PHP's resource to object migration plan, the classes are heavily restricted at this stage.
- The classes are declared
final
, preventing from being extended. - The classes cannot be instantiated with
new CurlHandle
pattern. Users must use the same functions to instantiate them.
Resource Closing
Traditional resource
objects required a resource-specific function to close a resource.
For example, a curl
resource opened by curl_init
function is closed with curl_close
function. PHP tries to close the resource automatically when there are no references to the object, but it is a good practice to explicitly close the resources to avoid potential memory leaks.
In resource to object migration, the resource-closing functions are not removed, as it would cause a backwards-compatibility break. They are either deprecated, or turned into no-op functions that do not make any changes to the object.
Caveats When Migrating
is_resource()
function returns false
for resources that are closed, and the use of is_resource
to validate a resource when it is accepted as a parameter, or right after creating a resource is quite common in many PHP applications.
After the migration, is_resource
calls will return false
, and can cause problems.
For the compatibility with all PHP versions, it might be necessary to check against the new class objects too:
- is_resource($curl_handle)
+ is_resource($curl_handle) || $curl_handle instanceof \CurlHandle
This is still an anti-pattern, because a failed resource creation function returns false
instead of a resource (prior to migration) or a class object (after migration). A check against not false
still more readable, accurate, and compatible across all PHP versions.
- is_resource($curl_handle)
+ $curl_handle !== false
Future Scope
When a traditional resource
type is migrated to standard PHP classes, the functions that handle resource operations can be incorporated to class methods (e.g. curl_setopt
to CurlHandle::setOpt
).
This can simplify the code and improve developer experience, while removing several functions that were once scattered.
As of now, the new resource class objects are restricted from being extended, or let alone being instantiated with the new Foo()
construct. This ensures that once PHP improves the class objects, there will be no further backwards-compatibility issues.
In PHP 8.0
In PHP 8.0, some of the most used extensions changes moves away from the traditional resource
objects to standard PHP classes.
In PHP 8.0, they are work as value-objects as opposed to fully-featured classes with methods in them. Majority of these classes do not allow instantiating with the new Foo()
construct either, and must be instantiated with the existing functions that returned resource
objects in prior versions.
PHP 8.0's resource
to object migration is quite seamless, as in all functions return and accept the new objects, and behave by the same semantics of the previous resource
objects.
Extension | resource (PHP < 8.0) |
object (PHP >= 8.0) |
---|---|---|
Curl | Curl |
CurlHandle |
Curl | curl_multi |
CurlMultiHandle |
Curl | curl_share |
CurlShareHandle |
GD | gd |
GdImage |
Sockets | Socket |
Socket |
Sockets | AddressInfo |
AddressInfo |
OpenSSL | OpenSSL key |
OpenSSLAsymmetricKey |
OpenSSL | OpenSSL X.509 |
OpenSSLCertificate |
OpenSSL | OpenSSL X.509 CSR |
OpenSSLCertificateSigningRequest |
XMLWriter | xmlwriter |
XMLWriter |
XML | xml |
XMLParser |
In PHP 8.1
PHP continues the migration in PHP 8.1 as well. With the Namespaces in bundled PHP extensions RFC passed for PHP 8.1, new classes declared on bundled extensions use namespaces.
Extension | resource (PHP < 8.1) |
object (PHP >= 8.1) |
---|---|---|
GD | gd font (integer ) |
GdFont |
FTP | ftp |
FTP\Connection |
IMAP | imap |
IMAP\Connection |
finfo | file_info |
finfo |
PSpell | pspell (int ) |
PSpell\Dictionary |
PSpell | pspell config (int ) |
PSpell\Config |
LDAP | ldap link |
LDAP\Connection |
LDAP | ldap result |
LDAP\Result |
LDAP | ldap result entry |
LDAP\ResultEntry |
PgSQL | pgsql link |
\PgSql\Connection |
PgSQL | pgsql result |
\PgSql\Result |
PgSQL | pgsql large object |
\PgSql\Lob |