I am developing a CMS with OOP PHP. It all was working well, but yesterday after adding a new feature it just crash. Now it retrieve this error when accessing any page:
Warning: mysqli::mysqli(): (HY000/1040): Too many connections in
/var/www/html/Projects/CMS4/lib/DB.php on line 15 Connection failed:
1040 Too many connections Fatal error: Call to a member function
render() on a non-object in
/var/www/html/Projects/CMS4/controllers/BaseController.php on line 33
I can handle the fatal error. But, about the "too many connections" error, I understand that I am doing too many calls to new mysqli() and that maybe I could solve it by just changing the max_connections on mysql, but is this the appropriate? Or I am doing something wrong in my code that I should improve? How can I reduce my connections? This is my DB class:
DB.php
<?php
namespace Core;
if ( !class_exists( 'DB' ) ) {
class DB {
private $cxn = null;
public function __construct() {
$this->connect();
}
private function connect() {
if ( $this->cxn !== null ) {
return;
}
$this->cxn = new \mysqli( DB_HOST, DB_USER, DB_PASS, DB_NAME );
if ( $this->cxn->connect_error ) {
die( "Connection failed: " . $this->cxn->connect_errno . ' ' . $this->cxn->connect_error );
}
}
private function create_db( $name ) {
$sql = "CREATE DATABASE IF NOT EXISTS $name";
if( !$this->cxn->query( $sql ) ){
die( "Error creating database: " . $this->cxn->errno . ' ' . $this->cxn->error );
}
}
private function select_db( $name ) {
if( !$this->cxn->select_db( $name ) ){
die( "Error selecting database: " . $this->cxn->errno . ' ' . $this->cxn->error );
}
}
/**
* Create a new table in the db with the specified columns
* #param array $tables
*/
public function create_tables( $tables ) {
$sql = '';
if ( !is_array( $tables ) ) $tables = array();
foreach ( $tables as $name => $columns ) {
$sql .= "CREATE TABLE IF NOT EXISTS $name ($columns);";
}
if( !$this->cxn->multi_query( $sql ) ){
die( "Error creating table: " . $this->cxn->errno .' '. $this->cxn->error );
}
}
/**
* Insert a row in table
* #param string $table
* #param array $data
* #param array $format
* #return boolean
*/
public function insert( $table, $data, $format ) {
if ( empty($table) || empty($data) ) {
return false;
}
//cast $data and $format to array
$data = (array) $data;
$format = (array) $format;
//Build format string
$format = $this->build_format($format);
//prepare data
list($fields, $placeholders, $values) = $this->prep_query($data);
//prepend $format in to $values
array_unshift($values, $format);
//prepare statements
if ( !( $stmt = $this->cxn->prepare("INSERT INTO {$table} ({$fields}) VALUES ({$placeholders})") ) ) {
echo "Error preparating the query: (" . $this->cxn->errno . ") " . $this->cxn->error;
}
//Dinamically binding
if ( !call_user_func_array(array($stmt, 'bind_param'), $this->ref_values($values)) ) {
echo "Error binding parameters: (" . $this->cxn->errno . ") " . $this->cxn->error;
}
//execute the query
if (!$stmt->execute()) {
echo "Error executing the insert query: (" . $this->cxn->errno . ") " . $this->cxn->error;
}
//Check for succesful insertion
if ( $stmt->affected_rows ) {
return $stmt->insert_id;
}
return false;
}
/**
* Update a row in a table
* #param string $table
* #param array $data
* #param string $format
* #param array $where
* #param string $where_format
* #return boolean
*/
public function update( $table, $data, $format, $where, $where_format ) {
if ( empty($table) || empty($data) ) {
return false;
}
//cast to array
$data = (array) $data;
$format = (array) $format;
$where_format = (array) $where_format;
//Build format string
$format = $this->build_format($format);
$where_format = $this->build_format($where_format);
$format .= $where_format;
//prepare data
list($fields, $placeholders, $values) = $this->prep_query($data, 'update');
list($where_clause, $where_values) = $this->prep_where($where);
//prepend $format onto $values
array_unshift($values, $format);
$values = array_merge($values, $where_values);
//prepare statements
if ( !( $stmt = $this->cxn->prepare("UPDATE {$table} SET {$placeholders} WHERE ({$where_clause})") ) ) {
echo "Error preparating the update query: (" . $this->cxn->errno . ") " . $this->cxn->error;
}
//bind params
if ( !call_user_func_array(array($stmt, 'bind_param'), $this->ref_values($values)) ) {
echo "Error binding parameters: (" . $this->cxn->errno . ") " . $this->cxn->error;
}
//execute
if (!$stmt->execute()) {
echo "Error executing the query: (" . $this->cxn->errno . ") " . $this->cxn->error;
}
//Check for succesful insertion
if ( $stmt->affected_rows ) {
return true;
}
return false;
}
/**
* Delete a row from a table
* #param string $table
* #param string|array $where
* #param string|array $where_format
* #return false
*/
public function delete( $table, $where = '', $where_format = '' ) {
if ( !is_array( $where ) ) {
$where = array( 'ID' => $where );
$where_format = 'i';
}
$where_format = (array) $where_format;
$where_format = $this->build_format($where_format);
//prepare data
list($where_clause, $where_values) = $this->prep_where($where);
//prepend $format onto $values
$values = $where_values;
array_unshift($values, $where_format);
//prepare statements
if ( !( $stmt = $this->cxn->prepare("DELETE FROM {$table} WHERE {$where_clause}") ) ) {
echo "Error preparating the delete query: (" . $this->cxn->errno . ") " . $this->cxn->error;
}
//bind params
if ( !call_user_func_array(array($stmt, 'bind_param'), $this->ref_values($values)) ) {
echo "Error binding parameters: (" . $this->cxn->errno . ") " . $this->cxn->error;
}
//execute
if (!$stmt->execute()) {
echo "Error executing the query: (" . $this->cxn->errno . ") " . $this->cxn->error;
}
//Check for succesful insertion
if ( $stmt->affected_rows ) {
return true;
}
return false;
}
/**
* Select a row from a table
* #param string $table
* #param string $where
* #param string $where_format
* #return array
*/
public function select( $table, $where = '', $where_format = '' ) {
if ( !is_array( $where ) ) {
$where = array( 'ID' => $where );
$where_format = 'i';
}
$where_format = (array) $where_format;
$where_format = $this->build_format($where_format);
//prepare data
list($where_clause, $where_values) = $this->prep_where($where);
//prepend $format onto $values
$values = $where_values;
array_unshift($values, $where_format);
//prepare statements
if ( !( $stmt = $this->cxn->prepare("SELECT * FROM {$table} WHERE {$where_clause}") ) ) {
echo "Error preparating the query: (" . $this->cxn->errno . ") " . $this->cxn->error;
}
//bind params
if ( !call_user_func_array(array($stmt, 'bind_param'), $this->ref_values($values)) ) {
echo "Error binding parameters: (" . $this->cxn->errno . ") " . $this->cxn->error;
}
//execute
if (!$stmt->execute()) {
echo "Error executing the query: (" . $this->cxn->errno . ") " . $this->cxn->error;
}
$results = $this->get_results($stmt);
if ( $results ) {
return $results;
} else {
throw new \Exception('Invalid query, no results founds.');
}
}
/**
* Select multiple row from a table
* #param string $table
* #param string $where
* #param string $where_format
* #return array
*/
public function select_array( $table, $where = '', $where_format = '' ) {
if ( !is_array( $where ) ) {
$where = array( 'ID' => $where );
$where_format = 'i';
}
$where_format = (array) $where_format;
$where_format = $this->build_format($where_format);
//prepare data
list($where_clause, $where_values) = $this->prep_where($where);
//prepend $format onto $values
$values = $where_values;
array_unshift($values, $where_format);
//prepare statements
if ( !( $stmt = $this->cxn->prepare("SELECT * FROM {$table} WHERE {$where_clause}") ) ) {
echo "Error preparating the query: (" . $this->cxn->errno . ") " . $this->cxn->error;
}
//bind params
if ( !call_user_func_array(array($stmt, 'bind_param'), $this->ref_values($values)) ) {
echo "Error binding parameters: (" . $this->cxn->errno . ") " . $this->cxn->error;
}
//execute
if (!$stmt->execute()) {
echo "Error executing the query: (" . $this->cxn->errno . ") " . $this->cxn->error;
}
$results = $this->get_results($stmt, 'array');
if ( $results ) {
return $results;
} else {
throw new \Exception('Invalid query, no results founds.');
}
}
/**
* Select all the rows from a table
* #param string $table
* #return array
*/
public function select_all( $table ) {
//prepare statements
if ( !( $stmt = $this->cxn->prepare("SELECT * FROM {$table}") ) ) {
echo "Error preparating the query: (" . $this->cxn->errno . ") " . $this->cxn->error;
}
//execute
if (!$stmt->execute()) {
echo "Error executing the query: (" . $this->cxn->errno . ") " . $this->cxn->error;
}
$results = $this->get_results($stmt, 'array');
if ( $results ) {
return $results;
} else {
throw new \Exception('Invalid query, no results founds.');
}
}
/**
* Get results from a query
* #param object $stmt
* #param string $type
* #return array
*/
private function get_results($stmt, $type = 'string') {
$stmt->store_result();
$meta = $stmt->result_metadata();
while ( $field = $meta->fetch_field() ) {
$params[] = &$row[$field->name];
}
call_user_func_array( array( $stmt, 'bind_result' ), $params );
$results = array();
while ( $stmt->fetch() ) {
foreach( $row as $key => $val ) {
$c[$key] = $val;
}
if ($type === 'array') {
$results[] = $c;
} else {
$results = $c;
}
}
if ( !empty( $results) ) return $results;
return false;
}
/**
* Build the format string for the query values
* #param array $format
* #return string
*/
private function build_format( $format ) {
$format = implode('', $format);
$format = str_replace('%', '', $format);
return $format;
}
/**
* Prepare data for a query
* #param array $data
* #param string $type
* #return array
*/
private function prep_query($data, $type = 'insert') {
//instantiate $fields and $placeholders for looping
$fields = '';
$placeholders = '';
$values = array();
//loop through $data and build $fields, $placeholders and $values
foreach ( $data as $field => $value ) {
$fields .= "{$field},";
$values[] = $value;
if ( $type == 'update' ) {
$placeholders .= $field . '=?,';
} else {
$placeholders .= '?,';
}
}
//normalize $fields and $placeholder for inserting
$fields = substr( $fields, 0, -1 );
$placeholders = substr( $placeholders, 0, -1 );
return array( $fields, $placeholders, $values );
}
/**
* Prepare where data for a query
* #param array $where
* #return array
*/
private function prep_where($where) {
$where_clause = '';
$where_values = array();
$count = 0;
foreach ($where as $field => $value) {
if ( $count > 0 ) {
$where_clause .= ' AND ';
}
$where_clause .= $field . '=?';
$where_values[] = $value;
$count++;
}
return array($where_clause, $where_values);
}
/**
* Create references for query values
* #param array $array
* #return array
*/
private function ref_values( $array ) {
$refs = array();
foreach ( $array as $key => $value ) {
$refs[$key] = &$array[$key];
}
return $refs;
}
/**
* Hash a password
* #param string $password
* #param string $nonce
* #return string
*/
public function hash_password($password, $nonce) {
$secureHash = hash_hmac('sha512', $password . $nonce, SITE_KEY);
return $secureHash;
}
/**
* Close the connection to database
*/
private function close() {
if ( !$this->cxn->close() ) {
die('Can\'t close the connection');
}
}
}
}
?>
Then I use these methods in my model classes, like in this example of the Page class:
Models/Page.php
public function insert( $data ) {
//prepare data
list($data, $cats, $tags) = $this->prep_data($data);
//insert the post
$post = $this->db->insert( $this->table, $data, 'sssss' );
//insert the post-cats relations
if ( isset( $cats ) ) {
foreach ( $cats as $cat ) {
$relation = array(
'post_id' => $post,
'term_id' => $cat,
);
$this->db->insert( 'term_relationships', $relation, 'ii' );
}
}
//insert the post-tags relations
if ( isset( $tags ) ) {
foreach ( $tags as $tag ) {
$relation = array(
'post_id' => $post,
'term_id' => $tag,
);
$this->db->insert( 'term_relationships', $relation, 'ii' );
}
}
return $post;
}
Here is the whole code: Github
It looks like you are using constants to connect to the DB - which means the class will only support connections with one set of parameters.
You could make the connection static and then you will only get one per script no matter how many times you instantiate the class. The PHP manual provides a good explanation of static usage.
This won't scale if you require a second connection to a different DB, but by then you'll be needing to re-factor the class a little anyway and shift the other constants.
if ( !class_exists( 'DB' ) ) {
class DB {
// make $cxn a static property shared between instances of this class
private static $cxn = null;
public function __construct() {
$this->connect();
}
private function connect() {
if(!is_null(self::$cxn)) {
// a connection has already been tried, and succeeded
return;
}
self::$cxn = new \mysqli( DB_HOST, DB_USER, DB_PASS, DB_NAME );
if ( self::$cxn->connect_error ) {
die( "Connection failed: " . self::$cxn->connect_errno . ' ' . self::$cxn->connect_error );
}
}
You can use this function for close connections
function __destruct()
{
//destroy open connection
}
Related
I'm coding my web app in PHP and when I run my script, I've got this error :
array to string conversion
Here is the script where the error fires.
public function insert($table, array $columns, array $values) {
$sql = "INSERT INTO " . $table .
" (" . implode(',' , $columns) . " )
VALUES (" . implode(',',
$values) . ")";
$this->request($sql);
}
Here is the request function :
public function request($sql, $param = null) {
if ($param == null) {
$query = Database::getInstance()->getDb()->query($sql);
}
else {
$query = Database::getInstance()->getDb()->prepare($sql);
$query->execute(array($param));
}
return $query;
}
N.B : I'm using my own MVC framework.
So, any advise or help would be apreciated ?
Regards
YT
I advice to modify your class methods for using parameterized queries:
class Test {
public function insert($table, array $columns, array $values) {
$sql = "INSERT INTO " . $table . " (" . implode(',' , $columns) . ")
VALUES (?" . str_repeat(",?", count($columns) - 1) . ");";
echo $sql;
$this->request($sql, $values);
}
public function request($sql, $param = null) {
if ($param == null) {
$query = Database::getInstance()->getDb()->query($sql);
}
else {
$query = Database::getInstance()->getDb()->prepare($sql);
$query->execute($param);
}
return $query;
}
};
$test = new Test();
$test->insert('tbl', ['id', 'name'], [1, 'first row']);
Online PHP test
It's far from perfect, but I'm trying to create a function to insert data into a SQL table, using MySQLI. I want to create a generic function, to insert different types of data in different databases. I have the following so far:
/**
* Add data to specified table. Data consist of column name as key, and value.
* Table is a string of the table to insert into.
* #param array $data
* #param string $table
* #return string
*/
private function insert( $data = array(), $table = null ){
foreach( $data as $key => $value ){
// Create arrays of separate keys and values
$keys[] = $key;
$values[] = $value;
// Get type of data
switch( gettype( $value ) ){
case "integer":
$types[] = "i";
break;
case "string":
$types[] = "s";
break;
default:
$types[] = "i";
break;
};
// for each variable, add a questionmark
$vars[] = "?";
}
// Create strings out of the data
$key = implode( ",", $keys );
$var = implode( ",", $vars );
$type = implode( "", $types );
$value = '"' . implode( '\", \"', $values ) . '"';
// prepare SQL statement
// var_dump( $sql ) = 'INSERT INTO table (var1,var2,var3) VALUES (?,?,?)'
$sql = "INSERT INTO " . $table . " (" . $key . ") VALUES (" . $var . ")";
// Prepare SQL insert
// $this->conn = new mysqli($this->server, $this->user, $this->pass, $this->name);
if( ! ( $stmt = $this->conn->prepare( $sql ) ) ) {
return "Preparing failed!: (" . $this->conn->errno . ") " . $this->conn->error;
}
// Bind parameters. THIS IS WHERE THE ISSUE IS!
if( ! $stmt->bind_param( $type, $values ) ) {
return "Binding failed! (" . $stmt->errno . ") " . $stmt->error;;
}
// Execute the statement
if( ! $stmt->execute() ){
return "Executing failed! (" . $stmt->errno . ") " . $stmt->error;;
}
}
The issue is at binding the parameters. I can't find a good way to bind them, as I've got multiple variables with values and keys, but they're all in array format, and bind_param requires a new variable for each
In short, I'm looking for a way to add an array of unspecified length into my SQL (in a secure way, ofcourse).
If you are using php 5.6+, you can use the ... operator to unpack an array.
In your example:
$stmt->bind_param( $type, ...$values )
See example #14 in the manual.
/* Bind parameters. Types: s = string, i = integer, d = double, b = blob */
$a_params = array();
$param_type = '';
$n = count($a_param_type);
for($i = 0; $i < $n; $i++) {
$param_type .= $a_param_type[$i];
}
/* with call_user_func_array, array params must be passed by reference */
$a_params[] = & $param_type;
for($i = 0; $i < $n; $i++) {
/* with call_user_func_array, array params must be passed by reference */
$a_params[] = & $a_bind_params[$i];
}
/* Prepare statement */
$stmt = $conn->prepare($sql);
if($stmt === false) {
trigger_error('Wrong SQL: ' . $sql . ' Error: ' . $conn->errno . ' ' . $conn->error, E_USER_ERROR);
}
/* use call_user_func_array, as $stmt->bind_param('s', $param); does not accept params array */
call_user_func_array(array($stmt, 'bind_param'), $a_params);
/* Execute statement */
$stmt->execute();
/* Fetch result to array */
$res = $stmt->get_result();
while($row = $res->fetch_array(MYSQLI_ASSOC)) {
array_push($a_data, $row);
}
Reference: http://www.pontikis.net/blog/dynamically-bind_param-array-mysqli
Hello so currently I am using a php pdo class for my database connection and here is the code
class db extends PDO {
private $error;
private $sql;
private $bind;
private $errorCallbackFunction;
private $errorMsgFormat;
public function __construct($dsn='', $user='', $passwd='') {
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
if (empty($dsn)) {
$dsn = 'mysql:host=localhost;dbname=db_disaster';
}
if (empty($user)) {
$user = 'root';
}
try {
parent::__construct($dsn, $user, $passwd, $options);
} catch (PDOException $e) {
$this->error = $e->getMessage();
}
}
private function debug() {
if(!empty($this->errorCallbackFunction)) {
$error = array("Error" => $this->error);
if(!empty($this->sql))
$error["SQL Statement"] = $this->sql;
if(!empty($this->bind))
$error["Bind Parameters"] = trim(print_r($this->bind, true));
$backtrace = debug_backtrace();
if(!empty($backtrace)) {
foreach($backtrace as $info) {
if($info["file"] != __FILE__)
$error["Backtrace"] = $info["file"] . " at line " . $info["line"];
}
}
$msg = "";
if($this->errorMsgFormat == "html") {
if(!empty($error["Bind Parameters"]))
$error["Bind Parameters"] = "<pre>" . $error["Bind Parameters"] . "</pre>";
$css = trim(file_get_contents(dirname(__FILE__) . "/error.css"));
$msg .= '<style type="text/css">' . "\n" . $css . "\n</style>";
$msg .= "\n" . '<div class="db-error">' . "\n\t<h3>SQL Error</h3>";
foreach($error as $key => $val)
$msg .= "\n\t<label>" . $key . ":</label>" . $val;
$msg .= "\n\t</div>\n</div>";
}
elseif($this->errorMsgFormat == "text") {
$msg .= "SQL Error\n" . str_repeat("-", 50);
foreach($error as $key => $val)
$msg .= "\n\n$key:\n$val";
}
$func = $this->errorCallbackFunction;
$func($msg);
}
}
public function delete($table, $where, $bind="") {
$sql = "DELETE FROM " . $table . " WHERE " . $where . ";";
$this->run($sql, $bind);
}
private function filter($table, $info) {
$driver = $this->getAttribute(PDO::ATTR_DRIVER_NAME);
if($driver == 'sqlite') {
$sql = "PRAGMA table_info('" . $table . "');";
$key = "name";
}
elseif($driver == 'mysql') {
$sql = "DESCRIBE " . $table . ";";
$key = "Field";
}
else {
$sql = "SELECT column_name FROM information_schema.columns WHERE table_name = '" . $table . "';";
$key = "column_name";
}
if(false !== ($list = $this->run($sql))) {
$fields = array();
foreach($list as $record)
$fields[] = $record[$key];
return array_values(array_intersect($fields, array_keys($info)));
}
return array();
}
private function cleanup($bind) {
if(!is_array($bind)) {
if(!empty($bind))
$bind = array($bind);
else
$bind = array();
}
return $bind;
}
public function insert($table, $info) {
$fields = $this->filter($table, $info);
$sql = "INSERT INTO " . $table . " (" . implode($fields, ", ") . ") VALUES (:" . implode($fields, ", :") . ");";
$bind = array();
foreach($fields as $field)
$bind[":$field"] = $info[$field];
return $this->run($sql, $bind);
}
public function run($sql, $bind="") {
$this->sql = trim($sql);
$this->bind = $this->cleanup($bind);
$this->error = "";
try {
$pdostmt = $this->prepare($this->sql);
if($pdostmt->execute($this->bind) !== false) {
if(preg_match("/^(" . implode("|", array("select", "describe", "pragma")) . ") /i", $this->sql))
return $pdostmt->fetchAll(PDO::FETCH_ASSOC);
elseif(preg_match("/^(" . implode("|", array("delete", "insert", "update")) . ") /i", $this->sql))
return $pdostmt->rowCount();
}
} catch (PDOException $e) {
$this->error = $e->getMessage();
$this->debug();
return false;
}
}
public function select($table, $where="", $bind="", $fields="*") {
$sql = "SELECT " . $fields . " FROM " . $table;
if(!empty($where))
$sql .= " WHERE " . $where;
$sql .= ";";
return $this->run($sql, $bind);
}
public function setErrorCallbackFunction($errorCallbackFunction, $errorMsgFormat="html") {
//Variable functions for won't work with language constructs such as echo and print, so these are replaced with print_r.
if(in_array(strtolower($errorCallbackFunction), array("echo", "print")))
$errorCallbackFunction = "print_r";
if(function_exists($errorCallbackFunction)) {
$this->errorCallbackFunction = $errorCallbackFunction;
if(!in_array(strtolower($errorMsgFormat), array("html", "text")))
$errorMsgFormat = "html";
$this->errorMsgFormat = $errorMsgFormat;
}
}
public function update($table, $info, $where, $bind="") {
$fields = $this->filter($table, $info);
$fieldSize = sizeof($fields);
$sql = "UPDATE " . $table . " SET ";
for($f = 0; $f < $fieldSize; ++$f) {
if($f > 0)
$sql .= ", ";
$sql .= $fields[$f] . " = :update_" . $fields[$f];
}
$sql .= " WHERE " . $where . ";";
$bind = $this->cleanup($bind);
foreach($fields as $field)
$bind[":update_$field"] = $info[$field];
return $this->run($sql, $bind);
}
}
And I am also using Smarty template engine for me to separate my presentation with the application code. So I am now doing a CRUD, and in my edit.php this what it looks like
require_once('header.php');
include('class.db.php');
$db = new db();
$id = $_GET['id'];
$bind = array(
":id" => $id
);
$results = $db->select("Contacts", "ContactID = :id", $bind);
$app->assign('contact', $results);
$app->display('edit.tpl');
My problem is I am still using foreach to output data/s in the textbox in my edit.tpl
{include file="header.tpl" title="Edit Contact"}
{foreach $contact as $r}
<form action="edit.php" method="POST">
Name: <input type="text" name="txt_name" value="{$r.ContactName}"> <br />
Contact: <input type="text" name="txt_contact" value="{$r.ContactNumber}"> <br />
<input type="submit" name="edit_btn" value="Edit Contact">
</form>
{/foreach}
{include file="footer.tpl"}
Is there a better way of doing this? I just want to use foreach in displaying all data, not by just one data from my id
The thing is your db class 'select' method always makes use of PDO fetchAll, if your action is based on a single row only then why not pass just the one result to the template?
$results = $db->select("Contacts", "ContactID = :id", $bind);
if (empty($results)) {
throw new Exception("No contact found");
}
$app->assign('contact', $results[0]); // only pass the one result to the template
$app->display('edit.tpl');
And in the template file, you can simply remove the foreach and since the result is guaranteed to be there already, no need for any checking:
{include file="header.tpl" title="Edit Contact"}
<form action="edit.php" method="POST">
Name: <input type="text" name="txt_name" value="{$contact.ContactName}"> <br />
Contact: <input type="text" name="txt_contact" value="{$contact.ContactNumber}"> <br />
<input type="submit" name="edit_btn" value="Edit Contact">
</form>
{include file="footer.tpl"}
As the title says, I'm having problems trying to use a pre-made wrapper class for PDO prepared statements. I'm not able to insert more than 5 values at a time in one row, this is the first time I have ever encountered this error in my programing days. Here is the class source code, and first attempt at implementation;
class db extends PDO {
private $error;
private $sql;
private $bind;
private $errorCallbackFunction;
private $errorMsgFormat;
public function __construct($dsn, $user="", $passwd="") {
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
try {
parent::__construct($dsn, $user, $passwd, $options);
} catch (PDOException $e) {
$this->error = $e->getMessage();
}
}
private function debug() {
if(!empty($this->errorCallbackFunction)) {
$error = array("Error" => $this->error);
if(!empty($this->sql))
$error["SQL Statement"] = $this->sql;
if(!empty($this->bind))
$error["Bind Parameters"] = trim(print_r($this->bind, true));
$backtrace = debug_backtrace();
if(!empty($backtrace)) {
foreach($backtrace as $info) {
if($info["file"] != __FILE__)
$error["Backtrace"] = $info["file"] . " at line " . $info["line"];
}
}
$msg = "";
if($this->errorMsgFormat == "html") {
if(!empty($error["Bind Parameters"]))
$error["Bind Parameters"] = "<pre>" . $error["Bind Parameters"] . "</pre>";
$css = trim(file_get_contents(dirname(__FILE__) . "/error.css"));
$msg .= '<style type="text/css">' . "\n" . $css . "\n</style>";
$msg .= "\n" . '<div class="db-error">' . "\n\t<h3>SQL Error</h3>";
foreach($error as $key => $val)
$msg .= "\n\t<label>" . $key . ":</label>" . $val;
$msg .= "\n\t</div>\n</div>";
}
elseif($this->errorMsgFormat == "text") {
$msg .= "SQL Error\n" . str_repeat("-", 50);
foreach($error as $key => $val)
$msg .= "\n\n$key:\n$val";
}
$func = $this->errorCallbackFunction;
$func($msg);
}
}
public function delete($table, $where, $bind="") {
$sql = "DELETE FROM " . $table . " WHERE " . $where . ";";
$this->run($sql, $bind);
}
private function filter($table, $info) {
$driver = $this->getAttribute(PDO::ATTR_DRIVER_NAME);
if($driver == 'sqlite') {
$sql = "PRAGMA table_info('" . $table . "');";
$key = "name";
}
elseif($driver == 'mysql') {
$sql = "DESCRIBE " . $table . ";";
$key = "Field";
}
else {
$sql = "SELECT column_name FROM information_schema.columns WHERE table_name = '" . $table . "';";
$key = "column_name";
}
if(false !== ($list = $this->run($sql))) {
$fields = array();
foreach($list as $record)
$fields[] = $record[$key];
return array_values(array_intersect($fields, array_keys($info)));
}
return array();
}
private function cleanup($bind) {
if(!is_array($bind)) {
if(!empty($bind))
$bind = array($bind);
else
$bind = array();
}
return $bind;
}
public function insert($table, $info) {
$fields = $this->filter($table, $info);
$sql = "INSERT INTO " . $table . " (" . implode($fields, ", ") . ") VALUES (:" . implode($fields, ", :") . ");";
$bind = array();
foreach($fields as $field)
$bind[":$field"] = $info[$field];
return $this->run($sql, $bind);
}
public function run($sql, $bind="") {
$this->sql = trim($sql);
$this->bind = $this->cleanup($bind);
$this->error = "";
try {
$pdostmt = $this->prepare($this->sql);
if($pdostmt->execute($this->bind) !== false) {
if(preg_match("/^(" . implode("|", array("select", "describe", "pragma")) . ") /i", $this->sql))
return $pdostmt->fetchAll(PDO::FETCH_ASSOC);
elseif(preg_match("/^(" . implode("|", array("delete", "insert", "update")) . ") /i", $this->sql))
return $pdostmt->rowCount();
}
} catch (PDOException $e) {
$this->error = $e->getMessage();
$this->debug();
return false;
}
}
public function select($table, $where="", $bind="", $fields="*") {
$sql = "SELECT " . $fields . " FROM " . $table;
if(!empty($where))
$sql .= " WHERE " . $where;
$sql .= ";";
return $this->run($sql, $bind);
}
public function setErrorCallbackFunction($errorCallbackFunction, $errorMsgFormat="html") {
//Variable functions for won't work with language constructs such as echo and print, so these are replaced with print_r.
if(in_array(strtolower($errorCallbackFunction), array("echo", "print")))
$errorCallbackFunction = "print_r";
if(function_exists($errorCallbackFunction)) {
$this->errorCallbackFunction = $errorCallbackFunction;
if(!in_array(strtolower($errorMsgFormat), array("html", "text")))
$errorMsgFormat = "html";
$this->errorMsgFormat = $errorMsgFormat;
}
}
public function update($table, $info, $where, $bind="") {
$fields = $this->filter($table, $info);
$fieldSize = sizeof($fields);
$sql = "UPDATE " . $table . " SET ";
for($f = 0; $f < $fieldSize; ++$f) {
if($f > 0)
$sql .= ", ";
$sql .= $fields[$f] . " = :update_" . $fields[$f];
}
$sql .= " WHERE " . $where . ";";
$bind = $this->cleanup($bind);
foreach($fields as $field)
$bind[":update_$field"] = $info[$field];
return $this->run($sql, $bind);
}
}
$db = new db("mysql:host=xxxx;dbname=xxxx", 'xxxxx', "xxxxx");
$insert = array(
"network" => "High",
"to" => "High",
"from" => "High",
"headerType" => "High",
"date" => "High",
"priority" => "High",
"content" => "High"
);
$db->insert("email_queue", $insert);
Any help would be appropriated!
I've tested this wrapper and it works with 6-fields inserts. Proof:
<?php
ini_set('display_errors', true);
require_once __DIR__.'/class.db.php';
$db = new db('mysql:host=127.0.0.1;dbname=test', 'root', '');
$db->setErrorCallbackFunction('echo', 'text');
$createTableQuery = <<<SQL
CREATE TABLE IF NOT EXISTS `q17224744` (
`name` varchar(100) NOT NULL,
`lastname` varchar(100) NOT NULL DEFAULT '',
`gender` char(1) NOT NULL DEFAULT '',
`birthday` date NOT NULL,
`city` varchar(100) NOT NULL DEFAULT '',
`country` char(2) NOT NULL DEFAULT ''
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SQL;
$db->run($createTableQuery);
$db->insert('q17224744', array(
'name' => 'John',
'lastname' => 'Doe',
'gender' => 'm',
'birthday' => '1971-12-03',
'city' => 'London',
'country' => 'GB',
));
I assume your problem is about incorrect values in your insert. Wrapper lib wont say you about errors if you not set error callback function. Try to enable debug output with setErrorCallbackFunction function as in example above.
P.S. This wrapper has counterintuitive behavior - by default it says nothing when exception coming. It would be much better by default throw exception and silent only when programmer set exception handler.
Every time I call a query with my class for select * from table where blank=blank it always comes up "NULL" on a var_dump for the results at the end of the class. I'm still stuck on this and don't know why it's doing it, but it sends no responses for sure, because I'm getting nothing back.
mysqli.class.php
<?php
class DATABASE
{
//set up variables only for this class
private $db_host;
private $db_user;
private $db_pass;
private $db_name;
private $connection;
private $paramaters = array();
private $results = array();
private $numrows;
//call connection on call of class
public function __construct($db_host, $db_user, $db_pass, $db_name)
{
$this->host = $db_host;
$this->user = $db_user;
$this->pass = $db_pass;
$this->name = $db_name;
$this->mysqli = new mysqli($this->host, $this->user, $this->pass, $this->name) or die('There was a problem connecting to the database! Error #: '. $this->mysqli->connect_errno);
}
//close mysqli connection on class close
public function __destruct()
{
$this->mysqli->close();
}
//query
public function select($fields, $table, $where, $whereVal, $type, $orderByVal, $ASDESC, $limitVal, $sets, $setVal)
{
switch($type)
{
case "regular":
if ($where == null)
{
$queryPre = "SELECT " . $fields . " FROM " . $table;
$querySuff = "";
} else {
$queryPre = "SELECT " . $fields . " FROM " . $table;
$querySuff = " WHERE " . $where . " = ?";
}
break;
case "orderByLimit":
$queryPre = "SELECT " . $fields . " FROM " . $table;
$querySuff = " ORDER BY " . $orderByVal . " " . $ASDESC . " LIMIT " . $limitVal;
break;
case "update":
if ($where == null)
{
$queryPre = "UPDATE " . $table;
//need for loop for multiple sets, check for is_array and do multiple if so.
$querySuff = " SET " . $sets . " = " . $setVal;
} else {
$queryPre = "UPDATE " . $table;
//need for loop for multiple sets, check for is_array and do multiple if so.
$querySuff = " SET " . $sets . " = " . $setVal . " WHERE " . $where . " = ?";
}
break;
case "insert":
if ($sets == null)
{
$queryPre = "INSERT INTO " . $table;
$querySuff = " VALUES(" . setVal . ")";
} else {
$queryPre = "INSERT INTO " . $table . " (" . $sets . ")";
$querySuff = " VALUES(" . setVal . ")";
}
case "delete":
if ($where == null)
{
$queryPre = "DELETE FROM " . $table;
$querySuff = "";
} else {
$queryPre = "DELETE FROM " . $table;
$querySuff = " WHERE " . $where . " = ?";
}
}
//$sql = $queryPre . "" . $querySuff;
//var_dump($sql);
//exit;
$stmt = $this->mysqli->prepare($queryPre . "" . $querySuff) or die('There was a problem preparing the Query! Error#: '. $this->mysqli->errno);
if ($whereVal == null)
{
$stmt = $this->bindVars($stmt,$setVal);
} else {
$stmt = $this->bindVars($stmt,$whereVal);
}
$stmt->execute();
$meta = $stmt->result_metadata();
while ($field = $meta->fetch_field())
{
$parameters[] = &$row[$field->name];
}
call_user_func_array(array($stmt, 'bind_result'), $parameters);
while ($stmt->fetch())
{
$x = array();
foreach($row as $key => $val)
{
$x[$key] = $val;
}
$results[] = $x;
}
$stmt->close();
//var_dump($results);
if ($results == "" || $results == NULL)
{
return null;
} else {
return $results;
}
}
private function bindVars($stmt,$params)
{
if ($params != null)
{
$types = '';
//initial sting with types
if (is_array($params))
{
foreach($params as $param)
{
//for each element, determine type and add
if(is_int($param))
{
$types .= 'i'; //integer
} elseif (is_float($param))
{
$types .= 'd'; //double
} elseif (is_string($param))
{
$types .= 's'; //string
} else {
$types .= 'b'; //blob and unknown
}
}
} else {
if (is_int($params))
{
$types = 'i';
} elseif (is_float($params))
{
$types = 'd';
} elseif (is_string($params))
{
$types = 's';
} else {
$types = 'b';
}
}
$bind_names[] = $types;
if (is_array($params))
{
//go through incoming params and added em to array
for ($i=0; $i<count($params);$i++)
{
//give them an arbitrary name
$bind_name = 'bind' . $i;
//add the parameter to the variable variable
$$bind_name = $params[$i];
//now associate the variable as an element in an array
$bind_names[] = &$$bind_name;
}
} else {
$int0 = 0;
$bind_name = 'bind' . $int0;
$$bind_name = $params;
$bind_names[] = &$$bind_name;
}
call_user_func_array(array($stmt,'bind_param'),$bind_names);
}
return $stmt; //return the bound statement
}
}
?>
example to call and check fields - process_availability.php:
<?php
//require necessary files
require('../config/dbconfig.php');
include('../classes/mysqli.class.php');
//initiate connection
$mysqli = new DATABASE($db_host,$db_user,$db_pass,$db_name);
//take type of check
$checktype = $_POST['type'];
//check the user name
if ($checktype == "username") {
//change post to variable
$username = $_POST['username'];
//check if user name is empty
if ($username == "") {
$validuser = array("empty", "false");
echo implode(',', $validuser);
exit;
}
//if user name is more characters than 30
if (strlen($username) > 30) {
$validuser = array("max", "false");
echo implode(',', $validuser);
exit;
}
//search for same user name in database
$resultsU = $mysqli->select('*','users','username',$username,'regular',null,null,null,null,null);
//var_dump($resultsU);
if (is_array($resultsU))
{
var_dump($resultsU);
foreach($resultsU as $rowU)
{
//return results
if($rowU['username'] == "" || $rowU['username'] == NULL)
{
//user name is blank
$validuser = array("yes", "true");
echo implode(',', $validuser);
exit;
}
else {
//username is not blank, so it's taken
$validuser = array("no", "false");
echo implode(',', $validuser);
exit;
}
}
}
}
And just to show what I'm actually doing with the information, here is a PART of the java (just handles username mostly, there is a ton more for email, ect not included):
fiddle
And, of coarse, the link to the page: page link
I've been fixing other things on here, and on a technicality it works. I get a response if there IS something in the database that matches the username i type, but if there is no match, for some reason it doesn't respond at all.....
Specifically...right at the bottom of the 2nd to last function in the class:
$stmt->close();
//var_dump($results);
if ($results == "" || $results == NULL)
{
return null;
} else {
return $results;
}
When you are returning no results to the client, you need to indicate to the client that this is what you have done, and the code shown above simply outputs nothing in this case. While it is easily possible to handle this empty response correctly on the client side a better solution would be to do one of the following:
If you need the data from the result, json_encode() the results before sending them back to the client. This would mean that if the were no results you would return an empty array, but it would still be valid JSON and crucially you can easily check whether the result array is empty on the client side using result.length.
If you don't actually need the result data and all you need is to determine whether there were any results, you can simply return a 1 or a 0. This kind of boolean response takes minimal bandwidth and minimal processing, and the best thing about it is all you need to do is evaluate it as a boolean on the client side - i.e. if (result) { /* do stuff */ }