Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:129314 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 D1E2E1A00BC for ; Wed, 19 Nov 2025 20:45:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1763585161; bh=sMjiZ2Hhhq8ootBY34kym0ijbmyhHFiLufu4bRp1PH0=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=mosESO/NKd/NBycbInJCFZbpw2WYMv/xTx7BLuhv07aOeNarIr1nWcRZugE9ijQgK xGtFYr5Rb84uU4Aba5Mfv1L8rG7v/e6EKj315AgCIVejUP+qHqgRa94Sj00jETXTrD fnchzYZ2Ib/LhlSebYeB+cgqtgepFocsGIHzOg0uwmxwOEFWL9fSkG0NK83gyTFkI6 2ZbF9NtrvaekJ5JyOwRd4+PX7Asixn5lcDjalfCFi4t0o6a6OnYj6NIgp8nhdz/E+6 vYl00fgy3bvxNhsjmfLLIxEGffVcpvAZe/eTSk7jn5lKCJE9bdfD6w2VO+LY/NzBcF AA/CtvELsqYlQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 9315118004C for ; Wed, 19 Nov 2025 20:46:00 +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=0.6 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL, SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: No X-Envelope-From: Received: from mail-qt1-f170.google.com (mail-qt1-f170.google.com [209.85.160.170]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 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 20:46:00 +0000 (UTC) Received: by mail-qt1-f170.google.com with SMTP id d75a77b69052e-4ede6b5cad7so686441cf.2 for ; Wed, 19 Nov 2025 12:45:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1763585154; x=1764189954; darn=lists.php.net; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=CqrJBUzkLSgKvstcl9FrKj6BfRouTvGF1343k1Di/QE=; b=hitxSPwYHNiiPCkHkEiRIJXmX3KX5U53qkevxO/b6MuK8aQSEyKAxJ68ZLOVQj7vD8 qQ3dSaiD1sEB7oirvOhUZ6wbdh5++0aplZHRtTE5QqvMpLjGzZJAodPYofDR2G20zyGt mqjJ5RinqLFZQUPoXSMTEwoM+/pS/kMcFOPnyAxNaoqvz6RdohE6I6Tc2TYE2rdtj4U/ NXaEIYdn6Sw4y4KpM2iwUW5PsBeDjonapE9qjJzyAQoRsO/vDQWHW5XF5iH7dAx8x5N7 rcbVCQFgiHFYGV7mtFQYg0gn0TjFLONsjsgn1dO+vCQJEVYKozaPKUauvWD1uGaVizf+ 9hKg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763585154; x=1764189954; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=CqrJBUzkLSgKvstcl9FrKj6BfRouTvGF1343k1Di/QE=; b=cUjOpnmn0DxNWGJlK9TgVkiYyylnLmy9aX/dqgrOXe83FQA3fX6xAjAdXLKTOghj5W vL+0kdzFTQzpCN7HVRFTAnEacdiv6FWJb298WAUIUMr6UlQWybysoQb8eEXilGiHTdoU hpNWJP2Y5JY7xO7ZNifDUiMyxMEfWIgTF0/eYP9UEYHFl/mVChg/N6cisK7fP9Vq97Gm AKhx7e9xzViOGPqI0pYS+8459auMz6+/VKvPzElXak7ViDbud3mPcEXsqzFe7bR3DVvG 8g++M4GnYm4+lDbY+l4ep6mM9ZUlZL7WoQDtBbAXiA4K6CwC0fBzbm8f1SEQIih/81be mCZQ== X-Gm-Message-State: AOJu0YzmJbFy0WSbBhxMaQnqCLJLibiBuYtO0N498AVRIZ6vwUndgnlM sX1Kbc9ziTr7KlTcJQAI4pAa0j9u/k/nVvnzLrxKpe17F1w6l15oa67mQsUUvThOphsTWXTzz9w Kbwb6fhC8rYO6zNsjtksovL1PGr9CtFmLYCcN X-Gm-Gg: ASbGncsXEsrwuthK3ObnKy6gC5Yzox5+kjIfTTGlR/ivr8KhhIPyhqpebH3ZskcG22D q3hkvqhohzD0oQf2zXj9NSmAd4oRUdDmx2K4+pozPBDjXSJnsv2Zxiscav0kbJswDGhtNzOwHew mfrHmm+eia+bubgBgasiOAk57Q/D2Md+Ay8acl8mO0p5tACfv7VGnWNLoE/v9dpqgRKTfnNPP8q pDcTMC5SwO9/MHhuooEawGa7fCX+OfhPQddEOeuO2x1WOBDnQxOKHhZ4Ax36ZJxS2hAujJ8lOZK JyCgwUyev/qvQ39Q3HAIWc8z5+ngvd7Hk0evo8THiiXBoxmo+7JfxwXigJP/TWzzbw== X-Google-Smtp-Source: AGHT+IGly0qGAi9yJrF6LJUDWSuXg54z6iQ8X0jxf7yHzz5vVjvlGKVj/Lr5RwPJ7oVLIVwTDGRsLxFXLfJO+2k77YE= X-Received: by 2002:ac8:5e08:0:b0:4ed:b47d:8f68 with SMTP id d75a77b69052e-4ee494250e1mr10006181cf.15.1763585154489; Wed, 19 Nov 2025 12:45:54 -0800 (PST) Precedence: list list-help: list-unsubscribe: list-post: List-Id: x-ms-reactions: disallow MIME-Version: 1.0 References: <4b6abc36d8ab6cd306e95141869db3b0@bastelstu.be> <21ff8dee5016a1b7ecc412c44233abd1@bastelstu.be> <3a20b5ef66fc23de6bad022685b41190@bastelstu.be> <4e3061bfaa5218309041460d31be2856@bastelstu.be> In-Reply-To: Date: Wed, 19 Nov 2025 21:45:43 +0100 X-Gm-Features: AWmQ_bnWhcGS3fURrxrNmLLVNP5SLS1RHpzkzgNcjjV2PHUSHPuez0Sg4jCoL80 Message-ID: Subject: Re: [PHP-DEV] [RFC][Discussion] Add #[NoSerialize] attribute for excluding properties or classes from serialization To: Dmytro Kulyk Cc: PHP internals Content-Type: multipart/alternative; boundary="000000000000aa23150643f8aa8f" From: nicolas.grekas+php@gmail.com (Nicolas Grekas) --000000000000aa23150643f8aa8f Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi Dmytro =D0=BF=D1=82, 7 =D0=BB=D0=B8=D1=81=D1=82. 2025=E2=80=AF=D1=80. =D0=BE 12:57= Tim D=C3=BCsterhus =D0=BF=D0=B8=D1=88=D0=B5: > > > > Thank you. The updated RFC is looking good to me. I also appreciate tha= t > > you kept a changelog within the RFC. > > > > One thing you could mention and that should be done as part of the RFC > > is migrating all existing classes with `@not-serializable` to make use > > of the attribute instead. That's also what has been done when the > > `#[\Deprecated]` attribute was introduced. Other than that, I don't hav= e > > any further comments. > > > > Thank you for the review and the helpful suggestion. > > I agree that all internal classes currently annotated with > @not-serializable should also receive the corresponding > #[\NoSerialize] attribute as part of this RFC. This ensures that > internal metadata, reflection, and userland tooling remain fully > aligned, and that the behavior is consistently exposed at both the > engine and stub levels. > > The RFC has been updated to explicitly state that these attributes > will be added for all such internal classes. > > I also have one technical question regarding stub generation: > When attributes are emitted into generated arginfo.h files, should the > generator automatically include zend_attributes.h? > This would avoid requiring extensions to include zend_attributes.h > explicitly when they do not use attributes themselves but only include > generated stubs that reference them. Before making any implementation > adjustments, I=E2=80=99d appreciate confirmation that this approach is > acceptable within the current stub-generation design. > > ``` > /* This is a generated file, edit the .stub.php file instead. > * Stub hash: 2fc12d1fde65efbec4305f4934a3f4b25282a552 */ > > #include "zend_attributes.h" > ... > zend_add_class_attribute(class_entry, > ZSTR_KNOWN(ZEND_STR_NO_SERIALIZE), 0); > ... > ``` Thanks for the RFC. I think it=E2=80=99s an easy sell, but not necessarily for the right reason= s. Let me explain. First, on property-level usage: The RFC states that the attribute covers a common use case: transient or resource-based properties in frameworks and libraries. On which fact is this based? I checked code bases I know well, Symfony in particular, and I had a hard time finding any implementation of __serialize()/__sleep() that the property-level attribute would replace (ignoring BC). Symfony usually exhibits most common and uncommon patterns, so if this were widespread, I would expect to see some. More generally, having objects that embed both value-state and a PDO connection looks like a design smell: such objects behave differently before and after serialization, which is inherently brittle. Such cases exist in the wild, of course, but making them a first-class, favored use case in the language seems questionable. Second, on class-level not-serializability: This is a very common need. We had a thread a year ago discussing a #[NotSerializable] attribute with identical semantics ( https://externals.io/message/121969). My position was and still is: attaching this semantics to classes fits an interface far better than an attribute. Even if it=E2=80=99s a negative contract, it=E2=80=99s still a c= ontract, and contracts propagate naturally to child classes. Attributes don=E2=80=99t, a= nd making this one an exception raises the question: why break attribute rules when PHP already has the right tool - interfaces - to express this? The engine currently uses a flag internally, but it could just as well use an interface, removing a special case and aligning internal and userland behavior. This follows a long and good tradition of replacing ad-hoc engine features with userland-visible constructs. Finally, a practical concern: I wrote and still maintain the symfony/var-exporter component, which implements PHP=E2=80=99s serialization semantics in userland but outputs ex= ecutable PHP code. It is used in performance-sensitive cache systems. With the proposed attribute, we would need reflection-based checks on every property during both serialization and unserialization. Even with caching, this would noticeably hurt performance. This is another argument in favor of an interface BTW: instanceof/is_subclass_of() checks are much cheaper than reading attributes for userland (not even counting the proposed inheritance rule). Thanks for considering my feedback. Best regards, Nicolas --000000000000aa23150643f8aa8f Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi Dmytro=C2=A0


=D0=BF=D1=82, 7 =D0=BB=D0=B8=D1=81= =D1=82. 2025=E2=80=AF=D1=80. =D0=BE 12:57 Tim D=C3=BCsterhus <tim@bastelstu.be> =D0=BF= =D0=B8=D1=88=D0=B5:
>
> Thank you. The updated RFC is looking good to me. I also appreciate th= at
> you kept a changelog within the RFC.
>
> One thing you could mention and that should be done as part of the RFC=
> is migrating all existing classes with `@not-serializable` to make use=
> of the attribute instead. That's also what has been done when the<= br> > `#[\Deprecated]` attribute was introduced. Other than that, I don'= t have
> any further comments.
>

Thank you for the review and the helpful suggestion.

I agree that all internal classes currently annotated with
@not-serializable should also receive the corresponding
#[\NoSerialize] attribute as part of this RFC. This ensures that
internal metadata, reflection, and userland tooling remain fully
aligned, and that the behavior is consistently exposed at both the
engine and stub levels.

The RFC has been updated to explicitly state that these attributes
will be added for all such internal classes.

I also have one technical question regarding stub generation:
When attributes are emitted into generated arginfo.h files, should the
generator automatically include zend_attributes.h?
This would avoid requiring extensions to include zend_attributes.h
explicitly when they do not use attributes themselves but only include
generated stubs that reference them. Before making any implementation
adjustments, I=E2=80=99d appreciate confirmation that this approach is
acceptable within the current stub-generation design.

```
/* This is a generated file, edit the .stub.php file instead.
=C2=A0* Stub hash: 2fc12d1fde65efbec4305f4934a3f4b25282a552 */

#include "zend_attributes.h"
...
=C2=A0 =C2=A0 zend_add_class_attribute(class_entry, ZSTR_KNOWN(ZEND_STR_NO_= SERIALIZE), 0);
...
```

Thanks for the RFC.

I think it=E2=80=99s an easy sell, but not necessarily for the right = reasons. Let me explain.

First, on property-level usage:<= br>The RFC states that the attribute covers a common use case: transient or= resource-based properties in frameworks and libraries. On which fact is th= is based? I checked code bases I know well, Symfony in particular, and I ha= d a hard time finding any implementation of __serialize()/__sleep() that th= e property-level attribute would replace (ignoring BC). Symfony usually exh= ibits most common and uncommon patterns, so if this were widespread, I woul= d expect to see some. More generally, having objects that embed both value-= state and a PDO connection looks like a design smell: such objects behave d= ifferently before and after serialization, which is inherently brittle. Suc= h cases exist in the wild, of course, but making them a first-class, favore= d use case in the language seems questionable.

Second, on= class-level not-serializability:
This is a very common need. We had a t= hread a year ago discussing a #[NotSerializable] attribute with identical s= emantics (https://externals= .io/message/121969). My position was and still=C2=A0is: attaching this = semantics to classes fits an interface far better than an attribute. Even i= f it=E2=80=99s a negative contract, it=E2=80=99s still a contract, and cont= racts propagate naturally to child classes. Attributes don=E2=80=99t, and m= aking this one an exception raises the question: why break attribute rules = when PHP already has the right tool - interfaces - to express this? The eng= ine currently uses a flag internally, but it could just as well use an inte= rface, removing a special case and aligning internal and userland behavior.= This follows a long and good tradition of replacing ad-hoc engine features= with userland-visible constructs.

Finally, a practical c= oncern:
I wrote and still maintain the symfony/var-exporter component, which im= plements PHP=E2=80=99s serialization semantics in userland but outputs exec= utable PHP code. It is used in performance-sensitive cache systems. With th= e proposed attribute, we would need reflection-based checks on every proper= ty during both serialization and unserialization. Even with caching, this w= ould noticeably hurt performance. This is another argument in favor of an i= nterface BTW: instanceof/is_subclass_of() checks are much cheaper than read= ing attributes for userland (not even counting the proposed inheritance rul= e).

Thanks for considering my feedback.
Best re= gards,

Nicolas
--000000000000aa23150643f8aa8f--