I have a webapplication where I use a registry class. The registry class holds important classes that I need troughout my application.
I have made the registry class a Singleton class and this class is static.
The content of the registry class is here:
<?php
class registry
{
private static $objects = array();
private static $instance;
private function __construct(){}
private function __clone(){}
public static function singleton()
{
if( !isset( self::$instance ) )
{
self::$instance = new registry();
}
return self::$instance;
}
public function storeObjects()
{
$Objects = array_merge($GLOBALS['EXTRA_LIBS'],array('data','page','session','uri','loader','modules'));
foreach($Objects as $name)
{
$this->$name = $name;
}
}
public function __set( $object, $key )
{
require_once(__LIBRARIES . DS . $object . '.class.php');
self::$objects[ $key ] = new $object( self::$instance );
}
public function __get( $key )
{
if( is_object ( self::$objects[ $key ] ) )
{
return self::$objects[ $key ];
}
}
public function returnAllObjects()
{
return self::$objects;
}
}
?>
Now whenever I want to use one of the classes here in my application I do:
registry::singleton()->data->adddata('somedata');
I use the classes a lot in my application, minimum of 1 time in every method.
Now my question is what is the best thing to do:
1) call the whole thing everytime
2) Class $registry = registry::singleton(); one time in every method and then just use the local variable. (I do not know if this will work)
Or is there a more elegant method to tackle this problem.
Whether you assign the registry instance to a local variable or not is irrelevant. If this is your only concern, use what makes the code more readable in the specific block of code.
I'd be more concerned about the implications of having to hardcode the Registry classname into my other classes and whether I really need a Singleton for the Registry. Have a look at Dependency Injection.
(2) would work just fine, try it. If you're using it a few times in each method it will save you some typing.
You could consider adding some sanity checking in __set() to make sure the file exists before require_once, as well as logging and/or returning an error if it doesn't (same for __get()).
Related
I have only basic PHP knowledge and am reading the book "PHP 5 E-commerce Development.pdf" which code source can be found here: https://github.com/rogeriorps/books_demo.
I am right at the beginning, on the creation of the registry with "objects" such as database handling, authentication and template sending.
I have a problem with the last line of code of this function, in a class that is a singleton and has objects:
public function storeObject( $object, $key )
{
if( strpos( $object, 'database' ) !== false ) {
$object = str_replace( '.database', 'database', $object);
require_once('databaseobjects/' . $object
. '.database.class.php');
} else {
require_once('objects/' . $object . '.class.php');
}
self::$objects[ $key ] = new $object( self::$instance );
}
Well, for the authentication class for instance, the constructor is empty: public
function __construct() { }
So it would require authentication.class.php and then create a new authentification(self::$instance)... On a constructor has no arguments!
How is that possible? What bothers me is the use of the word new, which normally calls the empty constructor, and gives it arguments out of the blue.
Any further explanations about how this all works are welcome as well, thank you :-)
PHP is a quite forgiving language, in that certain language constructs and practices are not as strictly applied as in other programming languages.
PHP does not complain if you provide more parameters than a class method expects, whether that method is a costructor or regular method. See below, which outputs "Hello World!" just fine:
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
class Foo
{
public function __construct() {}
public function hello($input) { return 'Hello ' . $input . '!'; }
}
$foo = new Foo(123);
echo $foo->hello('World', 'Universe');
I'm still trying to understand what your question is.
While the link to the source code is good, can you be more specific as to what files you are talking about.
Is this the line you are referring too? :
//I will call this class 'Singleton',as I have no idea what it's name is.
class Singleton
{
protected static $instance;
protected static $objects = [];
...
public function storeObject( $object, $key )
{
...
self::$objects[ $key ] = new $object( self::$instance ); //<--- this line
...
}
}
And then you say you have a class like this ( with an empty constructor )
class authenticate{
public function __construct() {} //empty
}
IF I follow that right, the extra argument is ignored in this case. However, consider having another class that can be stored in Singleton
class user{
//instance of singleton
protected $singleton;
public function __construct( $Singleton ) {
$this->singleton = $Singleton;
}
}
So in this case the same class Singlton calls a different class that does accept an instance of Singlton.
This is what I would call a form of Polymorphism.
Polymorphism is the provision of a single interface to entities of different types.
Personally I would prefer they actually have an interface for this. I will try to explain this as best I can. An interface can be thought of like a contract, it guarantees that any class implementing it will expose some public methods, with given arguments, in a specific order.
For example this
interface StorableObjectInterface{
//interfaces do not contain a method body, only the definition
//here I am using type hinting to tell it to only accept instance of Singleton
public function __construct( Singleton $Singleton );
}
So what this does is require that every class that implements this interface requires an instance of singleton as it's constructors first argument. They can still ignore it, but it should be a contractual obligation of being called from Singleton (IMO).
Then your clasess would look like this
class authenticate implements StorableObjectInterface{
public function __construct(Singleton $Singleton) {} // still empty
}
class user implements StorableObjectInterface{
//instance of singlton
protected $singlton;
public function __construct(Singleton $Singleton ) {
$this->singlton = $Singleton;
}
}
And then to lock it all together in Singleton you would check that $Object implements the interface.
public function storeObject( $object, $key )
{
...
if( is_a( $object, StorableObjectInterface::class ){
self::$objects[ $key ] = new $object( self::$instance );
}else{
//throw exception
throw new Exception("Class {$object} must implement interface ".StorableObjectInterface::class);
}
...
}
This is the way I would do it... It wasn't clear if you are just Using someones system, or creating your own.
So you may wonder why go through all this trouble, I'll give you an example.
Say later on you may need something like a path to a config file in authenticate, so you can easily load you credentials etc.
So you look in that class and see this ( we'll forget we know what we know )
class authenticate{
public function __construct() {} // still empty
}
So you figure you can just tack it in the constructor ( say you were using this class somewhere outside of Singlton ). So you change it.
class authenticate{
protected $config;
public function __construct($configFile = null) {
if( $configFile )
$this->config = include $configFile;
}
}
//then you call it for you new code
$z = new authenticate('passwordsAndStuf.php');
This is all fine until Singleton calls that constructor with an instance of itself. Now everything blows up. The main issue is that just looking at authenticate there is no way to tell this is going to happen. So by adding an Interface we are making a contract with Singleton any class implementing this interface will always accept an instance ofSingleton` as the first argument.
Hope that makes sense.
Why can't I get access to my "incSessionCount" function inside my "newSession" function?
class Session {
private $_num_session = 0;
private function incSessionCount() {
$this->_num_session++;
}
public static function newSession($key, $value) {
if( !isset( $_SESSION[$key] ) ) {
$_SESSION[$key] = $value;
$this->incSessionCount();
return true;
} else {
return false;
}
}
}
I just played around, like making incSessionCount() public and so on...
And then I thought, that it must be even accessible, when it's set to private ...
It's possible, that I missed a useful article, which should have helped me, but finally I ended up asking.
So why doesn't this work?
The problem is that your newSession is static, thus you are not supposed to call instance methods from it.
I suppose you're trying to do:
Session::newSession($key, $value);
instead of
$session = new Session();
$session->newSession($key, $value);
The error is not because you're calling a private method from within a public one, but because you're using $this instead of self.
$this special variable represents the current instance object while self represents the class itself.
If you enable error display and set error reporting level to E_ALL, you will see the problem is about using $this in a wrong context.
See below theses little modifications to do what you want, and check theses pages about
class Session {
private $_num_session = 0;
private static $inst = null;
public static function instance(){
if (!static::$inst)
static::$inst = new Session();
return static::$inst;
}
private function incSessionCount() {
$this->_num_session++;
}
public static function newSession($key, $value) {
if( !isset( $_SESSION[$key] ) ) {
$_SESSION[$key] = $value;
Session::getInstance()->incSessionCount();
return true;
} else {
return false;
}
}
}
You can look for design pattern and singleton on internet, and use magic __clone() to forbid more than one instance
I only found the german version of the documentation, I don't know why : http://de.php.net/manual/de/language.oop5.patterns.php
EDIT: Check this link about design patterns : http://www.phptherightway.com/pages/Design-Patterns.html
Remember, that static methods are binded with the class. Non-static methods are binded with the instance (when you do something like $instance = new MyClass();). But when you call something on the static context, you don't have to have any instance.
It's the same, when you want to call something on instance($this), because in static context doesn't exist any instance.
The problem is the public method is static and you are trying to use a method for an instantiated object. In a static method the $this variable refers to other static methods and properties only.
I'm currently in the process of moving from our own proprietary logging solution to log4php.
We use a lot of classes with only static methods in our project. The documentation defines the basic use case like:
class MyClass {
private $logger;
public function __construct() {
$this->logger = Logger::getLogger(__CLASS__);
$this->logger->debug('currently in constructor');
}
}
But I can't use that, cause I need $logger to be available in a static context as well. Making $logger static as well doesn't help either, because the constructor for my class is never called (as all its members are static).
The documentation tells me to use a static initializer for that member then. But then I would have to remember to call that for all classes I use. And that seems too error-prone.
So I came up with this:
class Foo {
private static $logger = null;
private static function logger() {
if( null == self::$logger ) self::$logger = Logger::getLogger( __CLASS__ );
return self::$logger;
}
public static function bar() {
self::logger()->debug( "test" );
}
}
Foo::bar();
But that seems like too much overhead as well. So, any suggestions?
I came up with one solution that works quite well but requires $logger to be public.
class Foo {
public static $logger = null;
public static function bar() {
self::$logger->debug( "test" );
}
}
$loggerName = "logger";
// Iterate over all declared classes
$classes = get_declared_classes();
foreach( $classes as $class ) {
$reflection = new ReflectionClass( $class );
// If the class is internally defined by PHP or has no property called "logger", skip it.
if( $reflection->isInternal() || !$reflection->hasProperty( $loggerName ) ) continue;
// Get information regarding the "logger" property of this class.
$property = new ReflectionProperty( $class, $loggerName );
// If the "logger" property is not static or not public, then it is not the one we are interested in. Skip this class.
if( !$property->isStatic() || !$property->isPublic() ) continue;
// Initialize the logger for this class.
$reflection->setStaticPropertyValue( $loggerName, Logger::getLogger( $class ) );
}
This I only have to define the $logger property once per class and run my initialization code once (I guess after the require_once section of the entry point of my application).
The performance impact of that code is negligible, especially since it is only run once (compared to my initial solution). This is what I measured inside a VirtualBox VM on an Intel Core2 Q9450 #2.66GHz:
10000 iterations for 157 classes completed in 2.6794s. Average per iteration: 0.00026794s
I am working with PHP classes and objects now. In this question the names of fields and methods are made up just so you get an idea of what I am talking about.
It is related to using the singleton and registry design patterns.
Now lets say I need to access a Database object, Cache Object, Core Settings object, Session object in almost every other class I will need to have access to these. SO I would use a registry to store all 4 of those object into 1 registry class object. I could then easiyl just pass in my 1 object into any other object that needs to access these. So that sounds great so far but what if I have some classes that do not need all 4 of those objects, what If I ONLY need to access the Database object or the Session object in some of my other classes? For perfromance would it be best to just use a singleton inside these other objects or would it be the same to go ahead and use my registry in these?
I do not know well enough how the objects work in PHP to know if there would be any sort of performnce gain (less memory usage, cpu usage, load time).
So anyone with experience in this maybe can tell me if there would be any gain using one or the other, I am at the stage where I can go ether way without it affecting production time or anything. I would like to use the best method if I can now.
That depends on your application. If you still need 3 out of the 4 classes, then it'd be more ideal to use the Registry than to handle the 3 independently only because you don't need the fourth. Loading the classes lazily would be one approach to reduce memory footprint, but then you need to instruct the registry when to create the objects and that's not much different than handling singletons. Alternatively, you could create an n-parameter constructor or use an array to instruct your Registry which classes to instantiate during construction.
class Registry {
public $class1;
public $class2;
function __construct($uses) {
foreach($uses as $class) {
$this->{$class} = new {$class}();
}
}
}
Then instantiate your Registry by specifying which classes to instantiate.
$reg = new Registry(array('class1'));
You would obviously want your constructor to handle zero parameters to account for instantiating all classes by default.
You can implement lazy loading to only load the objects you really need:
class Registry
{
private static $database = null;
private static function connectDatabase($key)
{
[... do connection stuff ...]
}
public static function getDatabase($key)
{
if (Registry::$database == null)
{
Registry::connectDatabase($key);
}
return Registry::$database;
}
}
The code to register the database connection parameters is left as exercise to the reader.
Perhaps this is the proper Singleton-Registry pattern. OFC, you can implement different things, SplFixedArray, ArrayAccess interface, and others. Also it is not bad to add the destruct and destroy inner objects to ensure no leak possible.
class oRegistry{
private static $instance = null;
private $storage = array();
private function __construct(){}
private function __clone(){}
public static function getInstance(){
if( self::$instance === null ){
self::$instance = new self();
}
return self::$instance;
}
public function attach($name, $o) {
if( true === isset($this->storage[$name]) ) {
throw new Exception('The instance with name '.$name.' already exists in registry.');
}
if( !empty( $name ) ) {
$this->storage[ $name ] = $o;
}
}
public function detach( $name ){
if( isset( $this->storage[ $name ] ) ) {
$this->storage[ $name ] = null;
unset( $this->storage[ $name ] );
}
}
public function get( $name ){
if( false === isset( $this->storage[$name] ) ) {
throw new Exception('Invalid instance requested');
}
return $this->storage[ $name ];
}
}
// usage example
$storage = oRegistry::getInstance();
$obj = new stdClass;
$obj2 = new stdClass;
$obj->test1 = 'test';
$obj2->test2 = 't2';
$storage->attach( 'test1', $obj );
$storage->attach( 'test2', $obj2 );
$got = $storage->get( 'test2' );
var_dump($got); // object(stdClass)#3 (1) { ["test2"]=> string(2) "t2" }
$storage->detach( 'test2' );
$got = $storage->get( 'test2' );
var_dump($got); // bool(false)
First thing i want to say that it's not an easy question to explain, so please be patient if it seems confusing.
I have a set of classes like this
class Product {
public static $static_type = 'product';
public static $static_table = 'product_table';
public function __construct($params) { //do some }
}
and then there are the classes News, Events etc
From another class i need to access to those static variables inside these classes in an iterative way. Something like:
//...
if (Product::$static_type) { //do some }
else if (News::$static_type) { //do other }
//...
I want to trasform it in a cycle, like foreach in a way like this (it's not correct but makes sense to my question)
foreach ($classes as $class) {
echo $class::$static_type; //brrrr, just to render the idea :)
}
So i think about a singleton/static class that has a static method returning an array of my classes (not instantiated). Like this:
class Conf {
public function __construct() {
//nothing
}
public static function get_class_array () {
//how to do this???
}
}
and then
foreach (Conf::get_class_array() as $class) {
echo $class::$static_type; //brrrr, just to render the idea :)
}
How i can reach this? I don't want to instantiate Product, News or others in this case.
Edit: eval is evil, i don't want to use it. No tricks with get_declared_class, if there's no way to solve I will use reflection, that i think it's the more elegant way among the mentioned :(.
Edit: in the meantime i'll do the Conf::get_class_array() in this way
public static function get_class_array () {
return array(new ReflectionClass('Prodotto'), new ReflectionClass('News'));
}
and then call it here:
foreach (Conf::get_class_array() as $class) {
echo $class->getStaticPropertyValue('static_type');
}
I don't think you can do this. You could however do one of these:
$properties = get_class_vars('Product');
echo $properties['static_type'];
or
$class = new ReflectionClass('product');
echo $class->getStaticPropertyValue('static_type');
Note that in PHP 5.3 echo $class::$static_type; will work (http://php.net/manual/en/language.oop5.static.php)
Until 5.3.0, you can try this method. Create a container class as you suggested (we'll call it Conf to stick with what you had), and provide two methods for setting and getting applicable classes that you want to iterate over:
<?php
class Conf {
private static $instance;
private $classes = array();
public static function getInstance() {
if ( is_null(self::$instance) ) {
self::$instance = new self();
}
return self::$instance;
}
public function registerClass($className) {
// Use associative index to maintain uniqueness
$this->classes[$className] = $className;
}
public function getRegisteredClasses() {
return $this->classes;
}
}
Some example classes and how to register them:
class X {
public static $a = "catus";
public static $b = "pants";
}
class Y {
public static $a = "apples";
public static $b = "bananers";
}
$conf = Conf::getInstance();
$conf->registerClass("X");
$conf->registerClass("Y");
Now, to access and/or alter the static members, you can do something like the following (using RefelectionClass as tom Haigh pointed out):
$conf = Conf::getInstance();
echo "<pre>";
foreach ( $conf->getRegisteredClasses() as $class ) {
$reflection = new ReflectionClass($class);
echo "<hr/>Class: $class\n";
// Access example
print_r( $reflection->getStaticProperties() );
// Alter example
$reflection->setStaticPropertyValue("a",
$reflection->getStaticPropertyValue("a") . "-modified"
);
print_r( $reflection->getStaticProperties() );
}
If you have a class naming convention like Com_Example_Static_X and Com_Example_Static_Y, you can simplify Conf->getRegisteredClasses() (and even make it a static method if you so desire) by doing as n3rd suggested:
class Conf {
// ....
static public function getMatchingClasses($pattern="/^Com_Example_Static_.+$/") {
$response = array();
foreach ( get_declared_classes() as $className ) {
if ( preg_match($pattern, $className, $m) ) {
$response[] = $className;
}
}
return $response;
}
}
And, of course, update your foreach to:
foreach ( Conf::getMatchingClasses() as $class ) {
// ...
}
Hope that was helpful.
You can use get_declared_classes() to get a list of classes. This will be all class though, not just the ones you've declared.
You should make all your classes inherit from a base class:
class Product extends MyBase {}
Then you can list the classes like this
function get_class_array()
{
$myClasses = array();
foreach (get_declared_classes as $class)
{
if (is_subclass_of($class, 'MyBase'))
$myClasses[] = $class;
}
return $myClasses;
}
Then you can get the data like this:
foreach (get_class_array() as $class)
echo eval("return $class::\$foo;"); // Yes yes, eval is evil, we know...
To get a list of classes, you can use get_declared_classes. Then you'll have to determine which of those classes you want to process.
You could do this by looking for a common base class with is_subclass_of, or using ReflectionClass to see if it has the static member variables you are interested in.
I don't think there's an easy way to do this. Here are a few ideas off the top of my head how you could go about doing this:
Use get_declared_classes() to retrieve a list of all defined classes and check them against your naming scheme (e.g. MyNamespace_*) or whether they implement an interface (e.g. MyStaticEnumerable).
Kinda like the above, but a little more sophisticated: write your on class loader and have it check whether a loaded class is one of ones you want to enumerate. If so, make it known to some manager class.
Check the directory in which the classes are defined to manually enumerate all classes.