'SQLSTATE[HY000]: General error' for inserting data into database? - php

So I am creating a login system that uses php classes to insert data into database. The following is the insert method for the database.
public function insert($table, $fields = array()){
if(count($fields)){
$keys = array_keys($fields);
$values = '';
$x = 1;
foreach($fields as $field){
$values .= '?';
if($x < count($fields)){
$values .= ', ';
}
$x++;
}
$sql = "INSERT INTO {$table} (".implode(', ', $keys).") VALUES ({$values})";
if(!$this->query($sql, $fields)->error()){
return true;
}
}
return false;
}
The next method is where i grab input fields to input into database.
public function create($fields = array()) {
if ($this->_db->insert('users', $fields)) {
throw new Exception('There was a problem with creating an account. Try again later.');
}
}
And this is how I am registering the user.
if ($validation->passed()) {
//register user
$user = new User();
try {
$salt = Hash::salt(32);
$address =
Input::get('street_address') . ':' .
Input::get('city') . ':' .
Input::get('state_province') . ':' .
Input::get('zip_code')
;
echo $address;
$user->create(array(
'first_name' => Input::get('first_name'),
'last_name' => Input::get('last_name'),
'email' => Input::get('email'),
'address' => $address,
'password' => Hash::make(Input::get('password'), $salt),
'salt' => $salt,
'date_joined' => date('Y-m-d H:i:s'),
'groups' => 1
));
Session::flash('home', 'You have been registered and can now log in!');
header('Location: index.php');
} catch(Exception $e) {
die($e->getMessage());
}
// Session::flash('success', 'You registered successfully!');
// header('Location: index.php');
} else {
//show errors
foreach($validation->errors() as $error) {
echo $error . "<br>";
}
}
I tried fixing the query and making sure there was no mistakes, but I can't seem to find anything wrong with it.

Related

Verifying successful row creation in when using prepared_insert in PDO

How can I verify successful row creation (and modification) when using prepared_insert in PDO?
Here is my code:
try {
$options = [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
\PDO::ATTR_EMULATE_PREPARES => false,
];
$conn = new PDO("mysql:charset=utf8mb4;host=$servername;dbname=$dbname", $username, $password);
function escape_mysql_identifier($field){
return "`".str_replace("`", "``", $field)."`";
}
function prepared_insert($pdo, $table, $data) {
$keys = array_keys($data);
$keys = array_map('escape_mysql_identifier', $keys);
$fields = implode(",", $keys);
$table = escape_mysql_identifier($table);
$placeholders = str_repeat('?,', count($keys) - 1) . '?';
$sql = "INSERT INTO $table ($fields) VALUES ($placeholders)";
$pdo->prepare($sql)->execute(array_values($data));
}
$data = array_filter($data);
//var_dump ($data);
prepared_insert($conn, 'products', $data);
$id = $conn->lastInsertId();
if ($id > 0) {
echo json_encode(array('response'=>'success','message'≥'Row successfully added'));
}else{
echo json_encode(array('response'=>'danger','message'≥'Row not successfully added'));
}
}catch(PDOException $e){
echo json_encode(array('response'=>'danger','message'=>$e->getMessage()));
}
$conn = null;
As you can see, right now I am doing it by using lastInsertId() but I do not think that's the correct way to do it.
Additionally, if the row was not created, how can I capture the error behind it and report it?
you not need create this condition :
if ($id > 0) {
echo json_encode(array('response'=>'success','message'≥'Row successfully added'));
}else{
echo json_encode(array('response'=>'danger','message'≥'Row not successfully added'));
}
because you using try and catch.. you can make sure it with database transaction.. try to commit and catch to rollback..

I do not know why my callback_insert does not work. It does not seem to get called

