---
title: Fix the PHP “Warning: count()” After an Upgrade, and the PHP 8 TypeError
url: https://ilogr.com/php5-to-php7-upgrade-warning-count-function-error/
published: 2020-01-07
updated: 2026-11-02
author: iLogr Team
categories: Development
reading_time: 8 min
description: Fix the count() warning in PHP 7.2 and the PHP 8 TypeError: find the null or false value, then return an empty array or use is_countable().
---

# Fix the PHP “Warning: count()” After an Upgrade, and the PHP 8 TypeError

**In short:** Since PHP 7.2, count() raises a warning when it receives null, false or any other value that is not an array or Countable object, and since PHP 8.0 it throws a TypeError. The fix is to make that value an array, not to silence the message.

You upgrade an older PHP 5 application to PHP 7.2 or later, open a page, and the screen fills with this:

```text
Warning: count(): Parameter must be an array or an object that implements Countable in /var/www/html/users.php on line 156
```

On PHP 8 the same line stops the page:

```text
Fatal error: Uncaught TypeError: count(): Argument #1 ($value) must be of type Countable|array, null given in /var/www/html/users.php:156
```

This post began as a note about the PHP 5 to PHP 7 upgrade and now covers the PHP 8 error as well. It shows what changed in each version, works through a database helper that returns null or false, and gives four fixes you can choose from. It also explains why one popular quick fix, casting to an array, hides a real bug.

**Before you run anything:** the code in this post changes how your application handles database results. Try it on a local copy or a staging site first, never directly on a live site, take a backup of the files and the database, and run it at your own risk. iLogr cannot see your application and accepts no responsibility for damage, data loss or downtime.

## What changed in count() between PHP 7.1, 7.2 and 8.0?

Until PHP 7.1, count() accepted any value without complaint. PHP 7.2 added a warning for a value that is neither an array nor an object that implements Countable, and the sizeof() alias behaves the same way. [1] [2] PHP 8.0 turned that warning into a TypeError. [1] [3]

This is what count() did with each kind of value in a test:

| Value passed to count() | PHP 7.0 and 7.1 | PHP 7.2 to 7.4 | PHP 8.0 to 8.5 |
| --- | --- | --- | --- |
| An array or a Countable object | The number of elements | The number of elements | The number of elements |
| null | 0, silently | 0, with a warning | TypeError |
| false | 1, silently | 1, with a warning | TypeError |
| A string, a number or an object that is not Countable | 1, silently | 1, with a warning | TypeError |

