简体   繁体   中英

PHP - Calling a class function from another class

I am trying to create an error handling class that can be called from other classes, however when I call a function within the error handling class from another class, I get the following error:

Fatal error: Call to a member function fetch_error_text() on null in (filepath_here) on line 361

Here is the code I have so far:

global $errhandle_func_call;    
class ERR_HANDLE
{
   public function __construct()
   {
   }

   public function fetch_error_text($err_text)
   {
       $err_text_display = $return_msg[$err_text];
       return "<h4>" . $err_text_display . "</h4>";
   }
}

$errhandle_func_call = new ERR_HANDLE();

class BASKET
{
    public function view_basket()
    {
        //some_code_here
        if($query->num_rows < 1)
        {
            echo $errhandle_func_call->fetch_error_text("basket_empty");
        }
    }
}

Thanks.

I do NOT recommend using globals...but this should fix it... global needs to be in EVERY function that uses it. Looks like you also need global for your $return_msg .

global $errhandle_func_call;    
class ERR_HANDLE
{
   public function __construct()
   {
   }

   public function fetch_error_text($err_text)
   {
       global $return_msg;
       $err_text_display = $return_msg[$err_text];
       return "<h4>" . $err_text_display . "</h4>";
   }
}

$errhandle_func_call = new ERR_HANDLE();

class BASKET
{
    public function view_basket()
    {
        global $errhandle_func_call;
        //some_code_here
        if($query->num_rows < 1)
        {
            echo $errhandle_func_call->fetch_error_text("basket_empty");
        }
    }
}

[UPDATE 2015-04-29] Just for the heck of it, a quick, crude, introduction to my recommendations...from Easiest to Hardest...I'm also going to change your casing as ALL_UPPERCASE is usually used to denote constants and I'm a bit OCD.

Static Error Class:

class ErrorType {
    const UserError     = 1;
    const NotFoundError = 2;

    public static function getMessage( $messageId ) {
        switch( $messageId ) {
            case self::UserError: return "user error";
            case self::NotFoundError: return "not found error";
        }
    }
}
class ErrorHandler {
    public static function fetchErrorText( $errorType) {
        return "<h4>".ErrorType::getMessage($errorType)."</h4>";
    }
}

ErrorHandler::fetchErrorText( ErrorType::UserError );

This is definitely the easiest and gets you away from globals. I added the ErrorType class to reduce the "magic strings" in your code by giving you constant values to pass into the function. This will help to avoid typos, etc., as your IDE can give you intellisense for it.

However, static classes are not friendly to Unit Tests. So, that's where "Inversion of Dependency" can come into play. The easiest way to invert your dependencies is a Service Locator because you don't have to be able to modify your constructor to be able to pass in an instance of an object. Some consider this an anti-pattern, but it's extremely useful in the right situations (eg Convention over Configuration, etc.).

Inversion of Dependency: Service Locator

//first thing you need for inversion is an interface
interface IHandleError {
    public function getErrorMessage( $errorType );
}

//imaginary ServiceLocator class, defining instance of interface to use
ServiceLocator::define( array(
    'IHandleError' => 'SomeNamespace\ErrorHandler'
) );

class Basket {
    public function viewBasket() {
        //grab it when you need it
        $errorHandler = ServiceLocator::get('IHandleError');
        if( query->num_rows < 1 ) {
            echo $errorHandler->getErrorMessage( ErrorType::BasketEmpty );
        }
    }
}

The ServiceLocator object is imaginary...but in its simplest form it's just an array of key => value, where the value points to a class...and the ::get() method instantiates an instance or singleton of the class. I'm doing more "magic strings" here, but didn't want to make it too convoluted.

Inversion of Dependency: Dependency Injection

Dependency Injection, on the other hand, is simpler than the ServiceLocator in concept...but sometimes harder in implementation because you need access to modify the constructor of the class and be able to modify its instantiations to pass in the object.

class Basket {
    private $_errorHandler;
    public function __construct( IHandleError $errorHandler ) {
        $this->_errorHandler = $errorHandler;
    }
}

$basket = new Basket( $errorHandler );

Any of these 3 will steer you away from globals and improve the maintainability of your code a bit.

Why not use heritage ? Your class BASKET can extends from ERR_HANDLE :

class BASKET  extends ERR_HANDLE { 
      public function view_basket() {
          //some_code_here 
          if($query->num_rows < 1) { 
                  echo parent::fetch_error_text("basket_empty"); 
          } 
      }  
   }

Dependency Injection (DI) Is Easy

Depending on weather your class requires a dependency or if it is optional depends on how you 'inject' it. Note that a 'dependence' could be either an object (an instantiated class) or a property (a class member variable).

As it looks, your 'BASKET' class requires the 'ERR_HANDLE" class for it to work correctly thus it would be 'best practice' for you to use 'constructor injection'. Constructor injection mean 'injecting' it into the 'construct' method of the class.

This (dependency injection) allows for separation of concerns amongst many, many other things.

ErrorHandle.php

<?php

class ERR_HANDLE
{
    public function fetch_error_text($err_text)
    {
        $err_text_display = $return_msg[$err_text];
        return "<h4>" . $err_text_display . "</h4>";
    }
}

Basket.php

<?php

class BASKET
{
    private $err_handle;

    public function __construct($err_handle)
    {
        $this->err_handle = $err_handle;
    }

    public function view_basket()
    {
        //some_code_here

        if($query->num_rows < 1)
        {
            // The next line is where you use the injected objects method.
            return $this->err_handle->fetch_error_text("basket_empty");
        }
    }
}

Then The Actual Injection

<?php

// Instantiate the error handle class.
$err_handle = new ERR_HANDLE();

// Whilst instantiating the BASKET class, inject the ERR_HANDLE object.
$basket = new Basket($err_handle);

// View the basket.
echo $basket->view_basket();

Now, the final problem you should be able to solve is how to get the variable '$return_msg' into your 'ERR_HANDLE' class. PS: Inject it.

I recommend reading up on namespaces , becoming more familiar with classes and objects and reading about dependency injection.

Never stop learning...

Doesn't look like you're actually instantiating an instance of the ERR_HANDLE inside the scope you're trying to run its member function from.

$errhandle_func_call is being created outside of the BASKET class scope, but that object needs to be available within that public function of the BASKET class

you can use a autoload file to load all the ".class.php" file inside a directory by using this code.

<?
    function __autoload($classname)
    {
        require_once(strtolower($classname).'.class.php');
    }
        $first_class = new first_class();
        $second_class = new second_class();

    ?>

so you put all the class file in the same directory that this file. and you just include this file in all the page where you want to use any class

You can do this another way.

<?php

class error {

    public function hes_dead() {
        // deal with error
    }

}

class needs_error_class {

    private $err;
    public function __construct() {

        // This is called dependency injection.
        $this->err = new Error;

    }

    public function some_function() {

        // need that error handing now
        $this->err->hes_dead();
    }
}

Alternatively you can try this way to call your class.

class ERR_HANDLE
{
   public function empty_basket_error_text($err_text)
   {
       return "<h4>".$err_text."</h4>";
   }
}
class BASKET
{
    public function view_basket()
    {
        //some_code_here
        //if($query->num_rows < 1)
        // {
            $this->errhandle = new ERR_HANDLE();
            return $this->errhandle->empty_basket_error_text("Empty basket");
        // }
    }
}
$basket = new BASKET();
echo $basket->view_basket();

http://3v4l.org/U2fsH

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM