38

I know you can override a trait method by declaring it in your class, I was curious if was possible to over ride a trait Property the same way. Is this safe to do? Its not in the Documentation so I am hesitant to implement this.

From the Documentation

An inherited member from a base class is overridden by a member inserted by a Trait. The precedence order is that members from the current class override Trait methods, which in turn override inherited methods.

http://php.net/manual/en/language.oop5.traits.php

DanHabib
  • 824
  • 4
  • 15
  • 25
  • 1
    A member can be a property or a method, so I would assume so yes. Doctrine uses the DocBlock when looking at entity properties so I would assume that is overriden. The best thing to do would be to just try. Create an entity using traits, use Doctrine's schema tool to see what the SQL would be, then override with a different DocBlock and see what happens (don't forget to clear cache if you're using one). – DanielM Sep 21 '15 at 20:59

4 Answers4

66

You cannot override a trait's property in the class where the trait is used. However, you can override a trait's property in a class that extends the class where the trait is used. For example:

trait ExampleTrait
{
    protected $someProperty = 'foo';
}

abstract class ParentClass
{
    use ExampleTrait;
}

class ChildClass extends ParentClass
{
    protected $someProperty = 'bar';
}
Mathew Tinsley
  • 6,805
  • 2
  • 27
  • 37
  • 30
    I'm curious, what is the logic behind this ? *A son won't suffer punishment for the father's iniquity..* – Teoman Tıngır Oct 04 '18 at 03:32
  • Not a problem but an annoyance: In PHP5.6 it triggers a STRICT [E_STRICT][2048] Notice. The notice will explain that the Class and the Trait are defining the same property. It will also say the following: "This might be incompatible, to improve maintainability consider using accessor methods in traits instead. Class was composed.", which is funny because it is exactly the contrary of what PHP now tells us to do handle it. :) – Julio Marchi Apr 22 '19 at 03:24
  • 16
    Sometimes I think that the creators of PHP are trolling me. – keyboardSmasher Nov 16 '19 at 18:35
  • 8
    The reason, if anyone still wonders, is that traits are simply copy-paste. You can't define the same property twice in a class, but you can override it in children classes - whenever you wonder about trait strangeness, think about how the code would work if you'd simply replace the `use Trait` with the code verbatim, and it all makes sense again. It's really nothing else. – Moritz Friedrich Nov 25 '21 at 14:35
  • 3
    @MoritzFriedrich That's what should make sense, but it doesn't. "_traits are simply copy-paste_" - correct, but they allow pasting _methods_ over the class's concrete methods _and_ provide a mechanism to resolve conflicts when needed. Why shouldn't properties have the same privilege? i tend to lean toward the troll theory from the earlier comment. – cautionbug Sep 09 '22 at 18:24
37

My solution was to use the constructor, example:

trait ExampleTrait
{
    protected $someProperty = 'foo';
}

class MyClass
{
    use ExampleTrait;

    public function __construct()
    {
         $this->someProperty = 'OtherValue';
    }
}
pedro.caicedo.dev
  • 2,269
  • 2
  • 16
  • 19
  • @siannone Do not use static properties, it is a bad design pattern. – Yevgeniy Afanasyev Nov 26 '19 at 23:51
  • 1
    Using the constructor was a very valid solution and this worked for me. – mchljams Apr 10 '20 at 04:07
  • Unfortunately, this won't help with static properties. In one instance, I had to define a custom method `__include()` to change the static trait properties, and call it inside the `autoloader` when the class was accessed. – akinuri Apr 03 '21 at 15:02
8

An alternative solution, in this case using the property updatable.

I use this when the property is only required within the trait's methods...

trait MyTrait
{
    public function getUpdatableProperty()
    {
        return isset($this->my_trait_updatable) ?
            $this->my_trait_updatable:
            'default';
    }
}

...and using the trait in a class.

class MyClass
{
    use MyTrait;

    /**
     * If you need to override the default value, define it here...
     */
    protected $my_trait_updatable = 'overridden';
}
Steve
  • 1,853
  • 16
  • 30
-8

You can declare a trait property in a class but you must keep the same definition from the trait. It couldn't be overridden with different definition. So, as you already has access to trait properties from class, it's not needed to redefined again. Think that a trait works as a copy paste code.

<?php
trait FooTrait 
{
    protected $same       = '123';
    protected $mismatch  = 'trait';
}

class FooClass 
{
    protected $same      = '123';

    // This override property produces: 
    // PHP Fatal error:  FooClass and FooTrait define the same property
    // ($mismatchValue) in the composition of FooClass. However, the definition
    // differs and is considered incompatible
    protected $mismatch  = 'class';

    use FooTrait;
}
martinezdelariva
  • 5,011
  • 2
  • 25
  • 30
  • 8
    Terrible answer, first the contradiction "you can override, but you can't" Make up your mind. Secondly the example does not explain anything we didn't know already, nor does it give a practical solution. Better answer here http://stackoverflow.com/a/20437742/208946 – goyote Dec 22 '15 at 06:01
  • @goyote thanks for the link. I've replaced the word "override" by "declare" and explicitly say that couldn't be overridden with different definition. I hope this help you. – martinezdelariva Dec 22 '15 at 08:48
  • @martinezdelariva, sorry, it is not helping. – Yevgeniy Afanasyev Nov 26 '19 at 23:53
  • 3
    This answer totally correct. The problem is not with the answer. See: https://www.php.net/manual/en/language.oop5.traits.php: "f a trait defines a property then a class can not define a property with the same name unless it is compatible (same visibility and initial value), otherwise a fatal error is issued." – Zoltán Fekete Jun 26 '20 at 12:24