I want to use a static method of an example class without instantiating class. This method uses a dependencie class and is instantiated with __construct method. How can this dependency class be instantiated. Example:
class user {
protected static $db;
public function __construct() {
self::$db = database::getInstance();
}
public static function get_user() {
$user = self::$db->query("sql");
return $user;
}
}
I know the solution with autoloader or I could just add self::$db = database::getInstance(); in every static method.
Could someone kindly show me better suggestions?
You can add a static setter and getter for the db object and throw an exception if someone tries to access the getter without calling the setter first:
class User
{
protected static $db;
public static function setDB($db)
{
self::$db = $db;
}
protected static function getDB()
{
if (!self::$db) {
throw new Exception('You must `setDB()` the db object before attempting to get it.');
}
return self::$db;
}
public static function getUser()
{
return self::getDB()->query('sql');
}
}
User::setDB(database::getInstance());
User::getUser();
Related
I have an Connection class which connects to a specific "Service". You call the specific Service such as mysqli or PDO when instantiating the class.
class Connection
{
private $service;
private $state = null;
public function __construct(Service $service) {
$this->service = $service;
}
public function initialize() {
....
}
public function destruct() {
....
}
//Maybe some getters and setters
}
In the Service class there is an getObject() method, this contains the object which has to be instantiated to make a connection to a Database or something else.
There is also an getInstance() method. This is used for returning the object in the getObject method if it isnt already instantiated.
abstract class Service
{
public static function getInstance() {
$instance = null;
if ($instance == null) {
$instance = self::getObject();
}
return $instance;
}
/**
* #return object Returns the object where the service should start from.
*/
public abstract function getObject();
}
Here is an example of an Service class.
class MySQLService extends Service
{
public function getObject() {
return new mysqli('127.0.0.1', 'root', '', 'db');
}
}
Problem
When using this code like this:
$connection = new Connection(MySQLService::getInstance());
$connection->initialize();
It comes with this error:
Fatal error: Cannot call abstract method Service::getObject() in
C:\Users.\Documents...\Service.php on line 18
Questions
How does it come that this error appears?
How can I solve this error?
How can I call a function from a class that extends the Service class?
In order to get this working you need to declare the getObject methods as the static methods they are.
In Service:
public abstract function getObject()
Should be:
public static function getObject() {}
(Sorry, you can't have a static abstract)
In MySQLService:
public function getObject() {
Should be:
public static function getObject() {
You can then direct the call to the right class by using the following:
public static function getInstance() {
static $instance = null;
if ($instance == null) {
$instance = static::getObject();
}
return $instance;
}
Note - you missed the static keyword from the instance variable too.
I have two classes:
Singleton.php
namespace Core\Common;
class Singleton
{
protected static $_instance;
private function __construct(){}
private function __clone(){}
public static function getInstance() {
if (null === self::$_instance) {
self::$_instance = new self();
}
return self::$_instance;
}
}
Config.php
namespace Core;
class Config extends Common\Singleton
{
private $configStorage = array();
public function setConfig($configKey, $configValue)
{
$this->configStorage[$configKey] = $configValue;
}
public function getConfig($configKey)
{
return $this->configStorage[$configKey];
}
}
my index.php
require_once 'common\Singleton.php';
require_once 'Config.php';
$config = \Core\Config::getInstance();
$config->setConfig('host', 'localhost');
but got the error: "Call to undefined method Core\Common\Singleton::setConfig()"
So as i can see getInstance() return me Singleton class instance, but not Config, how i can return Config instance from Singleton?
You can change your getInstance to this:
public static function getInstance() {
if (!isset(static::$_instance)) {
static::$_instance = new static;
}
return static::$_instance;
}
The difference between self and static is highlighted here:
self refers to the same class whose method the new operation takes place in.
static in PHP 5.3's late static bindings refers to whatever class in the hierarchy which you call the method on.
So it means that is bounded dynamically to the extending class, hence new static in your case refers to the Config class, using self will always statically refers to the Singleton class.
Working example here.
Is there any way in php to make sure that a class can be extended by one and only one class?
I have some code that illustrates what I'm trying to do, basically I have a DB manager class and a DB query class that is extended by the manager class. What I'd like to do is make sure that the DB query class can only be used by the DB manager class.
The code below works, but it seems very rough. In the code I delcare the query class abstract with a single abstract function that checks the classname, or I could simply declare all of the Manager functions as abstract in the query class (which seems hacky). If there is a simpler way to do this than my code below that would be very useful...
abstract class DB_Query {
private static $HOST = 'localhost';
private static $USERNAME = 'guest';
private static $PASSWORD = 'password';
private static $DATABASE = 'APP';
//////////
/* USING ABSTRACT FUNCTION HERE TO ENFORCE CHILD TYPE */
abstract function isDB();
/* OR USING ALTERNATE ABSTRACT TO ENFORE CHILD TYPE */
abstract function connect();
abstract function findConnection();
abstract function getParamArray();
//////////
private function __construct() { return $this->Connect(); }
public function Read($sql) { //implementation here }
public function Query($sql) { //implementation here }
public function Fetch($res, $type='row', $single='true') { //implementation here }
}
class DB extends DB_Query {
public $connections = array();
public static $instance;
public function isDB() {
if (get_parent_class() === 'Database' && get_class($this)!=='DB') {
throw new \Exception('This class can\'t extend the Database class');
}
}
public function connect($host=null,$user=null,$pass=null,$db=null) { //implementation here }
function findConnection($user, $password=null) { //implementation here }
public function getParamArray($param) {}
public function threadList() {}
public function getThread($threadId=null) {}
public static function Singleton() { //implementation here }
private function __construct() { //implementation here }
}
I would go after marking the constructor of DB_Query as final and implementing it the way that it checks the instance and fires some exception. Something like this
class Base {
final function __construct() {
if (!$this instanceof Base && !$this instanceof TheChosenOne) {
throw new RuntimeException("Only TheChosenOne can inherit Base");
}
/**
* use this function as constructor
*/
$this->__internal_base_construct();
}
protected function __internal_base_construct() {
// constructor code
}
}
But your problem is rather strange and kind of breaking the idea of OOP in several ways. Just combine it into a single class and use final class directive.
http://php.net/manual/en/language.oop5.final.php
class Database_Query extends Database {
public static $instance;
public function Query($sql) {}
public function Fetch($res, $type='row', $single='true') {}
public static function Singleton() {}
private function __construct() {
$this->link = $this->connect()->find('guest')->getLink();
}
}
How to set value for public variable when this in another context and self wants only static props, which seems not be accessible after instantiation. There is a little extends for mysqli connection:
class db extends mysqli {
...
public static $bar;
private function __construct() {
self::$bar = "test";
...
}
db::setOptions($db_options);
$link = db::getInstance();
echo $link->bar;
Notice: Undefined property: db::$bar in ... blah-blah-blah
Is there a right way to do it? Thanks in advance.
As stated in the comments designating this as a static property makes the property belong to the class as opposed to an instance of an object of the class. To access class properties you have to use classname::$propertyName to access the property.
What you probably mean to do is:
class db extends mysqli {
protected static $instance;
public $bar;
private function __construct() {
$this->bar = "test";
}
public static function getInstance(){
if (!isset(static::$instance)){
static::$instance = new static();
}
return static::$instance;
}
}
$link = db::getInstance();
echo $link->bar;
UPDATE
Looking at the problem you are having I think you are just trying to provided global access to a shared db object that you can extend. If that is the case you may get better mileage from providing a wrapper for a mysqli instance instead of extending it. A simple way to do this is to create a wrapper object that proxies all undefined property access to an internally stored object.
class db {
protected $db;
public $bar;
protected static $instance;
protected function __construct(){
$this->db = new mysqli (....);
$this->bar = "something";
}
public static function getInstance(){
if(!isset(static::$instance)){
static::$instance = new static();
}
return static::$instance();
}
// magic accessor proxy
public function __get($property){
return $this->db->$property;
}
// magic mutator proxy
public function __set($property, $value){
$this->db->$property = $value;
}
// magic call function
public function __call($method, $args){
return call_user_func_array(array($this->db, $method), $args);
}
public function __callStatic($method, $args){
return call_user_func_array(array(static::getInstance(), $method), $args);
}
}
Consider the following code below, I've been thought by Lynda.com
to create a database class like this, my question is why not create
a static method for the database entirely instead of storing an
instance into a static property?
<?php
class Database {
private $conn;
private static $init;
public function __construct() {
$this->conn = new mysqli('localhost','root','root','mydb');
}
public static function getInstance() {
self::$init = new self();
return self::$init;
}
}
$db = Database::getInstance();
?>
If you want to use singleton you should to protect __construct()
class DB
{
private static $instance;
private function __construct($args)
{
// do some
}
public static function getInstance()
{
// get instance
}
}
$query = 'SELECT etc...';
$stmt = DB::getInstance()->prepare($query);
I use this pattern in DB class. But if you have more than 1 connection you should NOT! use singleton.
My guess is that code you posted was intended to be the following because it looks like it was intended to be a singleton. I've only changed the getInstance() method.
class Database {
private $conn;
private static $init;
public function __construct() {
$this->conn = new mysqli('localhost','root','root','mydb');
}
public static function getInstance() {
if (is_null(self::$init)) {
self::$init = new self();
}
return self::$init;
}
}
$db = Database::getInstance();
I think this should clear up the confusion of why a static instance variable is used.
If this wasn't intended to be a singleton, then your question of "why didn't they just use a static method" should have the answer "they should have".
Just to clarify, $db is a Database object, which is instantiated by calling static method getInstance().
why not create a static method for the database entirely?
A static method is defined inside a class, however it can be called without having to instantiate the class. In other words, a static method belongs to the class, since in can be called without instantiating an object.
You can add methods to the Database class to interact with the database as you deem necessary.