Autoload classes and functions from different files and directories - php

I have this autoload code :
function __autoload($class_name)
{
//class directories
$directorys = array(
'./Controls/',
'./Config/',
'./Utility/'
);
//for each directory
foreach($directorys as $directory)
{
//see if the file exsists
if(file_exists($directory.$class_name . '.php'))
{
require_once($directory.$class_name . '.php');
//only require the class once, so quit after to save effort (if you got more, then name them something else
return;
}
}
}
I have three directories that they're holding all of my classes and functions.
Can I create a single autoload file in Controls directory and use it to load all function or classes in other php files I mean for example index.php file in /portal/main/index.php
is it possible to load classes that are controls and config in index.php file without including any file in above of index.php file
I mean autoload automatically understands which file is requesting a class or function and include that file for it.
updated code :
function __autoload($class_name)
{
//class directories
$directorys = array(
'/Controls/',
'/Config/',
'/Utility/'
);
//for each directory
$ds = "/"; //Directory Seperator
$dir = dirname(__DIR__); //Get Current file path
$windir = "\\"; //Windows Directory Seperator
$path = str_replace($windir, $ds, $dir);
foreach($directorys as $directory)
{
//see if the file exsists
if(file_exists( $path . $directory . $class_name . '.php'))
{
require_once( $path . $directory . $class_name . '.php');
//only require the class once, so quit after to save effort (if you got more, then name them something else
return;
}
}
}
I have updated the code and it include files, but the only problem that i have is that this function is not running automatically,
for example my autoload file is in : root/Controls/autoload.php
and I need some classes and functions in :root/portal/index.php
when i define classes in index.php i get error that the file is not exists
and i should manually call autoload.php file in index.php
how can i make autoload smart which i shouldn't have include it in each file for including classes ?
please help me out.
thanks in advance

Simple manual solution : put your autoload file in your project root and include it in your index file this will do the job.
but if you want to use htaccess or php.ini :
Place a file called .user.ini into the document root and add the auto_prepend_file directive in there:
auto_prepend_file = /home/user/domain.com/init.php
The file must be inside PHP's include_path. So you must either set the file's directory to be in the include_path inside php.ini, or do it in the .htaccess with a php_value statement.
php_value include_path ".:/path/to/file_directory"
php_value auto_prepend_file "file.php
If you use the above method in .htaccess, be sure to copy the include_path from php.ini in and add the :/path_to/file_directory so you don't lose any already needed includes.
Alternatively, just add :/path/to/file_directory to include_path directly in the php.ini
Update
If you cannot modify the include_path, you might try specifying a relative path to the auto_prepend_file. This should work since the file path sent is processed identically as if it was called with require():
php_value auto_prepend_file "./file.php"

Your program is only as smart as you.
If you dream of solving a problem beyond a mainstream language's capability, problem is with your dream, not the language.
To autoload PHP code, set auto_prepend_file in php.ini to your autoloader script.
Alternatively you may make it the centralised entry point and direct traffic with mod_rewirte.
This is bad practice. Both methods relies on server config such as .htaccess, and is not always available.
You may also override existing configs or fail to rewrite some paths.
You should manually centralise the PHP requests (and seal off non-entry points), or require_once in each entry points.
PHP does not support autoloading function.
Include all functions you might need if you can't bother to code their inclusions.
Look at Wikipedia, Magento, or WordPress.
They have hundreds if not thousands global functions.
Do not worry about including - trust your disk cache and enable opcode cache.
Static inclusion is pretty fast and with opcode cache it may be faster than conditional loads.
If you have multiple classes / functions in a file, I know of no dynamic language that can magically pick the correct file to include.
Your autoloader looks pretty standard.
You can dynamically define anything in PHP.
PHP can never be confident what it will get unless it really runs the code.
Auto loaders are simply trusting filenames as correct and complete declaration of what is in the file.
Since your autoloader scans all folders, you may want to make a static file map on first run, and use the map for subsequence loads.
Assuming you are not going to create class files dynamically with PHP, of course.

Related

spl autoload register error

