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?
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.
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.
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.
... 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.
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.
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.
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...
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:
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:
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.
In general, endeavor to:
Here is a good article on the subject:
http://weierophinney.net/matthew/archives/254-Why-PHP-Namespaces-Matter.html
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With