SunshinePHP Developer Conference 2015

Suporte ao método PUT

O suporte ao método PUT mudou entre PHP 3 e PHP 4. No PHP 4, deve se usar a entrada padrão para ler os conteúdos de um PUT.

Exemplo #1 Salvando arquivos HTTP PUT com o PHP 4

<?php
/* PUT data vem do stdin stream */
$putdata fopen("php://stdin""r");

/* Abre um arquivo para escrita */
$fp fopen("myputfile.ext""w");

/* Lê os dados 1KB de cada vez
   e escreve no arquivo */
while ($data fread($putdata,1024))
  
fwrite($fp,$data);

/* Fecha os streams */
fclose($fp);
fclose($putdata);
?>

Nota:

Toda a documentação abaixo aplica-se ao PHP 3 somente.

PHP prove suporte para o método HTTP PUT usado por clientes como Netscape Composer e W3C Amaya. Requisições PUT são muito mais simples do que o upload de arquivo e se parecem com isto:

PUT /path/filename.html HTTP/1.1
     

Isto normalmente indica que o cliente remoto gostaria de salvar o conteúdo que se segue como: /path/filename.html na sua arvore web. É obvio que não é uma boa idéia para o Apache ou o PHP automaticamente permitir que todos possam sobrescrever arquivos na sua arvore web. Então, para manusear este tipo de requisição você tem primeiro que dizer ao seu servidor web que você quer que um certo script PHP cuide da requisição. No apache você faz isto com a diretiva Script. Pode ser colocada praticamente em qualquer lugar do seu arquivo de configuração do Apache. Um lugar comum é dentro de um bloco <Directory> ou talvez dentro de um bloco <Virtualhost>. Uma linha como esta deve fazer o truque:

Script PUT /put.php
     

Isto diz para o apache enviar todas as requisições PUT que estejam no contexto que você colocou esta linha para o script put.php. Isto assume, é claro, que você tem o PHP ativado para a extensão .php e que o PHP esta ativo.

Dentro do seu arquivo put.php você deve então fazer algo parecido com isto:

<?php copy($PHP_UPLOADED_FILE_NAME$DOCUMENT_ROOT $REQUEST_URI); ?>

Isto deve copiar o arquivo para a localização requisitada pelo cliente remoto. Você provavelmente quer fazer alguma checagem e/ou autenticar o usuário antes de fazer esta copia de arquivo. O único truque aqui é que quando o php vê uma requisição com o método PUT ele guarda o arquivo carregado em um arquivo temporário justo como se fosse manuseado pelo método POST. Quando a requisição termina, este arquivo temporário é apagado. Assim seu script de manuseio do PUT tem que copiar este arquivo em outro lugar. O nome deste arquivo temporário esta na variável $PHP_PUT_FILENAME, e você pode ver o nome de arquivo de destino sugerido em $REQUEST_URI (deve variar em servidores diferentes do apache). Este nome do arquivo de destino é o que o cliente remoto especificou. Você não tem que ouvir o cliente. Você pode, por exemplo, copiar todos os arquivos carregados para um diretório especial de uploads.

add a note add a note

User Contributed Notes 7 notes

up
15
micronix at gmx dot net
4 years ago
Hello PHP World After many Hours of worryness :=)

I have found the Solution for Resume or Pause Uploads
In this Code Snippet it is the Server Side not Client on any Desktop Programm you must use byte ranges to calculate the uploaded bytes and missing of total bytes.

Here the PHP Code

