We have recently been rewriting our PHP application's model code using OO. We have the classes done, and we included namespacing (in the form of Project\Level1\Level2 or whatever). Since this is an already-built application, there are a lot of classes. We are using an autoloader, for ease of loading the classes, which is great. But here's where the hassle comes in.
We have probably 300 pages strewn all across the application, and all of them require a PHP file at the very beginning (a sort of bootstrap script). The autoloader script is required in that bootstrap file.
Now, most of our pages will be using at least one but likely many of the classes. We know that we have two options here:
//either call the classes using qualified names
$person = new Project\Person;
//or include the "use" keyword on every page, so we can alias the classes for ease of use
use Project\Person as Person;
$person = new Person;
But the hassle is that we would really rather not have to do this on every single page in our application:
require_once('../php/bootstrap.php');
use Project\Person\ as Person,
Project\Address\ as Address,
Project\Group\ as Group
Project\CustomField\ as CustomField;
$person = new Person;
$address = new Address;
We tried, for the heck of it, including the use keyword for all our classes in the bootstrap file, but according to the PHP manual, "Importing rules are per file basis, meaning included files will NOT inherit the parent file's importing rules."
It just seems like including these sprawling use statements on every page of our application is creating more hassle than it's worth. And what if we are using 15 classes on one page? Then the use statement would be huge.
My question is this: is it too much hassle? Are we correct in using namespaces, or is it not all that helpful in PHP? And now knowing what we are trying to do, are we doing it right? Is there something we are missing? Should we be calling classes with fully qualified names all the time or something?
(this is my first question on stackoverflow, so please let me know if I need to do anything differently or better with my questions in the future)
While using vanilla Zend Framework Autoloader might not be they way you want to go, the Zend Loader does support custom autoloaders, that can respect namespaces specified in-line or dynamically at run-time. In your case, you are using use Project\Person\ as Person which can become cumbersome, as you already noticed.
Using namespaces with the autoloader will allow you to take advantage of auto loading using simple path traversal to organize classes, without having to append large paths just to get to the interesting parts.
So, instead of new Corp_Assets_Generic_Person_Address, you could keep the folder structure and use new Namespace_Person_Address.
Namespaces avoid clashes in class names. Specially in large applications implementing autoloaders that need to load the right class from a number of different libraries. Using shorter class names makes the code easier to read but may make it harder to maintain. Adding the use statements makes it easier to read and later maintain but then it would be cumbersome to write and refactor. Documenting the expected behavior of the autoloader and using custom autoloaders to make more readable class names would be my suggestion.
In php.ini add your file as a prepend_file
Related
I'm wondering, that if my functions don't have similar names, do I need to use namespaces?
plus I can't get the grips of importing all namespaces from a specific folder...
I'm wondering, that if my functions don't have similar names, do I need to use namespaces?
No.
However, namespacing does offer more benefits than avoiding namespace collisions, it also forces you to maintain good directory and file structures which makes your files easy to find, and an intuitive approach to determining parent/child relationships.
For example:
// Zend/Translate/Adapter.php
class Zend_Translate/Adapter {}
// Zend/Translate/Adapter/Csv.php
class Zend_Translate_Adapter_Csv extends Zend_Translate_Adapter {}
As you can see, classes relating to similar objects are logically grouped together, and as the above code snippet demonstrates, signifies possible parent/child relationships. One caveat, even though a directory may contain a class file along with a sub-directory, it is not guaranteed that the files within the sub-directory will extend the file.
I think you are approaching this question from the wrong perspective. Sure, you can avoid using namespaces if you know you have unique function names. But that is a very short sighted approach and doesn't take into account how complex the application is, how it is structured, etc.
As mentioned by Mike, if you are going to be using frameworks like Zend or Symfony or any other code heavy on Object Orientated Programming, then I would strongly recommend using namespaces. Let's say you want to include a class from a great PHP package. Without namespaces, you will have no assurance there will not be any conflicts.
Additionally, new frameworks like Symfony 2.0 REQUIRE you to use namespaces. So you better get used to it.
If I have several classes in different php files that I'm including into a single file, and some of these classes have dependencies on other classes, does the order in which they are included matter? Or, is simply including them (in any order) before they are used all that matters?
Yes, it matters. This is mentioned as a note in object inheritance documentation:
Unless autoloading is used, then classes must be defined before they are used. If a class extends another, then the parent class must be declared before the child class structure. This rule applies to classes that inherit other classes and interfaces.
I recommend to use autoloading to resolve class dependencies. That way classes are only loaded when they are actually needed and you don't have to bother with including every single PHP file by hand.
You have to keep every class in it's own file for this to work, though. But that's a good way to keep everything tidy and clean, anyway.
Within 1 PHP file, you can declare functions/classes anywhere and call them anywhere (within that file), order does not matter.
As for includes, you must include the file BEFORE you attempt to use a declared function/class within that file.
Simply include them before they needed and everything will be fine.
It would matter as if one class tried to use another which had not yet been parsed, then an error would occur. The best option is to place the classes in the script so the dependancy will still be intact.
If they all are in the same file, no the order shouldn't matter. There are some rare instances (I've seen with namespaces) where the order does matter, but don't worry about that too much unless you're pushing the envelope.
Try it. The worst that can happen is that it'll give you an error...
Yes it does matter and when a large number of classes are involved, it can cause problems. Using autoloading is not feasible when your production scripts needs to include 40 or 50 classes, which is always the case if your application is properly designed in accordance with best OOP practices, and you need to deal with forms etc...
Let me explain why the order matters. If you use an autoloader, every time the keyword extends is found, there is an attempt to autoload a class. So if you extend before the class being extended has been defined, it will trigger an autoload event.
When you develop your application, use a custom autoloader. Make that custom autoloader give you information about timing and memory used. It will load all class files one by one and waste a lot of time doing so. When your application is finished, you can optimize by concentrating many related classes into one file. For example, my form class is in class.form.php and it does not just load the form class. It loads another 50 classes related to forms. So I have everything in one file and when I create a new form(), the autoloader loads class.form.php which include the other classes that a form usually need (form_checkbox, form_input and so on) so when they are needed, they are not autoloader because they are already defined.
You can create a PHP script that will recurse, resolving dependencies and order all your classes within the single class file (class.form.php using my example) so that they all have the parent class defined before the extends keyword (which triggers autoloading) is found. I run php -w on the concatenated file which strips comments and spaces. It has reduced my form classes from 80K to 18K. Whenever I use a form, I create a new form() and it loads 18Ks worth of form-related classes, which is not excessive.
Initially I used the linux command cat but it does not work. You need a smarter script that can resolve the parent of each extended class and properly order classes in the file that contains many related classes.
I'm starting to write a application in php with one of my friends and was wondering, if you have any advice on how to implement module support into our application.
Or is there a way how to automatically load modules written in php by a php application? Or should i just rely on __autoload function?
And we don't need plugins that are installable via a web interface, we just need some clever way to associate web pages of our project to some classes (they will render the page) so index.php can call the right class and retrieve it's generated sub-page.
And we are not using any kind of framework, for now at least.
Re your comment: Autoloading in conjunction with strict file naming would be totally enough in this case IMO.
You could define a specific namespace (not in the new PHP 5.3 namespace way, just in the sense of a common prefix to names), e.g. Module_* for module class names.
You could then organize your modules in directories with class files that contain exactly one class definition, for example:
/modules/Mail/index.php // defines class Module_Mail
/modules/Database/index.php // defines class Module_Database
/modules/Image/index.php // defines class Module_Image
Your autoloader function would then, whenever a Module_* class is requested:
$Database = new Module_Database("localhost", .....);
include the correct file from the right directory.
That's the way e.g. the Zend Framework does it, and does so pretty well.
Consider choosing a more specific namespace than Module_ to assure interoperability with other scripts and applications if that is an option in the future.
Slightly related: In a PHP project, how do you organize and access your helper objects?
It sounds like you are looking for a way to organise the different tasks that each page needs to carry out. In this case, take a look at the MVC pattern. It provides a simple way to seperate your data access (models) and how you render/present information (views).
You can map pages to functions with ease. If you store the information in an array of mapped values, and then use a function to compare the requested URL with each of the URLs in the array. Such an array could look like this:
$urls = array(
'/' => 'index',
'/aboutus/' => 'aboutUs',
);
There are several articles that discuss how to implement it in PHP in a couple of hours. This article is a very simple guide. I am not a fan of their use of the registry pattern but reading through should provide you with enough information as to how you can implement it yourself.
Regarding autoloading, you can also use spl_autoload_register, to define several (more than one) autoload functions, so each module can set up it's own implementation of autoloading.
All you need here is an MVC architecture, with one Controller class associated with each "module".
If you are not against using a framework, go for Zend MVC
It allows you to have the following principle :
An URL like this : http://yoursite.com/my-module/edit
Will more or less automatically call the editAction method in the MyModuleController class.
As far as I can tell, the only reason we have namespacing in PHP is to fix the problem of classes (+ functions & constants) clashing with others classes of the same name.
The problem is that most frameworks setup their autoload and filesystem hierarchy to reflect the names of the classes. And no-one actually require()s or include()s files anymore.
So how does namespacing help this any? Either the class is loaded based off of it's name:
new Zend_Db_Table_Rowset_Abstract;
or off it's namespace
new Zend\Db\Table\Rowset\Abstract;
Either way I am stuck with only being able to create one class with this name.
/var/www/public/Zend/Db/Table/Rowset/Abstract.php
UPDATE
I'm not sure I'm getting the point across.
Even if I created two files with the same class Zend\Db\Table\Rowset\Abstract in them I still couldn't use them together since they both claim the same namespace. I would have to change their namespace names which is what we already do!
This leaves me to belive that the only use for namespaces is function names. Now we can finally have three functions all named the same thing!
Or wait, I forgot you can't do that either since each requires the namespace prefix!
a\myfunction();
b\myfunction();
c\myfunction();
Taking ircmaxell's example:
$model = new \Application\Model\User;
$controller = new \Application\Controller\User;
How is that any different than without?
$model = new Application_Model_User;
$controller = new Application_Controller_User;
This is also a neat sounding feature - but what does it truly do for us?
use \Application\Model\User as UserModel;
use \Application\Controller\User as UserController;
$foo = new UserModel;
$bar = new UserController;
Now you cannot have a class named 'UserModel' since you have a namespace setting for that term. You also still cannot have two classes named under the same alias.
I guess the good thing is that you can rename the long Zend_Db_Table_Rowset_Abstract
use Zend_Db_Table_Rowset_Abstract as RowAbstract;
leading to developer confusion about where the non-existent class "RowAbstract" is defined and coming from in the system.
My impression is that namespaces are used in a cargo cult programming fashion. Because it's new, it gets used like crazy. Deeply nested namespaces, like in Doctrine2, add no further protection against name conflicts. And it's very obvious that \nested\name\spaces are just used to achieve a 1:1 mapping to directory/file names. Clearly a code smell.
But I suppose this phenomenon is also caused by trying to mimick Java module names in PHP. And furthermore the backslash syntax doesn't convey a sensible semantic as in other languages.
Anyway, the ability to rename classes while importing namespaces is a big bon. That's useful when it actually comes to mixing conflicting definitions. And the namespace syntax can simply be added, once an actual name conflict arises. (I see no need to implement namespaces right away, when name conflicts are that rare and for most projects a purely fictional problem.)
If you do it only when required, the awkward namespace syntax never even has to rear its ugly head. If there is only one namespace level to import, you can just use namespace1\Class as LocalName and don't convolute the application with any namespace1\Class syntax. (Still it's a better idea not to use overly generic class names in namespaces.)
Actually, you can create more than one class with the same name
Suppose you have 2 classes:
\Application\Controller\User
and
\Application\Model\User
You couldn't import both into the same file without aliasing one, but you still can define both the same way:
$model = new \Application\Model\User;
$controller = new \Application\Controller\User;
Plus you can import and alias:
use \Application\Model\User as UserModel;
use \Application\Controller\User as UserController;
$foo = new UserModel;
$bar = new UserController;
So it really is quite powerful, as it does let you name your classes however you want (and reference them by arbitrary names inside your code). The only rules are reserved keywords... See: http://www.php.net/manual/en/language.namespaces.importing.php
The point you seem to be missing is that namespace names are composable. I.e. you can do:
use Zend\Db\Table as Table;
$a = new Table\Rowset();
$b = new Table\Fields();
etc. I.e. it allows you to define your context (or set of contexts) and then refer to it. Of course, you can reduce it to just one name, but you don't have to. That btw also helps with generic class names - Fields may be too generic, but Table\Fields less to.
Another thing is if you get sick of Zend tables and want to write your own Db table classes, you change the above to:
use My\Own\Table as Table;
and all the code now uses your set of classes.
Also, you don't actually define the class by the long name. What you do is:
namespace Zend\Db\Table;
class Rowset exteds AbstractRowset {
function doStuff() {
$this->fields = new Fields();
if($this->fields->areBroken()) {
throw Exception("Alas, fields are broken!");
}
}
}
Note that here we have used 4 classes from Zend\Db\Table space, but never once had to refer to them by a long name. Of course, in real life it probably won't be as easy :), but the idea is that code - especially library code - tends to be localized - i.e. if you are in Db part of the library, chances are you are using DB-related classes much more than LDAP or PDF classes. Namespacing allows you to exploit this locality by using shorter names.
You are right that namespacing doesn't help with loading - since class should be still loaded using the full name. But once you've got your loader working, you can use nice aliases instead of ugly full names - just as in the filesystem you can use nice relative paths and symlinks instead of ugly full path.
In your example Zend framework, you can throw namespace Zend; at the top of each class file, remove the Zend_ prefix from all of your classes and functions, and you (mostly) don't need to worry about name collisions ever again. Your Zend_Date class can be renamed as just Date without interfering with the built-in date classes. Meanwhile, users of your framework can write Zend\Date instead of Zend_Date which isn't any longer to type, but they now have several other options for accessing the class more easily.
It's probably not worth it to use namespaces unless you have a big project with multiple developers. I would even argue namespaces are overrated.
For example, in Java (since few people use namespaces yet in PHP I couldn't find any similar examples). These are the choices that come up for List in my IDE (Eclipse):
java.util.List
com.ibm.toad.utils.Strings.List
com.ibm.ws.objectManager.List
com.ibm.ws.webcontainer.util.List
java.awt.List
In this case, I don't really see why they couldn't have just kept java.util.List as the only List, and for example renamed java.awt.List to ScrollingList, which actually describes what it is, makes it more obvious that it is a GUI element, and avoids the collision. I would rather type out a longer and more descriptive class name than have to deal with that.
As for one of the above posters, if everyone in your team is making a class called Database perhaps you need to do some design discussion and use only one Database class instead of shoving each person's personal duplicate into a namespace.
Are you really trying to create a new class called Zend_Db_Table_Rowset_Abstract?
The more likely scenario is that you have a local class called something common like Date, Project, User, or something similar or you have a framework that already has those classes. By having namespaces, there shouldn't be any collisions.
In web2project, we've just added namespace support in v2.0 (but we don't require PHP 5.3) because our classes - like Date, DBQuery, Mail, and a few others - collided with things pretty easily. While we don't have an intention to add an external framework to the system, someone else could pretty easily if they wanted.
Are there less verbose ways of solving the problem? Potentially.. but this worked in the Java world with their piles of libraries, so it's not all new space.
Perhaps if you broaden your view a little :)
"As far as I can tell, the only reason we have namespacing in PHP is to fix the problem of classes (+ functions & constants) clashing with others classes of the same name." - yes this is a huge issue which has caused problems for many years in every language that doesn't have namespaces - everybody makes a class Database, Date, URL etc and this fixes it :)
"The problem is that most frameworks setup their autoload and filesystem hierarchy to reflect the names of the classes. And no-one actually require()s or include()s files anymore." - well actually they do, often, just because some common frameworks have had to come up with practises to work around a lack of namespaces, doesn't mean those work arounds or hacks should null and void the 'real' fix to the issue for all :)
follow?
One bad thing about using the autoloader is, that it doesn't know where to look at. My framework already has about 70 classes in about 15 different directories. Because of that, I didn't go for autoload, but instead struggle around with generating paths. So I have an associative paths array that give me paths from my root file to where I want to go. It works, but it's really becoming a pain.
First, the user of my framework will very likely have no big idea where a class resides. So creating a FormViewController is already a pain just because you need the path to include it.
For the beginning, I think it would be just fine if PHP had to "search" for the file through multiple directories when it wants to autoload it. So what? Shall PHP do so and save us a lot of pain. But how could I do that most efficently?
Ther's an array called $paths which I get out of a global context object. The paths could be defined in the array in the most reasonable order: Starting with the most frequently used paths.
I know the PEAR library has a funny naming convention like system_views_specialviews_SpecialView.php which gets converted in the Autoloader to system/views/specialviews/SpecialView.php ...but honestly, that's horrible. Don't want to mess up my file names like this. For the developer who creates his own classes that would suck just straight away.
Before I re-invent the wheel for this task, maybe someone knows a solution for this?
NB: I'm presuming you're creating your own custom autoloader via spl_autoload_register, etc.
If you're creating an autoloader and the classes you're looking to include are spread across 15 different directories, I don't see how you can hope to avoid using some form of class name to directory path mapping, although this obviously doesn't need to be quite as deep as the example you quote.
At a simple level, if you keep all of your system libraries in a single area, then a quick case match as such...
switch(true) {
case substr($className, 0, 6) == 'System': { $libraryPath = 'xxx'; break; }
...
}
...should be reasonably efficient approach, although it obviously depends on how many paths you need to manually check.
Additionally, I presume you're checking for the existance of the class prior to attempting to autoload? (i.e.: You're doing a class_exists($className) to ensure it's not already been included.) Then again, the requirement for this depends on how often you re-use objects.
You might want to look at Nette Framework's RobotLoader.
Basic idea is that it scans through directories you specify, parses .php (token_get_all) files and finds out in which file are which classes/interfaces. Locations of classes/interfaces are cached, so it is fast. Loader is registered with spl_autoload_register() and when PHP interpreter cannot find some class, it calls loader's tryLoad() method.
I'm sure you already know this, but just in case, you can write an __autoload() (API) function that takes the class name as a parameter in your front controller (or in any file included by it) that searches your directories based on any criteria that you want. All you do is require_once the correct file once you've found it, or throw an exception if the class name isn't valid.
From your description, I didn't get any indication that you were doing this. It sounds like you are just appending PHP's include path with values in unwieldy global arrays.
The PHP Autoloader does all you want. If you have different locations you just have to create several instances of the autoloader for each class path.
It seems it does the same like the mentioned RobotLoader, except the first searching is done with some intelligence. Files with lower Levenshtein distance to the class name are prefered as potential class definitions. But this is only relevant for the first search without the index.