Skip to content

Commit

Permalink
Tweak the API to be more consistent.
Browse files Browse the repository at this point in the history
Update docs.
  • Loading branch information
wez committed Mar 16, 2002
1 parent d4e63bc commit 3064793
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 69 deletions.
179 changes: 124 additions & 55 deletions README.STREAMS
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ $Id$

Please send comments to: Wez Furlong <[email protected]>

Note: this doc is preliminary and is intended to give the reader an idea of
how streams work and should be used.

Why Streams?
============
You may have noticed a shed-load of issock parameters flying around the PHP
Expand All @@ -26,7 +23,7 @@ The main functions are:

PHPAPI size_t php_stream_read(php_stream * stream, char * buf, size_t count);
PHPAPI size_t php_stream_write(php_stream * stream, const char * buf, size_t
count);
count);
PHPAPI int php_stream_eof(php_stream * stream);
PHPAPI int php_stream_getc(php_stream * stream);
PHPAPI char *php_stream_gets(php_stream * stream, char *buf, size_t maxlen);
Expand All @@ -40,16 +37,77 @@ names: fread, fwrite, feof, fgetc, fgets, fclose, fflush, fseek, ftell.

Opening Streams
===============
Ultimately, I aim to implement an fopen_wrapper-like call to do this with
minimum fuss.
Currently, mostly for testing purposes, you can use php_stream_fopen to open a
stream on a regular file.
In most cases, you should use this API:

PHPAPI php_stream *php_stream_open_wrapper(char *path, char *mode,
int options, char **opened_path TSRMLS_DC);

Where:
path is the file or resource to open.
mode is the stdio compatible mode eg: "wb", "rb" etc.
options is a combination of the following values:
IGNORE_PATH (default) - don't use include path to search for the file
USE_PATH - use include path to search for the file
IGNORE_URL - do not use plugin wrappers
REPORT_ERRORS - show errors in a standard format if something
goes wrong.
opened_path is used to return the path of the actual file opened.

If you need to open a specific stream, or convert standard resources into
streams there are a range of functions to do this defined in php_streams.h.
A brief list of the most commonly used functions:

PHPAPI php_stream *php_stream_fopen_from_file(FILE *file, const char *mode);
Convert a FILE * into a stream.

PHPAPI php_stream * php_stream_fopen(const char * filename, const char *
mode);
PHPAPI php_stream *php_stream_fopen_tmpfile(void);
Open a FILE * with tmpfile() and convert into a stream.

This call behaves just like fopen(), except it returns a stream instead of a
FILE *
PHPAPI php_stream *php_stream_fopen_temporary_file(const char *dir,
const char *pfx, char **opened_path TSRMLS_DC);
Generate a temporary file name and open it.

There are some network enabled relatives in php_network.h:

PHPAPI php_stream *php_stream_sock_open_from_socket(int socket, int persistent);
Convert a socket into a stream.

PHPAPI php_stream *php_stream_sock_open_host(const char *host, unsigned short port,
int socktype, int timeout, int persistent);
Open a connection to a host and return a stream.

PHPAPI php_stream *php_stream_sock_open_unix(const char *path, int persistent,
struct timeval *timeout);
Open a UNIX domain socket.


Stream Utilities
================

If you need to copy some data from one stream to another, you will be please
to know that the streams API provides a standard way to do this:

PHPAPI size_t php_stream_copy_to_stream(php_stream *src,
php_stream *dest, size_t maxlen);

If you want to copy all remaining data from the src stream, pass
PHP_STREAM_COPY_ALL as the maxlen parameter, otherwise maxlen indicates the
number of bytes to copy.
This function will try to use mmap where available to make the copying more
efficient.

If you want to read the contents of a stream into an allocated memory buffer,
you should use:

PHPAPI size_t php_stream_copy_to_mem(php_stream *src, char **buf,
size_t maxlen, int persistent);

This function will set buf to the address of the buffer that it allocated,
which will be maxlen bytes in length, or will be the entire length of the
data remaining on the stream if you set maxlen to PHP_STREAM_COPY_ALL.
The buffer is allocated using pemalloc(); you need to call pefree() to
release the memory when you are done.
As with copy_to_stream, this function will try use mmap where it can.

Casting Streams
===============
Expand All @@ -59,14 +117,14 @@ You need to "cast" the stream into a FILE*, and this is how you do it:
FILE * fp;
php_stream * stream; /* already opened */

if (php_stream_cast(stream, PHP_STREAM_AS_STDIO, &fp, 1) == FAILURE) {
RETURN_FALSE;
if (php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void*)&fp, REPORT_ERRORS) == FAILURE) {
RETURN_FALSE;
}

The prototype is:

