I have in my local tree some extensions to streams/wrappers that allow status notifications to be handled by user-space (and C extension code) callback handlers. In addition, the same mechanism can be extended to store context information, such as specifying cookie, SSL certificate, header and other meta data to be used during a stream-based transaction.
On the C level, php_streams.h includes definitions for two new structures; php_stream_context and php_stream_notifier. A stream_context can contain a reference to a stream_notifier (which contains information about the callback function for status notifications), and can be extended to hold other meta data. On the PHP level there are two new functions: file_context_create() and file_context_set_params(). These create/manipulate an instance of a php_stream_context. I've added an extra parameter to fopen, so it's proto looks like this: proto resource fopen(string filename, string mode [, bool use_include_path [, resource context]]) The immediate application of these changes allows the following PHP script: // $msg and $xcode are wrapper dependent messages/code numbers function notifier($code, $severity, $msg, $xcode, $sofar, $max) { $term_sol = "\x1b[1G"; // cursor to column 1 for xterms $sevmap = array(STREAM_SEVERITY_INFO => "INFO:", "STREAM_SEVERITY_ERR" => "ERR:"); $sevstr = $sevmap[$severity]; switch($code) { case STREAM_NOTIFY_CONNECT: // Sent when the wrapper connects to the resource printf("Connected\n"); break; case STREAM_NOTIFY_AUTH_REQUIRED: // Sent when the wrapper determines that authorization is // required. printf("%s Auth Required %d %s\n", $sevstr, $xcode, $msg); break; case STREAM_NOTIFY_AUTH_RESULT: // Sent when the wrapper determines the result of the // authenticated. $severity can be used to determine success printf("%s Auth Result %d %s\n", $sevstr, $xcode, $msg); break; case STREAM_NOTIFY_MIME_TYPE_IS: // Sent by HTTP wrapper when it spots a Content-Type header printf("%s Mime Type: %s\n", $sevstr, $msg); break; case STREAM_NOTIFY_FILE_SIZE_IS: // Sent by FTP wrapper when it determines file size, and // by HTTP wrapper when it spots a Content-Length header printf("%s File Size is %d\n", $sevstr, $max); break; case STREAM_NOTIFY_REDIRECTED: // Sent by HTTP wrapper when page is redirected printf("%s Redirected to %s\n", $sevstr, $msg); break; case STREAM_NOTIFY_PROGRESS: // Sent by streams with progress notification turned in // in their context. $max may be 0 if the max size is // unknown. printf("{$term_sol}PROG: %d / %d", $sofar, $max); break; case STREAM_NOTIFY_FAILURE: // Generic failure printf("%s Failure: %d %s\n", $sevstr, $xcode, $msg); break; } } $url = "ftp://wez:password@ftphost/file"; $context = file_context_create(array("notification" => "notifier")); $fp = fopen($url, "r", false, $context); if (is_resource($fp)) { while (!feof($fp)) { $data = fgets($fp, 1024); } fclose($fp); } Which outputs: Connected INFO: Auth Required 331 Password required for wez. INFO: Auth Result 230 230 User wez logged in. INFO: File Size is 440 PROG: 440 / 440 with the PROG line ticking upwards as data is transferred. At the moment, the only parameters for a stream_context are the status notification callback, but I can see this context as being useful for a generic means of passing data on to wrappers. In particular, I think Sterling might find this mechanism useful for more tightly integrating curl, perhaps as the default ftp/http fopen wrapper implementation if/when we bundle curl with PHP. The same thing goes for implementing wrappers aroung the WinInet URL functions, which have a very sophisticated callback mechanism. All comments are welcome, especially if I have forgotten something important... --Wez.