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.
Related
I use PHP classes for managing users accounts and I wonder if what I'm doing is correct.
I directly use the $_POST['form'] for hydrate User objects.
But there is some field that I don't want user to modify (i.e. : Id_user, admin_level,...) so i use an argument in each setID or setAdmin_level (a boolean called $force) :
public function __construct(array $donnees = null, $forcer = false)
{
if($donnees){
$this->hydrate($donnees,$forcer);
}
}
public function hydrate(array $donnees, $forcer = false)
{
foreach($donnees as $champ => $valeur){
$method = 'set'.ucfirst($champ);
if(method_exists($this,$method))
{
if($forcer){
try {
$this->$method($this->securite($valeur), true);
}catch(Exception $e){
$this->$method($this->securite($valeur));
}
}else {
$this->$method($this->securite($valeur));
}
}
}
}
Here is an example of a "secured" setter :
public function setId_user($id_user, $forcer = false)
{
if(is_numeric($id_user)&&$forcer)
{
$this->_id_user = $id_user;
return true;
}else {
$this->addErreur('id_user','User ID incorrect');
return false;
}
}
So, in order to register a new user, I hydrate a new User object with the form POST :
$user = new User($_POST[form], false);
And I need to set the $force bool to true for creating a new User from an Id or if I want to set a field protected.
$userManager->getUserFromId(new User(['id_user' => 1], true));
Or
$userManager->getUserFromEmail(new User(['email' => "my_email#email.com"], true));
Is this a good way?
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.
I have been creating a helper class for the Facebook PHP API in order to avoid reusing a lot of code. The helper works but the only problem is that its very slow.. and I also figured out why! when I initialize the class, the constructor is called twice! I checked in my code and the other elements which use this class only call it once (It's something inside the class itself) Could you please help me figure out what the problems could be?? Thanks!
class FbHelper
{
private $_fb;
private $_user;
function __construct()
{
// Initalize Facebook API with keys
$this->_fb = new Facebook(array(
'appId' => 'xxxxxxxxxxx',
'secret' => 'xxxxxxxxxxxxxxxxxxxxxx',
'cookie' => true,
));
// set the _user variable
//
$this->doLog("Called Constructor");
//
$this->_user = $this->UserSessionAuthorized();
return $this;
}
function doLog($text)
{
// open log file <----- THIS GETS CALLED TWICE EVERY TIME I INITIALIZE THE CLASS!!
$filename = "form_ipn.log";
$fh = fopen($filename, "a") or die("Could not open log file.");
fwrite($fh, date("d-m-Y, H:i")." - $text\n") or die("Could not write file!");
fclose($fh);
}
function getUser() { return $this->_user; }
function getLoginUrl() { return $this->_fb->getLoginUrl(); }
function getLogoutUrl() { return $this->_fb->getLogoutUrl(); }
function UserSessionAuthorized()
{
// Checks if user is authorized, if is sends back user object
$user = null;
$session = $this->_fb->getSession();
if (!$session) return false;
try {
$uid = $this->_fb->getUser();
$user = $this->_fb->api('/me');
if ($user) return $user;
else return false;
}
catch (FacebookApiException $e) { return false; }
}
private function _rebuildSelectedFriends($selected_friends)
{
// Creates a new array with less data, more useful and less malicious
$new = array();
foreach ($selected_friends as $friend)
{
$f = array('id' => $friend['id'], 'name' => $friend['name']);
$new[] = $f;
}
return $new;
}
function GetThreeRandomFriends()
{
$friends = $this->_fb->api('/me/friends');
$n = rand(1, count($friends['data']) - 3);
$selected_friends = array_slice($friends['data'], $n, 3);
return $this->_rebuildSelectedFriends($selected_friends);
}
function UserExists($user_id)
{
try { $this->_fb->api('/' . $user_id . '/'); return true; }
catch (Exception $e) { return false; }
}
}
You must be calling the FbHelper class twice as your doLog function is in the constructor, therefore the repetition is somewhere higher up in your application and not in this class itself.
For the OpenID authentication I'm using "PHP OpenID Library" (http://www.janrain.com/openid-enabled). How, with the help of this library, ask for additional information (nickname, email)?
I've got some problems with LightOpenID, when I ask email at yandex LightOpenID-> valid returns false(
class Ncw_OpenID extends LightOpenID
{
const OPENID_MODE_CANCEL = 'cancel';
public function __construct()
{
parent::__construct();
$this->required = array('namePerson/friendly', 'contact/email');
$this->optional = array('contact/email');
//$this->returnUrl = 'http://' . SITE_URI . '/users/login';
}
public function getAttributes() {
$attr = parent::getAttributes();
$newAttr = array();
foreach ($attr as $key => $value) {
if (isset(parent::$ax_to_sreg[$key])) $key = parent::$ax_to_sreg[$key];
$newAttr[$key] = $value;
}
return $newAttr;
}
}
class Users_IndexController extends Zend_Controller_Action
{
public function loginAction()
{
$openIDMode = $this->_request->getParam('openid_mode');
$openID = new Ncw_OpenID();
$form = new Users_Form_Login(array('action' => $this->view->url(array(), 'openIDLogin')));
if (null === $openIDMode) {
if ($this->_request->isPost() && $form->isValid($_POST)) {
$openID->identity = $form->getValue('openIDUri');
$this->_redirect($openID->authUrl());
exit();
}
$this->view->content = $form;
} elseif (Ncw_OpenID::OPENID_MODE_CANCEL == $openIDMode) {
$this->view->content = 'Cancel';
} else {
if ($openID->validate()) {
$this->view->content = 'Valid: ' . $openID->identity . ' = ' . var_export($openID->getAttributes(), true);
} else {
$this->view->content = 'Not Valid';
}
$this->view->content .= $form;
}
}
public function logoutAction()
{
// action body
}
}
Here is an incomplete example. It's incomplete in the sense that it's using only SREG, and not every provider supports it (for example, Google supports only AX).
As far as I know, php-openid doesn't offer a simple way to automatically detect what does the server support and accordingly use AX or SREG.
For more information, I'd look at the source code's comments or as the README suggests, generate documentation from them, using phpdoc.
However, if you can switch libraries, I'd recommend LightOpenID. It's easier to use and does most things automatically (contrary to php-openid).
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"/>';