Could be handy to allow this in normal functions too (but this it not
the main use case and could be disallowed):

public static function get(string $name, mixed $default = uninitialized): string
{
    $value = getenv($name);
    if ($value === false) {
        return $default; // throws uninitialized error.
    }
    return $value;
}

Not specifying a default value can now be done without creating a
class or 'some highly specific string to avoid conflicts' to check for
it just throws an error when it cant return a value.

However, the uninitialized error is too generic for this situation and
youd want a more specific error message.

This would naturally make you think to check for uninitialized instead
of using a try->catch block.

To avoid treating it as a type of value with === a helper function
could be provided changing the function like so:

public static function get(string $name, mixed $default = uninitialized): string
{
    $value = getenv($name);
    if ($value === false) {
        if (isInitialized($default)) return $default;        throw new
\Exception("Environment variable '$name' not found.");
    }
    return $value;
}


On Tue, Jun 3, 2025 at 2:22 PM Bradley Hayes <bradley.ha...@tithe.ly> wrote:

> 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.
>
>
> --
>
> Bradley Hayes / Engineer / TITHE.LY <http://tithe.ly/>
>
> <http://tithe.ly>
>
>

-- 

Bradley Hayes / Engineer / TITHE.LY <http://tithe.ly/>

<http://tithe.ly>

Reply via email to