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;
Related
I would like to make my projects so fexible that one can simply copy+paste them into each other.
My project structure is ProjectName/(bin, config, lib).
After copy+paste I would have a chain like ProjectName1/lib/ProjectName2/lib/ProjectName3/.
Now it would be great if the Autoloader searches files acording to the level where the file is been called.
For example if a class within ProjectName1/lib/ProjectName2/lib/ calls "new Config" it should receive the config file located in ProjectName1/lib/ProjectName2/config/config.php.
(But not the one in ProjectName1/config/config.php and neither the on in ProjectName1/lib/ProjectName2/lib/ProjectName3/config/config.php).
Is there a way to do this?
Edit:
Does it make sense to make files unique? For example: If 'config.php' was 'ProjectName2Config.php' there is (almost) no chance for conflicts. So the autoloader could search everywhere and will eventually find it's file.
Edit:
Each project would have his own autoloader availible for copy+paste reasons. However I thought that I would load just the one for ProjectName1. Is it better to load them all so that each one can stay simple?
Cheers,
Peter
PS: I just came back from a 3 years programming break. I am also happy, if you tell me that above is gennerally a bad idea and give me the reason why :-)
I think this is a strange way to manage your projects.
Where would be the main script of Project1 and the one of Project2 ?
For example, what I do is I write this in my index.php or main project/lib include file :
<?php define('ROOT_PATH', dirname(__FILE__) . '/'); ?>
And I always use this ROOT_PATH as a start of inclusion paths.
In your case Project1 ROOT_PATH would be something like "/var/www/Project1/" and Project2 ROOT_PATH would be "/var/www/Project1/lib/Project2/"
Thus a new Config in Project1 and in Project2 would use their own config.php file.
I wrote custom classes and want to use them in pimcore application.
I took them to /website/lib/Custom directory on server. Afterwards, I wrote recursive script includer for each Class located in the directory and included that script in /index.php file.
It is absolutely not pimcore standard but it works.
In pimcore/config/startup.php exists snippet:
$autoloaderClassMapFiles = [
PIMCORE_CONFIGURATION_DIRECTORY . "/autoload-classmap.php",
PIMCORE_CUSTOM_CONFIGURATION_DIRECTORY . "/autoload-classmap.php",
PIMCORE_PATH . "/config/autoload-classmap.php",
];
$test = PIMCORE_ASSET_DIRECTORY;
foreach ($autoloaderClassMapFiles as $autoloaderClassMapFile) {
if (file_exists($autoloaderClassMapFile)) {
$classMapAutoLoader = new \Pimcore\Loader\ClassMapAutoloader([$autoloaderClassMapFile]);
$classMapAutoLoader->register();
break;
}
}
I guess that this provides inclusion of all those classes put into returning array from autoload-classmap.php.
Having in mind that /pimcore/config/autoload-classmap.php exists, the mentioned loop would break at first iteration so classes that I would put into custom autoload-classmap are not going to be included in project.
My question is can I change files from /pimcore directory and expect that everything would be fine after system update?
No, you should not overwrite anything in the pimcore directory, since the files in there get overwritten by the update mechanism.
You can do what you want by using the /website/config/startup.php which will not get overwritten:
https://www.pimcore.org/wiki/display/PIMCORE4/Hook+into+the+startup-process
But instead of loading all your classes as you did, take advantage of the autoloader by adding this to the /website/config/startup.php:
// The first line is not absolutely necessary, since the $autoloader variable already gets
// set in the /pimcore/config/startup.php, but it is a more future-proof option
$autoloader = \Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace('Custom');
If you are properly using namespaces and naming your files correctly that's all you need to do.
In PHP when we include or require some file using some initializers like the following. How can we overcome filepath issues which occurs if you include the same initializer in a sub-directory or different location.
<?php
// settings
$settings = array('config');
foreach ($settings as $setting) {
require_once "../system/settings/{$setting}.php";
}
// neutrals
$neutrals = array('functions');
foreach ($neutrals as $neutral) {
require_once "../system/neutrals/{$neutral}.php";
}
// helpers
$helpers = array('database', 'file', 'logger', 'user', 'session', 'database');
foreach ($helpers as $helper) {
require_once "../system/helpers/{$helper}.php";
}
// models
$models = array('test');
foreach ($models as $model) {
require_once "../system/models/{$model}.php";
}
?>
Above script is in a file all_initializer.php. The hurdle here is that i cant use the same initializer in a public sub-directory or other location as it will occur fatal errors (if its a required file) of file not found.
EDIT
For e.g. I am using this initialzer in a public folder in a index.php file then there's a sub-directory in the public folder public/sub. How can I use the same initializer in public/sub/index.php as I used in public/index.php ?
You could use the file_exists to avoid fatal errors
// neutrals
$neutrals = array('functions');
foreach ($neutrals as $neutral) {
if (file_exists("../system/neutrals/{$neutral}.php")
{
require_once "../system/neutrals/{$neutral}.php";
}
else
{
// Do some logging here so as to know that something went wrong
}
}
As for the path issues you are referring to, you can include this file from anywhere so long as you supply the proper base path for your operations. Defining a ROOT_PATH in your index.php file would help you detect where your script is and what it needs to load.
So for instance if you have this structure:
/system
/system/neutrals
/system/models
/public
/public/index.php
in your index.php you can define a ROOT_PATH constant that will be used throughout the application and serve as a point of reference.
// this points to the folder that has /public and /system
define('ROOT_PATH', dirname(dirname(__FILE__))));
You can also have a constant just for your system folder
define('SYSTEM_PATH', ROOT_PATH . '/system');
and then all your require_once declarations become:
require_once SYSTEM_PATH ."/neutrals/{$neutral}.php";
EDIT: Based on additional information in the question
Structure:
/all_includes.php
/system
/system/neutrals
/system/models
/public
/public/index.php
/public/sub/index_sub.php
In index.php you define
// this points to the folder that has /public and /system
define('ROOT_PATH', dirname(dirname(__FILE__))));
and then:
require_once ROOT_PATH . '/all_includes.php';
to do your initialization. The same thing happens in the public/sub/index_sub.php.
Your all_includes.php becomes:
// neutrals
$neutrals = array('functions');
foreach ($neutrals as $neutral) {
if (file_exists(ROOT_PATH . "/system/neutrals/{$neutral}.php")
{
require_once ROOT_PATH . "/system/neutrals/{$neutral}.php";
}
else
{
// Do some logging here so as to know that something went wrong
}
}
The way most of the php frameworks work is similar to the following, zend framework works like this:
Declare a constant APPLICATION_PATH and then make all the paths relative to this one.
define("APPLICATION_PATH", "/var/www/mysite");
And then all your requires will be relative to your APPLICATION_PATH.
require_once APPLICATION_PATH ."/system/helpers/{$helper}.php";
* With this approach you can include files from whatever script without issues. Because all the paths are going to be relative to your APPLICATION_PATH.
Simple.. don't use relative paths... in your foreach loops, you could do:
require($_SERVER['DOCUMENT_ROOT'].'/system/thing.php');
the pathing problem can be solved in this way, you have one file that is always in the same place. for example you have (this is you public html)
.
..
folder/subfolder/filetoinclude.php
folder2/includein.php
thefilefolder/thefile.php
ok now in the file php you have a variable
$path=dirname(__FILE__);
this will always give you the absolute path to that file, then you ca use this $path variable and build your include paths around it.
in this example you have to include $path.'../folder/subfolder/filetoinclude.php';
The pint is to alway use the same point of origin, and not use relative paths.
and then you can make a custom include function in thefile.php, and then the things get really easy.
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.
I have a server with many customers on, when I develop I include my init.php in which I have an __autoloader() function that includes the file with dir_name(__FILE__)."/classes/".$className for instance.
But yesterday I saw that the server could not find the specific class, I restartat apache and then it worked again.
Every customer has this own init.php... ( and therefore many __autoloads on the same server )
customer1/init.php : holds __autoload()
customer1/classes/class.php
customer2/init.php : holds __autoload()
customer2/classes/class.php
I have not done some tests and I hope someone can answer my question before I try to reproduce the problem, but do you think it is possible for php to take the wrong autoload function when you get 2 or more requests at the same time?
Is spl_autoload_register the solution?
Many thanks for some ideas or brainstorming.
My guess is that you should have a typo in either one of your __autoload() functions or you are including the wrong init.php file.
Also, dir_name() does not exist, you should change that to dirname() instead or you can also use the new DIR constant for the same effect if you're using PHP >= 5.3.
EDIT: In light of your comment, use should use:
require(realpath(dirname(__FILE__)) . '/classes/' . $className);
or
require(realpath(__DIR__) . '/classes/' . $className);
Each PHP request is completely separate, in fact it is impossible for you to have two functions named __autoload() in the same PHP request, so they cannot interfere. Possible problems:
You are including the wrong customer's init.php
You forgot to include the init.php file, in which case there is no autoloading at all.