PHPAPI int php_stream_cast(php_stream * stream, int castas, void ** ret, int
show_err);
show_err);

The show_err parameter, if non-zero, will cause the function to display an
appropriate error message of type E_WARNING if the cast fails.
Expand All @@ -83,8 +141,14 @@ if you mix ANSI stdio calls on the FILE* with php stream calls on the stream.
If your system has the fopencookie function, php streams can synthesize a
FILE* on top of any stream, which is useful for SSL sockets, memory based
streams, data base streams etc. etc.
NOTE: There might be situations where this is not desireable, and we need to
provide a flag to inform the casting routine of this.

In situations where this is not desireable, you should query the stream
to see if it naturally supports FILE *. You can use this code snippet
for this purpose:

if (php_stream_is(stream, PHP_STREAM_IS_STDIO)) {
/* can safely cast to FILE* with no adverse side effects */
}

You can use:

Expand All @@ -93,10 +157,15 @@ PHPAPI int php_stream_can_cast(php_stream * stream, int castas)
to find out if a stream can be cast, without actually performing the cast, so
to check if a stream is a socket you might use:

if (php_stream_can_cast(stream, PHP_STREAM_AS_SOCKETD) == SUCCESS) {
/* it's a socket */
if (php_stream_can_cast(stream, PHP_STREAM_AS_SOCKETD) == SUCCESS) {
/* it can be a socket */
}

Please note the difference between php_stream_is and php_stream_can_cast;
stream_is tells you if the stream is a particular type of stream, whereas
can_cast tells you if the stream can be forced into the form you request.
The format doesn't change anything, while the later *might* change some
state in the stream.

Stream Internals
================
Expand All @@ -121,17 +190,17 @@ As an example, the php_stream_fopen() function looks like this:

PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode)
{
FILE * fp = fopen(filename, mode);
php_stream * ret;
if (fp) {
ret = php_stream_alloc(&php_stream_stdio_ops, fp, 0, 0, mode);
if (ret)
return ret;

fclose(fp);
}
return NULL;
FILE * fp = fopen(filename, mode);
php_stream * ret;
if (fp) {
ret = php_stream_alloc(&php_stream_stdio_ops, fp, 0, 0, mode);
if (ret)
return ret;

fclose(fp);
}
return NULL;
}

php_stream_stdio_ops is a php_stream_ops structure that can be used to handle
Expand All @@ -143,7 +212,7 @@ to be passed back to fopen_wrapper (or it's yet to be implemented successor).
The prototype for php_stream_alloc is this:

PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract,
size_t bufsize, int persistent, const char * mode)
size_t bufsize, int persistent, const char * mode)

ops is a pointer to the implementation,
abstract holds implementation specific data that is relevant to this instance
Expand Down Expand Up @@ -176,9 +245,9 @@ appropriately), and use the abstract pointer to refer to it.

For structured state you might have this:

