When is called private constructor in PHP5 class? - php

Let's say I'm writing a PHP (>= 5.0) class that's meant to be a singleton. All of the docs I've read say to make the class constructor private so the class can't be directly instantiated.
So if I have something like this:
class SillyDB
{
private function __construct()
{
}
public static function getConnection()
{
}
}
Are there any cases where __construct() is called other than if I'm doing a
new SillyDB()
call inside the class itself?
And why am I allowed to instantiate SillyDB from inside itself at all?

A private constructor makes sure you cannot instanciate this class outside of itself.
So calling
$obj = new SillyDB();
would result in an error.
This technique is usually used when creating singleton classes.
This stone old comment in the manual describes it pretty well: http://www.php.net/manual/de/language.oop5.decon.php#80314
You have a static method inside the class that manages a single instance of the class which can be retreived through the method.

Calling the static function within that class may run the construct from within the class.
class SillyDB
{
private function __construct()
{
$db->connect();
}
public static function getConnection()
{
self::__construct();
}
}
Running
SillyDB::getConnection()
will run the __construct() method and connect you to the db

Are there any cases where __construct() is called other than if I'm
doing a new SillyDB() call inside the class itself?
No.
And why am I allowed to instantiate SillyDB from inside itself at all?
Why would you not be allowed to?
The better question would be what use is it for a constructor that can only be called from inside its own class?. That is useful when you want to ensure total control of how instances are created, for example when you implement a singleton.

__construct() would only be called if you called it from within a static method for the class containing the private constructor. So for your Singleton, you might have a method like so:
class DBConnection
{
private static $Connection = null;
public static function getConnection()
{
if(!isset(DBConnection::$Connection))
{
DBConnection::$Connection = new DBConnection();
}
return DBConnection::$Connection;
}
private function __construct()
{
}
}
$dbConnection = DBConnection::getConnection();
The reason you are able/would want to instantiate the class from within itself is so that you can check to make sure that only one instance exists at any given time. This is the whole point of a Singleton, after all. Using a Singleton for a database connection ensures that your application is not making a ton of DB connections at a time.

You are able to call it because scope is checked based on class rather than instance (i.e. an object is able to access the private and protected methods/properties of another instance of the same class without issue)

Related

How to initialize object property on first access in php 7?

let's say i have this class:
class DB {
private DBConnection $connection;
private string $databaseName;
public function __construct($databaseName) {
$this->databaseName = $databaseName;
}
private function initConnection(): void {
if (!isset($this->connection)) {
$this->connection = new DBConnection($this->databaseName);
}
}
public function query($str): array {
$this->initConnection();
return $this->connection->query($str);
}
public function something(): void {
$this->initConnection();
$this->connection->something();
}
}
Then I have some code that may or may not call the ->query / ->something methods, depending on the situation.
How can I avoid calling initConnection in every single place where $this->connection has to be used?
I only want to initialize it IF it is accessed, on its first access.
I already tried with the magic method __get but it only gets called if $connection is not in the class definition.
Another way is to create an accessor method (getConnection) which calls initConnection and use $this->getConnection() (returning the connection object, instead of void) instead of $this->connection, but still, initConnection would get called for nothing.
The problem with this approach is that I could accidentally still use $this->connection instead of the getter method; is there a way I can disallow the direct use of the property?
In my opinion, you are correct by concluding to use a (private) getConnection method to instantiate the connection only on its first use. And have the rest of the class method use getConnection() instead of $this->connection.
However, I also believe you are experiencing the downside of the singleton pattern. Perhaps consider refactoring out the actual DBConnection, and pass it as a constructor argument (dependency) to DB. You can then provide implementations of DBConnection that support lazy loading and singletons.

How to access other classes methods within a class (PHP)

