I should start by saying that I am not super familiar with deploying apps. Most of my web development has been through a framework that handles everything for me, or it's been done locally. Now that I'm trying to deploy a personal project, I'm having issues.
I have a PHP website that I have deployed on Heroku. I have been having one issue after another with sessions (everything is working perfectly locally, but breaks on Heroku). I have solved most of the issues by going through about a million other posts. I'm using Memcachier, I'm making sure there is a favicon, I went through and added "exit();" after each header("location: ... ") call, etc.
Finally, I have sessions working almost perfectly except on one page. I have on this page the following code:
<?php
include('header.php');
include('functions.php');
//if id is set, flag as help needed
if(isset($_GET['id'])) {
flagHelped($_GET['id'], 1);
}
//if cancel is set, unflag as help needed
if(isset($_GET['cancel'])){
flagHelped($_GET['cancel'], 0);
}
//start the session and store userID in variables.
session_start();
$userID = $_SESSION['user_id'];
//grab all pets attached to this user.
$pets = getPets($userID);
?>
<!-- create table of pets -->
<div class='container bg-white'>
<table class='table'>
<tr><th>Picture</th><th>Pet Type</th><th>Pet Name</th><th>Zip Code</th><th> Profile </th></tr>
<?php
if($pets != null){
foreach($pets as $pet){
echo "<tr>";
echo "<td><img style='width:150px' src=\"".$pet["pictureLink"]."\"></td>";
echo "<td>".$pet["type"]."</td>";
echo "<td>".$pet["name"]."</td>";
echo "<td>".$pet["zipCode"]."</td>";
if($pet["needsHelp"] == 0){
echo "<td><button class = 'btn btn-success'>Request An Angel</button></td>";
}
else{
echo "<td><button class = 'btn btn-danger'>Cancel Request</button></td>";
}
echo "</tr>";
}
}
?>
</table>
<?php
include('footer.php');
?>
flagHelped() looks like this:
function flagHelped($petId, $status){
echo "in flag helped";
$connection = new mysqli(//credentials here);
$connection->query("UPDATE Pets Set `needsHelp`=".$status." where `petId`=".$petId.";");
$connection->close();
return;
}
It's a pretty simple page that prints a list of pets associated with the user that is logged in, and prints a button that redirects back to the current page as a get request with a URL variable.
The first time the pages loads (with no URL variable), there is no issue. After clicking a button, it calls the flagHelped() method, gets all the way through, saves properly to the database and returns. However, session_start() doesn't seem to do anything on return and it never gets to getPets();
I have put debugging print statements pretty much everywhere, and I have reordered the page in different ways. If I set the session before calling flagHelped(), I am able to print the $userID variable, but once I return from that function (which is in functions.php), $userID is no longer valid. Since I didn't need any of the session variables for that function, I decided I would just call the session variables after I returned. But even that isn't working.
Again, I'm sorry if I'm asking a dumb question here, I've been at it for hours and I'm at my wit's end.
Side Note: I am aware that there might be security risks on this page. This is for an application I built in less than 16 hours at a hackathon, so I'm not worried about the security risks. I'm purposely putting up how far I got before the code turn-in time. The database is filled with fake data and I have a disclaimer where I link to it saying not to use real information because of potential risks.
Edit: Adding pictures:
First time going through page (no URL variable) with echo statements
Second time going thorugh page (with URL variable) with echo statements
Ok, so I finally figured it out. I'm posting here in case any one else runs into this issue and comes across my post.
This worked locally and not on heroku and I couldn't figure out why. It turns out it's because I was trying to store images locally on heroku's server. When I stored them locally in my own project, they would stay in the folder that I saved them. When I stored them locally on heroku (if a user uploaded a picture on the heroku url while deployed), once my application slept after 30 minutes of inactivity, it would delete the picture.
What was happening was that my project couldn't find that image, and for whatever reason, that caused a session invalidation. Locally, all worked fine (even without being able to find the image), it only stopped working once deployed on heroku. I manually deleted those records from the database, and that page worked perfectly again.
(Now I'm looking into heroku storage options for projects to help permanently solve the problem.)
Related
I have a client that uses a website builder that does not offer the ability to edit the code or access the database but hey want to track the downloads of a file.
I setup a system in which I host the file elsewhere, and their website has an <a> link to a page on the host site with a very simple script to insert a record to track the download, as well as download the file.
This is the php script in the page on my site, download-agreement.php:
<?php
$db = new mysqli('localhost', 'username', 'password', 'database');
if ($db->connect_error) {
die("Connection failed: " . $db->connect_error);
}
$file_path = 'agreement.pdf';
$stmt = $db->prepare("INSERT INTO `download_log` (`date`) VALUES (CURRENT_TIMESTAMP())");
if ($stmt===false) {
error_log($db->error);
}
$result = $stmt->execute();
if ($result===false) {
error_log($stmt->error);
}
$stmt->close();
$db->close();
error_log("Downloaded: " . date("Y-m-d H:i:s"));
header('Content-type: application/pdf');
header('Content-Disposition: attachment; filename=agreement.pdf');
readfile($file_path);
?>
And the link I have placed on their site:
https://www.host.com/theirdirectory/download-agreement.php
If link is visited directly, it works properly - the error_log() executes and the record is inserted.
If the link is visited after being clicked on their website, only the file downloads. The error_log() is not executed and the record is not inserted.
I am at a bit of a loss as I cannot even get an error. And thoughts or ideas would be appreciated.
EDIT
This seems to be an issue with Chrome for Desktop (which doesn't really make any sense). This works properly on all browsers except for Chrome for Desktop (it works on chrome for mobile), tested on all with caching disabled on multiple computers.
Thanks to everyone for all of the help, the issue turned out to be the elements in the "GoDaddy Website Builder" on the client's website where the link was stored.
I am still not sure what exactly the issue was but my solution was to make this work I had to use an HTML Section that allowed me to write in the link manually as an html code block - which happens to render as an iframe in their builder, differently from other pre-built elements such as buttons with links etc. It seems that there may be some caching happening on their end (as esqew mentioned there may be) or something going on behind the scenes with those elements that is behaving differently in different browsers.
I have a very special problem and I don't know how to deal with it.
I have web App in Laravel, when i open index page, I receive text message to my mobile phone.
Problem is, sometimes I receive 2 messages or 3, sometimes 1.
Is there a tool how to debug this strange behavior which is not always the same?
A few words about my code:
user opens the page, and because its first visit Session doesn't have attribute message_sent and SendTextMessage::SendMessage($phoneNumber, $id_message, $smsCode, $newDateFormat); is executed. After that Session has message_sent and can't be sent again, for example if I refresh the page.
SendTextMessage::SendMessage() is Class in Laravel Helpers.
controller code:
public function index($url_attribute, $id_message, Request $request)
{
if(!Session::has('message_sent'))
{
$user = User::where('id_message', $id_message)->first()->toArray();
$phoneNumber = $user['mobile_phone'];
$smsCode = $user['sms_code'];
$newDateFormat = date("d.m.yy", strtotime($smsExpirationTime));
$request->session()->flash('message', 'Text message sended.' );
SendTextMessage::SendMessage($phoneNumber,$id_message, $smsCode, $newDateFormat);
Session::put('message_sent', true);
}
return view('login');
}
SendTextMessage Class:
class SendTextMessage
{
public static function SendMessage($phoneNumber, $id_message, $smsCode, $newDateFormat)
{
$sms = new Connect();
$sms->Create("user","pass",Connect::AUTH_PLAIN);
$sms->Send_SMS($phoneNumber,"Message");
$sms->Logout();
}
}
Many thanks for any tip or help.
UPDATE:
problem is only in Chrome.
Edge and internet explorer are fine.
As this script runs on server-side the browser shouldn't be an issue. Based on your code provided, there is no clear answer to give here.
Please try the following in order to debug your problem:
Log messages at each stage of the script in order to see which part was called how often. That will help you to locate the problem. You can use \Log::error("Message") to do that.
Once you know where the problem might be, try to log "decision" making / mission critical variables to logile as well. E.g. \Log::error($session) so that you can understand why that problem might occur. One reason could be that you have a bad configured session caching or your cookies might be messed up. At some point there is probably a piece of data not the way you expect it to be.
You should maybe try to change the way you use Laravel Session.
You indicated that it was working fine on some browsers, that means your server-side code is correct so far, but there is someting messing with Chrome… From there,
if you take a quick look at the Laravel Session doc, you'll see that Session can be stored in cookies, and I bet that this is your actual setup (check in your .env file the SESSION_DRIVER constant, or in your config/session.php file).
If so, to confirm that this cookies-based session setting is the culprit, you might want to change the Session config to make it browser-independent: any other option than cookies will work, the database or file options might be the easier to setup… And if it works I would strongly encourage you to keep using this no-cookie setting to make your code browser-safe.
First: please forgive me - Im a bit of a novice as some of this...
I have a working test site which is running the php facebook SDK to perform some simple graphAPI requests successfully. Namely read a group's feed, which the user is a member of, and process this and display it back on a webpage.
This all works fine, the problem I have encountered is when trying to perform the same request via a php curl POST to another webpage (on the same domain). It seems that the SDK does not carry the expected session to another page when a post request is formed (see "AUTH ERROR2" in code)...this works fine when the following file is included via a "require_once" but not when a curl is made.
I would much rather do a "curl" as Im finding when a "require_once" is done from a page in a different directory level, Im getting php errors of the page not being found - which is expected.
I may just be tackling this problem all wrong...there may be a simpler way to make sure when files are includes, their correct directly level remains intact, or there may be a way to send over the currently authorised facebook sdk session via a curl post. All of which I have tried to no avail, and I would really appreciate any help or advise on this.
Thank you for your time.
//readGroupPosts.inc.php
function readGroupPosts($postVars)
{
//$access_token = $postVars[0];
// ^-- I'm presuming I need this? I have been experimenting appending it to
// the graphAPI request to no success...
$groupID = $postVars[1];
$limit = $postVars[2];
require_once("authFb.inc.php"); //link to the facebookSDK & other stuff
if ($user) {
try {
$groupFeed = $facebook->api("/$groupID/feed?limit=$limit"); //limit=0 returns all;
$groupFeed = $groupFeed['data']; //removes first tier of array for simpler access
$postArray;
for($i=0; $i<count($groupFeed); $i++)
{
$postArray[$i] = array($groupFeed[$i]['from']['name'], $groupFeed[$i]['message'], $groupFeed[$i]['updated_time'], count($groupFeed[$i]['likes']['data']));
}
return $postArray;
} catch (FacebookApiException $e) {
error_log($e);
$user = null;
return "AUTH ERROR1"; //for testing..
}
}
else
{
return "AUTH ERROR2"; //no user is authenticated i.e. $user == null..
}
}
I would much rather do a "curl" as Im finding when a "require_once" is done from a page in a different directory level, Im getting php errors of the page not being found - which is expected.
I may just be tackling this problem all wrong...
Definitively.
Using cURL as a “workaround” just because you’re not able to find your way around your server’s file system is an outrageous idea. Don’t do it. Stop even thinking about it. Now.
there may be a simpler way to make sure when files are includes, their correct directly level remains intact
Yes – for example, to use absolute paths instead of relative ones. Prefixing the path with the value of $_SERVER['DOCUMENT_ROOT'] for example – that way, once you’ve given the path correctly in respect to this “base path”, it does not matter where you’re requiring the file from, because an absolute path is the same no matter from where you look at it.
(And since this is not a Facebook-related problem at all, but just concerns basics of PHP and server-side programming, I’ll edit the tags.)
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.
I want to create a way to test different layouts on a page to see which get more conversions.
For example. If I have 2 versions of a page and I send 50% to page A and 50% to page B and see which one converts more sales.
So I am thinking maybe use .htaccess to rewrite half to page A and the other half to page B.
But how can I do that with .htaccess is there a way? do I need to use PHP instead to do this?
Also if there is a better way to do this, or any cautions I should be aware of, please let me know.
Lots of ways to deal with it on your own code. If however you're already using Google Analytics and don't care to use javascript for the test, spare yourself a lot of trouble and look at http://www.google.com/websiteoptimizer/index.html
Update (Reconfine): Google website optimizer no longer exists, this has been replaced with "Google Analytics content experiments" https://developers.google.com/analytics/devguides/platform/experiments-overview
I would do that using php, following way:
After the user got to the default php file, i would store his browser data in a db table, and the active layout identifier (filename, row id, etc...).
Everytime the server gets a request from this user, it shows the page, mapped to him.
But! If you have two independent pages, i would only store how many people visited site one, and site two, and redirect them to page A, and to page B by a 50% division.
If you've got a supported database and are using PHP 5.2 or later, you can use a free split testing library called phpScenario, found at www.phpscenario.org
Then you write more or less something like this:
require_once 'scenario_setup.php'; // you write this
if (Scenario::IsControl('experimentname')) {
// read and output version 1
} else {
// read and output version 2
}
Then when you get to your conversion point (say, sign up):
require_once 'scenario_setup.php'; // same
Scenario::Complete('experimentname');
And to view the stats (probably on your admin page):
require_once 'scenario_setup.php'; // yup
Scenario::RenderXml('experimentname');
I think php will be very useful. For example, you can use rand or push a variable in a file :
$int = file_get_contents('var');
if ($int) {
$int++;
} else{
$int = 1;
}
if (($int % 2) == 0) {
header('Location: url1);
} else {
header('Location: url2);
}
file_put_contents ('var', $int);
With apache, you have to setup a load balancer : http://httpd.apache.org/docs/2.1/mod/mod_proxy_balancer.html
I would use php personally. Then you can save which page layout you chose for them as a session var making it easy to load that layout on each page refresh. You would probably also want to save into the database with their username (if they login) and if they visit later show them the same layout.