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 — Una función de retrollamada para el parámetro de contexto de notificación
$notification_code
,$severity
,$message
,$message_code
,$bytes_transferred
,$bytes_max
Una función de tipo callable, usada por el parámetro de contexto de notificación, llamado durante un evento.
Nota:
Esta no es una función real, sólo un prototipo de cómo la función debería ser.
notification_code
Una de las constantes de notificación STREAM_NOTIFY_*
.
severity
Una de las constantes de notificación STREAM_NOTIFY_SEVERITY_*
.
message
Pasado si un mensaje descriptivo está disponible para el evento.
message_code
Pasado si un código de mensaje descriptivo está disponible para el evento.
Esto significa que el valor depende de la envoltura específica en uso.
bytes_transferred
Si se puede aplicar, el parámetro bytes_transferred
será
rellenado.
bytes_max
Si se puede aplicar, el parámetro bytes_max
será
rellenado.
No devuelve ningún valor.
Ejemplo #1 Ejemplo 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);
/* Ignore */
break;
case STREAM_NOTIFY_REDIRECTED:
echo "Redireccionando a: ", $message;
break;
case STREAM_NOTIFY_CONNECT:
echo "Conectado...";
break;
case STREAM_NOTIFY_FILE_SIZE_IS:
echo "Obtenido el tamaño de archivo: ", $bytes_max;
break;
case STREAM_NOTIFY_MIME_TYPE_IS:
echo "Encontrado el tipo mime: ", $message;
break;
case STREAM_NOTIFY_PROGRESS:
echo "Hecho algún progreso, descargado ", $bytes_transferred, " hasta el momento";
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);
?>
El resultado del ejemplo sería algo similar a:
Conectado... Encontrado el tipo mime: text/html; charset=utf-8 Redireccionando a: http://no.php.net/contact Conectado... Obtenido el tamaño de archivo: 0 Found the mime-type: text/html; charset=utf-8 Redireccionando a: http://no.php.net/contact.php Conectado... Obtenido el tamaño de archivo: 4589 Encontrado el tipo mime: text/html;charset=utf-8 Hecho algún progreso, descargado 0 hasta el momento Hecho algún progreso, descargado 0 hasta el momento Hecho algún progreso, descargado 0 hasta el momento Hecho algún progreso, descargado 1440 hasta el momento Hecho algún progreso, descargado 2880 hasta el momento Hecho algún progreso, descargado 4320 hasta el momento Hecho algún progreso, descargado 5760 hasta el momento Hecho algún progreso, descargado 6381 hasta el momento Hecho algún progreso, descargado 7002 hasta el momento
Ejemplo #2 Barra de progreso sencilla para la descarga por parte del cliente por línea de comandos
<?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 "Redireccionando a: ", $message, "\n";
break;
case STREAM_NOTIFY_CONNECT:
echo "Conectado...\n";
break;
case STREAM_NOTIFY_FILE_SIZE_IS:
$filesize = $bytes_max;
echo "Tamaño de archivo: ", $filesize, "\n";
break;
case STREAM_NOTIFY_MIME_TYPE_IS:
echo "Tipo mime: ", $message, "\n";
break;
case STREAM_NOTIFY_PROGRESS:
if ($bytes_transferred > 0) {
if (!isset($filesize)) {
printf("\rTamaño de archivo desconocido.. %2d kb hecho..", $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 "\n¡Hecho!\n";
exit(0);
}
$err = error_get_last();
echo "\nErrrrrorr..\n", $err["message"], "\n";
exit(1);
?>
Ejecutar el ejemplo anterior con:
php -n fetch.php
http://no2.php.net/get/php-5-LATEST.tar.bz2/from/this/mirror
php-latest.tar.bz2
imprimirá algo similar a:
Conectado... Tipo mime: text/html; charset=utf-8 Redireccionando a: http://no2.php.net/distributions/php-5.2.5.tar.bz2 Conectado... Tamaño de archivo: 7773024 Tipo mime: 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";
?>