struct my_state {
MYSQL conn;
MYSQL_RES * result;
struct my_state {
MYSQL conn;
MYSQL_RES * result;
};

struct my_state * state = pemalloc(sizeof(struct my_state), persistent);
Expand All @@ -201,32 +270,32 @@ For example, for reading from this wierd mysql stream:

static size_t php_mysqlop_read(php_stream * stream, char * buf, size_t count)
{
struct my_state * state = (struct my_state*)stream->abstract;

if (buf == NULL && count == 0) {
/* in this special case, php_streams is asking if we have reached the
* end of file */
if (... at end of file ...)
return EOF;
else
return 0;
}
/* pull out some data from the stream and put it in buf */
... mysql_fetch_row(state->result) ...
/* we could do something strange, like format the data as XML here,
and place that in the buf, but that brings in some complexities,
such as coping with a buffer size too small to hold the data,
so I won't even go in to how to do that here */
struct my_state * state = (struct my_state*)stream->abstract;

if (buf == NULL && count == 0) {
/* in this special case, php_streams is asking if we have reached the
* end of file */
if (... at end of file ...)
return EOF;
else
return 0;
}
/* pull out some data from the stream and put it in buf */
... mysql_fetch_row(state->result) ...
/* we could do something strange, like format the data as XML here,
and place that in the buf, but that brings in some complexities,
such as coping with a buffer size too small to hold the data,
so I won't even go in to how to do that here */
}

Implement the other operations - remember that write, read, close and flush
are all mandatory. The rest are optional. Declare your stream ops struct:

php_stream_ops my_ops = {
php_mysqlop_write, php_mysqlop_read, php_mysqlop_close,
php_mysqlop_flush, NULL, NULL, NULL,
"Strange mySQL example"
php_mysqlop_write, php_mysqlop_read, php_mysqlop_close,
php_mysqlop_flush, NULL, NULL, NULL,
"Strange mySQL example"
}

Thats it!
Expand All @@ -240,4 +309,4 @@ connection and then use pefree to dispose of the struct you allocated.
You may read the stream->persistent field to determine if your struct was
allocated in persistent mode or not.

vim:tw=78
vim:tw=78:et
4 changes: 0 additions & 4 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@ global
* on some platforms unimplemented function will just do nothing
(e.g. symlink) they should print a warning or not even be defined!
(DONE ?)
* Finish PHP streams abstraction, nuke all that issock stuff, implement SSL
socket support. (wez)
- ext/ftp/ -> all FILEs to streams
- ext/bz2/ -> convert to stream impl.
* Use arg_separator.input to implode args in the CGI sapi extension
and arg_separator.input to explode in php_build_argv(). (DONE?)
* Change the odbc_fetch_into() function to require ALWAYS the first two
Expand Down
2 changes: 1 addition & 1 deletion ext/gd/gd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1171,7 +1171,7 @@ static void _php_image_create_from(INTERNAL_FUNCTION_PARAMETERS, int image_type,
char *buff;

/* needs to be malloc (persistent) - GD will free() it later */
buff_size = php_stream_read_all(stream, &buff, 1);
buff_size = php_stream_copy_to_mem(stream, &buff, PHP_STREAM_COPY_ALL, 1);

if(!buff_size) {
php_error(E_WARNING,"%s: Cannot read image data", get_active_function_name(TSRMLS_C));
Expand Down
2 changes: 1 addition & 1 deletion ext/hyperwave/hw.c
Original file line number Diff line number Diff line change
Expand Up @@ -2892,7 +2892,7 @@ PHP_FUNCTION(hw_new_document_from_file)
if(NULL == doc)
RETURN_FALSE;

doc->size = php_stream_read_all(stream, &doc->data, 1);
doc->size = php_stream_copy_to_mem(stream, &doc->data, PHP_STREAM_COPY_ALL, 1);

php_stream_close(stream);

Expand Down
2 changes: 1 addition & 1 deletion main/php_streams.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ PHPAPI int php_stream_puts(php_stream *stream, char *buf);
PHPAPI size_t php_stream_copy_to_stream(php_stream *src, php_stream *dest, size_t maxlen);
/* read all data from stream and put into a buffer. Caller must free buffer when done.
* The copy will use mmap if available. */
PHPAPI size_t php_stream_read_all(php_stream *src, char **buf, int persistent);
PHPAPI size_t php_stream_copy_to_mem(php_stream *src, char **buf, size_t maxlen, int persistent);

/* maybe implement someday */
#define php_stream_error(stream) (0)
Expand Down
26 changes: 19 additions & 7 deletions main/streams.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ PHPAPI int php_stream_seek(php_stream *stream, off_t offset, int whence)
return -1;
}

PHPAPI size_t php_stream_read_all(php_stream *src, char **buf, int persistent)
PHPAPI size_t php_stream_copy_to_mem(php_stream *src, char **buf, size_t maxlen, int persistent)
{
size_t ret = 0;
char *ptr;
Expand All @@ -238,6 +238,15 @@ PHPAPI size_t php_stream_read_all(php_stream *src, char **buf, int persistent)
int srcfd;
#endif

if (buf)
*buf = NULL;

if (maxlen == 0)
return 0;

if (maxlen == PHP_STREAM_COPY_ALL)
maxlen = 0;

#if HAVE_MMAP
/* try and optimize the case where we are copying from the start of a plain file.
* We could probably make this work in more situations, but I don't trust the stdio
Expand All @@ -251,18 +260,21 @@ PHPAPI size_t php_stream_read_all(php_stream *src, char **buf, int persistent)

if (fstat(srcfd, &sbuf) == 0) {
void *srcfile;

srcfile = mmap(NULL, sbuf.st_size, PROT_READ, MAP_SHARED, srcfd, 0);

if (maxlen > sbuf.st_size || maxlen == 0)
maxlen = sbuf.st_size;

srcfile = mmap(NULL, maxlen, PROT_READ, MAP_SHARED, srcfd, 0);
if (srcfile != (void*)MAP_FAILED) {

*buf = pemalloc(persistent, sbuf.st_size);
*buf = pemalloc(persistent, maxlen);

if (*buf) {
memcpy(*buf, srcfile, sbuf.st_size);
ret = sbuf.st_size;
memcpy(*buf, srcfile, maxlen);
ret = maxlen;
}

munmap(srcfile, sbuf.st_size);
munmap(srcfile, maxlen);
return ret;
}
}
Expand Down

0 comments on commit 3064793

Please sign in to comment.