Opaque Objects & GdImage/CURLHandle
debt(d3/e1/b2/t6)
Closest to 'default linter catches the common case' (d3), PHPStan and Rector (listed in detection_hints) readily flag is_resource() calls on GdImage/CurlHandle and missing type hints — this is a well-known migration pattern.
Closest to 'one-line patch or single-call swap' (e1), quick_fix is literally replacing is_resource() with instanceof GdImage/CurlHandle — a mechanical one-line swap Rector can automate.
Closest to 'localised tax' (b3) but lighter, scoring b2 — the choice only affects functions interacting with GD/cURL/XML extensions, not the broader codebase shape; it's a localised concern at extension boundaries.
Closest to 'serious trap' (t7) leaning down to t6 — the misconception is that these behave like resources (legacy PHP 7) or like regular objects, but is_resource() silently returns false in PHP 8+, contradicting decades of PHP resource idioms; clone/serialize also unexpectedly fail.
TL;DR
Explanation
Before PHP 8.0, many extensions returned resource types (is_resource() check, no type hints). PHP 8.0+ returns typed objects: imagecreatetruecolor() returns GdImage, curl_init() returns CurlHandle, xml_parser_create() returns XMLParser. These are opaque — you can't inspect their internals — but they're proper objects with type hints and are accepted by instanceof. Old code using is_resource() checks breaks. Type hints now possible: function process(GdImage $image). These objects are still reference-counted and freed when no longer referenced, similar to resources.
Common Misconception
Why It Matters
Common Mistakes
- Using is_resource() on GdImage/CurlHandle in PHP 8.0+ — always returns false.
- Not type-hinting function parameters accepting GdImage/CurlHandle.
- Trying to clone or serialize opaque objects — not supported.
Code Examples
$image = imagecreatetruecolor(100, 100);
if (!is_resource($image)) { // Always false in PHP 8!
throw new Exception('Failed to create image');
}
$image = imagecreatetruecolor(100, 100);
if (!($image instanceof \GdImage)) {
throw new \RuntimeException('Failed to create image');
}
function applyFilter(\GdImage $img, int $filter): void {
imagefilter($img, $filter);
}