0

I am having trouble figuring out where my insert (using pdo) is failing. I passed it 19 parameters via an array, and then bound 19 values from that same array.

It fails at the bind step with the following error:

Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens' 

I was wondering if you guys would be able to figure out why its failing. The code I am using is below:

addevent.php

if(isset($_POST['add_event'])){

    $eventDetails = $_POST;
    $event_code = htmlspecialchars($eventDetails['ecode']);
    $event_title = htmlspecialchars($eventDetails['etitle']);
    $location = htmlspecialchars($eventDetails['location']);
    $start_date = htmlspecialchars($eventDetails['start_date']);
    $end_date = htmlspecialchars($eventDetails['end_date']);
    $super_early_date = htmlspecialchars($eventDetails['super_early_date']);
    $early_date = htmlspecialchars($eventDetails['early_date']);
    $tax_type = htmlspecialchars($eventDetails['tax_desc']);
    $tax_rate = htmlspecialchars($eventDetails['tax_rate']);
    $is_call_open = htmlspecialchars($eventDetails['is_call_open']);
    $domain_name = htmlspecialchars($eventDetails['domain']);
    $sold_out_msg = htmlspecialchars($eventDetails['sold_out_msg']);
    $group_min = htmlspecialchars($eventDetails['group_discount_min']);
    $group_num = htmlspecialchars($eventDetails['group_discount_per']);

    //errors array that will be passed to view if there are errors;
    $errors = array();

    //if there are contents in the error array from previous execution empty error
    if(!empty($errors)){ unset($errors); }

    //if any of these fields are empty add them to the array
    if(empty($event_code)) { array_push($errors, ucwords('event code is required')); }
    if(empty($event_title)) { array_push($errors, ucwords('event title is required')); }
    if(empty($location)) { array_push($errors, ucwords('location is required')); }
    if(empty($start_date)) { array_push($errors, ucwords('start date is required')); }
    if(preg_match('/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/',$start_date) == 0) { array_push($errors, ucwords('incorrect format for start date, it should be').' yyyy-mm-dd'); }
    if(empty($end_date)) { array_push($errors, ucwords('end date is required')); }
    if(preg_match('/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/', $end_date) == 0) { array_push($errors, ucwords('incorrect format for end date, it should be').' yyyy-mm-dd'); }
    if(preg_match('/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/', $super_early_date) == 0) { array_push($errors, ucwords('incorrect format for super early bird date, it should be').' yyyy-mm-dd'); }
    if(preg_match('/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/', $early_date) == 0) { array_push($errors, ucwords('incorrect format for early bird date, it should be').' yyyy-mm-dd'); }
    if($tax_type == 'none') { array_push($errors, ucwords('tax type is required')); }
    if(empty($tax_rate)) { array_push($errors, ucwords('tax rate is required')); }
    if(empty($domain_name)) { array_push($errors, ucwords('domain name is required')); }
    if(preg_match('/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/', $domain_name) == 0) { array_push($errors, ucwords('domain name is not valid')); }
    if(empty($sold_out_msg)) { array_push($errors, ucwords('sold out message is required')); }
    if(!empty($group_min) && empty($group_num)) { array_push($errors, ucwords('group discount (percent) is required')); }

    if(!empty($errors)){
        $this->set('errors', $errors);
    }
    else{
        //remove the add_event button from the event 
        $add_event_index = array_pop($eventDetails);
        $keys = array_keys($eventDetails);

        $addEvent = "INSERT INTO v_events (" . implode(",", $keys) . ") VALUES (";
        for($i = 0; $i < sizeof($eventDetails); $i+=1){
            if($i == 0) { $addEvent .= "?"; }
            else { $addEvent .= ",?"; }
        }
        $addEvent .= ")";

        $success = $this->Cms->query($addEvent, $eventDetails);

        if(!$success){
            print_r($this->Cms->getError());
        }

        $this->set('success', ucwords('event succcesfully added!'));
    }
}

$this->Cms->query function:

public function query($query, $attributes = array(), $singleResult = 0) {

    echo $query;
    $stmt = $this->dbc->prepare($query);

    if(!empty($attributes)){

        echo'<pre>';
        print_r($attributes);
        echo'</pre>';
        foreach($attributes as $values){
            if(empty($values) || $values) { $values = 'not provided'; }
            if($values == NULL) { $values = '0'; }
            echo $stmt->bindParam('?', $values);
            echo'<pre>';
            $stmt->debugDumpParams();
            echo'</pre>';
        }

    }

    $success = $stmt->execute();

    return $success;
}
Kevin
  • 41,694
  • 12
  • 53
  • 70
NSaid
  • 689
  • 2
  • 16
  • 32
  • 4
    Lovely SQL injection attack vulnerability you've got there, inspite of all your attempts at using placeholders... Just because you're using placeholders for the $_POST **VALUES** doesn't mean you can trust the $_POST **KEYS**... yet you're blindly stuffing those into the query as if they could never ever be exploited. – Marc B Aug 13 '14 at 20:00
  • I thought the whole point of using pdo was to deal with sql injection. At the same time I thought I was dealing sql injection by converting special characters such as '<', '>', etc to their html entities through htmlspecialchars. In any case thank you for the comment please provide me a tutorial I can read to better improve security, I'm always looking to improve in that area – NSaid Aug 13 '14 at 20:10
  • 2
    http://stackoverflow.com/questions/60174/how-can-i-prevent-sql-injection-in-php – Marc B Aug 13 '14 at 20:11
  • @MarcB Thanks, will read up – NSaid Aug 13 '14 at 20:22
  • 1
    @NSaid, the problem is not the bind part, the problem is the `implode(",", $keys)` part. Every parameter (via bind) is save, because your database knows, it is a variable, but with your `implode(",", $keys)` you don't have variable. Your a literaly say, this is "SQL Command" instead of a "User Input Variable". – Christian Gollhardt Aug 14 '14 at 01:53

0 Answers0