Solved

Trouble with absolute links vs document relative links in PHP.

Posted on 2014-01-22
28
247 Views
Last Modified: 2014-01-24
So I wanted to do the right thing and have pages with almost no code on them, pages which contained mostly includes to other files with HTML and PHP.  But quickly started running into problems when the files I was including pointed to other PHP resources with document-relative links.  And the file receiving the included files had a different path to the various directors and etc.  It was a mess and a half.  So i just wanted to know how some of you advanced users address this issue.  Do you just put in the entire absolute links everywhere in the site?
0
Comment
Question by:LB1234
  • 13
  • 10
  • 4
  • +1
28 Comments
 
LVL 108

Assisted Solution

by:Ray Paseur
Ray Paseur earned 75 total points
Comment Utility
Speaking only for myself, I try to keep as much as possible in the document root.  There are some exceptions, for example, I load my private information like secure documents and data base credentials from one level above the document root.  But for the most part, everything I want to address is relative to where the script resides.  CSS, JavaScript and media are in their own directories   HTML tags are pretty simple: For example, images, they look like this:

<img src="images/red_wagon.png" />

I have only idempotent scripts in the web root.  This requires a bit of understanding but it's not that hard to get right.  The rule is, "if anybody runs this script either once or a thousand times, nothing will change in the data model."

My standard startup script looks like this:

<?php require_once('common.php');

That connects the data base, starts the session, retrieves the cookie and finds the active record for the user of the site.

Other designs differ.  WordPress, for example has several sub-directories but the three main ones are wp-admin, wp-content, wp-includes.

Does that help?
0
 
LVL 58

Expert Comment

by:Gary
Comment Utility
Are we talking HTML links or PHP include links?
If HTML prepend all your links with / (forward slash) which means the link will start from your root folder and then follow that with the directory structure to the file.
0
 
LVL 82

Assisted Solution

by:Dave Baldwin
Dave Baldwin earned 425 total points
Comment Utility
I do the same as Ray if I am starting a new project.  HTML and PHP in the document root.  If I have 'admin' functions that I want to protect a little more, I might put those in their own subdirectory.  But I don't recall every doing a project where I had more than two directory levels.  It gets too messy for the reasons that you have encountered on your project.

Too many directory levels is hard to keep track of.  Keeping everything in the root can make it hard to find things.  I have one customer that puts everything in the root including over 1000 images and backup copies of the scripts.  And of course all of his sites are live so he doesn't want to change anything.
0
 
LVL 108

Expert Comment

by:Ray Paseur
Comment Utility
A PHP function to help you stay organized:
http://php.net/manual/en/function.getcwd.php
0
 
LVL 1

Author Comment

by:LB1234
Comment Utility
I hear you guys.  But it sounds like there are cases when files must be in two different directories (as in the case of security with sensitive files).  Was just wondering the correct approach in those instances when a multi-directory file structure is unavoidable.  

For example I have a dynamic left menu that constructs its links based on what's in the $_GET array that linked to that particular page.  At the top of that file called "menu.php" it references a file called dbconnection.com so that it can contact the database and pull the appropriate links, etc.   When I add "menu.php" to another page that also has a document relative link to dbconnection.com, things seem to go awry.  Seems like the only solution besides having both files in the same directory is to use absolute links?
0
 
LVL 108

Assisted Solution

by:Ray Paseur
Ray Paseur earned 75 total points
Comment Utility
Some of your solution can come from better organization.  Example: Why would the menu need to call the DB connection?  Wouldn't other parts of the page have a dependency on the DB connection, too?  I would just make sure that all of the script initialization is completed in one place before starting any of the other stuff on the page.

Here is the general framework I start from.

<?php 
require_once('common.php');

/* DATA MODEL AND CONTROLLER SCRIPT GOES HERE */

require_once('header.php');

/* VIEW SCRIPT GOES HERE */

require_once('footer.php');

Open in new window

0
 
LVL 1

Author Comment

