Extending a class from the class which is extended from mysqli

Hi,

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();
    		}
    	}
    	
    	...
    
    	# 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
    ";
    
    # use the stored connection object from the class_page_controller.php, to process the query
    $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

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…

What have I done incorrectly? How can I fix it? Any ideas?

Thanks.

#1 - To simply avoid the error you would use $items = $this->fetch_all($sql), not parent::fetch_all($sql)…but more importantly:

#2 - I don’t see the point in extending mysqli. Your search class should not extend your database class - A search USES a database, it’s not a child of it - just like a carpenter USES a hammer, he is not the same type of thing as a hammer. Pass the database object to the class in the constructor (sort of like handing the hammer to the carpenter) like this:

<?php

class search
{

    public $database;
       
    public function __construct(database $database)
    {
       $this->database = $database;    
    }
    
    
    public function get_result($keyword)
    {
        $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 '%".$keyword."%'
        OR root_pages.pg_content_2 LIKE '%".$keyword."%'
        
        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 = $this->database->fetch_all($sql);
        
        return $items;
    }
} 

#3 - your query is very susceptible to an sql injection - you should be using bound parameters

Thank you very much for this! can I ask what you mean by ‘bound parameters’? Any examples of this kind?

Mind if I ask - what is wrong with parent::fetch_all($sql) as I thought I am extending from mysqli class and I want to access

fetch_all

from the parent class which is mysqli?

Thanks.

You want to look at prepared statements - there is a good example in the php mysqli manual

When you extend a class, the child class acts inherits all of the methods of it’s parent, so calling a method in the child class is the same as calling it in the parent class - i.e. $this->doStuff(). parent:: is used to access static variables and methods of the parent class, or within a child class method which overrides the parent method to call the parent method - i.e.:

class foo
{
    function bar()
    {
        echo 'called from foo<br />';
    }
}


class foo2 extends foo
{

    function bar()
    {
        echo 'called from foo2<br />';
        parent::bar();
    }
}

$foo2 = new foo2;
$foo2->bar();

will output:
called from foo2
called from foo

there’s a good explanation in the manual:
http://us3.php.net/manual/en/language.oop5.paamayim-nekudotayim.php

hope that makes sense - cheers

Yes it does make sense. I will go and read the documentation from your links. Thank you very much! :smiley:

Sorry, can I ask - how can I turn my search class with a prepared and bound statement?

I have read some articles online why we should use prepared statements,

http://forum.codecall.net/php-tutorials/12442-php-5-mysqli-prepared-statements.html

But I still don’t have a clue how to improve my query… I tried with this amendment below,

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 = '?'
		ORDER BY cnt_id DESC
		";
		
		$stmt = $this->mysqli->prepare($sql);
		
		/* bind parameters for markers */
		$stmt->bind_param("s", $parameter);
		
		/* execute query */
		$stmt->execute();
		
		 /* fetch value */
		return $stmt->fetch();
		
	}
}

I will get this error,

Warning: mysqli_stmt::bind_param() [mysqli-stmt.bind-param]: Number of variables doesn’t match number of parameters in prepared statement in C:\wamp\www\xxxl\class_database.php on line 487

line 487 refers to

$stmt->bind_param("s", $parameter);

Thanks.

Almost there - you just need to remove the singles quote around ?. i.e.:

$sql = " 
        SELECT * 
        FROM root_contacts_cfm 
        WHERE root_contacts_cfm.cnt_id = ?
        ORDER BY cnt_id DESC 
        "; 

Thanks. It now returns 1 instead of fetching and returning data from the query… :confused:

Is it something wrong in my database class?

I’m not familiar with mysqli (I use PDO), but I would guess $stmt->fetch() is returning true (1) on success - I’d look up the fetch method in the manual and go from there. Maybe someone more familiar with it can help.

edit - just checked out the manual on that method and it looks as though you need to bind the variables and do a while loop over the results.

Thanks. I will have a look and try it again :slight_smile: