SQLSTATE [HY093]: Invalid parameter number: Parameter not defined (PDO)

I know it was done before death.

But believe me, I've been researching how to fix this for quite some time. What I'm trying to achieve is a MySQL PDO database wrapper for use with PDO, which I can just include in my code. My main problem has to do with, in particular, the two functions as well as the actual binding of the parameters that I am trying to achieve. The reason I am declaring two features and not one is despite my efforts, I have not been able to determine who is trying this issue. The variable I var_dump

ed confirmed that it is not a variable, it is something else. However, the fact that I get this error in the first place means that something must be wrong with the code.

Illustration A: fetch( $table, $columns, $whereArgs )

The purpose of this function is to just get a string. This is accomplished by accepting the table row to retrieve, as well as any columns and where-clauses to accomplish a specific task. Once the parameters have been submitted, one or two loops are called, which dynamically form the request.

public function fetch( $table, $columns, $whereArgs )
    {
        if ( $whereArgs === NULL && $columns === NULL ) return false;

        $select = "SELECT ";
        $where  = " WHERE ";

        $iQuery = 0;

        $sqlParams = array();

        $columnCount = count( $columns ) - 1;

        foreach( $whereArgs as $key => $value )
        {
            $paramKey = sprintf( ':%s', $key );

            $where .= sprintf( "`%s`= %s", $key, $paramKey );

            $sqlParams[ "{$paramKey}" ] = sprintf( "%s", $value );

            if ( $iQuery <= $columnCount )
            {
                $select .= sprintf( '`%s`', $columns[ $iQuery ] );  
                $select .= ', ';
            }
            else 
            {
                $select .= ' '; 
            }

            ++$iQuery;
        }

        if ( $iQuery <= $columnCount )
        {
            for( ; $iQuery < $columnCount; ++$iQuery )
            {
                if ( $iQuery < $columnCount )
                {
                    $select .= sprintf( '`%s`', $columns[ $iQuery ] );  
                    $select .= ', ';
                }
                else 
                {
                    $select .= ' '; 
                }
            }
        }

        $select .= sprintf( "FROM `%s`", $table );

        $query = $select . $where;

        return $this->doQuery( $query, $sqlParams, TRUE, QueryType::Row );
    }

      

Illustration B: doQuery( $query, $sqlParams, $return = FALSE, $queryType = QueryType::None )

This function is relatively simple: all it does is bind values ​​and statement execution, and when checking what type will be returned (return types which are either "row", "column", or "all") specified by the class QueryType

, a class that is beyond the scope of this problem) and then returns whatever is required.

protected function doQuery( $query, $sqlParams, $return = FALSE, $queryType = QueryType::None )
    {
        $statement = $this->mConnection->prepare( $query );

        foreach( $sqlParams as $param => $value )
        {
            $statement->bindValue( $param, $value );
        }

        $statement->execute( );

        if ( $return )
        {
            switch( $queryType )
            {
                case QueryType::Row:
                    return $statement->fetch( );
                case QueryType::Column:
                    return $statement->fetchColumn( );
                case QueryType::All:
                    return $statement->fetchAll( );
                case QueryType::None:
                    return $statement;
                default:
                    return false;   
            }
        }

    }

      

Illustration C: test.php

This is just a little test script I wrote to test the database.

$database = new Database( 'evolve_admin' );

$res = $database->fetch(
    'evol_users', 
    array( 'user.id', 'user.email', 'user.firstname' ), 
    array( 'user.email' => 'test1234@test.com' )
);


var_dump( $res );

      

Other comments

I found out that something was wrong with my code, I just lost what exactly could be. As far as my debugging skills go, I have done quite a bit of research into this question and it seems like this error is very common. My main goal is to get this wrapper working, and if anyone sees any errors within the code itself (including those outside the scope of this issue in particular) please let me know.

To those who offer a hand in this: thank you very much.

+3


source to share


2 answers


I think you are chasing some whitespace in there:

$where .= sprintf( "`%s`= %s", $key, $paramKey );

      

The next time you used a variable $wher

, you added it to $select

.

If more than that, where is arg where:

WHERE `%s`= %s`%s`= %s`%s`= %s`%s`= %s`%s`= %s

      

You haven't made a mistake with the thought of your SELECT. By the way, you have two identical loops and tests for your generation if ( $iQuery <= $columnCount )

. One in a loop where and outside. What's the use?

Edit: And of course I forgot to mention why you have this error: It's in

$sqlParams[ "{$paramKey}" ] = sprintf( "%s", $value );

      

You create a table that looks like this: array ( "{:akey}" => "avalue")

(I read the value as a string. Why did you use curly braces ({}), it completely changes the key name (should be :keyname

not {:keyname

}

Edit 2: Was in a good mood, so here is a simplified version of your fetch method (not tested, but should work fine)



/*
* $whereArgs default value is an array, so you can call a select 
* without an empty array supplied if you does not have some where clause arguments
* The separator is how the element will be binded alltogether in the where clause
*/
public function fetch( $table, $columns, $whereArgs = array(), $separator= 'AND' )
{
    /* We return false, if the columns variable is not set, or is not an array, 
    *  or (if it is an array) does not contain anything 
    *  or the $whereArgs is not and array (it would mean something bad have been given)
    */
    if ( false == isset($columns) || false == is_array($columns) 
            || 0 == count($columns) || false == is_array($whereArgs) )
    {
        return false;
    }

    $select = "SELECT";
    $from = " FROM `$table`";
    $where  = " WHERE ";

    /* SELECT generation */
    for ( $columIt = 0; $columIt < count($columns);  $columIt++)
    {
        $select .= " " . $columns[$columIt];
        // We check if we need to add a ','
        if ( $columIt+1 < count($columns) )
        {
            $select .= ",";
        }
    }

    /* WHERE clause generation */
    $sqlParams = array();
    $whereIt = 0;
    foreach( $whereArgs as $key => $value )
    {
        $stripedKey = preg_replace('/\\./', '_', $key);
        $where .= " $key= :$stripedKey";
        $sqlParams[ ":$stripedKey" ] = "$value";
        // We check if we need to add a where separator
        if ( $whereIt +1 < count($whereArgs ) )
        {
            $select .= " $separator";
        }
        $whereIt++;

    }

    /*  the generated where clause is only printed if $whereArgs 
    *   is not an empty array 
    */
    $query = $select . $from . ((0<count($whereArgs))?$where:"");

    return $this->doQuery( $query, $sqlParams, TRUE, QueryType::Row );
}

      

Edit 3: BTW didn't see your test sample, but the parameter name cannot contain '.' char

Edit 4: Didn't see you solved this, I added pre_replace to replace '.' in your subset. Also get rid of the '' 'char when repeating the key, the query will fail otherwise

`user.email`=:arg

      

Don't like it =)

user.email=:arg or user.`email`=:arg

      

Preferred (`) is used to include a special char in the column name, so as before, the column name does not match any existing ones.

Edit 5: Instead of hiding your key to create arguments and an array of arguments. You can safely use what prevents erroneous hackers from appearing by using the $ whereIt counter:

$where .= " $key= :arg_$whereIt";
$sqlParams[ ":arg_$whereIt" ] = "$value";

      

Hello

+4


source


The problem was that every column I had in my test db had a "." between "user" and the column name. I solved the problem by simply replacing each period with an underscore and got rid of this error. For some reason the request still doesn't work, I'm sure everything will be fine.

So for example:



user.email

= user_email

.

0


source







All Articles