I have just finished installing anchor-cms but when I go to view the admin page or the homepage I get the following output
Unhandled Exception
Message:
Database Error:00' for column 'date' at row 1
SQL: INSERT INTO `sessions` (`id`, `date`, `data`) values (?, ?, ?)
Location:
/Library/WebServer/Documents/anchor/system/database/connection.php on line 128
Stack Trace:
#0 /Library/WebServer/Documents/anchor/system/database/query.php(254): System\Database\Connection->execute('INSERT INTO `se...', Array)
#1 /Library/WebServer/Documents/anchor/system/session/database.php(42): System\Database\Query->insert(Array)
#2 /Library/WebServer/Documents/anchor/system/session/payload.php(78): System\Session\Database->save(Array, Array, false)
#3 [internal function]: System\Session\Payload->save()
#4 /Library/WebServer/Documents/anchor/system/session.php(58): call_user_func_array(Array, Array)
#5 /Library/WebServer/Documents/anchor/system/bootstrap.php(124): System\Session::__callStatic('save', Array)
#6 /Library/WebServer/Documents/anchor/system/bootstrap.php(124): System\Session::save()
#7 /Library/WebServer/Documents/anchor/index.php(33): require('/Library/WebSer...')
#8 {main}
Here is my connection.php file
<?php namespace System\Database;
/**
* Nano
*
* Lightweight php framework
*
* #package nano
* #author k. wilson
* #link http://madebykieron.co.uk
*/
use System\Config;
use PDO, PDOStatement, PDOException, Exception;
class Connection {
public $pdo, $config, $queries = array();
public function __construct(PDO $pdo, $config) {
$this->pdo = $pdo;
$this->config = $config;
}
public function transaction($callback) {
$this->pdo->beginTransaction();
// After beginning the database transaction, we will call the callback
// so that it can do its database work. If an exception occurs we'll
// rollback the transaction and re-throw back to the developer.
try {
call_user_func($callback);
}
catch(PDOException $e) {
$this->pdo->rollBack();
throw $e;
}
$this->pdo->commit();
}
public function query($sql, $bindings = array()) {
$sql = trim($sql);
list($statement, $result) = $this->execute($sql, $bindings);
// The result we return depends on the type of query executed against the
// database. On SELECT clauses, we will return the result set, for update
// and deletes we will return the affected row count.
if(stripos($sql, 'select') === 0 or stripos($sql, 'show') === 0) {
return $this->fetch($statement, Config::get('database.fetch'));
}
elseif(stripos($sql, 'update') === 0 or stripos($sql, 'delete') === 0) {
return $statement->rowCount();
}
// For insert statements that use the "returning" clause, which is allowed
// by database systems such as Postgres, we need to actually return the
// real query result so the consumer can get the ID.
elseif (stripos($sql, 'insert') === 0 and stripos($sql, 'returning') !== false) {
return $this->fetch($statement, Config::get('database.fetch'));
}
else {
return $result;
}
}
public function first($sql, $bindings = array()) {
list($statement, $result) = $this->execute($sql, $bindings);
if($result) return $statement->fetch(Config::get('database.fetch'));
}
public function column($sql, $bindings = array()) {
list($statement, $result) = $this->execute($sql, $bindings);
if($result) return $statement->fetchColumn();
}
public function type($var) {
if(is_null($var)) {
return PDO::PARAM_NULL;
}
if(is_int($var)) {
return PDO::PARAM_INT;
}
if(is_bool($var)) {
return PDO::PARAM_BOOL;
}
return PDO::PARAM_STR;
}
public function execute($sql, $bindings = array()) {
// Each database operation is wrapped in a try / catch so we can wrap
// any database exceptions in our custom exception class, which will
// set the message to include the SQL and query bindings.
try {
$statement = $this->pdo->prepare($sql);
// bind paramaters by data type
// test key to see if we have to bump the index by one
$zerobased = (strpos(key($bindings), ':') === 0) ? false : true;
foreach($bindings as $index => $bind) {
$key = $zerobased ? ($index + 1) : $index;
$statement->bindValue($key, $bind, $this->type($bind));
}
$start = microtime(true);
$result = $statement->execute();
$this->queries[] = array($statement->queryString, $bindings);
}
// If an exception occurs, we'll pass it into our custom exception
// and set the message to include the SQL and query bindings so
// debugging is much easier on the developer.
catch(PDOException $exception) {
$message = explode(':', $exception->getMessage());
$error = '<strong>Database Error:</strong>' . end($message) . str_repeat("\n", 3) .
'<strong>SQL: </strong>' . $sql;
$exception = new Exception($error, 0, $exception);
throw $exception;
}
return array($statement, $result);
}
protected function fetch($statement, $style) {
// If the fetch style is "class", we'll hydrate an array of PHP
// stdClass objects as generic containers for the query rows,
// otherwise we'll just use the fetch style value.
if($style === PDO::FETCH_CLASS) {
return $statement->fetchAll(PDO::FETCH_CLASS, 'stdClass');
}
else {
return $statement->fetchAll($style);
}
}
}
Second Error
Database Error: Invalid datetime format: 1292 Incorrect datetime value: '2013-02-19T06:47:59+00:00' for column 'date' at row 1
Disclaimer: technically this is not an answer ;)
Apparently the anchor-CMS uses the ISO 8601-format to store (at least) the session-date, which is apparently not safe to use, especially when the "strict mode" is active!
I think for the moment it's probably the best to disable the strict mode of MySQL. If you don't "own" the SQL-Server you can disable the strict mode for a single session.
You can do that by editing system/database.php starting at line 51. Change the following lines from
if(version_compare(PHP_VERSION, '5.3.6', '<=')) {
$options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES ' . $config['charset'];
}
to
if(version_compare(PHP_VERSION, '5.3.6', '<=')) {
$options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES ' . $config['charset']
. ', sql_mode=\'ALLOW_INVALID_DATES\'';
} else {
$options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET sql_mode=\'ALLOW_INVALID_DATES\'';
}
I think that should work until anchor-CMS uses the correct dates (I probably make a pull-request for that this evening after work).
Related
I'm having a bit of trouble with the OOP CRUD method. My POST method is not retrieving or posting data to the DB. And I'm not sure where to look since it does not give any errors to display.
The logic above the form:
$id = $_GET['id'];
//Add Board
$b = new Board();
$userID = $_SESSION['id'];
$boards= $b->loadBoards($userID);
if(isset($_POST['addBoard'])){
try{
$sB = new Board();
$postID = 61;
$boardID = 1;
$sBoard = $sB->savePostToBoard($postID, $boardID);
} catch (Exception $e) {
$error = $e->getMessage();
}
}
This is the form:
<form method="post">
<div class="btn-group" data-toggle="buttons">
<?php foreach($boards as $key) : ?>
<label class="btn btn-primary active">
<input type="radio" name="option[]"value="
<?php echo $key['boardID'];?>">
<?php echo $key['boardTitle']; ?></label>
<?php endforeach ?>
<input class="btn btn-danger"type="submit" value="Toevoegen"
id="addBoard" name="addBoard">
</div>
</form>
And the class function:
public function getConnection() {
$conn = Db::getInstance();
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
return $conn;
}
private function getInputParameterDataType($value) {
$dataType = PDO::PARAM_STR;
if (is_int($value)) {
$dataType = PDO::PARAM_INT;
} elseif (is_bool($value)) {
$dataType = PDO::PARAM_BOOL;
}
return $dataType;
}
public function savePostToBoard($postID, $boardID)
{
$sql ="UPDATE board SET postID=:". $postID . " WHERE boardID=:boardID";
$statement = $this->getConnection()->prepare($sql);
$statement->bindValue(":boardID",$boardID, $this-
>getInputParameterDataType($boardID));
$statement->bindValue(":postID", $postid);
return $statement->execute();
}
Any feedback is highly appreciated, thanks for taking the time.
Kind regards
*) All your radio buttons have the same id attribute. It should be unique.
*) Give the id "addBoard" to the submit button.
*) Why do you use POST and GET?
$boardID = $_POST['option'];
$postID = $_GET['id'];
*) You missed the $ sign in statement->bindValue(":boardID",boardID);!
*) Posted data type should correspond to db data type. Use a third parameter in bindValue() to define the corresponding data type. And use a function:
private function getInputParameterDataType($value) {
$dataType = PDO::PARAM_STR;
if (is_int($value)) {
$dataType = PDO::PARAM_INT;
} elseif (is_bool($value)) {
$dataType = PDO::PARAM_BOOL;
}
return $dataType;
}
And then call like this:
$statement->bindValue(":boardID",$boardID, $this->getInputParameterDataType($boardID));
*) PDO::prepare() can throw a PDOException OR the value FALSE. So you should handle both cases. I wrote an answer about this: My answer for exception handling of prepare() & execute().
*) Have you made some changes until now? Works everything, or not yet?
Ok, I'll study it now.
Solutions for the new code:
*) Don't use semicolon ";" at the end of sql statements.
*) Update should have this form:
UPDATE [table-name] SET [col1]=:[col1],[col2]=:[col2] WHERE [PK-name]=:[PK-name]
*) For readability: pass the sql statement in a variable and use points to delimit used variables in it. Like:
$sql = "UPDATE board SET postID=:" . $postid . " WHERE boardID=:boardID"
$statement = $conn->prepare($sql);
*) As #teresko reminded you: you didn't pass $boardID to savePostToBoard():
$sBoard = $sB->savePostToBoard($postID, $boardID);
public function savePostToBoard($postID, $boardID) {...}
*) Use either $postID or $postid overall. Right now you are using both forms. Make a choice.
It should work now. Let me know.
Some recommandations:
*) Let your methods do only one thing (if possible). In your question, the connection creation doesn't belong in the method. If you call 20 methods which require a connection, then you have to write the same connection creation code in each of them. In your case, better define a getConnection() method in the Board class.
public function getConnection() {
$conn = Db::getInstance();
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
return $conn;
}
And now you have only this in your savePostOnBoard() by calling it:
public function savePostToBoard($postID) {
$statement = $this->getConnection()->prepare(...);
//...
}
*) In general, it's better to use the constructor for passing the variables used later by the methods. That's the role of the constructor: to initialize, e.g to give initial values to the object properties on creation. Example:
class Auto {
private $color;
private $doors;
private $airConditioning;
public __function __construct($color = 'blue', $doors = 3, $airConditioning = true) {
$this->color = $color;
$this->doors = $doors;
$this->airConditioning = $airConditioning;
}
}
$myAuto = new Auto('red', 4, false);
$hisAuto = new Auto('yellow', 8, true);
Oh, and always give easy-to-follow names to variables, functions, classes, etc. In your case, the upper phrase and this one would apply, for example, like this:
$board = new Board($boardID);
$boardUpdated = $board->update($postID);
See? Nicer names, more logical (following our real-world perception) arrangement of the arguments.
*) I would also recommend you to split your code in methods. This way you achieve a better reusability of code pieces and an elegant, easy-to-follow structure. Maybe something like this in analogy with your code:
public function savePostToBoard($postID, $boardID) {
$sql = "UPDATE board SET postID=:" . $postID . " WHERE boardID=:boardID";
$bindings = array(
':postID' => $postID,
':boardID' => $boardID
);
$statement = $this->execute($sql, $bindings);
return $statement->rowCount();
}
protected function execute($sql, array $bindings = array()) {
$statement = $this->prepareStatement($sql);
$this->bindInputParameters($statement, $bindings);
$this->executePreparedStatement($statement);
return $statement;
}
private function prepareStatement($sql) {
$statement = $this->getConnection()->prepare($sql);
return $statement;
}
private function bindInputParameters($statement, $bindings) {
foreach ($bindings as $key => $value) {
$statement->bindValue(
$this->getInputParameterName($key)
, $value
, $this->getInputParameterDataType($value)
);
}
return $this;
}
private function getInputParameterName($key) {
if (is_int($key)) {
return $key + 1;
}
$trimmed = ltrim($key, ':');
return ':' . $trimmed;
}
private function getInputParameterDataType($value) {
$dataType = PDO::PARAM_STR;
if (is_int($value)) {
$dataType = PDO::PARAM_INT;
} elseif (is_bool($value)) {
$dataType = PDO::PARAM_BOOL;
}
return $dataType;
}
private function executePreparedStatement($statement) {
$statement->execute();
return $this;
}
Good luck with your project!
You really should use these in your PDO instantiation:
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
Then you would have seen the syntax error in your query:
for some reason you have VALUES (:postID;) (notice the semicolon).
Also, you are not actually passing $boardID to the savePostToBoard() method at any point. You should add a second prarameter in that method.
As for your general application structure, you really should avoid using singletons to share the database connection and you should separate the domain logic from the persistence logic. Reading this post might be beneficial for you.
My SQL Statement works in phpMyAdmin, but when I use PHP to run it from my webpage, it does nothing.
My code is as below, which always returns true. I have solved this problem, but the primary problem is that the code does not delete the row.
// Delete Area
public function deleteArea($product_area_id){
$this->db->query("
DELETE
FROM product_area
WHERE product_area_id = :product_area_id
LIMIT 1
");
//bind
$this->db->bind(':product_area_id', $product_area_id);
//Execute
if($this->db->execute()){
return true;
} else {
return false;
}
}
My database class:
public function bind($param, $value, $type = null) {
if (is_null ( $type )) {
switch (true) {
case is_int ( $value ) :
$type = PDO::PARAM_INT;
break;
case is_bool ( $value ) :
$type = PDO::PARAM_BOOL;
break;
case is_null ( $value ) :
$type = PDO::PARAM_NULL;
break;
default :
$type = PDO::PARAM_STR;
}
}
$this->stmt->bindValue ( $param, $value, $type );
}
public function query($query) {
$this->stmt = $this->dbh->prepare($query);
}
public function execute(){
return $this->stmt->execute();
}
You're mixing PDO with the mysql_ functions. They do NOT work interoperably. What you need here is rowCount
$this->db->query("
DELETE
FROM product_area
WHERE product_area_id = :product_area_id
LIMIT 1
");
//bind
$this->db->bind(':product_area_id', $product_area_id);
//Execute
$this->db->execute();
if(this->db->rowCount() > 0){
A comment quote from the op (the last comment at the moment):
The primary problem is, it will not delete. even though the sql
statment is correct when tested directly in phpmyadmin. the secondary
problem is, it will also say true. even though it didn't delete.
Machavity solved the secondary problem. the primary remains. -
codenoob
TLDR: // Note: PDOStatement::execute .... Returns TRUE on success or FALSE on failure.
So that is why it returns 1 all the time for you. See the below for 2 files to test this with a change using rowCount() off of a PDOStatement object.
You said in comments that the primary problem remains. I have no problem using the below after sort of hand crafting a database class, because you did not offer one (you mention one). Note the use of try/catch blocks.
Frankly we don't know if you had any exceptions or how you are handling them, or whether or not you have error reporting activated. The below should survive any testing, assuming the pdo object can successfully return the rowCount() value.
Schema for testing:
create table product_area
( product_area_id int primary key,
theName varchar(100) not null
);
-- blockA begin
truncate product_area;
insert product_area (product_area_id,theName) values
(1,'Houston zone B'),(2,'Houston zone BH'),(20,'Houston zone Z');
-- blockA end
Test File:
For the test file, there are only a few lines of code near the top for testing in the section called "Mini test area"
<?php
// pdo_del_test_20160703.php
// related to answering http://stackoverflow.com/questions/38061597 at the moment
error_reporting(E_ALL);
ini_set("display_errors", 1);
include "myPDO_DB.php"; // Database class. The class name = myPDO_DB
// Mini test area BEGIN:
$a1=new aClass();
$ret=$a1->deleteArea(2);
echo "retValue=".$ret."<br>";
// Mini test area ... END
class aClass {
private $db=null;
public function __construct(){
echo "in constructor1<br>";
$this->db=new myPDO_DB();
echo "in constructor2<br>";
//$this->db=null;
echo "in constructor3<br>";
}
public function deleteArea($product_area_id){
$this->db->query("
DELETE
FROM product_area
WHERE product_area_id = :product_area_id
LIMIT 1
");
// Note: PDOStatement::execute .... Returns TRUE on success or FALSE on failure.
//
// so on a Delete call, it just says sure, OK, done with that (if no exception)
// It doesn't give feedback natively as to whether or not a row was actually deleted
//
//bind
$this->db->bind(':product_area_id', $product_area_id);
//Execute
// Don't forget to run schema `blockA` before testing (so that data is there)
if ($this->db->execute()) { // if this function returns anything other than TRUE you have exception problems
$pdo_rowcount=$this->db->stmt->rowCount(); // see http://php.net/manual/en/pdostatement.rowcount.php
// Depending on your systems ability to get pdo.rowCount() to work, consider the following
// Please see http://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_row-count
$this->db->query("select row_count()"); // from mysql "Information Functions". Based on prior mysql call.
$mysql_rowcount=$this->db->execute();
// but note, row_count() in mysql is not exactly a solution for the majority of systems out there.
// http://www.heidisql.com/forum.php?t=9791
// on mysql 5.5 and my 5.6.24, it returns 0
// so depending on your system, you will choose between $pdo_rowcount and $mysql_rowcount
return $pdo_rowcount; // your best bet, bust test it on your setup.
}
else {
// do something
return 0; // you would have already endured an exception though
}
}
}
?>
Database class:
<?php
// myPDO_DB.php
//
error_reporting(E_ALL);
ini_set("display_errors", 1);
include "db_connect_info.php"; // brings in the "defines" .. Shoot for a secure o/s vault in this file.
class myPDO_DB {
// will grab the stub from http://culttt.com/2012/10/01/roll-your-own-pdo-php-class/
//
// and then build your class into it (because you did not provide it)
//
// and then further improve it with try/catch blocks that were lacking
//
private $host = DB_HOST; // these were brought in with the include above. File not shown.
private $user = DB_USER;
private $pass = DB_PASS;
private $dbname = DB_NAME;
private $dbh;
public $stmt; // was made public to get into rowCount(); .... change this for your needs
private $error;
public function __construct(){
// Set DSN
$dsn = 'mysql:host=' . $this->host . ';dbname=' . $this->dbname;
// Set options
$options = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false
);
// Create a new PDO instanace
try{
$this->dbh = new PDO($dsn, $this->user, $this->pass, $options);
echo "Connect Ok<br>";
}
catch(PDOException $e){
$this->error = $e->getMessage();
}
}
public function bind($param, $value, $type = null) {
try {
if (is_null ( $type )) {
switch (true) {
case is_int ( $value ) :
$type = PDO::PARAM_INT;
break;
case is_bool ( $value ) :
$type = PDO::PARAM_BOOL;
break;
case is_null ( $value ) :
$type = PDO::PARAM_NULL;
break;
default :
$type = PDO::PARAM_STR;
}
}
$this->stmt->bindValue ( $param, $value, $type );
}
catch(PDOException $e){
$this->error = $e->getMessage();
throw $e;
}
}
public function query($query) {
try {
$this->stmt = $this->dbh->prepare($query);
}
catch(PDOException $e){
$this->error = $e->getMessage();
throw $e;
}
}
public function execute(){
try {
return $this->stmt->execute();
}
catch(PDOException $e){
$this->error = $e->getMessage();
throw $e;
}
}
}
Sorry I derped. the value I passed in was $Product_area_id and the one I used is $product_area_id. god.
I'm working for a company that uses this Mysqli php class to do mysql calls.
The problem is, the previous programmer wasn't great about preventing unbounded queries. So there are things dispersed throughout the code like the following:
$db -> where('id',$_POST['id']);
$db -> delete('table');
This code is supposed to only delete one record where id = $_POST['id']. However, if $_POST['id'] is empty, we've got problems. It then deletes the entire table. One solution to this problem would be to find all the places in the code where delete or update functions are called and then make sure that the where variable is actually set.
if(isset($_POST['id']) && $_POST['id']!=''){
$db -> where('id',$_POST['id']);
$db -> delete('table');
}
But, that would take a lot of work because I know there are about 200 instances in the code. I'm hoping there might be a way to alter the following 2 functions to prevent them from executing unbound queries in the first place. Any help is appreciated!!
/**
* Update query. Be sure to first call the "where" method.
*
* #param string $tableName The name of the database table to work with.
* #param array $tableData Array of data to update the desired row.
*
* #return boolean
*/
public function update($tableName, $tableData)
{
if ($this->isSubQuery)
return;
$this->_query = "UPDATE " . self::$_prefix . $tableName ." SET ";
$stmt = $this->_buildQuery (null, $tableData);
$status = $stmt->execute();
$this->reset();
$this->_stmtError = $stmt->error;
$this->count = $stmt->affected_rows;
return $status;
}
/**
* Delete query. Call the "where" method first.
*
* #param string $tableName The name of the database table to work with.
* #param integer $numRows The number of rows to delete.
*
* #return boolean Indicates success. 0 or 1.
*/
public function delete($tableName, $numRows = null)
{
if ($this->isSubQuery)
return;
$this->_query = "DELETE FROM " . self::$_prefix . $tableName;
$stmt = $this->_buildQuery($numRows);
$stmt->execute();
$this->_stmtError = $stmt->error;
$this->reset();
return ($stmt->affected_rows > 0);
}
public function where($whereProp, $whereValue = 'DBNULL', $operator = '=', $cond = 'AND')
{
// forkaround for an old operation api
if (is_array($whereValue) && ($key = key($whereValue)) != "0") {
$operator = $key;
$whereValue = $whereValue[$key];
}
if (count($this->_where) == 0) {
$cond = '';
}
$this->_where[] = array($cond, $whereProp, $operator, $whereValue);
return $this;
}
You should catch the bad value when it's being passed to the where function, not later. That way it's easier to follow stack traces.
public function where($whereProp, $whereValue = 'DBNULL', $operator = '=', $cond = 'AND')
{
if (is_null($whereValue) || trim($whereValue) == '') {
throw new Exception('Cannot pass null or empty string as a condition to MysqliDb::where')
}
// ...
}
You could check through the _where protected property array inside of the delete function too, but it's not good practice to silently fail a method by doing a simple return out of the function. If you insist, though:
public function delete($tableName, $numRows = null)
{
foreach ($this->_where as $w) {
if (is_null($w[3]) || trim($w[3]) == '') {
return;
// or alternatively throw new Exception('...')
}
}
// ...
}
What about returning if this (the where clause) is empty?
https://github.com/joshcam/PHP-MySQLi-Database-Class/blob/b3754d20bcebf07d65d552b2257696539a1cb144/MysqliDb.php#L1074
if (count($this->_where) == 0) {
return;
}
please update to a latest stable version. this issue is fixed there.
where('val', unset) wont count as no condition.
May wabsite has faced max_user_connections error
After contacting the website hosting provider they said I have to close the connection immediately after accessing the database using mysql_close()
My question is that how I can close the connection if my php file is :
*/
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../');
exit;
}
/**
* Indicates to the Q2A database layer that database connections are permitted fro this point forwards
* (before this point, some plugins may not have had a chance to override some database access functions).
*/
function qa_db_allow_connect()
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_db_allow_connect;
$qa_db_allow_connect=true;
}
/**
* Connect to the Q2A database, select the right database, optionally install the $failhandler (and call it if necessary).
* Uses mysqli as of Q2A 1.7.
*/
function qa_db_connect($failhandler=null)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_db_connection, $qa_db_fail_handler, $qa_db_allow_connect;
if (!$qa_db_allow_connect)
qa_fatal_error('It appears that a plugin is trying to access the database, but this is not allowed until Q2A initialization is complete.');
if (isset($failhandler))
$qa_db_fail_handler = $failhandler; // set this even if connection already opened
if ($qa_db_connection instanceof mysqli)
return;
// in mysqli we connect and select database in constructor
if (QA_PERSISTENT_CONN_DB)
$db = new mysqli('p:'.QA_FINAL_MYSQL_HOSTNAME, QA_FINAL_MYSQL_USERNAME, QA_FINAL_MYSQL_PASSWORD, QA_FINAL_MYSQL_DATABASE);
else
$db = new mysqli(QA_FINAL_MYSQL_HOSTNAME, QA_FINAL_MYSQL_USERNAME, QA_FINAL_MYSQL_PASSWORD, QA_FINAL_MYSQL_DATABASE);
// must use procedural `mysqli_connect_error` here prior to 5.2.9
$conn_error = mysqli_connect_error();
if ($conn_error)
qa_db_fail_error('connect', $db->connect_errno, $conn_error);
// From Q2A 1.5, we explicitly set the character encoding of the MySQL connection, instead of using lots of "SELECT BINARY col"-style queries.
// Testing showed that overhead is minimal, so this seems worth trading off against the benefit of more straightforward queries, especially
// for plugin developers.
if (!$db->set_charset('utf8'))
qa_db_fail_error('set_charset', $db->errno, $db->error);
qa_report_process_stage('db_connected');
$qa_db_connection=$db;
}
/**
* If a DB error occurs, call the installed fail handler (if any) otherwise report error and exit immediately.
*/
function qa_db_fail_error($type, $errno=null, $error=null, $query=null)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_db_fail_handler;
#error_log('PHP Question2Answer MySQL '.$type.' error '.$errno.': '.$error.(isset($query) ? (' - Query: '.$query) : ''));
if (function_exists($qa_db_fail_handler))
$qa_db_fail_handler($type, $errno, $error, $query);
else {
echo '<hr><font color="red">Database '.htmlspecialchars($type.' error '.$errno).'<p>'.nl2br(htmlspecialchars($error."\n\n".$query));
qa_exit('error');
}
}
/**
* Return the current connection to the Q2A database, connecting if necessary and $connect is true.
*/
function qa_db_connection($connect=true)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_db_connection;
if ($connect && !($qa_db_connection instanceof mysqli)) {
qa_db_connect();
if (!($qa_db_connection instanceof mysqli))
qa_fatal_error('Failed to connect to database');
}
return $qa_db_connection;
}
/**
* Disconnect from the Q2A database.
*/
function qa_db_disconnect()
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_db_connection;
if ($qa_db_connection instanceof mysqli) {
qa_report_process_stage('db_disconnect');
if (!QA_PERSISTENT_CONN_DB) {
if (!$qa_db_connection->close())
qa_fatal_error('Database disconnect failed');
}
$qa_db_connection=null;
}
}
/**
* Run the raw $query, call the global failure handler if necessary, otherwise return the result resource.
* If appropriate, also track the resources used by database queries, and the queries themselves, for performance debugging.
*/
function qa_db_query_raw($query)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
if (QA_DEBUG_PERFORMANCE) {
global $qa_usage;
// time the query
$oldtime = array_sum(explode(' ', microtime()));
$result = qa_db_query_execute($query);
$usedtime = array_sum(explode(' ', microtime())) - $oldtime;
// fetch counts
$gotrows = $gotcolumns = null;
if ($result instanceof mysqli_result) {
$gotrows = $result->num_rows;
$gotcolumns = $result->field_count;
}
$qa_usage->logDatabaseQuery($query, $usedtime, $gotrows, $gotcolumns);
}
else
$result = qa_db_query_execute($query);
// #error_log('Question2Answer MySQL query: '.$query);
if ($result === false) {
$db = qa_db_connection();
qa_db_fail_error('query', $db->errno, $db->error, $query);
}
return $result;
}
/**
* Lower-level function to execute a query, which automatically retries if there is a MySQL deadlock error.
*/
function qa_db_query_execute($query)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$db = qa_db_connection();
for ($attempt = 0; $attempt < 100; $attempt++) {
$result = $db->query($query);
if ($result === false && $db->errno == 1213)
usleep(10000); // deal with InnoDB deadlock errors by waiting 0.01s then retrying
else
break;
}
return $result;
}
/**
* Return $string escaped for use in queries to the Q2A database (to which a connection must have been made).
*/
function qa_db_escape_string($string)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$db = qa_db_connection();
return $db->real_escape_string($string);
}
/**
* Return $argument escaped for MySQL. Add quotes around it if $alwaysquote is true or it's not numeric.
* If $argument is an array, return a comma-separated list of escaped elements, with or without $arraybrackets.
*/
function qa_db_argument_to_mysql($argument, $alwaysquote, $arraybrackets=false)
{
if (is_array($argument)) {
$parts=array();
foreach ($argument as $subargument)
$parts[] = qa_db_argument_to_mysql($subargument, $alwaysquote, true);
if ($arraybrackets)
$result = '('.implode(',', $parts).')';
else
$result = implode(',', $parts);
}
elseif (isset($argument)) {
if ($alwaysquote || !is_numeric($argument))
$result = "'".qa_db_escape_string($argument)."'";
else
$result = qa_db_escape_string($argument);
}
else
$result = 'NULL';
return $result;
}
/**
* Return the full name (with prefix) of database table $rawname, usually if it used after a ^ symbol.
*/
function qa_db_add_table_prefix($rawname)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$prefix = QA_MYSQL_TABLE_PREFIX;
if (defined('QA_MYSQL_USERS_PREFIX')) {
switch (strtolower($rawname)) {
case 'users':
case 'userlogins':
case 'userprofile':
case 'userfields':
case 'messages':
case 'cookies':
case 'blobs':
case 'cache':
case 'userlogins_ibfk_1': // also special cases for constraint names
case 'userprofile_ibfk_1':
$prefix = QA_MYSQL_USERS_PREFIX;
break;
}
}
return $prefix.$rawname;
}
/**
* Callback function to add table prefixes, as used in qa_db_apply_sub().
*/
function qa_db_prefix_callback($matches)
{
return qa_db_add_table_prefix($matches[1]);
}
/**
* Substitute ^, $ and # symbols in $query. ^ symbols are replaced with the table prefix set in qa-config.php.
* $ and # symbols are replaced in order by the corresponding element in $arguments (if the element is an array,
* it is converted recursively into comma-separated list). Each element in $arguments is escaped.
* $ is replaced by the argument in quotes (even if it's a number), # only adds quotes if the argument is non-numeric.
* It's important to use $ when matching a textual column since MySQL won't use indexes to compare text against numbers.
*/
function qa_db_apply_sub($query, $arguments)
{
$query = preg_replace_callback('/\^([A-Za-z_0-9]+)/', 'qa_db_prefix_callback', $query);
if (!is_array($arguments))
return $query;
$countargs = count($arguments);
$offset = 0;
for ($argument = 0; $argument < $countargs; $argument++) {
$stringpos = strpos($query, '$', $offset);
$numberpos = strpos($query, '#', $offset);
if ($stringpos === false || ($numberpos !== false && $numberpos < $stringpos)) {
$alwaysquote = false;
$position = $numberpos;
}
else {
$alwaysquote = true;
$position = $stringpos;
}
if (!is_numeric($position))
qa_fatal_error('Insufficient parameters in query: '.$query);
$value = qa_db_argument_to_mysql($arguments[$argument], $alwaysquote);
$query = substr_replace($query, $value, $position, 1);
$offset = $position + strlen($value); // allows inserting strings which contain #/$ character
}
return $query;
}
/**
* Run $query after substituting ^, # and $ symbols, and return the result resource (or call fail handler).
*/
function qa_db_query_sub($query) // arguments for substitution retrieved using func_get_args()
{
$funcargs=func_get_args();
return qa_db_query_raw(qa_db_apply_sub($query, array_slice($funcargs, 1)));
}
/**
* Return the number of rows in $result. (Simple wrapper for mysqli_result::num_rows.)
*/
function qa_db_num_rows($result)
{
if ($result instanceof mysqli_result)
return $result->num_rows;
return 0;
}
/**
* Return the value of the auto-increment column for the last inserted row.
*/
function qa_db_last_insert_id()
{
$db = qa_db_connection();
return $db->insert_id;
}
/**
* Return the number of rows affected by the last query.
*/
function qa_db_affected_rows()
{
$db = qa_db_connection();
return $db->affected_rows;
}
/**
* For the previous INSERT ... ON DUPLICATE KEY UPDATE query, return whether an insert operation took place.
*/
function qa_db_insert_on_duplicate_inserted()
{
return (qa_db_affected_rows() == 1);
}
/**
* Return a random integer (as a string) for use in a BIGINT column.
* Actual limit is 18,446,744,073,709,551,615 - we aim for 18,446,743,999,999,999,999.
*/
function qa_db_random_bigint()
{
return sprintf('%d%06d%06d', mt_rand(1, 18446743), mt_rand(0, 999999), mt_rand(0, 999999));
}
/**
* Return an array of the names of all tables in the Q2A database, converted to lower case.
* No longer used by Q2A and shouldn't be needed.
*/
function qa_db_list_tables_lc()
{
return array_map('strtolower', qa_db_list_tables());
}
/**
* Return an array of the names of all tables in the Q2A database.
*/
function qa_db_list_tables()
{
return qa_db_read_all_values(qa_db_query_raw('SHOW TABLES'));
}
/*
The selectspec array can contain the elements below. See qa-db-selects.php for lots of examples.
I bet you have your code set to use persistent connections for MySQL. This can result in open but idle connections, maxing out whatever your (most likely shared) hosting provider allocates for you.
You have a QA_PERSISTENT_CONN_DB constant in your code that determines your connection mode. You will find it defined as true or false somewhere in a configuration file. (It's not defined in your code above). Ensure it is set to false and see if the problem is resolved. When persistent connections are off, the code above takes care of closing the connections. You don't need to add it in.
If you're interested in knowing more about persistent connections, this answer covers a lot of ground; and there's more in the manual:
Persistent connections are links that do not close when the execution of your script ends.
Next time, make sure you read through your code to see if the functionality is already there (in this case, it's inside function qa_db_disconnect()), and if it's simply a matter of configuration.
I am somewhat new to object oriented programming in php. And currently working on interface project. I have interne_nummer field which is coming from database. And i need to validate this field to check that there should not be duplicate interne_nummer field allowed but not getting idea of how to validate it. I have posted a small code where i want to validate for interne_nummer field as everything else is running fine in my code.
case 'interne_nummer':
{
/* validate against valid interne_nummer */
$this->interne_nummer = trim(strval($value));
break;
}
For example there are some float fields for which i am validating it as follows and its working fine:
case 'schlussrate':
{
/* #todo validate as float*/
try {
$this->{$name} = $this->getFloatValue($value);
} catch (Execption $e) {
$this->{$name} = '';
// Throw Exception to higher Level
throw new Exeption("Field '".$name."': ".$e->getMessage());
}
break;
}
protected function getFloatValue($value) {
if (is_numeric($value)) {
return floatval($value);
}
throw new \Exeption("Value not allowed '".$value."'");
}
You could give a function like this a try:
public function exists($detail, $table, $column, $value) {
$stmt = $this->mysqli->prepare("SELECT `$detail` FROM `$table` WHERE `$column` = ?");
$stmt->bind_param('s', $value);
$stmt->execute();
$stmt->store_result();
if($stmt->num_rows >= 1) {
return true;
} else {
return false;
}
}
So when you call the function it could look like this:
// "SELECT `interne_nummer` FROM `workers` WHERE `interne_nummer` = '$interne_nummer_val'"
if($class->exists('interne_nummer', 'workers', 'interne_nummer', '$interne_nummer_val')) {
echo 'Number exists';
} else {
echo 'Number doesn\'t exists';
}