Existing class name clash in PHP, need a creative solution - php

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().

Related

Which code is better if I need to use only one static property from PHP class?

If I need to use only ::class property in my code, should I import a whole class like this?
use my\namespace\MyClass;
MyClass::class;
Or should I do this?
my\namespace\MyClass::class;
Which is a faster/better solution? Or there is no difference?
I'm using the Yii2 framework if it matters
There is no difference, it is only matter of code style. Both variants generates the same opcodes, so they work the same and there is no performance difference between them. You can use version which suits you better, but importing classes using use statement have some advantages:
It is more DRY: if you need to use the same class twice, you don't need to repeat FQN twice. It also ensures consistency - there is no risk that in one place you use my\namespace\MyClass and in other some\vendor\namespace\MyClass.
It generates less fuss on refactoring - changing class namespace will only affect import section without touching actual code that uses this class (no meaningless changes in git blame).
"import everything" is easier to follow and configure CS fixers to ensure this as CS rule.
As a result you always have a nice list of classes used in specific file, which may help you find some bugs ("uh, this should be my\namespace\MyClass instead of some\vendor\namespace\MyClass") and architectural issues ("hmm, there is 60 classes imported in this file, maybe it has too much responsibilities...").
Namespace :
Both are valid and working properly but in terms of simplicity you can declare it in one line
my\namespace\MyClass::class;

Why do I have to re-declare my Classes in an included file?

I'm building a website for a client, and he wishes to have a calculator for clients to calculate their monthly premium when they want to lease a car. This calculator appears in three different places in the website, so I decided to build it once, then include() the file into the main file. For instance:
case 'calculator':
case 'bereken-leasebedrag':
include('calculator.php');
break;
However, I have several classes defined in the main file:
use site\database\Vote;
use site\database\Vehicle;
use site\database\VehicleType;
use site\utils\ApiConnect;
use site\utils\Calculator;
And some more.
Some of these classes are used by the calculator file, such as Calculator and Vehicle and a few others. These classes are defined in the main file, but are not transferred to the calculator file, whereas variables like $_SESSION, $_POST and even my own defined variables like $car and $calc are available in the included file.
If I don't re-declare my classes at the beginning of calculator.php, it throws a fatal error in the page, saying it can't find, for instance, the class ApiConnect.
Am I doing something wrong here? Logically speaking, included files should inherit pretty much everything, and it seems counter-productive to have to specify every class needed for the included files.
I'm terrible at explaining things, so if I need to elaborate on something, I'd be more than happy to.
Thanks to deceze, I learned that use is used for aliasing, something I didn't derive from my Google searches.
Apparently the autoloader automatically includes all the classes we have, we just manually apply aliasing for every class. Which is kind of weird, but there you go.
Long story short, I had to either apply aliasing in the included file so that I could just say new Calculator(), or remove the aliasing and change every new {CLASS}() to new \{PATH}\{TO}\{CLASS}. I personally went with the first option, but if you have an autoloader and are running into thesame problem, at least you have a choice.

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

WordPress Class Name Conflicts

