I have a main file (framework.php) that requires a few classes. For simplicity of my question lets assume only messages with the class Messages and automate with the class Automate. The messages class stores session messages that appear to the user on the frontend whenever they refresh their page .etc. When I add a message in Automate I do so via $msg = new Messages; $msg->add('s', 'Hello World!'); However I keep repeating $msg = new Messages everytime I want to add a $msg, and because this is messy, I want to just call the class once. So I initialize the class in the constructor like so:
class Automate
{
protected $msg;
//public $connection;
public $cke;
public $debug = false;
public $disallow_insert;
public $bImgUp = BACKEND_IMAGE_UPLOAD_PATH;
public $fImgUp = FRONTEND_IMAGE_UPLOAD_PATH;
public function __construct() {
global $disallow_insert;
$this->disallow_insert = $disallow_insert;
$this->cke = (bool) self::ckeCheck();
$this->msg = new Messages();
}
and in my main file I add it $msg = new Messages(); so I can just call the display function echo $msg->display();
However this method does not work (no errors, but still), the only thing that seems to work is when I initialize the $msg = new Messages(); before every add or display. What am I doing wrong?
I should mention I'm using this session based message script.
UPDATE:
I have determined the the messages are getting added by commenting out the clear function in the messages class. It almost seems as though the variable is getting unset before displaying.
I figured it out after researching OOP practices. I made the messages class a singelton via:
public static function getInstance()
{
static $instance = NULL;
if (NULL === $instance) {
$instance = new static();
}
return $instance;
}
and in my classes
var $msg;
public function __construct() {
$this->msg = Messages::getInstance();
}
I figure the messages class needed to be constructed once, not multiple times which is why the function clear kept being called!
Related
I am rather new to using PHP OOP and attempting the following:
$this->array = new array();
$this->array[1] = new myClass($UniqueName);
Within myClass I have a loop sending a message to the console every 5 seconds displaying the unique name. While the application is running new instances of the myClass will be created ( 1, 2, 3, 4, etc..), older ones become irrelevant and should be removed.
When I unset an array object the loop from that class keeps sending messages to the console.
unset($this->array[1])
My concern is that the class is not really gone wasting resources. As for the loop I could manually cancel it before running unset but it seems like its hiding my problem from view.
Hopefully this makes sense and someone can help me understand if this is possible.
Solution to my own problem. Here is a stripped down version I am testing with. The Issue is with the timer within myClass. It seems that because the timer is still running the class will not destruct. This has just been wishful thinking on my part that it would also terminate. Removing the timer allows the destruct to occur.
class myClass extends DefaultConfig {
public $fileName;
public $logLevel;
public $loop;
public $config;
public $db;
function __construct ($loop, $db, $config) {
$this->fileName = 'myClassObject';
$this->logLevel = 3;
$this->loop = $loop;
$this->config = new Config($config);
$this->HB();
}
function __destruct() {
print "Destroying " . $this->fileName . "\n";
}
public function HB(){
$this->loop->addPeriodicTimer(1, function($timer){
$this->Logging(3, $this->fileName, $this->logLevel, "Server Message");
});
}
}
Let's take the following classes for a moment:
class member {
public $id;
public function __construct($id) {
$this->id = $id;
// Check user level
if ($this->check_user_level() == moderator) {
$this = new moderator($this->id);
}
}
private function check_user_level() {
// Return user level from system based on user ID
}
}
class moderator extends member {
public function show_moderator_tools() {
// etc.
}
}
==================
$user = new member($user_id);
The desired behavior is to have $user detect whether the user is authorized to have moderator access, and if so recast the user using the moderator class instead of member.
PHP forbids simply reassigning $this, so what appears to be the best solution would be one of the following:
Run check_user_level($id) as a regular function and using an if statement
$user_level = check_user_level($id);
if ($user_level == "moderator") {
$user = new moderator($id);
} else {
$user = new member($id);
}
Set a flag in the base class that can be used for a check/redefinition after initializing $user
$user = new member($id);
if ($user->flag = TRUE) {
$user = new moderator($id);
}
The latter feels like it's introducing a security flaw, especially since the flag (which could just as easily be $user->user_level, or similar, I guess) would have to be public to be able to check it afterward.
What I would like to do would be to just make one call to new member($id) and have it handle things automatically from there, without the need for if statements after the fact. Is there a way to do this?
You can of do this by introducing another class (lets call it user) and using the __call magic method in php and call_user_func_array for calling the methods.
The logic is something like this -
Create a user class that has no method except check_user_level. It checks proper details and assigns it's $obj to the instance of either member or moderator class.
Here is how the classes would look like (I've changed the functions to print something out)-
class user{
public function __construct($id) {
$this->id = $id;
if ($this->check_user_level() == "moderator") {
$this->obj = new moderator($this->id);
}else{
$this->obj = new member($this->id);
}
}
public function __call($method, $args){
call_user_func_array(array($this->obj,$method), $args);
}
public function __get($prop){
if(isset($this->obj->$prop)){
return $this->obj->$prop;
}
return NULL;
}
private function check_user_level() {
// Return user level from system based on user ID
if($this->id == 1){
return "moderator";
}
return "member";
}
}
class member {
public $id;
public function __construct($id) {
$this->id = $id;
}
public function show_message($arg){
var_dump($this->id.$arg);
}
}
class moderator extends member{
public function show_moderator_tools() {
var_dump($this->id ."My toolset!");
}
}
So, now you can simply call the user class and that will automatically decide if it's a member or a moderator and call the method if it exists.
//Trying out a few examples
//Creating a member user
$mem_obj = new user(213);
$mem_obj->show_message("New message");
//Creating a moderator user
$mod_obj = new user(1);
$mod_obj->show_moderator_tools();
/*
OUTPUTS
-------
string(14) "213New message"
string(12) "1My toolset!"
*/
But you need to be careful with these kind of hacks.
For instance -
//This will fail
//because mem_obj doesn't have show_moderator_tools()
$mem_obj->show_moderator_tools();
EDIT
You can similarly go ahead with redirecting to properties using __get.
I have modified the code above to add this method beneath __call.
//Testing it
var_dump($mem_obj->id);
//This is an illegal property
var_dump($mem_obj->illegelProperty);
/*
OUTPUTS
int(213)
NULL
*/
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);
I have an abstract class called ContentAbstract which looks something like this
abstract class ContentAbstract
{
protected static $type;
protected $id;
protected $title;
protected $description;
protected $page;
protected $section;
...
function __construct($id = NULL, Page $page = NULL, Section $section = NULL)
{
if($id != NULL)
{
$data = get_data_from_content_table_by_id($id);
if($data['type'] == static::$type)
{
initialize_fields_with_data($data);
$this->page = fetch_page_object_from_registry($data['page_id']);
$this->section = fetch_section_object_from_registry($data['section_id']);
}
else
throw new IncompatibleContentTypeException('Foo');
}
else if($page != NULL && $section != NULL)
{
$this->page = $page;
$this->section = $section;
}
else
throw new OphanContentException('Foo');
}
}
Then the Page Class is also a subclass of ContentAbstract
class Page extends ContentAbstract
{
protected static $type = 'Page';
private $template_file;
private $short_name;
static function newFromName($name)
{
$data = get_content_id_from_page_table_using_the_short_name($name);
$page = new Page($data['id']);
$page->template_file = $data['template_file'];
...
}
static function newFromID($id)
{
$data = get_content_id_from_page_table_using_the_ID($id);
$page = new Page($data['id']);
$page->template_file = $data['template_file'];
...
}
}
Now my problem lies in the fact that the Page constructor is public and users can do this:
$page = new Page($valid_page_id);
and end up calling ContentAbstract::__construct() but not being able to initialize the data for the page itself (template_file, and short_name) since it was called outside of Page::newFromName() and Page::newFromID(). So I end up with a half-cooked content data. One solution would be to override the parent constructor with something similar to Page::newFromID() making sure that we would be able to set all fields when the Page is instantiated (of course by still calling the parent constructor within Page::__construct()).
Now the problem lies in the Page::newFromName() method since such an approach will require me to make 2 queries, one is to get the content id of the page using the short_name column, and then when the constructor of the page is called within Page::newFromName(), it will then create a new query to get the data associated with the page. That's not desirable isn't it?
So the only solution I see is to make Page::__construct() private and force end users to use the static methods to instantiate the object.
My question is, this is something I'd like to release as an open source project and it allows users to add more types of contents by simply subclassing the ContentAbstract class. Is requiring a private constructor detrimental to the said objective (accounting for human error and the laziness to read through documentation)? Or should such matters be the least of my concerns? Or are the structures of the actual classes themselves lend to this problem?
"Now my problem lies in the fact that the Page constructor is public and users can do this:
$page = new Page($valid_page_id);"
According to the code you posted in the OP this shouldn't be the case. Your page extends ContentAbstract where the construct resides, but you aren't actually calling construct from the Page class. You would have to force a call to the parent from the child:
class Page extends Content // I wouldn't name this abstract as it may change in the future
{
public function __construct($args)
{
// Here I forced a call to parent
// If I comment this out, the parent construct will never be accessed
parent::__construct($args);
}
}
Its difficult to explain this situation but please see the example.
I have coded a website where the page loads, I initialize a database class. I sent this class as a function parameter to any functions that needs to access database.
I know this is bad approach but currently I have no clue how to do this any other way. Can you please help me.
Example
class sms {
function log_sms($message, $db) {
$sql = "INSERT INTO `smslog` SET
`mesasge` = '$message'
";
$db->query($sql);
if ($db->result)
return true;
return false;
}
}
then on the main page..
$db = new db(username,pass,localhost,dbname);
$sms = new sms;
$sms->log_sms($message, $db);
Is there any better approach than this ?
there are number of options how to resolve dependencies problem (object A requires object B):
constructor injection
class Sms {
function __construct($db) ....
}
$sms = new Sms (new MyDbClass);
setter injection
class Sms {
protected $db;
}
$sms = new Sms;
$sms->db = new MyDbClass;
'registry' pattern
class Registry {
static function get_db() {
return new MyDbClass;
}}
class Sms {
function doSomething() {
$db = Registry::get_db();
$db->....
}}
'service locator' pattern
class Loader {
function get_db() {
return new MyDbClass;
}}
class Sms {
function __construct($loader) {
$this->loader = $loader;
function doSomething() {
$db = $this->loader->get_db();
$db->....
}}
$sms = new Sms(new Loader);
automated container-based dependency injection, see for example http://www.sitepoint.com/blogs/2009/05/11/bucket-is-a-minimal-dependency-injection-container-for-php
interface DataSource {...}
class MyDb implements DataSource {...}
class Sms {
function __construct(DataSource $ds) ....
$di = new Dependecy_Container;
$di->register('DataSource', 'MyDb');
$sms = $di->get('Sms');
to name a few ;)
also the Fowler's article i gave you before is really worth reading
For starters you can make a protected $db variable in each of your classes that need to utilize the database. You could then pass $db in to the class constructor. Here's the updated code:
$db = new db(username,pass,localhost,dbname);
$sms = new sms($db);
$sms->log_sms($message);
class sms {
protected $db;
public function __construct($db) {
$this->db = $db;
}
public function log_sms($message) {
$sql = "INSERT INTO `smslog` SET
`mesasge` = '$message'
";
$this->db->query($sql);
if ($this->db->result)
return true;
return false;
}
}
This example is in PHP 5.
Singleton is also good practice in application design. They are very useful to avoid repeated queries or calculations during one call.
Passing Database object to every method or constructor is not a very good idea, use Singleton instead.
extend your database, or insert static method inside your db class. (I would also call for config within db constructor method)
class database extends db
{
public static function instance()
{
static $object;
if(!isset($object))
{
global $config;
$object = new db($config['username'],$config['pass'],$config['localhost'],['dbname']);
}
return $object;
}
}
make your method static
class sms {
public static function log($message) {
$sql = "INSERT INTO `smslog` SET `mesasge` = '$message'";
database::instance()->query($sql);
return (bool) database::instance()->result;
}
}
Next time you need to log sms just do static call like this
sms::log($message);
You could either have a static class that has two lists, one of available connections, the other of handed out connections, and just have a connection pool.
Or, if you are using something that has a quick connection time, such as MySQL, then just create the connection in your database class, do all the queries needed for that functionality, then close it. This is my preferred approach.