I'm using a composer Library, with lots of classes. There is one class (Alibrary\FileA) in this which doesn't do exactly what I want it to do.
namespace Alibrary;
class FileA
{
public function sayHello()
{
echo 'hello';
}
}
So I've written a replacement called Mylibrary\FileB. As you can see it's so must better.
namespace MyLibrary;
use \Alibrary\FileA;
class FileB extends FileA
{
public function sayHello()
{
echo 'hi';
}
}
Is there any way to tell Composer to load FileB every time FileA is asked for? I just want to replace one class, basically for testing purposes. I don't want to create a whole new repo - I've looked at https://getcomposer.org/doc/04-schema.md#replace already.
Is there something like this that I can do?
"classmap": [
"\Alibrary\FileA": "MyLibrary\FileB"
],
Thanks.
Your question has not very much to do with Composer in particular, but with how PHP works in general - and the code you are using.
Autoloading gets the name of the class that is still unknown to PHP and has to find the code that defines this class. But the situation you are facing would be the same if all classes are in one single file: The code is using class \Alibrary\FileA, and is not using class \MyLibrary\FileB. Note that your invented class names do suggest you are thinking in files, which is not the primary effect. Yes, classes' code is usually stored in files, but as soon as the code is loaded by PHP, it is only "the class" that is relevant, not where the code came from.
So you have some classes that do something, and one of them is \Alibrary\FileA. How can you change this to your own class? It depends. If the code creates that FileA class itself, you cannot override it from outside. If the code wants you to pass an instance of FileA as a parameter, you can instead pass your FileB class that inherits from FileA and must implement all that methods with the same parameter signature, and must return the same types. If it returns something completely different, it may work, but is likely to break.
Note that inheriting another class is in itself likely to break if you update the original library.
Related
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.
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 want to do something like class categories in Objective-C, to define a class in one file, implement some core methods there, then implement some helper methods in another without subclassing or interfaces, just "continue" the class.
Possible in PHP?
As of PHP 5.4, you can accomplish this with Traits. To quote the official documentation:
Traits [enable] a developer to reuse sets of methods freely in several independent classes living in different class hierarchies. [...] A Trait is similar to a class, but only intended to group functionality in a fine-grained and consistent way. It is not possible to instantiate a Trait on its own. It is an addition to traditional inheritance and enables horizontal composition of behavior; that is, the application of class members without requiring inheritance.
Using a trait allows you to store helper methods that address cross-cutting concerns in a central place and use these methods in whatever classes you need.
// File "HelperMethodTrait.php"
trait HelperMethodTrait
{
public function myIncredibleUsefulHelperFunction()
{
return 42;
}
}
// File "MyClass.php"
class MyClass
{
use HelperMethodTrait;
public function myDomainSpecificFunction()
{
return "foobar";
}
}
$instance = new MyClass();
$instance->myIncredibleUsefulHelperFunction(); // 42
a-priori PHP doest not give you this feature by language construction, the whole class must be in a single file.
Now there are tricks that could do the stuff, but they are quite heavy CPU demanding:
You can for example dissociate your classes between different files, then load the files and build a single string with the whole code, then do an eval(string) to interprete the classes and build them into the runnable code area.
not really a good idea btw for many reasons
If your only goal is to make the class definition file smaller, you can include files within a function definition
class MyClass {
public function doStuff($foo,$bar) {
global $quz;
include('MyClass.doStuff.php');
}
}
Within the include, your local scope applies ($foo,$bar and $quz will be defined)
I'm not sure what the overhead is - the include seems to happen at run-time, not compile time (since an include can actually return a value ..)
I think you better split your class in n parts along functional criteria, and then use the dependency injection pattern to "inject" class A into class B in the constructor for B, without a need for subclassing/interfaces.
You could use this method to add functions to some objects externally, which is different from adding methods to the class itself.
https://stackoverflow.com/a/2938020/2277620
Another not-so-good-way is it create a multiple subclasses so every level of subclasses add only a 'group' of functions.
I used this trick in a single case where a class was more than 10k rows.
Now I've 6 levels of inheritance where every level add a single 'category' of functions the the "previous level" class.
Not so cpu-consuming, ugly to see, not so difficult to maintain
It is highly dependant on what you wish to achieve. I had this bright idea doing what you wish to do. I likes the key words set, get run delete etc. Suppose there was a class and I wanted set of delete. As Ph.T pointed out class needs to be in single file
So make abc->set in one file and abc->get in another file made no use and sense.
So had to lot of rethinking and think of classes as micro services doing specific tasks broken down in functionality.
Solution went with name spacing.
namespace A;
class Get{public static function myCode(){}
public static function myAnotherCode(){}}
this would then be called A\Get::myCode(); or A\Get::myAnotherCode();
Similarly set would be
namespace A;
class Set{public static function myCode(){}
public static function myAnotherCode(){}}
This would then be called A\Set::myCode(); or A\Set::myAnotherCode();
Then user spl_autoload_register to load classes remember to replace \ with /
spl_autoload_register(function ($class) {
require_once __DIR__ . '/' . strtolower(str_replace('\\', '/', $class) . '.php')
});
and directory structure is as following
main (folder)
-- A (folder)
-- set.php (file)
-- get.php (file)
we asked for A\Get::myCode()
require would see it as A/Get.php then run its logic whether it is right name space or class etc.
good side of it if planned out right is it would force you to run things run in logical not just wishy washy there and there. Second you would only load functionality what you need not just whole bunch of this and that.
down side two parts of classes wont share between them unless one extends one from another which would defy the whole logic because other one would be loaded as a consequence any ways.
Careful negation has to be made as in what logic and planning data will be shared and has to be worked out before hand.
namespace A;
class Universal {
protected static $share_one;
protected static $share_two;
}
namespace A;
class Set extends Universal {
public static function myCode(){ parent::$share_one ='wow';}
}
namespace A;
class Get extends Universal {
public static function myCode(){ return parent::$share_one;}
}
main (folder)
-- A (folder)
-- universal.php (file)
-- set.php (file)
-- get.php (file)
run the code and see the magic
A\Set::myCode();
echo A\Get::myCode();
if you only need one set of functionality e.g. get then just use get echo A\Get::myCode(); would work without errors.
To be honest without playing around with name spaces its just expenditure of time earning headaches.
Side note: if done right it can speed up things as class universal wont load twice. even if called gazillion times whats what we want load defaults and then populate them where ever we need them. I have made huge classes where besides defaults only needed few functions rest was just memory load. breaking down just spead up things.
I'm in need of some help.
I have a folder containing classes: /my/folder
I have a class file /my/otherfolder/MyBaseClass.php containing a corresponding class.
I would like to dynamically (in the sense that I do not hard code any class names inside /my/folder) load all classes in /my/folder and for the ones inheriting from MyBaseClass I would like to call the base method myBase().
I'm looking for the cleanest, easiest way. Performance is not an issue.
A very old example from code I wrote long ago... the require_once works because all the classes involved were in the same directory, which was added manually to the path.
function __autoload($class_name)
{
require_once("{$class_name}.php");
}
However, apparently that function has been superseded:
http://www.php.net/manual/en/function.spl-autoload-register.php
When extending classes in Java, class name ambiguity is avoided by the usage of qualified package names in the import statements.
For example: Say I want my controller to extend Spring's MultiActionController - I'll import the same from the standard Spring package. This also prevents me from extending Mike's or Perry's MultiActionController, since I have not imported MultiActionController from their packages.
Similarly in PHP, say we have 10 classes in 10 different library folders, all of which are called MultiActionController.
When I write:
class MyController extends MultiActionController {
function __construct() {
parent::__construct();
}
}
How do I tell PHP which MultiActionController (from which folder), to extend from?
Having several classes with the same name will, one day or another, cause some problems : you cannot include two classes with the same name during the execution of one script -- It'll get you a Fatal Error.
What's generally done, in PHP (before PHP 5.3 and namespaces, at least) is to include the name of the library and/or the "package" in the class name.
For instance, you could have a class name MyLibrary_Package_MultiActionController, and another called OtherLib_Blah_MultiActionController.
Then, what's generally done is using that class name to "map" it to directories and files, replacing the '_' by '/', and adding .php at the end of the last level -- this being often done using the autoloading feature of PHP, to avoid having to write an enormous amount of require directives.
For instance, a class named MyLibrary_Package_MultiActionController should be stored in MyLibrary/Package/MultiActionController.php.
As a sidenote : you used the tag "php4", in your question... If you are actually using PHP 4, you should not forget that it's old, not maintained anymore (Not even for security-related problems), and that PHP 5 is really the way to go !
In fact, you won't be able to do much about object-oriented programming, with PHP 4 ; the object-oriented stuff in PHP 4 was really basic...
(Stuff such as autoloading, which I wrote about a couple of paragraph earlier didn't exists in PHP 4 -- same for public/private/protected, and lots of other OO-related things...)
It depends which one you include. PHP will not let you redefine a class of the same name, so just include above the class definition (change to fit the file names and your software layout):
include('../includes/Spring/MultiActionController.php');
class MyController extends MultiActionController {
....
}
PHP will extend the class that you included with an include statement.
For example, say that you have a class foo declared in file bar.php:
class Foo {
// methods and fields
}
Then in another fie:
include 'bar.php';
class Aardvark extends Foo {
// this class will extend the class Foo in file bar.php
}
You have to include the file holding the class, with a banal include() statement:
include('lib/controllers/MultiAction.php');
Then you can extend it!
I would use namaspaces
namespace package_products_multicontroller {
include("/packages/products/multicontroller.php");
}
class MyController extends package_products_multicontroller\MultiActionController {
function __construct() {
parent::__construct();
}
}