Can we have custom variable types, from classes?

  • This topic has 8 replies, 1 voice, and was last updated 1 week, 1 day ago by przemo_li.
Viewing 9 posts - 1 through 9 (of 9 total)
  • Author
    Posts
  • #5524
    Annh1234
    Participant

    Would having custom variable types which get their functionality from some class be useful?

    **Example:**

    class Foo {
    private $data;

    # called automatically when something is type casted to Foo
    public function __construct($data = ‘default’) {
    ($value !== 1) throw new InvalidArgumentException(“Must be 1”);
    $this->data = $value;
    }

    # some magic getter
    public function &__getValue() {
    return $this->data;
    }
    }

    class Bar {
    public Foo $v;
    }

    $b = new Bar();
    $b->v = 1; # Since Bar->$v is Foo, auto cast/call constructor.
    echo $b-v; # 1
    $b->v = 2; # InvalidArgumentException(“Must be 1”)
    echo $b->v::class; # Foo

    With the union types of PHP 8, being able to have custom object types like this would mean that

    – our DTOs can have the business format validation rules built in, and would simplify allot dealing with JSON/DB data.
    – if we json_decode a string, it can create a tree structure of different classes, automatically validated.
    – we can have immutable/readonly objects without that `readonly` rfc extra tag.

    ​

    ​

    I’m not sure how hard would be to implement in the PHP source, but I think this would require two changes.

    #1 type casting [something] to whatever that variable is defined as.
    – Will have the same issued as `public int|string $foo;` & `public int|string $foo;` where if `$foo = 1;` will it be an `int` or a `string`. (it could take the first that can match)

    #2 having a magic getter `public function &__getValue() {` to get the data without explicitly calling some function.
    – We can do it now with echo `$b-v->getValue()` and `$b->v->setValue()`, but the code is messy.

    [View Poll](https://www.reddit.com/poll/i8zusm)

    #5525
    __radmen
    Guest

    My first thought – hey, I like it!
    After the second thought – not really.

    This looks appealing as you can easily wrap primitives in some custom values. The problem I have – this makes impossible to read the code without knowledge of each class.

    Consider this example:

    “`php
    $foo = new Foo();
    $foo->counter = 1;

    $bar = $foo->counter + 1;
    “`

    This expression throws a fatal error. Why? Because the counter was implicitly cast to a custom object. This is confusing.

    Now, this can get even worse. Consider using `Foo` in many places and taking advantage of this implicit cast. Now, someone decides to change the type of `counter` (or remove it). Everything falls apart.

    #5526
    czbz
    Guest

    I don’t think I’d like this. From just reading the code of `Bar` there would be no way to tell that `$b->v = 1;` would be legal. And if you go the opposite way and read `$b->v = 1;` and want to find out what it does, you’d have to first read `Bar` to find out that `v` is typed as `Foo`, and then read the `Foo` class to see that it accepts `1`.

    If `Foo` changes, e.g. the magic constructor is deleted, then the PHP script would break even though it doesn’t make any mention of `Foo`.

    #5527
    sproingie
    Guest

    Looks a lot like Scala’s implicit classes: a powerful weapon, but an equally-powerful footgun. An _actual_ use case not using metasyntax would go a long way toward justifying this. I know you list them below, but I want to see code.

    Also, too late to change now, but I suggest adding a “maybe” option to your next poll. The idea is reasonable, but likely not feasible in PHP with its gradual type system. That plus lack of a real-world example made me hit “no”.

    #5528
    TheVenetianMask
    Guest

    I think you can already use __set and __get on inaccessible properties and do some magic to handle it.

    Please don’t.

    #5529
    SaraMG
    Guest

    A related thought.

    class Foo implements CastableFrom, CastableTo {
    static public function __castFrom($var): Foo {
    // Factory method allows custom construction
    return new Foo(123. $var, 456);
    }
    public function __castTo(string $typeName) {
    return somethngOfTheRightType;
    }
    }

    This could respond to explicit casts like `$foo = (Foo)$bar;` or `$bar = (int)$foo;`

    And in `strict_types=0` mode, we could apply it to assigning to a typed prop, or passing as an arg as well.

    Biggest question is, “Who wins when both sides of the cast have appropriate handlers?” I’d vote for the castFrom handler, but arguments could go either way.

    #5530
    slepicoid
    Guest

    Something like this maybe?
    https://github.com/slepic/php-value-object

    It gets a bit expensive for big data tho…

    #5532
    Huliek
    Guest

    Scala was already mentioned.

    C++ does this by default unless you mark the constructor `explicit`. It is generally regarded as a bad default.

    #5533
    przemo_li
    Guest

    Nope.

    We need less implicit type conversion & coercion in PHP, not more.

    ​

    Truth tables are slowly getting fixed in consecutive PHP releases. Lets keep it up!

Viewing 9 posts - 1 through 9 (of 9 total)
  • You must be logged in to reply to this topic.