Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Interfaces in an custom OO PHP framework

I'm working on a fairly simple OO PHP framework (not really important in this case I guess..), with the following basic structure:

application/
  classes/
  controllers/
  includes/
  models/
  views/
classes/
includes/

I know that using interfaces rather than hardcoding classes is good practice for OOP, but I'm not sure what best practice is when it comes to the actual location/structure of interface directories and files.

Should interfaces be separated into multiple files under a directory:

interfaces/
  iDatabase.php
  iRouter.php

or should they all be put in a single file since they're not that large:

includes/
  interfaces.php (with all Interfaces inside)

With the first option I could use an autoloader to load interfaces and not have to load every file when not all may be used, while with the second option they would all be loaded initially but it would save me having to load multiple files each time.

What are your thoughts? Am I totally looking at this the wrong way (I tend to do this with most of my problems until someone steers me in the right direction! haha)

Thanks heaps!

Ryan

Edit 2011-02-07:

After reading the answers I've been given so far, I've tried out a few things.

Assuming the classes below get autoloaded from the exact location on the disk (Database_Database would get loaded in 'classes/Database/Database.php'), would this set up be effective?

class Database_Mysql_Database extends Database_DatabaseAbstract implements Database_Database {}

Database_Mysql_Database is a normal class, Database_DatabaseAbstract is an abstract class with the basic methods common to different types of databases, Database_Database would be the interface that users would typehint against to ensure compatibility with their classes.

Am I on the right track?

like image 707
manbeardpig Avatar asked Feb 04 '11 16:02

manbeardpig


People also ask

How is interface used in PHP?

A PHP interface defines a contract which a class must fulfill. If a PHP class is a blueprint for objects, an interface is a blueprint for classes. Any class implementing a given interface can be expected to have the same behavior in terms of what can be called, how it can be called, and what will be returned.

Can I implement multiple interfaces in PHP?

Yes, more than two interfaces can be implemented by a single class. From the PHP manual: Classes may implement more than one interface if desired by separating each interface with a comma.

What is the advantage of using interface in PHP?

Advantages of PHP Interface An interface allows unrelated classes to implement the same set of methods, regardless of their positions in the class inheritance hierarchy. An interface can model multiple inheritances because a class can implement more than one interface whereas it can extend only one class.

Can we instantiate interface in PHP?

... Cannot instantiate interface ... It tells you that you are trying to instantiate an interface somewhere in your code. The problem with doing that is that interfaces should not be instantiated — instead you need to pick the specific class that implements the interface, and then instantiate that directly.


4 Answers

Personally, I would suggest that you put interfaces and exceptions where they are semantically appropriate. There's no reason to lump them all into one folder away from classes. But at the same time, don't put them next to the concrete implementations just for the sake of it. I'll give an example.

Let's say we're dealing with a database abstraction layer. You'll have an iDatabase interface, and an iDatabaseDriver interface. Say your folder (and class) structure is like this:

/classes/database/idatabase.php
/classes/database/database.php
/classes/database/drivers/mysql/databasedrivermysql.php
/classes/database/drivers/postgres/databasedriverpostgres.php

