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__)));
Related
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.
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.
I've been going over those two topics:
include, require and relative paths
PHP - with require_once/include/require, the path is relative to what?
and couldn't make my script to work, none of presented methods are working or maybe I'm doing something wrong.
Anyway this is where my problem occurred:
Root/ //this is root location for server
APP/ //this is root location for script
Root/APP/core/init.php //this is where I include classes and functions from
Root/APP/classes/some_class.php //this is where all classes are
Root/APP/functions/some_function.php //this is where all functions are
and so obviously I need to include init.php everywhere so I did in every file like this:
require_once 'core/init.php';
it was working until I have decided to create a location for admin files like this:
Root/APP/Admin/some_admin_file.php
and when I included init this way:
require_once '../core/init.php';
script failed to open functions, no such file in APP/Core/ folder
so I used DIR method presented in topic above and than even weirder thing happened, error:
no such file in APP/Core/classes/Admin/
What is that? :D I'm lost with this, could someone help a bit ;)
Include paths are relative to the current working directory, which can be inspected using getcwd(); this can be a source of many issues when your project becomes bigger.
To make include paths more stable, you should use the __DIR__ and __FILE__ magic constants; for instance, in your particular case:
require_once dirname(__DIR__) . '/core/init.php';
The dirname(__DIR__) expression is effectively the parent directory of the script that's currently being run.
Btw, __DIR__ could also be written as dirname(__FILE__).
I have the current tree.
/edu/index.php
/edu/Classes/Connection.php
/edu/Classes/User.php
/edu/Handlers/Post.php
When I need to use functions from Connection.php and User.php inside index.php, I require them using:
require_once './Classes/Connection.php';
require_once './Classes/User.php';
But some functions from User.php also needs functions from Connection.php to work, so since index.php is where the requests come from, I require Connection.php inside User.php using:
require_once './Classes/Connection.php';
But sometimes I need to use the same functions from Post.php, so
require_once './Classes/Connection.php'; inside User.php does not work if Post.php is the file sending requests.
Isn't there a unified path or solution so that I can be able to require the file from any place inside my project in the same way? I would really like to avoid a solution such as...
public function UserFunction($path){
require_once $path;
}
And change the parameter on each call depending on my position inside the project folder.
If the files you are trying to include are mostly classes, it would probably be best to implement the __autoload function. When you instantiate a class, if the class hasn't been defined, then the __autoload method is called with the class name so you can include the file. If you create a simple standard, like all classes reside in the $_SERVER['DOCUMENT_ROOT'].'/classes/' folder and the filename is the same as the class name like ClassName.php, you can dynamically load in any classes as they are needed.
Also, generally looked down upon but I figured I would mention it because I hadn't seen it offered yet. There is the set_include_path() function you can call that will allow you to set the path(s) that php looks when including a file. HOWEVER, this can cause some trouble. As an example, my work hosts several sites under sub-domains each with their own document root. Under the document root each site has its own "includes" folder. Well some genius about 8 years ago set the include path in the global apache config to /var/www/site1/includes;/var/www/site2/includes...etc. Problem is that when a file exists with the same name in site1 as site2 and you try to include it, you will always include the file from site1 because php will find the file there and stop looking. It has caused some small headaches from time to time.
Your best best is to include relative to __DIR__ to get a file relative to another file.
Other good points that you can include from are
$_SERVER["DOCUMENT_ROOT"]
or make your own using htaccess.
in .htaccess put
SetEnv MY_INCLUDE_PATH "/path/to/my/php/files";
then in php
include ($_SERVER["MY_INCLUDE_PATH"]."/Connection.php");
As many do I have a config.php file in the root of a web app that I want to include in almost every other php file. So most of them have a line like:
require_once("config.php");
or sometimes
require_once("../config.php");
or even
require_once("../../config.php");
But I never get it right the first time. I can't figure out what php is going to consider to be the current working directory when reading one of these files. It is apparently not the directory where the file containing the require_once() call is made because I can have two files in the same directory that have different paths for the config.php.
How I have a situation where one path is correct for refreshing the page but an ajax can that updates part of the page requires a different path to the config.php in the require_once() statement;
What's the secret? From where is that path evaluated?
Shoot, I was afraid this wouldn't be a common problem - This is occurring under apache 2.2.8 and PHP 5.2.6 running on windows.
The current working directory for PHP is the directory in which the called script file is located. If your files looked like this:
/A
foo.php
tar.php
B/
bar.php
If you call foo.php (ex: http://example.com/foo.php), the working directory will be /A/. If you call bar.php (ex: http://example.com/B/bar.php), the working directory will be /A/B/.
There is where it gets tricky. Let us say that foo.php is such:
<?php
require_once( 'B/bar.php' );
?>
And bar.php is:
<?php
require_once( 'tar.php');
?>
If we call foo.php, then bar.php will successfully call tar.php because tar.php and foo.php are in the same directory which happens to be the working directory. If you instead call bar.php, it will fail.
Generally you will see either in all files:
require_once( realpath( dirname( __FILE__ ) ).'/../../path/to/file.php' );
or with the config file:
// config file
define( "APP_ROOT", realpath( dirname( __FILE__ ) ).'/' );
with the rest of the files using:
require_once( APP_ROOT.'../../path/to/file.php' );
I like to do this:
require_once(dirname(__FILE__)."/../_include/header.inc");
That way your paths can always be relative to the current file location.
I use the dirname(__FILE__) thing like bobwienholt most the time, but what it could pay to do is have a base entry point that loads all your other code that defines a constant refereing to the root of the project, ie
define("ROOT",dirname(__FILE__).'/' );
and then later all you need to know is where the path is relative to root, ie:
require(ROOT . "/lib/tool/error.php");
note,
you should REALLY avoid paths with "../" at the start of them, they are not relative to the file, but relative to where you ARE and this creates broken-ass code.
cd foo
php bar/baz.php
-> some error saying it cant find the file
cd bar
php baz.php
-> suddenly working.
Important
If you use "../" notation, it takes complete ignorance of the PHP Include Path, And ONLY considers where the person whom is running it is.
I include this code at the top of every page:
//get basic page variables
$self=$_SERVER['PHP_SELF'];
$thispath=dirname($_SERVER['PHP_SELF']);
$sitebasepath=$_SERVER['DOCUMENT_ROOT'];
//include the global settings, variables and includes
include_once("$sitebasepath/globals/global.include.php");
Include and require both take either a relative path or the full rooted path. I prefer working with the full path and make all my references like the inlcude statement above. This allows me to enter a general variable $sitebasepath that handles account specific information that may change from machine to machine and then simply type the path from the webroot, ie. /globals/whatever_file.php
I also use the $self variable in forms that may call themselves to handle data input.
Hope that helps.
If you have sufficient access rights, try to modify PHP's include_path setting for the whole site. If you cannot do that, you'll either have to route every request through the same PHP script (eg. using Apache mod_rewrite) or you'll have to use an "initialization" script that sets up the include_path:
$includeDir = realpath(dirname(__FILE__) . '/include');
ini_set('include_path', $includeDir . PATH_SEPARATOR . ini_get('include_path'));
After that file is included, use paths relative to the include directory:
require_once '../init.php'; // The init-script
require_once 'MyFile.php'; // Includes /include/MyFile.php
Since require and require_once are very similar to include and include_once, all the documentation is posted under the "include" functions doc area on php.net From that page
Files for including are first looked
for in each include_path entry
relative to the current working
directory, and then in the directory
of current script. E.g. if your
include_path is libraries, current
working directory is /www/, you
included include/a.php and there is
include "b.php" in that file, b.php
is first looked in /www/libraries/
and then in /www/include/. If filename
begins with ./ or ../, it is looked
only in the current working directory.
Further, you can find all the current include paths by doing a "php -i" from the command line. You can edit the include path in your php.ini file, and also via ini_set(). You can also run the php_info() function in your page to get a printout of your env vars if the CLI is inconvenient.
The only place I've seen the path evaluated from is the file that you are currently editing. I've never had any problems with it, but if you are, you might want to provide more information (PHP version, OS, etc).
The path of the PHP file requested in the original GET or POST is essentially the 'working directory' of that script. Any "included" or "required" scripts will inherit that as their working directory as well.
I will either use absolute paths in require statements or modify PHP's include_path to include any path in my app I may want to use to save me the extra typing. You'll find that in php.ini.
include_path = ".:/list/of/paths/:/another/path/:/and/another/one"
I don't know if it'll help you out in this particular instance but the magical constants like FILE and DIR can come in handy if you ever need to know the path a particular file is running in.
http://us2.php.net/manual/en/language.constants.predefined.php
Take a look at the function getcwd. http://us2.php.net/getcwd