Load a particular version of a class when Autoload is used - php

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;

Related

How can I specialize a class of a namespace and use it without modifying too much code?

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.

PHP exclude namespace usage from file

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

How to use one object in another class : PHP framework

This is my current system for a framework that I'm making:
I create an object upon a request to access a non-existing property, that has the name of an existing class through the __get function defined in the Core class. The Core class extends every class.
So it works like this:
class Someclass extends Core
{
public function classmethod()
{
$this->otherclass->method();
}
}
This works exactly how I want it to work. However, I have no idea (after a lot of frustration) how to edit/create properties of the object, using this system.
So something like this would't work.
$this->view->somevar = "newvalue"; // this doesn't work.
I guess it has to do something with the __set function, but I failed to find out.
I received the following suggestions how to tackle this problem:
Dependency injection, Namespaces, Singleton pattern.
I would love to see how to implement what I'm trying to do using one of these patterns.
I have no idea which to choose in order to achieve my simple problem: Use objects in other classes, so i don't need to put anything into __construct's parameters or using global variables.
I am using __autoload.
I hope i can tackle this problem with your help.
First of all, the architecture you're attempting is extremly bad. Aparently you are using "magic" to create objects attached as properties to other objects. Which is bad, because if you use _get/_set, you will end up in problems sooner rather than later. Also extending from a common parent is a bad thing because it generates huge inheritance trees, and it allows most of the objects to have more than one responsability.
Before starting a framework, you might want to look over: SOLID
Now coming back to your question, if you use __get for getting a value, you can as well use __set for setting a value.
__construct method is there in order to allow you to initialize the object with all the data it needs in order to fulfill his sole purpose. Avoiding the __construct is kinda stupid, and defeats the purpose. You might want to pay attention to the D in SOLID in order to see what Dependency Inversion really is and to understand it.
Using __set loses completely the ability to typehint interfaces and so on. Therefore the code can become really buggy and ijcredibly hard to follow, since the flow is not so well defined, and the allocation is done in the back via magic. Also i can come up with 1 million other reason for which the architecture you are trying to use is wrong, but i will leave that for another time.

A group of related PHP functions: what is the most acceptable way to organize them?

This might be a stupid question but I have to ask:
I have a big group of related functions for a project I am doing. The functions need to access a few global variables, so I was thinking about putting them into a class and loading the class as needed. I suppose my other option is to just include them as unrelated functions in an included PHP file, but putting them into 1 class seems to make sense. Is this an acceptable practice? I have worked with people who did this but it always seemed to not quite be in the spirit of good OOP practices because the classes were almost never instantiated but the functions were still called. Or maybe I'm over thinking it.
Any input would be awesome, thanks a bunch.
A class does make the most sense. Whenever you can eliminate global variables, it is a good thing. Whether the class is instantiated or a static helper usually depends on the context. However, for future unit testing, instantiations allow dependency injection.
According to http://en.wikipedia.org/wiki/Class_%28computer_programming%29, a class defines constituent members which enable class instances to have state and behavior. If you will be providing only behavior (functions) and not state (properties), you should include your functions in an include file and forgo the overhead of a class.
Correct me if I'm wrong but this seems like you should create a class that acts as a static service, where no explicit instantiation is needed of the class, yet you will still call the methods contained within this class.
Now, if you're thinking of storing those global variables in the class, obviously that's no longer a static class because there would have to be some sort of lifetime for the object, and at which point you'd have to instantiate the class first and then make calls to those methods.
Regardless, if they're all related functions working on the same data, it certainly makes sense to group them within their own class.

Are namespaces really all that useful in frameworks?

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?

Categories