Maintain php object state - php

My php code creates a "School" object which is (among other functions) able to return several forms which are being ,on submit, handled by php.
Thru one of these forms i'm able to add a "SchoolClass" object to a array in the "School" object but it seems that the "School" object is recreated at some point after i add the "SchoolClass" object. This re-creation makes the array of "SchoolClass" disappear.
The functions that returns the forms are static, but the functions where "SchoolClass" objects are added to the array are not static; ("School::ShowStudentForm() and School->RegisterSchoolClass($class)")
Both the class definition and the php-script which handles the form after submit is in the same file(index.php). I had the idea that keeping both the class code and the form handling code in the same file would do the trick, but it did not make a change.
Question 1: how do i maintain the "School" object for as long as i need it?
Question 2: is it ok to create a class with only static methods and write the data directly to a file or database to avoid the whole problem or just skip the whole object approach and just use "normal" functions?
Yes, this problem is probably encounterd before and therefore also answered before but i have not been able to find a answer which i understand.

OK....i figured it out.
I can use the $_SESSION variable to store it.
Now i do like this in the start of the page:
$school = null;
if(!isset($_SESSION["school"]))
{
$school = new School;
$_SESSION["school"] = $school;
echo "New school <br>";
} else {
$school = $_SESSION["school"];
}

Related

Passing complex objects via PHP