![Three columns compare count() on null and false. On PHP 7.0 and 7.1: count(null) gives 0 and count(false) gives 1, silently. On PHP 7.2 to 7.4: the same results of 0 and 1, with a warning triangle. On PHP 8.0 and later: a stop sign and a TypeError for both. A flag chip under the false row says a failed query counted as one row.](https://ilogr.com/wp-content/uploads/2020/01/count-null-false-by-php-version.webp)

*What count() does with null and false: silent results of 0 and 1 up to PHP 7.1, the same results with a warning on PHP 7.2 to 7.4, and a TypeError on PHP 8.0 and later.*

**Tested on** PHP 7.0.33, 7.1.33, 7.2.34, 7.3.33, 7.4.33, 8.0.30, 8.1.34, 8.2.34, 8.3.35, 8.4.26 and 8.5.11 in the official command-line containers. The results also agree with the PHP manual. [1] [3]

The last column also differs in its wording. For false, the message ends in `bool given` on PHP 8.0 to 8.2 and `false given` on PHP 8.3 and later, so search for the part up to `must be of type Countable|array`.

## Why does the old code fail, and was it ever correct?

The failing line is usually a check that decides whether a query returned anything:

```php
$result = $objDb->fetchArray('SELECT * FROM Users;');
if (count($result) > 0) {
    echo 'Yes';
} else {
    echo 'No';
}
```

A database helper written years ago often returns an array of rows when it finds something, null when it finds nothing and false when the query fails. PHP 5 counted all three without complaint, so the page worked. It was only working by accident, though, because count(false) is 1 and a failed query counted as one row.

In a test, a stand-in database helper returned three shapes: two rows, null for no rows and false for a failed query. With two rows, the original check printed Yes on every version from 7.0 to 8.5. With no rows it printed No, but with a warning on PHP 7.2 to 7.4 and a TypeError on PHP 8.0 to 8.5. With the failed query it printed Yes on PHP 7.0 and 7.1, Yes with a warning on 7.2 to 7.4, and threw on PHP 8. The warning was not a nuisance: it was the first sign that the old page had been taking the success branch after a failed query.

## How do you find the value that is not an array?

Read the file and line number in the message, then print the value on a new line just above the count() call:

```php
var_dump($result);
```

You will see `NULL` or `bool(false)` where you expected an array. If the file named in the message is inside `wp-includes`, a plugin or a theme, the code is not yours; the section on WordPress below applies.

To find every such call at once, run PHPStan at level 8. It reports a call to count() when the declared or documented return type of the function includes false (from level 7) or null (from level 8):

```bash
vendor/bin/phpstan analyse -l 8 src
```

In a test with a method documented as returning `array|false`, PHPStan 2.3.1 printed `Parameter #1 $value of function count expects array|Countable, array<int, array<string, int>>|false given` at levels 7 and 8, and nothing at levels 5 and 6. With `array|null` it reported the same error at level 8 only. [4] With no return type declared or documented, it reported only the missing type. So the check is only as good as the types in your code.

## How do you fix the count() error?

There are four fixes. Use the first where you can, because it cures every caller at once.

**1. Make the function return an array, always.** Change the helper so that no rows means an empty array, and a failed query is an error and not a value:

```php
public function fetchArray($sql)
{
    $rows = $this->runQuery($sql);   // an array, null when empty, false when the query failed
    if ($rows === false) {
        throw new RuntimeException('Query failed');
    }
    return $rows ?: [];
}
```

Callers can then use count() safely. PDOStatement::fetchAll() returns an empty array for zero rows. Before PHP 8.0 it could also return false on failure, so handle that case separately. [5]

**2. Ask whether there are any rows, without counting.** If the code only needs to know that something came back, empty() answers it for an array, null and false, on every PHP version: [6]

```php
if (!empty($result)) {
    echo 'Yes';
} else {
    echo 'No';
}
```

**3. Count only what can be counted (PHP 7.3 and later).** When you need the number, is_countable() checks for an array or a Countable object before counting. [7]

```php
$total = is_countable($result) ? count($result) : 0;
```

**4. Use is_array() on PHP 7.2 and earlier.** is_countable() does not exist before PHP 7.3, so use this form while you are still on an older version:

```php
$total = is_array($result) ? count($result) : 0;
```

In the test, all four gave the right answer for rows, no rows and a failed query on every version they could run on, with no warning: fix 1 raised the exception for the failed query, and fixes 2 to 4 returned No or 0 for both no rows and a failed query. If a failed query matters to your page, use fix 1 so that it is reported instead of treated as empty.

## Is casting the value to an array a safe fix?

No. It is often suggested, and it is wrong:

```php
$result = (array) $objDb->fetchArray('SELECT * FROM Users;');
```

A cast turns null into an empty array, which is right, but it turns false into `[false]`, an array with one element. count() then returns 1 and the page takes the success branch. In the test, a failed query printed Yes with this fix on every version from 7.0 to 8.5, with no warning to say why.

The null coalescing operator has a similar limit. `count($result ?? [])` handles null, but not false: on PHP 8.1 to 8.5 `count(false ?? [])` still throws the TypeError, because `??` only replaces null.

## What if the warning comes from WordPress, a theme or a plugin?

Look at the path in the message. If it names a file under `wp-includes`, `wp-admin`, `wp-content/plugins` or `wp-content/themes`, the call is not in your code. A reader of this post reported exactly this on PHP 7.4, from a WordPress core file. Do not edit core files or plugin files: the next update overwrites the change.

Update WordPress, the theme and the plugin instead. The WordPress compatibility handbook marks WordPress 4.9 as the first release listed as supporting PHP 7.2, 5.3 for PHP 7.4 and 5.6 for PHP 8.0. [8] If the warning remains after updating, use the file path in the message to identify the component, report the problem to its maintainer, and replace it if no supported fix is available.

On PHP 7.2 to 7.4 the warning does not stop the page, so you can log it and fix the cause in order. On PHP 8 there is no such option, because the call is fatal. Staying on an old PHP version to avoid the error is not a plan: PHP 7.2 reached end of life on 30 November 2020, PHP 7.4 on 28 November 2022 and PHP 8.1 on 31 December 2025. [9]

## How do you check the rest of the code before the next PHP upgrade?

Search for `count(` and `sizeof(` in your own code, then run PHPStan at level 8 on a copy as shown above. After that, run the pages and tests on the PHP version you are moving to, with errors displayed, because static analysis cannot see a value that only arrives at run time. Fix the warnings on PHP 7.4 first, then test on the target version for TypeErrors and other compatibility problems.

Choose the target from the support dates. PHP 8.2 receives security fixes until 31 December 2026, PHP 8.3 until 31 December 2027, PHP 8.4 until 31 December 2028 and PHP 8.5 until 31 December 2029. [10] A longer upgrade with Rector and PHPStan, and what the newer versions deprecate, is covered in the iLogr guide to [upgrading from PHP 8.2](https://ilogr.com/php-8-2-end-of-life-upgrade-rector-phpstan/).

**Next step:** Search your code for count( and sizeof(, make no rows return an empty array, report query failures separately, and run PHPStan at level 8 on a copy.

## References

1. [PHP Manual: count()](https://www.php.net/manual/en/function.count.php). Used for: the PHP 7.2 warning and PHP 8.0 TypeError history.
2. [PHP Manual: Migrating from PHP 7.1.x to PHP 7.2.x, Backward Incompatible Changes](https://www.php.net/manual/en/migration72.incompatible.php). Used for: the warning wording and the sizeof() alias.
3. [PHP Manual: Migrating from PHP 7.4.x to PHP 8.0.x, Backward Incompatible Changes](https://www.php.net/manual/en/migration80.incompatible.php). Used for: count() throwing a TypeError in PHP 8.
4. [PHPStan: Rule Levels](https://phpstan.org/user-guide/rule-levels). Used for: what levels 5, 7 and 8 check and the analyse command.
5. [PHP Manual: PDOStatement::fetchAll()](https://www.php.net/manual/en/pdostatement.fetchall.php). Used for: the empty array returned when there are no results.
6. [PHP Manual: empty()](https://www.php.net/manual/en/function.empty.php). Used for: empty() treating null, false and an empty array as empty.
7. [PHP Manual: is_countable()](https://www.php.net/manual/en/function.is-countable.php). Used for: the function being available from PHP 7.3.
8. [Make WordPress Core Handbook: PHP Compatibility and WordPress Versions](https://make.wordpress.org/core/handbook/references/php-compatibility-and-wordpress-versions/). Used for: which WordPress versions are listed for PHP 7.2, 7.4 and 8.0.
9. [PHP: Unsupported Branches](https://www.php.net/eol.php). Used for: end-of-life dates of PHP 7.2, 7.4 and 8.1.
10. [PHP: Supported Versions](https://www.php.net/supported-versions.php). Used for: security support end dates of PHP 8.2 to 8.5.