I've been creating WordPress plug-ins for some months now and I have developed a set of "base classes" (most are abstract classes but not all of them) that I use with the plug-ins (some are used with all plug-ins, others are used when a plug-in needs the specific thing the class provides - maybe an admin screen or a button on the visual editor or whatever).
This, of course, works great BUT if I update one or more base classes to add some new functionality then if someone has more than one of my plug-ins installed it is very possible for one or more of those plug-ins to crash.
This is because if a plug-in using an older version of the updated class(es) loads earlier then it "reserves" that name and the others that require the updated class don't have the functionality/fix/etc.
I've tried "nice" solutions but the only thing I can get to work reliably is to change the actual base class names for each plug-in, which is far from ideal.
Namespaces obviously work but can't be used because so many popular hosting companies are using 5.2x versions of PHP (probably why WordPress doesn't require 5.3 either)
I tried to dynamically create the class names and to use class factories but they didn't work or didn't overcome the need to rename all the base classes. I even tried (although I didn't expect it to work but you never know with PHP):
class childClassName extends $parentClassName
This situation can't be that unusual (do people really rewrite everything from scratch everytime and/or use distinct class names) but I can't find a solution and I can't think of an elegant one. It isn't even a WordPress issue, multiple applications using common base classes within a company would have the same problem.
I also can't just force everyone to upgrade all their plug-ins every time because it may crash before they can and I'm not sure it's reasonable for me require that of them or even that I can personally update and re-test, every time, every one of the what's now almost 10 plug-ins with more in the future.
My currect best solution is to use factories to create all base classes and to put tokens in my base classes that I can do a global search and replace on so that the class names are unique for each plug-in; note the replaceable tokens alone would work.
Is there some PHP code that will allow me to achieve a simple, easy solution to this common class name conflict issue?
========================================
It doesn't look like anyone knows how to do this in PHP, maybe it can't be done, so I went with the way I mentioned above.
I thought I'd put it here because this is what I ended up with that works well, if not what I had hoped, and maybe it can help others:
I added tokens to the base class names, for example:
class someBaseClass[TokenNameGoesHere] (I used [ReplaceWithVersionNumber])
also
class someChildClass extends someBaseClass[TokenNameGoesHere]
I created a method called createNewClass and passed the class name (as a string) and any parameters to pass to the new class (in an array).
I also passed information like the token value being used in the application (version number in my case) and the namespace (to be used instead of the token value if I can use PHP 5.3 and above).
I then used reflection to create the class. While reflection is obviously slower I found that I didn't have that many times when I needed to use createNewClass so the trade off of ease of remembering and increased clarity won out.
$reflectionOfClass = new \ReflectionClass($desiredClassName);
$newObject = $reflectionOfClass->newInstanceArgs($ParametersToPass);
Where $desiredClassName is built from the class name and the other parameters: version and namespace in my case.
Then, everywhere I wanted to create a base class object I'd use:
$myNewObject = $this->createNewObject("someBaseClass", other parameters);
When I copied the class library over for a new application I just did a massive search and replace of the token with what I wanted to replace it with in all the files. It could be version number or even an empty string if I don't have to worry about clashing with other versions of my class library like I do with WordPress plug-ins.
It was pretty easy to do and works great although going back through and inserting the tokens in was tedious.
It all could, of course, be done with just tokens but I didn't want to have to remember to do put suffixes on the names and which token name I was using and whatnot.
I hope that helps someone and if anyone has a better way (which to me means less code modification and more code clarity) I'd love to hear about it.
Not sure if you've found a proper solution to your question yet. I'm dealing with the same problem and my approach (not perfect, but does what I need) would be this one:
Create a class alias of your reusable class from a variable, like this:
$aliasclass = $really_unique_prefix."_Prefix_MyPlugin_Class";
class_alias("Prefix_MyPlugin_Class", $aliasclass);
new $aliasclass();
Hope it helps!
Yes namespace is perfect solution.
If php could have provided way to unload / load classes problem could have be easily addressed. But again you need to relink / instantiate your other depended active plugins so as to used object of new classes, this involves deactivation and activation of existing plugins.
With same thought I can think of this solution, you very well know you have 1,2,3 .. n plugins using X class. 1 & 2 plugins are using older version of X and you are about to install / update plugin 3 with newer version of X. Deactivate all your plugins & first activate plugin (3) which has your latest features/version of class X, later activate all others.
$all_plugins = array(
ABSPATH.'wp-content/plugins/plugin_1/index.php',
ABSPATH.'wp-content/plugins/plugin_2/index.php'
......
ABSPATH.'wp-content/plugins/plugin_n/index.php'
);
if (class_exists('X') && X::version < currentXVersion) {
deactivate_plugins($all_plugins);
$plugin_3_lastest_class_X = ABSPATH.'wp-content/plugins/plugin_3/index.php';
array_unshift($all_plugins, $plugin_3_lastest_class_X);
activate_plugins($all_plugins);
}
*provided if its ok to deactivate / activate your plugins & you don't modify signature of existing functions of class X, you can add new functions though.

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