Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:129311 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by lists.php.net (Postfix) with ESMTPS id D27051A00BC for ; Wed, 19 Nov 2025 16:37:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1763570238; bh=t1mFj6FjCbT79OQA8LiuoRZjjQg9PB7W3/ASH5eyTTs=; h=Date:Subject:To:Cc:References:From:In-Reply-To:From; b=ED/63EhGz9sbyalVfJi6R2gZpIf8wWy+JKu0TJx4CQ093fCmFqvQEPZsuwt/n/jIb ApqZAJU1pgd7sPq5gbuB0FXjTCFC6FQW2cJfu4t8aseOzIeM8f5sA9ap+Dv261XpRQ 0eYuyhcCZdwL7OqbP4Mz+uZFJmyoZ/HUtjsPh8gIPv11tudOzOYcmcV9RlGT9iltJ/ vHysPL0trZOx3SxOimaAaRAVKREZROVOgE309eF05+9+ua0ONOlEdQvhMMEXktWLNr 0mRP0y4B0dJsoZb/L3ugk+J3NaC//j/KHbwpvT6SmX9cAGn4smiiJ4DY7XLCCEfz9q OJBn//SUHzB1g== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 5B55B1804CD for ; Wed, 19 Nov 2025 16:37:14 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on php-smtp4.php.net X-Spam-Level: ** X-Spam-Status: No, score=2.1 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_50, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS, FREEMAIL_ENVFROM_END_DIGIT,FREEMAIL_FROM,HTML_MESSAGE, NUMERIC_HTTP_ADDR,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_PASS, SPF_PASS autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: No X-Envelope-From: Received: from DU2PR03CU002.outbound.protection.outlook.com (mail-northeuropeazolkn19011077.outbound.protection.outlook.com [52.103.32.77]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (secp384r1) server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Wed, 19 Nov 2025 16:37:11 +0000 (UTC) ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=fzDNMgxju3RmPraoiq64tee1+FiJXuUcBkgG5zdXS6/12h1kEGKCre1O8bYHE9EfJgsdOJQrmNnbS43Dr5BVcrx+MgFV3MRuAcsliYqTc+Yn8uctS6trDglt+GHU8IXR1bkXA1kAyJnYk2b8WHb4cqR70zBA0esotC4Lnut6kucT9A6fxP96+qIa1LNwxvxV12EukxtOny+H9j7og/9Plrupu+Hp2+baoVNOve53ihWN7T1z83507fHxj1NSVyeOaYeANQKaAYSlNDAb1dTkT7tzKiPXG3jBrf+By1s5TbXvhTILzODW5GERsLFpuvq5EjOTkWkQa8MfLoBnLndQNA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=s/p7cZFgRGQDmNueNUgz+SdWlg8VoKS7SKCyfoWJsTk=; b=iVsjLREQK1wL9Xg+ZnXg4aL54TBRvgKheUZGiS3hy3H7bPqFAEUrs3Vrk0U2C/SJL5RzW6yHOXRvoXtg6g7G6cEZyYTSsbeZSTNO6gN9NCH9TQ/+HfT5pGPQl0nP7Ck6fWPui5QBkbUJs9UYDO/+rLWmHVkG0oL0PkzGOwF21TjbqqugHQ6QUhYyi+FUXilWjNaLEeLttqFu0Ouuj6thoX9/B4KdSHoPRkScJKBksEMLsMhDnlglrhSKBWtyu9Xh6V7cpv3eCfQbGJQYvnvGyz3bFA8sC8gsNcozDoSGPQ9h+GOzNuNZpG9xjS3m78QJN4ww4+5/t+S0xeE/zy9g9w== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=s/p7cZFgRGQDmNueNUgz+SdWlg8VoKS7SKCyfoWJsTk=; b=V/gpfuqLBIoao/Pvt65YZqL1svXqEAMFYPDYjzdpfe2mqpXo2pXSOzLJmTxLToCQjA1rB00PfioYAJmJcpSkJb8Tqv6e8MNo41XY0+pTShMc4Vy9kNNUi6avriW1XVqZ04RhMdEHXp53CA/Oxx4yUQeYHp//xZpRHFRJLrY0R8s6jOmYGKAts1u3aEqvGmuvD9VDnfl/yYybCrmlXfg1gR2STeWa0OzFkphzlQG2bO1PMuRhzNU3sVb/RwJPk+9Phxl2UgnXcqlqUblk6lkhD3jsNo0NFXZwf2+RynuC9RqGJavPZsAkts112w7wix3hygpVBOfd9HZPQb059dnjAA== Received: from AM8P250MB0170.EURP250.PROD.OUTLOOK.COM (2603:10a6:20b:321::21) by AS4P250MB0728.EURP250.PROD.OUTLOOK.COM (2603:10a6:20b:577::15) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9343.10; Wed, 19 Nov 2025 16:37:04 +0000 Received: from AM8P250MB0170.EURP250.PROD.OUTLOOK.COM ([fe80::651e:bbd2:b18a:80ff]) by AM8P250MB0170.EURP250.PROD.OUTLOOK.COM ([fe80::651e:bbd2:b18a:80ff%3]) with mapi id 15.20.9320.013; Wed, 19 Nov 2025 16:37:04 +0000 Content-Type: multipart/alternative; boundary="------------MfTn0W2yjVhalHPWN72Oamn1" Message-ID: Date: Wed, 19 Nov 2025 17:37:02 +0100 User-Agent: Mozilla Thunderbird Subject: Re: [PHP-DEV] [RFC] Stream Error Handling Improvements To: Jakub Zelenka Cc: PHP internals list References: Content-Language: en-US In-Reply-To: X-ClientProxiedBy: PAYP264CA0012.FRAP264.PROD.OUTLOOK.COM (2603:10a6:102:11e::17) To AM8P250MB0170.EURP250.PROD.OUTLOOK.COM (2603:10a6:20b:321::21) X-Microsoft-Original-Message-ID: <21c25612-3b1b-4858-a138-8959f4fc1779@hotmail.com> Precedence: list list-help: list-unsubscribe: list-post: List-Id: x-ms-reactions: disallow MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: AM8P250MB0170:EE_|AS4P250MB0728:EE_ X-MS-Office365-Filtering-Correlation-Id: 270ad2cb-1fc6-4686-f683-08de2789df00 X-MS-Exchange-SLBlob-MailProps: 9IecXKUgicCO8OP9JSHQ+xjWRIebUMjRL3Q8nFCSiMJ7fpzg9MpYe8spyoyPIVZKgW0AWpaGhFU/pdStKoHQbBujgY/1S1+HSw0PMmHsv7uB62BCejUSCbXROtkg1GsUiueRYqAkw8XMxmSF8bUbitrmYoY7LF7FDkvXM6wvwZ4O/aiIpXJ1gQdCwbzf6d91UPo863laNrsLQmtIRcp81i+D+L4b4IG96kkM8xgWNLtY1k1P44E9c++8Ech7eZfce6zeZ2FOEzEsQcK1cBSf9T42n88Yja0ewONcwkJi4U1XWBjDfAg9d2Jb7DhMUw4g5S7XGH9QemqAOXnyD1gpH/Bbz8pFvoa5hS9TIeW3nXAkfsUNsEybiY+3ho32/Xl7tjhmah0QQ910SadSrX9txh8GjH79VCETthAzO+9CDqYUdEEw0W3lXaXKBEub96R4kdBYbfMIP3/MW7M2b0XV+G3UhSpIyugzf400cMphjW8olyKwsv7lcKDcJD3evje/EDgN4mPbZ5dKsqhV7+RIpOSuPwKqvP7hH851MIpU7tPIWnuSpjPtDgiUrZsYPTwGgd1a/Z9J92+khAk6fJLCu4kWYN4AGRwxbf8ZxbsnqJ5bhmkavW/WmVsKxwlYY0bZYBuXTceo5NWHVI0KyiMNIC78HnqgLfoC3Mz0u2aJMekVpi4jibFqJ1hY54wdTWL6N+nRXZfG50MY4IInEMEfR2NNR2pY10e18Ut+N5J+OCTjJLc8uBC7Kw== X-Microsoft-Antispam: BCL:0;ARA:14566002|51005399006|12050799012|9400799040|41001999006|8060799015|19110799012|5072599009|461199028|15080799012|12121999013|23021999003|3412199025|440099028|40105399003|26104999006|56899033; X-Microsoft-Antispam-Message-Info: =?utf-8?B?NmdXMUhkRDRzQ3ZnYXFTRkxlRWNxOVAwKy9xdUhoVU95eVVoZmExelliNHdW?= =?utf-8?B?RjJQQzJaeEZ1KzdsTWtXNXBoUXJISStBTW5QaHNRQUw1V1Y2SEt5RjQ5NXVr?= =?utf-8?B?K0V4bFh3QUZmbG9Scm5wZ0RER1JnQjUvUHkrQm05SlhBRFUvSlhqK1ZDU0t5?= =?utf-8?B?ZTBVdy8zSE1MckdpNm4vVUZBZXB2aTRzbzlLb0c5ZHpiUUNQMW15cDdiRFBI?= =?utf-8?B?Sks0dnVzcGtXbllqeFJKenN3WEljb1dQU1ZkWEw2djBiN3Z2WmNOaHRnWmlX?= =?utf-8?B?SExtODBqbW56UzRVbCtBZlhYVW9aOEpRK3NEbXZUb2ZyQnQxaWR5cGZrQkNK?= =?utf-8?B?Rml2c3hPYWgyOTk5SVhINUdxbWFSdXV4U240ZmhlbTg1SFZIVGI0T1JkRmds?= =?utf-8?B?YitXbVNHLzFYY3FYSllrOEdIV1lFMjhHZlRyYitSL1ZjRU9ZQklwOXpqa2JE?= =?utf-8?B?OUhta1FmR21CTWZGOU1PcGJkaDZ3SU5lTWhDemt4eFh4MDF6MG1JTHJDRWxY?= =?utf-8?B?ZEh2TmltWlNXYWpDMHRrZnRWWWdZd3hFQUEwSHdSVU5FMmRiVWxieCtSZ0tR?= =?utf-8?B?N29RT1MyTlZYQTkvcCtiUzJqL1ZaWWE5ZklHT1ZRSEdQRFArM2xDd2tIUDFR?= =?utf-8?B?eFEzaXRpa1JMYUQxaEVBU2hZYjJGa1BJWEE4cU1USFp5QUdGcmYzQXV2a3hz?= =?utf-8?B?empRaU1UNVY2bjl4Sko2NGhOOXFZZVRGOW1Yb2JvR3FYZWFFYW9xaXJZVTEw?= =?utf-8?B?NTVOYWw1bURXT1N4QnVReGZnRGY1V0E2TVd2d2cyQzd4aVpCNFB4NU5JSURE?= =?utf-8?B?cjlXWnRUb0luMHNJcDUwelJZdXE0cnhob1l6SzZDKzg4UlBGSDNweG9WbGJN?= =?utf-8?B?YXM0OTRmT2NndkdtMHRVYkg5Z1BrNHUxOTFPUjQ3dVM1Y3lPWFNDYTNEMU9Q?= =?utf-8?B?RTN0THVYMkNMYkhCUmRWUjRYTGNCQ1FXWXEyTGFHclIzclBUSnh3cU90SGFK?= =?utf-8?B?U0NtQjRINHlaMlphWjVaY2xmODR2NStTckwvb1RRSjhRZ21qaWM0emxkSjlw?= =?utf-8?B?R1gxNUVCM1JNbzhMSmMxTGZkTEs3TTgvaEhZdU9hdWlTbml3bXRnUElGK0dR?= =?utf-8?B?Zzl4WE5NUzVTSUVuUmt1a2J6dnpEKzUweDRNdy9OYkRja3RzczVOTUwrM1du?= =?utf-8?B?OW5HcERrNHZsZVFDOGtTRlM1bHI3NlJyTGp2Z29kNHZ5TTB3Yk5FQWV4SFh1?= =?utf-8?B?cDZvTWFGV3Y1TWx1aDNaQ3hRSmlnMEQ0NElYNDRrRWM2bG5PN0gzVXRuNWUx?= =?utf-8?B?Y3FLRG9qZjg4ZVRncXZWbGRIMi9JVnBBY1diVGRxZDhualZCSnRrNDRMc1pV?= =?utf-8?B?K1dqRThIaHRqc1VQUHFDN21UNGJsU1JWZ2ZsMWRsT2VJTTNFM1hYMU42cWQ3?= =?utf-8?B?YkErVnlUV0VXRDAwUEx2WlhNQWFYbnNaK1c0VWpYNTNaQURXdWNzbXBEOEp2?= =?utf-8?B?TllZRm00SllVbE9GQVQ4QklyMGVmZnZBWGhlNWJVcU0zdVpnREJRL0J0RTdD?= =?utf-8?B?Zkxpd2NYVjRtMWtreExDWnFXRGNKbHpidGxpOTlzOGcxZ0FCWkZGd1VQZVN0?= =?utf-8?B?TW95MDBSYmVrckdXRFg4L0V6MDQ3THk1dWwvbVV2TUpMUVVkVlNMdUhMbmNI?= =?utf-8?B?dzgwWmtQc2cycGlnWmYxdmFWZ0l5SHBhMEUvRzhWcGtBczVXT0hjOEF4alV3?= =?utf-8?B?V0N5eGhyN3U3eGVqdnZSbjBxWkJNRkJyQ24zaWFCenlPWC85U1JQRXViWUlL?= =?utf-8?B?cjJKVWYzZ0t6Qms0RU9UZz09?= X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?RHRzSUNjWE8zNTV3STMxNmlKVzdRSEZlVHEvbzlNd3NsbTR6L0xYNmFXanFI?= =?utf-8?B?MThORnRNUUo2UC93V2htMjFCdUczc1R0R25UVldGZmQrbms1dVkxMk16RzRP?= =?utf-8?B?ZHFWOGNRd1VFL2VOSVQ0QWE1YmgvKzRnanhJWGFTV2h6a2NUY2ZTcDBYMUpD?= =?utf-8?B?TVQ0bWpkVGh6NW5aN1FVeWg3SHQ2a0Q0bXU4ZlJKYWZzekNLU2JZN0ttQVIz?= =?utf-8?B?NFk3YWpReFFUR1VlcEgzK2F1bTFkd2Z6NjRlZ0xFbExGakNVR0Mzbld6bndC?= =?utf-8?B?VzJpdVRaSHNrUGUvR3I2My9Kb3RFRkNMbnBTUmMvUThiWFZ3QlM0UzNFQ2VU?= =?utf-8?B?bm9hSnkvSldaN09paE9rZWdCNVUxc3luYSs2T2pSYXNUSUZpdVpXVGhwTDB2?= =?utf-8?B?S3Fab2ZtT2ZvZGlpcTgrejVNOHd4MG1DOVEwbDdybkRGL0cvaDVWSmZVeWZF?= =?utf-8?B?RHZ3RjRpUjgxeFhmYmx1Mjh2SnRncDN5L3RpZktacXAvbytjZldFcE9nckVm?= =?utf-8?B?azZSeTBCNDhrUUNPT29hUWpWSENudUx0V1NNYi9yU25JbHBTM0hVeHlSTGFl?= =?utf-8?B?T3lWZStyK0hLZWYxc3FRbndpc0w3cDdxd3R1cWYyV1JqeTNHWkVoZkgxb3B4?= =?utf-8?B?d1pRSkdBM2NNYTd1QkVYTTYrRG9PK0Jaa2dwbVExNHFoblNkWEw2RnV0SlZU?= =?utf-8?B?N29wWGxkbGJyL1AxVGxBd0tUcU5obUdlZG5vUmpmejFxMmtTdktLNSsrVDBB?= =?utf-8?B?MjhDTUxla3AzZTd1ak9WTlRFV1ErQmNUQzZtTlh4TXV5ZTZObElnUzljSUp6?= =?utf-8?B?WlVJeGhVZEVGRXhNMUhsT2Jnb3V4SndlMzFtcU0vdk9FWloxZHlqYko1RmdV?= =?utf-8?B?YzQ3OGJRYk9FUVJqRENPSUNiN25LTWJKeGJWTk92ekMxYzRUT1ZpeFRLVXdO?= =?utf-8?B?RE5PU3ZNd3JZOFFMbzFDVFRzeXVPUldBcU9KdHowcUFwV1JUUk9vaFJtNy9h?= =?utf-8?B?NHRsdlRkUVFpR1k5ZGlLR2E2bnR6VDZKMTRobzEyZDFBZWVhRHkyYW1lUFRJ?= =?utf-8?B?MWoxaDRsL2tyK3lUSzVHWkE1N2JjcDdjb0ExSHhUNzZXNnMxUlJKcmtLTlhp?= =?utf-8?B?ekpnT0RCYWNKTXBubEFObkRqcXM5MitLRk9LNGVxZ0tBdWRBajRYNDNZNlJV?= =?utf-8?B?a1BSS0VWQTF2UGl3cEJ1SytSSjRQbGhVci9uZnNJNlZ0OENtVnJEdFJxM3g3?= =?utf-8?B?a0ROMUc1MlZ0K3BiLzFOYWRpbFpmV04vdHFGejlyNEZkaXBhcVNtSXhTSmE5?= =?utf-8?B?cndYNUM5WUhWYmpGU1hVRW5ZTTVzYmtvM3h4YjBCM2lRVHprTDRnQmxLYkg4?= =?utf-8?B?TC90WElzTHJUTlYrV2dvQlcwUXZXditiMEhkQS9nejk0a3lpajc1MzY1SlJK?= =?utf-8?B?eTFDdU82MVNuTnkxaklvbndpMGVWRDIxSVpUUTVwcjRlWXpWRG9MbXBmcTVk?= =?utf-8?B?dUwwc29wcXFqVVRpamxDc3J1UEV5L05RZXNmRVEvbXNHRG81TnlDQlBSTUps?= =?utf-8?B?NzRXMURLRHBYUlFkK0piZ1Rhd0xwZkdva2I4M2pyZjlaUk9MV2ZYWWFVZ0R0?= =?utf-8?B?Wnc0LzdTcEpHYnFsU3BuNElCSTZqa2tvR1ArOFF6N1lyd202aGhnNFpSQ245?= =?utf-8?B?MURIaXJCcDVKZXNjb1VCZTVzY0FRQ2JEL21RZnFNUlFjRy9qaFJKKzZpK0Qr?= =?utf-8?Q?HpkziQeTEbmZ7WA74U=3D?= X-OriginatorOrg: sct-15-20-8534-15-msonline-outlook-5f066.templateTenant X-MS-Exchange-CrossTenant-Network-Message-Id: 270ad2cb-1fc6-4686-f683-08de2789df00 X-MS-Exchange-CrossTenant-AuthSource: AM8P250MB0170.EURP250.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 19 Nov 2025 16:37:04.3668 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: AS4P250MB0728 From: bobwei9@hotmail.com (Bob Weinand) --------------MfTn0W2yjVhalHPWN72Oamn1 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit Hey Jakub, thanks for the detailed reply. On 19.11.2025 12:52:35, Jakub Zelenka wrote: > The code one should be probably named StreamErrorCode (to not interact > with what Jordi proposed) and think it will need to be backed as there > should be a straightforward way how to compare it with exception code. > I might need to extend the gen stub as I'm not sure it allows macro > import for enum value like for constants or I will need to come up > with some mapping. I just don't wan to duplicate the numbers. I will > figure something out. How important is it to have exception code? I found that for our purposes we generally skip the exception code (i.e. keep it at zero). As a simple example, ErrorException does take two integers - one for severity, and one for code, instead of using the severity as code. I would propose something similar here: call it StreamError and provide a public $error property for the actual enum, rather than squeezing it into $code. This also solves your gen-stub issues by making them unncessary. > Should StreamException be attached the non-terminal errors which are > caused by the terminal errors? I.e. StreamException clearly is > STREAM_ERROR_CODE_PERMISSION_DENIED for example, but it happens > during a > copy, so, should the information that a STREAM_ERROR_CODE_COPY_FAILED > was caused by that, also be present on the StreamException? > > > I think I get what you mean from the user point of view but I'm not > sure how could this be reasonably linked. > > I could maybe check during stream error if there is EG(exception) it's > a StreamException and if so, I could the update it with this error > (e.g. some property that would hold those error can could be > retrieved). I guess it could be potentially extended for terminal > errors as well so it could have method like getAllErrors which would > return all errors because sometimes there can be more terminal errors > and some of them might get lost (last one will be added). However this > would work just for exception so maybe more generic solution is needed > here (see below). Replying below to this. > Are stream errors which happen as part of a fully wrapped > functionality > marked as wrapper error as well? Like if the server sent an > invalid TLS > response to a file_get_contents("https://...") operation, making the > whole operation fail? You obviously don't have access to the > intermediary internal stream resource there, where the stream > error is > attached. > > > I actually thought about this yesterday that it's not ideal that some > errors might get lost in this way so I think it should go to wrapper > errors as well. If we properly wrap the nested operations into a single error, then it would be automatically part of the wrapper error, with no need to explicitly add it to stream error. > Is this what the logged errors section is about? Is that section > talking > about "php_stream_display_wrapper_errors", which can concat some > errors? > I see that this logic is mostly retained. > > > Yeah the logged errors is an existing mechanism for grouping errors to > a single one (concatenating message). The display is a bit messy > because it separated them by new line or
(if html errors > enabled) but I just kept it to limit BC impact. I don't think changing the display / grouping here would be problematic with respect to BC. But I get that this initial draft tries to limit what it changes. > Do I understand the implementation correctly, that user error > handlers > can be called multiple times for a same operation? E.g. when > copying a > file, you'll get first a PERMISSION_DENIED then a COPY_FAILED? Should > these not be somehow merged? Especially with global user_error > handlers, > these cannot be aware of the fact that the permission denied is > part of > anything else - at least not until possibly a second user_error > invocation happens. > > > This is somehow related to the exception error grouping as it requires > some sort of linking between the errors. > > I guess to make this work we would need some top level wrapping > similar to what logged errors do but instead of concatenating, it > would group the errors in a better way which could then be used in > exception or handler. This would, however, increase the complexity > significantly as it is possible to nest the stream calls (e.g. stream > calls in notification callback or in the new error handler) so there > would probably need to be some stack. Also I'm not sure about the API. > Should we pass always array of errors to the callback (which would > mostly have just one element anyway or somehow chain the StreamError > object (something like previousError()) and what message should be > used in exception (e.g. last error and then helper function to get all > errors)? > > I'm willing to look into this but would like to know if this is > something important for user space and what is the usual flow how > could this be used in frameworks and so on. In other words I would > like to get more input to make sure that this is really needed as it > requires quite a bit of effort to do. Doing this work will definitely tidy up the whole error story. It's not fundamentally crucial, but it definitely will make for a nicer API. And yes, chaining StreamError sounds and nesting exceptions to $previous sounds like the most straightforward way. Also a possibility for exceptions: this might be a bit of work, but you could insert a fake frame for exceptions to indicate when an error appears as a sub-operation. Like: StreamException: Permission denied #0 [internal function]: fopen("target.php", "w+") #1 [internal function]: copy("source.php", "target.php") #2 ... I also like the suggestion by Jordi to pass the StreamError right away into the user handler. > Also, is the internal resource used for e.g. file_get_contents() > leaked > into user error handlers? I'm not sure whether that's desirable. If > that's intended, that's fine too, I think. > > > Currently yes. I need to actually double check the consequences of > that as I didn't really think about internal resources. If we decide to go with completely wrapping and calling the user error handler only once per top-level operation, this point would be moot anyway. > Does stream_get_errors() ever reset? Looking at the code it > doesn't seem > so. It will just forever grow. E.g. fwrite() will report an error > every > single time when the buffer is full and a write is attempted. If > errors > aren't freed, that's just a memory leak (as long as the socket > lives). > This is a serious flaw. > > Lastly, stream_get_errors() is overkill in a lot of cases (especially > when using fread()/fwrite() on simple sockets). I just care about the > last error code (there's just going to be one anyway). I'd love > having a > stream_get_last_error() returning just the code. > > > I guess this makes more sense but it kind of also implies the top > level wrapping as it should contain all errors in the call. Or > alternatively it could be just a ring with limited number of errors be > behave in the same way like openssl_error_string. I guess it depends > if we go with that top level wrapping. I feel like the top-level wrapping would simplify things a lot (from point of addressing shortcomings), and if this just works like json_get_last_error(), where you only ever see the last error, there's no risks of being unbounded nor having arbitrary size limits. The only disadvantage of top-level wrapping is probably a bit more book-keeping as in "storing that there is an outer operation before invoking an inner operation"? Haven't thought much about the actual implementation of this, but should be doable. Thanks, Bob --------------MfTn0W2yjVhalHPWN72Oamn1 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: 8bit