by:LB1234
Comment Utility
here's the menu code from the file called menu.php. When I reference menu.php from a file called dashboard.php, which itself includes a dbconnection.php file, I get an error telling me that $connection in the mysqli_query command below is an undefined variable.  Which lead me to thinking, "ok the menu.php file must need its own database connection, so that's why I included a link to the dbconnection.php file in menu.php.  Should $connection just be put in a $_SESSION array?


<?php

		if (!isset($_GET["id"])) {
			?>
			
			 <div id="menu">
					<ul>
						<li><a href="#">Pending Expenses</a></li>
						<li><a href="#">Open Report</a></li>
					</ul>
			</div>
<?php
		} else {
			$id = $_GET["id"];
			
			$query = "SELECT * FROM pages WHERE subject = '{$id}'";
	
			$result = mysqli_query($connection, $query);
			
			while ($row = mysqli_fetch_assoc($result)) {
			echo $row["link"] . "<br>";
			}
		} 
	
?>

Open in new window

0
 
LVL 1

Author Comment

by:LB1234
Comment Utility
Here's the dashboard code, which references the code above.  Db_connection establishes the $connection variable, but it appears the menu.php page doesn't have access to that variable, since the error message says it's undefined.

<?php session_start();?>
<?php include ("../includes/db_connection.php");?>
<?php include ("../includes/layouts/header.php");?>
<?php include ("../includes/layouts/nav.php");?>
<?php include ("../includes/layouts/menu.php");?>
























<?php include ("../includes/layouts/footer.php");?>

Open in new window

0
 
LVL 108

Expert Comment

by:Ray Paseur
Comment Utility
Maybe something is wrapped in a class or function definition?  It looks like the variable may be out of scope.  That's just a guess, but it's a common mistake.

Required reading: http://www.php.net/manual/en/language.variables.scope.php
0
 
LVL 1

Author Comment

by:LB1234
Comment Utility
I guess my question would be when I include a file, does it run the code in THAT page, or does it pull the code into the existing code and run it there.  That would make all the difference in determining whether the page being included should have its own connection to the database.
0
 
LVL 82

Assisted Solution

by:Dave Baldwin
Dave Baldwin earned 425 total points
Comment Utility
'include' includes the code in the current page so that it all becomes one page.  Addresses are relative to the 'current page', not the included page.

The exception to that is when you 'include' another PHP page using 'http://'.  Anytime you use 'http://' to get another page, what you actually get is the 'results' of whatever that page does.  If it is HTML or text, you will get the HTML or text.  If it is PHP or ASP or any other programming language, you will get the 'results' of the code but not the source code.
0
 
LVL 1

Author Comment

by:LB1234
Comment Utility
So Dave, in the following page called dashboard.php, whenever I remove the same db_connection seen below FROM the menu page, I get an error stating that the $connection parameter in the mysqli_query($connection, $query) on the menu.php is an undefined variable.  When I included the db_connection.php include to the top of menu.php page, it says it's already been defined. This makes no sense.

<?php session_start();
include ("db_connection.php");
include ("header.php");
include ("nav.php");
require_once ("menu.php");
?>

Open in new window

0
 
LVL 108

Expert Comment

by:Ray Paseur
Comment Utility
Required reading here: http://php.net/manual/en/function.include.php

One of the most important things about include() is this:
When a file is included, the code it contains inherits the variable scope of the line on which the include occurs.
That means among other things, that if you have include() inside a function call, the variables available to the included file are only the variables that are available inside the function.  This may include global variables or class properties.
0
 
LVL 1

Author Comment

by:LB1234
Comment Utility
Inside the db_connection file there's no functions (I'm familiar with global and local scope), there's just this:

  // 1. Create a database connection
  $connection = mysqli_connect(DB_SERVER, DB_USER, DB_PASS, DB_NAME);
  // Test if connection succeeded
  if(!$connection) {
    die("Database connection failed: " .
         mysqli_connect_error() .
         " (" . mysqli_connect_errno() . ")"
    );

  }

So dashboard.php should already have $connection created (see above).  If what Dave says is true, and it seems to make sense that it would be, $connection should be available for the menu.php query to use.
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 1

Author Comment

by:LB1234
Comment Utility
If I add the db_connection include to the top of the menu.php file i get the following:

( ! ) Notice: Constant DB_SERVER already defined in C:\wamp\www\expenses\db_connection.php on line 4
Call Stack
#      Time      Memory      Function      Location
1      0.0006      670832      {main}( )      ..\dashboard.php:0
2      1.0045      691648      require_once( 'C:\wamp\www\expenses\menu.php' )      ..\dashboard.php:5
3      1.0047      699584      include( 'C:\wamp\www\expenses\db_connection.php' )      ..\menu.php:1
4      1.0047      699792      define ( )      ..\db_connection.php:4

( ! ) Notice: Constant DB_USER already defined in C:\wamp\www\expenses\db_connection.php on line 5
Call Stack
#      Time      Memory      Function      Location
1      0.0006      670832      {main}( )      ..\dashboard.php:0
2      1.0045      691648      require_once( 'C:\wamp\www\expenses\menu.php' )      ..\dashboard.php:5
3      1.0047      699584      include( 'C:\wamp\www\expenses\db_connection.php' )      ..\menu.php:1
4      1.0049      699992      define ( )      ..\db_connection.php:5

( ! ) Notice: Constant DB_PASS already defined in C:\wamp\www\expenses\db_connection.php on line 6
Call Stack
#      Time      Memory      Function      Location
1      0.0006      670832      {main}( )      ..\dashboard.php:0
2      1.0045      691648      require_once( 'C:\wamp\www\expenses\menu.php' )      ..\dashboard.php:5
3      1.0047      699584      include( 'C:\wamp\www\expenses\db_connection.php' )      ..\menu.php:1
4      1.0050      699992      define ( )      ..\db_connection.php:6

( ! ) Notice: Constant DB_NAME already defined in C:\wamp\www\expenses\db_connection.php on line 7
Call Stack
#      Time      Memory      Function      Location
1      0.0006      670832      {main}( )      ..\dashboard.php:0
2      1.0045      691648      require_once( 'C:\wamp\www\expenses\menu.php' )      ..\dashboard.php:5
3      1.0047      699584      include( 'C:\wamp\www\expenses\db_connection.php' )      ..\menu.php:1
4      1.0051      699992      define ( )      ..\db_connection.php:7
0
 
LVL 108

Expert Comment

by:Ray Paseur
Comment Utility
Maybe I am missing an important detail.  Where does dashboard.php come into play here?

Please try it this way to see if anything in the header or nav scripts is causing a loss of scope.

<?php session_start();
include ("db_connection.php");
// include ("header.php");
// include ("nav.php");
require_once ("menu.php");

Open in new window

If the menu can find the connection with the header and the nav removed, you at least know that one (or perhaps both) of those scripts is where the problem comes up.

If that doesn't help lead you to a solution, please post the exact and complete contents of those four scripts here in the code snippet.  You can obscure passwords and such with XXX, but please do not change or omit anything in the data and logic.  We need to see it line-for-line, thanks.
0
 
LVL 1

Author Comment

by:LB1234
Comment Utility
Dashboard is the landing page of the entire site.  It has a navigation bar across the top.  Each link in that nav bar has a GET id attached.  Ie, users.php?id=users.  Menu.php looks at the GET info and pulls the appropriate pages and links from the database and creates a menu along the left handside.  Menu.php is included at the top of the dashboard.php page.

\\all dashboard.php code below

<?php session_start();
include ("db_connection.php");
include ("header.php");
include ("nav.php");
require_once ("menu.php");
?>

Open in new window

0
 
LVL 1

Author Comment

by:LB1234
Comment Utility
menu.php is here:

<link href="new.css" rel="stylesheet" type="text/css">




<div id="menu">

<?php

			if (!isset($_GET["id"])) { ?>
				<ul>
                	<li><a href="#">Pending Expenses</a></li>
                    <li><a href="#">Open Report</a></li>
        		</ul>
			
			<?php
			} else {
				
			$id = $_GET["id"];
			
			$query = "SELECT * FROM pages WHERE subject = '{$id}'";
	
			$result = mysqli_query($connection, $query);
			
				if (!$result) {
					die;	
				}
			
			
			while ($row = mysqli_fetch_assoc($result)) {
			?>
            <ul>
                	<li><a href="<?php echo $row["page_link"]?>"><?php echo $row["page_name"]?></a></li>
        	</ul>
		<?php
        			}
			}
			?>



