Fatal error: Call to a member function real_escape_string() on a non-object in

from lynda.com php object oriented tutorial

class.Address.inc
<?php

/**
 * Physical address. 
 */
class Address {
  
  const ADDRESS_TYPE_RESIDENCE = 1;
  const ADDRESS_TYPE_BUSINESS = 2;
  const ADDRESS_TYPE_PARK = 3;
  
  // Address types.
  static public $valid_address_types = array(
    Address::ADDRESS_TYPE_RESIDENCE => 'Residence',
    Address::ADDRESS_TYPE_BUSINESS => 'Business',
    Address::ADDRESS_TYPE_PARK => 'Park',
  );
  
  // Street address.
  public $street_address_1;
  public $street_address_2;
  
  // Name of the City.
  public $city_name;
  
  // Name of the subdivison.
  public $subdivision_name;
  
  // Postal code.
  protected $_postal_code;
  
  // Name of the Country.
  public $country_name;
  
  // Primary key of an Address.
  protected $_address_id;
  
  // Address type id.
  protected $_address_type_id;
  
  // When the record was created and last updated.
  protected $_time_created;
  protected $_time_updated;
  
  /**
   * Constructor.
   * @param array $data Optional array of property names and values.
   */
  function __construct($data = array()) {
    $this->_time_created = time();
    
    // Ensure that the Address can be populated.
    if (!is_array($data)) {
      trigger_error('Unable to construct address with a ' . get_class($name));
    }
    
    // If there is at least one value, populate the Address with it.
    if (count($data) > 0) {
      foreach ($data as $name => $value) {
        // Special case for protected properties.
        if (in_array($name, array(
          'time_created',
          'time_updated',
        ))) {
          $name = '_' . $name;
        }
        $this->$name = $value;
      }
    }
  }
  
  /**
   * Magic __get.
   * @param string $name 
   * @return mixed
   */
  function __get($name) {
    // Postal code lookup if unset.
    if (!$this->_postal_code) {
      $this->_postal_code = $this->_postal_code_guess();
    }
    
    // Attempt to return a protected property by name.
    $protected_property_name = '_' . $name;
    if (property_exists($this, $protected_property_name)) {
      return $this->$protected_property_name;
    }
    
    // Unable to access property; trigger error.
    trigger_error('Undefined property via __get: ' . $name);
    return NULL;
  }
  
  /**
   * Magic __set.
   * @param string $name
   * @param mixed $value 
   */
  function __set($name, $value) {
    // Only set valid address type id.
    if ('address_type_id' == $name) {
      $this->_setAddressTypeId($value);
      return;
    }
    // Allow anything to set the postal code.
    if ('postal_code' == $name) {
      $this->$name = $value;
      return;
    }
    
    // Unable to access property; trigger error.
    trigger_error('Undefined or unallowed property via __set(): ' . $name);
  }
  
  /**
   * Magic __toString.
   * @return string 
   */
  function __toString() {
    return $this->display();
  }
  
  /**
   * Guess the postal code given the subdivision and city name.
   * @todo Replace with a database lookup.
   * @return string 
   */
  protected function _postal_code_guess() {
    $db = Database::getInstance();
    $mysqli = $db->getConnection();
    $sql_query = 'SELECT postal_code ';
    $sql_query.= 'FROM location ';
    
    $city_name = $mysqli->real_escape_string($this->city_name);
    $sql_query .= 'WHERE city_name = "' . $city_name . '" ';
    
    $subdivision_name = $mysqli->real_escape_string($this->subdivision_name);
    $sql_query .= 'AND subdivision_name = "' .$subdivision_name . '"';
    
    $result = $mysqli->query($sql_query);
    
    if ($row = $result->fetch_assoc()){
      return $row['postal_code'];
    }
  }
  
  /**
   * Display an address in HTML.
   * @return string 
   */
  function display() {
    $output = '';
    
    // Street address.
    $output .= $this->street_address_1;
    if ($this->street_address_2) {
      $output .= '<br/>' . $this->street_address_2;
    }
    
    // City, Subdivision Postal.
    $output .= '<br/>';
    $output .= $this->city_name . ', ' . $this->subdivision_name;
    $output .= ' ' . $this->postal_code;
    
    // Country.
    $output .= '<br/>';
    $output .= $this->country_name;
    
    return $output;
  }
  
  /**
   * Determine if an address type is valid.
   * @param int $address_type_id
   * @return boolean
   */
  static public function isValidAddressTypeId($address_type_id) {
    return array_key_exists($address_type_id, self::$valid_address_types);
  }
  
  /**
   * If valid, set the address type id.
   * @param int $address_type_id 
   */
  protected function _setAddressTypeId($address_type_id) {
    if (self::isValidAddressTypeId($address_type_id)) {
      $this->_address_type_id = $address_type_id;
    }
  }
}