I'm building a PHP OOP MVC Framework for personal use.
I'd like to know if there's a way or the correct implementation of the following:
Being able to call a method from a "subclass" (not extended) to another "subclass". I mean...
I have a class that creates new objects for each subclass instead of using inheritance. Let's call it MainClass, and it has 3 "SubClasses" like this
class MainClass {
public $db;
public $forms;
public $pagination;
function __construct() {
$this->db = new class_db();
$this->forms = new class_forms();
$this->utils = new class_utils();
}
}
And the initialization which is
$MainClass = new MainClass();
I can do for example
$MainClass->utils->show_text("Hello World");
And works fine.
What I'd like to do is... within the $MainClass->utils->test() (Another test method), is to be able to access $MainClass->db without using global or $GLOBALS.
Is there any alternative way of achieving this? To be able to access $MainClass methods and submethods within another submethod (access db from utils and from the main page where MainClass is initialized)? How would it be? I want to be able to access al the submethods, like utils being able to access db and forms method. as well as the pages that are outside MainClass.
Thank you
If utils has to use db, you either have to pass the MainClass instance to utils, so it can call $this->myRefToMain->db, or pass the instance of db itself, so utils can call $this->db. Either way, it cannot (reasonably) crawl up the call stack to find the object that called it.
Object if your class class_utils can exists without MainClass. And its method test() should access some object db of class class_db. This means class_utils depends on class_db and you should inject object of class_db in constructor, for example:
class MainClass {
public $db;
public $forms;
public $pagination;
function __construct() {
$this->db = new class_db();
$this->forms = new class_forms();
$this->utils = new class_utils($this->db);
}
}

How do I instantiate my database object, to be used in other classes?

I've encountered an architectural issue with my application. I've rolled my own (very basic) MVC, and one of my models is a database object: class MySQLDatabase { }
There's a number of places in which I'd want to use my database object, without creating duplicate instances. Inside my controller, I have declared public $db; and within the __construct { } I have $this->db = new MySQLDatabase;
Question:
How do I use $db within my other classes--they're all instantiated within the controller's __construct { } as well... would I declare global $db at the top of all my classes that require database connectivity?
I'm used to global variables being declared in the global scope as regular variables, and then using the global keyword to reference the global scope... I'm not sure if that applies to variables declared within a class (my controller.)
I would stay away from using globals or the Singleton pattern (which is essentially a global anyway), and try and find some alternatives. Additionally you are talking about a database connection, by using the Singleton pattern you are saying that there will never be more than one database connection, whilst that is generally true in smaller applications, as they grow larger you won't be able to accomodate multiple connections.
Once you make something global then you lose the automatic contraints of where it can be used/modified. Using MVC a view shouldn't be used for anything other than to display data, by using a global/singleton it is up to the developer to not make use of the globals. Whereas with a different design they don't have that option.
You mentioned you've created your own MVC framework, so I imagine the classes you want to use it in are your models? Correct me if they are anywhere else.
If your models extend from a common base class then you could pass your database object to that class as a static variable which can be assigned to any new instances in the construct or using a factory method in the factory method.
This isn't to say that globals or singletons should be avoided at all costs, but definitely try consider the alternatives that could lead to a neater design.
Here's some reading on the Singleton pattern if you're interested:
Patterns I Hate #1: Singleton
Why Singletons are Evil
Singleton Considered Stupid
Use your singletons wisely
There are many more out there...
If I understand correctly you have a single controller that instantiates the database object and it also takes care of instantiating other classes. If so, you could implement some form of dependency injection either passing the db object in the constructor of the other classes or creating a setter method.
A good blog article on the subject:
http://www.potstuck.com/2009/01/08/php-dependency-injection/
I Think you going about this the wrong way, you should not be performaing quesries to the database from you controller.
this means that the below is invalid.
class ControllerIndex extends Controller
{
public function index()
{
$this->db->selectAll("table");
}
}
There should be a layer that separates your controller from your database interface, this is where a Model comes in.
You should have a models folder that contain classes for actions taken such as users,posts,logging etc.
class Users_Model extends Model
{
public function getUser($id)
{
}
}
The model class should be part of your system core, and should extend your Database Class, this way within your main controller you should be loading the models via the ModelLoader class.
for example:
class ModelLoader
{
private $models = array();
public function __get($model)
{
//load (/application/models/?.php) and initiate it here
//Storing it in models array above
}
}
Then in your main controller:
class Controller
{
private $model;
public function __construct()
{
$this->model = new ModelLoader;
}
}
this way your bringing your loader into scope for the child controller:
class Controller_index extends Controller
{
public function index()
{
$user = $this->model->users->getUser(22);
}
}
Hope this helps!
I think what you need here is a singleton for you Database object :)
See here for more details : http://en.wikipedia.org/wiki/Singleton_pattern
Edit with sample singleton for php :
<?php
class UniqueObject {
private $_uniq = null;
//private cause you don't want to instanciate the classic way
private function __construct() {
//...
}
//check if unique object exists or not, then return it
public static function uniq() {
if(!self::$_uniq)
self::$_uniq = new UniqueObject();
return self::$_uniq;
}
}
//call your unique object whenever you need it
UniqueObject::uniq();
?>
(it's late, i hope i didn't do any mistake :))
Don't use singletons. It's much better to explicitly pass around data. For example:
abstract class Controller {
private static $conn; // could be an array for multiple connections
final protected function getDBConnection() {
if (!$this->conn) {
$this->conn = new DBConnection();
}
return $this->conn;
}
abstract public function process(Request $r);
}
class HomePageController extends Controller {
public function process(Request $r) {
$results = $this->getDBConnection()->query('SELECT stuff FROM foo;');
// do stuff with $results
}
}
You could also have an explicit model object you pass around, e.g. the one that represents the user, but that may be overkill for your project.
You'll need to use a singleton pattern. They give examples in the php docs
<?php
class Example
{
// Hold an instance of the class
private static $instance;
// A private constructor; prevents direct creation of object
private function __construct()
{
echo 'I am constructed';
}
// The singleton method
public static function singleton()
{
if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
}
return self::$instance;
}
// Example method
public function bark()
{
echo 'Woof!';
}
// Prevent users to clone the instance
public function __clone()
{
trigger_error('Clone is not allowed.', E_USER_ERROR);
}
}
?>
http://php.net/manual/en/language.oop5.patterns.php