I'm writing some classes that I want to load using the spl_autoloader_register() function. I've created a file named autoloader.php and I've saved it inside the folder where I'm saving all the classes needed for my project.
So, to verify if it work, I've loaded it using require_once inside a test php page, but unfortunately the console log show me an error output PHP Warning: include_once(lib\DataManager.php): failed to open stream: No such file or directory.
The test file is inside the same folder who host all the classes, is this error because of this motivation?
Here is the autoloader code
<?php
function autoloader($className){
include_once($className . '.php');
}
spl_autoload_register('autoloader');
?>
and here is how I tried to instantiate it from the test page
<?php
require_once 'autoloader.php';
require_once 'config.php';
use lib\DataManager as DataManager;
$dataManager = new DataManager($db);
?>
Relative paths are not processed relative to the file where they are written, but rather the "current working directory" of the PHP process (often, the document root of your web process, but not worth relying on).
To reference the location of the current source file, PHP provides two magic constants:
__FILE__ is the fully-qualified filename of whatever source file you write it in
__DIR__ is the directory containing that file
So, assuming your classes are in the same directory as the definition of your autoloader function, you can write this:
function autoloader($className){
include_once __DIR__ . DIRECTORY_SEPARATOR . $className . '.php';
}
The __DIR__ is resolved once, when the function is compiled, so whenever this function is run, it will always refer to the directory where this function was defined, regardless of where it was called from, or how the script is run.
(Note that include, require, include_once, and require_once are keywords not functions, and don't need brackets around them. It won't hurt to add them in general, but can cause confusion in some cases, so is best avoided.)
You got it almost right. The problem is because it's not pointing to the correct local filesystem to find the file. The following autoloader will work with a relative plugin model, and a library folder model. This will assume the on your configuration file you have defined a variable $GLOBALS['config']->lib that defines a filesystem folder that could contains extra system libraries. Of course, this could defined differently, it depends of the application.
<?php
function autoloader($className){
$paths=[getcwd(),$GLOBALS['config']->lib];
foreach($paths as $p){
$filename = str_replace('\\','/',$p.'/'.$className.'.php');
if (is_readable($filename)) {
require_once($filename);
return;
}
}
}
spl_autoload_register('autoloader');
?>
This will work taking on consideration two scenarios, class is relative to the last requested script, if not tries a predefined folder with classes. Once it's found and loaded it should return, to avoid keep trying loading other paths.
On the array more paths to try could be defined, but try to keep it no more of four paths for performance reasons. Also the order of the paths are the priority where to look first.
This script should work on PHP 5.4 up.

Running php from cmd: relative paths issue

I am currently testing some code in cmd, and I'm experiencing some problems with relative paths.
It is clearly visible that the path is set correctly, furthermore it works as expected under http protocol. I assume there's something that's blocking relative paths in cmd, because if I replace that with an absolute path the file gets included. This however is not efficient as filesystems may change and the use of relative paths is a must. I'm really bad with OS stuff so I guess I shouldn't be making any more assumptions. Thanks!
EDIT: Mind = Blown
Basics:
When called from HTTP the working directory of index.php is C:\xampp\htdocs\actualframework\public\ when called from command line (in your example) it is C:\xampp\php. So the script tries to include C:\xampp\Framework/class/routers/System.php which isn't there.
You need to utilize the __DIR__ constant in order to make the require command working independently from where index.php was called:
require(__DIR__ . "/../Framework/class/routers/System.php");
__DIR__ points to the directory where the source file is located which is using the __DIR__ constant, in your example: C:\xampp\htdocs\actualframework\public\
Using include_path
Another good idea is to utilize the include_path directive in order to make the real location of a library file transparent to the application. This will give you more freedom when you once change the directory layout. include_path is a configuration value that can be set in php.ini or in scripts and contains a list od directories where php should look if you pass relative paths to require or include as well as some functions like file_get_contents(), fopen(), ...
Configure the include path on top of your index.php (or in a separate bootstrap.php which gets included):
ini_set('include_path', implode(PATH_SEPARATOR, array(
__DIR__ . '/../Framework/class',
__DIR__ . '/../Framework/interface',
// add the existing value at the end
ini_get('include_path')
)));
Now you can use paths like this to require classes:
require_once 'System.php';
require_once 'routers/Foo.php';
Autoloading
Since PHP5 there is a feature called autoloading which eases the including of classes. Autoloading basically provides the ability to define a hook function which gets called every time a class is accessed which has not been defined before. Imagine you have the following files:
lib/Person.php
<?php
class Person {
... some code
}
index.php
<?php
$hek2mgl = new Person();
Normally this code will trigger an error because Person is accessed without including lib/Person.php before. This is the point where autoloading can be used. Lets see how a simple autoload method can look like in this (simple example):
function autoload($classname) {
$path = __DIR__ . "/lib/$classname.php";
if(file_exists($path)) {
require_once $path;
}
}
You need to reqister the autoloader using spl_autoload_register(). index.php can look like this:
<?php
// define simple autoloader for project
function autoload($classname) {
$path = __DIR__ . "/lib/$classname.php";
if(file_exists($path)) {
require_once $path;
}
}
// register autoloader
spl_autoload_register('autoload');
// will work now
$hek2mgl = new Person();
If you ask me, autoloading is one of the coolest things in PHP5. You can refine the autoload method in order to work with the include_path directive. Doing so it gets easy to work with a couple of libraries without taking care about where they are physically stored in filesystem. You may give my Jm_Autloader a try, the code is on github and you can install it using PEAR or composer.
As #hek2mgl points out, it has to do with the current working directory being wrong.
In your case the working directory is c:/xampp/php/ but it should be c:/xampp/htdocs/actualframework/public/.
There's at least two solutions to fix this, the most obvious one being changing the working directory on the command line:
cd c:/xampp/htdocs/actualframework/public/
c:/xampp/php/php.exe index.php
Alternatively you can set the current working directory from your PHP script by adding the following into your index.php before you try to include/require anything:
chdir(realpath(dirname(__FILE__)));

