Singleton Class:
<?php
class db_singleton
{
const ORACLE_HOST = "SOMEIP";
const ORACLE_USER = "validuser";
const ORACLE_PASS = "validpass";
const ORACLE_DB = "SOMEIP/DBNAME";
private static $instance; // stores the oci_* instance
private function __construct() { } // block directly instantiating
private function __clone() { } // block cloning of the object
public static function call()
{
// create the instance if it does not exist
if(!isset(self::$instance))
{
// the ORACLE_* constants should be set to or
// replaced with your db connection details
self::$instance = oci_connect(self::ORACLE_USER, self::ORACLE_PASS, self::ORACLE_DB);
if(self::$instance->connect_error)
{
throw new Exception('Oracle connection failed: ' . self::$instance->connect_error);
}
}
// return the instance
return self::$instance;
}
public function __destruct() {
oci_close($instance);
}
public function queryresult($query)
{
$result_set_array =array();
$this->stmt = oci_parse($this->con, $query);
oci_execute($this->stmt);
while($row=oci_fetch_array($this->stmt,OCI_ASSOC+OCI_RETURN_NULLS))
{
$result_set_array[] = $row;
}
oci_free_statement($this->stmt);
return $result_set_array;
}
}
?>
When I try using singleton class with below code, it works perfect and fetch results.
$conn = db_singleton::call();
$stid = oci_parse($conn, 'SELECT * FROM somevalid_table');
oci_execute($stid);
while($result=oci_fetch_array($stid,OCI_ASSOC+OCI_RETURN_NULLS))
{
$result_set_array[] = $result;
}
Now, when I try extending my class using model, it throws exception
class Myclass Extends db_singleton{
public function someModel()
{
$result = parent::queryresult(" select * from somevalid_table");
return $result;
}
}
Exception:
Fatal error: Call to private db_singleton::__construct() from context 'someController'
I know that class cannot be instantiated having private constructor. __construct() functions are always called when an object is instantiated, so trying to do something like $x = new MyObject() will cause a fatal error with a private construction function.
I am using Singleton classes to prevent direct instantiation of an object. How can I overcome issue ? What would be the best solution ?
Thanks.
$x = new MyObject() will never work if your constructor is private in that class because __construct() is the first method that is invoked on object creation.
Create a public method
/**
* Singleton class
*
*/
final class UserFactory
{
/**
* Call this method to get singleton
*
* #return UserFactory
*/
public static function Instance()
{
static $inst = null;
if ($inst === null) {
$inst = new UserFactory();
}
return $inst;
}
/**
* Private ctor so nobody else can instance it
*
*/
private function __construct()
{
}
}
To use:
$fact = UserFactory::Instance();
$fact2 = UserFactory::Instance();
$fact == $fact2;
But:
$fact = new UserFactory()
PHP Class Script:
class db_singleton
{
const ORACLE_USER = "validuser";
const ORACLE_PASS = "validpass";
const ORACLE_DB = "SOMEIP/DBNAME";
private static $instance = null; // stores the oci_* instance
// private constructor
private function __construct() { } // block directly instantiating
private function __clone() { trigger_error('Clone is not allowed.', E_USER_ERROR); } // block cloning of the object
public static function getInstance()
{
// create the instance if it does not exist
if(!isset(self::$instance))
{
// the ORACLE_* constants should be set to or
// replaced with your db connection details
self::$instance = oci_connect(self::ORACLE_USER, self::ORACLE_PASS, self::ORACLE_DB);
if(self::$instance->connect_error)
{
throw new Exception('Oracle connection failed: ' . self::$instance->connect_error);
}
}
// return the instance
return self::$instance;
}
public static function queryresult($query)
{
$result_set_array =array();
$stmt = oci_parse(db_singleton::getInstance(), $query);
oci_execute($stmt);
while($row=oci_fetch_array($stmt,OCI_ASSOC+OCI_RETURN_NULLS))
{
$result_set_array[] = $row;
}
oci_free_statement($stmt);
return $result_set_array;
}
Now to prevent Fatal error: Call to private db_singleton::__construct(), I have added an empty constructor to my child class which is model class in my case. This will override the parent class constructor which is private.
class Myclass Extends db_singleton{
public function __construct() {}
public function someModel(){
$result = parent::queryresult(" select * from somevalid_table");
return $result;
}
}
Hope it helps someone.
Thanks.
Related
I have custom class with DI ImapClient $imapClient:
class MailBoxCleaner
{
public function __construct(ImapClient $imapClient)
{
}
}
And there is an facade class:
class ImapConnection {
public function __construct()
{
return new ImapClient();
}
}
I tried to use this like:
$MailBoxCleaner = new MailBoxCleaner(new ImapConnection());
But it does not work.
A constructor never return any data.
You have to create a getter method that return the instance of your ImapClient class, so you inject it in the other class.
Based on your code :
class ImapConnection {
private $imapClient = null;
public function __construct()
{
$this->imapClient = new ImapClient();
}
public function getImapClient(){
return $this->imapClient;
}
}
You can inject :
$idObj = new ImapConnection(); // Instanciation
$MailBoxCleaner = new MailBoxCleaner($idObj->get());
You also can use a "pattern" :
class ImapConnection {
private $instance = null;
private $imapClient = null;
private function __construct()
{
$this->imapClient = new ImapClient();
}
public static function getImapClient(){
if(is_null($this->instance){
$this->instance = new ImapConnection();
}
return $this->instance->get();
}
private function get(){
return $this->imapClient;
}
}
Then, you can use in your code :
$MailBoxCleaner = new MailBoxCleaner(ImapConnection::getImapClient());
How to make class structure like PDO or ORM
$query = DB::table('users')->select('name');
$users = $query->addSelect('age')->get();
OR
$stmt = $pdo->prepare($sql);
$stmt->bindvalue(':u',intval($_SESSION['userId']),PDO::PARAM_INT);
$stmt->execute();
What is returned in to $query or $stmt?
how to design class structure like them?
Thank you
EDIT
$query = DB::table('users')->select('name');
meaning :
function select(){
//
return $this;
}
what's returned in to $query for this structure :
$query->addSelect('age')->get();
PDO is done by returning a new class (PDOStatement) with its own methods (read more about it), but it would be the same as:
<?php
class ClassOne
{
private $connection;
public function __construct($database_stuff)
{
$this->connection = $database_stuff;
}
public function prepare($sql)
{
// Code that does something with the $sql
// Then return a new class
return new ClassTwo($this);
}
}
class ClassTwo
{
private $ClassOne;
public function __construct(ClassOne $Class)
{
$this->ClassOne = $Class;
}
public function execute()
{
// Code that does something with ClassOne
}
}
# Start that initial class
$Class = new ClassOne('database:type;host=example;etc=yadayada');
# Do class one method
$query = $Class->prepare("SELECT * FROM fake_table");
# $query is now ClassTwo, so you do method from ClassTwo
$query->execute();
The ability to chain methods together is achieved because the current method returns the object back in the form of $this:
<?php
class DBClass
{
protected $connection,
$value;
public function __construct($connection)
{
$this->connection = $connection;
}
public function prepare($value)
{
$this->value = $value;
# Return the object
return $this;
}
public function execute()
{
echo $this->value;
# Return the object
return $this;
}
}
$con = new DBClass("login creds");
$con->prepare("update stuff if stuff = 'things'")->execute();
?>
I have something like this:
class MyParent {
protected static $object;
protected static $db_fields;
public function delete() {
// delete stuff
}
public static function find_by_id($id = 0) {
global $database;
$result_array = self::find_by_sql("SELECT * FROM " . static::$table_name . " WHERE id=" . $database -> escape_value($id) . " LIMIT 1");
return !empty($result_array) ? array_shift($result_array) : false;
}
public static function find_by_sql($sql = "") {
global $database;
// Do Query
$result_set = $database -> query($sql);
// Get Results
$object_array = array();
while ($row = $database -> fetch_array($result_set)) {
$object_array[] = self::instantiate($row);
}
return $object_array;
}
private static function instantiate($record) {
$object = self::$object;
foreach ($record as $attribute => $value) {
if (self::has_attribute($attribute)) {
$object -> $attribute = $value;
}
}
return $object;
}
}
class TheChild extends MyParent {
protected static $db_fields = array('id', 'name');
protected static $table_name = "my_table";
function __construct() {
self::$object = new TheChild;
}
}
$child= TheChild::find_by_id($_GET['id']);
$child->delete();
I get this: Call to undefined method stdClass::delete() referring to the last line above. What step am I missing for proper inheritance?
You never actually instanciate the TheChild class, which should be done by
$var = new TheChild();
except in TheChild constructor itself.
So, the static $object field is never affected (at least in your example), so affecting a field to it (the line $object -> $attribute = $value; ) causes the creation of an stdClass object, as demonstrated in this interactive PHP shell session:
php > class Z { public static $object; }
php > Z::$object->toto = 5;
PHP Warning: Creating default object from empty value in php shell code on line 1
php > var_dump(Z::$object);
object(stdClass)#1 (1) {
["toto"]=>
int(5)
}
This object does not have a delete method.
And as said before, actually creating a TheChild instance will result in an infinite recursion.
What you want to do is this, probably:
class TheChild extends MyParent {
protected static $db_fields = array('id', 'name');
protected static $table_name = "my_table";
function __construct() {
self::$object = $this;
}
}
Edit: Your updated code shows a COMPLETE different Example:
class MyParent {
protected static $object;
public function delete() {
// delete stuff
}
}
class TheChild extends MyParent {
function __construct() {
self::$object = new TheChild;
}
}
$child = new TheChild;
$child->delete();
Calling "Child's" Constructor from within "Child's" Constructor will result in an infinite loop:
function __construct() {
self::$object = new TheChild; // will trigger __construct on the child, which in turn will create a new child, and so on.
}
Maybe - i dont know what you try to achieve - you are looking for:
function __construct() {
self::$object = new MyParent;
}
ALSO note, that the :: Notation is not just a different Version for -> - it is completely different. One is a Static access, the other is a access on an actual object instance!
Bellow is a PHP script.
I tried to implement the Observer pattern (without MVC structure)... only basic.
The error which is encountered has been specified in a comment.
First I tried to add User objects to the UsersLibrary repository. There was a error such as User::update() does not exists or something.
Why is that error encountered? What fix should be applied and how?
interface IObserver {
public function update(IObservable $sender);
}
interface IObservable {
public function addObserver(IObserver $obj);
public function notify();
}
class UsersLibrary implements IObservable {
private $container;
private $contor;
//private $z;
public function __construct() {//IObserver $a) {
$this->container = array();
$this->contor = 0;
echo "<div>[constructing UsersLibrary...]</div>";
$this->addObserver(new Logger());
//$this->z = $a;
}
public function add($obj) {
echo "<div>[adding a new user...]</div>";
$this->container[$this->contor] = $obj;
$this->contor++;
$this->notify();
}
public function get($index) {
return $this->container[$index];
}
public function addObserver(IObserver $obj) {
$this->container[] = $obj;
}
public function notify() {
echo "<div>[notification in progress...]</div>";
foreach($this->container as $temp) {
//echo $temp;
#################################################################
$temp->update(); //--------ERROR
//Fatal Error: Call to a member function update() on a non-object.
#################################################################
}
//$this->container[0]->update();
//$this->z->update($this);
}
}
class User {
private $id;
private $name;
public function __construct($id, $name) {
$this->id = $id;
$this->name = $name;
}
public function getId() {
return $this->id;
}
public function getName() {
return $this->name;
}
}
class Logger implements IObserver {
public function __construct() {
echo "<div>[constructing Logger...]</div>";
}
public function update(IObservable $sender) {
echo "<div>A new user has been added.</div>";
}
}
$a = new UsersLibrary(); //new Logger());
//$a->add(new User(1, "DemoUser1"));
//$a->add(new User(2, "DemoUser2"));
$a->add("Demo");
echo $a->get(0);
//echo $a->get(0)->getName();
Your User class is not implementing interface IObserver and therefore is not forced to have the method update().
You have to instantiate a new User() in order to add it to the UsersLibrary:
$library = new UsersLibrary();
$user = new User(1, "Demo");
$library->add($user);
Also, you are mixing Users and Loggers into your UsersLibrary container. Maybe think about separating the containers for them?
You are passing a string instead of an object in your $a->add() call. You should either pass in an object, or alter the code in UserLibrary::add() to wrap it's argument in an appropriate object (or do an object lookup of it sees a string, for instance find a user with that name).
$user = new User(1, "Demo");
$a = new UsersLibrary();
$a->add($user);
I have this code running on PHP 5.2.6
class Singleton {
static private $instance = false;
private $id = false;
protected function __construct() {
$this->id = uniqid();
}
static public function instance() {
if (!self :: $instance) {
self :: $instance = new self();
}
return self :: $instance;
}
public function get_id() {
return $this->id;
}
}
class Chucknorris extends Singleton {
}
echo "php version = ".phpversion()."<br>";
$singleton = Singleton::instance();
echo get_class($singleton)."<br>";
echo "singleton id = ".$singleton->get_id()."<br>";
$chucknorris = Chucknorris::instance();
echo get_class($chucknorris)."<br>";
echo "chucknorris id = ".$chucknorris->get_id()."<br>";
Here's the output
php version = 5.2.6
Singleton
singleton id = 4ea7dca7d8f23
Singleton
chucknorris id = 4ea7dca7d8f23
When I ask for an instance of Chucknorris, I always get the Singleton one. I'd like to find out a way to extend the Singleton.
I know we can use get_called_class method to do it but it comes only with PHP 5.3. Is there anyway I can extend a Singleton without redefining the design pattern in the extended classes ?
Your best bet in PHP < 5.3 is to use a Singleton Factory:
class Singleton
{
private $id = false;
public function __construct() {
$this->id = uniqid();
}
public function get_id() {
return $this->id;
}
}
class SingletonFactory
{
private static $instance_array = array();
public static function getInstance($class_name)
{
if (!isset(self::$instance_array[$class_name]))
{
self::$instance_array[$class_name] = new $class_name();
}
return self::$instance_array[$class_name];
}
}
class Chucknorris extends Singleton {}
$singleton = SingletonFactory::getInstance('Singleton');
echo get_class($singleton)."<br>";
echo "singleton id = ".$singleton->get_id()."<br>";
$chucknorris = SingletonFactory::getInstance('Chucknorris');
echo get_class($chucknorris)."<br>";
echo "chucknorris id = ".$chucknorris->get_id()."<br>";
The only downside here is that your Singleton constructor is public.. so that's a basic violation of that pattern.
Update:
Here's a version that removes the public constructor (warning: this is getting into messy/hacky/poor design territory)
class Singleton
{
private $id = false;
public function __construct() {
$back = debug_backtrace(false);
if (!isset($back[1]['class']) || $back[1]['class'] != 'SingletonFactory')
{
throw new Exception('Consturctor not available, use SingletonFactory::getInstance("CLASSNAME")');
}
$this->id = uniqid();
}
public function get_id() {
return $this->id;
}
}
class SingletonFactory
{
private static $instance_array = array();
public static function getInstance($class_name)
{
if (!isset(self::$instance_array[$class_name]))
{
self::$instance_array[$class_name] = new $class_name($class_name);
}
return self::$instance_array[$class_name];
}
}
class Chucknorris extends Singleton {}
$singleton = SingletonFactory::getInstance('Singleton');
echo get_class($singleton)."<br>";
echo "singleton id = ".$singleton->get_id()."<br>";
$chucknorris = SingletonFactory::getInstance('Chucknorris');
echo get_class($chucknorris)."<br>";
echo "chucknorris id = ".$chucknorris->get_id()."<br>";
$badchuck = new Chucknorris(); // Exception!
Why don't you simulate the get_class_function if it doesn't exist with 5.3 PHP version ?
This code may answer your question.
if (!function_exists('get_called_class')) {
function get_called_class() {
$bt = debug_backtrace();
$lines = file($bt[1]['file']);
preg_match(
'/([a-zA-Z0-9\_]+)::'.$bt[1]['function'].'/',
$lines[$bt[1]['line']-1],
$matches
);
return $matches[1];
}
}
abstract class Singleton {
private $id = false;
protected function __construct() {
$this->id = uniqid();
}
static public function instance() {
static $instances = array();
$called_class_name = get_called_class();
if (!isset($instances[$called_class_name])) {
$instances[$called_class_name] = new $called_class_name();
}
return $instances[$called_class_name];
}
public function get_id() {
return $this->id;
}
}
class Chucknorris extends Singleton {}
class Brucelee extends Singleton {}
echo "php version = ".phpversion()."<br>";
$chucknorris = Chucknorris::instance();
echo get_class($chucknorris)."<br>";
echo "chucknorris id = ".$chucknorris->get_id()."<br>";
$brucelee = Brucelee::instance();
echo get_class($brucelee)."<br>";
echo "brucelee id = ".$brucelee->get_id()."<br>";
You can redefine just the getinstance method (and the instance itself) in Chucknorris to get an instance of it instead of the parent, but I'm not exactly sure what your end goal is. Just change the extending class to:
class Chucknorris extends Singleton {
static private $instance = false;
static public function instance()
{
if (!self :: $instance) {
self :: $instance = new self();
}
return self :: $instance;
}
}
Is this what you want? And if so - what is the reason you want it? I could think of a few, but would be glad if you share you goal.
Your code will most likely work if you move static private $instance = false; to the subclass and make it protected instead of private.
You also need to replace self:: with static:: so the static var is set in the subclass.
This requires PHP 5.3 - however, this shouldn't be a problem because PHP 5.2 reached end-of-life/support (that includes security updates!) as of january 2011!