How should I make up for the lack of static initializers in PHP?

I'm thinking about putting every class into a separate file and doing the static initialization outside of the class definition.
The problem with this is the fact that the initialization will happen before the said class is actually needed (it will happen when the file which contains the class is included for the first time). It's a problem, because it may happen that the class won't be used at all, hence the initialization is unnecessary. And I think the practice of including the used files not in the beginning of your code is simply a dirty technique.
If anybody has a viable solution to this problem, I would greatly appreciate it.
Take a look at code like this. It uses a singleton instance of the database yet you can still create instances of the class:
class DB
{
private static $_db;
public static function pdo()
{
if (is_null(self::$_db))
{
self::$_db=new PDO('pgsql:host=localhost;
port=5432;
dbname=testdb;
user=postgres;
password=abc123');
self::$_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
return self::$_db;
}
public static function factory()
{
return new self;
}
private function __construct() {}
When you want an instance of the DB, you do DB::factory(). DB uses self::pdo() to return the singleton to access the database.
The singleton answer has a problem here... if you use the static methods before an instance of the class is created... then the static initializer has not run... so you might say that one can run the initializer in every static method... that is true but what about just getting and setting static variables... I suppose one could also use __get and __set magic functions for this... there is just a problem with the language in that it does not support static intialization... here is the way it can be done...
in file Hi.php:
class Hi{
public static $v = "0";
}
Hi::$v= "2";
// end of Hi.php file
simple use a .php file per class... and do the static initialization outside of the
class definition...
You might look up for __autoload when a class is not found it is called and supposed to include the file contains that class. You can put static initializers in that function.
Use singleton pattern instead of static calls:
<?php
class Foo {
protected static $_instance;
/**
* #return Foo
*/
public static function getInstance() {
return (null === self::$_instance)?
self::$_instance :
self::$_instance = new Foo();
}
protected function __construct() {
// initialize here
}
}
This is the way I do this. The only cost is testing the $doneinit static member each time you construct.
class Foo {
private static $doneinit=false;
private static init() {
// do static initialisation here
self::$doneinit=true;
}
public function __construct() {
if (!self::$doneinit) self::init();
// go on to do the rest of construction
}
}
Simple, and it keeps everything within the class.

php5 extend main class and use statics

why I can't do like this?
<?php
class core {
public static $db;
function __construct() {
$this->db = new mysql('host', 'user', 'pw', 'db');
}
}
class stat extends core {
public static function log() {
core::$db->query("insert into mytable values(now())");
}
}
// do something
stat::log();
?>
By the looks of your code, because you don't assign anything into $db. The constructor is only called when you create an instance of the class, not with statics.
Also, why is your code even extending core? You don't need to extend it to use static methods/variables. Perhaps it would make more sense to actually make it an instance property, and use a new instance instead of static?
The core::__construct() method is only called when you call new core or new stat, invoking the creation of an object. You go straight to stat::log(), so core::$db has never been initialized.

Categories