in my php application, I'm aggregating some rss feeds. I want this to be done every 24h and I don't want to store it in database.
I have built a singleton class that creates my rss informations ($ActuList) :
class ActualitesManager{
/**
* #var Singleton
* #access private
* #static
*/
private static $_instance = null;
public $ActuList = null;
private function __construct()
{
error_log("construct");
/* My stuff to create $ActuList */
}
public static function getInstance()
{
error_log("instance before : ". json_encode(self::$_instance) );
//if(is_null(self::$_instance)){
//if(!isset(self::$_instance)){
if (null === self::$_instance)
{
$object = __CLASS__;
self::$_instance = new $object;
}else
{
error_log("skip constructor");
}
error_log("instance after : ". json_encode(self::$_instance) );
return self::$_instance;
}
}
but each call to the getInstance() calls the constructor as it should normally be done only once and then give the already instanciated $_instance
My debug always gives :
instance before : null
instance after : {"ActuList":null}
and never displays the "skip constructor".
What am I missing ?
and in a general way : is this the correct way to do ? As parsing rss feeds is time consuming I don't want this task to be done for each visitor : how to keep results in an always instanciated php class ?
Thanks for your ideas !
I don't want this task to be done for each visitor : how to keep
results in an always instanciated php class
I focused on that part of the question, which makes me think that you rather missconcept the Singleton pattern, objects and requests.
Let me change your sample as another demonstration which maybe you will understand better
<?php
class Singleton {
public $x = 1;
private static $_inst = null;
private function __construct() { }
/**
* #return Singleton
*/
public static function getInstace() {
if (self::$_inst == null) {
self::$_inst = new self();
}
return self::$_inst;
}
}
if (isset($_POST['y'])) {
Singleton::getInstace()->x++;
echo Singleton::getInstace()->x;
}
?>
<form action="" method="post">
<input type="submit" name="y"/>
</form>
We have a Singleton class which contains public property $x accessible via its instance. Since constructor is private, you can access instance only from getInstance() method. Singleton::getInstace()->x will access the property $x.
In this code, if the button is clicked, we expect the $x property to increment by one.
When we first launch the script, the property has value of 1. After we press the button, it has value of 1 + 1 = 2. And now, you expect, the value of 2 somehow to be written in the memory, so if the button is clicked for third time, it should show 3. But, it unfortunately is not true, and no matter how many times you do click the button, you will always recieve a value of 2, since after requesting the page for N-th time, it reinstantiates the class and it loses all of its changes.
There is no way to keep that persistence between all your clients only in the memory, because it's flushed right after it is used.
My suggestion is to keep changes into a database.
You can also do an object serialization, so you can save your changes into the database;
E.g.:
serialize(Singleton::getInstance());
outputs:
O:9:"Singleton":1:{s:1:"x";i:1;}
You can store this somewhere i.e. in db col serialized
afterwards extract it and assign it to variable:
$singleton = unserialize($row['serialized']);
Perform a change:
$singleton->x++;
See the serialized changes again:
O:9:"Singleton":1:{s:1:"x";i:2;}
Save them back.
Assign again
$singleton = unserialize($row['serialized']);
$singleton->x++;
echo $singleton->x;
Outputs: 3
This might not be the most efficient way, but you cannot rely on PHP objects to save in memory like database. That's why databases are present, and all the information for users, etc. is not stored into objects kept in the memory.
If there are a lot of credentials to save, the best design decision is to save each field into the DB instead of a serialized object, so you can set to the new instance the value from the database. That's what, in practice, the ORM's are for. Bind resultset to object.
Think twice which approach can fit your needs, if reinstantiating an object / pulling from the database for each user is costly, you should think about a caching approach, if no changes - no db pull, take from the cache.
I guess you are trying to get an instance of a child-class of ActualitesManager.
To achieve this, you need to declare $_instance as protected. Rename it to $instances too since it will be an array.
Change the code of getInstance to something like the following:
foreach ( static::$instances as $instance )
{
if ( $instance instanceof static )
{
return $instance;
}
}
$instance = new static;
static::$instances[] = $instance;
return $instance;
We store every instance into one array and loop this. In static is a pointer to the extending class and it can be used with instanceof.
Using "static" will use the current child-class and not the parent. "self" is always a pointer to the class containing this function.
Since you access the instance via parent and not the class itself you need to declare it as "protected" to be accessible.
However you only have one static protected $instances therefor it is an array. You loop that array and return the instance matching the current class if found. Else you create it and return the new instance.
Hope I could help with your issue :)
Related
I am trying to grasp an OOP concept brought into PHP 7+ for Conflict Resolution. I also want to make a dynamic call to save() in my design, which will take in a argument by reference.
To test the concept before I created this addition to my framework, I wanted to try the basics of simply outputting the zval of a variable.
My current trait looks like this:
trait Singleton {
# Holds Parent Instance
private static $_instance;
# Holds Current zval
private $_arg;
# No Direct Need For This Other Than Stopping Call To new Class
private function __construct() {}
# Singleton Design
public static function getInstance() {
return self::$_instance ?? (self::$_instance = new self());
}
# Store a reference of the variable to share the zval
# If I set $row before I execute this method, and echo $arg
# It holds the correct value, _arg is not saving this same value?
public function bindArg(&$arg) { $this->_arg = $arg; }
# Output the value of the stored reference if exists
public function helloWorld() { echo $this->_arg ?? 'Did not exist.'; }
}
I then created a class which utilises the Singleton trait.
final class Test {
use \Singleton { helloWorld as public peekabo; }
}
I passed in the variable I wanted to reference like so, since the method expects a reference of the variable - it does not need to be set yet.
Test::getInstance()->bindArg($row);
I now want to mimic the concept of looping through rows from a database result, the concept is to allow a save() method to be added to my design, but getting the basic concept working comes first.
foreach(['Hello', ',', ' World'] as $row)
Test::getInstance()->peekabo();
The issue is, the output looks like this:
Did not exist.Did not exist.Did not exist.
My expected output would look like:
Hello, World
How can I store the zval inside of my class for later usage within a separate method?
Demo for future viewers of this now working thanks to the answers
Demo of this working for a database concept like I explained in the question here:
"I now want to mimic the concept of looping through rows from a database result, the concept is to allow a save() method to be added to my design"
Use public function bindArg(&$arg) { $this->_arg = &$arg; } It works with PHP 7.3
I just stumbled over a PHP class and wonder if there was a valid reason for the way one of it's methods is written.
LogUtility::getLogger() is called as a static method in various other PHP classes of the PHP application. Does the used if statement make sense or is $logManager always null when getLogger() is called?
class LogUtility
{
/**
* #var LogManager
*/
protected static $logManager;
/**
* #return Logger
*/
public static function getLogger($name)
{
if (!self::$logManager) {
self::$logManager = GeneralUtility::makeInstance(LogManager::class);
}
return self::$logManager->getLogger($name);
}
}
You could quickly whip up a test, like below, and test / prove it yourself:
class someClass {
protected static $stored;
public static function test() {
echo '<br>Stored state:' . self::$stored;
if ( ! self::$stored) {
self::$stored = "set";
}
}
}
someClass::test();
someClass::test();
Output is:
Stored state:
Stored state:set
So, based on this simple test, the answer is Yes, the if statement makes sense. The static variable is set and maintained.
$logManager is set to a property of the class, so for now, its pretty much an empty class, just returning with a getter, the instance. This just sets the code to just reuse the object. So a bit of recycling code there.
In the class you are now able to play with this object freely. So if you run this LogUtility from another piece of code by setting it to a var, you have already instantiated it. e.g. $util = new LogUtility();now if someone comes along and tries to instantiate it again, $anotherUtil=new LogUtility(); this recycles the class, passing back the already instantiated instance, instead of instantiating a new one.
Therefore, yes, it kept it. Although, the var doesn't contain the "same value" per say, it contains a reference to the "same" class that was instantiated with he other variable, so it ends up a copy of the same instance there.
It will be null for only the first call in a lifecycle. This implements a design pattern called Singleton.
Check out https://sourcemaking.com/design_patterns/singleton/php/1
The following is an excerpt from some code I wrote to assign the $user->privilege based on a method from that same class. It seems excessively repetitive, and I am wondering if there is something I can do to make it more readable -- given that I haven't seen this kind of repetition too much in codes I have looked at.
$user -> privileges = $user -> get_privileges ( $user -> username );
It doesn't look particularly repetitious to me, but it is a little unusual to be assigning an object's property based on a method outside the class. Instead, this might be better handled inside the object constructor, eliminating the need for you to remember to set the property when coding:
class User {
public $username;
public $privileges;
public function __construct() {
// setup the user however that's done...
// And assign privileges in the constructor
$this->privileges = $this->get_privileges();
}
// In get_privilegs, rather than passing the username property,
// just access it via $this->username.
// Unless you need to use this method from time to time outside the class, it can be private
private function get_privileges() {
// Get privs for $this->username
}
}
And as an alternative to $this->privileges = $this->get_privileges(); called in the constructor, you might just set $this->privileges inside the get_privileges() method. Then you can just call it as $this->get_privileges() in the constructor, no assignment necessary. Either way works.
I use this pattern a lot when a method is expensive and I can just store the result for the remainder of the request:
class User {
protected $_privileges = null;
public function getPrivileges() {
if ($this->_privileges == null) {
// code to populate privileges array
$this->_privileges = $privileges;
}
return $this->_privileges;
}
}
That way getPrivileges() will only do the hard work once and afterward it uses its own locally cached copy for the remainder of the request for that object instance.
I use a data class to feed templates my data, I want to calculate a unique id from the data in the data class so I can check if the template with that data is already in cache and then serve that version.
so a function to get an unique id from an array of a class would help me out.
something like this works but is rather costly md5(serialize($classdata))
I'm hoping there is some function to get the unique id without serializing all data, or at least not to have to in php.
edit:
I celebrated too soon, the unique id is only the same in the current instance
a restart of the same script makes another id, which then of course is not in cache.
testscript used:
<?php
class foo {}
$f = new foo;
print spl_object_hash($f);
I'll explain in some more depth
class template_data implements IteratorAggregate, ArrayAccess, Countable {
private $_data;
//some methods for the overloaded classes
//
//the getId function
public function getId() {
return hash('md5',serialize($this->_data));
}
}
$t = new template('file');
$d = new template_data('some data');
$t->addData($d);
$t->display();
Now if the data given to the template engine is in cache it uses that version
preventing to having to re-parse the template for the dataset.
This is a simplistic view of the template_data, it is actually lazy loading and uses memcached dataid's so the data isn't actually fetched till it is used in the template.
You could try spl_object_hash()
From the docs
This function returns a unique identifier for the object. This id can be used as a hash key for storing objects or for identifying an object.
PHP does not create unique IDs that persist between executions for objects, this means that you are going about producing the desired behavior correctly. So while there is no good answer for the asked question I can give some suggestions to reduce the cost of producing your IDs.
First you can use json_encode rather than serialize. Second, you can store the value, so that multiple calls to the function will not re-serialize the data every time.
The json_encode function is not only faster than serialize, but it also produces a shorter string as output.
http://cw-internetdienste.de/2015/05/04/serialize-vs-json_encode/
class template_data implements IteratorAggregate, ArrayAccess, Countable {
private $_data;
private $_id;
//
//some methods for the overloaded classes
//
//the getId function
public function getId() {
if(empty($this->_id))
$this->_id = hash('md5',json_encode($this->_data));
return $this->_id;
}
}
Lastly; the best solution will probably be to cache the output of the template using the route, or arguments as the basis for the unique cache keys rather than the individual data sets used.
Why not look into and overriding the __toString() method on the object to get and hash the relevant data in the object.
For example
class Object
{
// Some vars
public $name = "Jake";
public $age = 26;
public $dob = "1/1/10"
// the toString method
public function __toString()
{
return md5($this->name . $this->age . $this->dob);
}
}
// Create new object
$object = new Object();
// echo the object, this automatically calls your __toString method
echo $object
In this situation you don't use serialize, which is costly, instead just use __toString() to generate your own unique id based on variables stored with the object.
I want to stop object creation by the user of class "OzoneRequest" and "OzoneResponse"in PHP . Only one object is created at OzoneApplication's constructor. How I'll do this? May be you understand my question
I do not want creation of object by the user Only I create an object that only one object exist. If user want to create object then this will not be performed... this will give an error......
class OzoneRequest
{
private static $instance = null;
private function __construct() { }
private function __clone() { }
public static function getInstance()
{
if (!isset(self::$instance)) {
self::$instance = new OzoneRequest();
}
return self::$instance;
}
}
class OzoneApplication
{
protected $req;
public function __construct()
{
$this->req = OzoneRequest::getInstance();
}
}
Make a private constructor, then call this from a static method within the class to create your one object. Also, lookup the singleton design pattern.
That would be the UseCase for a Singleton.
However, I do not see the point in restricting the User (read: the developer) to not create a Request or Response object if he wants to. Even if conceptually there is only one Request object (which is arguable; what if I need to dispatch multiple Requests against a remote service), the question is: why do you forbid a developer to change your code? I am a grown-up. If I want to break your code, let me break it.
Also note that the Singleton pattern is widely regarded an Anti-Pattern nowadays.