Jump to content

Recommended Posts

I need to make my client's site distinguish among different tabs in the same browser. (See http://www.phpfreaks.com/forums/index.php?topic=357772.0 for background.)

 

I create a tab ID when the user first visits the site in a particular tab, and I'm passing it from page to page as a parameter. This seems to be the only way to do what I need.

 

The tab ID is always passed through POST so that the user won't see it. If it were visible, users could make trouble (or more likely get in trouble) by adding or removing the ID themselves.

 

This is not simple where a page is loaded by a hyperlink. The anchor tag passes parameters via GET, period.

 

The solution I found is to link to a script named post.php, which takes the real link target and the tab ID as parameters, sends the browser a form that passes the tab ID to the real link target via POST, and auto-submits the form via JavaScript.

 

This works, but it requires two round trips to the browser to load a page. It's also a pain to code.

 

Is there a more efficient way to do this... perhaps a way to make the server send the browser a POST response, even though it made a GET request?

 

If it matters, the server is Apache 2.x.

If it were visible, users could make trouble (or more likely get in trouble) by adding or removing the ID themselves.

 

They can still edit the value whether it's a POST or a GET, it's just not immediately visible. Anybody with a bit of knowledge that wants to "cause trouble" could still easily do so. HTTP responses aren't GET or POST; they're simply a response. You can include a location header as part of it, which the browser can use to construct a new request, but you can't send back the next request. (Which is a good thing.)

 

Looking at your other post I understand why you want to do this, but you're trying to force the browser to behave in a way it's not meant to. What about "auto saving" work? Or just extending the session expiry? Using different tabs shouldn't create a new session, so a long life session should prevent any issues.

Using different tabs shouldn't create a new session

 

+1

 

It sounds like your code is unconditionally clearing/initializing session variable(s) at some point or step in the process and the problem can be fixed by testing if the session variable(s) already exist when someone arrives at the first step in the process and skip over the first step or skip to the correct step they were actually on.

Example code to accomplish the above suggestion -

 

<?php
session_start();
if(isset($_SESSION['step'])){
// you have already started this process
echo "You are now at step: {$_SESSION['step']}";

// code to detect and operate on steps 2,3,4,...	

// successfully completed a step, go onto the next step.
$_SESSION['step']++;

} else {
// this is the first time during the current browser session that you have started step 1
echo "Thank you for starting this process, you are at step 1.";

// code for step 1

// successfully complete step 1, go onto the next step
$_SESSION['step'] = 2;
}

Here are some responses that should make the situation clearer. I may have more to offer when I get a chance to refer the code tonight.

 

First, I'll explain what the site is for. It awards continuing education credits to users who take an on-line "class" and pass a test. I didn't disclose that in the OP because I was afraid it would complicate the situation rather than clarify it. Some the reasons why I need to solve this technical problem still won't make a lot of sense unless I disclose details of the site's internal operation, and obviously I can't do that.

 

Ronnie Wood wrote:

 

They can still edit the value whether it's a POST or a GET, it's just not immediately visible....

 

True, but not important. We aren't dealing with security here; we're just trying to prevent users from unintentionally destroying their own work.

 

Few of our users have the skill to intercept a POST response, but the ones that do should also have the insight to recognize a token when they see one and understand the probable consequences if they try to hijack one. If they understand and still want to do it... well, OK. Some people have strange hobbies.

 

What about "auto saving" work?

 

I think I misrepresented the situation by referring to "saving work." There isn't any work to save, in the sense that there would be if a user were editing one document and wanted to edit a different one. If we offered saving work as an option, our users would just get confused when we tried to explain when (never mind why) they have to do it!

 

Or just extending the session expiry?

 

I don't understand what session life has to do with the problem. (The session life is already set to 24 hours, and we get very few trouble reports that seem related to session expiry.)

 

Batwimp wrote:

 

Do you have a sample page that works?

 

I'm not sure what you want a sample of. I could easily provide a copy of the file I referred to as post.php. I could also prepare a set of sanitized pages that show how it works, but that would take some time, and I'd like to understand how it will help before I do it.

 

PFMaBiSmAd wrote:

 

It sounds like your code is unconditionally clearing/initializing session variable(s)... and the problem can be fixed by testing if the session variable(s) already exist when someone arrives at the first step in the process and skip over the first step or skip to the correct step they were actually on.

 

Well, no. There are a number of reasons for that. The one that's simplest to explain is that when the user finishes taking a class she may want to return to the beginning of the workflow and take another one. If we don't clear the session variables then, she won't be able to do so.

finishes taking a class she may want to return to the beginning of the workflow and take another one

 

You are thinking too literally. You just need logic that tests when they can start at step 1. If you want to limit them to one class at a time, you would require that they finish a class in progress (the step number is equal to the end value) before being able to start a different one or you would have a link that would cancel the current class in progress, which would clear the session information and permit them to start a new class.

 

