I am currently working as a student on php project which has grown since the beginning of time and has about 1800 php-files.
The problem is: it is completely without namespaces, or any of the PSR-4, etc. recommendations. The technical debt is strong with this one :).
We want to use composer (and twig and some libraries more) and having problems including this (especially composer). I think it's because of the overwrite of __autoload() via spl_autoload_register() in the composer-autoloader?
Is there a good and fast way to start integrating namespaces without rewriting the whole project?
You can still use Composer with PSR-0 or classmap.
I'd probably go with the classmap first. Advantages: Can deal with multiple classes per file. Can deal with arbitrary file structures.
Once you achieved using Composer's autoloading, you can start removing either the existing autoloader or those require_once/include_once that are likely spread all over the place.
Once you've got rid of all that file loading legacy and have established Composer autoloading, you can try to organize the code according to PSR-0. This will probably require you to rename files and reposition them. It might also be the case that the classes don't have any identifiable prefixes - which is bad for your PSR-0 autoloading because all these files would belong into one single folder.
Note that until this point you haven't changed the name of any class, so the code should run without any changes.
Using namespaces does not have a very noticeable advantage. You'd be forced to rename all classes, rename all usages of that class name, and all in all it won't provide any noticeable benefit if used alone.
On the other hand, you sound like you do want to refactor everything else as well, so switching to namespaces can be used as a signal for "newer" code.
You can use PSR-4 and PSR-0 at the same time, so it won't affect your renaming of classes (besides the necessary class name changes in all the places).
Related
I've been progressively starting to use Composer on my PHP projects, however there's something that I always questioned.
For instance, let's say this is my composer.json file:
{
"require": {
"ramsey/uuid": "~2.8"
}
}
I'm telling composer I want the package ramsey/uuid and as expected it download and includes the package.
When I want to access the classes on the package, I'm forced to something like:
$uuid = \Rhumsaa\Uuid\Uuid::uuid4();
Is there a way I can require the package and force a simpler namespace like for eg. \Uuid::uuid4();, avoiding the need to write the full NS including package author?
What should I change on my composer.json to be able to do this? Thank you.
Note: I'm aware of PHP's use. I could use Rhumsaa\Uuid\Uuid;... However I need to do this in every single file, it's not practical. Even less if I'm putting together a small self-usage framework. I want for instante to have \Util\UUIDmapped to \Rhumsaa\Uuid\Uuid.
I also think the file autoload_psr4.php could be changed to accomplish this, however after an update all changes are discarded.
No, there is nothing you can do to shorten these namespaces.
Composer doesn't have to do anything with PHP namespaces. The name you use to identify the software package is unrelated to the PHP namespace. They don't have to match in any way. Composer only provides an autoloader that will include the source code of a class you are using in your code. The possible alteratives would be to either load that source code manually using include_once() or require_once(), or create an autoloader on your own that does this.
Now that Composer is out of the way, we can discuss namespaces and class names. Before PHP 5.3, there were no namespaces. Classes either were not generally used in multiple projects because they were created with short names that likely would conflict with other classes bearing the same name, or they were extended to contain a distinguishing component in their name, like Zend_Controller_Abstract or sfController (from Symfony 1).
These class names also tend to get very long, especially with the invention of PSR-0 autoloading (which was for a very long time the only PSR standard ever defined).
With namespaces you get at least a method to shorten those class name references in your code.
You have to either use the original, long form in every place you use, or you have to import it with a shorter alias. Yes, you have to repeat the import clause in every file - but you don't import EVERY class into EVERY file, you would only import the classes you actually use.
Using an IDE is very helpful with these tasks. They offer you a way to search in all available classes for the one you want to use but can't remember. They also deal with importing namespaced classes into your file. You'd rarely ever need to manually "add the imports everywhere".
In composer documentation section about autoloading I found at least two means to load classes that not corresponds to psr-0/4. First one is to specify classmap property of composer.json file and second one is fill include-path property in my composer.json.
As I can see include-path is more plain feature while classmap caused scanning for classes in specified locations. Who can explain which one should I use in different cases?
Avoid using include-path. It is meant for old software that expects the include_path setting to contain some directories, so that require_once "Relative/Path/To/Class.php" works from ANY location (think of the way PEAR works). Using too many paths impacts performance, because PHP needs to scan starting from the first directory, until it finds the relative path requested.
Classmaps are an always working solution if the classes do not conform to PSR-0 or PSR-4. Autoloading works by knowing the name of the class and finding out the file this class is contained in. PSR-0/4 define a way to know the file name by using and splitting the class name. Classmaps however know every class name and their file name directly. The bad thing about classmaps is that if they grow too large, they also affect performance because loading a huge classmap and then only using about 1% of the contained classes has a big overhead.
include-path and classmap are not mutually exclusive. In fact, they might be both needed: To load the first class, you'd need the classmap (otherwise you'd be forced to explicitly use require_once), and if that file will load dependencies using relative paths inside require_once (and does not know about autoloading), a proper include path has to be set.
If you ever have the chance to change it, I highly recommend to avoid setting the include path, and only use the classmap feature to autoload classes (which means you should remove any include/require functions in that code base). It would be even better if your code can be transformed to be PSR-0 compliant, but this usually is a huge rewriting task for old code with barely any benefit. You'd have to worry about performance only if you really have a HUGE framework with many files and classes - which usually is not the case with older code bases.
So my login check I have split into two sections that I tried putting in two different files and including them. First I will show the original code then the split code. I think my problem is with the link to the include. I try to use an absolute path. But it seems to be failing. My whole App is modular so I have global files set up outside of the rest of the App structure and I call the files as needed. I have also thought of just loading the functions through _autoload() but I don't know that this would solve my issue.
<?
//Inventory index.php
include$_SERVER['DOCUMENT_ROOT'].'/Globals/db_connect.php';
include$_SERVER['DOCUMENT_ROOT'].'/Globals/functions.php';
sec_session_start();if(login_check($mysqli)==true){?>
/////Html and or more php code to be executed. Usualy a mix of both.
<?}else{
echo ("You are not authorized to access this page, please login. <br/>");}?>
here is what I am trying to do....P.S. I know my code is kinda hard to read I am trying to format it for easier reading but I can process clumps better than I can spaced code. Not sure why.
<?
//Inventory index.php
include$_SERVER['DOCUMENT_ROOT'].'/Globals/auth1.php';?>
/////Html and or more php code to be executed. Usualy a mix of both.
<?}else{include$_SERVER['DOCUMENT_ROOT'].'/Globals/auth2.php';?>
What I think is happening is the includes in Auth1 are failing. are my absolute paths failing? Am I better off using _autoload().
You should always choose "autoloading" in favor of manual includes for multiple reasons.
Readable structure -- one class for each file and one folder for each namespace segment allows you to find anything in the directory structure of your project very fast.
Easy maintenance -- if you change the folder structure/position of your files (as well as their namespaces) you're done, no paths to be rewritten and verified.
Compatibility with other projects for example using PSR-0 or PSR-4. PSR-0 is meant for covering legacy code, PSR-4 should be used for new libraries.
Compatibility with package management software -- which expect some kind of predictable conventions to be respected. For example Composer expects either PSR-0 or PSR-4 and it enables autoloads for you.
On the subject of coding standards and formatting the same applies:
familiar structures are easier to recognize.
The decision is perfectly arbitrary; it's always your (or your team') choice. Either will work fine -- you will get used to any one of them.
Consistence is important, PSR-1 and PSR-2 are the lingua franca of both Symfony & friends as well as many other projects which use composer as their package management (and others).
I can suggest to use composer and it's autoloader which support different types of files loading. For production you can use --optimize option to make autoloading works better (as I remember composer scan all folders and build array of existing files)
I know this is highly unlikely but thought I would ask anyway.
I am using DomPdf to render pdfs.
Currently it does not implement namespaces and loads a lot of classes.
I can autoload the library with composer and "classmap": ["include/"].
Is it in any way possible to autoload this library implementing a custom namespace in order to avoid possible class name conflicts?
Or is there a tool to search and replace in a library to add namespaces?
I would prefer not to touch the library and was just wondering if there is some way this could be done with composer's autoloader.
(I don't currently have conflicts but would like to keep my libs from running into future issues by implementing namespaces wherever I can.)
Conflicts would arise because of two classes having the same name, the same (or absent) namespace, but reside in different files (and paths) and contain different code.
Composer autoloading cannot do anything about this.
If you come into this situation, you have to resolve it on the code level first, i.e. you have to rename one of the classes, probably moving it into a namespace and keeping it's name as a quick help. Effectively renaming it means to fix every other line of code that has references to the old class.
Fortunately this will only happen if you try to include new code into your project, so you'd be able to stop using whatever you started importing, and look around for an alternative instead.
What solution would you recommend for including files in a PHP project?
There aren't manual calls of require/include functions - everything loads through autoload functions
Package importing, when needed.
Here is the package importing API:
import('util.html.HTMLParser');
import('template.arras.*');
In this function declaration you can explode the string with dots (package hierarchy delimeter), looping through files in particular package (folder) to include just one of them or all of them if the asterisk symbol is found at the end of the string, e.g. ('template.arras.*').
One of the benefits I can see in package importing method, is that it can force you to use better object decomposition and class grouping.
One of the drawbacks I can see in autoload method - is that autoload function can become very big and not very obvious/readable.
What do you think about it?
What benefits/drawbacks can you name in each of this methods?
How can I find the best solution for the project?
How can I know if there will be any performance problems if package management is used?
I use __autoload() extensively. The autload function that we use in our application has a few tweaks for backwards compatibility of older classes, but we generally follow a convention when creating new classes that allow the autoload() to work fairly seemlessly:
Consistent Class Naming: each class in its own file, each class is named with camel-case separated by an underscore. This maps to the class path. For example, Some_CoolClass maps to our class directory then 'Some/CoolClass.class.php'. I think some frameworks use this convention.
Explicitly Require External Classes: since we don't have control over the naming of any external libraries that we use, we load them using PHP's require_once() function.
The import method is an improvement but still loads up more than needed.
Either by using the asterisk or loading them up in the beginning of the script (because importing before every "new Classname" will become cumbersome)
I'm a fan of __autoload() or the even better spl_autoload_register()
Because it will include only the classes you're using and the extra benefit of not caring where the class is located. If your colleges moves a file to another directory you are not effected.
The downside is that it need additional logic
to make it work properly with directories.
I use require_once("../path-to-auto-load-script.php.inc") with auto load
I have a standard naming convention for all classes and inc files which makes it easier to programaticaly determine what class name is currently being requested.
for example, all classes have a certain extension like inc.php (so I know that they'll be in the /cls directory)
and
all inc files start with .ht (so they'll be in the /inc directory)
auto load accepts one parameter: className, which I then use to determine where the file is actually located. looping once I know what my target directory is, each time adding "../" to account for sub sub pages, (which seemed to break auto load for me) and finally require_once'ing the actual code file once found.
I strongly suggest doing the following instead:
Throw all your classes into a static array, className => filepath/classFile. The auto load function can use that to load classes.
This ensures that you always load the minimum amount of files. This also means you avoid completely silly class names, and parsing of said names.
If it's slow, you can throw on some accelerator, and that will gain you a whole lot more, if it still is slow, you can run things through a 'compile' process, where often used files are just dumped into common files, and the autoload references can be updated to point to the correct place.
If you start running into issues where your autoloading is too slow, which I find hard to believe, you can split that up according to packages, and have multiple autoloading functions, this way only subsets of the array are required, this works best if your packages are defined around modules of your software (login, admin, email, ...)
I'm not a fan of __autoload(). In a lot of libraries (some PEAR libraries, for instance), developersuse class_exists() without passing in the relatively new second parameter. Any legacy code you have could also have this issue. This can cause warnings and errors if you have an __autoload() defined.
If your libraries are clear though, and you don't have legacy code to deal with, it's a fantastic tool. I sometimes wish PHP had been a little smarter about how they managed the behavior of class_exists(), because I think the problem is with that functionality rather than __autoload().
Rolling your own packaging system is probably a bad idea. I would suggest that you go with explicit manual includes, or with autoload (or a combination for that matter).