2

I started developing my own little extension for PHP. But I'm struggling now with the resource types.

Basically I like to use a file pointer passed as parameter to my own function. So let's say the following PHP code is the end result:

<?php

$fp = fopen('file.txt', 'w');

my_ext_function($fp);

Now I like to play around with this file pointer in C back in my extension:

PHP_FUNCTION(my_ext_function)
{
        zval *res;
        php_stream *stream;

        char ch;

        if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &res) == FAILURE) {
                RETURN_FALSE;
        }

        PHP_STREAM_TO_ZVAL(stream, res); // Not sure about that

        // Do some crazy stuff here like it would be a native file pointer from C
        while( ( ch = fgetc(res) ) != EOF )
            php_printf("%c", res);

        RETURN_TRUE;
}

My extension function is based on the fread function of PHP itself.

So how can I achieve this? Basically the question is: how can cast the php_stream pointer to a FILE pointer?

BACKTRACE

#0  0x000000010081b330 in zend_parse_va_args (num_args=1, type_spec=0x7fff5fbfec88 "@9\321\002\001", va=0x7fff5fbfec10, flags=0, tsrm_ls=0x102cdef07) at Zend/zend_API.c:790
#1  0x000000010081bc67 in zend_parse_parameters (num_args=1, tsrm_ls=0x102cdef07, type_spec=0x7fff5fbfec88 "@9\321\002\001") at Zend/zend_API.c:924
#2  0x0000000102cdeb2a in zif_my_ext_function (ht=1, return_value=0x102cd2d38, return_value_ptr=0x102c9e168, this_ptr=0x0, return_value_used=0, tsrm_ls=0x102d13940) at hello.c:65
#3  0x00000001008cacd5 in zend_do_fcall_common_helper_SPEC (execute_data=0x102c9e1c0, tsrm_ls=0x102d13940) at Zend/zend_vm_execute.h:558
#4  0x0000000100882252 in ZEND_DO_FCALL_SPEC_CONST_HANDLER (execute_data=0x102c9e1c0, tsrm_ls=0x102d13940) at Zend/zend_vm_execute.h:2595
#5  0x0000000100869f28 in execute_ex (execute_data=0x102c9e1c0, tsrm_ls=0x102d13940) at Zend/zend_vm_execute.h:363
#6  0x000000010086a060 in zend_execute (op_array=0x102cd39f0, tsrm_ls=0x102d13940) at Zend/zend_vm_execute.h:388
#7  0x00000001007fde27 in zend_eval_stringl (str=0x102d138a0 "$fp = fopen(\"test.txt\", \"r\"); my_ext_function($fp);", str_len=41, retval_ptr=0x0,
    string_name=0x100dbd1d6 "Command line code", tsrm_ls=0x102d13940) at Zend/zend_execute_API.c:1077
#8  0x00000001007fe627 in zend_eval_stringl_ex (str=0x102d138a0 "$fp = fopen(\"test.txt\", \"r\"); my_ext_function($fp);", str_len=41, retval_ptr=0x0,
    string_name=0x100dbd1d6 "Command line code", handle_exceptions=1, tsrm_ls=0x102d13940) at Zend/zend_execute_API.c:1124
#9  0x00000001007fe6ff in zend_eval_string_ex (str=0x102d138a0 "$fp = fopen(\"test.txt\", \"r\"); my_ext_function($fp);", retval_ptr=0x0, string_name=0x100dbd1d6 "Command line code",
    handle_exceptions=1, tsrm_ls=0x102d13940) at Zend/zend_execute_API.c:1135
#10 0x00000001008f96ab in do_cli (argc=3, argv=0x102d13840, tsrm_ls=0x102d13940) at sapi/cli/php_cli.c:1034
#11 0x00000001008f8172 in main (argc=3, argv=0x102d13840) at sapi/cli/php_cli.c:1378
10
  • 1
    My bet is stream->stdiocast (a FILE*), but I haven't tried it. Could you try and see if it works? Commented Jun 9, 2015 at 13:41
  • At least it compiles now but I get an Internal Server Error now if I call the my_ext_function($fp). Can I somewhere see the runtime errors of the extension? Commented Jun 9, 2015 at 14:22
  • I'd check the error logs of PHP and the web server. Also, if you're on Mac/Linux and gdb is available, try sudo gdb -ex run --args php -a, then type $fp = fopen('file.txt', 'w'); my_ext_function($fp); and hit enter. If it crashes, you should get an error message and you can use bt to get a stack trace to the position where it crashes (doesn't mean the code there is responsible for the crash). Commented Jun 9, 2015 at 14:38
  • I installed gdb but when I enter the code after php > nothing happens. I can even put invalid PHP code like xyz; and nothing happens. Just another php > if I hit enter. Commented Jun 10, 2015 at 6:04
  • If you actually put a semicolon at the end and it behaves like this, then that sounds seriously broken o.0 Commented Jun 10, 2015 at 6:31

1 Answer 1

1

The PHP_STREAM_TO_ZVAL can't be used directly because it's declared inside the implementation of ext/standard/file.c; you need to use this instead:

php_stream_from_zval(stream, &res);

You can cast the stream to what you need using php_stream_cast():

{
    FILE *f;

    if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void**)&f, REPORT_ERRORS))   {
        RETURN_FALSE;
    }
    /* do what you want with f */
}
Sign up to request clarification or add additional context in comments.

5 Comments

That did the trick! Thank you so much :-). But I need first php_stream_from_zval(stream, &res); because now it tells me: that I can not implicit declare PHP_STREAM_TO_ZVAL
@TiMESPLiNTER No problem! I'm not sure how this affects the stream, though; should be fine if you don't read from the file outside of the extension, though :)
@TiMESPLiNTER Right, I didn't check where that macro was declared :) updated my answer.
Perfect. Just because I'm interested in... where did you find the solution? Are your skills that strong in PHP extension development or did you found the solution somewhere in the internet to my question?
@TiMESPLiNTER A bit of both; I'm a core developer and I use this part of the internet to find answers pertaining to these sorts of questions :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.