2016-02-12 9 views
6

PHP manual afferma che un flusso aperto con php: // supporto di input cerca operazione e può essere letto più volte a partire da PHP 5.6, ma non riesco a fare funziona. L'esempio seguente mostra chiaramente che non funziona:php: // input può essere letto solo una volta in PHP 5.6.16

<!DOCTYPE html> 
<html> 
<body> 
<form method="post"> 
<input type="hidden" name="test_name" value="test_value"> 
<input type="submit"> 
</form> 
<?php 
if ($_SERVER['REQUEST_METHOD'] === 'POST') 
{ 
    $input = fopen('php://input', 'r'); 
    echo 'First attempt: ' . fread($input, 1024) . '<br>'; 
    if (fseek($input, 0) != 0) 
     exit('Seek failed'); 
    echo 'Second attempt: ' . fread($input, 1024) . '<br>'; 
} 
?> 
</body> 
</html> 

uscita:

First attempt: test_name=test_value 
Second attempt: 

php: // flusso di voce è stata

  1. leggere correttamente
  2. riavvolto con successo (fseek riuscita
  3. leggere senza successo

Sto facendo qualcosa di sbagliato?

+2

Con la quantità di eccezioni nel manuale e la mancanza di portabilità, vi consiglio semplicemente salvando il risultato dopo aver letto la prima volta. Dopo questo puoi cercare quanto vuoi con il risultato (e non devi salvarlo su un file - php: // memory' dovrebbe farlo.) – h2ooooooo

+0

@ h2ooooooo grazie, ho rinunciato a "riparare "questo problema e si è concluso con un piano per evitare di leggere l'input più di una volta in tutti i miei progetti futuri, come suggerivi tu. Se convertirai il tuo commento in una risposta, lo accetterò. – gseric

+0

Prego, dammi un secondo e avrò una risposta. – h2ooooooo

risposta

1

Con la quantità di eccezioni e mancanza di portabilità utilizzando php://input Si consiglia di leggere lo stream e salvarlo su un altro stream per evitare comportamenti imprevisti.

È possibile utilizzare php://memory al fine di creare un wrapper di file-stream-like, che vi darà tutte le stesse funzionalità che php://inputdovrebbe hanno senza tutto il comportamento fastidioso.

Esempio:

<?php 

$inputHandle = fopen('php://memory', 'r+'); 

fwrite($inputHandle, file_get_contents('php://input')); 

fseek($inputHandle, 0); 

Inoltre è possibile creare la propria classe per fare riferimento a questo oggetto in modo coerente:

<?php 

class InputReader { 
    private static $instance; 

    /** 
    * Factory for InputReader 
    * 
    * @param string $inputContents 
    * 
    * @return InputReader 
    */ 
    public static function instance($inputContents = null) { 
     if (self::$instance === null) { 
      self::$instance = new InputReader($inputContents); 
     } 

     return self::$instance; 
    } 

    protected $handle; 

    /** 
    * InputReader constructor. 
    * 
    * @param string $inputContents 
    */ 
    public function __construct($inputContents = null) { 
     // Open up a new memory handle 
     $this->handle = fopen('php://memory', 'r+'); 

     // If we haven't specified the input contents (in case you're reading it from somewhere else like a framework), then we'll read it again 
     if ($inputContents === null) { 
      $inputContents = file_get_contents('php://input'); 
     } 

     // Write all the contents of php://input to our memory handle 
     fwrite($this->handle, $inputContents); 

     // Seek back to the start if we're reading anything 
     fseek($this->handle, 0); 
    } 

    public function getHandle() { 
     return $this->handle; 
    } 

    /** 
    * Wrapper for fseek 
    * 
    * @param int $offset 
    * @param int $whence 
    * 
    * @return InputReader 
    * 
    * @throws \Exception 
    */ 
    public function seek($offset, $whence = SEEK_SET) { 
     if (fseek($this->handle, $offset, $whence) !== 0) { 
      throw new \Exception('Could not use fseek on memory handle'); 
     } 

     return $this; 
    } 

    public function read($length) { 
     $read = fread($this->handle, $length); 

     if ($read === false) { 
      throw new \Exception('Could not use fread on memory handle'); 
     } 

     return $read; 
    } 

    public function readAll($buffer = 8192) { 
     $reader = ''; 

     $this->seek(0); // make sure we start by seeking to offset 0 

     while (!$this->eof()) { 
      $reader .= $this->read($buffer); 
     } 

     return $reader; 
    } 

    public function eof() { 
     return feof($this->handle); 
    } 
} 

Usage:

$first1024Bytes = InputReader::instance()->seek(0)->read(1024); 
$next1024Bytes = InputReader::instance()->read(1024); 

Usage (leggi tutto):

$phpInput = InputReader::instance()->readAll(); 
1

Un altro approccio potrebbe essere quello di aprire il flusso di input ogni volta invece di riavvolgere e cercare.

$input = fopen('php://input', 'r'); 
echo 'First attempt: ' . fread($input, 1024) . '<br>'; 
$input2 = fopen('php://input', 'r'); 
echo 'Second attempt: ' . fread($input2, 1024) . '<br>'; 

Se il costo delle risorse non rappresenta un problema.

Inoltre c'è file_get_contents

$input = file_get_contents("php://input"); 
$input = json_decode($input, TRUE); 

se si sta inviando JSON.

+0

Sì, ho già notato che funziona in quel caso ma non è adatto a me perché sto usando il componente "zend-diactoros" invece dell'accesso diretto al flusso (il mio esempio è stato semplificato per evitare confusione). Questo componente astrae il flusso di input PHP con (stringa) $ request-> getBody(), quindi non voglio ignorare zend-diactoros e aprire il flusso manualmente. – gseric

Problemi correlati