PHP require_once relative to variable document root

I'm trying to include using require_once, however, I don't always know what the file structure will be relative to the DOCUMENT_ROOT...
it could be...
/config.php or /theapp/config.php or /dev/theapp/config.php or /something_else/theapp/config.php
I COULD path back from the file like require_once('../config.php') except in some cases the files may be in a symlink directory.
Basically I'm trying to find a way where NO MATTER the circumstance, any files that call the config.php file can find it.
This is what the include_path configuration setting is for. I usually set it in the Apache config for my site, or in a local .htaccess file. Use the php_value directive.
So, basically, in your Apache config file:
php_value include_path .:/var/www/where-your-site-is
Then, from your scripts, you just use:
<?php
require_once 'conf/config.php';
require_once 'views/template.php';
?>
No matter where in your sites directory structure you are.
If you have PHP 5.3+ use this:
// My page: /dir1/dir2/welcome.php
// My include: /inc/top.php
require_once(__DIR__.'/../../top.php');
From the docs: __DIR__ is the directory of the file. If used inside an include, the directory of the included file is returned.
You can use dirname(__FILE__)
It returns the complete path of your script.
example
require_once(dirname(__FILE__).'/../config/config.php');
Came up with a pretty simple solution that works when the app is running as one unit, as well as when the core system is linked in via symlink:
Instead of:
require_once('../../../config.php');
I use:
function changeDir($up_n){
$split = explode("/",$_SERVER['SCRIPT_FILENAME']);
array_pop($split); // Remove current file name
for ($i=1; $i<=$up_n; $i++){ array_pop($split); }
return implode("/",$split);
}
require_once(changeDir(3)."/config.php");
Just pop off

PHP, How to set include path

I've been writing:
include('a.php')
include('b.php')
etc. in my script to include to use functions a() and b(). It gets pretty tedious. Is there a way to set a path of a directory and have multiple files there be accessible by a script?
I tried this in my script:
set_include_path('.;C:\xampp\htdocs\myfunctionfolder');
And I set an environmental variable PATH to have this older in there.
I also in modified php.ini to include
include_path = ".;\xampp\php\PEAR;\xampp\htdocs\myfunctionfolder"
If I have many files in there, how do I access these files without having to include each individually? Setting the environmental variable definitely works in the command prompt.
Do I need to do something else for .php files to be accessible collectively under a directory?
Common practice is to have a "common.php" or "includes.php" file that includes the include/include_once calls (for the sake of simplicity). e.g.
root
[lib]
a.php
b.php
includes.php
index.php
Then includes.php contains:
<?php
include_once('a.php');
include_once('b.php');
?>
Then in any script it's a matter of including the includes.php file.
However, to answer your original question, you can only include one file at a time, per call. You can use something like opendir and readdir to iterate over all files in a specific directory and include them as found (automated so-to-speak) or write out each include yourself based on the files you're creating.
Also, all setting the include path does is set a directory to look in when an include call is made. It's not a directory where the files should automatically be loaded (which is the impression I get from your post).
Setting the include_path will not include every file in that directory, it only adds that directory to the list PHP will search when including a file.
Specifies a list of directories where the require(), include(), fopen(), file(), readfile() and file_get_contents() functions look for files.
Source
This would simplify including files in a deep structure or in a completely different section of the filesystem.
include('/var/somewhere/else/foo.php');
With /var/somewhere/else/ added to the php.ini include_path could become
include('foo.php');
Additionally, as others pointed out, there are common practices but you could look into OOPHP and autoloading classes. This will not work for functions that I know of.
Many developers writing object-oriented applications create one PHP source file per-class definition. One of the biggest annoyances is having to write a long list of needed includes at the beginning of each script (one for each class).
In PHP 5, this is no longer necessary. 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. By calling this function the scripting engine is given a last chance to load the class before PHP fails with an error.
PHP's parser is pretty efficient - you'll waste a lot more time loading a ton of individual files instead of one (or a few) more monolithic files. However, if you insist on keeping things segregated like that, you CAN create meta-include files to load sets of individual files, so you'd only include the one single meta-include file, and it does the rest for you:
meta.php:
include('a.php');
include('p.php');
...
include('z.php');
And then you simply do:
<?php
include('meta.php');
in your scripts and you've got all the individual ones loaded for you.
I have a function like this in most of my projects:
function appendToIncludePath($path)
{
ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR . BASE_DIR . $path . DIRECTORY_SEPARATOR);
}
see this question:
How to include() all PHP files from a directory?
Also, in terms of best practices, you can include multiple functions in the same file if they are at all related, and I would also suggest having more descriptive names of your functions and files. For example, if your a() and b() functions both related to validation for example, name your file validation.php and put both functions in there and try to rename them to something that is related to what they do. This will allow you to remember what they do when you start piling up a huge list of functions ;)
include __DIR__ . '/../folder1/folder2/folder3/Target.php';
include __DIR__ . '/../folder1/folder2/Target.php';
It helps you go to any path.
Download latest PHP zip and extract to C drive then download composer and install it, during installation it ask for PHP path so just select extracted PHP path.
As follow below step.
Go to Computer.
Select Advance System Settings.
From system properties select Environmental Varaibles.
In Environmental Varaibles add Path in User Variable for PCNAME
In Environmental Varaibles add Path in System Variables.
Hit OK.
Restart PC.
Win + R type cmd.
type command php -v.
Vola you good to go.
PHP
http://php.net/downloads.php
Composer
https://getcomposer.org/download/

