I am having a problem when trying to access some functions in my mysql class. The function I have to connect to my database is as follows:
function connect_DB() {
$this->connection = mysql_connect($this->dbURL, $this->dbUser, $this->dbPass) or trigger_error('Connection failed: ' . mysql_error($connection), E_USER_ERROR);
$this->db = mysql_select_db($this->dbName, $this->connection) or trigger_error('Database selection failed: ' . mysql_error($db), E_USER_ERROR);
}
And I create my mysql object using this:
$dbConf = new StoreConfDB();
$dbConf->connect_DB();
Now I have a function that is supposed to grab all of the column names from a specified table. Code:
function get_db_columns($table) {
global $dbConf;
print_r($dbConf->get_db());
$result = $dbConf->query_DB_resource('SELECT * FROM ' . $table);
for($i = 0; $i < mysql_num_fields($result); $i++) {
$meta = mysql_fetch_field($result, $i);
$fields[] = $meta->name;
}
mysql_free_result($result);
return $fields;
}
But I am getting errors, such as:
Warning: mysql_query(): 5 is not a valid MySQL-Link resource
And if I try to remove the connection argument from the mysql_query call I get this:
Notice: Could not run query: No database selected in ...
Now I am running a debugger and have pinpointed what the problem is (I think). When the class works, the connection variable is set to: resource id='5' type='mysql link'
But when it calls the query function in the mysql class from inside the get_db_columns() function the connection variable is: resource id='5' type='Unknown'
So somehow the connection variable gets messed up even though both of the connection variables should be the same thing? (from $dbConf)? I've tested this function in 2 different places, it works in one and not the other! Please help!
#footy:
A print_r on $dbConf returns:
StoreConfDB Object ( [dbURL] => localhost [dbUser] => root [dbPass] => [dbName] => db1 [connection] => Resource id #5 [db] => 1 )
The query_DB_resource function:
function query_DB_resource($query) {
$sql_query = mysql_query($query) or trigger_error('Could not run query: '. mysql_error());
return $sql_query;
}
I noticed a couple of issues:
You have mysql_error($connection) and mysql_error($db). Those aren't valid parameters. You should be using $this->connection in both cases. That will give you better error output.
Normally, it doesn't terribly matter if you pass in a connection resource to mysql_query, but in this case you may want to: mysql_query($query, $this->connection). While I can't say that that is a guaranteed fix, it is likely to help.
Oh, and BTW -- every time someone uses global God kills a kitten. It's true. He told me. Either that or I had some bad nachos. (Yes, I'm a zealot on this issue. It's a good thing. trust me)
Regardless, if you need a variable inside of a function either pass it in, or make a property of a new class (make the StoreConfDB a Singleton? Make it accessible by factory method? You have a number of choices) -- avoiding global as a habit removes something nasty called unexpected side effects.
Related
I have cerb2 https://github.com/wgm/cerb2, it is an old ticketing system, it might looks weird but I have a a lot of knowledgeable information burried that I want to exploit.
It is basically a PHP5/Mysql software with the mysql_connect() constructor
The main issues are queries that stay null at all times. The code base lays on Class definition and pseudo-variable $this to call a query method from another Class.
function CER_HASH_QUEUES(&$parent)
{
global $session;
global $queue_access; e();
$this->db = (new cer_Database())->getInstance();
$this->_parent = &$parent;
if(empty($queue_access))
$this->queue_access = new CER_QUEUE_ACCESS();
else
$this->queue_access = $queue_access;
$sql = "SELECT q.queue_id, q.queue_name FROM queue q ORDER BY q.queue_name";
$res = $this->_db->query($sql);
That last line ($res =… ) is stopping code execution with:
Fatal error: Call to a member function query() on null in
The Query method called from cer_Database.class looks like this ( https://raw.githubusercontent.com/wgm/cerb2/stable/cerberus-api/database/cer_Database.class.php )
function query($sqlString,$return_assoc=true)
{
$config_db = (new cerConfiguration())->getInstance();
if($return_assoc === true) $this->db->SetFetchMode(ADODB_FETCH_ASSOC);
else $this->db->SetFetchMode(ADODB_FETCH_NUM);
$res = $this->db->Execute($sqlString);
if($cfg->settings["debug_mode"]) {
$time_end = microtime();
$query_time = microtime_diff($time_end,$time_start) * 1000; // convert secs to millisecs
echo "<b>[CERBERUS QUERY]:</b> " . $sqlString . " (Ran: <b>" . sprintf("%0.3f",$query_time) . "ms</b>)<hr>";
}
return $res;
}
That last method depends on a second Class cerConfiguration() which relies on import_config_db() to construct the mysql connector.
I am unable to figure out how to pass successfully my sql request following the design pattern carried by the relevant software here.
The databases and the tables are OK on their sides, and the following php script is returning the sql query OK.
<?php
include('includes/third_party/adodb/adodb.inc.php');
$db = ADONewConnection('mysql');
$db->Connect("localhost", "user", "pass", "databasename");
$rs = $db->Execute('SELECT q.queue_id, q.queue_name FROM queue q ORDER BY q.queue_name');
print "<pre>";
print_r($rs->GetRows());
print "</pre>";
I guess without the object oriented design pattern understanding used by PHP I am going to struggle to resuscitate that old piece of software.
Any help, ideas very welcome.
Yours.
Fatal error: Call to a member function query() on null in
You are likely to find further issues in your investigation, but the problem you're asking for help with is relatively simple.
The database property in the class is defined early on in your code as $this->db, but you are then trying to access it as $this->_db. The underscore makes it a different variable name, and thus it doesn't exist. So it is null, hence why you are getting Call to a member function query() on null.
To fix it, make sure you're using the same name for it throughout the class.
On my site, we connect to several databases and we have a database handling class that stores the handles in a static array. 99.9% of the time, everything seems to work fine, but every now and then the handle fails silently with no error, and no results are returned. I've been able to recreate the error, then recreate the connection using the following syntax:
$db = new PDO("dblib:host=" . DB_HOST . ";dbname=" . DB_DEFAULT, DB_USER, DB_PASS);
Then, it returns results correctly. However, I would prefer to use the same handle for all databases, though, to preserve the extra overhead associated with creating a new connection to a database... or, at least, recreate the connection a minimum number of times.
Is there anyway to evaluate the PDO object to figure out why it's failing, so I can, hopefully, figure out what's going on?
EDIT:
I've been looking into this further, and profiled the DB while this error is occuring. PDO stops passing queries to the server after it hits certain queries. (In this case, it was an insert query that tests to see if a record already exists, and if it doesn't then it inserts it).
Make no mistake, this is a horrible and inelligant solution to my problem, but I copied and pasted (and universalized) what resolved it. Basically, when no results are returned, I select 'foo' from the database, if it comes back with no results, I rebuild the connection and attempt to execute the query again. I'm not accepting this as an answer, because it's pretty bad. But this code should better illustrate the problem I am, attempting, to fix.
/**
* Executes a line of sql with PDO.
*
* #param string $sql
* #param array $params
* #param bool
* #return array
*/
function execute($sql, $params = array(), $retry = false) {
//this call gets the database handle that is stored in a static array, so we can reuse the same connection
$db = db::getDB($this->_database);
if (get_class($db) != 'PDO') {
error('database handle wasn\'t created succesfully...');
}
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {
$query = $db->prepare($sql);
$query->execute($params);
} catch (Exception $ex) {
var_dump($ex);
return false;
}
$results = $query->fetchALL(PDO::FETCH_ASSOC);
if(($retry == false) && (!$results)){
$newResults = $this->testAndFixResultsIfDBGoneAway($sql, $params);
if($newResults != false){
$results = $newResults;
}
}
return $results;
}
function isConnected(){
$db = db::getDB($this->_database);
$query = $db->prepare("SELECT 'foo' AS test");
$query->execute();
$results = $query->fetchAll(PDO::FETCH_ASSOC);
//we aren't connected if we get no response
if($results == array()){
return false;
}
//if there is a response, return true
return true;
}
function testAndFixResultsIfDBGoneAway($sql, $params){
if($this->isConnected()){
return false;
}
//recreates the db connection in the db connection class
db::connect($this->_database);
$results = $this->execute($sql, $params, true);
return $results;
}
I've noticed that my connection goes away, I can restart it successfully, then the next query can fail, and I can repeat this process many times, until the offending queries are done, then everything starts working normally, again.
I REALLY want to know how queries kill my database connection, so I can stop it from happening so I don't have to do a check after every query that returns no results.
Setting the PDO error-handling mode to use exceptions should make the cause immediately apparent so you don't have to hunt around and use a lot of debugging code.
$db = new PDO("dblib:host=" . DB_HOST . ";dbname=" . DB_DEFAULT, DB_USER, DB_PASS, [PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION]);
However, you'll have to update your error-handling code [failed prepares, failed queries] to be aware of the Exceptions.
I have come to the conclusion that using mysqli in an OO approach is better than a procedural approach. (Source: Why is object oriented PHP with mysqli better than the procedural approach?). But I'm not quite sure if what I am doing is really all that more efficient than what I was doing before.
I have a function that runs sql queries. This is what my block of code looked like:
Database connection:
function connectDB(){
$con = mysqli_connect(server, username, password, database);
return $con;
}
Query function:
function executeQuery($payload){
$con = connectDB;
$result = mysqli_query($con, $payload);
return $result;
}
As you can see, that's not very efficient because I'm creating a new database connection every time executeQuery is called. So I figured I'd try it using OOP.
Database connection (OOP):
function connectDB(){
$con = new mysqli(server, username, password, database);
return $con;
}
Database query (OOP):
function executeQuery($payload){
$con = connectDB();
$result = $con->query($payload);
return $result;
}
Now to me, it seems that I am obviously doing something wrong. Each time a query is called I am re-instantiating the mysqli class and I assume that mean's that I am making another database connection.
So how do I do this properly and efficiently?
So how do I do this properly and efficiently?
This really has nothing to do with using MySQLi in a procedural versus OOP way.
What this has to do with is the following line:
$con = connectDB();
This will recreate the database connection on every query. Which, as you noted, is not efficient.
There are many ways to solve this. For example:
Use the mysqli class directly.
Pass $con to executeQuery() (Dependency Injection)
Create a DB class with both connectDB() and executeQuery().
I usually use mysqli directly as I see no reason to wrap the native class. I create the connection object globally (in a config file) and use Dependency Injection when other objects/functions need it.
Although your procedural approach can be solved pretty easily
function connectDB(){
return mysqli_connect(server, username, password, database);
}
function executeQuery($payload){
static $con;
id (!$con)
{
$con = connectDB();
}
return $con->query($payload);
}
an OOP approach would be indeed better. I am not an OOP pro, but you can take a look at my approach which at least using encapsulation to hide all the dirty job inside and provide concise methods to get the data already in desired format like this:
// to get a username
$name = $db->getOne('SELECT name FROM table WHERE id = ?i',$_GET['id']);
// to get an array of data
$data = $db->getAll("SELECT * FROM ?n WHERE mod=?s LIMIT ?i",$table,$mod,$limit);
// to get an array indexed by id field
$data = $db->getInd('id','SELECT * FROM ?n WHERE id IN ?a','table', array(1,2));
// to get a single dimensional array
$ids = $db->getCol("SELECT id FROM tags WHERE tagname = ?s",$tag);
// a simple code for the complex INSERT
$data = array('offers_in' => $in, 'offers_out' => $out);
$sql = "INSERT INTO stats SET pid=?i,dt=CURDATE(),?u ON DUPLICATE KEY UPDATE ?u";
$db->query($sql,$pid,$data,$data);
As a solution for your exact problem : "You do not want to instantiate a new MySQL connection for each time a query is executed" ,
Well, we can think about the following :
You need to make your connection variable ($con) in GLOBAL scope, such that when accessed through any function you can grab THAT variable you set before, not instantiate a new one.
we can do this using keyword "global" , as following :
The connection function :
function &connectDB(){
global $con;
if(empty($con)) {
$con = new mysqli(server, username, password, database);
}
return $con;
}
And for more performance , we avoid cloning/copying the connection variable/resource by using reference function ( &connectDB ),
Query Execution function
Now we've set the connection function in a flexible way , to set the queryExecution function , we can use more than one solution :
First solution :
function executeQuery($payload){
$con = &connectDB(); // do not forget the () , it's good practice
return $con->query($payload);
}
In this solution we made use of "reference" , so the expression :
$con = &connectDB();
will set the variable $con as a reference/shortcut for global $con (i.e : just pointing to the global variable $con)
or
Second solution :
function executeQuery($payload){
global $con;
return $con->query($payload);
}
but for the second solution : Function "connectDB()" MUST be called at least once before any calling to "executeQuery()", In order to make sure that there a connection has been established with the database,
Keep in mind that, according to this solution , calling "connectDB()" more than once will not create more than one connection , once it is called , connection is created, if called again it will return the PREVIOUSLY created connection.
Hope it helps :)
by the way : stay with the OOP approach for database connection , it has much more benefits over the procedural ways,
& I recommend using PDO, it is much more portable.
OK, I see some similar questions to mine, but their examples all use PHP classes...mine does not. Maybe that's the problem? I shouldn't need classes because my site is exceedingly simple at this point in time.
Anyway, I'm trying to use PDO to connect to a MySQL db. I connect to the db fine in a file called config.php, and include this file in index.php with require_once().
I can successfully query the db from another file called process.php, but the problem is within a function within that file; it seems my DBO object is out of scope within that function.
Here are the relevant code snippets:
index.php
require_once('./lib/config.php');
config.php
// tested and connects fine
$pdo = new PDO('mysql:host=' . $hostname . ';dbname=' . $dbname, $username, $password, array(
PDO::ATTR_PERSISTENT => true
));
process.php
<?php
...
// can call $pdo fine in this file outside of functions
...
function authenticate($u, $p) {
// can't call $pdo in here, error says $pdo is non-object
$que = $pdo->query('select user_id, user_pass from users where user_name = \'' . $u . '\' limit 1');
...
}
?>
By the way, I'm using PDO because I was having similar trouble with mysqli, and am trying to get away from mysql, which is apparently depreciated and discouraged.
EDIT: I should have clarified first based on the number of responses I got on this matter: I did try to pass $pdo in as a param to the function, with no luck or change in the error message.
SOLUTION: OK, apparently the problem was that I needed to add require_once('config.php') in my process.php file as well. Not sure why (wouldn't it already be included when index.php was run first?). Then I was able to successfully pass $pdo in as a param to my function, and voila.
That's pretty basic PHP stuff. Variables inside functions are local variables unless you use the global keyword to load them. I suppose you want this:
function authenticate(PDO $pdo, $u, $p) {
$que = $pdo->query('select user_id, user_pass from users where user_name = \'' . $u . '\' limit 1');
//...
}
Edit: If PHP claims that $pdo is not an object, it's not an object, so it doesn't really matter how it's passed to the function. Inspect the variable right before you call authenticate():
var_dump($pdo);
Without the relevant code there's no way to say why. (Assuming it's true that new PDO succeeds.)
You need to pass the PDO object as a parameter to the authenticate() function:
function authenticate(PDO $pdo, $u, $p) {
// ..as in the question..
}
Oh and you should be using a place holder for that username in the query, not string concatenation which is prone to SQL injection attacks.
because $pdo has been declared outside of the function authenticate it isn't available inside it. You need to either pass $pdo in
function authenticate($u, $p, $pdo) {
$que = $pdo->query('...');
}
or declare it as global inside the function to be able to access it
function authenticate($u, $p) {
global $pdo;
$que = $pdo->query('...');
}
I want to be able to call a query function and based on the results they are put into an object that I can call outside the function. This is what I have:
<?php
function query($sql) {
global $r;
$database = "theatre";
$db_conn = pg_connect ("host=localhost port=5432
dbname=$database user=postgres");
if (!$db_conn): ?>
<h1>Failed connecting to postgres database <?php echo $database;?></h1>
<?php
exit;
endif;
$ep = pg_query ($db_conn, $sql);
while ($r = pg_fetch_object ($ep)) {
}
pg_free_result($ep);
pg_close($db_conn);
}
query('select * from test');
echo $r->fname;
I don't want to hardcode any column names into the function. So fname is an example but it will be dynamic based on the query. I have a while loop but not sure what to put in it.
Value returned by function pg_fetch_object is already an object, so no need to loop through results. Just return it:
return pg_fetch_object ($ep);
Then just use the returned results without referring to this awkward global variable $r.
$r = query('select * from test');
echo $r->fname;
Also, no need to free the resource with pg_free_result. It gets freed automatically at end of scope it exists within (that is current function in this case). Calling pg_close may also slow down your script, as the connection will have to be recreated anew next time you call the function. I'd suggest storing connection in local static variable and not closing it at all - it will be closed automatically.
function query($sql) {
$database = "theatre";
static $db_conn;
if (!$db_conn) {
$db_conn = pg_connect ("host=localhost port=5432 dbname=$database user=postgres");
}
...
As a long term solution I'd recommend researching subject of Object-Relational Mapping and learning one of available ORM-ish tools (i.e. Zend Db Table, Doctrine, Propel, or one of existing implementations of ActiveRecord pattern).