Open in new window


class.Database.inc
<?php
/**
* mysqli database: only one connection is allowed
*/
class Database{
  private $_connection;
  //store the single instance
  private static $_instance;
  /**
  * get an instance of the database
  * @return Database
  * 
  */
  public static function getInstance(){
    if (!self::$_instance){
      self::$_instance= new self();
    }
    return self::$_instance;
  }
  /**
  * constructor
  */
  public function __construct(){
    $this->connection = new mysqli('localhost','username','pass','dbname');
    //error handling
    if (mysqli_connect_error()){
      trigger_error('Failed to connect to MySql: '.mysqli_connect_error(),E_USER_ERROR);
    }

  }
  
  
    /**
    * Empty clone magic method to prevent duplication.
    */
    private function __clone(){}
    /**
    * get the mysqli connection
    */
    public function getConnection(){
      return $this->_connection;
    }  
}

Open in new window


demo.php
<?php

require 'class.Address.inc';
require 'class.Database.inc';
echo '<h2>Instantiating Address</h2>';
$address = new Address;

echo '<h2>Setting properties...</h2>';
$address->street_address_1 = '555 Fake Street';
$address->city_name = 'Townsville';
$address->subdivision_name = 'State';
$address->country_name = 'United States of America';
$address->address_type_id = 1;
echo $address;

echo '<h2>Testing Address __construct with an array</h2>';
$address_2 = new Address(array(
  'street_address_1' => '123 Phony Ave',
  'city_name' => 'Villageland',
  'subdivision_name' => 'Region',
  'postal_code' => '67890',
  'country_name' => 'Canada',
));
echo $address_2;

Open in new window



ouput
Instantiating Address
Setting properties...

Fatal error: Call to a member function real_escape_string() on a non-object in C:\wamp\www\Ex_Files_oophp-edit\04_05\class.Address.inc on line 134
LVL 1
rgb192Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Ray PaseurCommented:
Looks like the error occurs in here (this starts at line 128 of Address)

  protected function _postal_code_guess() {
    $db = Database::getInstance();
    $mysqli = $db->getConnection();
    $sql_query = 'SELECT postal_code ';
    $sql_query.= 'FROM location ';
    
    $city_name = $mysqli->real_escape_string($this->city_name);
    $sql_query .= 'WHERE city_name = "' . $city_name . '" ';
    
    $subdivision_name = $mysqli->real_escape_string($this->subdivision_name);
    $sql_query .= 'AND subdivision_name = "' .$subdivision_name . '"';
    
    $result = $mysqli->query($sql_query);
    
    if ($row = $result->fetch_assoc()){
      return $row['postal_code'];
    }
  }

Open in new window

Suggest you use our friend var_dump() to see what is contained in the variables.

It may not be related, but this code looks a bit goofy, like perhaps it is incomplete?  In the constructor at line 54 we have this:

trigger_error('Unable to construct address with a ' . get_class($name));

At that point the variable $name is undefined.  Code smell.
0
rgb192Author Commented:
var_dump($address)
object(Address)#1 (10) { ["street_address_1"]=> string(15) "555 Fake Street" ["street_address_2"]=> NULL ["city_name"]=> string(10) "Townsville" ["subdivision_name"]=> string(5) "State" ["_postal_code:protected"]=> NULL ["country_name"]=> string(24) "United States of America" ["_address_id:protected"]=> NULL ["_address_type_id:protected"]=> int(1) ["_time_created:protected"]=> int(1377861541) ["_time_updated:protected"]=> NULL }

I can not see the output for
var_dump($address2)



also when I comment $mysqli->real_escape_string

I get this new error

Fatal error: Call to a member function query() on a non-object in C:\wamp\www\Ex_Files_oophp-edit\04_05\class.Address.inc on line 140
0
Ray PaseurCommented:
See also line 23 of class Database.  The connection is made with OOP notation, but the error test is made with procedural code.  I don't know if that would cause an error to be overlooked because I would never try something like that.  I think you want to change the if (mysqli_connect_error()) to if ($mysqli->connect_error)
http://php.net/manual/en/mysqli.connect-error.php

