Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

barbarianbob posted:

Make a header file that does all the bootstrapping, such as starting the session and connecting to the db. Add require('header.inc.php') at the top of other files to load it.

This is what I do in my scripts. Apart from doing the work of connecting to your database and starting sessions, it also means that you can define various functions in one place (if you're doing a site with multiple pages, I'm sure you can think of a few things that it would be handy to define in one central file), along with definitions of constants, classes and whatever else.

Adbot
ADBOT LOVES YOU

thedaian
Dec 11, 2005

Blistering idiots.

rotaryfun posted:

How horrible is my login process? Am I missing something that will clean it up or optimize it?

It also looks like you're storing the password in plain text in the database. That's a bad idea, since if someone gets access to the database, then they have everyone's password. Either use md5 or crypt to encode the password so your users passwords are safe.

rotaryfun
Jun 30, 2008

you can be my wingman anytime
I added
php:
<?
function sanitize($data) {
$data = trim($data);
if(get_magic_quotes_gpc()){
$data = stripslashes($data);
}
$data = mysql_real_escape_string($data);
return $data;
}

$usernamePost = sanitize($_POST['username']);
$passwordPost = sanitize($_POST['password']);
?>
just from a google search. the pdo thing seems pretty neat and that would probably work well for me... just seems a bit overwhelming to me at the moment.

Also just so it's known, this is just for a database web interface that i'm making and it's only available to the IT department at my office through our intranet.

This is really my first project with mysql and php so i'm learning as i go. I'll look into the password encryption... just seems a little silly for 3 entries in the db.

rotaryfun fucked around with this message at 15:52 on Apr 8, 2010

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

rotaryfun posted:

I added
php:
<?
...
?>
just from a google search. the pdo thing seems pretty neat and that would probably work well for me... just seems a bit overwhelming to me at the moment.

The code you posted would be sufficient to keep that page safe from injection. If you will be developing more scripts, part of your plan should be how you will go about making sure all your scripts are secure. Best to be consistent across different pages. Of course if you're learning as you go, you can do it one way for now and then refactor it later, so long as it's secure all along.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

rotaryfun posted:

This is really my first project with mysql and php so i'm learning as i go. I'll look into the password encryption... just seems a little silly for 3 entries in the db.

Sure, but on the other hand, the easiest time to make sure things are being done "the right way" is when the app is still small (or better, still in the planning stage).

rotaryfun
Jun 30, 2008

you can be my wingman anytime
Very true. I learn better from finding usable examples than from just reading about how the function works in a php man page. So I'll keep searching for the PDO stuff.

fletcher
Jun 27, 2003

ken park is my favorite movie

Cybernetic Crumb

rotaryfun posted:

I added
php:
<?
function sanitize($data) {
$data = trim($data);
if(get_magic_quotes_gpc()){
$data = stripslashes($data);
}
$data = mysql_real_escape_string($data);
return $data;
}

$usernamePost = sanitize($_POST['username']);
$passwordPost = sanitize($_POST['password']);
?>
just from a google search. the pdo thing seems pretty neat and that would probably work well for me... just seems a bit overwhelming to me at the moment.

Keep reading about it then. It's even easier to use then what you are trying to do, and it's a way better way of doing it.

gwar3k1
Jan 10, 2005

