Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a variable private to a trait?

Tags:

oop

php

traits

I'd like to reuse a functionality several times in a single class. This functionality relies on a private variable:

trait Address {
    private $address;

    public function getAddress() {
        return $this->address;
    }

    public function setAddress($address) {
        $this->address = $address;
    }
}

The only way I've found to use the trait twice, is the following:

class User  {
    use Address {
        getAddress as getHomeAddress;
        setAddress as setHomeAddress;

        getAddress as getWorkAddress;            
        setAddress as setWorkAddress;
    }
}

The problem is, by doing this, the private variable $address is shared across the different methods, and the code will not work as expected:

$user = new User();
$user->setHomeAddress('21 Jump Street');
echo $user->getWorkAddress(); // 21 Jump Street

Is there a solution to really use the trait twice, while not sharing its private variables?

like image 753
BenMorel Avatar asked Nov 27 '12 10:11

BenMorel


People also ask

Can traits have private methods?

Traits can have properties and methods with private and protected visibility too. You can access them like they belong to class itself. There is no difference.

Can variable be private?

Variables are private to protect the state of your objects - in object-oriented programming terms, this is called encapsulation.

Can PHP traits have variables?

Traits can define static variables, static methods and static properties. Note: As of PHP 8.1. 0, calling a static method, or accessing a static property directly on a trait is deprecated.

How do you make a variable private in PHP?

Use the __setter (__set) function to set value(s) to your private variable inside a the class, and when the value is needed, use the __getter (__get) function to return the values.


2 Answers

Declaring a trait with use will not create an instance of that trait. Traits are basically just code that is copy and pasted into the using class. The as will only create an Alias for that method, e.g. it will add something like

public function getHomeAddress()
{
    return $this->getAddress();
}

to your User class. But it will still only be that one trait. There will not be two different $address properties, but just one.

You could make the methods private and then delegate any public calls to it via __call by switch/casing on the method name and using an array for address, e.g.

trait Address {
    private $address = array();

    private function getAddress($type) {
        return $this->address[$type];
    }

    private function setAddress($type, $address) {
        $this->address[$type] = $address;
    }

    public function __call($method, $args) {
        switch ($method) {
            case 'setHomeAddress':
                return $this->setAddress('home', $args[0]);
            // more cases …
        }
    }
}

But that is just a can of worms.

In other words, you cannot sanely do what you are trying to do with traits. Either use two different traits. Or use good old aggregation and add concrete proxy methods.

like image 111
Gordon Avatar answered Sep 16 '22 15:09

Gordon


i may be a little late for the party, but the behavior you are trying to create is not something that should be covered by a trait, but by simple object composition.

<?php
class Adddress {
    private $street;
    private $number;
    public function __construct(string $street, ?string $number) {}
    public function street() : string {}
    public function number() : string {}
}
class User {
    private $homeAddress;
    private $workAddress;
    public function getHomeAddress() : Address {}
    public function setHomeAddress(Address $homeAddress) : self {}
    public function getWorkAddress() : Address {}
    public function setWorkAddress(Address $workAddress) : self {}
}
like image 33
hagnat Avatar answered Sep 17 '22 15:09

hagnat