I'm using the Grocery Crud and Ion Auth libraries in Codeigniter. The callback_insert does not get called when I try to create a user. callback_insert and callback_update work
The Chrome console does not display any messages. When I put print_r ($post_array) in the _create_user function the following message appears:
Uncaught SyntaxError: Unexpected token A in JSON at position 0.
I already tried to rename the function and it did not work.
function __construct() {
parent::__construct();
}
function _render_output($output = null) {
$data['title'] = 'Gerir utilizadores';
$output->data = $data;
$this->load->view('includes/header_crud', (array) $output);
$this->load->view('crud_view', (array) $output);
$this->load->view('includes/footer_crud', (array) $output);
}
function gerir() {
try {
$crud = new grocery_CRUD();
$crud->set_table('users');
$crud->set_subject('Utilizador');
//FORM LAYOUT
$crud->unset_export();
$crud->unset_print();
$crud->columns('username', 'email', 'active', 'last_login');
$crud->display_as('username', 'Nome')
->display_as('active', 'Estado')
->display_as('first_name', 'Primeiro nome')
->display_as('last_name', 'Último nome')
->display_as('last_login', 'Último login')
->display_as('password_confirm', 'Confirmar password');
$crud->add_fields('first_name', 'last_name', 'email', 'password', 'password_confirm');
$crud->edit_fields('first_name', 'last_name', 'email');
//VALIDATION
$crud->required_fields('first_name', 'last_name', 'email', 'password', 'password_confirm');
$crud->set_rules('email', 'E-mail', 'required|valid_email');
$crud->set_rules('password', 'Password', 'required|matches[password_confirm]');
//FIELD TYPES
$crud->change_field_type('last_login', 'readonly');
$crud->change_field_type('last_login', 'readonly');
$crud->change_field_type('password', 'password');
$crud->change_field_type('password_confirm', 'password');
//CALLBACKS
$crud->callback_insert(array($this, '_create_user'));
$crud->callback_update(array($this, 'edit_user'));
$crud->callback_delete(array($this, 'delete_user'));
//OUTPUT
$output = $crud->render();
$this->_render_output($output);
} catch (Exception $e) {
show_error($e->getMessage() . ' --- ' . $e->getTraceAsString());
}
}
function _create_user($post_array) {
try {
$username = $post_array['first_name'] . ' ' . $post_array['last_name'];
$password = $post_array['password'];
$email = $post_array['email'];
$data = array(
'first_name' => $post_array['first_name'],
'last_name' => $post_array['last_name'],
);
$this->ion_auth_model->register($username, $password, $email, $data);
return $this->db->insert_id();
} catch (Exception $e) {
show_error($e->getMessage() . ' --- ' . $e->getTraceAsString());
}
}
function delete_user($primary_key) {
if ($this->ion_auth_model->delete_user($primary_key)) {
return true;
} else {
return false;
}
}
function edit_user($post_array, $primary_key = null) {
$username = $post_array['first_name'] . ' ' . $post_array['last_name'];
$email = $post_array['email'];
$data = array(
'username' => $username,
'email' => $email,
'first_name' => $post_array['first_name'],
'last_name' => $post_array['last_name'],
);
$this->ion_auth_model->update($primary_key, $data);
return true;
}
I want to use Ion Auth model to create a user

try and catch executed at the same time

