I'm a .Net developer that have taken over a PHP project. This project has a database layer that looks like this:
<?php
class DatabaseManager {
private static $connection;
const host = "projectname.mysql.hostname.se";
const database = "databaseName";
const username = "userName";
const password = "password";
public function __construct()
{
}
public function instance($_host = null, $_username = null, $_password = null)
{
if(!self::$connection)
{
if(!$_host || !$_username || !$_password)
{
$host = self::host;
$username = self::username;
$password = self::password;
}else{
$host = $_host;
$username = $_username;
$password = $_password;
}
self::$connection = mysql_connect($host, $username, $password);
$this->setDatabase();
}
return self::$connection;
}
public function setDatabase($_database = null)
{
if(!$_database)
{
$database = self::database;
}else{
$database = $_database;
}
$connection = $this->instance();
mysql_select_db($database, $connection) or die(mysql_error());
}
} ?>
I have written a php file that uses this layer but after a while i got these mysql errors implying i didn't close my connections which i hadn't. I try to close them but know i get other weird errors like system error: 111. Very simplyfied my php file looks like this:
<?php
$return = new stdClass();
$uid = '9999999999999';
$return->{"myUid"} = $uid;
$dm = new DatabaseManager();
$dmInstance = $dm->instance();
/* MY CLICKS */
$sql = sprintf("SELECT count(*) as myClicks FROM clicks2011, users2011 WHERE clicks2011.uid = users2011.uid AND users2011.uid = %s AND DATEDIFF(DATE(at), '%s') = 0 AND exclude = 0", mysql_real_escape_string($uid), mysql_real_escape_string($selectedDay));
$result = mysql_query($sql, $dmInstance) or die (mysql_error());
$dbResult = mysql_fetch_row($result);
$return->{"myClicks"} = $dbResult[0];
mysql_close($dmInstance);
echo json_encode($return); ?>
Okay, I'm going to post this as an answer because I think one (possibly both) of these things will help you.
First: You don't need to manually close your MySQL connections. Unless you have set them up so that they persist, they will close automatically. I would avoid doing that unless you determine that every other problem is NOT the solution.
In addition, I would switch to using prepared statements. It's more secure, and pretty future-proof. I prefer PHP's PDO over mysqli, but that's up to you.
If you'd like to look over an example of a simple PDO object to take the many lines out of creating prepared statements and connections and getting results, you can look at my homebrew solution.
Second: "System Error 111" is a MySQL error. From what I've read, it appears that this error typically occurs when you are using PHP and MySQL on the same server, but telling PHP to connect to MySQL via an IP address. Switch your $host variable to 'localhost'. It is likely that this will solve that error.
The problem here is you're calling mysql_close and not specifying a valid mysql connection resource object. You're, instead, trying to close an instance of the DatabaseManager object.
You'll probably want to run mysql_close(DatabaseManager::connection); which is where the DatabaseManager is storing the resource object.
Additionally, I'd personally recommend you learn PDO or use the mysqli drivers. In future releases of PHP the built in mysql functions will be moved into E_DEPRECATED
Try implement __destrcut
public function __destruct()
{
mysql_close(self::$connection)
}
Then simply use unset($dm);
Related
I'm in the process of upgrading from mysql to mysqli.
All my mysql code was procedural, and I'd now like to convert to OOP, as most mysqli examples online are in OOP.
The problem I'm having is that, with mysql, once I had set up a connection, I never had to inject that connection into any functions as arguments for mysql to be accessible in the function.
Here is my old connection code:
$location = "localhost";
$user = "rogerRamjet";
$pass = "bestPassInTheWorld";
$dbName = "myDBName";
$link = mysql_connect($location, $user, $pass);
if (!$link) {
die("Could not connect to the database.");
}
mysql_select_db("$dbName") or die ("no database");
And an example function that has access to the mysql connection, without $link needing to be injected into the function:
function getUser($data)
{
$data=mysql_real_escape_string($data);
$error = array('status'=>false,'userID'=>-1);
$query = "SELECT `user_id`, `user_email` FROM `myTable` WHERE `data`='$data'";
if ($result = mysql_query($query))
{
$row = mysql_fetch_array($result, MYSQL_ASSOC);
if ($row['user_id']!="")
{
return array( 'status'=>true, 'userID'=>$row['user_id'], 'email'=>$row['user_email'] );
}
else return $error;
}
else return $error;
}
And here's my new mysqli connection:
$mysqli=new MySQLi($location, $user, $pass, $dbName);
So, to upgrade the first line in the above function, I'd need:
$data = $mysqli->real_escape_string($data);
But that throws the error:
Undefined variable: mysqli
Does this mean that for any function needing access to $mysqli, I need to inject $mysqli as an argument into it, or is there a way for it to be accessible the way mysql is without injection?
I know I need to move to prepared statements, but this is just so I can get my head around mysqli basics.
Making the variable global is bad practice. The singleton pattern solves the issue of needing to share one instance of an object throughout an application lifecycle. Consider using a Singleton.
The crude solution would be global $mysqli; as first line of your function. But as hsan wrote, read about PHP variable scope
This question already has an answer here:
PHP: mysql_connect not returning FALSE
(1 answer)
Closed 8 years ago.
I'm very new to OOP and am trying to learn it. So please excuse my noobness. I'm trying to connect to mysql and to test whether the connection is successful or not, I'm using if-else conditions.
Surprisingly, the mysql_connect is always returning true even on passing wrong login credentials. Now I'm trying to figure out why it does and after spending about 20 minutes, I gave up. Hence, I came here to seek the help of the community. Here is my code:
class test
{
private $host = 'localhost';
private $username = 'root2'; // using wrong username on purpose
private $password = '';
private $db = 'dummy';
private $myConn;
public function __construct()
{
$conn = mysql_connect($this->host, $this->username, $this->password);
if(!$conn)
{
die('Connection failed'); // this doesn't execute
}
else
{
$this->myConn = $conn;
$dbhandle = mysql_select_db($this->db, $this->myConn);
if(! $dbhandle)
{
die('Connection successful, but database not found'); // but this gets printed instead
}
}
}
}
$test = new test();
Please don't use the mysql_* functions, there are many, many reasons why - which are well documented online. They are also deprecated and due to be removed.
You'd be much better off using PDO!
Also I'd strongly advise abstracting this database code into a dedicated database class, which can be injected where necessary.
On-topic:
That code snippet seems to work for me, have you tried var_dumping $conn? Does that user have correct rights?
I also hope that you don't have a production server which allows root login without a password!
Ignoring the fact that you're using mysql_* functions rather than mysqli or pdo functions, you should utilise exceptions in OOP code rather than die(). Other than that, I can't replicate your problem - it may be that your mysql server is set up to accept passwordless logins.
class test
{
private $host = 'localhost';
private $username = 'root2'; // using wrong username on purpose
private $password = '';
private $db = 'dummy';
private $myConn;
public function __construct()
{
// returns false on failure
$conn = mysql_connect($this->host, $this->username, $this->password);
if(!$conn)
{
throw new RuntimeException('Connection failed'); // this doesn't execute
}
else
{
$this->myConn = $conn;
$dbhandle = mysql_select_db($this->db, $this->myConn);
if (!$dbhandle)
{
throw new RuntimeException('Connection successful, but database not found'); // but this gets printed instead
}
}
}
}
try {
$test = new test();
} catch (RuntimeException $ex) {
die($ex->getMessage());
}
this is my current Database class:
class Database {
private $db;
function Connect() {
$db_host = "localhost";
$db_name = "database1";
$db_user = "root";
$db_pass = "root";
try {
$this->db = new PDO("mysql:host=" . $db_host . ";dbname=" . $db_name, $db_user, $db_pass);
} catch(PDOException $e) {
die($e);
}
}
public function getColumn($tableName, $unknownColumnName, $columnOneName, $columnOneValue, $columnTwoName = "1", $columnTwoValue = "1") {
$stmt = $this->db->query("SELECT $tableName FROM $unknownColumnName WHERE $columnOneName='$columnOneValue' AND $columnTwoName='$columnTwoValue'");
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $results[0][$unknownColumnName];
}
}
I'm trying to run it using the following code:
$db = new Database();
$db->Connect();
echo $db->getColumn("Sessions", "token", "uid", 1);
And i get the following error:
PHP Fatal error: Call to a member function fetchAll() on a non-object in /Users/RETRACTED/RETRACTED/root/includes/Database.php on line 19
Any idea what's up? Thanks
This function is prone to SQL injection.
This function won't let you get a column using even simplest OR condition.
This function makes unreadable gibberish out of almost natural English of SQL language.
Look, you even spoiled yourself writing this very function. How do you suppose it to be used for the every day coding? As a matter of fact, this function makes your experience harder than with raw PDO - you have to learn all the new syntax, numerous exceptions and last-minute corrections.
Please, turn back to raw PDO!
Let me show you the right way
public function getColumn($sql, $params)
{
$stmt = $this->db->prepare($sql);
$stmt->execute($params);
return $stmt->fetchColumn();
}
used like this
echo $db->getColumn("SELECT token FROM Sessions WHERE uid = ?", array(1));
This way you'll be able to use the full power of SQL not limited to a silly subset, as well as security of prepared statements, yet keep your code comprehensible.
While calling it still in one line - which was your initial (and extremely proper!) intention.
it means your $stmt variable is not returning a PDOStatement object. your query is failing since PDO::query either returns a PDOStatement or False on error.
Use fetch instead of fetchAll..that will be easy in your case
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $results[0][$unknownColumnName];
It will be
$results = $stmt->fetch(PDO::FETCH_ASSOC);
return $results[$unknownColumnName];
I am trying to pass an mysqli database connection to a php class. The code I have so far (cut down for simplicity) is as follows:
db.php
$db_host = 'localhost';
$db_name = 'dbname';
$db_user = 'username';
$db_password = 'password';
$db = array('db_host'=>$db_host,
'db_name'=>$db_name,
'db_user'=>$db_user,
'db_password'=>$db_password);
$dbCon = new mysqli( $db['db_host'],
$db['db_user'],
$db['db_password'],
$db['db_name']);
if (mysqli_connect_errno())
{
die(mysqli_connect_error()); //There was an error. Print it out and die
}
index.php
<?php
require_once($_SERVER["DOCUMENT_ROOT"] . "/db.php");
$sql = "SELECT id FROM usr_clients";
$stmt = $dbCon->prepare( $sql );
if ($stmt)
{
$stmt->execute();
$stmt->bind_result($id);
while($stmt->fetch())
{
$cl = new Client($id, $dbCon);
$cl->doIt();
}
$stmt->close();
}
?>
client.php
<?php
Class Client
{
private $con;
public static $clientCount = 0;
public function __construct( $id, $con )
{
$this->con = $con;
$sql = "SELECT id FROM usr_clients WHERE id = $id";
$stmt = $this->con->prepare( $sql );
if ($stmt)
{
echo "it worked!";
}
else
{
echo "it failed";
}
}
}
?>
Now the index.php page successfully recognises the database connection declared in db.php, and returns a list of all clients. It then loops through each client, and creates a "client" object, passing it the database connection.
It is here that the problem seems to start. In the client class, the database connection is not recognised. I get multiple errors on the page saying "it failed". In the logs, there is a line about calling prepare() on a non object.
Can anyone explain why the connection works in index.php, but not in the client class?
Thanks
Your main problem is assumptions.
You are assuming that there is no connection passed, judging by indirect consequence.
But a programmer should be always logically correct in their reasoning.
Talking of connection? Verify the very connection. var_dump($con) in the constructor. var_dump($this->con) in the method. If it fails - only now you can blame connection and start for the solution.
If not - there is no reason in looking for another connection passing method. Yet it's time to find the real problem.
If your query fails, you have to ask mysql, what's going on, using $this->con->error, as this function will provide you with a lot more useful information than simple "it fails". The right usage I've explained here: https://stackoverflow.com/a/15447204/285587
I have an application in which I want to authenticate a user from a first database & manage other activities from another database.
I have created two classes. An object of the classes is defined in a file:
$objdb1=new db1(),$objdb2=new db2();
But when I try to call $objdb1->fn(). It searches from the $objdb2 & is showing table1 doesnot exists?
My first file database.php
class database
{
private $hostname;
private $database;
private $username;
private $password;
private $dblinkid;
function __construct()
{
if($_SERVER['SERVER_NAME'] == 'localhost')
{
$this->hostname = "localhost";
$this->database = "aaaa";
$this->username = "xxx";
$this->password = "";
}
else
{
$this->hostname = "localhost";
$this->database = "xxx";
$this->username = "xxx";
$this->password = "xxx";
}
$this->dblinkid = $this->connect();
}
protected function connect()
{
$linkid = mysql_connect($this->hostname, $this->username, $this->password) or die("Could not Connect ".mysql_errno($linkid));
mysql_select_db($this->database, $linkid) or die("Could not select database ".mysql_errno($linkid)) ;
return $linkid;
}
Similarly second file
class database2
{
private $vhostname;
private $vdatabase;
private $vusername;
private $vpassword;
private $vdblinkid;
function __construct()
{
if($_SERVER['SERVER_NAME'] == 'localhost')
{
$this->vhostname = "xxx";
$this->vdatabase = "bbbb";
$this->vusername = "xxx";
$this->vpassword = "";
}
else
{
$this->vhostname = "localhost";
$this->vdatabase = "xxxx";
$this->vusername = "xxxx";
$this->vpassword = "xxxx";
}
$this->vdblinkid = $this->vconnect();
}
protected function vconnect()
{
$vlinkid = mysql_connect($this->vhostname, $this->vusername, $this->vpassword) or die("Could not Connect ".mysql_errno($vlinkid));
mysql_select_db($this->vdatabase, $vlinkid) or die("Could not select database ".mysql_errno($vlinkid)) ;
return $vlinkid;
}
Third file
$objdb1 = new database();
$objdb2 = new database2();
Can you help me on this?
Regards,
Pankaj
Without knowing your classes, it is difficult to help. If you are using PDO, I can guarantee you that you can create multiple instances connected to different databases without any problem. If you are using the mysql_ family of functions you probably just forgot to set the link_identifier parameter (see here).
However, having a class db1 and a class db2 sounds like a code smell to me. You probably want to have two instances of the same class with different attributes.
Each time you call mysql_connect() or the equivalent mysqli functions, if a connection already exists using those same credentials it gets reused - so anything you do to modify the state of the connection, including changing database, charsets, or other mysql session variables affects "both" connections.
Since you are using the mysql_connect() function you have the option to force a new connection each time but this is not supported on all the extensions (IIRC mysqli and PDO don't alow for this).
However IMHO this is the wrong way to solve the problem. It just becomes messy trying to keep track of what's connected where.
The right way would be to specify the database in every query:
SELECT stuff FROM aaaa.first f, aaaa.second s
WHERE f.something=s.something;
Most likely your class does not pass the appropriate connection resource to the database functions. The second argument to e.g. mysql_query() is the connection resource. Simply store this resource in an instance variable on connection, and use it every time you do something with the database.
Your problem may be in checking if the SERVER_NAME is "localhost". Seems like you may be using the same connection strings in both classes. What is $_SERVER['SERVER_NAME'] resolving to?
You're looking for the fourth parameter of mysql_connect(), which states that it shouldn't reuse existing connections:
$dbLink1 = mysql_connect($server, $user, $pass, true);
mysql_select_db($db1, $dbLink1);
$dbLink2 = mysql_connect($server, $user, $pass, true);
mysql_select_db($db2, $dbLink2);
mysql_query("SELECT * FROM table1", $dbLink1); // <-- Will work
mysql_query("SELECT * FROM table1", $dbLink2); // <-- Will fail, because table1 doesn't exists in $db2
Passing the fourth parameter as true to mysql_connect resolves the issue.
$linkid = mysql_connect($this->hostname, $this->username, $this->password,true) or die("Could not Connect ".mysql_errno($linkid));