Hey Jakub,

thanks for the detailed reply.

On 19.11.2025 12:52:35, Jakub Zelenka wrote:
The code one should be probably named StreamErrorCode (to not interact with what Jordi proposed) and think it will need to be backed as there should be a straightforward way how to compare it with exception code. I might need to extend the gen stub as I'm not sure it allows macro import for enum value like for constants or I will need to come up with some mapping. I just don't wan to duplicate the numbers. I will figure something out.

How important is it to have exception code?

I found that for our purposes we generally skip the exception code (i.e. keep it at zero).
As a simple example, ErrorException does take two integers - one for severity, and one for code, instead of using the severity as code.
I would propose something similar here: call it StreamError and provide a public $error property for the actual enum, rather than squeezing it into $code.

This also solves your gen-stub issues by making them unncessary.  


Should StreamException be attached the non-terminal errors which are
caused by the terminal errors? I.e. StreamException clearly is
STREAM_ERROR_CODE_PERMISSION_DENIED for example, but it happens during a
copy, so, should the information that a STREAM_ERROR_CODE_COPY_FAILED
was caused by that, also be present on the StreamException?

I think I get what you mean from the user point of view but I'm not sure how could this be reasonably linked. 

I could maybe check during stream error if there is EG(exception) it's a StreamException and if so, I could the update it with this error (e.g. some property that would hold those error can could be retrieved). I guess it could be potentially extended for terminal errors as well so it could have method like getAllErrors which would return all errors because sometimes there can be more terminal errors and some of them might get lost (last one will be added). However this would work just for exception so maybe more generic solution is needed here (see below).