</div>

Open in new window

0
 
LVL 108

Assisted Solution

by:Ray Paseur
Ray Paseur earned 75 total points
Comment Utility
Yes, the define() can only define one constant of each name.  You cannot change constants, hence the name "constants."
0
 
LVL 108

Expert Comment

by:Ray Paseur
Comment Utility
I see you're using a copy of the GET argument "id" directly in a query string.  You might want to see AntiPractice #19, then go back and do something about that!
0
 
LVL 82

Expert Comment

by:Dave Baldwin
Comment Utility
That can not be the same 'menu.php' as in the errors above.  There is no 'db_connection.php'  in that code.
0
 
LVL 108

Expert Comment

by:Ray Paseur
Comment Utility
Other than the security exposure I don't see anything wrong in menu.php, as posted here.
0
 
LVL 1

Author Comment

by:LB1234
Comment Utility
A massive amount of troubleshooting is telling me that the menu.php must have its own connection to the database.  But Dave's saying that's not true.  It just pulls in all menu.php and runs it within dashboard.php.  But dashboard already has db_connection.php included at the top.
0
 
LVL 1

Author Comment

by:LB1234
Comment Utility
menu.php looks fine to me except that when i run it, I get it:

ement

( ! ) Notice: Undefined variable: connection in C:\wamp\www\expenses\menu.php on line 24
Call Stack
#      Time      Memory      Function      Location
1      0.0006      671192      {main}( )      ..\user_management.php:0
2      0.0033      680616      include( 'C:\wamp\www\expenses\menu.php' )      ..\user_management.php:3

( ! ) Warning: mysqli_query() expects parameter 1 to be mysqli, null given in C:\wamp\www\expenses\menu.php on line 24
Call Stack
#      Time      Memory      Function      Location
1      0.0006      671192      {main}( )      ..\user_management.php:0
2      0.0033      680616      include( 'C:\wamp\www\expenses\menu.php' )      ..\user_management.php:3
3      0.0035      681192      mysqli_query ( )      ..\menu.php:24
0
 
LVL 1

Author Comment

by:LB1234
Comment Utility
So it's like dared if I do, darned if I don't.  If I leave db_connect out of menu.php i get $connection is undefined, and if i include it, it says I cannot redefine the constant because it's already been set by Db_connection by a previous include in the dashboard.php file.  It's a catch 22.
0
 
LVL 82

Accepted Solution

by:
Dave Baldwin earned 425 total points
Comment Utility
Where is 'user_management.php' coming from?  That's not in the files above either!
0
 
LVL 1

Author Comment

by:LB1234
Comment Utility
Dave that was it!!! Crap!  I had included a db_connection at the top of user_management.php a while back and completely forgot!   I won't bore you guys with the details.  Thanks so much!  I need to start paying more attention to the errors.
0
 
LVL 108

Expert Comment

by:Ray Paseur
Comment Utility
There is something else wrong here.  Maybe the paths to the test scripts are confused (a common error).  The db connection variable is just another variable; there is nothing magic about it.

At this point, it's not a question any more -- it's becoming an exercise in long-distance debugging without being able to see the details of the situation, so I'll sign off.  If you want to fix this, the wisest thing to do would be to start over completely with an empty new code base.  Then systematically add back the software components one at a time, testing each time to see what the defined variables are at each iteration.  You can use this function to show that:
http://php.net/manual/en/function.get-defined-vars.php

Best of luck with it, ~Ray
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Consider the following scenario: You are working on a website and make something great - something that lets the server work with information submitted by your users. This could be anything, from a simple guestbook to a e-Money solution. But what…
This article discusses how to create an extensible mechanism for linked drop downs.
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…
This tutorial will teach you the core code needed to finalize the addition of a watermark to your image. The viewer will use a small PHP class to learn and create a watermark.

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now