So i have this OOP Login & register system which gives a user the ability to change his/her name.
When a user is pressing "Update" button his/her name is changed in the DB the problem is that tho it changes the name in DB instead of redirecting the user where i want him/her to be redirected i get the error that should appear only when that name couldn't be changed.
User.php
<?php
class User{
private $_db,
$_data,
$_sessionName,
$_cookieName,
$_isLoggedIn;
public function __construct($user = null){
$this->_db = DB::getInstance();
$this->_sessionName = Config::get('session/session_name');
$this->_cookieName = Config::get('remember/cookie_name');
if(!$user){
if(Session::exists($this->_sessionName)){
$user = Session::get($this->_sessionName);
if($this->find($user)){
$this->_isLoggedIn = true;
} else{
//process logout
}
}
} else{
$this->find($user);
}
}
public function update($fields = array(), $id = null){
if(!$id && $this->isLoggedIn()){
$id = $this->data()->id;
}
if(!$this->_db->update('users', $id, $fields)){
throw new Exception('There was a problem updating your profile.');
}
}
public function create($fields = array()){
if(!$this->_db->insert('users', $fields)){
throw new Exception('There was a problem creating an account');
}
}
public function find($user = null){
if($user){
$field = (is_numeric($user)) ? 'id' : 'username';
$data = $this->_db->get('users', array($field, '=', $user));
if($data->count()){
$this->_data = $data->first();
return true;
}
}
return false;
}
public function login($username = null, $password = null, $remember = false){
if(!$username && !$password && $this->exists()){
Session::put($this->_sessionName, $this->data()->id);
} else{
$user = $this->find($username);
if($user){
if($this->data()->password === Hash::make($password, $this->data()->salt)){
Session::put($this->_sessionName, $this->data()->id);
if($remember){
$hash = Hash::unique();
$hashCheck = $this->_db->get('users_session', array('user_id', '=', $this->data()->id));
if(!$hashCheck->count()){
$this->_db->insert('users_session',array(
'user_id' => $this->data()->id,
'hash' => $hash
));
} else{
$hash = $hashCheck->first()->hash;
}
Cookie::put($this->_cookieName, $hash, Config::get('remember/cookie_expiry'));
}
return true;
}
}
}
return false;
}
public function exists(){
return(!empty($this->_data)) ? true : false;
}
public function logout(){
$this->_db->delete('users_session', array('user_id', '=', $this->data()->id));
Session::delete($this->_sessionName);
Cookie::delete(Config::get('remember/cookie_name'));
}
public function data(){
return $this->_data;
}
public function isLoggedIn(){
return $this->_isLoggedIn;
}
}
edit-profile.php
<?php
$user = new User();
if(!$user->isLoggedIn()){
Redirect::to('login');
}
if(Input::exists()){
if(Token::check(Input::get('token'))){
$validate = new Validate();
$validation = $validate->check($_POST, array(
'name' => array(
'required' => true,
'min' => 2,
'max' => 50
)
));
if($validation->passed()){
try{
$user->update(array(
'name' => Input::get('name')
));
Session::flash('flash', 'Your profile has been edited with success!');
Redirect::to('flash');
} catch(Exception $e){
die($e->getMessage());
}
} else{
foreach($validation->errors() as $error){
echo $error . '<br />';
}
}
}
}
?>
<form action="" method="post">
<div class="field">
<input type="text" name="name" value="<?php echo escape($user->data()->name); ?>">
</div>
<input type="submit" value="Update">
<input type="hidden" name="token" value="<?php echo Token::generate(); ?>">
</form>
I have no clue why that is happening
This is the update() method in my DB.class
public function update($table, $id, $fields){
$set = '';
$x = 1;
foreach ($fields as $name => $value) {
$set .= "{$name} = ?";
if($x < count($fields)){
$set .= ', ';
}
$x++;
}
$sql = "UPDATE {$table} SET {$set} WHERE id = {$id}";
if($this->query($sql, $fields)->error()){
return true;
}
return false;
}
For your update function
public function update($table, $id, $fields){
$set = '';
$x = 1;
foreach ($fields as $name => $value) {
$set .= "{$name} = ?";
if($x < count($fields)){
$set .= ', ';
}
$x++;
}
$sql = "UPDATE {$table} SET {$set} WHERE id = {$id}";
if($this->query($sql, $fields)->error()){
return true;
}
return false;
}
Instead do
public function update($table, $id, $fields){
$set = [];
foreach ($fields as $name => $value) {
$set[] = "{$name} = ?";
}
$set = implode(', ', $set);
$sql = "UPDATE {$table} SET {$set} WHERE id = ?";
$fields[] = $id; //id should always be the last ?
if($this->query($sql, $fields)->error()){
return true;
}
return false;
}
Also there is the potential for the table and id and even the keys of $fields to be used as a vector for SQL injection. You may be entering it where ever you use them. But it's possible because you are not checking the table against a white list and anytime you concatenate values into SQL there is the potential for them to be exploited. All it takes is one mistake, you class should not allow coding errors in other places to compromise it's security.
You can get a list of tables from the database itself.
$statement = 'SELECT `TABLE_NAME` FROM `information_schema`.`TABLES` WHERE `TABLE_SCHEMA` LIKE "'.$database.'"';
So when you connect to the DB you could set a list of acceptable tables then check when a table is put in ( with in_array or such ). Just a thought.
It may also be possible to comprise the keys of the $fields array, for that you can do something similar to the table. but with this query
SHOW COLUMNS FROM {table}
For example, imagine a post request that has inputs matching your array $fields. All someone would have to do is send your server a request with the SQL Attack part in the key instead of the value and you are unprotected. Something like this ( don't drop your DB without a backup, feel i should say that.)
$_POST['1=1; DROP DATABASE --'] = true;
When you construct your query you would have
INSERT INTO table SET 1=1; DROP DATABASE -- = ?, {field} = ? .. rest of query
The -- starts a comment in SQL so nothing after the -- matter to the DB this prevents an error from happening.. So be careful of just dumping post keys into your SQL I'm not sure 1=1 would work but they could use a field out of the input list just as easily. this is just for example purposes.
To me this line
if($this->query($sql, $fields)->error()){
return true;
}else{
return false;
}
Says if there is an error return true not sure if that is the case as i don't know what $this->query or ->error() is but I imagine $this->query must return $this or some object that would contain the error. It's just confusing worded that way.

Passing $_POST values to output individual results information

My goal is to create a general template to be used to INSERT INTO testquiz (a MySQL table). This will be used for storing quiz results and user information (name and email are the only user input in the database) from quiz takers. I am new to PHP/MySQL and feel like I am just stumbling around.
My problem is that I am unable to get the $_POST values that are generated by the quiz to appear in the database. I know the values are being generated because they will display with a basic echo. There is a 'send to email' feature that works with the values that is working as well. I can get this code to work if I manually assign values to the $_POST array by uncommenting the first comment block.
What am I missing here?
Sidenote: I'll take security suggestions as well. Thank you.
Code below (user specific information omitted):
<?php
//disable magic quotes (PHP book says it's a good idea)
if (get_magic_quotes_gpc())
{
$process = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST);
while (list($key, $val) = each($process))
{
foreach ($val as $k => $v)
{
unset($process[$key][$k]);
if (is_array($v))
{
$process[$key][stripslashes($k)] = $v;
$process[] = &$process[$key][stripslashes($k)];
}
else
{
$process[$key][stripslashes($k)] = striplashes($v);
}
}
}
unset($process);
}
/* //Manually declare $_POST variables (can be disabled)
$_POST['v'] = '6.5.1';
$_POST['sp'] = 80;
$_POST['psp'] = 75;
$_POST['tp'] = 80;
$_POST['sn'] = 'user';
$_POST['se'] = 'abc123#fake.com';
$_POST['qt'] = 'Test Quiz';
*/
//Assign $_POST values to static variables???
$version = $_POST['v'];
$points = $_POST['sp'];
$passing_percent = $_POST['psp'];
$gained_score = $_POST['tp'];
$username = $_POST['sn'];
$email = $_POST['se'];
$quiz_title = $_POST['qt'];
//MySQL database connection PDO
try
{
$pdo = new PDO('mysql:host=localhost;dbname=quizresults', 'user', 'password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->exec('SET NAMES "utf8"');
}
catch (PDOException $e)
{
$error = 'Unable to connect to the database server.';
include 'error.html.php';
exit();
}
//Prepare input for database entry
try
{
$sql = $pdo->prepare("INSERT INTO testquiz (version, points, passing_percent, gained_score, username, email, quiz_title, date) VALUES (:version, :points, :passing_percent, :gained_score, :username, :email, :quiz_title, CURDATE())");
$sql->execute(array(":version" => $version, ":points" => $points, ":passing_percent" => $passing_percent, ":gained_score" => $gained_score, ":username" => $username, ":email" => $email, ":quiz_title" => $quiz_title));
//echo for debugging purposes
echo $version . '<br />', $points . '<br />', $passing_percent . '<br />', $gained_score . '<br />', $username . '<br />', $email . '<br />', $quiz_title . '<br />', date(DATE_ATOM);
}
catch (PDOException $e)
{
$error = 'Error adding quiz results to database: ' . $e->getMessage();
include 'error.html.php';
exit();
}
//Calculate user score
$points_num = (int)$points;
$passing_num = ((int)$passing_percent)/100 * (int)$gained_score;
//Write results to a text file
$f = fopen("result.txt", "w") or die("Error opening file 'result.txt' for writing");
fwrite($f, "--------------------------\n");
fwrite($f, "User name: ".$username."\n");
fwrite($f, "User email: ".$email."\n");
fwrite($f, "Quiz title: ".$quiz_title."\n");
fwrite($f, "Points awarded: ".$points."\n");
fwrite($f, "Total score: ".$gained_score."\n");
fwrite($f, "Passing score: ".$passing_num."\n");
if ($points_num >= $passing_num)
{
fwrite($f, "User passes\n");
}
else
{
fwrite($f, "User fails\n");
}
fwrite($f, "--------------------------\n");
if($f)
{
fclose($f);
}
?>
I'm not sure if this will fix everything but
$sql->execute(array(":version" => $version, ":points" => $points, ":passing_percent" => $passing_percent, ":gained_score" => $gained_score, ":username" => $username, ":email" => $email, ":quiz_title" => $quiz_title));
should be:
$sql->execute(array("version" => $version, "points" => $points, "passing_percent" => $passing_percent, "gained_score" => $gained_score, "username" => $username, "email" => $email, "quiz_title" => $quiz_title));
(remove the : from the array. it is only for PDO to 'name' the variables).

Php Exception error thrown

I'm building this Login system for my website. Everything seemed to work ok until I came across updating user details stored in database. Every time I try to update dummy details I get thrown an Exception and Can't figure out why.
Could You please scan through quickly and help me find an error if there is one? There are no syntax errors.
update.php
$user = new User();
if(!$user->isLoggedIn()){
Redirect::to('index.php');
}
// Check whether token is submited and user exists
if(Input::exists()){
if(Token::check(Input::get('token'))){
$validate = new Validate();
$validation = $validate->check($_POST, array(
'Name' => array(
'required' => true,
'min' => 4,
'max' => 30
),
'email' => array(
'required' =>true
)
));
if($validation->passed()){
// Update
try{
$user->update(array(
'Name' => Input::get('Name'),
'email' => Input::get('email')
));
Session::flash('home', 'Your details have been updated');
Redirect::to('index.php');
}catch(Exception $e) {
die($e->getMessage());
}
} else {
foreach($validation->errors() as $error){
echo ('<p>' . $error . '</p>');
}
}
}
}
User.php class
class User{
private $_db,
$_data,
$_sessionName,
$_cookieName,
$_isLoggedIn;
public function __construct($user = null){
$this->_db = DB::getInstance();
$this->_sessionName = Config::get('session/session_name');
$this->_cookieName = Config::get('remember/cookie_name');
if(!$user){
if(Session::exists($this->_sessionName)){
$user = Session::get($this->_sessionName);
if($this->find($user)){
$this->_isLoggedIn = true;
} else {
// Process Log out
}
}
} else {
$this->find($user);
}
}
public function update($fields = array(), $id = null){
if(!$id && $this->isLoggedIn()){
$id = $this->data()->id;
}
if(!$this->_db->update('user', $id, $fields)){
throw new Exception('Sorry, there was problem updating. Please try again later.');
}
}
public function create($fields = array()){
if(!$this->_db->insert('user', $fields)){
throw new Exception('There was a problem creating new account.');
}
}
public function find($user = null){
if($user){
$field = (is_numeric($user)) ? 'id' : 'Username';
$data = $this->_db->get('user', array($field, '=', $user));
if($data->count()){
$this->_data = $data->first();
return true;
}
}
return false;
}
public function login($Username = null, $password = null, $remember = false){
if(!$Username && !$password && $this->exists()){
// Log User in
Session::put($this->_sessionName, $this->data()->id);
} else {
$user = $this->find($Username);
if($user){
if($this->data()->Password === Hash::make($password, $this->data()->salt)){
Session::put($this->_sessionName, $this->data()->id);
if($remember){
$hash = Hash::unique();
$hashCheck = $this->_db->get('users_session', array('user_id', '=', $this->data()->id));
if(!$hashCheck->count()){
$this->_db->insert('users_session', array(
'user_id' => $this->data()->id,
'hash' => $hash
));
} else {
$hash = $hashCheck->first()->hash;
}
Cookie::put($this->_cookieName, $hash, Config::get('remember/cookie_expiry'));
}
return true;
}
}
}
return false;
}
public function exists(){
return (!empty($this->_data)) ? true : false;
}
public function logout(){
$this->_db->delete('users_session', array('user_id', '=', $this->data()->id));
Session::delete($this->_sessionName);
Cookie::delete($this->_cookieName);
}
public function data(){
return $this->_data;
}
public function isLoggedIn(){
return $this->_isLoggedIn;
}
}
(if(!$this->_db->update('user', $id, $fields)){
throw new Exception('Sorry, there was problem updating. Please try again later.');
})
This is the exception i get.. Thanks a million
If it helps update() is the method I get the error from
This is my DB class:
class DB{
private static $_instance = null;
private $_pdo,
$_query,
$_error = false,
$_results,
$_count = 0;
private function __construct(){
try {
$this->_pdo = new PDO('mysql:host=' .
Config::get('mysql/host') . ';dbname=' .
Config::get('mysql/db'),
Config::get('mysql/username'),
Config::get('mysql/password'));
} catch(PDOException $e){
die($e -> getMessage());
}
}
public static function getInstance(){
if(!isset(self::$_instance)) {
self::$_instance = new DB();
}
return self::$_instance;
}
public function query($sql, $params = array()){
$this->_error = false;
// Check if query has been prepared properly
if($this->_query = $this->_pdo->prepare($sql)){
$x = 1;
if(count($params)){
foreach($params as $param){
$this->_query->bindValue($x, $param);
$x++;
}
}
// If the query has been prepared successfuly, store the result
if($this->_query->execute()){
$this->_results = $this->_query->fetchAll(PDO::FETCH_OBJ);
$this->_count = $this->_query->rowCount();
} else {
$this->_error = true;
}
}
return $this;
}
public function action($action, $table, $where = array()){
if(count($where) === 3){
$operators = array('=', '>', '<', '>=', '<=');
$field = $where[0];
$operator = $where[1];
$value = $where[2];
if(in_array($operator, $operators)){
$sql = "{$action} FROM {$table} WHERE {$field} {$operator} ?";
if(!$this->query($sql, array($value))->error()){
return $this;
}
}
}
return false;
}
// QUERYING DATA FROM DATABASE
public function get($table, $where){
return $this->action('SELECT *', $table, $where);
}
// DELETING DATA FROM DATABASE
public function delete($table, $where){
return $this->action('DELETE', $table, $where);
}
// INSERTING DATA INTO DATABASE
public function insert($table, $fields = array()){
$keys = array_keys($fields);
$values = '';
$x = 1;
foreach($fields as $field){
$values .= "?";
if($x < count($fields)){
$values .= ', ';
}
$x++;
}
$sql = "INSERT INTO {$table} (`" . implode('`, `', $keys) . "`) VALUES({$values})";
if(!$this->query($sql, $fields)->error()){
return true;
}
return false;
}
public function results(){
return $this->_results;
}
public function update($table, $userID, $fields){
$set = '';
$x = 1;
foreach($fields as $name => $value){
$set .= "{$name} = ?";
if($x < count($fields)){
$set .= ', ';
}
$x++;
}
$sql = "UPDATE {$table} SET {$set} WHERE userID = {ID}";
if(!$this->query($sql, $fields)->error()){
return true;
}
return false;
}
public function first(){
return $this->results()[0];
}
public function error(){
return $this->_error;
}
public function count(){
return $this->_count;
}
}
If you tried to return the sql you will find that it is not valid, such as:
function update($table, $userID, $fields){
$set = '';
$x = 1;
foreach($fields as $name => $value){
$set .= "{$name} = ?";
if($x < count($fields)){
$set .= ', ';
}
$x++;
}
$sql = "UPDATE {$table} SET {$set} WHERE userID = {ID}";
return $sql;
}
echo update('table',1,array('f1'=>'v1','f2'=>'v2','f3'=>'v3','f4'=>'v4'));
Results would look like:
UPDATE table SET f1 = ?, f2 = ?, f3 = ?, f4 = ? WHERE userID = {ID}
so your ID is not the actual integer that I passed.
but if you changed your statement to be:
//some code
$sql = "UPDATE {$table} SET {$set} WHERE userID = {$userID}";
return $sql;
the result would be:
UPDATE table SET f1 = ?, f2 = ?, f3 = ?, f4 = ? WHERE userID = 1

Categories