Matt Indeedhat Holmes
Matt Indeedhat Holmes

Reputation: 725

Trait inheritance checking PHP

So ive finally gotten round to playing with traits and they are very handy, the problem that i have been having is that i want to have some traits to add functionality to my data objects. In of itself this is simple except that in doing so im using methods that are defined in my base data object

abstract class Base_Object {
  protected function _addToUpdate($field, $value) {
    ...
  }
  ...
}

trait Extended_Object {
  public function doSomeStuff() {
    ...
    $this->_addToUpdate($someVar, $someOtherVar);
  }
  ...
}

class User extends Base_Object {
  use Extended_Object;
  ...
}

My problem is then if someone else in my team decides to use the Extended_Object trait on an object that doesnt extend Base_Object. I had thought about putting a check in the _addToUpdate method but ideally i would like an error to be shown when creating the instance.

I have come up with a solution that works but makes me feel a bit dirty and is far from ideal

abstract class Base_Object {
  protected function _addToUpdate($field, $value) {
    ...
  }
  ...
}

trait Extended_Object {
  abstract protected function _addToUpdate($field, $value);

  public function doSomeStuff() {
    ...
    $this->_addToUpdate($someVar, $someOtherVar);
  }
  ...
}

class User extends Base_Object {
  use Extended_Object;
  ...
}

By adding an abstract method to the Extended_Object i then can at least be sure that an error will be show if the method i need in Base_Object isnt present but i am not guaranteed that the method in question will actually do what i want it to do.

Ideally i would like to be able to run something like the code below when an object is instantiated using the Extended_Object trait

if (!($this instanceof Base_Object)) {
  throw new Inheritance_Exception("Base_Object");
}

Im hoping that someone has found a way to do this or at least a better solution than mine.

Note: i know that i could do this with a constructor but that would only be viable when using a single trait if i decided at a later date to create anther few object extension traits its going to get very messy very quickly

Edit: i do realize that traits arnt really designed for what im trying to do but they do at least in part allow me to get around the problem of single inheritance and i know im not the only dev planning to use them in this way

Upvotes: 3

Views: 1557

Answers (4)

MrWhite
MrWhite

Reputation: 45829

My problem is then if someone else in my team decides to use the Extended_Object trait on an object that doesnt extend Base_Object.

:

Ideally i would like to be able to run something like the code below when an object is instantiated using the Extended_Object trait

if (!($this instanceof Base_Object)) {
    throw new Inheritance_Exception("Base_Object");
}

Maybe I'm missing something, but if this trait should only ever be applied to instances of Base_Object, then it sounds as if this "trait" should be a subclass of the Base_Object and not a trait at all?

The User class then extends this Extended_Object (a subclass), rather than useing the Extended_Object (a trait).

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

A Trait is intended to reduce some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes living in different class hierarchies.

Upvotes: 0

user5321531
user5321531

Reputation: 3265

I am faced with a similar problem, however with multiple subclasses inheriting from the base class.

It would make sense at this point to put trait abstract declarations into some sort of include file and as a trait itself, e.g.:

abstract class Base_Object {

  use BaseObjectTrait;
  ...
}

trait BaseObjectTrait {

  protected function _addToUpdate($field, $value) {
    ...
  }

}

trait BaseObjectTraitPrototypes {

  abstract protected function _addToUpdate($field, $value);

}

trait Extended_Object {

  use BaseObjectTraitPrototypes;

  public function doSomeStuff() {
    ...
    $this->_addToUpdate($someVar, $someOtherVar);
  }
  ...
}

class User extends Base_Object {
  use Extended_Object;
  ...
}

Can anyone think of a more elegant solution than this? I'm using the term 'prototype' very loosely here from a C programming sense. 'Header' files are likewise from a C programming context but still not ideal. Can anyone think of a more concise concept for the problem domain, something php-fig acceptable perhaps?

Upvotes: 0

IMSoP
IMSoP

Reputation: 97718

I think your solution with an abstract function is precisely what that facility is intended for. I agree with your gut feeling:

i am not guaranteed that the method in question will actually do what i want it to do

However, this is the same assumption you make whenever you use an interface: the only thing that is asserted is that a method exists with the correct signature (which in PHP amounts mostly to its name and number of parameters). There is no way to know that the function actually behaves in a particularly useful way when given those parameters.

In the case of a trait, an abstract method is effectively the same kind of contract as an interface: "in order to use this trait, you must provide these pre-requisites".

I would also note that common description of traits is "automated copy and paste", so it should generally be thought of as separate from object hierarchies. More technically, it represents "horizontal code reuse", not "multiple inheritance". For example, you can define a trait that allows a class to implement an interface, but to the outside code, it is the interface that is important, not the trait.

Upvotes: 2

mamdouh alramadan
mamdouh alramadan

Reputation: 8528

Well (IMHO) the problem exists with the way you are using traits which is technically anti-pattern.

First, What are traits and why should I use them: To quote from one of the comments in php.net

The best way to understand what traits are and how to use them is to look at them for what they essentially are: language assisted copy and paste.

If you can copy and paste the code from one class to another (and we've all done this, even though we try not to because its code duplication) then you have a candidate for a trait.

Now even that traits lets you use members of the class which is using them, that does not mean you should do that. back to SRP, this is a real violation. PHP lets you do echo $_POST['XSS data'] but that does not mean you should do it. So, regardless to the way you want to strict your trait usage, what you are actually doing is that you are introducing the issue and you are trying to solve it so to answer your question, simply redesign your code in order for you not to use class methods and members based on an assumption that each class which will use the trait should have these methods and members.

Upvotes: 2

Related Questions