I am working on a new PHP project now, this time I want to get the basics right at the beginning. Previously I've found requiring/including files in php a bit of pain, please consider the following structure:
/application_root/index.php
/js/...
/css/...
/php/...
/conf/...
...
In the index.php I can certainly use something like:
<link rel="stylesheet" href="css/sample.css" ... />
<script type="text/javascript" src="js/sample.js"></script>
To refer to the included css and js, or even php snippets. However, this would only work in the index.php which resides under the root of my application folder. I reckon this is no good.
I came across Java application configuration file "web.xml" where you can define application scope variables that you can simply refer to. .NET with C# has a similar thing. How to achieve this in simple php code so that from any php file in my app, I can type:
<?php echo "<link href='".$application_root_url."/php/sample.css' ..."; ?>
And it will evaluate to the right location?
I am thinking to use:
Global variables <== bad practice as violation to OOP? I stop doing this since c programming;
set_include_path <== so php will look for it, requires unique name and proper naming convention?
load variables from ini files? <== how to make this happen?
any new thoughts?
You don't want to use global variables because they break encapsulation.
set_include_path will do no good especially if you are using those variables in HTML, because the include_path is relative to the application's filesystem path and not to its base url.
Determining the application base paths is generally not done from a configuration file, as it easy to detect when your application has a gateway script. Since those are constant values, it makes sense to define a constant:
define('APP_ROOT', dirname(__FILE__));
define('APP_URL', dirname($_SERVER['PHP_SELF']));
If you want to parse INI files however, you could use parse_ini_file, or parse_ini_string (>=5.3.0):
$config = parse_ini_file(APP_ROOT.'/'.CONFIG_DIR.'/database.ini');
echo $config['host'];
The path here is a URI--not a filesystem location. You may be trying to go about this the wrong way anyway.
You have to use relative path. So just "/css/sample.css" instad of "css/sample.css".
That would than always load from yourdomain.com/css/sample.css even if your .php is in yourdomain.com/somefolder/file.php
The PHP script only produces an output that the browser interprets. And the scope is the URL in the browser not on the filesystem.
So the value of the $application_root_url variable is for the browser not for the PHP script!
If you want to use INI files, you can use the parse_ini_file() function of PHP.
no reason for using global variables except for lazy coding
is not efficient, and PHP will get harder to figure which file to be included if two same filename on different path
parse_ini_file is what you looking for
However, I prefer using constant, I did not ask to define constant everywhere, just put all your essential path into a config file, and require that at the beginning on your application.
Some might say constant is slow, comparing using class constant which might require you to include multiple files, which does better ? And the best things is once constant defined, no-one or code can override it.
example to illustrate
define('css_root', '/home/user/apache/css'); <-- server absolute path
define('css_web_root', '/css'); <-- web root, for HTML
define('css_cache_root', '/cache/css'); <-- cache directory
You might want to try something like the MVC pattern. As an example the ZendFrameworkds MVC passes a variable $this->base_url to the views. The views are where you HTML resides so you will be able to do the following in the view:
<link rel="stylesheet" href="<?php echo $this->base_url; ?>/css/sample.css" ... />
Your problem is exactly the reason that led me to the MVC pattern and it's many advantages.
Related
My PhP files contain some long string constants and I'm trying to factor them out. So I created "my_string_constants.php" and used include in header.php. So far that works fine.
Now another file, page.php also requires the string constants and header.php. The scheme below tries to clarify these dependendies.
The string constants now seem available in my header only, not the rest of my page. I tried to resolve this by adding global ... to each string constant in string_constants.php. This resolved the error of "Unknown variable" but my string constants still seem unavailable to most of the page content.
What's the right way to get this working?
UPDATE
The issue's been solved. I should have used define(myString instead of $myString = .... By doing so, I need just one include in header.php and the constants will be available to page.php as well.
Thanks a million, you guys are great.
One thing you would want to do is distinguish a constant from a variable. Especially if other developers end up working on this, they will be confused by your terminology.
For constants, you do not need to declare them as global and they are defined like so:
define('MY_CONSTANT', 'Value');
It seems to me that the constants file is acting as your site wide configuration file, so to me, it makes sense to have that on every page, regardless of whether header is used or not. I would normally create a bootstrap file for this purpose.
bootstrap.php:
<?php
require_once(__DIR__ . '/constants.php');
require_once(__DIR__ . '/database.php');
require_once(__DIR__ . '/session.php');
You get the point, then every accessible page needs to include this bootstrap file and then possibly the header.
page.php:
<?php
require_once(__DIR__ . '/bootstrap/bootstrap.php');
require_once(__DIR__ . '/header.php');
?>
<h1>Page Title</h1>
In header.php, since it requires these constants, you can handle this in two ways, either check that the constants are defined (meaning, bootstrap was included first) or just use another require_once to make sure that file was loaded.
if (!defined('MY_CONSTANT')) exit('Bootstrap failure');
or
require_once(__DIR__ . '/bootstrap/constants.php');
Going to many directories or "files" deep regarding includes can really cause issues later when you are trying to debug. As a rule I try to only go one level deep in regards to including files. I.e. Create a folder called includes and place everything in there. If there is a file that needs multiple variables, functions etc, then include them in the needed pages at that point doing several includes like so:
<?php
include("includes/header.php");
includes("includes/functions.php");
?>
There are also other issues in regards to having multiple includes, like if you have sessions or cookies some LAMP stacks will require you to declare
session_start();
at the top of every page including all included php files that may need access to that session or cookie.
So to answer your question I believe the simplest solution would be to re-organize your site or script.
in the header page ontop u write
include 'header.php';
and in header.php you write
include 'my_string_constants.php';
so in this case the page.php calls the header and in the header the my_string_constants is being called...is this what you mean?
I'm building an application and using index.php as and entry point to different modules. I noticed SugarCRM does this and it appears like a good idea.
The URL Looks like this
http://www.mypage.com/index.php?mod=log&pag=login
Where mod is the module and pag is the page
The index.php looks line this:
<?PHP
define('INCLUDE_CHECK',true);
// Class Loader
require ('app/inc/app_autoload.php');
// HTML Header with js and css links
require ('header.php');
// Content Page
$url_module = $_GET["mod"];
$url_page = $_GET["pag"];
$content = $url_module."/".$url_page.".php";
// For the above URL $content = log/login.php
if (!file_exists ($content)) {
require ($content);
}else{
// Handle Error
}
// Footer
require ('footer.php');
?>
Is this safe?
If it's safe, Is it in line with practices?
This can be potentially unsafe. Depends on all the other PHP files that PHP can open. If all of them are class files that don't execute anything, then you're safe. However, if any of them execute something automatically...maybe not.
Let's say that you have PHP files inside a folder:
/secured/file.php
And let's say that the folder has an .htaccess that prohibits anyone from navigating to the page directly. Or better, let's say it's above your root directory. However, the hacker sends "../secured" as the value of mod and "file" as the value of page. In such a case, PHP may allow the person to include that file, and if it self-executes, it may have unintended consequences.
This is why Zend Framework requires explicit configuration of all MVC paths. Other frameworks allow for a some dynamic inclusion, but they often do something like append "Controller.php" to the end of the string, which ensures that the file included must be a Controller...and thus intended to be included in such a way.
When it comes to security, the best thing you can do is make sure that YOU...with all the knowledge of the entire server...can't open up any file that you don't want to be opened by someone else. If you can't get the code to do it, knowing what files are there, then you have implemented some decent (though likely still not flawless) security.
This is a newbie question, and I know it.
Template structure is your usual index.php, with a few require_once()'s for the header/footer etc.
I define a var at the top of index.php before any of the require_once()'s for the base url, such as $url = 'http://url';
I then want to echo this out into all template files, header/index/footer etc, it works inside index.php as expected, but fails with a undefined var in all template files that are included in.
I know it's a var scope issue, but I'm totally perplexed how to fix it.
I'm aware that the manual says vars are available to included files, however they aren't. Could it be a issue with my local PHP install?
edit : Created a couple of test files, and a var is defined between 2 files, so why are they not working on my main site files?
Any helps gracefully recieved.
Many Thanks
if you use functions or methods (functions in classes) then you need to do global $variable inside the function. Otherwise you will not have access to it, you also could define it as constant. A constant is always global.
define('MYURL', $url);
You might want to use a PHP framework, if you not already do so.
I was wondering if it possible to add constants to php before any scripts are ran, thus on startup. If this is possible, could it be done with classes etc aswell?
I was thinking in the direction of creating a plugin for php but maybe there is a way simpler way.
I don't mean including a file in every script.
thanks in advance
Not constants as far as I'm aware, but this is ok:
.htaccess
SetEnv MYVAR "hello"
somefile.php
echo $_SERVER['MYVAR'];
See the Apache docs on SetEnv for more.
To directly answer the question, there are two approaches:
Use auto_prepend_file to auto include a PHP file that has define calls.
Configure your web server to set server variables.
I think the second is a better approach. However, I don't see how either of them are very useful in the context of a plugin. Usually a class autoloader of some sort is the way to go there, or to require a single include file.
If I understand your question correctly, what I do is to include a file before all else on my index.php. That same file contains tons of constants, control verifications, initialization for the DB object, etc...
e.g.,
INSIDE index.php
<?php
$moduleRoot = dirname(__FILE__);
require_once($moduleRoot."/components/inc/inc.php");
// continue to render the web page and perform as usual
?>
INSIDE THE inc.php
// When in development, all errors should be presented
// Comment this two lines when in production
error_reporting(E_ALL);
ini_set('display_errors', '1');
// Website id for this project
// the website must be present in the table site in order to get
// the configurations and records that belong to this website
define("CONF_SITE_ID",1);
// Domain path where the project is located
// Should be like the access used on the browser
$serverDomain = $_SERVER["HTTP_HOST"];
$serverAccess = (!empty($_SERVER['HTTPS'])) ? ('https://') : ('http://');
$serverRoot = dirname(__FILE__);
define("CONF_DOMAIN", $serverAccess.$serverDomain);
// etc ...
EDITED
Since you have multiple "startup" files and you need all of them to call inc.php, the best choise seems to be .user.ini as of PHP 5.3.0, you can read about it here!.
And an article on the subject.
I'm trying to dynamically detect the root directory of my page in order to direct to a specific script.
echo ($_SERVER['DOCUMENT_ROOT']);
It prints /myName/folder/index.php
I'd like to use in a html-file to enter a certain script like this:
log out
This seems to be in bad syntax, the path is not successfully resolved.
What's the proper approach to detect the path to logout.php?
The same question in different words:
How can I reliably achieve the path to the root directory (which contains my index.php) from ANY subdirectory? No matter if the html file is in /lib/subfolder or in /anotherDirectory, I want it to have a link directing to /lib/logout.php
On my machine it's supposed to be http://localhost/myName/folder (which contains index.php and all subdirectories), on someone else's it might be http://localhost/project
How can I detect the path to application root?
After some clarification from the OP it become possible to answer this question.
If you have some configuration file being included in all php scripts, placed in the app's root folder you can use this file to determine your application root:
$approot = substr(dirname(__FILE__),strlen($_SERVER['DOCUMENT_ROOT']));
__FILE__ constant will give you filesystem path to this file. If you subtract DOCUMENT_ROOT from it, the rest will be what you're looking for. So it can be used in your templates:
log out
Probably you are looking for the URL not the Path
log out
and you are not echoing the variable in your example.
Your DOCUMENT_ROOT is local to your machine - so it might end up being c:/www or something, useful for statements like REQUIRE or INCLUDE but not useful for links.
If you've got a page accessible on the web - linking back to a document on C: is going to try and get that drive from the local machine.
So for links, you should just be able to go /lib/logout.php with the initial slash taking you right to the top of your web accessible structure.
Your page, locally - might be in c:/www/myprojects/project1/lib/logout.php but the site itself might be at http://www.mydomain.com/lib/project.php
Frameworks like Symfony offer a sophisticated routing mechanism which allows you to write link urls like this:
log out
It has tons of possibilities, which are described in the tutorial.
Try this,
log out
This jumps to the root directly.
DOCUMENT_ROOT refers to the physical path on the webserver. There is no generic way to detect the http path fragment. Quite often you can however use PHP_SELF or REQUEST_URI
Both depend on how the current script was invoked. If the current request was to the index.php in a /whatever/ directory, then try the raw REQUEST_URI string. Otherwise it's quite commonly:
<?= dirname($_SERVER["SCRIPT_NAME"]) . "/lib/logout.php" ?>
It's often best if you use a configurable constant for such purposes however. There are too many ifs going on here.
I'm trying to figure this out for PHP as well. In asp.net, we have Request.ApplicationPath, which makes this pretty easy.
For anyone out there fluent in PHP who is trying to help, this code does what the OP is asking, but in asp.net:
public string AppUrl
{
get
{
string appUrl = Request.Url.GetLeftPart(UriPartial.Authority) + Request.ApplicationPath;
if (appUrl.Substring(appUrl.Length - 1) != "/")
{
appUrl += "/";
}
// Workaround for sockets issue when using VS Built-int web server
appUrl = appUrl.Replace("0.0.0.0", "localhost");
return appUrl;
}
}
I couldn't figure out how to do this in PHP, so what I did was create a file called globals.php, which I stuck in the root. It has this line:
$appPath = "http://localhost/MyApplication/";
It is part of the project, but excluded from source control. So various devs just set it to whatever they want and we make sure to never deploy it. This is probably the effort the OP is trying to skip (as I skipped with my asp.net code).
I hope this helps lead to an answer, or provides a work-around for PHPers out there.