If you wanted to permit concurrent different classes, you would use $_SESSION[some_class_id]['step'], $_SESSION[some_other_class_id]['step'], ... to keep track of what step they are in each class. To prevent them from re-starting the current class, you would check if the class_id already existed in the session array.

After reading through both posts, I get the impression you require the user to visit every page in the "course". Or do you require that they visit every page in sequence?

 

If you want to make sure they go in sequence then I would have the last action of each script be to set $_SESSION['LastPage'] = $MyPageNumber. Then have the first action of each script be to compare $MyPageNumber to the session and emit message if they are out of sequence.

 

If you just want them to visit/complete every page, I would add an array to the session at the start of the "course":

$_SESSION['Pages'] = array(1 => false, 2=>false, 3=>false ...);

then as each script decides that the user has satisfied the requirement of "visiting the page", you could mark that page's value to true. When you get to the end page, you check the array for any false values and tell the user to go back and complete that page.

 

:facepalm: Rats! someone beat me again! PFMaBiSmAd's suggestion can be merged with mine (which is of course a better suggestion  ;) ), so I'm going to post it anyway.

 

 

Sample code that would force a single class at a time. Any other tabs or windows open in the same browser will just get the current step, even if the request is link indicating to start a class -

 

<?php
// assuming one class at a time

// define data structure for demo purposes
$classes[1] = array('name'=>'Class 1','steps'=>4);
$classes[2] = array('name'=>'Class 2','steps'=>5);
$classes[3] = array('name'=>'Class 3','steps'=>3);
$classes[4] = array('name'=>'Class 4','steps'=>2);

session_start();

// process any 'end class' get request
$endclass = isset($_GET['endclass']) ? true : false;
if($endclass){
unset($_SESSION['class_id']); // clear class id (no class in progress)
header("Location: {$_SERVER['SCRIPT_NAME']}"); // clear get (or post) variables
exit;
}

// if no class in progress or the current one is done, display class menu and process any 'start class' get request
if(!isset($_SESSION['class_id']) || (isset($_SESSION['step']) && $_SESSION['step'] == 'done')){

// check if starting a class (can be done using $_POST as well)
$startclass = isset($_GET['startclass']) ? intval($_GET['startclass']) : false;
if(isset($classes[$startclass])){
	$_SESSION['step'] = 1; // start at 1
	$_SESSION['class_id'] = $startclass; // remember the class id 
	header("Location: {$_SERVER['SCRIPT_NAME']}"); // clear get (or post) variables
	exit;
}

// class menu (can be done using a form and $_POST instead of links)
echo "<h4>You may start a class, click on a choice -</h4>";
echo "<ul>";
foreach($classes as $id=>$class){
	echo "<li><a href='?startclass=$id'>{$class['name']}</a></li>";
}
echo "</ul>";
}

// a class is in progress
if(isset($_SESSION['class_id'])){
// check if done
if($_SESSION['step'] == 'done'){
	echo "You have completed: {$classes[$_SESSION['class_id']]['name']}";
} else {
	// not done
	echo "Thank you for picking: {$classes[$_SESSION['class_id']]['name']}, you are at step: {$_SESSION['step']} out of {$classes[$_SESSION['class_id']]['steps']}.";

	// code to detect and operate on each step would go here...
	// for demo, just a form that submits
	echo "<form method='post' action=''><input type='submit'></form>";

	// when you have successfully completed a step, go onto the next step
	$_SESSION['step']++; // in real code, this would be inside some logic that tests if the step is successfully completed

	// detect the end of the current class
	if($_SESSION['step'] > $classes[$_SESSION['class_id']]['steps']){$_SESSION['step'] = 'done';}

	// menu
	echo "<ul>";
	echo "<li><a href='?endclass'>Drop this class (your progress in the class will be deleted.)</a></li>";
	echo "</ul>";
}
}

I'm not sure how this thread got off the rails, but I'd like to do a reset.

 

I originally asked a specific technical question: given that a page must allow users to load a new page by clicking a hyperlink, is there a way to pass the new page parameters via POST, rather than GET, without two round trips to the browser?

 

Somehow we've gotten into a discussion of the site's user-facing behavior; That isn't useful, because I can't disclose all of the considerations that determined the present design, or even exactly what the design is. And even if it were useful, it's not what I need.

 

I gather that the literal answer to my original question is probably "no." I'm hoping for a somewhat broader consideration of the problem: is there some technique that would give me the result I want with a reasonable amount of effort, even if it doesn't work the way I original intended?

 

I wonder whether AJAX would be helpful here. I haven't worked with it before, so I don't have a good understanding of its capabilities.

