Hmm. Here's a section from count:

Returns the number of elements in array_or_countable. When the parameter is neither an array nor an object with implemented Countable interface, 1 will be returned. There is one exception, if array_or_countable is NULL, 0 will be returned.

I recently noticed the error_log barking about foreach() ["invalid argument supplied"]. Here's the code:

if (isset($products) && count($products)) {
    foreach ($products as $item) {

Generally, $products should be an array rather than an object. I think I've seen this error popping up in several places and worked around it a few times with something like count($products) > 0 ... but my question is how does an invalid argument pass the conditional in the first place? Have I been misunderstanding count() all along? Has it's behavior been modified since, oh, I dunno ... 2003? 😃

    I guess you could add is_iterable() to your conditions?

      I could, and I might, but I'd like to understand the issue first.

      is_iterable() didn't exist before PHP7. So did PHP change the way count() works and I missed the memo (which might very well be the lingo from the manual I posted above), or did I just not realize my coding was broken between 200X and December 2015?

      Hmm...should it always be an array, or do you want to allow for iterable objects, as well? You could test for is_array(), or if either maybe use && (is_array($product) || $product instanceof Traversable) -- but that's getting pretty ugly. 🙁

      In any case, TIL that the Ven diagram of what count() and foreach() accept do not completely overlap.

      dalecosp So did PHP change the way count() works and I missed the memo

      count did change its behaviour in 7.2 to not just return "1" for non-countable things (non-arrays and objects that don't implement Countable). but also trigger a warning saying as much. But that's not the error message you're getting.

      The behaviour of foreach hasn't changed: it still loops over arrays and objects, and complains if it's given something else; if the object doesn't implement Iterable for custom iteration then foreach happily casts it to an array first, turning its properties into name=>value pairs.

      So in that sense: yes, it has been broken all this time. In another sense, it looks like what has broken is whatever created $products. (Y'know, the count shouldn't be necessary anyway: if $products has nothing to iterate over then the foreach will loop zero times and isn't that what you want?)

      NogDog TIL that the Ven diagram of what count() and foreach() accept do not completely overlap.

      Obviously there are things that are iterable but not countable—but now I'm trying to think of something that's countable but not iterable; when will you have a collection that you might want to know the population of but never the actual elements of the collection? Thanks for that 👍.

      Thank you, gentlemen ... you're hitting pretty close here.

      NogDog getting pretty ugly

      We're pretty pragmatic here ... I'm not above ugly if it works.

      Weedpacket when will you have a collection that you might want to know the population of but never the actual elements of the collection?

      Pretty sure we just want to know if the collection's empty or not; see below.

      Weedpacket if $products has nothing to iterate over then the foreach will loop zero times and isn't that what you want?

      I'm pretty sure the conditional was put there to decide whether to display the collection or show a nice error page saying "there's nothing here for the params specified." The foreach() is in the non-error loop; it seemed to me, ever so long ago, that if count() returned non-zero for an array, that meant that we had a non-empty collection and foreach() could operate. I need to pin down the exact state/nature of $products when we get this error; obviously foreach() works as expected thousands of times a day and we see this only a couple dozen times a day in an errorlog.

      In a more basic setting, or the Elder Days, I'd probably do something likeif ($result->num_rows) {, but we've got things being held in objects, so in many cases $products is actually $category->products or $search->products and the SQL's long since over && done. I guess we could refactor all those objects so they'd know (and advertise?) if they had rows, but then there's the issue of pagination which will also be touched ... which was also "solved" quite some time ago and will probably make a refactor a tad more challenging; off the top of my head I can't remember what we did there (seems it was maybe some math to figure beginning and ending numbers for the SQL LIMIT clause ... maybe it wouldn't be terrible.

      Not to mention I have several projects with pending changes to be committed and the Tracker hates me:

      @MantisBT Viewing Issues 1 - 50 / 160 😬

      Those are the unresolved ones ....

        Write a Reply...