<?php
$CHUNK
= 8192;

        try {
            if (!(
$putData = fopen("php://input", "r")))
                throw new
Exception("Can't get PUT data.");

           
// now the params can be used like any other variable
            // see below after input has finished

           
$tot_write = 0;
           
$tmpFileName = "/var/dev/tmp/PUT_FILE";
           
// Create a temp file
           
if (!is_file($tmpFileName)) {
               
fclose(fopen($tmpFileName, "x")); //create the file and close it
                // Open the file for writing
               
if (!($fp = fopen($tmpFileName, "w")))
                    throw new
Exception("Can't write to tmp file");

               
// Read the data a chunk at a time and write to the file
               
while ($data = fread($putData, $CHUNK)) {
                   
$chunk_read = strlen($data);
                    if ((
$block_write = fwrite($fp, $data)) != $chunk_read)
                        throw new
Exception("Can't write more to tmp file");

                   
$tot_write += $block_write;
                }

                if (!
fclose($fp))
                    throw new
Exception("Can't close tmp file");

                unset(
$putData);
            } else {
               
// Open the file for writing
               
if (!($fp = fopen($tmpFileName, "a")))
                    throw new
Exception("Can't write to tmp file");

               
// Read the data a chunk at a time and write to the file
               
while ($data = fread($putData, $CHUNK)) {
                   
$chunk_read = strlen($data);
                    if ((
$block_write = fwrite($fp, $data)) != $chunk_read)
                        throw new
Exception("Can't write more to tmp file");

                   
$tot_write += $block_write;
                }

                if (!
fclose($fp))
                    throw new
Exception("Can't close tmp file");

                unset(
$putData);
            }

           
// Check file length and MD5
           
if ($tot_write != $file_size)
                throw new
Exception("Wrong file size");

           
$md5_arr = explode(' ', exec("md5sum $tmpFileName"));
           
$md5 = $md5sum_arr[0];
            if (
$md5 != $md5sum)
                throw new
Exception("Wrong md5");
        } catch (
Exception $e) {
            echo
'', $e->getMessage(), "\n";
        }
?>
up
1
yaogzhan at gmail dot com
8 years ago
PUT raw data comes in php://input, and you have to use fopen() and fread() to get the content. file_get_contents() is useless.

The HTTP PUT request MUST contain a Content-Length header to specify the length (in bytes) of the body, or the server will not be able to know when the input stream is over. This is the common problem for many to find the php://input empty if no such header available.

This should make PUT work properly on win32 using PHP5.1.1 and apache2.
up
1
mikeb at mikebanahan dot com
10 years ago
I have spent a lot of time trying to make PUT work with Apache 2.0.40. I have not yet been able to find any way of making the Script directive invoke php via mod_php, the only way has been to have a file called example.cgi and invoke it via CGI, with the file starting
#!/usr/bin/php
so the PHP interpreter is invoked through the CGI mechanism and not as a module.

If there IS a way of making it work 'right' I'd love to know! After six hours of messing around, I've settled for CGI. The error messages in the apache error log are significantly misleading and the whole thing has been an exercise in frustration.

Attempts to use AddHandler and all 'normal' ways of trying to persuade Apache to do this have been fruitless. It does seem as if PUT can only be handled by CGI invocation.
up
0
San
7 months ago
Instead of using fread fwrite to save uploaded content to a file.
stream_copy_to_stream is much cleaner.
up
0
warhog at warhog dot net
9 years ago
NOTE: The <Script>-Directive can not be placed in .htaccess files.

So if you're having shared webspace and no access to the apache-configuration file you will have little chance to make something like this work.

But you can solve the problem, using mod_rewrite (for Apache) - for further information see the documentation at http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html
up
-1
gherson
9 years ago
A Case Study:  To set up publishing with Netscape 7.2 Composer to Apache/PHP, no need to use CGI (which I tried unsuccessfully for too long) or to alter Apache's httpd.conf.  I needed only to click Publish As, fill in put2disk.php as the filename (where its contents are the below), and fill in that file's dir as the "Publishing address".
XAMPP 1.4.14: Apache/2.0.54 (Win32) mod_ssl/2.0.54 OpenSSL/0.9.7g PHP/5.0.4.

<? // filename: put2disk.php.

//file_put_contents ("get_def.out", print_r (get_defined_vars(), TRUE)); // debugging

// Two slurp methods: (a) didn't work, (b) did.
//$stdin_rsc = fopen("php://input", "r");
//$putdata='';
//while ($putdata .= fread($stdin_rsc, 1024)); // a. Hangs the "Publishing..." dialog.
//while (!feof($stdin_rsc)) $putdata.=fread($stdin_rsc, 8192); // b. Worked, but file_get_contents is faster.
//fclose($stdin_rsc);

// All that's nec:
$putdata=file_get_contents('php://input'); // Not php://stdin! (When the ability to see error messages isn't available, the doc (this manual page) needs to be more accurate.)

file_put_contents("stdin.out",$putdata);
?>
up
-6
kicaj
10 months ago
to create variable such as $_GET, $_POST use
that solution

<?php
    $_SERVER
['REQUEST_METHOD']==="PUT" ? parse_str(file_get_contents('php://input', false , null, -1 , $_SERVER['CONTENT_LENGTH'] ), $_PUT): $_PUT=array();
?>
To Top