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.
Related
In my current application I have a number of objects that are required repeatedly
To save overhead of instantiating the same object over and over again, I keep an array of 'known' objects.
I can check the input data against the array, and - if already set - use the existing object, else go ahead to instantiate the new object and add the pointer reference to the relevant known objects array.
In most use cases, I can check prior to instantiating the class:
if(array_key_exists($identifier,$known_ClassObjects)){
$object = $known_ClassObjects[$identifier];
} else {
$object = new Class($params);
}
However, in some cases I can only identify that the object I am instantiating already exists once already inside it.
In that case I would like to be able to do one of 2 things:
return the OTHER (pre-existing) object instead of this one, e.g.
class Test{
public function __construct($params){
//apply processing to $params, resulting in $identifier
if(array_key_exists($identifier, $known_ClassObjects)){ //$known_ClassObjects is global
return $known_ClassObjects[$identifier];
} else {
//continue __construct() logic
return $this;
}
}
}
However, PHP ALWAYS returns the current object, even with code return $other_object;
'Internally Clone' the current object from the found one [of the same class, obv] so that when it returns, it has the correct relevant properties populated.
NOTE: including any parent/child class properties
-> I want to make this object EXACTLY the same as the found one.
So, if there was a PHP function clone_from(), it would work something like:
if(array_key_exists($identifier,$known_ClassObjects)){
$this->clone_from ($known_ClassObjects[$identifier]);
} else {
//continue with __construct()
}
return $this;
Given that 1. doesn't work and 2. doesn't seem to exist, I have only been able to do this in very 'hacky' ways: iterating through all properties of source object and setting all properties of current object.
However, this has obvious issues esp. with extended parent/child classes etc. which then requires things like reflection classes.
This seems like there SHOULD be a really simple solution, but I have been unable to find one
What you actually could do is using either a Singleton Pattern or a Factory pattern - in both cases, the creation of objects is controlled by some piece of code and you can decide, which object to return. Singleton already is a special form of a Factory pattern.
Consider this code
class Singleton {
protected static $instance;
protected function __construct() {
}
public static function instance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
}
The constructor is protected which will prevent a object construction from "outside" via new. However, there is also a static function instance with which somebody can request an object instance from the factory method.
$obj = Singleton::instance();
So, the (internal) object is created only once, and then delivered afterwards until the script ends.
However, PHP ALWAYS returns the current object, even with code return $other_object;
Constructor is not a regular function. You do not return from it. If you are in __construct() you are already constructing the new object. Before proceed, I strongly recommend you read at least: https://www.php.net/manual/en/language.oop5.decon.php
You most likely need a helper method or factory that would deal with that instead. Putting the logic you tried to place into your constructor is wrong.
Also you missed to clarify what the $identifier really is. But if that's object's property, then you can expose it (i.e. via getter) and have it read for your comparison/whatever by other code.
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 :)
My question is how should i be inserting php class objects into the database?
If i have
Cat.php
class Cat{
private $name;
public function setName($name){
$this->name = $name
}
public function database(){
....$this->name;
//Insert stuff into db
}
}
Somefile.php
if($_POST['catname']){
//This way?
$obj1 = new Cat();
$obj1->setName($_POST['catname']);
$obj1->database();
//Or this way
//Insert Cat directly into the db without creating an object of Cat
}
And i want to insert this cat into the database. Would it be best to create a function within cat that gets $this->name or before i create the object should i just insert it into the database?
The easiest way of doing it would be to serialize your class to string.
$string = serialize($cat);
http://php.net/manual/en/language.oop5.serialization.php
But this way you will create lots of overhead in your database.
The best way to do it would be to have the relevant fields in your table and just save the corresponding values. You can easily create a function which reads / saves your class to a table. This would make your data much more portable and exchangeable.
You can use serialize, and if you have a good autoloader, you will not have to worry about the class not existing when you unserialize.
Also note the 2 magic functions that you can use:
__sleep() - custom code to use when serializing an object
__wakeup() - custom code when unserializing an object
PS:
Replace:
$obj1->setName = $_POST['catname'];
with
$obj1->setName($_POST['catname']);
References:
http://php.net/manual/en/oop4.magic-functions.php
http://php.net/manual/en/function.serialize.php
http://php.net/manual/en/language.oop5.serialization.php
Ideally, the object itself should deal with saving itself to the database. This way, your logic doesn't need to know what cat is, or where it is saving itself. The cat class should be self-contained, and this means if you want to change DB types or anything else in the future, you can make it in the class without affecting your logic.
You should have a separate object that takes an instance of cat and puts the data into the database. You may want to have a method of getVitalData which provides all the data that should go into the database (you may eventually have more than just name).
Each class should have one responsibility
Normally there are several layers in your project and one of this layer is the DAO (Data Access Object). In this class you link your application to a database. this layer has to save your object into the database via a save method for example...
The day you wanna change the database you just rewrite this layer
To be really ideal, the object that is present in the DAO layer is different from the model object (CAT) : it is a persistent Object that looks like the table in the database
Take a look at serialize and unserialize, you can store the serialised string in your database if required - here's a quick example.
<?php
class Cat {
protected
$name;
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
return $this;
}
}
$cat = new Cat;
$cat->setName('Tiddles');
$string = serialize($cat);
$cat = unserialize($string);
var_dump($cat);
/*
object(Cat)#2 (1) {
["name":protected]=>
string(7) "Tiddles"
}
*/
Anthony.
What is the use of the __sleep and __wakeup magic methods in PHP? I read the PHP documentation but it's still not clear:
class sleepWakeup {
public function __construct() {
// constructor //
}
public function __sleep() {
echo 'Time to sleep.';
}
public function __wakeup() {
echo 'Time to wakeup.';
}
}
$ob = new sleepWakeup();
// call __sleep method
echo $ob->__sleep();
echo "\n";
// call __wakeup method
echo $ob->__wakeup();
This sample code prints:
Time to sleep.
Time to wakeup.
If I were to rename __sleep and __wakeup to foo and bar then it does the same thing. What is the proper use of these two methods?
As already described, __sleep() is called when you serialize() an object and __wakeup() after you unserialize() it.
Serialization is used to persist objects: You will get a representation of an object as a string that can then be stored in $_SESSION, a database, cookies or anywhere else you desire.
Resource values
However, serialize() cannot serialize (i.e. transform into a textual representation) values of the resource type. This is why all of these values will go missing after unserialize()ing it.
Object graph
or members, and the member's members and the ... ad infinitum
Another, perhaps more important point is, that serialize() will traverse the entire object graph of $obj if you serialize it. This is great when you need it, but if you only need parts of the object and certain linked objects are "runtime-specific" and shared across a lot of objects but also by other objects, you may not want that behavior.
PHP handles cyclic graphs correctly! Meaning: If (a member of) $a links to $b, and $b links to $a is handled correctly however many levels deep.
Example - session specific (shared) objects
For instance, a $database object is referenced by $obj->db, but also by other objects. You will want $obj->db to be the same objects - after unserialize()ing - that all the other objects in your next session have, not an isolated instance of the database object.
In this case, you would have __sleep() method such as this:
/**
/* DB instance will be replaced with the one from the current session once unserialized()
*/
public function __sleep() {
unset($this->db);
}
and then restore it like this:
public function __wakeup() {
$this->db = <acquire this session's db object>
}
Another possibility is, that the object is part of some (global) datastructure where it needs to be registered. You could do this manually of course:
$obj = unserialize($serialized_obj);
Thing::register($obj);
However, if it is part of the objects contract that it needs to be in that registry, it's not a good idea to leave this magical call up to the user of your object. The ideal solution is, if the object cares about its responsibilities, i.e. being registered in Thing. That's what __wakeup() allows you to do transparently (i.e. he need no longer worry about that magical dependency) to your client.
Similarly, you could use __sleep() to "un-register" an object if appropriate. (Objects are not destroyed when they're serialized, but it may make sense in your context.)
Closures
Last but not least, closures do not support serialization either. This means that you will have to re-create all attached closures in __wakeup().
They are pretty much like hook functions, which we can use according to our needs. I came up with this simple real time example. Now try executing this code in two scenarios:
class demoSleepWakeup {
public $resourceM;
public $arrayM;
public function __construct() {
$this->resourceM = fopen("demo.txt", "w");
$this->arrayM = array(1, 2, 3, 4); // Enter code here
}
public function __sleep() {
return array('arrayM');
}
public function __wakeup() {
$this->resourceM = fopen("demo.txt", "w");
}
}
$obj = new demoSleepWakeup();
$serializedStr = serialize($obj);
var_dump($obj);
var_dump($serializedStr);
var_dump(unserialize($serializedStr));
Scenario 1:
First by commenting __sleep() and __wakeup() methods, check the output. You will find the resource missing when you unserialize it.
Scenario 2:
Now try running it uncommenting them, you will figure out that the object dumped in first and last var_dump would be same.
These methods are used when calling serialize() and unserialize() on the objects to make sure you have a hook to remove some properties like database connections and set them back when loading. This happens when storing objects in sessions among other things.
Since PHP 7.4 there will be new methods __serialize() and __unserialize() available which should slightly change the usage of __sleep and __wakeup magic methods.
PHP currently provides two mechanisms for custom serialization of
objects: The __sleep()/__wakeup() magic methods, as well as the
Serializable interface. Unfortunately, both approaches have issues
that will be discussed in the following. This RFC proposes to add a
new custom serialization mechanism that avoids these problems.
More in PHP RFC manual https://wiki.php.net/rfc/custom_object_serialization.
// Returns array containing all the necessary state of the object.
public function __serialize(): array;
// Restores the object state from the given data array.
public function __unserialize(array $data): void;
The usage is very similar to the Serializable interface. From a practical perspective the main difference is that instead of calling serialize() inside Serializable::serialize(), you directly return the data that should be serialized as an array.
The following example illustrates how __serialize()/__unserialize() are used, and how they compose under inheritance:
class A {
private $prop_a;
public function __serialize(): array {
return ["prop_a" => $this->prop_a];
}
public function __unserialize(array $data) {
$this->prop_a = $data["prop_a"];
}
}
class B extends A {
private $prop_b;
public function __serialize(): array {
return [
"prop_b" => $this->prop_b,
"parent_data" => parent::__serialize(),
];
}
public function __unserialize(array $data) {
parent::__unserialize($data["parent_data"]);
$this->prop_b = $data["prop_b"];
}
}
This resolves the issues with Serializable by leaving the actual serialization and unserialization to the implementation of the serializer. This means that we don't have to share the serialization state anymore, and thus avoid issues related to backreference ordering. It also allows us to delay __unserialize() calls to the end of unserialization.
try out this
<?php
$ob = new sleepWakeup();
$safe_me = serialize($ob);
$ob = unserialize($safe_me);
?>
I have a helper class DAO (I do not know if is OK have this) for get Categories from MySQL DB, the struct is basically this:
<?php
require_once '../include/PDOConnectionFactory.php';
class CategoryDAO extends PDOConnectionFactory
{
/**
*
* #var PDO $conn
*/
private $conn;
public function __construct()
{
$this->conn = PDOConnectionFactory::getConnection();
}
}
?>
This class have these methods (some then):
getMaxLevel()
getAllCategories()
getAllCategoriesOfLevel($level)
haveChildCategory($categoryName)
getIdCategory($categoryName)
getCategoryName($idCategory)
Edit: The body of the method getAllCategories() is similar to this below, and almost all method of this class call this getAllCategories():
public function method()
{
try {
$stmt = $this->conn->prepare("SELECT * FROM category");
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (Exception $e) {
echo $e->getMessage();
}
return $result;
}
I do not know what the best approach to reduce the redundancy of queries, I think in these:
Instance the class object, call getAllCategories() and call the others methods passing the result by parameter.
Have a private property with the result of getAllCategories(), populate when the objects is created by __construct().
But in these cases I see this drawback:
Do not appear be a good use of OOP.
The object may be outdated after a DB UPDATE or INSERT.
If my problem is conceptual in OOP, please let me know.
This response is dependent on the current query structure, where there are no conditionals
class CategoriaDAO extends PDOConnectionFactory
{
/*DB Connection, static member since you only need one connection*/
private static $dbConnection;
/*Sql result set, static since there is not conditonal and only a single table used*/
private static $resultSet;
private static function getConnection()
{
/*Connect to mysql db, set CategoriaDAO::dbConnection; */
}
private static function populateResultSet()
{
/*Run query and populate resultSet - either as sql result or parse to array - your call*/
}
/**
*
* #var PDO $conn
*/
private $conn;
public function __construct()
{
/*Get sql connection if one hasn't already been established*/
if(!CategoriaDAO::dbConnection)
$this->conn = PDOConnectionFactory::getConnection();
}
}
The thought process behind this is that since the results will always be the same (ignore, update, insert, delete for now) there's no requirement to keep a copy of the results in each object.
As you pointed out table updates will knock the stored result set out of sync with the object; this is where I'd like to back track a bit and say that if the result set for a given object only has to be up to date at the time of creation then use normal object members.
Also worth considering both independently and in conjunction with the previous comment is whether or not the query will change and if it will does it require object members to be generated. If the query doesn't change then there's nothing to worry about - except the previous point. If it does change your options are more or less covered in the following examples.
class Foo{
private $someMember;
/*
$params = Associative array of fields and values
*/
private static buildAndRunQuery($params)
{
/*Build sql query based on the given params Array()*/
}
public __construct($someMemebrValue)
{
$this->someMember = $someMemberValue;
Foo::buildAndRunQuery(Array("fieldName" => $this->someMember));
}
}
In this example you're still using a static method to generate the query but you're passing non-static members for the process/ At this point (see comment on objects being up to date at time of creation) you can either store the results within the static member or pass them back to the __construct() function and store int he object instance.
Then there's the potential that the query your using is a bit more involved than simply requesting certain fields such that creating a multidimensional array to pass to the static function would be more hassle than it's worth. In which case you might split the buildAndRunQuery() into buildQuery() - instance method and runQuery() static method such as.
class Foo{
private $someMember;
/*
$params = Associative array of fields and values
*/
private static runQuery($query)
{
/*Build sql query based on the given params Array()*/
}
private function buildQuery()
{
/*Construct your query here and either return calling method or store in instance member*/
/*Either*/
return <Constructed query>;
/*Or*/
$this->query = <Constructed query>;
}
public __construct($someMemebrValue)
{
$this->someMember = $someMemberValue;
/*As per buildQuery() comment either:*/
Foo::runQuery($this->buildQuery());
/*Or*/
Foo::runQuery($this->query);
}
}
In this case there are a couple of options for handling the generated query prior to calling Foo::runQuery().
Of course there's always the possibility that you don't want to create and run the query in a synchronous manner or indeed in the constructor.
In conclusion I personally feel that for methods that interact with services independent of the object itself such as Sql or perhaps focused DOMDocument, or similar, object interactions it is best to use Static methods where they are both relevant and not ultimately cutting off your nose to spite your face (needlessly complex etc). Of course all of this needs to be considered on a per Class basis.
Your DAO sounds like a really bad idea from the standpoint of efficiency. Personally, I don't find Data Access Objects to provide any real value. When would you pull a category name without also pulling its ID? You'd be better of querying for entire rows at a time and writing a few specialized queries to perform your other functions.