Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:125414 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 953351A00BD for ; Wed, 4 Sep 2024 14:56:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1725461931; bh=oBmo1fb+8GPoWDphaADsVDFKI4/N+UHXIlzkHruwKeY=; h=Subject:From:In-Reply-To:Date:Cc:References:To:From; b=cQdKGMAGeiGsyDQ5k80WlsEnFrtUurZRPzBrDlp457nAlFBPMHj8HELoy5dJ961Zk 7Sg+hdIuDDMCC2FZ6P3Q/FKdPmgxql2iSPk4o+e7Z9mmiswBklcdrPjAgtCkeMGKIA 9jL3ViEfUu6YDwdh1P6i66t5LcK95CnaBaYFcRuOr7wnnqC1AVm5Qcr6UlXpP7YJDd DZmcgctJN0gdTgLYnPegZNdOWyny0HYnaY72dZB+DTzY5Ki3qaNR0dUE2iC0bS2OLm F0aZWcTxw+3YmjBXjJqMSNJG9L/SCkKPuSmp798irKOBoph2G9oDtjeov3Hor9vx3x Bd8bUEWrR02oA== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 3A20C18005B for ; Wed, 4 Sep 2024 14:58:50 +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=0.8 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DMARC_MISSING,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-yw1-f169.google.com (mail-yw1-f169.google.com [209.85.128.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 ; Wed, 4 Sep 2024 14:58:49 +0000 (UTC) Received: by mail-yw1-f169.google.com with SMTP id 00721157ae682-6d49cec2a5dso47378887b3.3 for ; Wed, 04 Sep 2024 07:56:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=newclarity-net.20230601.gappssmtp.com; s=20230601; t=1725461811; x=1726066611; darn=lists.php.net; h=to:references:message-id:content-transfer-encoding:cc:date :in-reply-to:from:subject:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=VQhGg+N59N+G3hiLQHQav5XIMJDVA/WncczokWC7Kkw=; b=rllCkuywoJR9ZLL8jeFB+dfnKmGnNNDq5R/7DEPb9J6DTw4xxzYLHWdJpyZaY9QLti AVjQJwy4ZQmP8NWw7AAccZ8bWQZ7Q5u6J84lxG27IPhnUvLth+LbS8tSvRW06ndH3GZ1 UjjSsWBdo+jJ4cUfSf6H4HytkAU/68iU9jQi4D2+iBWeBJRrruQedH5HdPxDbm/5xqYd sqSTyUcMRhDkuK9wjz36qbzhNmTt0B2MiHdpKX59tpYvkQ4Q3safczveJb3at45hQyhk 6lsjAA/ko7wSeC5RWDbNWXg8VB7aImVNbEzHgSZdX0sq+B+FPocQ9HhcjsAMwX+1yWIC Vd7g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1725461811; x=1726066611; h=to:references:message-id:content-transfer-encoding:cc:date :in-reply-to:from:subject:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=VQhGg+N59N+G3hiLQHQav5XIMJDVA/WncczokWC7Kkw=; b=HEmwhd5YEI8Fmx4U/srxxqPvjx/lRKgPBZOm3XhR7HePkmPuLJl/svF28POGDJPaDM CUnGviZXCcSRoqkpC8necH7xDrIibhiM1f60HPJ0Nb2E/cIYpkWnTTCXbBqJqiZf3IGG j0r6XY0eccAXZ4oZBSFIm9UnpMOlVkL3rFjEEJPrElHe/o+YUhsOhG00dl4X4V30uOs0 01xtcyVrZyUK728D7LWG1Wv62ald9srPu7Go/+vxqeBYw9IBw/jOHpL7943AIpHxEZ7R xCAF1qbDFqFByxHzGcobB7/wXeVyKhkKR00YZ/tZJ0Kx/g+WmhyXxNPdx/d9GMEXpXau OIig== X-Gm-Message-State: AOJu0Yyp5wGZBRjSm/JDBI08yWsBIC7CbKdn7cJyeuMZtD9KKgPr8MYy ITdh7Olx3GLd6cI53RMvFcsa4hna/J3xvcqpx5JD6FApVkGxKsmiGFVzMEYgbL0uG3FjjPbNFoU +7YA= X-Google-Smtp-Source: AGHT+IHu2USjyLzJSF1SFWe2ob7Svpdqf+aHPA2sOPRDBBlv5nZ7ia1c/LoC0f1071eHdz+SJM8PFw== X-Received: by 2002:a05:690c:c10:b0:6d3:ceb1:30d8 with SMTP id 00721157ae682-6d40f341534mr221497847b3.14.1725461810939; Wed, 04 Sep 2024 07:56:50 -0700 (PDT) Received: from smtpclient.apple (c-98-252-216-111.hsd1.ga.comcast.net. [98.252.216.111]) by smtp.gmail.com with ESMTPSA id 00721157ae682-6d73827f207sm11892757b3.59.2024.09.04.07.56.49 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 04 Sep 2024 07:56:49 -0700 (PDT) Content-Type: text/plain; charset=utf-8 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 \(3696.120.41.1.10\)) Subject: Re: [PHP-DEV] Pre-RFC Discussion: Support for String Literals as Object Properties and Named Parameters in PHP In-Reply-To: Date: Wed, 4 Sep 2024 10:56:48 -0400 Cc: PHP internals Content-Transfer-Encoding: quoted-printable Message-ID: References: <1C1BB102-EE71-49A6-A057-C727A39FC38F@newclarity.net> <3DFFDF2E-5C2F-4DEA-9C47-3830B09D9406@newclarity.net> To: Hammed Ajao X-Mailer: Apple Mail (2.3696.120.41.1.10) From: mike@newclarity.net (Mike Schinkel) > On Sep 2, 2024, at 11:10 PM, Hammed Ajao wrote: >=20 > That's one of the reasons, but arrays are not strict enough (you can = add anything to any array, no distinction between a list and map), and = you can't really specify the shape of an array. What do you mean by "shape of an array" in this context? =20 I am trying to understand how an object differs in "shape" and I am = coming up with nothing. > And/or do you want the compiler to validate when you use a name that = has not been declared, i.e. when you type "Content_Type" or "Content = Type" for you want an error message? >=20 > The cool thing is that the compiler already does this with named = parameters or the splat operator applied to an array = (https://3v4l.org/tholi#v8.3.11). That is quite interesting, I did not know that was possible. Unfortunately though, the validation is done at the point of call not at = the point of array instantiation which can result in spooky action at a = distance if use in more than trivial ways: https://3v4l.org/ZJXYX#v8.3.11 > Not at all, I just prefer custom and strict data structures with a = clear intention. +1=20 > I mean it is more work having to define a custom attribute and = reflecting It's target each time you want literal string names. The = compiler already does all I need, I just want to extend that support to = literal strings. > =20 > I'm proposing using a class every time you need a strict data = structure with the keys known ahead of time, trust me i have no issue = with more work when it's warranted, your version is just a lot of = unnecessary code that doesn't actually achieve that. Okay, I hear where you are coming from. > I'm proposing two things: > 1. Properties keys as literal strings: > ``` > class A { public array "key"; } > ``` > Since php also allows declaring properties in functions (promoted = properties), we have to decide what to do with: > ``` > class A { public function __construct(public array "key"); } > ``` > Which means we would run into cases where it's valid to pass a literal = string as a named parameter > ``` > $a =3D new A("key": 1); > ``` > I decided to add that to my proposal since there's no point = restricting it to only constructors, which brings us to: > 2. Literal strings as named parameters > Since I decided to include functions, we now have to decide what to = allow and what not, for example, can you now define params as strings = too: > ``` > function func(string "param", array "withDefault" =3D []) {} > ``` > or is it only allowed in variadic functions and func_get_args() (so = syntactic sugar for what we have now with func(...[])) That is very helpful to understand why you came to what you are = proposing. For me though, extending it in that way would potentially infect other = much more common use-cases so I would prefer to have a constrained data = type that allowed this rather than an open-ended one. And one that did = not perpetuate the requirement to use quotes for what are effectively = identifiers that do not follow the syntactical rules of identifiers. > Say I have a response class: > ``` > class Response {=20 > public function __construct(private MyHeaders $headers) {} > } > ``` > With MyHeaders being a class, nothing is stopping me from passing in: > ``` > class MyCustomHeaders extend MyHeaders { > public function __construct() { > parent::__construct(); > //unset one of the keys or set more > } > } > ``` > That isn't possible with the enum and I would have to store MyHeaders = as an array in the Response class. Fair point on the enum OTOH as for storing as an array in your Response class, I don't see how = that is huge issue. I do see how the array does not have a reflectable = identity, but stuff it in a single property Response class and then = together they do.=20 But I do get that you want to minimize that boilerplate. > > MyHeaders::create("Content-Type": "text/html; charset=3Dutf-8", = "X-Custom-Header": "value") >=20 > But it's clear, each key does what it intends, no additional = processing will be required to transform/validate the keys. It is only "clear" if you can get past all the quotes obscuring what is = a key and what it a value.=20 When I first saw your proposal I did not notice the colons and thought = you had written the following. If you read back at my original reply = you'll recognize that I misunderstood your example: MyHeaders::create("Content-Type", "text/html; charset=3Dutf-8", = "X-Custom-Header", "value") IOW, I find it too easy to misread and would not want to have to read = and/or maintain code that did not have a more easily visible distinction = between what goes on the left of the colon and what goes on the right, = remembering that reading and maintenance comprise far more time in the = life of working code than the initial development. =20 > MyHeaders::create(Content-Type: "text/html; charset=3Dutf-8", = X-Custom-Header: "value") >=20 > That works for me, but the literal has clearer intent and doesn't rely = on transformation. Clearer in your view, but not in mine. =20 Although I do concur that is requires a transformation I do not find = that to be problematic as the rules would need to be determinative and = would be easier to recognize key vs. value. Especially when reading = code written by those darn developers who do not write each key:value = pair on a new line of their own. ;-) It would be interesting to know who else on the list thinks it is clear = enough with the quote vs. those who would prefer a transform to avoid = having to use quotes? > Are you sure what you are proposing would _not_ end up having to use = reflection under the hood and be equally less performant? >=20 > Yep, it would be mostly handled in the parser/compiler, and = internally, named params are already represented as a php array so we = wouldn't even have to change much. In your example which that I copied below for reference, how will = `MyHeaders::create(...)` and `new self(...$headers)` work without = reflection? class MyHeaders { public function __construct( public string "Content-Type" =3D "application/json", public string "Cache-Control" =3D "no-cache, no-store, = must-revalidate", public string "Pragma" =3D "no-cache", public string "Expires" =3D "0", public string "X-Frame-Options" =3D "SAMEORIGIN", public string "X-XSS-Protection" =3D "1; mode=3Dblock", public string "X-Content-Type-Options" =3D "nosniff", public string "Referrer-Policy" =3D = "strict-origin-when-cross-origin", public string "Access-Control-Allow-Origin" =3D "*", public string "X-Custom-Header" =3D "value", ) {} public static function create(string ...$headers): self { return new self(...$headers); // Throws an error if an unknown = named parameter is passed } public function dispatch(): void { foreach ((array) $this as $name =3D> $value) { header("$name: $value"); } } } $headers =3D new MyHeaders("Content-Type": "application/json", = "X-Custom-Header": "value"); // or $headers =3D MyHeaders::create("Content-Type": "text/html; = charset=3Dutf-8", "X-Custom-Header": "value"); $headers->dispatch(); > For example, what if PHP had two new datatypes DataSet and DataPair = (or some other bikeshed names) that allowed for defining a set of = name-valued pairs, that could be optionally be close-end or open-ended, = that could be converted to arrays or stdClass objects, that could be = validated by the compiler for close-ended sets, that could be used to = specify named arguments for functions, that do not need to use quotes = for names, and probably other very flexible aspects? Wouldn't that be = better than shoehorning features onto existing classes to handle your = use-case? (DataSet and DataPair could be classes under the hood, just = like Enums are.) >=20 > I would love more data types but that doesn't solve this issue. I specifically crafted the idea of `DataSet` and `DataPair` to solve = your issues, at least as I understand them. Consider the following = hypothetical example: $value) {=20 header("$name: $value->current");=20 }=20 }=20 }=20 // Giving each header its own line so they are not so damn hard to = read...=20 $headers =3D new MyHeaders(=20 Content-Type: "application/json", X-Custom-Header: "value", );=20 // or=20 $headers =3D MyHeaders::create(=20 Content-Type: "text/html; charset=3Dutf-8", X-Custom-Header: "value", );=20 $headers->dispatch();=20 =20 As I am proposing, the above defines a `closed` dataset meaning only the = properties declared are valid. If declared as open-ended, keys assigned = to an instance would not need to be validated in a constructor. `datapair` properties and variables would be value objects with have a = `->current` property and an `->original` property where the default = value would gets assigned to `->original` and `->current`, but = `->current can be overwritten (or `->value` and `->default` if `default` = could be used in this context.)=20 Anything declared to be a `datapair` type could be declared to have = names that are not valid identifiers and end with whitespace.=20 A declared `datapair` inner type would default to `string` but could be = declared otherwise if needed, see how I declared `Expires` as `int`. = However, it would be good if they could always just be `string` and = never anything else. By their nature, any dataset properties or variables would need to be = accessed with braces, e.g. `$this->{Content-Type}` and `${Content-Type}` = or if without quotes is not possible in the parser then by = `$this->{'Content-Type'}` and `${'Content-Type'}` Given the above =E2=80=94 assuming it were accepted and added as a = feature to PHP =E2=80=94 how would this not address your use-case(s)? Note I have no idea if the above would even be considered, but I am = proposing here to understand what alternatives may be to address your = desired use-case(s). > Good call, I should probably separate both proposals.=20 +1=20 -Mike