design patterns – Apply a value object (typed string) in PHP

What is the best way to prevent erroneous states in an object? That's the source of my question.

Basically, my curiosity began with the intention of not letting a class exist with bad values. Prevent programmers from making errors in class implementations and extensions.

I did not want a class to even care about having to deal with bad value. I just wanted it not to be started if a bad value of a given status or type was passed on.

I work a lot with string types, because of a legacy code request. For that, I find it interesting to work with "valuables". This is apparently the main design idea. I've tried doing something with very tight characters (another new term that I just discovered).

So below, I have the first example. The scenario is as follows: A class needs a type and instead calls a string "type_x". invokes a class and this class resolves the value if it is valid. This class is the one we will see below.

/ **
* StringlyTypeSecondOption
* /
StringlyTypeFirstOption class
{
private type $;

public static function type_1 ()
{
return a new me (& # 39; type_1 & # 39;);
}

public static function type_2 ()
{
return a new me (& # 39; type_2 & # 39;);
}

__construct private function (string $ type)
{
$ this-> type = $ type;
}

public service __toString ()
{
returns $ this-> type;
}
}

echo StringlyTypeFirstOption :: type_2 (); // here it's ok

echo StringlyTypeFirstOption :: type_3 (); // here we have an error because type_3 does not exist

This is a very good example because we have no exception or audit logic. Is it alone? And I think that's good.

And now we have the second example. Will provide a solution for the same problem that I proposed.

StringlyTypeSecondOption class
{
private type $;

const TYPE1 = & # 39; type_1 & # 39 ;;
const TYPE2 = & # 39; type_2 & # 39 ;;

Private Const ALLOWED_TYPES = [StringlyTypeSecondOption::TYPE1, StringlyTypeSecondOption::TYPE2];

public static function factory ($ type)
{
if (! in_array ($ type, StringlyTypeSecondOption :: ALLOWED_TYPES, true)) {
throw a new exception ("invalid type: {$ type}");
}

return a new self ($ type);
}

__construct private function (string $ type)
{
$ this-> type = $ type;
}

public service __toString ()
{
returns $ this-> type;
}
}

echo StringlyTypeSecondOption :: factory (type_2); // here it's ok

echo StringlyTypeSecondOption :: factory (-3_type); // we have an exception because type_3 does not exist

This is a very good example too, but I already have some logic and is not as pure as the first. But solve the problem as a charm too.

Both regulations have strengths and weaknesses (I think).
But if there is a consolidated design that corrects the allowed values ​​for a state of a class, how is its name implemented, and what is the best optimized strategy for preventing an invalid value in an object?

I think this is no longer a discussion of an exact solution. If this is not the right place, I ask moderators to point me to a better channel.

Thanks in advance!