This question is a bit odd and really hard to explain so I'll try my best.
Basically, I've been wondering how to actually use classes inside their own files and then use the namespace for that class elsewhere.
As an example most PHP developers might know, like Laravel does with routes, you have routes.php file that is for routes only, there's no namespace defined at the top e.g: use Acme\Router - However, the class still works and doesn't spit any errors. How is this accomplished?
Example
My index loads in composer autoloader which also loads my application class
<?php
// Used namespace here to actually make it function
use Acme\Application as App;
require_once __DIR__ . '/vendor/autoload.php';
$app = new App;
$app->launch();
The launch is for things that the application should do once launched, here I am thinking loading in the routes and etc. (I am not sure if this is the proper way to handle this, but it's my best shot.)
The class is like so:
<?php
namespace Acme;
class Application
{
public function __construct()
{
}
public function launch()
{
// Require routes etc.
}
}
What I was thinking initially was requiring the files, but I was thinking it would be bad practice to require files within a method. And even so, that would also become a huge list of things it should require.
However whatever I have tried didn't work out, what I am trying to do is avoid having use in my routes.php or etc to avoid a list of things I want to use such as:
use Acme\Database\Handler;
use Acme\Router;
because in the long run, this can become pretty massive.
What am I doing wrong and how should I actually approach this?
You probably didn't clearly state your question, so I'm doing a bit of guessing from my encounters with less experienced developers from the past.
I think we can stop discussing "requiring the files", because you should use autoloading to make classes, interfaces or traits available. And it is easy to do so because you are already using Composer. Check.
The remaining point is that you are afraid to add a huge number of "use" statements to your Acme\Application class because of the size of that list. There's two answers to that.
First, there is a bit of basic PHP knowledge about the "use" statement. You can address explicit classes with it, like you did in use Acme\Application as App;, but you can also address namespace prefixes, like use Acme\Models; and make use of it like this: new Models\User. This will both shorten the list of use statements (because one statement will cover more than one used class), and also make the code more readable because you have to add more namespace as context (new User; is less descriptive than new Models\User). Readability is a good thing, because you'll write the code only once, but read it plenty of times thereafter. And you don't want to invent alias names every time you encounter ambigous class names (which is a strong indicator that you should add that piece of context, and on the other hand always inventing alias names will likely start to confuse the reader).
Second, if you have to deal with a lot of classes, you have to deal with a lot of classes. There is no other way around but to separate the task that is dealing with all these classes. Then you still have to deal with all of them, but not in the same place. And you probably don't want to do everything that's done in this launch() method every time unless you know your application will need it right now in this request.
You may want to read about two topics: Dependency injection, and lazy loading, because I think your question is more directed towards application architecture.
So after more research into this I found out what I was trying to achieve was Facades not application structure, as I said it was hard to explain so I'm going to throw the solution to my problem out there.
Basically since this approach comes from using Laravel a lot I asked in the Laracasts forum which gave me exactly what I was looking for, it has a link to a working repository as well.
Thanks for the help regardless!
Solution including repository
Related
I have a namespace-based code, with a "Model" folder. I call my models statically everywhere in my code:
\Myapp\Model\PersonModel::doSomething()
Now, I would like to distribute my application in several countries, and be able to override some features of PersonModel for some countries (and add models for some countries, and so on).
I will have:
\MyApp\Model\France\PersonModel::doSomething()
I would like to be able to use the specialized version, with that in mind:
Not modifying too much code
Keep my IDE code completion
One solution would be to specialize every controller for every country, and use fully qualified names everywhere, but I'm not sure it is realistic (time consuming and maybe even not functional).
Another solution would be to override every model, and add a function somewhere that would give me the name of the fully qualified class (for the current country), but the code would become ugly, and I would lose code completion.
A third solution would be to have a repository for every country, and try to modify everything so that the core code is in a Git submodule and everything else is specialized classes... It seems to be the best solution to me but it seems like a Titan work, and I would prefer to have all countries inside the same repository to keep things under control.
I have really no other idea. I'm sure I'm not the only one with that problem, but I searched and found nothing. Please, tell me there is a magic feature for namespaces that I wasn't aware about, or something? :)
Edit: this is partially solved
I am now using a custom autoloader, which will load the proper country-specific class. But all country-specific classes will share the same namespace (which will work because we are using only 1 country at a given time). However, I lose code completion, but it's a tradeoff I'm ok with. If someone has a solution that would also allow to keep code completion, please feel free to answer!
Ok I found a globally satisfying solution:
All country-specific classes will share the same namespace (for example "Myapp\Model\Custom*"), which will not cause conflicts because there is only 1 country at a time.
A custom autoloader will load the proper class/file based on the current country.
Inconvenients of this solution are:
Not possible to "use" multiple countries at the same time (which is not the goal, so it's ok)
Losing code completion :(
Advantages are:
Much less time consuming and hair-pulling than creating a repo for each country
Globally clean code and code structure
Please feel free to post a more elegant solution, I will be glad to read it (and accept it)!
Another idea, following on from my thoughts in the comments. Move this:
\Myapp\Model\PersonModel
to here (random location, as I don't know your default territory):
\Myapp\Model\Germany\PersonModel
You'll now have two classes with a country in the namespace, and of course you can add more. You can now add a new generalised class, thus:
namespace \Myapp\Model;
/*
* Document your methods here using #method, so you get autocompletion
*/
class PersonModel
{
public function __callStatic($name, $args)
{
// Convert the call to an appropriate static call on the correct class.
// The first parameter is always the country name, which you can examine
// and then remove from the list. Throw an exception if $args[0] is
// empty.
}
}
You can then do this:
\MyApp\Model\PersonModel::doSomething('France', $args);
The use of #method (see here) will ensure you retain auto-completion, and you can learn more about the call magic methods here.
Of course, make sure your language-specific classes refer to a parent class, so you are not duplicating large blobs of code. Some methods may also not care about language, in which case they can go in \Myapp\Model\PersonModel, with the benefit that they do not need manual phpdoc entries.
Consider a large application which autoloads classes as such:
function __autoload($class)
{
require_once("/var/www/application/classes/{$class}.php");
}
There may be tens of files which use the class, all of which relied on a particular bug in the class. Now that I've fixed the bug, I need to update all the places where the class is called.
From within the class, I would like to whitelist known-fixed places where the class is called from where I have fixed the bug. For the un-whitelisted places, I would replicate the old behaviour and in the whitelisted places I would give the correct behaviour. Is there a clean way to do this?
I was thinking about adding a new constructor argument $useFixedVersion for the duration of the fix (which might take a few days), and then going back and removing that argument afterwards. However, this means that I need to go over each place twice and it seems a rather bad workaround. Is there a better way to detect from where the class is being called? Consider that both instance and static methods are affected.
Let us ignore the coupling and bad code design for a moment which means that I need to correct the calling code, and take this unfortunate fact as a given. Let us also ignore the unfortunate fact that I cannot simple branch off in Git, make my fixes, and then push to production!
There is a debug_backtrace() function, which would allow you to know where things are getting called from:
http://php.net/manual/en/function.debug-backtrace.php
That said, have you considered using namespaces for this? Something like:
namespace Foo\Bar;
class FixedBaz extends Baz {}
And then alias the classes as needed:
namespace Foo\Bar;
use FixedBaz as Baz;
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.
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?
I realize that you cannot "undeclare" or "redeclare" a class in PHP, nor can I use extension methods as in C# > 3.0. So, here's my situation.
I'm working with someone else's code base. They must have been using a slightly older version of PHP where a DateTime class did not exist, so they included an open source library that adds Time, Date, and DateTime classes. Well, I go to deploy this to the client's server, and this class is already defined. To resolve the crash it causes, I add a "if class is not defined" check, however it seems that (as you'd expect) the api of the classes are not exactly the same, and somewhere in the code it may or may not be using undefined methods of the DateTime class included with the version of the language being used.
Are there any ways to unload a class, or any creative ways you can think of to get around this. I'd prefer not to have to search all files for all references and maybe rename the class. That sounds messy, but I guess it may be my only option. I'd much prefer a fix that happens in one place and one place only.
Thanks.
You may want to enclose the class definition inside a Namespace. Then you declare a "using namespace xxx" ( I don't remember the exact PHP syntax ) inside every file that uses the current DateTime class.
This is not exactly a "one place and one place only" solution, but, at least you don't have to replace every reference to the class, just reference the namespace at the beginning of the files.
Hope this helps.
Some good ideas here...
Using a Namespace - suggested by Petruza - is probably the best way to handle it... but it's PHP 5.3 only.
Alternatively, Robert's suggestion of refactoring the code is a reasonable idea but will likely take a good deal of time and effort. Since this will have to be done eventually, you might want to do that.
Alternatively, if getting the system online is the top priority and you can refactor it afterwards, you might be able to rename the class and all references to it. By adding a prefix - DateTime to company_DateTime - you're namespacing it the old way and your classes should coexist without much difficulty. Of course, if you have to update the supporting libraries any time soon, this is going to make life more difficult, so don't blindly jump into this one...
Perhaps this can help: Unload dynamic class in PHP.
I would attempt to migrate the code base to the more recent version of PHP. If you are going to use the code in production on the new PHP version, then you need to test it on that version anyway. If you do not want to invest the efforts to update and test the code base you should probably require the older version to be installed on the target system. Otherwise there is no way you can guarantee that the code you have works correctly.
And with proper tools searching through a ton of files and identifying all occurrences of those classes isn't too hard.
Well, this solution is really ugly and it should be used only as a last resort. Example, in Opencart the Category classes of "admin" and "catalog" both have the same class name, and if you want to use both within the same code piece, that is a really difficult task to do. So a workaround is to just rename the class which you are going to include. Something like this:
$file = file_get_contents("path/to/file.php");
$file = str_replace("OriginalClassName", "ModifiedClassName", $file);
// must remove <?php tag from the beginning
eval(substr($file, 5));
then instead of new OriginalClassName() use the new class name new ModifiedClassName().