CakeFest 2024: The Official CakePHP Conference

stream_notification_callback

(PHP 5 >= 5.2.0, PHP 7, PHP 8)

stream_notification_callbacknotification コンテキストパラメータ用のコールバック関数

説明

stream_notification_callback(
    int $notification_code,
    int $severity,
    ?string $message,
    int $message_code,
    int $bytes_transferred,
    int $bytes_max
): void

イベント発生時にコールされる callable 関数です。 notification context parameter が使用します。

注意:

これは実際の関数では ありません。 コールバック関数がどうあるべきかというプロトタイプだけを示すものです。

パラメータ

notification_code

STREAM_NOTIFY_* 通知定数のひとつ。

severity

STREAM_NOTIFY_SEVERITY_* 通知定数のひとつ。

message

イベントについて説明するメッセージが存在する場合に渡されます。

message_code

イベントについて説明するコードが存在する場合に渡されます。

この値の意味は、使用するラッパーによって異なります。

bytes_transferred

利用可能な場合は bytes_transferred の値が格納されます。

bytes_max

利用可能な場合は bytes_max の値が格納されます。

戻り値

値を返しません。

例1 stream_notification_callback() の例

<?php
function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {

switch(
$notification_code) {
case
STREAM_NOTIFY_RESOLVE:
case
STREAM_NOTIFY_AUTH_REQUIRED:
case
STREAM_NOTIFY_COMPLETED:
case
STREAM_NOTIFY_FAILURE:
case
STREAM_NOTIFY_AUTH_RESULT:
var_dump($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max);
/* 無視 */
break;

case
STREAM_NOTIFY_REDIRECTED:
echo
"Being redirected to: ", $message;
break;

case
STREAM_NOTIFY_CONNECT:
echo
"Connected...";
break;

case
STREAM_NOTIFY_FILE_SIZE_IS:
echo
"Got the filesize: ", $bytes_max;
break;

case
STREAM_NOTIFY_MIME_TYPE_IS:
echo
"Found the mime-type: ", $message;
break;

case
STREAM_NOTIFY_PROGRESS:
echo
"Made some progress, downloaded ", $bytes_transferred, " so far";
break;
}
echo
"\n";
}

$ctx = stream_context_create();
stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));

file_get_contents("http://php.net/contact", false, $ctx);
?>

上の例の出力は、 たとえば以下のようになります。

Connected...
Found the mime-type: text/html; charset=utf-8
Being redirected to: http://no.php.net/contact
Connected...
Got the filesize: 0
Found the mime-type: text/html; charset=utf-8
Being redirected to: http://no.php.net/contact.php
Connected...
Got the filesize: 4589
Found the mime-type: text/html;charset=utf-8
Made some progress, downloaded 0 so far
Made some progress, downloaded 0 so far
Made some progress, downloaded 0 so far
Made some progress, downloaded 1440 so far
Made some progress, downloaded 2880 so far
Made some progress, downloaded 4320 so far
Made some progress, downloaded 5760 so far
Made some progress, downloaded 6381 so far
Made some progress, downloaded 7002 so far

例2 コマンドラインのダウンロードツール用のシンプルなプログレスバー

<?php
function usage($argv) {
echo
"Usage:\n";
printf("\tphp %s <http://example.com/file> <localfile>\n", $argv[0]);
exit(
1);
}

function
stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
static
$filesize = null;

switch(
$notification_code) {
case
STREAM_NOTIFY_RESOLVE:
case
STREAM_NOTIFY_AUTH_REQUIRED:
case
STREAM_NOTIFY_COMPLETED:
case
STREAM_NOTIFY_FAILURE:
case
STREAM_NOTIFY_AUTH_RESULT:
/* 無視 */
break;

case
STREAM_NOTIFY_REDIRECTED:
echo
"Being redirected to: ", $message, "\n";
break;

case
STREAM_NOTIFY_CONNECT:
echo
"Connected...\n";
break;

case
STREAM_NOTIFY_FILE_SIZE_IS:
$filesize = $bytes_max;
echo
"Filesize: ", $filesize, "\n";
break;

case
STREAM_NOTIFY_MIME_TYPE_IS:
echo
"Mime-type: ", $message, "\n";
break;

case
STREAM_NOTIFY_PROGRESS:
if (
$bytes_transferred > 0) {
if (!isset(
$filesize)) {
printf("\rUnknown filesize.. %2d kb done..", $bytes_transferred/1024);
} else {
$length = (int)(($bytes_transferred/$filesize)*100);
printf("\r[%-100s] %d%% (%2d/%2d kb)", str_repeat("=", $length). ">", $length, ($bytes_transferred/1024), $filesize/1024);
}
}
break;
}
}

