Commit 51b934d4450 for php.net

commit 51b934d445074fdae28a744d2b094297136137cd
Author: Gina Peter Banyard <girgias@php.net>
Date:   Sun Jun 28 18:54:51 2026 +0100

    ext/openssl: convert stream errors to new stream error API (#22318)

    Convert the ext/openssl stream messages to the new stream error API so they
    honour the context error_mode/store like the other converted streams.

    All messages emitted while a stream error operation is open must go through
    the new API, otherwise mixing immediate php_error_docref() with buffered
    reporting reorders the output. This required emitting "Accept failed" via
    php_stream_warn() (adding StreamErrorCode::AcceptFailed) and threading a
    php_stream* through php_openssl_check_path_ex() (NULL for non-stream callers).

    Fingerprint changes: refactor php_openssl_x509_fingerprint_cmp() into an
    'is equal' variant using zend_string; make php_openssl_x509_fingerprint_match()
    warn on all failure cases to avoid double warnings; thread a php_stream* into
    php_openssl_x509_fingerprint() so it reports through the operation, and demote
    its internal "Could not generate signature" failure from E_ERROR to a warning.

    Co-authored-by: Jakub Zelenka <bukka@php.net>

diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c
index 6b29471b248..1e63eb1381f 100644
--- a/ext/openssl/openssl.c
+++ b/ext/openssl/openssl.c
@@ -651,7 +651,8 @@ static void php_openssl_check_path_error(uint32_t arg_num, int type, const char
 /* openssl file path check extended */
 bool php_openssl_check_path_ex(
 		const char *file_path, size_t file_path_len, char *real_path, uint32_t arg_num,
-		bool contains_file_protocol, bool is_from_array, const char *option_name)
+		bool contains_file_protocol, bool is_from_array, const char *option_name,
+		php_stream *stream)
 {
 	const char *fs_file_path;
 	size_t fs_file_path_len;
@@ -686,8 +687,13 @@ bool php_openssl_check_path_ex(
 		if (arg_num == 0) {
 			const char *option_title = option_name ? option_name : "unknown";
 			const char *option_label = is_from_array ? "array item" : "option";
-			php_error_docref(NULL, E_WARNING, "Path for %s %s %s",
-					option_title, option_label, error_msg);
+			if (stream != NULL) {
+				php_stream_warn(stream, InvalidPath, "Path for %s %s %s",
+						option_title, option_label, error_msg);
+			} else {
+				php_error_docref(NULL, E_WARNING, "Path for %s %s %s",
+						option_title, option_label, error_msg);
+			}
 		} else if (is_from_array && option_name != NULL) {
 			php_openssl_check_path_error(
 					arg_num, error_type, "option %s array item %s", option_name, error_msg);
@@ -1294,7 +1300,7 @@ PHP_FUNCTION(openssl_x509_fingerprint)
 		RETURN_FALSE;
 	}

-	fingerprint = php_openssl_x509_fingerprint(cert, method, raw_output);
+	fingerprint = php_openssl_x509_fingerprint(cert, method, raw_output, NULL);
 	if (fingerprint) {
 		RETVAL_STR(fingerprint);
 	} else {
diff --git a/ext/openssl/openssl_backend_common.c b/ext/openssl/openssl_backend_common.c
index ba9bccff98c..8115a7c91b3 100644
--- a/ext/openssl/openssl_backend_common.c
+++ b/ext/openssl/openssl_backend_common.c
@@ -307,7 +307,7 @@ int php_openssl_parse_config(struct php_x509_request * req, zval * optional_args

 	/* read in the oids */
 	str = php_openssl_conf_get_string(req->req_config, NULL, "oid_file");
-	if (str != NULL && php_openssl_check_path_ex(str, strlen(str), path, 0, false, false, "oid_file")) {
+	if (str != NULL && php_openssl_check_path_ex(str, strlen(str), path, 0, false, false, "oid_file", NULL)) {
 		BIO *oid_bio = BIO_new_file(path, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
 		if (oid_bio) {
 			OBJ_create_objects(oid_bio);
@@ -513,7 +513,7 @@ X509 *php_openssl_x509_from_str(
 	BIO *in;

 	if (ZSTR_LEN(cert_str) > 7 && memcmp(ZSTR_VAL(cert_str), "file://", sizeof("file://") - 1) == 0) {
-		if (!php_openssl_check_path_str_ex(cert_str, cert_path, arg_num, true, is_from_array, option_name)) {
+		if (!php_openssl_check_path_str_ex(cert_str, cert_path, arg_num, true, is_from_array, option_name, NULL)) {
 			return NULL;
 		}

@@ -582,7 +582,7 @@ X509 *php_openssl_x509_from_zval(
 	return cert;
 }

-zend_string* php_openssl_x509_fingerprint(X509 *peer, const char *method, bool raw)
+zend_string* php_openssl_x509_fingerprint(X509 *peer, const char *method, bool raw, php_stream *stream)
 {
 	unsigned char md[EVP_MAX_MD_SIZE];
 	const EVP_MD *mdtype;
@@ -590,12 +590,20 @@ zend_string* php_openssl_x509_fingerprint(X509 *peer, const char *method, bool r
 	zend_string *ret;

 	if (!(mdtype = php_openssl_get_evp_md_by_name(method))) {
-		php_error_docref(NULL, E_WARNING, "Unknown digest algorithm");
+		if (stream != NULL) {
+			php_stream_warn(stream, Generic, "Unknown digest algorithm");
+		} else {
+			php_error_docref(NULL, E_WARNING, "Unknown digest algorithm");
+		}
 		return NULL;
 	} else if (!X509_digest(peer, mdtype, md, &n)) {
 		php_openssl_release_evp_md(mdtype);
 		php_openssl_store_errors();
-		php_error_docref(NULL, E_ERROR, "Could not generate signature");
+		if (stream != NULL) {
+			php_stream_warn(stream, EncodingFailed, "Could not generate signature");
+		} else {
+			php_error_docref(NULL, E_WARNING, "Could not generate signature");
+		}
 		return NULL;
 	}

@@ -792,7 +800,7 @@ X509_STORE *php_openssl_setup_verify(zval *calist, uint32_t arg_num)
 				return NULL;
 			}

-			if (!php_openssl_check_path_str_ex(str, file_path, arg_num, false, true, NULL)) {
+			if (!php_openssl_check_path_str_ex(str, file_path, arg_num, false, true, NULL, NULL)) {
 				zend_string_release(str);
 				continue;
 			}
diff --git a/ext/openssl/php_openssl.h b/ext/openssl/php_openssl.h
index 20a04bbcd1a..b88a6c59e4e 100644
--- a/ext/openssl/php_openssl.h
+++ b/ext/openssl/php_openssl.h
@@ -102,34 +102,39 @@ void php_openssl_store_errors(void);
 void php_openssl_errors_set_mark(void);
 void php_openssl_errors_restore_mark(void);

-/* openssl file path extra */
+/* openssl file path extra
+ * When a non-NULL stream is passed and the path check fails for a stream context option
+ * (arg_num == 0), the warning is reported via the stream error API so it participates in
+ * any active stream error operation; otherwise it is emitted immediately. */
 bool php_openssl_check_path_ex(
 		const char *file_path, size_t file_path_len, char *real_path, uint32_t arg_num,
-		bool contains_file_protocol, bool is_from_array, const char *option_name);
+		bool contains_file_protocol, bool is_from_array, const char *option_name,
+		struct _php_stream *stream);

 /* openssl file path check */
 static inline bool php_openssl_check_path(
 		const char *file_path, size_t file_path_len, char *real_path, uint32_t arg_num)
 {
 	return php_openssl_check_path_ex(
-			file_path, file_path_len, real_path, arg_num, false, false, NULL);
+			file_path, file_path_len, real_path, arg_num, false, false, NULL, NULL);
 }

 /* openssl file path extra check with zend string */
 static inline bool php_openssl_check_path_str_ex(
 		zend_string *file_path, char *real_path, uint32_t arg_num,
-		bool contains_file_protocol, bool is_from_array, const char *option_name)
+		bool contains_file_protocol, bool is_from_array, const char *option_name,
+		struct _php_stream *stream)
 {
 	return php_openssl_check_path_ex(
 			ZSTR_VAL(file_path), ZSTR_LEN(file_path), real_path, arg_num, contains_file_protocol,
-			is_from_array, option_name);
+			is_from_array, option_name, stream);
 }

 /* openssl file path check with zend string */
 static inline bool php_openssl_check_path_str(
 		zend_string *file_path, char *real_path, uint32_t arg_num)
 {
-	return php_openssl_check_path_str_ex(file_path, real_path, arg_num, true, false, NULL);
+	return php_openssl_check_path_str_ex(file_path, real_path, arg_num, true, false, NULL, NULL);
 }

 PHP_OPENSSL_API zend_long php_openssl_cipher_iv_length(const char *method);
diff --git a/ext/openssl/php_openssl_backend.h b/ext/openssl/php_openssl_backend.h
index 2d37e594ea5..bd12a5fe312 100644
--- a/ext/openssl/php_openssl_backend.h
+++ b/ext/openssl/php_openssl_backend.h
@@ -262,7 +262,8 @@ X509 *php_openssl_x509_from_param(
 X509 *php_openssl_x509_from_zval(
 		zval *val, bool *free_cert, uint32_t arg_num, bool is_from_array, const char *option_name);

-zend_string* php_openssl_x509_fingerprint(X509 *peer, const char *method, bool raw);
+zend_string* php_openssl_x509_fingerprint(
+		X509 *peer, const char *method, bool raw, struct _php_stream *stream);

 int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension);

diff --git a/ext/openssl/tests/ServerClientTestCase.inc b/ext/openssl/tests/ServerClientTestCase.inc
index c5db41d4841..b7e866954fc 100644
--- a/ext/openssl/tests/ServerClientTestCase.inc
+++ b/ext/openssl/tests/ServerClientTestCase.inc
@@ -129,6 +129,10 @@ class ServerClientTestCase
     private function cleanupWorkerProcess($worker)
     {
         fclose($this->workerStdIn[$worker]);
+        /* Drain stdout to EOF before closing it, so late worker writes (e.g.
+         * buffered error output) don't fail and abort the worker mid-output. */
+        stream_set_blocking($this->workerStdOut[$worker], true);
+        stream_get_contents($this->workerStdOut[$worker]);
         fclose($this->workerStdOut[$worker]);
         proc_close($this->workerHandle[$worker]);
     }
diff --git a/ext/openssl/tests/bug68920.phpt b/ext/openssl/tests/bug68920.phpt
index 4abe12586b0..a2d669e5b7d 100644
--- a/ext/openssl/tests/bug68920.phpt
+++ b/ext/openssl/tests/bug68920.phpt
@@ -76,8 +76,6 @@

 Warning: stream_socket_client(): Invalid peer_fingerprint array; [algo => fingerprint] form required in %s on line %d

-Warning: stream_socket_client(): peer_fingerprint match failure in %s on line %d
-
 Warning: stream_socket_client(): Failed to enable crypto in %s on line %d

 Warning: stream_socket_client(): Unable to connect to %s (Unknown error) in %s on line %d
@@ -85,8 +83,6 @@

 Warning: stream_socket_client(): Invalid peer_fingerprint array; [algo => fingerprint] form required in %s on line %d

-Warning: stream_socket_client(): peer_fingerprint match failure in %s on line %d
-
 Warning: stream_socket_client(): Failed to enable crypto in %s on line %d

 Warning: stream_socket_client(): Unable to connect to %s (Unknown error) in %s on line %d
diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c
index a154aa4572f..bc483df617b 100644
--- a/ext/openssl/xp_ssl.c
+++ b/ext/openssl/xp_ssl.c
@@ -145,7 +145,8 @@
 #endif

 extern php_stream* php_openssl_get_stream_from_ssl_handle(const SSL *ssl);
-extern zend_string* php_openssl_x509_fingerprint(X509 *peer, const char *method, bool raw);
+extern zend_string* php_openssl_x509_fingerprint(
+		X509 *peer, const char *method, bool raw, php_stream *stream);
 extern int php_openssl_get_ssl_stream_data_index(void);
 static struct timeval php_openssl_subtract_timeval(struct timeval a, struct timeval b);
 static int php_openssl_compare_timeval(struct timeval a, struct timeval b);
@@ -281,16 +282,14 @@ static int php_openssl_handle_ssl_error(php_stream *stream, int nr_bytes, bool i
 			if (ERR_peek_error() == 0) {
 				if (nr_bytes == 0) {
 					if (!php_openssl_is_http_stream_talking_to_iis(stream) && ERR_get_error() != 0) {
-						php_error_docref(NULL, E_WARNING, "SSL: fatal protocol error");
+						php_stream_warn(stream, ProtocolError, "SSL: fatal protocol error");
 					}
 					SSL_set_shutdown(sslsock->ssl_handle, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
 					stream->eof = 1;
 					retry = false;
 				} else {
 					char *estr = php_socket_strerror(php_socket_errno(), NULL, 0);
-
-					php_error_docref(NULL, E_WARNING,
-							"SSL: %s", estr);
+					php_stream_warn(stream, ProtocolError, "SSL: %s", estr);

 					efree(estr);
 					retry = false;
@@ -305,7 +304,7 @@ static int php_openssl_handle_ssl_error(php_stream *stream, int nr_bytes, bool i

 			switch (ERR_GET_REASON(ecode)) {
 				case SSL_R_NO_SHARED_CIPHER:
-					php_error_docref(NULL, E_WARNING,
+					php_stream_warn(stream, ProtocolError,
 							"SSL_R_NO_SHARED_CIPHER: no suitable shared cipher could be used.  "
 							"This could be because the server is missing an SSL certificate "
 							"(local_cert context option)");
@@ -324,7 +323,7 @@ static int php_openssl_handle_ssl_error(php_stream *stream, int nr_bytes, bool i

 					smart_str_0(&ebuf);

-					php_error_docref(NULL, E_WARNING,
+					php_stream_warn(stream, ProtocolError,
 							"SSL operation failed with code %d. %s%s",
 							err,
 							ebuf.s ? "OpenSSL Error messages:\n" : "",
@@ -379,21 +378,19 @@ static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) /* {{{ */
 }
 /* }}} */

-static int php_openssl_x509_fingerprint_cmp(X509 *peer, const char *method, const char *expected)
+static bool php_openssl_x509_fingerprint_is_equal(php_stream *stream, X509 *peer, const char *method, const zend_string *expected)
 {
-	zend_string *fingerprint;
-	int result = -1;
-
-	fingerprint = php_openssl_x509_fingerprint(peer, method, false);
+	bool is_equal = false;
+	zend_string *fingerprint = php_openssl_x509_fingerprint(peer, method, false, stream);
 	if (fingerprint) {
-		result = strcasecmp(expected, ZSTR_VAL(fingerprint));
-		zend_string_release_ex(fingerprint, 0);
+		is_equal = zend_string_equals_ci(fingerprint, expected);
+		zend_string_release_ex(fingerprint, false);
 	}

-	return result;
+	return is_equal;
 }

-static bool php_openssl_x509_fingerprint_match(X509 *peer, zval *val)
+static bool php_openssl_x509_fingerprint_match(php_stream *stream, X509 *peer, const zval *val)
 {
 	if (Z_TYPE_P(val) == IS_STRING) {
 		const char *method = NULL;
@@ -408,29 +405,41 @@ static bool php_openssl_x509_fingerprint_match(X509 *peer, zval *val)
 				break;
 		}

-		return method && php_openssl_x509_fingerprint_cmp(peer, method, Z_STRVAL_P(val)) == 0;
+		if (UNEXPECTED(method == NULL)) {
+			php_stream_warn(stream, AuthFailed, "peer_fingerprint length doesn't match a md5 or sha1 hash");
+			return false;
+		}
+		if (!php_openssl_x509_fingerprint_is_equal(stream, peer, method, Z_STR_P(val))) {
+			php_stream_warn(stream, AuthFailed, "peer_fingerprint match failure");
+			return false;
+		}
+		return true;
 	} else if (Z_TYPE_P(val) == IS_ARRAY) {
 		zval *current;
 		zend_string *key;

 		if (!zend_hash_num_elements(Z_ARRVAL_P(val))) {
-			php_error_docref(NULL, E_WARNING, "Invalid peer_fingerprint array; [algo => fingerprint] form required");
+			// TODO: Should this be a ValueError (also must not be empty error)?
+			php_stream_warn(stream, Generic, "Invalid peer_fingerprint array; [algo => fingerprint] form required");
 			return false;
 		}

 		ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(val), key, current) {
 			if (key == NULL || Z_TYPE_P(current) != IS_STRING) {
-				php_error_docref(NULL, E_WARNING, "Invalid peer_fingerprint array; [algo => fingerprint] form required");
+				// TODO: Should this be a ValueError?
+				php_stream_warn(stream, Generic, "Invalid peer_fingerprint array; [algo => fingerprint] form required");
 				return false;
 			}
-			if (php_openssl_x509_fingerprint_cmp(peer, ZSTR_VAL(key), Z_STRVAL_P(current)) != 0) {
+			if (!php_openssl_x509_fingerprint_is_equal(stream, peer, ZSTR_VAL(key), Z_STR_P(current))) {
+				php_stream_warn(stream, AuthFailed, "peer_fingerprint match failure");
 				return false;
 			}
 		} ZEND_HASH_FOREACH_END();

 		return true;
 	} else {
-		php_error_docref(NULL, E_WARNING,
+		// TODO: Should this be a TypeError?
+		php_stream_warn(stream, Generic,
 			"Invalid peer_fingerprint value; fingerprint string or array of the form [algo => fingerprint] required");
 	}

@@ -555,7 +564,7 @@ static bool php_openssl_matches_san_list(X509 *peer, const char *subject_name) /
 }
 /* }}} */

-static bool php_openssl_matches_common_name(X509 *peer, const char *subject_name) /* {{{ */
+static bool php_openssl_matches_common_name(php_stream *stream, const X509 *peer, const char *subject_name) /* {{{ */
 {
 	char buf[1024];
 	X509_NAME *cert_name;
@@ -566,13 +575,13 @@ static bool php_openssl_matches_common_name(X509 *peer, const char *subject_name
 	cert_name_len = X509_NAME_get_text_by_NID(cert_name, NID_commonName, buf, sizeof(buf));

 	if (cert_name_len == -1) {
-		php_error_docref(NULL, E_WARNING, "Unable to locate peer certificate CN");
+		php_stream_warn(stream, NetworkRecvFailed, "Unable to locate peer certificate CN");
 	} else if ((size_t)cert_name_len != strlen(buf)) {
-		php_error_docref(NULL, E_WARNING, "Peer certificate CN=`%.*s' is malformed", cert_name_len, buf);
+		php_stream_warn(stream, AuthFailed, "Peer certificate CN=`%.*s' is malformed", cert_name_len, buf);
 	} else if (php_openssl_matches_wildcard_name(subject_name, buf)) {
 		is_match = true;
 	} else {
-		php_error_docref(NULL, E_WARNING,
+		php_stream_warn(stream, AuthFailed,
 			"Peer certificate CN=`%.*s' did not match expected CN=`%s'",
 			cert_name_len, buf, subject_name);
 	}
@@ -605,7 +614,7 @@ static zend_result php_openssl_apply_peer_verification_policy(SSL *ssl, X509 *pe
 	peer_fingerprint = val;

 	if ((must_verify_peer || must_verify_peer_name || must_verify_fingerprint) && peer == NULL) {
-		php_error_docref(NULL, E_WARNING, "Could not get peer certificate");
+		php_stream_warn(stream, NetworkRecvFailed, "Could not get peer certificate");
 		return FAILURE;
 	}

@@ -624,8 +633,7 @@ static zend_result php_openssl_apply_peer_verification_policy(SSL *ssl, X509 *pe
 				/* not allowed, so fall through */
 				ZEND_FALLTHROUGH;
 			default:
-				php_error_docref(NULL, E_WARNING,
-						"Could not verify peer: code:%d %s",
+				php_stream_warn(stream, AuthFailed, "Could not verify peer: code:%d %s",
 						err,
 						X509_verify_cert_error_string(err)
 				);
@@ -636,14 +644,11 @@ static zend_result php_openssl_apply_peer_verification_policy(SSL *ssl, X509 *pe
 	/* If a peer_fingerprint match is required this trumps peer and peer_name verification */
 	if (must_verify_fingerprint) {
 		if (Z_TYPE_P(peer_fingerprint) == IS_STRING || Z_TYPE_P(peer_fingerprint) == IS_ARRAY) {
-			if (!php_openssl_x509_fingerprint_match(peer, peer_fingerprint)) {
-				php_error_docref(NULL, E_WARNING,
-					"peer_fingerprint match failure"
-				);
+			if (!php_openssl_x509_fingerprint_match(stream, peer, peer_fingerprint)) {
 				return FAILURE;
 			}
 		} else {
-			php_error_docref(NULL, E_WARNING,
+			php_stream_warn(stream, AuthFailed,
 				"Expected peer fingerprint must be a string or an array"
 			);
 			return FAILURE;
@@ -662,7 +667,7 @@ static zend_result php_openssl_apply_peer_verification_policy(SSL *ssl, X509 *pe
 		if (peer_name) {
 			if (php_openssl_matches_san_list(peer, peer_name)) {
 				return SUCCESS;
-			} else if (php_openssl_matches_common_name(peer, peer_name)) {
+			} else if (php_openssl_matches_common_name(stream, peer, peer_name)) {
 				return SUCCESS;
 			} else {
 				return FAILURE;
@@ -725,7 +730,8 @@ static int php_openssl_win_cert_verify_callback(X509_STORE_CTX *x509_store_ctx,
 				err_code = e;
 			}

-			php_error_docref(NULL, E_WARNING, "Error encoding X509 certificate: %lu: %s", err_code, ERR_error_string(err_code, err_buf));
+			php_stream_warn(stream, EncodingFailed,
+				"Error encoding X509 certificate: %lu: %s", err_code, ERR_error_string(err_code, err_buf));
 			RETURN_CERT_VERIFY_FAILURE(SSL_R_CERTIFICATE_VERIFY_FAILED);
 		}

@@ -734,7 +740,7 @@ static int php_openssl_win_cert_verify_callback(X509_STORE_CTX *x509_store_ctx,

 		if (cert_ctx == NULL) {
 			char *err = php_win_err();
-			php_error_docref(NULL, E_WARNING, "Error creating certificate context: %s", err);
+			php_stream_warn(stream, CreateFailed, "Error creating certificate context: %s", err);
 			php_win_err_free(err);
 			RETURN_CERT_VERIFY_FAILURE(SSL_R_CERTIFICATE_VERIFY_FAILED);
 		}
@@ -758,7 +764,7 @@ static int php_openssl_win_cert_verify_callback(X509_STORE_CTX *x509_store_ctx,

 		if (!CertGetCertificateChain(NULL, cert_ctx, NULL, NULL, &chain_params, chain_flags, NULL, &cert_chain_ctx)) {
 			char *err = php_win_err();
-			php_error_docref(NULL, E_WARNING, "Error getting certificate chain: %s", err);
+			php_stream_warn(stream, NetworkRecvFailed, "Error getting certificate chain: %s", err);
 			php_win_err_free(err);
 			CertFreeCertificateContext(cert_ctx);
 			RETURN_CERT_VERIFY_FAILURE(SSL_R_CERTIFICATE_VERIFY_FAILED);
@@ -801,7 +807,7 @@ static int php_openssl_win_cert_verify_callback(X509_STORE_CTX *x509_store_ctx,

 		if (!verify_result) {
 			char *err = php_win_err();
-			php_error_docref(NULL, E_WARNING, "Error verifying certificate chain policy: %s", err);
+			php_stream_warn(stream, AuthFailed, "Error verifying certificate chain policy: %s", err);
 			php_win_err_free(err);
 			RETURN_CERT_VERIFY_FAILURE(SSL_R_CERTIFICATE_VERIFY_FAILED);
 		}
@@ -836,11 +842,12 @@ static long php_openssl_load_stream_cafile(X509_STORE *cert_store, const char *c
 	stream = php_stream_open_wrapper(cafile, "rb", 0, NULL);

 	if (stream == NULL) {
+		// TODO: no stream and no wrapper, cannot use php_stream_warn(stream, ReadFailed, ...) nor php_stream_wrapper_log_error()
 		php_error(E_WARNING, "failed loading cafile stream: `%s'", cafile);
 		return 0;
 	} else if (stream->wrapper->is_url) {
+		php_stream_warn(stream, PermissionDenied, "remote cafile streams are disabled for security purposes");
 		php_stream_close(stream);
-		php_error(E_WARNING, "remote cafile streams are disabled for security purposes");
 		return 0;
 	}

@@ -898,7 +905,7 @@ static long php_openssl_load_stream_cafile(X509_STORE *cert_store, const char *c
 	}

 	if (certs_added == 0) {
-		php_error(E_WARNING, "no valid certs found cafile stream: `%s'", cafile);
+		php_stream_warn(stream, DecodingFailed, "no valid certs found cafile stream: `%s'", cafile);
 	}

 	return certs_added;
@@ -924,7 +931,7 @@ static zend_result php_openssl_enable_peer_verification(SSL_CTX *ctx, php_stream
 		if (cert_names != NULL) {
 			SSL_CTX_set_client_CA_list(ctx, cert_names);
 		} else {
-			php_error(E_WARNING, "SSL: failed loading CA names from cafile");
+			php_stream_warn(stream, InvalidFormat, "SSL: failed loading CA names from cafile");
 			return FAILURE;
 		}
 	}
@@ -946,7 +953,7 @@ static zend_result php_openssl_enable_peer_verification(SSL_CTX *ctx, php_stream
 		SSL_CTX_set_cert_verify_callback(ctx, php_openssl_win_cert_verify_callback, (void *)stream);
 #else
 		if (sslsock->is_client && !SSL_CTX_set_default_verify_paths(ctx)) {
-			php_error_docref(NULL, E_WARNING,
+			php_stream_warn(stream, ProtocolError,
 				"Unable to set default verify locations and no CA settings specified");
 			return FAILURE;
 		}
@@ -980,13 +987,13 @@ static zend_result php_openssl_set_local_cert(SSL_CTX *ctx, php_stream *stream)

 		if (!php_openssl_check_path_ex(
 				certfile, certfile_len, resolved_path_buff, 0, false, false,
-				"local_cert in ssl stream context")) {
-			php_error_docref(NULL, E_WARNING, "Unable to get real path of certificate file `%s'", certfile);
+				"local_cert in ssl stream context", stream)) {
+			php_stream_warn(stream, NotFound, "Unable to get real path of certificate file `%s'", certfile);
 			return FAILURE;
 		}
 		/* a certificate to use for authentication */
 		if (SSL_CTX_use_certificate_chain_file(ctx, resolved_path_buff) != 1) {
-			php_error_docref(NULL, E_WARNING,
+			php_stream_warn(stream, WriteFailed,
 				"Unable to set local cert chain file `%s'; Check that your cafile/capath "
 				"settings include details of your certificate and its issuer",
 				certfile);
@@ -996,16 +1003,16 @@ static zend_result php_openssl_set_local_cert(SSL_CTX *ctx, php_stream *stream)
 		GET_VER_OPT_STRINGL("local_pk", private_key, private_key_len);
 		if (private_key && !php_openssl_check_path_ex(
 				private_key, private_key_len, resolved_path_buff, 0, false, false,
-				"local_pk in ssl stream context")) {
-			php_error_docref(NULL, E_WARNING, "Unable to get real path of private key file `%s'", private_key);
+				"local_pk in ssl stream context", stream)) {
+			php_stream_warn(stream, NotFound, "Unable to get real path of private key file `%s'", private_key);
 			return FAILURE;
 		}
 		if (SSL_CTX_use_PrivateKey_file(ctx, resolved_path_buff, SSL_FILETYPE_PEM) != 1) {
-			php_error_docref(NULL, E_WARNING, "Unable to set private key file `%s'", resolved_path_buff);
+			php_stream_warn(stream, WriteFailed, "Unable to set private key file `%s'", resolved_path_buff);
 			return FAILURE;
 		}
 		if (!SSL_CTX_check_private_key(ctx)) {
-			php_error_docref(NULL, E_WARNING, "Private key does not match certificate!");
+			php_stream_warn(stream, PermissionDenied, "Private key does not match certificate!");
 		}
 	}

@@ -1140,7 +1147,7 @@ static void php_openssl_limit_handshake_reneg(const SSL *ssl) /* {{{ */
 			/* Closing the stream inside this callback would segfault! */
 			stream->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
 			if (FAILURE == call_user_function(NULL, NULL, val, &retval, 1, &param)) {
-				php_error(E_WARNING, "SSL: failed invoking reneg limit notification callback");
+				php_stream_warn(stream, UserspaceCallFailed, "SSL: failed invoking reneg limit notification callback");
 			}
 			stream->flags ^= PHP_STREAM_FLAG_NO_FCLOSE;

@@ -1151,7 +1158,7 @@ static void php_openssl_limit_handshake_reneg(const SSL *ssl) /* {{{ */

 			zval_ptr_dtor(&retval);
 		} else {
-			php_error_docref(NULL, E_WARNING,
+			php_stream_warn(stream, ProtocolError,
 				"SSL: client-initiated handshake rate limit exceeded by peer");
 		}
 	}
@@ -1224,7 +1231,7 @@ static zend_result php_openssl_set_server_dh_param(php_stream * stream, SSL_CTX
 	BIO *bio = BIO_new_file(Z_STRVAL_P(zdhpath), PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));

 	if (bio == NULL) {
-		php_error_docref(NULL, E_WARNING, "Invalid dh_param");
+		php_stream_warn(stream, NotFound, "Invalid dh_param");
 		return FAILURE;
 	}

@@ -1233,12 +1240,12 @@ static zend_result php_openssl_set_server_dh_param(php_stream * stream, SSL_CTX
 	BIO_free(bio);

 	if (pkey == NULL) {
-		php_error_docref(NULL, E_WARNING, "Failed reading DH params");
+		php_stream_warn(stream, ReadFailed, "Failed reading DH params");
 		return FAILURE;
 	}

 	if (SSL_CTX_set0_tmp_dh_pkey(ctx, pkey) == 0) {
-		php_error_docref(NULL, E_WARNING, "Failed assigning DH params");
+		php_stream_warn(stream, WriteFailed, "Failed assigning DH params");
 		EVP_PKEY_free(pkey);
 		return FAILURE;
 	}
@@ -1247,12 +1254,12 @@ static zend_result php_openssl_set_server_dh_param(php_stream * stream, SSL_CTX
 	BIO_free(bio);

 	if (dh == NULL) {
-		php_error_docref(NULL, E_WARNING, "Failed reading DH params");
+		php_stream_warn(stream, ReadFailed, "Failed reading DH params");
 		return FAILURE;
 	}

 	if (SSL_CTX_set_tmp_dh(ctx, dh) == 0) {
-		php_error_docref(NULL, E_WARNING, "Failed assigning DH params");
+		php_stream_warn(stream, WriteFailed, "Failed assigning DH params");
 		DH_free(dh);
 		return FAILURE;
 	}
@@ -1271,7 +1278,8 @@ static zend_result php_openssl_set_server_specific_opts(php_stream *stream, SSL_

 	/* We now use php_openssl_tmp_rsa_cb to generate a key of appropriate size whenever necessary */
 	if (php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "rsa_key_size") != NULL) {
-		php_error_docref(NULL, E_WARNING, "rsa_key_size context option has been removed");
+		// TODO: Should this be E_DEPRECATED instead?
+		php_stream_warn(stream, Generic, "rsa_key_size context option has been removed");
 	}

 	if (php_openssl_set_server_dh_param(stream, ctx) == FAILURE) {
@@ -1326,18 +1334,18 @@ static int php_openssl_server_sni_callback(SSL *ssl_handle, int *al, void *arg)
 }
 /* }}} */

-static SSL_CTX *php_openssl_create_sni_server_ctx(char *cert_path, char *key_path)  /* {{{ */
+static SSL_CTX *php_openssl_create_sni_server_ctx(php_stream *stream, char *cert_path, char *key_path)  /* {{{ */
 {
 	/* The hello method is not inherited by SSL structs when assigning a new context
 	 * inside the SNI callback, so the just use SSLv23 */
 	SSL_CTX *ctx = SSL_CTX_new(SSLv23_server_method());
 	if (!ctx) {
-		php_error_docref(NULL, E_WARNING, "Failed to create the SSL context");
+		php_stream_warn(stream, CreateFailed, "Failed to create the SSL context");
 		return NULL;
 	}

 	if (SSL_CTX_use_certificate_chain_file(ctx, cert_path) != 1) {
-		php_error_docref(NULL, E_WARNING,
+		php_stream_warn(stream, ReadFailed,
 			"Failed setting local cert chain file `%s'; " \
 			"check that your cafile/capath settings include " \
 			"details of your certificate and its issuer",
@@ -1346,7 +1354,7 @@ static SSL_CTX *php_openssl_create_sni_server_ctx(char *cert_path, char *key_pat
 		SSL_CTX_free(ctx);
 		return NULL;
 	} else if (SSL_CTX_use_PrivateKey_file(ctx, key_path, SSL_FILETYPE_PEM) != 1) {
-		php_error_docref(NULL, E_WARNING,
+		php_stream_warn(stream, ReadFailed,
 			"Failed setting private key from file `%s'",
 			key_path
 		);
@@ -1380,7 +1388,8 @@ static zend_result php_openssl_enable_server_sni(
 	}

 	if (Z_TYPE_P(val) != IS_ARRAY) {
-		php_error_docref(NULL, E_WARNING,
+		// TODO: should this be a TypeError?
+		php_stream_warn(stream, Generic,
 			"SNI_server_certs requires an array mapping host names to cert paths"
 		);
 		return FAILURE;
@@ -1388,7 +1397,8 @@ static zend_result php_openssl_enable_server_sni(

 	sslsock->sni_cert_count = zend_hash_num_elements(Z_ARRVAL_P(val));
 	if (sslsock->sni_cert_count == 0) {
-		php_error_docref(NULL, E_WARNING,
+		// TODO: should this be a ValueError?
+		php_stream_warn(stream, Generic,
 			"SNI_server_certs host cert array must not be empty"
 		);
 		return FAILURE;
@@ -1403,7 +1413,8 @@ static zend_result php_openssl_enable_server_sni(
 		(void) key_index;

 		if (!key) {
-			php_error_docref(NULL, E_WARNING,
+			// TODO: should this be a ValueError?
+			php_stream_warn(stream, Generic,
 				"SNI_server_certs array requires string host name keys"
 			);
 			return FAILURE;
@@ -1418,7 +1429,8 @@ static zend_result php_openssl_enable_server_sni(

 			local_cert = zend_hash_str_find(Z_ARRVAL_P(current), "local_cert", sizeof("local_cert")-1);
 			if (local_cert == NULL) {
-				php_error_docref(NULL, E_WARNING,
+				// TODO: should this be a ValueError?
+				php_stream_warn(stream, Generic,
 					"local_cert not present in the array"
 				);
 				return FAILURE;
@@ -1430,8 +1442,8 @@ static zend_result php_openssl_enable_server_sni(
 			}
 			if (!php_openssl_check_path_str_ex(
 					local_cert_str, resolved_cert_path_buff, 0, false, false,
-					"SNI_server_certs local_cert in ssl stream context")) {
-				php_error_docref(NULL, E_WARNING,
+					"SNI_server_certs local_cert in ssl stream context", stream)) {
+				php_stream_warn(stream, OpenFailed,
 					"Failed setting local cert chain file `%s'; could not open file",
 					ZSTR_VAL(local_cert_str)
 				);
@@ -1442,7 +1454,8 @@ static zend_result php_openssl_enable_server_sni(

 			local_pk = zend_hash_str_find(Z_ARRVAL_P(current), "local_pk", sizeof("local_pk")-1);
 			if (local_pk == NULL) {
-				php_error_docref(NULL, E_WARNING,
+				// TODO: should this be a ValueError?
+				php_stream_warn(stream, Generic,
 					"local_pk not present in the array"
 				);
 				return FAILURE;
@@ -1454,8 +1467,8 @@ static zend_result php_openssl_enable_server_sni(
 			}
 			if (!php_openssl_check_path_str_ex(
 					local_pk_str, resolved_pk_path_buff, 0, false, false,
-					"SNI_server_certs local_pk in ssl stream context")) {
-				php_error_docref(NULL, E_WARNING,
+					"SNI_server_certs local_pk in ssl stream context", stream)) {
+				php_stream_warn(stream, OpenFailed,
 					"Failed setting local private key file `%s';  could not open file",
 					ZSTR_VAL(local_pk_str)
 				);
@@ -1464,18 +1477,19 @@ static zend_result php_openssl_enable_server_sni(
 			}
 			zend_string_release(local_pk_str);

-			ctx = php_openssl_create_sni_server_ctx(resolved_cert_path_buff, resolved_pk_path_buff);
+			ctx = php_openssl_create_sni_server_ctx(stream, resolved_cert_path_buff, resolved_pk_path_buff);
 		} else if (Z_TYPE_P(current) == IS_STRING) {
-			if (php_openssl_check_path_str_ex(Z_STR_P(current), resolved_path_buff, 0, false, false, "SNI_server_certs in ssl stream context")) {
-				ctx = php_openssl_create_sni_server_ctx(resolved_path_buff, resolved_path_buff);
+			if (php_openssl_check_path_str_ex(Z_STR_P(current), resolved_path_buff, 0, false, false, "SNI_server_certs in ssl stream context", stream)) {
+				ctx = php_openssl_create_sni_server_ctx(stream, resolved_path_buff, resolved_path_buff);
 			} else {
-				php_error_docref(NULL, E_WARNING,
+				php_stream_warn(stream, NotFound,
 						"Failed setting local cert chain file `%s'; file not found",
 						Z_STRVAL_P(current)
 				);
 			}
 		} else {
-			php_error_docref(NULL, E_WARNING, "SNI_server_certs options values must be of type array|string");
+			// TODO: should this be a TypeError?
+			php_stream_warn(stream, Generic, "SNI_server_certs options values must be of type array|string");
 		}

 		if (ctx == NULL) {
@@ -1902,12 +1916,13 @@ static int php_openssl_psk_find_session_cb(SSL *ssl, const unsigned char *identi
 /* PSK setup */

 static zend_result php_openssl_validate_and_allocate_psk_callback(
+		php_stream *stream,
 		php_openssl_netstream_data_t *sslsock, const zval *callable,
 		bool is_client, bool is_persistent)
 {
 	const char *callback_name = is_client ? "psk_client_cb" : "psk_server_cb";
 	if (is_persistent) {
-		php_error_docref(NULL, E_WARNING,
+		php_stream_warn(stream, PersistentNotSupported,
 				"%s is not supported for persistent streams", callback_name);
 		return FAILURE;
 	}
@@ -1950,7 +1965,7 @@ static zend_result php_openssl_setup_client_psk(php_stream *stream,
 	}

 	if (FAILURE == php_openssl_validate_and_allocate_psk_callback(
-			sslsock, val, true, php_stream_is_persistent(stream))) {
+			stream, sslsock, val, true, php_stream_is_persistent(stream))) {
 		return FAILURE;
 	}

@@ -1972,7 +1987,7 @@ static zend_result php_openssl_setup_server_psk(php_stream *stream,
 	}

 	if (FAILURE == php_openssl_validate_and_allocate_psk_callback(
-			sslsock, val, false, php_stream_is_persistent(stream))) {
+			stream, sslsock, val, false, php_stream_is_persistent(stream))) {
 		return FAILURE;
 	}

@@ -2103,6 +2118,7 @@ enum php_openssl_session_callback_type {
  * Validate callable and allocate callback structure if needed.
  */
 static zend_result php_openssl_validate_and_allocate_session_callback(
+		php_stream *stream,
 		php_openssl_netstream_data_t *sslsock, const zval *callable,
 		enum php_openssl_session_callback_type cb_type, bool is_persistent)
 {
@@ -2123,7 +2139,7 @@ static zend_result php_openssl_validate_and_allocate_session_callback(

 	/* Callbacks not supported for persistent streams */
 	if (is_persistent) {
-		php_error_docref(NULL, E_WARNING,
+		php_stream_warn(stream, PersistentNotSupported,
 				"%s is not supported for persistent streams", callback_name);
 		return FAILURE;
 	}
@@ -2184,7 +2200,7 @@ static zend_result php_openssl_setup_client_session(php_stream *stream,

 	if (GET_VER_OPT("session_new_cb")) {
 		if (FAILURE == php_openssl_validate_and_allocate_session_callback(
-				sslsock, val, PHP_OPENSSL_NEW_CB, is_persistent)) {
+				stream, sslsock, val, PHP_OPENSSL_NEW_CB, is_persistent)) {
 			return FAILURE;
 		}

@@ -2231,7 +2247,7 @@ static zend_result php_openssl_setup_server_session(php_stream *stream,
 	/* Check for session_get_cb first (determines cache mode) */
 	if (GET_VER_OPT("session_get_cb")) {
 		if (FAILURE == php_openssl_validate_and_allocate_session_callback(
-				sslsock, val, PHP_OPENSSL_GET_CB, is_persistent)) {
+				stream, sslsock, val, PHP_OPENSSL_GET_CB, is_persistent)) {
 			return FAILURE;
 		}
 		has_get_cb = true;
@@ -2250,7 +2266,7 @@ static zend_result php_openssl_setup_server_session(php_stream *stream,
 	/* Check for session_new_cb */
 	if (GET_VER_OPT("session_new_cb")) {
 		if (FAILURE == php_openssl_validate_and_allocate_session_callback(
-				sslsock, val, PHP_OPENSSL_NEW_CB, is_persistent)) {
+				stream, sslsock, val, PHP_OPENSSL_NEW_CB, is_persistent)) {
 			return FAILURE;
 		}
 		has_new_cb = true;
@@ -2271,7 +2287,7 @@ static zend_result php_openssl_setup_server_session(php_stream *stream,
 	/* Check for session_remove_cb (optional) */
 	if (GET_VER_OPT("session_remove_cb")) {
 		if (FAILURE == php_openssl_validate_and_allocate_session_callback(
-				sslsock, val, PHP_OPENSSL_REMOVE_CB, is_persistent)) {
+				stream, sslsock, val, PHP_OPENSSL_REMOVE_CB, is_persistent)) {
 			return FAILURE;
 		}

@@ -2356,7 +2372,8 @@ static zend_result php_openssl_apply_client_session_data(php_stream *stream,
 		if (php_openssl_is_session_ce(val)) {
 			session = php_openssl_session_from_zval(val);
 			if (!session) {
-				php_error_docref(NULL, E_WARNING,
+				// TODO: Should this be a TypeError?
+				php_stream_warn(stream, Generic,
 						"Invalid OpenSSLSession object, falling back to full handshake");
 				return FAILURE;
 			}
@@ -2369,7 +2386,7 @@ static zend_result php_openssl_apply_client_session_data(php_stream *stream,

 		if (session) {
 			if (SSL_set_session(sslsock->ssl_handle, session) != 1) {
-				php_error_docref(NULL, E_WARNING,
+				php_stream_warn(stream, ResumptionFailed,
 						"Failed to set session for resumption, falling back to full handshake");
 				if (needs_free) {
 					SSL_SESSION_free(session);
@@ -2396,7 +2413,7 @@ static zend_result php_openssl_create_server_ctx(php_stream *stream,
 	sslsock->ctx = SSL_CTX_new(method);

 	if (sslsock->ctx == NULL) {
-		php_error_docref(NULL, E_WARNING, "SSL context creation failure");
+		php_stream_warn(stream, CreateFailed, "SSL context creation failure");
 		return FAILURE;
 	}

@@ -2462,7 +2479,8 @@ static zend_result php_openssl_create_server_ctx(php_stream *stream,
 	if (GET_VER_OPT("security_level")) {
 		zend_long lval = zval_get_long(val);
 		if (lval < 0 || lval > 5) {
-			php_error_docref(NULL, E_WARNING, "Security level must be between 0 and 5");
+			// TODO: Should this be a ValueError?
+			php_stream_warn(stream, Generic, "Security level must be between 0 and 5");
 		}
 #ifdef HAVE_SEC_LEVEL
 		SSL_CTX_set_security_level(sslsock->ctx, lval);
@@ -2478,7 +2496,7 @@ static zend_result php_openssl_create_server_ctx(php_stream *stream,
 			unsigned char *alpn = php_openssl_alpn_protos_parse(&alpn_len, alpn_protocols);

 			if (alpn == NULL) {
-				php_error_docref(NULL, E_WARNING, "Failed parsing comma-separated TLS ALPN protocol string");
+				php_stream_warn(stream, DecodingFailed, "Failed parsing comma-separated TLS ALPN protocol string");
 				SSL_CTX_free(sslsock->ctx);
 				sslsock->ctx = NULL;
 				return FAILURE;
@@ -2494,7 +2512,7 @@ static zend_result php_openssl_create_server_ctx(php_stream *stream,
 			efree(alpn);
 		}
 #else
-		php_error_docref(NULL, E_WARNING,
+		php_stream_warn(stream, ProtocolUnsupported,
 			"alpn_protocols support is not compiled into the OpenSSL library against which PHP is linked");
 #endif
 	}
@@ -2544,7 +2562,7 @@ static zend_result php_openssl_setup_crypto(php_stream *stream,
 {
 	if (sslsock->ssl_handle) {
 		if (sslsock->s.is_blocked) {
-			php_error_docref(NULL, E_WARNING, "SSL/TLS already set-up for this stream");
+			php_stream_warn(stream, Generic, "SSL/TLS already set-up for this stream");
 			return FAILURE;
 		} else {
 			return SUCCESS;
@@ -2563,11 +2581,11 @@ static zend_result php_openssl_setup_crypto(php_stream *stream,
 		php_openssl_netstream_data_t *parent_sslsock;

 		if (cparam->inputs.session->ops != &php_openssl_socket_ops) {
-			php_error_docref(NULL, E_WARNING, "Supplied session stream must be an SSL enabled stream");
+			php_stream_warn(stream, SslNotSupported, "Supplied session stream must be an SSL enabled stream");
 		} else if ((parent_sslsock = cparam->inputs.session->abstract)->ctx == NULL) {
-			php_error_docref(NULL, E_WARNING, "Supplied SSL session stream is not set up");
+			php_stream_warn(stream, SslNotSupported, "Supplied SSL session stream is not set up");
 		} else if (sslsock->is_client && parent_sslsock->ssl_handle == NULL) {
-			php_error_docref(NULL, E_WARNING, "Supplied SSL session stream is not initialized");
+			php_stream_warn(stream, SslNotSupported, "Supplied SSL session stream is not initialized");
 		} else {
 			SSL_CTX_up_ref(parent_sslsock->ctx);
 			sslsock->ctx = parent_sslsock->ctx;
@@ -2582,7 +2600,7 @@ static zend_result php_openssl_setup_crypto(php_stream *stream,

 			sslsock->ssl_handle = SSL_new(sslsock->ctx);
 			if (!sslsock->ssl_handle) {
-				php_error_docref(NULL, E_WARNING, "SSL handle creation failure");
+				php_stream_warn(stream, CreateFailed, "SSL handle creation failure");
 				SSL_CTX_free(sslsock->ctx);
 				sslsock->ctx = NULL;
 				return FAILURE;
@@ -2598,7 +2616,7 @@ static zend_result php_openssl_setup_crypto(php_stream *stream,
 				if (SSL_copy_session_id(sslsock->ssl_handle, parent_sslsock->ssl_handle)) {
 					SSL_CTX_set_session_cache_mode(sslsock->ctx, SSL_SESS_CACHE_CLIENT);
 				} else {
-					php_error_docref(NULL, E_WARNING, "SSL session copying failed creation failure");
+					php_stream_warn(stream, CreateFailed, "SSL session copying failed creation failure");
 				}
 			}

@@ -2614,7 +2632,7 @@ static zend_result php_openssl_setup_crypto(php_stream *stream,

 	if (sslsock->ssl_handle == NULL
 		|| !SSL_set_ex_data(sslsock->ssl_handle, php_openssl_get_ssl_stream_data_index(), stream)) {
-		php_error_docref(NULL, E_WARNING, "SSL handle creation failure");
+		php_stream_warn(stream, CreateFailed, "SSL handle creation failure");
 		SSL_CTX_free(sslsock->ctx);
 		sslsock->ctx = NULL;
 #ifdef HAVE_TLS_ALPN
@@ -2764,7 +2782,7 @@ static int php_openssl_enable_crypto(php_stream *stream,
 				elapsed_time = php_openssl_subtract_timeval(cur_time, start_time);

 				if (php_openssl_compare_timeval( elapsed_time, *timeout) > 0) {
-					php_error_docref(NULL, E_WARNING, "SSL: Handshake timed out");
+					php_stream_warn(stream, TimeOut, "SSL: Handshake timed out");
 					return -1;
 				}
 			}
@@ -3220,7 +3238,7 @@ static inline int php_openssl_tcp_sockop_accept(php_stream *stream, php_openssl_
 			if (php_stream_xport_crypto_setup(xparam->outputs.client, clisockdata->method,
 					sock->ctx ? stream : NULL) < 0 || php_stream_xport_crypto_enable(
 					xparam->outputs.client, 1) < 0) {
-				php_error_docref(NULL, E_WARNING, "Failed to enable crypto");
+				php_stream_warn(stream, ProtocolError, "Failed to enable crypto");

 				php_stream_close(xparam->outputs.client);
 				xparam->outputs.client = NULL;
@@ -3490,7 +3508,7 @@ static int php_openssl_sockop_set_option(php_stream *stream, int option, int val

 						if (php_stream_xport_crypto_setup(stream, sslsock->method, session_stream) < 0 ||
 								php_stream_xport_crypto_enable(stream, 1) < 0) {
-							php_error_docref(NULL, E_WARNING, "Failed to enable crypto");
+							php_stream_warn(stream, ProtocolError, "Failed to enable crypto");
 							xparam->outputs.returncode = -1;
 						}
 					}
@@ -3686,7 +3704,7 @@ php_stream *php_openssl_ssl_socket_factory(const char *proto, size_t protolen,
 		sslsock->enable_on_connect = 1;
 		sslsock->method = php_openssl_get_crypto_method(context, STREAM_CRYPTO_METHOD_TLS_ANY_CLIENT);
 	} else if (strncmp(proto, "sslv2", protolen) == 0) {
-		php_error_docref(NULL, E_WARNING, "SSLv2 unavailable in this PHP version");
+		php_stream_warn(stream, ProtocolUnsupported, "SSLv2 unavailable in this PHP version");
 		php_stream_close(stream);
 		return NULL;
 	} else if (strncmp(proto, "sslv3", protolen) == 0) {
@@ -3694,7 +3712,7 @@ php_stream *php_openssl_ssl_socket_factory(const char *proto, size_t protolen,
 		sslsock->enable_on_connect = 1;
 		sslsock->method = STREAM_CRYPTO_METHOD_SSLv3_CLIENT;
 #else
-		php_error_docref(NULL, E_WARNING,
+		php_stream_warn(stream, ProtocolUnsupported,
 			"SSLv3 support is not compiled into the OpenSSL library against which PHP is linked");
 		php_stream_close(stream);
 		return NULL;
@@ -3710,7 +3728,7 @@ php_stream *php_openssl_ssl_socket_factory(const char *proto, size_t protolen,
 		sslsock->enable_on_connect = 1;
 		sslsock->method = STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
 #else
-		php_error_docref(NULL, E_WARNING,
+		php_stream_warn(stream, ProtocolUnsupported,
 			"TLSv1.1 support is not compiled into the OpenSSL library against which PHP is linked");
 		php_stream_close(stream);
 		return NULL;
@@ -3720,7 +3738,7 @@ php_stream *php_openssl_ssl_socket_factory(const char *proto, size_t protolen,
 		sslsock->enable_on_connect = 1;
 		sslsock->method = STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
 #else
-		php_error_docref(NULL, E_WARNING,
+		php_stream_warn(stream, ProtocolUnsupported,
 			"TLSv1.2 support is not compiled into the OpenSSL library against which PHP is linked");
 		php_stream_close(stream);
 		return NULL;
@@ -3730,7 +3748,7 @@ php_stream *php_openssl_ssl_socket_factory(const char *proto, size_t protolen,
 		sslsock->enable_on_connect = 1;
 		sslsock->method = STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT;
 #else
-		php_error_docref(NULL, E_WARNING,
+		php_stream_warn(stream, ProtocolUnsupported,
 			"TLSv1.3 support is not compiled into the OpenSSL library against which PHP is linked");
 		php_stream_close(stream);
 		return NULL;
diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c
index bad645f8668..7962f0f85de 100644
--- a/ext/standard/streamsfuncs.c
+++ b/ext/standard/streamsfuncs.c
@@ -325,7 +325,7 @@ PHP_FUNCTION(stream_socket_accept)
 		if (peername) {
 			zend_string_release(peername);
 		}
-		php_error_docref(NULL, E_WARNING, "Accept failed: %s", errstr ? ZSTR_VAL(errstr) : "Unknown error");
+		php_stream_warn(stream, AcceptFailed, "Accept failed: %s", errstr ? ZSTR_VAL(errstr) : "Unknown error");
 		RETVAL_FALSE;
 	}

diff --git a/ext/standard/tests/streams/stream_errors_exception_mode_terminal.phpt b/ext/standard/tests/streams/stream_errors_exception_mode_terminal.phpt
index 37f5d39bfa7..6f470c61d1a 100644
--- a/ext/standard/tests/streams/stream_errors_exception_mode_terminal.phpt
+++ b/ext/standard/tests/streams/stream_errors_exception_mode_terminal.phpt
@@ -26,6 +26,6 @@
 ?>
 --EXPECTF--
 Caught: Failed to open stream: operation failed
-Code: 20
+Code: 21
 Wrapper: PHP
 Error code name: OpenFailed
diff --git a/main/streams/stream_errors.stub.php b/main/streams/stream_errors.stub.php
index 2c56932c485..255c92beb89 100644
--- a/main/streams/stream_errors.stub.php
+++ b/main/streams/stream_errors.stub.php
@@ -22,6 +22,7 @@ enum StreamErrorCode
     case ConnectFailed;
     case BindFailed;
     case ListenFailed;
+    case AcceptFailed;
     case NotWritable;
     case NotReadable;

@@ -86,6 +87,7 @@ enum StreamErrorCode
     case InvalidParam;
     case RedirectLimit;
     case AuthFailed;
+    case TimeOut;

     /* Encoding/decoding/archiving operations */
     case ArchivingFailed;
diff --git a/main/streams/stream_errors_arginfo.h b/main/streams/stream_errors_arginfo.h
index 3a10e9bc5a4..89df622beb0 100644
Binary files a/main/streams/stream_errors_arginfo.h and b/main/streams/stream_errors_arginfo.h differ
diff --git a/main/streams/stream_errors_decl.h b/main/streams/stream_errors_decl.h
index 4748768195f..69fe96252b4 100644
Binary files a/main/streams/stream_errors_decl.h and b/main/streams/stream_errors_decl.h differ