TalkPHP

TalkPHP (http://www.talkphp.com/forums.php)
-   Advanced PHP Programming (http://www.talkphp.com/advanced-php-programming/)
-   -   application-wide object (http://www.talkphp.com/advanced-php-programming/3780-application-wide-object.html)

robocop 12-17-2008 08:43 PM

application-wide object
 
quick question -- i have a couple of core classes that "run" my application that i'd love to have persist over the entire application so that i can easily use it throughout the application without having to include and instantiate the class on every one of my controllers...

so, right now i have this:

Code:

       
// start the session
session_start();       
// only instantiate the core class once per session       
if(!isset($_SESSION['site_core'])){
  $_SESSION["careers_core"] = new careersCore('site.com');
}

then, throughout my application, i can reference it by doing this:

Code:

$_SESSION['site_core']->do_something();
this seems...i dunno...bad. any advice for how to accomplish better what i am doing?

thanks everyone!

robocop

sketchMedia 12-17-2008 09:28 PM

Would the registry pattern help?

PHP Code:

class Registry
{
    private static 
$aObjects = array();
    
    public static function 
get($szObj)
    {
        if(
array_key_exists($szObjself::$aObjects))
        {
            return 
self::$aObjects[$szObj];
        }
        return 
false;
    }
    public static function 
set($szObj$mValue)
    {
        
self::$aObjects[$szObj] = $mValue;
    }


Then all you have to do is set the variable:
PHP Code:

Registry::set('careers_core', new careersCore('site.php')); 

Then you can access it from all parts of your script (provided its included and set before you call it) like this:
PHP Code:

Registry::get('careers_core')->do_something(); 


robocop 12-17-2008 09:33 PM

ok -- that could work...

my question is where would i put the code below?
Code:

Registry::set('careers_core', new careersCore('site.php'));
I really want to be able to instantiate the class ONCE when a person visits the site, and then have it globally accessible to me without having to instantiate it on every controller. (The class is a Singleton)

thanks for the help from across the pond!

kasey

sketchMedia 12-19-2008 10:38 AM

I would put it in a 'bootstrap loader' (just like your computer), a file that is executed once per execution of the script that sets up your script.

In actual fact, it doesn't really matter where you do 'set', as long as its before you call the 'get' for that specific object.

Scottymeuk 01-06-2009 12:51 AM

I need help with this also, I have $app->core->xss_clean(''); for example working, but i need it to be defined throughout the application (in included files etc). I want to be able to access it through $app. Is this possible.

Please note all files are loaded from index.php through functions and includes etc. At the moment im using Factory() but i really dont like it and would like to just use $app->CLASS->FUNCTION.

Also is there a way to make this dynamic so that i dont have to do $app->core = new core();

Thanks

Wildhoney 01-06-2009 01:46 AM

1 Attachment(s)
How about using like the following to achieve what you're after? All you have to do is ensure the TalkPHP_Factory is included on every single page, and then make your calls as so:

php Code:
echo TalkPHP_Factory::getInstance()->TalkPHP_Hello()->getText();
echo TalkPHP_Factory::getInstance()->TalkPHP_Goodbye()->getText();

This is in the style Class->Function(). Is this something like what you're after?

Obviously you'll need to modify it a touch to auto include files, but I added the functionality in the __autoload() function, just to show you how you could do it.

php Code:
function __autoload($szClass)
{
    $szFile = sprintf('./includes/%s.php', $szClass);
   
    if (!file_exists($szFile))
    {
        return false;
    }
   
    include_once($szFile);
}

Scottymeuk 01-06-2009 01:57 AM

So is there no way of doing it with haveing $app. At the moment i use Factory('core')->xss_clean('')but it does not look good

Wildhoney 01-06-2009 02:01 AM

Do you want to use the singleton on an individual basis like the following? Do you really want a factory, as I showed you above?

php Code:
class TalkPHP_Hello
{
    public static $m_pInstance;
   
    public static function getInstance()
    {
        if (!isset(self::$m_pInstance))
        {
            self::$m_pInstance = new self();
        }
       
        return self::$m_pInstance;
    }
   
    public function getText()
    {
        return 'Hello TalkPHP.com';
    }
}

echo TalkPHP_Hello::getInstance()->getText();

This is the best way for it to be application wide. You just call getInstance() and there you have the instance. If you don't need many instances of the object, simply use it like so.

Scottymeuk 01-06-2009 02:29 AM

Again that is similar to what i have. I idealy would like to have like a parent class of $app and all classes under that $app->class as its a framework im making so it needs to be simple code to use. I already have the $app->class->function() working but i need to make it so i dont have to keep doing $app = new app;. the reason for needing it is i use a lot of included files and classes in other classes

Wildhoney 01-06-2009 02:48 AM

I see. Well, just use my original code and set that to $pApp like so:

php Code:
$pApp = TalkPHP_Factory::getInstance();

If you do that in a file that is loaded towards the beginning of the execution, then $pApp will be accessible from most places throughout your applications. You could make it a global, but I'm not too fond of that. Outside of classes and functions, it will work perfectly well like so, even when in different files:

php Code:
echo $pApp->TalkPHP_Goodbye()->getText();

Scottymeuk 01-06-2009 02:50 PM

My index.php (This needs to carry to:

All Other classes
All Other files that are included by functions
Everything that is included really without having to do
PHP Code:

$core  = new app_1

at the top of each page.

PHP Code:

$core = new app_1;
$core->app = new app($core);

echo 
$core->app->xss_clean('f'); 

The class:

PHP Code:

class app_1
{
    public 
$app;
    public 
$database;
    public 
$form;
    public 
$input;
    public 
$language;
    public 
$session;
    public 
$user;
    public 
$plugin;
    
    function 
__construct()
    {
        
$this->app = new app();
        
$this->database = new database();
        
$this->form = new form();
        
$this->input = new input();
        
$this->language = new language();
        
$this->session = new session();
        
$this->user = new user();
        
//$this->plugin = new plugin();
    
}


If i am honest with you I dont need any registry etc if its possible. I just really need to make that global. I have tried what you said above but i have to do
PHP Code:

$pApp TalkPHP_Factory::getInstance(); 

again in all the included files etc which i cannot do as it needs to be all automatic as it needs to be fast to develop. I know its only 1 bit of code but it will get really annoying and it seems a bit pointless.

Scottymeuk 01-06-2009 03:15 PM

Sorry for the double post but i wanted to show what code I have been using to make it global untill now.

PHP Code:

function Factory($__CLASS__)
{
    static  
$list = array();
    if(!isset(
$list[$__CLASS__]))
    {
        
$list[$__CLASS__] = new ReflectionClass($__CLASS__);
    }
    
$arguments func_get_args();
    
array_shift($arguments);    
    return  
$list[$__CLASS__]->getConstructor() ? $list[$__CLASS__]->newInstanceArgs($arguments) : $list[$__CLASS__]->newInstance();
}
function 
Singleton($__CLASS__)
{
    static  
$list = array();
    if(!isset(
$list[$__CLASS__]))
    {
        
$arguments func_get_args();
        
$list[$__CLASS__] = call_user_func_array('Factory'$arguments);
    }
    return  
$list[$__CLASS__];
}
class 
Singleton 
{
    private 
$_class,$_instance;
    
    public function 
__construct($__CLASS__)
    {
        
$arguments func_get_args();
        
$this->_class = new ReflectionClass($this->_instance call_user_func_array(__CLASS__$arguments));
    }
    
    public function  
__get($property)
    {
        return 
$this->_instance->$property;
    }
    
    public function 
__call($method, array $arguments)
    {
        return 
$this->_class->getMethod($method)->invokeArgs($this->_instance$arguments);
    }
    public function 
__set($property$value)
    {
        
$this->_instance->$property $value;
    }
    public function  
__clone()
    {
        die(
__CLASS__.' cannot be cloned');
    }
    public function 
equal($_instance)
    {
        return 
$_instance instanceof Singleton $this->_instance === $_instance->_instance $this->_instance === $_instance;
    }
}
class 
Factory extends Singleton 
{
    protected 
$_arguments;
    public function 
__construct($__CLASS__)
    {
        
$this->_arguments func_get_args();
        
$this->_class = new ReflectionClass($this->_instance call_user_func_array(__CLASS__$this->_arguments));
        
array_shift($this->_arguments);
    }
    public function 
__clone()
    {
        return 
count($this->_arguments) ? $this->_class->newInstanceArgs($this->_arguments) : $this->_class->newInstance();
    }


It works perfect, I can use it inside all functions and classes and anywhere on the site but its just not very nice to use. It needs to be simple for people to call it etc when they use this.

Example usage of above:

PHP Code:

echo Factory('app')->xss_clean('f); 


sketchMedia 01-07-2009 12:47 AM

Quote:

If i am honest with you I dont need any registry etc if its possible. I just really need to make that global
But it is accessible globally via the static method of the factory (which kind of behaves like a registry, apart from it adds non-existent objects automatically as opposed to the registry above (my example) that requires you to at start up load or 'register' objects to the array.

There is no way to make a variable, such as '$app' fully accessible like that (i.e. automatically available in other scopes) as far as im aware anyway, it will work great until you enter into another scope, then it wont be accessible (as you are no longer in the global scope).
We can however use the global keyword to 'import' any variables defined in the global scope into our current scope.

An example:
PHP Code:

//Some init file --- bootstrap.php
$app = new stdClass();
$app->test "A test message";

//'app' defined in 'global' scope

//implementation file --- driver.php
function aSimpleFunction()
{
    
//We are now in the aSimpleFunction's scope, therefore this will fail as 'app' doesn't exist in this scope
    
echo $app->test;
}
//however! this still works as we are now back in the global scope and 'app' is once again in our 'sight':
echo $app->test;

aSimpleFunction(); 

If we put 'global' in there:
PHP Code:

$app = new stdClass();
$app->test "A test message";

function 
aSimpleFunction()
{
    global 
$app;
    
//we can now see app again!!!
    
echo $app->test;
}
aSimpleFunction(); 

But however such variables are considered bad practice for many reasons and frowned upon by many (myself included). This however (as with many things in programming) opens another heated debate which I wont be going into now I don't have the energy.

I'm not sure if I understand your implementation of a singleton (or indeed most of it, seems very complex for a relatively simple problem in my opinion).

I approve of the use of a factory method; as it allows you to access system objects without worrying about whether the class has been included (if its implemented to include the file needed) and/or pushed on the the system object array (i.e. registry), but is there anything stopping me doing this?:
PHP Code:

$newApp = new class(); 

Sorry if I have mis-interpreted your code, but I'm tired and my right eye seems to be twitching? which is either down to the fact that i have had FAR far too much coffee today, or indeed not enough! now there's a conumdrum. Its getting rather annoying actually; not that's any excuse mind you :-@.

Anywho.....

A singleton object is an object that can be created only once, there shouldn't really be an external factory/registry implementation involved in order to make an object singleton. This is a classic singleton implementation:

PHP Code:

class someRandomClass
{
    private static 
$_pSelf;
//you could do this more elegantly than induce a fatal error but I cba ;)
    
private function __construct() {}
    private function 
__clone() {}

    public function 
getInstance()
    {
        if(!
self::$_pSelf instanceof self)
        {
            
self::$_pSelf = new self();
        }
        return 
self::$_pSelf;
    }
}

$obj someRandomClass::getInstance(); //works
$obj = new someRandomClass//will fail
$obj2 = clone $obj//will fail 

you can call :
PHP Code:

$obj someRandomClass::getInstance(); 

as many times as your server or indeed patience will alow and you will always end up with the same instantiation of that particular class and creating a new one is simply not possible via 'new' keyword or even cloning the existing object returned by 'getInstance' thus making it singleton. The added bonus to this of course (and used to good effect in the factory implementation by Wildhoney) is that it is (once included) accessible globally regardless of the scope, by just typing this:
PHP Code:

$obj someRandomClass::getInstance(); 

For what its worth, if you dont like the 'factory::accessor('classname')' syntax you may as well do this:
PHP Code:


function factory($szObject)
{
    return 
TalkPHP_Factory::getInstance()->$szObject();   
}

echo 
factory('TalkPHP_Hello')->getText();
echo 
factory('TalkPHP_Goodbye')->getText(); 

A little pointless and one more function call than there should be but it basically provides the same basic api and behaviours as your solution.

BTW, a few errors in your code (well notices if you read as PHP would) Wildhoney, a few static properties called in an objective context. Nothing however to scream about :-D
PHP Code:

    if (!isset($this->m_aInstances[$szClass]))
        {
            
$this->m_aInstances[$szClass] = new $szClass();
        }
        
        return 
$this->m_aInstances[$szClass]; 

should be
PHP Code:

    if (!isset(self::$m_aInstances[$szClass]))
        {
            
self::$m_aInstances[$szClass] = new $szClass();
        }
        
        return 
self::$m_aInstances[$szClass]; 


Wildhoney 01-07-2009 02:13 AM

That doesn't need to be static as it is called after the getInstance() call. Therefore it is now an object, as it were.

Nonetheless, a very good post!

Another way to do this, although I don't approve of this way, is to use $GLOBALS. I've wrapped it in two functions just to make it look slightly more elegant, but not a lot!

php Code:
class TalkPHP_Hello
{
    public function getText()
    {
        return 'Hello TalkPHP.com';
    }
}

class TalkPHP_Hello_Child
{
    public function getText()
    {
        /* The above class is now accessible from everywhere! */
        return getGlobal('pHello')->getText();
    }
}

function getGlobal($szVariable)
{
    return $GLOBALS[$szVariable];
}

function setGlobal($szVariable, $pObject)
{
    $GLOBALS[$szVariable] = $pObject;
}

setGlobal('pHello', new TalkPHP_Hello());
setGlobal('TalkPHP_Hello_Child', new TalkPHP_Hello_Child());

echo getGlobal('TalkPHP_Hello_Child')->getText();

Scottymeuk 01-07-2009 02:19 AM

Ok thanks for all the help guys, I have decided to use Kalle's way of doing it as he showed me on msn that its a good way. The class is:

PHP Code:

class core
{
    protected static 
$instance
    protected static 
$objects = Array(); 

    private function 
__construct() 
    { 
    } 

    private function 
__clone() 
    { 
    } 

    public function 
__get($component
    { 
        
$component strtolower((string) $component); 
        if(
array_key_exists($componentself::$objects)) 
        { 
            return(
self::$objects[$component]); 
        } 
        return(
NULL); 
    } 

    public static function 
init() 
    { 
        if(!
is_object(self::$instance)) 
        { 
            
self::$instance = new self
        } 
        return(
self::$instance); 
    } 

    public static function 
load($component
    { 
        
$component strtolower((string) $component); 

        if(
array_key_exists($componentself::$objects)) 
        { 
            return(
true); 
        } 
        
$class $component
        if(!
class_exists($class)) 
        { 
            die(
$class ' - class not found!'); 
        } 

        
self::$objects[$component] = new $class

        return(
self::$objects[$component]); 
    } 


and i now use it like:

PHP Code:

class controller
{
    public 
$core;
    
    function 
controller()
    {
        
$this->core core::init();
    }   


PHP Code:

class theClass extends controller 

and on the index.php page i have:

PHP Code:

$core core::init();
core::load('app');
core::load('input');
core::load('session');
core::load('database');
core::load('user');
core::load('template');
core::load('form');
core::load('language'); 

and at the top of all files (not classes) i just use:

PHP Code:

$core core::init(); 

Thanks for all the help guys. Really appreciate it. No doubt I will be coming back with some more problems soon and then once i have all them sorted i can help others :). (Next problem is going to be something along the lines of view files and template system and controllers lol).

Thanks

sketchMedia 01-07-2009 03:06 PM

Quote:

That doesn't need to be static as it is called after the getInstance() call. Therefore it is now an object, as it were.
Yes, but is also throws you a load of PHP5 E_STRICT Warnings:
Code:


Strict Standards:  Accessing static property TalkPHP_Factory::$m_aInstances as non static in /home/unix/sam/projects/TalkPHP_Factory/index.php on line 25

Strict Standards:  Accessing static property TalkPHP_Factory::$m_aInstances as non static in /home/unix/sam/projects/TalkPHP_Factory/index.php on line 27

Strict Standards:  Accessing static property TalkPHP_Factory::$m_aInstances as non static in /home/unix/sam/projects/TalkPHP_Factory/index.php on line 30
Hello TalkPHP.com
Strict Standards:  Accessing static property TalkPHP_Factory::$m_aInstances as non static in /home/unix/sam/projects/TalkPHP_Factory/index.phpon line 25

Strict Standards:  Accessing static property TalkPHP_Factory::$m_aInstances as non static in /home/unix/sam/projects/TalkPHP_Factory/index.php on line 27

Strict Standards:  Accessing static property TalkPHP_Factory::$m_aInstances as non static in /home/unix/sam/projects/TalkPHP_Factory/index.php on line 30
Goodbye TalkPHP.com


Wildhoney 01-07-2009 03:28 PM

Oh, I see. $m_aInstances just shouldn't be set as static.


All times are GMT. The time now is 05:30 AM.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2013, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.1.0