Когда используется StreamContext? И когда это не следует использовать повторно?

Я перехожу от http к https, поэтому мне нужно добавить StreamContext к нескольким вызовам read_file и get_file_contents.

Мне нужно заменить

read_file('http://'.$host.$uri);

$stream_context = stream_context_create([
    /* some lenghty options array */
]);
read_file('https://'.$host.$uri, false, $stream_context);

Теперь мой вопрос: Возможно ли повторное использование $stream_context следующим образом:

$stream_context = stream_context_create([
    /* some lenghty options array */
]);
read_file('https://'.$host.$uri, false, $stream_context);
get_file_contents($another_url, false, $stream_context);
read_file($even_another, false, $stream_context);

или мне нужно воссоздать новый StreamContext для каждого URL?

Задано иначе: Является ли контекст потока только дескриптором параметров и параметров или он привязан к ресурсу при его использовании?

Изменить: Как видно из комментариев, повторное использование StreamContext часто, но не всегда. Это не совсем удовлетворительно, как ответ.

Когда его можно или нужно использовать повторно, и когда его нельзя использовать повторно? Может кто-то пролить свет на внутреннюю работу StreamContext. Документация выглядит довольно скудной для меня.

Ответ 1

контексты потоков повторно используются, и их можно повторно использовать всегда, а не часто.

Комментарий от @ilpaijin, указывающий на "непредсказуемый комментарий к поведению", является простым недоразумением автора, оставляющего комментарий.

Когда вы указываете свой контекст для обертки HTTP, вы указываете оболочку как HTTP независимо от схемы, на которую вы нацеливаетесь, то есть нет такой вещи, как HTTPS-обертка.

Если вы попытаетесь сделать следующее:

"https" => [
// options will not be applied to HTTPS stream as there is no such wrapper (https)
]

Правильный способ:

"http" => [
// options will apply to http:// and https:// streams.
]

Когда следует/может повторно использовать?

Это действительно зависит от вас и до логики, которую вы пытаетесь реализовать.

Не забывайте, что у вас установлен контекст по умолчанию для всех собственных оболочек PHP.

Пример, который вы отправили, где у вас есть тот же контекстный поток, который передается на 3 разных вызова, не нужен, просто используйте stream_context_set_default и установите контекст по умолчанию для запроса, исходящий из вашего кода.

Существуют определенные ситуации, когда вы задаете значение по умолчанию, но для одного конкретного запроса, который вы хотите использовать в другом контексте, было бы неплохо создать другой поток и передать его.

Включен ли контекст потока, например, cookie или tls начальное согласование, которое проходит от одного вызова к другому?

Контекст потока не содержит состояния, однако вы могли бы получить такой макет, как это, с помощью дополнительного кода. Любое состояние, пусть это будет cookie или TLS-рукопожатие, - это просто заголовки запросов. Вам нужно будет прочитать эту информацию из входящего запроса и установить ее в потоке, а затем передать этот поток другому запросу, таким образом высмеивая "состояние" родительского запроса. Это сказано - не делайте этого, просто используйте CURL.

С одной стороны, реальная мощность потоков создает собственный/пользовательский stream. Управление заголовками и контроль состояния намного проще (и лучше) достигается с помощью CURL.

Ответ 2

Кажется, что вы можете. Я использовал xdebug_debug_zval и провел несколько простых тестов, чтобы проверить, сохраняет ли PHP его внутренне (я использовал PHP 7.1.3 с xdebug на внутреннем сервере разработки)

$context = stream_context_create(['https' => ['method' => 'GET']]);
xdebug_debug_zval('context');
$stream = file_get_contents('https://secure.php.net/manual/en/function.file-get-contents.php', false, $context);
xdebug_debug_zval('context');
$stream = fopen('https://secure.php.net/', 'r', false, $context);
xdebug_debug_zval('context');

То, что я получил, было

контекст:

(refcount = 1, is_ref = 0) resource (2, stream-context)

контекст:

(refcount = 1, is_ref = 0) resource (2, stream-context)

контекст:

(refcount = 2, is_ref = 0) resource (2, stream-context)

Интересно, что второй вызов увеличил коэффициент пересчета, то есть он был передан по ссылке внутри страны. Даже отключение $stream не удаляло его или не позволяло мне снова называть его.

Изменить

Поскольку вопрос был изменен...

Контекст создает тип данных resource. Поскольку он содержит экземпляр данных PHP, он передается по ссылке неявно, что означает, что PHP напрямую передает внутренние данные, а не просто делает его копию. Там нет собственного способа уничтожить контекст.

Ответ 3

Он, по-видимому, служит в качестве объекта соединения (такая же логика, как при подключении к базе данных), и его можно использовать повторно аналогичным образом:

<?php
$default_opts = array(
  'http'=>array(
    'method'=>"GET",
    'header'=>"Accept-language: en\r\n" .
              "Cookie: foo=bar",
    'proxy'=>"tcp://10.54.1.39:8000"
  )
);


$alternate_opts = array(
  'http'=>array(
    'method'=>"POST",
    'header'=>"Content-type: application/x-www-form-urlencoded\r\n" .
              "Content-length: " . strlen("baz=bomb"),
    'content'=>"baz=bomb"
  )
);

$default = stream_context_get_default($default_opts);
$alternate = stream_context_create($alternate_opts);

/* Sends a regular GET request to proxy server at 10.54.1.39
 * For www.example.com using context options specified in $default_opts
 */
readfile('http://www.example.com');

/* Sends a POST request directly to www.example.com
 * Using context options specified in $alternate_opts
 */
readfile('http://www.example.com', false, $alternate);

?>

Ответ 4

Я согласен с приведенными выше ответами, stream_context_create() создаст и вернет дескриптор ресурса, установив параметры параметра для соединения. Это можно повторно использовать для разных ресурсов, поскольку это дескриптор. Не имеет значения, где он используется, но должен иметь обработку в запросе.