php streams: lucky dip

46
PHP Streams a Lucky Dip Wez Furlong <[email protected] >

Upload: wez-furlong

Post on 05-Jul-2015

8.589 views

Category:

Technology


1 download

DESCRIPTION

A number of tid-bits of streams information of varying levels of difficulty

TRANSCRIPT

Page 2: PHP Streams: Lucky Dip

About the author

• PHP Core Developer since 2001

• Author of the Streams layer

• I hold the title “King” of PECL

• Author of most of PDO and its drivers

• Day-job is developing the fastest MTA on Earth

Page 3: PHP Streams: Lucky Dip

Lucky Dip!?

• Streams is a big topic area

• Every uses them

• A lot of people misuse them

• tid-bits from basic to advanced level

Page 4: PHP Streams: Lucky Dip

What is a stream?

• View of some kind of data

• Presented in chunks

• Readable

• Writable

• Sometimes seekable

Page 5: PHP Streams: Lucky Dip

File based streams

• Most common (include/require)

• Represent data held in a filesystem

Page 6: PHP Streams: Lucky Dip

Typical file reading code

$fp = fopen(‘myfile.txt’, ‘r’);

while (!feof($fp)) { $data .= fread($fp, 8192);}

$fp = fopen(‘myfile.txt’, ‘r’); $data = fread($fp, filesize(‘myfile.txt’));

Page 7: PHP Streams: Lucky Dip

Comparisons

file_get_contents() and stream_get_contents()

get all data into a variable

Fastest, most efficient

Page 8: PHP Streams: Lucky Dip

Comparisons

while (!feof($fp)) fread($fp, 8192)

Reads chunks

Most memory efficient way

8192 matches internal chunk size

Page 9: PHP Streams: Lucky Dip

Comparisons

while (!feof($fp)) fgets($fp)

Reads lines

Most memory efficient way

Slowest way to read chunks

Slowest way to read whole file

Page 10: PHP Streams: Lucky Dip

Comparisons

foreach (file($filename) as $line)

Reads lines

Fastest way

Loads whole file into memory

Page 11: PHP Streams: Lucky Dip

flock()

• Locks a file

• Shared of exclusive

• Advisory

• Only works if everyone uses flock()

• Mandatory on Windows!

• Sounds useful for web apps that use files

Page 12: PHP Streams: Lucky Dip

Using flock()

Reader:

$fp = fopen($filename, ‘r’);

flock($fp, LOCK_SH);

# it’s now safe to read the file

$data = fread($fp, 8192);

flock($fp, LOCK_UN);

Page 13: PHP Streams: Lucky Dip

Using flock()

Writer:

$fp = fopen($filename, ‘w+’);

flock($fp, LOCK_EX);

# we’re the only writer now

fwrite($fp, $data);

flock($fp, LOCK_UN);

Page 14: PHP Streams: Lucky Dip

flock() blocks

• flock() is “safe” because it will block until the lock is obtained

• Blocking is like sleeping

• While you’re asleep, you’re not doing any work

• Sometimes you want to do work while you wait

Page 15: PHP Streams: Lucky Dip

Using flock()

Non-blocking:

$fp = fopen($filename, ‘w+’);

if (flock($fp, LOCK_EX|LOCK_NB)) {

# we got the lock

} else {

# do something productive while we wait# for the lock

}

Page 16: PHP Streams: Lucky Dip

flock() summary

• Can be useful to ensure consistency

• Caveat: doesn’t work on most network filesystems

• Caveat: doesn’t work in threaded servers

• ISAPI, NSAPI, win32 apache

• threaded apache 2

• Caveat: some Linux kernels have broken flock()

Page 17: PHP Streams: Lucky Dip

Network Streams: sockets

• “Wormholes”

• according to the Unix Socket FAQ

• Bi-directional

• data goes both ways

• Differences in behavior from file streams

Page 18: PHP Streams: Lucky Dip

fsockopen()

An HTTP request:

$fp = fsockopen(‘www.php.net’, 80);

