I'm working in a project that works with OOP in PHP.
The problem is that when I try to instantiate two subclasses from an abstract class, only the first instance and not both.
DataBase.php:
abstract class DataBase {
private $_connection;
private static $_singleton = false;
/**
* Fetch an instance of the class.
*/
public final static function connect () {
if ( self::$_singleton === false ) {
self::$_singleton = new static();
}
return self::$_singleton;
}
}
UserDataBaseManager.php:
require_once 'DataBase.php';
class UserDataBase extends DataBase { (...) }
ImageDataBaseManager.php:
require_once 'DataBase.php';
class UserDataBase extends DataBase { (...) }
So, when I'm trying to instantiate ImageDataBase and UserDataBase:
$imageDB = ImageDataBaseManager::connect();
$userDB = UserDataBase::connect();
var_dump($userDB);
And this prints:
object(ImageDataBaseManager)#2 (1) { ["_connection":"DataBase":private]=> object(PDO)#3 (0) { } }
So I'm getting only the first class instance, but I want both.
how I can fix it?
When you initiate a new ImageDataBaseManager it stores the object in $_singleton of the abstract class and next time UserDataBase::connect request a new instance it returns it (static properties does not depends on instances). So it might not be a good idea to subclass a singleton pattern. However to get this working you need to store instance in the subclass not the parent class. Parent class and it's static properties are common to both (in runtime). Refer this code. The subclasses has their own $_singleton to store an instance.
abstract class DataBase {
private $_connection;
protected static $_singleton = false;
/**
* Fetch an instance of the class.
*/
public final static function connect () {
if ( static::$_singleton === false ) {
static::$_singleton = new static();
}
return static::$_singleton;
}
}
class UserDataBase extends DataBase { protected static $_singleton = false; }
class ImageDataBaseManager extends DataBase { protected static $_singleton = false; }
$imageDB = ImageDataBaseManager::connect();
$userDB = UserDataBase::connect();
var_dump($userDB);
Try this:
protected static $_singleton = false;
public final static function connect () {
if ( static::$_singleton === false ) {
static::$_singleton = new static();
}
Related
here is my situation :
I have a class that is inherited by a dozen of others, in this class I have a copy method which returns a copy of itself.
I can use this method in the inheriting class but, obviously, the method always return an instance of the super class, not the one which inherit from it.
I would like my copy method to return an instance of the ihneriting class.
BaseEntity.php :
class BaseEntity
{
protected $id;
protected $name;
protected $active;
protected $deleted;
// ...
public function copy()
{
$copy = new BaseEntity();
$copy->id = $this->id;
$copy->name = $this->name;
$copy->active = $this->active;
$copy->deleted = $this->deleted;
return $copy;
}
}
User.php :
class User extends BaseEntity
{
// ...
// Properties are the same as BaseEntity, there is just more methods.
}
One more way to achieve what you want:
<?php
class BaseEntity
{
protected $id;
public function copy()
{
$classname = get_class($this);
$copy = new $classname;
return $copy;
}
}
class Test extends BaseEntity
{
}
$test = new Test;
$item = $test->copy();
var_dump($item); // object(Test)
I see two ways of doing this:
using clone - it will make a shallow copy of your object
using static for creating a new object
<?php
class BaseEntity {
public function copy() {
return new static;
}
}
class User extends BaseEntity {
}
$user = new User;
var_dump($user->copy());
Result of this code: https://3v4l.org/2naQI
I have class DbTable, which implements all db queries to database such as insertRecord, updateRecord, ... But variable is not rewriting.
abstract class DbTable {
public static $table;
public static function insertRecord($data) {
// here I add some values to data, but that's not important
my_db::insert(self::$table, $data);
}
}
class User extends DbTable {
public static $table = 'table_users';
}
// everywhere I can call
User::insertRecord($data);
I know I can call
$c = get_called_class();
my_db::insert($c::$table, $data);
but I think that's not best solution at all.
Method and variables can be non static, I just use them because it is comfortable to write User::insertRecord instead of $user = new User(); $user->insertRecord($data);
When you're working with static classes you need to specify your variable source, in this case you're scoping to both classes and not on single class, this makes a difference, because self is scoping to concurrent class and when you want to scope for both classes you have to use static.
/**
* For testing
*/
class my_db {
public static function insert($table, $data){
echo $table;
}
}
abstract class DbTable {
public static $table = null;
public static function insertRecord($data) {
//self::$table is empty
//static::$table has 'table_users'
// here I add some values to data, but that's not important
my_db::insert(static::$table, $data);
}
}
class User extends DbTable {
public static $table = 'table_users';
}
// everywhere I can call
User::insertRecord(['Hi']);
self::$table is empty
static::$table has 'table_users'
You can read more about this here: SO Answer and PHP Documentation
Use static variables are unnecessary in this case. You just need dynamically create User object and them call method.
abstract class DbTable
{
protected $tableName;
public static function insertRecord($data)
{
$object = static::newInstance();
$object->insert($data);
}
public static function newInstance()
{
$className = get_called_class();
return new $className();
}
public function insert($data)
{
my_db::insert($this->tableName, $data);
}
}
class User extends DbTable
{
public function __construct()
{
$this->tableName = 'table_users';
}
}
You can now call:
User::insertRecord(['col1' => 'val1']);
But also you can insert rows from instated object:
$user = new User();
$user->insert(['col1' => 'val1']);
I have a parent class that depends on whether child class are instantiated.
class GoogleApp {
protected $auth_token;
public function __construct($scopes) {
$this->auth_token = $scopes;
}
}
class Gmail extends GoogleApp {
public function __construct() {
print_r($this->auth_token);
}
}
$googleApp = new GoogleApp('gmail'); // Change the actual class for all child instances
$gmail = new Gmail();
The idea is that all the children use the same auth_token (which is generated on whether the child classes are used - as of now, I'm just manually adding them to whether I included them in my code). Since I have quite a few child classes (like Calendar or Drive), do I have to inject the parent into each child instance or is there an easier way?
If I understand your request correctly, you're pretty close, you just need to declare your property as static.
class FooParent
{
protected static $scope = null;
public function __construct($scope)
{
self::$scope = $scope;
}
public function getScope()
{
return self::$scope;
}
}
class FooChild extends FooParent
{
public function __construct()
{
if (self::$scope === null) {
throw new Exception('Must set scope first.');
}
}
}
$parent = new FooParent('foo');
$child = new FooChild();
echo $child->getScope(), "\n"; // prints "foo"
# database.php #
class Database
{
public function connect(){ //code }
public function select(){ //code }
public function insert(){ //code }
public function update(){ //code }
public function delete(){ //code }
}
# encryption.php #
class Crypt
{
public function encrypt(){ //code }
public function decrypt(){ //code }
}
# notify.php #
class notify
{
public function setNotify(){ //code }
public function getNotify(){ //code }
}
# index.php #
include ('database.php');
include ('encryption.php');
include ('notify.php');
$db = new Database();
$crypt = new Crypt();
$notify = new notify();
class one
{
function execute()
{
$db->select(); // $db is the external object of Database class (database.php)
$notify->setNotify(); // $notify is the external object of Notify class (notify.php)
}
function store()
{
$db->insert(); // $db is the external object of Database class (database.php)
$notify->setNotify(); // $notify is the external object of Notify class (notify.php)
}
}
class two
{
function cypher()
{
$crypt->encrypt(); // $crypt is the external object of Crypt class (crypt.php)
$db->update(); // $db is the external object of Database class (database.php)
$notify->setNotify(); // $notify is the external object of Notify class (notify.php)
}
}
$one = new one();
$two = new two();
$one->execute();
$two->cypher();
$one->store();
There are 4 files, database.php, encryption.php, notify.php and index.php. The first 3 files are instantiated only once. They can be called in any file or class. How to call or access the objects of a class which are instantiated outside the class. For example:
$db = new Database();
$crypt = new Crypt();
$notify = new notify();
are the objects with are instantiated outside the class one and class two in index.php. How to access the objects $db, $crypt and $notify within the class one {} and class two {}? How to make those objects act like a global object?
What you are looking for is a Singleton design pattern. Be careful when introducing global state in your software because it can cause problems later on.
One possible solution to your specific problem:
class SingletonManager
{
/**
* Stores singleton instances
* #var array
*/
protected static $_instances = array();
/**
* Used to instantiate singletons
* #param callable $factoryMethod
* #return object of type returned by $factoryMethod or null
*/
public static function factory(callable $factoryMethod)
{
/**
* #var object
*/
$singleton = $factoryMethod();
// save reference to created object
self::$_instances[get_class($singleton)] = $singleton;
// return object
return $singleton;
}
/**
* Returns singleton object
* #param string $className
* #return object|null
*/
public static function instance($className)
{
// check if there is an instance of $className
if (isset(self::$_instances[$className])) {
return self::$_instances[$className];
}
return null;
}
}
// end class definition
// mock database class
class Database
{
public $username = '';
}
// create database object for the first time
SingletonManager::factory(function(){
$database = new Database();
$database->username = "username";
return $database;
});
// access database object later from anywhere
$databaseObject = SingletonManager::instance("Database");
print_r($databaseObject);
Note that 'callable' type hinting is available in php5.4+
I have a simple question. I use a singleton which implements an abstract class. Is it possible to put the getInstance() Method and the variable $_instance in the abstract class instead of the concrete one I want to create?
Here's my code:
<?php
class Command_Log extends Command_Abstract {
private static $_instance=null;
public static function getInstance() {
if (self::$_instance==null)
self::$_instance=new self();
return self::$_instance;
}
protected function realExecute() {
}
protected function realSimulate($fileHandle) {
}
}
and
<?php
abstract class Command_Abstract implements Command_Interface {
protected $_data=array();
//private static $_instance=null;
protected $_isExecuted=false;
protected $_execute=false;
public function enableExecute() {
$this->_execute=true;
return $this;
}
protected function __construct() {
}
protected function __clone() {}
public function addData($data) {
array_push($this->_data,$data);
return $this;
}
abstract protected function realExecute();
abstract protected function realSimulate($fileHandle);
public function execute() {
if(!$this->_isExecuted && $this->_execute) {
$this->_isExecuted = true;
$this->realExecute();
}
}
public function simulate() {
$exitSystem = false;
if(!$this->_isExecuted && $this->_execute) {
$this->_isExecuted = true;
$exitSystem = $this->realSimulate($fh);
}
}
return $exitSystem;
}
}
I have many implementations of the the commands, so I don't want redundant code everywhere in my implementations. Is it possible to put these two things in the abstract class, if yes please tell me how.
If not please explain it to me why it isnt possbile. Or if I need to change something to do it, anyhow.
regards
YES WE CAN!
I have a class called Singleton that is abstract... and many classes that extends that Singleton class... this is the code:
abstract class Singleton {
private static $instances = array();
final private function __construct($_params) {
$class = get_called_class();
if (array_key_exists($class, self::$instances))
throw new Exception('An instance of '. $class .' already exists !');
//static::initialize(); //In PHP 5.3
$this->initialize($_params);
}
final private function __clone() { }
abstract protected function initialize();
public static function getInstance($_params=array()) {
$class = get_called_class();
if (array_key_exists($class, self::$instances) === false){
self::$instances[$class] = new $class($_params);
}
return self::$instances[$class];
}
}
and (for example) the class DBConnection that extends from Singleton
class DBConnection extends Singleton{
private $connexion_pdo=null;
protected function initialize(){
//connect to the DB
$this->connexion_pdo = blablalba;
}
}
although there are some problems in php 5.2.. specially with the function get_called_class() and the static::initialize()
You could also check the php site for patterns... there are lots of contributions for the singleton
Good Luck