isset(
$argv[1], $argv[2]) or usage($argv);

$ctx = stream_context_create();
stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));

$fp = fopen($argv[1], "r", false, $ctx);
if (
is_resource($fp) && file_put_contents($argv[2], $fp)) {
echo
"\nDone!\n";
exit(
0);
}

$err = error_get_last();
echo
"\nErrrrrorr..\n", $err["message"], "\n";
exit(
1);
?>

上の例を php -n fetch.php http://no2.php.net/get/php-5-LATEST.tar.bz2/from/this/mirror php-latest.tar.bz2 のように実行すると、次のような出力となります。

Connected...
Mime-type: text/html; charset=utf-8
Being redirected to: http://no2.php.net/distributions/php-5.2.5.tar.bz2
Connected...
Filesize: 7773024
Mime-type: application/octet-stream
[========================================>                                                           ] 40% (3076/7590 kb)

add a note

User Contributed Notes 3 notes

up
9
recycling dot sp dot am at gmail dot com
13 years ago
Please note that the callback function never gets called when the stream is (at least) of type socket. It is probably the same for the file:// proto although I've not done further tests.

I'm using php 5.3.3.
up
2
billynoah at gmail dot com
6 years ago
A couple of notes regarding the examples here (I'm using PHP 7.0.18):

1) fopen() did not provide STREAM_NOTIFY_PROGRESS for me but `file_get_contents()` worked fine.

2) The bytes transferred when transferring a zip file seems to always be shy by 8192 (8k). This varies depending on filetype and also breaks STREAM_NOTIFY_PROGRESS when the filesize is below 8k. I modified the function and it's now displaying the correct amount transferred and percentage downloaded on zip files above 8k. I'm not sure why the callback is so inconsistent but hopefully this is helpful to someone. This was also modified to only display progress:

<?php

$ctx
= stream_context_create();
stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
$fileData = @file_get_contents('http://example.com/test.zip',false,$ctx);

function
stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
static
$filesize = null;
switch(
$notification_code) {
case
STREAM_NOTIFY_FILE_SIZE_IS:
$filesize = $bytes_max;
break;
case
STREAM_NOTIFY_CONNECT:
echo
"Connected...\n";
break;
case
STREAM_NOTIFY_PROGRESS:
if (
$bytes_transferred > 0 && $filesize >= 8192) {
$bytes_transferred += 8192;
if (!isset(
$filesize)) {
printf("\rUnknown filesize.. %2d kb done..", $bytes_transferred/1024);
} else {
$length = (int)(($bytes_transferred/$filesize)*100);
printf("\r[%-100s] %d%% (%2d/%2d kb)", str_repeat("=", $length). ">", $length, ($bytes_transferred/1024), $filesize/1024);
}
}
break;
}
}
?>
up
1
aetonsi
1 year ago
A couple of unreported behaviors:
- case 1) if this callback calls die/exit($msg), it will print $msg, then the execution will go on until the request/wrapper is consumed, emitting a "PHP Warning: Failed to call user notifier" on each invocation of the callback. After the last callback invocation, the script is immediately terminated.
- case 2) if this callback throws an exception, it will behave the same way as exit/die, except for the fact that after the last callback invocation it does not terminate the script. The exception is instead raised in the scope of the request/wrapper, and can be caught with a try catch (right there or at a higher level).

Example code for case 1). The final "TEST ECHO" string will NOT be printed.
<?php
$context
= stream_context_create(['http' => ['ignore_errors' => true,]]);
stream_context_set_params($context, ['notification' => function () {
die(
'error');
}]);

file_get_contents('https://www.google.com', false, $context);
echo
"TEST ECHO";
?>

Example code for case 2). The exception is raised at the level of the file_get_contents call, it's catched, and the final "TEST ECHO" gets printed.
<?php
$context
= stream_context_create(['http' => ['ignore_errors' => true,]]);
stream_context_set_params($context, ['notification' => function () {
throw new
Exception('...');
}]);

try{
file_get_contents('https://www.google.com', false, $context);
}catch(
exception $e) { }
echo
"TEST ECHO";
?>
To Top