Now, there are 2 logical places to put iDatabaseDriver. You could put it under database, or under drivers. Personally, I would put it under database, since it's kept close to where it's needed (since it's more likely that Database requires an iDatabaseDriver, so the dependency is there).

So, with that, you can see that sometimes it's semantically appropriate to put the interface right next to the concrete implementation. But other times it's more appropriate to put the interface next to the dependency than the concrete implementations.

Now this example is a gross-over-simplification, but I think it should get the point across.

  • Have rules for your naming and storage of interfaces

    Come up with a system for organization of code. That way it's more predictable and easier to autoload. Plus, it becomes far easier to maintain when you can tell where something's supposed to be by the rules

  • Abide by those rules!

    This is more important than having rules. If you don't follow the rules, it's worse than not having them at all since you're expecting something that's not going to happen.

  • Favor semantic relationships over code-level relationships

    The semantic relationship between an interface and its concrete implementations is more important than the relationship that an interface is an interface. So put semantically related code in the same (or similar) places.

Edit: Regarding naming and your edit:

Personally, I hate things like Database_Database. While it may make sense given the structure of the application, it makes no semantic sense whatsoever. Instead, what I like to do in my autoloader(s) is to test for the file, and if it doesn't exist but the directory does, test for the same file inside of that directory. So, Database would result in a check in /database.php and if that fails, /database/database.php. It removes the need for double naming. Database_DatabaseAbstract would become Database_Abstract. So your Database_Mysql_Database could become Database_Mysql stored in /database/mysql/mysql.php (which to me seems cleaner).

As far as your naming convention of abstract classes and the such, I personally prefer to identify interfaces by the name. It makes it easier to understand at a glance (You know that public function foo(iDatabase $database) is looking for an instance of the interface instead of an abstract class or concrete class). Now, there are two real ways of doing this.

  1. Append Interface to the name, so Database_Database would become Database_Interface. I personally feel this is a bit too verbose for my needs, however the benefit here is that all of your special class types (Exceptions, Interfaces, Iterators, etc) can simply be mapped like this. The class name tells you exactly what you have with no ambiguity whatsoever.

  2. Prepend the entire sequence with i. So Database_Database would become iDatabase which would then be translated in the autoloader to /database/interface.php. Then, if you had deeper interfaces, iDatabase_Mysql_Query could work as well (Which would map to /database/mysql/query/interface.php.

As far as the abstract class is concerned, I wouldn't do that. The fact that a class is abstract shouldn't really have anything to do with its semantic meaning. The abstract nature is a coding construct and not a semantic one (the abstract class is used for nothing other than inheritance since you're using the interface for type-checking). Therefore I would not recommend including Abstract in the class name. Just call it Database and be done. It reads better semantically (IMHO) and conveys the same meaning.

I hope that helps and makes sense...

like image 148
ircmaxell Avatar answered Oct 05 '22 19:10

ircmaxell


The standard way is to treat them exactly as you would treat classes - store them in separate files, load with autoloader. Also:

  • with standard naming conventions you would call the interfaces Database and Router, not iDatabase and iRouter.

  • don't think about the performance before profiling: how can you be sure that the impact of having all the interfaces parsed with every single request does not offset loading many files? the files will be in a buffer anyway.

EDIT: (after the question got edited).

You want:

  • Database (classes/Database.php), an interface for type hinting / code completion (if you insist on having an interface in the first place, but we are not discussing that);
  • Database_Abstract (classes/Database/Abstract.php), a class that implements Database and has the common, abstract functionality; users are never aware it exists;
  • Database_Mysql (classes/Database/Mysql.php), a class that extends Database_Abstract. It does not have to implements Database (Database_Abstract already does it);

You could also prepend all the paths (and names) with the name of your framework; this way if you use some classes from ZF, say, for PDF generation, you can keep the same autoloader and the same classes directory and not have the two mixed up.

If you are really into the OOP craze, you might add:

  • Database_Factory (classes/Database/Factory.php) - a class that has a single method called createDatabase; the method has a doc comment of /** @returns Database */ and accepts a database configuration (as a string or maybe an array, or maybe a Database_Config object, if you are a hard core); this would be necessary to really decouple the implementation from the interface.

As for the answers that give you some different ideas for naming files, consider them, but take into account that: - uniformity is a great value on its own; - if you stick to pear convention, you can use the same autoloaders to load pear classes and a lot of PHP libraries; - if you stick to pear convention, you get used to modern PHP.

like image 28
fdreger Avatar answered Oct 05 '22 20:10

fdreger


In general, endeavor to:

  • Use an autoloader.
  • Have one file contain one class.
  • Keep interfaces near their concrete implementations.

Here is a good article on the subject:

http://weierophinney.net/matthew/archives/254-Why-PHP-Namespaces-Matter.html

like image 29
Alex Howansky Avatar answered Oct 05 '22 19:10

Alex Howansky


If you are using PEAR or Zend naming conventions, the interface will be in a the components folder.

I.e., the a class Myprefix_Router_Interface translates into the path /Myprefix/Router/Interface.php

This is an excellent way to compartmentalize your components, and makes organization logical when looking at just the class name (you know exactly where the specific files are located).

like image 42
John Cartwright Avatar answered Oct 05 '22 19:10

John Cartwright