pusher / pusher/pusher-http-php

getChannels() crashes on servers returning an empty channels array — the guard exists in process_trigger_result() but not here

Open Beginner friendly
#408 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
PHP
Stars
1.5k
Forks
317
PR merge metrics
No merged PRs in 30d

Description

Summary

getChannels() calls get_object_vars() without a type check, so a server response where channels is an empty JSON array crashes the client:

TypeError: get_object_vars(): Argument #1 ($object) must be of type object, array given
  src/Pusher.php:664

The interesting part: this library already guards against exactly this, but only on one code path.

process_trigger_result() (added in #323, fixing #322) checks the type:

https://github.com/pusher/pusher-http-php/blob/master/src/Pusher.php#L1195-L1197

if (property_exists($result, 'channels') && is_object($result->channels)) {
    $result->channels = get_object_vars($result->channels);
}

getChannels() does not:

https://github.com/pusher/pusher-http-php/blob/master/src/Pusher.php#L660-L666

public function getChannels(array $params = []): object
{
    $result = $this->get('/channels', $params);

    $result->channels = get_object_vars($result->channels);

    return $result;
}

Why it happens in practice

Pusher-protocol implementations other than Pusher itself are common now (Reverb, Sockudo, Soketi). I hit this with Laravel Reverb v1.11.1, which serialises an empty channel list as [] instead of {} — I reported it there as laravel/reverb#402, and it should be fixed on their side too.

But the failure mode is worth guarding here regardless, because it is silent and intermittent: the response is an array only while no channel is occupied. One connected client turns the PHP array into a string-keyed map, json_encode emits an object, and the crash disappears. So it reproduces on idle systems and vanishes under load.

Measured side by side, same signed request:

Reverb   →  {"channels":[]}
Sockudo  →  {"channels":{}}

Suggestion

Apply the same guard that process_trigger_result() already uses:

$result->channels = is_object($result->channels) ? get_object_vars($result->channels) : (array) $result->channels;

This keeps the documented return shape (channels as an array of channel names) for every server, instead of a fatal error for some of them. Happy to send a PR.

Environment

  • pusher/pusher-php-server ^7.2
  • PHP 8.3

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/Pusher.php at getChannels(), then compare its channels handling with process_trigger_result(), which already guards non-object responses. Exercise the method with an empty channels array and verify it returns the documented array shape without a TypeError; run the relevant project tests to confirm existing behavior remains intact.

Written by the indexing model from the issue text.

Assessment

Tech stack
php
Domain
api
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
84/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.