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.
(PHP 5 >= 5.2.0, PHP 7, PHP 8)
stream_notification_callback — Uma função de chamada de retorno para o parâmetro de contexto de notification
$notification_code
,$severity
,$message
,$message_code
,$bytes_transferred
,$bytes_max
Uma função do tipo callable, usada pelo parâmetro de contexto de notificação, chamado durante um evento.
Nota:
Esta não é uma função real, apenas um protótipo de como uma função deve ser.
notification_code
Uma das constantes de notificação STREAM_NOTIFY_*
.
severity
Uma das constantes de notificação STREAM_NOTIFY_SEVERITY_*
.
message
Passado se uma mensagem descritiva estiver disponível para o evento.
message_code
Passado se um código de mensagem descritiva estiver disponível para o evento.
O significado deste valor depende do empacotador específico em uso.
bytes_transferred
Se aplicável, o número de bytes em bytes_transferred
será
preenchido.
bytes_max
Se aplicável, o número máximo de bytes em bytes_max
será
preenchido.
Nenhum valor é retornado.
Exemplo #1 Exemplo de 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);
/* Ignora */
break;
case STREAM_NOTIFY_REDIRECTED:
echo "Sendo redirecionado para: ", $message;
break;
case STREAM_NOTIFY_CONNECT:
echo "Conectado...";
break;
case STREAM_NOTIFY_FILE_SIZE_IS:
echo "Tamanho do arquivo: ", $bytes_max;
break;
case STREAM_NOTIFY_MIME_TYPE_IS:
echo "Encontrado o tipo de mídia: ", $message;
break;
case STREAM_NOTIFY_PROGRESS:
echo "Algum progresso, já baixados ", $bytes_transferred, " bytes até agora";
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);
?>
O exemplo acima produzirá algo semelhante a:
Conectado... Encontrado o tipo de mídia: text/html; charset=utf-8 Sendo redirecionado para: http://no.php.net/contact Conectado... Tamanho do arquivo: 0 Encontrado o tipo de mídia: text/html; charset=utf-8 Sendo redirecionado para: http://no.php.net/contact.php Conectado... Tamanho do arquivo: 4589 Encontrado o tipo de mídia: text/html;charset=utf-8 Algum progresso, já baixados 0 até agora Algum progresso, já baixados 0 até agora Algum progresso, já baixados 0 até agora Algum progresso, já baixados 1440 até agora Algum progresso, já baixados 2880 até agora Algum progresso, já baixados 4320 até agora Algum progresso, já baixados 5760 até agora Algum progresso, já baixados 6381 até agora Algum progresso, já baixados 7002 até agora
Exemplo #2 Barra de progresso simples para cliente de download em linha de comando
<?php
function usage($argv) {
echo "Uso:\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:
/* Ignore */
break;
case STREAM_NOTIFY_REDIRECTED:
echo "Sendo redirecionado para: ", $message, "\n";
break;
case STREAM_NOTIFY_CONNECT:
echo "Conectado...\n";
break;
case STREAM_NOTIFY_FILE_SIZE_IS:
$filesize = $bytes_max;
echo "Tamanho do arquivo: ", $filesize, "\n";
break;
case STREAM_NOTIFY_MIME_TYPE_IS:
echo "Tipo de mídia: ", $message, "\n";
break;
case STREAM_NOTIFY_PROGRESS:
if ($bytes_transferred > 0) {
if (!isset($filesize)) {
printf("\rTamanho de arquivo desconhecido.. %2d kb concluídos..", $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 "\nPronto!\n";
exit(0);
}
$err = error_get_last();
echo "\nErrrrro..\n", $err["message"], "\n";
exit(1);
?>
Executing the example above with:
php -n fetch.php
http://no2.php.net/get/php-5-LATEST.tar.bz2/from/this/mirror
php-latest.tar.bz2
will output something similar too:
Conectado... Tipo de mídia: text/html; charset=utf-8 Sendo redirecionado para: http://no2.php.net/distributions/php-5.2.5.tar.bz2 Conectado... Tamanho do arquivo: 7773024 Tipo de mídia: application/octet-stream [========================================> ] 40% (3076/7590 kb)
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.
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;
}
}
?>
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";
?>