I have worked on my own factory class that instantiates dependencies and passes through constructs when a dependency is set/requested. If the dependency is cached ( Has already been instantiated by another class ) i pass that class instance instead of instantiating all over again ( mainly used for db connection ). The issue i am having at the moment is as follows.
** To avoid a large question & save reading time i am attempting to illustrate the issue as simply as possible, if the actual code is needed i can paste in.
Class View {
// Construct Requests User Model
}
Class Controller {
// Construct Requests User Model & Class View
$this->user->set($newuserid);
$this->view->display('file');
}
So Controller is instantiated, since View is set as a dependencies it is instantiated and passed to Controller via __construct. Everything is fine, but for things like a profile page. Where i set a new user ( illustrated above ) setting the new userid also alters the userid that is contained within the View User Model. I am not using static vars so i am confused as to why changed made in controller affect the view user model. This causes an issue for me because the logged in User's ID is set via entry point of site ( bootstrap ) & errors are caused when the profile page overwrites the logged in users id. I have added a newInstance option within my factory, to instead instantiate a new user model for the user profile. Things work fine, but i am still curious as to why i had/have this issue.
In PHP5, variables that hold objects do not contain the actual object itself, but an identifier to reference the actual object.
So when you pass objects around, you actually pass a copy of the identifier to the object which of course points to the same object.
PHP Docs on Objects and references
So when you use the same user object in both, your Controller and your View and manipulate it, the actual User Object will be changed. Since both object variables still hold the same identifier to the object, the objects state will be the same in your Controller as well as in your View.
<?php
class User {
protected $name;
public function __construct($name)
{
$this->setName($name);
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
}
class View {
protected $user;
public function __construct( User $user )
{
$this->user = $user;
}
public function render()
{
echo $this->user->getName();
$this->user->setName('Tony');
}
}
class Controller {
protected $user;
protected $view;
public function __construct( User $user, View $view )
{
$this->user = $user;
$this->view = $view;
}
public function someAction()
{
$this->user->setName('Thomas');
$this->view->render();
echo $this->user->getName();
}
}
$user = new User('Jeffrey');
$view = new View($user);
$controller = new Controller($user, $view);
$controller->someAction(); // Output: ThomasTony
The cruicial thing to understand is that both, the View and the Controller are referencing the same object, thus manipulating the object in one class will result in also manipulating it in the other class (technically that's wrong because both just point to the same object).
Now let's use the clone keyword in the View:
public function __construct( User $user )
{
$this->user = clone $user;
}
Now the $user property of the view will hold a "pointer" to a copy of the user object. Changes made on that object will not affect the initial object that was passed.
Thus the output will be: JeffreyThomas
I want to lose a few words of caution:
Instead of cloning the object you should rather make sure to have clean way of flow of your objects. Your View should not manipulate the state of the User once it got passed.
Using clone can lead to undesired behavior. If someone passes an object to one of your classes and expects class to alter the objects state (we're not talking about "Views" here) while it doesn't it can give them a very hard time of debugging.
Related
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 :)
I have read the tutorials on how to make a sub class of CWebUser and followed the instructions. The paths all work, and the code is getting into the right methods, however the value returned from my getter is always nil.
class PersonUser extends CWebUser {
// Store model to not repeat query.
private $_person;
// Load user model.
public function loadPerson($id=null, $duration=0)
{
$this->login($id,$duration);
$this->_person=Person::model()->findByPk(Yii::app()->user->id);
}
public function getPerson()
{
return $this->_person;
//return Person::model()->findByPk($this->id);
}
}
If I echo in the loadPerson method $this->_person->first_name after I set _person I get the value I expect. However, at any later time, if I ask for Yii::app()->user->person, the getPerson() method gets called, but $this->_person is now null. I know it's getting in there, if I uncomment the line below and have it look up the person every time, it works.
Is this an issue with Yii? I would really like to be able to cache the person object so I can reference it throughout the session without having to make more calls to the database. What am I missing??
There is no issue with Yii....
As per the documentation, CWebUser class identifies predefined variables "id" and "name" which remains persistent through out the session. Any additional variables should be used with getState() and setState() methods.
" Moreover CWebUser should be used together with IUserIdentity Class which implements the actual authentication algorithm. "
The method loadUser() is never called. And the login() call inside also doesn't make sense. A simpler implementation of getPerson() would be.
private $_person = false;
public function getPerson()
{
if($this->_person===false)
$this->_person = Person::model()->findByPk($this->id);
return $this->_person;
}
I'm writing my first real MVC-application with PHP. I'm not using a framework because my application is so small that I figured it would be faster to write everything from scratch.
In my app, users can register, login and then write/edit/delete content. Each piece of content references its owner by a userid-column in the database.
I am now about to implement user access restrictions (in the sense that users can only view/edit/delete their OWN content/items/models). I'm wondering where the check for "valid access" should happen and where user-objects are instantiated.
I mean, I definitely need information about the current user in controllers, models and views. So I'm thinking if it's viable to have a global user object (defined in index.php) that stores all the user information so I could access it comfortably from each part of my application.
At the moment, this snippet grants my controllers access to user information which I then also store in the data-array that is passed to the view:
class Controller {
protected $data, $id, $user;
public function __construct($action = null, $data = null) {
if (User::isLoggedIn()) {
$this->user = new User($_SESSION['user']);
$this->data['user'] = $this->user;
}
}
}
Following this pattern, I'd have to pass on user information to each model I create or alternatively have them instantiate their own user-object.
What is the way to go here? Global user object, instantiation in each model or passing the user-object as a parameter to models and views?
Thank you for your help!
There are several things to note here. Firstly: "I then also store in the data-array that is passed to the view:"
In MVC, the view has direct access to the model, see my answer here How is MVC supposed to work in CodeIgniter for an overview of that.
Secondly, your question is really about Dependency Management. Globals (and by extension statics) are problematic ( Make all variables global, PHP , Are global variables bad? , Static methods or not? ). The preferred method is passing the fully-constructed $user object into class which requires it.
class Controller {
protected $data, $id, $user;
public function __construct(User $user) {
if ($user->isLoggedIn()) {
//...
}
}
}
However, in this case you can't have a fully-constructed user object because you need to know whether the user is logged in prior to constructing the object so pass a mapper, factory or DAO object into the constructor and create the user as needed.
class Controller {
protected $data, $id, $user;
public function __construct(DataMapper $userMapper) {
if (isset($_SESSION['user'])) {
$user = $userMapper->find($_SESSION['user']);
}
}
}
Finally, however, if you take my first point, that Views can access their model directly and don't get passed data by their controller, then your controller may not even need to know whether the user is logged in. This is, after all, domain logic so belongs in the model.
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 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.