Replying below to this.


Are stream errors which happen as part of a fully wrapped functionality
marked as wrapper error as well? Like if the server sent an invalid TLS
response to a file_get_contents("https://...") operation, making the
whole operation fail? You obviously don't have access to the
intermediary internal stream resource there, where the stream error is
attached.

I actually thought about this yesterday that it's not ideal that some errors might get lost in this way so I think it should go to wrapper errors as well.

If we properly wrap the nested operations into a single error, then it would be automatically part of the wrapper error, with no need to explicitly add it to stream error.


Is this what the logged errors section is about? Is that section talking
about "php_stream_display_wrapper_errors", which can concat some errors?
I see that this logic is mostly retained.

Yeah the logged errors is an existing mechanism for grouping errors to a single one (concatenating message). The display is a bit messy because it separated them by new line or <br/> (if html errors enabled) but I just kept it to limit BC impact.

I don't think changing the display / grouping here would be problematic with respect to BC. But I get that this initial draft tries to limit what it changes.


Do I understand the implementation correctly, that user error handlers
can be called multiple times for a same operation? E.g. when copying a
file, you'll get first a PERMISSION_DENIED then a COPY_FAILED? Should
these not be somehow merged? Especially with global user_error handlers,
these cannot be aware of the fact that the permission denied is part of
anything else - at least not until possibly a second user_error
invocation happens.