I've been researching a bit but I cannot find any way of doing this...
I need to pass an instance of a complex PHP class (formed by properties, functions, arrays, etc.) between different pages. The variable of the object is called $Model; I've been trying to use the GET method and "serialize" / "unserialize", but I cannot retrieve the object in the new page...
I'm trying to pass the object as follows:
echo 'Pass Model Object';
Then, I'm trying to retrieve it like this:
if(isset($_GET['modelobject'])) //$_GET when used in a link!
{
$ModelObjPost = unserialize($_GET['modelobject']); //$_GET when used in a link!
echo "POST: Model Object retrieved<br>";
var_dump($ModelObjPost);
}
There could be some kind of problem with certain characters in the serialization (I'm not sure, though), as the link that should send the object sometimes gets printed as (and it is also recognized as a URL):
r";s:3:"new";b:0;}i:2;(... MORE STUFF ...)s:9:"dbModelId";s:1:"1";}">Pass Model Object
Should I try a completely different approach, or this method should work, but there is just something I'm doing wrong?
Thanks in advance for your time!
Warning: I would discourage serializing a class and placing the result in an end-user readable location. This probably reveals far more about your application than you should be disclosing publicly.
Let's clear up some terminology right up front. I assume you know this, but just to be clear... A class is both the code that defines the behavior you want for an object and data (properties). An instance is when you use the new keyword on a class to create a usable object using that class definition.
By the very nature of how PHP works (typically), all instances are unloaded and deleted after a page loads. It cannot survive in memory to be used on a second page.
Typically you would create a file that contains the class definition you're trying to pass between pages, and create a new instance of the class on each page.
If you're trying to keep state between pages, you should look at using sessions. If you choose to use sessions, and want to keep an instance of your class inside your session, keep in mind what I said above - it will be deleted and recreated between the pages. You will need to make sure your class is set up to reload everything it needs to operate. PHP provides for a "magic" method to do this: __wakeup() in this method, you will need to restore the object back to the same state it was in on the previous page load.
Other ways to pass data between pages (or page loads) would be arrays for HTTP GET or POST.
$data = array( 1,2,3,4, 'a' => 'abcd' );
$query = http_build_query(array('aParam' => $data));
$url = 'somepage.php?data=' . $query;
Forms may be created to pass arrays of data by utilizing array notation in the form field names
<form action="somepage.php" method="post">
<input name="option[a][]" value="option a,0">
<input name="option[a][]" value="option a,1">
<input name="option[b][]" value="option b,0" />
<input name="option[b][]" value="option b,1" />
<input name="option[]" value="option 0" />
</form>
Access this data like this:
<?php
echo $option['a'][0];
echo $option['a'][1];
echo $option['b'][0];
// etc
Your two best options in this case would be to either:
Use a $_SESSION variable. You could save the object as $_SESSION['Model'] and access that on each page that you need to use it. Be sure to call session_start(); on each of those pages to resume the session.
Use urlencode(serialize($Model)) in the URL and use urldecode() on the next page to make sure that you don't have encoding issues in the URL. json_encode() and json_decode() would also be a good option for object to string serialization.

CodeIgniter, Accessing class property after page reloads, is it possible?

I have a CI page that will load to a div in view file, using jQuery. Using switch(page_parameter), I control what is showing from the page.
When I call the page for 3rd time, I set a value to the class array.
But when I call the 4th time, the array become empty.
I was wondering, is it actually possible to use the class property to store value that can be used after page re-access? Or something missing in my head?
I know that using session is not a good idea, since the real array is a big chunk of serialized xml.
Here's my code:
class MyClass extends MY_Controller
{
public static $pitems = array();
function Hotel(){
parent::MY_Controller();
}
function new_campaign(){
$params = $this->uri->uri_to_assoc();
switch($params['step']){
case '3' : self::$pitems = array("test","another"); //here the class array was set successfully
$this->load->view('viewer');
break;
case '4' : print_r(self::$pitems); //here the array is empty
break;
}
}
In the viewer page, there's a call to the page:
Next page
Same issue also with $this->
What am i missing here?
Thanks in advance~
edit:
I saw a script that has similar scenario. it successfully reused the variable set in the constructor, instead of treating it as a class variable. i'll look thorough to confirm this, but for now, i'll close this thread. Thanks Chris for sharing.
Im not really sure what your trying to do but I have users jQuery post()/get()/ajax() many of times in CI and have had no problems. So despite not knowing or understanding what your trying to do. I thought I'd at least say I know loading data without refresh in CI through something like jQuery isn't an issue. Example on system I built on CI had a twitter like feed of tweets where jQuery on a timer was polling for new data and coming back with it each time accordingly if something new was to be shown.

Reduce database calls for php web shop

I'm looking for a way to prevent repeated calls to the database if the item in question has already been loaded previously. The reason is that we have a lot of different areas that show popular items, latest releases, top rated etc. and sometimes it happens that one item appears in multiple lists on the same page.
I wonder if it's possible to save the object instance in a static array associated with the class and then check if the data is actually in there yet, but then how do I point the new instance to the existing one?
Here's a draft of my idea:
$baseball = new Item($idOfTheBaseballItem);
$baseballAgain = new Item($idOfTheBaseballItem);
class Item
{
static $arrItems = array();
function __construct($id) {
if(in_array($id, self::arrItems)){
// Point this instance to the object in self::arrItems[$id]
// But how?
}
else {
// Call the database
self::arrItems[id] = $this;
}
}
}
If you have any other ideas or you just think I'm totally nuts, let me know.
You should know that static variables only exist in the page they were created, meaning 2 users that load the same page and get served the same script still exist as 2 different memory spaces.
You should consider caching results, take a look at code igniter database caching
What you are trying to achieve is similar to a singleton factory
$baseball = getItem($idOfTheBaseballItem);
$baseballAgain =getItem($idOfTheBaseballItem);
function getItem($id){
static $items=array();
if(!isset($items[$id])$items[$id]=new Item($id);
return $items[$id];
}
class Item{
// this stays the same
}
P.S. Also take a look at memcache. A very simple way to remove database load is to create a /cache/ directory and save database results there for a few minutes or until you deem the data old (this can be done in a number of ways, but most approaches are time based)
You can't directly replace "this" in constructor. Instead, prepare a static function like "getById($id)" that returns object from list.
And as stated above: this will work only per page load.

Understanding OOP Principles in passing around objects/values

I'm not quite grokking a couple of things in OOP and I'm going to use a fictional understanding of SO to see if I can get help understand.
So, on this page we have a question. You can comment on the question. There are also answers. You can comment on the answers.
Question
- comment
- comment
- comment
Answer
-comment
Answer
-comment
-comment
-comment
Answer
-comment
-comment
So, I'm imagining a very high level understanding of this type of system (in PHP, not .Net as I am not yet familiar with .Net) would be like:
$question = new Question;
$question->load($this_question_id); // from the URL probably
echo $question->getTitle();
To load the answers, I imagine it's something like this ("A"):
$answers = new Answers;
$answers->loadFromQuestion($question->getID()); // or $answers->loadFromQuestion($this_question_id);
while($answer = $answers->getAnswer())
{
echo $answer->showFormatted();
}
Or, would you do ("B"):
$answers->setQuestion($question); // inject the whole obj, so we have access to all the data and public methods in $question
$answers->loadFromQuestion(); // the ID would be found via $this->question->getID() instead of from the argument passed in
while($answer = $answers->getAnswer())
{
echo $answer->showFormatted();
}
I guess my problem is, I don't know when or if I should be passing in an entire object, and when I should just be passing in a value. Passing in the entire object gives me a lot of flexibility, but it's more memory and subject to change, I'd guess (like a property or method rename). If "A" style is better, why not just use a function? OOP seems pointless here.
Thanks,
Hans
While I like Jason's answer, it is not, strictly speaking OO.
$question = new Question($id);
$comments = $question->getComments();
$answers = $question->getAnswers();
echo $question->getTitle();
echo $question->getText();
foreach ($comments as $comment)
echo $comments->getText();
The problems are:
There is no information hiding, a fundamental principle of OO.
If the format of the answers needs to change, it must be changed in a place that is not associated with the object that houses the data.
The solution is not extensible. (There is no behaviour to inherit.)
You must keep behaviour (tightly coupled) with the data. Otherwise you are not writing OO.
$question = new Question($id);
$questionView = new QuestionView( $question );
$questionView->displayComments();
$questionView->displayAnswers();
How the information is displayed is now an implementation detail, and reusable.
Notice how this opens up the following possibility:
$question = new Question( $id );
$questionView = new QuestionView( $question );
$questionView->setPrinterFriendly();
$questionView->displayComments();
$questionView->displayAnswers();
The idea is that now you can change how the questions are formatted from a single location in the code base. You can support multiple formats for the comments and answers without the calling code (a) ever knowing; and (b) ever needing to change (to a significant degree).
If you are coding text formatting details in more than one location because you are misusing accessor methods, the life of any future maintainers will be miserable. If the maintainer is a psychopath who knows where you live, you will be in trouble.
Objects, Data, and Views
Here's the problem, as I understand it:
Database -> Object -> Display Content
You want to keep the behaviour of the object centred around logic that is intrinsic to the object. In other words, you don't want the Object to have to do things that have nothing to do with its core responsibilities. Most commonly this will include load, save, and print functionality. You want to keep these separate from the object itself because if you ever have to change database, or output format, you want to make as few changes in the system as possible, and restrain the ripple effect.
To simplify this, let's take a look at loading only Comments; everything is applicable to Questions and Answers as well.
Comment Class
The Comment class might offer the following behaviours:
Reply
Delete
Update (requires permission)
Restore (from a delete)
etc.
CommentDB Class
We can create a CommentDB object that knows how to manipulate the Comments in the database. A CommentDB object has the following behaviours:
Create
Load
Save
Update
Delete
Restore
Notice that these behaviours will likely be common across all objects and can therefore be subject to refactoring. This will also let you change databases quite easily as the connection information will be isolated to a single class (the grandfather of all database objects).
Example usage:
$commentDb = new CommentDB();
$comment = $commentDb->create();
Later:
$comment->update( "new text" );
Notice that there are a number of possible ways to implement this, but you can always do so without violating encapsulation and information hiding.
CommentView Class
Lastly, the CommentView class will be tightly coupled to a Comment class. That it can obtain the attributes of Comment class via accessors is expected. The information is still hidden from the rest of the system. The Comment and its CommentView are tightly coupled. The idea is that the formatting is kept in a single place, not scattered throughout classes that need to use the data willy nilly.
Any classes that need to display comments, but in a slightly different format, can inherit from CommentView.
See also: Allen Holub wrote "You should never use get/set functions", is he correct?
Why pass either? What about:
<?php
$question = new Question($id);
$comments = $question->getComments();
$answers = $question->getAnswers();
echo $question->getTitle();
echo $question->getText();
foreach ($comments as $comment)
echo $comments->getText();
foreach ($answers as $answer)
{
$answer_comments = $answer->getComments();
echo $answer->getText();
foreach ($answer_comments as $comment)
echo $comment->getText();
}
Where getComments() and getAnswers() use $this->id to retrieve and return an array of comment or answer objects?
You could build utility methods in the comment and answer objects that allow you to load by parent id. In which case, just taking an id as a parameter would be nice.
$question = new Question($id);
$answers = Answer::forQuestion($question->id);
$comments = Comment::forQuestion($question->id);
$ans_comments = Comment::forAnswer($answer->id); // or some way to distinguish what the parent object is.
Edit: Likely the child model (Comment or Answer in this case) doesn't need anything from the parent except and id to do db queries with. Passing in the entire parent object would be overkill. (Also, PHP has a terrible time garbage collecting objects with circular references, which might be fixed in the 5.3 series.)
Both styles are acceptable. Sometimes you only need the value, sometimes you'll need the object. In this example I would personally do something along the lines of your first example, but trivial programs like this don't tend to exist in the wild very often so maybe you want the second piece.
My rule of thumb is to do the thing in the least number of lines that still clearly demonstrates what you're attempting to do to anyone who comes after you. The overhead of most object creation vs value passing is something you'll likely never ever have to deal with on modern arch.
Adding to what #jasonbar already mentioned:
I don't know when or if I should be passing in an entire object, and when I should just be passing in a value.
It depends on the Coupling you need and the Cohesion you desire.
Passing in the entire object gives me a lot of flexibility, but it's more memory and subject to change.
PHP does not copy the object when you use it as an argument to a function. Neither do most other languages (either by default, like C# and Java, or upon explicit request, like C and C++)
To add to Dave Jarvis and jasonbar answers, I usually have DataMappers to convert between relational data and objects, instead of using an ActiveRecord approach. So, following your example, we would have these classes:
Question
Answer
Comment
and their data mappers:
QuestionMapper
AnswerMapper
CommentMapper
Each mapper implementing a similar interface:
save(object) // creates or updates a record in the database (or text file, for that matter)
delete(id)
get(id)
Then, we would do as:
$q = QuestionMapper::get( $questionid );
// here we could either (a) just return a list of Answers
// previously eagerly-loaded by the
// QuestionMapper, or (b) lazy load the answers by
// calling AnswerMapper::getByQuestionID( $this->id ) or similar.
$aAnswers = $q->getAnswers();
foreach($aAnswers as $oAnswer){
echo $oAnswer->getText();
$aComments = $oAnswer->getComments();
foreach($aComments as $oComment){
echo $oComment->getText();
}
}
Regarding the use of things like QuestionView->render( $question ), I prefer to have Views which display the data using getters from the domain objects. If you pass a Question to a HTMLView, it will render it as HTML; if you pass it to a JSONView, then you'll get JSON-formatted content. This means that the domain objects need to have getters.
PS: We could also consider the QuestionMapper to load everything related to Questions, Answers, and Comments. Since Comments always belongs to Answers or Questions, and Answers always belong to Questions, it could make sense that the QuestionMapper loaded everything. Of course we would have to consider different strategies for lazy loading a Question's set of Answers and Comments, to avoid hogging the server.

Using objects in Ajax calls PHP files

I have instantiated a class in my index.php file. But then I use jQuery Ajax to call some PHP files, but they can't use my object that I created in the index.php file.
How can I make it work? Because I donĀ“t want to create new objects, because the one I created holds all the property values I want to use.
Use the session to save the object for the next page load.
// Create a new object
$object = new stdClass();
$object->value = 'something';
$object->other_value = 'something else';
// Start the session
session_start();
// Save the object in the user's session
$_SESSION['object'] = $object;
Then in the next page that loads from AJAX
// Start the session saved from last time
session_start();
// Get the object out
$object = $_SESSION['object'];
// Prints "something"
print $object->value;
By using the PHP sessions you can save data across many pages for a certain user. For example, maybe each user has a shopping cart object that contains a list of items they want to buy. Since you are storing that data in THAT USERS session only - each user can have their own shopping cart object that is saved on each page!
Another option if you dont want to use sessions is to serialize your object and send it through a $_POST value in your AJAX call. Not the most elegant way to do it, but a good alternative if you don't want to use sessions.
See Object Serialization in the documentation for more informations.
mm, you should store in session, $_SESSION["someobj"] = $myobj;, and ensure that when you call the Ajax PHP file this includes the class necessary files which defines the class of $myobj and any contained object in it.
Could you be more specific? I can try.
This is how I create an object then assign it to a session variable:
include(whateverfilethathastheclassorincludeit.php)
$theObject = new TheObjectClass();
//do something with the object or not
$_SESSION['myobject'] = $theObject;
This is how I access the object's members in my Ajax call PHP file:
include(whateverfilethathastheclassorincludeit.php)
$theObject = $_SESSION['myobject'];
//do something with the object
If you don't want to move your object that is in your index.php, have your ajax make a request to index.php but add some extra parameters (post/get) that let your index.php know to process it as an ajax request and not return your normal web page html output.
You have not provided code, but what I guess is that you need to make your instantiated object global for other scripts to see it, example:
$myobject = new myobject();
Now I want to use this object elsewhere, probably under some function or class, or any place where it is not getting recognized, so I will make this global with the global keyword and it will be available there as well:
global $myobject;
Once you have the object, you can put it into the session and then utilize it in the Ajax script file.
As others have suggested, $_SESSION is the standard way to do it, in fact, that was one of the reasons, that sessions where invented to solve. Other options, i.e. serializing the object rely on the client side to hold the object and then return it untampered. Depending on the data in the object, it is not a good solution, as a) the object may include information that should not be available on the client side for security reasons and b) you will have to verify the object after receiving it.
That said, and if you still want to use the object on the client side, then JSON is an option for serializing object data, see JSON functions in PHP.
Based on most of the answers here, referring to storing the object in $_SESSION, is it more efficient to store only the individual properties that need to be accessed in AJAX as opposed to the whole object, or does it not matter?
E.g.
$_SESSION['object'] = $object;
vs
$_SESSION['property1'] = $object->property1;
$_SESSION['property2'] = $object->property2;
I know the OP is asking about accessing the entire object, but I guess my question pertains to if it's just a matter of only accessing certain properties of an object, and not needing to access methods of a class to alter the object once it's in AJAX.

Categories