PHP Interface and Abstract Class best practice for inheritance?

16,854

Solution 1

Abstract classes defines an interface that must be implemented to the heirs of the abstract class. An Interface-Construct defines an interface that must be implemented by a class that implements the interface-construct, the implementation of the interface is not limited to a single interface, whereas class inheritance is coupled to a single (abstract) class.

Interfaces in PHP are intentionally used to allow typehints of an limited subset of an entire class interface. There is no reason for an interface on abstract classes aslong their receiver of instances of their heirs did not use them ( with typehinting or logical identification over instanceof / is_a ). The more valuable benefit of interface-constructs are the possibility of replacing an common implementation of an interfaces with a alternate implementation.

In case of your BaseData-Example, i recommend to drop the abstract idea and use a trait and seperate interfaces instead.

trait connectionBrokerConstructor {
    protected $connection;

    public function isConnected()
    {
        return $this->connection instanceof Connection;
    }

    public function setConnection(Connection $connection)
    {
        $this->connection = $connection;
    }
}

interface connectable
{
    public function setConnection(Connection $connection);
    public function isConnected();
}

interface userDataRepositoryInterface
{
    public function get();
}

class UserData implements connectable, userDataRepositoryInterface
{
    use connectionBrokerConstructor;

    public function __construct(Connection $connect = null)
    {
        $this->setConnection($connection);
    }

    public function get()
    {
        return array('something');
    }
}

Solution 2

Really abstract classes and interfaces are different.

Consider an interface as a contract, it lays out the rules that other classes (which implement it) must follow.

Abstract classes on the other hand are more like starting points, which other classes can build on, hence why they are sometimes called base classes.

------- Edit with example

I'm not an expert on such things, but I've always just done interface -> class.

For example, here is a simple interface:

interface AnimalInterface {

  public function all();

  public function findBySlug($slug);

}

And here is the class which implements that interface (simplified):

class AnimalEloquentRepository implements AnimalInterface {

  public function all()
  {
    return Animal::all();
  }

  public function findBySlug($slug)
  {
    return Animal::whereSlug($slug)->first();
  }

}

I do often have a base class, which others extend, but I've only once used an abstract class in a real world app, and that ended up getting replaced.

I'm sure there are better ways to do things, but this has worked really well for me in the past.

Share:
16,854

Related videos on Youtube

user702300
Author by

user702300

Updated on September 16, 2022

Comments

  • user702300
    user702300 over 1 year

    When defining the structure and inheriting Interface and/or Abstract Class, which one is the best practice? And why? Here are 2 examples:

    Here is the example for [Interface] -> [Abstract Class] -> [Class]

    Interface DataInterface
    {
        public function __construct($connection);
        public function connected();
        public function get();
    }
    
    Abstract class BaseData implements DataInterface
    {
        protected $connection;
    
        public function __construct($connection)
        {
            $this->connection = $connection;
        }
    }
    
    
    class UserData extends BaseData
    {
        public function exists()
        {
            return is_connected($this->connection);
        }
    
        public function get()
        {
            return get_data($this->connection);
        }
    }
    
    $oUserData = new UserData(new Connection());
    

    And here is the sample for [Abstract Class] -> [Class] without the Interface

    Abstract class BaseData
    {
        protected $connection;
    
        public function __construct($connection)
        {
            $this->connection = $connection;
        }
    
        abstract public function connected();
        abstract public function get();
    }
    
    class UserData extends BaseData
    {
        public function exists()
        {
            return is_connected($this->connection);
        }
    
        public function get()
        {
            return get_data($this->connection);
        }
    }
    
    $oUserData = new UserData(new Connection());
    

    I am currently creating a small app (might grow larger) and confused on how to implement in the beginning correctly.

    By the way, is this declaration for __construct() with parameter make sense in Interface?

    public function __construct($connection);
    
    • Mark Baker
      Mark Baker over 10 years
      Interfaces and Abstracts are totally different things with totally different purposes, so you can't really compare them in this way
    • user702300
      user702300 over 10 years
      I can see the differences but can't really understand the difference in practical sense :( Because both of them works if I want to inherit from one Interface? Although it can implement from more than one Interfaces, correct? Thanks!
  • user702300
    user702300 over 10 years
    But based on the examples above, which one would you follow and why?
  • Tom Green
    Tom Green over 10 years
    I've amended my answer with a real world example that I use.
  • user702300
    user702300 over 10 years
    Okay, thank you for the example, what about defining the __construct() in Interface? Does it make sense to enforce it to pass the parameter variable like: public function __construct($connection);
  • Tom Green
    Tom Green over 10 years
    It depends on your situation, obviously if you've got a class which requires the $connection variable then you could define this using the interface. Personally I wouldn't because I mainly use __construct() for dependency injection.
  • echochamber
    echochamber over 10 years
  • user702300
    user702300 over 10 years
    Thank you, combination of interfaces and using traits is an interesting idea.
  • user702300
    user702300 over 10 years
    Thank you @Tom Green and @echochamber!
  • RightHandedMonkey
    RightHandedMonkey over 9 years
    Please note that Traits requires PHP 5.4.0 or greater