1

It is useful that methods defined inside traits have an access to common scope of properties like methods defined in the same object. However is there any way that two traits could use the same property name but each of the property would be in a different scope so setTwo() would not overwrite $this->data of trait one and vice-versa?

example:

<?php

namespace one;

trait one {

    public function setOne($data) {
        $this->data = $data;
    }

    public function getOne()
    {
        return $this->data;
    }
}

namespace two;

trait two {

    public function setTwo($data) {
        $this->data = $data;
    }

    public function getTwo()
    {
        return $this->data;
    }
}

namespace bar;

class Bar {
    use \one\one;
    use \two\two;
}

$bar = new Bar;

$bar->setOne(1);
$bar->setTwo(2);

echo "Bar getOne: " . $bar->getOne() . '<br>' . PHP_EOL; // echoes 2 instead of wanted 1
echo "Bar getTwo: " . $bar->getTwo() . '<br>' . PHP_EOL; // overwrites $this->data = 1 with 2
echo "Bar->data: " . $bar->data . '<br>' .  PHP_EOL;

The reason why I'm looking for the separate scope is that for common properties' names like: sum, result, cache you may end up unintentionally overwriting them at some point when composing a class of many traits.

The only thing that comes to my mind is to use an intermediate class (that has it own namespace and by that its own scope) composed from traits and pass it as dependency to other class that needs some functionalities of the traits used for the intermediary composition. In the example as above I would need two intermediary classes - one with trait one, the other with trait two.

Are there any other options, besides the intermediate class or changing in every trait $this->data to more custom names like $this->dataOne and $this->dataTwo?

Jimmix
  • 5,644
  • 6
  • 44
  • 71
  • Does this answer your question? [PHP Traits: How to resolve a property name conflict?](https://stackoverflow.com/questions/41679384/php-traits-how-to-resolve-a-property-name-conflict). In short: not possible without making sure the property names are different to begin with (some kind of prefix that is unique to each trait could work). I'd add that using traits is often considered bad practice, I'd suggest taking a look at [this question](https://stackoverflow.com/questions/7892749/traits-in-php-any-real-world-examples-best-practices). – Jeto Jan 24 '20 at 18:10
  • @Jeto Unlucky not, this is a conflict resolving technique that eliminates one of the trait's method but my case is when both traits' methods are needed but they use properties with the same name so one trait's method overwrites the property of the other unintentionally. Hence a need of creating dedicated scope for each of the traits. – Jimmix Jan 24 '20 at 18:26
  • Yeah I'm not exactly sure what point that answer is attempting to make with that sample, actually. I'll revoke the close vote, even though I doubt someone will magically find something. – Jeto Jan 24 '20 at 18:35
  • @Jeto I had a humble hope that perhaps if using namespace of Trait differs from class or interface namespace use (trait use statement starts with a class namespace implicitly) then maybe traits were able to provide a kind of sub-namespace for the properties they operated on, but since they are nothing more than just a compiler assisted copy and paste I am disappointed. Thanks for the links you provided, they've shorten my time having wrong hope :) – Jimmix Jan 24 '20 at 21:26
  • You could always use dynamic properties and `__TRAIT__` [like this](https://3v4l.org/oHcn9). But 1) that's not very clean/convenient, 2) you can't define the actual properties if you do that, 3) might as well prefix them manually if you're gonna do something like that. – Jeto Jan 24 '20 at 21:41

1 Answers1

2

Each trait should define the properties that its methods needs.

Relying on undefined properties or properties defined on the class that uses the traits for composition is a bad idea. Hence, having multiple traits that depend on the same properties is also a bad idea.

Each trait should provide all that's it needs to work, logic wise.

Additionally, traits do not have "scope". When using traits, it's as if the code in the trait is copied to the consuming class. In your example, the scope for setOne() and getTwo() is an instance of Bar, that the methods were defined in traits is irrelevant.

The saner way to do this is include whatever properties your trait need in the trait itself:

E.g.:

 trait CanLog {

    private LoggerInterface $logger;

    public function error(string $data)
    {
        $this->logger->error($data);
    }

    public function alert(string $data)
    {
        $this->logger->error($data);
    }
}

This way a consuming class simply imports the behaviour it needs from the trait, complete, and can inject whatever they need in the properties that the trait provides:

class Bar
{
    use CanLog;

    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger; 
    }
}
yivi
  • 42,438
  • 18
  • 116
  • 138
  • I think they are asking what if you had another trait which also had `private LoggerInterface $logger;` as part of its definition. Can each trait have it's own copy of the value. – Nigel Ren Jan 24 '20 at 17:29
  • Thanks, but in their example they are relying on dynamically generated properties, without any definition. But in any case, defined or not the answer is the same: traits to not have "scope". Classes that use the traits have scope. – yivi Jan 24 '20 at 17:35
  • @NigelRen Yes exactly like that. Defining or not does not make any difference for creating a kind of trait's own copy (scope) of the property. – Jimmix Jan 24 '20 at 18:10
  • @Jimmix Yup, and as I said earlier, it doesn't make any difference regarding the "scope". The scope it's still the one of the class that uses the respective traits. – yivi Jan 24 '20 at 19:19
  • @yivi Right, so unlucky they have rather limited use cases and having plenty of them in the same class where many different traits' methods reaches for a one or the other property of that class makes code almost so error prone as if you were writing everything straight into the "main loop" not taking care for the scope at all. The only reasonable use case I see is when trait's method does not need to reach out of the method scope for any property but in that case one may simply use a static method instead. – Jimmix Jan 24 '20 at 21:11