Multithreading in php as implemented. Introduction to PHP Streams

It seems that PHP developers rarely use parallelism. I will not talk about the simplicity of synchronous code, single-threaded programming is, of course, simpler and clearer, but sometimes a little use of parallelism can bring a noticeable performance increase.

In this article, we'll take a look at how multithreading can be achieved in PHP using the pthreads extension. This will require a ZTS (Zend Thread Safety) version of PHP 7.x installed, along with the pthreads v3 extension installed. (At the time of writing, in PHP 7.1, users will need to install from the master branch in the pthreads repository - see third party extension.)

A little clarification: pthreads v2 is for PHP 5.x and is no longer supported, pthreads v3 is for PHP 7.x and is being actively developed.

After such a digression, let's get right down to business!

Handling one-time tasks

Sometimes you want to process one-time tasks in a multi-threaded way (for example, executing some I/O-bound task). In such cases, you can use the Thread class to create a new thread and run some processing on a separate thread.

For example:

$task = new class extends Thread ( private $response; public function run() ( $content = file_get_contents("http://google.com"); preg_match("~ (.+)~", $content, $matches); $this->response = $matches; ) ); $task->start() && $task->join(); var_dump($task->response); // string (6) "Google"

Here, the run method is our processing that will be performed inside the new thread. When Thread::start is called, a new thread is spawned and the run method is called. We then join the spawned thread back to the main thread by calling Thread::join , which will block until the spawned thread has finished executing. This ensures that the task finishes executing before we try to output the result (which is stored in $task->response).

It may not be desirable to pollute a class with additional responsibilities related to thread logic (including the responsibility of defining a run method). We can isolate such classes by deriving from the Threaded class. Then they can be run inside another thread:

Class Task extends Threaded ( public $response; public function someWork() ( $content = file_get_contents("http://google.com"); preg_match("~ (.+) ~", $content, $matches); $ this->response = $matches; ) ) $task = new Task; $thread = new class($task) extends Thread ( private $task; public function __construct(Threaded $task) ( $this->task = $task; ) public function run() ( $this->task->someWork( ); ) ); $thread->start() && $thread->join(); var_dump($task->response);

Any class that needs to be run on a separate thread, must inherit from the Threaded class. This is because it provides the necessary capabilities to perform processing on different threads, as well as implicit security and useful interfaces (such as resource synchronization).

Let's take a look at the class hierarchy provided by the pthreads extension:

Threaded (implements Traversable, Collectable) Thread Worker Volatile Pool

We have already covered and learned the basics of the Thread and Threaded classes, now let's take a look at the other three (Worker , Volatile , and Pool).

Thread reuse

Starting a new thread for each task that needs to be parallelized is quite expensive. This is because the "nothing-common" architecture must be implemented in pthreads in order to achieve multithreading within PHP. Which means that the entire execution context of the current instance of the PHP interpreter (including every class, interface, trait, and function) must be copied for every thread created. Because this has a noticeable performance impact, a thread should always be reused whenever possible. Threads can be reused in two ways: with Worker s or with Pool s.

The Worker class is used to perform a series of tasks synchronously within another thread. This is done by creating a new instance of the Worker (which creates a new thread) and then pushing tasks onto that individual thread's stack (using Worker::stack).

Here is a small example:

Class Task extends Threaded ( private $value; public function __construct(int $i) ( $this->value = $i; ) public function run() ( usleep(250000); echo "Task: ($this->value) \n"; ) ) $worker = new Worker(); $worker->start(); for ($i = 0; $i stack(new Task($i)); ) while ($worker->collect()); $worker->shutdown();

In the example above, 15 tasks are pushed onto the stack for a new $worker object via the Worker::stack method, and then they are processed in the order they were pushed. The Worker::collect method, as shown above, is used to clean up tasks as soon as they have finished executing. With it, inside the while loop, we block the main thread until all the tasks on the stack are completed and until they are cleared - before we call Worker::shutdown . Terminating a worker early (i.e., while there are still tasks to be completed) will still block the main thread until all tasks have completed their execution, just the tasks are not garbage collected (which entails are memory leaks).

The Worker class provides several other methods related to its task stack, including Worker::unstack to remove the last inserted task and Worker::getStacked to get the number of tasks in the execution stack. The worker's stack contains only tasks that need to be completed. Once a task on the stack has been completed, it is removed and placed on a separate (internal) stack for garbage collection (using the Worker::collect method).

