In my PHP project I created a singletone class that mange my access tokens.
The tokens are some constant strings with timestamp of +/- 5 min hashed with SHA1.
I want that each page can access to this singletone instance (the same instance) because it holds the tokens. Also there going to be a procedure that refresh the tokens in that instance.
The name TokenManager with located in different php file.
I create the instance $TOKENS in different file.
<?php
require_once 'TokenManager.php';
$TOKENS = TokenManager::Instance();
And another file for refresh action (refreshTokens.php):
<?php
require_once 'Tokens.php';
global $TOKENS;
$TOKENS->refreshTokens();
var_dump($TOKENS->tokens);
In another page that is a web service (AddUser) I use the this Tokens.php instance as global.
require_once 'TokenManager.php';
require_once 'Tokens.php';
...................................
function start(){
global $userParams;
global $TOKENS;
//Check for all mandatory params
if(!ValidateParams()){
finish();
}
if(!$TOKENS->checkTokenAndGetChannel($userParams[PARAM_TOKEN])){
setError(ERR6_BAD_TOKEN, CODE6_DESC);
finish();
}
if(!isEmailValidByDrupal($userParams[PARAM_EMAIL])){
setError(ERR3_BAD_EMAIL, CODE3_DESC . $userParams[PARAM_EMAIL]);
finish();
}
finish();
}
The problem that each time I call refreshTokens.php and take the token I have each time new instance with a different values what makes the tokens each time invalid.
What can I do about that?
Well when a called PHP Script ends all objects are destroyed and a singleton class will not really help you here.
A singleton class can be used as a cache or storage during the entire runtime of one page-call.
In other words, the singleton instance alone cannot keep the data between script calls.
Save them in the session, in a file or a database.
#Edit: After discussing the issue we came up it is the best to use class constants, I present you an example here:
#Edit2: Rethought the discussion and a Singleton class is fine if you do it correct, I leave you an example here:
final class Tokens {
static private $instance = null;
private $tokens = array();
static public function getInstance() {
if(self::$instance === null) {
self::$instance = new self();
self::$instance->initialize();
}
return self::$instance;
}
private function initialize() {
$this->tokens[] = "erhdfhegrtoken1!";
$this->tokens[] = "4h43gherhtoken2!";
$this->tokens[] = "egegtoken3!";
}
public function getToken($index) {
$retVal = "";
if(isset($this->tokens[$index])) {
$retVal = $this->tokens[$index];
}
return $retVal;
}
private function __construct() { }
private function __clone() { }
}
Usage:
$tokens = Tokens::getInstance();
$myToken = $tokens->getToken(2);
Related
I created one class (ParseDAO.php) and made it singleton with this method:
public static function getInstance(){
if (self::$instance == null) {
self::$instance = new self();
}
return self::$instance;
}
Then I created 2 controllers. One to login stuff (LoginController.php) and another do dashboard stuff (DashboardController.php)
In LoginController.php I use this code and it works perfectly:
$instance = ParseDAO::getInstance();
$loginResponse = $instance->loginParse($request->get('userName'), $request->get('password'));
if($loginResponse == true){
return redirect()->route('dashboard');
}
else {
return view('login.erroLogin');
}
At DashboardController I have this code:
$instance = ParseDAO::getInstance();
$userId = $instance->getUserId();
This second line just returns the objectId form parse. If I put this line on loginController.php it returns the correct Id, but at DashboardController.php (where I need this data) nothing returns.
It's like another instance was created even using singleton.
Anybody knows how to solve this problem?
Follow the code of loginParse and getUserId:
/**
* Method login
*/
public function loginParse($username, $password){
if($username != null && $password != null){
try {
self::$user = ParseUser::logIn($username, $password);
return true;
}catch (ParseException $error){
return false;
}
}
else{
return false;
}
}
/**
* #return getUserId
*/
public function getUserId(){
return self::$user->getObjectId();
}
And this is the constructor code (with the true keys):
public function __construct()
{
ParseClient::initialize('xxx','xxx','xxx');
self::$user = new ParseUser();
}
Singletons allow for code from a single execution to use the same instance. On every execution (page load), a new instance is created. Every call to ParseDAO::getInstance() will return the same instance, but only within that execution context.
Without seeing the code in loginParse and getUserId, what is most likely occurring is that you are storing information in the ParseDAO class. That information would be kept during the same execution context, but would disappear on the next execution (because it's a new instance).
You'll have to use Sessions to persist information across multiple executions.
Laravel's Service Container offers a quick and easy way to bind a class as a singleton without the need to implement that pattern yourself. So you can use this to register you singleton with the service container:
app()->bind('ParseDAO', function ($app) {
return new ParseDAO;
});
And then you can use this to access that instance:
$instance = app('ParseDAO');
That will make sure you always get the same instance. You can read more about singletons and the Laravel Service container in the Laravel Documentation.
I'm moving onto teaching myself OOP in PHP.
I'm creating a couple of little web apps and have followed a lot of tutorials that either create the database (using PDO) via a Singleton, or via passing the global around. I've read that these are pretty much the same thing and are both to be avoided like the plague.
So I've watched the Google Tech Talks on clean code, and read almost every SO article on dependency injection and the like. I have a couple of questions.
The clean code videos suggest you shouldn't do 'work' in your constructors. Is this 'work' in reference to business logic. Ie. If my class's job is to create another object, is that an OK kind of 'work'?
For example, in trying to conform to single repsonibility classes I created three.
Class DB - which actually connects to the database.
Class DBFactory - which creates the DB object which connects to the database.
Class DBInstance - which returns a single instance of the DBFactory created PDO object.
Please note that I'm trying to create a single instance, without creating a Singleton pattern.
So I try and pass my dependencies for each class up the chain. I find myself in a position where I have to create all of the objects (from DB down) so I can inject the dependencies. For some reason I thought it would work the other way, I'd create the first object, which would create the second for me etc. I'm clearly missing something?
Hopefully this helps others as well - there seems to be a myriad of questions relating to this stuff and databases but very little good examples.
(I should mention this does work, I do get a list of hotel names out of the database!)
TestCode.php
include './classes/DB.php';
include './classes/DBFactory.php';
include './classes/DBInstance.php';
include './classes/Location.php';
$db = new DB;
$dbfactory = new DBFactory($db);
$dbinstance = new DBInstance($dbfactory);
$dbh = $dbinstance->getDbInstance();
//Example business logic
$location_names = Location::getLocationNames($dbh);
print_r($location_names);
Class DB.php:
class DB {
private $_dbhost = 'myhost';
private $_dbname = 'myname';
private $_dbuser = 'myuser';
private $_dbpass = 'mypass';
private $_error;
public function connect() {
try {
return new PDO("mysql:host=$this->_dbhost;dbname=$this->_dbname",
$this->_dbuser, $this->_dbpass);
}
catch (PDOException $e) {
$this->_error = 'Error! ' . $e->getMessage() . '<br />';
die();
}
}
public function getError() {
if (isset($this->_error)) {
return $this->_error;
}
}
}
Class DBFactory.php
class DBFactory {
private $_dbh;
public function __construct(DB $db) {
$this->_dbh = $db;
}
public function Create() {
return $this->_dbh->Connect();
}
}
Class DBInstance.php
class DBInstance {
private static $_dbinstance;
public function __construct(DBFactory $dbfactory) {
if (!isset(self::$_dbinstance)) {
self::$_dbinstance = $dbfactory->Create();
}
}
public function getDbInstance() {
return self::$_dbinstance;
}
}
Your code seems to do what you want it to.. but maybe we can use less object instantiation using inheritance and maybe we can avoid static properties in instanciated classes.
Also in regard to using a pattern of dependency injection that is able to handle multiple connections, but support using a single instance of it. exemple first, classes after
$params = array
('host'=>'localhost',
'db'=>'ice',
'user'=>'kopitar',
'pass'=>'topnet',
'charset'=>'utf8'); // passing the charset explicitely is great
$handle = new handle($params);
$db = $handle->getInstance();
we can either pass the $db to our functions
$location_names = Location::getLocationNames($db);
or the whole $handle. as long as $handle is not reconstructed, it will always return the same database connection.
$location_names = Location::getLocationNames($handle);
if I want to reconstruct I need the whole $handle
$handle->__construct(/* params but with another database infos */);
$db2 = $handle->getInstance();
As for the classes, I think we want the params to arrive from the instanciated class, so we can change them later.
class db {
function __construct($params) {
foreach ($params as $param => $value) {
$this->{$param} = $value; // assigns the connections infos
}
}
protected function connect() {
$dsn = 'mysql:host='.$this->host.';dbname='.$this->db.';charset='.$this->charset;
return new PDO($dsn,$this->user,$this->pass);
}
}
the factory creates a connection from params and passes it to something else, good factory
class factory extends db {
protected function create() {
return $this->connect();
}
}
now we want to have our object to keep it's connection as long as we do not rebuild it. so we give it to instance
class instance extends factory {
function instantiate() {
$this->instance = $this->create();
}
}
and last but not least, our handle which returns the instance. it could be in instance class.....................
but I feel like having four and find no real reason not to.
class handle extends instance {
function __construct($params) {
db::__construct($params);
$this->instantiate(); // when we construct a handle, we assign an instance to the instance property
}
function getInstance() {
return $this->instance;
}
}
KISS
Don't make things more complex than they are, of course this is just my opinion, but as I see it you are building a complex solution for a problem that someone else says might exist is some cases.
Php is not multi threaded so there goes one of the biggest arguments overboard. (in very rare-occasions it might be)
I'm using singletons for my database connections for about 15 years now and never ever had a problem with them, I do play around with different connections having one singleton handle several connection instances, but whatever... it works great and everyone that looks at the code.. understands it directly.
I'm not using globals because they can be overwritten and are kind of hard to predict (when it holds the correct object, and when/why they don't)
Use OOP to make your code cleaner, easier to work with and more flexible.
Don't use it to fix problems that aren't there and make your code more complex because others tell you to.
An very simple example of a db-connection singleton class handling several different connections.
class singleton{
private static $_instances=array();
public static function getInstance($connectionName){
if(!isset(self::$_instance[$connectionName]){
self::$_instance[$connectionName]=self::_getConnection($connectionName);
}
return self::$_instance[$connectionName];
}
}
just my 2 cents
Why do you have a factory if you have a singleton? This is needless.
This is a never-ending debate, but I'm advocate of do not use singletons for database connections.
As far as in most applications, you have only one data channel, you can consider your database connection unique, but this might not be always true.
In deed, the effort made to create a singleton database connection is even bigger than just create a regular one.
Also, your class DB is not configurable, therefore, you need to change it when your connection parameters change. And I think DB is a very bad name for this.
I'd rather call this Storage and do something like:
inteface Storage {
public function insert($container, array $data);
public function update($container, array $data, $where);
public function delete($container, $where);
public function getAll($container);
public function getOne($identifier);
}
final class PdoStorage implements Storage {
private $dbh;
private $dsn;
private $user;
private $pswd;
public function __construct($dsn, $user, $pswd) {
$this->dsn = $dsn;
$this->user = $user;
$this->pswd = $pswd;
}
// Lazy Initialization
private function connect() {
if ($this->dbh === null)
$this->dbh = new PDO($this->dsn, $this->user, $this->pswd);
}
public function insert($container, array $data) {
$this->connect();
// ... omitted for brevity
}
}
Now, when you need a database storage, you do:
$someObj = new SomeClass(new PdoStorage(...));
Now you might be wondering if you will need to create an PdoStorage for each single object that depends on it.
The answer is: no!
Now you can use a factory to simplify your life.
class SomeFactory {
private $defaultStorage;
public function __construct(Storage $storage) {
$this->defaultStorage = $storage;
}
public function create($type) {
// Somehow fetches the correct class to instantiate and put it into $class variable , for example... and then
return new $class($this->defaultStorage); // Or you'd better do this with reflection
}
}
$factory = new SomeFactory(new PdoStorage(...));
$factory->create('SomeClass');
This way, you can have just one database connector or more if you need.
I have created a CI model that dynamically loads certain classes depending on a parameter that is passed in. These classes are just wrapper classes around phpseclib to make ssh connections to different devices.
What I'm noticing is that when I try to execute one particular method, I'm getting the above error message.
Here's some sample code to help you understand what I'm doing. This is what my model looks like:
public function get_portstatusall($ip, $switchname)
{
$classname = $this->switchToClassName($switchname);
try{
include_once(APPPATH.'libraries/'.$classname.'.php');
$switch_obj = new $classname($ip, 'password', '');
$switch_obj->connect();
$data = $switch_obj->dosomething();
$switch_obj->disconnect();
return $data;
}
catch (Exception $e) {
echo 'this really should be logged...';
return false;
}
}
public function get_portstatusindividual($ip, $switchname)
{
$classname = $this->switchToClassName($switchname);
try{
include_once(APPPATH.'libraries/'.$classname.'.php');
$switch_obj = new $classname($ip, 'password', '');
$switch_obj->connect();
$data = $switch_obj->dosomethingelse();
$switch_obj->disconnect();
return $data;
}
catch (Exception $e) {
echo 'this really should be logged...';
return false;
}
}
As you can see, i'm dynamically determining which class to load depending on the switchname that is passed in. This code successfully loads a class called "device123.php", let's say. Class device123 in turn, instantiates the SSH2 object that comes with the phpseclib, and uses it to send ssh commands to the device.
Here's a clip of code from device 123:
class device123
{
// sample code to demo how to use phpseclib to create an interactive ssh session.
//this library relies on phpseclib. you must include this class and SSH2.php from Net/phpseclib.
private $_hostname;
private $_password;
private $_username;
private $_connection;
private $_data;
private $_timeout;
private $_prompt;
public function __construct($hostname, $password, $username = "", $timeout = 10)
//public function __construct($params)
{
echo 'in the switch constructor<br>';
set_include_path(get_include_path() . PATH_SEPARATOR . '/var/www/phpseclib');
include('Net/SSH2.php');
$this->_hostname = $hostname;
$this->_password = $password;
$this->_username = $username;
} // __construct
public function connect()
{
$ssh = new Net_SSH2($this->_hostname);
if (!$ssh->login($this->_username, $this->_password)) { //if you can't log on...
die("Error: Authentication Failed for $this->_hostname\n");
}
else {
$output= $ssh->write("\n"); //press any key to continue prompt;
$prompt=$ssh->read('/([0-9A-Z\-])*(#)(\s*)/i', NET_SSH2_READ_REGEX);
if (!$prompt) {
die("Error: Problem connecting for $this->_hostname\n");
}
else {
$this->_connection = $ssh;
}
}
} // connect
public function close()
{
$this->_send('exit');
} // close
public function disconnect()
{
$this->_connection->disconnect();
$ssh=NULL;
}
I don't think I quite understand how I can be redeclaring the SSH2 class... but i wanted to see if perhaps I was not destroying / cleaning up after myself properly.
To help troubleshoot, I've tried adding debug echo statements in the constructor and destructor of both the SSH2 class, and also the wrapper class called device123.
It all looks proper...
I don't think I'm on the right track... Can you tell me where you think I should start looking?
Is it because it's possible that both methods are called ... one after the other... and both can possibly load the same class?
Thanks.
Php gives such error when you are trying to load class multiple times like same name class exists in other source file.
All php classes must have unique names. You should include all the files at one place, keep track of all loaded classes if you want a lazy loading, or just use http://cz.php.net/include_once. The same goes for the require function.
The _once postfix does exactly what you need: it prevents the files from loading if it already has been loaded, thus preventing the class redeclaration.
I'm used to java, objective c and a little c++.
Now i want to use PHP to create a website. I created several classes but to keep it simple: 3 classes.
Account - DataMapper - DataManager
This means that i can get an account from the database. In DataManager i keep track of all things. Like the userId of the user.
The thing is, normally all setted variables stay 'set', but now i'm using php i apperently need to store them by using a session.
DataManager:
<? php
class DataManager
{
// Hold an instance of the class
private static $dm;
private $dataMapper;
private $dictationView;
private $userId;
private function __construct()
{
$this->dataMapper = new DataMapper();
$this->dictationView = new DictationView();
}
// The singleton method
public static function singleton()
{
if (!isset(self::$dm)) {
$c = __CLASS__;
self::$dm = new $c;
}
return self::$dm;
}
// Prevent users to clone the instance
public function __clone()
{
trigger_error('Clone is not allowed.', E_USER_ERROR);
}
function __get($prop) {
return $this->$prop;
}
function __set($prop, $val) {
$this->$prop = $val;
}
}
?>
If i set the userId in the singleton DataManager class, the next time i
call an instance of the DataManager class it will not rememeber the userId. Somewhere i have to deal with session i guess. How to use sessions in a good OOP way in the DataManager? Thanks!
If you want you can create a wrapper for sessions in PHP. It actually could be beneficial, especially if your application later had to migrate to a cluster of servers and sessions would be moved to a distributed cache. Then, to facilitate this migration you would only have to provide different implementation if same Session class interface.
That said.
This code there itself is not a good OOP. You should stop using singletons. And, if you class require instances of DataMapper and DictationView, then they should be created outside the class and provided in the constructor. Instead of creating a tight coupling because you constructor is making other objects.
Now, what you refer to is not PHP but rather how a client-server architecture is being handled.
Here is a change, which assumes you manage the session correctly (with regard to session_start - should be in the bootstrap of your file)
I have also added some of topic corrections to your code, which will help u in the future:
private function __construct()
{
$this->dataMapper = new DataMapper();
$this->dictationView = new DictationView();
}
// The singleton method
public static function singleton()
{
if(isset($_SESSION[self::MY_UNIQUE_IDENTIFIER] &&
get_class($_SESSION[self::MY_UNIQUE_IDENTIFIER] == 'DataManager'){
self::$dm = $_SESSION[self::MY_UNIQUE_IDENTIFIER];
}
if (!self::$dm) {//LOOK HERE LOOK HERE!!!!!!!!!!!!!!!!!!!!
$_SESSION[self::MY_UNIQUE_IDENTIFIER] = self::$dm = new self;
}
return self::$dm;
}
// Prevent users to clone the instance
public function __clone()
{
trigger_error('Clone is not allowed.', E_USER_ERROR);
}
function __get($prop) {
return $this->$prop;
}
function __set($prop, $val) {
$this->$prop = $val;
}
}
//LOOK HERE LOOK HERE no closing ?>
Some traps I did not address here as they are not the subject of this question:
The right way to manage sessions
Adjustment to the class to allow inheritance without trashing the session
I've noticed that the keyword static in PHP is not that static at all.
Lets say Elmo is my singleton:
class Elmo
{
private static $instance;
private function __construct()
{
echo 'Elmo says constructor\n';
}
public static function getInstance()
{
if (!isset(self::$instance))
self::$instance = new Elmo();
return self::$instance;
}
public function boo()
{
echo 'Elmo says boo!\n';
}
}
And the following file is just a regular .php script.
<?php
Elmo::getInstance()->boo();
Elmo::getInstance()->boo();
// Output:
// Elmo says constructor
// Elmo says boo!
// Elmo says boo!
?>
Every new page Elmo gets re-constructed. Why don't subsequent pages have the following output?
<?php
// Output:
// Elmo says boo!
// Elmo says boo!
?>
I hope someone can enlighten me on this, thanks!
because on every page load all memory is wiped ?
Static scoping does not mean it will stay in memory forever, it means that the variable operates outside the program call stack, and will persist during the execution of the script. It is still cleared after the program ends.
This is because every time you do a page load it runs {main} separately. This would be like running a java program two separate times and the static property not being retained. Elmo::$instance will only remain instantiated in the context of the same script. If you want it to work across page loads, you can serialize it in the session (or DB) and check this instead of $instance each time:
const SESSION = 'session';
public static function inst() {
!isset($_SESSION[self::SESSION]) and self::init();
self::$inst = $_SESSION[self::SESSION];
return self::$inst;
}
private static function init() {
$_SESSION[self::SESSION] = new self;
}