Someday soon
I just want to say that I've been using mysqli recently having moved from not using the class and it's just as easy to use but definitely affords that extra security without having to write too much extra code. My learning curve from one method to the other has practically been zero (if not for learning OOP concepts anyway, but this isn't necessary - I just wanted to learn.)

Begby
Apr 7, 2005

Light saber? Check. Black boots? Check. Codpiece? Check. He's more machine than kid now.
The best part of PDO is that you can extend it and make poo poo real easy on yourself. I like to set it up so I can do stuff like this.

php:
<?
$values = new array(
 'field1' = 'moo',
 'field2' = 'neeblenarf',
 'otherfield' = 104
);

$db = new MyPdoClass($connString);

$newId = $db->InsertIntoTable('sometable', $values);
?>
In another application I overrode all the default pdo methods and made it so it does benchmarking and saves a dump of the generated sql query and result set if a debug mode property is set to true. Then I call a method to display that dump at the bottom of the page. It was really helpful when trying to optimize a big crappy php app that did a lot of queries from a variety of included classes on each page load.

Begby fucked around with this message at 19:51 on Apr 8, 2010

spiritual bypass
Feb 19, 2008

Grimey Drawer

Begby posted:

The best part of PDO is that you can extend it and make poo poo real easy on yourself. I like to set it up so I can do stuff like this.

php:
<?
$values = new array(
 'field1' = 'moo',
 'field2' = 'neeblenarf',
 'otherfield' = 104
);

$db = new MyPdoClass($connString);

$newId = $db->InsertIntoTable('sometable', $values);
?>

I see you've implemented your own version of pear::MDB2 :smug:

Begby
Apr 7, 2005

Light saber? Check. Black boots? Check. Codpiece? Check. He's more machine than kid now.

rt4 posted:

I see you've implemented your own version of pear::MDB2 :smug:

Yeah, one that doesn't require pear. Also I wrote it before MDB2 came out.

Thirteenth Step
Mar 3, 2004

I'm trying to create a drop down combo box which populates from MySQL, then allows me to delete the selected user in the combo box from the DB when the submit button is pressed.

I'm having a bit of a confusing time with it and documentation on this specific subject is a bit thin as far as I can find.

Can anyone give me a poke in the right direction?

Here's what I have so far, I have a delete statement, but im not sure how to attach it. I haven't done much today apart from sit here so I'm a bit brain dead, so sorry for the stupid question if indeed it turns out to be.

php:
<?
<form action="" method="post" name="deleteuser">

// query to populate the combo box
$query="SELECT forename,surname,dept FROM staff";
$result = mysql_query ($query);

// delete query, not sure how to attach it to the form D:
$delquery = mysql_query("DELETE FROM staff WHERE staffid = '$nt[staffid]'") or die(mysql_error());
echo "User Deleted";

// echo out the combo box
echo "<select name=staff value=''>Staff Name</option>";
while($nt=mysql_fetch_array($result)){
echo "<option value=$nt[staffid]>$nt[forename] $nt[surname]   - $nt[dept] Staff</option>";
}
echo "</select>";

<input name="deleteusersubmit" type="submit" value=" Delete User " onClick="if (! confirm('Are you sure you wish to delete this user?')) {return false;} ">
</form>
?>
Would I do this using the ONSUBMIT function? Can I run a query in that way?

gwar3k1
Jan 10, 2005

Someday soon
That will output a blank option if no rows are returned (i.e.: " - Staff"). You want to add in a check on the number of rows returned from your query, then if there are one or more rows, you would do the loop.

Why do you have markup in your function? That's bad practice and I'm not sure it would work. Putting markup in echos is cool for making stuff on the fly, but you have the input and form tags vanilla in the function.

How I'd approach this is to have the function build a string of Option tags then echo that inline with the rest of the markup:

php:
<?
  function buildOptions()
  {
    $sReturn = "";
    //setup query
    while($query)
    {
      if($query->num_rows >= 1)
      {
        $sReturn .= "<option></option>";
      }
    }

    // dispose connection
    return $sReturn;
  }

  $list = buildOptions();
?>

<html>
  ...
  <form>
    <select>
      <? echo $list; ?>
    </select>
  </form>
</html>

DarkLotus
Sep 30, 2001

Lithium Hosting
Personal, Reseller & VPS Hosting
30-day no risk Free Trial &
90-days Money Back Guarantee!
I am experiencing a strange issue while developing an addon module for a product I use. The software developer has given me the usual "There isn't anything that would cause this" type of run-around.

php:
<?
$value = "test";

function test_function($someothershit) {
  global $value;
  var_dump($value);
}
?>
This outputs null for $value. I can't figure out why global isn't working. I've tested this in a folder outside the root folder of the software package I am using and it works brilliantly. Ideas?

gwar3k1
Jan 10, 2005

Someday soon

DarkLotus posted:

I am experiencing a strange issue while developing an addon module for a product I use. The software developer has given me the usual "There isn't anything that would cause this" type of run-around.

php:
<?
$value = "test";

function test_function($someothershit) {
  global $value;
  var_dump($value);
}
?>
This outputs null for $value. I can't figure out why global isn't working. I've tested this in a folder outside the root folder of the software package I am using and it works brilliantly. Ideas?

Change the name of the variable. There may be another global $value in the product?

DarkLotus
Sep 30, 2001

Lithium Hosting
Personal, Reseller & VPS Hosting
30-day no risk Free Trial &
90-days Money Back Guarantee!

gwar3k1 posted:

Change the name of the variable. There may be another global $value in the product?

Very good suggestion. I forgot to mention I tried that and the result is the same.

thedaian
Dec 11, 2005

Blistering idiots.

Thirteenth Step posted:

Can anyone give me a poke in the right direction?

Here's what I have so far, I have a delete statement, but im not sure how to attach it. I haven't done much today apart from sit here so I'm a bit brain dead, so sorry for the stupid question if indeed it turns out to be.

Would I do this using the ONSUBMIT function? Can I run a query in that way?

The attributes of a form don't show up in PHP until you've submitted the form. You need to grab the ID of the user being deleted from the $_POST array. In general, something like this:

php:
<?
<form action="" method="post" name="deleteuser">

// query to populate the combo box
$query="SELECT forename,surname,dept FROM staff";
$result = mysql_query ($query);

if(isset($_POST['staff']))
{
  // delete query, not sure how to attach it to the form D:
  $delquery = mysql_query("DELETE FROM staff WHERE staffid = '$_POST['staff']'") or die(mysql_error());
  echo "User Deleted";
}

// echo out the combo box
echo "<select name=staff value=''>Staff Name</option>";
while($nt=mysql_fetch_array($result)){
echo "<option value=$nt[staffid]>$nt[forename] $nt[surname]   - $nt[dept] Staff</option>";
}
echo "</select>";

<input name="deleteusersubmit" type="submit" value=" Delete User " onClick="if (! confirm('Are you sure you wish to delete this user?')) {return false;} ">
</form>
?>
Other thing to do :siren:VERY IMPORTANT:siren: is sanitizing your user input to avoid any sort of SQL injection. There's posts about methods to do this in the last few pages, or just look up and use PDO. Additionally, you might want to read up on some php tutorials since POST and GET variables are one of the first things you should learn about using.

gwar3k1
Jan 10, 2005

Someday soon

DarkLotus posted:

Very good suggestion. I forgot to mention I tried that and the result is the same.

Perhaps try $value .= "test"; which would further suggest existance of a constant $value somewhere.

Have you tried $_GLOBALS['value'] to check its not an issue with global? I can't see how it would be as it isn't version specific.

And with the renaming, did you use something stupidly unique like XD4AO or a common word? I can't tell if your posted code is examplary for help or directly from your script.

Can you set value from within the function and have it output?

DarkLotus
Sep 30, 2001

Lithium Hosting
Personal, Reseller & VPS Hosting
30-day no risk Free Trial &
90-days Money Back Guarantee!

gwar3k1 posted:

Perhaps try $value .= "test"; which would further suggest existance of a constant $value somewhere.

Have you tried $_GLOBALS['value'] to check its not an issue with global? I can't see how it would be as it isn't version specific.

And with the renaming, did you use something stupidly unique like XD4AO or a common word? I can't tell if your posted code is examplary for help or directly from your script.

Can you set value from within the function and have it output?

I set a value on one page:
php:
<?
$DEBUG = true;
?>
And then include apiutil.php There is a function:
php:
<?
$debug = defined('C_DEBUG') ? ((strcasecmp(C_DEBUG, 'true') == 0) ? true : false) : $DEBUG;
function debugfunction($serviceObj)
{
  global $debug;
  if($debug)
  {
    print "<b>Debug Mode is True:</b></br></br>";
    print "<b>XML Sent:</b><br><br>";
    print "<xmp>" . $serviceObj->request . "</xmp>";
    print "<br><b>XML Received:</b><br><br>";
    print "<xmp>" . $serviceObj->response . "</xmp>";
    print "<br>";
  }
  else
  {
    print "<b>Debug Mode is False:</b></br></br>";
  }
}
?>
All I get is the Debug Mode is False statement when I run the script.

$GLOBALS['debug'] is empty. I did try this before to see what it said.

I did not write this code, I am simply trying to build a module using an API which I have done without issue but anytime the code executes as part of a different application, all kinds of strangeness happens.

DarkLotus
Sep 30, 2001

Lithium Hosting
Personal, Reseller & VPS Hosting
30-day no risk Free Trial &
90-days Money Back Guarantee!
Another note... nothing in the error_log, error_reporting(E_ALL) doesn't show any errors or warnings regarding anything I am trying to do. The real kicker is that I can echo $DEBUG before that function is called and it reports true but using global to use the variable in the function doesn't work. Even using a completely random variable name has the same results.

Thirteenth Step
Mar 3, 2004

thedaian posted:

The attributes of a form don't show up in PHP until you've submitted the form. You need to grab the ID of the user being deleted from the $_POST array. In general, something like this:

php:
<?
<form action="" method="post" name="deleteuser">

// query to populate the combo box
$query="SELECT forename,surname,dept FROM staff";
$result = mysql_query ($query);

if(isset($_POST['staff']))
{
  // delete query, not sure how to attach it to the form D:
  $delquery = mysql_query("DELETE FROM staff WHERE staffid = '$_POST['staff']'") or die(mysql_error());
  echo "User Deleted";
}

// echo out the combo box
echo "<select name=staff value=''>Staff Name</option>";
while($nt=mysql_fetch_array($result)){
echo "<option value=$nt[staffid]>$nt[forename] $nt[surname]   - $nt[dept] Staff</option>";
}
echo "</select>";

<input name="deleteusersubmit" type="submit" value=" Delete User " onClick="if (! confirm('Are you sure you wish to delete this user?')) {return false;} ">
</form>
?>
Other thing to do :siren:VERY IMPORTANT:siren: is sanitizing your user input to avoid any sort of SQL injection. There's posts about methods to do this in the last few pages, or just look up and use PDO. Additionally, you might want to read up on some php tutorials since POST and GET variables are one of the first things you should learn about using.

Thanks for the input, I tried this but im getting no luck, seems to be failing around the echos, i don't know if its just a long day getting to me but i cant fix it.

gwar3k1
Jan 10, 2005

Someday soon

DarkLotus posted:

Code

Looks like you should be setting C_DEBUG not debug. The line above the function is checking that C_DEBUG is set to 'true' So try setting $debug to 'true' (quote) rather than true (unquoted).

If setting C_DEBUG doesn't help, comment out that line, set $debug and see if it works.

And just to check, are you setting $debug before including the api? You should try doing it the other way around:

php:
<?php
  include('api.php');
  $debug 'true'// or C_DEBUG = 'true';

  debugFunction($whatever)
?>

gwar3k1
Jan 10, 2005

Someday soon

Thirteenth Step posted:

Thanks for the input, I tried this but im getting no luck, seems to be failing around the echos, i don't know if its just a long day getting to me but i cant fix it.

Which echos? The options? What's it outputting? Did you see my post previously about the SQL select?

DarkLotus
Sep 30, 2001

Lithium Hosting
Personal, Reseller & VPS Hosting
30-day no risk Free Trial &
90-days Money Back Guarantee!

gwar3k1 posted:

Looks like you should be setting C_DEBUG not debug. The line above the function is checking that C_DEBUG is set to 'true' So try setting $debug to 'true' (quote) rather than true (unquoted).

If setting C_DEBUG doesn't help, comment out that line, set $debug and see if it works.

And just to check, are you setting $debug before including the api? You should try doing it the other way around:

php:
<?php
  include('api.php');
  $debug 'true'// or C_DEBUG = 'true';

  debugFunction($whatever)
?>
defining C_DEBUG or $DEBUG has the same outcome. Setting $debug = true and bypassing the if statement also has the same result. No matter what, I can't get global to work. Also including the api file before defining variables doesn't matter.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

DarkLotus posted:

Another note... nothing in the error_log, error_reporting(E_ALL) doesn't show any errors or warnings regarding anything I am trying to do. The real kicker is that I can echo $DEBUG before that function is called and it reports true but using global to use the variable in the function doesn't work. Even using a completely random variable name has the same results.

Are you definitely defining these variables in the global scope, i.e. not inside a function?

code:
// This outputs '$banana is not set'
function myfunction() {
    $banana = 5;
    function bananavalue(){
        global $banana;
        return $banana;
    }
    $x = bananavalue();
    if ( isset($x) ) { echo '$banana is '.$x;     }
    else             { echo '$banana is not set'; }
}
myfunction();

// This outputs '$banana is 5'
$banana = 5;
function myfunction() {
    function bananavalue(){
        global $banana;
        return $banana;
    }
    $x = bananavalue();
    if ( isset($x) ) { echo '$banana is '.$x;     }
    else             { echo '$banana is not set'; }
}
myfunction();

// This also outputs '$banana is 5'
function myfunction() {
    global $banana;
    $banana = 5;
    function bananavalue(){
        global $banana;
        return $banana;
    }
    $x = bananavalue();
    if ( isset($x) ) { echo '$banana is '.$x;     }
    else             { echo '$banana is not set'; }
}
myfunction();
Use of include() or require() may make it less obvious that this is happening, since if you include() within a function, PHP executes the included code in the scope of the function.

nb. a function that is defined inside another function can later be called from any scope.

Thirteenth Step
Mar 3, 2004

gwar3k1 posted:

Which echos? The options? What's it outputting? Did you see my post previously about the SQL select?

code:

<form action="" method="post" name="deleteuser">
<?


// query to populate the combo box
$query="SELECT forename,surname,dept FROM staff";
$result = mysql_query ($query);

if(isset($_POST['staff']))
{
  // delete query, not sure how to attach it to the form D:
  $delquery = mysql_query("DELETE FROM staff WHERE staffid = '$_POST['staff']'") or die(mysql_error());
  echo "User Deleted";
}

// echo out the combo box
echo "<select name=staff value=''>Staff Name</option>";
while($nt=mysql_fetch_array($result)){
echo "<option value=$nt[staffid]>$nt[forename] $nt[surname]   - $nt[dept] Staff</option>";
}
echo "</select>";

?>

<input name="deleteusersubmit" type="submit" value=" Delete User " onClick="if (! confirm('Are you sure you wish to delete this user?')) {return false;} ">
</form>
is outputting (in plain text)

code:
Staff Name"; while($nt=mysql_fetch_array($result)){ echo "$nt[forename] $nt[surname] - $nt[dept] Staff"; } echo ""; ?>
This is going to be something stupid, somebody's going to point it out and it'll be one of those "ffs" moments. It's been a long day, and is boiling in here, time to call it a night after this.

gwar3k1
Jan 10, 2005

Someday soon
Just to clarify, are you setting it as a string or boolean? Maybe the variable has been defined as string explicitly,

php:
<?
  $debug = true;   //this isn't working, and the code isn't looking for boolean true
  $debug = 'true'; //the code is looking for a string of "true" so assist by quoting it
?>

Thirteenth Step posted:

Echo problem

The fact that it's outputting your script shows that you have a string issue somewhere and it'll be down to a lack of closing quote. Are you using an editor with markup highlighting? They make it easy to spot this type of thing (which I did but couldn't spot the problem).

You should quote your variable values (you've done so for value but not name) within tags. The post variable staff might be interfering here. Do this:

php:
<?
echo "<option name='".$_POST['staff']."' value=''>\n;?>


gwar3k1 fucked around with this message at 22:08 on Apr 9, 2010

DarkLotus
Sep 30, 2001

Lithium Hosting
Personal, Reseller & VPS Hosting
30-day no risk Free Trial &
90-days Money Back Guarantee!

Hammerite posted:

Are you definitely defining these variables in the global scope, i.e. not inside a function?

code:
example code
Use of include() or require() may make it less obvious that this is happening, since if you include() within a function, PHP executes the included code in the scope of the function.

nb. a function that is defined inside another function can later be called from any scope.

All of the code to perform the function I want to perform is all ran inside a function.

The software calls a specific function at a specific time and everything in that function happens. So I am defining these variables in that function and including all of the files from within that function.

DarkLotus
Sep 30, 2001

Lithium Hosting
Personal, Reseller & VPS Hosting
30-day no risk Free Trial &
90-days Money Back Guarantee!

gwar3k1 posted:

Just to clarify, are you setting it as a string or boolean? Maybe the variable has been defined as string explicitly,

php:
<?
  $debug = true;   //this isn't working, and the code isn't looking for boolean true
  $debug = 'true'; //the code is looking for a string of "true" so assist by quoting it
?>
php:
<?
if ($debug)
?>
I set $debug = true in the function and the debug function works great. If I set $debug = true; outside the function and use global $debug in the function, poo poo happens.

DarkLotus
Sep 30, 2001

Lithium Hosting
Personal, Reseller & VPS Hosting
30-day no risk Free Trial &
90-days Money Back Guarantee!
Ok, the issue wasn't with global not working, but with nested functions and includes and files that include files that include the parent file that includes already included files and what a mess.

I've figured it out thanks to the troubleshooting steps in this thread.

gwar3k1
Jan 10, 2005

Someday soon
Thirteenth Step, I've written this up for you using what you provided. Do you understand the concept of functions? I get the impression PHP may be your first language and perhaps you should read up on procedural programming and how it can benefit you (greatly). Sorry if this is being presumptuous and is incorrect.

Try to minimise combining PHP and HTML. Use functions to build what your output at the top of your page (or in an external file), and call those functions from within the markup. Look at my markup and see how the HTML looks like a basic webpage but occasionally invokes PHP with inline <? ?>.


php:
<?php
  // Functions only execute when called
  function buildSelect()
  {
    $list "";
    $query="SELECT forename,surname,dept FROM staff";
    $result mysql_query ($query);
    while($nt=mysql_fetch_array($result))
    {
      $list .= "  <option value='".$nt[staffid]."'>".$nt[forename]." ".$nt[surname]."   - ".$nt[dept]." Staff</option>\n";
    }
    return $list;
  }
  
  function deleteStaff($name)
  {
    $bDeleted false;
    $delquery mysql_query("DELETE FROM staff WHERE staffid = '".$name."'") or die(mysql_error());
    // this obviously needs work to sanitize input and connect to the database etc.
    // you would set $bDeleted = true if the delete was successful
    
    return $bDeleted;
  }
  
  // Execute this on load
  $list buildSelect();
  
  if(isset($_POST['staff']))
  {
    if(deleteStaff($_POST['staff']))
    { // true
      $deleted "Name Deleted";
    }
    else
    { // false
      $deleted "There was an error and Name hasn't been deleted";
    }
  }
?>

<html>
  <form action="" method="post" name="deleteuser">
    <? echo $deleted; ?>
    
    <select name='staff' value=''>Staff Name</option>
      <? echo $list; ?>
    </select>
    <input name="deleteusersubmit" type="submit" value=" Delete User " onClick="if (! confirm('
Are you sure you wish to delete this user?')) {return false;} ">
  </form>
</html>

DarkLotus posted:

Ok, the issue wasn't with global not working, but with nested functions and includes and files that include files that include the parent file that includes already included files and what a mess.

I've figured it out thanks to the troubleshooting steps in this thread.

Good work. Sorry for leading you down avenues you'd already explored!

gwar3k1 fucked around with this message at 22:33 on Apr 9, 2010

DarkLotus
Sep 30, 2001

Lithium Hosting
Personal, Reseller & VPS Hosting
30-day no risk Free Trial &
90-days Money Back Guarantee!

gwar3k1 posted:

Good work. Sorry for leading you down avenues you'd already explored!

Sometimes it takes a second opinion and re-tracing steps to find the problem. Thnaks for the help.

Thirteenth Step
Mar 3, 2004

gwar3k1 posted:

Thirteenth Step, I've written this up for you using what you provided.

PHP is my first language but I did quite a bit of it a few years ago, I have a terrible memory (not entirely my own fault) and I tend to forget things especially when they've been on the back burner for a while like PHP has. Thanks for your help though I'll have another go tommorow maybe using what you posted :)

Cheers again!

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
I've been meaning for a while to adopt a more systematic, less error-prone way of making sure that the content I send in database queries is properly sanitised. I considered taking the advice to use prepared statements, but looking over the kind of code that is apparently needed to get prepared statements to execute properly I just think it looks needlessly complicated and ugly (bind this, prepare that... what?). So I came up with a function to let me do something similar with less fuss.

I've tested this and it works how I expect it to. Is there any reason not to do things this way?

(Apologies for breaking tables)

code:
$readerrormessage =
'Failed to read from the database. Suggest you try a second time.
If the same error message appears again, please contact the Administrator.';
$writeerrormessage =
'Failed to write to the database. Suggest you try a second time.
If the same error message appears again, please contact the Administrator.';

function myerror($msg_not_test_mode,$msg_test_mode=null) {
    if ( TEST_MODE and !is_null($msg_test_mode) ) {
        trigger_error($msg_test_mode,E_USER_ERROR);
    } else {
        die($msg_not_test_mode);
    }
}

define('DBQUERY_WRITE',0);
define('DBQUERY_READ_RESULTSET',1);
define('DBQUERY_READ_SINGLEROW',2);
define('DBQUERY_READ_INTEGER',3);
define('DBQUERY_READ_INTEGER_TOLERANT_ZERO',4);
define('DBQUERY_READ_INTEGER_TOLERANT_NONE',5);
function dbquery($QueryType,$Query,$ParameterNames=null,$ParameterValues=null,$ShowInsteadOfIssue=false) {
    /*
        Example:
            dbquery(DBQUERY_WRITE,
                    'UPDATE `User` SET `IceCream` = :icecream:, `IQ` = :iq: WHERE `UserID` = :userid:',
                    array('icecream'  ,'iq','userid'),
                    array('Strawberry',105 ,1234    )
                    )
        would perform the query
            UPDATE `User` SET `IceCream` = 'Strawberry', `IQ` = 105 WHERE `UserID` = 1234
        Input must be checked beforehand for magic_quotes_gpc contamination.
        $ParameterNames and $ParameterValues should satisfy one of the following three criteria:
            - Both are null;
            - $ParameterNames is a string, and $ParameterValues is either a string or an integer;
            - $ParameterNames is an array of strings, and $ParameterValues is an array with
              the same number of elements each of which is either a string or an integer.
        $QueryType has no effect on the query itself; it controls return values as well as
        error messages displayed on failure. It should have one of the following values:
            DBQUERY_WRITE -> Write query (INSERT, UPDATE or DELETE); return mysqli result object
                             or 'NONE' if empty result set (should be 'NONE' or you are
                             doing it wrong, but this is not checked within the function)
            DBQUERY_READ_RESULTSET -> SELECT query; return mysqli result object or 'NONE' if empty result set
            DBQUERY_READ_SINGLEROW -> SELECT query, at most one row expected in resultset; return
                                      associative array or 'NONE' if empty result set
                                      (if there is more than one row in the result set
                                      then the second and subsequent rows are ignored)
            DBQUERY_READ_INTEGER -> SELECT query, resultset expected to be an integer scalar
                                    (e.g. 'SELECT COUNT(*) FROM TableName');
                                    return integer (error out if empty result set)
                                    (if there is more than one row in the result set
                                    then the second and subsequent rows are ignored;
                                    if there is more than one column in the result set
                                    then the second and subsequent columns are ignored)
            DBQUERY_READ_INTEGER_TOLERANT_ZERO -> As DBQUERY_READ_INTEGER but tolerates an empty
                                                  result set (returns integer 0)
            DBQUERY_READ_INTEGER_TOLERANT_NONE -> As DBQUERY_READ_INTEGER but tolerates an empty
                                                  result set (returns 'NONE')
    */
    global $cxn,$readerrormessage,$writeerrormessage;
    if ( $QueryType == DBQUERY_WRITE ) { $ErrorMessage = $writeerrormessage; }
    else                               { $ErrorMessage = $readerrormessage;  }
    if ( is_string($ParameterNames) and
         ( is_string($ParameterValues) or is_int($ParameterValues) )
         ) {
        $ParameterNames  = array($ParameterNames);
        $ParameterValues = array($ParameterValues);
    }
    if ( is_array($ParameterNames) and
         is_array($ParameterValues) and
         count($ParameterNames) == count($ParameterValues)
         ) {
        if ( count(array_unique($ParameterNames)) != count($ParameterNames) ) {
            myerror($ErrorMessage,'Duplicated parameter names');
        }
        $ParameterLocations = array();
        $OrderingArray      = array();
        for ($i=0;$i<count($ParameterNames);$i++) {
            if ( !isset($ParameterNames[$i]) or
                 !isset($ParameterValues[$i]) or
                 !is_string($ParameterNames[$i]) or
                 ( !is_string($ParameterValues[$i]) and !is_int($ParameterValues[$i]) )
                 ) {
                myerror($ErrorMessage,'Parameter array elements are the wrong types or have nonstandard keys');
            }
            $ParameterNames[$i] = ':'.$ParameterNames[$i].':';
            if ( is_string($ParameterValues[$i]) ) {
                $ParameterValues[$i] = '\''.mysqli_real_escape_string($cxn,$ParameterValues[$i]).'\'';
            }
            $SearchStartPoint = 0;
            while ( true ) {
                $CurrentParameterLocation = strpos(substr($Query,$SearchStartPoint),$ParameterNames[$i]);
                if ( $CurrentParameterLocation === false ) {
                    break;
                } else {
                    $ParameterLocations[]  = $SearchStartPoint + $CurrentParameterLocation;
                    $SearchStartPoint     += $CurrentParameterLocation + strlen($ParameterNames[$i]);
                    $OrderingArray[]       = $i;
                }
            }
        }
        if ( count($ParameterLocations) ) {
            array_multisort($ParameterLocations,$OrderingArray);
            $ExplodedQuery = explode(':!!:',str_replace($ParameterNames,':!!:',$Query));
            $Query = '';
            for ($i=0;$i<count($ParameterLocations);$i++) {
                $Query .= $ExplodedQuery[$i].
                          $ParameterValues[$OrderingArray[$i]];
            }
            $Query .= $ExplodedQuery[count($ParameterLocations)];
        }
    } else if ( !is_null($ParameterNames) or !is_null($ParameterValues) ) {
        myerror($ErrorMessage,'Parameter arguments are the wrong types');
    }
    if ( $ShowInsteadOfIssue ) { die('<pre>'.$Query.'</pre>'); }
    $QueryResult = mysqli_query($cxn,$Query);
    if ( !$QueryResult ) {
        myerror($ErrorMessage,'Query results in error message: "'.mysqli_error($cxn).'"');
    }
    if ( mysqli_num_rows($QueryResult) == 0 ) { $NoResults = true;  }
    else                                      { $NoResults = false; }
    if ( $NoResults and $QueryType == DBQUERY_READ_INTEGER ) {
        myerror($ErrorMessage,'Expecting at least one row of output but found no rows');
    } else if ( $NoResults and $QueryType == DBQUERY_READ_INTEGER_TOLERANT_ZERO ) {
        $rtnvalue = 0;
    } else if ( $NoResults ) {
        $rtnvalue = 'NONE';
    } else if ( $QueryType == DBQUERY_READ_INTEGER or
                $QueryType == DBQUERY_READ_INTEGER_TOLERANT_ZERO or
                $QueryType == DBQUERY_READ_INTEGER_TOLERANT_NONE
                ) {
        $rtnvalue = mysqli_fetch_row($QueryResult);
        $rtnvalue = (int)$rtnvalue[0];
    } else if ( $QueryType == DBQUERY_READ_SINGLEROW ) {
        $rtnvalue = mysqli_fetch_assoc($QueryResult);
    } else {
        $rtnvalue = $QueryResult;
    }
    while ( mysqli_more_results($cxn) ) { mysqli_next_result($cxn); }
    return $rtnvalue;
}

fletcher
Jun 27, 2003

ken park is my favorite movie

Cybernetic Crumb
If you think that is less ugly than using a prepared statement with PDO you need to have your head checked man.

Also, the way that you do prepared statements in PDO is very similar to how you can do it in other languages, so learn to love it.

I mean for a simple query like the example in your code:

php:
<?
    $db = new PDO("blah");
    $update = $db->prepare("UPDATE User SET IceCream = :IceCream, IQ = :IQ WHERE UserId = :UserId");
    $db->bindParam(":IceCream", $iceCream);
    $db->bindParam(":IQ", $iq);
    $db->bindParam(":UserId", $userId);
    if ($update->execute())
        //do poo poo
?>
How is that not easier to read and write?

Also, PDO handles escaping things, so you don't have to deal with all that magic_quotes_gpc/mysqli_real_escape_string bullshit.

fletcher fucked around with this message at 03:05 on Apr 10, 2010

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

fletcher posted:

If you think that is less ugly than using a prepared statement with PDO you need to have your head checked man.

I mean for a simple query like the example in your code:

php:
<?
    $db = new PDO("blah");
    $update = $db->prepare("UPDATE User SET IceCream = :IceCream, IQ = :IQ WHERE UserId = :UserId");
    $db->bindParam(":IceCream", $iceCream);
    $db->bindParam(":IQ", $iq);
    $db->bindParam(":UserId", $userId);
    if ($update->execute())
        //do poo poo
?>
How is that not easier to read and write?

Are you saying that

php:
<?
    $db = new PDO("blah");
    $update = $db->prepare("UPDATE User SET IceCream = :IceCream, IQ = :IQ WHERE UserId = :UserId");
    $db->bindParam(":IceCream", $iceCream);
    $db->bindParam(":IQ", $iq);
    $db->bindParam(":UserId", $userId);
    if ($update->execute())
        //do poo poo
?>
is easier to read, write and comprehend than

php:
<?
    dbquery(DBQUERY_WRITE,
            'UPDATE `User` SET `IceCream` = :icecream:, `IQ` = :iq: WHERE `UserID` = :userid:',
            array('icecream'  ,'iq','userid'),
            array('Strawberry',105 ,1234    )
            );
?>
? If so, well, I disagree and I have to confess to being a bit baffled at you thinking that.

quote:

Also, PDO handles escaping things, so you don't have to deal with all that magic_quotes_gpc/mysqli_real_escape_string bullshit.

There are two things I would say to this.

1) Of course I have to deal with magic_quotes_gpc bullshit. magic_quotes_gpc doesn't just affect user input that is to be sent to the database - it affects any and all user input, and it's not just user input for the database that I might need to watch for it. This being the case, of course it's right and proper that I should un-magic-quote content with the use of another function before I go about sending it to the database; it's not the role of this function to take care of that stuff.

For example, on my site there is a crude message-board facility. Users can post messages in threaded discussions, like the SA forums (but on a comparatively tiny scale and with a comparatively crude feature set). They submit messages using a POST form. But sometimes when they submit the form, it won't meet certain criteria and it will have to be rejected. For example, they might not be logged in, or they might be banned, or the person who created the thread might have closed it, disallowing further messages. If one of these things happens, then I serve a page that says, "You can't post that message right now" - but crucially I also echo back the message they entered (after htmlspecialchars()ing it, of course), as a courtesy, in case it took them a long time to type it out. Now that's not material that was on its way to the database, but nevertheless it might be magic-quoted.

Do you see? It's not a concern of the database specifically that magic_quotes might have ballsed things up. It is properly the role of another function to deal with magic_quoting.

2) If I use this function then I do indeed not have to deal with "mysqli_real_escape_string bullshit" - just as you do not have to deal with it when you use PDO - because I have delegated said "bullshit" to my function. The function mysqli_real_escape_string appears nowhere in my code (save in the included function code).

