php ClassName::staticFunction()->publicFunction() What is this doing? - php

I'm reading up on php design patterns, and I saw this code:
<?php
require_once("DB.php");
class DatabaseConnection
{
public static function get()
{
static $db = null;
if ( $db == null )
$db = new DatabaseConnection();
return $db;
}
private $_handle = null;
private function __construct()
{
$dsn = 'mysql://root:password#localhost/photos';
$this->_handle =& DB::Connect( $dsn, array() );
}
public function handle()
{
return $this->_handle;
}
}
print( "Handle = ".DatabaseConnection::get()->handle()."\n" );
print( "Handle = ".DatabaseConnection::get()->handle()."\n" );
?>
I understand it all except the last two print statements. I've been messing around with it, but I don't understand the static function somehow calling a public non-static function.
I've notice I can do:
DatabaseConnection::get()->get()->get()->handle();
but I can't so something like:
DatabaseConnection::get()->handle()->get();
I just don't understand what this is doing, other than calling the get function then calling the handle function.

This works because the static function returns a new object. This type of construction is typically referred to as a Singleton, since it is attempting to enforce that only one instance of a DatabaseConnection is ever available.
Notice that the constructor is private, so you cannot explicitly call new DatabaseConnection() unless you are already inside the class. Solutions utilizing a Singleton will have a property, initally null, that is then set to a non-null value upon object instantiation. The 'getInstance' (or get in this case) method will only return a new object if the property is null.

DatabaseConnection::get() creates an instance of DatabaseConnection and returns it.
So...
DatabaseConnection::get()->handle();
...could also be written as follows...
$db = DatabaseConnection::get();
$db->handle();

Related

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:
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?
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 anything1. 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();

How do I create dynamic classes within a Factory class with variable input parameters?

