I am having a problem with my namespace fallbacks and using PSR-4 loader in Composer.
What I am trying to do is this:
Have a core which can overwritten / extended.
The core is based off an interface.
The directory structure is like so:
site/app/View/Example.php
site/src/ACME/app/View/Example.php
site/src/ACME/app/Interface/View.php
I am not set on this configuration so if you have a better suggestion then go for it.
My composer json is like so for psr-4:
"autoload": {
"psr-4": {
"ACME\\App\\Site\\" : "app/",
"ACME\\App\\" : "src/AMCE/app/"
}
}
I thought this would make ACME\App\Site\View fallback to ACME\App\View if the site one was not found (Note I haven't done the interface part yet...).
My code for site/app/View/Example.php is like so:
namespace ACME\App\Site\View;
class ViewExample extends View {
Which works, when I have site/app/View/View.php as well. That looks like:
namespace ACME\App\Site\View;
class View extends \ACME\App\View\View {
The site/src/app/View/View.php look like this:
namespace ACME\APP\View;
class View {
This one should use the interface (I haven't tried yet).
So what I really want to do is make it so I don't have to have site/app/View/View.php, and I don't have to have site/app/View/Example.php - it can use site/src/ACME/app/View/Example.php.
Sorry I'm new to namespaces so I may not of phrased it very well.
What I am getting at is I thought ACME\App\Site would fallback to ACME\App - it doesn't? Or I am doing it wrong? At the moment it needs all the files in place.
Edit: Turns out I was originally wrong, it is possible to get your example working with PSR-4! You just need to specify an array of directories for namespaces that can be loaded from different places.
Easy solution
{
"autoload": {
"psr-4": {
"ACME\\App\\Site\\": ["app/", "src/ACME/app"],
"ACME\\App\\": "src/ACME/app/"
}
}
}
Personally, I would rather name my namespaces more explicitly, see below.
Original Answer
The composer PSR-4 loader does not fall back when trying to load files that do not exist. It just fails immediately. Its flow looks like:
\ACME\App\Site\View is not loaded
Scan PSR-4 entries for matching namespaces
Class name matches the namespace \ACME\App\Site (your first PSR-4 entry).
Load file app/View.php
File does not exist. Error.
It never goes back to step 3 and tries the next namespace.
So how do I fix it?
It looks like you want to separate your reusable library code from your site code. If that's the case, I would use separate namespaces. For example, use the ACME\Site namespace to hold your reusable code, and use ACME\MySiteName for your site-specific code. Then there will be no ambiguity, and composer will have no trouble loading your classes.
But I don't want to rearrange my namespaces!
Ok, that's fine, but you'll have to use a hack to get around your problem. Composer has a classmap loader, and you'll have to use that instead of the preferred PSR-4 loader.
{
"autoload": {
"classmap": ["app/", "src/"]
}
}
Let's separate the things a bit, because they are all mixed up for now.
What I am trying to do is this:
Have a core which can overwritten / extended.
The core is based off an interface.
This sounds like basic object oriented inheritance. An interface defines the proposed public behaviour, the core implements the needed basics, and the detail implementation changes some parts, and reuses the others.
Let's write your example code in a way PHP sees it with absolute namespace names:
class \ACME\App\Site\View\ViewExample extends \ACME\App\Site\View\View {}
class \ACME\App\Site\View\View extends \ACME\App\View\View {}
class \ACME\App\View\View {}
You have three classes explicitly named. You'd need three files that match the namespace and class name. The autoloading does not need to do any detection whether or not a class is present - because you cannot optionally inherit from a class that isn't there, or omit it otherwise.
On the other hand, implementing three levels of inheritance by default very likely is too much. It looks like bad design to me, and will make maintaining the code harder than necessary. Depending on what you want to achieve, there are plenty of alternatives to get what you want easier. For example, to change some details of behavior, there are the decorator pattern or the strategy pattern.
So what I really want to do is make it so I don't have to have site/app/View/View.php, and I don't have to have site/app/View/Example.php - it can use site/src/ACME/app/View/Example.php.
You cannot have this. Your code explicitly states that it inherits from \ACME\App\Site\View\View, so this class MUST be present somewhere.
This is independent of any autoloading. To experiment, you can add all your code into one single file and then run it. This will make all classes known to PHP immediately, and the problem will become obvious: You cannot remove a class when at the same time other classes inherit it.
Sorry I'm new to namespaces so I may not of phrased it very well.
Namespaces are nothing really fancy, the same problem would arise if you would use the PSR-0 style classnames with underscores:
class ACME_App_Site_View_ViewExample extends ACME_App_Site_View_View {}
// This class MUST be present for the above class to work
class ACME_App_Site_View_View extends ACME_App_View_View {}
class ACME_App_View_View {}
The main new feature with namespaces is that you can import one class under a second name within a file with use OtherNamespace\Classname. But this is only an alias within the scope of this file (i.e. it does not affect other files or the global scope).
Namespaces and autoloading are not the right tool for this job. A namespace is just a way of making sure two people (or parts of your code) don't use the same name to mean different things. Autoloading is just a way to avoid having to list every source file you want to load code from.
When you override the behaviour of one class in another, these are not the same class; often, you'll want to inherit the default actions and reuse parts of them.
You might want to create several sub-classes for different purposes, so you need somewhere to hold the logic of which to use. The component which deals with this is called a "service locator" or sometimes a "DI container".
Namespaces let you map short names to longer, unique class names; autoloading let's you map a specific unique class name to a source file; service location is how you choose which unique class you want to use in a specific circumstance.
Related
I've been reading a lot of posts on StackOverflow but I'm not really sure I'm using namespaces, autoloading, and aliasing correctly. This is functioning fine, but I'm not sure I'm properly using these concepts. I've listed some reasons why I think this setup is incorrect at the bottom of the post.
Imagine the following directory structure:
\public_html
- index.php
\Classes
\A
- One.php
\B
- Two.php
One.php is structured like:
<?php
namespace Classes\A;
class A { ....
Two.php is structured like:
<?php
namespace Classes\B;
class B { ....
Then, in index.php I do something like:
<?php
use Classes\A\One as One;
use Classes\B\Two as Two;
spl_autoload_register(function ($className) {
...
});
... ?>
So, a couple things that bug me about this:
If I am doing aliasing (the "use" statements) I still need to list out all of the files. Aren't we trying to avoid doing this by using autoload?
If I want to use internal classes, I need to add a line such as "use \mysqli;" into the class that uses this and do things like "new \mysqli()". Seems kind of messy?
If a class extends a class from another namespace (say Two.php extends One.php for example) then I need to include "use \Classes\A\One as One;" in One.php which seems to be what we want to avoid in the first place
You don't have to reference all of your namespaced classes via a use statement. You can use partial namespaces.
use Classes\A;
new A\One();
So you can see if you had another class in the A namespace it could be instantiated with
new A\Three();
There are many things in the global namespace, you don't need to define them with use. You can just call them \mysqli(). Yes it's a bit unsightly but it allows you to make a function called mysqli in your own code without worrying about collisions with globally namespaced functions.
I'm not sure I follow you on this one. You don't need to do a use in the base class which references itself.
Ultimately it kinda seems like you view use almost like include when they are very different, use, in this context, is a convenience thing so you don't have to type out full namespaces every time.
Your Autoloader also doesn't need to know about your namespaces. The autoloader just tells PHP how to load a class. So if it sees a class come in with the name Classes\A\One you can make it look in the directory /Classes/A for a file called One.php and include it. PHP will make sure that the class is allocated in the proper namespace. The autoloader is just a convenience thing to keep you from having to do includes and requires in each of your PHP files.
I'm a beginner to L5. I read the documentation about extending classes but i didn't find any information about where to put the file in which i extend the class.
**I have to extend Str.php class. I have read that in L4 it had to be done by putting that file under App/folder but i didn't find that folder in L5.
So please can you tell me how can i do that?
This is the information i have now:
First, you must find where the class file is. We will be extending the Str class, which is under vendor/laravel/framework/src/Illuminate/Support. Note that you can also find this class under the aliases key in app/config/ app.php.
Now create a new folder called lib under app/folder. This folder will hold our class extensions. Because the Str class is grouped under the folder Support, it is suggested that you create a new folder named Support under lib too.
Now create a new file named Str.php under app/lib/Support, which you've just created:
But this is for L4
That's more of a general PHP question and there are two parts: 1) How to extend a class and 2) where to put files.
1) Extending classes isn't something Laravel or anyone else provides. That's right there in the language:
class A {}
class B extends A {}
As long as class A exists and is available, then class B can extend from it.
2) Where the files are is also important here. If you're defining class B and want to extend class A, the php runtime needs to know where to find A. Usually class A isn't defined in the same file as class B.
There are many ways to do this. You could require or include class A when you define class B. That would look something like:
a.php
class A {}
b.php
require "a.php";
class B extends A {}
Now with a lot of files like in the Laravel framework or any worthy library, you're going to have a lot of files to include and have to keep track of how to include those files. That's no fun. So, instead of doing that, PHP has provided a way to autoload the classes. That is, if you define classes in a predictable way, PHP can figure out what classes you're talking about without you having to use require or include statements.
There are also many ways of autoloading php files. In Laravel (and many, many other projects), the composer autoloader is used.
This means that files have to be placed in a pre-defined way in order for the composer autoloader to find them. By reading about the composer autoloader and then digging into the code to see how Laravel's classes are autoloaded, you'll be able to figure out how that happens.
Despite the intricate detail of Peter's answer, I figured I'd write something much more concrete: it doesn't matter.
If you check composer.json, you'll see that we are autoloading everything that is placed inside the app directory anyway. Hence, the choice is really yours. All that matters is that you maintain a sensible and readable structure. For example, you could place it in app/Lib, and namespace all your classes App\Lib (if App is your base namespace of course, which can be changed with php artisan app:name). Of course, you could also have a folder like Helpers/Lib for your extended classes, and keep some form of helpers.php with global helper functions in Helpers.
Why would you do this? Well, you might want to have an easy way to call your new Strfunctions, so instead of having to do Str::yourNewMethod($argument) everywhere, you could add a helper function yourMethod($argument) to easier call the function (if you intend to use it extensively).
One thing you have to remember though, as mentioned by Peter, is that the class you are extending won't automagically be found. It will, however, be autoloaded. Hence, to reference it you have to remember to namespace it, such as in the example below.
<?php namespace App\Helpers\Lib
Class Str extends \Illuminate\Support\Str {}
Also remember to namespace correctly when you call your own class.
I have one namespace (for example \App\) that contains all my app encapsulated, currently I'm using composer to autoload this namespace using PSR-0 and checking for two different folders, "Main" and "Client". (Giving priority to the client folder, allowing me to override the main app functionality to meet the client's requests by only creating the necessary override files in the client's folder)
Now, I'm thinking that it would be better if the client's override classes extended the original one, because I realized that the main use for this is to edit only some of the class methods, and I want to future proof the "override class" for new methods that could appear in the "main class". And I've been struggling with a way to make this happen, keeping the namespaces.
Example: Sales Controller Class ==> \App\Controller\Sale
If there isn't a "Client/App/Controller/Sale.php" file it uses the default "Main/App/Controller/Sale.php"
But if there is, what I want is that "Client/App/Controller/Sale.php" could be able to extend "Main/App/Controller/Sale.php"
<?php
namespace App\Controller
use \Main\Controller\Sale as OriginalClass //The Sale class in Main Folder
class Sale extend OriginalClass {...}
This way, I could override only some methods in the client's class and if the main class gets updated it would be reflected in the client's app.
The problem is, that since both, the client and main class are in the \App\ namespace, I can't figure out a way to get the "use" statement above to work. The main reason is that any prepended namespace (in the example "\Main + namespace) that I put in it won’t work, because the file's namespace would be different.
Another way I thought it could work is by tinkering with the composer autoload, and check if the namespace starts with "Main" or maybe "Original", then remove that part from the namespace and force to use the "Main" folder. But I couldn't find where this could be implemented.
Another solution I considered was to subdivide the main class functionality in sub classes, that could be overridden using the current autoload scheme, but I don't know if it is wise to have so many classes and files scattered through the system.
Any help or guidance is always welcome.
No Solution, but a workaround
I ended up separating the clients and main classes namespaces. Then, I made a function that recives a class name and checks if the class exists in the client's folder and prepend the "Client\" namespace, or append the "Main\" namespace before initializing.
So
$class = "Path\\To\\My\\Class";
$class = checkClass($class);
// Now class is either "Client\\Path\\To\\My\\Class; or Main\\Path\\To\\My\\Class;
//Uses:
$object = new $class();
$static = $class::StaticMethod();
Also, the "Client" version of the classes extends their "Main" --base-- class.
Eg: Client\MyClass extends Main\MyClass
Here is the scenario.
I am implementing namespaces into my projects.
I have my own custom bridge library that calls other libraries like Zend to do the heavy lifting.
I have no problem using fully qualified namespaces in my custom bridge library but would like to keep the code as terse as possible in my controllers, models and view.
Here is an example of some aliasses i would like to use:
use BridgeLibName\Stdlib\Arrays as arr;
use BridgeLibName\Stdlib\Objects as obj;
use BridgeLibName\Stdlib\Strings as str;
use BridgeLibName\Stdlib\Numbers as num;
use BridgeLibName\Stdlib\File as file;
etc.........
Example usage:
$file = new file('path/to/file.txt');
$file->create();
or
$obj = arr::toObject(['key1'=>'value1']);
is it possible in any way to create an alias or constant that can be globally accessible and not discarded at the end of each file?
Some kind of bootstrap file that can make these aliases stick.
As I was writing the question i thought of a solution.
You can fake it by creating classes that extend the namespaced classes.
example:
class arr extends BridgeLibName\Stdlib\Arrays{
}
One important thing to remember:
If you are going to extend the classes the namespaced class will have to be loaded.
This could have performance implications if used too much since aliases and namespaces are only loaded as needed.
Since I am only using it to bridge to other classes there is very little logic inside my bridge files.
These bridge files in turn uses aliases and namespaces correctly thus loading the real files as needed.
I you are not careful with the implementation you can load a lot of unnecessary stuff and cause your app to become slow and bloated.
A nice thing I noticed is that good IDEs like netbeans also seems to be able to do auto completion with this method.
If there is a better way to do this please let me know.
Just thought of an amendment to this method to fix the problem with unnecessary class instantiation.
The core library can work with the normal psr-0 loader.
To have the aliases autoload I created an aditional dir named includes next to my namespaced class.
in composer you describe it like so:
"autoload": {
"psr-0": {
"BridgeLibName\\": "."
},
"classmap": ["include/"]
}
Now your libraries will load as expected from the correct namespace and your alias classes will autoload as needed.
Classes put into the include dir can now extend the namespaced classes ( as shown above ) and will no longer be loaded prior to being used.
Now you have global aliases without having to sacrifice performance by loading unused classes.
I've seen a bunch of examples using PSR to do autoloading with namespaces. Maybe this is a dumb question, but why isn't this bad practice? Doesn't that defeat the purpose of having namespaces?
For example, say you have two libraries, FormBuilder and MySweetForms, and both have a Form class. Locations are at:
lib/FormBuilder/Core/Form.php and lib/MySweetForms/Form.php
If you autoload based on the namespaces those Form classes are in, aren't you going to run into the exact problem that namespaces are meant to prevent: ambiguous identifiers of classes and methods?
Won't it fail when you successfully locate another class in the MySweetForms namespace, say AjaxFileField, that relies on \MySweetForms\Form, but it finds the FormBuilder's implementation of the Form class?
Because of namespaces, the autoloading won't find the FormBuilder's implementation of the Form class when it's looking for the MySweetForms implementation, provided the autoloader and the two libraries follow best (or close to best) practices and are implemented correctly.
If the MySweetForms/AjaxFileField.php file defines a class inside the MySweetForms namespace as shown:
namespace MySweetForms;
class AjaxFileField
{
public function doFormStuff()
{
$form = new Form();
}
}
Then the reference to the Form class inside AjaxFileField is really a short-hand for the fully qualified class name MySweetForms\Form.
When the autoloader is invoked to load the class, it will be asked to load the class based on it's fully qualified name. A PSR-0 autoloader would translate the MySweetForms\Form classname into the MySweetForms/Form.php path, and (provided it was told to look in the lib directory) it would find it's way to the correct file. If that file was missing, the autoloader would ultimately fail. Similarly, if that file defined a Form class but neglected to supply a namespace the actual class (MySweetForms\Form) would not exist (instead, a global class Form would) -- that doesn't match the fully qualified name of the class referred to in the AjaxFileField class, so an error would occur.
Note that if the code had included $form = new \FormBuilder\Core\Form(); instead the class name would already be fully qualified, and the autoloader would be asked to load the FormBuilder\Core\Form class (and would expect it to be in the FormBuilder/Core/Form.php file).
If we had used $form = new \Form(); we would be referring to the Form class in the global namespace and the autoloader would be asked to find Form (it would look in the Form.php file directly inside the lib folder at this point).
The key is to recognize that there are not, really, two Form classes -- there is one class with a fully qualified name of FormBuilder\Core\Form and one with a fully qualified name of MySweetForms\Form -- and a properly implemented autoloader will expect them in completely different locations, and will not try to load one file in place of the other.
The combination of namespaces and the PSR style of organizing files into directories based on their namespaces makes it very easy to re-use common words without causing conflicts, and to predictably map fully qualified class names to the corresponding files.
The resolution of class names to fully qualified class names is key to this, and is discussed in the PHP manual (the namespace FAQ has a couple of points about it: http://www.php.net/manual/en/language.namespaces.faq.php). The PSR-0 standard itself is also a useful reference: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md