-
Notifications
You must be signed in to change notification settings - Fork 7.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Explicit undefined/uninitialized default value for constructor property promotion #17771
Comments
A trait can do it. "Language-assisted copy-and-paste" is pretty much literally what you want here. Or maybe I'm missing something? I don't know if it's just what you typed up there, but this $this->${$key} = $val; //causes error weather the property exists or not. isn't the right syntax. That will look for the |
Omg your right! I must have screwed it up when I tested trait usage before. |
Actually, I still think there would be good merit in having I'll edit the issue. |
Hey, @b-hayes. It can be done in userland like this: <?php
final class uninitialized
{
private function __construct() {}
public static function instance(): self
{
return new self();
}
}
define('uninitialized', uninitialized::instance());
class C1
{
public uninitialized|string $prop1 = uninitialized;
}
$o1 = new C1();
var_dump($o1->prop1, $o1->prop1 === uninitialized); |
Yeah I did think of that @erickcomp but then you loose the advantages of uninitialized properties. |
IMO, uninitialized properties after the object construction are pretty much always a bad idea. They introduce a hidden value into your type set that the user never knows to check for. It's much better to be explicit. Adding an explicit |
Your perspective is the right one when objects are meant to be a complete or having no value is fine. But in this situation all data points are optional id say its better practice to run into the uninitialized error. Manually checking the data type and throwing an error vs expecting the only data types the value can be hold and hitting an error is the same and id argue that Object with public properties with no constructor were always like this and can serve this purpose but immutability is desirable and that cant be done without a constructor. |
If you want optional constructor params, you can use a named parameters constructor, like this: <?php
trait HasConstructorWithOptionalParameters {
public function __construct (...$args) {
foreach ($args as $prop => $value) {
if (!\is_string($prop)) {
throw new \LogicException('Method [' . static::class . '::__construct] must be used with named parameters');
}
if (!\property_exists($this,$prop)) {
throw new \RuntimeException("Property [$prop] does not exist on class [" . static::class . ']');
}
$this->$prop = $value;
}
}
}
readonly class DtoClass {
use HasConstructorWithOptionalParameters;
public string $id;
public string $name;
}
var_dump(new DtoClass(id: "123", name: "Test"), new DtoClass(...['id' => '456', 'name' => 'Test2']));
$o = new DtoClass(id: '789');
var_dump($o->name); In this case I did not implement positional arguments, but it's possible using reflection. I actually had to do something similar for one project. |
In reality, there is no such thing as "no value". But as mentioned, this is just my opinion. Please start a discussion on the mailing list if you think this is a worthwhile feature. Otherwise, the issue will go stale and auto-close in 3 months. |
Description
There is currently no way to truly make constructor parameters optional forcing workarounds like this:
The use case for this is the fact that we need to allow properties to be uninitialized for a GraphQL like situation.
You cant use fn arguments because that forces you to have a default value to be optional.
Null is considered a real value in the database and many other aspects of the code and is different from a "missing value".
Uninitialized vars are easily automatically ignored with for each loops and thus avoids a great deal of code needed to simply not return the keys you never asked for in an API response. Related data for things you didnt ask for are not fetched from the database, data calls return only the relevant data AS the property name they will be assigned to in the class making all of this incredibly streamlined.
The only thing thats missing is immutability and adding readonly will prevent the constructor form working.
Getters with setters would defeat the purpose, and copying the same code to hundreds of classes is far from ideal.
Traits can work for reusable code but the base class will still have to enforce some kind of check to make sure that every child class is using the trait. (this is going to be my current work around after damianwadley pointed this out below).
It would be great if there was some way to explicitly indicate an optional parameter without assuming a default value for a nice clean readonly class that allows properties to not have a value.
Like so:
Or perhaps introduce an
optional
keyword in front of the param, but I think uninitialized as a keyword would potentially become useful for other senarios.The text was updated successfully, but these errors were encountered: