Tìm hiểu về Streams trong PHP

Streams là các tài nguyên được cung cấp bởi PHP mà chúng ta ít để ý đến. Streams có thể được dùng như là công cụ rất mạnh mẽ và bằng cách khai thác sức mạnh của Streams, chúng ta có thể đưa ứng dụng của mình lên một level cao hơn. PHP Streams được định nghĩa như sau (theo PHP Manual)

Streams were introduced with PHP 4.3.0 as a way of generalizing file, network, data compression, and other operations which share a common set of functions and uses. In its simplest definition, a stream is a resource object which exhibits streamable behavior. That is, it can be read from or written to in a linear fashion, and may be able to fseek() to an arbitrary locations within the stream.

Streams được giới thiệu lần đầu ở phiên bản PHP 4.3.0 như một cách để khái quát hóa cho File , Network, Data compression và các tiến trình khác trong đó dùng chung một tập hợp các functins hoặc có chung cách sử dụng. Một cách định nghĩa đơn giản hơn, Stream là một nguồn tài nguyên đổi tượng đại diện cho cách hành vi có thể stream được. Theo đó Stream có thể được đọc, ghi vào theo một phong cách "tuyến tính" và có thể được fseek() đến một vị trí bất kỳ trong luồng.

Mỗi stream sẽ có một implementation wrapper trong đó tồn tại một số code bổ sung cần thiết để xử lý các giao thức đặc biệt hoặc thực hiện việc encode. PHP cung cấp một số wrapper dựng sẵn và chúng ta có thể dễ dang tạo và đăng ký những wrapper tùy biến cho riêng mình. Ngoài ra chúng ta cũng có thể sửa đổi hoặc cải tiến "hành vi" của các wrappers sử dụng contextsfilters

Stream cơ bản

Một stream được tham chiếu dưới dạng <scheme>://<target>. Trong đó<scheme>là tên của wrapper và<target>` sẽ phụ thuộc vào cú pháp của wrapper.

Wrapper mặc định sẽ là file://, điều naỳ có nghĩa là mỗi khi chúng ta access đến filesystem, chúng ta đều phải dùng qua stream. Ví dụ với việc đọc file, ta có thể viết bằng hai cách là readfile('/path/to/somefile.txt') hoặc readfile('file:///path/to/somefile.txt'), sẽ cho ra cùng một kết quả. Một ví dụ khác, nếu chúng ta có một đoạn code là readfile('http://google.com/') thì khi đó chúng ta đang yêu cầu PHP sử dụng HTTP Stream wrapper thay vì File Stream wrapper.

Như mình đã nói bên trên, PHP cung cấp một số wrappers dựng sẵn, giao thức vào filters. Để tìm hiểu cụ thể hơn các wrappers có sẵn với môi trường của bạn, chúng ta có thể dùng các hàm sau:

stream_get_transports(); stream_get_wrappers(); stream_get_filters()

Trên đây là cách transports, wrappers và filters trên môi trường máy của mình, ngoài ra thì nếu cần thiết chúng ta cũng có thể viết thêm stream cho Amazon S3, MS Excel, Google Storage, Dropbox hoặc thậm chí là cả Twitter.

php:// Wrapper

Bản thân PHP cũng tự có cho riêng mình một wrapper để có thể access vào I/O streams. Một số wrapper cơ bản như: php://stdin, php://stdout, and php://stderr được map đến với các tài nguyên I/O mặc định. PHP cũng có thêm php://input là một read-only stream có thể truy cập trực tiếp đến dữ liệu raw của POST request. Điều này khá là thuận tiện khi chúng ta đang làm việc với các remote service mà payload data được đặt tại body của một POST request.

Cùng thử làm một kiểm tra nhỏ sử dụng curl nhé

curl -d "Hello" -d "a=b&c=d" http://localhost/dev/streams/php_input.php

Kết quả của đoạn code printr($POST) trả về sẽ là

Array
(
    [a] => b
    [c] => d
)

Để ý rằng chúng ta không thể lấy được gói dữ liệu đầu tiên từ $_POST nhưng nếu thay vào đó chúng ta dùng eadfile('php://input') thì lại có thể đưa ra kết quả đầy đủ: Hello&a=b&c=d

