This previous question shows how to force autoloading of all classes.
However I need to force autoloading of just one class on its own. How could I do that?
It mustn't:
Involve changing the class's source code
Rely on any part of the class's source code (methods, variables, modifiers e.g. it must be free to change from concrete to abstract without affecting this).
Also, preferably it would not involve coding the class's name as a string. (To help with IDE refactoring and so on).
The best option I have found so far would be to just use spl_autoload_call():
spl_autoload_call("Jodes\\MyClass");
or for non-namespaced classes:
spl_autoload_call("MyClass");
I had the same need recently. Doing require_once was not an option because the class really needed to be located by the autoloader because of some more complex rules, there was no way to know exactly the path to the file with the class.
Although the function spl_autoload_call($classname) is designed to do precisely this, it suffers from a fundamental flaw: it will throw a FATAL ERROR in case you call it twice for the same classname or even if some of the child classes of that class were already loaded. This happens because it's not possible to redeclare classes in PHP:
<?php
spl_autoload_call('TheClass');
// no problem
spl_autoload_call('TheClass');
// PHP Fatal error: Cannot declare class TheClass, because the name is already in use in ... on line ...
My solution for this problem is no rely on the side-effect of class_exists($classname) which was not designed for this purpose, but is more configurable and, therefor, offers more control about triggering the autoloader.
Even better, it has absolutely no problem on multiple calls or if something was already loaded in the inheritance chain. It simply has the side-effect (if you want) of requiring the file, if the class is not there yet!
<?php
class_exists('TheClass');
// no problem
class_exists('TheClass');
// already exists. No need to autoload it and no fatal errors!
With this you have a safe and idempotent way to load the class through the autoloader.
And, if you don't want to hard-code the string with the classname in there, from PHP version 5.5 onward, you can use the ::class pseudo constant, which is resolved at compile time to the string with the fully qualified classname (including namespaces):
<?php
class_exists(TheClass::class);
I had exactly the same problem and tried different autoload functions. On a Windows PHP 8.1.5 version I had these two different autoloaders:
set_include_path(get_include_path() . PATH_SEPARATOR . 'class/');
spl_autoload_extensions('.class.php');
spl_autoload_register();
or
$autoloader = function( string $class_name )
{
$filename = $_SERVER["DOCUMENT_ROOT"].'/class/' . str_replace( '\\', '/', $class_name) . '.class.php';
require_once $filename;
};
// our class loader
spl_autoload_register( $autoloader );
which seemingly do the same thing.
As one class file in my application also create some supporting global function variables that can be used during construction of the said class I needed to force the class file loading before accessing said functions variables and construction. I did this with the class_exist() on the specific class.
Using the spl_auoload_extenions version this worked fine; using the spl_autoload_register($autoloader) version causes an undefined variable when using the function variables.
Related
This is my project path configuration
./create.php
/Install/Install.php
create.php
<?php
use Install\Install;
echo "Starting";
$install = new Install();
This gives me the error
PHP Fatal error: Uncaught Error: Class 'Install\Install' not found in /project/create.php:6
Install.php
<?php
namespace Install;
class Install
{
//whatever
}
Can someone explain me what is happening there ?
Obviously I guess that using a require_once line with my filename would probably fix the issue...but I thought using namespace and use import could prevent me from doing that like we do in classic framework like symfony / magento ?
I've seen some post speaking about autoloading, but i'm a little bit lost. Haven't been able to find a clear explanation on the other stack topic neither.
PHP compiles code one file at a time. It doesn't have any native concept of a "project" or a "full program".
There are three concepts involved here, which complement rather than replacing each other:
Namespaces are just a way of naming things. They allow you to have two classes called Install and still tell the difference between them. The use statement just tells the compiler (within one file) which of those classes you want when you write Install. The PHP manual has a chapter on namespaces which goes into more detail on all of this.
Require and include are the only mechanisms that allow code in one file to reference code in another. At some point, you need to tell the compiler to load "Install.php".
Autoloading is a way for PHP to ask your code which file it should load, when you mention a class it hasn't seen the definition for yet. The first time a class name is encountered, any function registered with spl_autoload_register will be called with that class name, and then has a chance to run include/require to load the definition. There is a fairly brief overview of autoloading in the PHP manual.
So, in your example:
use Install\Install; just means "when I write Install, I really mean Install\Install"
new Install() is translated by the compiler to new Install\Install()
the class Install\Install hasn't been defined; if an autoload function has been registered, it will be called, with the string "Install\Install" as input
that autoload function can then look at that class name, and run require_once __DIR__ . '/some/path/Install.php';
You can write the autoload function yourself, or you can use an "off-the-shelf" implementation where you just have to configure the directory where your classes are, and then follow a convention for how to name them.
If you want to Use class from another file, you must include or require the file.
Use require('Install.php'); before use Install\Install;.
If you are planning to do a big project I would recommend to use PHP frameworks rather than coding from scratch.
I have 3 files.
vehicleClass.php
motorbikeClass.php (extends vehicleClass)
index.php
My question is... How do I connect all 3. On my index page, do I have to have
include 'classes/vehicleClass.php';
Do I need to have an include for the motorbike class or will the extend (inheritence) cover this?
You can let php autoload your files, by registering your own autoload function. If you have, in example, all your class files in the directory DOCROOT . 'classes' (DOCROOT being your document root), you can use a function like this (example):
function class_loader($class) {
include DOCROOT . 'classes/' . $class . '.class.php';
}
spl_autoload_register('class_loader');
Now if you try to create an object of class 'foo', it will try to include the file DOCROOT . '/classes/foo.class.php' before creating the object. You might want to extend the function a bit, eg. to lowercase file names (include DOCROOT . 'classes/'. strtolower($class) .'.class.php';). This way you can place class Foo in foo.class.php.
Short answer: a class that is extending (or otherwise using) another class must already have defined the parent class before the definition of the child class. Your assumption is correct, your VehicleClass must be included (or better, require'd) prior to your definition of MotorBike class.
However, most frameworks don't go about and include every depedency before all class definitions. This would become unwieldy on any system that has any amount of complexity to it. Instead, the developers of PHP have provided methods for autoloading classes. Using spl_autoload_register will allow you to write a function that will attempt to load in the source file for a given class whenever it is referenced but a definition for it has not yet been found.
Furthermore, once you get a system together that becomes complex, you don't want to store all of your files in a single place. Most frameworks leverage the filesystem and namespaces to help better organize all of their classes. Because of this, the PSR-0 standard was developed in order to help facilitate autoloading between frameworks. Take a look at this question for examples of PSR-0 compliant autoloaders.
Example of PSR-0 compliant class:
<?php namespace Vendor\Package;
class ClassName { }
This file would live in the filesystem at /Vendor/Package/ClassName.php
What you have to do is include 2 files in the index.php.
For example, your index.php page could be something like this.
<?php
require 'classes/vehicleClass.php';
require 'classes/motorbikeClass.php';
// Assuming your class name is MotorBike
$motorBike = new MotorBike();
// And just call the method you want, for example If you have a method called bikeName
echo $motorBike->bikeName();
?>
I hope you get an idea now.
P/S: I prefer require over include. :) Include() should work fine too.
When you manually include a PHP class you can do it while the current script is running, right? Then you can decide, if a condition matches you load it and if it doesn't you don't. Like this:
if ( $_SERVER['REQUEST_METHOD'] === 'POST' ) {
include '../../../Whatever/SanitizeUserInput.class.php';
SanitizeUserInput::sanitize($_POST['someFieldName']);
}
But let's say I use the autoload feature with this class. Will it be effectively loaded at the beginning or will it be loaded only if it's used?
I mean, should I add the __autoload function only in classes that I'm 100% sure I'm going to use in any script (e.g. database connection, session management, etc.)?
Thanks
Autoload is called only when you are trying to access desired class. And it would be better to use spl_autoload_register() instead of __autoload
Documentation:
You may define an __autoload() function which is automatically called
in case you are trying to use a class/interface which hasn't been
defined yet.
and
spl_autoload_register() provides a more flexible alternative for
autoloading classes. For this reason, using __autoload() is
discouraged and may be deprecated or removed in the future.
Autoloading kicks in when you are trying to use a class that has not yet been loaded:
include 'foo.php';
new Foo; // autoload not used, because the class already exists
// Bar is not yet loaded here, auto or otherwise
new Bar; // Bar is being autoloaded, because it was not yet loaded
As such, autoloading can be very efficient. It's slightly less efficient than loading classes by hand at the time when you need them, because of the overhead of invoking the autoload function. But keeping track of loaded classes by hand is more work for very little return over autoload.
Try it out, you will see, that whenever PHP stumbles over a class it doesn't know yet, it will call your autoload function. When your autoload function tells PHP in which file the class is, it will load this file.
To make the answer short, PHP only loads the file when needed, that is true even for conditions, so following test class will never be loaded.
if (false)
{
$test = new CTest(); // never loaded with autoload.
}
So I have an idea, but I'm thinking I need to run it by StackOverflow before I do something stupid.
I want to have an associative array of class names => filepaths. If PHP ever runs into a fatal error where the class is not defined, it will check if the key exists in my array and then require_once the class. This will prevent unnecessary bulk loading of classes that may never be used.
Bad idea?
How about trying PHP's built in autoloading.
Autoloading is the right way to do it, but spl_autoload_register is a cleaner way than __autoload, because it allows multiple autoloaders. Function __autoload also AFAIK stops working when spl_autoload_register is called, unless __autoload is also registered.
You can write your own autoload or use an existing one. For example, Zend Framework has an autoloader that uses conventions (Foo_Bar is in Foo/Bar.php). Nette Framework has RobotLoader, that indexes your classes and uses the index when neccessary. However, unless you use other things from the framework, it is probably too large.
see: http://www.php.net/manual/en/function.spl-autoload-register.php
If you are on PHP5, you can use __autoload().
makes your code a bit more manageable , although performance-wise, it's a bad choice. But I wouldn't worry it unless I'm building a Facebook.
What you are trying to do is already handled by the php __autoload function. You can read all about it here: http://php.net/manual/en/language.oop5.autoload.php
So, not a bad idea at all ;)
you should use autoloading with specified clas name structure, here is an example
the class names should should be only alpha and _ case-insensitive.
Lets take this directory structure and files
/classes/class.php
/classes/input/input.php
/classes/output/output.php
/classes/regex/regex.php
/classes/interface/parser/interface_parser.php
/classes/parser/parser.php
/classes/parser/xml/parser_xml.php
/classes/parser/html/parser_html.php
having the structure like this is good as it encourages you to code better when it comes to OOP.
Now if we take a look at the /classes/parser/html/html_parser.php file:
class Parser_Html extends Parser implements Interface_Parser
{
//looks nice in here
}
usually you would have to make sure the interface and the extended class is loaded, but these get autoloaded as well if they have not already.
creating the auto load system for this is not that complex, its just 1 function.
function __autoload($name)
{
//Classes
$parts = explode('_',strtolower($name));
$path = '/classes/';
foreach($parts as $p)
{
$path .= $p;
}
$path .= '/' . $name . '.php';
if(file_exists($path))
{
require_once $path;
}
}
so instead of including the class file first just run the class initiation.
$HtmlParser = new Parser_Html();
as the file has not been include the __autoload is run with a param of the class name, the autoload then looks in the directory that's relevant to the class name to try and load it.
also as your using the extend keyword in the class file shown above the class that is to be the parent gets run threw trhe autoloader aswell so you do not need to pre-load interfaces and classes etc.
Hope this helps you.
Note:
All code provided is untested and written for informational purposes, I would recommend you research the techniques more in detail before any implementation is done.
My application uses a 'central' page controller that grabs a bunch of files (I hesitate to say libraries) each containing a few related classes, using require_once, before fetching the request. As in:
require_once (dir_lib . 'db.php');
require_once (dir_lib . 'uuid.php');
require_once (dir_lib . 'data.php');
require_once (dir_lib . 'token.php');
require_once (dir_lib . 'logs.php');
require_once (dir_lib . 'time.php');
etc...
I haven't bothered to check memory usage until recently installing the (awesome, but gigantic) 'HTML Purifier' library and glancing at its autoloader. Apparently, autoloader on not, each script instance now weighs in at a whopping (sweet jesus!) 5376 kilobytes memory usage. (I have no idea what the built-in autoloader is for if that's the end result, but I digress) Without the HTML Purifier, most instances still weight in at over a megabyte.
Reading about the PHP autoload functions I'm getting the impression that the autoloader is meant strictly for OOP. With the exception of the purifier library, I'm using very little object-oriented code. Am I just misunderstanding the concept? Is there any other practical way to avoid blindly loading a bunch of classes that may not be needed for each request? Am I just being lazy for trying to include them all up-front?
EDIT -
Repeating this comment up up here to clarify what I meant by non-oo, if that makes much difference:
I am
basically using classes in lieu of
namespaces without using (hardly) any
actual OOP. That is to say, the
"DBFunctions" class may contain, for
example, the functions "execute" and
"getRow". The functions are invoked
with a static method call such as
"dbFunctions::execute($sql)."
Is there a particular reason for those function files to not be classes? The only one I can think of would be PHP4 compatibility, but autoload doesn't exist in PHP4 anyway.
Just wrapping those existing code files in class DBFunctions { ... } and prefixing your function calls with DBFunctions:: to match should be enough to let the autoloader go to work (after setting an spl_autoload_register() callback to suit), and it's worth it. It's still stylistically procedural if you wanted to keep the code that way, and a decent step towards OOPifying your whole code base should you want to go that way.
(And HTML Purifier is an elephant, have you checked out PHP's internal filter functions?)
Using Autoload shoudn't hit performance as the class them selves aren't loaded until they are called or used. Autoload contruct basically loads class as needed is they haven't been instanciated already.
Find in the code where the class is been instanciated first. Unless class files are instanciated automatically, it shoudn't take any memory. Maybe not all are been instanciated, but not unset when not needed.
When you find where each is been instanciated or the one in question you can use then:
//Gets the object name using the class.
echo get_class($classinstance) , "\n";
You can handle it by placing some break points, using memory_get_peak_usage().
//Output memory usage at start of class instanciation.
echo memory_get_usage()
#memory_get_peak_usage() prior to 5.2.1.
$classobject1 = new theclass();
//Output memory usage after class instanciation.
echo memory_get_usage()
Now try the same thing but this time using unset() after class have been used:
//Output memory usage at start of class instanciation.
echo memory_get_usage()
#memory_get_peak_usage() prior to 5.2.1.
$classobject1 = new theclass();
unset($classobject1);
//Output memory usage after class instanciation.
echo memory_get_usage()
So basically in theory, unset any unused instance across app, find large objects, debug.
This is to clarify #greg's comments regards OOP:
Take this chunk as sample:
class Tun{
private $run = 'i\'m running';
function run(){
echo $this->run;
}
}
echo Tun::run;
Output:
Error: Fatal error: Undefined class constant 'run' in C:\Work\pro\debug.php on line 16
The above sample because is referencing a function which uses OOP, in this case the class private variable $run. Since the class isn't instantiated(an object instance), it will error out. So, yes you can use functions inside of a class by reference but they mostly have to be plain procedural or constant references.
Hope this help.
PHP5's autoload is only for loading classes on the fly. Only one autoload function can be used in your application (but see below). Read the autoload docs for details. Basically, you can define an __autoload() function that will load any file you want (or do anything you want, for that matter), containing any classes you want, when PHP tries to call a class that isn't loaded yet.
The link that you provided in your question is about the Standard PHP Library's autoload, which is different, and more flexible. spl_autoload_register() lets you register a stack of autoload functions, rather than just one. This is most useful when you're using libraries in your code that make use of autoload, because their autoload doesn't have to clobber yours or those of other libraries.
If you're just starting out with OO on small projects, you probably just want __autoload(), but if you want to integrate libraries like HTMLPurifier, which does use spl_autoload, google for spl_autoload for the docs, they're the first results.