Parent's class constructor call error? - php

I'm trying to call parent class constructor but it throws an error
Fatal error: Cannot call constructor
and the same code was working well before, I didn't change anything and but suddenly don't know what could have happened, It is throwing this error.
I've read some answers on stackoverflow, but they say that your parent class doesn't contain a constructor, well, this is not my case I have constructor in my parent class. Here's my code:
class DB
{
var $con;
public function __construct()
{
require_once 'configs/dbconfig.php';
$this->connect();
}
function connect()
{
try{
$this->con = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME,DB_USER,DB_PASS);
$this->con->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
}catch(PDOException $ex) {
echo $ex->getMessage();
}
}
}
and then I have a class Posts which is extending DB and calling DB's constructor.
class Posts extends DB
{
var $user_id;
public function __construct($user_id)
{
parent::__construct();
$this->user_id = $user_id;
}
function get_posts($search,$pages,$since_id=0,$max_id=false,$count=20,$status='active')
{
$extra = '';
if($search) $extra .= " AND text LIKE CONCAT('%',:search,'%')";
if(!empty($pages)) $extra .= " AND page IN('".implode("','", $pages)."')";
if(!empty($status) && $status != 'all') $extra .= " AND status=:status";
$max_id = ($max_id) ? $max_id : time();
$sqlCommand = "SELECT id,pid,text,media,media_url,type,name,u_id,username,user_profile_url,user_photo_url,post_url,date,status,source,page_id FROM ".POSTS." WHERE date>=:since_id AND date<=:max_id".$extra." AND user_id=:user_id ORDER BY date DESC LIMIT $count";
$params = array(':since_id'=>$since_id,':max_id'=>$max_id,':user_id'=>$this->user_id);
if($search) $params[':search'] = $search;
if($status && $status != 'all') $params[':status'] = $status;
$posts = $this->fetch($sqlCommand,$params);
return $posts;
}
}

This is with your php version, It must have been changed, please check the version of php and try to update that.

I think this is a known PHP bug (look here), try to make a function in DB that returns a new DB (by calling constructor) and in Posts call this function that you created.

Well maybe it's a basic silly answer, but did you try 'require'?

Related

Assign object attributes from the result of prepared statement

