streams, stderr/out and zend_fprintf ?

php.internals

Hans Zaunere

23 years ago
I'm developing an extension that talks to a serial device (under RedHat 7.3). The dynamic module will then be loaded into a CLI PHP, which will use an ncurses interface, MySQL connectivity, etc. to make the application complete. In light of recent talk about streams in extensions, I have a couple of questions (I haven't done much extension programming yet, so forgive any naiveness and hopefully I haven't missed anything in the mail archives): -- With most console C programs I use the builtin stdin/stdout/stderr macros with functions like fprintf. As a PHP extension, should I still be using these, or does php_streams implement something different (ie, a php_stderr macro)? -- I'm seeing there is a zend_printf() but no zend_fprintf(). Am I missing something, or should I just be using the standard C lib functions? -- Should I be using php_stream_* functions to explicitly open the standard I/O streams? Please say no. For the extension I'm developing now, I can see how much of this is irrelevant; it's a CLI based application, and when the process ends, so will everything else, thanks to the kernel. However, I would like to design the extension so that if I were to load it into an Apache DSO PHP, it'd be working "the right way" and utilize all that's modern PHP extension development (yes, I would have a reason to talk a serial device protocol from a web server :) Any pointers on these types of best practices would be greatly appreciated. Thank you, Hans

Wez Furlong

23 years ago
Tips: Don't use zend_printf. Do try and use the php_stream_XXX API where it makes sense. When running in a web environment, or without an ncurses interface, use the PHP_WRITE() or php_printf() functions to generate output to send to the browser; the output will be captured in the output buffering layer. From what you've described, it sounds like you don't need to use the streams API, so you are free to use the usual stdio approach, just beware of the limitations of the solaris libc; if you are using the pre-opened std* streams, you will be fine; if you need to open your own, you might run out of luck. Consider using POSIX open(), read(), write() rather than stdio. If you miss printf and fprintf, you can open a php stream around an existing file descriptor (with no more overhead than stdio) and use php_stream_printf(). To access the std* streams via the streams API, you can open them using the special php://stdin, php://stdout and php://stderr filenames. This is, of course, not always a sensible thing to do in a web environment. For more info, read the docs online at http://php.net/streams --Wez. On Tue, 6 May 2003, Hans Zaunere wrote:

Hans Zaunere

23 years ago
--- Wez Furlong <wez@thebrainroom.com> wrote:
> Tips: > > Don't use zend_printf. > Do try and use the php_stream_XXX API where it makes sense.
Yes, knowing where it makes sense is the trick :)
> When running in a web environment, or without an ncurses interface, use > the PHP_WRITE() or php_printf() functions to generate output to send to > the browser; the output will be captured in the output buffering layer.
OK, that's good to know.
> From what you've described, it sounds like you don't need to use the > streams API, so you are free to use the usual stdio approach, just > beware of the limitations of the solaris libc; if you are using the > pre-opened std* streams, you will be fine; if you need to open your own, > you might run out of luck.
I don't have to deal with Solaris, fortunately. However, I'm unclear as to what I've described that makes it sound like I don't need to use the streams API. Because it's primarily a CLI extension? Or because I'm talking to a serial port?
> Consider using POSIX open(), read(), write() rather than stdio. If you > miss printf and fprintf, you can open a php stream around an existing > file descriptor (with no more overhead than stdio) and use > php_stream_printf().
My initial take on getting the serial device online was to use a resource, defined as (where msr is the name of the device): typedef struct { int filedes; struct termios sio; } msr_resource; Thus, also having init and destructor functions to open(), cfmakeraw(), cfsetspeed(), tcsetattr() and close() the returned filedes. Then it hit me that php.net/streams says a stream is already a resource with many of the resource housekeeping tasks taken care of. Great, but then how can I set my serial options on a php stream (like cfmakeraw, for example)? I see the ability to cast a php_stream to a filedes, but vice-versa, or as you said "you can open a php stream around an existing file descriptor," I'm having no luck?
> To access the std* streams via the streams API, you can open them using > the special php://stdin, php://stdout and php://stderr filenames. This > is, of course, not always a sensible thing to do in a web environment.
Ahh, just like in PHPland... thanks. Perhaps I'm making it overly complex, and simply using a resource->filedes is the way to go, but I'd like to get familiarized with php streams. Any additional tips would be helpful. Thanks Wez, Hans

Sascha Schumann

23 years ago
Hans, If you are talking to a serial device, just use posix open. Handling resources is easy, the necessary API calls boil down to this: static int le_mcrypt; le_mcrypt = zend_register_list_destructors_ex(php_mcrypt_module_dtor, NULL, "mcrypt", module_number); ZEND_REGISTER_RESOURCE (return_value, td, le_mcrypt); ZEND_FETCH_RESOURCE (td, MCRYPT, mcryptind, -1, "MCrypt", le_mcrypt); - Sascha

Hans Zaunere

23 years ago
Hi Sascha, --- Sascha Schumann <sascha@schumann.cx> wrote:
> Hans, > > If you are talking to a serial device, just use posix > open. Handling resources is easy, the necessary API calls > boil down to this: > > static int le_mcrypt; > > le_mcrypt = > zend_register_list_destructors_ex(php_mcrypt_module_dtor, > NULL, "mcrypt", module_number); > > ZEND_REGISTER_RESOURCE (return_value, td, le_mcrypt); > > ZEND_FETCH_RESOURCE (td, MCRYPT, mcryptind, -1, "MCrypt", > le_mcrypt);
I think in this case standard POSIX calls (on the serial end) is the way to go. I've been modeling how resources are handled in ext/mysql and what you have above jibes with what I'm doing, so I'm happy :) One note, though, is that the documentation for resources (http://us2.php.net/manual/en/zend.variables.resource.php) seems to clash a bit with what you have Sascha and what ext/mysql does. Perhaps it's simply that I'm not familiar enough with it, but AFAIK there is no zend_register_resource_destructors_ex() and the return value of ZEND_REGISTER_RESOURCE() isn't caught. Anyway, maybe this is worth taking a look at. Thank you, Hans

