PHP/Smarty - Random Session data loss between 2 pages - php

I'm facing the weirdest issue I've ever seen with PHP. I will try to be really exhaustive because each detail can count.
I have 3 PHP pages, and their HTML is generated using Smarty. They all call session_start() at their top.
the first page can be heavy to load, it displays a big amount of clickable pictures that are previews of PHP complex objects
if a picture is chosen it leads to the second page with a specific URL.This page will generate a PHP object related to the URL info (so this page does not need info from previous page to work. If you copy-paste this URL at anytime it will work). This object will be stored into $_SESSION with a unique id.
the third page is included as an img src in the second page's HTML code (generated with Smarty). This is a PHP page having the object's session id in URL parameter and reading the object's info in $_SESSION, creating it's image preview and turns into an image with header('Content-type: image/jpeg').
To sum up :
heavy first page => second page creating and storing a PHP object into $_SESSION => third page, included in second page's HTML code, searching for this object and creating a preview.
The problem is, sometimes, this third page is trying to load the object in $_SESSION and it's just not there !
Some facts :
the problem never occurs when the second page is loaded as a stand-alone with its own URL
the problem only occurs randomly when the heavy first page starts loading images and we furiously click on one of the first ones to appear
whatever the situation is, if I dump my $_SESSION variables at the very end of the second page I can ALWAYS see the generated object in my session
So it seems that the issue is located between the very end of the second page and the beginning of the third, but all of this related to the activity of the first one !
All I know is that it is linked to a fast navigation between pages.
I've tried almost everything :
session_write_close() everywhere it's possible (at the end of my pages before/after smarty display, then at the beginning before session_start())
add exit() at the end of my scripts
even sleep(1) at the beginning of the third in case session close operation needs more time
Nothing works. I have no more clues ... Maybe Smarty ? Maybe some odd PHP session behaviour named bug #4454 somewhere ?
Thanks a lot in advance for helping me with this.
Edit : piece of code after serialize() chat
/* ===== Page2.php ===== */
/*creating object $card and setting some values ...*/
/*calling the function that gives the card preview*/
$assigns['front'] = $card->getPreviewURL();
/*other stuff ... assign $assigns to smarty ... etc*/
/* ===== Card object class ===== */
function getPreviewURL()
{
$_SESSION['products'][$this->getObjectId()] = serialize($this);
$url = '/page3.php?s='.$this->getObjectId();
return $url;
}
/* ===== Page2.html ===== */
img src="{$front}" alt="toto"
/* ===== Page3.php ===== */
/*getting id value and reading session*/
if(!empty($_GET['s'])) {
session_write_close();
$session = new Session;
if(!empty($_SESSION['products'][$_GET['s']])) {
$product = unserialize($_SESSION['products'][$_GET['s']]);
}
else {
log('$_SESSION[products]['.$_GET['s'].'] does NOT exist');
header("Status: 404 Not Found");
exit;
}
}
and when bug occurs, I get :
page2.php debug log :
Session value : [o20aee110e0853e74da4d17c9b7ab3075]=>O:8:"Postcard":19:{s:4:"tmpl";O:16:"PostcardTemplate":20:{s:2:"id";s:3:"152";s:2:"or";i:0;s:3:"ord";s:2:"14";s:11:"description";s:0:"" ... etc
page3.php debug log :
$_SESSION[products][o20aee110e0853e74da4d17c9b7ab3075] does NOT exist

Probably, the problem is, that PHP tries to unserialize your object but cannot find the class definition.
You can do one of the following two things:
serialize() the object manually before you put it into the session and unserialize() it after you loaded the class definition.
Or: Use autoloading.

Related

Page updates while processing

I have a PHP script that can take a few minutes to be done. It's some search engine which executes a bunch of regex commands and retrieve the results to the user.
I start by displaying a "loading page" which does an AJAX call to the big processing method in my controller (let's call it 'P'). This method then returns a partial view and I just replace my "loading page" content with that partial view. It works fine.
Now what I would like to do is give the user some information about the process (and later on, some control over it), like how many results the script has already found. To achieve that, I do another AJAX call every 5 seconds which is supposed to retrieve the current number of results and display it in a simple html element. This call uses a method 'R' in the same controller as method 'P'.
Now the problem I have is that I'm not able to retrieve the correct current number of results. I tried 2 things :
Session variable ('file' driver) : in 'P' I first set a session variable 'v' to 0 and then update 'v' every time a new result is found. 'R' simply returns response()->json(session('v'))
Controller variable : same principle as above but I use a variable declared at the top of my controller.
The AJAX call to 'P' works in both cases, but everytime and in both cases it returns 0. If I send back 'v' at the end of the 'P' script, it has the correct value.
So to me it looks like 'R' can't access the actual current value of 'v', it only access some 'cached' version of it.
Does anyone have an idea about how I'm going to be able to achieve what I'd like to do? Is there another "cleaner" approach and/or what is wrong with mine?
Thank you, have a nice day!
__
Some pseudo-code to hopefully make it a bit more precise.
SearchController.php
function P() {
$i = 0;
session(['count' => $i]); // set session variable
$results = sqlQuery(); // get rows from DB
foreach ($results as $result) {
if (regexFunction($result))
$i++
session(['count' => $i]); // update session variable
}
return response()->json('a bunch of stuff');
}
function R() {
return response()->json(session('count')); // always returns 0
}
I would recommend a different approach here.
Read a bit more about flushing content here http://php.net/manual/en/ref.outcontrol.php and then use it.
Long story short in order to display the numbers of row processed with flushing you could just make a loop result and flush from time to time or at an exact number or rows, the need for the 5 seconds AJAX is gone. Small untested example :
$cnt = 0;
foreach($result as $key => $val) {
//do your processing here
if ($cnt % 100 == 0) {
//here echo smth for flushing, you can echo some javascript, tough not nice
echo "<script>showProcess({$cnt});</script>";
ob_flush();
}
}
// now render the proccessed full result
And in the showProcess javascript function make what you want... some jquery replace in a text or some graphical stuff...
Hopefully u are not using fast_cgi, beacause in order to activate output buffering you need to disable some important features.
I believe you have hit a wall with PHP limitations. PHP doesn't multithread, well. To achieve the level of interaction you are probably required to edit the session files directly, the path of which can be found in your session.save_path global through php_info(), and you can edit this path with session_save_path(String). Though this isn't recommended usage, do so at your own risk.
Alternatively use a JSON TXT file stored somewhere on your computer/server, identifying them in a similar manner to the session files.
You should store the current progress of the query to a file and also if the transaction has been interrupted by the user. a check should be performed on the status of the interrupt bit/boolean before continuing to iterate over the result set.
The issue arises when you consider concurrency, what if the boolean is edited just slightly before, or at the same time, as the count array? Perhaps you just keep updating the file with interrupts until the other script gets the message. This however is not an elegant solution.
Nor does this solution allow for concurrent queries being run by the same user. to counter this an additional check should be performed on the session file to determine if something is already running. An error should be flagged to notify the user.
Given the option, I would personally, rewrite the code in either JSP or ASP.NET
All in all this is a lot of work for an unreliable feature.

serialized class instance disappears from session

I've made an img editor which works fine in 99.9% of the situations.
However, in that rare 0.1% something weird happends, my session remember doesn't the proper value.
I have this in the page that inits the class:
$imgScreen = new img4crop();
// And here some settings like $imgScreen->setExtraJs($extraJs);
In this class I have a functions to save the $this data to an array:
private function save(){
$_SESSION['cropper'][$this->uniqid] = serialize($this);
}
In init-page.php I open a fancybox, loading the settings back into the class:
$info = unserialize($_SESSION['cropper'][$_GET['id']]);
// Should now be the same as $imgScreen
The problem
Sometimes $_SESSION['cropper'][$_GET['id']] is set to NULL. I have no clue why. This only happens very rarely. The key is set.
When I debug, and appendecho $_SESSION['cropper'][$this->uniqid]; to the save() function, it gives me the value I want it to have.
So, where does my session value go, only in that rare occation? Why isn't it there in the fancybox screen?
After 1st comment: It is possible for the init-page to start multiple instances, they each get their own value in $_SESSION['cropper']. In the situation above, they all work, just that one doesnt. On reload, I start new instances and the exact same screen doesnt work, even though I now has a new, fresh id
Edit: It gets weirder: If the location where the image has a file with the same name as the class will use, there are no problems. Weird part: The whole class has no code regarding to files, apart from the final step (the screen has 3 steps), where I use ImageMagick.
Important to know: When I installed the page where this occurs, the targetfile didn't exist either, started blank.
When you are serializing objects you have to make sure you load the base class (prototype) before unserializing the object. For PHP native objects you don't have to do anything, it will load the base classes.
Your variable $this is an instance of a class. In order for $this to exist the base class need to exist. Perhaps it's working most of the time because of an autoloader or the particular sequence you are loading items.
Turns out the solution was, as to be expected, a quircky little something unrelated to the actual code:
In my index.php I do $_SESSION['cropper'] = array(); to reset the array, then let the function which shows an editorscreen set new settings to the cropper, this works perfectly fine.
In that html I have an image with an anti cache:
echo '<img src="'.$image_url.'?'.time.'" />';
The problem occured when $image_url is empty/undefined, resulting in a source something like the following: ?1420793438. It starts with a ?, so it's effectively calling index.php?1420793438.
And at the beginning of that index.php we reset, resulting in an empty array.

Why is my PHP $_SESSION cleared when doing an AJAX Post?

I'm trying to get some values from my $_SESSION variable but it seems to be cleared everytime an AJAX request is initiated.
I have pasted the code below, any help much appreciated :
/*
* The input request from angularjs
*/
$data = file_get_contents("php://input");
$data = json_decode($data, true);
switch( strtolower($data['action']) ) {
case 'products': getAllProducts($data['fields']);
break;
default : echo json_encode(array('success' => false));
}
//fb is a firebug debug function, prints PHP vars to console.
fb($_SESSION['var'); //when loading the php file the first time $_SESSION is set
UPDATE
I'm using AngularJS for my AJAX calls, it works great but now I want to read data from my $_SESSION variable and all I get is an empty $_SESSION. Does this have anything to do with my file_get_contents("php://input");
SOLVED
I found the problem, the previous developer did not take into account that one might desire to include files from 2 levels deep :) As such, I scanned through the PHP error logs and saw that a previous script was not loading - this particular script was included with the session_start and $_SESSION variable values.
Checking the error logs will be my first priority from now on
I found the problem, the previous developer did not take into account that one might desire to include files from 2 levels deep :) As such, I scanned through the PHP error logs and saw that a previous script was not loading - this particular script was included with the session_start and $_SESSION variable values. Checking the error logs will be my first priority from now on

accessing array from another page in php

I would like to know how can I access or read the array from another page. I am working on a PHP page which contains the array, and I want to display the content of that array on another PHP page.
For example, I used the following method in a PHP file and I want to get the content of the array in another PHP file. what is the method that is going to receive the array's content in the second page.
<?php
$r = new HttpRequest('http://localhost/sameh.php', HttpRequest::METH_POST);
$r->addPostFields(array("n" => 'heba')) ;
$r->send();
?>
This code is in the first page but I don't know what to write to receive it on the second one.
Maybe my question was not that clear and sorry about that ,, I want to find a way to access the array that is defined inside the HttpRequest() class on another page. So that the array "n" that include value "heba" will be displayed on another.php page. this is what make me thinking that the problem is on how to access the content of the array on the second page.
I tried the session and It sends the array to another page ,, but when I tested with the
httpRequest() method it doesn't send the content of the array "heba" to the second page.
Thanks for your help.
Sounds like this is a job for Sessions.
You can read the complete session guide here
In the script that has the array you can do something like:
session_start();
$_SESSION['array'] = $array;
In the next script you access it similarly:
session_start();
print_r($_SESSION['array']);
Include that file in your php file where you want to use that array. This should solve your issue PHP - How to send an array to another page?
I'm not sure what the HttpRequest class is, but at a guess, it's POSTing variables to the sameh.php file. You should be able to access the variable on the next page by doing this:
echo $_POST['n'];
Which should print "heba".
Note that to work with serialized arrays, you need to use POST as the form's transmission method, as GET has a size limit somewhere around 1024 characters.
I'd use SESSIONs wherever possible.
You can use serialize() and unserialize() on your array to represent it as a string and pass it via POST.

How to store search result?

I am working on my personal site, where I want to store my customers recent search result limited to that particular session.
I am using PHP platform and Javascripts.
Here is an example of what I am exactly looking at :
It stores your previously searched domain name for that particular session so that user can make decision by comparing those results.
Thanks.
EDIT- Well Thanks for all of your answers and suggestions.
But If you have noticed
above example
It looks like some kind of script loading a new content on the same page without refreshing it and keeping previous search content <div> as it is.
How to achieve this using javascripts or some sort of div layer ????
UPDATE START
This example uses page reload. If you want to do it without page reload, you can but you'll have to use AJAX to load new search results. But then, it's not a PHP question. I suggest looking at jquery library, as it makes it easy. Tutorials: http://docs.jquery.com/Tutorials and e.g. this one ( http://docs.jquery.com/Tutorials:Getting_Started_with_jQuery#Rate_me:_Using_Ajax ).
When loading data via AJAX, the page rendering result (in my example search.php) should return only HTML for results part, not whole HTML page. This is generally a first part of my tutorial (without session).
But I really think that AJAX in here is not really needed. Session is more reliable and allows access to your page from older / mobile browsers where not always JS works correctly.
UPDATE END
Ok then. Let's try the simple tutorial then. Sorry if too simple, but I don't know your exact level.
PHP has mechanism called sessions. In reality they are just bytes stored on server. Server knows which session is for each client by reading session cookie from client browser.
Not every page uses sessions (not every page needs it, and session uses server space, even if only temporarily), session is not enabled by default. To turn on session you use command
<?php session_start(); ?>
In most cases this is either run by PHP framework you use, or put near the top of your site. Session is definitely needed if you want to authenticate user somehow. Or in your case :)
To access session you can use superglobal $_SESSION variable (superglobal means that you can access it anywhere). It's an array, so session element will be e.g. $_SESSION['search'] etc.
As example, let's assume that your page looks like that
<html>
...
<form action="search.php" method="post">
Search: <input type="text" name="searchQuery" />
<input type="submit" value="Search" />
</form>
...
</html>
this very form will send user search to file named search.php. It can be the same file where the form resides - in simplest case when you put both your code and HTML in one file. Beginners often use this schema, although it's not advisable as result is a mess and hard to further change.
In search.php then, you'll use similar code:
<?php
if (!empty($_POST['searchQuery'])) //we have a new search
{
$result = do_search($_POST['searchQuery']);
}
?>
Then, somewhere below you'll display your search result ($result variable). do_search() function is your search mechanism, I guess you have it somewhere. You may have it not 'wrapped' in a function, then I advise to create it like that, it's much more useful.
function do_search($searchQuery)
{
...
return $result;
}
mind it, the above code doesn't use sessions yet. Let's add saving previous search results in session. The code may then look like that:
<?php
session_start(); //Starting session
//let's create session variable used to store results
if (!isset($_SESSION['searches']))
$_SESSION['searches'] = array();
if (!empty($_POST['searchQuery'])) //we have a new search
{
if (isset($_SESSION['searches'][$_POST['searchQuery']]) //User already searched on this value, delete previous result from sesion
{
unset($_SESSION['searches'][$_POST['searchQuery']]);
}
$result = do_search($_POST['searchQuery']);
//Let's add new search on the begining of session array to make iterations easier.
$result = array($_POST['searchQuery'] => $result); //convert result to same format as session table
$_SESSION['searches'] = array_merge($result, $_SESSION['searches']);
}
?>
In display you'll now not iterate on $result variable as before, but instead you will do something like
foreach ($_SESSION['searches'] as $query => $result)
{
...//display of single result
}
I haven't tested following code and it's not a full program. Parts to display result and to do actual search are not described but I guess you have them already prepared. Also, this is only one possible approach of countless possibilities. But I hope this helps :)
Possible modification - now I always perform search, even if user already searched on this term. You may want to receive the result from cache without second search. Then the code will look like
if (isset($_SESSION['searches'][$_POST['searchQuery']]) //User already searched on this value
{
$result = $_SESSION['searches'][$_POST['searchQuery']];
unset($_SESSION['searches'][$_POST['searchQuery']]);
}
else
{
$result = do_search($_POST['searchQuery']);
}
For more in-depth information about sessions and some other constructs used in my example I suggest PHP manual
http://pl.php.net/manual/en/book.session.php
and various tutorials over the network. Or you can add a comment here :)
Put this code near the beginning of your script(s):
if (!isset($_SESSION['previous_searches']) || !is_array($_SESSION['previous_searches'])) {
$_SESSION['previous_searches'] = array();
}
[edit]
This code snippet checks if if there is already an array with prevous searches and if not it will be created.
[/edit]
Then when the user hits the search page put this code in the receiving script of the search:
$_SESSION['previous_searches'][] = $_GET['what_ever_your_search_value_might_be'];
[edit]
This code snippet adds the current search value to the and of the array with previous search values
[/edit]
Now you have all previous search values in $_SESSION['previous_searches']
If your website is a web application where you never reload the page nor change the page, you can keep it JavaScript in a global store (declare at top level something like var StoredSearch = []; and use it). If not, then use $_SESSION to store this and AJAX to save/load searches from JavaScript to PHP.

Categories