Question

Handling PHP Warnings with try/catch and Error Handlers

phperror-handlingtry-catch

Question

How can I handle warnings produced by native PHP functions such as dns_get_record()?

array|false dns_get_record(
    string $hostname,
    int $type = DNS_ANY,
    array &$authns = null,
    array &$addtl = null
)

When a DNS query fails, the function may emit a warning. A try/catch block does not catch it because a PHP warning is not an exception.

Should I install an error handler, suppress or hide the warning and inspect the return value, or use another approach? What is the best practice?

Short Answer

PHP warnings and exceptions are separate error-reporting mechanisms. You will learn how to handle a warning from a single native-function call without globally changing application behavior, and how to use the function's documented return value as the primary result signal.

Concept

A PHP warning is a runtime diagnostic: PHP reports that something went wrong but usually allows the script to continue. An exception is an object that changes control flow until code catches it (or it reaches the top of the program).

try {
    // This catches thrown Exception or Throwable objects.
} catch (Throwable $error) {
    // A normal PHP warning does not arrive here.
}

Native functions commonly report recoverable failures in two ways:

  1. They emit a warning for diagnostic information.
  2. They return a special failure value, often false.

For dns_get_record(), check its return value first. A failed query is represented by false; a successful lookup returns an array, which can be empty when the lookup succeeds but produces no matching records.

If the warning is expected and you need to prevent it from being displayed or logged, install a temporary, narrowly scoped error handler around only that call. The handler can turn the relevant warning into an ErrorException, which your normal try/catch code can handle.

This matters because warnings should not be silently ignored across an entire application. A scoped handler keeps the special treatment limited to the operation that expects the failure.

Mental Model

Think of a warning as a dashboard warning light and an exception as an emergency stop.

  • A dashboard light tells you about a problem, but the car keeps moving. That is like a PHP warning.
  • An emergency stop interrupts the journey until someone deals with it. That is like an exception.

try/catch is designed for emergency stops, not dashboard lights. A temporary error handler is like assigning someone to watch one particular warning light during one specific test drive and report it as an emergency if it appears.

Take Quiz

Syntax and Examples

dns_get_record() returns either an array of records or false when it cannot complete the lookup.

<?php

$records = dns_get_record('example.com', DNS_A);

if ($records === false) {
    echo 'The DNS lookup failed.';
} elseif ($records === []) {
    echo 'The lookup completed, but no A records were returned.';
} else {
    foreach ($records as $record) {
        echo $record['ip'] . PHP_EOL;
    }
}

Use strict comparison:

  • === false means the function reported failure.
  • === [] means it returned a valid but empty array.

If you also need to intercept the warning, set a handler immediately before the call and restore the previous handler immediately afterward:

<?php

set_error_handler(
    function (int $severity, string $message, string $file, int $line): never {
        throw new ErrorException($message, 0, $severity, $file, $line);
    },
    E_WARNING
);

try {
    $records = dns_get_record('invalid-hostname.example', DNS_A);
} catch (ErrorException $exception) {
    $records = false;
    error_log('DNS lookup warning: ' . $exception->getMessage());
} finally {
    restore_error_handler();
}

The second argument, E_WARNING, limits this handler to warnings. The finally block is essential: it restores the previous handler whether the call succeeds or fails.

Step by Step Execution

Consider this helper that returns null rather than exposing PHP's warning behavior to the rest of an application:

<?php

function lookupARecords(string $hostname): ?array
{
    set_error_handler(
        function (int $severity, string $message, string $file, int $line): never {
            throw new ErrorException($message, 0, $severity, $file, $line);
        },
        E_WARNING
    );

    try {
        $records = dns_get_record($hostname, DNS_A);

        return $records === false ? null : $records;
    } catch (ErrorException $exception) {
        return null;
    } finally {
        restore_error_handler();
    }
}

Execution for a failing lookup:

  1. set_error_handler() installs a handler for E_WARNING only.
  2. dns_get_record() attempts the DNS lookup.
  3. PHP emits a warning instead of returning normally without diagnostics.
  4. The temporary handler receives that warning and throws an ErrorException.
  5. Control jumps to catch, which returns null to represent a failed lookup.
  6. finally runs even after the exception and restores the prior error handler.

Execution for a successful lookup:

  1. No warning is emitted.
  2. dns_get_record() returns an array.
  3. The helper returns that array.
  4. finally restores the previous handler.

Real World Use Cases

Warnings from native functions often represent external or environment-dependent failures that an application can handle gracefully.

  • DNS tools and monitoring: Show "lookup unavailable" instead of exposing a PHP warning to a visitor.
  • Optional remote resources: Handle a temporary failure while reading data from an external service.
  • File operations: Treat an unavailable optional file as a recoverable application condition while still logging it.
  • Image and document processing: Convert a library warning into a clear validation error for an uploaded file.
  • Legacy integrations: Wrap older PHP APIs that signal errors with warnings and false, then expose a consistent application-level result.

For user-facing applications, do not display raw warnings in production. Log useful diagnostic details while returning a safe message or fallback result.

Take Quiz

Real Codebase Usage

In real projects, developers usually isolate warning-producing APIs behind a small wrapper function. The rest of the code then works with predictable return values or domain-specific exceptions.

A common pattern is validation plus a guard clause:

<?php

