php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #79144 FFI call exported function without referencing it
Submitted: 2020-01-20 21:27 UTC Modified: 2020-01-20 22:53 UTC
From: php at tim dot ainfach dot de Assigned:
Status: Not a bug Package: *Extensibility Functions
PHP Version: 7.4.1 OS: OSX 10.14.6
Private report: No CVE-ID: None
 [2020-01-20 21:27 UTC] php at tim dot ainfach dot de
Description:
------------
when using a function that is exported by php (ZEND_API) it's not possible to call the function directly. You've to create a reference to the function and then it is possible to call it.

i would expect that it is possible to call the function directly.

these are working fine:
$a = $zend->zend_write; $a("aaa", 3);
($zend->zend_write)("aaa", 3);
(clone $zend->zend_write)("aaa", 3);

this doesn't work -> Attempt to call undefined C function 'zend_write'
$zend->zend_write("aaa", 3);
$a = [$zend, 'zend_write']; $a("aaa", 3);

Test script:
---------------
    public static function assertObSame(string $expected, callable $cb)
    {
        ob_start();
        $cb();
        $res = ob_get_contents();
        ob_end_clean();

        static::assertSame($expected, $res);
    }

    public function testReturnU64()
    {

        $zend = FFI::cdef("
    typedef int (*zend_write_func_t)(const char *str, size_t str_length);
    extern zend_write_func_t zend_write;
        ");

        static::assertObSame('aaa', function() use ($zend) { $a = $zend->zend_write; $a("aaa", 3); });
        static::assertObSame('aaa', function() use ($zend) { ($zend->zend_write)("aaa", 3); });
        static::assertObSame('aaa', function() use ($zend) { (clone $zend->zend_write)("aaa", 3); });

        // does not work -> end up in a Attempt to call undefined C function 'zend_write'
        static::assertObSame('aaa', function() use ($zend) { $zend->zend_write("aaa", 3); });
        static::assertObSame('aaa', function() use ($zend) { $a = [$zend, 'zend_write']; $a("aaa", 3); });

    }

Expected result:
----------------
all ways to call zend_write should give the same result (pass the assertion).

Actual result:
--------------
FFI\Exception: Attempt to call undefined C function 'zend_write'

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2020-01-20 21:53 UTC] requinix@php.net
-Status: Open +Status: Not a bug
 [2020-01-20 21:53 UTC] requinix@php.net
$zend->zend_write is a property, not a method. It's as if you wrote

$zend = new stdClass();
$zend->zend_write = function(string $str, int $str_length) { ... };
 [2020-01-20 22:02 UTC] php at tim dot ainfach dot de
thank you for the explanation.

it feels very inconsitent because for example if i export a function from C like here https://bugs.php.net/bug.php?id=79125 i am able fo call $ffi->FUNCTION() directly.

there is a reason why different approaches were chosen here?
 [2020-01-20 22:53 UTC] requinix@php.net
In other other ticket,
> struct bug79096 bug79096(void);
bug79096 is defined as a function.

In this ticket,
> extern zend_write_func_t zend_write;
zend_write is defined as a variable. Because it actually is a variable. Zend Engine stuff.

If you want to call "the zend_write function" you have to find out what the actual underlying function is. I'll spare you the legwork and tell you that (normally) it is php_output_write, implemented in main/output.c.

So

$zend = FFI::cdef("
    int php_output_write(const char *str, size_t str_length);
");
$zend->php_output_write("aaa", 3);

Of course, doing it this way doesn't get you the actual zend_write "function". It gets you php_output_write. But maybe that's fine for you.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Apr 19 19:01:28 2024 UTC