I have the following code that is able to create a class that has a single static instance of the database object, and two static functions for rows and columns.
<?php class Database{
private static $instance;
private function __construct() {}
private function __clone(){}
public static function call(){
if(!isset(self::$instance)){
self::$instance = new MySQLi("localhost", "foo", "bar", "fizz");
if(self::$instance->connect_error){
throw new Exception('Error MySQL: ' . self::$instance->connect_error);
}
}
return self::$instance;
}
public static function getRow($id, $db){
$query = "SELECT * FROM ".$db." WHERE id=".$id;
$result = self::call()->query($query);
return $result->fetch_assoc();
}
} ?>
However, when I call getRow from another class, like this
$data = Database::getRow($id, "posts");
I get the following error:
Fatal error: Call to a member function
fetch_assoc() on a non-object in
database.php
on line 27
And I check it again and again and everything seems to be in order, maybe I have a error in call() ?
That will happen if you have an error in your SQL syntax -- did you check it and make sure there were no errors?
I would have error checking, something like
if( self::$instance->error ) {
// Handle error
}
You are passing in a variable called $db to getRow(), maybe that should be a table?
query() will return false if the SQL query fails. You can add the following to handle this error:
$result = self::call()->query($query)
if($result === false)
throw new Exception(call()->error); // throw exception, use MySQLi error as message
return $result->fetch_assoc();
public static function getRow($id,$table)
{
$query = "SELECT * FROM ".$table." WHERE id=".$id." LIMIT 1";
$result = $this->instance->query($query);
return $result->fetch_assoc();
}
this line
if(!isset(self::$instance)){
will have sometimes strange behaviours. checking isset() against a null value is subject to failings. try this:
private static $instance = null; // it defaults to null but hey, rather be on the sure side
// foo
if (self::$instance === null) {
// mysqli creation foo
I have tried this code and it works, you must have an error in the query. What may cause this error is:
Check what you pass to Database::getRow(). There may be some non-numerical characters in your id
Check if column name 'id' exists, this sounds funny but it happened to me a million times, i was querying column 'id' instead of 'product_id' and so on
Check if table $db exists
Make sure you don't switch the function parameters by an accident. Database::getRow(24, 'people'); instead of Database::getRow('people', 24);
I can't think of anything else that may cause this error.
Related
I have the following shortened PHP 7.4 code
class xyz extends abc {
protected ?mysqli_result $result = null;
//some more class properties..
public function queryExecute(string $query): void {
$this->result = $this->mysqli->query($query);
//some more doings..
}
}
when I call the function with a valid query e.g. Select id from test a valid result is returned and no error occurs.
If I, for example, execute an "invalid" query Select id from test2 (table does not exist) the mysqli->query returns a bool and I get the error:
Fatal error: Uncaught TypeError: Typed property xyz::$result must be an instance of mysqli_result or null, bool used in xyz.php on line 107
TypeError: Typed property xyz::$result must be an instance of mysqli_result or null, bool used in xyz.php on line 107
Is there some possibility to get the properties to accept bool and mysqli_result type in one? Mixed is no valid keyword so I am a little bit clueless right now how to get around this.
Not until PHP 8 is released. It will be possible with PHP 8 to have union types, but that doesn't mean it is a good idea.
First of all, you should always enable proper mysqli error reporting. Just add this line before new mysqli():
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
If a query fails then mysqli will throw an exception instead of returning FALSE value.
However, mysqli_query() can also return TRUE for queries that do not return any results e.g. INSERT.
Since PHP 8 you will be able to do union types:
class xyz extends abc {
protected mysqli_result|bool $result;
public function queryExecute(string $query): void {
$this->result = $this->mysqli->query($query);
}
}
In PHP 7.4 you can do this by replacing true with null. After all, you really don't need that boolean value.
class xyz extends abc {
protected ?mysqli_result $result;
public function queryExecute(string $query): void {
$res = $this->mysqli->query($query);
$this->result = true === $res ? null : $res;
}
}
How to write mysqli abstraction library?
It looks like you are trying to build a mysqli abstraction library. PHP already has a much better library called PDO, but if you want to build your own here are some pointers.
Use prepared statements all the time. There's barely any reason to use mysqli_query.
Keep it simple. Don't bother with storing mysqli_result in an attribute. You only need this object to fetch results and then you can discard it.
Always enable error reporting.
Don't expose mysqli_result nor mysqli_stmt objects. Use them only internally.
I have used this class for demonstration purposes how you can start with such library.
class DBClass extends mysqli {
public function __construct(
$host = null,
$username = null,
$passwd = null,
$dbname = null,
$port = null,
$socket = null
) {
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
parent::__construct($host, $username, $passwd, $dbname, $port, $socket);
$this->set_charset('utf8mb4');
}
/**
* Executed prepared statement
*
* #param string $sql SQL query with placeholders e.g. SELECT * FROM users WHERE Id=?
* #param array $params An array of parameters to be bound
* #return array|null
*/
public function safeQuery(string $sql, array $params = []): ?array {
// prepare/bind/execute
$stmt = $this->prepare($sql);
if ($params) {
$stmt->bind_param(str_repeat("s", count($params)), ...$params);
}
$stmt->execute();
// If the query produces results then fetch them into multidimensional array
if ($result = $stmt->get_result()) {
return $result->fetch_all(MYSQLI_BOTH);
}
// return nothing if the query was successfully executed and it didn't produce results
return null;
}
}
There is no need to store the result in an attribute because we return the array with values immediately. This class fulfils most use cases and is very simple to use.
$db = new DBClass('localhost', 'user', 'pass', 'db_name');
// INSERT
$db->safeQuery('INSERT INTO user(name) VALUES(?)', ['Dharman']);
// You still have access to the parent mysqli object if needed
var_dump($db->insert_id);
//SELECT
$usernames = $db->safeQuery('SELECT * FROM user WHERE name=?', ['Dharman']);
foreach ($users as $user) {
echo $user['id'];
}
You can wrap this in a function like this (or modify your existing function):
public function queryExecute(string $query): void
{
$result = $this->mysqli->query($query);
if (!$result) {
// Handle error eg: Throw Exception
// In case you want to ignore the error (not recommended)
// you want to place a 'return;' here
}
$this->result = $result;
}
In PHP8 you wont have this problem anymore. Due to union-types, which are likely to come.
In case you are using an IDE like PhpStorm you can do this aswell, to get intellisense:
/** #var null|bool|mysqli_result */
protected $result = null;
In case you are trying to build an abstraction-layer for mysqli, you may want to have a look at PDO.
So I am working on PHP and hence I got to know about __call() method since, it calls the unknown method and so here I call the method name as it's the table name of my database and as I want to access all the serial number(sn) from the database and print on screen using this method. I know there is very big mistake in my code and I'm not getting it, I'm struck. Please HELP!
and if you are not getting my point then please ask, I'll explain!
WHAT I ACTUALLY WANT FROM THIS SCRIPT is that I want a access from database using __call() method and var_dump() the data from the database like I am doing here,
public function getOne($table_name) {
$rss= mysqli_query(self::$connection,"SELECT * FROM $table_name LIMIT 1");
return mysqli_fetch_assoc($rss);
}
here I am directly getting all the data from database and it echo on the screen, but with __call() also I want to do the same but with rows. How can I do that?
Also what is the actual use of __call()?
class mysql_db {
private static $instance;
public static $connection;
public function __construct($host=null, $user=null, $password=null, $db=null) {
if(!self::$instance && !$host) {
throw new \Exception("Missing Database..!!!");
}
self::$connection = mysqli_connect($host,$user,$password,$db);
}
public function __destruct() {
mysqli_close(self::$connection);
}
public function getData($table_name) {
$rs= mysqli_query(self::$connection,"SELECT * FROM $table_name");
return mysqli_fetch_all($rs,MYSQLI_ASSOC);
}
public function getOne($table_name) {
$rss= mysqli_query(self::$connection,"SELECT * FROM $table_name LIMIT 1");
return mysqli_fetch_assoc($rss);
}
public function find($table_name,$sn,$val) {
$rq= mysqli_query(self::$connection,"SELECT * FROM $table_name WHERE $sn='$val'");
return mysqli_fetch_all($rq,MYSQLI_ASSOC);
}
**// I AM GETTING ERROR FROM THESE LINES**
//-> public function __call($table_name,$sn) {
//-> //var_dump("SELECT * FROM $table_name where sn='1'");
//-> $rt= mysqli_query(self::$connection,"SELECT * FROM $table_name
WHERE '$sn'")or die(mysqli_error()) ;
//-> return mysqli_fetch_all($rt,MYSQLI_ASSOC);
}
public static function getInstance() {
try {
if(!self::$instance) {
self::$instance=new static('localhost','root','','xam');
}
} catch(\Exception $e) {
echo 'Caught Error: ', $e->getMessage(), "\n";
}
return self::$instance;
}
}
$h= mysql_db::getInstance();
var_dump($h->name("1"));
The __call() method is one of php magic methods.
As it states in the documentation:
The overloading methods are invoked when interacting with properties
or methods that have not been declared or are not visible in the
current scope
About the __call() magic method:
__call( string $name, array $arguments) is triggered when invoking inaccessible methods in an object context.
Why is your code not working?
The error message states that you are trying to convert an array into a string. So probably, the variable $sn that you're passing to the __call() method is not an array.
Also, you're not concatenating your strings properly. If you want to inject a variable inside a string. The correct term is string interpolation, and it should be implemented as it follows:
echo "Variables should always be inside brackets. My {$name} is!"
Also, remember to sanitize your querys to avoid sql injections! check this answer
I know this is an old question but I am not finding answer. Here is my code:
class SqlConnect implements connectionInterface {
//put your code here
private $datasource=null;
private $credentials = null;
public function __construct() {
$netsConn = new configInit(); // Here where the error is happing
$datasource = $this->$netsConn->getDatabase();
$credentials = $this->$netsConn->getBasicCredentials();
}
public function connect()
{
$conn = mysqli_connect($datasource['servername'], $credentials['username'], $credentials['password']);
if (!$conn)
{
die("Connection failed: " . $conn->connect_error);
} else {
return $conn;
}
}
}
I am then calling the class like
class applicationsController implements Controller {
private $conn = null;
public function __construct() {
$conn = new SqlConnect();
$this->$conn->connect();
}
...
}
However I am getting the following error:
Catchable fatal error: Object of class configInit could not be converted to string
This error happens in SqlConnect. I have looked through about 6 answers on stackoverflow and most all of them were about trying to echo some objects, which is not my case
Ahh, I think I see it.
You are assigning to global variable $netsConn, not object property $netsConn ...
Try this:
private $netsConn;
...
$this->netsConn = new configInit();
... that is, if you want to keep it around. Otherwise, refer directly to the variable not to a (never-declared ...) property.
The reason you get the error is because of the following 2 lines
netsConn = new configInit(); // Here where the error is happing
$datasource = $this->$netsConn->getDatabase();
First line is fine, but on the second line, you are using $netsConn as an attribute name for the SqlConnect class ($this). For this to work, php has to use the string representation of $netsConn by attempting to call it's implementation of the magic method __toString(). Since this probably does not exist in your configInit() class, you end up with that error.
I have an object which pulls back details about the site. Basically the name address etc. One of the fields it pulls back is IDCounty. see below. I then want to feed this into a new instance of another object and it automatically give me the county name. Here is what i have
so my system object
class System{
private $db;
public $Ad1;
public $Ad2;
public $County;
public $IDSystem;
//connect to database
public function __construct($IDSystem ='1') {
$this->db = new Database();
$this->IDSystem = $IDSystem;
}
//get address
public function ContactInfo() {
$Query = sprintf("SELECT BSAd1, BSAd2, IDCounty FROM BusinessSettings WHERE IDBusinessSettings = %s",
GetSQLValueString($this->IDSystem, "int"));
$Query = $this->db->query($Query);
$Result = $this->db->fetch_assoc($Query);
$this->Ad1 = $Result['BSAd1'];
$this->Ad2 = $Result['BSAd2'];
$County = new County($Result['IDCounty']);
$this->County = $County;
}
//end address
}
As you can see this object is calling another County and setting the $County to $this->County. My details for that are below
class County {
public $IDCounty;
private $db;
public function __construct($IDCounty) {
$this->db = new Database();
$this->IDCounty = $IDCounty;
$Query = sprintf("SELECT CountyName FROM County WHERE IDCounty = %s", GetSQLValueString($this->IDCounty, "int"));
$Query = $this->db->query($Query);
$County = $this->db->fetch_assoc($Query);
return $County['CountyName'];
}
}
When I'm calling the object I call it like so
$SiteDetails = new System();
$SiteDetails->ContactInfo();
echo $SiteDetails->Ad1;
echo $SiteDetails->County;
Im getting an error from error reporting on echo $SiteDetails->County; which says
"Catchable fatal error: Object of class County could not be converted to string"
After Googling this error I see the System class is having trouble converting getting the county name from the County class and converting it to $this->County
Unfortunately for me I'm not sure how to fix it though. I thought I could return a value from a function upon instantiation but it seems I'm wrong. Please help. thanks guys.
Whatever return statements a constructor may contain, they will be ignored. A constructor will, by definition, return a new instance of a given class. Read the docs. Though the signature of __construct() shows a return-type of void, you should think of it as void* (a void pointer). That means, it returns a reference to the newly created object. You should replace the return statement with soimething like:
$this->stringValue = $County['CountyName'];
And, because of many other magic-methods, you can implement another one to use a class as a string: the magic __toString method:
public function __toString()
{
return (string) $this->stringValue;//the cast is optional
}
As ever, read through the documentation on some of the other magic-methods. They're quite useful, though I must add, they are/can be slow. you could, equally well implement a getter:
//assuming this is in your constructor:
$this->stringValue = $County['CountyName'];
public function getContyName($default = '')
{
if (!$this->stringValue)
{
return $default;
}
return $this->stringValue;
}
Now, thanks to the $default argument, I can do this:
$county = new County();//not passing any values
echo $county->getCountyName('unknown');//will echo unknown
echo $county->getCountyName();//echoes empty string, because that's the default value of $default...
$county2 = new County('Foobar');
echo $county2->getCountyName('unknown');//echoes Foobar, county is known...
Other than that, your sprintf call isn't making much sense to me. The format used is %s, so I'd expect you pass a string. In fact you're passing GetSQLValueString($this->IDCounty, "int"), which is a the return value of a call to a global function (terrible code smell BTW). Since you're passing 'int' as an argument there, I'd expect the return value to be an int, so why not %d in your sprintf format?
There are many, many other things that need work here (for example: creating a new Database instance without params? really? Why not query using a DB object? why use a global function? Why aren't you using prepared statements? ... Please, read up some more on OOP
Implement the method __toString() in County class.
Example:
public function __toString()
{
return $this->foo;
}
This question already has answers here:
Call to a member function on a non-object [duplicate]
(8 answers)
Closed 9 years ago.
I have this code:
<?php
class guildData {
public $email = NULL;
public $hash_pw = NULL;
public $user_id = NULL;
public $clean_username = NULL;
public $display_username = NULL;
public function selectGuild($g_id)
{
global $db,$db_table_prefix;
$this->g_id = $g_id;
$sql = "SELECT
name
FROM
guild
WHERE
id = '".$g_id."'";
$result = $db->sql_query($sql);
$row = $db->sql_fetchrow($result);
return ($row['name']);
}
}
?>
<?php echo $guildData->selectGuild(1); ?>
I'm simply getting a 500 error and IDEone gave me this also:
Fatal error: Call to a member function selectGuild() on a non-object in /home/VT00Ds/prog.php on line 32
I can not see the error, can you help me?
You are doing it wrong.
Get rid of the global variables. Instead , if the class needs DB access , then you should inject it in the constructor:
class GuildData
{
// ... snip
protected $connection;
public function __construct( PDO $connection )
{
$this->connection = $connection;
}
// ... snip
}
Your code has potential for SQL injections. Instead of concatenating the query, you should use prepared statements:
$statement = $this->connection->prepare(
'SELECT name FROM guild WHERE id = :id'
);
$statement->bindParam( ':id', $this->g_id, PDO::PARAM_INT );
if ( $statement->execute() )
{
$data = $statement->fetch( PDO::FETCH_ASSOC );
}
You have to instantiate object, before you can use them:
$pdo = new PDO('mysql:host=localhost;dbname=myMagicalDB;charset=UTF-8',
'username', 'password');
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$guild = new GuildData( $pdo );
$guild->selectGuild(42);
You might consider separating the part with deals with DB from the domain logic. Essentially letting some other class to handle the fetching and storing the data, while the Guild class manages the logic. You might find this and this answer relevant.
Do not use public variables. You are breaking the encapsulation of the object by directly exposing the internal data. Instead you should define them as protected or private.
You might also take a hard look at what you are actually keeping there. Why does GuildData need $hash_pw or $clean_username ?
You haven't instantiated $guildData. If you want to use this method without instantiating an object, the method should be static.
class guildData {
public static function selectGuild($g_id) { ... }
}
Then you can call it from
echo guildData::selectGuild(1);
Otherwise, you need to instantiate an object
$guildData = new GuildData();
echo $guildData->selectGuild(1);
Also, you should have some sort of __construct() method in there, in order to set up your member variables.
UPDATE I also noticed an error in your selectGuild() method:
$this->g_id = $g_id;
Sets the value of g_id, which is not defined as a member variable in the class. You must declare g_id as a member variable in your class definition:
class guildData {
public $email = NULL;
public $hash_pw = NULL;
public $user_id = NULL;
public $clean_username = NULL;
public $display_username = NULL;
public $g_id = NULL;
.
.
.
}
Finally, sql_query() is not a PHP method that I've ever heard of. Unless you're using a library that defines these methods, I think you mean mysql_query(). If this is the case, you should stop using mysql_* functions. They're being deprecated. Instead use PDO (supported as of PHP 5.1) or mysqli (supported as of PHP 4.1). If you're not sure which one to use, read this SO article.