Setting PHP Include Path on a per site basis?

I can set the PHP include path in the php.ini:
include_path = /path/to/site/includes/
But then other websites are affected so that is no good.
I can set the PHP include in the start of every file:
$path = '/path/to/site/includes/';
set_include_path(get_include_path() . PATH_SEPARATOR . $path);
But that seems like bad practice and clutters things up.
So I can make an include of that and then include it into every file:
include 'includes/config.php';
or
include '../includes/config.php';
This is what I'm doing right now, but the include path of config.php will change depending on what is including it.
Is there a better way? Does it matter?
If you're using apache as a webserver you can override (if you allow it) settings using .htaccess files. See the PHP manual for details.
Basically you put a file called .htaccess in your website root, which contains some PHP ini values. Provided you configured Apache to allow overrides, this site will use all values in your PHP config, + the values you specify in the .htaccess file.
Can be used only with PHP_INI_ALL and PHP_INI_PERDIR type directives
as stated in the page I linked. If you click through to the full listing, you see that the include path is a PHP_INI_ALL directive.
Erik Van Brakel gave, IMHO, one of the best answers.
More, if you're using Apache & Virtual hosts, you can set up includes directly in them. Using this method, you won't have to remember to leave php_admin commands in your .htaccess.
Use a php.ini file in website root, if your setup uses PHP as CGI (the most frequent case on shared hosts) with the same syntax as the server-wide php.ini; put it into .htaccess if you have PHP as an Apache module (do a phpinfo() if unsure):
php_value include_path "wherever"
Note that per-folder php.ini does not affects subfolders.
Why do you think append to include path is bad practice?
This code near top of root script shouldn't be that bad...
$path = '/path/to/site/includes/';
set_include_path($path . PATH_SEPARATOR . get_include_path());
IMHO the main advantage is that it's portable and compatible not only with Apache
EDIT: I saw a drawback of this method: small performance impact. see http://www.geeksengine.com/article/php-include-path.html
Depending on how your host is set up, you may be permitted to place a php.ini file in the root of your home directory with extra configuration directives.
Your application should have a config file written in PHP. Then include that with a relative page into every page in the program. That config file will have a variable for the path to the includes dir, templates dir, images dir, etc.
You can set include_path in your php.ini file too. I'm a perl guy, so I expect to be able to load includes and have include do the right thing. I have all my includes in a specific directory, which is added to include_path. I can do things like
require_once "ClassName.php";
I don't need to worry about relative paths or locations of files.
I've also written my own CustomRequire to do things like
function CustomRequire ($file) {
if(defined('MYINCLUDEPATH')) {
require_once MYINCLUDEPATH . "/$file";
} else {
require_once $file;
}
}
That way I can change how I do includes at a later date. Of course, you still need to find a way to include your include code :)

Categories