function getMailServerRecords(string $domain): array
{
    if ($domain === '') {
        return [];
    }

    $records = dns_get_record($domain, DNS_MX);

    if ($records === false) {
        return [];
    }

    return $records;
}

When the warning itself must be controlled, keep the error handler inside the wrapper. This prevents unrelated warnings elsewhere in the request from being converted or suppressed.

A more expressive application design can throw a custom exception:

<?php

final class DnsLookupException extends RuntimeException
{
}

The wrapper can catch ErrorException, log technical details, and throw DnsLookupException. Controllers, commands, or API endpoints can then decide whether to retry, return a 503 response, or use cached data.

Use global error handlers for application-wide policies, such as central logging or converting selected PHP errors throughout the whole application. Do not install one merely to handle one expected call unless it is carefully scoped and restored.

Common Mistakes

Expecting catch to receive a warning

<?php

try {
    dns_get_record('invalid-hostname.example');
} catch (Exception $exception) {
    // Normal PHP warnings do not reach this block.
}

A warning is not automatically an Exception. Use the return value, or explicitly convert the selected warning with an error handler.

Forgetting to restore the error handler

<?php

set_error_handler($handler, E_WARNING);
$records = dns_get_record($hostname);
// The handler is still active for all later E_WARNING messages.

This can accidentally change how unrelated code behaves. Always use restore_error_handler() in finally.

Treating an empty array as failure

<?php

if (!$records) {
    // This is true for both false and an empty array.
}

Use strict checks instead:

if ($records === false) {
    // Failure.
}

Using @ as normal error handling

<?php

$records = @dns_get_record($hostname);

The @ operator hides diagnostics and can make debugging and monitoring harder. It also does not explain why failure occurred. Prefer documented return values, a scoped error handler when necessary, and logging.

Catching every PHP error indiscriminately

Converting all error levels into exceptions can change the behavior of dependencies and framework code. Restrict the handler to the relevant level, such as E_WARNING, and to the smallest possible code region.

Comparisons

ApproachWhat it handlesBest useMain caution
Check return valueThe API's documented success or failure resultMost native PHP function callsIt may not stop the warning from being displayed or logged
try/catchThrown Throwable objectsAPIs that throw exceptions or errorsDoes not catch ordinary PHP warnings by itself
Scoped set_error_handler()Selected PHP error levels during a small code regionWrapping one warning-producing legacy/native callMust restore the previous handler
Global error handlerErrors across the applicationCentral logging and consistent application policyCan unexpectedly affect unrelated code
@ error suppressionDisplay/reporting suppression for one expressionRare legacy situationsHides useful diagnostics; generally avoid

A useful rule is: use the return value to decide the outcome; use a scoped handler only when you must control or transform the warning itself.

Take Quiz

Cheat Sheet

// Primary result check
$records = dns_get_record($hostname, DNS_A);

if ($records === false) {
    // Lookup failed
}

if ($records === []) {
    // Lookup succeeded but returned no matching records
}
// Temporarily convert warnings into ErrorException objects
set_error_handler(
    function (int $severity, string $message, string $file, int $line): never {
        throw new ErrorException($message, 0, $severity, $file, $line);
    },
    E_WARNING
);

try {
    $records = dns_get_record($hostname, DNS_A);
} catch (ErrorException $exception) {
    // Handle warning as an exception
} finally {
    restore_error_handler();
}
  • try/catch catches thrown Throwable objects, not ordinary warnings.
  • Check === false, not if (!$value), when an empty result is valid.
  • Limit an error handler with an error-level mask such as E_WARNING.
  • Always restore a temporary handler in finally.
  • Prefer logging and explicit failure handling over @ suppression.

FAQ

Can PHP try/catch catch warnings?

Not directly. Ordinary PHP warnings are not exceptions. A custom error handler can convert a warning into an ErrorException, which catch can then handle.

Does set_error_handler() handle every warning on the page?

It handles matching errors that occur while it is installed. If you install it only immediately around one call and restore it in finally, it affects only that small section of code.

Should I check dns_get_record() for false or an empty array?

Check both when the distinction matters. false means failure; an empty array is a successful call with no matching records returned.

Is it safe to hide PHP warnings in production?

Raw warnings should generally not be displayed to users in production. However, hiding output is not enough: handle the failure through return values or exceptions and log useful diagnostic information.

Should I use the @ operator with dns_get_record()?

Usually no. It suppresses useful diagnostics and makes failures harder to observe. A return-value check and, if needed, a scoped error handler are clearer.

Why use finally with restore_error_handler()?

finally runs whether the code succeeds, returns, or throws. It guarantees that later code does not accidentally keep using your temporary handler.

Should I catch Exception or Throwable in PHP?

When handling thrown values at an application boundary, Throwable is broader. For a handler that intentionally throws ErrorException, catching ErrorException is more precise.

Take Quiz

Mini Project

Description

Build a small DNS lookup helper for a command-line script. The helper will convert a warning from dns_get_record() into a predictable null result, while preserving PHP's previous error handler after the lookup completes.

Goal

Create a function that returns IPv4 DNS records for a hostname or null when the lookup cannot be completed.

Requirements

  • Accept a hostname as a string.
  • Query only A records with dns_get_record().
  • Return an array for a successful lookup, including an empty array when no A records are returned.
  • Return null if the lookup emits a warning or reports false.
  • Limit warning conversion to the DNS lookup call.
  • Restore the previous PHP error handler in all cases.
Take Quiz

Keep learning

Related questions

View more