This is somehow related to the exception error grouping as it requires some sort of linking between the errors.

I guess to make this work we would need some top level wrapping similar to what logged errors do but instead of concatenating, it would group the errors in a better way which could then be used in exception or handler. This would, however, increase the complexity significantly as it is possible to nest the stream calls (e.g. stream calls in notification callback or in the new error handler) so there would probably need to be some stack. Also I'm not sure about the API. Should we pass always array of errors to the callback (which would mostly have just one element anyway or somehow chain the StreamError object (something like previousError()) and what message should be used in exception (e.g. last error and then helper function to get all errors)? 

I'm willing to look into this but would like to know if this is something important for user space and what is the usual flow how could this be used in frameworks and so on. In other words I would like to get more input to make sure that this is really needed as it requires quite a bit of effort to do.

Doing this work will definitely tidy up the whole error story. It's not fundamentally crucial, but it definitely will make for a nicer API.

And yes, chaining StreamError sounds and nesting exceptions to $previous sounds like the most straightforward way.

Also a possibility for exceptions: this might be a bit of work, but you could insert a fake frame for exceptions to indicate when an error appears as a sub-operation. Like:

StreamException: Permission denied
#0 [internal function]: fopen("target.php", "w+")
#1 [internal function]: copy("source.php", "target.php")
#2 ...

I also like the suggestion by Jordi to pass the StreamError right away into the user handler.


Also, is the internal resource used for e.g. file_get_contents() leaked
into user error handlers? I'm not sure whether that's desirable. If
that's intended, that's fine too, I think.


Currently yes. I need to actually double check the consequences of that as I didn't really think about internal resources.

If we decide to go with completely wrapping and calling the user error handler only once per top-level operation, this point would be moot anyway.

  

Does stream_get_errors() ever reset? Looking at the code it doesn't seem
so. It will just forever grow. E.g. fwrite() will report an error every
single time when the buffer is full and a write is attempted. If errors
aren't freed, that's just a memory leak (as long as the socket lives).
This is a serious flaw.

Lastly, stream_get_errors() is overkill in a lot of cases (especially
when using fread()/fwrite() on simple sockets). I just care about the
last error code (there's just going to be one anyway). I'd love having a
stream_get_last_error() returning just the code.


I guess this makes more sense but it kind of also implies the top level wrapping as it should contain all errors in the call. Or alternatively it could be just a ring with limited number of errors be behave in the same way like openssl_error_string. I guess it depends if we go with that top level wrapping.

I feel like the top-level wrapping would simplify things a lot (from point of addressing shortcomings), and if this just works like json_get_last_error(), where you only ever see the last error, there's no risks of being unbounded nor having arbitrary size limits.


The only disadvantage of top-level wrapping is probably a bit more book-keeping as in "storing that there is an outer operation before invoking an inner operation"? Haven't thought much about the actual implementation of this, but should be doable.


Thanks,
Bob

--------------MfTn0W2yjVhalHPWN72Oamn1--