> On Sep 2, 2024, at 1:48 PM, Hammed Ajao <hamieg...@gmail.com> wrote:
>
> Hello Mike,
>
> Thanks for reading and responding. While your enum does technically address
> some of the issues I am discussing, it does so in a very verbose and
> cumbersome way.
> - First you need the DefaultValue attribute
> - Then you need to use it on each of your `properties`
> - You also need to define an identifier for the const which defeats the
> purpose of using literal strings
> - Then you need to use reflection to access the properties
> - Then you're left with : MyHeaders::create(MyHeaders::CONTENT_TYPE,
> "plain/text", MyHeaders::X_CUSTOM_HEADER,"new_value"); which isn't super
> clear that it's mapping every other value to the one before it.
That is why I wrote that maybe we could consider reducing the verbosity and
more cumbersome aspects as I proactively recognized it was painful.
That is why I asked about other use-cases. If the only solid use-case is HTTP
headers we write it once and its done.
OTOH, if there are many other use-cases then we could look at adding syntax
sugar. Adding syntax sugar to languages when boilerplate patterns emerge has
many benefits: fewer bugs, easier coding, making the language more powerful,
and etc.
> As for the idea that an object property must be a valid identifier, I'm not
> sure that's correct. Objects can have integer properties natively i.e
> $obj->{1}, and literal string properties are already possible when you cast
> an array to an object
Yes, that was pointed out to me.
I was thinking more about the PHP language for language parsers syntax when I
wrote that and thinking less about the fact that instances can have dynamically
assigned properties. My bad.
> Let me show you a few other use cases i've run into
>
> ```php
>
> class Sizes {
> public function __construct(
> private string $sm,
> private string $md,
> private string $lg,
> private string $xl,
> private string $2xl, // will throw
> private string "10xl", // wouldn't this be nice
> ) {}
> }
Is that really a class that someone would write, with a different property for
each size and a constructor that required setting each size? Or wouldn't they
typically have an Enum for the sizes and an array property indexed by the enum?
Maybe your example was just to illustrate your desire for a `$2xl` and `"10xl"`
property? I see what you want here, OTOH I would not be a fan. I'm just as
happy to name them `$twoXL` and `$tenXL`, or `$_2xl` and `$_10xl` vs. dealing
with non-identifiers in declared property names. I get that is not what you
want, but realize I am not the decider, I am just representing one person's
opinion, and I can't even vote on RFCs, so there is that. ¯\_(ツ)_/¯
> function htmlElement(string $tag, string $content, string|bool
> ...$attr) {}
> htmlElement("div", "Hello, world!", class: "greeting", id:
> "greeting", "data-foo": "bar");
I did not grasp the use-case you are showing here because the one in your
original email included using non-identifiers as parameter names so I conflated
your two proposals as being one.
In the case you presenting here —assuming I understand what you are proposing —
I would likely be in favor of something like it. That is if it were orthogonal
and not requiring of your first proposal i.e. in its own RFC.
Taking what you have I would expect something more like the following but where
`[HtmlAttribute]` is my placeholder for a syntax that would specify that zero
or more named parameters be collected and assigned by property name to the
object `$attr` of type `HtmlAttribute` — bikeshed away on that syntax —, and to
assign to arrays you specify the array index in the named argument, e.g.
`data["foo"]`:
class HtmlAttribute {
public string $_class;
public string $id;
public array $data;
}
function htmlElement(string $tag, string $content, [HtmlAttribute] ...$attr) {}
htmlElement("div", "Hello, world!", class: "greeting", id: "greeting",
data["foo"]: "bar");
Alternately if you were not envisioning the mapping to an object a similar
feature could just be way to use a function to collect arbitrarily names
parameters into an array, e.g.:
function htmlAttributes(string|int ...$attr) {}
htmlAttributes(class: "greeting", id: "greeting", data["foo"]: "bar");
The above would collect the args into an array `$attr` shaped like this:
array(
"class" => "greeting",
"id" => "greeting",
"data" => array(
"foo" => "greeting",
),
);
Actually, it would be nice if PHP offered both options.
> - Then you're left with : MyHeaders::create(MyHeaders::CONTENT_TYPE,
> "plain/text", MyHeaders::X_CUSTOM_HEADER,"new_value"); which isn't super
> clear that it's mapping every other value to the one before it.
BTW: it could currently be written like any of the following:
// 2-element arrays represents name-value pairs
public static function create(string ...$headers): self {...}
MyHeaders::create(
[MyHeaders::CONTENT_TYPE, "plain/text"],
[MyHeaders::X_CUSTOM_HEADER,"new_value"]
)
// 1-element arrays w/key and value represents name-value pairs
public static function create(string ...$headers): self {...}
MyHeaders::create(
[MyHeaders::CONTENT_TYPE => "plain/text"],
[MyHeaders::X_CUSTOM_HEADER =>"new_value"]
)
// 1-element array w/multiple elements represents name-value pairs
public static function create(array $headers): self {...}
MyHeaders::create([
MyHeaders::CONTENT_TYPE => "plain/text",
MyHeaders::X_CUSTOM_HEADER =>"new_value"
])
Alternately imagine some syntax sugar that allows dropping the `MyHeaders::`
from the enum reference when a parameter is declared to be an instance of that
enum:
// Objects represents name-value pairs
Header(MyHeaders $header, string $name):array {...}
public static function create(Header ...$headers): self {...}
MyHeaders::create(
Header(CONTENT_TYPE, "plain/text"),
Header(X_CUSTOM_HEADER, "new_value")
)
Finally imagine adding a feature that allows specifying that a parameter is the
result of a function call give brace enclosed parameters where `Header(*)` is
my placeholder syntax for bikeshedding, e.g.:
// Objects represents name-value pairs
public static function create(Header(*) ...$headers): self {...}
MyHeaders::create(
{ CONTENT_TYPE, "plain/text" },
{ X_CUSTOM_HEADER, "new_value"}
)
#fwiw
-Mike