Ajax manipulation of php object instances without reconstructing them? - php

OK, so I've created two classes, event:
class Event {
public $id;
public $u_id;
public $title;
public $type;
public $e_date;
public $e_time;
public $allday;
public $location;
public $dueby;
public $notes;
public $e_next = NULL;
function __construct($result = false, $row = false){
$this->id=mysql_result($result,$row,"id");
$this->u_id=mysql_result($result,$row,"u_id");
$this->title=mysql_result($result,$row,"title");
$this->type=mysql_result($result,$row,"type");
$this->e_date=mysql_result($result,$row,"date");
$this->e_time=mysql_result($result,$row,"time");
$this->allday=mysql_result($result,$row,"allday");
$this->location=mysql_result($result,$row,"location");
$this->dueby=mysql_result($result,$row,"dueby");
$this->notes=mysql_result($result,$row,"notes");
}
}
and EventList, which is my attempt at a linked list:
class EventList {
//public properties
public $e_count = 0;
public $first_event = NULL;
//public methods
private $e_array;
private const $hostname="#################";
private const $username="#######";
private const $password="#######";
private const $database="#######";
private const $table="events";
function __construct($u_id = NULL){
/*Connect To Database*/
mysql_connect($this->hostname,$this->username,$this->password);
#mysql_select_db($this->database) or die("Error");
private $eventquery="SELECT * FROM ".$this->table." WHERE u_id = '".$u_id."'";
private $eventresult=mysql_query($this->eventquery);
private $num=mysql_numrows($this->eventresult);
mysql_close();
private $i=0;
while ($this->i < $this->num) {
private $cur_event;
private $cur_date;
private $placed=false;
$this->cur_event = new Event($this->eventresult, $this->i);
private $j=0;
private $loop_event = $this->first_event;
private $p_loop_event;
while ($this->placed == false && $this->j < $this->e_count) {
private $loop_date = strtotime($this->loop_event->e_date);
$this->cur_date=strtotime($this->cur_event->e_date);
if ($this->cur_date > $this->loop_date) {
$this->p_loop_event = $this->loop_event;
$this->j++;
} else {
private $cur_next = $this->p_loop_event->e_next;
$this->e_array[$this->e_count] = $this->cur_event;
$this->e_array[$this->e_count]->e_next = $this->cur_next;
$this->p_loop_event->e_next = $e_count;
$this->placed = true;
}
}
if ($this->first_event == NULL) {
$this->e_array[$this->e_count] = $this->cur_event;
$this->first_event = $this->e_array[$this->e_count];
$this->placed = true;
} else if ($this->placed == false) {
$this->e_array[$this->e_count] = $this->cur_event;
$this->loop_event->e_next = $e_count;
$this->placed = true;
}
$this->i++;
}
}
}
I'm making quite a basic calendar app. On the homepage i do this:
populateEvents(<?=$u_id?>);
on document load.
Currently (without my linked list implementation, and directly interfacing with the event class), populate events, calls an ajax function which posts user id to this php file:
include ("class.event.php");
$hostname="############";
$username="#################";
$password="#################";
$database="#############";
$table="events";
$u_id = trim($_GET['u_id']);
/*Connect To Database*/
mysql_connect($hostname,$username,$password);
#mysql_select_db($database) or die("Error");
/*If there are, throw an exception*/
try
{
$eventquery="SELECT * FROM ".$table." WHERE u_id = '".$u_id."'";
$eventresult=mysql_query($eventquery);
$num=mysql_numrows($eventresult);
mysql_close();
$i=0;
while ($i < $num) {
$userEvents[$i] = new Event($eventresult, $i);
$i++;
}
}
catch(Exception $e)
{
echo $e->getMessage();
mysql_close();
}
$odd = 1;
switch(count($eventlist)){
case 0:
echo '<p>You have no events. Click "New" to add a new event.</p>';
break;
default:
foreach($userEvents as $event){
$odd = $odd * -1;
if ($odd == -1) echo '<div id="eventWrap" class="oddItem">';
else echo '<div id="eventWrap">';
echo '<div id="eventItem">';
echo '<div id="eventIcon">';
switch ($event->type){
case 'event':
echo '<img src="images/event.png"/>';
break;
default:
break;
}
echo '</div>';
echo '<h3>'.$event->title.'</h3>';
echo '<p><span class="eventDate">'.$event->e_date.'</span></p>';
echo '</div>';
echo '</div>';
}
break;
}
Then the javascript puts the output into a div. Anyway, I want to maintain the same instance of the eventlist object in php, so all the events are searchable in date order, and i can output them as such. I have no idea how to start..

