I know PHP > 5.3 added the Magic constants : __DIR__ to get the father dir path,
But how can I get the grandfather dir path in a smarter way?
1.__DIR__.'/../'
2.dirname(__DIR__)
Use the second parameter levels for the dirname call
echo dirname(__DIR__,2); // 1 Level up = Father. 2 Levels up = Grandfather
However, it is not any better than what you already have. It's just smarter like you asked for :)
You can simply move up a folder by using ...
For example:
$path = dirname(__DIR__);
$grandfatherPath = $path.'/../../';
All my PHP include files are in a single directory:
https://www.mywebsite.com/includes
Inserting these files in top level pages is easy enough:
<?php include 'includes/logo.php'; ?>
<?php include 'includes/main_nav.php'; ?>
<?php include 'includes/news.php'; ?>
etc.
For sub-directory pages I've been doing this:
<?php include '../includes/logo.php'; ?>
<?php include '../includes/main_nav.php'; ?>
<?php include '../includes/news.php'; ?>
and this:
<?php include '../../includes/logo.php'; ?>
<?php include '../../includes/main_nav.php'; ?>
<?php include '../../includes/news.php'; ?>
So far so good, but I suspected it wasn't going to continuing being this easy.
Now I need to include this file:
top_five_news_stories.php
in this:
news.php
At this point, my relative path strategy fails because the include in the include can have only one path structure.
I've read several posts recommending absolute paths using:
dirname(__FILE__)
realpath(dirname(__FILE__)
$_SERVER["DOCUMENT_ROOT"]
However, they all come with some kind of caveat relating to PHP configuration, server configuration or operating system. In other words, there's often a comment by somebody saying it didn't work in their case, or doesn't work in IIS, or doesn't work in UNIX, or something else.
A solution I haven't seen is one I thought would be most simple: Just set a variable:
$base = "https://www.mywebsite.com/includes";
then:
<?php include $base . "logo.php" ?>
Considering that I already use the HTML base element, which works in a similar way, this method strikes me as simple and efficient.
But since it wasn't mentioned in any of the posts I read, I'm wondering if I'm overlooking a potential problem.
Frankly, if I had to go to production to today, I would use this:
<?php include $_SERVER['DOCUMENT_ROOT'] . '/logo.php" ?>
which works for me and is commonly mentioned.
But I'm wondering if using a variable is a reliable, efficient method?
Don't
I would advise against using anything that needs something outside of PHP, like the $_SERVER variable.
$_SERVER['DOCUMENT_ROOT'] is usually set by the webserver, which makes it unusable for scripts running from the command line. So don't use this.
Also don't use url's. The path-part in a url is not the same as the path of the file on disk. In fact, that path can not even exist on disk (think Apache rewrites).
Including url's also needs you to turn allow_url_include on, which introduces (severe) security risks if used improperly.
Do
If your minimal supported PHP version is 5.3 (I hope it is!), you can use the magic constant __DIR__. 2 examples:
define(ROOT_DIR, __DIR__);
define(ROOT_DIR, realpath(__DIR__ . '/..'));
If you need to support lower versions, use dirname(__FILE__). 2 examples:
define(ROOT_DIR, dirname(__FILE__));
define(ROOT_DIR, realpath(dirname(__FILE__) . '/..'));
Make sure ROOT_DIR points to the root of you project, not some subdirectory inside it.
You can then safely use ROOT_DIR to include other files:
include ROOT_DIR . '/some/other/file.php';
Note that I'm defining a constant (ROOT_DIR), not a variable. Variables can change, but the root directory of you project doesn't, so a constant fits better.
realpath()
realpath() will resolve any relative parts and symlinks to the canonicalized absolute pathname.
So given the following files and symlink:
/path/to/some/file.php
/path/to/another/file.php
/path/to/symlink => /path/to/another
and /path/to/file.php contains:
define(ROOT_DIR, realpath(__DIR__ . '/../symlink'));
then ROOT_DIR would become /path/to/another, because:
__DIR__ equals to /path/to/some (so we get /path/to/some/../symlink)
.. is 1 directory up (so we get /path/to/symlink)
symlink points to /path/to/another
You don't really need to use realpath(), but it does tidy up the path if you're relying on relative parts or symlinks. It's also easier to debug.
Autoloading
If you need to include files that contain classes, you'd best use autoloading. That way you won't need include statements at all.
Use a framework
One last pease of advise: This problem has been solved many many times over. I suggest you go look into a framework like Symfony, Zend Framework, Laravel, etc. If you don't want a "full stack" solution, look into micro-frameworks like Silex, Slim, Lumen, etc.
Jasper makes some good points, and a further reason for not using DOCUMENT_ROOT is that content accessible via a URL does not have to be within this directory (consider Apache's alias, scriptalias and mod_user_dir, for example).
As Barmar points out PHP explicitly provides functionality for declaring a base directory for includes. While this is typically set in the config it can be overridden/added to at runtime in your code. You never want to see a variable in your include/require directives. It breaks automatic tools and hides vulnerabilities. Nor should you ever include using the file wrappers.
There is an argument in OO programming for never using include/require explicitly but just autoloading class definitions. However the problem of locating the code remains.
The short answer is that there is no best solution for the problem you describe. Each method has its drawbacks - the best solution is completely dependent on the context. For an enterprise application, setting the include_path simplifies development processes and, if not directly accessible from the webserver enhances security. It also allows for selectively overlaying functionality by manipulating the order of multiple entries in the path.
On the other hand this is not a good model for software you intend to distribute to less technical users likely to be confused about multiple paths whom may not have access to directories outside the document root or to change the default config.
Using relative paths is a robust and portable solution. I don't understand your problem with including top_five_news_stories.php.
A solution which gives you the benefits of both the enterprise and low-end hosting is shown below. This however has the drawback that it needs code added to each entry point in the site (and requires the app to be installed in a named sub directory):
define('APP_NAME', 'mikesdemo');
$base=substr(__DIR__, 0, strrpos(__DIR__, APP_NAME))
. APP_NAME . '/include';
set_include_path(get_include_path() . PATH_SEPARATOR . $base);
The more sophisticated user can then simply....
mv /var/www/html/mikesdemo/include/* /usr/local/php/include/
File Arquitecture Rework:
define a path for every tipe of file like that:
+root
|
+------app(D)(all php script MVC)
|
+------conf(D)(all php Config file)
|
+------assets(D)(all file js and css static image)
|
+------fileup(D)(all file Uploades)
|
+------index.php(F)(Procesor of petition http)
in your index you need include all File of config Like style C++:
Example:
require_once ('conf/config.security.php'); #Configuration around Security in PHP
require_once ('conf/config.conpro.php'); #Configuration around Constantent
require_once ('conf/config.classlib.php'); #Lib class around Generic DB Conection ETC
require_once ('conf/config.classlibmvc.php'); #Lib class around MVC specific class
And example of config file:
Declare Root Directory + Path that shared the file
$APP_DIR_CLASS = $_SERVER['DOCUMENT_ROOT'] . '/app/classgeneric/';
Define library file:
if (!defined('DBMANAGER_CLASS')) define('DBMANAGER_CLASS' ,'class.managerdb.php' );
Include or require the class
require_once $APP_DIR_CLASS . DBMANAGER_CLASS;
when you are in a class and need use DB class you can call it easy:
class Class_Exmaple{
public function __construct(){
$this -> DBMANAGER = new Class_BDManager();
}
public function __destruct(){
$this -> DBMANAGER = new Class_BDManager();
}
public function ConsultDB(){
$query ='Selec * From Tablename';
$result = $this -> DBMANAGER -> ExecuteQ($query);
print_r(result);
}
}
is a easy way to implement but you need learn more about injection and Class Loaders.
There is no "correct way" to require/include an internal script in your project. A lot (most) MVC frameworks use similar best practices to global file access in a router object.
Let's take an example, here is our directory infrastructure:
App/
Controllers/
Controller.php
Models/
Model.php
Views/
View.php
404/
index.php
index.php
.htaccess
Inside our .htaccess we would have a rewrite rule to the index.php in the root directory of your server.
Inside this file, is where we actually run the whole of your Software. For example, this is a great router I use AltoRouter.
First things first, we need to add a way to stop direct browser access and an error path to any controllers, models and views:
define( 'ERROR_PATH', strtolower(explode( '/', $_SERVER['SERVER_PROTOCOL'][0]) . '://' . $_SERVER['SERVER_NAME'] . '/404' );
define( 'IN_APP', 0 );
Which is then used in your controllers, models and views like:
if( !defined( 'IN_APP' ) ) {
header('Location: ' . ERROR_PATH);
exit();
}
Your file path will be the root file path if you declare __FILE__ in this instance (index.php) so we can use it any way (best practice is global defines).
define( '_DIR_', dirname( __FILE__ ) );
Then start requiring your files:
$includeFiles = [
glob( _DIR_ . '/Controllers/*.php' ),
glob( _DIR_ . '/Models/*.php' )
];
foreach( $includeFiles as $dir ):
foreach( $dir as $file ):
require_once( $file );
endforeach;
endforeach;
I am using jquery datatable plugin which is in outside of my Codeigniter app. The datatable php editor uses namespaces.
And i have to import all editor class files inside the codeigniter controllers. So, i have to pull all files and classes inside to the "/assets/datatable/extensions/Editor/php" into my newly created controller file.
Controller file code,
// trying to import all files in this directory
$path = $_SERVER['DOCUMENT_ROOT'] . '/datatables/extensions/Editor/php';
//set_include_path(get_include_path() . PATH_SEPARATOR . $path);
ini_set('include_path', get_include_path() . PATH_SEPARATOR . $path);
Editor::inst($db, 'contactus', 'id')
->fields(
Field::inst('Position')
->validator('Validate::numeric', array('empty' => false)), Field::inst('Question')
->validator('Validate::notEmpty'), Field::inst('Answer')
->validator('Validate::notEmpty')
)
->process($_POST)
->json();
The both "set_include_path" and "ini_set" not working in my case. It returns the below error
Fatal error: Class 'Editor' not found in /var/www/html//application/controllers/ajax.php on line 50.
Please suggest for the same.
This always works for me:
require_once($path);
instead of ini_set(). Also make sure that the path is really correct. You can use a relative path (relative to index.php). So you get something like $path = './application/libraries/custom/autoload.php';.
(I can't post this as a comment)
When you use base_path() or public_path() , the result contains bootstrap/.. or bootstrap/../public because the path is taken from the bootstrap/paths.php file. Is there any method to get only the real base path?
ex : /homepages/0/d271770356/htdocs/ or /homepages/0/d271770356/htdocs/public
Thanks
PHP has a function for this called realpath(). However it's not necessary in most cases as the file functions usually work with .. fine.
I am seeking a way of allowing my PHP applications to be perfectly portable. My problem is, although I am utilizing relative path to include PHP classes, I always face issues when I try to deploy me application in a new environment.
For example, I have implemented an application under Ubuntu and it just run perfectly. However, when I moved it to a shared hosting running Centos, I had to modify all the include statements.
So, I am asking about the best way to include classes, considering having multiple folders which contain various classes that are dependent on another multiple classes in different levels of the folder hierarchy.
just keep one "main" folder.
In your index.php (for ex.) configure the "main" folder location and either use that as the 'base' for includes (I suppose you hard-code the include/require path?)
Else use the 'base' within the autoload functionality.
Now you are able to move the 'main' folder around and all you need to do is update just one line of code in your index.php
It is still a manual update. True that. You can also ofc. use something like glob() and search for you "mainlib.php" file (for ex.) and 'cache' that folders location to use it in the next calls?
This for example is how I do it:
<?php
/**
* cfg.php
*
* Main config file
*
* #package Public
*/
// Compatibility
$version = '5.2.3';//restricted by htmlentities()' 4th parameter
if(version_compare(PHP_VERSION, $version, '<')) {
die('Required PHP version is ' . $version . ', current is ' . PHP_VERSION);
}
// Environment
define('DEVELOPMENT', in_array($_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1')));
define('PRIVATE_DIR', DEVELOPMENT ? 'private' . DIRECTORY_SEPARATOR : '..'.DIRECTORY_SEPARATOR.'private_html'.DIRECTORY_SEPARATOR.'tickets');
define('APPLICATION_LINK','application_red'.DIRECTORY_SEPARATOR);
define('LIBRARY_LINK','library'.DIRECTORY_SEPARATOR);
define("MEM_START",memory_get_usage(true));
// Behavior
if(DEVELOPMENT) {
ini_set('display_errors', 'On');
error_reporting(E_ALL | E_STRICT);//report all errors
}
else {
ini_set('display_errors', 'Off');
error_reporting(0);
}
// Timezone
date_default_timezone_set('Europe/Amsterdam');
// Constants
define('ROOT', dirname(__FILE__) . DIRECTORY_SEPARATOR);
define('APP', ROOT . '..'.DIRECTORY_SEPARATOR.PRIVATE_DIR.''.APPLICATION_LINK);
define('LIB', ROOT . '..'.DIRECTORY_SEPARATOR.PRIVATE_DIR.''.LIBRARY_LINK);
define('CACHE', APP.'cache'.DIRECTORY_SEPARATOR);
index.php/utest.php:
<?php
include("cfg.php");
// Start library
require_once LIB.'Library.php';
$library = new Library();
//etc.......
You don't need to make reference to a hardwired folder at all. In my current project I do this:
public static function getProjectRoot()
{
return realpath(
dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' .
DIRECTORY_SEPARATOR . '..'
);
}
The class in which this features is two folder levels inside the project - hence the two .. operators to traverse up the directory structure. Since that location will never change in relation to the project root, this doesn't need changing, and I don't ever need to hardwire any paths.
Edit: in relation to include/require statements, use an autoloader, and (apart from a couple of bootstrap files) you don't generally need to use includes/requires at all.