2016-02-12 16 views
6

PHP manual stwierdza, że ​​strumień otwarty za pomocą php: // input support szuka operacji i może być odczytywany wiele razy od PHP 5.6, ale nie mogę działa. Poniższy przykład pokazuje wyraźnie, że nie działa:php: // wejście może być odczytane tylko raz w 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> 

wyjściowa:

First attempt: test_name=test_value 
Second attempt: 

php: // strumień wejściowy został

  1. z powodzeniem czytać
  2. powodzeniem przewijana (fseek udało)
  3. bezskutecznie przeczytano

Czy robię coś nie tak?

+2

Z kwoty wyjątków w instrukcji oraz brak przenośności, polecam po prostu zapisując wynik po przeczytaniu go po raz pierwszy. Po tym możesz szukać jak najwięcej z wynikiem (i nie musisz zapisywać go w pliku - php: // memory powinno to zrobić.) – h2ooooooo

+0

@ h2ooooooo dziękuję, zrezygnowałem z "naprawy" "ten problem i zakończył się planem uniknięcia więcej niż jednego wejścia do czytania we wszystkich przyszłych projektach minowych, jak sugerowałeś. Jeśli skonwertujesz swój komentarz na odpowiedź, zaakceptuję to. – gseric

+0

Nie ma za co - daj mi sekundę, a ja będę miał odpowiedź. – h2ooooooo

Odpowiedz

1

Przy ilości wyjątków i braku możliwości przenoszenia przy użyciu php://input, polecam przeczytanie strumienia i zapisanie go w innym strumieniu, aby uniknąć nieoczekiwanego zachowania.

Można użyć php://memory aby utworzyć plik-stream-jak opakowanie, które daje wszystkie te same funkcje, które php://inputpowinien mieć bez wszystkich irytujących zachowań.

Przykład:

<?php 

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

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

fseek($inputHandle, 0); 

Dodatkowo można stworzyć własną klasę odnieść się do tego obiektu konsekwentnie:

<?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); 
    } 
} 

Zastosowanie:

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

Usage (czytaj całość):

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

Innym rozwiązaniem może być otwieranie strumienia wejściowego za każdym razem zamiast przewijania i wyszukiwania.

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

Jeśli koszt zasobów nie będzie problemem.

Również tam file_get_contents

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

jeśli wysyłasz JSON.

+0

Tak, już zauważyłem, że działa w tym przypadku, ale nie jest odpowiedni dla mnie, ponieważ używam komponentu "zend-diactoros" zamiast bezpośredniego dostępu do strumienia (mój przykład został uproszczony, aby uniknąć nieporozumień). Ten składnik streszcza strumień wejściowy PHP za pomocą (string) $ request-> getBody(), więc naprawdę nie chcę ominąć Zend-diaktorów i otworzyć strumienia ręcznie. – gseric

Powiązane problemy