php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #77535 Invalid callback, h2 server push
Submitted: 2019-01-28 19:21 UTC Modified: 2019-03-15 18:22 UTC
From: tobias dot nyholm at gmail dot com Assigned: pmmaga (profile)
Status: Closed Package: cURL related
PHP Version: master-Git-2019-01-28 (Git) OS:
Private report: No CVE-ID: None
 [2019-01-28 19:21 UTC] tobias dot nyholm at gmail dot com
Description:
------------
When I set my callback in a different function I get a warning. I cannot figure out why. 

If I move the contents of addServerPushCallback() to sendRequest() then I wont have the same issue. 

Test script:
---------------
<?php

class MyHttpClient
{
    private $mh;
    private $curl;

    public function sendRequest()
    {
        if (false === $this->mh = curl_multi_init()) {
            throw new \RuntimeException('Unable to create a new cURL multi handle');
        }

        // FIXME Using a function like this does not work. If we inline the contents of addServerPushCallback(), then we got no problem
        $this->addServerPushCallback();

        $this->curl = curl_init();
        curl_setopt($this->curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
        curl_setopt($this->curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
        curl_setopt($this->curl, CURLOPT_HEADER, false);
        curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, false);
        curl_setopt($this->curl, CURLOPT_FAILONERROR, false);
        curl_setopt($this->curl, CURLOPT_URL, 'https://http2.golang.org/serverpush');
        curl_setopt($this->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);

        curl_setopt($this->curl, CURLOPT_HEADERFUNCTION, function ($ch, $data) {
            return \strlen($data);
        });

        curl_setopt($this->curl, CURLOPT_WRITEFUNCTION, function ($ch, $data) {
            return \strlen($data);
        });

        curl_multi_add_handle($this->mh, $this->curl);


        $stillRunning = null;
        while (true) {
            do {
                $mrc = curl_multi_exec($this->mh, $stillRunning);
            } while (CURLM_CALL_MULTI_PERFORM === $mrc);

            $info = curl_multi_info_read($this->mh);
            while (false !== $info && $info['msg'] == CURLMSG_DONE) {
                if (CURLMSG_DONE !== $info['msg']) {
                    continue;
                }
                die("Start handle request. ");
            }
        }
    }

    private function addServerPushCallback(): void
    {

        $callback = static function () {
            return CURL_PUSH_OK;
        };

        curl_multi_setopt($this->mh, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
        curl_multi_setopt($this->mh, CURLMOPT_PUSHFUNCTION, $callback);
    }
}

$buzz = new MyHttpClient();
$buzz->sendRequest();


Expected result:
----------------
Output only "Start handle request."

Actual result:
--------------
Warning: Invalid callback , no array or string given in /usr/src/myapp/test.php on line 40

Warning: curl_multi_exec(): Cannot call the CURLMOPT_PUSHFUNCTION in /usr/src/myapp/test.php on line 40


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2019-01-28 20:41 UTC] requinix@php.net
What about if you have the code in sendRequest and use

curl_multi_setopt($this->mh, CURLMOPT_PUSHFUNCTION, static function () {
  return CURL_PUSH_OK;
});

without putting the callback into a variable?
 [2019-01-28 20:53 UTC] tobias dot nyholm at gmail dot com
Interesting. 

If I don't use a variable and I set CURLMOPT_PUSHFUNCTION in sendRequest() then I also get the warning.
 [2019-01-28 21:05 UTC] tobias dot nyholm at gmail dot com
Btw, You can test this with this repo: https://github.com/Nyholm/php-test-server-push

There is a test called "test77535.php"
 [2019-01-28 21:12 UTC] pmmaga@php.net
IIRC, and according to the warning, the current implementation is only accepting callables but not Closures. If you make the callback as a named function it should be possible to set it up.
 [2019-01-28 21:39 UTC] tobias dot nyholm at gmail dot com
Using a callback like "[$this, '_callback']" does not work. I did manage to declare a global function foobar() and use "foobar" as callback. But that seams suboptimal.
 [2019-02-02 13:04 UTC] tobias dot nyholm at gmail dot com
I think I've figured it out. (Or, I got help figure this out)

It is related to the garbage collector. If I do $this->tmp   $callback = function.... Then I've got not issues.
 [2019-03-15 16:10 UTC] nikic@php.net
I've committed https://github.com/php/php-src/commit/97f9fd6949a52d1eb30c3e1c90f0789b57c29468 to fix this, but don't have a curl to test with (segfaults for other reasons).
 [2019-03-15 18:22 UTC] pmmaga@php.net
-Status: Open +Status: Closed -Assigned To: +Assigned To: pmmaga
 [2019-03-15 18:22 UTC] pmmaga@php.net
This issue has been fixed by the commit from NikiC and I've pushed a test via https://github.com/php/php-src/commit/f167b06d4c86c96291c21c027ba3cae22f5b5be8
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Apr 19 07:01:27 2024 UTC