On Thu, 13 Jan 2022 at 15:36, Andreas Hennings <andr...@dqxtech.net> wrote:
>
> Hello list,
> I want to bring up a topic that has bothered me whenever I use traits.
> I don't have a solution proposal for it, yet, unfortunately.
> I was going to comment in the other thread about traits, but it seems
> better suited for a new discussion.
>
> ----------
>
> Traits allow to share code between classes of different inheritance chains.
> They can be used instead of composition, or they can be used to help
> with composition - e.g. a trait can expose functionality from a
> specific injected dependency.
>
> ----------
>
> When using base classes, we can follow a convention to always call the
> parent constructor.
> We can even make the properties in the base class private, to fully
> encapsulate them.
>
> But:
> When using properties in traits, how can I make sure that they are
> properly initialized in the class constructor?

I think I have a solution:
abstract properties in traits!

The idea would be:
- traits can have abstract properties that are private, protected or public.
- non-abstract classes cannot have abstract properties.
- class properties override trait properties, with some compatibility
requirements.
- non-abstract protected or public class properties from the parent
class also override trait properties.
- (optional) abstract classes can have abstract properties that are
protected or public.
- (optional) abstract class properties from a parent class are
overridden by the trait property, but with compatibility checks.

This implies that abstract trait properties _must_ be redeclared in
the class that uses the trait,

interface X {}

interface XHaving {
  public function getX(): X;
}

trait T {
  abstract private X $x;
  public function getX(): X {return $this->x;}
}

class C implements XHaving {
  use T;
  public function __construct(
    private X $x,
  ) {}
}

class D implements XHaving {
  use T;  // Error, must redeclare abstract property T::$x.
}

The benefit:
Properties are initialized in the same file where they are declared.

I don't know if we need aliasing for properties, perhaps we should
first go without that.

I did find a discussion about abstract properties in externals.io, but
this was for interfaces and classes, not for traits.
https://externals.io/message/64126#66682

>
> Also, what if I want to provide an init method with specific logic to
> set that property? How can I make sure that method will be called in
> the constructor?

This part would not be solved by the abstract properties.
But I think that's ok.

>
> I found that Psalm can detect "PropertyNotSetInConstructor", which is
> also applied to properties from traits.
> But this is not as straightforward as calling a parent constructor.
>
> Can and should we provide a language-level solution for this?
> Or should this be left to static analysis tools and IDEs?
>
> Cheers,
> Andreas

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php

Reply via email to