Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 409
Vincent Mimoun-Prat Avatar asked Mar 08 '26 08:03

Vincent Mimoun-Prat


1 Answers

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.

like image 53
Romantic Dev Avatar answered Mar 10 '26 22:03

Romantic Dev



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!