quote:

Also, the way that you do prepared statements in PDO is very similar to how you can do it in other languages, so learn to love it.

I don't currently consider this to be important.

Tad Naff
Jul 8, 2004

I told you you'd be sorry buying an emoticon, but no, you were hung over. Well look at you now. It's not catching on at all!
:backtowork:
Well I don't know about you guys, but I'm stuck with some pre-PDO-capable systems and I find ADOdb to be pretty capable and programmer-friendly. And I tend to avoid OO in Web situations, what with all that construction on each page load.

MrMoo
Sep 14, 2000

Hammerite posted:

If so, well, I disagree and I have to confess to being a bit baffled at you thinking that.

You're just being a pedant on syntax, PDO allows you four different ways,

http://php.net/manual/en/pdostatement.execute.php

Lumpy
Apr 26, 2002

La! La! La! Laaaa!



College Slice

MrMoo posted:

You're just being a pedant on syntax, PDO allows you four different ways,

http://php.net/manual/en/pdostatement.execute.php

Plus the biggest advantage: Somebody else already wrote it, it's already in my PHP install, and it's thoroughly tested.

Adbot
ADBOT LOVES YOU

fletcher
Jun 27, 2003

ken park is my favorite movie

Cybernetic Crumb

Lumpy posted:

Plus the biggest advantage: Somebody else already wrote it, it's already in my PHP install, and it's thoroughly tested.

Well...2/3 ain't bad.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply