Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:126597 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 qa.php.net (Postfix) with ESMTPS id 6830D1A00BC for ; Thu, 6 Mar 2025 09:03:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1741251665; bh=0XbOHNNemGFPmNraHLWAPFSUlD8/6h8tPliNpneomVM=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=EGZccVgF5nLjbCJRsn+udee0fFkLVtlNyJL/lslETTPg1ApymV3AZyQzs6l1sawoe uJvkW6zVbcvYKli2fZjA5OJVSA6gG141fX28ZjhK63eb50aoBH+K7Aqi8rE/Hca5yo s0spIGOZr4S0mC6o3Djil6+CK9+ZIl/RHEc6Z4BC68/DCkw71tGwc6h8mdlKb/kZ0a 59wh12pPecVkdLpCKhhAJZLTflIfdIU8n1u8v/k4DdEK6+MAGkbF8UaKFLkaMV4mG3 esrkJkjSvdH4yInSEv8g0KNm8VSo2bNol+/qwKm3UxQ+X9/BpUACwn5smoRSKChaZi 4XmKKpX1veBzg== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 7E4E7180034 for ; Thu, 6 Mar 2025 09:01:04 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-3.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE, SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-yb1-f169.google.com (mail-yb1-f169.google.com [209.85.219.169]) (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 ; Thu, 6 Mar 2025 09:01:01 +0000 (UTC) Received: by mail-yb1-f169.google.com with SMTP id 3f1490d57ef6-e5dcc411189so322267276.0 for ; Thu, 06 Mar 2025 01:03:36 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1741251816; x=1741856616; 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=ZmCJLLiTnWy1oUYkmbHS1zBWv6qi2HLXx+jnmR5NVZs=; b=KrK/GaLRqKW6EGTWSHSW3nWCmamwIEXdTtaOVw4i4JxPzVIvoM3Qs/IhbH5rmdJoMW iys4bl/ElJnZjmeSmAHsrm+vxvUgdcSRAAcDqwLmvmIOVBoPuZs6olKbNQA3iB7KGs7P 90QdpkMVGaQdHXKrUz1VO/0mon4A/keoUkrHzf7r0364+vEZyejYz96k21efnmBSbKqN OiV2wofNN2NBaimn7o7I3g9iwUufvbiPP4A2MKLOfxwb8Ca59DnaCGHNWd4SibUgIr4Q L2Z13jac3SnyqShulz1VzI87EFbLnF6yhdT7xqjGhum0oRBj1WUEeshSi1pOCkNuWEnK NXqQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741251816; x=1741856616; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=ZmCJLLiTnWy1oUYkmbHS1zBWv6qi2HLXx+jnmR5NVZs=; b=o5PiBPGf+smPquWAVaU591WXyBNteG2s1XC7S71gz1EtEwYty700CceIXEz8Fodpmr 1PzC6OZnlIUAomNS1RtKQfs1sbjh3sR0f+sZrO9DrC8u58buX1M/W3oQq2XznZoXf10d esvOIAktPcz7ZVRVzYBTlIoMhMQErufyGR3kTC9VHGZQPp9rRwXzHME3Xgt0x/Fdehj4 fDMkQD2yL+SqfqvVv8mzgb3kvVIMSNovR8e4HP93ZFRRZZfjxuD6RHoth7Dtxm9McxEj dT7y8Maph45pfNtWfaMZLFQ1OlCNCO9uVFmboeWLCs5DbHPydTf5gFz2H0GlKIWUB+s9 a95A== X-Gm-Message-State: AOJu0YwbotdBGazfschZ1nuefiHnEBOEZno6WZhW6XHc+zhu7BhrdWVU dtaQs8YxBLWJWEwMj0Q7UMSWVOrJzXq6+6YmtaxnHJFu4QXyqmBcOP8tHFxRqwMRyv9oljSYzZI kadNtycqDzHjKpFGKrZlJIwUd3gDNwZyNuvw= X-Gm-Gg: ASbGncvCUMEiMQwblorguDJyPFGwdjV3abpZP0RUQjpWeZ6yGH160mjvX0sQzvrXOIp z++Cuunj8uotnCA1YTFTcezoRwS1vsZwMzVv++U3K6WWdsIdRIl34CKEp83/0SIXzxBsmoKHsfO 6d/Rsb7l1gydMpRnRb/yGrRCwF2EQ= X-Google-Smtp-Source: AGHT+IHHnP1SVSKr+pK38tP9ANWcbMZQwU9ns9rncDGuhvYeDY+DkHiEFO76W6p/F1koquCJE3izVLkBnp4WrRGiV08= X-Received: by 2002:a05:6902:3007:b0:e60:9ea8:d9b2 with SMTP id 3f1490d57ef6-e611e35501cmr5596385276.35.1741251815762; Thu, 06 Mar 2025 01:03:35 -0800 (PST) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 References: In-Reply-To: Date: Thu, 6 Mar 2025 10:03:24 +0100 X-Gm-Features: AQ5f1Jp1AKvAuw2tK6f8EFBPL-Xp8IGGLtln3JGUtVPULpRCPf4YwuV9kn2DIbc Message-ID: Subject: Re: [PHP-DEV] RFC: short and inner classes To: Rob Landers Cc: internals@lists.php.net Content-Type: multipart/alternative; boundary="000000000000f176ce062fa8c7d4" From: michal.brzuchalski@gmail.com (=?UTF-8?Q?Micha=C5=82_Marcin_Brzuchalski?=) --000000000000f176ce062fa8c7d4 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi Rob, czw., 6 mar 2025 o 00:16 Rob Landers napisa=C5=82(a): > Hello PHP Internals, > > I'd like to introduce my RFC for discussion: > https://wiki.php.net/rfc/short-and-inner-classes > > This RFC defines a short class syntax as well as the ability to nest > classes inside another class. This introduces an unprecedented amount of > control, flexibility, and expressiveness over how objects are used and > instantiated in PHP. There is a PR ( > https://github.com/php/php-src/pull/17895) that implements this > functionality -- all test failures are related to different/new/incorrect > error messages being generated. However, the core functionality exists to > take for a test ride. > > So, what do I mean by "unprecedented amount of control"? With this change= , > you can declare an inner class as private or protected, preventing its > usage outside of the outer class: > > class User { > private class Id {} > > public function __construct(public self::Id $id) {} > } > > In the above example, the class `User` is impossible to construct even > though it has a public constructor (except through reflection) because > User::Id is private; User::Id cannot be instantiated, used as a type hint= , > or even via `instanceof` outside of the User class itself. This example > isn't practical but demonstrates something that is nearly impossible in > previous versions of PHP, where all classes are essentially publicly > accessible from anywhere within the codebase. > > As a number of inner classes will probably be used as DTOs, the RFC > introduces a "short syntax" for declaring classes, which enhances > expressiveness, even allowing the usage of traits, all in a single line: > > // declare a readonly Point, that implements Vector2 and uses the > Evolvable trait > readonly class Point(public int $x, public int $y) implements Vector2 use > Evolvable; > > When combined with inner classes, it looks something like this: > > class Pixel { > public readonly class Point(public int $x, public int $y) implements > Vector2 use Evolvable; > } > > // Create a new pixel point with property $x and $y set to 0 > $p =3D new Pixel::Point(0, 0); > > There are far more details in the RFC itself, so please check it out. I'm > quite excited to hear your thoughts! > > =E2=80=94 Rob > > PS. I know I tend to rush into things, but I want to make it clear that > I'm not rushing this -- I've learned from my mistakes (thank you to those > who have given me advice). I'm going to do this right. > Inner classes - YES, Short classes - YES, Short empty classes - YES - very nice solution for extending Exception class, maybe there is no need for parentheses??, Traits in single line - dunno, personally don't see the need so no much preference here, IMO would be debatable. Especially both together. Inner classes are something I was thinking about many years ago [1]. And the short classes are something that is also in my field of interest. I remember this was proposed some time ago on ML. Currently, all of the classes replace array-shapes I mark with additional docblock including `@internal` tag. Many DTO's have no logic or not that much, meaning they don't need methods. An example which you may think could be expressed differently but since I'm used to expressing my opinion as fast as possible this is what came to my mind now. Possibly not all of the inner classes should be inner, but there is one that definitely should be, see: final class TZParser { /** * @param TZTypeInfo[] $types * @param TTInfo[] $transitions * @param string[] $abbreviations * @param array $leapSecondData */ public class TZInfo( string $version, bool $isV2Plus, array $types, array $transitions, array $abbreviations, array $leapSecondData, string|null $posixString, ); protected class TZTypeInfo(public int $gmtOffset, bool $isDst, int $abbreviationIndex, bool $isStd =3D false, bool $isUt =3D false) protected class TTInfo(int $timestamp, int $typeIndex); private class TZInfoSection(array $types, array $transitions, array $abbreviations, array $leaps, int $newOffset); public static function parse(string $filename): TZInfo { /** impl */ } } While it looks different with short class and doc block for TZInfo, for the others that don't require docblock this is pretty nice, even if the fields would be declared each in a separate line - it's a personal preference. Additionally, I could easily remove the some prefixes I see this way of declaring DTO's more convenient than having 3 separate files for just a small DTO class where I need to put `/** @internal */` on each of them. Inner classes won't be possible to instantiate outside of the TZParser class which is intended here. All instances should be available outside of the parser to read the information with one exception for TZInfoSection. In this example the TZInfoSection class is used internally (which is why marked as private) by the parser itself, it's not exposed outside because there is no real use case for that which makes this feature very interesting to me. If the RFC would be splitted into smaller or not I say YES for inner+short(and empty). I hope others will see proposed features as useful as I. Good luck. [1] https://brzuchal.com/posts/inner-classes-in-php-concept/ --000000000000f176ce062fa8c7d4 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi Rob,

czw., 6 mar = 2025 o 00:16=C2=A0Rob Landers <rob@bottled.codes> napisa=C5=82(a):
Hello PHP Internals,
I'd like to introduce my RFC for discussion:=C2=A0htt= ps://wiki.php.net/rfc/short-and-inner-classes

<= div>This RFC defines a short class syntax as well as the ability to nest cl= asses inside another class. This introduces an unprecedented amount of cont= rol, flexibility, and expressiveness over how objects are used and instanti= ated in PHP. There is a PR (https://github.com/php/php-src/pull/17895) tha= t implements this functionality -- all test failures are related to differe= nt/new/incorrect error messages being generated. However, the core function= ality exists to take for a test ride.

So, what= do I mean by "unprecedented amount of control"? With this change= , you can declare an inner class as private or protected, preventing its us= age outside of the outer class:

class User {
=C2=A0 private class Id {}

=C2=A0= public function __construct(public self::Id $id) {}
}
<= div>
In the above example, the class `User` is impossible to = construct even though it has a public constructor (except through reflectio= n) because User::Id is private; User::Id cannot be instantiated, used as a = type hint, or even via `instanceof` outside of the User class itself. This = example isn't practical but demonstrates something that is nearly impos= sible in previous versions of PHP, where all classes are essentially public= ly accessible from anywhere within the codebase.

As a number of inner classes will probably be used as DTOs, the RFC intr= oduces a "short syntax" for declaring classes, which enhances exp= ressiveness, even allowing the usage of traits, all in a single line:

// declare a readonly Point, that implements Vector= 2 and uses the Evolvable trait
readonly class Point(public int $x= , public int $y) implements Vector2 use Evolvable;

=
When combined with inner classes, it looks something like this:
<= div>
class Pixel {
=C2=A0 public readonly class= Point(public int $x, public int $y) implements Vector2 use Evolvable;
<= /div>
}

// Create a new pixel point with p= roperty $x and $y set to 0
$p =3D new Pixel::Point(0, 0);

There are far more details in the RFC itself, so plea= se check it out. I'm quite excited to hear your thoughts!
=E2=80=94 Rob

PS. I = know I tend to rush into things, but I want to make it clear that I'm n= ot rushing this -- I've learned from my mistakes (thank you to those wh= o have given me advice). I'm going to do this right.
<= /div>

Inner classes - YES,
Short = classes - YES,
Short empty classes - YES - very nice solution for= extending Exception class, maybe there is no need for parentheses??,
=
Traits in single line - dunno, personally don't see the need so no= much preference here, IMO would be debatable.

Esp= ecially both together. Inner classes are something I was thinking about man= y years ago [1].
And the short classes are something that is also= in my field of interest. I remember this was proposed some time ago on ML.=

Currently, all of the classes=C2=A0replace array-= shapes I mark=C2=A0with additional docblock including `@internal` tag.
Many DTO's have no logic or not that much, meaning they don't= need methods.

An example=C2=A0which you may think= could be expressed differently but since I'm used to expressing my opi= nion as fast as possible this is what came to my mind now. Possibly not all= of the inner classes should be inner, but there is one that definitely sho= uld be, see:

final class TZParser
{=C2=A0 =C2=A0 /**
=C2=A0 =C2=A0 =C2=A0* @param TZTypeInfo[] $types
= =C2=A0 =C2=A0 =C2=A0* @param TTInfo[] $transitions
=C2=A0 =C2=A0 =C2=A0*= @param string[] $abbreviations
=C2=A0 =C2=A0 =C2=A0* @param array<in= t,array{timestamp:int,corr:int}> $leapSecondData
=C2=A0 =C2=A0 = =C2=A0*/
=C2=A0 public class TZInfo(
=C2=A0 =C2=A0 stri= ng $version,
=C2=A0 =C2=A0 bool $isV2Plus,
=C2=A0 =C2=A0 array $types= ,
=C2=A0 =C2=A0 array $transitions,
=C2=A0 =C2=A0 array $abbreviation= s,
=C2=A0 =C2=A0 array $leapSecondData,
=C2=A0 =C2=A0 string|null $po= sixString,
=C2=A0 );

=C2=A0 protecte= d class TZTypeInfo(public int $gmtOffset, bool $isDst, int $abbreviationInd= ex, bool $isStd =3D false, bool $isUt =3D false)

= =C2=A0 protected class TTInfo(int $timestamp, int $typeIndex);
=C2=A0 private class TZInfoSection(array $types, array $transi= tions, array $abbreviations, array $leaps, int $newOffset);
=C2= =A0=C2=A0
=C2=A0 public static function parse(string $filename): = TZInfo
=C2=A0 { /** impl */ }
}

While it looks different with short class and doc block for TZInfo, for t= he others that don't require docblock this is pretty nice, even if the = fields would be declared each in a separate line - it's a personal pref= erence.
Additionally, I could easily remove the some prefixes

I see this way of declaring DTO's more convenient= than having 3 separate files for just a small DTO class where I need to pu= t `/**=C2=A0@internal */` on each of them. Inner classes won't be possi= ble to instantiate outside of the TZParser class which is intended here. Al= l instances should be available outside of the parser to read the informati= on with one exception for TZInfoSection.

In this e= xample the TZInfoSection class is used internally (which is why marked as p= rivate) by the parser itself, it's not exposed outside because there is= no real use case=C2=A0for that which makes this feature very interesting t= o me.

If the RFC would be splitted into smaller or= not I say YES for inner+short(and empty).
I hope others will see= proposed features as useful=C2=A0as I. Good luck.

[1]=C2=A0https://brzuchal.com/posts/inner-classes-in-php-concept/
--000000000000f176ce062fa8c7d4--