Let me know if you have a better title for this question :)
Up till now, I've created my factory class by doing this:
include_once('menu.class.php');
include_once('gallery.class.php');
class Factory {
function new_menu_obj() { return new Menu(Conn::get_conn()); }
function new_gallery_obj($type ='', $id='') { return new Gallery(Conn::get_conn(), $type, $id); }
/* Many more defined functions here */
}
class Conn { // DB connection }
// To create a new class object I just do this
$menu = Factory::new_menu_obj();
$gallery= Factory::new_gallery_obj('some-type','3');
Now I'm trying to do this more dynamically by using this code:
include_once('menu.class.php');
include_once('gallery.class.php');
class Factory {
private $db_conn;
private function __construct() {
$this->db_conn = Conn::get_conn();
}
public function create( $class_name ) {
if ( $this->db_conn === null ) {
$this->db_conn = Conn::get_conn();
}
return new $class_name( $this->db_conn );
}
}
class Conn { // DB connection }
// To create a new class object I just do this
$menu = Factory->create("Menu");
$gallery= Factory->create("Gallery"); // How do I pass more parameters here?
Is this the "correct way" of being efficient? :)
How can I create a new object passing variables when I do not know how many variables needs to be passed? Using a array?
1 - Factory->anything will fail, I guess you mean Factory::something in your code. Anyway, with a private constructor, you won't be able to create any instance of the Factory class outside of some static Factory method...
2 - since you use static methods, use the static keyword in your function declaration should be wise.
3 - as a side and personnal note, I'm not sure why you would want to do that. The first flavour of your class does a real factory job : it creates a consistent set of classes. And you will use another flavour of factory to create another set of similar but still consistent classes.
E.g. the Factory class creates Menu and Gallery instances, the AltFactory class creates AltMenu and AltGallery instances and so on.
But you loose this benefit with the second version of your factory. Just calling the new operator on your classes instead of calling your create construct will give you exactly the same degree of dependencies, so...
Using reflection and adding a $params parameter to your create method:
class Factory {
private $db_conn;
private function __construct() {
$this->db_conn = Conn::get_conn();
}
public function create( $class_name, $params = []) {
if ( $this->db_conn === null ) {
$this->db_conn = Conn::get_conn();
}
array_unshift($params, $this->db_conn);
$reflect = new ReflectionClass($class_name);
return $reflect->newInstanceArgs($params);
}
}
class Conn { // DB connection }
$menu = Factory->create("Menu");
$gallery= Factory->create("Gallery", ['pics' => ... ])

Setting a static member of a variable class in php

I'm trying to add a static member to each of my classes that contains the default database connection that they should use when instantiated. Here's how I'm trying to do it:
<?php //other classes extend Generic
class Generic {
public static $defaultDatabase;
public $db;
function __construct (&$pDatabase = null){
if ($pDatabase!==null)
$this->db = &$pDatabase;
else
$this->db = &$defaultDatabase;
}
}
?>
<?php
include_once("/classes/class.Database.php");
$db = new Database ("localhost", "username", "password", "TestDatabase");
$classes = array("Generic", "Member");
foreach ($classes as $class){
include_once("/classes/class.$class.php");
$class::defaultDatabase = &$db;//throws error here, unexpected "="
}
?>
What am I doing wrong? Is there a better way to do this, or do I have to set the defaultDatabase for each class individually? I'm using php 5.3, which I understand should support something like this.
In this code
$class::defaultDatabase = &$db
You should add $ before defaultDatabase, since static properties are accessed via
ClassName::$staticProperty
Unlike the others which are accessed via
$class->property;
Use self::$propertyName to access static properties:
function __construct (&$pDatabase = null){
if ($pDatabase!==null)
$this->db = &$pDatabase;
else
$this->db = self::$defaultDatabase;
}
Also note, that using the reference operator &$var is meaningless if $var is an object. This is because all objects in PHP are actually references.

PHP constructors and static functions

I'm a bit confused on how constructors work in PHP.
I have a class with a constructor which gets called when I instantiate a new object.
$foo = new Foo($args);
__construct($params) is called in the class Foo and it executes the appropriate initialization code.
However when I use the class to call a static function, the constructor is called again.
$bar = Foo::some_function(); //runs the constructor from Foo
This causes the constructor to execute, running the object initialization code that I intended only for when I create a new Foo object.
Am I missing the point of how constructors work? Or is there a way to prevent __construct() from executing when I use the class to make static function calls?
Should I use a "factory" function instead to do the object initialization? If so, what's the point of the constructor then?
::EDIT::
I have a form where users can upload photos to an album (create_photo.php) and an area where they can view the album (view_photos.php). Upon form submit:
$photo = new Photo($_FILES['photo'], $_POST['arg1'], ect..);
The Photo constructor creates and saves the photo. However in view_photo.php, when I call:
$photo = Photo::find_by_id($_POST['id']) //user-defined function to query database
This is causing Photo's constructor to run!
I see nothing that replicates your question.
See Demo: http://codepad.org/h2TMPYUV
Code:
class Foo {
function __construct(){
echo 'hi!';
}
static function bar(){
return 'there';
}
}
echo Foo::bar(); //output: "there"
Assumption
PHP 5.x
Different goals, different path
create a new instance of a class (object)
class myClassA
{
public $lv;
public function __construct($par)
{
echo "Inside the constructor\n";
$this->lv = $par;
}
}
$a = new myClassA(11);
$b = new myClassA(63);
because we create a new object PHP calls:
__construct($par);
of the new object, so:
$a->lv == 11
$b->lv == 63
use a function of a class
class myClassB
{
public static $sv;
public static function psf($par)
{
self::$sv = $par;
}
}
myClassB::psf("Hello!");
$rf = &myClassB::$sv;
myClassB::psf("Hi.");
now $rf == "Hi."
function or variabiles must defined static to be accessed by ::, no object is created calling "psf", the "class variable" sv has only 1 instance inside the class.
use a singleton created by a Factory (myClassA is above)
class myClassC
{
private static $singleton;
public static function getInstance($par){
if(is_null(self::$singleton)){
self::$singleton = new myClassA($par);
}
return self::$singleton;
}
}
$g = myClassC::getInstance("gino");
echo "got G\n";
$p = myClassC::getInstance("pino");
echo "got P\n";
Using the factory (getInstance) the first time we construct a new object having $par set to gino.
Using the factory the second time $singleton has already a value that we return. No new object is created (no __construct is called, less memory & cpu is used).
The value of course is an object instanceOf myClassA and don't forget:
myClassC::$singleton->lv == "gino"
Pay attention to singletons:
What is so bad about singletons?
http://www.youtube.com/watch?v=-FRm3VPhseI
By my answer I don't want promote/demote singleton. Simply from the words in the question, I made this calc:
"static"+"__construct"="singleton"!
Here is my workaround:
I put method construct() in static class. Notice, it is different than __construct() which I use in regular classes.
Each class is in own file, so I lazy load that file on first use of class. This gives me event of first use of class.
spl_autoload_register(function($class) {
include_once './' . $class . '.php';
if (method_exists($class, 'construct')) {
$class::construct();
}
});
I define class properties as array in a static method and call them via the method. I'm not sure if it's the best solution or not but works great.
Example:
class Foo
{
private static construct_method()
{
return [
'one' => 1,
'two' => 2
];
}
public static any_method()
{
return self::construct_method()['one'] + self::construct_method()['two'];
}
}
echo Foo::any_method(); // 3

A simpler singleton

All the singleton patterns I have seen use a reference to the object to determine if the object has been instantiated. However, if I am using a singleton to guarantee only one db connection, why not use the db connection resource link to do this? Here is the code I am using. (PS: it works fine). I use the comment to be able to search for my classes easily.
/*one*/
class one
{
public static $db;
private function __construct()
{
self::$db=new mysqli(DB_HOST, DB_USER, DB_PASS, DB_DATABASE);
}
public static function get()
{
if(self::$db==NULL)
{
new self();
}
return self::$db;
}
}
In PHP, a constructor does not return.
So your get method returns a one object, the first time it's called, then a mysqli object. Probably not what you want.
if( self::$_db == NULL )
{
return new self(); // Here you return an object of class one
}
else
{
return self::$_db; // Here you return an object of type mysqli
}
If you want to return the mysqli object, you do not need a singleton, as there is no need to create an instance of an object that's only here to return an instance of another object.
The registry pattern would be better in such a case.
If you need to provide methods (a wrapper for your DB object), then create a real singleton.
EDIT
I checked your updated code.
Now you return always the mysqli instance. But you do not need to instantiate your own object. That's completely useless...
If you really want to go with your kind of pattern, as golden said, in your static instance, checks if self::db is NULL. If yes, creates the mysqli instance, and assign it to self::db. Then returns it.
public static getDatabaseInstance()
{
if( self::$_db == NULL )
{
self::$_db = new mysqli( ... );
}
return self::$_db;
}
Also set the constructor private, so users won't be able to create useless instances of your class. Or better make it public and throw an exception:
public function __construct()
{
throw new Exception( 'This class is not supposed to be instantiated' );
}
Define it works fine :)
Try to:
compare object hashes returned from both of the methods (does matter when you use object cloning)
connect to DB using different credentials (e.g. in unit tests)
disconnect from the DB and reconnect again
reset the instance of the object (now creating 1000 objects using new fills the memory up)
tell some other developer to create in instance of this class, he will for sure look for one::getInstance() method. How do he guesses the behavior of this class?
Singletons are about global state. Looks like here you have global state + some mess.
Assuming I'm parsing your question correctly, you're asking if it's okay to use the nullity of $db to make sure that you only instantiate one; that's a perfectly valid way to do things, and in fact is what I would recommend. PHP null is explicitly intended to represent a variable with "no value" - a perfect fit for the uninitialized state of the singleton pattern.
Typically, these things will just be called by an intuitive name, e.g. SomeAppDbConn.
class one
{
private static $_selfInstance;
public $db;
private function __construct()
{
}
public function getDb()
{
if($this->db == null)
$this->db=new mysqli(DB_HOST, DB_USER, DB_PASS, DB_DATABASE);
return $this->db;
}
public static function getInstance()
{
if( !(self::$_selfInstance instanceof self) ) {
self::$_selfInstance= new self();
}
return self::$_selfInstance;
}
}
Access
$db = one::getInstance()->getDb();
As Macmade already pointed out, the constructor method doesn't return anything but an instance of the class. Assuming you always want the same instance of mysqli, here's how I'd do it.
class DBInstance
{
protected static $db;
private function __construct()
{
// intentionally empty
}
public static function get()
{
if(self::$db === NULL)
{
self::$db=new mysqli(DB_HOST, DB_USER, DB_PASS, DB_DATABASE);
}
return self::$db;
}
}
It most certainly is a singleton pattern with an added bonus of separating instantiation from the instantiated class.

Categories