I'm wanting to create a new instance of my Class and assign it's attributes the values that are returned. The reason for this is I'm creating a series of methods inheriting from the calling class, as opposed to using static methods which I already had working.
Example of what I'm using currently:
public static function findById($id) {
$id = self::escapeParam($id);
$idVal = is_int($id) ? "i" : "s";
$sql = "SELECT * FROM ".static::$db_table." WHERE id = ? LIMIT 1";
return static::findByQuery($sql,$idVal,$id);
}
public static function findByQuery($sql,$bindChar = '',$bindVal = '') {
try {
$callingClass = get_called_class();
$object = new $callingClass;
$statement = Database::$connection->prepare($sql);
if(!empty($bindChar)) :
$statement->bind_param($bindChar, $bindVal);
endif;
if($statement->execute()) :
$result = $statement->get_result();
$object = $result->fetch_object();
endif;
$statement->close();
if(!empty($object)) :
return $object;
endif;
} catch(Exception $e) {
}
}
What I tried was writing an instantiation method that creates a new instance of my class, and then assign each attribute of the object the value it returns from an array from a tutorial I did. However, the tutorial was fairly outdated and didn't use any new syntax or binding, so I was trying to rework this.
Example from the tutorial below:
public static function find_by_id($id) {
global $database;
$the_result_array = static::find_by_query("SELECT * FROM " . static::$db_table . " WHERE id = $id LIMIT 1");
return !empty($the_result_array) ? array_shift($the_result_array) : false;
}
public static function find_by_query($sql) {
global $database;
$result_set = $database->query($sql);
$the_object_array = array();
while($row = mysqli_fetch_array($result_set)) {
$the_object_array[] = static::instantation($row);
}
return $the_object_array;
}
public static function instantation($the_record){
$calling_class = get_called_class();
$the_object = new $calling_class;
foreach ($the_record as $the_attribute => $value) {
if($the_object->has_the_attribute($the_attribute)) {
$the_object->$the_attribute = $value;
}
}
return $the_object;
}
private function has_the_attribute($the_attribute) {
return property_exists($this, $the_attribute);
}
What I was trying to do from the tutorial, was to return my result as an array using a while, and then assigning a variable by passing the built array into the static::instantation() method, but it doesn't seem to ever be working correctly, as any public functions I create in my calling class (Admin for example) aren't called after as they don't exist due to the Class not being instantiated.
mysqli_result::fetch_object() accepts the class name as the first argument. You can pass the class name as an argument to that method and get the instance of the model. I am not sure why you have that much code but consider my example which I wrote based on your own code:
<?php
class Model
{
public static function findByQuery(string $sql, ?string $bindChar = null, ?string $bindVal = null): ?static
{
$statement = Database::$connection->prepare($sql);
if ($bindChar) :
$statement->bind_param($bindChar, $bindVal);
endif;
$statement->execute();
$result = $statement->get_result();
return $result->fetch_object(static::class);
}
}
class User extends Model
{
private $id;
}
class Database
{
public static mysqli $connection;
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
Database::$connection = new mysqli('localhost', 'user', 'password', 'test');
$user = User::findByQuery('SELECT ? as id', 's', 'Dharman');
var_dump($user);
The output from that example is:
object(User)#4 (1) {
["id":"User":private]=>
string(7) "Dharman"
}
As you can see, the code created an instance of the class using late-static binding and it also assigned the value to a private property, which you can't do otherwise.
P.S. My example is a little bit tidier. I added parameter typing and removed a lot of unnecessary code. In particular, I remove empty try-catch which is a terrible practice.
I have now got this working, although I feel this is probably not the best way of doing it.
I'm primarily front end so please comment if there are improvements or best practices.
public static function findByQuery($sql,$bindChar = '',$bindVal = '') {
try {
$statement = Database::$connection->prepare($sql);
if(!empty($bindChar)) :
$statement->bind_param("$bindChar", $bindVal);
endif;
if($statement->execute()) :
$result = $statement->get_result();
$output = $result->fetch_object();
endif;
$statement->close();
if(!empty($output)) :
$class = get_called_class();
$object = new $class;
foreach(get_object_vars($output) as $key => $value) :
$object->$key = $value;
endforeach;
endif;
if(!empty($object)) :
return $object;
endif;
} catch(Exception $e) {
}
}
My initial thoughts were declaring an object and then I thought that the PHP fetch_object call would have just assigned my object it's properties after initiating the Class but that wasn't the case.
So what I've done is that if the statement is successful and a results object is created, I then get the object properties and values with the get_object_vars() command, and then loop through these as a key value pair, assigning each attribute it's returned value.
I can confirm this works as I can now run $admin->remove() from my removal script, as opposed to what I was having to do before which was Admin::remove($id);

Call to a member function prepare() on null if __construct function is present

I've recently came across the need of pagination and I'll be using it quite a bit so I figured a class would be a perfect fit, for some reason when I try to use a __construct I get the fatal error:
Call to a member function prepare() on null
Thrown in '\App\Helpers\PaginationHelper.php' on line 31
Line 31 is this:
$sth = $this->db->prepare('SELECT * FROM '. $this->table_name .' ORDER BY id LIMIT :page_first_result, :per_page');
in the retrieveData function.
It's a big messy as I'm just beginning but for the life of me I can't figure out this error, I've read a few other questions that have the same error but they are all related to PDO not connecting, unlike this issue.
\Core\Model just extends a pdo connection
class PaginationHelper extends \Core\Model {
public $results = [];
public $tabs = '';
public $set_data;
public $table_name;
public $results_per_page;
public function __construct($sd){
$this->set_data = $sd;
}
public function table_name($tn){
$this->table_name = $tn;
}
public function results_per_page($rpp){
$this->results_per_page = $rpp;
}
public function retrieveData($page_first_result, $per_page){
$sth = $this->db->prepare('SELECT * FROM '. $this->table_name .' ORDER BY id LIMIT :page_first_result, :per_page');
$sth->bindValue(':page_first_result', $page_first_result, PDO::PARAM_INT);
$sth->bindValue(':per_page', $per_page, PDO::PARAM_INT);
$sth->execute();
return $sth->fetchAll(PDO::FETCH_ASSOC);
}
public function getResults(){
$number_of_results = $this->set_data;
$this->number_of_pages = ceil( count($number_of_results) / $this->results_per_page);
// determine which page number visitor is currently on
if (!isset($_GET['pagination'])) {
$this->page = 1;
} else {
$this->page = $_GET['pagination'];
}
$this_page_first_result = ($this->page - 1) * $this->results_per_page;
$fetch = $this->retrieveData($this_page_first_result, $this->results_per_page);
foreach($fetch as $data){
$this->results[] = $data;
}
return $this;
}
public function loopResults(){
return $this->results;
}
public function getTabs(){
if($this->page == 1){
$this->back = $this->page;
} else {
$this->back = $this->page-1;
}
if($this->page == $this->number_of_pages){
$this->next = $this->page;
} else {
$this->next = $this->page + 1;
}
$this->tabs .= '
<div class="pagination">
<a class="pagination__buttons-href" href="?pagination='.$this->back.'"><span class="pagination__buttons flaticon-keyboard-left-arrow-button"></span></a>
<span class="pagination__current_page"> '. $this->page .' </span>
<a class="pagination__buttons-href" href="?pagination='. $this->next .'"><span class="pagination__buttons flaticon-keyboard-right-arrow-button"></span></a>
</div>
';
return $this->tabs;
}
}
Now, here is something interesting: If I remove the __construct function, and create a new method to put the $set_data property like below, everything works perfectly.
public function set_data($sd){
$this->set_data = $sd;
}
This is how I'm calling the class:
This is how I'm calling the class with the function set_data:
$pagination = new PaginationHelper();
$pagination->set_data($array_data);
$pagination->table_name('recipe');
$pagination->results_per_page(20);
$pagination->getResults();
If you define your own constructor, the parent's constructor will not be called automatically.
Try doing this instead:
public function __construct($sd){
parent::__construct();
$this->set_data = $sd;
}
Direct quote from the documentation (http://php.net/manual/en/language.oop5.decon.php):
Note: Parent constructors are not called implicitly if the child class defines a constructor. In order to run a parent constructor, a call to parent::__construct() within the child constructor is required. If the child does not define a constructor then it may be inherited from the parent class just like a normal class method (if it was not declared as private).
When you extend a class and have a __constructor() function in it, you should call its parent's constructor as well:
parent::__construct();
If you skip this line, \Core\Model's constructor is never called. If you remove the constructor, it is called automatically.
try this one
public function __construct($sd){
parent::__construct();
$this->set_data = $sd;
}
call the parent constructor manually

Declaring new class() beginning of class

I'm working on a Content Management System for My wife who wants to create a website, and I have a way I've always done things within the past, but I want to deliver better coding procedures, so this is my dilemma.
This is Code I've used in the past that I know works.
class accounts {
public function CheckAccountLogin() {
global $db;
$query = <<<SQL
SELECT id,gaToken
FROM accounts
WHERE password_hash = :hashedpw
SQL;
$resource = $db->sitedb->prepare( $query );
try {
$resource->execute( array (
':hashedpw' => sha1($_POST['user-name'].':'.$_POST['user-pass']),
));
if($resource->rowCount() == 0 ) { echo false;}
else {
foreach($resource as $row) {
$this->authkey = $row['gaToken'];
if($this->authkey == "") {
self::SetSession();
}
else {
self::CheckAuth();
}
}
}
catch(PDOException $e) {
echo $e->getMessage();
}
}
}
Now every function would need global $db at the start of the function in order to utilize $db->sitedb or else we would have an error thrown our way, so what I want to do instead is
class accounts {
public $db = new db();
public function CheckAccountLogin() {
$query = <<<SQL
SELECT id,gaToken
FROM accounts
WHERE password_hash = :hashedpw
SQL;
$resource = $this->sitedb->prepare( $query );
try {
$resource->execute( array (
':hashedpw' => sha1($_POST['user-name'].':'.$_POST['user-pass']),
));
if($resource->rowCount() == 0 ) { echo false;}
else {
foreach($resource as $row) {
$this->authkey = $row['gaToken'];
if($this->authkey == "") {
self::SetSession();
}
else {
self::CheckAuth();
}
}
}
catch(PDOException $e) {
echo $e->getMessage();
}
}
}
This way within all my new functions I'm able to simply declare $this->sitedb whenever I need to connect to the database. With the above code I'm given
Parse error: syntax error, unexpected 'new' (T_NEW) in /var/www/html/functions.d/accounts.class.php on line 3. I know where the issue is, I'm just looking for a cleaner way than my first code block. Any help in getting this to work correctly would be greatly appreciated.
You can't initialize a variable there unless it can be evaluated at compile time. So you can't call new there. See the docs. Do it in the constructor.
class accounts {
public $db;
public function __construct() {
$this->db = new db();
}
}
You can create a global class DbConnect where you statically define the database object db and connect it to your database, in this way you will keep a single database connection object throughout your application.
Include this class at the beginning before any code, then you can access this object anywhere through :: operator in any class that follows.

How to Access Data from One Class in multiple Children Classes

I'm having a bit of trouble in designing my classes in php.
As you can see in my Code, i want to have one Class instance and having more classes as children which "talk" from one to another. im getting the logged user and get all his information stored to a variable. In my other Classes i recently need to get this UserData.
Any help and Ideas are welcome :)
class Factory
{
private $UserData;
public function Factory()
{
DB::connect();
$this->getLoggedUserData( $_SERVER['REMOTE_USER'] );
}
private function getLoggedUserData( $user )
{
$result = DB::query( "SELECT * FROM users WHERE user='$user' LIMIT 1" );
$this->UserData = $result->fetch_assoc();
}
public function getMyTasks()
{
// how to call that class, without instancing it over and over again
MyOtherClass -> getMyTasks();
}
}
class MyOtherClass
{
public function getMyTasks()
{
// how to access the "global" variable
$result = DB::query( "SELECT * FROM tasks WHERE userID=" . $UserData['userID'] . " LIMIT 1" );
// doSomething ($result);
}
}
class DB
{
private static $mysqli;
public static function connect()
{
$mysqli = new mysqli(MYSQL_SERVER, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DB);
if ($mysqli->connect_error) {
die('Connect Error (' . $mysqli->conect_errno . ')' . $mysqli->connect_error);
}
mysqli_set_charset($mysqli, 'utf8');
self::$mysqli = $mysqli;
}
public static function query( $query )
{
$result = self::$mysqli->query( $query );
if ( self::$mysqli->error ) {
error_log("QUERY ERROR: " . self::$mysqli->error);
error_log("QUERY: " . $query);
}
return $result;
}
}
$Factory = new Factory();
OK, here goes a simple trivial approach to your problem
Mind you, this is not complete. Gimme some feedback if this is closing in on what you'd expect
your classes changed a bit
<?php
class Factory {
private $UserData;
private $UserTask;
public function Factory() {
DB::connect();
$this->getLoggedUserData($_SERVER['REMOTE_USER']);
}
private function getLoggedUserData($user) {
$result = DB::query('SELECT * FROM users WHERE user="'.$user.'" LIMIT 1');
$this->UserData = $result->fetch_assoc();
}
public function getMyTasks() {
// how to call that class, without instancing it over and over again
if (!isset($this->UserTask)) $this->UserTask = new MyOtherClass($this->UserData);
return $this->UserTask->getMyTasks();
}
}
class MyOtherClass {
private $UserData;
public function __construct($userData) {
$this->userData = $userData;
}
public function getMyTasks() {
// how to access the "global" variable
$task = DB::query('SELECT * FROM tasks WHERE userID='.$this->UserData['userID'].' LIMIT 1');
return $this->performTask($task);
}
public function performTask($task) {/* doSomething(); */}
}
// usage is not complete, waiting for some extra input
$factory = new Factory();
$taskResults = $factory->getMyTasks();
Any input on how to improve this is very welcome
edit following comments
Let's take a look at how you can solve the problem of having to share instances between different "apps" in your code
the singleton approach: an instance is created on the first call, all subsequent calls are passed the single instance
the registry pattern: an object created at the start of the script picks up all initialized requirements and stores them. If any "app" needs the basic set of services (it's not standalone), then pass the registry object to it's initializer/constructor.
I hope I understood your comments well enough, if not feel free to ask and correct me
Hard to say what would be best for you when i dont know more about the scale of your application etc.
Anyway the simplest way is something like this:
$otherClass = new MyOtherClass();
$Factory = new Factory($otherClass);
Class Factory
class Factory
{
private $UserData;
private someClass;
public function Factory(&$someClass)
{
$this->someClass = $someClass;
DB::connect();
$this->getLoggedUserData( $_SERVER['REMOTE_USER'] );
}
...
Usage
$this->someClass->getMyTasks();
But in case you only want access to the methods/variables of the parent, then yes extend the class.

Extending a class from the class which is extended from mysqli

I am not sure if this is possible as I am not very good in OOP programming yet.
I have this db class extended from mysqli,
class database extends mysqli
{
# overwrite parent __construct
public function __construct($hostname = null,$username = null,$password = null,$database = null,$port = null, $socket = null)
{
$hostname = $hostname !== null ? $hostname : ini_get("mysqli.default_host");
$username = $username !== null ? $username : ini_get("mysqli.default_user");
$password = $password !== null ? $password : ini_get("mysqli.default_pw");
$database = $database !== null ? $database : "";
$port = $port !== null ? $port : ini_get("mysqli.default_port");
$socket = $socket !== null ? $socket : ini_get("mysqli.default_socket");
parent::__construct($hostname,$username,$password,$database,$port,$socket);
# check if connect errno is set
if (mysqli_connect_errno())
{
throw new RuntimeException('Cannot access database: ' . mysqli_connect_error());
}
}
# fetches all result rows as an associative array, a numeric array, or both
# mysqli_fetch_all (PHP 5 >= 5.3.0)
public function fetch_all($query)
{
$result = parent::query($query);
if($result)
{
# check if mysqli_fetch_all function exist or not
if(function_exists('mysqli_fetch_all'))
{
# NOTE: this below always gets error on certain live server
# Fatal error: Call to undefined method mysqli_result::fetch_all() in /.../class_database.php on line 28
return $result->fetch_all(MYSQLI_ASSOC);
}
# fall back to use while to loop through the result using fetch_assoc
else
{
while($row = $result->fetch_assoc())
{
$return_this[] = $row;
}
if (isset($return_this))
{
return $return_this;
}
else
{
return false;
}
}
}
else
{
return self::get_error();
}
}
# fetch a result row as an associative array
public function fetch_assoc($query)
{
$result = parent::query($query);
if($result)
{
return $result->fetch_assoc();
}
else
{
# call the get_error function
return self::get_error();
# or:
# return $this->get_error();
}
}
public function query($query)
{
$result = $this->query($query);
if($result)
{
return $result;
}
else
{
return $this->get_error();
}
}
...
# display error
public function get_error()
{
if($this->errno || $this->error)
{
return sprintf("Error (%d): %s",$this->errno,$this->error);
}
}
public function __destruct()
{
parent::close();
//echo "Destructor Called";
}
}
and I have this procedural style of code which I want to turn it into a class that extended from the database class above,
if(isset($_REQUEST['search'])) $search = $_REQUEST['search'];
$sql = "
SELECT *
FROM root_pages
WHERE root_pages.pg_cat_id = '2'
AND root_pages.parent_id != root_pages.pg_id
AND root_pages.pg_hide != '1'
AND root_pages.pg_url != 'cms'
AND root_pages.pg_content_1 LIKE '%".$search."%'
OR root_pages.pg_content_2 LIKE '%".$search."%'
AND root_pages.pg_content_1 NOT LIKE '%http://%'
AND root_pages.pg_content_2 NOT LIKE '%http://%'
ORDER BY root_pages.pg_created DESC
";
$items = $connection->fetch_all($sql);
$total_item = $connection->num_rows($sql);
so I think, by theory I can extend this code into a class like this,
class search extends database
{
public $search = null;
public function __construct($keyword)
{
$this->search = $keyword;
}
public function get_result()
{
$sql = "
SELECT*
FROM root_pages
WHERE root_pages.pg_cat_id = '2'
AND root_pages.parent_id != root_pages.pg_id
AND root_pages.pg_hide != '1'
AND root_pages.pg_url != 'cms'
AND root_pages.pg_content_1 LIKE '%".$this->search."%'
OR root_pages.pg_content_2 LIKE '%".$this->search."%'
AND root_pages.pg_content_1 NOT LIKE '%http://%'
AND root_pages.pg_content_2 NOT LIKE '%http://%'
ORDER BY root_pages.pg_created DESC
";
$items = parent::fetch_all($sql);
return $items;
}
}
then I call the object of search,
$output = new search('1');
print_r($output->get_result());
but I get lots of errors instead,
Warning: mysqli::query()
[mysqli.query]: Couldn't fetch search
in C:\wamp\www\xxx\class_database.php
on line xx
Warning: database::get_error()
[database.get-error]: Couldn't fetch
search in
C:\wamp\www\xxx\class_database.php on
line xx
Warning: mysqli::close()
[mysqli.close]: Couldn't fetch search
in C:\wamp\www\xxx\class_database.php
on line xx
What have I done incorrectly? How can I fix it?
Thanks.
EDIT:
When I tried to call the child class (search) from the parent class (database) in this way,
$database = new database(DB_HOST,DB_USER,DB_PASS,DB_NAME);
print_r(search::get_result());
then I get this error,
Fatal error: Non-static method
mysqli::query() cannot be called
statically in
C:\wamp\www\xxx\class_database.php on
line
Sigh...
Any ideas?
Because the search property in the search class is non-static, you have to first instantiate an object:
$database = new database(DB_HOST,DB_USER,DB_PASS,DB_NAME);
$search = new search("search term");
print_r($search->get_result());
Maybe have a read of a basic tutorial on OOP in PHP, the first one from Google:
http://www.killerphp.com/tutorials/object-oriented-php/
EDIT: There's also a few calls to self::get_error(); in your database class, these should be re-written as instance methods rather than class methods:
$this->get_error()
You are using the wrong method to call methods of the mysqli class in your database class.
You only want to use the double colon :: when accessing static methods, static properties, constants, or the parent of the current method you are overwriting. None of the methods in the mysqli class are static despite how the php manual lists them with the :: in the list of methods.
The way you use parent::__construct() in your __construct() is correct because you are in the current method of the parent you are overwriting.
But in your other methods you want to use $this-> to refer to other parent methods.
In fetch_all() and fetch_assoc() use $this->query() instead of parent::query().
In __destruct() use $this->close(); instead.
When you use self::get_error();, you will either want to change that to $this-> or modify your the function definition to make it static like so public static function get_error()
I think extending the class from database class is probably a bad idea...
Here is my solution:
class search
{
public $mysqli = null;
public function __construct($mysqli)
{
$this->mysqli = $mysqli;
}
public function get_result($parameter)
{
$sql = "
SELECT *
FROM root_contacts_cfm
WHERE root_contacts_cfm.cnt_id = '".$parameter."'
ORDER BY cnt_id DESC
";
$item = $this->mysqli->fetch_assoc($sql);
return $item;
}
}
Passing the object database class into the search class instead, so I can call this search class by this:
$mysqli = new database(DB_HOST,DB_USER,DB_PASS,DB_NAME);
$output = new search($mysqli);
print_r($output->get_result('1'));
Am I right...?

Categories