PHP objects and variables in memory are lost between requests. You could cache the EventList object using memcache or similar, but you are likely to run into concurrency issues with multiple requests.
PHP isn't really designed for this kind of thing, and while I'm sure it is possible to use it in this way, I don't think it will be straightforward.

I've sussed it, I'm storing a serialized version of my eventlist instance in the $_SESSION superglobal.

I want to maintain the same instance
of the eventlist object in php
You don't need the same instance of object. It dosn't work this way. Every request (opening page) extracts eventList object form database (or it may be serialized in file on server). If there is any change to that object it is saved to database (or file). Every user works with the same object, but not with the same instance of the object. It is like tv set we have different instances but we can see the same moovie in the same time.

Related

Botman yii2. Properties of the conversation lost

BotMan Version: 2.1
PHP Version:7.3
Messaging Service(s):
Cache Driver: SymfonyCache
Description:
I trying to have conversation. In every next method I lost data from conversation properties, that was saved in properties before!
class GetAlertDataConversation extends AppConversation
{
public $bot;
public $alertTitle;
public $alertDescription;
public $alertLatitude;
public $alertLongitude;
public $alertAuthorId;
public $alertAuthorName;
public function __construct($bot)
{
$this->bot = $bot;
}
private function askTitle()
{
$this->ask('Что случилось? (кратко)', function (Answer $answer) {
$this->alertTitle = $this->askSomethingWithLettersCounting($answer->getText(), 'Слишком коротко!', 'askDescription');
\Yii::warning($this->alertTitle);
});
}
public function askDescription()
{
$this->ask('Расскажи подробней!', function (Answer $answer) {
$this->alertDescription = $this->askSomethingWithLettersCounting($answer->getText(), 'Слишком коротко!', 'askLocation');
\Yii::warning($this->alertTitle);
});
}
private function askLocation()
{
\Yii::warning($this->alertTitle);
$this->askForLocation('Локация?', function (Location $location) {
// $location is a Location object with the latitude / longitude.
$this->alertLatitude = $location->getLatitude();
$this->alertLongitude = $location->getLongitude();
$this->endConversation();
return true;
});
}
private function endConversation()
{
\Yii::warning($this->alertTitle);
$alertId = $this->saveAlertData();
if ($alertId)
$this->say("Событие номер {$alertId} зарегистрировано!");
else
$this->say("Ошибка при сохранении события, обратитесь к администратору!");
}
private function saveAlertData()
{
$user = $this->bot->getUser();
$this->alertAuthorId = $user->getId();
$this->alertAuthorName = $user->getFirstName() . ' ' . $user->getLastName();
$alert = new Alert();
\Yii::warning($this->alertTitle);
$alert->name = $this->alertTitle;
$alert->description = $this->alertDescription;
$alert->latitude = $this->alertLatitude;
$alert->longitude = $this->alertLongitude;
$alert->author_id = $this->alertAuthorId;
$alert->author_name = $this->alertAuthorName;
$alert->chat_link = '';
$alert->additional = '';
if ($alert->validate()) {
$alert->save();
return $alert->id;
} else {
\Yii::warning($alert->errors);
\Yii::warning($alert);
return false;
}
}
}
There is user's text answer in the first \Yii::warning($this->alertTitle); in the askTitle() function.
But all other \Yii::warning($this->alertTitle); returns NULL!!!!
As the result, saving of Alert object not working!
Please, help me. Some ideas?
I think, that it can be by some caching + serialise problem.
I was trying to change cache method to Redis. Same result.
Problem was in this function call and caching:
$this->askSomethingWithLettersCounting($answer->getText(), 'Слишком коротко!', 'askDescription');
If you will read other conversation botman issues in github, you wil see, that most of issues in conversation cache and serialise.
Not any PHP code of conversation can be cached right.
In this case, not direct call of functions askDescription() and askLocation() broken conversation caching.
I fixed that by removing askSomethingWithLettersCounting() function.

Load a PHP class multiple times

