Vincent Mimoun-Prat
Vincent Mimoun-Prat

Reputation: 28541

Detect recursion when overridding trait methods

Two libraries provide each inside a trait, a method to override a default base class method.

// This is a Framework base class
class Model { 
  function getAttribute($key) {
    return "Model->$key";
  }
}

// This is a trait from library A which provides extra stuff
trait HasUuid {
  function getAttribute($key) {
    if ($key==='uuid') return "HasUuid->$key";

    return parent::getAttribute($key);
  }
}

// This is a trait from library B which provides extra stuff
trait HasQuantity {
  function getAttribute($key) {
    if ($key==='quantity') return "HasQuantity->$key";

    return parent::getAttribute($key);
  }
}

Now, I have a class in my application which inherits from Model and needs to use both traits.

PHP allows to alias the trait functions in order to resolve naming conflicts between libraries. So here is what I end up with:

class Product extends Model {
  use HasUuid { getAttribute as getHasUuidAttribute; }
  use HasQuantity { getAttribute as getHasQuantityAttribute; }

  function getAttribute($key) {
    // Framework default value to use as fallback
    $parentValue = parent::getAttribute($key);

    $overrides = ['getHasUuidAttribute', 'getHasQuantityAttribute',];

    foreach ($overrides as $override)
    {
      $overriddenValue = $this->$override($key);

      // A trait has some better value than the fallback
      if ($overriddenValue !== $parentValue) return $overriddenValue;
    }

    // Nothing better, use fallback from framework
    return $parentValue;
  }
}

We end up with a recursion issue: when calling the overridden trait methods, their refer to the parent::getAttribute method which triggers another call to Product::getAttribute which recurses infinitly.

How can we solve this recursion issue, by keeping access to both trait features?

Of course, as the traits each come from a library, I cannot change their code.

Upvotes: 3

Views: 304

Answers (1)

FULL STACK DEV
FULL STACK DEV

Reputation: 15951

You can take advantage of making these private using a private keyword.

class Product extends Model {
  use HasUuid { getAttribute as private getHasUuidAttribute; }
  use HasQuantity { getAttribute as private getHasQuantityAttribute; }

  function getAttribute($key) {
    // Framework default value to use as fallback
    $parentValue = parent::getAttribute($key);

    $overrides = ['getHasUuidAttribute', 'getHasQuantityAttribute',];

    foreach ($overrides as $override)
    {
      $overriddenValue = $this->$override($key);

      // A trait has some better value than the fallback
      if ($overriddenValue !== $parentValue) return $overriddenValue;
    }

    // Nothing better, use fallback from framework
    return $parentValue;
  }
}

Hope this helps.

Upvotes: 2

Related Questions