PHP 5 .1 giới thiệu thêm các wrapper: php://memory và php://temp được sử dụng để đọc và ghi các dữ liệu tạm thời. Bản thân tên của các wrapper này cũng cho thấy rằng dữ liệu được lưu trữ tương ứng ở memory hoặc ở trong một bộ file tạm được quản lý bởi system. Ngoài ra còn có thêm php://filter, một meta-wrapper được thiết kế để có thể áp dụng các filters trong việc mở một stream với các hàm như là readfile() hoặc file_get_contents() hoặc stream_get_contents().

<?php
// Write encoded data
file_put_contents("php://filter/write=string.rot13/resource=file:///path/to/somefile.txt", "Hello World");

// Read data and encode/decode
readfile("php://filter/read=string.toupper|string.rot13/resource=http://www.google.com");

Trong ví dụ đầu tiên chúng ta đã dùng thêm một filter để encode dữ liệu ghi vào ổ đĩa. Ở ví dụ tiếp theo chúng ta có các filters được áp dụng cho việc đọc nội dung của một remote url. Hiệu quả của filter có thể từ rất đơn giản đến trở nên vô cùng mạnh mẽ.

Stream Contexts

Một Context là một stream đặc biệt với một bộ parameters hoặc options mà có thể sử dụng để ay thay đổi, tối ưu hành vi của một wrapper. Một use case phổ biên của context đó là dùng để thay đổi HTTP wrapper. Điều này giúp chúng ta tránh khỏi sử dụng cURL cho các tác vụ network đơn giản

<?php
$opts = array(
  'http'=>array(
    'method'=>"POST",
    'header'=> "Auth: SecretAuthTokenrn" .
        "Content-type: application/x-www-form-urlencodedrn" .
              "Content-length: " . strlen("Hello World"),
    'content' => 'Hello World'
  )
);
$default = stream_context_get_default($opts);
readfile('http://localhost/dev/streams/php_input.php');

Đầu tiên chúng ta sẽ định nghĩa mảng options của mình với định dạng $array['wrapper']['option_name']. (mỗi một wrapper sẽ cho một tập hợp các option khác nhau). Sau đó chúng ta có thể gọi stream_context_get_default() để trả về context mặc định và accept mảng options để thay thế cho các giá trị mặc định. Hàm readfile sau đó được dùng để lấy dữ liệu về

Trong ví dụ này, content được trả về thông qua body của request, do đó remote script sử dụng php://input có thể đọc nó, chúng ta có thể access vào headers sử dụng hàm apache_request_headers() và thêm:

Array
(
    [Host] => localhost
    [Auth] => SecretAuthToken
    [Content-type] => application/x-www-form-urlencoded
    [Content-length] => 11
)

Khi đó chúng ta đã thay đổi context mặc định. Trong trường hợp bạn muốn modify context nhưng không làm ảnh hưởng đến context mặc định thì bạn có thể tạo ra một context mới dựa trên context mặc định mà sử dụng chúng song song với nhau.

<?php
$alternative = stream_context_create($other_opts);
readfile('http://localhost/dev/streams/php_input.php', false, $alternative);

Kết luận

Qua những mô tả bên trên, có thể bạn đã thấy sức mạnh của PHP Stream, vậy nếu bạn muốn tìm hiểu thêm hoặc muốn khai thác sức mạnh của Stream trong ứng dụng của mình thì đừng ngại bắt tay vào làm thử. Như bạn có thể thấy, các Streams share nhau một phần hoặc tất các các hàm liên quan đến filesystem, vậy nên cách tốt nhất là hãy tìm hiểu thật kỹ về filesystem và cố gắng thử implement một custom wrapper để làm việc với filesystem nhé.

Tham khảo:

http://php.net/manual/en/features.commandline.io-streams.php

https://www.sitepoint.com/understanding-streams-in-php/

http://php.net/manual/en/book.stream.php

http://php.net/manual/en/wrappers.php.php

http://php.net/manual/en/function.stream-context-create.php

All Rights Reserved