On Thu, Jun 5, 2025 at 10:41 AM Faizan Akram Dar <he...@faizanakram.me>
wrote:

>
>
> On Thu, 5 Jun 2025, 13:56 Bradley Hayes, <bradley.ha...@tithe.ly> wrote:
>
>> Here is another way to think about it, trying to make the arguments
>> optional not have a new value type.
>>
>> Optional arguments currently requires a default value.
>>
>> Assigning values to properties is truly optional but we cant reflect this
>> in the constructor parameters.
>>
>> class DTO {
>>     public function __construct(
>>         public string $id,
>>         optional public string $name,
>>         optional public null|int $age,
>>     ) {}
>> }
>> new DTO('some-id');
>> new DTO(...['id' => 'some-id']); // no error for missing keys for named 
>> arguments
>>
>>
>
> Hi Bradley,
>
>
> You can already achieve this, just don’t add those properties to the
> constructor.
>
> Yes, static analysis tools might complain, but that’s fine if your
> intention is to leave some properties uninitialized after construction.
>
> The constructor is, by definition, for properties that must be initialized
> during object creation.
>
>
> Kind regards,
> Faizan
>
>
>
>> On Thu, Jun 5, 2025 at 2:33 PM Bradley Hayes <bradley.ha...@tithe.ly>
>> wrote:
>>
>>> Hey Claude, i did think of the same thing you proposed and I cover that
>>> in the gihub issue I linked to. This doesnt replicate the uninitialized
>>> state.
>>>
>>> Doing ti with custom classes or enums means now not only are forced to
>>> manually check each property everywhere it might be used you also have to
>>> write thousands of checks and throw errors across all the constructors of
>>> these objects.
>>>
>>> When properties have an uninitialized state you remove the need to
>>> handle any of this when loops and serializers simply exclude them.
>>>
>>> Uninitialized states is already an incredibly useful feature that exists
>>> right now.
>>>
>>> Im only proposing that we have a way to tell the constructors to ignore
>>> the parameter instead of being forced to have array as the only parameter.
>>>
>>> I get what you mean about null, but null is serving a different purpose.
>>> Its a value that represents nothing, so that you can assign something as
>>> nothing.
>>>
>>> Uninitialized is not like assigning null to a variable.
>>>
>>> // This implementation:
>>> class DTO {
>>>     public function __construct(
>>>         public string $id = uninitialized,
>>>         public string $name = uninitialized,
>>>         public null|int $age = uninitialized,
>>>     ) {}
>>> }
>>> new DTO('some-id');
>>> // Would produce the same result as this one...
>>> class DTO {
>>>     public function __construct(array $parameters) {
>>>         foreach ($parameters as $key => $value) {
>>>             $this->{$key} = $value;
>>>         }
>>>     }
>>> }
>>> new DTO(['id' => 'some-id']);
>>>
>>>
>>> On Wed, Jun 4, 2025 at 7:11 PM Claude Pache <claude.pa...@gmail.com>
>>> wrote:
>>>
>>>>
>>>>
>>>> Le 3 juin 2025 à 06:22, Bradley Hayes <bradley.ha...@tithe.ly> a écrit
>>>> :
>>>>
>>>> Uninitialized properties are really useful.
>>>> Being skipped in foreach loops and JSON encoded results and other
>>>> behaviours around uninitialized properties save a lot of time wasted on
>>>> basic checks and uncaught logical mistakes around null values.
>>>>
>>>> With the introduction of named arguments and promoted constructor
>>>> properties and read-only classes, it would be great to have the true
>>>> ability to not specify a value.
>>>>
>>>> class DTO {
>>>>     public function __construct(
>>>>         public string $id = uninitialized,
>>>>         public string $name = uninitialized,
>>>>         public null|int $age = uninitialized,
>>>>     ) {}
>>>> }
>>>>
>>>> $dto = new DTO(id: 'someid', age: null);
>>>> if ($dto->age === null) echo "no age was given\n";
>>>> echo  $dto->name, PHP_EOL; // triggers the standard access before 
>>>> initialisation error
>>>>
>>>>
>>>> EXAMPLE: A graphQL like API that only returns data that was asked for,
>>>> is serviced by a PHP class that only fetched the data that was asked for
>>>> and thus the DTO only has assigned values if they were fetched.
>>>> (These situations usually way more complex involving multiple SQL
>>>> joins/filters etc and nested objects/arrays in the return DTO).
>>>>
>>>> The DTO object has all the possible values defined on the class for
>>>> type safety and IDE indexing, but allows the uninitialized error to happen
>>>> if you try to use data that was never requested.
>>>> Uninitialized Errors when directly accessing a property that was not
>>>> assigned is also desirable as it indicates a logical error instead of
>>>> thinking the value is null. Null is considered a real value in the database
>>>> in countless situations and API can assign null to delete a value from an
>>>> object.
>>>>
>>>> Additionally, since array unpacking now directly maps to named arguments 
>>>> this would also save a ton of mapping code.
>>>>
>>>> *//array unpacking direct from the source
>>>> *$dto = new DTO( ...$sqlData);
>>>>
>>>> (FYI: SQL is way faster at mapping thousands of values to the naming
>>>> convention of the class than doing it in php so we do it in SQL. So yes we
>>>> would directly array unpack an sql result here.)
>>>>
>>>> I have is a discussion on this in github here:
>>>> https://github.com/php/php-src/issues/17771
>>>>
>>>> The current workaround is to make the constructor take an array as its
>>>> only parameter and looping over it assigning matching array key values to
>>>> class properties and ignoring the rest.
>>>>
>>>> This works but breaks indexing and prevents the use of class
>>>> inheritance because not all the properties can be seen from the same scope
>>>> forcing every extender of the class to copy paste the constructor code from
>>>> the parent class.
>>>>
>>>>
>>>>
>>>>
>>>> Hi Bradley,
>>>>
>>>> Originally, `null` was intended to mean “no value”. Today, `null` is a
>>>> value in itself, and there has been a necessity to have something else to
>>>> encode an uninitialised state, meaning “really, no value”. Although I
>>>> understand your specific use case, I don’t think that it is good long term
>>>> design decision to rely on various built-in variations of general “no
>>>> value” states: maybe tomorrow there will be a request for some “really and
>>>> truly, no value” state? Instead, I think one should use
>>>> application-specific states. With enums and union types, it is possible:
>>>>
>>>> ```php
>>>> enum DTO_status {
>>>>     case uninitialized;
>>>>     case deleted;
>>>> }
>>>>
>>>>
>>>> class DTO {
>>>>
>>>>     function __construct(
>>>>         public int|DTO_status $id = DTO_status::uninitialized
>>>>       , public string|DTO_status $name = DTO_status::uninitialized
>>>>       , public int|null|DTO_status $age = DTO_status::uninitialized
>>>>     ) { }
>>>>
>>>> }
>>>> ```
>>>>
>>>> Or, if you want to rely on the handy error “must not be accessed before
>>>> initialization” for free, you could also write:
>>>>
>>>> ```php
>>>> class DTO {
>>>>
>>>>     public int $id;
>>>>     public string $name;
>>>>     public int|null $age;
>>>>
>>>>     function __construct(
>>>>         int|DTO_status $id = DTO_status::uninitialized
>>>>       , string|DTO_status $name = DTO_status::uninitialized
>>>>       , int|null|DTO_status $age = DTO_status::uninitialized
>>>>     ) {
>>>>         foreach ([ 'id', 'name', 'age' ] as $var) {
>>>>             if (! ${$var} instanceof DTO_status) {
>>>>                 $this->$var = ${$var};
>>>>             }
>>>>         }
>>>>
>>>>     }
>>>>
>>>> }
>>>> ```
>>>>
>>>> With property hooks, you can support more elaborate things such as
>>>> `$foo->id = DTO_status::deleted`, although you cannot (and should not) rely
>>>> on the built-in “must not be accessed before initialization” error anymore,
>>>> because you cannot (and are not supposed to) return to the uninitialised
>>>> state: you have to manually throw the appropriate error in the getter.
>>>>
>>>> —Claude
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>
>>> --
>>>
>>> Bradley Hayes / Engineer / TITHE.LY <http://tithe.ly/>
>>>
>>> <http://tithe.ly>
>>>
>>>
>>
>> --
>>
>> Bradley Hayes / Engineer / TITHE.LY <http://tithe.ly/>
>>
>> <http://tithe.ly>
>>
>>
Hi all!
This discussion seems related to the one that I started last year:
https://news-web.php.net/php.internals/123338
Maybe you can find some additional feedback in that discussion.

Regards,
Luigi

Reply via email to