简体   繁体   中英

What is the difference between a public constructor which calls a class method and a class method that calls another class method?

I'm a little new to OO programming and am having trouble grasping why one mechanism works and another does not.

I've create a simple class that is to return a MySQL database handle. My attempt at returning the handle directly from a constructor fails. But succeeds from either a class method or from a class(?) method after an instance has been created. Here's the class definition and the sample script

<?php

class HMMDatabaseHandle {

        private static $configfile = "config.json";

// uncomment for test 1
//      public function __construct () {
//              return self::get_handle_admin();
//      }

        public static function create() {
                return self::get_handle_admin();
        }

        private static function get_handle_admin() {
                $config = json_decode(file_get_contents(self::$configfile));
                $dbhost = $config->database->dbhost;
                $dbname = $config->database->dbname;
                $dbuser = $config->database->dbuser;
                $dbpass = $config->database->dbpass;

                try {
                        return new PDO("mysql:host=$dbhost;dbname=$dbname", $dbuser, $dbpass);
                }
                        catch(PDOException $e) {
                        echo $e->getMessage();
                }
        }
}

?>

And here is the test script I'm using:

<?php
require_once 'HMMDatabaseHandle.php';

// Test 1 - fails (uncomment constructor func) at call to prepare() with:
// PHP Fatal error:  Call to undefined method HMMDatabaseHandle::prepare()
//$dbh = new HMMDatabaseHandle();

// Test 2 - works when class creates default constructor
// i.e. no explicit __construct() func
// Fetching data from executed query is fine
//$db = new HMMDatabaseHandle();
//$dbh = $db->create();

// Works using static class methods rather than instance
$dbh = HMMDatabaseHandle::create();

$sth = $dbh->prepare('select data_title,track_id from master');
$sth->execute();
while($row = $sth->fetch(PDO::FETCH_ASSOC)) {
   ...
}

My questions are:

  1. Why can't I return the handle directly from a constructor when it seems so similar to calling the class method directly? Why does it matter whether the constructor calls the class method or my script calls it?

  2. If I create an instance with PHP's default constructor, am I really calling a class method with $db->create()?

I seem to be missing some fundamental concept here. Thanks in advance!

You can't return the handle from the constructor in that context because that would violate the defined behavior of new . new SomeClass(); will only ever return an instance of the class, regardless of what other methods are called in the constructor.

__construct() is a void method. It is not intended to return anything 1 . That doesn't mean that the other code in it doesn't get executed, just that your return is disregarded in the context of creating a new object. This makes sense as the primary purpose for the constructor is to provide a means to pass dependencies to the object. Sometimes it is used to do additional initialization/setup of the object, but many people believe it should not do any work other than assigning the given arguments to the object's properties. Either way, there should be no need for it to return anything.



1 You can actually call the __construct() method explicitly after you create the object, and then it will behave like a normal method and your return will work.

$db = new HMMDatabaseHandle();
$dbh = $db->__construct();
var_dump($dbh);  // PDO Object

This isn't a normal thing to do though, and I can't think of a scenario where it would be useful or desirable. I just wanted to point out that it is possible.

Why can't I return the handle directly from a constructor when it seems so similar to calling the class method directly?

If you were able to do that, then you wouldn't have an instance of HMMDatabaseHandle ; you'd have an instance of PDO . How would you access any other methods that HMMDatabaseHandle provides?


While I fully agree with @Don't Panic's answer, I need to also point out that you're mixing static and instance methods.

As a general rule of thumb, use the static keyword when you want to be able to call a method without instantiating the object first. If you want to actually create and use an object , then you can define you class like so and use $this-> (or $object-> if outside of the class) instead of :: to access instance properties and methods.

<?php
class HMMDatabaseHandle
{

    private $configfile = "config.json";


    public function __construct()
    {
        // You're not initializing anything in here, so this constructor is optional.
    }

    public function create()
    {
        return $this->get_handle_admin();
    }

    private function get_handle_admin()
    {
        $config = json_decode(file_get_contents($this->configfile));
        $dbhost = $config->database->dbhost;
        $dbname = $config->database->dbname;
        $dbuser = $config->database->dbuser;
        $dbpass = $config->database->dbpass;

        try {
            return new PDO("mysql:host=$dbhost;dbname=$dbname", $dbuser, $dbpass);
        }
        catch (PDOException $e) {
            echo $e->getMessage();
        }
    }
}

To actually execute this, you need to now instantiate new class.

$dbManager = new HMMDatabaseHandle();
$handle = $dbManager->create();

Finally, there is a trick you can employ to make your constructor chainable . Simply wrap it in brackets.

$handle = (new HMMDatabaseHandle())->create();

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