I have succesfully created an autoload function for a project I am working on.
I have several classes and 1 index where I instantiate all of the classes.
In some of the classes however I do need to require some other classes in order to let the project work.
My question being: How will it affect my project if I instead of requiring the few classes I need in the other classes require the autoload class?
The whole idea of autoloading classes is that you don't need to worry about this anymore. To achieve this, once you added your individual autoloading function, it will be used.
Say you have the following setup:
index.php
spl_autoload_register(function ($class) {
//something
});
$a = new Foo();
and the file where Foo is routed to
class Foo extends Bar {
public function __construct() {
$b = new Baz();
}
}
Since the autoloading was already invoked when searching for Foo, the same rules will now apply to find Bar and Baz.
This means once you defined your personal autoloading rules, PHP will go with them (until you redefine). This doesn't only work for inheritence but regular class calls inside your classes as well.
Related
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.
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 am using an aliased and namespaced class in a parent class successfully but it doesn't seem to be available in the child class. The actual error is from the autoloader. The weird thing is that the function does work in the parent class and loads fine. How can I make a class brought in by use available in subclasses?
edit: the recipes are stateless -- would it make sense to make them singletons in Base and then reference them as members in the child class MyTest?
I have the two files:
Base.php:
namespace selenium;
use selenium\recipe\Cms as Cms;
class Base extends \PHPUnit_Framework_TestCase
{
public function __construct()
{
Cms::staticfunc(); //works fine
}
}
MyTest.php:
class MyTest extends \selenium\Base
{
public testMyTest()
{
Cms::staticfunc(); //errors here
}
}
From comment:
i was hoping for a way to cascade the use without duplicating that line among the 20 or so child classes
That is one of the biggest issues I have with PHP namespacing, that you have to call use for every file the current script needs access to. It's the same situation we used to face having to call require_once 20 times on some scripts in order to bring in the necessary libraries.
What I prefer to do is namespace my files (as they reside on the filesystem, like Zend Framework does) and use an autoloader to avoid the whole mess. I currently use ZF autoloader, which can be used outside of the framework, or you can also use the vanilla PHP implementation using SplAutoload.
-- Update --
I have a library which I have written over the last few years which is namespaced as Hobis_Api, and are located on the filesystem with the same convention; ~/projects/projects/dp/hobis/lib/Hobis/Api/*. In order to register the namespace with Zend_Loader I do the following:
// Be sure to set the include path to include the Zend and Hobis_Api files
// Not sure how your setup is, but would look something like:
set_include_path(get_include_path() . ':' . DIRNAME(__FILE__));
require_once 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace(
array(
'Hobis_Api_'
)
);
Normally the above code would go into some bootstrap file, which you can call from a centralized script in order to register the autoloader, once.
Now, if your include path is set correctly, anytime you reference Hobis_Api_* it will be autoloaded for you, so you don't need to call use or require_once, example usage:
// SomeScript.php
// Notice no requires
// I can make a call to Hobis_Api_Image without error
$image = Hobis_Api_Image;
$image->setHeight(400);
I need to load a class in Symfony 1.4 but the simple placing of the file in /apps/frontend/lib seems not to be enough.
class test
{ function foo ($foo) { echo $foo; }
}
and I tried to use it in an action:
public function executeTest(sfWebRequest $request)
{
$a = new test();
$a->foo('aaa');
}
I have refreshed the cache and still:
Fatal error: Class 'test' not found in ...
Do I have to declare it somewhere?
You have to place your file in your project lib folder:
myproject/lib
[Updating]
About loading a class from a file in myproject/apps/frontend/lib/ maybe the problem is the filename where is the class.
I found in this page A Gentle Introduction to symfony - Chapter 2 - Exploring Symfony's Code that:
Symfony will then look for a MyClass definition in all files ending
with class.php in one of the project's lib/ directories. If the class
definition is found, it will be included automatically.
So if you store all your classes in lib/ directories, you don't need
to include classes anymore. That's why symfony projects usually do not
contain any include_once or require_once statements.
Contrary to the other answer, classes in /apps/frontend/lib/ absolutely should be loaded by the autoloader if you are in a frontend action.
Something else is going on here. Is your class actually named test? If you're in production, have you cleared your cache?
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();
}
}