Wez Furlong

23 years ago
On Tue, 6 May 2003, Hans Zaunere wrote:
> I don't have to deal with Solaris, fortunately. However, I'm unclear as to > what I've described that makes it sound like I don't need to use the streams > API. Because it's primarily a CLI extension? Or because I'm talking to a > serial port?
Both ;) Usually, you will want to perform low level operations on the serial port using ioctl() or other library functions that expect to work with the fd. Also, you might not want to have any buffering at all; so it sounds like you don't need a stream. (but read on...)
> My initial take on getting the serial device online was to use a resource, > defined as (where msr is the name of the device): > > typedef struct { > int filedes; > struct termios sio; > } msr_resource; > > Thus, also having init and destructor functions to open(), cfmakeraw(), > cfsetspeed(), tcsetattr() and close() the returned filedes. > > Then it hit me that php.net/streams says a stream is already a resource with > many of the resource housekeeping tasks taken care of. Great, but then how > can I set my serial options on a php stream (like cfmakeraw, for example)? > > I see the ability to cast a php_stream to a filedes, but vice-versa, or as > you said "you can open a php stream around an existing file descriptor," I'm > having no luck? >
php_stream_fopen_from_fd() is present in 4.3.2 and later (try a snapshot from snaps.php.net), however, it sounds to me like you *might* benefit from implementing your own stream. (depends on how complex your API is).
> Perhaps I'm making it overly complex, and simply using a resource->filedes is > the way to go, but I'd like to get familiarized with php streams. Any > additional tips would be helpful.
If you are tempted to implement your own streams, I recommend reading my articles in PHP Magazine </plug> and/or the content from my Streams talk at the International PHP Conference in Amsterdam this week </plug> ;) (I'm more than half serious here) For starters, check out the API docs for streams at php.net/streams; although by no means complete, its better than nothing; they focus more on using the streams API than implementing streams. Its hard to advise you on which is the "best" approach for your extension without really knowing what it does, or what functions it exposes to PHP user-land. Generally, it is best to keep it simple (KISS), so it might make you life easier to go with the structure you described and make it into a resource, but replace the "int fildes" with "php_stream *stream". Hope that helps :) --Wez.

Hans Zaunere

23 years ago
--- Wez Furlong <wez@thebrainroom.com> wrote:
> > > On Tue, 6 May 2003, Hans Zaunere wrote: > > I don't have to deal with Solaris, fortunately. However, I'm unclear as > to > > what I've described that makes it sound like I don't need to use the > streams > > API. Because it's primarily a CLI extension? Or because I'm talking to > a > > serial port? > > Both ;) > Usually, you will want to perform low level operations on the serial > port using ioctl() or other library functions that expect to work with > the fd. Also, you might not want to have any buffering at all; so it > sounds like you don't need a stream. (but read on...) > > > My initial take on getting the serial device online was to use a > resource, > > defined as (where msr is the name of the device): > > > > typedef struct { > > int filedes; > > struct termios sio; > > } msr_resource; > > > > Thus, also having init and destructor functions to open(), cfmakeraw(), > > cfsetspeed(), tcsetattr() and close() the returned filedes. > > > > Then it hit me that php.net/streams says a stream is already a resource > with > > many of the resource housekeeping tasks taken care of. Great, but then > how > > can I set my serial options on a php stream (like cfmakeraw, for > example)? > > > > I see the ability to cast a php_stream to a filedes, but vice-versa, or > as > > you said "you can open a php stream around an existing file descriptor," > I'm > > having no luck? > > > > php_stream_fopen_from_fd() is present in 4.3.2 and later (try a snapshot > from snaps.php.net), however, it sounds to me like you *might* benefit > from implementing your own stream. (depends on how complex your API is).
Ahh, that's where the function is. The protocol this serial device uses isn't very complex, and in fact I already have it written using the POSIX salute (open/read/write). So you may ask "why even waste time with all these streams questions?" and I would say "because I have some extra time at work and wanted to see where it'd take me" :)
> > Perhaps I'm making it overly complex, and simply using a > resource->filedes is > > the way to go, but I'd like to get familiarized with php streams. Any > > additional tips would be helpful. > > If you are tempted to implement your own streams, I recommend reading my > articles in PHP Magazine </plug> and/or the content from my Streams talk > at the International PHP Conference in Amsterdam this week </plug> ;) > > (I'm more than half serious here)
I had hoped to get to Amsterdam but it's not going to happen. Best of luck with the presentation and I'll see if I can get my hands on those articles.
> For starters, check out the API docs for streams at php.net/streams; > although by no means complete, its better than nothing; they focus more > on using the streams API than implementing streams. > > Its hard to advise you on which is the "best" approach for your > extension without really knowing what it does, or what functions it > exposes to PHP user-land. Generally, it is best to keep it simple > (KISS), so it might make you life easier to go with the structure you > described and make it into a resource, but replace the "int fildes" with > "php_stream *stream".
After chasing the definition of php_stream down the rabbit hole, I think I'll stick with what I know for now :) It's a very exciting prospect, though, and I'm anxious to work with it in more interesting and appropiate projects.
> Hope that helps :)
Very much so, thanks for the pointers all H