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.

Reply via email to