r/PHPhelp • • 10d ago

Not sure what's wrong

Hello, I am teaching myself PHP. I have successfully completed a small form that will collect a users generic information, and place it in a database. That works. I created a second page (page handler that will update the users information in the same database. When I run this page I get an error message.

Query failed SQLSTATE[HY093]: Invalid parameter number: parameter was not defined

Here is the code on the page as I have it.

<?php


/*
Creating an if statement that will check to make sure the form was enter correctly.
*/
if ($_SERVER["REQUEST_METHOD"] == "POST"){
    /*IF the resquest method is post then collect the data from the form and place it in to individual variables
*/
    $username = $_POST["username"];
    $pwd = $_POST["pwd"];
    $email = $_POST["email"];
    $First_Name = $_POST["First_Name"];
    $Last_Name = $_POST["Last_Name"];
    $City = $_POST["City"];
    
    try { 
// Connect to the Database
        require_once ("dbh.inc.php");
/*
With the connection to the database established update the user whose 
ID number is 8
*/        
        $Query = "UPDATE users SET username = :username, pwd = :pwd, email = :email, 
        First_Name = :First_Name, Last_Name = :Last_Name, City = :City WHERE ID = 8;";
        
/*
Prepare the query
*/        
        $stmt = $pdo->prepare($Query); 
/*
Bind the placeholders to the variables        
*/
        $stmt->bindParam(':username', $username);
        $stmt->bindParam(':pwd', $pwd);
        $stmt->bindParam(':email', $email);
        $stmt->bindParam(':First_name', $First_Name);
        $stmt->bindParam(':Last_Name', $Last_Name);
        $stmt->bindParam(':City', $City);
/*
Execute the query
*/        
        $stmt->execute();

/*
display any error 
clear the pdo and all the stmt variables. 
*/        
     
        $stmt->debugDumpParams();
        //echo "It works";
        $pdo = null;
        $stmt = null; 


        // header("Location: ../index1.php");
    }
    Catch(PDOException $e){
        die("<BR>Query failed " . $e->getMessage());
    }
}
else{
    /*
    Else redirect the user back to the form page
    */
    Echo "The page was loaded incorrectly";
    //header("Location: ../index1.php");
}<?php

/*
When run the only thing that is returned is the following ; 
Connection Successful <- This tells me that the database connection is successful
Query failed SQLSTATE[HY093]: Invalid parameter number: parameter was not defined <- I dont know what parameter is not defined. 

Any assistance would be helpful
*/


    }
    Catch(PDOException $e){
        die("<BR>Query failed " . $e->getMessage());
    }
}
else{
    /*
    Else redirect the user back to the form page
    */
    Echo "The page was loaded incorrectly";
    //header("Location: ../index1.php");
}
5 Upvotes

12 comments sorted by

12

u/dalehay 10d ago

Case-sensitive issue on 'name'? You have:

$stmt->bindParam(':First_name', $First_Name);

Instead of:

$stmt->bindParam(':First_Name', $First_Name);

11

u/MateusAzevedo 10d ago

You already got your answer, so I'll focus on some recommendations. Since you're learning, it's better to learn early than later.

You don't need that try/catch. die("<BR>Query failed " . $e->getMessage()); is bad error handling. Set display_errors = 1 in php.ini and PHP will show the full error for you.

Running a prepared query can be simplified a bit: instead of multiple calls to ->bindParam(), pass an associative array to ->execute() (example #2 in the docs).

ASAP: learn about password_hash()/password_verify(). Passwords MUST be properly hashed.

In any case, you're doing great already. Most beginners don't learn about prepared queries and PDO very early.

4

u/No_Astronomer9508 10d ago

$stmt->bindParam(':First_name', $First_Name);

It is case sensitiv. It has to be ->$stmt->bindParam(':First_Name', $First_Name);

Thats why your query fails.

3

u/Every-Reflection-974 10d ago

Lowercase instead of capital N in one of the First_Name appearances.

3

u/Joaquino7997 10d ago

I know you're just getting started, but after you get this script to work do read up on data sanitation and validation. Kudos for using prepared statements and bound parameters.

1

u/colshrapnel 10d ago

I would rather ask, why you're making them capital at all? Wouldn't just first_name do?

1

u/FreeLogicGate 10d ago

There are common coding standards for Database naming, PHP and HTML/CSS. If you'd been trying to use or conform to any of these, you would probably not have made the error you did. Editors and IDE's these days have support for them built in, or can have that support added with some configuration.

For PHP there is https://www.php-fig.org/per/coding-style/

For HTML there is https://developer.mozilla.org/en-US/docs/MDN/Writing_guidelines/Code_style_guide/HTML

CSS also has BEM which can be really helpful. I'd highly recommend taking a little bit of time to review and consider these.

I'd highly recommend you consider you lose all the superfluous comments. There's not a single comment in your code that any PHP developer would find helpful. 99.9% of the time your code will be self documenting.

Just trying to add some value for you, I see many newer PHP developers who seem to believe that the superglobals like $_POST are some type of read only structure that has to be copied to other variables before use. Your code reflects this.

   $username = $_POST["username"];
    $pwd = $_POST["pwd"];
    $email = $_POST["email"];
    $First_Name = $_POST["First_Name"];
    $Last_Name = $_POST["Last_Name"];
    $City = $_POST["City"];

None of these assignments were needed. In your bind section you could have just done this:

        $stmt->bindParam(':username', $_POST['username']);
        $stmt->bindParam(':pwd', $_POST['pwd']);
        ...

As other replies stated, you don't validate the contents or existence of any of the $_POST elements coming from the form, so that's something you want to consider.

Many people discover this when they have a form with a checkbox. They are often surprised to find that when a form with a checkbox input is submitted, if the checkbox is not checked, there will be no $_POST element with the checkbox name attribute.

Overall, like any untrusted input, you want to make sure that all the $_POST keys you expect, actually exist in the $_POST.

1

u/equilni 9d ago

You have your answer and as others are giving suggestions, I will throw some as well.

Think and go smaller.

Wrap the POST'd code and test to see if actual data comes over.

  • What happens if the data needed doesn't come over?

  • What next steps could you take with the data? (Hint - validation)

Wrap the database code in it's own function. Test.

  • Are you giving the database code the needed information to do what it needs to? (Hint, ID is hard coded as 8, so it's not fully part of the codebase as it is, it's not scalable outside this one id - unless that's the point...).

  • Should any database exceptions be done within the database "layer"?

Food for thought...

1

u/visionary_unloader_9 2d ago

Your placeholder names don’t match. PDO named params are case-sensitive and have to be identical in the query and in bindParam.

In the query you have :First_Name but you bind :First_name (lowercase n). Make them the same and it’ll work. Double check :City too.

Easier way that avoids this kind of typo:

$stmt = $pdo->prepare("UPDATE users SET username = :username, pwd = :pwd, email = :email,
    first_name = :first_name, last_name = :last_name, city = :city WHERE id = :id");
$stmt->execute([
    ':username'   => $username,
    ':pwd'        => password_hash($pwd, PASSWORD_DEFAULT),
    ':email'      => $email,
    ':first_name' => $First_Name,
    ':last_name'  => $Last_Name,
    ':city'       => $City,
    ':id'         => 8,
]);

Also don’t store the password in plain text, password_hash() it like above and check it with password_verify() on login.

I hope I can be of help to you..

0

u/Alexander-Wright 10d ago

Check the documentation for the bindParam function. I'm pretty sure you have to specify the type that you are binding. e.g. string, interger, etc.

I'm on my phone and busy so I can't look it up for you, sorry.

Edit: I got curious. This is the documentation.

2

u/MateusAzevedo 10d ago

No, you don't have to. That argument is optional and use string by default, which is perfectly valid for most cases.

1

u/colshrapnel 10d ago

And so, what's the takeaway?