Another way to reuse a thread for many tasks is to use a thread pool (via the Pool class). The thread pool uses a group of Workers to allow tasks to run simultaneously, in which the concurrency factor (the number of pool threads it runs on) is set when the pool is created.

Let's adapt the above example to use a pool of workers:

Class Task extends Threaded ( private $value; public function __construct(int $i) ( $this->value = $i; ) public function run() ( usleep(250000); echo "Task: ($this->value) \n"; ) ) $pool = new Pool(4); for ($i = 0; $i submit(new Task($i)); ) while ($pool->collect()); $pool->shutdown();

There are a few notable differences when using a pool as opposed to a worker. Firstly, the pool does not need to be started manually, it starts executing tasks as soon as they become available. Second, we send tasks to the pool, not put them on a stack. Also, the Pool class does not inherit from Threaded and therefore cannot be passed to other threads (unlike Worker).

As a good practice, workers and pools should always clean up their tasks as soon as they have completed and then manually terminate them themselves. Threads created with the Thread class must also be attached to the parent thread.

pthreads and (not) mutability

The last class we'll touch on is Volatile , a new addition to pthreads v3. The notion of immutability has become an important concept in pthreads, as without it, performance drops significantly. Therefore, by default, the properties of Threaded classes that are themselves Threaded objects are now immutable, and therefore cannot be overwritten after they were originally assigned. Explicit mutability for such properties is currently preferred, and can still be achieved with the new Volatile class.

Let's take a look at an example that demonstrates the new immutability constraints:

Class Task extends Threaded // a Threaded class ( public function __construct() ( $this->data = new Threaded(); // $this->data is not overwritable, since it is a Threaded property of a Threaded class ) ) $task = new class(new Task()) extends Thread ( // a Threaded class, since Thread extends Threaded public function __construct($tm) ( $this->threadedMember = $tm; var_dump($this->threadedMember-> data); // object(Threaded)#3 (0) () $this->threadedMember = new StdClass(); // invalid, since the property is a Threaded member of a Threaded class ) );

The Threaded properties of the Volatile classes, on the other hand, are mutable:

Class Task extends Volatile ( public function __construct() ( $this->data = new Threaded(); $this->data = new StdClass(); // valid, since we are in a volatile class ) ) $task = new class(new Task()) extends Thread ( public function __construct($vm) ( $this->volatileMember = $vm; var_dump($this->volatileMember->data); // object(stdClass)#4 (0) () // still invalid, since Volatile extends Threaded, so the property is still a Threaded member of a Threaded class $this->volatileMember = new StdClass(); ) );

We see that the Volatile class overrides the immutability imposed by the Threaded parent class to provide the ability to change the Threaded properties (as well as unset() ).

There is one more subject to discuss to cover the topic of mutability and the Volatile class - arrays. In pthreads, arrays are automatically cast to Volatile objects when assigned to a property of the Threaded class. This is because it is simply unsafe to manipulate an array from multiple PHP contexts.

Let's look at the example again to better understand some things:

$array = ; $task = new class($array) extends Thread ( private $data; public function __construct(array $array) ( $this->data = $array; ) public function run() ( $this->data = 4; $ this->data = 5; print_r($this->data); ) ); $task->start() && $task->join(); /* Output: Volatile Object ( => 1 => 2 => 3 => 4 => 5) */

We see that Volatile objects can be treated as if they were arrays, since they support array operations such as (as shown above) the subset operator (). However, the Volatile classes do not support basic array functions such as array_pop and array_shift . Instead, the Threaded class provides us with similar operations as built-in methods.

As a demo:

$data = new class extends Volatile ( public $a = 1; public $b = 2; public $c = 3; ); var_dump($data); var_dump($data->pop()); var_dump($data->shift()); var_dump($data); /* Output: object( [email protected])#1 (3) ( ["a"]=> int(1) ["b"]=> int(2) ["c"]=> int(3) ) int(3) int(1) object ( [email protected])#1 (1) ( ["b"]=> int(2) ) */

Other supported operations include Threaded::chunk and Threaded::merge .

Synchronization

In the last section of this article, we will look at synchronization in pthreads. Synchronization is a method that allows you to control access to shared resources.

For example, let's implement a simple counter:

$counter = new class extends Thread ( public $i = 0; public function run() ( for ($i = 0; $i i; ) ) ); $counter->start(); for ($i = 0; $i i; ) $counter->join(); var_dump($counter->i); // prints a number from 10 to 20

Without the use of synchronization, the output is not deterministic. Multiple threads write to the same variable without controlled access, which means updates will be lost.

Let's fix this so that we get the correct output of 20 by adding a sync:

$counter = new class extends Thread ( public $i = 0; public function run() ( $this->synchronized(function () ( for ($i = 0; $i i; ) )); ) ); $counter->start(); $counter->synchronized(function ($counter) ( for ($i = 0; $i i; ) ), $counter); $counter->join(); var_dump($counter->i); // int(20)

Synchronized blocks of code can also communicate with each other using the Threaded::wait and Threaded::notify (or Threaded::notifyAll) methods.

Here is an alternate increment in two synchronized while loops:

$counter = new class extends Thread ( public $cond = 1; public function run() ( $this->synchronized(function () ( for ($i = 0; $i notify(); if ($this->cond === 1) ( $this->cond = 2; $this->wait(); ) ) )); ) ); $counter->start(); $counter->synchronized(function ($counter) ( if ($counter->cond !== 2) ( $counter->wait(); // wait for the other to start first ) for ($i = 10; $i notify(); if ($counter->cond === 2) ( $counter->cond = 1; $counter->wait(); ) ) ), $counter); $counter->join(); /* Output: int(0) int(10) int(1) int(11) int(2) int(12) int(3) int(13) int(4) int(14) int(5) int( 15) int(6) int(16) int(7) int(17) int(8) int(18) int(9) int(19) */

You may notice additional conditions that have been placed around the call to Threaded::wait . These conditions are critical because they allow a synchronized callback to resume when it has received a notification and the specified condition is true . This is important because notifications can come from other places than when Threaded::notify is called. Thus, if calls to the Threaded::wait method were not wrapped in conditions, we would execute false wake up calls, which will lead to unpredictable code behavior.

Conclusion

We looked at the five classes in the pthreads package (Threaded , Thread , Worker , Volatile and Pool) and how each class is used. We also took a look at the new concept of immutability in pthreads, made a brief overview of the supported synchronization features. With these basics in place, we can now start looking at how to use pthreads in real world cases! This will be the topic of our next post.

If you are interested in the translation of the next post, let me know: comment on the social. networks, upvote and share the post with colleagues and friends.

Sometimes it becomes necessary to perform several actions at the same time, for example, to check for changes in one database table and make modifications to another. Moreover, if one of the operations (for example, checking for changes) takes a long time, it is obvious that sequential execution will not provide resource balancing.

To solve such problems, programming uses multithreading - each operation is placed in a separate thread with a dedicated amount of resources and works inside it. With this approach, all tasks will be performed separately and independently.

Although PHP does not support multithreading, there are several methods for emulating it, which will be discussed below.

1. Running multiple copies of the script - one copy per operation

//woman.php if (!isset($_GET["thread"])) ( system("wget ​​http://localhost/woman.php?thread=make_me_happy"); system("wget ​​http://localhost/ woman.php?thread=make_me_rich"); ) elseif ($_GET["thread"] == "make_me_happy") ( make_her_happy(); ) elseif ($_GET["thread"] == "make_me_rich") ( find_another_one( ); )

When we execute this script without parameters, it automatically starts two copies of itself, with operation IDs ("thread=make_me_happy" and "thread=make_me_rich"), which initiate the execution of the necessary functions.

Thus, we achieve the desired result - two operations are performed simultaneously - but this is of course not multithreading, but simply a crutch for performing tasks simultaneously.

2. Path of the Jedi - using the PCNTL extension

PCNTL is an extension that allows you to fully work with processes. In addition to management, it supports sending messages, checking status, and setting priorities. This is what the previous script looks like using PCNTL:

$pid = pcntl_fork(); if ($pid == 0) ( make_her_happy(); ) elseif ($pid > 0) ( $pid2 = pcntl_fork(); if ($pid2 == 0) ( find_another_one(); ) )

Looks pretty confusing, let's go line by line.

In the first line, we "fork" the current process (fork - copying the process from saving the values ​​of all variables), dividing it into two processes (current and child) running in parallel.

To understand where we are at the moment, in the child or parent process, the pcntl_fork function returns 0 for the child and the process ID for the parent. Therefore, in the second line, we look at $pid, if it is zero, then we are in the child process - we execute the function, otherwise, we are in the parent (line 4), then we create another process and perform the task in the same way.

Script execution process:

Thus, the script creates 2 more child processes, which are copies of it, contain the same variables with similar values. And with the help of the identifier returned by the pcntl_fork function, we are guided in which stream we are currently in and perform the necessary actions.

There is no official one, I'll tell you right away. There are only numerous crutches. Examples will mostly be useful in SEO software.

In general, the task in front of me was to roll up a multi-threaded one, I won’t say that, and not just multi-threaded, but so that you can also manage the threads, as in the same Delphi. That is, in order to be able to stop the thread, start it, pause it, resume it, and the threads still have to notify about their state, saying that I am currently doing something.

Sobsno, the theory of implementation.

Two scripts - one thread, the second thread launcher. The thread does its job, the launcher calculates how many threads to run and with what parameters.

Example one, starts 20 threads if total number of threads is lower than 10

1): $thCount = exec("ps a | thread.php | wc -l"); echo $thCount."\r\n"; if($thCount< 10){ for($i = 0; $i < 20; $i++){ echo "launch thread\r\n"; passthru("(php -f thread.php &) >> /dev/null 2>&1"); //sleep(1); ) ) else ( sleep(5); ) endwhile; ?>

It turns out crap, which dozapuskivat flows from time to time under the condition. There are always a different number of threads at different intervals of time and there is no way to stop them. But, if all these things are not critical, the loop works quite well. Suitable, for example, all sorts of regers. When finishing, you can keep the required number of threads with an error of + - 1. To do this, you just need to dynamically calculate the number of threads to be launched. Yes, and ps shows three running threads when called like this, even if there aren't any. One gives the grep, the second the script itself, the third fig knows who.

Option 2.

Here I already wanted to make a more flexible thing. Namely, take for example 1k urls and distribute them equally to N streams.

> /dev/null 2>&1"); ) if($i == $threads-1) ( passthru("(php -f thread.php "".$perThread * $i."|".count($ base)."" &) >> /dev/null 2>&1"); ) if(($i !== 0)&&($i !== $threads-1)) ( $start = $perThread * $i + 1; $end = $start -1 + $perThread; passthru("(php -f thread.php "".$start."|".$end."" &) >> /dev/null 2 >&1"); ) ) ?>

This example starts a thread with parameters, which it then gets from the $argv array and uses. Such is the division of labor.

Interaction between thread and mother.
I have only a theory here. I think to implement it through files. The thread creates a file with the name of its session (numbers, pida), the mother reads from there. To display information about threads nicely and clearly, I think to use ncurses . This is a sish lib, midnight commander, top are made on it. Available not only for php.

Thread state changes.
So I didn't think of it. You can try through the same files, but after each line of the flow code, you need to insert a file check for the presence of instructions there. The speed will automatically drop. Yes, and with such implementations, the mother is killed only manually.

Sanya, aka pokhape dud, immediately chopped off - I would not solve such problems in php. I also look towards other languages, I think to learn something like C, but until I learn, I probably won't need anything anymore. There is a python version. And just in the reader I came across an article by one comrade who began to study it and

Is there a realistic way to implement the multi-threaded model in PHP, is it real or is it just to mimic it. It was suggested some time ago that you can force the operating system to load another instance of the PHP executable and handle other concurrent processes.

The problem is that when the PHP code finishes executing the PHP instance, it stays in memory because there is no way to kill it from PHP. So if you are simulating multiple threads you can imagine what will happen. So I'm still looking for a way to multithread that can be mimicked effectively or efficiently from PHP. Any ideas?

Multithreading is possible in php

Yes you can multithread in PHP with pthreads

From the PHP documentation:

pthreads is an object oriented API that provides all the tools you need for multithreading in PHP. PHP applications can create, read, write, execute, and synchronize with Threads, Workers, and Threaded.

Warning. The pthreads extension cannot be used in a web server environment. Therefore, threads in PHP should remain in CLI-based applications.

simple test

#!/usr/bin/phparg = $arg; ) public function run() ( if ($this->arg) ( $sleep = mt_rand(1, 10); printf("%s: %s -start -sleeps %d" . "\n", date(" g:i:sa"), $this->arg, $sleep); sleep($sleep); printf("%s: %s -finish" . "\n", date("g:i:sa" ), $this->arg); ) ) ) // Create an array $stack = array(); //Initiate Multiple Thread foreach (range("A", "D") as $i) ( $stack = new AsyncOperation($i); ) // Start The Threads foreach ($stack as $t) ( $t- >start(); ) ?>

First run

12:00:06pm: A -start -sleeps 5 12:00:06pm: B -start -sleeps 3 12:00:06pm: C -start -sleeps 10 12:00:06pm: D -start -sleeps 2 12: 00:08pm: D-finish 12:00:09pm: B-finish 12:00:11pm: A-finish 12:00:16pm: C-finish

Second run

12:01:36pm: A -start -sleeps 6 12:01:36pm: B -start -sleeps 1 12:01:36pm: C -start -sleeps 2 12:01:36pm: D -start -sleeps 1 12: 01:37pm: B-finish 12:01:37pm: D-finish 12:01:38pm: C-finish 12:01:42pm: A-finish

Real world example

error_reporting(E_ALL); class AsyncWebRequest extends Thread ( public $url; public $data; public function __construct($url) ( $this->url = $url; ) public function run() ( if (($url = $this->url)) ( /* * If a large amount of data is being requested, you might want to * fsockopen and read using usleep in between reads */ $this->data = file_get_contents($url); ) else printf("Thread #%lu was not provided a URL\n", $this->getThreadId()); ) ) $t = microtime(true); $g = new AsyncWebRequest(sprintf("http://www.google.com/?q=%s", rand() * 10)); /* starting synchronization */ if ($g->start()) ( printf("Request took %f seconds to start ", microtime(true) - $t); while ($g->isRunning()) ( echo "."; usleep(100); ) if ($g->join()) ( printf(" and %f seconds to finish receiving %d bytes\n", microtime(true) - $t, strlen($g ->data)); ) else printf(" and %f seconds to finish, request failed\n", microtime(true) - $t); )

why don't you use popen ?

For ($i=0; $i<10; $i++) { // open ten processes for ($j=0; $j<10; $j++) { $pipe[$j] = popen("script2.php", "w"); } // wait for them to finish for ($j=0; $j<10; ++$j) { pclose($pipe[$j]); } }

Threading is not available in the PHP stock, but concurrent programming is possible using HTTP requests in the form of asynchronous calls.

If the curl timeout option is set to 1 and uses the same session_id for the processes you want to bind to each other, you can bind to session variables like in my example below. With this method, you can even close your browser and the concurrent process still exists on the server.

Don't forget to check the correct session id, for example:

http://localhost/test/verifysession.php? sessionid = [correct id]

startprocess.php

$request = "http://localhost/test/process1.php?sessionid=".$_REQUEST["PHPSESSID"]; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $request); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 1); curl_exec($ch); curl_close($ch); echo $_REQUEST["PHPSESSID"];

process1.php

set_time_limit(0); if ($_REQUEST["sessionid"]) session_id($_REQUEST["sessionid"]); function checkclose() ( global $_SESSION; if ($_SESSION["closesession"]) ( unset($_SESSION["closesession"]); die(); ) ) while(!$close) ( session_start(); $_SESSION ["test"] = rand(); checkclose(); session_write_close(); sleep(5); ) with set_time_limit(0); if ($_REQUEST["sessionid"]) session_id($_REQUEST["sessionid"]); function checkclose() ( global $_SESSION; if ($_SESSION["closesession"]) ( unset($_SESSION["closesession"]); die(); ) ) while(!$close) ( session_start(); $_SESSION ["test"] = rand(); checkclose(); session_write_close(); sleep(5); ) in set_time_limit(0); if ($_REQUEST["sessionid"]) session_id($_REQUEST["sessionid"]); function checkclose() ( global $_SESSION; if ($_SESSION["closesession"]) ( unset($_SESSION["closesession"]); die(); ) ) while(!$close) ( session_start(); $_SESSION ["test"] = rand(); checkclose(); session_write_close(); sleep(5); )

verifysession.php

if ($_REQUEST["sessionid"]) session_id($_REQUEST["sessionid"]); session_start(); var_dump($_SESSION);

closeprocess.php

if ($_REQUEST["sessionid"]) session_id($_REQUEST["sessionid"]); session_start(); $_SESSION["closesession"] = true; var_dump($_SESSION);

While you can't thread, you have a certain degree of process control in php. Here are two useful sets:

Process control functions http://www.php.net/manual/en/ref.pcntl.php

You can fork your process with pcntl_fork - return the PID of the child. You can then use posix_kill to use that PID.

However, if you kill the parent process, a signal must be sent to the child process, telling it to die. If php itself doesn't recognize this, you can register a function to manage it and do a clean exit using pcntl_signal.

You can simulate threads. PHP can start background processes via popen (or proc_open). These processes can be piped using stdin and stdout. Of course these processes can be a php program. It's probably as close as you get.

You can use exec() to run a command line script (e.g. php command line) and if you pipe the output to a file your script won't wait for the command to complete.

I can't remember the CLI php syntax, but what you need is something like:

Exec("/path/to/php -f "/path/to/file.php" | "/path/to/output.txt"");

I think a few shared hosting servers have exec() disabled by default for security reasons, but it might be worth a try.

Depending on what you are trying to do, you can also use curl_multi to achieve it.

It supports bi-directional inter-thread communication and also has built-in protection for killing child threads (orphan prevention).

You may have the option:

  1. multi_curl
  2. You can use the system command for the same
  3. The ideal scenario is to create a streaming function in C and compile/configure in PHP. This function will now be a PHP function.

How about pcntl_fork?

check our man page for examples: PHP pcntl_fork

pcntl_fork will not work in a web server environment if enabled safe mode. In this case, it will only work in the PHP version of the CLI.

The Thread class has been available since PECL pthreads ≥ 2.0.0.

In programming, you constantly have to work with various resources: files, sockets, http connections. And they all have some kind of access interface, often incompatible with each other. Therefore, in order to eliminate these inconsistencies and unify work with various data sources, starting with PHP 4.3 were invented PHP Streams - streams.

Although PHP 4.3 came out a long time ago, many PHP programmers have very little idea of streams in PHP, and continue to use CURL everywhere, although PHP for this there is a more convenient alternative in the form Stream Context.

The following types of streams exist in PHP:

  • File on hard drive;
  • HTTP connection with a website;
  • Compound UDP with the server
  • ZIP file;
  • File * .mp3.

What do all these resources have in common? All of them can be read and written, ie. all of them can be read and written. Force PHP streams the point is that you can access all of these resources using the same set of functions. It is very comfortable. Also, if the need arises, you can write your own thread handler implementation "stream wrapper". In addition to reading and writing, streams in PHP also allows you to perform other operations such as renaming and deleting.

Programming on PHP, You have already met with flows, although you may not have guessed about it. So, the functions that work with streams are fopen(), file_get_contents(), file() etc. So, in fact, you are already using file streams all this time, completely transparently.

To work with another type of stream, you must specify its protocol (wrapper) in the following way: wrapper://some_stream_resource, Where wrapper://- this is, for example http://, file://, ftp://, zip:// etc., and some_stream_resource - URI, identifies what you want to open. URI does not impose any restrictions on the format. Examples:

  • http://website/php-stream-introduction.html
  • file://C:/Projects/rostov-on-don.jpg
  • ftp://user: [email protected]/pub/file.txt
  • mpeg://file:///music/song.mp3
  • data://text/plain;base64,SSBsb3ZlIFBIUAo=

However, please note that not all protocols and handlers may work for you, as support for some shells depends on your settings. Therefore, to find out which protocols are supported, you need to run the following script:

// list of registered socket transports
print_r(stream_get_transports());

// list of registered threads (handlers)
print_r(stream_get_wrappers());

// list of registered filters
print_r(stream_get_filters();

PHP Stream Contexts

Often there is a need to specify additional parameters for an http request. Flow contexts solve this problem by allowing you to specify additional options. Many functions that support threading have an optional thread context parameter. Let's look at the function file_get_contents():

String file_get_contents(string $filename [, int $flags = 0 [, resource $context [, int $offset = -1 [, int $maxlen = -1]]]])

As you can see, the thread context is passed as the third parameter. Contexts are created using the function stream_context_create(), which takes an array and returns a context resource.

$options = array(
"http" => array(
"method" => "GET",
"header" => "Accept-language: en\r\n".
"Cookie: foo = bar\r\n"
);

$context = stream_context_create($options);

// Using this with file_get_contents ...
echo file_get_contents("http://www.example.com/", 0, $context);

So today we learned what is threads and thread contexts in PHP, looked at examples of their use, and in the following articles we will talk about stream metadata and create our own handler.