public function __construct(){
    $this->connection = new mysqli('localhost','username','pass','dbname');
    //error handling
    if (mysqli_connect_error()){
      trigger_error('Failed to connect to MySql: '.mysqli_connect_error(),E_USER_ERROR);
    }

Open in new window

0
Exploring SharePoint 2016

Explore SharePoint 2016, the web-based, collaborative platform that integrates with Microsoft Office to provide intranets, secure document management, and collaboration so you can develop your online and offline capabilities.

Ray PaseurCommented:
Go back to this:
http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/Q_28226938.html#a39452073

Use var_dump() with $db and $mysqli in the postal code function.
0
rgb192Author Commented:
if ($mysqli->connect_error)

database still connects

in demo.php
var_dump($db);
var_dump($mysqli);

NULL NULL

I do not see any output when I add to postal code function
var_dump($db);
var_dump($mysqli);
0
ArgentiCommented:
Fatal error: Call to a member function [...] on a non-object...

Is your $mysqli variable properly initialized ??
0
ArgentiCommented:
well... you cannot query() on a NULL object, sorry :(
0
rgb192Author Commented:
when I put var_dump($db) after the $db = Database::getInstance();
object(Database)#2 (2) { ["_connection:private"]=> NULL ["connection"]=> object(mysqli)#3 (0) { }

var_dump($mysqli) after
 $mysqli = $db->getConnection();

NULL
0
rgb192Author Commented:
>>Is your $mysqli variable properly initialized ??

how to check
0
Ray PaseurCommented:
The constructor says

$this->connection

getConnection() says

return $this->_connection;

You need to get those two things to be the same, then try it again.  Probably you want to use the one with the leading underscore because of this:

private $_connection;

Also, are you using error_reporting(E_ALL) ?
0
rgb192Author Commented:
this is mysql, which I can see in mysql workbench query editor
CREATE TABLE IF NOT EXISTS `address` (
  `address_id` int(11) NOT NULL AUTO_INCREMENT,
  `address_type_id` int(11) NOT NULL,
  `time_created` int(11) NOT NULL,
  `time_updated` int(11) NOT NULL,
  `street_address_1` varchar(255) NOT NULL,
  `street_address_2` varchar(255) NOT NULL,
  `city_name` varchar(255) NOT NULL,
  `subdivision_name` varchar(255) NOT NULL,
  `postal_code` varchar(20) NOT NULL,
  `country_name` varchar(255) NOT NULL,
  PRIMARY KEY (`address_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;

INSERT INTO `address` (`address_id`, `address_type_id`, `time_created`, `time_updated`, `street_address_1`, `street_address_2`, `city_name`, `subdivision_name`, `postal_code`, `country_name`) VALUES
(1, 1, 762483360, 0, '300 Oracle Parkway', '', 'Redwood City', 'California', '94065', 'United States');

CREATE TABLE IF NOT EXISTS `location` (
  `location_id` int(11) NOT NULL AUTO_INCREMENT,
  `city_name` varchar(255) NOT NULL,
  `city_latitude` double NOT NULL,
  `city_longitude` double NOT NULL,
  `subdivision_name` varchar(255) NOT NULL,
  `postal_code` varchar(20) NOT NULL,
  PRIMARY KEY (`location_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ;

INSERT INTO `location` (`location_id`, `city_name`, `city_latitude`, `city_longitude`, `subdivision_name`, `postal_code`) VALUES
(1, 'Townsville', 38.7153408676386, -75.0866805016994, 'State', '12345'),
(2, 'Villageland', 33.1156463623047, -117.120277404785, 'Region', '67890'),
(3, 'Hamlet', 43.6666296, -92.9746367, 'Territory', '34567'),
(4, 'Redwood City', 37.5311965942383, -122.2646484375, 'California', '94065');

Open in new window

0
Ray PaseurCommented:
The queries are (probably) OK.  It's the use of an uninitialized variable that's causing the problem -- at least that's what I think.

Class Database, line 24

$this->_connection = new mysqli('localhost','username','pass','dbname');
0
rgb192Author Commented:
where does the

The constructor says

$this->connection


  function __construct($data = array()) {
    $this->_time_created = time();
    
    // Ensure that the Address can be populated.
    if (!is_array($data)) {
      trigger_error('Unable to construct address with a ' . get_class($name));
    }
    
    // If there is at least one value, populate the Address with it.
    if (count($data) > 0) {
      foreach ($data as $name => $value) {
        // Special case for protected properties.
        if (in_array($name, array(
          'time_created',
          'time_updated',
        ))) {
          $name = '_' . $name;
        }
        $this->$name = $value;
      }
    }
  }

Open in new window

0
rgb192Author Commented:
The queries are (probably) OK.  It's the use of an uninitialized variable that's causing the problem -- at least that's what I think.

Class Database, line 24

$this->_connection = new mysqli('localhost','username','pass','dbname');


I am using my database connection (which I do not show to experts-exchange.com)


now with
error_reporting(E_ALL)

output is
Instantiating Address
Setting properties...
object(Address)#1 (10) { ["street_address_1"]=> string(15) "555 Fake Street" ["street_address_2"]=> NULL ["city_name"]=> string(10) "Townsville" ["subdivision_name"]=> string(5) "State" ["_postal_code:protected"]=> NULL ["country_name"]=> string(24) "United States of America" ["_address_id:protected"]=> NULL ["_address_type_id:protected"]=> int(1) ["_time_created:protected"]=> int(1377863706) ["_time_updated:protected"]=> NULL }
Notice: Undefined variable: db in C:\wamp\www\Ex_Files_oophp-edit\04_05\demo.php on line 15
NULL
Notice: Undefined variable: mysqli in C:\wamp\www\Ex_Files_oophp-edit\04_05\demo.php on line 16
NULL
Notice: Use of undefined constant connect_error - assumed 'connect_error' in C:\wamp\www\Ex_Files_oophp-edit\04_05\class.Database.inc on line 26

Notice: Undefined variable: mysqli_ in C:\wamp\www\Ex_Files_oophp-edit\04_05\class.Database.inc on line 26
object(Database)#2 (2) { ["_connection:private"]=> NULL ["connection"]=> object(mysqli)#3 (0) { } } whatNULL
Fatal error: Call to a member function query() on a non-object in C:\wamp\www\Ex_Files_oophp-edit\04_05\class.Address.inc on line 140
0
Ray PaseurCommented:
I believe the error is in Class.Database.inc which is posted above with the original question.

Have a look at this:
object(Database)#2 (2) {
["_connection:private"]=> NULL
["connection"]=> object(mysqli)#3 (0) { }
}

What that tells me is that I am looking at the var_dump() output of an object instance of the Database class.  The object has two properties.  The _connection property is private and NULL (this is because it was declared private in the class definition, but no value was assigned).  The connection property contains an object instance of the mysqli class.  Note the difference between these two properties -- one starts with a leading underscore and is private.  The other is apparently accidentally created by a typographical error in the Database class constructor.
0
rgb192Author Commented:
So how could I
var_dump from the database class
or
edit the database class
0
Ray PaseurCommented:
Let's try this.  Go find the Database class that you're actually using in these tests.  Blot out the password, then post the entire Database class in the code snippet here.  I'll show you what I think needs to change.
0
rgb192Author Commented:
I do not understand

I posted class.Database.inc in the first part of the question

I also posted .sql file
http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/Q_28226938.html#a39452133
0
Ray PaseurCommented:
OK, I think I was confused by this:
I am using my database connection (which I do not show to experts-exchange.com)
I will post a new version of the class.Database.inc in a moment.  I just want to make sure you understand the change I'm making and how to find out where the error has been hiding.
0
Ray PaseurCommented:
See if this makes sense.
<?php // class.Database.inc
/**
* mysqli database: only one connection is allowed
*/
class Database
{
    private $_connection;
    //store the single instance
    private static $_instance;
    /**
    * get an instance of the database
    * @return Database
    */
    public static function getInstance(){
        if (!self::$_instance){
            self::$_instance= new self();
        }
        return self::$_instance;
    }
    /**
    * constructor
    */
    public function __construct(){
        $this->_connection = new mysqli('localhost','username','pass','dbname');
        //error handling
        if ($this->_connection->_connect_error){
            trigger_error('Failed to connect to MySql: '.mysqli_connect_error(),E_USER_ERROR);
        }
    }
    
    
    /**
    * Empty clone magic method to prevent duplication.
    */
    private function __clone(){}
    /**
    * get the mysqli connection
    */
    public function getConnection(){
        return $this->_connection;
    }  
}

Open in new window

0
Ray PaseurCommented:
I have tested this version of it.  The only changes I made before posting here are the authentication credentials.

The problem with this Database class is that it is not a "real" Singleton, in that it does not enforce the policy that requires only one connection to the data base.  Because it can be instantiated, you can wind up with multiple data base connections.  

In the next post, I will show you a real Singleton.

<?php // RWY_temp_rgb192.php
error_reporting(E_ALL);
/**
* mysqli database: only one connection is allowed (BUT THIS IS NOT A TRUE SINGLETON)
*/
class Database
{
    private $_connection;
    //store the single instance
    private static $_instance;
    /**
    * get an instance of the database
    * @return Database
    */
    public static function getInstance(){
        if (!self::$_instance){
            self::$_instance= new self();
        }
        return self::$_instance;
    }
    /**
    * constructor
    */
    public function __construct(){
        $this->_connection = new mysqli('localhost','user','pass','name');
        //error handling
        if ($this->_connection->connect_error){
            trigger_error('Failed to connect to MySql: '.mysqli_connect_error(),E_USER_ERROR);
        }
    }


    /**
    * Empty clone magic method to prevent duplication.
    */
    private function __clone(){}
    /**
    * get the mysqli connection
    */
    public function getConnection(){
        return $this->_connection;
    }
}
// CREATE INSTANCES
$db1 = new Database;
$db2 = $db1->getConnection();
$db3 = new Database;
$db4 = $db3->getConnection();
$db5 = $db3->getConnection();

// THIS WILL PROVE THAT THERE ARE TWO CONNECTIONS (NO ECHO)
if ($db2 === $db4) echo 'EQUAL';

// THIS WILL SHOW THAT CONNECTIONS FROM THE SAME INSTANCE ARE EQUAL
if ($db4 === $db5) echo 'CONNECTION EQUAL';

Open in new window

0
Ray PaseurCommented:
This is a true Singleton class.  It cannot be instantiated, therefore you cannot wind up with any instance of the object, much less several instances of the object.  And since it will always return a private class property representing the MySQLi connection, you can be sure that any access to the Database will be using the same connection.

I believe that the only thing we want back from this is the connection, not the internal instance, so I changed the getInstance() to eliminate that additional step.

<?php // RAY_database_singleton.php
error_reporting(E_ALL);


// SINGLETON DATA BASE CONNECTION CLASS
class Database
{
    // CLASS PROPERTIES ARE ALL PRIVATE
    private static $_connection;
    private static $_instance;

    // CONNECTION VALUES
    const DB_HOST = 'localhost';
    const DB_USER = 'user';
    const DB_PASS = 'pass';
    const DB_NAME = 'name';

    // NULLIFY THE CLONE
    private function __clone() {}

    // OUR ONLY PUBLIC METHOD RETURNS THE CONNECTION
    public static function getConnection()
    {
        if (!self::$_instance) self::$_instance = new self();
        return self::$_connection;
    }

    // CONSTRUCTOR RETURNS THE CONNECTION
    private function __construct()
    {
        self::$_connection
        = new mysqli
        ( self::DB_HOST
        , self::DB_USER
        , self::DB_PASS
        , self::DB_NAME
        )
        ;
        if (self::$_connection->connect_error)
        {
            trigger_error(self::$_connection->connect_error, E_USER_ERROR);
        }
    }
}

$mysql1 = database::getConnection();
$mysql2 = database::getConnection();


// PROVE THAT THESE ARE THE SAME OBJECT http://php.net/manual/en/language.oop5.object-comparison.php
if ($mysql1 === $mysql2) echo 'EQUAL';

Open in new window

HTH, ~Ray
0
rgb192Author Commented:
output of
http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/Q_28226938.html#a39452593

EQUAL
Instantiating Address
Setting properties...
object(Address)#3 (10) { ["street_address_1"]=> string(15) "555 Fake Street" ["street_address_2"]=> NULL ["city_name"]=> string(10) "Townsville" ["subdivision_name"]=> string(5) "State" ["_postal_code:protected"]=> NULL ["country_name"]=> string(24) "United States of America" ["_address_id:protected"]=> NULL ["_address_type_id:protected"]=> int(1) ["_time_created:protected"]=> int(1377885590) ["_time_updated:protected"]=> NULL }
Notice: Undefined variable: db in C:\wamp\www\Ex_Files_oophp-edit\04_05\demo.php on line 15
NULL
Notice: Undefined variable: mysqli in C:\wamp\www\Ex_Files_oophp-edit\04_05\demo.php on line 16
NULL
Fatal error: Call to undefined method Database::getInstance() in C:\wamp\www\Ex_Files_oophp-edit\04_05\class.Address.inc on line 129
0
rgb192Author Commented:
output of
http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/Q_28226938.html#a39452571

CONNECTION EQUAL
Instantiating Address
Setting properties...
object(Address)#5 (10) { ["street_address_1"]=> string(15) "555 Fake Street" ["street_address_2"]=> NULL ["city_name"]=> string(10) "Townsville" ["subdivision_name"]=> string(5) "State" ["_postal_code:protected"]=> NULL ["country_name"]=> string(24) "United States of America" ["_address_id:protected"]=> NULL ["_address_type_id:protected"]=> int(1) ["_time_created:protected"]=> int(1377885797) ["_time_updated:protected"]=> NULL }
Notice: Undefined variable: db in C:\wamp\www\Ex_Files_oophp-edit\04_05\demo.php on line 15
NULL
Notice: Undefined variable: mysqli in C:\wamp\www\Ex_Files_oophp-edit\04_05\demo.php on line 16
NULL object(Database)#6 (1) { ["_connection:private"]=> object(mysqli)#7 (0) { } } whatobject(mysqli)#7 (0) { }
Notice: Undefined variable: sql_query in C:\wamp\www\Ex_Files_oophp-edit\04_05\class.Address.inc on line 140

Warning: mysqli::query() [mysqli.query]: Empty query in C:\wamp\www\Ex_Files_oophp-edit\04_05\class.Address.inc on line 140

Fatal error: Call to a member function fetch_assoc() on a non-object in C:\wamp\www\Ex_Files_oophp-edit\04_05\class.Address.inc on line 142
0
rgb192Author Commented:
output of
http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/Q_28226938.html#a39452502

Instantiating Address
Setting properties...
object(Address)#1 (10) { ["street_address_1"]=> string(15) "555 Fake Street" ["street_address_2"]=> NULL ["city_name"]=> string(10) "Townsville" ["subdivision_name"]=> string(5) "State" ["_postal_code:protected"]=> NULL ["country_name"]=> string(24) "United States of America" ["_address_id:protected"]=> NULL ["_address_type_id:protected"]=> int(1) ["_time_created:protected"]=> int(1377885928) ["_time_updated:protected"]=> NULL }
Notice: Undefined variable: db in C:\wamp\www\Ex_Files_oophp-edit\04_05\demo.php on line 15
NULL
Notice: Undefined variable: mysqli in C:\wamp\www\Ex_Files_oophp-edit\04_05\demo.php on line 16
NULL
Notice: Undefined property: mysqli::$_connect_error in C:\wamp\www\Ex_Files_oophp-edit\04_05\class.Database.inc on line 26
object(Database)#2 (1) { ["_connection:private"]=> object(mysqli)#3 (0) { } } whatobject(mysqli)#3 (0) { }
Notice: Undefined variable: sql_query in C:\wamp\www\Ex_Files_oophp-edit\04_05\class.Address.inc on line 140

Warning: mysqli::query() [mysqli.query]: Empty query in C:\wamp\www\Ex_Files_oophp-edit\04_05\class.Address.inc on line 140

Fatal error: Call to a member function fetch_assoc() on a non-object in C:\wamp\www\Ex_Files_oophp-edit\04_05\class.Address.inc on line 142
0
Ray PaseurCommented:
You're trying to do too much at one time.  Simplify!  Let's create the SSCCE for this problem and then build the app up from that platform.

The problem, as I can see it, is that the class.Database.inc script is either wrong or being used wrong, so the first step will be to isolate that class and test it.  This script will do that for you.

1. Put your own credentials on line 26
2. Run it and see if it creates reasonable output (See the test query at line 51).
3. Assuming this creates reasonable output, copy line 4 through 44 and save them in place of your current copy of class.Database.inc
4. Take all of the var_dump() statements out of class.Address.inc and demo.php
5. Rebuild your application using the newly replaced class.Database.inc and try your tests again.

I cannot say that this is the only thing wrong; there may be many other things wrong.  Any time we see a code base with an undiscovered error and hundreds of lines of code, we cannot fix everything at once, so we have to take the problem apart.  If this change to class.Database.inc works correctly, we can then go on to the next problem.

<?php // RAY_temp_rgb192.php
error_reporting(E_ALL);

/**
* mysqli database: only one connection is allowed
*/
class Database
{
    private $_connection;
    //store the single instance
    private static $_instance;
    /**
    * get an instance of the database
    * @return Database
    */
    public static function getInstance(){
        if (!self::$_instance){
            self::$_instance= new self();
        }
        return self::$_instance;
    }
    /**
    * constructor
    */
    public function __construct(){
        $this->_connection = new mysqli('localhost','user','pass','name');
        //error handling
        if ($this->_connection->connect_error){
            trigger_error('Failed to connect to MySql: '.mysqli_connect_error(),E_USER_ERROR);
        }
    }


    /**
    * Empty clone magic method to prevent duplication.
    */
    private function __clone(){}
    /**
    * get the mysqli connection
    */
    public function getConnection(){
        return $this->_connection;
    }
}

// CREATE INSTANCE OF DATABASE AND GET CONNECTION OBJECT
$db = Database::getInstance();
$mysqli = $db->getConnection();

// RUN A QUERY
$sql = 'SELECT 1+1';
$res = $mysqli->query($sql);

// IF mysqli_query() RETURNS FALSE, LOG AND SHOW THE ERROR
if (!$res)
{
    $err
    = 'QUERY FAILURE:'
    . ' ERRNO: '
    . $mysqli->errno
    . ' ERROR: '
    . $mysqli->error
    . ' QUERY: '
    . $sql
    ;
    trigger_error($err, E_USER_ERROR);
}
// IF WE GET THIS FAR, THE QUERY SUCCEEDED AND WE HAVE A RESULT OBJECT IN $res
// AND SO WE CAN NOW USE $res IN OTHER MYSQLI FUNCTIONS


// DETERMINE HOW MANY ROWS OF RESULTS WE GOT
$num     = $res->num_rows;
$num_fmt = number_format($num);
if (!$num)
{
    echo "<br/>QUERY: $sql ";
    echo "<br/>FOUND NO DATA ";
    echo PHP_EOL;
}
else
{
    echo "<br/>QUERY: $sql ";
    echo "<br/>FOUND $num_fmt ROWS OF DATA ";
    echo PHP_EOL;
}
echo PHP_EOL;


// ITERATE OVER THE RESULTS SET AS AN OBJECT TO SHOW WHAT WE FOUND
echo PHP_EOL . 'USING MySQLi_Result::Fetch_<i>Object</i>(): ';
echo PHP_EOL;
while ($row = $res->fetch_object())
{
    // ROW BY ROW PROCESSING IS DONE HERE
    print_r($row);
    echo PHP_EOL;
}

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
rgb192Author Commented:
QUERY: SELECT 1+1
FOUND 1 ROWS OF DATA USING MySQLi_Result::Fetch_Object(): stdClass Object ( [1+1] => 2 )
Instantiating Address
Setting properties...
object(Address)#4 (10) { ["street_address_1"]=> string(15) "555 Fake Street" ["street_address_2"]=> NULL ["city_name"]=> string(10) "Townsville" ["subdivision_name"]=> string(5) "State" ["_postal_code:protected"]=> NULL ["country_name"]=> string(24) "United States of America" ["_address_id:protected"]=> NULL ["_address_type_id:protected"]=> int(1) ["_time_created:protected"]=> int(1377895740) ["_time_updated:protected"]=> NULL } object(Database)#1 (1) { ["_connection:private"]=> object(mysqli)#2 (0) { } } object(mysqli)#2 (0) { } object(Database)#1 (1) { ["_connection:private"]=> object(mysqli)#2 (0) { } } whatobject(mysqli)#2 (0) { }
Notice: Undefined variable: sql_query in C:\wamp\www\Ex_Files_oophp-edit\04_05\class.Address.inc on line 140

Warning: mysqli::query() [mysqli.query]: Empty query in C:\wamp\www\Ex_Files_oophp-edit\04_05\class.Address.inc on line 140

Fatal error: Call to a member function fetch_assoc() on a non-object in C:\wamp\www\Ex_Files_oophp-edit\04_05\class.Address.inc on line 142



output after commenting var_dump
QUERY: SELECT 1+1
FOUND 1 ROWS OF DATA USING MySQLi_Result::Fetch_Object(): stdClass Object ( [1+1] => 2 )
Instantiating Address
Setting properties...

Notice: Undefined variable: sql_query in C:\wamp\www\Ex_Files_oophp-edit\04_05\class.Address.inc on line 140

Warning: mysqli::query() [mysqli.query]: Empty query in C:\wamp\www\Ex_Files_oophp-edit\04_05\class.Address.inc on line 140

Fatal error: Call to a member function fetch_assoc() on a non-object in C:\wamp\www\Ex_Files_oophp-edit\04_05\class.Address.inc on line 142



I noticed that I added echo and var_dump in class.Address.inc and demo.php, changing the values so I copy paste those files

current version of class.Address.inc
<?php

/**
 * Physical address. 
 */
class Address {
  
  const ADDRESS_TYPE_RESIDENCE = 1;
  const ADDRESS_TYPE_BUSINESS = 2;
  const ADDRESS_TYPE_PARK = 3;
  
  // Address types.
  static public $valid_address_types = array(
    Address::ADDRESS_TYPE_RESIDENCE => 'Residence',
    Address::ADDRESS_TYPE_BUSINESS => 'Business',
    Address::ADDRESS_TYPE_PARK => 'Park',
  );
  
  // Street address.
  public $street_address_1;
  public $street_address_2;
  
  // Name of the City.
  public $city_name;
  
  // Name of the subdivison.
  public $subdivision_name;
  
  // Postal code.
  protected $_postal_code;
  
  // Name of the Country.
  public $country_name;
  
  // Primary key of an Address.
  protected $_address_id;
  
  // Address type id.
  protected $_address_type_id;
  
  // When the record was created and last updated.
  protected $_time_created;
  protected $_time_updated;
  
  /**
   * Constructor.
   * @param array $data Optional array of property names and values.
   */
  function __construct($data = array()) {
    $this->_time_created = time();
    
    // Ensure that the Address can be populated.
    if (!is_array($data)) {
      trigger_error('Unable to construct address with a ' . get_class($name));
    }
    
    // If there is at least one value, populate the Address with it.
    if (count($data) > 0) {
      foreach ($data as $name => $value) {
        // Special case for protected properties.
        if (in_array($name, array(
          'time_created',
          'time_updated',
        ))) {
          $name = '_' . $name;
        }
        $this->$name = $value;
      }
    }
  }
  
  /**
   * Magic __get.
   * @param string $name 
   * @return mixed
   */
  function __get($name) {
    // Postal code lookup if unset.
    if (!$this->_postal_code) {
      $this->_postal_code = $this->_postal_code_guess();
    }
    
    // Attempt to return a protected property by name.
    $protected_property_name = '_' . $name;
    if (property_exists($this, $protected_property_name)) {
      return $this->$protected_property_name;
    }
    
    // Unable to access property; trigger error.
    trigger_error('Undefined property via __get: ' . $name);
    return NULL;
  }
  
  /**
   * Magic __set.
   * @param string $name
   * @param mixed $value 
   */
  function __set($name, $value) {
    // Only set valid address type id.
    if ('address_type_id' == $name) {
      $this->_setAddressTypeId($value);
      return;
    }
    // Allow anything to set the postal code.
    if ('postal_code' == $name) {
      $this->$name = $value;
      return;
    }
    
    // Unable to access property; trigger error.
    trigger_error('Undefined or unallowed property via __set(): ' . $name);
  }
  
  /**
   * Magic __toString.
   * @return string 
   */
  function __toString() {
    return $this->display();
  }
  
  /**
   * Guess the postal code given the subdivision and city name.
   * @todo Replace with a database lookup.
   * @return string 
   */
  protected function _postal_code_guess() {
    $db = Database::getInstance();
    //var_dump($db);
    $mysqli = $db->getConnection();
    //echo 'what';
    //var_dump($mysqli);    
    $city_name = /*$mysqli->real_escape_string(*/$this->city_name/*)*/;
    //$sql_query .= 'WHERE city_name = "' . $city_name . '" ';
    
    $subdivision_name = /*$mysqli->real_escape_string(*/$this->subdivision_name/*)*/;
    //$sql_query .= 'AND subdivision_name = "' .$subdivision_name . '"';
    
    $result = $mysqli->query($sql_query);
    
    if ($row = $result->fetch_assoc()){
      return $row['postal_code'];
    }
    //var_dump($db);
//var_dump($mysqli);
  }
  
  /**
   * Display an address in HTML.
   * @return string 
   */
  function display() {
    $output = '';
    
    // Street address.
    $output .= $this->street_address_1;
    if ($this->street_address_2) {
      $output .= '<br/>' . $this->street_address_2;
    }
    
    // City, Subdivision Postal.
    $output .= '<br/>';
    $output .= $this->city_name . ', ' . $this->subdivision_name;
    $output .= ' ' . $this->postal_code;
    
    // Country.
    $output .= '<br/>';
    $output .= $this->country_name;
    
    return $output;
  }
  
  /**
   * Determine if an address type is valid.
   * @param int $address_type_id
   * @return boolean
   */
  static public function isValidAddressTypeId($address_type_id) {
    return array_key_exists($address_type_id, self::$valid_address_types);
  }
  
  /**
   * If valid, set the address type id.
   * @param int $address_type_id 
   */
  protected function _setAddressTypeId($address_type_id) {
    if (self::isValidAddressTypeId($address_type_id)) {
      $this->_address_type_id = $address_type_id;
    }
  }
}

Open in new window


current version of demo.php
<?php
error_reporting(E_ALL);
require 'class.Address.inc';
require 'class.Database.inc';
echo '<h2>Instantiating Address</h2>';
$address = new Address;

echo '<h2>Setting properties...</h2>';
$address->street_address_1 = '555 Fake Street';
$address->city_name = 'Townsville';
$address->subdivision_name = 'State';
$address->country_name = 'United States of America';
$address->address_type_id = 1;
//var_dump($address);
//var_dump($db);
//var_dump($mysqli);
echo $address;

echo '<h2>Testing Address __construct with an array</h2>';
$address_2 = new Address(array(
  'street_address_1' => '123 Phony Ave',
  'city_name' => 'Villageland',
  'subdivision_name' => 'Region',
  'postal_code' => '67890',
  'country_name' => 'Canada',
));
//var_dump($address2);
echo $address_2;

Open in new window

0
Ray PaseurCommented:
Looks like the _postal_code_guess() function is damaged by comments.  What should be in that function? Suggest you restore it to its original state and try the tests again
0
rgb192Author Commented:
Using wamp on my desktop and linux server I get the same warnings and fatal error.
0
Ray PaseurCommented:
Then you're probably not running the original script.  Please revert to the original script for class.Address.inc, check to be sure it has the correct query in the _postal_code_guess() function and try again.

You might want to learn about GIT or SubVersion so you don't run the risk of losing a prior version of your software to an accidental overwrite.
0
rgb192Author Commented:
with the original class.Address

all your examples worked except
http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/Q_28226938.html#a39452593

Fatal error: Call to undefined method Database::getInstance() in C:\wamp\www\Ex_Files_oophp-edit\04_05\class.Address.inc on line 129
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
PHP

From novice to tech pro — start learning today.