Permalink
Browse files

Tweak the API to be more consistent.

Update docs.
  • Loading branch information...
1 parent d4e63bc commit 30647934a477f876992c3390ce483bb55a3eee51 @wez wez committed Mar 16, 2002
Showing with 146 additions and 69 deletions.
  1. +124 −55 README.STREAMS
  2. +0 −4 TODO
  3. +1 −1 ext/gd/gd.c
  4. +1 −1 ext/hyperwave/hw.c
  5. +1 −1 main/php_streams.h
  6. +19 −7 main/streams.c
View
@@ -4,9 +4,6 @@ $Id$
Please send comments to: Wez Furlong <wez@thebrainroom.com>
-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
@@ -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);
@@ -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
===============
@@ -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.
@@ -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:
@@ -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
================
@@ -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
@@ -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
@@ -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);
@@ -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!
@@ -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
View
4 TODO
@@ -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
View
@@ -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));
View
@@ -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);
View
@@ -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)
View
@@ -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;
@@ -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
@@ -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;
}
}

0 comments on commit 3064793

Please sign in to comment.