fwrite($fp, “GET / HTTP/1.0\r\n” . “Host: www.php.net\r\n\r\n”);

$data = fread($fp, 8192);

Page 19: PHP Streams: Lucky Dip

Alternative HTTP request:

$fp = fopen(‘http://www.php.net’, ‘r’);

$data = fread($fp, 8192);

This is an example of a wrapper

Page 20: PHP Streams: Lucky Dip

Network Streams: feof()

• Common error:

• Assuming that feof() means “connection_closed()”

• feof() returns true when:

• A read fails and the buffer is empty

• or: buffer is empty and no data has been received within the socket timeout

• Lack of data is often a temporary condition for network streams

Page 21: PHP Streams: Lucky Dip

Short reads, short writes

Another common mistake:

$fp = fopen(‘http://www.php.net’, ‘r’);echo fread($fp, 100000000);fclose($fp);

Reads are non-greedy

Think in terms of 8k chunks

Check the return value from fwrite()!

Page 22: PHP Streams: Lucky Dip

Speaking the lingo

Common mistake: “PHP hangs!”

$fp = fsockopen(‘www.php.net’, 80);fwrite($fp, “GET / HTTP/1.0\r\n” . “Host: www.php.net\r\n”);$data = fread($fp, 8192);

Page 23: PHP Streams: Lucky Dip

Sockets block

• Sockets are blocking by default

• Blocking is like sleeping

• Can cause long delays in your scripts

• PHP has 2.5 ways to control them

Page 24: PHP Streams: Lucky Dip

1. Non-blocking mode

• stream_set_blocking($fp, false);

• Reads/writes will “fail” instead of blocking

• Check the return values from fread() and fwrite()

• zero or zero-length: try again later

• try again means: try sending that data again

• implement your own buffering

• feof() loses meaning

Page 25: PHP Streams: Lucky Dip

Awful non-blocking code

function get_sock($host, $port) { $s = fsockopen($host, $port); stream_set_blocking($s, false); return $s;}

$hosts[] = get_sock(...); $hosts[] = get_sock(...); $hosts[] = get_sock(...); # repeat 20 times

# read all data without blocking do { foreach ($hosts as $s) echo fread($s, 8192);} while (true);

Page 26: PHP Streams: Lucky Dip

2. Timeouts

• stream_set_timeout($fp, $sec, $usec);

• Default 60 seconds

• Waits for up-to the timeout period

• Returns data if it is available before then

• Otherwise

• Sets ‘timed_out’ meta data field

• Returns an empty string (or zero)

Page 27: PHP Streams: Lucky Dip

Non-blocking vs. timeouts

• Timeouts

• Easiest when working with a single socket

• Non-blocking

• Useful for doing multiple things at once

• Painful when using what we’ve seen so far

Page 28: PHP Streams: Lucky Dip

2.5: stream_select()

• Non-blocking and timeouts combined!

• Sockets don’t have to be non-blocking

• Can wait for read/write/exceptional data

• stream_select() tells you when what you want to do will not block

Page 29: PHP Streams: Lucky Dip

stream_select()

$fp = fsockopen(‘www.php.net’, 80);fwrite($fp, “GET / HTTP/1.0\r\n” .“Host: www.php.net\r\n\r\n”);# wait up to 2 seconds$n = stream_select($r = array($fp), $w = null, $e = null, 2); if ($n) $data = fread($fp, 8192);else echo “timed out”;

Page 30: PHP Streams: Lucky Dip

Awful code made nicer

$sites[] = open_site(‘www.php.net’);$sites[] = open_site(‘pecl.php.net’);$sites[] = open_site(...); # repeat 20 times$n = stream_select($r = $sites, $w = null, $e = null, 30);if ($n) foreach ($r as $fp) { $x = fread($fp, 8192); if (strlen($x)) echo $x; else { # it closed unset($sites[array_search($fp, $sites)]); }}

Page 31: PHP Streams: Lucky Dip

Meta data

• stream_get_meta_data();

• People mis-used it

• The manual says “Hands off!”

• Especially eof and unread_bytes

• “I do not think it means what you think it means”INIGO, The Princess Bride

Page 32: PHP Streams: Lucky Dip

pipes

• A bit like sockets

• But they’re uni-directional

• data flows only one way

• Often used to pipe the output of one process into another

• popen() and proc_open()

• STDIN, STDOUT, STDERR

Page 33: PHP Streams: Lucky Dip

popen()

$pipe = popen(‘ls -l’, ‘r’);fpassthru($pipe);

$mail = popen(‘sendmail -t -i’, ‘w’);fwrite($mail, ‘Subject: hello...’);

Page 34: PHP Streams: Lucky Dip

proc_open()

• More powerful than popen()

• Cwd

• Environment

• Multiple pipes

• Files

• Suppress GPF dialog (Win32)

• pty

Page 35: PHP Streams: Lucky Dip

proc_open()

$p = proc_open( ‘--command--fd 0 --status-fd 1’, array( 0 => array(‘pipe’, ‘r’), array( 1 => array(‘pipe’, ‘w’), array( 2 => array(‘pipe’, ‘w’), $pipes, # will hold the pipe handles ‘/’, # child cwd will be / array( # set the environment ‘LOGNAME’ => ‘wez’, ‘HOME’ => ‘/home/wez/’), array(‘suppress_errors’ => 1));

Page 36: PHP Streams: Lucky Dip

proc_open()

fwrite($pipes[0], ‘some gpg command’);do { $r = array($pipes[1], $pipes[2]); if (stream_select($r, $w = null, $e = null, 30)) { foreach ($r as $p) { $data = fread($p, 8192); $prefix = $p == $pipes[1] ? ‘out’ : ‘err’; echo “$prefix: $data\n”; } }} while (true);

Page 37: PHP Streams: Lucky Dip

Transports and Wrappers

• Three primitives in PHP Streams:

• Streams themselves (“objects”)

• Transports (“classes”)

• Wrappers (“glue”)

Page 38: PHP Streams: Lucky Dip

Transports

• Are streams that implement the internal transports API

• fsockopen(): tcp://, udp://, ssl://

• send/receive the data you send/receive

Page 39: PHP Streams: Lucky Dip

Wrapper

• Glue code that acts as a factory for speaking a protocol

• fopen(): http://, ftp://

• Work happens, and you are handed back a stream

Page 40: PHP Streams: Lucky Dip

User-defined streams

PHP allows you to create your own stream implementations in PHP

It’s actually a hybrid wrapper/stream

class mystream { # methods go here}

stream_wrapper_register(‘myFile’, ‘mystream’);

Page 41: PHP Streams: Lucky Dip

User-defined streams

class mystream { var $fp; function stream_open($path, $mode, $options, &$opened_path) { $url = urldecode(substr($path, 9)); $this->fp = fopen($url, $mode); $opened_path = $path; return $this->fp; }}

Page 42: PHP Streams: Lucky Dip

User-defined streams

... function stream_close() { $this->fp = null; return true; } function stream_read($count) { return fread($this->fp, $count); } function stream_write($data) { return fwrite($this->fp, $data); }...

Page 43: PHP Streams: Lucky Dip

User-defined streams

... function stream_tell() { return ftell($this->fp); } function stream_eof() { return feof($this->fp); } function stream_seek($offset, $whence) { return fseek($this->fp, $offset, $whence) == 0 ? true : false; }...

Page 44: PHP Streams: Lucky Dip

User-defined streams

... function stream_flush() { return fflush($this->fp); } function stream_stat() { return fstat($this->fp); } function stream_lock($action) { return flock($this->fp, $action); }...

Page 45: PHP Streams: Lucky Dip

User-defined streams

... function unlink($path); function rename($old, $new); function mkdir($path, $mode, $opts); function rmdir($path, $opts); function url_stat($path, $flags); function dir_opendir($path, $opts); function dir_readdir(); function dir_rewinddir(); function dir_closedir();...

Page 46: PHP Streams: Lucky Dip

Questions?

• My blog: http://netevil.org

• These slides on my blog and on slideshare.net