I've created a class which has multiple private an public functions and an construct function. It's an client to connect to the vCloud API. I want two objects loaded with different initiations of this class. They have to exist in parallel.
$vcloud1 = new vCloud(0, 'system');
$vcloud2 = new vCloud(211, 'org');
When I check the output of $vcloud1 it's loaded with info of $vcloud2. Is this correct, should this happen? Any idea how I can load a class multiple times and isolate both class loads?
This is part of my class, it holds the most important functions. Construct with user and org to login to. If info in the DB exists, then we authenticate with DB info, else we authenticate with system level credentials. So I would like to have two class loads, one with the user level login and one with system level login.
class vCloud {
private $client;
private $session_id;
private $sdk_ver = '7.0';
private $system_user = 'xxxxxxxxxxx';
private $system_password = 'xxxxxxxxxxxxxxxxx';
private $system_host = 'xxxxxxxxxxxxx';
private $org_user;
private $org_password;
private $org_host;
private $base_url;
public function __construct($customerId, $orgName) {
if ($this->vcloud_get_db_info($customerId)) {
$this->base_url = 'https://' . $this->org_host . '/api/';
$this->base_user = $this->org_user . "#" . $orgName;
$this->base_password = $this->org_password;
} else {
$this->base_url = 'https://' . $this->system_host . '/api/';
$this->base_user = $this->system_user;
$this->base_password = $this->system_password;
}
$response = \Httpful\Request::post($this->base_url . 'sessions')
->addHeaders([
'Accept' => 'application/*+xml;version=' . $this->sdk_ver
])
->authenticateWith($this->base_user, $this->base_password)
->send();
$this->client = Httpful\Request::init()
->addHeaders([
'Accept' => 'application/*+xml;version=' . $this->sdk_ver,
'x-vcloud-authorization' => $response->headers['x-vcloud-authorization']
]);
Httpful\Request::ini($this->client);
}
public function __destruct() {
$deleted = $this->vcloud_delete_session();
if (!$deleted) {
echo "vCloud API session could not be deleted. Contact administrator if you see this message.";
}
}
private function vcloud_delete_session() {
if (isset($this->client)) {
$response = $this->client::delete($this->base_url . 'session')->send();
return $response->code == 204;
} else {
return FALSE;
}
}
public function vcloud_get_db_info($customerId) {
global $db_handle;
$result = $db_handle->runQuery("SELECT * from vdc WHERE customer=" . $customerId);
if ($result) {
foreach ($result as $row) {
if ($row['org_host'] != "") {
$this->org_user = $row['org_user'];
$this->org_password = $row['org_password'];
$this->org_host = $row['org_host'];
return true;
} else {
return false;
}
}
} else {
return false;
}
}
public function vcloud_get_admin_orgs() {
$response = $this->client::get($this->base_url . 'query?type=organization&sortAsc=name&pageSize=100')->send();
return $response->body;
}
}
$vcloud1 = new vCloud('user1', 'system');
$vcloud2 = new vCloud('user2', 'org');
This is enough to make two instances which are not related.
I suppose your database is returning the same results.
How about providing a custom equals method to each object that retreives an instance of vCloud?
class vCloud {
//Other definitions
public function equals(vCloud $other){
//Return true if $other is same as this class (has same client_id etc etc)
}
}
So you just need to do as the code says:
$vcloud1 = new vCloud('user1', 'system');
$vcloud2 = new vCloud('user2', 'org');
if($vcloud1.equals($vclous2)){
echo "Entries are the same";
} else {
echo "Entries are NOT the same";
}
Also you may need to have various getter and setter methods into your class definitions. What is needed for you to do is to fill the equals method.

PHP set parent class property works but subsequent if statement doesn't

I am building a genetics calculator, and have reduced my code to a simple format to explain my issue.
I basically have this line which instantiates a hatch object:
$hatch = new Hatch($maleGeneticsPOST, $femaleGeneticsPOST, 'leopardGecko', true);
This takes a form post for the parent genetics and sets the species type. Below is my Parent class and Child class to show how this essentially works:
class Genetics
{
public $species = '';
public $dominants = [];
public $recessives = [];
public $snows = [];
public $wildtypes = [];
function __construct($species)
{
$this->species = $species;
echo $species; // returns leopardGecko as expected
}
}
class Hatch extends Genetics
{
function __construct($father, $mother, $species, $autoHatch = true, $hatchMethod = "punnett")
{
parent::__construct($species);
// Other code for $father, $mother etc.
}
}
On the face of it, those 2 classes are working well with each other, I can set the species type in the object and Hatch will set the parent to it.
However, what I am struggling to do is to then use the $species property in the parent to set the genetics, based off of the species selected/set; here's an example:
class Genetics
{
public $species = '';
public $dominants = [];
public $recessives = [];
public $snows = [];
public $wildtypes = [];
function __construct($species)
{
$this->species = $species;
echo $species; // returns leopardGecko as expected
if($species === "leopardGecko"){
$this->dominants = ['NN', 'BB', 'TT'];
$this->recessives = ['Bb', 'Tt', 'Rr'];
$this->snows = ['Mm', 'Gg'];
$this->wildtypes = ['QQ', 'Qq'];
}
}
}
And when I try and use them further down in my Hatch class, they just return empty arrays:
foreach ($alleles as $allele) {
//echo $this->allGenetics[$allele].' ';
if (in_array($allele, $this->dominants, true)) {
//echo $this->allGenetics[$allele].' ';
array_push($geckoGenetics['Gecko']['Dominants'], $this->allGenetics[$allele]);
array_push($geckoGenetics['Gene']['Dominants'], $allele);
} elseif (in_array($allele, $this->recessives, true)) {
//echo $this->allGenetics[$allele].' ';
array_push($geckoGenetics['Gecko']['Recessives'], $this->allGenetics[$allele]);
array_push($geckoGenetics['Gene']['Recessives'], $allele);
} elseif (in_array($allele, $this->wildtypes, true)) {
//echo $this->allGenetics[$allele].' ';
array_push($geckoGenetics['Gecko']['Wildtypes'], $this->allGenetics[$allele]);
array_push($geckoGenetics['Gecko']['Recessives'], $this->allGenetics[$allele]);
array_push($geckoGenetics['Gene']['Wildtypes'], $allele);
array_push($geckoGenetics['Gene']['Recessives'], $allele);
} elseif (in_array($allele, $this->snows, true)) {
array_push($geckoGenetics['Gecko']['Snows'], $this->allGenetics[$allele]);
array_push($geckoGenetics['Gene']['Snows'], $allele);
}
}
Please note: The rest of that code works fine, I'm just talking about the $this->dominants, $this->recessives, $this->wildtypes & $this->snows variables - they return empty.
Am I missing something obvious? This is my first proper go at OOP and it's going well, apart from this bit!
'There is a difference in how you're calling the field $species and how you're trying to do so with $dominants. If you want to access fields outside of the scope of your function, you'll need to call them using $this->.
So in the constructor, if you replace the following:
public $dominants = ['NN', 'BB', 'TT'];
public $recessives = ['Bb', 'Tt', 'Rr'];
public $snows = ['Mm', 'Gg];
public $wildtypes = ['QQ', 'Qq'];
with:
$this->dominants = ['NN', 'BB', 'TT'];
$this->recessives = ['Bb', 'Tt', 'Rr'];
$this->snows = ['Mm', 'Gg'];
$this->wildtypes = ['QQ', 'Qq'];
It should work.

AJAX calls return results in a (seemingly) synchronous manner

All,
Background of how problem was detected
My question concerns the performance of a web app, mainly the index page. I noticed the problem when I was giving a demonstration at a local branch of my company that has slow internet (I don't know the exact speeds, or ping rate) judged by the fact that Google took about 10 seconds to load. My index page took ~10-20 times longer to load. I was under the assumption that my app did most of the work on the server side (as php is making all of the database queries...). But this led me to look at the network tool of Chrome and see the latency times of these 4 divs being loaded by ajax (I'll elaborate in a bit). Interestingly, the scripts being called appear to run sequentially, but not necessarily in the order I invoked the ajax calls (sometimes they do, other times they don't).
What are these divs / ajax requests?
Here is a code snippets of a request:
Yii::app()->clientScript->registerScript('leftDiv', '
$( "#left_dash" ).load(
"'.$this->createUrl("/site/page?view=leftDashLoad") .'",
function(){
$("#left_dash p a").click(function() {
$(this).parent().parent().find("div.scroll100").slideUp();
$(this).parent().next().stop(false, false).slideDown();
});
$("p:first-child").next().slideDown();
}
);
' );
Here is the page requested:
$this->widget('widgets.ScrollList',array(
'condition'=>
function($muddJob,$scrollList)
{
$job = $muddJob->Job;; //returns a job or empty array
if(!empty($job) )
{
if( $muddJob->uploadArtwork == null && $muddJob->uploadData == null ) {
array_push($scrollList->_models,$job);
$scrollList->columnValues = array($muddJob->jobDescription,$muddJob->dropDate1);
return true;
}
}
return false;
},
'columns' => array('col1'=>"MuddJob#",'col2'=>"Desc",'col3'=>"Dealer Name"),
'name'=> "Print New Ticket",
'muddJobs' => $currentExchanges->getCurrentMuddExchanges(),
)
);
Imagine that page (the page that ajax has called) having 6 similar declarations that create widgets. The goal is to return html to put back in place of a loading gif on the index page.
Here is the scroll widget:
<?php
Yii::import('widgets.ScrollListBase');
include_once Yii::app()->extensionPath . "/BusinessDay.php";
class ScrollList extends ScrollListBase
{
private $_content;
public $columns = array();
public $columnValues;
private $_listInfo;
public $name;
public $_models = array();
public $condition;
public $muddJobs; //object to pass
public $jobsMailingTodayArray = array();
public function init()
{
//$this->init();
$this->_listInfo = $this->generateListInfo($this->columns);
//$muddJobs = $this->getCurrentMuddExchanges();
$listInfo = $this->newScrollList($this->muddJobs);
$contents = $this->createContent($listInfo,$this->name);
$this->_content = $contents[0];
// $this->_fullTableContent = $contents[1];
//$this->_listInfo = $contents[2];
}
public function run()
{
//if($this->data['isVisible'])
echo $this->_content;
Yii::app()->session["exploded_content_{$this->name}"] = $this->_models;
}
private function newScrollList($muddJobs)
{
$listInfo = $this->_listInfo;
$tempCount = 0;
foreach($muddJobs as $muddJob)
{
$condition = $this->condition;
if($condition($muddJob,$this) && empty($this->jobsMailingTodayArray) ) //if no job exists for the muddExchange...
{
$tempArray = $this->createWidgetLinks($tempCount,$listInfo,$muddJob,$this->columnValues);
$listInfo = $tempArray[0];
$tempCount = $tempArray[1];
}
elseif ( !empty($this->jobsMailingTodayArray ) )
{
foreach ($this->jobsMailingTodayArray as $jobMailingToday) //change to for loop over the length of the jobsMailingToday
{
$tempArray = $this->createWidgetLinks($tempCount,$listInfo,$muddJob,$this->columnValues);
$listInfo = $tempArray[0];
$tempCount = $tempArray[1];
}
$this->jobsMailingTodayArray = array();
}
}
return array($listInfo,$tempCount);
}
}
?>
Here is it's parent:
<?php
class ScrollListBase extends CWidget
{
private $content = "<p>";
private $divDeclaration = "<div class='scroll100'>\n<table class='quickInfoTable'>\n<thead>\n";
private $headTag = "<th>";
private $headTagClose = "</th>\n";
private $theadTagClose = "</thead>\n";
private $bodyTag = "<tbody>\n";
private $listInfo = "<div class='scroll100'>\n<table class='quickInfoTable'>\n<thead>\n<th>Job#</th>\n<th>Package#</th>\n<th>Entry Date</th>\n</thead>\n<tbody>\n";
/**
* Initializes the widget.
*/
public function createContent($listInfo,$name)
{
$largeHref = Yii::app()->request->baseUrl . '/index.php/site/fullTableView';
$this->content .= "<span class='badge' >{$listInfo[1]} </span> <a href='#'>{$name} </a> <a href='$largeHref/Name/{$name}'> <small>(view larger)</small> </a> </p>";
if( $listInfo[1] > 0 )
{
// $this->fullTable .= substr($listInfo[0],22);
// $this->fullTableContent= $this->fullContent .= $this->fullTable . "</tbody>\n</table>\n</div>";
$this->content .= $listInfo[0] . "</tbody>\n</table>\n</div>";
}
return array($this->content);
}
//Helper Methods
/**
*
* #param type $attributeArray. send an accociative array
* #return type = either a job or an empty array
*/
protected function getJobByAttributes($attributeArray)
{
return Jobs::model()->with('MuddExchange')->findByAttributes($attributeArray);
}
protected function createWidgetLinks($tempCount,$listInfo,$muddJob,$columnValues,$url="/MuddExchange/")
{
$tempCount++;
$viewIndex = $muddJob->exchange_id;
$model = $muddJob;
$job = $muddJob->Job;
if ( isset($job ))
{
$model = $job;
$url = "/Jobs/";
$viewIndex = $model->job_id;
}
$link = CHtml::link("$model->jobNumber",array("{$url}{$viewIndex}"));
$listInfo .= "<tr>\n<td>$link</td>\n";
foreach ($columnValues as $columnValue)
{
$listInfo .= "<td>{$columnValue}</td>\n";
}
$listInfo .= "</tr>";
return array($listInfo,$tempCount);
}
protected function getListInfo()
{
return $this->listInfo;
}
/**
* Takes an array of strings to generate the column names for a particular list.
* #param array $heads
* #return string
*
*/
protected function generateListInfo($heads)
{
//<th>Job#</th>\n<th>Package#</th>\n<th>Entry Date</th>\n</thead>\n<tbody>\n";
$htmlScrollStart = $this->divDeclaration;
foreach ($heads as $tableColumn => $name)
{
$htmlScrollStart .= $this->headTag . $name . $this->headTagClose;
}
$htmlScrollStart .= $this->theadTagClose . $this->bodyTag;
return $htmlScrollStart;
}
public function calculateDueDate($jobsMailDate,$job)
{
$package = PackageSchedule::model()->findByAttributes(array('package_id'=>$job->packageID));
$projectedDays = $package->projected_days_before_mail_date;
$dropDate1 = $jobsMailDate->projected_mail_date;
$dropDate = wrapBusinessDay($dropDate1); //use this for actual command...
$toSec = 24*60*60;
$dayInt =0;
$secDropDate = strtotime($dropDate1);
do{
$dayInt +=1;
$daysInSec = ($dayInt) * $toSec ;
$secGuessDueDate = $secDropDate - $daysInSec;
$dueDate = date('Y-m-d',$secGuessDueDate);
$difference = $dropDate->difference($dueDate);
}while( $difference != $projectedDays);
return $dueDate;
}
}
?>
Why I think this behavior is odd
The whole slow internet thing is a beast in and of itself, but I don't think that is in the scope of StackOverflow. I'm more concerned about the loading of these divs. The div that loads last, i.e., takes on average 1.5 to 2 seconds, is an ajax request to a page that creates a single widget. The logic behind it is here:
<?php
include_once Yii::app()->extensionPath . "/CurrentExchanges.php";
$currentExchanges = Yii::app()->session['currentExchanges'];
$this->layout = 'barebones';
$this->widget('widgets.ScrollList',array(
'condition'=>
function($muddJob,$scrollList)
{
if ($muddJob->dropDate1 != null && $muddJob->dropDate1 != '0000-00-00')
{
$job = $muddJob->Job;;
if(!empty($job) && $job->packageID != null) //if job exists for the muddExchange and has a package
{
if($job->uploadArtwork == null )
{
$jobsMailDate = JobsMailDate::model()->findByAttributes(array("job_id"=>$job->job_id,'sequence_num'=>1));
if(!empty($jobsMailDate))
{
$calculatedDueDate = $scrollList->calculateDueDate($jobsMailDate,$job);
if (strtotime($calculatedDueDate) <= strtotime(date("Y-m-d")) )
{
array_push($scrollList->_models , $job);
$scrollList->columnValues = array($muddJob->jobDescription,$muddJob->dropDate1,$jobsMailDate->projected_mail_date);
return true;
}
}
}
}
}
return false;
},
'columns' => array('col1'=>"MuddJob#",'col2'=>"Desc",'col3'=>"Drop Date", 'col4' =>'Projected Drop Date'),
'name'=> "Artwork Due Today",
'muddJobs' => $currentExchanges->getCurrentMuddExchanges(),
)
);
?>
The calculateduedate method makes 2 additional calls to the server.
What I'm failing to understand is why the left div (with the most proccessing to do) is usually the first to return and the artLoad is usually the last to load (by a substantial difference). Here are some times returned by chromes network tool:
leftDashLoad: 475ms
rightDashLoad: 593ms
dataLoad: 825ms
artLoad: 1.41s
dataLoad: 453ms
rightDashLoad: 660ms
leftDashLoad: 919ms
artLoad: 1.51s
rightDashLoad: 559ms
leftDashLoad: 1.17s
dataLoad: 1.65s
artLoad: 2.01s
I just can't fathom why the left/right dashloads return so much faster than the artLoad. The code for artLoad and dataLoad are nearly identical save the actual comparison (the one if statement). If this were truly asynchronous, I'd expect the order to be art/dataLoad, rightDashLoad and leftDashLoad based purely on the amounts of computation done in each page. Perhaps the server isn't multithreading, or there is some weird configuration, but if that were the case, I don't see why the effects of the loading would be hit so hard by slow internet.
If I have overlooked something obvious or failed to use google appropriately, I do apologize. Thanks for any help you can offer!
Language/other tech info
The app was developed using the Yii framework. PHP 5.3. Apache Server. INNODB Tables. Server is hosted in dreamhost.
Update
I've changed the view page so that the ajax calls are now calling a controller action. It seems to have made the loading times more similar (asynchronous?) on my local dev environment, but really slowed it down on the QA environment (hosted on dreamhost...). Here is screen shot of the local network tools info:
dev environment
and the qa site (note, that the databases have about the same amounts of data...)
qa environment
Thoughts? It seems to me that my original problem may be solved, as the return times (on my local dev) look more like I expect them to.
Also, my own ignorance as to how NetBeans debugs was playing a part in this synchronous loading thing as xdebug is using a session. I believe this was forcing the ajax calls to wait their turn.
Thanks to #Rowan for helping me diagnose this strange behavior. PHP was trying to request a session before the session was closed in hopes to prevent a race hazard. There were session requests in my code, and there was a session started by my IDE (NetBeans). Removing all references to sessions in the ajax called pages and having ajax call actions that use renderPartial() proved to return the expected behavior (and much better code IMO). Let me know how to properly thank users in terms of StackOverflow (can I up comments, or what is there available? ). Thank you all!

Why is this Zend Framework _redirect() call failing?

I am developing a Facebook app in Zend Framework. In startAction() I am getting the following error:
The URL http://apps.facebook.com/rails_across_europe/turn/move-trains-auto is not valid.
I have included the code for startAction() below. I have also included the code for moveTrainsAutoAction (these are all TurnController actions) I can't find anything wrong with my _redirect() in startAction(). I am using the same redirect in other actions and they execute flawlessly. Would you please review my code and let me know if you find a problem? I appreciate it! Thanks.
public function startAction() {
require_once 'Train.php';
$trainModel = new Train();
$config = Zend_Registry::get('config');
require_once 'Zend/Session/Namespace.php';
$userNamespace = new Zend_Session_Namespace('User');
$trainData = $trainModel->getTrain($userNamespace->gamePlayerId);
switch($trainData['type']) {
case 'STANDARD':
default:
$unitMovement = $config->train->standard->unit_movement;
break;
case 'FAST FREIGHT':
$unitMovement = $config->train->fast_freight->unit_movement;
break;
case 'SUPER FREIGHT':
$unitMovement = $config->train->superfreight->unit_movement;
break;
case 'HEAVY FREIGHT':
$unitMovement = $config->train->heavy_freight->unit_movement;
break;
}
$trainRow = array('track_units_remaining' => $unitMovement);
$where = $trainModel->getAdapter()->quoteInto('id = ?', $trainData['id']);
$trainModel->update($trainRow, $where);
$this->_redirect($config->url->absolute->fb->canvas . '/turn/move-trains-auto');
}
.
.
.
public function moveTrainsAutoAction() {
$log = Zend_Registry::get('log');
$log->debug('moveTrainsAutoAction');
require_once 'Train.php';
$trainModel = new Train();
$userNamespace = new Zend_Session_Namespace('User');
$gameNamespace = new Zend_Session_Namespace('Game');
$trainData = $trainModel->getTrain($userNamespace->gamePlayerId);
$trainRow = $this->_helper->moveTrain($trainData['dest_city_id']);
if(count($trainRow) > 0) {
if($trainRow['status'] == 'ARRIVED') {
// Pass id for last city user selected so we can return user to previous map scroll postion
$this->_redirect($config->url->absolute->fb->canvas . '/turn/unload-cargo?city_id='.$gameNamespace->endTrackCity);
} else if($trainRow['track_units_remaining'] > 0) {
$this->_redirect($config->url->absolute->fb->canvas . '/turn/move-trains-auto');
} else { /* Turn has ended */
$this->_redirect($config->url->absolute->fb->canvas . '/turn/end');
}
}
$this->_redirect($config->url->absolute->fb->canvas . '/turn/move-trains-auto-error'); //-set-destination-error');
}
As #Jani Hartikainen points out in his comment, there is really no need to URL-encode underscores. Try to redirect with literal underscores and see if that works, since I believe redirect makes some url encoding of its own.
Not really related to your question, but in my opinion you should refactor your code a bit to get rid of the switch-case statements (or at least localize them to a single point):
controllers/TrainController.php
[...]
public function startAction() {
require_once 'Train.php';
$trainTable = new DbTable_Train();
$config = Zend_Registry::get('config');
require_once 'Zend/Session/Namespace.php';
$userNamespace = new Zend_Session_Namespace('User');
$train = $trainTable->getTrain($userNamespace->gamePlayerId);
// Add additional operations in your getTrain-method to create subclasses
// for the train
$trainTable->trackStart($train);
$this->_redirect(
$config->url->absolute->fb->canvas . '/turn/move-trains-auto'
);
}
[...]
models/dbTable/Train.php
class DbTable_Train extends Zend_Db_Table_Abstract
{
protected $_tableName = 'Train';
[...]
/**
*
*
* #return Train|false The train of $playerId, or false if the player
* does not yet have a train
*/
public function getTrain($playerId)
{
// Fetch train row
$row = [..];
return $this->trainFromDbRow($row);
}
private function trainFromDbRow(Zend_Db_Table_Row $row)
{
$data = $row->toArray();
$trainType = 'Train_Standard';
switch($row->type) {
case 'FAST FREIGHT':
$trainType = 'Train_Freight_Fast';
break;
case 'SUPER FREIGHT':
$trainType = 'Train_Freight_Super';
break;
case 'HEAVY FREIGHT':
$trainType = 'Train_Freight_Heavy';
break;
}
return new $trainType($data);
}
public function trackStart(Train $train)
{
// Since we have subclasses here, polymorphism will ensure that we
// get the correct speed etc without having to worry about the different
// types of trains.
$trainRow = array('track_units_remaining' => $train->getSpeed());
$where = $trainModel->getAdapter()->quoteInto('id = ?', $train->getId());
$this->update($trainRow, $where);
}
[...]
/models/Train.php
abstract class Train
{
public function __construct(array $data)
{
$this->setValues($data);
}
/**
* Sets multiple values on the model by calling the
* corresponding setter instead of setting the fields
* directly. This allows validation logic etc
* to be contained in the setter-methods.
*/
public function setValues(array $data)
{
foreach($data as $field => $value)
{
$methodName = 'set' . ucfirst($field);
if(method_exists($methodName, $this))
{
$this->$methodName($value);
}
}
}
/**
* Get the id of the train. The id uniquely
* identifies the train.
* #return int
*/
public final function getId ()
{
return $this->id;
}
/**
* #return int The speed of the train / turn
*/
public abstract function getSpeed ();
[..] //More common methods for trains
}
/models/Train/Standard.php
class Train_Standard extends Train
{
public function getSpeed ()
{
return 3;
}
[...]
}
/models/Train/Freight/Super.php
class Train_Freight_Super extends Train
{
public function getSpeed ()
{
return 1;
}
public function getCapacity ()
{
return A_VALUE_MUCH_LARGER_THAN_STANDARD;
}
[...]
}
By default, this will send an HTTP 302 Redirect. Since it is writing headers, if any output is written to the HTTP output, the program will stop sending headers. Try looking at the requests and response inside Firebug.
In other case, try using non default options to the _redirect() method. For example, you can try:
$ropts = { 'exit' => true, 'prependBase' => false };
$this->_redirect($config->url->absolute->fb->canvas . '/turn/move-trains-auto', $ropts);
There is another interesting option for the _redirect() method, the code option, you can send for example a HTTP 301 Moved Permanently code.
$ropts = { 'exit' => true, 'prependBase' => false, 'code' => 301 };
$this->_redirect($config->url->absolute->fb->canvas . '/turn/move-trains-auto', $ropts);
I think I may have found the answer. It appears that Facebook does not play nice with redirect, so it is neccessary to use Facebook's 'fb:redirect' FBML. This appears to work:
$this->_helper->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender();
echo '<fb:redirect url="' . $config->url->absolute->fb->canvas . '/turn/move-trains-auto"/>';

Categories