Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:126534 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 297721A00BC for ; Sat, 1 Mar 2025 01:32:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1740792572; bh=ZdugW7YHwZkUAvfEvXaK3xO6UoNFfCm4q91U+9I/ems=; h=Subject:From:In-Reply-To:Date:Cc:References:To:From; b=THATHvSWQ3AKF3xrEcucrDNFlZ/BqlAys80spJirRnZ0DJZubjMmtQhI4gobw40Vo w8EWHFmvKv16VSfIZIyuitjvnzbjn8akvhVi+l6Q5b5qPv+QiuE5ZeUDZR4Ui7pvj5 bT/XtMQm/0PCHMXuQjKlgj6ktgKRkfkrLEwdMEjnBGntNNjuDHBkJE+MUk41fiX4DQ eWexRvMKh2s92TrQzs5qQrLZFKo5PwLniHV2TLdzGBnnlxbClL3Rt77gZrutCyScuN 4HEBYPH5BHwn0XY3H5UlS7jK3ZFm0PbJuRjIaClR1dT0/xej8kET/VYbsb4VEiYcHV CXdowL01o9JrQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id AA5BD18006C for ; Sat, 1 Mar 2025 01:29:31 +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=-2.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from st43p00im-ztbu10073601.me.com (st43p00im-ztbu10073601.me.com [17.58.63.184]) (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 ; Sat, 1 Mar 2025 01:29:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=garsi.de; s=sig1; bh=sM61SVqQEgbWDaDrl8651M5t0LFmhDmf8L3OvDreH+k=; h=Content-Type:Mime-Version:Subject:From:Date:Message-Id:To:x-icloud-hme; b=Dp37aKLRkKmBFJKij9wpWFUa8P/2KdHnU3r0le4bak3SdQIy9LoIUkdZ5+Tx6k6cE t+VUXGLmRCGdchk3ykavPg0Bpogh22QBdOMLO9hinYzSjYKE7QS0pxaf9VK1Eo4LBd yek+hMl8hfJTH08rBGePlbEAr8995uOE/+lcofE0v+m2izygGrW55mwJh2agoN927H jKgsijB/GD2eeMc+NeLHL+A7viI26ymSbql6WDtFJhG6NMoWsmmcukogB4nXkxWRId u03X+eGc5p5Zmmh2EuSWIxRkiPDgdnbtZ7nXQywqhmhuHZCLDiY8Q39ytJgv89IJ/D c34Dyeg1IqyEg== Received: from smtpclient.apple (st43p00im-dlb-asmtp-mailmevip.me.com [17.42.251.41]) by st43p00im-ztbu10073601.me.com (Postfix) with ESMTPSA id 65CB518046A; Sat, 1 Mar 2025 01:32:06 +0000 (UTC) Content-Type: text/plain; charset=us-ascii Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3826.400.131.1.6\)) Subject: Re: [PHP-DEV] Vibe check: array shapes In-Reply-To: <86f10d5a-c7d0-4592-900f-fdcd11b8ff3c@app.fastmail.com> Date: Sat, 1 Mar 2025 02:31:53 +0100 Cc: PHP Internals Content-Transfer-Encoding: quoted-printable Message-ID: References: <81F87FFD-91EF-4FDF-A929-9BE1CA08AA1E@garsi.de> <86f10d5a-c7d0-4592-900f-fdcd11b8ff3c@app.fastmail.com> To: Larry Garfield X-Mailer: Apple Mail (2.3826.400.131.1.6) X-Proofpoint-GUID: sOHMDVqoamzWfE-rDEa36cuanE7PwiMu X-Proofpoint-ORIG-GUID: sOHMDVqoamzWfE-rDEa36cuanE7PwiMu X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.272,Aquarius:18.0.1057,Hydra:6.0.680,FMLib:17.12.68.34 definitions=2025-02-28_07,2025-02-28_01,2024-11-22_01 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 phishscore=0 mlxscore=0 clxscore=1030 suspectscore=0 mlxlogscore=999 adultscore=0 bulkscore=0 spamscore=0 malwarescore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2308100000 definitions=main-2503010009 From: alwin@garsi.de (Alwin Garside) On 1 Mar 2025, at 00:28, Larry Garfield wrote: > Your friends are correct. Arrays in PHP are an awful thing. They are = twice as memory hungry as objects, they provide no static guarantees, = and they're a security hole waiting to happen (and has in fact = happened). >=20 > And since PHP 8.0, *we have something equivalent to array shapes = already that is vastly more efficient*: Constructor promotion. >=20 > // This tells me nothing. > $arrayPoint =3D ['x' =3D> 1, 'y' =3D> 2]; >=20 > // This tells me everything. > class Point { > public function __construct( > public int $x, > public int $y, > ) {} > } > $objectPoint =3D new Point(x: 1, y: 2); >=20 > $objectPoint will use half as much memory as $arrayPoint. (Not = meaningful in something this small, but if you have 100 of them...) It = provides all the static type validation you can get in PHP right now. = It's self-documenting. It works with everything else in the language. = And it's already been available for 5 years. And now you can also toss = readonly, private(set), or hooks around on it if you feel like it. >=20 > This is a solved problem, and the solution is objects. Using arrays = as pseudo-objects is the wrong approach 99.9% of the time. Collections = are a different matter, as those are real lists. >=20 > For more details: https://www.youtube.com/watch?v=3DnNtulOOZ0GY >=20 > All that said! >=20 > A side effect of the pattern matching RFC (which we'll be formally = proposing real-soon-now-I-swear) is that it effectively gives an = array-shape validation check for free. >=20 > https://wiki.php.net/rfc/pattern-matching >=20 > So yeah, no thank you on a separate array shapes RFC. >=20 > --Larry Garfield I get you. Used analogously to objects, arrays are indeed awful by = comparison. However, arrays are very ergonomic to work with, especially for passing = along a list of properties, which is why many frameworks and libraries = still use them. The combination of constructor promotion, named arguments and property = hooks (thank you for those btw!) have definitely helped to make classes = more usable in this regard, but they are still too "rigid" compared to = structural interfaces as in, for example, TypeScript. Consider if you have several interface defined for a set of options, = you'll always need to declare a class to actually implement them. interface ALotOfOptions { public int $this { get; } public ?float $that { get; } public ?string $then { get; } public ?string $there { get; } // [...] } =20 interface AdditionalOptions { public ?string $thisToo { get; } } =20 class AllTheOptions implements ALotOfOptions, AdditionalOptions { public __construct( public int $this { get; } public ?float $that { get; } public ?string $then { get; } public ?string $there { get; } // [...] public ?string $thisToo { get; } ) } =20 $options =3D new AllTheOptions( this: 42, that: 1.337, there: 'here', // [...] ); =20 function iWantOptions(ALotOfOptions & AdditionalOptions $options) { = ... } I guess you _could_ do this with anonymous classes, but that doesn't = make things any prettier. With my hypothetical shapes you'd declare a structural interface, and = both implement and instantiate it at the same time by initializing an = array. shape ALotOfOptions { 'this': int; 'that': ?float; 'then': ?string; 'there': ?string; // [...] } =20 shape AdditionalOptions { 'thisToo': ?string; } =20 $options =3D [ 'this' =3D> 42, 'that' =3D> 1.337, 'there' =3D> 'here', // [...] ]; =20 function iWantOptions(ALotOfOptions & AdditionalOptions $options) { = ... } As you can see, there is a less boilerplate code, and it becomes easier = to combine sets of options. This is a very popular pattern in = TypeScript, for example when working with AWS CDK, and in my experience = it is very ergonomic to work with. Are PHP arrays the ideal solution for this? No, not really. But at the = moment they are the only realistic option if you want to provide a = flexible and ergonomic solution. I know arrays are inferior to objects = in terms of type-safety and memory consumption, but they have their = legit use cases (besides serving as glorified lists), and often are = simply the more ergonomic option. The truth of the matter is, arrays = aren't going anywhere anytime soon, and I think it would not be a bad = idea to at least make them less treacherous to work with for the people = that have legit use cases for them. Having said all that! After Tim suggested your RFC I have taken a better look, and patterns = are indeed a very interesting proposition. However, for pattern matching = to cover my wishes regarding arrays shapes would depend on several = features described as future scopes, namely 'Optional array key marker' = and 'Patterns as variables/types'. Plus I'm a bit unclear about what the = syntax would exactly look like. =46rom what I can gather it would look = something like this: pattern [ 'this' =3D> int, ?'that' =3D> float, ?'then' =3D> string, ?'there' =3D> string, // [...] ] as ALotOfOptions; =20 pattern [ ?'thisToo': string, ] as AdditionalOptions; I'm not too sure how I feel about the `as` keyword at the end... But = that's a story for a different thread. At any rate, it's clear that Pattern Matching makes any array shape RFC = completely redundant. I'll be rooting for the Pattern Matching RFC! Seems like it would be a = very powerful addition to PHP. And I hope that the future scopes I = mentioned will find their way to their own respective RFC(s) soon after, = then everybody will be happy. :-) Alwin=