We went "off the rails" trying to help you solve a problem that, perhaps, we don't completely understand. The literal answer to your question is, as Adam said in response #1, there is no such think as a "POST response". 

 

If you want to turn all of your links into POSTs (at the browser), and if you consider JavaScript as part of the solution set -- keeping in mind that the user can turn off JavaScript and thereby defeat your protection -- you can place a form on the page with a hidden field and no visible elements, and apply an onClick event to your <A> tags. Then have the onClick event change the Action of the form to the destination of the link, set the hidden field to whatever value you want passed to the new page, and then POST the form from JavaScript.

 

As an alternative to POSTing a hidden form, you could have the JavaScript event put the value into a cookie and then allow the browser to issue the GET request. Of course this requires the user to have JavaScript AND Cookies enabled.

 

You will, likely, want to have your PHP script verify that the Request Method is POST (in option 1) or that the COOKIE exists (option 2). I'm not terribly good with JS, and would probably choose option 1, since I find working with cookies in JS to be tedious. In either case, I would be using JQuery to simplify my life as much as possible.

 

 

I originally asked a specific technical question: given that a page must allow users to load a new page by clicking a hyperlink, is there a way to pass the new page parameters via POST, rather than GET, without two round trips to the browser?

 

I answered that question at the start.

 

Somehow we've gotten into a discussion of the site's user-facing behavior; That isn't useful, because I can't disclose all of the considerations that determined the present design, or even exactly what the design is. And even if it were useful, it's not what I need.

 

I gather that the literal answer to my original question is probably "no." I'm hoping for a somewhat broader consideration of the problem: is there some technique that would give me the result I want with a reasonable amount of effort, even if it doesn't work the way I original intended?

 

You have a very specific problem that we can't just give you some generic answer for. In order for us to help and suggest solutions, we need to know specifics about your application. If you're unable to disclose that on a public forum, or unwilling, here really isn't the best place to be seeking help.

 

Edit: In response to DavidAM's post, while they're viable solutions I wouldn't ever implement them myself. We can't suggest anything better though with the information we have. It just seems like you want to fudge together a fix instead of fixing the problem at the source.

Is there a more efficient way to do this...

 

Yes, fix what is causing the problem.

 

In programming, people regularly spend a HUGE amount of time trying to fix SYMPTOMS, when often, once the actual problem is known, there is a simple fix that will work for all cases, instead of adding more and more code to handle special case conditions.

DavidAM: that's a viable solution to the problem, and I think I could implement it without a great deal of rewriting, once I get the site working with the double-load solution. That's encouraging.

 

Adam, you commented that while David's proposed solution would work, you'd never use it yourself. You didn't say anything about why you wouldn't use it, or what you'd do instead. Could you explain please? If David's solution has drawbacks, I want to understand them.

 

PFM..., I really don't know what to make of your last post. I firmly agree with the principle you stated, but I don't see how it applies to the situation. Are you implying that the way the site behaves to the user should be determined by what is convenient to implement? I can't agree with that, apart from considering whether the desired behavior can be implemented at a cost that the client finds acceptable.

For clarification, the solutions I offered would not ordinarily be my first choice either. However, I have often come to a situation where the design and system requirements required a "non-standard" solution. In this case, the OP has indicated that he can not or will not reveal detail design issues. That may be a valid decision based on proprietary issues and / or corporate mentality; or it may be that the OP just thinks he knows the best approach and just wants an answer. Either way, I decided to provide workable solutions. -- By the way, there is a third solution, which would be my personal last-ditch approach, and that would be to use flash for the whole thing.

 

You didn't say anything about why you wouldn't use it, ...

1) JavaScript is not reliable. I'm not saying it does not work, I'm saying you can not depend on anything that the user has control over. A savy user (i.e. most of the people answering questions on this board) can intercept your JS and make it do whatever we want.

2) The browser is not reliable. Again, just about anybody can manipulate the "hidden" form that I recommended.

3) Cookies are not reliable (see 1 and 2 above).

In Short, JS should never be used as a security or validation technique. Always design and develop the site to work entirely without JS. Then add JS to enhance the user's experience. It is fine to validate data in the browser with Javascript, as long as it is validated at the server when it gets there. Validating with JS is only a convenience for the user, saves time and band-width; it is NOT part of the application's data validation.

 

In programming, people regularly spend a HUGE amount of time trying to fix SYMPTOMS, ...

PFMaBiSmAd is also correct. I have spent many many hours writing code to circumvent problems because that was what the managers said to do. I almost lost my job (working for a major consulting company, at the time) because I told the project manager we needed to fix a design conflict between two modules (designed and in development by a competitor) instead of writing and executing code every-other day to fix the data that the two modules were updating inconsistently. In the end, they had to address the design conflict, but not before the customer had spent lots of dollars fighting it. (By the way, it was the customer who refused to address the issue.)

 

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.